aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamuel Ortiz <sameo@linux.intel.com>2009-11-23 22:33:31 -0500
committerJohn W. Linville <linville@tuxdriver.com>2009-11-28 15:04:44 -0500
commita7af530d45969a63e20708417b70c547596ce3a9 (patch)
treec643269ad98d2689e1a011e13e2568615f01b0df
parent2351178c52fedf1846c84b35418f4102487ec00e (diff)
iwmc3200wifi: 802.11n Tx aggregation support
To support 802.11n Tx aggregation support with iwmc3200 wifi, we have to handle the UMAC_CMD_OPCODE_STOP_RESUME_STA_TX notification from the UMAC. Before sending an AddBA, the UMAC synchronizes with the host in order to know what is the last Tx frame it's supposed to receive before it will be able to start the actual aggregation session. We thus have to keep track of the last sequence number that is scheduled for transmission on a particular RAxTID, send an answer to the UMAC with this sequence number. The UMAC then does the BA negociation and once it's done with it sends a new UMAC_CMD_OPCODE_STOP_RESUME_STA_TX notification to let us know that we can resume the Tx flow on the specified RAxTID. Signed-off-by: Samuel Ortiz <sameo@linux.intel.com> Reviewed-by: Zhu Yi <yi.zhu@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/iwmc3200wifi/commands.c31
-rw-r--r--drivers/net/wireless/iwmc3200wifi/commands.h10
-rw-r--r--drivers/net/wireless/iwmc3200wifi/iwm.h10
-rw-r--r--drivers/net/wireless/iwmc3200wifi/main.c11
-rw-r--r--drivers/net/wireless/iwmc3200wifi/netdev.c8
-rw-r--r--drivers/net/wireless/iwmc3200wifi/rx.c66
-rw-r--r--drivers/net/wireless/iwmc3200wifi/tx.c57
-rw-r--r--drivers/net/wireless/iwmc3200wifi/umac.h34
8 files changed, 217 insertions, 10 deletions
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c
index 7e12438551ba..46ca7c58f5ac 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.c
+++ b/drivers/net/wireless/iwmc3200wifi/commands.c
@@ -929,3 +929,34 @@ int iwm_target_reset(struct iwm_priv *iwm)
929 929
930 return iwm_hal_send_target_cmd(iwm, &target_cmd, NULL); 930 return iwm_hal_send_target_cmd(iwm, &target_cmd, NULL);
931} 931}
932
933int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm,
934 struct iwm_umac_notif_stop_resume_tx *ntf)
935{
936 struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT;
937 struct iwm_umac_cmd umac_cmd;
938 struct iwm_umac_cmd_stop_resume_tx stp_res_cmd;
939 struct iwm_sta_info *sta_info;
940 u8 sta_id = STA_ID_N_COLOR_ID(ntf->sta_id);
941 int i;
942
943 sta_info = &iwm->sta_table[sta_id];
944 if (!sta_info->valid) {
945 IWM_ERR(iwm, "Invalid STA: %d\n", sta_id);
946 return -EINVAL;
947 }
948
949 umac_cmd.id = UMAC_CMD_OPCODE_STOP_RESUME_STA_TX;
950 umac_cmd.resp = 0;
951
952 stp_res_cmd.flags = ntf->flags;
953 stp_res_cmd.sta_id = ntf->sta_id;
954 stp_res_cmd.stop_resume_tid_msk = ntf->stop_resume_tid_msk;
955 for (i = 0; i < IWM_UMAC_TID_NR; i++)
956 stp_res_cmd.last_seq_num[i] =
957 sta_info->tid_info[i].last_seq_num;
958
959 return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stp_res_cmd,
960 sizeof(struct iwm_umac_cmd_stop_resume_tx));
961
962}
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.h b/drivers/net/wireless/iwmc3200wifi/commands.h
index b36be2b23a3c..95cdf941b222 100644
--- a/drivers/net/wireless/iwmc3200wifi/commands.h
+++ b/drivers/net/wireless/iwmc3200wifi/commands.h
@@ -450,6 +450,14 @@ struct iwm_umac_cmd_stats_req {
450 __le32 flags; 450 __le32 flags;
451} __attribute__ ((packed)); 451} __attribute__ ((packed));
452 452
453struct iwm_umac_cmd_stop_resume_tx {
454 u8 flags;
455 u8 sta_id;
456 __le16 stop_resume_tid_msk;
457 __le16 last_seq_num[IWM_UMAC_TID_NR];
458 u16 reserved;
459} __attribute__ ((packed));
460
453/* LMAC commands */ 461/* LMAC commands */
454int iwm_read_mac(struct iwm_priv *iwm, u8 *mac); 462int iwm_read_mac(struct iwm_priv *iwm, u8 *mac);
455int iwm_send_prio_table(struct iwm_priv *iwm); 463int iwm_send_prio_table(struct iwm_priv *iwm);
@@ -478,6 +486,8 @@ int iwm_send_umac_channel_list(struct iwm_priv *iwm);
478int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, 486int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids,
479 int ssid_num); 487 int ssid_num);
480int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len); 488int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len);
489int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm,
490 struct iwm_umac_notif_stop_resume_tx *ntf);
481 491
482/* UDMA commands */ 492/* UDMA commands */
483int iwm_target_reset(struct iwm_priv *iwm); 493int iwm_target_reset(struct iwm_priv *iwm);
diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index a9bf6bc97bea..8d091f918f33 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -131,11 +131,18 @@ struct iwm_notif {
131 unsigned long buf_size; 131 unsigned long buf_size;
132}; 132};
133 133
134struct iwm_tid_info {
135 __le16 last_seq_num;
136 bool stopped;
137 struct mutex mutex;
138};
139
134struct iwm_sta_info { 140struct iwm_sta_info {
135 u8 addr[ETH_ALEN]; 141 u8 addr[ETH_ALEN];
136 bool valid; 142 bool valid;
137 bool qos; 143 bool qos;
138 u8 color; 144 u8 color;
145 struct iwm_tid_info tid_info[IWM_UMAC_TID_NR];
139}; 146};
140 147
141struct iwm_tx_info { 148struct iwm_tx_info {
@@ -185,6 +192,8 @@ struct iwm_key {
185struct iwm_tx_queue { 192struct iwm_tx_queue {
186 int id; 193 int id;
187 struct sk_buff_head queue; 194 struct sk_buff_head queue;
195 struct sk_buff_head stopped_queue;
196 spinlock_t lock;
188 struct workqueue_struct *wq; 197 struct workqueue_struct *wq;
189 struct work_struct worker; 198 struct work_struct worker;
190 u8 concat_buf[IWM_HAL_CONCATENATE_BUF_SIZE]; 199 u8 concat_buf[IWM_HAL_CONCATENATE_BUF_SIZE];
@@ -341,6 +350,7 @@ int iwm_up(struct iwm_priv *iwm);
341int iwm_down(struct iwm_priv *iwm); 350int iwm_down(struct iwm_priv *iwm);
342 351
343/* TX API */ 352/* TX API */
353u16 iwm_tid_to_queue(u16 tid);
344void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages); 354void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages);
345void iwm_tx_worker(struct work_struct *work); 355void iwm_tx_worker(struct work_struct *work);
346int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev); 356int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev);
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index 92e4eaf32ff2..087f04355c1b 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -248,7 +248,7 @@ static void iwm_watchdog(unsigned long data)
248 248
249int iwm_priv_init(struct iwm_priv *iwm) 249int iwm_priv_init(struct iwm_priv *iwm)
250{ 250{
251 int i; 251 int i, j;
252 char name[32]; 252 char name[32];
253 253
254 iwm->status = 0; 254 iwm->status = 0;
@@ -292,6 +292,8 @@ int iwm_priv_init(struct iwm_priv *iwm)
292 return -EAGAIN; 292 return -EAGAIN;
293 293
294 skb_queue_head_init(&iwm->txq[i].queue); 294 skb_queue_head_init(&iwm->txq[i].queue);
295 skb_queue_head_init(&iwm->txq[i].stopped_queue);
296 spin_lock_init(&iwm->txq[i].lock);
295 } 297 }
296 298
297 for (i = 0; i < IWM_NUM_KEYS; i++) 299 for (i = 0; i < IWM_NUM_KEYS; i++)
@@ -299,6 +301,12 @@ int iwm_priv_init(struct iwm_priv *iwm)
299 301
300 iwm->default_key = -1; 302 iwm->default_key = -1;
301 303
304 for (i = 0; i < IWM_STA_TABLE_NUM; i++)
305 for (j = 0; j < IWM_UMAC_TID_NR; j++) {
306 mutex_init(&iwm->sta_table[i].tid_info[j].mutex);
307 iwm->sta_table[i].tid_info[j].stopped = false;
308 }
309
302 init_timer(&iwm->watchdog); 310 init_timer(&iwm->watchdog);
303 iwm->watchdog.function = iwm_watchdog; 311 iwm->watchdog.function = iwm_watchdog;
304 iwm->watchdog.data = (unsigned long)iwm; 312 iwm->watchdog.data = (unsigned long)iwm;
@@ -572,6 +580,7 @@ void iwm_link_off(struct iwm_priv *iwm)
572 580
573 for (i = 0; i < IWM_TX_QUEUES; i++) { 581 for (i = 0; i < IWM_TX_QUEUES; i++) {
574 skb_queue_purge(&iwm->txq[i].queue); 582 skb_queue_purge(&iwm->txq[i].queue);
583 skb_queue_purge(&iwm->txq[i].stopped_queue);
575 584
576 iwm->txq[i].concat_count = 0; 585 iwm->txq[i].concat_count = 0;
577 iwm->txq[i].concat_ptr = iwm->txq[i].concat_buf; 586 iwm->txq[i].concat_ptr = iwm->txq[i].concat_buf;
diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
index 4f8dbdd7b917..e4f0f8705f65 100644
--- a/drivers/net/wireless/iwmc3200wifi/netdev.c
+++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
@@ -76,6 +76,14 @@ static int iwm_stop(struct net_device *ndev)
76 */ 76 */
77static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; 77static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
78 78
79u16 iwm_tid_to_queue(u16 tid)
80{
81 if (tid > IWM_UMAC_TID_NR - 2)
82 return -EINVAL;
83
84 return iwm_1d_to_queue[tid];
85}
86
79static u16 iwm_select_queue(struct net_device *dev, struct sk_buff *skb) 87static u16 iwm_select_queue(struct net_device *dev, struct sk_buff *skb)
80{ 88{
81 skb->priority = cfg80211_classify8021d(skb); 89 skb->priority = cfg80211_classify8021d(skb);
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
index bdb1d7e7979d..72c27a3e5528 100644
--- a/drivers/net/wireless/iwmc3200wifi/rx.c
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -1087,6 +1087,71 @@ static int iwm_ntf_channel_info_list(struct iwm_priv *iwm, u8 *buf,
1087 return 0; 1087 return 0;
1088} 1088}
1089 1089
1090static int iwm_ntf_stop_resume_tx(struct iwm_priv *iwm, u8 *buf,
1091 unsigned long buf_size,
1092 struct iwm_wifi_cmd *cmd)
1093{
1094 struct iwm_umac_notif_stop_resume_tx *stp_res_tx =
1095 (struct iwm_umac_notif_stop_resume_tx *)buf;
1096 struct iwm_sta_info *sta_info;
1097 struct iwm_tid_info *tid_info;
1098 u8 sta_id = STA_ID_N_COLOR_ID(stp_res_tx->sta_id);
1099 u16 tid_msk = le16_to_cpu(stp_res_tx->stop_resume_tid_msk);
1100 int bit, ret = 0;
1101 bool stop = false;
1102
1103 IWM_DBG_NTF(iwm, DBG, "stop/resume notification:\n"
1104 "\tflags: 0x%x\n"
1105 "\tSTA id: %d\n"
1106 "\tTID bitmask: 0x%x\n",
1107 stp_res_tx->flags, stp_res_tx->sta_id,
1108 stp_res_tx->stop_resume_tid_msk);
1109
1110 if (stp_res_tx->flags & UMAC_STOP_TX_FLAG)
1111 stop = true;
1112
1113 sta_info = &iwm->sta_table[sta_id];
1114 if (!sta_info->valid) {
1115 IWM_ERR(iwm, "Stoping an invalid STA: %d %d\n",
1116 sta_id, stp_res_tx->sta_id);
1117 return -EINVAL;
1118 }
1119
1120 for_each_bit(bit, (unsigned long *)&tid_msk, IWM_UMAC_TID_NR) {
1121 tid_info = &sta_info->tid_info[bit];
1122
1123 mutex_lock(&tid_info->mutex);
1124 tid_info->stopped = stop;
1125 mutex_unlock(&tid_info->mutex);
1126
1127 if (!stop) {
1128 struct iwm_tx_queue *txq;
1129 u16 queue = iwm_tid_to_queue(bit);
1130
1131 if (queue < 0)
1132 continue;
1133
1134 txq = &iwm->txq[queue];
1135 /*
1136 * If we resume, we have to move our SKBs
1137 * back to the tx queue and queue some work.
1138 */
1139 spin_lock_bh(&txq->lock);
1140 skb_queue_splice_init(&txq->queue, &txq->stopped_queue);
1141 spin_unlock_bh(&txq->lock);
1142
1143 queue_work(txq->wq, &txq->worker);
1144 }
1145
1146 }
1147
1148 /* We send an ACK only for the stop case */
1149 if (stop)
1150 ret = iwm_send_umac_stop_resume_tx(iwm, stp_res_tx);
1151
1152 return ret;
1153}
1154
1090static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf, 1155static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf,
1091 unsigned long buf_size, 1156 unsigned long buf_size,
1092 struct iwm_wifi_cmd *cmd) 1157 struct iwm_wifi_cmd *cmd)
@@ -1371,6 +1436,7 @@ static const iwm_handler iwm_umac_handlers[] =
1371 [UMAC_NOTIFY_OPCODE_STATS] = iwm_ntf_statistics, 1436 [UMAC_NOTIFY_OPCODE_STATS] = iwm_ntf_statistics,
1372 [UMAC_CMD_OPCODE_EEPROM_PROXY] = iwm_ntf_eeprom_proxy, 1437 [UMAC_CMD_OPCODE_EEPROM_PROXY] = iwm_ntf_eeprom_proxy,
1373 [UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST] = iwm_ntf_channel_info_list, 1438 [UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST] = iwm_ntf_channel_info_list,
1439 [UMAC_CMD_OPCODE_STOP_RESUME_STA_TX] = iwm_ntf_stop_resume_tx,
1374 [REPLY_RX_MPDU_CMD] = iwm_ntf_rx_packet, 1440 [REPLY_RX_MPDU_CMD] = iwm_ntf_rx_packet,
1375 [UMAC_CMD_OPCODE_WIFI_IF_WRAPPER] = iwm_ntf_wifi_if_wrapper, 1441 [UMAC_CMD_OPCODE_WIFI_IF_WRAPPER] = iwm_ntf_wifi_if_wrapper,
1376}; 1442};
diff --git a/drivers/net/wireless/iwmc3200wifi/tx.c b/drivers/net/wireless/iwmc3200wifi/tx.c
index e3b4f7902daf..01cc2101e682 100644
--- a/drivers/net/wireless/iwmc3200wifi/tx.c
+++ b/drivers/net/wireless/iwmc3200wifi/tx.c
@@ -329,7 +329,7 @@ static int iwm_tx_build_packet(struct iwm_priv *iwm, struct sk_buff *skb,
329 329
330 memcpy(buf + sizeof(*hdr), skb->data, skb->len); 330 memcpy(buf + sizeof(*hdr), skb->data, skb->len);
331 331
332 return 0; 332 return umac_cmd.seq_num;
333} 333}
334 334
335static int iwm_tx_send_concat_packets(struct iwm_priv *iwm, 335static int iwm_tx_send_concat_packets(struct iwm_priv *iwm,
@@ -361,9 +361,10 @@ void iwm_tx_worker(struct work_struct *work)
361 struct iwm_priv *iwm; 361 struct iwm_priv *iwm;
362 struct iwm_tx_info *tx_info = NULL; 362 struct iwm_tx_info *tx_info = NULL;
363 struct sk_buff *skb; 363 struct sk_buff *skb;
364 int cmdlen, ret;
365 struct iwm_tx_queue *txq; 364 struct iwm_tx_queue *txq;
366 int pool_id; 365 struct iwm_sta_info *sta_info;
366 struct iwm_tid_info *tid_info;
367 int cmdlen, ret, pool_id;
367 368
368 txq = container_of(work, struct iwm_tx_queue, worker); 369 txq = container_of(work, struct iwm_tx_queue, worker);
369 iwm = container_of(txq, struct iwm_priv, txq[txq->id]); 370 iwm = container_of(txq, struct iwm_priv, txq[txq->id]);
@@ -373,8 +374,40 @@ void iwm_tx_worker(struct work_struct *work)
373 while (!test_bit(pool_id, &iwm->tx_credit.full_pools_map) && 374 while (!test_bit(pool_id, &iwm->tx_credit.full_pools_map) &&
374 !skb_queue_empty(&txq->queue)) { 375 !skb_queue_empty(&txq->queue)) {
375 376
377 spin_lock_bh(&txq->lock);
376 skb = skb_dequeue(&txq->queue); 378 skb = skb_dequeue(&txq->queue);
379 spin_unlock_bh(&txq->lock);
380
377 tx_info = skb_to_tx_info(skb); 381 tx_info = skb_to_tx_info(skb);
382 sta_info = &iwm->sta_table[tx_info->sta];
383 if (!sta_info->valid) {
384 IWM_ERR(iwm, "Trying to send a frame to unknown STA\n");
385 kfree_skb(skb);
386 continue;
387 }
388
389 tid_info = &sta_info->tid_info[tx_info->tid];
390
391 mutex_lock(&tid_info->mutex);
392
393 /*
394 * If the RAxTID is stopped, we queue the skb to the stopped
395 * queue.
396 * Whenever we'll get a UMAC notification to resume the tx flow
397 * for this RAxTID, we'll merge back the stopped queue into the
398 * regular queue. See iwm_ntf_stop_resume_tx() from rx.c.
399 */
400 if (tid_info->stopped) {
401 IWM_DBG_TX(iwm, DBG, "%dx%d stopped\n",
402 tx_info->sta, tx_info->tid);
403 spin_lock_bh(&txq->lock);
404 skb_queue_tail(&txq->stopped_queue, skb);
405 spin_unlock_bh(&txq->lock);
406
407 mutex_unlock(&tid_info->mutex);
408 continue;
409 }
410
378 cmdlen = IWM_UDMA_HDR_LEN + skb->len; 411 cmdlen = IWM_UDMA_HDR_LEN + skb->len;
379 412
380 IWM_DBG_TX(iwm, DBG, "Tx frame on queue %d: skb: 0x%p, sta: " 413 IWM_DBG_TX(iwm, DBG, "Tx frame on queue %d: skb: 0x%p, sta: "
@@ -393,13 +426,20 @@ void iwm_tx_worker(struct work_struct *work)
393 if (ret) { 426 if (ret) {
394 IWM_DBG_TX(iwm, DBG, "not enough tx_credit for queue " 427 IWM_DBG_TX(iwm, DBG, "not enough tx_credit for queue "
395 "%d, Tx worker stopped\n", txq->id); 428 "%d, Tx worker stopped\n", txq->id);
429 spin_lock_bh(&txq->lock);
396 skb_queue_head(&txq->queue, skb); 430 skb_queue_head(&txq->queue, skb);
431 spin_unlock_bh(&txq->lock);
432
433 mutex_unlock(&tid_info->mutex);
397 break; 434 break;
398 } 435 }
399 436
400 txq->concat_ptr = txq->concat_buf + txq->concat_count; 437 txq->concat_ptr = txq->concat_buf + txq->concat_count;
401 iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr); 438 tid_info->last_seq_num =
439 iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr);
402 txq->concat_count += ALIGN(cmdlen, 16); 440 txq->concat_count += ALIGN(cmdlen, 16);
441
442 mutex_unlock(&tid_info->mutex);
403#endif 443#endif
404 kfree_skb(skb); 444 kfree_skb(skb);
405 } 445 }
@@ -419,14 +459,14 @@ int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
419 struct iwm_priv *iwm = ndev_to_iwm(netdev); 459 struct iwm_priv *iwm = ndev_to_iwm(netdev);
420 struct net_device *ndev = iwm_to_ndev(iwm); 460 struct net_device *ndev = iwm_to_ndev(iwm);
421 struct wireless_dev *wdev = iwm_to_wdev(iwm); 461 struct wireless_dev *wdev = iwm_to_wdev(iwm);
422 u8 *dst_addr;
423 struct iwm_tx_info *tx_info; 462 struct iwm_tx_info *tx_info;
424 struct iwm_tx_queue *txq; 463 struct iwm_tx_queue *txq;
425 struct iwm_sta_info *sta_info; 464 struct iwm_sta_info *sta_info;
426 u8 sta_id; 465 u8 *dst_addr, sta_id;
427 u16 queue; 466 u16 queue;
428 int ret; 467 int ret;
429 468
469
430 if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { 470 if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) {
431 IWM_DBG_TX(iwm, DBG, "LINK: stop netif_all_queues: " 471 IWM_DBG_TX(iwm, DBG, "LINK: stop netif_all_queues: "
432 "not associated\n"); 472 "not associated\n");
@@ -440,7 +480,8 @@ int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
440 txq = &iwm->txq[queue]; 480 txq = &iwm->txq[queue];
441 481
442 /* No free space for Tx, tx_worker is too slow */ 482 /* No free space for Tx, tx_worker is too slow */
443 if (skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) { 483 if ((skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) ||
484 (skb_queue_len(&txq->stopped_queue) > IWM_TX_LIST_SIZE)) {
444 IWM_DBG_TX(iwm, DBG, "LINK: stop netif_subqueue[%d]\n", queue); 485 IWM_DBG_TX(iwm, DBG, "LINK: stop netif_subqueue[%d]\n", queue);
445 netif_stop_subqueue(netdev, queue); 486 netif_stop_subqueue(netdev, queue);
446 return NETDEV_TX_BUSY; 487 return NETDEV_TX_BUSY;
@@ -477,7 +518,9 @@ int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
477 else 518 else
478 tx_info->tid = IWM_UMAC_MGMT_TID; 519 tx_info->tid = IWM_UMAC_MGMT_TID;
479 520
521 spin_lock_bh(&iwm->txq[queue].lock);
480 skb_queue_tail(&iwm->txq[queue].queue, skb); 522 skb_queue_tail(&iwm->txq[queue].queue, skb);
523 spin_unlock_bh(&iwm->txq[queue].lock);
481 524
482 queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker); 525 queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker);
483 526
diff --git a/drivers/net/wireless/iwmc3200wifi/umac.h b/drivers/net/wireless/iwmc3200wifi/umac.h
index be903543bb47..70094bfe7c91 100644
--- a/drivers/net/wireless/iwmc3200wifi/umac.h
+++ b/drivers/net/wireless/iwmc3200wifi/umac.h
@@ -83,6 +83,20 @@ struct iwm_udma_out_wifi_hdr {
83 ((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\ 83 ((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\
84 (UMAC_HDI_ACT_TBL_IDX_TID_LMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS)) 84 (UMAC_HDI_ACT_TBL_IDX_TID_LMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS))
85 85
86/* STA ID and color */
87#define STA_ID_SEED (0x0f)
88#define STA_ID_POS (0)
89#define STA_ID_MSK (STA_ID_SEED << STA_ID_POS)
90
91#define STA_COLOR_SEED (0x7)
92#define STA_COLOR_POS (4)
93#define STA_COLOR_MSK (STA_COLOR_SEED << STA_COLOR_POS)
94
95#define STA_ID_N_COLOR_COLOR(id_n_color) \
96 (((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS)
97#define STA_ID_N_COLOR_ID(id_n_color) \
98 (((id_n_color) & STA_ID_MSK) >> STA_ID_POS)
99
86/* iwm_umac_notif_alive.page_grp_state Group number -- bits [3:0] */ 100/* iwm_umac_notif_alive.page_grp_state Group number -- bits [3:0] */
87#define UMAC_ALIVE_PAGE_STS_GRP_NUM_POS 0 101#define UMAC_ALIVE_PAGE_STS_GRP_NUM_POS 0
88#define UMAC_ALIVE_PAGE_STS_GRP_NUM_SEED 0xF 102#define UMAC_ALIVE_PAGE_STS_GRP_NUM_SEED 0xF
@@ -260,6 +274,9 @@ struct iwm_udma_out_wifi_hdr {
260#define UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST 0x16 274#define UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST 0x16
261#define UMAC_CMD_OPCODE_SET_PARAM_LIST 0x17 275#define UMAC_CMD_OPCODE_SET_PARAM_LIST 0x17
262#define UMAC_CMD_OPCODE_GET_PARAM_LIST 0x18 276#define UMAC_CMD_OPCODE_GET_PARAM_LIST 0x18
277#define UMAC_CMD_OPCODE_STOP_RESUME_STA_TX 0x19
278#define UMAC_CMD_OPCODE_TEST_BLOCK_ACK 0x1A
279
263#define UMAC_CMD_OPCODE_BASE_WRAPPER 0xFA 280#define UMAC_CMD_OPCODE_BASE_WRAPPER 0xFA
264#define UMAC_CMD_OPCODE_LMAC_WRAPPER 0xFB 281#define UMAC_CMD_OPCODE_LMAC_WRAPPER 0xFB
265#define UMAC_CMD_OPCODE_HW_TEST_WRAPPER 0xFC 282#define UMAC_CMD_OPCODE_HW_TEST_WRAPPER 0xFC
@@ -691,13 +708,13 @@ struct iwm_umac_notif_rx_ticket {
691#define UMAC_PHY_NUM_CHAINS 3 708#define UMAC_PHY_NUM_CHAINS 3
692 709
693#define IWM_UMAC_MGMT_TID 8 710#define IWM_UMAC_MGMT_TID 8
694#define IWM_UMAC_TID_NR 8 711#define IWM_UMAC_TID_NR 9 /* 8 TIDs + MGMT */
695 712
696struct iwm_umac_notif_stats { 713struct iwm_umac_notif_stats {
697 struct iwm_umac_wifi_in_hdr hdr; 714 struct iwm_umac_wifi_in_hdr hdr;
698 __le32 flags; 715 __le32 flags;
699 __le32 timestamp; 716 __le32 timestamp;
700 __le16 tid_load[IWM_UMAC_TID_NR + 2]; /* 1 non-QoS + 1 dword align */ 717 __le16 tid_load[IWM_UMAC_TID_NR + 1]; /* 1 non-QoS + 1 dword align */
701 __le16 tx_rate[UMAC_NTF_RATE_SAMPLE_NR]; 718 __le16 tx_rate[UMAC_NTF_RATE_SAMPLE_NR];
702 __le16 rx_rate[UMAC_NTF_RATE_SAMPLE_NR]; 719 __le16 rx_rate[UMAC_NTF_RATE_SAMPLE_NR];
703 __le32 chain_energy[UMAC_PHY_NUM_CHAINS]; 720 __le32 chain_energy[UMAC_PHY_NUM_CHAINS];
@@ -742,6 +759,19 @@ struct iwm_umac_notif_stats {
742 __le32 roam_ap_loadblance; 759 __le32 roam_ap_loadblance;
743} __attribute__ ((packed)); 760} __attribute__ ((packed));
744 761
762#define UMAC_STOP_TX_FLAG 0x1
763#define UMAC_RESUME_TX_FLAG 0x2
764
765#define LAST_SEQ_NUM_INVALID 0xFFFF
766
767struct iwm_umac_notif_stop_resume_tx {
768 struct iwm_umac_wifi_in_hdr hdr;
769 u8 flags; /* UMAC_*_TX_FLAG_* */
770 u8 sta_id;
771 __le16 stop_resume_tid_msk; /* tid bitmask */
772} __attribute__ ((packed));
773
774
745/* WiFi interface wrapper header */ 775/* WiFi interface wrapper header */
746struct iwm_umac_wifi_if { 776struct iwm_umac_wifi_if {
747 u8 oid; 777 u8 oid;