ath6kl: Add functionality for starting AP mode
authorJouni Malinen <jouni@qca.qualcomm.com>
Tue, 30 Aug 2011 18:57:50 +0000 (21:57 +0300)
committerKalle Valo <kvalo@qca.qualcomm.com>
Wed, 31 Aug 2011 07:13:00 +0000 (10:13 +0300)
Use cfg80211 add/del_beacon callbacks for starting/stopping AP mode and
set_beacon to update AP configuration (mainly, to update Beacon and
Probe Response IEs).

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/core.h
drivers/net/wireless/ath/ath6kl/wmi.c
drivers/net/wireless/ath/ath6kl/wmi.h

index 9128aa3..4752a76 100644 (file)
@@ -1435,6 +1435,181 @@ static int ar6k_cfg80211_suspend(struct wiphy *wiphy,
 }
 #endif
 
+static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev,
+                             struct ieee80211_channel *chan,
+                             enum nl80211_channel_type channel_type)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+
+       if (!ath6kl_cfg80211_ready(ar))
+               return -EIO;
+
+       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n",
+                  __func__, chan->center_freq, chan->hw_value);
+       ar->next_chan = chan->center_freq;
+
+       return 0;
+}
+
+static int ath6kl_ap_beacon(struct wiphy *wiphy, struct net_device *dev,
+                           struct beacon_parameters *info, bool add)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+       struct ieee80211_mgmt *mgmt;
+       u8 *ies;
+       int ies_len;
+       struct wmi_connect_cmd p;
+       int res;
+       int i;
+
+       ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: add=%d\n", __func__, add);
+
+       if (!ath6kl_cfg80211_ready(ar))
+               return -EIO;
+
+       if (ar->next_mode != AP_NETWORK)
+               return -EOPNOTSUPP;
+
+       if (info->beacon_ies) {
+               res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_BEACON,
+                                              info->beacon_ies,
+                                              info->beacon_ies_len);
+               if (res)
+                       return res;
+       }
+       if (info->proberesp_ies) {
+               res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_PROBE_RESP,
+                                              info->proberesp_ies,
+                                              info->proberesp_ies_len);
+               if (res)
+                       return res;
+       }
+       if (info->assocresp_ies) {
+               res = ath6kl_wmi_set_appie_cmd(ar->wmi, WMI_FRAME_ASSOC_RESP,
+                                              info->assocresp_ies,
+                                              info->assocresp_ies_len);
+               if (res)
+                       return res;
+       }
+
+       if (!add)
+               return 0;
+
+       /* TODO:
+        * info->interval
+        * info->dtim_period
+        */
+
+       if (info->head == NULL)
+               return -EINVAL;
+       mgmt = (struct ieee80211_mgmt *) info->head;
+       ies = mgmt->u.beacon.variable;
+       if (ies > info->head + info->head_len)
+               return -EINVAL;
+       ies_len = info->head + info->head_len - ies;
+
+       if (info->ssid == NULL)
+               return -EINVAL;
+       memcpy(ar->ssid, info->ssid, info->ssid_len);
+       ar->ssid_len = info->ssid_len;
+       if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE)
+               return -EOPNOTSUPP; /* TODO */
+
+       ar->dot11_auth_mode = OPEN_AUTH;
+
+       memset(&p, 0, sizeof(p));
+
+       for (i = 0; i < info->crypto.n_akm_suites; i++) {
+               switch (info->crypto.akm_suites[i]) {
+               case WLAN_AKM_SUITE_8021X:
+                       if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+                               p.auth_mode |= WPA_AUTH;
+                       if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+                               p.auth_mode |= WPA2_AUTH;
+                       break;
+               case WLAN_AKM_SUITE_PSK:
+                       if (info->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+                               p.auth_mode |= WPA_PSK_AUTH;
+                       if (info->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+                               p.auth_mode |= WPA2_PSK_AUTH;
+                       break;
+               }
+       }
+       if (p.auth_mode == 0)
+               p.auth_mode = NONE_AUTH;
+       ar->auth_mode = p.auth_mode;
+
+       for (i = 0; i < info->crypto.n_ciphers_pairwise; i++) {
+               switch (info->crypto.ciphers_pairwise[i]) {
+               case WLAN_CIPHER_SUITE_WEP40:
+               case WLAN_CIPHER_SUITE_WEP104:
+                       p.prwise_crypto_type |= WEP_CRYPT;
+                       break;
+               case WLAN_CIPHER_SUITE_TKIP:
+                       p.prwise_crypto_type |= TKIP_CRYPT;
+                       break;
+               case WLAN_CIPHER_SUITE_CCMP:
+                       p.prwise_crypto_type |= AES_CRYPT;
+                       break;
+               }
+       }
+       if (p.prwise_crypto_type == 0)
+               p.prwise_crypto_type = NONE_CRYPT;
+
+       switch (info->crypto.cipher_group) {
+       case WLAN_CIPHER_SUITE_WEP40:
+       case WLAN_CIPHER_SUITE_WEP104:
+               p.grp_crypto_type = WEP_CRYPT;
+               break;
+       case WLAN_CIPHER_SUITE_TKIP:
+               p.grp_crypto_type = TKIP_CRYPT;
+               break;
+       case WLAN_CIPHER_SUITE_CCMP:
+               p.grp_crypto_type = AES_CRYPT;
+               break;
+       default:
+               p.grp_crypto_type = NONE_CRYPT;
+               break;
+       }
+
+       p.nw_type = AP_NETWORK;
+       ar->nw_type = ar->next_mode;
+
+       p.ssid_len = ar->ssid_len;
+       memcpy(p.ssid, ar->ssid, ar->ssid_len);
+       p.dot11_auth_mode = ar->dot11_auth_mode;
+       p.ch = cpu_to_le16(ar->next_chan);
+
+       return ath6kl_wmi_ap_profile_commit(ar->wmi, &p);
+}
+
+static int ath6kl_add_beacon(struct wiphy *wiphy, struct net_device *dev,
+                            struct beacon_parameters *info)
+{
+       return ath6kl_ap_beacon(wiphy, dev, info, true);
+}
+
+static int ath6kl_set_beacon(struct wiphy *wiphy, struct net_device *dev,
+                            struct beacon_parameters *info)
+{
+       return ath6kl_ap_beacon(wiphy, dev, info, false);
+}
+
+static int ath6kl_del_beacon(struct wiphy *wiphy, struct net_device *dev)
+{
+       struct ath6kl *ar = ath6kl_priv(dev);
+
+       if (ar->nw_type != AP_NETWORK)
+               return -EOPNOTSUPP;
+       if (!test_bit(CONNECTED, &ar->flag))
+               return -ENOTCONN;
+
+       ath6kl_wmi_disconnect_cmd(ar->wmi);
+       clear_bit(CONNECTED, &ar->flag);
+
+       return 0;
+}
+
 static struct cfg80211_ops ath6kl_cfg80211_ops = {
        .change_virtual_intf = ath6kl_cfg80211_change_iface,
        .scan = ath6kl_cfg80211_scan,
@@ -1457,6 +1632,10 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = {
 #ifdef CONFIG_PM
        .suspend = ar6k_cfg80211_suspend,
 #endif
+       .set_channel = ath6kl_set_channel,
+       .add_beacon = ath6kl_add_beacon,
+       .set_beacon = ath6kl_set_beacon,
+       .del_beacon = ath6kl_del_beacon,
 };
 
 struct wireless_dev *ath6kl_cfg80211_init(struct device *dev)
index c5537b3..00d0add 100644 (file)
@@ -468,6 +468,8 @@ struct ath6kl {
 
        struct ath6kl_node_table scan_table;
        struct dentry *debugfs_phy;
+
+       u16 next_chan;
 };
 
 static inline void *ath6kl_priv(struct net_device *dev)
index d116d0e..0114a71 100644 (file)
@@ -1399,6 +1399,8 @@ int ath6kl_wmi_cmd_send(struct wmi *wmi, struct sk_buff *skb,
        enum htc_endpoint_id ep_id = wmi->ep_id;
        int ret;
 
+       ath6kl_dbg(ATH6KL_DBG_WMI, "%s: cmd_id=%d\n", __func__, cmd_id);
+
        if (WARN_ON(skb == NULL))
                return -EINVAL;
 
@@ -2392,6 +2394,29 @@ static int ath6kl_wmi_delba_req_event_rx(struct wmi *wmi, u8 *datap, int len)
 }
 
 /*  AP mode functions */
+
+int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p)
+{
+       struct sk_buff *skb;
+       struct wmi_connect_cmd *cm;
+       int res;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*cm));
+       if (!skb)
+               return -ENOMEM;
+
+       cm = (struct wmi_connect_cmd *) skb->data;
+       memcpy(cm, p, sizeof(*cm));
+
+       res = ath6kl_wmi_cmd_send(wmip, skb, WMI_AP_CONFIG_COMMIT_CMDID,
+                                 NO_SYNC_WMIFLAG);
+       ath6kl_dbg(ATH6KL_DBG_WMI, "%s: nw_type=%u auth_mode=%u ch=%u "
+                  "ctrl_flags=0x%x-> res=%d\n",
+                  __func__, p->nw_type, p->auth_mode, le16_to_cpu(p->ch),
+                  le32_to_cpu(p->ctrl_flags), res);
+       return res;
+}
+
 static int ath6kl_wmi_pspoll_event_rx(struct wmi *wmi, u8 *datap, int len)
 {
        struct wmi_pspoll_event *ev;
@@ -2456,6 +2481,26 @@ int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_ver,
        return ret;
 }
 
+int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
+                            u8 ie_len)
+{
+       struct sk_buff *skb;
+       struct wmi_set_appie_cmd *p;
+
+       skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len);
+       if (!skb)
+               return -ENOMEM;
+
+       ath6kl_dbg(ATH6KL_DBG_WMI, "set_appie_cmd: mgmt_frm_type=%u "
+                  "ie_len=%u\n", mgmt_frm_type, ie_len);
+       p = (struct wmi_set_appie_cmd *) skb->data;
+       p->mgmt_frm_type = mgmt_frm_type;
+       p->ie_len = ie_len;
+       memcpy(p->ie_info, ie, ie_len);
+       return ath6kl_wmi_cmd_send(wmi, skb, WMI_SET_APPIE_CMDID,
+                                  NO_SYNC_WMIFLAG);
+}
+
 static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb)
 {
        struct wmix_cmd_hdr *cmd;
index 8fa5d6e..6bdfd4a 100644 (file)
@@ -503,6 +503,15 @@ enum wmi_cmd_id {
        WMI_SET_EXCESS_TX_RETRY_THRES_CMDID,
 };
 
+enum wmi_mgmt_frame_type {
+       WMI_FRAME_BEACON = 0,
+       WMI_FRAME_PROBE_REQ,
+       WMI_FRAME_PROBE_RESP,
+       WMI_FRAME_ASSOC_REQ,
+       WMI_FRAME_ASSOC_RESP,
+       WMI_NUM_MGMT_FRAME
+};
+
 /* WMI_CONNECT_CMDID  */
 enum network_type {
        INFRA_NETWORK = 0x01,
@@ -1642,6 +1651,12 @@ struct wmi_get_keepalive_cmd {
        u8 keep_alive_intvl;
 } __packed;
 
+struct wmi_set_appie_cmd {
+       u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */
+       u8 ie_len;
+       u8 ie_info[0];
+} __packed;
+
 /* Notify the WSC registration status to the target */
 #define WSC_REG_ACTIVE     1
 #define WSC_REG_INACTIVE   0
@@ -2006,11 +2021,16 @@ struct bss *ath6kl_wmi_find_ssid_node(struct wmi *wmi, u8 *ssid,
 void ath6kl_wmi_node_return(struct wmi *wmi, struct bss *bss);
 
 /* AP mode */
+int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, struct wmi_connect_cmd *p);
+
 int ath6kl_wmi_set_pvb_cmd(struct wmi *wmi, u16 aid, bool flag);
 
 int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 rx_meta_version,
                                       bool rx_dot11_hdr, bool defrag_on_host);
 
+int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 mgmt_frm_type, const u8 *ie,
+                            u8 ie_len);
+
 void *ath6kl_wmi_init(struct ath6kl *devt);
 void ath6kl_wmi_shutdown(struct wmi *wmi);