--- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -3145,6 +3145,25 @@ static int hostapd_ctrl_iface_signal_mon return -1; } +static int hostapd_ctrl_iface_signal_txrate(struct hostapd_data *hapd, + char *cmd) +{ + char *pos; + unsigned int low_thold = 0, high_thold = 0; + + pos = os_strstr(cmd, "LOW="); + if (pos) + low_thold = atoi(pos + 4); + + pos = os_strstr(cmd, "HIGH="); + if (pos) + high_thold = atoi(pos + 5); + + if (hapd->driver->signal_txrate) + return hapd->driver->signal_txrate(hapd->drv_priv, low_thold, + high_thold); + return -1; +} static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf, size_t buflen) @@ -3791,6 +3810,9 @@ static int hostapd_ctrl_iface_receive_pr } else if (os_strncmp(buf, "SIGNAL_MONITOR", 14) == 0) { if (hostapd_ctrl_iface_signal_monitor(hapd, buf + 14)) reply_len = -1; + } else if (os_strncmp(buf, "SIGNAL_TXRATE", 13) == 0) { + if (hostapd_ctrl_iface_signal_txrate(hapd, buf + 13)) + reply_len = -1; } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) { reply_len = hostapd_ctrl_iface_get_capability( hapd, buf + 15, reply, reply_size); --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1505,6 +1505,12 @@ static int hostapd_cli_cmd_reload_wpa_ps } +static int hostapd_cli_cmd_signal_txrate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "SIGNAL_TXRATE", 0, argc, argv); +} + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); @@ -1680,6 +1686,9 @@ static const struct hostapd_cli_cmd host " = poll a STA to check connectivity with a QoS null frame" }, { "signal_monitor", hostapd_cli_cmd_signal_monitor, NULL, "= set signal monitor parameters" }, + { "signal_txrate", hostapd_cli_cmd_signal_txrate, NULL, + "= set signal tx rate parameters: signal_txrate " + "LOW=<> HIGH=<>" }, { "req_beacon", hostapd_cli_cmd_req_beacon, NULL, " [req_mode=] = send a Beacon report request to a station" }, { "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL, --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -4412,6 +4412,20 @@ struct wpa_driver_ops { */ int (*get_peer_inactive_time)(void *priv, const u8 *addr); + /** + * signal_txrate - Set signal monitoring parameters + * @priv: Private driver interface data + * @low_thold: Low threshold value for signal txrate events; 0 = disabled + * @high_thold: High threshold value for signal txrate events; 0 = disabled + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This function can be used to configure monitoring of signal tx rate + * with the current AP. Whenever txrate drops below the low_thold + * or increases above high_thold. + */ + int (*signal_txrate)(void *priv, const u32 low_thold, + u32 high_thold); + /** * update_connect_params - Update the connection parameters * @priv: Private driver interface data --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -8268,6 +8268,29 @@ static int nl80211_signal_monitor(void * return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); } +static int nl80211_signal_txrate(void *priv, const u32 low_thold, + u32 high_thold) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *cqm; + + if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_CQM)) || + !(cqm = nla_nest_start(msg, NL80211_ATTR_CQM)) || + nla_put_u32(msg, NL80211_ATTR_CQM_LOW_TX_RATE_THOLD, + low_thold) || + nla_put_u32(msg, NL80211_ATTR_CQM_HIGH_TX_RATE_THOLD, + high_thold)) { + nlmsg_free(msg); + wpa_printf(MSG_WARNING, "nl80211: Signal txrate returning"); + return -1; + } + + nla_nest_end(msg, cqm); + + return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL); +} static int get_channel_width(struct nl_msg *msg, void *arg) { @@ -11725,6 +11748,7 @@ const struct wpa_driver_ops wpa_driver_n .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli, .resume = wpa_driver_nl80211_resume, .signal_monitor = nl80211_signal_monitor, + .signal_txrate = nl80211_signal_txrate, .signal_poll = nl80211_signal_poll, .channel_info = nl80211_channel_info, .set_param = nl80211_set_param, --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -1274,12 +1274,15 @@ static void nl80211_cqm_event(struct wpa [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_BEACON_LOSS_EVENT] = { .type = NLA_FLAG }, [NL80211_ATTR_CQM_RSSI_LEVEL] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TX_RATE_THRESHOLD_EVENT] = + { .type = NLA_U32 }, }; struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; - enum nl80211_cqm_rssi_threshold_event event; + enum nl80211_cqm_rssi_threshold_event rssi_event; + enum nl80211_cqm_tx_rate_threshold_event txrate_event; union wpa_event_data ed; struct wpa_signal_info sig; - int res, rssi_level; + int res, rssi_level, txrate_level; u8 *addr = NULL; if (tb[NL80211_ATTR_CQM] == NULL || @@ -1324,19 +1327,41 @@ static void nl80211_cqm_event(struct wpa return; } + if (tb[NL80211_ATTR_MAC]) + addr = nla_data(tb[NL80211_ATTR_MAC]); + + if (cqm[NL80211_ATTR_CQM_TX_RATE_THRESHOLD_EVENT]) { + txrate_event = + nla_get_u32(cqm[NL80211_ATTR_CQM_TX_RATE_THRESHOLD_EVENT]); + txrate_level = + nla_get_u32(cqm[NL80211_ATTR_CQM_TX_RATE_LEVEL]); + + if (txrate_event == NL80211_CQM_TX_RATE_THRESHOLD_EVENT_HIGH) { + wpa_msg(drv->ctx, MSG_INFO, "nl80211: CQM TXRATE HIGH " + "event for "MACSTR " txrate :%d", + MAC2STR(addr), txrate_level); + } else if (txrate_event == + NL80211_CQM_TX_RATE_THRESHOLD_EVENT_LOW) { + wpa_msg(drv->ctx, MSG_INFO, "nl80211: CQM TXRATE LOW " + "event for "MACSTR " txrate :%d", + MAC2STR(addr), txrate_level); + } else { + wpa_msg(drv->ctx, MSG_INFO, "Unknown CQM TXRATE " + " threshold event :%d", txrate_event); + return; + } + } + if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) { wpa_printf(MSG_DEBUG, "nl80211: Not a CQM RSSI threshold event"); return; } - event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); - - if (tb[NL80211_ATTR_MAC]) - addr = nla_data(tb[NL80211_ATTR_MAC]); + rssi_event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); rssi_level = (s32) nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_LEVEL]); - if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { + if (rssi_event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { if (addr) { wpa_msg(drv->ctx, MSG_INFO, "nl80211: CQM RSSI HIGH " "event for "MACSTR " RSSI :%d", MAC2STR(addr), @@ -1346,7 +1371,7 @@ static void nl80211_cqm_event(struct wpa wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " "event: RSSI high"); ed.signal_change.above_threshold = 1; - } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) { + } else if (rssi_event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) { if (addr) { wpa_msg(drv->ctx, MSG_INFO, "nl80211: CQM RSSI LOW " "event for "MACSTR " RSSI :%d", MAC2STR(addr), @@ -1359,7 +1384,7 @@ static void nl80211_cqm_event(struct wpa } else { wpa_printf(MSG_DEBUG, "nl80211: Unknown CQM RSSI threshold event: %d", - event); + rssi_event); return; } --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -4792,6 +4792,17 @@ enum nl80211_ps_state { * loss event * @NL80211_ATTR_CQM_RSSI_LEVEL: the RSSI value in dBm that triggered the * RSSI threshold event. + * @NL80211_ATTR_CQM_LOW_TX_RATE_THOLD: TX_RATE threshold in Kbps. This value + * specifies the low threshold for the TX_RATE level at which an event will be + * sent. Zero to disable. Events will be sent when the TX_RATE value goes + * lesser than this threshold. + * @NL80211_ATTR_CQM_HIGH_TX_RATE_THOLD: TX RATE in Kbps. This value + * specifies the high threshold for the TX_RATE level at which an event will + * be sent. Zero to diable. Event will be sent when the TX_RATE values goes + * greater than this threshold. + * @NL80211_ATTR_CQM_TX_RATE_THRESHOLD_EVENT: TX_RATE threshold event + * @NL80211_ATTR_CQM_TX_RATE_LEVEL: the tx rate value in Kbps that triggered the + * TX_RATE threshold event. * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute */ @@ -4806,7 +4817,10 @@ enum nl80211_attr_cqm { NL80211_ATTR_CQM_TXE_INTVL, NL80211_ATTR_CQM_BEACON_LOSS_EVENT, NL80211_ATTR_CQM_RSSI_LEVEL, - + NL80211_ATTR_CQM_LOW_TX_RATE_THOLD, + NL80211_ATTR_CQM_HIGH_TX_RATE_THOLD, + NL80211_ATTR_CQM_TX_RATE_THRESHOLD_EVENT, + NL80211_ATTR_CQM_TX_RATE_LEVEL, /* keep last */ __NL80211_ATTR_CQM_AFTER_LAST, NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1 @@ -4820,13 +4834,17 @@ enum nl80211_attr_cqm { * configured threshold * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: (reserved, never sent) */ + enum nl80211_cqm_rssi_threshold_event { NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, NL80211_CQM_RSSI_BEACON_LOSS_EVENT, }; - +enum nl80211_cqm_tx_rate_threshold_event { + NL80211_CQM_TX_RATE_THRESHOLD_EVENT_LOW = 1, + NL80211_CQM_TX_RATE_THRESHOLD_EVENT_HIGH, +}; /** * enum nl80211_tx_power_setting - TX power adjustment * @NL80211_TX_POWER_AUTOMATIC: automatically determine transmit power