Add driver command and event for signal strength monitoring

This commit is contained in:
Jouni Malinen 2010-03-28 15:31:04 -07:00
parent 93910401c9
commit b625473c6c
4 changed files with 93 additions and 3 deletions

View file

@ -1758,6 +1758,22 @@ struct wpa_driver_ops {
* @priv: Private driver interface data * @priv: Private driver interface data
*/ */
void (*resume)(void *priv); void (*resume)(void *priv);
/**
* signal_monitor - Set signal monitoring parameters
* @priv: Private driver interface data
* @threshold: Threshold value for signal change events; 0 = disabled
* @hysteresis: Minimum change in signal strength before indicating a
* new event
* Returns: 0 on success, -1 on failure (or if not supported)
*
* This function can be used to configure monitoring of signal strength
* with the current AP. Whenever signal strength drops below the
* %threshold value or increases above it, EVENT_SIGNAL_CHANGE event
* should be generated assuming the signal strength has changed at
* least %hysteresis from the previously indicated signal change event.
*/
int (*signal_monitor)(void *priv, int threshold, int hysteresis);
}; };
@ -2030,7 +2046,16 @@ enum wpa_event_type {
* %wpa_supplicant, this event is used only if the send_eapol() handler * %wpa_supplicant, this event is used only if the send_eapol() handler
* is used to override the use of l2_packet for EAPOL frame TX. * is used to override the use of l2_packet for EAPOL frame TX.
*/ */
EVENT_EAPOL_RX EVENT_EAPOL_RX,
/**
* EVENT_SIGNAL_CHANGE - Indicate change in signal strength
*
* This event is used to indicate changes in the signal strength
* observed in frames received from the current AP if signal strength
* monitoring has been enabled with signal_monitor().
*/
EVENT_SIGNAL_CHANGE
}; };
@ -2402,6 +2427,13 @@ union wpa_event_data {
const u8 *data; const u8 *data;
size_t data_len; size_t data_len;
} eapol_rx; } eapol_rx;
/**
* struct signal_change - Data for EVENT_SIGNAL_CHANGE events
*/
struct signal_change {
int above_threshold;
} signal_change;
}; };
/** /**

View file

@ -3274,5 +3274,6 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = {
NULL /* disable_11b_rates */, NULL /* disable_11b_rates */,
NULL /* deinit_ap */, NULL /* deinit_ap */,
NULL /* suspend */, NULL /* suspend */,
NULL /* resume */ NULL /* resume */,
NULL /* signal_monitor */
}; };

View file

@ -859,6 +859,7 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
}; };
struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
enum nl80211_cqm_rssi_threshold_event event; enum nl80211_cqm_rssi_threshold_event event;
union wpa_event_data ed;
if (tb[NL80211_ATTR_CQM] == NULL || if (tb[NL80211_ATTR_CQM] == NULL ||
nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM], nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
@ -870,13 +871,21 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
return; return;
event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
os_memset(&ed, 0, sizeof(ed));
if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
"event: RSSI high"); "event: RSSI high");
ed.signal_change.above_threshold = 1;
} else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) { } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
"event: RSSI low"); "event: RSSI low");
} ed.signal_change.above_threshold = 0;
} else
return;
wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
} }
@ -5190,6 +5199,44 @@ static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap,
} }
static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg, *cqm = NULL;
wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
"hysteresis=%d", threshold, hysteresis);
msg = nlmsg_alloc();
if (!msg)
return -1;
genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
0, NL80211_CMD_SET_CQM, 0);
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
cqm = nlmsg_alloc();
if (cqm == NULL)
return -1;
NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold);
NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis);
nla_put_nested(msg, NL80211_ATTR_CQM, cqm);
if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
return 0;
msg = NULL;
nla_put_failure:
if (cqm)
nlmsg_free(cqm);
nlmsg_free(msg);
return -1;
}
const struct wpa_driver_ops wpa_driver_nl80211_ops = { const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211", .name = "nl80211",
.desc = "Linux nl80211/cfg80211", .desc = "Linux nl80211/cfg80211",
@ -5249,4 +5296,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.deinit_ap = wpa_driver_nl80211_deinit_ap, .deinit_ap = wpa_driver_nl80211_deinit_ap,
.resume = wpa_driver_nl80211_resume, .resume = wpa_driver_nl80211_resume,
.send_ft_action = nl80211_send_ft_action, .send_ft_action = nl80211_send_ft_action,
.signal_monitor = nl80211_signal_monitor,
}; };

View file

@ -466,4 +466,13 @@ static inline void wpa_drv_resume(struct wpa_supplicant *wpa_s)
wpa_s->driver->resume(wpa_s->drv_priv); wpa_s->driver->resume(wpa_s->drv_priv);
} }
static inline int wpa_drv_signal_monitor(struct wpa_supplicant *wpa_s,
int threshold, int hysteresis)
{
if (wpa_s->driver->signal_monitor)
return wpa_s->driver->signal_monitor(wpa_s->drv_priv,
threshold, hysteresis);
return -1;
}
#endif /* DRIVER_I_H */ #endif /* DRIVER_I_H */