From 6735329934e9acc1941a991ed6f6ad4be3e082a5 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Thu, 18 Nov 2010 15:19:02 +0200 Subject: [PATCH 01/15] wl12xx: Fix kernel crash related to hw recovery and interface shutdown It is possible that the op_remove_interface function is invoked exactly at the same time has hw recovery is started. In this case it is possible for the interface to be already removed in the op_remove_interface call, which currently leads to a kernel warning and a subsequent kernel crash. Fix this by ignoring the op_remove_interface call if the interface is already down at that point. Signed-off-by: Juuso Oikarinen Tested-by: Tuomas Katila Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 708ffe304c6d..35cfcf675795 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1157,10 +1157,16 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, struct wl1271 *wl = hw->priv; mutex_lock(&wl->mutex); - WARN_ON(wl->vif != vif); - __wl1271_op_remove_interface(wl); - mutex_unlock(&wl->mutex); + /* + * wl->vif can be null here if someone shuts down the interface + * just when hardware recovery has been started. + */ + if (wl->vif) { + WARN_ON(wl->vif != vif); + __wl1271_op_remove_interface(wl); + } + mutex_unlock(&wl->mutex); cancel_work_sync(&wl->recovery_work); } From 573c67cf819d52d2e12adf75a9a8cfbd216190a3 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 26 Nov 2010 13:44:59 +0200 Subject: [PATCH 02/15] wl12xx: disable 11a channels when regulatory changes if 11a is not supported Instead of simply not scanning for the 11a channels when not supported by the hardware, disable the channels in reg_notify. This centralizes the decision on whether to scan 5GHz channel in one place and allows userspace to know exactly which channels are in use. Based on Juuso Oikarinen's idea. Cc: Juuso Oikarinen Signed-off-by: Luciano Coelho Reviewed-by: Juuso Oikarinen --- drivers/net/wireless/wl12xx/main.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 35cfcf675795..97eb186b5a8a 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -336,7 +336,9 @@ out: } static int wl1271_reg_notify(struct wiphy *wiphy, - struct regulatory_request *request) { + struct regulatory_request *request) +{ + struct wl1271 *wl = wiphy_to_ieee80211_hw(wiphy)->priv; struct ieee80211_supported_band *band; struct ieee80211_channel *ch; int i; @@ -347,6 +349,11 @@ static int wl1271_reg_notify(struct wiphy *wiphy, if (ch->flags & IEEE80211_CHAN_DISABLED) continue; + if (!wl->enable_11a) { + ch->flags |= IEEE80211_CHAN_DISABLED; + continue; + } + if (ch->flags & IEEE80211_CHAN_RADAR) ch->flags |= IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN; From 2f6724b24525fc989c0707974b23d96b36132385 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Wed, 24 Nov 2010 08:16:57 +0200 Subject: [PATCH 03/15] wl1271: Fix setting of the hardware connection monitoring probe-req template The probe-request template used in the hardware connection monitoring feature thus far has been an empty one, without the SSID IE and without supported rate IEs. This causes problems with some AP's. Additionally, after connected scans, the template for connection maintenance would remain to be the one last used for scanning - potentially incorrect. Fix these by getting a pre-filled directed probe-request template for the associated-to AP from mac80211. Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/cmd.c | 28 +++++++++++++++++++++++ drivers/net/wireless/wl12xx/cmd.h | 2 ++ drivers/net/wireless/wl12xx/main.c | 34 +++++++++++++++++----------- drivers/net/wireless/wl12xx/scan.c | 4 ++++ drivers/net/wireless/wl12xx/wl12xx.h | 3 +++ 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index f3d0541aaad6..8e438e27e496 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -611,6 +611,34 @@ out: return ret; } +struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, + struct sk_buff *skb) +{ + int ret; + + if (!skb) + skb = ieee80211_ap_probereq_get(wl->hw, wl->vif); + if (!skb) + goto out; + + wl1271_dump(DEBUG_SCAN, "AP PROBE REQ: ", skb->data, skb->len); + + if (wl->band == IEEE80211_BAND_2GHZ) + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, + skb->data, skb->len, 0, + wl->conf.tx.basic_rate); + else + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, + skb->data, skb->len, 0, + wl->conf.tx.basic_rate_5); + + if (ret < 0) + wl1271_error("Unable to set ap probe request template."); + +out: + return skb; +} + int wl1271_build_qos_null_data(struct wl1271 *wl) { struct ieee80211_qos_hdr template; diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index 16d1bf814e76..111d112544fc 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -49,6 +49,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid); int wl1271_cmd_build_probe_req(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u8 band); +struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, + struct sk_buff *skb); int wl1271_build_qos_null_data(struct wl1271 *wl); int wl1271_cmd_build_klv_null_data(struct wl1271 *wl); int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 97eb186b5a8a..b2432dab4b51 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -1814,21 +1814,21 @@ out: return ret; } -static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *beacon) +static void wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb, + int offset) { - u8 *ptr = beacon->data + - offsetof(struct ieee80211_mgmt, u.beacon.variable); + u8 *ptr = skb->data + offset; /* find the location of the ssid in the beacon */ - while (ptr < beacon->data + beacon->len) { + while (ptr < skb->data + skb->len) { if (ptr[0] == WLAN_EID_SSID) { wl->ssid_len = ptr[1]; memcpy(wl->ssid, ptr+2, wl->ssid_len); return; } - ptr += ptr[1]; + ptr += (ptr[1] + 2); } - wl1271_error("ad-hoc beacon template has no SSID!\n"); + wl1271_error("No SSID in IEs!\n"); } static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, @@ -1871,8 +1871,11 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, if (beacon) { struct ieee80211_hdr *hdr; + int ieoffset = offsetof(struct ieee80211_mgmt, + u.beacon.variable); + + wl1271_ssid_set(wl, beacon, ieoffset); - wl1271_ssid_set(wl, beacon); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, beacon->data, beacon->len, 0, @@ -1952,6 +1955,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { u32 rates; + int ieoffset; wl->aid = bss_conf->aid; set_assoc = true; @@ -1980,13 +1984,13 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, goto out_sleep; /* - * The SSID is intentionally set to NULL here - the - * firmware will set the probe request with a - * broadcast SSID regardless of what we set in the - * template. + * Get a template for hardware connection maintenance */ - ret = wl1271_cmd_build_probe_req(wl, NULL, 0, - NULL, 0, wl->band); + dev_kfree_skb(wl->probereq); + wl->probereq = wl1271_cmd_build_ap_probe_req(wl, NULL); + ieoffset = offsetof(struct ieee80211_mgmt, + u.probe_req.variable); + wl1271_ssid_set(wl, wl->probereq, ieoffset); /* enable the connection monitoring feature */ ret = wl1271_acx_conn_monit_params(wl, true); @@ -2009,6 +2013,10 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags); wl->aid = 0; + /* free probe-request template */ + dev_kfree_skb(wl->probereq); + wl->probereq = NULL; + /* re-enable dynamic ps - just in case */ ieee80211_enable_dyn_ps(wl->vif); diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index f3f2c5b011ee..6f897b9d90ca 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -51,6 +51,10 @@ void wl1271_scan_complete_work(struct work_struct *work) wl->scan.req = NULL; ieee80211_scan_completed(wl->hw, false); + /* restore hardware connection monitoring template */ + if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) + wl1271_cmd_build_ap_probe_req(wl, wl->probereq); + if (wl->scan.failed) { wl1271_info("Scan completed due to error."); ieee80211_queue_work(wl->hw, &wl->recovery_work); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 3c836e6063e6..9f8aa695c3af 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -431,6 +431,9 @@ struct wl1271 { struct wl1271_scan scan; struct delayed_work scan_complete_work; + /* probe-req template for the current AP */ + struct sk_buff *probereq; + /* Our association ID */ u16 aid; From 7cb2cea9f0f207f819db9823413fa263175b6230 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 24 Nov 2010 12:53:15 +0200 Subject: [PATCH 04/15] wl1271: use debugfs_remove_recursive Documentation/filesystems/debugfs.txt: """ Once upon a time, debugfs users were required to remember the dentry pointer for every debugfs file they created so that all files could be cleaned up. We live in more civilized times now, though, and debugfs users can call: void debugfs_remove_recursive(struct dentry *dentry); """ Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 181 +++++--------------------- drivers/net/wireless/wl12xx/wl12xx.h | 104 +-------------- 2 files changed, 32 insertions(+), 253 deletions(-) diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index dd71b7d2105c..402df14e091b 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -66,19 +66,10 @@ static const struct file_operations name## _ops = { \ }; #define DEBUGFS_ADD(name, parent) \ - wl->debugfs.name = debugfs_create_file(#name, 0400, parent, \ - wl, &name## _ops); \ - if (IS_ERR(wl->debugfs.name)) { \ - ret = PTR_ERR(wl->debugfs.name); \ - wl->debugfs.name = NULL; \ - goto out; \ - } - -#define DEBUGFS_DEL(name) \ - do { \ - debugfs_remove(wl->debugfs.name); \ - wl->debugfs.name = NULL; \ - } while (0) + entry = debugfs_create_file(#name, 0400, parent, \ + wl, &name## _ops); \ + if (!entry || IS_ERR(entry)) \ + goto err; \ #define DEBUGFS_FWSTATS_FILE(sub, name, fmt) \ static ssize_t sub## _ ##name## _read(struct file *file, \ @@ -100,10 +91,7 @@ static const struct file_operations sub## _ ##name## _ops = { \ }; #define DEBUGFS_FWSTATS_ADD(sub, name) \ - DEBUGFS_ADD(sub## _ ##name, wl->debugfs.fw_statistics) - -#define DEBUGFS_FWSTATS_DEL(sub, name) \ - DEBUGFS_DEL(sub## _ ##name) + DEBUGFS_ADD(sub## _ ##name, stats) static void wl1271_debugfs_update_stats(struct wl1271 *wl) { @@ -305,109 +293,16 @@ static const struct file_operations gpio_power_ops = { .llseek = default_llseek, }; -static void wl1271_debugfs_delete_files(struct wl1271 *wl) -{ - DEBUGFS_FWSTATS_DEL(tx, internal_desc_overflow); - - DEBUGFS_FWSTATS_DEL(rx, out_of_mem); - DEBUGFS_FWSTATS_DEL(rx, hdr_overflow); - DEBUGFS_FWSTATS_DEL(rx, hw_stuck); - DEBUGFS_FWSTATS_DEL(rx, dropped); - DEBUGFS_FWSTATS_DEL(rx, fcs_err); - DEBUGFS_FWSTATS_DEL(rx, xfr_hint_trig); - DEBUGFS_FWSTATS_DEL(rx, path_reset); - DEBUGFS_FWSTATS_DEL(rx, reset_counter); - - DEBUGFS_FWSTATS_DEL(dma, rx_requested); - DEBUGFS_FWSTATS_DEL(dma, rx_errors); - DEBUGFS_FWSTATS_DEL(dma, tx_requested); - DEBUGFS_FWSTATS_DEL(dma, tx_errors); - - DEBUGFS_FWSTATS_DEL(isr, cmd_cmplt); - DEBUGFS_FWSTATS_DEL(isr, fiqs); - DEBUGFS_FWSTATS_DEL(isr, rx_headers); - DEBUGFS_FWSTATS_DEL(isr, rx_mem_overflow); - DEBUGFS_FWSTATS_DEL(isr, rx_rdys); - DEBUGFS_FWSTATS_DEL(isr, irqs); - DEBUGFS_FWSTATS_DEL(isr, tx_procs); - DEBUGFS_FWSTATS_DEL(isr, decrypt_done); - DEBUGFS_FWSTATS_DEL(isr, dma0_done); - DEBUGFS_FWSTATS_DEL(isr, dma1_done); - DEBUGFS_FWSTATS_DEL(isr, tx_exch_complete); - DEBUGFS_FWSTATS_DEL(isr, commands); - DEBUGFS_FWSTATS_DEL(isr, rx_procs); - DEBUGFS_FWSTATS_DEL(isr, hw_pm_mode_changes); - DEBUGFS_FWSTATS_DEL(isr, host_acknowledges); - DEBUGFS_FWSTATS_DEL(isr, pci_pm); - DEBUGFS_FWSTATS_DEL(isr, wakeups); - DEBUGFS_FWSTATS_DEL(isr, low_rssi); - - DEBUGFS_FWSTATS_DEL(wep, addr_key_count); - DEBUGFS_FWSTATS_DEL(wep, default_key_count); - /* skipping wep.reserved */ - DEBUGFS_FWSTATS_DEL(wep, key_not_found); - DEBUGFS_FWSTATS_DEL(wep, decrypt_fail); - DEBUGFS_FWSTATS_DEL(wep, packets); - DEBUGFS_FWSTATS_DEL(wep, interrupt); - - DEBUGFS_FWSTATS_DEL(pwr, ps_enter); - DEBUGFS_FWSTATS_DEL(pwr, elp_enter); - DEBUGFS_FWSTATS_DEL(pwr, missing_bcns); - DEBUGFS_FWSTATS_DEL(pwr, wake_on_host); - DEBUGFS_FWSTATS_DEL(pwr, wake_on_timer_exp); - DEBUGFS_FWSTATS_DEL(pwr, tx_with_ps); - DEBUGFS_FWSTATS_DEL(pwr, tx_without_ps); - DEBUGFS_FWSTATS_DEL(pwr, rcvd_beacons); - DEBUGFS_FWSTATS_DEL(pwr, power_save_off); - DEBUGFS_FWSTATS_DEL(pwr, enable_ps); - DEBUGFS_FWSTATS_DEL(pwr, disable_ps); - DEBUGFS_FWSTATS_DEL(pwr, fix_tsf_ps); - /* skipping cont_miss_bcns_spread for now */ - DEBUGFS_FWSTATS_DEL(pwr, rcvd_awake_beacons); - - DEBUGFS_FWSTATS_DEL(mic, rx_pkts); - DEBUGFS_FWSTATS_DEL(mic, calc_failure); - - DEBUGFS_FWSTATS_DEL(aes, encrypt_fail); - DEBUGFS_FWSTATS_DEL(aes, decrypt_fail); - DEBUGFS_FWSTATS_DEL(aes, encrypt_packets); - DEBUGFS_FWSTATS_DEL(aes, decrypt_packets); - DEBUGFS_FWSTATS_DEL(aes, encrypt_interrupt); - DEBUGFS_FWSTATS_DEL(aes, decrypt_interrupt); - - DEBUGFS_FWSTATS_DEL(event, heart_beat); - DEBUGFS_FWSTATS_DEL(event, calibration); - DEBUGFS_FWSTATS_DEL(event, rx_mismatch); - DEBUGFS_FWSTATS_DEL(event, rx_mem_empty); - DEBUGFS_FWSTATS_DEL(event, rx_pool); - DEBUGFS_FWSTATS_DEL(event, oom_late); - DEBUGFS_FWSTATS_DEL(event, phy_transmit_error); - DEBUGFS_FWSTATS_DEL(event, tx_stuck); - - DEBUGFS_FWSTATS_DEL(ps, pspoll_timeouts); - DEBUGFS_FWSTATS_DEL(ps, upsd_timeouts); - DEBUGFS_FWSTATS_DEL(ps, upsd_max_sptime); - DEBUGFS_FWSTATS_DEL(ps, upsd_max_apturn); - DEBUGFS_FWSTATS_DEL(ps, pspoll_max_apturn); - DEBUGFS_FWSTATS_DEL(ps, pspoll_utilization); - DEBUGFS_FWSTATS_DEL(ps, upsd_utilization); - - DEBUGFS_FWSTATS_DEL(rxpipe, rx_prep_beacon_drop); - DEBUGFS_FWSTATS_DEL(rxpipe, descr_host_int_trig_rx_data); - DEBUGFS_FWSTATS_DEL(rxpipe, beacon_buffer_thres_host_int_trig_rx_data); - DEBUGFS_FWSTATS_DEL(rxpipe, missed_beacon_host_int_trig_rx_data); - DEBUGFS_FWSTATS_DEL(rxpipe, tx_xfr_host_int_trig_rx_data); - - DEBUGFS_DEL(tx_queue_len); - DEBUGFS_DEL(retry_count); - DEBUGFS_DEL(excessive_retries); - - DEBUGFS_DEL(gpio_power); -} - static int wl1271_debugfs_add_files(struct wl1271 *wl) { int ret = 0; + struct dentry *entry, *stats; + + stats = debugfs_create_dir("fw-statistics", wl->rootdir); + if (!stats || IS_ERR(stats)) { + entry = stats; + goto err; + } DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow); @@ -500,15 +395,19 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl) DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data); DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data); - DEBUGFS_ADD(tx_queue_len, wl->debugfs.rootdir); - DEBUGFS_ADD(retry_count, wl->debugfs.rootdir); - DEBUGFS_ADD(excessive_retries, wl->debugfs.rootdir); + DEBUGFS_ADD(tx_queue_len, wl->rootdir); + DEBUGFS_ADD(retry_count, wl->rootdir); + DEBUGFS_ADD(excessive_retries, wl->rootdir); - DEBUGFS_ADD(gpio_power, wl->debugfs.rootdir); + DEBUGFS_ADD(gpio_power, wl->rootdir); -out: - if (ret < 0) - wl1271_debugfs_delete_files(wl); + return 0; + +err: + if (IS_ERR(entry)) + ret = PTR_ERR(entry); + else + ret = -ENOMEM; return ret; } @@ -524,23 +423,14 @@ int wl1271_debugfs_init(struct wl1271 *wl) { int ret; - wl->debugfs.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); + wl->rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); - if (IS_ERR(wl->debugfs.rootdir)) { - ret = PTR_ERR(wl->debugfs.rootdir); - wl->debugfs.rootdir = NULL; + if (IS_ERR(wl->rootdir)) { + ret = PTR_ERR(wl->rootdir); + wl->rootdir = NULL; goto err; } - wl->debugfs.fw_statistics = debugfs_create_dir("fw-statistics", - wl->debugfs.rootdir); - - if (IS_ERR(wl->debugfs.fw_statistics)) { - ret = PTR_ERR(wl->debugfs.fw_statistics); - wl->debugfs.fw_statistics = NULL; - goto err_root; - } - wl->stats.fw_stats = kzalloc(sizeof(*wl->stats.fw_stats), GFP_KERNEL); @@ -563,12 +453,8 @@ err_file: wl->stats.fw_stats = NULL; err_fw: - debugfs_remove(wl->debugfs.fw_statistics); - wl->debugfs.fw_statistics = NULL; - -err_root: - debugfs_remove(wl->debugfs.rootdir); - wl->debugfs.rootdir = NULL; + debugfs_remove_recursive(wl->rootdir); + wl->rootdir = NULL; err: return ret; @@ -576,15 +462,10 @@ err: void wl1271_debugfs_exit(struct wl1271 *wl) { - wl1271_debugfs_delete_files(wl); - kfree(wl->stats.fw_stats); wl->stats.fw_stats = NULL; - debugfs_remove(wl->debugfs.fw_statistics); - wl->debugfs.fw_statistics = NULL; - - debugfs_remove(wl->debugfs.rootdir); - wl->debugfs.rootdir = NULL; + debugfs_remove_recursive(wl->rootdir); + wl->rootdir = NULL; } diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 9f8aa695c3af..e904c72e8c8f 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -174,108 +174,6 @@ struct wl1271_stats { unsigned int excessive_retries; }; -struct wl1271_debugfs { - struct dentry *rootdir; - struct dentry *fw_statistics; - - struct dentry *tx_internal_desc_overflow; - - struct dentry *rx_out_of_mem; - struct dentry *rx_hdr_overflow; - struct dentry *rx_hw_stuck; - struct dentry *rx_dropped; - struct dentry *rx_fcs_err; - struct dentry *rx_xfr_hint_trig; - struct dentry *rx_path_reset; - struct dentry *rx_reset_counter; - - struct dentry *dma_rx_requested; - struct dentry *dma_rx_errors; - struct dentry *dma_tx_requested; - struct dentry *dma_tx_errors; - - struct dentry *isr_cmd_cmplt; - struct dentry *isr_fiqs; - struct dentry *isr_rx_headers; - struct dentry *isr_rx_mem_overflow; - struct dentry *isr_rx_rdys; - struct dentry *isr_irqs; - struct dentry *isr_tx_procs; - struct dentry *isr_decrypt_done; - struct dentry *isr_dma0_done; - struct dentry *isr_dma1_done; - struct dentry *isr_tx_exch_complete; - struct dentry *isr_commands; - struct dentry *isr_rx_procs; - struct dentry *isr_hw_pm_mode_changes; - struct dentry *isr_host_acknowledges; - struct dentry *isr_pci_pm; - struct dentry *isr_wakeups; - struct dentry *isr_low_rssi; - - struct dentry *wep_addr_key_count; - struct dentry *wep_default_key_count; - /* skipping wep.reserved */ - struct dentry *wep_key_not_found; - struct dentry *wep_decrypt_fail; - struct dentry *wep_packets; - struct dentry *wep_interrupt; - - struct dentry *pwr_ps_enter; - struct dentry *pwr_elp_enter; - struct dentry *pwr_missing_bcns; - struct dentry *pwr_wake_on_host; - struct dentry *pwr_wake_on_timer_exp; - struct dentry *pwr_tx_with_ps; - struct dentry *pwr_tx_without_ps; - struct dentry *pwr_rcvd_beacons; - struct dentry *pwr_power_save_off; - struct dentry *pwr_enable_ps; - struct dentry *pwr_disable_ps; - struct dentry *pwr_fix_tsf_ps; - /* skipping cont_miss_bcns_spread for now */ - struct dentry *pwr_rcvd_awake_beacons; - - struct dentry *mic_rx_pkts; - struct dentry *mic_calc_failure; - - struct dentry *aes_encrypt_fail; - struct dentry *aes_decrypt_fail; - struct dentry *aes_encrypt_packets; - struct dentry *aes_decrypt_packets; - struct dentry *aes_encrypt_interrupt; - struct dentry *aes_decrypt_interrupt; - - struct dentry *event_heart_beat; - struct dentry *event_calibration; - struct dentry *event_rx_mismatch; - struct dentry *event_rx_mem_empty; - struct dentry *event_rx_pool; - struct dentry *event_oom_late; - struct dentry *event_phy_transmit_error; - struct dentry *event_tx_stuck; - - struct dentry *ps_pspoll_timeouts; - struct dentry *ps_upsd_timeouts; - struct dentry *ps_upsd_max_sptime; - struct dentry *ps_upsd_max_apturn; - struct dentry *ps_pspoll_max_apturn; - struct dentry *ps_pspoll_utilization; - struct dentry *ps_upsd_utilization; - - struct dentry *rxpipe_rx_prep_beacon_drop; - struct dentry *rxpipe_descr_host_int_trig_rx_data; - struct dentry *rxpipe_beacon_buffer_thres_host_int_trig_rx_data; - struct dentry *rxpipe_missed_beacon_host_int_trig_rx_data; - struct dentry *rxpipe_tx_xfr_host_int_trig_rx_data; - - struct dentry *tx_queue_len; - - struct dentry *retry_count; - struct dentry *excessive_retries; - struct dentry *gpio_power; -}; - #define NUM_TX_QUEUES 4 #define NUM_RX_PKT_DESC 8 @@ -478,7 +376,7 @@ struct wl1271 { int last_rssi_event; struct wl1271_stats stats; - struct wl1271_debugfs debugfs; + struct dentry *rootdir; __le32 buffer_32; u32 buffer_cmd; From d60080ae06b98790036104f07fa897cfc151ce12 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Wed, 24 Nov 2010 12:53:16 +0200 Subject: [PATCH 05/15] wl1271: move wl12xx debugfs directory to under wiphy's debugfs Use per-device debugfs path, so multiple devices won't collide. in order to use wl->hw->wiphy->debugfsdir, we have to move the debugfs creation from wl1271_debugfs_init() to wl1271_register_hw(). Reported-by: Johannes Berg Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 3 ++- drivers/net/wireless/wl12xx/main.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 402df14e091b..2ac289e51484 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -423,7 +423,8 @@ int wl1271_debugfs_init(struct wl1271 *wl) { int ret; - wl->rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); + wl->rootdir = debugfs_create_dir(KBUILD_MODNAME, + wl->hw->wiphy->debugfsdir); if (IS_ERR(wl->rootdir)) { ret = PTR_ERR(wl->rootdir); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index b2432dab4b51..7fecefe8d3c1 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2602,6 +2602,8 @@ int wl1271_register_hw(struct wl1271 *wl) wl->mac80211_registered = true; + wl1271_debugfs_init(wl); + register_netdevice_notifier(&wl1271_dev_notifier); wl1271_notice("loaded"); @@ -2736,8 +2738,6 @@ struct ieee80211_hw *wl1271_alloc_hw(void) /* Apply default driver configuration. */ wl1271_conf_init(wl); - wl1271_debugfs_init(wl); - order = get_order(WL1271_AGGR_BUFFER_SIZE); wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order); if (!wl->aggr_buf) { From 43a598d5e40485fcfbebe0700077e83afd803ed5 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Tue, 30 Nov 2010 14:58:46 +0200 Subject: [PATCH 06/15] wl12xx: fix illegal memset if debugfs is not enabled If we try to reset the debugfs statistics when debugfs is not configured in the kernel, we're memset an illegal pointer, because it has never been allocated. So check whether we have debugfs enabled by looking into the wl->rootdir before trying to reset the fw_stats struct. Reported-by: Joerie de Gram Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 2ac289e51484..8106a6c8a1ba 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -414,6 +414,9 @@ err: void wl1271_debugfs_reset(struct wl1271 *wl) { + if (!wl->rootdir) + return; + memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats)); wl->stats.retry_count = 0; wl->stats.excessive_retries = 0; From 870c367cf829466f315de785ac613dd94eff5c50 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 29 Nov 2010 16:24:57 +0200 Subject: [PATCH 07/15] wl1271: Add wl1271_load_firmware() and export some functions For the SDIO testing module we need to load the firmware but not boot it. wl1271_load_firmware() is meant to do just the firmware loading part. We also export some functions so they are usable in the testing module. Signed-off-by: Roger Quadros Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/boot.c | 17 ++++++++++++++++- drivers/net/wireless/wl12xx/boot.h | 1 + drivers/net/wireless/wl12xx/io.c | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 1eafb8175832..4a9f929725fd 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c @@ -467,7 +467,8 @@ static void wl1271_boot_hw_version(struct wl1271 *wl) wl->hw_pg_ver = (s8)fuse; } -int wl1271_boot(struct wl1271 *wl) +/* uploads NVS and firmware */ +int wl1271_load_firmware(struct wl1271 *wl) { int ret = 0; u32 tmp, clk, pause; @@ -572,6 +573,20 @@ int wl1271_boot(struct wl1271 *wl) if (ret < 0) goto out; +out: + return ret; +} +EXPORT_SYMBOL_GPL(wl1271_load_firmware); + +int wl1271_boot(struct wl1271 *wl) +{ + int ret; + + /* upload NVS and firmware */ + ret = wl1271_load_firmware(wl); + if (ret) + return ret; + /* 10.5 start firmware */ ret = wl1271_boot_run_firmware(wl); if (ret < 0) diff --git a/drivers/net/wireless/wl12xx/boot.h b/drivers/net/wireless/wl12xx/boot.h index c7d771959f3a..d67dcffa31eb 100644 --- a/drivers/net/wireless/wl12xx/boot.h +++ b/drivers/net/wireless/wl12xx/boot.h @@ -27,6 +27,7 @@ #include "wl12xx.h" int wl1271_boot(struct wl1271 *wl); +int wl1271_load_firmware(struct wl1271 *wl); #define WL1271_NO_SUBBANDS 8 #define WL1271_NO_POWER_LEVELS 4 diff --git a/drivers/net/wireless/wl12xx/io.c b/drivers/net/wireless/wl12xx/io.c index 35c2f1aca6ba..d557f73e7c19 100644 --- a/drivers/net/wireless/wl12xx/io.c +++ b/drivers/net/wireless/wl12xx/io.c @@ -113,6 +113,7 @@ int wl1271_set_partition(struct wl1271 *wl, return 0; } +EXPORT_SYMBOL_GPL(wl1271_set_partition); void wl1271_io_reset(struct wl1271 *wl) { From 1036dc169f4cc6e5b753b1596d285d1cc3311a23 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Mon, 29 Nov 2010 12:05:53 +0200 Subject: [PATCH 08/15] wl12xx: Remove 11j channels from the supported channels list. Because we don't support them at this stage. Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 7fecefe8d3c1..dc3a09319d12 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2374,14 +2374,6 @@ static struct ieee80211_rate wl1271_rates_5ghz[] = { /* 5 GHz band channels for WL1273 */ static struct ieee80211_channel wl1271_channels_5ghz[] = { - { .hw_value = 183, .center_freq = 4915}, - { .hw_value = 184, .center_freq = 4920}, - { .hw_value = 185, .center_freq = 4925}, - { .hw_value = 187, .center_freq = 4935}, - { .hw_value = 188, .center_freq = 4940}, - { .hw_value = 189, .center_freq = 4945}, - { .hw_value = 192, .center_freq = 4960}, - { .hw_value = 196, .center_freq = 4980}, { .hw_value = 7, .center_freq = 5035}, { .hw_value = 8, .center_freq = 5040}, { .hw_value = 9, .center_freq = 5045}, From fb6a6819fad0d71b47577a51709440a9f8441f0a Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Fri, 3 Dec 2010 17:05:40 +0200 Subject: [PATCH 09/15] wl12xx: disable 11a channels when wl->enable_11a is known Disabling the 11a channels when not supported in the reg_notify function was not working as it should, because when the driver is initiailizing (and registering itself with mac80211), it would get the reg notification too early. At that point the driver wouldn't have received the NVS yet, so it wouldn't know whether 11a was supported. To fix this, we disable 11a channels when we read the NVS instead. Also, it is easier (and still safe) to set n_channels to zero instead of setting the disabled flag on every 11a channel. Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/main.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index dc3a09319d12..0b79c49cd877 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -338,7 +338,6 @@ out: static int wl1271_reg_notify(struct wiphy *wiphy, struct regulatory_request *request) { - struct wl1271 *wl = wiphy_to_ieee80211_hw(wiphy)->priv; struct ieee80211_supported_band *band; struct ieee80211_channel *ch; int i; @@ -349,11 +348,6 @@ static int wl1271_reg_notify(struct wiphy *wiphy, if (ch->flags & IEEE80211_CHAN_DISABLED) continue; - if (!wl->enable_11a) { - ch->flags |= IEEE80211_CHAN_DISABLED; - continue; - } - if (ch->flags & IEEE80211_CHAN_RADAR) ch->flags |= IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN; @@ -1071,6 +1065,16 @@ power_off: strncpy(wiphy->fw_version, wl->chip.fw_ver, sizeof(wiphy->fw_version)); + /* + * Now we know if 11a is supported (info from the NVS), so disable + * 11a channels if not supported + */ + if (!wl->enable_11a) + wiphy->bands[IEEE80211_BAND_5GHZ]->n_channels = 0; + + wl1271_debug(DEBUG_MAC80211, "11a is %ssupported", + wl->enable_11a ? "" : "not "); + out: mutex_unlock(&wl->mutex); From b69eb80bf7a6922fef8056d42b06124a7de31501 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 1 Dec 2010 11:58:54 +0200 Subject: [PATCH 10/15] wl1271_sdio_test: Add module for sdio RX/TX testing This module enables individually generating RX and TX traffic over the SDIO bus on which the WL1271 chipset is connected. This is required to perform RF interference testing. The module takes 2 module parameters 'rx' and 'tx'. To generate RX traffic: modprobe wl1271_sdio_test rx=1 To generate TX traffic: modprobe wl1271_sdio_test tx=1 To generate both RX & TX traffic, set both rx and tx to 1. You can change the testing configuration at runtime by changing the rx & tx values at /sys/modules/wl1271_sdio_test/ To stop testing simply unload the module. Signed-off-by: Roger Quadros Reviewed-by: Carlos Chinea Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/Kconfig | 8 + drivers/net/wireless/wl12xx/Makefile | 2 + .../net/wireless/wl12xx/wl1271_sdio_test.c | 510 ++++++++++++++++++ 3 files changed, 520 insertions(+) create mode 100644 drivers/net/wireless/wl12xx/wl1271_sdio_test.c diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig index d2adeb1f72b7..085bc44d814b 100644 --- a/drivers/net/wireless/wl12xx/Kconfig +++ b/drivers/net/wireless/wl12xx/Kconfig @@ -52,6 +52,14 @@ config WL12XX_SDIO If you choose to build a module, it'll be called wl12xx_sdio. Say N if unsure. +config WL1271_SDIO_TEST + tristate "TI wl1271 SDIO testing support" + depends on WL1271 && MMC + ---help--- + This module adds support for the SDIO bus testing with the + TI wl1271 chipset. Select this if your platform is using + the SDIO bus. + config WL12XX_PLATFORM_DATA bool depends on WL12XX_SDIO != n || WL1251_SDIO != n diff --git a/drivers/net/wireless/wl12xx/Makefile b/drivers/net/wireless/wl12xx/Makefile index 005a758174d9..187678503887 100644 --- a/drivers/net/wireless/wl12xx/Makefile +++ b/drivers/net/wireless/wl12xx/Makefile @@ -9,5 +9,7 @@ obj-$(CONFIG_WL12XX) += wl12xx.o obj-$(CONFIG_WL12XX_SPI) += wl12xx_spi.o obj-$(CONFIG_WL12XX_SDIO) += wl12xx_sdio.o +obj-$(CONFIG_WL1271_SDIO_TEST) += wl1271_sdio_test.o + # small builtin driver bit obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx_platform_data.o diff --git a/drivers/net/wireless/wl12xx/wl1271_sdio_test.c b/drivers/net/wireless/wl12xx/wl1271_sdio_test.c new file mode 100644 index 000000000000..42d13144f645 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_sdio_test.c @@ -0,0 +1,510 @@ +/* + * wl1271_sdio_test.c - SDIO testing driver for wl1271 + * + * Copyright (C) 2010 Nokia Corporation + * + * Contact: Roger Quadros + * + * wl1271 read/write routines taken from wl1271_sdio.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wl1271.h" +#include "wl1271_io.h" +#include "wl1271_boot.h" + +#ifndef SDIO_VENDOR_ID_TI +#define SDIO_VENDOR_ID_TI 0x0097 +#endif + +#ifndef SDIO_DEVICE_ID_TI_WL1271 +#define SDIO_DEVICE_ID_TI_WL1271 0x4076 +#endif + +static bool rx, tx; + +module_param(rx, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(rx, "Perform rx test. Default (0). " + "This test continuously reads data from the SDIO device.\n"); + +module_param(tx, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(tx, "Perform tx test. Default (0). " + "This test continuously writes data to the SDIO device.\n"); + +struct wl1271_test { + struct wl1271 wl; + struct task_struct *test_task; +}; + +static const struct sdio_device_id wl1271_devices[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) }, + {} +}; + +static inline struct sdio_func *wl_to_func(struct wl1271 *wl) +{ + return wl->if_priv; +} + +static struct device *wl1271_sdio_wl_to_dev(struct wl1271 *wl) +{ + return &(wl_to_func(wl)->dev); +} + +static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) +{ + int ret = 0; + struct sdio_func *func = wl_to_func(wl); + + if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { + ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret); + wl1271_debug(DEBUG_SDIO, "sdio read 52 addr 0x%x, byte 0x%02x", + addr, ((u8 *)buf)[0]); + } else { + if (fixed) + ret = sdio_readsb(func, buf, addr, len); + else + ret = sdio_memcpy_fromio(func, buf, addr, len); + + wl1271_debug(DEBUG_SDIO, "sdio read 53 addr 0x%x, %zu bytes", + addr, len); + wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len); + } + + if (ret) + wl1271_error("sdio read failed (%d)", ret); +} + +static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) +{ + int ret = 0; + struct sdio_func *func = wl_to_func(wl); + + if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { + sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret); + wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x", + addr, ((u8 *)buf)[0]); + } else { + wl1271_debug(DEBUG_SDIO, "sdio write 53 addr 0x%x, %zu bytes", + addr, len); + wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len); + + if (fixed) + ret = sdio_writesb(func, addr, buf, len); + else + ret = sdio_memcpy_toio(func, addr, buf, len); + } + if (ret) + wl1271_error("sdio write failed (%d)", ret); + +} + +static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable) +{ + struct sdio_func *func = wl_to_func(wl); + + /* Let the SDIO stack handle wlan_enable control, so we + * keep host claimed while wlan is in use to keep wl1271 + * alive. + */ + if (enable) { + sdio_claim_power(func); + sdio_claim_host(func); + sdio_enable_func(func); + } else { + sdio_disable_func(func); + sdio_release_host(func); + sdio_release_power(func); + } + + return 0; +} + +static void wl1271_sdio_disable_interrupts(struct wl1271 *wl) +{ +} + +static void wl1271_sdio_enable_interrupts(struct wl1271 *wl) +{ +} + + +static struct wl1271_if_operations sdio_ops = { + .read = wl1271_sdio_raw_read, + .write = wl1271_sdio_raw_write, + .power = wl1271_sdio_set_power, + .dev = wl1271_sdio_wl_to_dev, + .enable_irq = wl1271_sdio_enable_interrupts, + .disable_irq = wl1271_sdio_disable_interrupts, +}; + +static void wl1271_fw_wakeup(struct wl1271 *wl) +{ + u32 elp_reg; + + elp_reg = ELPCTRL_WAKE_UP; + wl1271_raw_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg); +} + +static int wl1271_fetch_firmware(struct wl1271 *wl) +{ + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, WL1271_FW_NAME, wl1271_wl_to_dev(wl)); + + if (ret < 0) { + wl1271_error("could not get firmware: %d", ret); + return ret; + } + + if (fw->size % 4) { + wl1271_error("firmware size is not multiple of 32 bits: %zu", + fw->size); + ret = -EILSEQ; + goto out; + } + + wl->fw_len = fw->size; + wl->fw = vmalloc(wl->fw_len); + + if (!wl->fw) { + wl1271_error("could not allocate memory for the firmware"); + ret = -ENOMEM; + goto out; + } + + memcpy(wl->fw, fw->data, wl->fw_len); + + ret = 0; + +out: + release_firmware(fw); + + return ret; +} + +static int wl1271_fetch_nvs(struct wl1271 *wl) +{ + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, WL1271_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); + + if (!wl->nvs) { + wl1271_error("could not allocate memory for the nvs file"); + ret = -ENOMEM; + goto out; + } + + wl->nvs_len = fw->size; + +out: + release_firmware(fw); + + return ret; +} + +static int wl1271_chip_wakeup(struct wl1271 *wl) +{ + struct wl1271_partition_set partition; + int ret; + + msleep(WL1271_PRE_POWER_ON_SLEEP); + ret = wl1271_power_on(wl); + if (ret) + return ret; + + msleep(WL1271_POWER_ON_SLEEP); + + /* We don't need a real memory partition here, because we only want + * to use the registers at this point. */ + memset(&partition, 0, sizeof(partition)); + partition.reg.start = REGISTERS_BASE; + partition.reg.size = REGISTERS_DOWN_SIZE; + wl1271_set_partition(wl, &partition); + + /* ELP module wake up */ + wl1271_fw_wakeup(wl); + + /* whal_FwCtrl_BootSm() */ + + /* 0. read chip id from CHIP_ID */ + wl->chip.id = wl1271_read32(wl, CHIP_ID_B); + + /* 1. check if chip id is valid */ + + switch (wl->chip.id) { + case CHIP_ID_1271_PG10: + wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete", + wl->chip.id); + break; + case CHIP_ID_1271_PG20: + wl1271_notice("chip id 0x%x (1271 PG20)", + wl->chip.id); + break; + default: + wl1271_warning("unsupported chip id: 0x%x", wl->chip.id); + return -ENODEV; + } + + return ret; +} + +static struct wl1271_partition_set part_down = { + .mem = { + .start = 0x00000000, + .size = 0x000177c0 + }, + .reg = { + .start = REGISTERS_BASE, + .size = 0x00008800 + }, + .mem2 = { + .start = 0x00000000, + .size = 0x00000000 + }, + .mem3 = { + .start = 0x00000000, + .size = 0x00000000 + }, +}; + +static int tester(void *data) +{ + struct wl1271 *wl = data; + struct sdio_func *func = wl_to_func(wl); + struct device *pdev = &func->dev; + int ret = 0; + bool rx_started = 0; + bool tx_started = 0; + uint8_t *tx_buf, *rx_buf; + int test_size = PAGE_SIZE; + u32 addr = 0; + struct wl1271_partition_set partition; + + /* We assume chip is powered up and firmware fetched */ + + memcpy(&partition, &part_down, sizeof(partition)); + partition.mem.start = addr; + wl1271_set_partition(wl, &partition); + + tx_buf = kmalloc(test_size, GFP_KERNEL); + rx_buf = kmalloc(test_size, GFP_KERNEL); + if (!tx_buf || !rx_buf) { + dev_err(pdev, + "Could not allocate memory. Test will not run.\n"); + ret = -ENOMEM; + goto free; + } + + memset(tx_buf, 0x5a, test_size); + + /* write something in data area so we can read it back */ + wl1271_write(wl, addr, tx_buf, test_size, false); + + while (!kthread_should_stop()) { + if (rx && !rx_started) { + dev_info(pdev, "starting rx test\n"); + rx_started = 1; + } else if (!rx && rx_started) { + dev_info(pdev, "stopping rx test\n"); + rx_started = 0; + } + + if (tx && !tx_started) { + dev_info(pdev, "starting tx test\n"); + tx_started = 1; + } else if (!tx && tx_started) { + dev_info(pdev, "stopping tx test\n"); + tx_started = 0; + } + + if (rx_started) + wl1271_read(wl, addr, rx_buf, test_size, false); + + if (tx_started) + wl1271_write(wl, addr, tx_buf, test_size, false); + + if (!rx_started && !tx_started) + msleep(100); + } + +free: + kfree(tx_buf); + kfree(rx_buf); + return ret; +} + +static int __devinit wl1271_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + const struct wl12xx_platform_data *wlan_data; + struct wl1271 *wl; + struct wl1271_test *wl_test; + int ret = 0; + + /* wl1271 has 2 sdio functions we handle just the wlan part */ + if (func->num != 0x02) + return -ENODEV; + + wl_test = kzalloc(sizeof(struct wl1271_test), GFP_KERNEL); + if (!wl_test) { + dev_err(&func->dev, "Could not allocate memory\n"); + return -ENOMEM; + } + + wl = &wl_test->wl; + + wl->if_priv = func; + wl->if_ops = &sdio_ops; + + /* Grab access to FN0 for ELP reg. */ + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; + + wlan_data = wl12xx_get_platform_data(); + if (IS_ERR(wlan_data)) { + ret = PTR_ERR(wlan_data); + dev_err(&func->dev, "missing wlan platform data: %d\n", ret); + goto out_free; + } + + wl->irq = wlan_data->irq; + wl->ref_clock = wlan_data->board_ref_clock; + + sdio_set_drvdata(func, wl_test); + + + /* power up the device */ + ret = wl1271_chip_wakeup(wl); + if (ret) { + dev_err(&func->dev, "could not wake up chip\n"); + goto out_free; + } + + if (wl->fw == NULL) { + ret = wl1271_fetch_firmware(wl); + if (ret < 0) { + dev_err(&func->dev, "firmware fetch error\n"); + goto out_off; + } + } + + /* fetch NVS */ + if (wl->nvs == NULL) { + ret = wl1271_fetch_nvs(wl); + if (ret < 0) { + dev_err(&func->dev, "NVS fetch error\n"); + goto out_off; + } + } + + ret = wl1271_load_firmware(wl); + if (ret < 0) { + dev_err(&func->dev, "firmware load error: %d\n", ret); + goto out_free; + } + + dev_info(&func->dev, "initialized\n"); + + /* I/O testing will be done in the tester thread */ + + wl_test->test_task = kthread_run(tester, wl, "sdio_tester"); + if (IS_ERR(wl_test->test_task)) { + dev_err(&func->dev, "unable to create kernel thread\n"); + ret = PTR_ERR(wl_test->test_task); + goto out_free; + } + + return 0; + +out_off: + /* power off the chip */ + wl1271_power_off(wl); + +out_free: + kfree(wl_test); + return ret; +} + +static void __devexit wl1271_remove(struct sdio_func *func) +{ + struct wl1271_test *wl_test = sdio_get_drvdata(func); + + /* stop the I/O test thread */ + kthread_stop(wl_test->test_task); + + /* power off the chip */ + wl1271_power_off(&wl_test->wl); + + vfree(wl_test->wl.fw); + wl_test->wl.fw = NULL; + kfree(wl_test->wl.nvs); + wl_test->wl.nvs = NULL; + + kfree(wl_test); +} + +static struct sdio_driver wl1271_sdio_driver = { + .name = "wl1271_sdio_test", + .id_table = wl1271_devices, + .probe = wl1271_probe, + .remove = __devexit_p(wl1271_remove), +}; + +static int __init wl1271_init(void) +{ + int ret; + + ret = sdio_register_driver(&wl1271_sdio_driver); + if (ret < 0) + pr_err("failed to register sdio driver: %d\n", ret); + + return ret; +} +module_init(wl1271_init); + +static void __exit wl1271_exit(void) +{ + sdio_unregister_driver(&wl1271_sdio_driver); +} +module_exit(wl1271_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Roger Quadros "); + From c5312772156bb5f9b2e95e4c91526d578426a069 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 9 Dec 2010 11:31:27 +0200 Subject: [PATCH 11/15] wl12xx: add auto-arp support The auto-arp feature of wl12xx allows the firmware to automatically response to arp requests asking for its ip. in order to use it, we configure the arp response template and enable the corresponding bit in wl1271_acx_arp_filter (along with passing its ip) Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/acx.c | 4 +-- drivers/net/wireless/wl12xx/acx.h | 9 +++-- drivers/net/wireless/wl12xx/cmd.c | 41 ++++++++++++++++++++++ drivers/net/wireless/wl12xx/cmd.h | 2 ++ drivers/net/wireless/wl12xx/init.c | 7 ++++ drivers/net/wireless/wl12xx/main.c | 24 ++++++++++--- drivers/net/wireless/wl12xx/wl12xx_80211.h | 14 ++++++++ 7 files changed, 93 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 7cbaeb6d2a37..cc4068d2b4a8 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1041,7 +1041,7 @@ out: return ret; } -int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, __be32 address) +int wl1271_acx_arp_ip_filter(struct wl1271 *wl, u8 enable, __be32 address) { struct wl1271_acx_arp_filter *acx; int ret; @@ -1057,7 +1057,7 @@ int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, __be32 address) acx->version = ACX_IPV4_VERSION; acx->enable = enable; - if (enable == true) + if (enable) memcpy(acx->address, &address, ACX_IPV4_ADDR_SIZE); ret = wl1271_cmd_configure(wl, ACX_ARP_IP_FILTER, diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 75a6306ff554..9cbc3f40c8dd 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -868,10 +868,15 @@ struct wl1271_acx_bet_enable { #define ACX_IPV4_VERSION 4 #define ACX_IPV6_VERSION 6 #define ACX_IPV4_ADDR_SIZE 4 + +/* bitmap of enabled arp_filter features */ +#define ACX_ARP_FILTER_ARP_FILTERING BIT(0) +#define ACX_ARP_FILTER_AUTO_ARP BIT(1) + struct wl1271_acx_arp_filter { struct acx_header header; u8 version; /* ACX_IPV4_VERSION, ACX_IPV6_VERSION */ - u8 enable; /* 1 to enable ARP filtering, 0 to disable */ + u8 enable; /* bitmap of enabled ARP filtering features */ u8 padding[2]; u8 address[16]; /* The configured device IP address - all ARP requests directed to this IP address will pass @@ -1168,7 +1173,7 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl); int wl1271_acx_init_rx_interrupt(struct wl1271 *wl); int wl1271_acx_smart_reflex(struct wl1271 *wl); int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable); -int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, __be32 address); +int wl1271_acx_arp_ip_filter(struct wl1271 *wl, u8 enable, __be32 address); int wl1271_acx_pm_config(struct wl1271 *wl); int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable); int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid); diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 8e438e27e496..0106628aa5a2 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -639,6 +639,47 @@ out: return skb; } +int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr) +{ + int ret; + struct wl12xx_arp_rsp_template tmpl; + struct ieee80211_hdr_3addr *hdr; + struct arphdr *arp_hdr; + + memset(&tmpl, 0, sizeof(tmpl)); + + /* mac80211 header */ + hdr = &tmpl.hdr; + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_DATA | + IEEE80211_FCTL_TODS); + memcpy(hdr->addr1, wl->vif->bss_conf.bssid, ETH_ALEN); + memcpy(hdr->addr2, wl->vif->addr, ETH_ALEN); + memset(hdr->addr3, 0xff, ETH_ALEN); + + /* llc layer */ + memcpy(tmpl.llc_hdr, rfc1042_header, sizeof(rfc1042_header)); + tmpl.llc_type = htons(ETH_P_ARP); + + /* arp header */ + arp_hdr = &tmpl.arp_hdr; + arp_hdr->ar_hrd = htons(ARPHRD_ETHER); + arp_hdr->ar_pro = htons(ETH_P_IP); + arp_hdr->ar_hln = ETH_ALEN; + arp_hdr->ar_pln = 4; + arp_hdr->ar_op = htons(ARPOP_REPLY); + + /* arp payload */ + memcpy(tmpl.sender_hw, wl->vif->addr, ETH_ALEN); + tmpl.sender_ip = ip_addr; + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_ARP_RSP, + &tmpl, sizeof(tmpl), 0, + wl->basic_rate); + + return ret; +} + int wl1271_build_qos_null_data(struct wl1271 *wl) { struct ieee80211_qos_hdr template; diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index 111d112544fc..2a1d9db7ceb8 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -51,6 +51,7 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl, const u8 *ie, size_t ie_len, u8 band); struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, struct sk_buff *skb); +int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, __be32 ip_addr); int wl1271_build_qos_null_data(struct wl1271 *wl); int wl1271_cmd_build_klv_null_data(struct wl1271 *wl); int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id); @@ -124,6 +125,7 @@ enum cmd_templ { CMD_TEMPL_CTS, /* * For CTS-to-self (FastCTS) mechanism * for BT/WLAN coexistence (SoftGemini). */ + CMD_TEMPL_ARP_RSP, CMD_TEMPL_MAX = 0xff }; diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 7949d346aadb..0392e37f0d66 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -102,6 +102,13 @@ int wl1271_init_templates_config(struct wl1271 *wl) if (ret < 0) return ret; + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_ARP_RSP, NULL, + sizeof + (struct wl12xx_arp_rsp_template), + 0, WL1271_RATE_AUTOMATIC); + if (ret < 0) + return ret; + for (i = 0; i < CMD_TEMPL_KLV_IDX_MAX; i++) { ret = wl1271_cmd_template_set(wl, CMD_TEMPL_KLV, NULL, WL1271_CMD_TEMPL_MAX_SIZE, i, diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 0b79c49cd877..f7d7cad730a2 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2110,10 +2110,26 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, __be32 addr = bss_conf->arp_addr_list[0]; WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS); - if (bss_conf->arp_addr_cnt == 1 && bss_conf->arp_filter_enabled) - ret = wl1271_acx_arp_ip_filter(wl, true, addr); - else - ret = wl1271_acx_arp_ip_filter(wl, false, addr); + if (bss_conf->arp_addr_cnt == 1 && + bss_conf->arp_filter_enabled) { + /* + * The template should have been configured only upon + * association. however, it seems that the correct ip + * isn't being set (when sending), so we have to + * reconfigure the template upon every ip change. + */ + ret = wl1271_cmd_build_arp_rsp(wl, addr); + if (ret < 0) { + wl1271_warning("build arp rsp failed: %d", ret); + goto out_sleep; + } + + ret = wl1271_acx_arp_ip_filter(wl, + (ACX_ARP_FILTER_ARP_FILTERING | + ACX_ARP_FILTER_AUTO_ARP), + addr); + } else + ret = wl1271_acx_arp_ip_filter(wl, 0, addr); if (ret < 0) goto out_sleep; diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h index 184628027213..8ee0d3a8fa6e 100644 --- a/drivers/net/wireless/wl12xx/wl12xx_80211.h +++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h @@ -2,6 +2,7 @@ #define __WL12XX_80211_H__ #include /* ETH_ALEN */ +#include /* RATES */ #define IEEE80211_CCK_RATE_1MB 0x02 @@ -140,6 +141,19 @@ struct wl12xx_probe_req_template { struct wl12xx_ie_rates ext_rates; } __packed; +struct wl12xx_arp_rsp_template { + struct ieee80211_hdr_3addr hdr; + + u8 llc_hdr[sizeof(rfc1042_header)]; + u16 llc_type; + + struct arphdr arp_hdr; + u8 sender_hw[ETH_ALEN]; + u32 sender_ip; + u8 target_hw[ETH_ALEN]; + u32 target_ip; +} __packed; + struct wl12xx_probe_resp_template { struct ieee80211_header header; From ea559b460509b241cc1a3f36eebe0b2b634b3cf2 Mon Sep 17 00:00:00 2001 From: Guy Eilam Date: Thu, 9 Dec 2010 16:54:59 +0200 Subject: [PATCH 12/15] wl1271: fixed problem with WPS IEs in probe requests Inclusion of a WPS IE in probe requests caused a problem in the driver due to the maximum size of the probe request template and the max_scan_ie_len values at initialization. Increased the size of probe request template to the maximum size allowed by the firmware. Struct wl12xx_probe_req_template, which was only used for calculating the max size of the probe request template, is no longer used and needed. max_scan_ie_len is used for validating the size of additional IEs in scan requests. Initialized the max_scan_ie_len field to the maximum size of the probe request template minus the ieee80211 header size. Signed-off-by: Guy Eilam Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/init.c | 6 ++---- drivers/net/wireless/wl12xx/main.c | 7 +++++++ drivers/net/wireless/wl12xx/wl12xx_80211.h | 7 ------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 0392e37f0d66..785a5304bfc4 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -53,18 +53,16 @@ static int wl1271_init_hwenc_config(struct wl1271 *wl) int wl1271_init_templates_config(struct wl1271 *wl) { int ret, i; - size_t size; /* send empty templates for fw memory reservation */ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, - sizeof(struct wl12xx_probe_req_template), + WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; - size = sizeof(struct wl12xx_probe_req_template); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, - NULL, size, 0, + NULL, WL1271_CMD_TEMPL_MAX_SIZE, 0, WL1271_RATE_AUTOMATIC); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index f7d7cad730a2..0865585c8a75 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2666,6 +2666,13 @@ int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); wl->hw->wiphy->max_scan_ssids = 1; + /* + * Maximum length of elements in scanning probe request templates + * should be the maximum length possible for a template, without + * the IEEE80211 header of the template + */ + wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - + sizeof(struct ieee80211_header); wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz; wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz; diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h index 8ee0d3a8fa6e..be21032f4dc1 100644 --- a/drivers/net/wireless/wl12xx/wl12xx_80211.h +++ b/drivers/net/wireless/wl12xx/wl12xx_80211.h @@ -134,13 +134,6 @@ struct wl12xx_qos_null_data_template { __le16 qos_ctl; } __packed; -struct wl12xx_probe_req_template { - struct ieee80211_header header; - struct wl12xx_ie_ssid ssid; - struct wl12xx_ie_rates rates; - struct wl12xx_ie_rates ext_rates; -} __packed; - struct wl12xx_arp_rsp_template { struct ieee80211_hdr_3addr hdr; From 17c1755c24d83f9fd0509b64c76cc43fc60cc642 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Sun, 12 Dec 2010 12:15:35 +0200 Subject: [PATCH 13/15] wl12xx: allow runtime changing of debug_level Currently, the debug level is set in compilation time (by the DEBUG_LEVEL const). This method has the advantage of compiling only the relevant messages, while optimizing out the unused ones. In order to allow runtime control over the debug_level, while optimizing out messages when debug messages are not needed, we combine some methods: 1. use dynamic_debug (pr_debug) rather then printk. 2. add debug_level module param in order to set debug level during insmod. 3. add debug_level sysfs file in order to allow dynamic control over the debug level. Since patches for pr_debug_hex_dump() implementation haven't been applied yet, we are still temporarly using print_hex_dump(). Signed-off-by: Eliad Peller Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 5 +++++ drivers/net/wireless/wl12xx/main.c | 5 +++++ drivers/net/wireless/wl12xx/wl12xx.h | 19 ++++++++++--------- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index 8106a6c8a1ba..c2cd58074372 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -401,6 +401,11 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl) DEBUGFS_ADD(gpio_power, wl->rootdir); + entry = debugfs_create_x32("debug_level", 0600, wl->rootdir, + &wl12xx_debug_level); + if (!entry || IS_ERR(entry)) + goto err; + return 0; err: diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 0865585c8a75..8c50d3b3fabb 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -2833,6 +2833,11 @@ int wl1271_free_hw(struct wl1271 *wl) } EXPORT_SYMBOL_GPL(wl1271_free_hw); +u32 wl12xx_debug_level; +EXPORT_SYMBOL_GPL(wl12xx_debug_level); +module_param_named(debug_level, wl12xx_debug_level, uint, DEBUG_NONE); +MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho "); MODULE_AUTHOR("Juuso Oikarinen "); diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index e904c72e8c8f..07c2297b89a2 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -60,31 +60,32 @@ enum { DEBUG_ALL = ~0, }; -#define DEBUG_LEVEL (DEBUG_NONE) +extern u32 wl12xx_debug_level; #define DEBUG_DUMP_LIMIT 1024 #define wl1271_error(fmt, arg...) \ - printk(KERN_ERR DRIVER_PREFIX "ERROR " fmt "\n", ##arg) + pr_err(DRIVER_PREFIX "ERROR " fmt "\n", ##arg) #define wl1271_warning(fmt, arg...) \ - printk(KERN_WARNING DRIVER_PREFIX "WARNING " fmt "\n", ##arg) + pr_warning(DRIVER_PREFIX "WARNING " fmt "\n", ##arg) #define wl1271_notice(fmt, arg...) \ - printk(KERN_INFO DRIVER_PREFIX fmt "\n", ##arg) + pr_info(DRIVER_PREFIX fmt "\n", ##arg) #define wl1271_info(fmt, arg...) \ - printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg) + pr_info(DRIVER_PREFIX fmt "\n", ##arg) #define wl1271_debug(level, fmt, arg...) \ do { \ - if (level & DEBUG_LEVEL) \ - printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg); \ + if (level & wl12xx_debug_level) \ + pr_debug(DRIVER_PREFIX fmt "\n", ##arg); \ } while (0) +/* TODO: use pr_debug_hex_dump when it will be available */ #define wl1271_dump(level, prefix, buf, len) \ do { \ - if (level & DEBUG_LEVEL) \ + if (level & wl12xx_debug_level) \ print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \ DUMP_PREFIX_OFFSET, 16, 1, \ buf, \ @@ -94,7 +95,7 @@ enum { #define wl1271_dump_ascii(level, prefix, buf, len) \ do { \ - if (level & DEBUG_LEVEL) \ + if (level & wl12xx_debug_level) \ print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \ DUMP_PREFIX_OFFSET, 16, 1, \ buf, \ From 6742f554db14da94172da9eb1875a1aa944a827f Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Mon, 13 Dec 2010 09:52:37 +0200 Subject: [PATCH 14/15] wl12xx: Change TX queue to be per AC With the current single-queue implementation traffic priorization is not working correctly - when using multiple BE streams and one, say VI stream, the VI stream will share bandwidth almost equally with the BE streams. To fix the issue, implement per AC queues, which are emptied in priority order to the firmware. To keep it relatively simple, maintain a global buffer count and global queue stop/wake instead of per-AC. With these changes, priorization appears to work just fine. Signed-off-by: Juuso Oikarinen Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/debugfs.c | 2 +- drivers/net/wireless/wl12xx/main.c | 12 ++++-- drivers/net/wireless/wl12xx/tx.c | 60 ++++++++++++++++++++++----- drivers/net/wireless/wl12xx/wl12xx.h | 3 +- 4 files changed, 60 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index c2cd58074372..ec6077760157 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -225,7 +225,7 @@ static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf, char buf[20]; int res; - queue_len = skb_queue_len(&wl->tx_queue); + queue_len = wl->tx_queue_count; res = scnprintf(buf, sizeof(buf), "%u\n", queue_len); return simple_read_from_buffer(userbuf, count, ppos, buf, res); diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 8c50d3b3fabb..062247ef3ad2 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -570,7 +570,7 @@ static void wl1271_irq_work(struct work_struct *work) /* Check if any tx blocks were freed */ if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags) && - !skb_queue_empty(&wl->tx_queue)) { + wl->tx_queue_count) { /* * In order to avoid starvation of the TX path, * call the work function directly. @@ -891,6 +891,7 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb); struct ieee80211_sta *sta = txinfo->control.sta; unsigned long flags; + int q; /* * peek into the rates configured in the STA entry. @@ -918,10 +919,12 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) set_bit(WL1271_FLAG_STA_RATES_CHANGED, &wl->flags); } #endif + wl->tx_queue_count++; spin_unlock_irqrestore(&wl->wl_lock, flags); /* queue the packet */ - skb_queue_tail(&wl->tx_queue, skb); + q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); + skb_queue_tail(&wl->tx_queue[q], skb); /* * The chip specific setup must run before the first TX packet - @@ -935,7 +938,7 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * The workqueue is slow to process the tx_queue and we need stop * the queue here, otherwise the queue will get too long. */ - if (skb_queue_len(&wl->tx_queue) >= WL1271_TX_QUEUE_HIGH_WATERMARK) { + if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) { wl1271_debug(DEBUG_TX, "op_tx: stopping queues"); spin_lock_irqsave(&wl->wl_lock, flags); @@ -2719,7 +2722,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->hw = hw; wl->plat_dev = plat_dev; - skb_queue_head_init(&wl->tx_queue); + for (i = 0; i < NUM_TX_QUEUES; i++) + skb_queue_head_init(&wl->tx_queue[i]); INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work); INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work); diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index d332b3f6d0fa..b44c75cd8c1e 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -125,7 +125,6 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, /* queue (we use same identifiers for tid's and ac's */ ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); desc->tid = ac; - desc->aid = TX_HW_DEFAULT_AID; desc->reserved = 0; @@ -228,7 +227,7 @@ static void handle_tx_low_watermark(struct wl1271 *wl) unsigned long flags; if (test_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags) && - skb_queue_len(&wl->tx_queue) <= WL1271_TX_QUEUE_LOW_WATERMARK) { + wl->tx_queue_count <= WL1271_TX_QUEUE_LOW_WATERMARK) { /* firmware buffer has space, restart queues */ spin_lock_irqsave(&wl->wl_lock, flags); ieee80211_wake_queues(wl->hw); @@ -237,6 +236,43 @@ static void handle_tx_low_watermark(struct wl1271 *wl) } } +static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) +{ + struct sk_buff *skb = NULL; + unsigned long flags; + + skb = skb_dequeue(&wl->tx_queue[CONF_TX_AC_VO]); + if (skb) + goto out; + skb = skb_dequeue(&wl->tx_queue[CONF_TX_AC_VI]); + if (skb) + goto out; + skb = skb_dequeue(&wl->tx_queue[CONF_TX_AC_BE]); + if (skb) + goto out; + skb = skb_dequeue(&wl->tx_queue[CONF_TX_AC_BK]); + +out: + if (skb) { + spin_lock_irqsave(&wl->wl_lock, flags); + wl->tx_queue_count--; + spin_unlock_irqrestore(&wl->wl_lock, flags); + } + + return skb; +} + +static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) +{ + unsigned long flags; + int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); + + skb_queue_head(&wl->tx_queue[q], skb); + spin_lock_irqsave(&wl->wl_lock, flags); + wl->tx_queue_count++; + spin_unlock_irqrestore(&wl->wl_lock, flags); +} + void wl1271_tx_work_locked(struct wl1271 *wl) { struct sk_buff *skb; @@ -270,7 +306,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl) wl1271_acx_rate_policies(wl); } - while ((skb = skb_dequeue(&wl->tx_queue))) { + while ((skb = wl1271_skb_dequeue(wl))) { if (!woken_up) { ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) @@ -284,9 +320,9 @@ void wl1271_tx_work_locked(struct wl1271 *wl) * Aggregation buffer is full. * Flush buffer and try again. */ - skb_queue_head(&wl->tx_queue, skb); + wl1271_skb_queue_head(wl, skb); wl1271_write(wl, WL1271_SLV_MEM_DATA, wl->aggr_buf, - buf_offset, true); + buf_offset, true); sent_packets = true; buf_offset = 0; continue; @@ -295,7 +331,7 @@ void wl1271_tx_work_locked(struct wl1271 *wl) * Firmware buffer is full. * Queue back last skb, and stop aggregating. */ - skb_queue_head(&wl->tx_queue, skb); + wl1271_skb_queue_head(wl, skb); /* No work left, avoid scheduling redundant tx work */ set_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); goto out_ack; @@ -440,10 +476,13 @@ void wl1271_tx_reset(struct wl1271 *wl) struct sk_buff *skb; /* TX failure */ - while ((skb = skb_dequeue(&wl->tx_queue))) { - wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); - ieee80211_tx_status(wl->hw, skb); + for (i = 0; i < NUM_TX_QUEUES; i++) { + while ((skb = skb_dequeue(&wl->tx_queue[i]))) { + wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); + ieee80211_tx_status(wl->hw, skb); + } } + wl->tx_queue_count = 0; /* * Make sure the driver is at a consistent state, in case this @@ -472,8 +511,7 @@ void wl1271_tx_flush(struct wl1271 *wl) mutex_lock(&wl->mutex); wl1271_debug(DEBUG_TX, "flushing tx buffer: %d", wl->tx_frames_cnt); - if ((wl->tx_frames_cnt == 0) && - skb_queue_empty(&wl->tx_queue)) { + if ((wl->tx_frames_cnt == 0) && (wl->tx_queue_count == 0)) { mutex_unlock(&wl->mutex); return; } diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 07c2297b89a2..ce3d31f98c55 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -292,7 +292,8 @@ struct wl1271 { int session_counter; /* Frames scheduled for transmission, not handled yet */ - struct sk_buff_head tx_queue; + struct sk_buff_head tx_queue[NUM_TX_QUEUES]; + int tx_queue_count; struct work_struct tx_work; From 248daa084cee4b212ff4408e9c9b05b3bdc0da0d Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Wed, 15 Dec 2010 16:10:12 +0200 Subject: [PATCH 15/15] wl12xx_sdio_test: rename files to match current style Change some file names and Kconfig settings so that this new module matches the new way of using wl12xx instead of wl1271. Also fix SDIO power enabling and disabling to match the latest way of doing it. Cc: Roger Quadros Signed-off-by: Luciano Coelho --- drivers/net/wireless/wl12xx/Kconfig | 12 ++++---- drivers/net/wireless/wl12xx/Makefile | 3 +- .../{wl1271_sdio_test.c => sdio_test.c} | 28 +++++++++++++------ 3 files changed, 28 insertions(+), 15 deletions(-) rename drivers/net/wireless/wl12xx/{wl1271_sdio_test.c => sdio_test.c} (96%) diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig index 085bc44d814b..0e65bce457d6 100644 --- a/drivers/net/wireless/wl12xx/Kconfig +++ b/drivers/net/wireless/wl12xx/Kconfig @@ -52,13 +52,15 @@ config WL12XX_SDIO If you choose to build a module, it'll be called wl12xx_sdio. Say N if unsure. -config WL1271_SDIO_TEST - tristate "TI wl1271 SDIO testing support" - depends on WL1271 && MMC +config WL12XX_SDIO_TEST + tristate "TI wl12xx SDIO testing support" + depends on WL12XX && MMC + default n ---help--- This module adds support for the SDIO bus testing with the - TI wl1271 chipset. Select this if your platform is using - the SDIO bus. + TI wl12xx chipsets. You probably don't want this unless you are + testing a new hardware platform. Select this if you want to test the + SDIO bus which is connected to the wl12xx chip. config WL12XX_PLATFORM_DATA bool diff --git a/drivers/net/wireless/wl12xx/Makefile b/drivers/net/wireless/wl12xx/Makefile index 187678503887..521c0414e52e 100644 --- a/drivers/net/wireless/wl12xx/Makefile +++ b/drivers/net/wireless/wl12xx/Makefile @@ -3,13 +3,14 @@ wl12xx-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \ wl12xx_spi-objs = spi.o wl12xx_sdio-objs = sdio.o +wl12xx_sdio_test-objs = sdio_test.o wl12xx-$(CONFIG_NL80211_TESTMODE) += testmode.o obj-$(CONFIG_WL12XX) += wl12xx.o obj-$(CONFIG_WL12XX_SPI) += wl12xx_spi.o obj-$(CONFIG_WL12XX_SDIO) += wl12xx_sdio.o -obj-$(CONFIG_WL1271_SDIO_TEST) += wl1271_sdio_test.o +obj-$(CONFIG_WL12XX_SDIO_TEST) += wl12xx_sdio_test.o # small builtin driver bit obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx_platform_data.o diff --git a/drivers/net/wireless/wl12xx/wl1271_sdio_test.c b/drivers/net/wireless/wl12xx/sdio_test.c similarity index 96% rename from drivers/net/wireless/wl12xx/wl1271_sdio_test.c rename to drivers/net/wireless/wl12xx/sdio_test.c index 42d13144f645..9fcbd3dd8490 100644 --- a/drivers/net/wireless/wl12xx/wl1271_sdio_test.c +++ b/drivers/net/wireless/wl12xx/sdio_test.c @@ -1,11 +1,11 @@ /* - * wl1271_sdio_test.c - SDIO testing driver for wl1271 + * SDIO testing driver for wl12xx * * Copyright (C) 2010 Nokia Corporation * * Contact: Roger Quadros * - * wl1271 read/write routines taken from wl1271_sdio.c + * wl12xx read/write routines taken from the main module * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -34,10 +34,11 @@ #include #include #include +#include -#include "wl1271.h" -#include "wl1271_io.h" -#include "wl1271_boot.h" +#include "wl12xx.h" +#include "io.h" +#include "boot.h" #ifndef SDIO_VENDOR_ID_TI #define SDIO_VENDOR_ID_TI 0x0097 @@ -130,22 +131,31 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf, static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable) { struct sdio_func *func = wl_to_func(wl); + int ret; /* Let the SDIO stack handle wlan_enable control, so we * keep host claimed while wlan is in use to keep wl1271 * alive. */ if (enable) { - sdio_claim_power(func); + /* Power up the card */ + ret = pm_runtime_get_sync(&func->dev); + if (ret < 0) + goto out; sdio_claim_host(func); sdio_enable_func(func); + sdio_release_host(func); } else { + sdio_claim_host(func); sdio_disable_func(func); sdio_release_host(func); - sdio_release_power(func); + + /* Power down the card */ + ret = pm_runtime_put_sync(&func->dev); } - return 0; +out: + return ret; } static void wl1271_sdio_disable_interrupts(struct wl1271 *wl) @@ -481,7 +491,7 @@ static void __devexit wl1271_remove(struct sdio_func *func) } static struct sdio_driver wl1271_sdio_driver = { - .name = "wl1271_sdio_test", + .name = "wl12xx_sdio_test", .id_table = wl1271_devices, .probe = wl1271_probe, .remove = __devexit_p(wl1271_remove),