}
+static int hostapd_ctrl_iface_req_link_measurement(struct hostapd_data *hapd,
+ const char *cmd, char *reply,
+ size_t reply_size)
+{
+ u8 addr[ETH_ALEN];
+ int ret;
+
+ if (hwaddr_aton(cmd, addr)) {
+ wpa_printf(MSG_ERROR,
+ "CTRL: REQ_LINK_MEASUREMENT: Invalid MAC address");
+ return -1;
+ }
+
+ ret = hostapd_send_link_measurement_req(hapd, addr);
+ if (ret >= 0)
+ ret = os_snprintf(reply, reply_size, "%d", ret);
+ return ret;
+}
+
+
static int hostapd_ctrl_iface_show_neighbor(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
} else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) {
reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11,
reply, reply_size);
+ } else if (os_strncmp(buf, "REQ_LINK_MEASUREMENT ", 21) == 0) {
+ reply_len = hostapd_ctrl_iface_req_link_measurement(
+ hapd, buf + 21, reply, reply_size);
} else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply,
reply_size);
}
+static int hostapd_cli_cmd_req_link_measurement(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "REQ_LINK_MEASUREMENT", 1, argc, argv);
+}
+
+
static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
"<addr> = poll a STA to check connectivity with a QoS null frame" },
{ "req_beacon", hostapd_cli_cmd_req_beacon, NULL,
"<addr> [req_mode=] <measurement request hexdump> = send a Beacon report request to a station" },
+ { "req_link_measurement", hostapd_cli_cmd_req_link_measurement, NULL,
+ "<addr> = send a link measurement report request to a station"},
{ "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL,
"= reload wpa_psk_file only" },
#ifdef CONFIG_IEEE80211R_AP
}
+static void hostapd_link_mesr_rep_timeout_handler(void *eloop_data,
+ void *user_ctx)
+{
+ struct hostapd_data *hapd = eloop_data;
+
+ wpa_printf(MSG_DEBUG,
+ "RRM: Link measurement request (token %u) timed out",
+ hapd->link_measurement_req_token);
+ hapd->link_mesr_req_active = 0;
+}
+
+
+static void hostapd_handle_link_mesr_report(struct hostapd_data *hapd,
+ const u8 *buf, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+ const struct rrm_link_measurement_report *report;
+ const u8 *pos, *end;
+ char report_msg[2 * 8 + 1];
+
+ end = buf + len;
+ pos = mgmt->u.action.u.rrm.variable;
+ report = (const struct rrm_link_measurement_report *) (pos - 1);
+ if (end - (const u8 *) report < (int) sizeof(*report))
+ return;
+
+ if (!hapd->link_mesr_req_active ||
+ (hapd->link_measurement_req_token != report->dialog_token)) {
+ wpa_printf(MSG_INFO,
+ "Unexpected Link measurement report, token %u",
+ report->dialog_token);
+ return;
+ }
+
+ hapd->link_mesr_req_active = 0;
+ eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler, hapd, NULL);
+
+ report_msg[0] = '\0';
+ if (wpa_snprintf_hex(report_msg, sizeof(report_msg),
+ pos, end - pos) < 0)
+ return;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, LINK_MSR_RESP_RX MACSTR " %u %s",
+ MAC2STR(mgmt->sa), report->dialog_token, report_msg);
+}
+
+
void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
const u8 *buf, size_t len)
{
case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
hostapd_handle_nei_report_req(hapd, buf, len);
break;
+ case WLAN_RRM_LINK_MEASUREMENT_REPORT:
+ hostapd_handle_link_mesr_report(hapd, buf, len);
+ break;
default:
wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
mgmt->u.action.u.rrm.action);
hapd->lci_req_active = 0;
eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
hapd->range_req_active = 0;
+ eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler, hapd, NULL);
}
" %u ack=%d", MAC2STR(mgmt->da),
mgmt->u.action.u.rrm.dialog_token, ok);
}
+
+
+int hostapd_send_link_measurement_req(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct wpabuf *buf;
+ struct sta_info *sta;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "Request Link Measurement: dest addr " MACSTR,
+ MAC2STR(addr));
+
+ if (!(hapd->iface->drv_rrm_flags &
+ WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
+ wpa_printf(MSG_INFO,
+ "Request Link Measurement: the driver does not support TX power insertion");
+ return -1;
+ }
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+ wpa_printf(MSG_INFO,
+ "Request Link Measurement: specied STA is not connected");
+ return -1;
+ }
+
+ if (!(sta->rrm_enabled_capa[0] & WLAN_RRM_CAPS_LINK_MEASUREMENT)) {
+ wpa_printf(MSG_INFO,
+ "Request Link Measurement: destination STA does not support link measurement");
+ return -1;
+ }
+
+ if (hapd->link_mesr_req_active) {
+ wpa_printf(MSG_DEBUG,
+ "Request Link Measurement: request already in process - overriding");
+ hapd->link_mesr_req_active = 0;
+ eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler,
+ hapd, NULL);
+ }
+
+ /* Action + Action type + token + Tx Power used + Max Tx Power = 5 */
+ buf = wpabuf_alloc(5);
+ if (!buf)
+ return -1;
+
+ hapd->link_measurement_req_token++;
+ if (!hapd->link_measurement_req_token)
+ hapd->link_measurement_req_token++;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+ wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REQUEST);
+ wpabuf_put_u8(buf, hapd->link_measurement_req_token);
+ /* NOTE: The driver is expected to fill the Tx Power Used and Max Tx
+ * Power */
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_u8(buf, 0);
+
+ ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+ wpabuf_head(buf), wpabuf_len(buf));
+ wpabuf_free(buf);
+ if (ret < 0)
+ return ret;
+
+ hapd->link_mesr_req_active = 1;
+
+ eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+ hostapd_link_mesr_rep_timeout_handler, hapd,
+ NULL);
+
+ return hapd->link_measurement_req_token;
+}