aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLarry Finger <Larry.Finger@lwfinger.net>2009-01-23 11:40:22 -0600
committerJohn W. Linville <linville@tuxdriver.com>2009-01-29 16:01:30 -0500
commit2f47690ed42a85820783dee7f16ae47edadf8fad (patch)
treeb16e286a5e306bd178cbaff061274d255d5cb562 /drivers
parent2a57cf3e83f89150f2ac9b6f01caf3fcdbb36486 (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.h4
-rw-r--r--drivers/net/wireless/rtl818x/rtl8187_dev.c73
2 files changed, 58 insertions, 19 deletions
diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h
index 3b1e1c2aad2..9718f61809c 100644
--- a/drivers/net/wireless/rtl818x/rtl8187.h
+++ b/drivers/net/wireless/rtl818x/rtl8187.h
@@ -100,6 +100,8 @@ struct rtl8187_priv {
struct usb_device *udev;
u32 rx_conf;
struct usb_anchor anchored;
+ struct delayed_work work;
+ struct ieee80211_hw *dev;
u16 txpwr_base;
u8 asic_rev;
u8 is_rtl8187b;
@@ -117,7 +119,7 @@ struct rtl8187_priv {
struct {
__le64 buf;
struct sk_buff_head queue;
- } b_tx_status;
+ } b_tx_status; /* This queue is used by both -b and non-b devices */
};
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 db7318d1d40..82bd47e7c61 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)
sizeof(struct rtl8187_tx_hdr));
ieee80211_tx_info_clear_status(info);
- if (!urb->status &&
- !(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
- priv->is_rtl8187b) {
- skb_queue_tail(&priv->b_tx_status.queue, skb);
+ if (!(urb->status) && !(info->flags & IEEE80211_TX_CTL_NO_ACK)) {
+ if (priv->is_rtl8187b) {
+ skb_queue_tail(&priv->b_tx_status.queue, skb);
- /* queue is "full", discard last items */
- while (skb_queue_len(&priv->b_tx_status.queue) > 5) {
- struct sk_buff *old_skb;
+ /* queue is "full", discard last items */
+ while (skb_queue_len(&priv->b_tx_status.queue) > 5) {
+ struct sk_buff *old_skb;
- dev_dbg(&priv->udev->dev,
- "transmit status queue full\n");
+ dev_dbg(&priv->udev->dev,
+ "transmit status queue full\n");
- old_skb = skb_dequeue(&priv->b_tx_status.queue);
- ieee80211_tx_status_irqsafe(hw, old_skb);
- }
- } else {
- if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && !urb->status)
+ old_skb = skb_dequeue(&priv->b_tx_status.queue);
+ ieee80211_tx_status_irqsafe(hw, old_skb);
+ }
+ return;
+ } else {
info->flags |= IEEE80211_TX_STAT_ACK;
+ }
+ }
+ if (priv->is_rtl8187b)
ieee80211_tx_status_irqsafe(hw, skb);
+ else {
+ /* Retry information for the RTI8187 is only available by
+ * reading a register in the device. We are in interrupt mode
+ * here, thus queue the skb and finish on a work queue. */
+ skb_queue_tail(&priv->b_tx_status.queue, skb);
+ queue_delayed_work(hw->workqueue, &priv->work, 0);
}
}
@@ -645,7 +653,7 @@ static int rtl8187_init_hw(struct ieee80211_hw *dev)
rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0);
rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0);
- rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81);
+ rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0);
// TODO: set RESP_RATE and BRSR properly
rtl818x_iowrite8(priv, &priv->map->RESP_RATE, (8 << 4) | 0);
@@ -765,9 +773,6 @@ static int rtl8187b_init_hw(struct ieee80211_hw *dev)
rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg);
rtl818x_iowrite16_idx(priv, (__le16 *)0xFFE0, 0x0FFF, 1);
- reg = rtl818x_ioread8(priv, &priv->map->RATE_FALLBACK);
- reg |= RTL818X_RATE_FALLBACK_ENABLE;
- rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, reg);
rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100);
rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2);
@@ -855,6 +860,34 @@ static int rtl8187b_init_hw(struct ieee80211_hw *dev)
return 0;
}
+static void rtl8187_work(struct work_struct *work)
+{
+ /* The RTL8187 returns the retry count through register 0xFFFA. In
+ * addition, it appears to be a cumulative retry count, not the
+ * value for the current TX packet. When multiple TX entries are
+ * queued, the retry count will be valid for the last one in the queue.
+ * The "error" should not matter for purposes of rate setting. */
+ struct rtl8187_priv *priv = container_of(work, struct rtl8187_priv,
+ work.work);
+ struct ieee80211_tx_info *info;
+ struct ieee80211_hw *dev = priv->dev;
+ static u16 retry;
+ u16 tmp;
+
+ mutex_lock(&priv->conf_mutex);
+ tmp = rtl818x_ioread16(priv, (__le16 *)0xFFFA);
+ while (skb_queue_len(&priv->b_tx_status.queue) > 0) {
+ struct sk_buff *old_skb;
+
+ old_skb = skb_dequeue(&priv->b_tx_status.queue);
+ info = IEEE80211_SKB_CB(old_skb);
+ info->status.rates[0].count = tmp - retry + 1;
+ ieee80211_tx_status_irqsafe(dev, old_skb);
+ }
+ retry = tmp;
+ mutex_unlock(&priv->conf_mutex);
+}
+
static int rtl8187_start(struct ieee80211_hw *dev)
{
struct rtl8187_priv *priv = dev->priv;
@@ -869,6 +902,7 @@ static int rtl8187_start(struct ieee80211_hw *dev)
mutex_lock(&priv->conf_mutex);
init_usb_anchor(&priv->anchored);
+ priv->dev = dev;
if (priv->is_rtl8187b) {
reg = RTL818X_RX_CONF_MGMT |
@@ -936,6 +970,7 @@ static int rtl8187_start(struct ieee80211_hw *dev)
reg |= RTL818X_CMD_TX_ENABLE;
reg |= RTL818X_CMD_RX_ENABLE;
rtl818x_iowrite8(priv, &priv->map->CMD, reg);
+ INIT_DELAYED_WORK(&priv->work, rtl8187_work);
mutex_unlock(&priv->conf_mutex);
return 0;
@@ -966,6 +1001,8 @@ static void rtl8187_stop(struct ieee80211_hw *dev)
dev_kfree_skb_any(skb);
usb_kill_anchored_urbs(&priv->anchored);
+ if (!priv->is_rtl8187b)
+ cancel_delayed_work_sync(&priv->work);
mutex_unlock(&priv->conf_mutex);
}