]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
atheros: Implement WNM-Sleep Mode driver operations
authorXi Chen <xichen@qca.qualcomm.com>
Sun, 26 Feb 2012 15:28:42 +0000 (17:28 +0200)
committerJouni Malinen <j@w1.fi>
Wed, 1 Aug 2012 10:21:30 +0000 (13:21 +0300)
Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>

src/drivers/driver_atheros.c

index bb80b0304d7f757e15e5d2324d8d3ec1389da6de..fefae8c373b42bd50d8dda551eaf21ec333b91e5 100644 (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 */
 };