diff options
author | Larry Finger <Larry.Finger@lwfinger.net> | 2009-01-23 12:40:22 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-01-29 16:01:30 -0500 |
commit | 2f47690ed42a85820783dee7f16ae47edadf8fad (patch) | |
tree | b16e286a5e306bd178cbaff061274d255d5cb562 /drivers | |
parent | 2a57cf3e83f89150f2ac9b6f01caf3fcdbb36486 (diff) |
rtl8187: Fix driver to return TX retry info for RTL8187L
Current code for the RTL8187 is not returning valid retry information, thus the
rate-setting mechanism is not functioning. As a further complication, this info
is only obtained by reading a register, which cannot be read while in interrupt
context.
This patch implements the TX status return to mac80211 through the use of a
work queue.
One additional problem is that the driver currently enables the rate fallback
mechanism of the device, which conflicts with the mac80211 rate-setting
algorithm. This version of the patch disables rate fallback.
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
Tested-by: Herton Ronaldo Krzesinski <herton@mandriva.com.br>
Tested-by: MartÃn Ernesto Barreyro <barreyromartin@gmail.com>
Acked-by: Hin-Tak Leung <htl10@users.sourceforge.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/rtl818x/rtl8187.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/rtl818x/rtl8187_dev.c | 73 |
2 files changed, 58 insertions, 19 deletions
diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h index 3b1e1c2aad26..9718f61809cf 100644 --- a/drivers/net/wireless/rtl818x/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187.h | |||
@@ -100,6 +100,8 @@ struct rtl8187_priv { | |||
100 | struct usb_device *udev; | 100 | struct usb_device *udev; |
101 | u32 rx_conf; | 101 | u32 rx_conf; |
102 | struct usb_anchor anchored; | 102 | struct usb_anchor anchored; |
103 | struct delayed_work work; | ||
104 | struct ieee80211_hw *dev; | ||
103 | u16 txpwr_base; | 105 | u16 txpwr_base; |
104 | u8 asic_rev; | 106 | u8 asic_rev; |
105 | u8 is_rtl8187b; | 107 | u8 is_rtl8187b; |
@@ -117,7 +119,7 @@ struct rtl8187_priv { | |||
117 | struct { | 119 | struct { |
118 | __le64 buf; | 120 | __le64 buf; |
119 | struct sk_buff_head queue; | 121 | struct sk_buff_head queue; |
120 | } b_tx_status; | 122 | } b_tx_status; /* This queue is used by both -b and non-b devices */ |
121 | }; | 123 | }; |
122 | 124 | ||
123 | void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); | 125 | 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 db7318d1d401..82bd47e7c617 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c | |||
@@ -177,25 +177,33 @@ static void rtl8187_tx_cb(struct urb *urb) | |||
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 | 179 | ||
180 | if (!urb->status && | 180 | if (!(urb->status) && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) { |
181 | !(info->flags & IEEE80211_TX_CTL_NO_ACK) && | 181 | if (priv->is_rtl8187b) { |
182 | priv->is_rtl8187b) { | 182 | skb_queue_tail(&priv->b_tx_status.queue, skb); |
183 | skb_queue_tail(&priv->b_tx_status.queue, skb); | ||
184 | 183 | ||
185 | /* queue is "full", discard last items */ | 184 | /* queue is "full", discard last items */ |
186 | while (skb_queue_len(&priv->b_tx_status.queue) > 5) { | 185 | while (skb_queue_len(&priv->b_tx_status.queue) > 5) { |
187 | struct sk_buff *old_skb; | 186 | struct sk_buff *old_skb; |
188 | 187 | ||
189 | dev_dbg(&priv->udev->dev, | 188 | dev_dbg(&priv->udev->dev, |
190 | "transmit status queue full\n"); | 189 | "transmit status queue full\n"); |
191 | 190 | ||
192 | old_skb = skb_dequeue(&priv->b_tx_status.queue); | 191 | old_skb = skb_dequeue(&priv->b_tx_status.queue); |
193 | ieee80211_tx_status_irqsafe(hw, old_skb); | 192 | ieee80211_tx_status_irqsafe(hw, old_skb); |
194 | } | 193 | } |
195 | } else { | 194 | return; |
196 | if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !urb->status) | 195 | } else { |
197 | info->flags |= IEEE80211_TX_STAT_ACK; | 196 | info->flags |= IEEE80211_TX_STAT_ACK; |
197 | } | ||
198 | } | ||
199 | if (priv->is_rtl8187b) | ||
198 | ieee80211_tx_status_irqsafe(hw, skb); | 200 | ieee80211_tx_status_irqsafe(hw, skb); |
201 | else { | ||
202 | /* Retry information for the RTI8187 is only available by | ||
203 | * reading a register in the device. We are in interrupt mode | ||
204 | * here, thus queue the skb and finish on a work queue. */ | ||
205 | skb_queue_tail(&priv->b_tx_status.queue, skb); | ||
206 | queue_delayed_work(hw->workqueue, &priv->work, 0); | ||
199 | } | 207 | } |
200 | } | 208 | } |
201 | 209 | ||
@@ -645,7 +653,7 @@ static int rtl8187_init_hw(struct ieee80211_hw *dev) | |||
645 | 653 | ||
646 | rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); | 654 | rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); |
647 | rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); | 655 | rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0); |
648 | rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81); | 656 | rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0); |
649 | 657 | ||
650 | // TODO: set RESP_RATE and BRSR properly | 658 | // TODO: set RESP_RATE and BRSR properly |
651 | rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); | 659 | rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0); |
@@ -765,9 +773,6 @@ static int rtl8187b_init_hw(struct ieee80211_hw *dev) | |||
765 | rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); | 773 | rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); |
766 | 774 | ||
767 | rtl818x_iowrite16_idx(priv, (__le16 *)0xFFE0, 0x0FFF, 1); | 775 | rtl818x_iowrite16_idx(priv, (__le16 *)0xFFE0, 0x0FFF, 1); |
768 | reg = rtl818x_ioread8(priv, &priv->map->RATE_FALLBACK); | ||
769 | reg |= RTL818X_RATE_FALLBACK_ENABLE; | ||
770 | rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, reg); | ||
771 | 776 | ||
772 | rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100); | 777 | rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100); |
773 | rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2); | 778 | rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2); |
@@ -855,6 +860,34 @@ static int rtl8187b_init_hw(struct ieee80211_hw *dev) | |||
855 | return 0; | 860 | return 0; |
856 | } | 861 | } |
857 | 862 | ||
863 | static void rtl8187_work(struct work_struct *work) | ||
864 | { | ||
865 | /* The RTL8187 returns the retry count through register 0xFFFA. In | ||
866 | * addition, it appears to be a cumulative retry count, not the | ||
867 | * value for the current TX packet. When multiple TX entries are | ||
868 | * queued, the retry count will be valid for the last one in the queue. | ||
869 | * The "error" should not matter for purposes of rate setting. */ | ||
870 | struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv, | ||
871 | work.work); | ||
872 | struct ieee80211_tx_info *info; | ||
873 | struct ieee80211_hw *dev = priv->dev; | ||
874 | static u16 retry; | ||
875 | u16 tmp; | ||
876 | |||
877 | mutex_lock(&priv->conf_mutex); | ||
878 | tmp = rtl818x_ioread16(priv, (__le16 *)0xFFFA); | ||
879 | while (skb_queue_len(&priv->b_tx_status.queue) > 0) { | ||
880 | struct sk_buff *old_skb; | ||
881 | |||
882 | old_skb = skb_dequeue(&priv->b_tx_status.queue); | ||
883 | info = IEEE80211_SKB_CB(old_skb); | ||
884 | info->status.rates[0].count = tmp - retry + 1; | ||
885 | ieee80211_tx_status_irqsafe(dev, old_skb); | ||
886 | } | ||
887 | retry = tmp; | ||
888 | mutex_unlock(&priv->conf_mutex); | ||
889 | } | ||
890 | |||
858 | static int rtl8187_start(struct ieee80211_hw *dev) | 891 | static int rtl8187_start(struct ieee80211_hw *dev) |
859 | { | 892 | { |
860 | struct rtl8187_priv *priv = dev->priv; | 893 | struct rtl8187_priv *priv = dev->priv; |
@@ -869,6 +902,7 @@ static int rtl8187_start(struct ieee80211_hw *dev) | |||
869 | mutex_lock(&priv->conf_mutex); | 902 | mutex_lock(&priv->conf_mutex); |
870 | 903 | ||
871 | init_usb_anchor(&priv->anchored); | 904 | init_usb_anchor(&priv->anchored); |
905 | priv->dev = dev; | ||
872 | 906 | ||
873 | if (priv->is_rtl8187b) { | 907 | if (priv->is_rtl8187b) { |
874 | reg = RTL818X_RX_CONF_MGMT | | 908 | reg = RTL818X_RX_CONF_MGMT | |
@@ -936,6 +970,7 @@ static int rtl8187_start(struct ieee80211_hw *dev) | |||
936 | reg |= RTL818X_CMD_TX_ENABLE; | 970 | reg |= RTL818X_CMD_TX_ENABLE; |
937 | reg |= RTL818X_CMD_RX_ENABLE; | 971 | reg |= RTL818X_CMD_RX_ENABLE; |
938 | rtl818x_iowrite8(priv, &priv->map->CMD, reg); | 972 | rtl818x_iowrite8(priv, &priv->map->CMD, reg); |
973 | INIT_DELAYED_WORK(&priv->work, rtl8187_work); | ||
939 | mutex_unlock(&priv->conf_mutex); | 974 | mutex_unlock(&priv->conf_mutex); |
940 | 975 | ||
941 | return 0; | 976 | return 0; |
@@ -966,6 +1001,8 @@ static void rtl8187_stop(struct ieee80211_hw *dev) | |||
966 | dev_kfree_skb_any(skb); | 1001 | dev_kfree_skb_any(skb); |
967 | 1002 | ||
968 | usb_kill_anchored_urbs(&priv->anchored); | 1003 | usb_kill_anchored_urbs(&priv->anchored); |
1004 | if (!priv->is_rtl8187b) | ||
1005 | cancel_delayed_work_sync(&priv->work); | ||
969 | mutex_unlock(&priv->conf_mutex); | 1006 | mutex_unlock(&priv->conf_mutex); |
970 | } | 1007 | } |
971 | 1008 | ||