diff options
author | John W. Linville <linville@tuxdriver.com> | 2010-12-16 23:21:33 +0300 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-12-16 23:21:33 +0300 |
commit | 5928b91acae97622a6f2e679eb7a9f19bed68e3e (patch) | |
tree | 02d55145d355735b12398ad3eab61a72c78d5c4e /drivers | |
parent | 7d5f01ad536afebde9a1c81d985f8d0eaf2a9ab6 (diff) | |
parent | 248daa084cee4b212ff4408e9c9b05b3bdc0da0d (diff) | |
download | linux-5928b91acae97622a6f2e679eb7a9f19bed68e3e.tar.xz |
Merge branch 'wl12xx-next' of git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/wl12xx/Kconfig | 10 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/Makefile | 3 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/acx.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/acx.h | 9 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/boot.c | 17 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/boot.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/cmd.c | 69 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/cmd.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/debugfs.c | 192 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/init.c | 13 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/io.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/main.c | 119 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/scan.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/sdio_test.c | 520 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/tx.c | 60 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl12xx.h | 129 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl12xx_80211.h | 17 |
17 files changed, 848 insertions, 324 deletions
diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig index d2adeb1f72b7..0e65bce457d6 100644 --- a/drivers/net/wireless/wl12xx/Kconfig +++ b/drivers/net/wireless/wl12xx/Kconfig @@ -52,6 +52,16 @@ config WL12XX_SDIO If you choose to build a module, it'll be called wl12xx_sdio. Say N if unsure. +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 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 depends on WL12XX_SDIO != n || WL1251_SDIO != n diff --git a/drivers/net/wireless/wl12xx/Makefile b/drivers/net/wireless/wl12xx/Makefile index 005a758174d9..521c0414e52e 100644 --- a/drivers/net/wireless/wl12xx/Makefile +++ b/drivers/net/wireless/wl12xx/Makefile @@ -3,11 +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_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/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/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/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index f3d0541aaad6..0106628aa5a2 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -611,6 +611,75 @@ 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_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 16d1bf814e76..2a1d9db7ceb8 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -49,6 +49,9 @@ 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_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); @@ -122,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/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index dd71b7d2105c..ec6077760157 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) { @@ -237,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); @@ -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,21 +395,33 @@ 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); + entry = debugfs_create_x32("debug_level", 0600, wl->rootdir, + &wl12xx_debug_level); + if (!entry || IS_ERR(entry)) + goto err; + + return 0; + +err: + if (IS_ERR(entry)) + ret = PTR_ERR(entry); + else + ret = -ENOMEM; return ret; } 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; @@ -524,23 +431,15 @@ 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, + wl->hw->wiphy->debugfsdir); - 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 +462,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 +471,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/init.c b/drivers/net/wireless/wl12xx/init.c index 7949d346aadb..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; @@ -102,6 +100,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/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) { diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 708ffe304c6d..062247ef3ad2 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -336,7 +336,8 @@ out: } static int wl1271_reg_notify(struct wiphy *wiphy, - struct regulatory_request *request) { + struct regulatory_request *request) +{ struct ieee80211_supported_band *band; struct ieee80211_channel *ch; int i; @@ -569,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. @@ -890,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. @@ -917,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 - @@ -934,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); @@ -1064,6 +1068,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); @@ -1157,10 +1171,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); } @@ -1801,21 +1821,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, @@ -1858,8 +1878,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, @@ -1939,6 +1962,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; @@ -1967,13 +1991,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); @@ -1996,6 +2020,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); @@ -2085,10 +2113,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; @@ -2353,14 +2397,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}, @@ -2581,6 +2617,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"); @@ -2631,6 +2669,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; @@ -2677,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); @@ -2715,8 +2761,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) { @@ -2793,6 +2837,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 <luciano.coelho@nokia.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); 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/sdio_test.c b/drivers/net/wireless/wl12xx/sdio_test.c new file mode 100644 index 000000000000..9fcbd3dd8490 --- /dev/null +++ b/drivers/net/wireless/wl12xx/sdio_test.c @@ -0,0 +1,520 @@ +/* + * SDIO testing driver for wl12xx + * + * Copyright (C) 2010 Nokia Corporation + * + * Contact: Roger Quadros <roger.quadros@nokia.com> + * + * 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 + * 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 <linux/irq.h> +#include <linux/module.h> +#include <linux/crc7.h> +#include <linux/vmalloc.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio_ids.h> +#include <linux/mmc/card.h> +#include <linux/gpio.h> +#include <linux/wl12xx.h> +#include <linux/kthread.h> +#include <linux/firmware.h> +#include <linux/pm_runtime.h> + +#include "wl12xx.h" +#include "io.h" +#include "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); + 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) { + /* 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); + + /* Power down the card */ + ret = pm_runtime_put_sync(&func->dev); + } + +out: + return ret; +} + +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 = "wl12xx_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 <roger.quadros@nokia.com>"); + 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 3c836e6063e6..ce3d31f98c55 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, \ @@ -174,108 +175,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 @@ -393,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; @@ -431,6 +331,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; @@ -475,7 +378,7 @@ struct wl1271 { int last_rssi_event; struct wl1271_stats stats; - struct wl1271_debugfs debugfs; + struct dentry *rootdir; __le32 buffer_32; u32 buffer_cmd; diff --git a/drivers/net/wireless/wl12xx/wl12xx_80211.h b/drivers/net/wireless/wl12xx/wl12xx_80211.h index 184628027213..be21032f4dc1 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 <linux/if_ether.h> /* ETH_ALEN */ +#include <linux/if_arp.h> /* RATES */ #define IEEE80211_CCK_RATE_1MB 0x02 @@ -133,11 +134,17 @@ 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; +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; |