diff options
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/commands.c | 31 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/commands.h | 10 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/iwm.h | 10 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/main.c | 11 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/netdev.c | 8 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/rx.c | 66 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/tx.c | 57 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/umac.h | 34 |
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 | |||
933 | int 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 | ||
453 | struct 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 */ |
454 | int iwm_read_mac(struct iwm_priv *iwm, u8 *mac); | 462 | int iwm_read_mac(struct iwm_priv *iwm, u8 *mac); |
455 | int iwm_send_prio_table(struct iwm_priv *iwm); | 463 | int iwm_send_prio_table(struct iwm_priv *iwm); |
@@ -478,6 +486,8 @@ int iwm_send_umac_channel_list(struct iwm_priv *iwm); | |||
478 | int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, | 486 | int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, |
479 | int ssid_num); | 487 | int ssid_num); |
480 | int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len); | 488 | int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len); |
489 | int 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 */ |
483 | int iwm_target_reset(struct iwm_priv *iwm); | 493 | int 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 | ||
134 | struct iwm_tid_info { | ||
135 | __le16 last_seq_num; | ||
136 | bool stopped; | ||
137 | struct mutex mutex; | ||
138 | }; | ||
139 | |||
134 | struct iwm_sta_info { | 140 | struct 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 | ||
141 | struct iwm_tx_info { | 148 | struct iwm_tx_info { |
@@ -185,6 +192,8 @@ struct iwm_key { | |||
185 | struct iwm_tx_queue { | 192 | struct 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); | |||
341 | int iwm_down(struct iwm_priv *iwm); | 350 | int iwm_down(struct iwm_priv *iwm); |
342 | 351 | ||
343 | /* TX API */ | 352 | /* TX API */ |
353 | u16 iwm_tid_to_queue(u16 tid); | ||
344 | void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages); | 354 | void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages); |
345 | void iwm_tx_worker(struct work_struct *work); | 355 | void iwm_tx_worker(struct work_struct *work); |
346 | int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev); | 356 | int 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 | ||
249 | int iwm_priv_init(struct iwm_priv *iwm) | 249 | int 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 | */ |
77 | static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; | 77 | static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; |
78 | 78 | ||
79 | u16 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 | |||
79 | static u16 iwm_select_queue(struct net_device *dev, struct sk_buff *skb) | 87 | static 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 | ||
1090 | static 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 | |||
1090 | static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf, | 1155 | static 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 | ||
335 | static int iwm_tx_send_concat_packets(struct iwm_priv *iwm, | 335 | static 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 | ||
696 | struct iwm_umac_notif_stats { | 713 | struct 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 | |||
767 | struct 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 */ |
746 | struct iwm_umac_wifi_if { | 776 | struct iwm_umac_wifi_if { |
747 | u8 oid; | 777 | u8 oid; |