summaryrefslogtreecommitdiff
path: root/drivers/staging/cw1200/queue.c
diff options
context:
space:
mode:
authorDmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>2010-06-05 23:26:14 +0200
committerRobert Marklund <robert.marklund@stericsson.com>2011-10-05 11:16:50 +0200
commit82b8089e8fca45e08ffa99cf3612c0f078899efe (patch)
tree0dd22acb11b496e914d2fab206926f413cb3ff86 /drivers/staging/cw1200/queue.c
parentf46a6bba76d040633ff59100e42aabd0784cb593 (diff)
WLAN: Initial commit of the STM CW1200 WLAN driver.
LL IO API is defined and implemented. TX queue is designed and implemented. WSM API subset is defined. mac80211 API is implemented partly. FW downloading is working. BH is _not_ implemented. RX BH is implemented. 1. RX WSM API is defined and stubs are created. 2. RX BH is implemented (not verifiyed yet). Reference WSM API implementation. wsm_configuration / wsm_configuration_confirm. Not veryfied yet. BH and WSM TX, basic WSM notification. Device startup is finished. TODO at mac80211 layer: - start - add_interface - config - config_tx - config_filter - hw_scan - remove_interface - stop TODO in general: - proper (random?) MAC address TODO in WSM: - datapath Basic mac80211 STA interface is implemented (not verified yet, but should be working). Random MAC address is implemented. Defined and implemented: - cw1200_start - cw1200_stop - cw1200_add_interface - cw1200_remove_interface - cw1200_config - cw1200_conf_tx FW code is moved to fwio.[hc] STA code is moved to sta.[hc] AP code is moved to ap.[hc] TODO: - configure_filter() - hw_scan() - datapath Some bugfix. Piggyback RX-ing. Bugbix: wrong buf_id_rx calculation. Finaly! RX datapath is implemented. TX datapath is implemented. HW scan is implemented. TODO: - hw_scan: little refactoring. I hate gotos. - Join - Unjoin - SKB pool - BH monitor / chip restarter Development. + Proper handling of scan IEs Join. TX/RX should be working now. Development. + Proper handling of scan IEs + Ping is working!!! (with some hacks) Development. + TX rate policy cache is implemented. + TX rate policy table uploading is implemented. + TX queues stop/resume. + WSM STA API (except MIBs) is 100% finished. + Direct TX-ing of probe requests is converted to start-scan WSM cmd (workaround against FW "feature"). + All work moved to an own workqueue. + Scan timeout is implemented (workaround against a known bug in FW). + set_key is implemented. TODO: - Full WSM API support. - Join refactoring - Assoc status updates. Verification and bugfixing. + Direct probe is verified. Some bugs fixed. + TX timeout is implemented... and rolled back: no way to cancel TX. + TX hang is fixed: FW hangs if tx->more is set but no more data available. + TX rate table uploading is moved into a separate workitem: can't schedule in datapath. + TX status reporting is enabled. + Removed hacks from Join: should be able to connect to any AP now. + TX rate cache bugfix: incorrect handling of policies like 100000000008900000000000. + TX rate cache optimization: upload only changed policies. + Unjoin is implemented for TX and RX. + Join is triggered for auth. frames only. + Join timeout is implemented. + wsm_set_bss_params is called from assoc notification. + wsm_set_association_mode is called from assoc notification. + set_beacon_wakeup_period is called from assoc notification. + HT information is now available in priv->ht_cap, priv->association_mode + CTS protection is set. + RTS threshold is set and verified. + Warning cleanup. + TODOs for compat-wireless-2010-07-16 API change. + Very basic RSSI subscription. However reporting is NIY. + Some bugfix for minestrel. F.e. policy like 10:9 11:9 0:1 0:1 -1:0 should be handled ok. + MaxTxCount (short/long) is implemented and verified. + Channel switch is protected now. + OverrideInternalTxRate is implemented.. and rolled back: internal rate controlselection is better. TODO: - Security is not validated yet - Set HT parameters of TX data according to priv->ht_cap, priv->association_mode. - Strange: set_beacon_wakeup_period is called with DTIM 1 always. Why??? - RSSI subscription events. - How to do unjoin? Style changes. Modifications to make the code compatible with checkpath. Not finished yet. 1. // -> /* */ 2. whitespaces 3. formatting 4. warning- & error fix No functional changes in the commit. Verification. + RSSI reporting is working. + WEP is working. Dynamic WEP key switching is implemented to select TX WEP key on-fly + WPA/CCMP and WPA2/CCMP are working. + WPA/TKIP and WPA2/TKIP are working. + MIC failure event is implemented. + Unjoin is implemented as a reset. Why not? + wsm_flush_tx() added to flush TX port after wsm_lock_tx_async(). + race condition in wsm_lock_tx()/wsm_flush_tx() is fixed. + RSSI subscription is under research. + Event reporting is implemented. + Beacon loss event is prepared (but not sent) + Link loss is implemented. TODO: On target.. + Bugfix: DMA from non-DMAble memory in FW downloading. + RSSI subscription is finished. + RSSI event reporting is implemented. + Beacon loss event is reimplemented with respect to scan. + Memory leak in WSM event processing is fixed. + Code is verified on target. Set CW1200_U8500_PLATFORM to build for target. + Scan turns on powersave to prevent AP from TXing. + Spinning of direct_probe work is fixed. + Workaround is implemented for "FIXME: we can't use 'more' at all: we don't know future." TODO: Verification on device. + Correct deinitialization of async work. + New WSM API. + WMM is verified and fixed (queue prioritization). + Bug in TX is fixed (freezing of TCP traffic). + STE CQM extensions are implemented. + HW revision detection is implemented. + A crash/hangs in scan is fixed. + 802.11 slot time is set + 11n: first steps: Mixed mode is working. + 11n: Block ACK config. + Keep-alive work is implemented (but it is not doing anything yet). TODO: - Copyright statements and COPYING - STE API for power-save - Need to deinit bt_thread, isn't it? Copyright statements and bt_thread deinitialization. + Copyright statements + bt_thread deinitialization (NOT TESTED ON DEVICE AT ALL!!!) Cumulative TODO: - COPYING - STE API for power-save - Set HT parameters of TX data. 11n verification. - SKB pool - reuse previously allocated SKBs - BH monitor / chip restarter - IBSS - AP mode - U-APSD configuration - Memory leak verification, coverity, checkpatch = It looks like that's it. Mainline to compat-wireless-20010-12-03 No functional changes in this commit. + Changes in compat-wireless API. + Changes in kerner API. + Changes in kernel header structures. TODO: - New code in cw1200_bss_info_changed() needs to be verified. - ht_operation_mode needs to be cached in priv. Some HT refactoring. + ht_operation_mode is cached in priv + channel_type is cached in priv + New header for HT-related code: ht.h + Greenfield mode is used for TX if possible. + HT flags are reported back to rate control correctly. TODO: - Verify A-MPDU factor settings in cw1200_band_2ghz. SKB cache is implemented. Firmware loading refactoring. + CUT 2.0 detection is implemented. + Standard firmware loading API is used. + Firmware loading path is changed to /lib/firmware/cw1200/ + Firmware is put to the project: ./firmware/ TODO: - Firmware copying at build time. Bringup after mainlining. + FW downloading is working now. TODO: - BH thread can't read data block from device. Init: removing GPIO toggling. Not needed anymore. WLAN: Fix for missing GPIO_HIGH, GPIO_LOW The patch defines CW1200_GPIO_{HIGH|LOW} as replacement of GPIO_{HIGH|LOW}, which might be absent on the target platform. Also it allows build without compat wireless tree. Added cw1200 to drivers/staging Kconfig and Makefile. Added two CONFIG_ flags: + CONFIG_CW1200 - Enables the generic cw1200 module + CONFIG_CW1200_U8500_PLATFORM - U8500 platform integration Removed binary firmware files, they do not belong in the kernel tree. Added TODO file for cw1200 staging driver. Added more help text to Kconfig Removed checkpatch warnings. There is still one left. Removed all checkpatch warnings for cw1200.h Removed checkpatch warnings from cw1200_stdio.c. There is still one left. Removed checkpatch warnings for fwio.h Removed checkpatch warning for hwio.c Removed checkpatch warnings for main.c There are still some warninges left. Removed checkpatch warnings from queue.c There are still some warnings left Removed checkpatch warnings for queue.h Removed checkpatch warnings for sbus.h Removed checkpatch warnings for scan.c Removed checkpatch warnings for scan.h Removed checkpatch warnings for sta.c Removed checkpatch warnings for txrc.c Removed checkpatch warnings for txrx.h Removed checkpatch warnings from wsm.c Lots of "line over 80 char..." warnings left. Minor checkpatch warning cleanup on wsm.c WLAN: CW1200: Development: SoftAP mode WLAN: CW1200: AP development. + Basic open AP is woking (prototyping done). TODO: - Check AP scan & assoc with sniffer - WSM API: TxPowerLevel in scan requests. - WSM API: Frame in TX and RX indication. - WSM API: Flags in set_bss_params. - WSM API: IGTKGroupKey in key info. - WSM API: MultiTX confirm - WSM API: Config block ACK - WSM API: BA timeout indication. CW1200: WLAN: Fix for a merge error. This commit fixes build error introduced by WLAN: CW1200: AP development. WLAN: CW1200: Fix for a warning in net/mac80211/rx.c WLAN: CW1200: Add / remove key refactoring for AP mode. Keys are cached in the driver and loaded after wsm_start() is called. Verified (AP mode): + Open security: OK + WEP40: OK + WEP104: FAIL (WSM_STATUS_DECRYPTFAILURE) + CCMP: FAIL (MIC failure detected by STA. STA->AP decrypted OK) + TKIP: FAIL (MIC failure detected by STA. STA->AP decrypted OK) WLAN: CW1200: Proper handling of stop request in the AP mode. WLAN: CW1200: Firmware-driven keep-alive is implemented. CW1200: SDIO card detection is implemented. Removed dependency to u8500_sdio_detect_card(). New moule parameter "device" is implemented to point to the SDIO interface device is connected to. WLAN: CW1200: Checkpatch cleanup. + wsm.c is cleared. + sta.c is cleared. + ap.c is cleared. + bh.c is cleared. + cw1200_sdio.c is cleared. + main.c is cleared. + queue.c is cleared. + scan.c is cleared. + txrx.c is cleared. + cw1200.h is cleared. + hwio.h is cleared. + ht.h is cleared. + wsm_get_tx is (finally!) redesigned. TODO: - wsm.h WLAN: CW1200: Checkpatch cleanup. + wsm.h is cleared. Code is ready for submission. WLAN: CW1200: Module parameter for MAC address. New module parameter for cw1200_core.ko: "macaddr". Usage: modprobe cw1200_core macaddr=0x00,0x80,0xE1,0x30,0x40,0x50 Random MAC address by default. WLAN: CW1200: Fix for a crash in skb_release_data(). WLAN: CW1200: Fix for a too short WSM timeout in stressed condition. WLAN: CW1200: Bugfix after regression testing. Datapath is verified (several gigabytes of downloaded data). WLAN: STA mode development. + AP API sta_add/sta_semove is implemented. + wsm_suspend_resume_indication is implemented. + Unique link ID is allocated for STAs in AP mode as WSM requires. + wsm_map_link is implemented. + wsm_reset is modified to support multiple link IDs. + cw1200_suspend_resume handler is implemented in the ap.c. + no more hardcoded SSIDs + Debug logs functionality is exposed to the kernel config. + Suspend / resume is supported by queue. + TKIP multicast TX (AP mode) is fixed. TODO: - AP mode with powersave is not verified. WLAN: Support for non-power-of-two SDIO transfer. WLAN: AP mode verification and bugfixing. + Fix for broken broadcasts. + Redesign of PS implementation in AP mode. + set_tim() is implemented. + sta_notify() is implemented. WLAN: IV/ICV injection fixed. Fixed: skb was modified prior to sanity check. There is also a workaround against a bug in WSM_A21.05.0288 firmware in this commit. In AP mode FW calculates FCS incorrectly when DA is FF:FF:FF:FF:FF:FF (was seen with TKIP and CCMP security). Workaround checks DA and replaces FF:FF:FF:FF:FF:FF with multicast address 01:00:5E:00:00:16. Workaround is disabled by #if 0 (can be used only for verification, should not be enabled in production). WLAN: Disabling debug printouts (STA/AP). WLAN: Updating u8500_defconfig with CW1200 driver. WLAN: AP mode fine-tuning. + Explicitly set broadcast bit in TIM when broadcasts are available. + Split handling of management frames (link ID = 0) and buffered multicasts (link ID = CW1200_LINK_ID_AFTER_DTIM) Signed-off-by: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
Diffstat (limited to 'drivers/staging/cw1200/queue.c')
-rw-r--r--drivers/staging/cw1200/queue.c409
1 files changed, 409 insertions, 0 deletions
diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c
new file mode 100644
index 00000000000..f21ff8f2f99
--- /dev/null
+++ b/drivers/staging/cw1200/queue.c
@@ -0,0 +1,409 @@
+/*
+ * O(1) TX queue with built-in allocator for ST-Ericsson CW1200 drivers
+ *
+ * Copyright (c) 2010, ST-Ericsson
+ * Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
+ *
+ * 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.
+ */
+
+#include "net/mac80211.h"
+#include "queue.h"
+#include "wsm.h"
+#include "txrx.h"
+#include "cw1200.h"
+
+/* private */ struct cw1200_queue_item
+{
+ struct list_head head;
+ struct sk_buff *skb;
+ u32 packetID;
+ /* For safety purposes only. I do not trust device too much.
+ * It was observed that it indicates TX for a packet several times,
+ * so it's not enough to use offset or address as an uniquie ID in a
+ * queue.
+ */
+ u8 generation;
+ u8 link_id;
+ u8 reserved[2];
+};
+
+static inline void __cw1200_queue_lock(struct cw1200_queue *queue,
+ struct cw1200_common *cw1200)
+{
+ if (queue->tx_locked_cnt++ == 0) {
+ txrx_printk(KERN_DEBUG "[TX] Queue %d is locked.\n",
+ queue->queue_id);
+ ieee80211_stop_queue(cw1200->hw, queue->queue_id);
+ }
+}
+
+static inline void __cw1200_queue_unlock(struct cw1200_queue *queue,
+ struct cw1200_common *cw1200)
+{
+ BUG_ON(!queue->tx_locked_cnt);
+ if (--queue->tx_locked_cnt == 0) {
+ txrx_printk(KERN_DEBUG "[TX] Queue %d is unlocked.\n",
+ queue->queue_id);
+ ieee80211_wake_queue(cw1200->hw, queue->queue_id);
+ }
+}
+
+static inline void cw1200_queue_parse_id(u32 packetID, u8 *queue_generation,
+ u8 *queue_id,
+ u8 *item_generation,
+ u8 *item_id)
+{
+ *item_id = (packetID >> 0) & 0xFF;
+ *item_generation = (packetID >> 8) & 0xFF;
+ *queue_id = (packetID >> 16) & 0xFF;
+ *queue_generation = (packetID >> 24) & 0xFF;
+}
+
+static inline u32 cw1200_queue_make_packet_id(u8 queue_generation, u8 queue_id,
+ u8 item_generation, u8 item_id)
+{
+ return ((u32)item_id << 0) |
+ ((u32)item_generation << 8) |
+ ((u32)queue_id << 16) |
+ ((u32)queue_generation << 24);
+}
+
+int cw1200_queue_init(struct cw1200_queue *queue, u8 queue_id,
+ size_t capacity, size_t map_capacity)
+{
+ size_t i;
+
+ memset(queue, 0, sizeof(*queue));
+ queue->capacity = capacity;
+ queue->map_capacity = map_capacity;
+ queue->queue_id = queue_id;
+ INIT_LIST_HEAD(&queue->queue);
+ INIT_LIST_HEAD(&queue->pending);
+ INIT_LIST_HEAD(&queue->free_pool);
+ spin_lock_init(&queue->lock);
+
+ queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity,
+ GFP_KERNEL);
+ if (!queue->pool)
+ return -ENOMEM;
+
+ queue->link_map_cache = kzalloc(sizeof(int[map_capacity]),
+ GFP_KERNEL);
+ if (!queue->link_map_cache) {
+ kfree(queue->pool);
+ queue->pool = NULL;
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < capacity; ++i)
+ list_add_tail(&queue->pool[i].head, &queue->free_pool);
+
+ return 0;
+}
+
+int cw1200_queue_clear(struct cw1200_queue *queue)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ queue->generation++;
+ list_splice_tail_init(&queue->queue, &queue->pending);
+ while (!list_empty(&queue->pending)) {
+ struct cw1200_queue_item *item = list_first_entry(
+ &queue->pending, struct cw1200_queue_item, head);
+ WARN_ON(!item->skb);
+ if (likely(item->skb)) {
+ dev_kfree_skb_any(item->skb);
+ item->skb = NULL;
+ }
+ list_move_tail(&item->head, &queue->free_pool);
+ }
+ queue->num_queued = 0;
+ queue->num_pending = 0;
+ queue->num_sent = 0;
+ memset(queue->link_map_cache, 0, sizeof(int[queue->map_capacity]));
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return 0;
+}
+
+int cw1200_queue_deinit(struct cw1200_queue *queue)
+{
+ cw1200_queue_clear(queue);
+ INIT_LIST_HEAD(&queue->free_pool);
+ kfree(queue->pool);
+ kfree(queue->link_map_cache);
+ queue->pool = NULL;
+ queue->capacity = 0;
+
+ return 0;
+}
+
+size_t cw1200_queue_get_num_queued(struct cw1200_queue *queue,
+ u32 allowed_mask)
+{
+ unsigned long flags;
+ size_t ret;
+ int i, bit;
+
+ if (!allowed_mask)
+ return 0;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ if (likely(allowed_mask == (u32) -1))
+ ret = queue->num_queued - queue->num_pending;
+ else {
+ ret = 0;
+ for (i = 0, bit = 1; i < queue->map_capacity; ++i, bit <<= 1) {
+ if (allowed_mask & bit)
+ ret += queue->link_map_cache[i];
+ }
+ }
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return ret;
+}
+
+int cw1200_queue_put(struct cw1200_queue *queue, struct cw1200_common *priv,
+ struct sk_buff *skb, u8 link_id)
+{
+ int ret;
+ unsigned long flags;
+ struct wsm_tx *wsm;
+
+ wsm = (struct wsm_tx *)skb_push(skb, sizeof(struct wsm_tx));
+ ret = cw1200_skb_to_wsm(priv, skb, wsm);
+ if (ret)
+ return ret;
+
+ if (link_id >= queue->map_capacity)
+ return -EINVAL;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ if (!WARN_ON(list_empty(&queue->free_pool))) {
+ struct cw1200_queue_item *item = list_first_entry(
+ &queue->free_pool, struct cw1200_queue_item, head);
+ BUG_ON(item->skb);
+
+ list_move_tail(&item->head, &queue->queue);
+ item->skb = skb;
+ item->packetID = cw1200_queue_make_packet_id(
+ queue->generation, queue->queue_id,
+ item->generation, item - queue->pool);
+ wsm->packetID = __cpu_to_le32(item->packetID);
+ item->link_id = link_id;
+
+ ++queue->num_queued;
+ ++queue->link_map_cache[link_id];
+
+ if (queue->num_queued >= queue->capacity) {
+ queue->overfull = true;
+ __cw1200_queue_lock(queue, priv);
+ }
+ } else {
+ ret = -ENOENT;
+ }
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return ret;
+}
+
+int cw1200_queue_get(struct cw1200_queue *queue,
+ u32 allowed_mask,
+ struct wsm_tx **tx,
+ struct ieee80211_tx_info **tx_info)
+{
+ int ret = -ENOENT;
+ unsigned long flags;
+ struct cw1200_queue_item *item;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ list_for_each_entry(item, &queue->queue, head) {
+ if (allowed_mask & (1 << item->link_id)) {
+ ret = 0;
+ break;
+ }
+ }
+
+ if (!WARN_ON(ret)) {
+ *tx = (struct wsm_tx *)item->skb->data;
+ *tx_info = IEEE80211_SKB_CB(item->skb);
+ list_move_tail(&item->head, &queue->pending);
+ ++queue->num_pending;
+ --queue->link_map_cache[item->link_id];
+ }
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return ret;
+}
+
+int cw1200_queue_requeue(struct cw1200_queue *queue, u32 packetID)
+{
+ int ret = 0;
+ unsigned long flags;
+ u8 queue_generation, queue_id, item_generation, item_id;
+ struct cw1200_queue_item *item;
+ cw1200_queue_parse_id(packetID, &queue_generation, &queue_id,
+ &item_generation, &item_id);
+
+ item = &queue->pool[item_id];
+
+ spin_lock_irqsave(&queue->lock, flags);
+ BUG_ON(queue_id != queue->queue_id);
+ if (unlikely(queue_generation != queue->generation)) {
+ ret = -ENOENT;
+ } else if (unlikely(item_id >= (unsigned) queue->capacity)) {
+ WARN_ON(1);
+ ret = -EINVAL;
+ } else if (unlikely(item->generation != item_generation)) {
+ WARN_ON(1);
+ ret = -ENOENT;
+ } else {
+ struct wsm_tx *wsm = (struct wsm_tx *)item->skb->data;
+ --queue->num_pending;
+ ++queue->link_map_cache[item->link_id];
+ item->generation = ++item_generation;
+ item->packetID = cw1200_queue_make_packet_id(
+ queue_generation, queue_id, item_generation, item_id);
+ wsm->packetID = __cpu_to_le32(item->packetID);
+ list_move_tail(&item->head, &queue->queue);
+ }
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return ret;
+}
+
+int cw1200_queue_requeue_all(struct cw1200_queue *queue)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ while (!list_empty(&queue->pending)) {
+ struct cw1200_queue_item *item = list_first_entry(
+ &queue->pending, struct cw1200_queue_item, head);
+ struct wsm_tx *wsm = (struct wsm_tx *)item->skb->data;
+
+ --queue->num_pending;
+ ++queue->link_map_cache[item->link_id];
+ ++item->generation;
+ item->packetID = cw1200_queue_make_packet_id(
+ queue->generation, queue->queue_id,
+ item->generation, item - queue->pool);
+ wsm->packetID = __cpu_to_le32(item->packetID);
+ list_move_tail(&item->head, &queue->queue);
+ }
+ spin_unlock_irqrestore(&queue->lock, flags);
+
+ return 0;
+}
+
+int cw1200_queue_remove(struct cw1200_queue *queue, struct cw1200_common *priv,
+ u32 packetID)
+{
+ int ret = 0;
+ unsigned long flags;
+ u8 queue_generation, queue_id, item_generation, item_id;
+ struct cw1200_queue_item *item;
+ struct sk_buff *skb_to_free = NULL;
+ cw1200_queue_parse_id(packetID, &queue_generation, &queue_id,
+ &item_generation, &item_id);
+
+ item = &queue->pool[item_id];
+
+ spin_lock_irqsave(&queue->lock, flags);
+ BUG_ON(queue_id != queue->queue_id);
+ if (unlikely(queue_generation != queue->generation)) {
+ ret = -ENOENT;
+ } else if (unlikely(item_id >= (unsigned) queue->capacity)) {
+ WARN_ON(1);
+ ret = -EINVAL;
+ } else if (unlikely(item->generation != item_generation)) {
+ WARN_ON(1);
+ ret = -ENOENT;
+ } else {
+ --queue->num_pending;
+ --queue->num_queued;
+ ++queue->num_sent;
+ ++item->generation;
+ skb_to_free = item->skb;
+ item->skb = NULL;
+ /* Do not use list_move_tail here, but list_move:
+ * try to utilize cache row.
+ */
+ list_move(&item->head, &queue->free_pool);
+
+ if (unlikely(queue->overfull) &&
+ (queue->num_queued <= (queue->capacity >> 1))) {
+ queue->overfull = false;
+ __cw1200_queue_unlock(queue, priv);
+ }
+ }
+ spin_unlock_irqrestore(&queue->lock, flags);
+
+ if (skb_to_free)
+ dev_kfree_skb_any(item->skb);
+
+ return ret;
+}
+
+int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID,
+ struct sk_buff **skb)
+{
+ int ret = 0;
+ unsigned long flags;
+ u8 queue_generation, queue_id, item_generation, item_id;
+ struct cw1200_queue_item *item;
+ cw1200_queue_parse_id(packetID, &queue_generation, &queue_id,
+ &item_generation, &item_id);
+
+ item = &queue->pool[item_id];
+
+ spin_lock_irqsave(&queue->lock, flags);
+ BUG_ON(queue_id != queue->queue_id);
+ if (unlikely(queue_generation != queue->generation)) {
+ ret = -ENOENT;
+ } else if (unlikely(item_id >= (unsigned) queue->capacity)) {
+ WARN_ON(1);
+ ret = -EINVAL;
+ } else if (unlikely(item->generation != item_generation)) {
+ WARN_ON(1);
+ ret = -ENOENT;
+ } else {
+ *skb = item->skb;
+ item->skb = NULL;
+ }
+ spin_unlock_irqrestore(&queue->lock, flags);
+ return ret;
+}
+
+void cw1200_queue_lock(struct cw1200_queue *queue, struct cw1200_common *cw1200)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&queue->lock, flags);
+ __cw1200_queue_lock(queue, cw1200);
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+void cw1200_queue_unlock(struct cw1200_queue *queue,
+ struct cw1200_common *cw1200)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&queue->lock, flags);
+ __cw1200_queue_unlock(queue, cw1200);
+ spin_unlock_irqrestore(&queue->lock, flags);
+}
+
+/*
+int cw1200_queue_get_stats(struct cw1200_queue *queue,
+ struct ieee80211_tx_queue_stats *stats)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->lock, flags);
+ stats->len = queue->num_queued;
+ stats->limit = queue->capacity;
+ stats->count = queue->num_sent;
+ spin_unlock_irqrestore(&queue->lock, flags);
+
+ return 0;
+}
+*/
+