diff options
author | Herton Ronaldo Krzesinski <herton@mandriva.com.br> | 2008-11-13 10:39:16 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-11-25 16:41:33 -0500 |
commit | 3517afdefc3ad335b276eb5f8691841f48097abf (patch) | |
tree | 150fb2ea5e8dcfe8f07523316a32dc45b94fba79 /drivers/net | |
parent | b4572a9264312fecdb530a3416b8c8336a3bb9a9 (diff) |
rtl8187: feedback transmitted packets using tx close descriptor for 8187B
Realtek 8187B has a receive command queue to feedback beacon interrupt
and transmitted packet status. Use it to feedback mac80211 about status
of transmitted packets. Unfortunately in the course of testing I found
that the sequence number reported by hardware includes entire sequence
control in a 12 bit only field, so a workaround is done to check only
lowest bits.
Tested-by: Larry Finger <Larry.Finger@lwfinger.net>
Tested-by: Hin-Tak Leung <htl10@users.sourceforge.net>
Signed-off-by: Herton Ronaldo Krzesinski <herton@mandriva.com.br>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/rtl818x/rtl8187.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/rtl818x/rtl8187_dev.c | 135 |
2 files changed, 136 insertions, 4 deletions
diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h index f09872eec3d..c385407a994 100644 --- a/drivers/net/wireless/rtl818x/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187.h | |||
@@ -113,6 +113,11 @@ struct rtl8187_priv { | |||
113 | u8 noise; | 113 | u8 noise; |
114 | u8 slot_time; | 114 | u8 slot_time; |
115 | u8 aifsn[4]; | 115 | u8 aifsn[4]; |
116 | struct { | ||
117 | __le64 buf; | ||
118 | struct urb *urb; | ||
119 | struct sk_buff_head queue; | ||
120 | } b_tx_status; | ||
116 | }; | 121 | }; |
117 | 122 | ||
118 | void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); | 123 | void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); |
diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index cb5bcefd1c3..876d4f93d31 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c | |||
@@ -176,8 +176,27 @@ static void rtl8187_tx_cb(struct urb *urb) | |||
176 | skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) : | 176 | skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) : |
177 | sizeof(struct rtl8187_tx_hdr)); | 177 | sizeof(struct rtl8187_tx_hdr)); |
178 | ieee80211_tx_info_clear_status(info); | 178 | ieee80211_tx_info_clear_status(info); |
179 | info->flags |= IEEE80211_TX_STAT_ACK; | 179 | |
180 | ieee80211_tx_status_irqsafe(hw, skb); | 180 | if (!urb->status && |
181 | !(info->flags & IEEE80211_TX_CTL_NO_ACK) && | ||
182 | priv->is_rtl8187b) { | ||
183 | skb_queue_tail(&priv->b_tx_status.queue, skb); | ||
184 | |||
185 | /* queue is "full", discard last items */ | ||
186 | while (skb_queue_len(&priv->b_tx_status.queue) > 5) { | ||
187 | struct sk_buff *old_skb; | ||
188 | |||
189 | dev_dbg(&priv->udev->dev, | ||
190 | "transmit status queue full\n"); | ||
191 | |||
192 | old_skb = skb_dequeue(&priv->b_tx_status.queue); | ||
193 | ieee80211_tx_status_irqsafe(hw, old_skb); | ||
194 | } | ||
195 | } else { | ||
196 | if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !urb->status) | ||
197 | info->flags |= IEEE80211_TX_STAT_ACK; | ||
198 | ieee80211_tx_status_irqsafe(hw, skb); | ||
199 | } | ||
181 | } | 200 | } |
182 | 201 | ||
183 | static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) | 202 | static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) |
@@ -219,7 +238,7 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) | |||
219 | hdr->flags = cpu_to_le32(flags); | 238 | hdr->flags = cpu_to_le32(flags); |
220 | hdr->len = 0; | 239 | hdr->len = 0; |
221 | hdr->rts_duration = rts_dur; | 240 | hdr->rts_duration = rts_dur; |
222 | hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); | 241 | hdr->retry = cpu_to_le32(info->control.rates[0].count << 8); |
223 | buf = hdr; | 242 | buf = hdr; |
224 | 243 | ||
225 | ep = 2; | 244 | ep = 2; |
@@ -237,7 +256,7 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) | |||
237 | memset(hdr, 0, sizeof(*hdr)); | 256 | memset(hdr, 0, sizeof(*hdr)); |
238 | hdr->flags = cpu_to_le32(flags); | 257 | hdr->flags = cpu_to_le32(flags); |
239 | hdr->rts_duration = rts_dur; | 258 | hdr->rts_duration = rts_dur; |
240 | hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); | 259 | hdr->retry = cpu_to_le32(info->control.rates[0].count << 8); |
241 | hdr->tx_duration = | 260 | hdr->tx_duration = |
242 | ieee80211_generic_frame_duration(dev, priv->vif, | 261 | ieee80211_generic_frame_duration(dev, priv->vif, |
243 | skb->len, txrate); | 262 | skb->len, txrate); |
@@ -403,6 +422,109 @@ static int rtl8187_init_urbs(struct ieee80211_hw *dev) | |||
403 | return 0; | 422 | return 0; |
404 | } | 423 | } |
405 | 424 | ||
425 | static void rtl8187b_status_cb(struct urb *urb) | ||
426 | { | ||
427 | struct ieee80211_hw *hw = (struct ieee80211_hw *)urb->context; | ||
428 | struct rtl8187_priv *priv = hw->priv; | ||
429 | u64 val; | ||
430 | unsigned int cmd_type; | ||
431 | |||
432 | if (unlikely(urb->status)) { | ||
433 | usb_free_urb(urb); | ||
434 | return; | ||
435 | } | ||
436 | |||
437 | /* | ||
438 | * Read from status buffer: | ||
439 | * | ||
440 | * bits [30:31] = cmd type: | ||
441 | * - 0 indicates tx beacon interrupt | ||
442 | * - 1 indicates tx close descriptor | ||
443 | * | ||
444 | * In the case of tx beacon interrupt: | ||
445 | * [0:9] = Last Beacon CW | ||
446 | * [10:29] = reserved | ||
447 | * [30:31] = 00b | ||
448 | * [32:63] = Last Beacon TSF | ||
449 | * | ||
450 | * If it's tx close descriptor: | ||
451 | * [0:7] = Packet Retry Count | ||
452 | * [8:14] = RTS Retry Count | ||
453 | * [15] = TOK | ||
454 | * [16:27] = Sequence No | ||
455 | * [28] = LS | ||
456 | * [29] = FS | ||
457 | * [30:31] = 01b | ||
458 | * [32:47] = unused (reserved?) | ||
459 | * [48:63] = MAC Used Time | ||
460 | */ | ||
461 | val = le64_to_cpu(priv->b_tx_status.buf); | ||
462 | |||
463 | cmd_type = (val >> 30) & 0x3; | ||
464 | if (cmd_type == 1) { | ||
465 | unsigned int pkt_rc, seq_no; | ||
466 | bool tok; | ||
467 | struct sk_buff *skb; | ||
468 | struct ieee80211_hdr *ieee80211hdr; | ||
469 | unsigned long flags; | ||
470 | |||
471 | pkt_rc = val & 0xFF; | ||
472 | tok = val & (1 << 15); | ||
473 | seq_no = (val >> 16) & 0xFFF; | ||
474 | |||
475 | spin_lock_irqsave(&priv->b_tx_status.queue.lock, flags); | ||
476 | skb_queue_reverse_walk(&priv->b_tx_status.queue, skb) { | ||
477 | ieee80211hdr = (struct ieee80211_hdr *)skb->data; | ||
478 | |||
479 | /* | ||
480 | * While testing, it was discovered that the seq_no | ||
481 | * doesn't actually contains the sequence number. | ||
482 | * Instead of returning just the 12 bits of sequence | ||
483 | * number, hardware is returning entire sequence control | ||
484 | * (fragment number plus sequence number) in a 12 bit | ||
485 | * only field overflowing after some time. As a | ||
486 | * workaround, just consider the lower bits, and expect | ||
487 | * it's unlikely we wrongly ack some sent data | ||
488 | */ | ||
489 | if ((le16_to_cpu(ieee80211hdr->seq_ctrl) | ||
490 | & 0xFFF) == seq_no) | ||
491 | break; | ||
492 | } | ||
493 | if (skb != (struct sk_buff *) &priv->b_tx_status.queue) { | ||
494 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||
495 | |||
496 | __skb_unlink(skb, &priv->b_tx_status.queue); | ||
497 | if (tok) | ||
498 | info->flags |= IEEE80211_TX_STAT_ACK; | ||
499 | info->status.rates[0].count = pkt_rc; | ||
500 | |||
501 | ieee80211_tx_status_irqsafe(hw, skb); | ||
502 | } | ||
503 | spin_unlock_irqrestore(&priv->b_tx_status.queue.lock, flags); | ||
504 | } | ||
505 | |||
506 | usb_submit_urb(urb, GFP_ATOMIC); | ||
507 | } | ||
508 | |||
509 | static int rtl8187b_init_status_urb(struct ieee80211_hw *dev) | ||
510 | { | ||
511 | struct rtl8187_priv *priv = dev->priv; | ||
512 | struct urb *entry; | ||
513 | |||
514 | entry = usb_alloc_urb(0, GFP_KERNEL); | ||
515 | if (!entry) | ||
516 | return -ENOMEM; | ||
517 | priv->b_tx_status.urb = entry; | ||
518 | |||
519 | usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, 9), | ||
520 | &priv->b_tx_status.buf, sizeof(priv->b_tx_status.buf), | ||
521 | rtl8187b_status_cb, dev); | ||
522 | |||
523 | usb_submit_urb(entry, GFP_KERNEL); | ||
524 | |||
525 | return 0; | ||
526 | } | ||
527 | |||
406 | static int rtl8187_cmd_reset(struct ieee80211_hw *dev) | 528 | static int rtl8187_cmd_reset(struct ieee80211_hw *dev) |
407 | { | 529 | { |
408 | struct rtl8187_priv *priv = dev->priv; | 530 | struct rtl8187_priv *priv = dev->priv; |
@@ -755,6 +877,7 @@ static int rtl8187_start(struct ieee80211_hw *dev) | |||
755 | (7 << 0 /* long retry limit */) | | 877 | (7 << 0 /* long retry limit */) | |
756 | (7 << 21 /* MAX TX DMA */)); | 878 | (7 << 21 /* MAX TX DMA */)); |
757 | rtl8187_init_urbs(dev); | 879 | rtl8187_init_urbs(dev); |
880 | rtl8187b_init_status_urb(dev); | ||
758 | mutex_unlock(&priv->conf_mutex); | 881 | mutex_unlock(&priv->conf_mutex); |
759 | return 0; | 882 | return 0; |
760 | } | 883 | } |
@@ -831,6 +954,9 @@ static void rtl8187_stop(struct ieee80211_hw *dev) | |||
831 | usb_kill_urb(info->urb); | 954 | usb_kill_urb(info->urb); |
832 | kfree_skb(skb); | 955 | kfree_skb(skb); |
833 | } | 956 | } |
957 | while ((skb = skb_dequeue(&priv->b_tx_status.queue))) | ||
958 | dev_kfree_skb_any(skb); | ||
959 | usb_kill_urb(priv->b_tx_status.urb); | ||
834 | mutex_unlock(&priv->conf_mutex); | 960 | mutex_unlock(&priv->conf_mutex); |
835 | } | 961 | } |
836 | 962 | ||
@@ -1317,6 +1443,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, | |||
1317 | goto err_free_dev; | 1443 | goto err_free_dev; |
1318 | } | 1444 | } |
1319 | mutex_init(&priv->conf_mutex); | 1445 | mutex_init(&priv->conf_mutex); |
1446 | skb_queue_head_init(&priv->b_tx_status.queue); | ||
1320 | 1447 | ||
1321 | printk(KERN_INFO "%s: hwaddr %pM, %s V%d + %s\n", | 1448 | printk(KERN_INFO "%s: hwaddr %pM, %s V%d + %s\n", |
1322 | wiphy_name(dev->wiphy), dev->wiphy->perm_addr, | 1449 | wiphy_name(dev->wiphy), dev->wiphy->perm_addr, |