cfg80211: consolidate passive-scan and no-ibss flags
[cascardo/linux.git] / drivers / net / wireless / ti / wlcore / main.c
index 38995f9..e9da47c 100644 (file)
@@ -44,6 +44,7 @@
 #define WL1271_BOOT_RETRIES 3
 
 static char *fwlog_param;
+static int fwlog_mem_blocks = -1;
 static int bug_on_recovery = -1;
 static int no_recovery     = -1;
 
@@ -90,8 +91,7 @@ static void wl1271_reg_notify(struct wiphy *wiphy,
                        continue;
 
                if (ch->flags & IEEE80211_CHAN_RADAR)
-                       ch->flags |= IEEE80211_CHAN_NO_IBSS |
-                                    IEEE80211_CHAN_PASSIVE_SCAN;
+                       ch->flags |= IEEE80211_CHAN_NO_IR;
 
        }
 
@@ -291,6 +291,18 @@ static void wlcore_adjust_conf(struct wl1271 *wl)
 {
        /* Adjust settings according to optional module parameters */
 
+       /* Firmware Logger params */
+       if (fwlog_mem_blocks != -1) {
+               if (fwlog_mem_blocks >= CONF_FWLOG_MIN_MEM_BLOCKS &&
+                   fwlog_mem_blocks <= CONF_FWLOG_MAX_MEM_BLOCKS) {
+                       wl->conf.fwlog.mem_blocks = fwlog_mem_blocks;
+               } else {
+                       wl1271_error(
+                               "Illegal fwlog_mem_blocks=%d using default %d",
+                               fwlog_mem_blocks, wl->conf.fwlog.mem_blocks);
+               }
+       }
+
        if (fwlog_param) {
                if (!strcmp(fwlog_param, "continuous")) {
                        wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS;
@@ -780,6 +792,7 @@ void wl12xx_queue_recovery_work(struct wl1271 *wl)
        if (wl->state == WLCORE_STATE_ON) {
                wl->state = WLCORE_STATE_RESTARTING;
                set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
+               wl1271_ps_elp_wakeup(wl);
                wlcore_disable_interrupts_nosync(wl);
                ieee80211_queue_work(wl->hw, &wl->recovery_work);
        }
@@ -787,19 +800,10 @@ void wl12xx_queue_recovery_work(struct wl1271 *wl)
 
 size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
 {
-       size_t len = 0;
-
-       /* The FW log is a length-value list, find where the log end */
-       while (len < maxlen) {
-               if (memblock[len] == 0)
-                       break;
-               if (len + memblock[len] + 1 > maxlen)
-                       break;
-               len += memblock[len] + 1;
-       }
+       size_t len;
 
        /* Make sure we have enough room */
-       len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size));
+       len = min(maxlen, (size_t)(PAGE_SIZE - wl->fwlog_size));
 
        /* Fill the FW log file, consumed by the sysfs fwlog entry */
        memcpy(wl->fwlog + wl->fwlog_size, memblock, len);
@@ -808,10 +812,9 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen)
        return len;
 }
 
-#define WLCORE_FW_LOG_END 0x2000000
-
 static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 {
+       struct wlcore_partition_set part, old_part;
        u32 addr;
        u32 offset;
        u32 end_of_log;
@@ -824,7 +827,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
        wl1271_info("Reading FW panic log");
 
-       block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL);
+       block = kmalloc(wl->fw_mem_block_size, GFP_KERNEL);
        if (!block)
                return;
 
@@ -850,17 +853,31 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
        if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) {
                offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor);
-               end_of_log = WLCORE_FW_LOG_END;
+               end_of_log = wl->fwlog_end;
        } else {
                offset = sizeof(addr);
                end_of_log = addr;
        }
 
+       old_part = wl->curr_part;
+       memset(&part, 0, sizeof(part));
+
        /* Traverse the memory blocks linked list */
        do {
-               memset(block, 0, WL12XX_HW_BLOCK_SIZE);
-               ret = wlcore_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE,
-                                        false);
+               part.mem.start = wlcore_hw_convert_hwaddr(wl, addr);
+               part.mem.size  = PAGE_SIZE;
+
+               ret = wlcore_set_partition(wl, &part);
+               if (ret < 0) {
+                       wl1271_error("%s: set_partition start=0x%X size=%d",
+                               __func__, part.mem.start, part.mem.size);
+                       goto out;
+               }
+
+               memset(block, 0, wl->fw_mem_block_size);
+               ret = wlcore_read_hwaddr(wl, addr, block,
+                                       wl->fw_mem_block_size, false);
+
                if (ret < 0)
                        goto out;
 
@@ -871,8 +888,9 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
                 * on demand mode and is equal to 0x2000000 in continuous mode.
                 */
                addr = le32_to_cpup((__le32 *)block);
+
                if (!wl12xx_copy_fwlog(wl, block + offset,
-                                      WL12XX_HW_BLOCK_SIZE - offset))
+                                       wl->fw_mem_block_size - offset))
                        break;
        } while (addr && (addr != end_of_log));
 
@@ -880,6 +898,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
 
 out:
        kfree(block);
+       wlcore_set_partition(wl, &old_part);
 }
 
 static void wlcore_print_recovery(struct wl1271 *wl)
@@ -924,7 +943,8 @@ static void wl1271_recovery_work(struct work_struct *work)
                goto out_unlock;
 
        if (!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) {
-               wl12xx_read_fwlog_panic(wl);
+               if (wl->conf.fwlog.output == WL12XX_FWLOG_OUTPUT_HOST)
+                       wl12xx_read_fwlog_panic(wl);
                wlcore_print_recovery(wl);
        }
 
@@ -1062,7 +1082,8 @@ int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode)
        static const char* const PLT_MODE[] = {
                "PLT_OFF",
                "PLT_ON",
-               "PLT_FEM_DETECT"
+               "PLT_FEM_DETECT",
+               "PLT_CHIP_AWAKE"
        };
 
        int ret;
@@ -1088,9 +1109,11 @@ int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode)
                if (ret < 0)
                        goto power_off;
 
-               ret = wl->ops->plt_init(wl);
-               if (ret < 0)
-                       goto power_off;
+               if (plt_mode != PLT_CHIP_AWAKE) {
+                       ret = wl->ops->plt_init(wl);
+                       if (ret < 0)
+                               goto power_off;
+               }
 
                wl->state = WLCORE_STATE_ON;
                wl1271_notice("firmware booted in PLT mode %s (%s)",
@@ -1925,8 +1948,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
 
        /*
         * FW channels must be re-calibrated after recovery,
-        * clear the last Reg-Domain channel configuration.
+        * save current Reg-Domain channel configuration and clear it.
         */
+       memcpy(wl->reg_ch_conf_pending, wl->reg_ch_conf_last,
+              sizeof(wl->reg_ch_conf_pending));
        memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last));
 }
 
@@ -2008,6 +2033,47 @@ out:
        mutex_unlock(&wl->mutex);
 }
 
+static void wlcore_pending_auth_complete_work(struct work_struct *work)
+{
+       struct delayed_work *dwork;
+       struct wl1271 *wl;
+       struct wl12xx_vif *wlvif;
+       unsigned long time_spare;
+       int ret;
+
+       dwork = container_of(work, struct delayed_work, work);
+       wlvif = container_of(dwork, struct wl12xx_vif,
+                            pending_auth_complete_work);
+       wl = wlvif->wl;
+
+       mutex_lock(&wl->mutex);
+
+       if (unlikely(wl->state != WLCORE_STATE_ON))
+               goto out;
+
+       /*
+        * Make sure a second really passed since the last auth reply. Maybe
+        * a second auth reply arrived while we were stuck on the mutex.
+        * Check for a little less than the timeout to protect from scheduler
+        * irregularities.
+        */
+       time_spare = jiffies +
+                       msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT - 50);
+       if (!time_after(time_spare, wlvif->pending_auth_reply_time))
+               goto out;
+
+       ret = wl1271_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out;
+
+       /* cancel the ROC if active */
+       wlcore_update_inconn_sta(wl, wlvif, NULL, false);
+
+       wl1271_ps_elp_sleep(wl);
+out:
+       mutex_unlock(&wl->mutex);
+}
+
 static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx)
 {
        u8 policy = find_first_zero_bit(wl->rate_policies_map,
@@ -2159,6 +2225,8 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
                          wlcore_channel_switch_work);
        INIT_DELAYED_WORK(&wlvif->connection_loss_work,
                          wlcore_connection_loss_work);
+       INIT_DELAYED_WORK(&wlvif->pending_auth_complete_work,
+                         wlcore_pending_auth_complete_work);
        INIT_LIST_HEAD(&wlvif->list);
 
        setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer,
@@ -2376,6 +2444,11 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
        int ret = 0;
        u8 role_type;
 
+       if (wl->plt) {
+               wl1271_error("Adding Interface not allowed while in PLT mode");
+               return -EBUSY;
+       }
+
        vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
                             IEEE80211_VIF_SUPPORTS_CQM_RSSI;
 
@@ -2572,6 +2645,12 @@ deinit:
            !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags))
                goto unlock;
 
+       if (wl->ap_count == 0 && is_ap) {
+               /* mask ap events */
+               wl->event_mask &= ~wl->ap_event_mask;
+               wl1271_event_unmask(wl);
+       }
+
        if (wl->ap_count == 0 && is_ap && wl->sta_count) {
                u8 sta_auth = wl->conf.conn.sta_sleep_auth;
                /* Configure for power according to debugfs */
@@ -2590,6 +2669,7 @@ unlock:
        cancel_work_sync(&wlvif->rx_streaming_disable_work);
        cancel_delayed_work_sync(&wlvif->connection_loss_work);
        cancel_delayed_work_sync(&wlvif->channel_switch_work);
+       cancel_delayed_work_sync(&wlvif->pending_auth_complete_work);
 
        mutex_lock(&wl->mutex);
 }
@@ -2875,6 +2955,25 @@ static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        wlvif->rate_set = wlvif->basic_rate_set;
 }
 
+static void wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                                  bool idle)
+{
+       bool cur_idle = !test_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags);
+
+       if (idle == cur_idle)
+               return;
+
+       if (idle) {
+               clear_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags);
+       } else {
+               /* The current firmware only supports sched_scan in idle */
+               if (wl->sched_vif == wlvif)
+                       wl->ops->sched_scan_stop(wl, wlvif);
+
+               set_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags);
+       }
+}
+
 static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                             struct ieee80211_conf *conf, u32 changed)
 {
@@ -3969,6 +4068,13 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl,
                        }
                } else {
                        if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) {
+                               /*
+                                * AP might be in ROC in case we have just
+                                * sent auth reply. handle it.
+                                */
+                               if (test_bit(wlvif->role_id, wl->roc_map))
+                                       wl12xx_croc(wl, wlvif->role_id);
+
                                ret = wl12xx_cmd_role_stop_ap(wl, wlvif);
                                if (ret < 0)
                                        goto out;
@@ -4120,6 +4226,9 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                do_join = true;
        }
 
+       if (changed & BSS_CHANGED_IDLE && !is_ibss)
+               wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle);
+
        if (changed & BSS_CHANGED_CQM) {
                bool enable = false;
                if (bss_conf->cqm_rssi_thold)
@@ -4656,29 +4765,49 @@ static void wlcore_roc_if_possible(struct wl1271 *wl,
        wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel);
 }
 
-static void wlcore_update_inconn_sta(struct wl1271 *wl,
-                                    struct wl12xx_vif *wlvif,
-                                    struct wl1271_station *wl_sta,
-                                    bool in_connection)
+/*
+ * when wl_sta is NULL, we treat this call as if coming from a
+ * pending auth reply.
+ * wl->mutex must be taken and the FW must be awake when the call
+ * takes place.
+ */
+void wlcore_update_inconn_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                             struct wl1271_station *wl_sta, bool in_conn)
 {
-       if (in_connection) {
-               if (WARN_ON(wl_sta->in_connection))
+       if (in_conn) {
+               if (WARN_ON(wl_sta && wl_sta->in_connection))
                        return;
-               wl_sta->in_connection = true;
-               if (!wlvif->inconn_count++)
+
+               if (!wlvif->ap_pending_auth_reply &&
+                   !wlvif->inconn_count)
                        wlcore_roc_if_possible(wl, wlvif);
+
+               if (wl_sta) {
+                       wl_sta->in_connection = true;
+                       wlvif->inconn_count++;
+               } else {
+                       wlvif->ap_pending_auth_reply = true;
+               }
        } else {
-               if (!wl_sta->in_connection)
+               if (wl_sta && !wl_sta->in_connection)
                        return;
 
-               wl_sta->in_connection = false;
-               wlvif->inconn_count--;
-               if (WARN_ON(wlvif->inconn_count < 0))
+               if (WARN_ON(!wl_sta && !wlvif->ap_pending_auth_reply))
                        return;
 
-               if (!wlvif->inconn_count)
-                       if (test_bit(wlvif->role_id, wl->roc_map))
-                               wl12xx_croc(wl, wlvif->role_id);
+               if (WARN_ON(wl_sta && !wlvif->inconn_count))
+                       return;
+
+               if (wl_sta) {
+                       wl_sta->in_connection = false;
+                       wlvif->inconn_count--;
+               } else {
+                       wlvif->ap_pending_auth_reply = false;
+               }
+
+               if (!wlvif->inconn_count && !wlvif->ap_pending_auth_reply &&
+                   test_bit(wlvif->role_id, wl->roc_map))
+                       wl12xx_croc(wl, wlvif->role_id);
        }
 }
 
@@ -5313,10 +5442,7 @@ static struct ieee80211_rate wl1271_rates_5ghz[] = {
 
 /* 5 GHz band channels for WL1273 */
 static struct ieee80211_channel wl1271_channels_5ghz[] = {
-       { .hw_value = 7, .center_freq = 5035, .max_power = WLCORE_MAX_TXPWR },
        { .hw_value = 8, .center_freq = 5040, .max_power = WLCORE_MAX_TXPWR },
-       { .hw_value = 9, .center_freq = 5045, .max_power = WLCORE_MAX_TXPWR },
-       { .hw_value = 11, .center_freq = 5055, .max_power = WLCORE_MAX_TXPWR },
        { .hw_value = 12, .center_freq = 5060, .max_power = WLCORE_MAX_TXPWR },
        { .hw_value = 16, .center_freq = 5080, .max_power = WLCORE_MAX_TXPWR },
        { .hw_value = 34, .center_freq = 5170, .max_power = WLCORE_MAX_TXPWR },
@@ -5896,14 +6022,20 @@ static const struct wiphy_wowlan_support wlcore_wowlan_support = {
 };
 #endif
 
+static irqreturn_t wlcore_hardirq(int irq, void *cookie)
+{
+       return IRQ_WAKE_THREAD;
+}
+
 static void wlcore_nvs_cb(const struct firmware *fw, void *context)
 {
        struct wl1271 *wl = context;
        struct platform_device *pdev = wl->pdev;
-       struct wlcore_platdev_data *pdev_data = pdev->dev.platform_data;
+       struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev);
        struct wl12xx_platform_data *pdata = pdev_data->pdata;
        unsigned long irqflags;
        int ret;
+       irq_handler_t hardirq_fn = NULL;
 
        if (fw) {
                wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
@@ -5932,12 +6064,14 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context)
        wl->platform_quirks = pdata->platform_quirks;
        wl->if_ops = pdev_data->if_ops;
 
-       if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
+       if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) {
                irqflags = IRQF_TRIGGER_RISING;
-       else
+               hardirq_fn = wlcore_hardirq;
+       } else {
                irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT;
+       }
 
-       ret = request_threaded_irq(wl->irq, NULL, wlcore_irq,
+       ret = request_threaded_irq(wl->irq, hardirq_fn, wlcore_irq,
                                   irqflags, pdev->name, wl);
        if (ret < 0) {
                wl1271_error("request_irq() failed: %d", ret);
@@ -6046,6 +6180,9 @@ module_param_named(fwlog, fwlog_param, charp, 0);
 MODULE_PARM_DESC(fwlog,
                 "FW logger options: continuous, ondemand, dbgpins or disable");
 
+module_param(fwlog_mem_blocks, int, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(fwlog_mem_blocks, "fwlog mem_blocks");
+
 module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR);
 MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery");