wl18xx: add radar detection implementation
authorEliad Peller <eliad@wizery.com>
Mon, 29 Dec 2014 06:24:07 +0000 (08:24 +0200)
committerKalle Valo <kvalo@codeaurora.org>
Fri, 9 Jan 2015 13:47:57 +0000 (15:47 +0200)
Add support for CAC start/stop commands, and pass
radar detection events from the fw to mac80211.

Bump fw name (to wl18xx-fw-4.bin) and min fw version
(to 8.9.*.*.11), and align event mailbox accordingly.

Signed-off-by: Guy Mishol <guym@ti.com>
Signed-off-by: Eliad Peller <eliad@wizery.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
12 files changed:
drivers/net/wireless/ti/wl18xx/cmd.c
drivers/net/wireless/ti/wl18xx/cmd.h
drivers/net/wireless/ti/wl18xx/event.c
drivers/net/wireless/ti/wl18xx/event.h
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/wl18xx.h
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/cmd.h
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h

index 44f0b205b065efa06233387e6545c8021fcf9931..10f9d1c064ba257067ad53fe1b986fdbfeb70113 100644 (file)
@@ -167,3 +167,34 @@ out_free:
 out:
        return ret;
 }
+
+int wl18xx_cmd_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start)
+{
+       struct wlcore_cmd_cac_start *cmd;
+       int ret = 0;
+
+       wl1271_debug(DEBUG_CMD, "cmd cac (channel %d) %s",
+                    wlvif->channel, start ? "start" : "stop");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd)
+               return -ENOMEM;
+
+       cmd->role_id = wlvif->role_id;
+       cmd->channel = wlvif->channel;
+       if (wlvif->band == IEEE80211_BAND_5GHZ)
+               cmd->band = WLCORE_BAND_5GHZ;
+       cmd->bandwidth = wlcore_get_native_channel_type(wlvif->channel_type);
+
+       ret = wl1271_cmd_send(wl,
+                             start ? CMD_CAC_START : CMD_CAC_STOP,
+                             cmd, sizeof(*cmd), 0);
+       if (ret < 0) {
+               wl1271_error("failed to send cac command");
+               goto out_free;
+       }
+
+out_free:
+       kfree(cmd);
+       return ret;
+}
index 92499e2dfa83206a83c7992e24fa21157cf776aa..91b3e2fe77cd24fb72b839b484e9e8341d872f7c 100644 (file)
@@ -59,6 +59,16 @@ struct wl18xx_cmd_smart_config_set_group_key {
        u8 key[16];
 } __packed;
 
+/* cac_start and cac_stop share the same params */
+struct wlcore_cmd_cac_start {
+       struct wl1271_cmd_header header;
+
+       u8 role_id;
+       u8 channel;
+       u8 band;
+       u8 bandwidth;
+} __packed;
+
 int wl18xx_cmd_channel_switch(struct wl1271 *wl,
                              struct wl12xx_vif *wlvif,
                              struct ieee80211_channel_switch *ch_switch);
@@ -66,4 +76,5 @@ int wl18xx_cmd_smart_config_start(struct wl1271 *wl, u32 group_bitmap);
 int wl18xx_cmd_smart_config_stop(struct wl1271 *wl);
 int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id,
                                          u8 key_len, u8 *key);
+int wl18xx_cmd_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start);
 #endif
index eb1848e084242f24e220a3d721b2da51df322b02..c28f0685419597ef0179d496594417eaa630c154 100644 (file)
@@ -47,6 +47,19 @@ int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
        return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout);
 }
 
+static const char *wl18xx_radar_type_decode(u8 radar_type)
+{
+       switch (radar_type) {
+       case RADAR_TYPE_REGULAR:
+               return "REGULAR";
+       case RADAR_TYPE_CHIRP:
+               return "CHIRP";
+       case RADAR_TYPE_NONE:
+       default:
+               return "N/A";
+       }
+}
+
 static int wlcore_smart_config_sync_event(struct wl1271 *wl, u8 sync_channel,
                                          u8 sync_band)
 {
@@ -115,6 +128,14 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl)
                        wl18xx_scan_completed(wl, wl->scan_wlvif);
        }
 
+       if (vector & RADAR_DETECTED_EVENT_ID) {
+               wl1271_info("radar event: channel %d type %s",
+                           mbox->radar_channel,
+                           wl18xx_radar_type_decode(mbox->radar_type));
+
+               ieee80211_radar_detected(wl->hw);
+       }
+
        if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
                wl1271_debug(DEBUG_EVENT,
                             "PERIODIC_SCAN_REPORT_EVENT (results %d)",
index 0680312d49439ca1fe8ea181855c54cb656117be..266ee87834e4332e9fe87abdc6fb172e58db8d8c 100644 (file)
@@ -42,6 +42,12 @@ enum {
        SMART_CONFIG_DECODE_EVENT_ID             = BIT(23),
 };
 
+enum wl18xx_radar_types {
+       RADAR_TYPE_NONE,
+       RADAR_TYPE_REGULAR,
+       RADAR_TYPE_CHIRP
+};
+
 struct wl18xx_event_mailbox {
        __le32 events_vector;
 
@@ -83,13 +89,19 @@ struct wl18xx_event_mailbox {
        u8 sc_token_len;
        u8 padding1;
        u8 sc_ssid[32];
-       u8 sc_pwd[32];
+       u8 sc_pwd[64];
        u8 sc_token[32];
 
        /* smart config sync channel */
        u8 sc_sync_channel;
        u8 sc_sync_band;
        u8 padding2[2];
+
+       /* radar detect */
+       u8 radar_channel;
+       u8 radar_type;
+
+       u8 padding3[2];
 } __packed;
 
 int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event,
index 04db941e19134398b43d7dc2707887d70aa6605a..c36b1afc3891b2a8d44b793b6b69e8d796bfbcf2 100644 (file)
@@ -655,7 +655,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = {
 };
 
 /* TODO: maybe move to a new header file? */
-#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-3.bin"
+#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-4.bin"
 
 static int wl18xx_identify_chip(struct wl1271 *wl)
 {
@@ -990,6 +990,7 @@ static int wl18xx_boot(struct wl1271 *wl)
 
        wl->event_mask = BSS_LOSS_EVENT_ID |
                SCAN_COMPLETE_EVENT_ID |
+               RADAR_DETECTED_EVENT_ID |
                RSSI_SNR_TRIGGER_0_EVENT_ID |
                PERIODIC_SCAN_COMPLETE_EVENT_ID |
                PERIODIC_SCAN_REPORT_EVENT_ID |
@@ -1703,6 +1704,7 @@ static struct wlcore_ops wl18xx_ops = {
        .interrupt_notify = wl18xx_acx_interrupt_notify_config,
        .rx_ba_filter   = wl18xx_acx_rx_ba_filter,
        .ap_sleep       = wl18xx_acx_ap_sleep,
+       .set_cac        = wl18xx_cmd_set_cac,
 };
 
 /* HT cap appropriate for wide channels in 2Ghz */
index 6a2b88030c1d37bdd0c6aa768e273a6fabff99b5..71e9e382ce80881dfbb43ea333e6c2e35158929b 100644 (file)
 
 /* minimum FW required for driver */
 #define WL18XX_CHIP_VER                8
-#define WL18XX_IFTYPE_VER      8
+#define WL18XX_IFTYPE_VER      9
 #define WL18XX_MAJOR_VER       WLCORE_FW_VER_IGNORE
 #define WL18XX_SUBTYPE_VER     WLCORE_FW_VER_IGNORE
-#define WL18XX_MINOR_VER       13
+#define WL18XX_MINOR_VER       11
 
 #define WL18XX_CMD_MAX_SIZE          740
 
index b82661962d337f2e2f785ebd10ad1f75f003d18c..aacad4eee070a33fc73bb231ba240b5c8e5c1920 100644 (file)
@@ -403,7 +403,7 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
        WARN_ON_ONCE(wl->active_link_count < 0);
 }
 
-static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
+u8 wlcore_get_native_channel_type(u8 nl_channel_type)
 {
        switch (nl_channel_type) {
        case NL80211_CHAN_NO_HT:
@@ -419,6 +419,7 @@ static u8 wlcore_get_native_channel_type(u8 nl_channel_type)
                return WLCORE_CHAN_NO_HT;
        }
 }
+EXPORT_SYMBOL_GPL(wlcore_get_native_channel_type);
 
 static int wl12xx_cmd_role_start_dev(struct wl1271 *wl,
                                     struct wl12xx_vif *wlvif,
index 453684a71d303db472cb3b5b566e40ae9badae4c..06bdee2a78d88b77865df9dfbf83b8a606b1295c 100644 (file)
@@ -105,6 +105,7 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif,
 void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid);
 int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
                                         u32 mask, bool *timeout);
+u8 wlcore_get_native_channel_type(u8 nl_channel_type);
 
 enum wl1271_commands {
        CMD_INTERROGATE = 1, /* use this to read information elements */
@@ -172,6 +173,11 @@ enum wl1271_commands {
        CMD_SMART_CONFIG_STOP           = 62,
        CMD_SMART_CONFIG_SET_GROUP_KEY  = 63,
 
+       CMD_CAC_START                   = 64,
+       CMD_CAC_STOP                    = 65,
+       CMD_DFS_MASTER_RESTART          = 66,
+       CMD_DFS_RADAR_DETECTION_DEBUG   = 67,
+
        MAX_COMMAND_ID = 0xFFFF,
 };
 
index 449050b5c7500267ee403cb4422819c176f524ce..42fef847dc5c0d939f1613751925f1c081853d78 100644 (file)
@@ -311,4 +311,13 @@ wlcore_smart_config_set_group_key(struct wl1271 *wl, u16 group_id,
 
        return wl->ops->smart_config_set_group_key(wl, group_id, key_len, key);
 }
+
+static inline int
+wlcore_hw_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start)
+{
+       if (!wl->ops->set_cac)
+               return -EINVAL;
+
+       return wl->ops->set_cac(wl, wlvif, start);
+}
 #endif
index caff69966ce1694e9c4d8adf8575628a37635b15..f46c9196530112a78a956f3f7c2b5703b5790978 100644 (file)
@@ -4629,10 +4629,46 @@ static void wlcore_op_change_chanctx(struct ieee80211_hw *hw,
                                     struct ieee80211_chanctx_conf *ctx,
                                     u32 changed)
 {
+       struct wl1271 *wl = hw->priv;
+       struct wl12xx_vif *wlvif;
+       int ret;
+       int channel = ieee80211_frequency_to_channel(
+               ctx->def.chan->center_freq);
+
        wl1271_debug(DEBUG_MAC80211,
                     "mac80211 change chanctx %d (type %d) changed 0x%x",
-                    ieee80211_frequency_to_channel(ctx->def.chan->center_freq),
-                    cfg80211_get_chandef_type(&ctx->def), changed);
+                    channel, cfg80211_get_chandef_type(&ctx->def), changed);
+
+       mutex_lock(&wl->mutex);
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       wl12xx_for_each_wlvif(wl, wlvif) {
+               struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
+
+               rcu_read_lock();
+               if (rcu_access_pointer(vif->chanctx_conf) != ctx) {
+                       rcu_read_unlock();
+                       continue;
+               }
+               rcu_read_unlock();
+
+               /* start radar if needed */
+               if (changed & IEEE80211_CHANCTX_CHANGE_RADAR &&
+                   wlvif->bss_type == BSS_TYPE_AP_BSS &&
+                   ctx->radar_enabled && !wlvif->radar_enabled &&
+                   ctx->def.chan->dfs_state == NL80211_DFS_USABLE) {
+                       wl1271_debug(DEBUG_MAC80211, "Start radar detection");
+                       wlcore_hw_set_cac(wl, wlvif, true);
+                       wlvif->radar_enabled = true;
+               }
+       }
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
 }
 
 static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
@@ -4643,13 +4679,26 @@ static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
        int channel = ieee80211_frequency_to_channel(
                ctx->def.chan->center_freq);
+       int ret = -EINVAL;
 
        wl1271_debug(DEBUG_MAC80211,
-                    "mac80211 assign chanctx (role %d) %d (type %d)",
-                    wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def));
+                    "mac80211 assign chanctx (role %d) %d (type %d) (radar %d dfs_state %d)",
+                    wlvif->role_id, channel,
+                    cfg80211_get_chandef_type(&ctx->def),
+                    ctx->radar_enabled, ctx->def.chan->dfs_state);
 
        mutex_lock(&wl->mutex);
 
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
        wlvif->band = ctx->def.chan->band;
        wlvif->channel = channel;
        wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def);
@@ -4657,6 +4706,15 @@ static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw,
        /* update default rates according to the band */
        wl1271_set_band_rate(wl, wlvif);
 
+       if (ctx->radar_enabled &&
+           ctx->def.chan->dfs_state == NL80211_DFS_USABLE) {
+               wl1271_debug(DEBUG_MAC80211, "Start radar detection");
+               wlcore_hw_set_cac(wl, wlvif, true);
+               wlvif->radar_enabled = true;
+       }
+
+       wl1271_ps_elp_sleep(wl);
+out:
        mutex_unlock(&wl->mutex);
 
        return 0;
@@ -4668,6 +4726,7 @@ static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
 {
        struct wl1271 *wl = hw->priv;
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
+       int ret;
 
        wl1271_debug(DEBUG_MAC80211,
                     "mac80211 unassign chanctx (role %d) %d (type %d)",
@@ -4676,6 +4735,97 @@ static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
                     cfg80211_get_chandef_type(&ctx->def));
 
        wl1271_tx_flush(wl);
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       if (wlvif->radar_enabled) {
+               wl1271_debug(DEBUG_MAC80211, "Stop radar detection");
+               wlcore_hw_set_cac(wl, wlvif, false);
+               wlvif->radar_enabled = false;
+       }
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
+static int __wlcore_switch_vif_chan(struct wl1271 *wl,
+                                   struct wl12xx_vif *wlvif,
+                                   struct ieee80211_chanctx_conf *new_ctx)
+{
+       int channel = ieee80211_frequency_to_channel(
+               new_ctx->def.chan->center_freq);
+
+       wl1271_debug(DEBUG_MAC80211,
+                    "switch vif (role %d) %d -> %d chan_type: %d",
+                    wlvif->role_id, wlvif->channel, channel,
+                    cfg80211_get_chandef_type(&new_ctx->def));
+
+       if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS))
+               return 0;
+
+       if (wlvif->radar_enabled) {
+               wl1271_debug(DEBUG_MAC80211, "Stop radar detection");
+               wlcore_hw_set_cac(wl, wlvif, false);
+               wlvif->radar_enabled = false;
+       }
+
+       wlvif->band = new_ctx->def.chan->band;
+       wlvif->channel = channel;
+       wlvif->channel_type = cfg80211_get_chandef_type(&new_ctx->def);
+
+       /* start radar if needed */
+       if (new_ctx->radar_enabled) {
+               wl1271_debug(DEBUG_MAC80211, "Start radar detection");
+               wlcore_hw_set_cac(wl, wlvif, true);
+               wlvif->radar_enabled = true;
+       }
+
+       return 0;
+}
+
+static int
+wlcore_op_switch_vif_chanctx(struct ieee80211_hw *hw,
+                            struct ieee80211_vif_chanctx_switch *vifs,
+                            int n_vifs,
+                            enum ieee80211_chanctx_switch_mode mode)
+{
+       struct wl1271 *wl = hw->priv;
+       int i, ret;
+
+       wl1271_debug(DEBUG_MAC80211,
+                    "mac80211 switch chanctx n_vifs %d mode %d",
+                    n_vifs, mode);
+
+       mutex_lock(&wl->mutex);
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       for (i = 0; i < n_vifs; i++) {
+               struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vifs[i].vif);
+
+               ret = __wlcore_switch_vif_chan(wl, wlvif, vifs[i].new_ctx);
+               if (ret)
+                       goto out_sleep;
+       }
+out_sleep:
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+
+       return 0;
 }
 
 static int wl1271_op_conf_tx(struct ieee80211_hw *hw,
@@ -5665,6 +5815,7 @@ static const struct ieee80211_ops wl1271_ops = {
        .change_chanctx = wlcore_op_change_chanctx,
        .assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
        .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
+       .switch_vif_chanctx = wlcore_op_switch_vif_chanctx,
        .sta_rc_update = wlcore_op_sta_rc_update,
        .get_rssi = wlcore_op_get_rssi,
        CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
index c8fe2ae272ac6e66a878a5f750a4d66aa3ae8b35..caee58f72a0541bee75b648858b76e77d2def555 100644 (file)
@@ -123,6 +123,8 @@ struct wlcore_ops {
        int (*smart_config_stop)(struct wl1271 *wl);
        int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id,
                                          u8 key_len, u8 *key);
+       int (*set_cac)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                      bool start);
 };
 
 enum wlcore_partitions {
index 811851d00b3320a1447cef961cdf2b486b8264f6..b2bdb139c0204ea1417860a85b81f4cf1c7538c9 100644 (file)
@@ -434,6 +434,8 @@ struct wl12xx_vif {
 
        bool wmm_enabled;
 
+       bool radar_enabled;
+
        /* Rx Streaming */
        struct work_struct rx_streaming_enable_work;
        struct work_struct rx_streaming_disable_work;