atheros: Implement WNM-Sleep Mode driver operations
Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
75cad1a0d4
commit
c7673de462
1 changed files with 215 additions and 1 deletions
|
@ -875,7 +875,56 @@ static void atheros_raw_recv_hs20(void *ctx, const u8 *src_addr, const u8 *buf,
|
|||
}
|
||||
#endif /* CONFIG_HS20 */
|
||||
|
||||
#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R)
|
||||
#if defined(CONFIG_IEEE80211V) && !defined(CONFIG_IEEE80211R)
|
||||
static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct atheros_driver_data *drv = ctx;
|
||||
union wpa_event_data event;
|
||||
const struct ieee80211_mgmt *mgmt;
|
||||
u16 fc;
|
||||
u16 stype;
|
||||
|
||||
/* Do 11R processing for WNM ACTION frames */
|
||||
if (len < IEEE80211_HDRLEN)
|
||||
return;
|
||||
mgmt = (const struct ieee80211_mgmt *) buf;
|
||||
|
||||
fc = le_to_host16(mgmt->frame_control);
|
||||
|
||||
if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
|
||||
return;
|
||||
stype = WLAN_FC_GET_STYPE(fc);
|
||||
|
||||
wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype,
|
||||
(int) len);
|
||||
|
||||
if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) {
|
||||
wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (stype) {
|
||||
case WLAN_FC_STYPE_ACTION:
|
||||
if (&mgmt->u.action.category > buf + len)
|
||||
break;
|
||||
os_memset(&event, 0, sizeof(event));
|
||||
event.rx_action.da = mgmt->da;
|
||||
event.rx_action.sa = mgmt->sa;
|
||||
event.rx_action.bssid = mgmt->bssid;
|
||||
event.rx_action.category = mgmt->u.action.category;
|
||||
event.rx_action.data = &mgmt->u.action.category;
|
||||
event.rx_action.len = buf + len - event.rx_action.data;
|
||||
wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211V */
|
||||
|
||||
#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211V)
|
||||
static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
|
||||
size_t len)
|
||||
{
|
||||
|
@ -885,6 +934,9 @@ static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
|
|||
#ifdef CONFIG_IEEE80211R
|
||||
atheros_raw_recv_11r(ctx, src_addr, buf, len);
|
||||
#endif /* CONFIG_IEEE80211R */
|
||||
#if defined(CONFIG_IEEE80211V) && !defined(CONFIG_IEEE80211R)
|
||||
atheros_raw_recv_11v(ctx, src_addr, buf, len);
|
||||
#endif /* CONFIG_IEEE80211V */
|
||||
#ifdef CONFIG_HS20
|
||||
atheros_raw_recv_hs20(ctx, src_addr, buf, len);
|
||||
#endif /* CONFIG_HS20 */
|
||||
|
@ -906,6 +958,9 @@ static int atheros_receive_pkt(struct atheros_driver_data *drv)
|
|||
IEEE80211_FILTER_TYPE_AUTH |
|
||||
IEEE80211_FILTER_TYPE_ACTION);
|
||||
#endif
|
||||
#ifdef CONFIG_IEEE80211V
|
||||
filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
|
||||
#endif /* CONFIG_IEEE80211V */
|
||||
#ifdef CONFIG_HS20
|
||||
filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
|
||||
#endif /* CONFIG_HS20 */
|
||||
|
@ -1906,6 +1961,162 @@ static int atheros_send_action(void *priv, unsigned int freq,
|
|||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_IEEE80211V
|
||||
static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer,
|
||||
u8 *ie, u16 *len, enum wnm_oper oper)
|
||||
{
|
||||
#define IEEE80211_APPIE_MAX 1024 /* max appie buffer size */
|
||||
u8 buf[IEEE80211_APPIE_MAX];
|
||||
struct ieee80211req_getset_appiebuf *tfs_ie;
|
||||
u16 val;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "atheros: ifname=%s, WNM TFS IE oper=%d " MACSTR,
|
||||
drv->iface, oper, MAC2STR(peer));
|
||||
|
||||
switch (oper) {
|
||||
case WNM_SLEEP_TFS_REQ_IE_SET:
|
||||
if (*len > IEEE80211_APPIE_MAX -
|
||||
sizeof(struct ieee80211req_getset_appiebuf)) {
|
||||
wpa_printf(MSG_DEBUG, "TFS Req IE(s) too large");
|
||||
return -1;
|
||||
}
|
||||
tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
|
||||
tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
|
||||
tfs_ie->app_buflen = ETH_ALEN + 2 + 2 + *len;
|
||||
|
||||
/* Command header for driver */
|
||||
os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
|
||||
val = oper;
|
||||
os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
|
||||
val = *len;
|
||||
os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
|
||||
|
||||
/* copy the ie */
|
||||
os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2 + 2, ie, *len);
|
||||
|
||||
if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie,
|
||||
IEEE80211_APPIE_MAX)) {
|
||||
wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: "
|
||||
"%s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case WNM_SLEEP_TFS_RESP_IE_ADD:
|
||||
tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
|
||||
tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
|
||||
tfs_ie->app_buflen = IEEE80211_APPIE_MAX -
|
||||
sizeof(struct ieee80211req_getset_appiebuf);
|
||||
/* Command header for driver */
|
||||
os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
|
||||
val = oper;
|
||||
os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
|
||||
val = 0;
|
||||
os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
|
||||
|
||||
if (set80211priv(drv, IEEE80211_IOCTL_GET_APPIEBUF, tfs_ie,
|
||||
IEEE80211_APPIE_MAX)) {
|
||||
wpa_printf(MSG_DEBUG, "%s: Failed to get WNM TFS IE: "
|
||||
"%s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*len = tfs_ie->app_buflen;
|
||||
os_memcpy(ie, &(tfs_ie->app_buf[0]), *len);
|
||||
wpa_printf(MSG_DEBUG, "atheros: %c len=%d", tfs_ie->app_buf[0],
|
||||
*len);
|
||||
break;
|
||||
case WNM_SLEEP_TFS_RESP_IE_NONE:
|
||||
*len = 0;
|
||||
break;
|
||||
case WNM_SLEEP_TFS_IE_DEL:
|
||||
tfs_ie = (struct ieee80211req_getset_appiebuf *) buf;
|
||||
tfs_ie->app_frmtype = IEEE80211_APPIE_FRAME_WNM;
|
||||
tfs_ie->app_buflen = IEEE80211_APPIE_MAX -
|
||||
sizeof(struct ieee80211req_getset_appiebuf);
|
||||
/* Command header for driver */
|
||||
os_memcpy(&(tfs_ie->app_buf[0]), peer, ETH_ALEN);
|
||||
val = oper;
|
||||
os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN, &val, 2);
|
||||
val = 0;
|
||||
os_memcpy(&(tfs_ie->app_buf[0]) + ETH_ALEN + 2, &val, 2);
|
||||
|
||||
if (set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, tfs_ie,
|
||||
IEEE80211_APPIE_MAX)) {
|
||||
wpa_printf(MSG_DEBUG, "%s: Failed to set WNM TFS IE: "
|
||||
"%s", __func__, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG, "Unsupported TFS oper %d", oper);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int atheros_wnm_sleep(struct atheros_driver_data *drv,
|
||||
const u8 *peer, enum wnm_oper oper)
|
||||
{
|
||||
u8 *data, *pos;
|
||||
size_t dlen;
|
||||
int ret;
|
||||
u16 val;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "atheros: WNM-Sleep Oper %d, " MACSTR,
|
||||
oper, MAC2STR(peer));
|
||||
|
||||
dlen = ETH_ALEN + 2 + 2;
|
||||
data = os_malloc(dlen);
|
||||
if (data == NULL)
|
||||
return -1;
|
||||
|
||||
/* Command header for driver */
|
||||
pos = data;
|
||||
os_memcpy(pos, peer, ETH_ALEN);
|
||||
pos += ETH_ALEN;
|
||||
|
||||
val = oper;
|
||||
os_memcpy(pos, &val, 2);
|
||||
pos += 2;
|
||||
|
||||
val = 0;
|
||||
os_memcpy(pos, &val, 2);
|
||||
|
||||
ret = atheros_set_wps_ie(drv, data, dlen, IEEE80211_APPIE_FRAME_WNM);
|
||||
|
||||
os_free(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer,
|
||||
u8 *buf, u16 *buf_len)
|
||||
{
|
||||
struct atheros_driver_data *drv = priv;
|
||||
|
||||
switch (oper) {
|
||||
case WNM_SLEEP_ENTER_CONFIRM:
|
||||
case WNM_SLEEP_ENTER_FAIL:
|
||||
case WNM_SLEEP_EXIT_CONFIRM:
|
||||
case WNM_SLEEP_EXIT_FAIL:
|
||||
return atheros_wnm_sleep(drv, peer, oper);
|
||||
case WNM_SLEEP_TFS_REQ_IE_SET:
|
||||
case WNM_SLEEP_TFS_RESP_IE_ADD:
|
||||
case WNM_SLEEP_TFS_RESP_IE_NONE:
|
||||
case WNM_SLEEP_TFS_IE_DEL:
|
||||
return athr_wnm_tfs(drv, peer, buf, buf_len, oper);
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG, "atheros: Unsupported WNM operation %d",
|
||||
oper);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211V */
|
||||
|
||||
|
||||
const struct wpa_driver_ops wpa_driver_atheros_ops = {
|
||||
.name = "atheros",
|
||||
.hapd_init = atheros_init,
|
||||
|
@ -1937,4 +2148,7 @@ const struct wpa_driver_ops wpa_driver_atheros_ops = {
|
|||
.add_sta_node = atheros_add_sta_node,
|
||||
#endif /* CONFIG_IEEE80211R */
|
||||
.send_action = atheros_send_action,
|
||||
#ifdef CONFIG_IEEE80211V
|
||||
.wnm_oper = atheros_wnm_oper,
|
||||
#endif /* CONFIG_IEEE80211V */
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue