[CONF_SG_BT_PER_THRESHOLD] = 7500,
[CONF_SG_HV3_MAX_OVERRIDE] = 0,
[CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400,
- [CONF_SG_BT_LOAD_RATIO] = 50,
+ [CONF_SG_BT_LOAD_RATIO] = 200,
[CONF_SG_AUTO_PS_MODE] = 1,
[CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
.ps_poll_threshold = 10,
.ps_poll_recovery_period = 700,
.bet_enable = CONF_BET_MODE_ENABLE,
- .bet_max_consecutive = 10,
+ .bet_max_consecutive = 50,
.psm_entry_retries = 5,
.psm_exit_retries = 255,
.psm_entry_nullfunc_retries = 3,
.tx_ba_win_size = 64,
.inactivity_timeout = 10000,
},
- .mem = {
+ .mem_wl127x = {
.num_stations = 1,
.ssid_profiles = 1,
.rx_block_num = 70,
.min_req_tx_blocks = 100,
.min_req_rx_blocks = 22,
.tx_min = 27,
- }
+ },
+ .mem_wl128x = {
+ .num_stations = 1,
+ .ssid_profiles = 1,
+ .rx_block_num = 40,
+ .tx_min_block_num = 40,
+ .dynamic_memory = 1,
+ .min_req_tx_blocks = 45,
+ .min_req_rx_blocks = 22,
+ .tx_min = 27,
+ },
};
static void __wl1271_op_remove_interface(struct wl1271 *wl);
},
};
+static DEFINE_MUTEX(wl_list_mutex);
static LIST_HEAD(wl_list);
static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
return NOTIFY_DONE;
wl_temp = hw->priv;
+ mutex_lock(&wl_list_mutex);
list_for_each_entry(wl, &wl_list, list) {
if (wl == wl_temp)
break;
}
+ mutex_unlock(&wl_list_mutex);
if (wl != wl_temp)
return NOTIFY_DONE;
struct conf_tx_tid *conf_tid;
int ret, i;
- ret = wl1271_cmd_general_parms(wl);
+ if (wl->chip.id == CHIP_ID_1283_PG20)
+ ret = wl128x_cmd_general_parms(wl);
+ else
+ ret = wl1271_cmd_general_parms(wl);
+ if (ret < 0)
+ return ret;
+
+ if (wl->chip.id == CHIP_ID_1283_PG20)
+ ret = wl128x_cmd_radio_parms(wl);
+ else
+ ret = wl1271_cmd_radio_parms(wl);
if (ret < 0)
return ret;
- ret = wl1271_cmd_radio_parms(wl);
+ if (wl->chip.id != CHIP_ID_1283_PG20) {
+ ret = wl1271_cmd_ext_radio_parms(wl);
+ if (ret < 0)
+ return ret;
+ }
if (ret < 0)
return ret;
- ret = wl1271_cmd_ext_radio_parms(wl);
+ /* Chip-specific initializations */
+ ret = wl1271_chip_specific_init(wl);
if (ret < 0)
return ret;
{
struct wl1271_fw_common_status *status = &full_status->common;
struct timespec ts;
+ u32 old_tx_blk_count = wl->tx_blocks_available;
u32 total = 0;
int i;
- if (wl->bss_type == BSS_TYPE_AP_BSS)
+ if (wl->bss_type == BSS_TYPE_AP_BSS) {
wl1271_raw_read(wl, FW_STATUS_ADDR, status,
sizeof(struct wl1271_fw_ap_status), false);
- else
+ } else {
wl1271_raw_read(wl, FW_STATUS_ADDR, status,
sizeof(struct wl1271_fw_sta_status), false);
+ /* Update tx total blocks change */
+ wl->tx_total_diff +=
+ ((struct wl1271_fw_sta_status *)status)->tx_total -
+ wl->tx_new_total;
+
+ /* Update total tx blocks */
+ wl->tx_new_total =
+ ((struct wl1271_fw_sta_status *)status)->tx_total;
+ }
+
wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
"drv_rx_counter = %d, tx_results_counter = %d)",
status->intr,
/* update number of available TX blocks */
for (i = 0; i < NUM_TX_QUEUES; i++) {
- u32 cnt = le32_to_cpu(status->tx_released_blks[i]) -
+ total += le32_to_cpu(status->tx_released_blks[i]) -
wl->tx_blocks_freed[i];
wl->tx_blocks_freed[i] =
le32_to_cpu(status->tx_released_blks[i]);
- wl->tx_blocks_available += cnt;
- total += cnt;
+
+ }
+
+ /*
+ * By adding the freed blocks to tx_total_diff we are actually
+ * moving them to the RX pool.
+ */
+ wl->tx_total_diff += total;
+
+ /* if we have positive difference, add the blocks to the TX pool */
+ if (wl->tx_total_diff >= 0) {
+ wl->tx_blocks_available += wl->tx_total_diff;
+ wl->tx_total_diff = 0;
}
/* if more blocks are available now, tx work can be scheduled */
- if (total)
+ if (wl->tx_blocks_available > old_tx_blk_count)
clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
/* for AP update num of allocated TX blocks per link and ps status */
switch (wl->bss_type) {
case BSS_TYPE_AP_BSS:
- fw_name = WL1271_AP_FW_NAME;
+ if (wl->chip.id == CHIP_ID_1283_PG20)
+ fw_name = WL128X_AP_FW_NAME;
+ else
+ fw_name = WL127X_AP_FW_NAME;
break;
case BSS_TYPE_IBSS:
case BSS_TYPE_STA_BSS:
- fw_name = WL1271_FW_NAME;
+ if (wl->chip.id == CHIP_ID_1283_PG20)
+ fw_name = WL128X_FW_NAME;
+ else
+ fw_name = WL1271_FW_NAME;
break;
default:
wl1271_error("no compatible firmware for bss_type %d",
const struct firmware *fw;
int ret;
- ret = request_firmware(&fw, WL1271_NVS_NAME, wl1271_wl_to_dev(wl));
+ ret = request_firmware(&fw, WL12XX_NVS_NAME, wl1271_wl_to_dev(wl));
if (ret < 0) {
wl1271_error("could not get nvs file: %d", ret);
return ret;
}
- wl->nvs = kmemdup(fw->data, sizeof(struct wl1271_nvs_file), GFP_KERNEL);
+ wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
if (!wl->nvs) {
wl1271_error("could not allocate memory for the nvs file");
if (ret < 0)
goto out;
break;
+ case CHIP_ID_1283_PG20:
+ wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
+ wl->chip.id);
+
+ ret = wl1271_setup(wl);
+ if (ret < 0)
+ goto out;
+ break;
+ case CHIP_ID_1283_PG10:
default:
wl1271_warning("unsupported chip id: 0x%x", wl->chip.id);
ret = -ENODEV;
return ret;
}
+static unsigned int wl1271_get_fw_ver_quirks(struct wl1271 *wl)
+{
+ unsigned int quirks = 0;
+ unsigned int *fw_ver = wl->chip.fw_ver;
+
+ /* Only for wl127x */
+ if ((fw_ver[FW_VER_CHIP] == FW_VER_CHIP_WL127X) &&
+ /* Check STA version */
+ (((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_STA) &&
+ (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_STA_MIN)) ||
+ /* Check AP version */
+ ((fw_ver[FW_VER_IF_TYPE] == FW_VER_IF_TYPE_AP) &&
+ (fw_ver[FW_VER_MINOR] < FW_VER_MINOR_1_SPARE_AP_MIN))))
+ quirks |= WL12XX_QUIRK_USE_2_SPARE_BLOCKS;
+
+ return quirks;
+}
+
int wl1271_plt_start(struct wl1271 *wl)
{
int retries = WL1271_BOOT_RETRIES;
wl->state = WL1271_STATE_PLT;
wl1271_notice("firmware booted in PLT mode (%s)",
wl->chip.fw_ver_str);
+
+ /* Check if any quirks are needed with older fw versions */
+ wl->quirks |= wl1271_get_fw_ver_quirks(wl);
goto out;
irq_disable:
return ret;
}
-int __wl1271_plt_stop(struct wl1271 *wl)
+static int __wl1271_plt_stop(struct wl1271 *wl)
{
int ret = 0;
spin_unlock_irqrestore(&wl->wl_lock, flags);
}
+#define TX_DUMMY_PACKET_SIZE 1400
+int wl1271_tx_dummy_packet(struct wl1271 *wl)
+{
+ struct sk_buff *skb = NULL;
+ struct ieee80211_hdr_3addr *hdr;
+ int ret = 0;
+
+ skb = dev_alloc_skb(
+ sizeof(struct wl1271_tx_hw_descr) + sizeof(*hdr) +
+ TX_DUMMY_PACKET_SIZE);
+ if (!skb) {
+ wl1271_warning("failed to allocate buffer for dummy packet");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ skb_reserve(skb, sizeof(struct wl1271_tx_hw_descr));
+
+ hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr));
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_FCTL_TODS |
+ IEEE80211_STYPE_NULLFUNC);
+
+ memcpy(hdr->addr1, wl->bssid, ETH_ALEN);
+ memcpy(hdr->addr2, wl->mac_addr, ETH_ALEN);
+ memcpy(hdr->addr3, wl->bssid, ETH_ALEN);
+
+ skb_put(skb, TX_DUMMY_PACKET_SIZE);
+
+ memset(skb->data, 0, TX_DUMMY_PACKET_SIZE);
+
+ skb->pkt_type = TX_PKT_TYPE_DUMMY_REQ;
+ /* Dummy packets require the TID to be management */
+ skb->priority = WL1271_TID_MGMT;
+ /* CONF_TX_AC_VO */
+ skb->queue_mapping = 0;
+
+ wl1271_op_tx(wl->hw, skb);
+
+out:
+ return ret;
+}
+
static struct notifier_block wl1271_dev_notifier = {
.notifier_call = wl1271_dev_notify,
};
goto out;
}
+ /*
+ * in some very corner case HW recovery scenarios its possible to
+ * get here before __wl1271_op_remove_interface is complete, so
+ * opt out if that is the case.
+ */
+ if (test_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
switch (vif->type) {
case NL80211_IFTYPE_STATION:
wl->bss_type = BSS_TYPE_STA_BSS;
wl->vif = vif;
wl->state = WL1271_STATE_ON;
+ set_bit(WL1271_FLAG_IF_INITIALIZED, &wl->flags);
wl1271_info("firmware booted (%s)", wl->chip.fw_ver_str);
/* update hw/fw version info in wiphy struct */
strncpy(wiphy->fw_version, wl->chip.fw_ver_str,
sizeof(wiphy->fw_version));
+ /* Check if any quirks are needed with older fw versions */
+ wl->quirks |= wl1271_get_fw_ver_quirks(wl);
+
/*
* Now we know if 11a is supported (info from the NVS), so disable
* 11a channels if not supported
out:
mutex_unlock(&wl->mutex);
+ mutex_lock(&wl_list_mutex);
if (!ret)
list_add(&wl->list, &wl_list);
+ mutex_unlock(&wl_list_mutex);
return ret;
}
wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
+ /* because of hardware recovery, we may get here twice */
+ if (wl->state != WL1271_STATE_ON)
+ return;
+
wl1271_info("down");
+ mutex_lock(&wl_list_mutex);
list_del(&wl->list);
-
- WARN_ON(wl->state != WL1271_STATE_ON);
+ mutex_unlock(&wl_list_mutex);
/* enable dyn ps just in case (if left on due to fw crash etc) */
if (wl->bss_type == BSS_TYPE_STA_BSS)
if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
wl->scan.state = WL1271_SCAN_STATE_IDLE;
- kfree(wl->scan.scanned_ch);
- wl->scan.scanned_ch = NULL;
+ memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
wl->scan.req = NULL;
ieee80211_scan_completed(wl->hw, true);
}
+ /*
+ * this must be before the cancel_work calls below, so that the work
+ * functions don't perform further work.
+ */
wl->state = WL1271_STATE_OFF;
mutex_unlock(&wl->mutex);
wl->time_offset = 0;
wl->session_counter = 0;
wl->rate_set = CONF_TX_RATE_MASK_BASIC;
- wl->flags = 0;
wl->vif = NULL;
wl->filters = 0;
wl1271_free_ap_keys(wl);
memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map));
wl->ap_fw_ps_map = 0;
wl->ap_ps_map = 0;
+ wl->block_size = 0;
+
+ /*
+ * this is performed after the cancel_work calls and the associated
+ * mutex_lock, so that wl1271_op_add_interface does not accidentally
+ * get executed before all these vars have been reset.
+ */
+ wl->flags = 0;
for (i = 0; i < NUM_TX_QUEUES; i++)
wl->tx_blocks_freed[i] = 0;
cancel_work_sync(&wl->recovery_work);
}
-static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
+void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
{
wl1271_set_default_filters(wl);
clear_bit(WL1271_FLAG_JOINED, &wl->flags);
memset(wl->bssid, 0, ETH_ALEN);
- /* stop filterting packets based on bssid */
+ /* stop filtering packets based on bssid */
wl1271_configure_filters(wl, FIF_OTHER_BSS);
out:
mutex_lock(&wl->mutex);
if (unlikely(wl->state == WL1271_STATE_OFF)) {
- ret = -EAGAIN;
+ /* we support configuring the channel and band while off */
+ if ((changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
+ wl->band = conf->channel->band;
+ wl->channel = channel;
+ }
+
goto out;
}
conf_tid->ack_policy = CONF_ACK_POLICY_LEGACY;
conf_tid->apsd_conf[0] = 0;
conf_tid->apsd_conf[1] = 0;
- } else {
- ret = wl1271_ps_elp_wakeup(wl);
- if (ret < 0)
- goto out;
+ goto out;
+ }
- /*
- * the txop is confed in units of 32us by the mac80211,
- * we need us
- */
- ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
- params->cw_min, params->cw_max,
- params->aifs, params->txop << 5);
- if (ret < 0)
- goto out_sleep;
+ ret = wl1271_ps_elp_wakeup(wl);
+ if (ret < 0)
+ goto out;
- ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
- CONF_CHANNEL_TYPE_EDCF,
- wl1271_tx_get_queue(queue),
- ps_scheme, CONF_ACK_POLICY_LEGACY,
- 0, 0);
- if (ret < 0)
- goto out_sleep;
+ /*
+ * the txop is confed in units of 32us by the mac80211,
+ * we need us
+ */
+ ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
+ params->cw_min, params->cw_max,
+ params->aifs, params->txop << 5);
+ if (ret < 0)
+ goto out_sleep;
+
+ ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
+ CONF_CHANNEL_TYPE_EDCF,
+ wl1271_tx_get_queue(queue),
+ ps_scheme, CONF_ACK_POLICY_LEGACY,
+ 0, 0);
out_sleep:
- wl1271_ps_elp_sleep(wl);
- }
+ wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return ret;
}
-int wl1271_op_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- enum ieee80211_ampdu_mlme_action action,
- struct ieee80211_sta *sta, u16 tid, u16 *ssn,
- u8 buf_size)
+static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+ u8 buf_size)
{
struct wl1271 *wl = hw->priv;
int ret;
#ifdef CONFIG_WL12XX_HT
#define WL12XX_HT_CAP { \
- .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20, \
+ .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | \
+ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), \
.ht_supported = true, \
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, \
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \
ret = wl1271_fetch_nvs(wl);
if (ret == 0) {
- u8 *nvs_ptr = (u8 *)wl->nvs->nvs;
+ /* NOTE: The wl->nvs->nvs element must be first, in
+ * order to simplify the casting, we assume it is at
+ * the beginning of the wl->nvs structure.
+ */
+ u8 *nvs_ptr = (u8 *)wl->nvs;
wl->mac_addr[0] = nvs_ptr[11];
wl->mac_addr[1] = nvs_ptr[10];
wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE -
sizeof(struct ieee80211_header);
+ /* make sure all our channels fit in the scanned_ch bitmask */
+ BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) +
+ ARRAY_SIZE(wl1271_channels_5ghz) >
+ WL1271_MAX_CHANNELS);
/*
* We keep local copies of the band structs because we need to
* modify them on a per-device basis.
wl->ap_ps_map = 0;
wl->ap_fw_ps_map = 0;
wl->quirks = 0;
+ wl->block_size = 0;
memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map));
for (i = 0; i < ACX_TX_DESCRIPTORS; i++)