summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/staging/cw1200/bh.c4
-rw-r--r--drivers/staging/cw1200/queue.c28
-rw-r--r--drivers/staging/cw1200/queue.h3
-rw-r--r--drivers/staging/cw1200/wsm.c67
-rw-r--r--drivers/staging/cw1200/wsm.h2
5 files changed, 79 insertions, 25 deletions
diff --git a/drivers/staging/cw1200/bh.c b/drivers/staging/cw1200/bh.c
index fe8ecc1287f..22b43a5f1cd 100644
--- a/drivers/staging/cw1200/bh.c
+++ b/drivers/staging/cw1200/bh.c
@@ -294,10 +294,10 @@ static int cw1200_bh(void *arg)
term = atomic_xchg(&priv->bh_term, 0);
suspend = pending_tx ?
0 : atomic_read(&priv->bh_suspend);
- (rx || tx || term || suspend);
+ (rx || tx || term || suspend || priv->bh_error);
}), status);
- if (status < 0 || term)
+ if (status < 0 || term || priv->bh_error)
break;
if (!status && priv->hw_bufs_used) {
diff --git a/drivers/staging/cw1200/queue.c b/drivers/staging/cw1200/queue.c
index ba2cc436eaf..014e6b2a8a4 100644
--- a/drivers/staging/cw1200/queue.c
+++ b/drivers/staging/cw1200/queue.c
@@ -20,7 +20,8 @@
struct list_head head;
struct sk_buff *skb;
u32 packetID;
- unsigned long timestamp;
+ unsigned long queue_timestamp;
+ unsigned long xmit_timestamp;
struct cw1200_txpriv txpriv;
u8 generation;
};
@@ -102,7 +103,7 @@ static void __cw1200_queue_gc(struct cw1200_queue *queue,
while (!list_empty(&queue->queue)) {
item = list_first_entry(
&queue->queue, struct cw1200_queue_item, head);
- if (jiffies - item->timestamp < queue->ttl)
+ if (jiffies - item->queue_timestamp < queue->ttl)
break;
--queue->num_queued;
--queue->link_map_cache[item->txpriv.link_id];
@@ -126,7 +127,7 @@ static void __cw1200_queue_gc(struct cw1200_queue *queue,
if (unlock)
__cw1200_queue_unlock(queue);
} else {
- unsigned long tmo = item->timestamp + queue->ttl;
+ unsigned long tmo = item->queue_timestamp + queue->ttl;
mod_timer(&queue->gc, tmo);
cw1200_pm_stay_awake(&stats->priv->pm_state,
tmo - jiffies);
@@ -309,7 +310,7 @@ int cw1200_queue_put(struct cw1200_queue *queue,
item->packetID = cw1200_queue_make_packet_id(
queue->generation, queue->queue_id,
item->generation, item - queue->pool);
- item->timestamp = jiffies;
+ item->queue_timestamp = jiffies;
++queue->num_queued;
++queue->link_map_cache[txpriv->link_id];
@@ -364,6 +365,7 @@ int cw1200_queue_get(struct cw1200_queue *queue,
list_move_tail(&item->head, &queue->pending);
++queue->num_pending;
--queue->link_map_cache[item->txpriv.link_id];
+ item->xmit_timestamp = jiffies;
spin_lock_bh(&stats->lock);
--stats->num_queued;
@@ -539,6 +541,24 @@ void cw1200_queue_unlock(struct cw1200_queue *queue)
spin_unlock_bh(&queue->lock);
}
+bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
+ unsigned long *timestamp)
+{
+ struct cw1200_queue_item *item;
+ bool ret;
+
+ spin_lock_bh(&queue->lock);
+ ret = !list_empty(&queue->pending);
+ if (ret) {
+ list_for_each_entry(item, &queue->pending, head) {
+ if (time_before(item->xmit_timestamp, *timestamp))
+ *timestamp = item->xmit_timestamp;
+ }
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
u32 link_id_map)
{
diff --git a/drivers/staging/cw1200/queue.h b/drivers/staging/cw1200/queue.h
index 2403b2519ba..aa9a7f9444c 100644
--- a/drivers/staging/cw1200/queue.h
+++ b/drivers/staging/cw1200/queue.h
@@ -96,6 +96,9 @@ int cw1200_queue_get_skb(struct cw1200_queue *queue, u32 packetID,
const struct cw1200_txpriv **txpriv);
void cw1200_queue_lock(struct cw1200_queue *queue);
void cw1200_queue_unlock(struct cw1200_queue *queue);
+bool cw1200_queue_get_xmit_timestamp(struct cw1200_queue *queue,
+ unsigned long *timestamp);
+
bool cw1200_queue_stats_is_empty(struct cw1200_queue_stats *stats,
u32 link_id_map);
diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c
index 21df2bf0bfe..d1ea95c28e4 100644
--- a/drivers/staging/cw1200/wsm.c
+++ b/drivers/staging/cw1200/wsm.c
@@ -33,7 +33,7 @@
#define WSM_CMD_JOIN_TIMEOUT (7 * HZ) /* Join timeout is 5 sec. in FW */
#define WSM_CMD_START_TIMEOUT (7 * HZ)
#define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */
-#define WSM_CMD_LAST_CHANCE_TIMEOUT (10 * HZ)
+#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 3 / 2)
#define WSM_SKIP(buf, size) \
do { \
@@ -1082,6 +1082,10 @@ int wsm_cmd_send(struct cw1200_common *priv,
priv->wsm_cmd_wq, priv->wsm_cmd.done,
WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0);
}
+
+ /* Kill BH thread to report the error to the top layer. */
+ priv->bh_error = 1;
+ wake_up(&priv->bh_wq);
ret = -ETIMEDOUT;
} else {
spin_lock(&priv->wsm_cmd.lock);
@@ -1100,15 +1104,8 @@ void wsm_lock_tx(struct cw1200_common *priv)
{
wsm_cmd_lock(priv);
if (atomic_add_return(1, &priv->tx_lock) == 1) {
- if (priv->bh_error) {
- wsm_printk(KERN_ERR "fatal error occured, "
- "could not take lock\n");
- } else {
- WARN_ON(wait_event_timeout(priv->bh_evt_wq,
- !priv->hw_bufs_used,
- WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0);
+ if (wsm_flush_tx(priv))
wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n");
- }
}
wsm_cmd_unlock(priv);
}
@@ -1116,18 +1113,52 @@ void wsm_lock_tx(struct cw1200_common *priv)
void wsm_lock_tx_async(struct cw1200_common *priv)
{
if (atomic_add_return(1, &priv->tx_lock) == 1)
- wsm_printk(KERN_DEBUG "[WSM] TX is locked.\n");
+ wsm_printk(KERN_DEBUG "[WSM] TX is locked (async).\n");
}
-void wsm_flush_tx(struct cw1200_common *priv)
+bool wsm_flush_tx(struct cw1200_common *priv)
{
- if (priv->bh_error)
- wsm_printk(KERN_ERR "fatal error occured, will not flush\n");
- else {
- BUG_ON(!atomic_read(&priv->tx_lock));
- WARN_ON(wait_event_timeout(priv->bh_evt_wq,
- !priv->hw_bufs_used,
- WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0);
+ unsigned long timestamp = jiffies;
+ bool pending = false;
+ long timeout;
+ int i;
+
+ /* Flush must be called with TX lock held. */
+ BUG_ON(!atomic_read(&priv->tx_lock));
+
+ /* First check if we really need to do something.
+ * It is safe to use unprotected access, as hw_bufs_used
+ * can only decrements. */
+ if (!priv->hw_bufs_used)
+ return true;
+
+ if (priv->bh_error) {
+ /* In case of failure do not wait for magic. */
+ wsm_printk(KERN_ERR "[WSM] Fatal error occured, "
+ "will not flush TX.\n");
+ return false;
+ } else {
+ /* Get a timestamp of "oldest" frame */
+ for (i = 0; i < 4; ++i)
+ pending |= cw1200_queue_get_xmit_timestamp(
+ &priv->tx_queue[i],
+ &timestamp);
+ /* It is allowed to lock TX with only a command in the pipe. */
+ if (!pending)
+ return true;
+
+ timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies;
+ if (timeout < 0 || wait_event_timeout(priv->bh_evt_wq,
+ !priv->hw_bufs_used,
+ timeout) <= 0) {
+ /* Hmmm... Not good. Frame had stuck in firmware. */
+ priv->bh_error = 1;
+ wake_up(&priv->bh_wq);
+ return false;
+ }
+
+ /* Ok, everything is flushed. */
+ return true;
}
}
diff --git a/drivers/staging/cw1200/wsm.h b/drivers/staging/cw1200/wsm.h
index 9c66c1ab654..fb15ef53294 100644
--- a/drivers/staging/cw1200/wsm.h
+++ b/drivers/staging/cw1200/wsm.h
@@ -1765,7 +1765,7 @@ static inline int wsm_set_override_internal_txrate(struct cw1200_common *priv,
void wsm_lock_tx(struct cw1200_common *priv);
void wsm_lock_tx_async(struct cw1200_common *priv);
-void wsm_flush_tx(struct cw1200_common *priv);
+bool wsm_flush_tx(struct cw1200_common *priv);
void wsm_unlock_tx(struct cw1200_common *priv);
/* ******************************************************************** */