diff options
author | Luciano Coelho <coelho@ti.com> | 2011-05-20 14:29:58 +0300 |
---|---|---|
committer | Luciano Coelho <coelho@ti.com> | 2011-05-20 14:29:58 +0300 |
commit | 842de05fe75d07bf4cfe35e5ecd89e57ece65d2c (patch) | |
tree | 8f96940d1fc6ba0a9a4e28676b92db481b0354cb | |
parent | 99540c9c84948f01d372ae6f25140eb1101f53d8 (diff) | |
parent | af035d9f59cf2b3e53f6baf160237a78026d91bb (diff) |
Merge branch 'wl12xx-next'wl12xx-2011-05-20
-rw-r--r-- | drivers/net/wireless/wl12xx/Kconfig | 2 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/acx.c | 48 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/acx.h | 14 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/cmd.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/conf.h | 30 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/debugfs.c | 136 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/event.c | 36 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/io.c | 7 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/main.c | 127 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/ps.c | 8 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/rx.c | 29 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/sdio.c | 28 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/spi.c | 15 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/tx.c | 25 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl12xx.h | 8 |
15 files changed, 449 insertions, 65 deletions
diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig index 35ce7b0f4a6..07bcb1548d8 100644 --- a/drivers/net/wireless/wl12xx/Kconfig +++ b/drivers/net/wireless/wl12xx/Kconfig @@ -11,7 +11,6 @@ config WL12XX depends on WL12XX_MENU && GENERIC_HARDIRQS depends on INET select FW_LOADER - select CRC7 ---help--- This module adds support for wireless adapters based on TI wl1271 and TI wl1273 chipsets. This module does *not* include support for wl1251. @@ -33,6 +32,7 @@ config WL12XX_HT config WL12XX_SPI tristate "TI wl12xx SPI support" depends on WL12XX && SPI_MASTER + select CRC7 ---help--- This module adds support for the SPI interface of adapters using TI wl12xx chipsets. Select this if your platform is using diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index c6ee530e5bf..b5880eba06e 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -25,7 +25,6 @@ #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/crc7.h> #include <linux/spi/spi.h> #include <linux/slab.h> @@ -1577,6 +1576,53 @@ out: return ret; } +int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable) +{ + struct wl1271_acx_ps_rx_streaming *rx_streaming; + u32 conf_queues, enable_queues; + int i, ret = 0; + + wl1271_debug(DEBUG_ACX, "acx ps rx streaming"); + + rx_streaming = kzalloc(sizeof(*rx_streaming), GFP_KERNEL); + if (!rx_streaming) { + ret = -ENOMEM; + goto out; + } + + conf_queues = wl->conf.rx_streaming.queues; + if (enable) + enable_queues = conf_queues; + else + enable_queues = 0; + + for (i = 0; i < 8; i++) { + /* + * Skip non-changed queues, to avoid redundant acxs. + * this check assumes conf.rx_streaming.queues can't + * be changed while rx_streaming is enabled. + */ + if (!(conf_queues & BIT(i))) + continue; + + rx_streaming->tid = i; + rx_streaming->enable = enable_queues & BIT(i); + rx_streaming->period = wl->conf.rx_streaming.interval; + rx_streaming->timeout = wl->conf.rx_streaming.interval; + + ret = wl1271_cmd_configure(wl, ACX_PS_RX_STREAMING, + rx_streaming, + sizeof(*rx_streaming)); + if (ret < 0) { + wl1271_warning("acx ps rx streaming failed: %d", ret); + goto out; + } + } +out: + kfree(rx_streaming); + return ret; +} + int wl1271_acx_max_tx_retry(struct wl1271 *wl) { struct wl1271_acx_max_tx_retry *acx = NULL; diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 9a895e3cc61..f1d55313617 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1153,6 +1153,19 @@ struct wl1271_acx_fw_tsf_information { u8 padding[3]; } __packed; +struct wl1271_acx_ps_rx_streaming { + struct acx_header header; + + u8 tid; + u8 enable; + + /* interval between triggers (10-100 msec) */ + u8 period; + + /* timeout before first trigger (0-200 msec) */ + u8 timeout; +} __packed; + struct wl1271_acx_max_tx_retry { struct acx_header header; @@ -1384,6 +1397,7 @@ int wl1271_acx_set_ba_session(struct wl1271 *wl, int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, bool enable); int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); +int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, bool enable); int wl1271_acx_max_tx_retry(struct wl1271 *wl); int wl1271_acx_config_ps(struct wl1271 *wl); int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 42935ac7266..b3a4f58249b 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -23,7 +23,6 @@ #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/crc7.h> #include <linux/spi/spi.h> #include <linux/etherdevice.h> #include <linux/ieee80211.h> diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 1ab6c86aac4..510c2f79671 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -1245,6 +1245,35 @@ struct conf_fm_coex { u8 swallow_clk_diff; }; +struct conf_rx_streaming_settings { + /* + * RX Streaming duration (in msec) from last tx/rx + * + * Range: u32 + */ + u32 duration; + + /* + * Bitmap of tids to be polled during RX streaming. + * (Note: it doesn't look like it really matters) + * + * Range: 0x1-0xff + */ + u8 queues; + + /* + * RX Streaming interval. + * (Note:this value is also used as the rx streaming timeout) + * Range: 0 (disabled), 10 - 100 + */ + u8 interval; + + /* + * enable rx streaming also when there is no coex activity + */ + u8 always; +}; + struct conf_drv_settings { struct conf_sg_settings sg; struct conf_rx_settings rx; @@ -1260,6 +1289,7 @@ struct conf_drv_settings { struct conf_memory_settings mem_wl127x; struct conf_memory_settings mem_wl128x; struct conf_fm_coex fm_coex; + struct conf_rx_streaming_settings rx_streaming; u8 hci_io_ds; }; diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index f1f8df9b6cd..c3f19463474 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -71,6 +71,14 @@ static const struct file_operations name## _ops = { \ if (!entry || IS_ERR(entry)) \ goto err; \ +#define DEBUGFS_ADD_PREFIX(prefix, name, parent) \ + do { \ + entry = debugfs_create_file(#name, 0400, parent, \ + wl, &prefix## _## name## _ops); \ + if (!entry || IS_ERR(entry)) \ + goto err; \ + } while (0); + #define DEBUGFS_FWSTATS_FILE(sub, name, fmt) \ static ssize_t sub## _ ##name## _read(struct file *file, \ char __user *userbuf, \ @@ -527,11 +535,129 @@ static const struct file_operations beacon_interval_ops = { .llseek = default_llseek, }; +static ssize_t rx_streaming_interval_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + char buf[10]; + size_t len; + unsigned long value; + int ret; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + + ret = kstrtoul(buf, 0, &value); + if (ret < 0) { + wl1271_warning("illegal value in rx_streaming_interval!"); + return -EINVAL; + } + + /* valid values: 0, 10-100 */ + if (value && (value < 10 || value > 100)) { + wl1271_warning("value is not in range!"); + return -ERANGE; + } + + mutex_lock(&wl->mutex); + + wl->conf.rx_streaming.interval = value; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + wl1271_recalc_rx_streaming(wl); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + return count; +} + +static ssize_t rx_streaming_interval_read(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + return wl1271_format_buffer(userbuf, count, ppos, + "%d\n", wl->conf.rx_streaming.interval); +} + +static const struct file_operations rx_streaming_interval_ops = { + .read = rx_streaming_interval_read, + .write = rx_streaming_interval_write, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + +static ssize_t rx_streaming_always_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + char buf[10]; + size_t len; + unsigned long value; + int ret; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + + ret = kstrtoul(buf, 0, &value); + if (ret < 0) { + wl1271_warning("illegal value in rx_streaming_write!"); + return -EINVAL; + } + + /* valid values: 0, 10-100 */ + if (!(value == 0 || value == 1)) { + wl1271_warning("value is not in valid!"); + return -EINVAL; + } + + mutex_lock(&wl->mutex); + + wl->conf.rx_streaming.always = value; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + wl1271_recalc_rx_streaming(wl); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + return count; +} + +static ssize_t rx_streaming_always_read(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + return wl1271_format_buffer(userbuf, count, ppos, + "%d\n", wl->conf.rx_streaming.always); +} + +static const struct file_operations rx_streaming_always_ops = { + .read = rx_streaming_always_read, + .write = rx_streaming_always_write, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + static int wl1271_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { int ret = 0; - struct dentry *entry, *stats; + struct dentry *entry, *stats, *streaming; stats = debugfs_create_dir("fw-statistics", rootdir); if (!stats || IS_ERR(stats)) { @@ -640,6 +766,14 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(dtim_interval, rootdir); DEBUGFS_ADD(beacon_interval, rootdir); + streaming = debugfs_create_dir("rx_streaming", rootdir); + if (!streaming || IS_ERR(streaming)) + goto err; + + DEBUGFS_ADD_PREFIX(rx_streaming, interval, streaming); + DEBUGFS_ADD_PREFIX(rx_streaming, always, streaming); + + return 0; err: diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index c3c554cd658..910244314dc 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -133,10 +133,13 @@ static int wl1271_event_ps_report(struct wl1271 *wl, if (ret < 0) break; - /* enable beacon early termination */ - ret = wl1271_acx_bet_enable(wl, true); - if (ret < 0) - break; + /* + * BET has only a minor effect in 5GHz and masks + * channel switch IEs, so we only enable BET on 2.4GHz + */ + if (wl->band == IEEE80211_BAND_2GHZ) + /* enable beacon early termination */ + ret = wl1271_acx_bet_enable(wl, true); if (wl->ps_compl) { complete(wl->ps_compl); @@ -168,6 +171,21 @@ static void wl1271_event_rssi_trigger(struct wl1271 *wl, wl->last_rssi_event = event; } +static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl, + u8 enable) +{ + if (enable) { + /* disable dynamic PS when the firmware senses BT*/ + ieee80211_disable_dyn_ps(wl->vif); + set_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); + } else { + ieee80211_enable_dyn_ps(wl->vif); + clear_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags); + wl1271_recalc_rx_streaming(wl); + } + +} + static void wl1271_event_mbox_dump(struct event_mailbox *mbox) { wl1271_debug(DEBUG_EVENT, "MBOX DUMP:"); @@ -211,14 +229,10 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) } } - /* disable dynamic PS when requested by the firmware */ if (vector & SOFT_GEMINI_SENSE_EVENT_ID && - wl->bss_type == BSS_TYPE_STA_BSS) { - if (mbox->soft_gemini_sense_info) - ieee80211_disable_dyn_ps(wl->vif); - else - ieee80211_enable_dyn_ps(wl->vif); - } + wl->bss_type == BSS_TYPE_STA_BSS) + wl12xx_event_soft_gemini_sense(wl, + mbox->soft_gemini_sense_info); /* * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon diff --git a/drivers/net/wireless/wl12xx/io.c b/drivers/net/wireless/wl12xx/io.c index da5c1ad942a..c2da66f4504 100644 --- a/drivers/net/wireless/wl12xx/io.c +++ b/drivers/net/wireless/wl12xx/io.c @@ -23,7 +23,6 @@ #include <linux/module.h> #include <linux/platform_device.h> -#include <linux/crc7.h> #include <linux/spi/spi.h> #include "wl12xx.h" @@ -128,12 +127,14 @@ EXPORT_SYMBOL_GPL(wl1271_set_partition); void wl1271_io_reset(struct wl1271 *wl) { - wl->if_ops->reset(wl); + if (wl->if_ops->reset) + wl->if_ops->reset(wl); } void wl1271_io_init(struct wl1271 *wl) { - wl->if_ops->init(wl); + if (wl->if_ops->init) + wl->if_ops->init(wl); } void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val) diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 610be03a198..fb0f76482a7 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -361,6 +361,12 @@ static struct conf_drv_settings default_conf = { .fm_disturbed_band_margin = 0xff, /* default */ .swallow_clk_diff = 0xff, /* default */ }, + .rx_streaming = { + .duration = 150, + .queues = 0x1, + .interval = 20, + .always = 0, + }, .hci_io_ds = HCI_IO_DS_6MA, }; @@ -472,6 +478,117 @@ static int wl1271_reg_notify(struct wiphy *wiphy, return 0; } +static int wl1271_set_rx_streaming(struct wl1271 *wl, bool enable) +{ + int ret = 0; + + /* we should hold wl->mutex */ + ret = wl1271_acx_ps_rx_streaming(wl, enable); + if (ret < 0) + goto out; + + if (enable) + set_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags); + else + clear_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags); +out: + return ret; +} + +/* + * this function is being called when the rx_streaming interval + * has beed changed or rx_streaming should be disabled + */ +int wl1271_recalc_rx_streaming(struct wl1271 *wl) +{ + int ret = 0; + int period = wl->conf.rx_streaming.interval; + + /* don't reconfigure if rx_streaming is disabled */ + if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags)) + goto out; + + /* reconfigure/disable according to new streaming_period */ + if (period && + test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) && + (wl->conf.rx_streaming.always || + test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) + ret = wl1271_set_rx_streaming(wl, true); + else { + ret = wl1271_set_rx_streaming(wl, false); + /* don't cancel_work_sync since we might deadlock */ + del_timer_sync(&wl->rx_streaming_timer); + } +out: + return ret; +} + +static void wl1271_rx_streaming_enable_work(struct work_struct *work) +{ + int ret; + struct wl1271 *wl = + container_of(work, struct wl1271, rx_streaming_enable_work); + + mutex_lock(&wl->mutex); + + if (test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags) || + !test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) || + (!wl->conf.rx_streaming.always && + !test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) + goto out; + + if (!wl->conf.rx_streaming.interval) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl1271_set_rx_streaming(wl, true); + if (ret < 0) + goto out_sleep; + + /* stop it after some time of inactivity */ + mod_timer(&wl->rx_streaming_timer, + jiffies + msecs_to_jiffies(wl->conf.rx_streaming.duration)); + +out_sleep: + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + +static void wl1271_rx_streaming_disable_work(struct work_struct *work) +{ + int ret; + struct wl1271 *wl = + container_of(work, struct wl1271, rx_streaming_disable_work); + + mutex_lock(&wl->mutex); + + if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags)) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl1271_set_rx_streaming(wl, false); + if (ret) + goto out_sleep; + +out_sleep: + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + +static void wl1271_rx_streaming_timer(unsigned long data) +{ + struct wl1271 *wl = (struct wl1271 *)data; + ieee80211_queue_work(wl->hw, &wl->rx_streaming_disable_work); +} + static void wl1271_conf_init(struct wl1271 *wl) { @@ -1691,6 +1808,9 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, cancel_delayed_work_sync(&wl->scan_complete_work); cancel_work_sync(&wl->netstack_work); cancel_work_sync(&wl->tx_work); + del_timer_sync(&wl->rx_streaming_timer); + cancel_work_sync(&wl->rx_streaming_enable_work); + cancel_work_sync(&wl->rx_streaming_disable_work); cancel_delayed_work_sync(&wl->pspoll_work); cancel_delayed_work_sync(&wl->elp_work); @@ -3956,6 +4076,11 @@ struct ieee80211_hw *wl1271_alloc_hw(void) INIT_WORK(&wl->tx_work, wl1271_tx_work); INIT_WORK(&wl->recovery_work, wl1271_recovery_work); INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); + INIT_WORK(&wl->rx_streaming_enable_work, + wl1271_rx_streaming_enable_work); + INIT_WORK(&wl->rx_streaming_disable_work, + wl1271_rx_streaming_disable_work); + wl->channel = WL1271_DEFAULT_CHANNEL; wl->beacon_int = WL1271_DEFAULT_BEACON_INT; wl->default_key = 0; @@ -3981,6 +4106,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->quirks = 0; wl->platform_quirks = 0; wl->sched_scanning = false; + setup_timer(&wl->rx_streaming_timer, wl1271_rx_streaming_timer, + (unsigned long) wl); memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index b59b67711a1..d3e377d7fe6 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -169,9 +169,11 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, wl1271_debug(DEBUG_PSM, "leaving psm"); /* disable beacon early termination */ - ret = wl1271_acx_bet_enable(wl, false); - if (ret < 0) - return ret; + if (wl->band == IEEE80211_BAND_2GHZ) { + ret = wl1271_acx_bet_enable(wl, false); + if (ret < 0) + return ret; + } /* disable beacon filtering */ ret = wl1271_acx_beacon_filter_opt(wl, false); diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 70091035e01..db230a503bf 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -95,6 +95,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) struct ieee80211_hdr *hdr; u8 *buf; u8 beacon = 0; + u8 is_data = 0; /* * In PLT mode we seem to get frames and mac80211 warns about them, @@ -137,6 +138,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) hdr = (struct ieee80211_hdr *)skb->data; if (ieee80211_is_beacon(hdr->frame_control)) beacon = 1; + if (ieee80211_is_data_present(hdr->frame_control)) + is_data = 1; wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon); @@ -149,7 +152,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) skb_queue_tail(&wl->deferred_rx_queue, skb); ieee80211_queue_work(wl->hw, &wl->netstack_work); - return 0; + return is_data; } void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) @@ -162,6 +165,8 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) u32 mem_block; u32 pkt_length; u32 pkt_offset; + bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); + bool had_data = false; while (drv_rx_counter != fw_rx_counter) { buf_size = 0; @@ -214,9 +219,11 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) * conditions, in that case the received frame will just * be dropped. */ - wl1271_rx_handle_data(wl, - wl->aggr_buf + pkt_offset, - pkt_length); + if (wl1271_rx_handle_data(wl, + wl->aggr_buf + pkt_offset, + pkt_length) == 1) + had_data = true; + wl->rx_counter++; drv_rx_counter++; drv_rx_counter &= NUM_RX_PKT_DESC_MOD_MASK; @@ -230,6 +237,20 @@ void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_common_status *status) */ if (wl->quirks & WL12XX_QUIRK_END_OF_TRANSACTION) wl1271_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); + + if (!is_ap && wl->conf.rx_streaming.interval && had_data && + (wl->conf.rx_streaming.always || + test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) { + u32 timeout = wl->conf.rx_streaming.duration; + + /* restart rx streaming */ + if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags)) + ieee80211_queue_work(wl->hw, + &wl->rx_streaming_enable_work); + + mod_timer(&wl->rx_streaming_timer, + jiffies + msecs_to_jiffies(timeout)); + } } void wl1271_set_default_filters(struct wl1271 *wl) diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 92d29a860fc..74fd7c6807f 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c @@ -23,7 +23,6 @@ #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> @@ -45,7 +44,7 @@ #define SDIO_DEVICE_ID_TI_WL1271 0x4076 #endif -static const struct sdio_device_id wl1271_devices[] = { +static const struct sdio_device_id wl1271_devices[] __devinitconst = { { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271) }, {} }; @@ -107,14 +106,6 @@ static void wl1271_sdio_enable_interrupts(struct wl1271 *wl) enable_irq(wl->irq); } -static void wl1271_sdio_reset(struct wl1271 *wl) -{ -} - -static void wl1271_sdio_init(struct wl1271 *wl) -{ -} - static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, size_t len, bool fixed) { @@ -215,8 +206,6 @@ static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable) static struct wl1271_if_operations sdio_ops = { .read = wl1271_sdio_raw_read, .write = wl1271_sdio_raw_write, - .reset = wl1271_sdio_reset, - .init = wl1271_sdio_init, .power = wl1271_sdio_set_power, .dev = wl1271_sdio_wl_to_dev, .enable_irq = wl1271_sdio_enable_interrupts, @@ -303,8 +292,6 @@ static int __devinit wl1271_probe(struct sdio_func *func, /* Tell PM core that we don't need the card to be powered now */ pm_runtime_put_noidle(&func->dev); - wl1271_notice("initialized"); - return 0; out_irq: @@ -398,23 +385,12 @@ static struct sdio_driver wl1271_sdio_driver = { static int __init wl1271_init(void) { - int ret; - - ret = sdio_register_driver(&wl1271_sdio_driver); - if (ret < 0) { - wl1271_error("failed to register sdio driver: %d", ret); - goto out; - } - -out: - return ret; + return sdio_register_driver(&wl1271_sdio_driver); } static void __exit wl1271_exit(void) { sdio_unregister_driver(&wl1271_sdio_driver); - - wl1271_notice("unloaded"); } module_init(wl1271_init); diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index 51662bb6801..b73cee117ad 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c @@ -435,8 +435,6 @@ static int __devinit wl1271_probe(struct spi_device *spi) if (ret) goto out_irq; - wl1271_notice("initialized"); - return 0; out_irq: @@ -473,23 +471,12 @@ static struct spi_driver wl1271_spi_driver = { static int __init wl1271_init(void) { - int ret; - - ret = spi_register_driver(&wl1271_spi_driver); - if (ret < 0) { - wl1271_error("failed to register spi driver: %d", ret); - goto out; - } - -out: - return ret; + return spi_register_driver(&wl1271_spi_driver); } static void __exit wl1271_exit(void) { spi_unregister_driver(&wl1271_spi_driver); - - wl1271_notice("unloaded"); } module_init(wl1271_init); diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index ca3ab1c1ace..6603e60da04 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -562,17 +562,29 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) spin_unlock_irqrestore(&wl->wl_lock, flags); } +static bool wl1271_tx_is_data_present(struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); + + return ieee80211_is_data_present(hdr->frame_control); +} + void wl1271_tx_work_locked(struct wl1271 *wl) { struct sk_buff *skb; u32 buf_offset = 0; bool sent_packets = false; + bool had_data = false; + bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); int ret; if (unlikely(wl->state == WL1271_STATE_OFF)) return; while ((skb = wl1271_skb_dequeue(wl))) { + if (wl1271_tx_is_data_present(skb)) + had_data = true; + ret = wl1271_prepare_tx_frame(wl, skb, buf_offset); if (ret == -EAGAIN) { /* @@ -619,6 +631,19 @@ out_ack: wl1271_handle_tx_low_watermark(wl); } + if (!is_ap && wl->conf.rx_streaming.interval && had_data && + (wl->conf.rx_streaming.always || + test_bit(WL1271_FLAG_SOFT_GEMINI, &wl->flags))) { + u32 timeout = wl->conf.rx_streaming.duration; + + /* enable rx streaming */ + if (!test_bit(WL1271_FLAG_RX_STREAMING_STARTED, &wl->flags)) + ieee80211_queue_work(wl->hw, + &wl->rx_streaming_enable_work); + + mod_timer(&wl->rx_streaming_timer, + jiffies + msecs_to_jiffies(timeout)); + } } void wl1271_tx_work(struct work_struct *work) diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index fbe8f46d123..1cafb081bb4 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -359,6 +359,8 @@ enum wl12xx_flags { WL1271_FLAG_DUMMY_PACKET_PENDING, WL1271_FLAG_SUSPENDED, WL1271_FLAG_PENDING_WORK, + WL1271_FLAG_SOFT_GEMINI, + WL1271_FLAG_RX_STREAMING_STARTED, }; struct wl1271_link { @@ -508,6 +510,11 @@ struct wl1271 { /* Default key (for WEP) */ u32 default_key; + /* Rx Streaming */ + struct work_struct rx_streaming_enable_work; + struct work_struct rx_streaming_disable_work; + struct timer_list rx_streaming_timer; + unsigned int filters; unsigned int rx_config; unsigned int rx_filter; @@ -601,6 +608,7 @@ struct wl1271_station { int wl1271_plt_start(struct wl1271 *wl); int wl1271_plt_stop(struct wl1271 *wl); +int wl1271_recalc_rx_streaming(struct wl1271 *wl); #define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */ |