@@ -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);
+ }
+}
@@ -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;
@@ -3088,6 +3116,36 @@ mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev,
}
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,
struct station_parameters *params)
@@ -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
@@ -108,6 +108,8 @@
#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_ */
@@ -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;
@@ -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);
}
@@ -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);
@@ -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);
@@ -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);
@@ -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);