mwifiex: add cfg80211 start_radar_detection handler
authorAvinash Patil <patila@marvell.com>
Wed, 28 Jan 2015 10:24:21 +0000 (15:54 +0530)
committerKalle Valo <kvalo@codeaurora.org>
Thu, 29 Jan 2015 08:22:04 +0000 (10:22 +0200)
This patch adds support for cfg80211 start_radar_detection handler.
Upon reception of start_radar_detection, driver prepares radar detect
command to FW.

Delayed work is queued for CAC time which sends radar detection finished
event to cfg80211.

Signed-off-by: Avinash Patil <patila@marvell.com>
Signed-off-by: Qingshui Gao <gaoqs@marvell.com>
Signed-off-by: Cathy Luo <cluo@marvell.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/mwifiex/11h.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/decl.h
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_cmdresp.c
drivers/net/wireless/mwifiex/uap_cmd.c

index f23b647a14113ae3b5bf4337751ed8c48ee1173e..8832c83816eeca1acd4ac6ab0ed8663958ac022e 100644 (file)
@@ -99,3 +99,69 @@ void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer,
                bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT;
        }
 }
+
+/* This is DFS CAC work queue function.
+ * This delayed work emits CAC finished event for cfg80211 if
+ * CAC was started earlier.
+ */
+void mwifiex_dfs_cac_work_queue(struct work_struct *work)
+{
+       struct cfg80211_chan_def chandef;
+       struct delayed_work *delayed_work =
+                       container_of(work, struct delayed_work, work);
+       struct mwifiex_private *priv =
+                       container_of(delayed_work, struct mwifiex_private,
+                                    dfs_cac_work);
+
+       if (WARN_ON(!priv))
+               return;
+
+       chandef = priv->dfs_chandef;
+       if (priv->wdev.cac_started) {
+               dev_dbg(priv->adapter->dev,
+                       "CAC timer finished; No radar detected\n");
+               cfg80211_cac_event(priv->netdev, &chandef,
+                                  NL80211_RADAR_CAC_FINISHED,
+                                  GFP_KERNEL);
+       }
+}
+
+/* This function prepares channel report request command to FW for
+ * starting radar detection.
+ */
+int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv,
+                                         struct host_cmd_ds_command *cmd,
+                                         void *data_buf)
+{
+       struct host_cmd_ds_chan_rpt_req *cr_req = &cmd->params.chan_rpt_req;
+       struct mwifiex_radar_params *radar_params = (void *)data_buf;
+
+       cmd->command = cpu_to_le16(HostCmd_CMD_CHAN_REPORT_REQUEST);
+       cmd->size = cpu_to_le16(S_DS_GEN);
+       le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_chan_rpt_req));
+
+       cr_req->chan_desc.start_freq = cpu_to_le16(MWIFIEX_A_BAND_START_FREQ);
+       cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value;
+       cr_req->chan_desc.chan_width = radar_params->chandef->width;
+       cr_req->msec_dwell_time = cpu_to_le32(radar_params->cac_time_ms);
+
+       dev_dbg(priv->adapter->dev,
+               "11h: issuing DFS Radar check for channel=%d\n",
+               radar_params->chandef->chan->hw_value);
+
+       return 0;
+}
+
+/* This function is to abort ongoing CAC upon stopping AP operations
+ * or during unload.
+ */
+void mwifiex_abort_cac(struct mwifiex_private *priv)
+{
+       if (priv->wdev.cac_started) {
+               dev_dbg(priv->adapter->dev,
+                       "Aborting delayed work for CAC.\n");
+               cancel_delayed_work_sync(&priv->dfs_cac_work);
+               cfg80211_cac_event(priv->netdev, &priv->dfs_chandef,
+                                  NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
+       }
+}
index 2e0834ca1cc08008fd01405f526020eb5e05a4a2..7a969fd2f0cd0b1b1d94997653b90ded06b776a1 100644 (file)
@@ -1651,6 +1651,8 @@ static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
 
+       mwifiex_abort_cac(priv);
+
        if (mwifiex_del_mgmt_ies(priv))
                wiphy_err(wiphy, "Failed to delete mgmt IEs!\n");
 
@@ -2383,6 +2385,7 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info,
        ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
 }
 
+#define MWIFIEX_MAX_WQ_LEN  30
 /*
  *  create a new virtual interface with the given name
  */
@@ -2396,6 +2399,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
        struct mwifiex_private *priv;
        struct net_device *dev;
        void *mdev_priv;
+       char dfs_cac_str[MWIFIEX_MAX_WQ_LEN];
 
        if (!adapter)
                return ERR_PTR(-EFAULT);
@@ -2560,6 +2564,24 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy,
                return ERR_PTR(-EFAULT);
        }
 
+       strcpy(dfs_cac_str, "MWIFIEX_DFS_CAC");
+       strcat(dfs_cac_str, name);
+       priv->dfs_cac_workqueue = alloc_workqueue(dfs_cac_str,
+                                                 WQ_HIGHPRI |
+                                                 WQ_MEM_RECLAIM |
+                                                 WQ_UNBOUND, 1);
+       if (!priv->dfs_cac_workqueue) {
+               wiphy_err(wiphy, "cannot register virtual network device\n");
+               free_netdev(dev);
+               priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED;
+               priv->netdev = NULL;
+               memset(&priv->wdev, 0, sizeof(priv->wdev));
+               priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED;
+               return ERR_PTR(-ENOMEM);
+       }
+
+       INIT_DELAYED_WORK(&priv->dfs_cac_work, mwifiex_dfs_cac_work_queue);
+
        sema_init(&priv->async_sem, 1);
 
        dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name);
@@ -2609,6 +2631,12 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
        if (wdev->netdev->reg_state == NETREG_REGISTERED)
                unregister_netdevice(wdev->netdev);
 
+       if (priv->dfs_cac_workqueue) {
+               flush_workqueue(priv->dfs_cac_workqueue);
+               destroy_workqueue(priv->dfs_cac_workqueue);
+               priv->dfs_cac_workqueue = NULL;
+       }
+
        /* Clear the priv in adapter */
        priv->netdev->ieee80211_ptr = NULL;
        priv->netdev = NULL;
@@ -3087,6 +3115,36 @@ mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
        return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK);
 }
 
+static int
+mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy,
+                                      struct net_device *dev,
+                                      struct cfg80211_chan_def *chandef,
+                                      u32 cac_time_ms)
+{
+       struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+       struct mwifiex_radar_params radar_params;
+
+       if (priv->adapter->scan_processing) {
+               dev_err(priv->adapter->dev,
+                       "radar detection: scan already in process...\n");
+               return -EBUSY;
+       }
+
+       memset(&radar_params, 0, sizeof(struct mwifiex_radar_params));
+       radar_params.chandef = chandef;
+       radar_params.cac_time_ms = cac_time_ms;
+
+       memcpy(&priv->dfs_chandef, chandef, sizeof(priv->dfs_chandef));
+
+       if (mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST,
+                            HostCmd_ACT_GEN_SET, 0, &radar_params, true))
+               return -1;
+
+       queue_delayed_work(priv->dfs_cac_workqueue, &priv->dfs_cac_work,
+                          msecs_to_jiffies(cac_time_ms));
+       return 0;
+}
+
 static int
 mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev,
                                const u8 *mac,
@@ -3151,6 +3209,7 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = {
        .tdls_oper = mwifiex_cfg80211_tdls_oper,
        .add_station = mwifiex_cfg80211_add_station,
        .change_station = mwifiex_cfg80211_change_station,
+       .start_radar_detection = mwifiex_cfg80211_start_radar_detection,
 };
 
 #ifdef CONFIG_PM
index 4481ac40c3ebe6cd09d28a0a95ea74a1e319c31e..68aed4e5ad837e3fdddeabd4a07c525e3bbd1498 100644 (file)
 #define MWIFIEX_MAX_UAP_NUM            1
 #define MWIFIEX_MAX_P2P_NUM            1
 
+#define MWIFIEX_A_BAND_START_FREQ      5000
+
 enum mwifiex_bss_type {
        MWIFIEX_BSS_TYPE_STA = 0,
        MWIFIEX_BSS_TYPE_UAP = 1,
@@ -242,4 +244,8 @@ struct mwifiex_iface_comb {
        u8 p2p_intf;
 };
 
+struct mwifiex_radar_params {
+       struct cfg80211_chan_def *chandef;
+       u32 cac_time_ms;
+} __packed;
 #endif /* !_MWIFIEX_DECL_H_ */
index 15ad776ae08ec721488996bf4e6f386e5ffe471f..739151c13f15ec0b7d6c95757039ebc43a158d4f 100644 (file)
@@ -336,6 +336,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER {
 #define HostCmd_CMD_11N_ADDBA_RSP                     0x00cf
 #define HostCmd_CMD_11N_DELBA                         0x00d0
 #define HostCmd_CMD_RECONFIGURE_TX_BUFF               0x00d9
+#define HostCmd_CMD_CHAN_REPORT_REQUEST               0x00dd
 #define HostCmd_CMD_AMSDU_AGGR_CTRL                   0x00df
 #define HostCmd_CMD_TXPWR_CFG                         0x00d1
 #define HostCmd_CMD_TX_RATE_CFG                       0x00d6
@@ -1216,6 +1217,17 @@ struct host_cmd_ds_tdls_oper {
        u8 peer_mac[ETH_ALEN];
 } __packed;
 
+struct mwifiex_chan_desc {
+       __le16 start_freq;
+       u8 chan_width;
+       u8 chan_num;
+} __packed;
+
+struct host_cmd_ds_chan_rpt_req {
+       struct mwifiex_chan_desc chan_desc;
+       __le32 msec_dwell_time;
+} __packed;
+
 struct mwifiex_fixed_bcn_param {
        __le64 timestamp;
        __le16 beacon_period;
@@ -1904,6 +1916,7 @@ struct host_cmd_ds_command {
                struct host_cmd_11ac_vht_cfg vht_cfg;
                struct host_cmd_ds_coalesce_cfg coalesce_cfg;
                struct host_cmd_ds_tdls_oper tdls_oper;
+               struct host_cmd_ds_chan_rpt_req chan_rpt_req;
        } params;
 } __packed;
 
index 1d1c3c8e516ef67d5d2cba70ff925c4085c22936..aa239a8826bcfc0df2c958d1e4692fdc9e35371b 100644 (file)
@@ -681,6 +681,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter)
                        priv = adapter->priv[i];
 
                        mwifiex_clean_auto_tdls(priv);
+                       mwifiex_abort_cac(priv);
                        mwifiex_clean_txrx(priv);
                        mwifiex_delete_bss_prio_tbl(priv);
                }
index c5ac20da273cd9af52846928bc310ca7dac4ed09..e266d99ac2b2945f715e40b6e4ff4a1b9d5c0a12 100644 (file)
@@ -588,6 +588,9 @@ struct mwifiex_private {
        spinlock_t ack_status_lock;
        /** rx histogram data */
        struct mwifiex_histogram_data *hist_data;
+       struct cfg80211_chan_def dfs_chandef;
+       struct workqueue_struct *dfs_cac_workqueue;
+       struct delayed_work dfs_cac_work;
 };
 
 enum mwifiex_ba_status {
@@ -754,6 +757,8 @@ struct mwifiex_adapter {
        struct work_struct main_work;
        struct workqueue_struct *rx_workqueue;
        struct work_struct rx_work;
+       struct workqueue_struct *dfs_workqueue;
+       struct work_struct dfs_work;
        bool rx_work_enabled;
        bool rx_processing;
        bool delay_main_work;
@@ -1376,6 +1381,9 @@ void mwifiex_check_auto_tdls(unsigned long context);
 void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac);
 void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv);
 void mwifiex_clean_auto_tdls(struct mwifiex_private *priv);
+int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv,
+                                         struct host_cmd_ds_command *cmd,
+                                         void *data_buf);
 
 void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
                                   void *event_body);
@@ -1383,6 +1391,8 @@ void mwifiex_parse_tx_status_event(struct mwifiex_private *priv,
 struct sk_buff *
 mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv,
                                struct sk_buff *skb, u8 flag, u64 *cookie);
+void mwifiex_dfs_cac_work_queue(struct work_struct *work);
+void mwifiex_abort_cac(struct mwifiex_private *priv);
 
 void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr,
                           s8 nflr);
index 92a66e8b6636e1e6a7ccfff74223edb9fd819dde..f7d204ffd6e97c2444129252535e9fd3580959cc 100644 (file)
@@ -1897,6 +1897,10 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
        case HostCmd_CMD_TDLS_OPER:
                ret = mwifiex_cmd_tdls_oper(priv, cmd_ptr, data_buf);
                break;
+       case HostCmd_CMD_CHAN_REPORT_REQUEST:
+               ret = mwifiex_cmd_issue_chan_report_request(priv, cmd_ptr,
+                                                           data_buf);
+               break;
        default:
                dev_err(priv->adapter->dev,
                        "PREP_CMD: unknown cmd- %#x\n", cmd_no);
index 65d10a33eab547eacedd8be1fd1561b44f5f5996..39f31766812fd84da2faca04ad2798dae419f28d 100644 (file)
@@ -1119,6 +1119,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no,
        case HostCmd_CMD_TDLS_OPER:
                ret = mwifiex_ret_tdls_oper(priv, resp);
                break;
+       case HostCmd_CMD_CHAN_REPORT_REQUEST:
+               break;
        default:
                dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
                        resp->command);
index baf53145207ea7e558a9c093c83abdb246887695..f5c2af01ba0a96c707edb76760d945154ddb30f2 100644 (file)
@@ -761,6 +761,11 @@ int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no,
                if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf))
                        return -1;
                break;
+       case HostCmd_CMD_CHAN_REPORT_REQUEST:
+               if (mwifiex_cmd_issue_chan_report_request(priv, cmd_buf,
+                                                         data_buf))
+                       return -1;
+               break;
        default:
                dev_err(priv->adapter->dev,
                        "PREP_CMD: unknown cmd %#x\n", cmd_no);