atheros: Implement WNM-Sleep Mode driver operations

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Xi Chen 2012-02-26 17:28:42 +02:00 committed by Jouni Malinen
parent 75cad1a0d4
commit c7673de462

View file

@ -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 */
};