@@ -2222,6 +2222,161 @@ struct cfg80211_qos_map {
};
/**
+ * struct cfg80211_ftm_target - data for an FTM target (FTM responder)
+ *
+ * @cookie: Extra data for the use of the invoking component. This will be
+ * passed back to the caller in the response, along with the rest of the
+ * request.
+ * @chan_def: target's channel info
+ * @bssid: target's BSSID.
+ * @one_sided: whether to perform a one-sided (flag set) or two-sided (flag
+ * clear) measurement.
+ * @asap: Whether to perform the measurement in ASAP mode. Ignored if one-sided.
+ * @lci: Whether to query for LCI in the request. Ignored if one-sided.
+ * @civic: Whether to query for CIVIC in the request. Ignored if one-sided.
+ * @num_of_bursts: number of measurement iterations.
+ * @burst_period: Measurement periodicity in units of 100ms. Ignored if num of
+ * bursts is 1.
+ * @samples_per_burst: Number of measurement frames requested per burst.
+ * @retries: Number of retries per sample.
+ */
+struct cfg80211_ftm_target {
+ u64 cookie;
+ struct cfg80211_chan_def chan_def;
+ u8 bssid[ETH_ALEN];
+ bool one_sided;
+ bool asap;
+ bool lci;
+ bool civic;
+ u16 num_of_bursts;
+ u16 burst_period;
+ u8 samples_per_burst;
+ u8 retries;
+};
+
+/**
+ * struct cfg80211_ftm_request - data for FTM requests
+ *
+ * @report_tsf: if true, report the TSF of the AP to which the vif is
+ * associated. Not relevant if the vif is not associated.
+ * @timeout: Timespan within which measurement should complete. Given in units
+ * of 100ms.
+ * @macaddr_template: Sets the fixed part of a randomized mac address.
+ * @macaddr_mask: Bits set to 1 shall be copied from @macaddr_template. Bits set
+ * to 0 shall be randomized by the device.
+ * @num_of_targets: Number of targets (with which to perform a measurement)
+ * contained in this request (see @targets). &num_of_target will not
+ * exceed the value reported for the device in
+ * %NL80211_ATTR_MAX_TOTAL_FTM_TARGETS.
+ * @targets: List of targets with which to perform the measurement. This list is
+ * dynamically allocated when the request arrives, and should be released
+ * using kfree by the underlying driver when it is no longer required.
+ * Amongst these targets, the number of 2-sided requests will not exceed
+ * the value reported for the device in
+ * %NL80211_ATTR_MAX_TWO_SIDED_FTM_TARGETS.
+ */
+struct cfg80211_ftm_request {
+ bool report_tsf;
+ u8 timeout;
+ u8 macaddr_template[ETH_ALEN];
+ u8 macaddr_mask[ETH_ALEN];
+ u8 num_of_targets;
+ struct cfg80211_ftm_target *targets;
+};
+
+/**
+ * struct cfg80211_msrment_request - measurement request data
+ *
+ * @type: Type of measurement. Determines the actual type of the union field
+ * below.
+ * @nl_portid: the netlink port used for this request
+ * @u: Data for the specific required measurement type.
+ */
+struct cfg80211_msrment_request {
+ enum nl80211_msrment_type type;
+ u32 nl_portid;
+ union {
+ struct cfg80211_ftm_request ftm;
+ } u;
+};
+
+/**
+ * struct cfg80211_ftm_result - data for an FTM result of a single target
+ *
+ * @status: Status of measurement
+ * @complete: Whether this measurement is the last one expected for this target.
+ * This implies that resources associated with this target may be released.
+ * @target: Pointer to the corresponding FTM target given in the request.
+ * @host_time: Time in which:
+ * - in case of error - error was detected
+ * - in case of success - successful measurement started
+ * Given value is in nanoseconds elapsed since host boot time
+ * (referring to CLOCK_BOOTTIME).
+ * Note that this reported value is an estimation of the actual event time,
+ * with expected error of up to 20ms off the actual mark. Underlying
+ * devices must make sure they comply with this limited tolerance.
+ * @tsf: Same as %host_time, but in the expressed as the TSF of the AP the vif
+ * is associated to. This value is not an estimation. If field
+ * &report_tsf in the request is not set, this field is ignored.
+ * @burst_index: Ordinal number of currently reported measurement iteration.
+ * @rssi: Measured RSSI, given in dBm. Valid values range: -128-0.
+ * @rssi_spread: The difference between max and min measured RSSI values
+ * @rate_info: Used rate-related data.
+ * @rtt: The Round Trip Time that took for the last measurement for current
+ * target, in nsec.
+ * @rtt_variance: The variance of the RTT values measured for current target, in
+ * nsec^2.
+ * @rtt_spread: The difference between max and min RTT values measured for
+ * the current target in the current session, in nsec.
+ */
+struct cfg80211_ftm_result {
+ enum nl80211_ftm_response_status status;
+ bool complete;
+ struct cfg80211_ftm_target *target;
+ u64 host_time;
+ u64 tsf;
+ u8 burst_index;
+ s8 rssi;
+ u8 rssi_spread;
+ struct rate_info rate_info;
+ u32 rtt;
+ u32 rtt_variance;
+ u32 rtt_spread;
+};
+
+/**
+ * struct cfg80211_ftm_results - data for FTM results of all targets
+ *
+ * @num_of_entries: num of entries in the results array
+ * @entries: an array of FTM results. this array is both allocated and
+ * released in the driver.
+ */
+struct cfg80211_ftm_results {
+ u8 num_of_entries;
+ struct cfg80211_ftm_result *entries;
+};
+
+/**
+ * struct cfg80211_msrment_response - measurement response data
+ * @cookie: Identifier of current measurement response, matching the one given
+ * in the request.
+ * @type: Type of measurement. Determines the actual type of the union field
+ * below.
+ * @status: Status of current measurement response.
+ * @nl_portid: netlink port this response should be sent to
+ * @u: Data for the specific reported measurement type.
+ */
+struct cfg80211_msrment_response {
+ u64 cookie;
+ enum nl80211_msrment_type type;
+ enum nl80211_msrment_status status;
+ u32 nl_portid;
+ union {
+ struct cfg80211_ftm_results ftm;
+ } u;
+};
+
+/**
* struct cfg80211_ops - backend description for wireless configuration
*
* This struct is registered by fullmac card drivers and/or wireless stacks
@@ -2494,6 +2649,12 @@ struct cfg80211_qos_map {
* and returning to the base channel for communication with the AP.
* @tdls_cancel_channel_switch: Stop channel-switching with a TDLS peer. Both
* peers must be on the base channel when the call completes.
+ *
+ * @perform_msrment: Perform a measurement according to the given request. Once
+ * this function returns, the given request pointer in no longer valid.
+ * The cookie must be filled to a unique value for this request, for later
+ * possible aborting.
+ * @abort_msrment: Abort a previously requested measurement.
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -2759,6 +2920,12 @@ struct cfg80211_ops {
void (*tdls_cancel_channel_switch)(struct wiphy *wiphy,
struct net_device *dev,
const u8 *addr);
+ int (*perform_msrment)(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_msrment_request *request,
+ u64 *cookie);
+ int (*abort_msrment)(struct wiphy *wiphy, struct wireless_dev *wdev,
+ u64 cookie);
};
/*
@@ -2805,6 +2972,8 @@ struct cfg80211_ops {
* @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
* @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
* beaconing mode (AP, IBSS, Mesh, ...).
+ * @WIPHY_FLAG_SUPPORTS_FTM_INITIATOR: Device supports 802.11 Fine Timing
+ * Measurement Initiator.
*/
enum wiphy_flags {
/* use hole at 0 */
@@ -2830,6 +2999,7 @@ enum wiphy_flags {
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL = BIT(21),
WIPHY_FLAG_SUPPORTS_5_10_MHZ = BIT(22),
WIPHY_FLAG_HAS_CHANNEL_SWITCH = BIT(23),
+ WIPHY_FLAG_SUPPORTS_FTM_INITIATOR = BIT(24),
};
/**
@@ -3178,6 +3348,11 @@ struct wiphy_vendor_command {
* low rssi when a frame is heard on different channel, then it should set
* this variable to the maximal offset for which it can compensate.
* This value should be set in MHz.
+ *
+ * @max_two_sided_ftm_targets: max number of 2-sided targets allowed in an FTM
+ * request.
+ * @max_total_ftm_targets: max number of targets (both 1-sided and 2-sided) in
+ * an FTM request. A value of 0 implies no FTM support.
*/
struct wiphy {
/* assign these fields before you register the wiphy */
@@ -3300,6 +3475,9 @@ struct wiphy {
u8 max_num_csa_counters;
u8 max_adj_channel_rssi_comp;
+ u8 max_two_sided_ftm_targets;
+ u8 max_total_ftm_targets;
+
char priv[0] __aligned(NETDEV_ALIGN);
};
@@ -5218,6 +5396,17 @@ void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp);
unsigned int ieee80211_get_num_supported_channels(struct wiphy *wiphy);
/**
+ * cfg80211_measurement_response - notify regarding a measurement response
+ *
+ * @wiphy: the wiphy
+ * @response: a response for which to notify
+ * @gfp: allocation flags
+ */
+void cfg80211_measurement_response(struct wiphy *wiphy,
+ struct cfg80211_msrment_response *response,
+ gfp_t gfp);
+
+/**
* cfg80211_check_combinations - check interface combinations
*
* @wiphy: the wiphy
@@ -824,6 +824,23 @@
* not running. The driver indicates the status of the scan through
* cfg80211_scan_done().
*
+ * @NL80211_CMD_MSRMENT_REQUEST: Request to perform some type of measurement.
+ * Request type is given by %NL80211_ATTR_MSRMENT_TYPE. Additional data is
+ * given according to the request type.
+ * When called, this operation returns a cookie (%NL80211_ATTR_COOKIE)
+ * that will be included with any events pertaining to this request.
+ * In order to abort the request, the socket which sent the request needs
+ * to be closed. It is strongly recommended that each request will have a
+ * separate socket.
+ * @NL80211_CMD_MSRMENT_RESPONSE: Reports measurement results in response to a
+ * previous measurement request. A cookie matching the previous request is
+ * given by %NL80211_ATTR_COOKIE. Response type is given by
+ * %NL80211_ATTR_MSRMENT_TYPE. Response status is given by
+ * %NL80211_ATTR_MSRMENT_STATUS. Additional data is given according to the
+ * request type.
+ * This message might be sent multiple times for one response, splitting
+ * the response into several segments. The @NL80211_ATTR_LAST_MSG flag
+ * should be set in the last message of the response.
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1012,6 +1029,9 @@ enum nl80211_commands {
NL80211_CMD_ABORT_SCAN,
+ NL80211_CMD_MSRMENT_REQUEST,
+ NL80211_CMD_MSRMENT_RESPONSE,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1790,6 +1810,25 @@ enum nl80211_commands {
* between scans. The scan plans are executed sequentially.
* Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan.
*
+ * @NL80211_ATTR_MSRMENT_TYPE: Type of current measurement request/response.
+ * (values defined in &enum nl80211_msrment_type).
+ * @NL80211_ATTR_MSRMENT_STATUS: Status of current measurement response.
+ * (values defined in &enum nl80211_msrment_status)
+ * @NL80211_ATTR_MSRMENT_FTM_REQUEST: Container for data of an FTM measurement
+ * request (nested. see &enum nl80211_ftm_request)
+ * @NL80211_ATTR_MSRMENT_FTM_RESPONSE: An AP with which a measurement was
+ * attempted (nested. see &enum nl80211_ftm_response_entry)
+ * An FTM response consists of series of such messages, where the last
+ * message is marked with the @NL80211_ATTR_LAST_MSG flag.
+ * @NL80211_ATTR_MAX_TWO_SIDED_FTM_TARGETS: Max number of 2-sided targets
+ * allowed by the device in an FTM request. Not in use in case FTM is not
+ * supported. (u32)
+ * @NL80211_ATTR_MAX_TOTAL_FTM_TARGETS: Max number of targets (both 1-sided and
+ * 2-sided) allowed by the device in an FTM request. Not in use in case FTM
+ * is not supported. (u32)
+ * @NL80211_ATTR_LAST_MSG: Indicates that this message is the last one in the
+ * series of messages. (flag)
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2164,6 +2203,16 @@ enum nl80211_attrs {
NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
NL80211_ATTR_SCHED_SCAN_PLANS,
+ NL80211_ATTR_MSRMENT_TYPE,
+ NL80211_ATTR_MSRMENT_STATUS,
+
+ NL80211_ATTR_MSRMENT_FTM_REQUEST,
+ NL80211_ATTR_MSRMENT_FTM_RESPONSE,
+ NL80211_ATTR_MAX_TWO_SIDED_FTM_TARGETS,
+ NL80211_ATTR_MAX_TOTAL_FTM_TARGETS,
+
+ NL80211_ATTR_LAST_MSG,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -4651,4 +4700,226 @@ enum nl80211_sched_scan_plan {
__NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1
};
+/**
+ * enum nl80211_msrment_type - measurement types
+ *
+ * Used to indicate the requested/reported measurement type in
+ * %NL80211_CMD_MSRMENT_REQUEST or %NL80211_CMD_MSRMENT_RESPONSE.
+ *
+ * @NL80211_MSRMENT_TYPE_FTM: Fine Timing Measurement.
+ * An FTM request should be constructed according to &enum
+ * nl80211_ftm_request.
+ * An FTM response is a serie of messages, each meassage including a
+ * single FTM target, described in &enum nl80211_ftm_target. The last
+ * message in the serie is marked with the @NL80211_ATTR_LAST_MSG flag.
+ * Status @NL80211_MSRMENT_STATUS_REFUSED is used if the device is not
+ * available for FTM operations. Status @NL80211_MSRMENT_STATUS_FAIL is
+ * used if the device attempted to perform the measurements, but all failed
+ * for local reasons. In these both cases, no response is included in the
+ * message. In other cases @NL80211_MSRMENT_STATUS_SUCCESS is used.
+ * In the latter case, internal status of each target is used to
+ * indicate the measurement status of each particular target.
+ */
+enum nl80211_msrment_type {
+ NL80211_MSRMENT_TYPE_FTM,
+};
+
+/**
+ * enum nl80211_msrment_status - measurement response status values
+ *
+ * @NL80211_MSRMENT_STATUS_SUCCESS: Measurement performed. This does not mean
+ * every sub-measurement was successful, but only that as a whole, the
+ * operation succeeded. More detailed status should reside in the internal
+ * parts of the response, and according to the measurement type.
+ * @NL80211_MSRMENT_STATUS_REFUSED: Device is refusing to perform the required
+ * measurement. Note that not every measurement can be performed at every
+ * given moment in time. See specific measurement details for execution
+ * conditions.
+ * @NL80211_MSRMENT_STATUS_FAIL: Measurement failed.
+ */
+enum nl80211_msrment_status {
+ NL80211_MSRMENT_STATUS_SUCCESS,
+ NL80211_MSRMENT_STATUS_REFUSED,
+ NL80211_MSRMENT_STATUS_FAIL,
+};
+
+/**
+ * enum nl80211_ftm_target - attributes for an FTM target
+ *
+ * An FTM target is a station with which to perform measurements.
+ *
+ * @__NL80211_FTM_TARGET_ATTR_INVALID: invalid
+ * @NL80211_FTM_TARGET_ATTR_FREQ: Target's frequency (u32)
+ * @NL80211_FTM_TARGET_ATTR_BW: Target's channel bandwidth. Only BWs supported
+ * by the device are allowed. (u8, one of &enum nl80211_chan_width)
+ * @NL80211_FTM_TARGET_ATTR_CNTR_FREQ_1: Center freq., 1st segment, if relevant
+ * (u32)
+ * @NL80211_FTM_TARGET_ATTR_CNTR_FREQ_2: Center freq., 2nd segment, if relevant
+ * (u32)
+ * @NL80211_FTM_TARGET_ATTR_BSSID: Target's BSSID (6 octets)
+ * @NL80211_FTM_TARGET_ATTR_ONE_SIDED: whether to perform a one-sided (flag set)
+ * or two-sided (flag clear) measurement. (flag)
+ * @NL80211_FTM_TARGET_ATTR_NUM_OF_BURSTS: number of measurement iterations.
+ * Optional (default: 1). (u16)
+ * @NL80211_FTM_TARGET_ATTR_BURST_PERIOD: Measurement periodicity in units of
+ * 100ms. Ignored if num of bursts is 1. (u16)
+ * @NL80211_FTM_TARGET_ATTR_SAMPLES_PER_BURST: Number of measurement frames
+ * requested per burst. Optional (default: 2) (u8)
+ * @NL80211_FTM_TARGET_ATTR_RETRIES: Number of retries per sample.
+ * Optional (default: 3). (u8)
+ * @NL80211_FTM_TARGET_ATTR_ASAP: Whether to perform the measurement in ASAP
+ * mode. Ignored if one-sided. (flag)
+ * @NL80211_FTM_TARGET_QUERY_LCI: Whether to include an LCI query in the
+ * request. (flag)
+ * @NL80211_FTM_TARGET_QUERY_CIVIC: Whether to include a CIVIC query in the
+ * request. (flag)
+ * @NL80211_FTM_TARGET_ATTR_COOKIE: Extra data for the use of the invoking
+ * component. This will be passed back to the caller in the response, along
+ * with the rest of the request. Optional. (u64)
+ * @__NL80211_FTM_TARGET_ATTR_AFTER_LAST: internal
+ * @NL80211_FTM_TARGET_ATTR_MAX: highest FTM target attribute
+ */
+enum nl80211_ftm_target {
+ __NL80211_FTM_TARGET_ATTR_INVALID,
+ NL80211_FTM_TARGET_ATTR_FREQ,
+ NL80211_FTM_TARGET_ATTR_BW,
+ NL80211_FTM_TARGET_ATTR_CNTR_FREQ_1,
+ NL80211_FTM_TARGET_ATTR_CNTR_FREQ_2,
+ NL80211_FTM_TARGET_ATTR_BSSID,
+ NL80211_FTM_TARGET_ATTR_ONE_SIDED,
+ NL80211_FTM_TARGET_ATTR_NUM_OF_BURSTS,
+ NL80211_FTM_TARGET_ATTR_BURST_PERIOD,
+ NL80211_FTM_TARGET_ATTR_SAMPLES_PER_BURST,
+ NL80211_FTM_TARGET_ATTR_RETRIES,
+ NL80211_FTM_TARGET_ATTR_ASAP,
+ NL80211_FTM_TARGET_ATTR_QUERY_LCI,
+ NL80211_FTM_TARGET_ATTR_QUERY_CIVIC,
+ NL80211_FTM_TARGET_ATTR_COOKIE,
+
+ /* keep last */
+ __NL80211_FTM_TARGET_ATTR_AFTER_LAST,
+ NL80211_FTM_TARGET_ATTR_MAX = __NL80211_FTM_TARGET_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_ftm_request - attributes for an FTM request
+ *
+ * Note: Only a single FTM request can be handled at a time.
+ *
+ * @__NL80211_FTM_REQ_ATTR_INVALID: invalid
+ * @NL80211_FTM_REQ_ATTR_TIMEOUT: Timespan within which measurement should
+ * complete. Given in tenths of a second. Optional (default: 50). (u8)
+ * @NL80211_FTM_REQ_ATTR_MACADDR_TEMPLATE: Device will use the given template
+ * (and mask, see ahead) to generate a mac address for identification. This
+ * attribute sets the fixed part of a randomized mac address. (6 octets)
+ * The MC bit must be set to 0.
+ * @NL80211_FTM_REQ_ATTR_MACADDR_MASK: Mask for mac address randomization. Bits
+ * set to 1 shall be copied from %NL80211_FTM_REQ_ATTR_MACADDR_TEMPLATE.
+ * Bits set to 0 shall be randomized by the device.
+ * MC bit should not be randomized(set to 1). (6 octets)
+ * @NL80211_FTM_REQ_ATTR_REPORT_TSF: Flag that indicates to use the associated
+ * AP's TSF in the %NL80211_FTM_RESP_ENTRY_ATTR_TSF field in the response.
+ * Useful for RRM requests, where an associated AP requires to perform FTM,
+ * and expects a timestamp in its own TSF. If not set, no tsf value is
+ * reported in the response. Ignored if no AP is associated. (flag)
+ * @NL80211_FTM_REQ_ATTR_TARGETS: List of targets with which to perform
+ * measurements. Length shall not exceed the value reported for the device
+ * in %NL80211_ATTR_MAX_TOTAL_FTM_TARGETS. Among these targets, the number
+ * of 2-sided requests shall not exceed the value reported for the device
+ * in %NL80211_ATTR_MAX_2_SIDED_FTM_TARGETS.
+ * (nested. see &enum nl80211_ftm_target)
+ *
+ * @__NL80211_FTM_REQ_ATTR_AFTER_LAST: internal
+ * @NL80211_FTM_REQ_ATTR_MAX: highest FTM request attribute
+ */
+enum nl80211_ftm_request {
+ __NL80211_FTM_REQ_ATTR_INVALID,
+ NL80211_FTM_REQ_ATTR_TIMEOUT,
+ NL80211_FTM_REQ_ATTR_MACADDR_TEMPLATE,
+ NL80211_FTM_REQ_ATTR_MACADDR_MASK,
+ NL80211_FTM_REQ_ATTR_REPORT_TSF,
+ NL80211_FTM_REQ_ATTR_TARGETS,
+
+ /* keep last */
+ __NL80211_FTM_REQ_ATTR_AFTER_LAST,
+ NL80211_FTM_REQ_ATTR_MAX = __NL80211_FTM_REQ_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_ftm_response_status - status of an FTM measurement attempt
+ *
+ * @NL80211_FTM_RESP_SUCCESS: Successful measurement, given results are valid.
+ * @NL80211_FTM_RESP_TARGET_INCAPAB: Target reported incapable
+ * @NL80211_FTM_RESP_TARGET_BUSY: Target reported busy
+ * @NL80211_FTM_RESP_FAIL: Failed for some other reason.
+ */
+enum nl80211_ftm_response_status {
+ NL80211_FTM_RESP_SUCCESS,
+ NL80211_FTM_RESP_TARGET_INCAPAB,
+ NL80211_FTM_RESP_TARGET_BUSY,
+ NL80211_FTM_RESP_FAIL,
+};
+
+/**
+ * enum nl80211_ftm_response_entry - attributes for an FTM response entry
+ *
+ * An FTM response entry represents a single target with which an FTM
+ * measurement was attempted.
+ *
+ * @__NL80211_FTM_RESP_ENTRY_ATTR_INVALID: invalid
+ * @NL80211_FTM_RESP_ENTRY_ATTR_STATUS: Status of measurement. (u8, one of
+ * &enum nl80211_ftm_response_status)
+ * @NL80211_FTM_RESP_ENTRY_ATTR_COMPLETE: Whether this measurement is the last
+ * one expected for this target. This implies that resources associated
+ * with this target may be released. (flag)
+ * @NL80211_FTM_RESP_ENTRY_ATTR_TARGET: The corresponding FTM target entry in
+ * the measurement request. (nested. see &enum nl80211_ftm_target)
+ * @NL80211_FTM_RESP_ENTRY_ATTR_HOST_TIME: Time, given in nanoseconds since
+ * host boot time(referring to CLOCK_BOOTTIME), in which:
+ * - in case of error - error was detected
+ * - in case of success - successful measurement started
+ * Note that this reported value is an estimation of the actual event time,
+ * with expected error of up to 20ms off the actual mark. Underlying
+ * devices must make sure they comply with this limited tolerance. (u64)
+ * @NL80211_FTM_RESP_ENTRY_ATTR_TSF: Same as %NL80211_FTM_RESP_ATTR_HOST_TIME,
+ * but the value is TSF of the associated AP. Optional - present only if
+ * %NL80211_FTM_REQ_ATTR_AP_REPORT_TSF was set in the request, and an
+ * associated AP exists. Also, this value is not an estimation. (u64)
+ * @NL80211_FTM_RESP_ENTRY_ATTR_BURST_INDEX: Ordinal number of currently
+ * reported measurement iteration. (u8)
+ * @NL80211_FTM_RESP_ENTRY_ATTR_RSSI: Measured RSSI, given in dBm. Valid values
+ * range: -128-0. (s8)
+ * @NL80211_FTM_RESP_ENTRY_ATTR_RSSI_SPREAD: The difference between max and min
+ * measured RSSI values. (u8)
+ * @NL80211_FTM_RESP_ENTRY_ATTR_RATE_INFO: Rate-related data. (nested. see enum
+ * nl80211_rate_info)
+ * @NL80211_FTM_RESP_ENTRY_ATTR_RTT: The Round Trip Time that took for the last
+ * measurement for current target, in nsec. (u32)
+ * @NL80211_FTM_RESP_ENTRY_ATTR_RTT_VAR: The variance of the RTT values measured
+ * for current target, in nsec^2. (u32)
+ * @NL80211_FTM_RESP_ENTRY_ATTR_RTT_SPREAD: The difference between max and min
+ * RTT values measured for the current target in the current session, given
+ * in nsec (u32)
+ */
+enum nl80211_ftm_response_entry {
+ __NL80211_FTM_RESP_ENTRY_ATTR_INVALID,
+ NL80211_FTM_RESP_ENTRY_ATTR_STATUS,
+ NL80211_FTM_RESP_ENTRY_ATTR_COMPLETE,
+ NL80211_FTM_RESP_ENTRY_ATTR_TARGET,
+ NL80211_FTM_RESP_ENTRY_ATTR_HOST_TIME,
+ NL80211_FTM_RESP_ENTRY_ATTR_TSF,
+ NL80211_FTM_RESP_ENTRY_ATTR_BURST_INDEX,
+ NL80211_FTM_RESP_ENTRY_ATTR_RSSI,
+ NL80211_FTM_RESP_ENTRY_ATTR_RSSI_SPREAD,
+ NL80211_FTM_RESP_ENTRY_ATTR_RATE_INFO,
+ NL80211_FTM_RESP_ENTRY_ATTR_RTT,
+ NL80211_FTM_RESP_ENTRY_ATTR_RTT_VAR,
+ NL80211_FTM_RESP_ENTRY_ATTR_RTT_SPREAD,
+
+ /* keep last */
+ __NL80211_FTM_RESP_ENTRY_ATTR_AFTER_LAST,
+ NL80211_FTM_RESP_ENTRY_ATTR_MAX =
+ __NL80211_FTM_RESP_ENTRY_ATTR_AFTER_LAST - 1,
+};
+
#endif /* __LINUX_NL80211_H */
@@ -3,6 +3,7 @@
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright 2015 Intel Deutschland GmbH
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -334,6 +335,33 @@ static void cfg80211_sched_scan_stop_wk(struct work_struct *work)
rtnl_unlock();
}
+static void cfg80211_abort_msrment_wk(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev;
+ struct cfg80211_active_msrment *msrment, *tmp;
+ LIST_HEAD(msrments_list);
+
+ rdev = container_of(work, struct cfg80211_registered_device,
+ msrment_abort_wk);
+
+ spin_lock_bh(&rdev->msrments_lock);
+ list_for_each_entry_safe(msrment, tmp, &rdev->msrments_list, list) {
+ if (msrment->nl_portid != 0)
+ continue;
+ list_del(&msrment->list);
+ list_add(&msrment->list, &msrments_list);
+ }
+ spin_unlock_bh(&rdev->msrments_lock);
+
+ rtnl_lock();
+ list_for_each_entry_safe(msrment, tmp, &msrments_list, list) {
+ rdev_abort_msrment(rdev, msrment->wdev, msrment->cookie);
+ list_del(&msrment->list);
+ kfree(msrment);
+ }
+ rtnl_unlock();
+}
+
/* exported functions */
struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
@@ -405,6 +433,9 @@ use_default_name:
spin_lock_init(&rdev->beacon_registrations_lock);
spin_lock_init(&rdev->bss_lock);
INIT_LIST_HEAD(&rdev->bss_list);
+ spin_lock_init(&rdev->msrments_lock);
+ INIT_LIST_HEAD(&rdev->msrments_list);
+ INIT_WORK(&rdev->msrment_abort_wk, cfg80211_abort_msrment_wk);
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
INIT_WORK(&rdev->sched_scan_results_wk, __cfg80211_sched_scan_results);
INIT_LIST_HEAD(&rdev->mlme_unreg);
@@ -616,6 +647,10 @@ int wiphy_register(struct wiphy *wiphy)
!rdev->ops->set_mac_acl)))
return -EINVAL;
+ if (WARN_ON((wiphy->flags & WIPHY_FLAG_SUPPORTS_FTM_INITIATOR) &&
+ (!rdev->ops->perform_msrment || !rdev->ops->abort_msrment)))
+ return -EINVAL;
+
if (wiphy->addresses)
memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
@@ -779,9 +814,11 @@ void wiphy_unregister(struct wiphy *wiphy)
rfkill_unregister(rdev->rfkill);
rtnl_lock();
+ /* since we no longer have any wdevs, the list should be empty */
+ WARN_ON(!list_empty(&rdev->msrments_list));
+
nl80211_notify_wiphy(rdev, NL80211_CMD_DEL_WIPHY);
rdev->wiphy.registered = false;
-
WARN_ON(!list_empty(&rdev->wdev_list));
/*
@@ -810,6 +847,7 @@ void wiphy_unregister(struct wiphy *wiphy)
flush_work(&rdev->destroy_work);
flush_work(&rdev->sched_scan_stop_wk);
flush_work(&rdev->mlme_unreg_wk);
+ flush_work(&rdev->msrment_abort_wk);
#ifdef CONFIG_PM
if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
@@ -852,9 +890,26 @@ EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);
void cfg80211_unregister_wdev(struct wireless_dev *wdev)
{
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct cfg80211_active_msrment *msrment, *tmp;
+ LIST_HEAD(msrments_list);
ASSERT_RTNL();
+ spin_lock_bh(&rdev->msrments_lock);
+ list_for_each_entry_safe(msrment, tmp, &rdev->msrments_list, list) {
+ if (msrment->wdev != wdev)
+ continue;
+ list_del(&msrment->list);
+ list_add(&msrment->list, &msrments_list);
+ }
+ spin_unlock_bh(&rdev->msrments_lock);
+
+ list_for_each_entry_safe(msrment, tmp, &msrments_list, list) {
+ rdev_abort_msrment(rdev, wdev, msrment->cookie);
+ list_del(&msrment->list);
+ kfree(msrment);
+ }
+
if (WARN_ON(wdev->netdev))
return;
@@ -2,6 +2,7 @@
* Wireless configuration interface internals.
*
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2015 Intel Deutschland GmbH
*/
#ifndef __NET_WIRELESS_CORE_H
#define __NET_WIRELESS_CORE_H
@@ -63,6 +64,10 @@ struct cfg80211_registered_device {
spinlock_t mlme_unreg_lock;
struct work_struct mlme_unreg_wk;
+ spinlock_t msrments_lock;
+ struct list_head msrments_list;
+ struct work_struct msrment_abort_wk;
+
/* protected by RTNL only */
int num_running_ifaces;
int num_running_monitor_ifaces;
@@ -258,6 +263,13 @@ struct cfg80211_iface_destroy {
u32 nlportid;
};
+struct cfg80211_active_msrment {
+ struct wireless_dev *wdev;
+ struct list_head list;
+ u64 cookie;
+ u32 nl_portid;
+};
+
void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev);
/* free object */
@@ -401,6 +401,13 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 },
[NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },
[NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG },
+ [NL80211_ATTR_MSRMENT_TYPE] = { .type = NLA_U32 },
+ [NL80211_ATTR_MSRMENT_STATUS] = { .type = NLA_U8 },
+ [NL80211_ATTR_MSRMENT_FTM_REQUEST] = { .type = NLA_NESTED },
+ [NL80211_ATTR_MSRMENT_FTM_RESPONSE] = { .type = NLA_NESTED },
+ [NL80211_ATTR_MAX_TWO_SIDED_FTM_TARGETS] = { .type = NLA_U8 },
+ [NL80211_ATTR_MAX_TOTAL_FTM_TARGETS] = { .type = NLA_U8 },
+ [NL80211_ATTR_LAST_MSG] = { .type = NLA_FLAG },
};
/* policy for the key attributes */
@@ -1389,6 +1396,14 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
rdev->wiphy.interface_modes))
goto nla_put_failure;
+
+ if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FTM_INITIATOR) &&
+ (nla_put_u32(msg, NL80211_ATTR_MAX_TWO_SIDED_FTM_TARGETS,
+ rdev->wiphy.max_two_sided_ftm_targets) ||
+ nla_put_u32(msg, NL80211_ATTR_MAX_TOTAL_FTM_TARGETS,
+ rdev->wiphy.max_total_ftm_targets)))
+ goto nla_put_failure;
+
state->split_start++;
if (state->split)
break;
@@ -10632,6 +10647,236 @@ static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb,
return 0;
}
+/* policy for the attributes of a single ftm target */
+static const struct nla_policy
+nl80211_ftm_target_policy[NL80211_FTM_TARGET_ATTR_MAX + 1] = {
+ [NL80211_FTM_TARGET_ATTR_FREQ] = { .type = NLA_U32 },
+ [NL80211_FTM_TARGET_ATTR_BW] = { .type = NLA_U8 },
+ [NL80211_FTM_TARGET_ATTR_CNTR_FREQ_1] = { .type = NLA_U32 },
+ [NL80211_FTM_TARGET_ATTR_CNTR_FREQ_2] = { .type = NLA_U32 },
+ [NL80211_FTM_TARGET_ATTR_BSSID] = { .len = ETH_ALEN },
+ [NL80211_FTM_TARGET_ATTR_ONE_SIDED] = { .type = NLA_FLAG },
+ [NL80211_FTM_TARGET_ATTR_NUM_OF_BURSTS] = { .type = NLA_U16 },
+ [NL80211_FTM_TARGET_ATTR_BURST_PERIOD] = { .type = NLA_U16 },
+ [NL80211_FTM_TARGET_ATTR_SAMPLES_PER_BURST] = { .type = NLA_U8 },
+ [NL80211_FTM_TARGET_ATTR_RETRIES] = { .type = NLA_U8 },
+ [NL80211_FTM_TARGET_ATTR_ASAP] = { .type = NLA_FLAG },
+ [NL80211_FTM_TARGET_ATTR_QUERY_LCI] = { .type = NLA_FLAG },
+ [NL80211_FTM_TARGET_ATTR_QUERY_CIVIC] = { .type = NLA_FLAG },
+ [NL80211_FTM_TARGET_ATTR_COOKIE] = { .type = NLA_U64 },
+};
+
+static int nl80211_parse_ftm_target(struct cfg80211_registered_device *rdev,
+ struct nlattr *ftm_target_attr,
+ struct cfg80211_ftm_target *target)
+{
+ struct nlattr *tb[NL80211_FTM_TARGET_ATTR_MAX + 1];
+ int err;
+
+ err = nla_parse_nested(tb, NL80211_FTM_TARGET_ATTR_MAX, ftm_target_attr,
+ nl80211_ftm_target_policy);
+ if (err)
+ return err;
+
+ if (!tb[NL80211_FTM_TARGET_ATTR_BSSID] ||
+ !tb[NL80211_FTM_TARGET_ATTR_FREQ] ||
+ !tb[NL80211_FTM_TARGET_ATTR_BW])
+ return -EINVAL;
+
+ nla_memcpy(target->bssid, tb[NL80211_FTM_TARGET_ATTR_BSSID], ETH_ALEN);
+
+ target->chan_def.chan = ieee80211_get_channel(&rdev->wiphy,
+ nla_get_u32(tb[NL80211_FTM_TARGET_ATTR_FREQ]));
+
+ target->chan_def.width = nla_get_u8(tb[NL80211_FTM_TARGET_ATTR_BW]);
+
+ if (tb[NL80211_FTM_TARGET_ATTR_CNTR_FREQ_1])
+ target->chan_def.center_freq1 =
+ nla_get_u32(tb[NL80211_FTM_TARGET_ATTR_CNTR_FREQ_1]);
+ else
+ target->chan_def.center_freq1 =
+ target->chan_def.chan->center_freq;
+
+ if (tb[NL80211_FTM_TARGET_ATTR_CNTR_FREQ_2])
+ target->chan_def.center_freq2 =
+ nla_get_u32(tb[NL80211_FTM_TARGET_ATTR_CNTR_FREQ_2]);
+
+ if (!cfg80211_chandef_valid(&target->chan_def))
+ return -EINVAL;
+
+ if (!tb[NL80211_FTM_TARGET_ATTR_NUM_OF_BURSTS])
+ target->num_of_bursts = 1;
+ else
+ target->num_of_bursts =
+ nla_get_u16(tb[NL80211_FTM_TARGET_ATTR_NUM_OF_BURSTS]);
+
+ if (target->num_of_bursts == 1) {
+ if (!tb[NL80211_FTM_TARGET_ATTR_BURST_PERIOD])
+ return -EINVAL;
+ target->burst_period =
+ nla_get_u16(tb[NL80211_FTM_TARGET_ATTR_BURST_PERIOD]);
+ }
+
+ target->samples_per_burst =
+ tb[NL80211_FTM_TARGET_ATTR_SAMPLES_PER_BURST] ?
+ nla_get_u8(tb[NL80211_FTM_TARGET_ATTR_SAMPLES_PER_BURST]) : 2;
+
+ if (!tb[NL80211_FTM_TARGET_ATTR_RETRIES])
+ target->retries = 3;
+ else
+ target->retries =
+ nla_get_u8(tb[NL80211_FTM_TARGET_ATTR_RETRIES]);
+
+ target->one_sided = nla_get_flag(tb[NL80211_FTM_TARGET_ATTR_ONE_SIDED]);
+ target->asap = nla_get_flag(tb[NL80211_FTM_TARGET_ATTR_ASAP]);
+ target->lci = nla_get_flag(tb[NL80211_FTM_TARGET_ATTR_QUERY_LCI]);
+ target->civic = nla_get_flag(tb[NL80211_FTM_TARGET_ATTR_QUERY_CIVIC]);
+
+ if (tb[NL80211_FTM_TARGET_ATTR_COOKIE])
+ target->cookie =
+ nla_get_u64(tb[NL80211_FTM_TARGET_ATTR_COOKIE]);
+
+ return 0;
+}
+
+/* policy for the ftm request attributes */
+static const struct nla_policy
+nl80211_ftm_request_policy[NL80211_FTM_REQ_ATTR_MAX + 1] = {
+ [NL80211_FTM_REQ_ATTR_TIMEOUT] = { .type = NLA_U8 },
+ [NL80211_FTM_REQ_ATTR_MACADDR_TEMPLATE] = { .len = ETH_ALEN },
+ [NL80211_FTM_REQ_ATTR_MACADDR_MASK] = { .len = ETH_ALEN },
+ [NL80211_FTM_REQ_ATTR_REPORT_TSF] = { .type = NLA_FLAG },
+ [NL80211_FTM_REQ_ATTR_TARGETS] = { .type = NLA_NESTED },
+};
+
+static int nl80211_parse_ftm_request(struct cfg80211_registered_device *rdev,
+ struct nlattr *ftm_attr,
+ struct cfg80211_ftm_request *ftm)
+{
+ struct nlattr *tb[NL80211_FTM_REQ_ATTR_MAX + 1];
+ struct nlattr *ap_attr;
+ int tmp, i, two_sided_counter;
+ int err;
+
+ err = nla_parse_nested(tb, NL80211_FTM_REQ_ATTR_MAX, ftm_attr,
+ nl80211_ftm_request_policy);
+ if (err)
+ return err;
+
+ if (!tb[NL80211_FTM_REQ_ATTR_MACADDR_TEMPLATE] ||
+ !tb[NL80211_FTM_REQ_ATTR_MACADDR_MASK] ||
+ !tb[NL80211_FTM_REQ_ATTR_TARGETS])
+ return -EINVAL;
+
+ if (nl80211_parse_random_mac(tb[NL80211_FTM_REQ_ATTR_MACADDR_TEMPLATE],
+ tb[NL80211_FTM_REQ_ATTR_MACADDR_MASK],
+ ftm->macaddr_template, ftm->macaddr_mask))
+ return -EINVAL;
+
+ if (!tb[NL80211_FTM_REQ_ATTR_TIMEOUT])
+ ftm->timeout = 50;
+ else
+ ftm->timeout = nla_get_u8(tb[NL80211_FTM_REQ_ATTR_TIMEOUT]);
+
+ if (tb[NL80211_FTM_REQ_ATTR_REPORT_TSF])
+ ftm->report_tsf = true;
+
+ nla_for_each_nested(ap_attr, tb[NL80211_FTM_REQ_ATTR_TARGETS], tmp)
+ ftm->num_of_targets++;
+ ftm->targets = kcalloc(ftm->num_of_targets,
+ sizeof(struct cfg80211_ftm_target), GFP_KERNEL);
+ if (!ftm->targets)
+ return -ENOMEM;
+
+ i = 0;
+ two_sided_counter = 0;
+ nla_for_each_nested(ap_attr, tb[NL80211_FTM_REQ_ATTR_TARGETS], tmp) {
+ err = nl80211_parse_ftm_target(rdev, ap_attr, &ftm->targets[i]);
+ if (err)
+ goto free_targets;
+ if (!ftm->targets[i].one_sided)
+ two_sided_counter++;
+ i++;
+ }
+ if (ftm->num_of_targets > rdev->wiphy.max_total_ftm_targets ||
+ two_sided_counter > rdev->wiphy.max_two_sided_ftm_targets)
+ goto free_targets;
+
+ return 0;
+
+free_targets:
+ kfree(ftm->targets);
+ return -EINVAL;
+}
+
+static void nl80211_msrment_request_free(struct cfg80211_msrment_request *req)
+{
+ switch (req->type) {
+ case NL80211_MSRMENT_TYPE_FTM:
+ kfree(req->u.ftm.targets);
+ req->u.ftm.targets = NULL;
+ break;
+ default:
+ break;
+ }
+}
+
+static int nl80211_msrment_request(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_msrment_request request = {0};
+ struct cfg80211_active_msrment *msrment = NULL;
+ u64 cookie = 0;
+ int err;
+
+ if (!info->attrs[NL80211_ATTR_MSRMENT_TYPE])
+ return -EINVAL;
+ request.type = nla_get_u32(info->attrs[NL80211_ATTR_MSRMENT_TYPE]);
+ request.nl_portid = info->snd_portid;
+
+ switch (request.type) {
+ case NL80211_MSRMENT_TYPE_FTM:
+ if (!info->attrs[NL80211_ATTR_MSRMENT_FTM_REQUEST])
+ return -EINVAL;
+ err = nl80211_parse_ftm_request(rdev,
+ info->attrs[NL80211_ATTR_MSRMENT_FTM_REQUEST],
+ &request.u.ftm);
+ if (err)
+ return err;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ msrment = kzalloc(sizeof(*msrment), GFP_KERNEL);
+ if (!msrment) {
+ err = -ENOMEM;
+ goto free_request;
+ }
+
+ err = rdev_perform_msrment(rdev, wdev, &request, &cookie);
+ if (err)
+ goto free_request;
+
+ WARN_ON(!cookie);
+
+ msrment->wdev = wdev;
+ msrment->cookie = cookie;
+ msrment->nl_portid = info->snd_portid;
+ spin_lock_bh(&rdev->msrments_lock);
+ list_add_tail(&msrment->list, &rdev->msrments_list);
+ spin_unlock_bh(&rdev->msrments_lock);
+
+ return 0;
+
+free_request:
+ kfree(msrment);
+ nl80211_msrment_request_free(&request);
+ return err;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -11458,6 +11703,14 @@ static const struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_MSRMENT_REQUEST,
+ .doit = nl80211_msrment_request,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
/* notification functions */
@@ -13211,6 +13464,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
bool schedule_scan_stop = false;
struct cfg80211_sched_scan_request *sched_scan_req =
rcu_dereference(rdev->sched_scan_req);
+ struct cfg80211_active_msrment *msrment;
if (sched_scan_req && notify->portid &&
sched_scan_req->owner_nlportid == notify->portid)
@@ -13234,6 +13488,15 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
}
spin_unlock_bh(&rdev->beacon_registrations_lock);
+ spin_lock_bh(&rdev->msrments_lock);
+ list_for_each_entry(msrment, &rdev->msrments_list, list) {
+ if (msrment->nl_portid == notify->portid) {
+ msrment->nl_portid = 0;
+ schedule_work(&rdev->msrment_abort_wk);
+ }
+ }
+ spin_unlock_bh(&rdev->msrments_lock);
+
if (schedule_destroy_work) {
struct cfg80211_iface_destroy *destroy;
@@ -13351,6 +13614,195 @@ void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp)
}
EXPORT_SYMBOL(cfg80211_crit_proto_stopped);
+static int nl80211_put_ftm_resp_target(struct sk_buff *msg,
+ struct cfg80211_ftm_target *target)
+{
+ struct nlattr *attr =
+ nla_nest_start(msg, NL80211_FTM_RESP_ENTRY_ATTR_TARGET);
+
+ if (!attr)
+ return -ENOBUFS;
+
+ if (nla_put_u32(msg, NL80211_FTM_TARGET_ATTR_FREQ,
+ target->chan_def.chan->center_freq) ||
+ nla_put_u8(msg, NL80211_FTM_TARGET_ATTR_BW,
+ target->chan_def.width) ||
+ nla_put_u32(msg, NL80211_FTM_TARGET_ATTR_CNTR_FREQ_1,
+ target->chan_def.center_freq1) ||
+ nla_put_u32(msg, NL80211_FTM_TARGET_ATTR_CNTR_FREQ_2,
+ target->chan_def.center_freq2) ||
+ nla_put(msg, NL80211_FTM_TARGET_ATTR_BSSID, ETH_ALEN,
+ target->bssid) ||
+ nla_put_u16(msg, NL80211_FTM_TARGET_ATTR_NUM_OF_BURSTS,
+ target->num_of_bursts) ||
+ nla_put_u16(msg, NL80211_FTM_TARGET_ATTR_BURST_PERIOD,
+ target->burst_period) ||
+ nla_put_u8(msg, NL80211_FTM_TARGET_ATTR_SAMPLES_PER_BURST,
+ target->samples_per_burst) ||
+ nla_put_u8(msg, NL80211_FTM_TARGET_ATTR_RETRIES, target->retries) ||
+ nla_put_u64(msg, NL80211_FTM_TARGET_ATTR_COOKIE, target->cookie))
+ return -ENOBUFS;
+
+ if (target->one_sided &&
+ nla_put_flag(msg, NL80211_FTM_TARGET_ATTR_ONE_SIDED))
+ return -ENOBUFS;
+
+ if (target->asap && nla_put_flag(msg, NL80211_FTM_TARGET_ATTR_ASAP))
+ return -ENOBUFS;
+
+ if (target->lci && nla_put_flag(msg, NL80211_FTM_TARGET_ATTR_QUERY_LCI))
+ return -ENOBUFS;
+
+ if (target->civic &&
+ nla_put_flag(msg, NL80211_FTM_TARGET_ATTR_QUERY_CIVIC))
+ return -ENOBUFS;
+
+ nla_nest_end(msg, attr);
+ return 0;
+}
+
+static int nl80211_put_ftm_result(struct sk_buff *msg,
+ struct cfg80211_ftm_result *ftm)
+{
+ struct nlattr *resp = nla_nest_start(msg,
+ NL80211_ATTR_MSRMENT_FTM_RESPONSE);
+
+ if (!resp)
+ return -ENOBUFS;
+
+ if (nla_put_u8(msg, NL80211_FTM_RESP_ENTRY_ATTR_STATUS, ftm->status) ||
+ (ftm->complete &&
+ nla_put_flag(msg, NL80211_FTM_RESP_ENTRY_ATTR_COMPLETE)) ||
+ nl80211_put_ftm_resp_target(msg, ftm->target) ||
+ nla_put_u64(msg, NL80211_FTM_RESP_ENTRY_ATTR_HOST_TIME,
+ ftm->host_time) ||
+ (ftm->tsf && nla_put_u64(msg, NL80211_FTM_RESP_ENTRY_ATTR_TSF,
+ ftm->tsf)) ||
+ nla_put_u8(msg, NL80211_FTM_RESP_ENTRY_ATTR_BURST_INDEX,
+ ftm->burst_index) ||
+ nla_put_s8(msg, NL80211_FTM_RESP_ENTRY_ATTR_RSSI, ftm->rssi) ||
+ nla_put_u8(msg, NL80211_FTM_RESP_ENTRY_ATTR_RSSI_SPREAD,
+ ftm->rssi_spread) ||
+ nl80211_put_sta_rate(msg, &ftm->rate_info,
+ NL80211_FTM_RESP_ENTRY_ATTR_RATE_INFO) ||
+ nla_put_u32(msg, NL80211_FTM_RESP_ENTRY_ATTR_RTT, ftm->rtt) ||
+ nla_put_u32(msg, NL80211_FTM_RESP_ENTRY_ATTR_RTT_VAR,
+ ftm->rtt_variance) ||
+ nla_put_u32(msg, NL80211_FTM_RESP_ENTRY_ATTR_RTT_SPREAD,
+ ftm->rtt_spread))
+ return -ENOBUFS;
+
+ nla_nest_end(msg, resp);
+
+ return 0;
+}
+
+static struct sk_buff *
+nl80211_alloc_measurement_response(struct wiphy *wiphy,
+ struct cfg80211_msrment_response *response,
+ void **hdr, gfp_t gfp)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+
+ if (!msg)
+ return NULL;
+
+ *hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MSRMENT_RESPONSE);
+ if (!*hdr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ nla_put_u64(msg, NL80211_ATTR_COOKIE, response->cookie) ||
+ nla_put_u32(msg, NL80211_ATTR_MSRMENT_TYPE, response->type) ||
+ nla_put_u8(msg, NL80211_ATTR_MSRMENT_STATUS, response->status))
+ goto nla_put_failure;
+
+ return msg;
+
+nla_put_failure:
+ WARN_ON(1);
+ nlmsg_free(msg);
+ return NULL;
+}
+
+static void
+nl80211_send_measurement_response(struct wiphy *wiphy,
+ struct cfg80211_msrment_response *response,
+ struct sk_buff *msg, void *hdr)
+{
+ genlmsg_end(msg, hdr);
+ genlmsg_unicast(wiphy_net(wiphy), msg, response->nl_portid);
+}
+
+static void nl80211_ftm_response(struct wiphy *wiphy,
+ struct cfg80211_msrment_response *response,
+ gfp_t gfp)
+{
+ void *hdr;
+ struct cfg80211_ftm_results ftm = response->u.ftm;
+ struct sk_buff *msg;
+ int i;
+
+ for (i = 0; i < ftm.num_of_entries; i++) {
+ msg = nl80211_alloc_measurement_response(wiphy, response,
+ &hdr, gfp);
+ if (!msg)
+ return;
+
+ if (nl80211_put_ftm_result(msg, &ftm.entries[i]))
+ goto nla_put_failure;
+
+ if (i == ftm.num_of_entries - 1 &&
+ nla_put_flag(msg, NL80211_ATTR_LAST_MSG))
+ goto nla_put_failure;
+
+ nl80211_send_measurement_response(wiphy, response, msg, hdr);
+ }
+
+ return;
+
+nla_put_failure:
+ WARN_ON(1);
+ nlmsg_free(msg);
+}
+
+void cfg80211_measurement_response(struct wiphy *wiphy,
+ struct cfg80211_msrment_response *response,
+ gfp_t gfp)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ struct cfg80211_active_msrment *tmp, *msrment = NULL;
+
+ trace_cfg80211_measurement_response(wiphy, response);
+
+ spin_lock_bh(&rdev->msrments_lock);
+ list_for_each_entry(tmp, &rdev->msrments_list, list) {
+ if (tmp->cookie == tmp->cookie) {
+ msrment = tmp;
+ list_del(&tmp->list);
+ break;
+ }
+ }
+ spin_unlock_bh(&rdev->msrments_lock);
+
+ /* if not found or no portid it was canceled already */
+ if (!msrment || !msrment->nl_portid)
+ goto free;
+
+ switch (response->type) {
+ case NL80211_MSRMENT_TYPE_FTM:
+ nl80211_ftm_response(wiphy, response, gfp);
+ break;
+ default:
+ WARN(1, "invalid measurement type %d\n", response->type);
+ break;
+ };
+ free:
+ kfree(msrment);
+}
+EXPORT_SYMBOL(cfg80211_measurement_response);
+
void nl80211_send_ap_stopped(struct wireless_dev *wdev)
{
struct wiphy *wiphy = wdev->wiphy;
@@ -1071,4 +1071,31 @@ rdev_set_coalesce(struct cfg80211_registered_device *rdev,
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
+
+static inline int rdev_perform_msrment(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_msrment_request *request,
+ u64 *cookie)
+{
+ int ret = -ENOTSUPP;
+
+ trace_rdev_perform_msrment(&rdev->wiphy, wdev, request);
+ if (rdev->ops->perform_msrment)
+ ret = rdev->ops->perform_msrment(&rdev->wiphy, wdev,
+ request, cookie);
+ trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
+ return ret;
+}
+
+static inline int rdev_abort_msrment(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, u64 cookie)
+{
+ int ret = -ENOTSUPP;
+
+ trace_rdev_abort_msrment(&rdev->wiphy, wdev, cookie);
+ if (rdev->ops->abort_msrment)
+ ret = rdev->ops->abort_msrment(&rdev->wiphy, wdev, cookie);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
#endif /* __CFG80211_RDEV_OPS */
@@ -2120,6 +2120,41 @@ TRACE_EVENT(rdev_tdls_cancel_channel_switch,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr))
);
+TRACE_EVENT(rdev_perform_msrment,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_msrment_request *request),
+ TP_ARGS(wiphy, wdev, request),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(enum nl80211_msrment_type, type)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->type = request->type;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", type %d",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->type)
+);
+
+TRACE_EVENT(rdev_abort_msrment,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie),
+ TP_ARGS(wiphy, wdev, cookie),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(u64, cookie)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->cookie = cookie;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie %llu",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie)
+);
+
/*************************************************************
* cfg80211 exported functions traces *
*************************************************************/
@@ -2841,6 +2876,26 @@ TRACE_EVENT(cfg80211_ft_event,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(target_ap))
);
+TRACE_EVENT(cfg80211_measurement_response,
+ TP_PROTO(struct wiphy *wiphy,
+ struct cfg80211_msrment_response *response),
+ TP_ARGS(wiphy, response),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(u64, cookie)
+ __field(enum nl80211_msrment_type, type)
+ __field(enum nl80211_msrment_status, status)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->cookie = response->cookie;
+ __entry->type = response->type;
+ __entry->status = response->status;
+ ),
+ TP_printk(WIPHY_PR_FMT ", cookie %llu, type %d, status %d",
+ WIPHY_PR_ARG, __entry->cookie, __entry->type, __entry->status)
+);
+
TRACE_EVENT(cfg80211_stop_iface,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
TP_ARGS(wiphy, wdev),