From 1c740ed2210a0d124674a477ea538468aba47810 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Fri, 31 Oct 2008 17:19:31 -0700 Subject: Move all rtl818x files to a common directory. This change improves the maintainability of these drivers. No functionality is changed. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8187.h | 220 +++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 drivers/net/wireless/rtl818x/rtl8187.h (limited to 'drivers/net/wireless/rtl818x/rtl8187.h') diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h new file mode 100644 index 000000000000..33725d0978b3 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187.h @@ -0,0 +1,220 @@ +/* + * Definitions for RTL8187 hardware + * + * Copyright 2007 Michael Wu + * Copyright 2007 Andrea Merello + * + * Based on the r8187 driver, which is: + * Copyright 2005 Andrea Merello , et al. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef RTL8187_H +#define RTL8187_H + +#include "rtl818x.h" + +#define RTL8187_EEPROM_TXPWR_BASE 0x05 +#define RTL8187_EEPROM_MAC_ADDR 0x07 +#define RTL8187_EEPROM_TXPWR_CHAN_1 0x16 /* 3 channels */ +#define RTL8187_EEPROM_TXPWR_CHAN_6 0x1B /* 2 channels */ +#define RTL8187_EEPROM_TXPWR_CHAN_4 0x3D /* 2 channels */ + +#define RTL8187_REQT_READ 0xC0 +#define RTL8187_REQT_WRITE 0x40 +#define RTL8187_REQ_GET_REG 0x05 +#define RTL8187_REQ_SET_REG 0x05 + +#define RTL8187_MAX_RX 0x9C4 + +struct rtl8187_rx_info { + struct urb *urb; + struct ieee80211_hw *dev; +}; + +struct rtl8187_rx_hdr { + __le32 flags; + u8 noise; + u8 signal; + u8 agc; + u8 reserved; + __le64 mac_time; +} __attribute__((packed)); + +struct rtl8187b_rx_hdr { + __le32 flags; + __le64 mac_time; + u8 sq; + u8 rssi; + u8 agc; + u8 flags2; + __le16 snr_long2end; + s8 pwdb_g12; + u8 fot; +} __attribute__((packed)); + +/* {rtl8187,rtl8187b}_tx_info is in skb */ + +struct rtl8187_tx_hdr { + __le32 flags; + __le16 rts_duration; + __le16 len; + __le32 retry; +} __attribute__((packed)); + +struct rtl8187b_tx_hdr { + __le32 flags; + __le16 rts_duration; + __le16 len; + __le32 unused_1; + __le16 unused_2; + __le16 tx_duration; + __le32 unused_3; + __le32 retry; + __le32 unused_4[2]; +} __attribute__((packed)); + +enum { + DEVICE_RTL8187, + DEVICE_RTL8187B +}; + +struct rtl8187_priv { + /* common between rtl818x drivers */ + struct rtl818x_csr *map; + const struct rtl818x_rf_ops *rf; + struct ieee80211_vif *vif; + int mode; + /* The mutex protects the TX loopback state. + * Any attempt to set channels concurrently locks the device. + */ + struct mutex conf_mutex; + + /* rtl8187 specific */ + struct ieee80211_channel channels[14]; + struct ieee80211_rate rates[12]; + struct ieee80211_supported_band band; + struct usb_device *udev; + u32 rx_conf; + u16 txpwr_base; + u8 asic_rev; + u8 is_rtl8187b; + enum { + RTL8187BvB, + RTL8187BvD, + RTL8187BvE + } hw_rev; + struct sk_buff_head rx_queue; + u8 signal; + u8 quality; + u8 noise; +}; + +void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); + +static inline u8 rtl818x_ioread8_idx(struct rtl8187_priv *priv, + u8 *addr, u8 idx) +{ + u8 val; + + usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), + RTL8187_REQ_GET_REG, RTL8187_REQT_READ, + (unsigned long)addr, idx & 0x03, &val, + sizeof(val), HZ / 2); + + return val; +} + +static inline u8 rtl818x_ioread8(struct rtl8187_priv *priv, u8 *addr) +{ + return rtl818x_ioread8_idx(priv, addr, 0); +} + +static inline u16 rtl818x_ioread16_idx(struct rtl8187_priv *priv, + __le16 *addr, u8 idx) +{ + __le16 val; + + usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), + RTL8187_REQ_GET_REG, RTL8187_REQT_READ, + (unsigned long)addr, idx & 0x03, &val, + sizeof(val), HZ / 2); + + return le16_to_cpu(val); +} + +static inline u16 rtl818x_ioread16(struct rtl8187_priv *priv, __le16 *addr) +{ + return rtl818x_ioread16_idx(priv, addr, 0); +} + +static inline u32 rtl818x_ioread32_idx(struct rtl8187_priv *priv, + __le32 *addr, u8 idx) +{ + __le32 val; + + usb_control_msg(priv->udev, usb_rcvctrlpipe(priv->udev, 0), + RTL8187_REQ_GET_REG, RTL8187_REQT_READ, + (unsigned long)addr, idx & 0x03, &val, + sizeof(val), HZ / 2); + + return le32_to_cpu(val); +} + +static inline u32 rtl818x_ioread32(struct rtl8187_priv *priv, __le32 *addr) +{ + return rtl818x_ioread32_idx(priv, addr, 0); +} + +static inline void rtl818x_iowrite8_idx(struct rtl8187_priv *priv, + u8 *addr, u8 val, u8 idx) +{ + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + (unsigned long)addr, idx & 0x03, &val, + sizeof(val), HZ / 2); +} + +static inline void rtl818x_iowrite8(struct rtl8187_priv *priv, u8 *addr, u8 val) +{ + rtl818x_iowrite8_idx(priv, addr, val, 0); +} + +static inline void rtl818x_iowrite16_idx(struct rtl8187_priv *priv, + __le16 *addr, u16 val, u8 idx) +{ + __le16 buf = cpu_to_le16(val); + + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + (unsigned long)addr, idx & 0x03, &buf, sizeof(buf), + HZ / 2); +} + +static inline void rtl818x_iowrite16(struct rtl8187_priv *priv, __le16 *addr, + u16 val) +{ + rtl818x_iowrite16_idx(priv, addr, val, 0); +} + +static inline void rtl818x_iowrite32_idx(struct rtl8187_priv *priv, + __le32 *addr, u32 val, u8 idx) +{ + __le32 buf = cpu_to_le32(val); + + usb_control_msg(priv->udev, usb_sndctrlpipe(priv->udev, 0), + RTL8187_REQ_SET_REG, RTL8187_REQT_WRITE, + (unsigned long)addr, idx & 0x03, &buf, sizeof(buf), + HZ / 2); +} + +static inline void rtl818x_iowrite32(struct rtl8187_priv *priv, __le32 *addr, + u32 val) +{ + rtl818x_iowrite32_idx(priv, addr, val, 0); +} + +#endif /* RTL8187_H */ -- cgit v1.2.3 From b4572a9264312fecdb530a3416b8c8336a3bb9a9 Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Thu, 13 Nov 2008 10:39:15 -0500 Subject: rtl8187: implement conf_tx callback to configure tx queues Add conf_tx callback and use it to configure tx queues of 8187L/8187B. Tested-by: Larry Finger Tested-by: Hin-Tak Leung Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8187.h | 2 + drivers/net/wireless/rtl818x/rtl8187_dev.c | 73 +++++++++++++++++++++++--- drivers/net/wireless/rtl818x/rtl8187_rtl8225.c | 6 --- 3 files changed, 69 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless/rtl818x/rtl8187.h') diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h index 33725d0978b3..f09872eec3d5 100644 --- a/drivers/net/wireless/rtl818x/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187.h @@ -111,6 +111,8 @@ struct rtl8187_priv { u8 signal; u8 quality; u8 noise; + u8 slot_time; + u8 aifsn[4]; }; 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 5fe7473124e8..cb5bcefd1c3e 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -712,6 +712,13 @@ static int rtl8187b_init_hw(struct ieee80211_hw *dev) rtl818x_iowrite16_idx(priv, (__le16 *)0xFFEC, 0x0800, 1); + priv->slot_time = 0x9; + priv->aifsn[0] = 2; /* AIFSN[AC_VO] */ + priv->aifsn[1] = 2; /* AIFSN[AC_VI] */ + priv->aifsn[2] = 7; /* AIFSN[AC_BK] */ + priv->aifsn[3] = 3; /* AIFSN[AC_BE] */ + rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0); + return 0; } @@ -919,24 +926,38 @@ static int rtl8187_config_interface(struct ieee80211_hw *dev, return 0; } +/* + * With 8187B, AC_*_PARAM clashes with FEMR definition in struct rtl818x_csr for + * example. Thus we have to use raw values for AC_*_PARAM register addresses. + */ +static __le32 *rtl8187b_ac_addr[4] = { + (__le32 *) 0xFFF0, /* AC_VO */ + (__le32 *) 0xFFF4, /* AC_VI */ + (__le32 *) 0xFFFC, /* AC_BK */ + (__le32 *) 0xFFF8, /* AC_BE */ +}; + +#define SIFS_TIME 0xa + static void rtl8187_conf_erp(struct rtl8187_priv *priv, bool use_short_slot, bool use_short_preamble) { if (priv->is_rtl8187b) { - u8 difs, eifs, slot_time; + u8 difs, eifs; u16 ack_timeout; + int queue; if (use_short_slot) { - slot_time = 0x9; + priv->slot_time = 0x9; difs = 0x1c; eifs = 0x53; } else { - slot_time = 0x14; + priv->slot_time = 0x14; difs = 0x32; eifs = 0x5b; } rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); - rtl818x_iowrite8(priv, &priv->map->SLOT, slot_time); + rtl818x_iowrite8(priv, &priv->map->SLOT, priv->slot_time); rtl818x_iowrite8(priv, &priv->map->DIFS, difs); /* @@ -957,18 +978,21 @@ static void rtl8187_conf_erp(struct rtl8187_priv *priv, bool use_short_slot, ack_timeout += 144; rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, DIV_ROUND_UP(ack_timeout, 4)); + + for (queue = 0; queue < 4; queue++) + rtl818x_iowrite8(priv, (u8 *) rtl8187b_ac_addr[queue], + priv->aifsn[queue] * priv->slot_time + + SIFS_TIME); } else { rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22); if (use_short_slot) { rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9); rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14); rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x14); - rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73); } else { rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14); rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24); rtl818x_iowrite8(priv, &priv->map->EIFS, 91 - 0x24); - rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5); } } } @@ -1017,6 +1041,42 @@ static void rtl8187_configure_filter(struct ieee80211_hw *dev, rtl818x_iowrite32_async(priv, &priv->map->RX_CONF, priv->rx_conf); } +static int rtl8187_conf_tx(struct ieee80211_hw *dev, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct rtl8187_priv *priv = dev->priv; + u8 cw_min, cw_max; + + if (queue > 3) + return -EINVAL; + + cw_min = fls(params->cw_min); + cw_max = fls(params->cw_max); + + if (priv->is_rtl8187b) { + priv->aifsn[queue] = params->aifs; + + /* + * This is the structure of AC_*_PARAM registers in 8187B: + * - TXOP limit field, bit offset = 16 + * - ECWmax, bit offset = 12 + * - ECWmin, bit offset = 8 + * - AIFS, bit offset = 0 + */ + rtl818x_iowrite32(priv, rtl8187b_ac_addr[queue], + (params->txop << 16) | (cw_max << 12) | + (cw_min << 8) | (params->aifs * + priv->slot_time + SIFS_TIME)); + } else { + if (queue != 0) + return -EINVAL; + + rtl818x_iowrite8(priv, &priv->map->CW_VAL, + cw_min | (cw_max << 4)); + } + return 0; +} + static const struct ieee80211_ops rtl8187_ops = { .tx = rtl8187_tx, .start = rtl8187_start, @@ -1027,6 +1087,7 @@ static const struct ieee80211_ops rtl8187_ops = { .config_interface = rtl8187_config_interface, .bss_info_changed = rtl8187_bss_info_changed, .configure_filter = rtl8187_configure_filter, + .conf_tx = rtl8187_conf_tx }; static void rtl8187_eeprom_register_read(struct eeprom_93cx6 *eeprom) diff --git a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c index 69030be62b36..4e75e8e7fa90 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c +++ b/drivers/net/wireless/rtl818x/rtl8187_rtl8225.c @@ -878,12 +878,6 @@ static void rtl8225z2_b_rf_init(struct ieee80211_hw *dev) for (i = 0; i < ARRAY_SIZE(rtl8225z2_ofdm); i++) rtl8225_write_phy_ofdm(dev, i, rtl8225z2_ofdm[i]); - rtl818x_iowrite32(priv, (__le32 *)0xFFF0, (7 << 12) | (3 << 8) | 28); - rtl818x_iowrite32(priv, (__le32 *)0xFFF4, (7 << 12) | (3 << 8) | 28); - rtl818x_iowrite32(priv, (__le32 *)0xFFF8, (7 << 12) | (3 << 8) | 28); - rtl818x_iowrite32(priv, (__le32 *)0xFFFC, (7 << 12) | (3 << 8) | 28); - rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0); - rtl8225_write_phy_ofdm(dev, 0x97, 0x46); rtl8225_write_phy_ofdm(dev, 0xa4, 0xb6); rtl8225_write_phy_ofdm(dev, 0x85, 0xfc); -- cgit v1.2.3 From 3517afdefc3ad335b276eb5f8691841f48097abf Mon Sep 17 00:00:00 2001 From: Herton Ronaldo Krzesinski Date: Thu, 13 Nov 2008 10:39:16 -0500 Subject: 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 Tested-by: Hin-Tak Leung Signed-off-by: Herton Ronaldo Krzesinski Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8187.h | 5 ++ drivers/net/wireless/rtl818x/rtl8187_dev.c | 135 ++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/rtl818x/rtl8187.h') diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h index f09872eec3d5..c385407a9941 100644 --- a/drivers/net/wireless/rtl818x/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187.h @@ -113,6 +113,11 @@ struct rtl8187_priv { u8 noise; u8 slot_time; u8 aifsn[4]; + struct { + __le64 buf; + struct urb *urb; + struct sk_buff_head queue; + } b_tx_status; }; 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 cb5bcefd1c3e..876d4f93d310 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) skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) : sizeof(struct rtl8187_tx_hdr)); ieee80211_tx_info_clear_status(info); - info->flags |= IEEE80211_TX_STAT_ACK; - ieee80211_tx_status_irqsafe(hw, skb); + + if (!urb->status && + !(info->flags & IEEE80211_TX_CTL_NO_ACK) && + 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; + + 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) + info->flags |= IEEE80211_TX_STAT_ACK; + ieee80211_tx_status_irqsafe(hw, skb); + } } 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) hdr->flags = cpu_to_le32(flags); hdr->len = 0; hdr->rts_duration = rts_dur; - hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); + hdr->retry = cpu_to_le32(info->control.rates[0].count << 8); buf = hdr; ep = 2; @@ -237,7 +256,7 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) memset(hdr, 0, sizeof(*hdr)); hdr->flags = cpu_to_le32(flags); hdr->rts_duration = rts_dur; - hdr->retry = cpu_to_le32((info->control.rates[0].count - 1) << 8); + hdr->retry = cpu_to_le32(info->control.rates[0].count << 8); hdr->tx_duration = ieee80211_generic_frame_duration(dev, priv->vif, skb->len, txrate); @@ -403,6 +422,109 @@ static int rtl8187_init_urbs(struct ieee80211_hw *dev) return 0; } +static void rtl8187b_status_cb(struct urb *urb) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)urb->context; + struct rtl8187_priv *priv = hw->priv; + u64 val; + unsigned int cmd_type; + + if (unlikely(urb->status)) { + usb_free_urb(urb); + return; + } + + /* + * Read from status buffer: + * + * bits [30:31] = cmd type: + * - 0 indicates tx beacon interrupt + * - 1 indicates tx close descriptor + * + * In the case of tx beacon interrupt: + * [0:9] = Last Beacon CW + * [10:29] = reserved + * [30:31] = 00b + * [32:63] = Last Beacon TSF + * + * If it's tx close descriptor: + * [0:7] = Packet Retry Count + * [8:14] = RTS Retry Count + * [15] = TOK + * [16:27] = Sequence No + * [28] = LS + * [29] = FS + * [30:31] = 01b + * [32:47] = unused (reserved?) + * [48:63] = MAC Used Time + */ + val = le64_to_cpu(priv->b_tx_status.buf); + + cmd_type = (val >> 30) & 0x3; + if (cmd_type == 1) { + unsigned int pkt_rc, seq_no; + bool tok; + struct sk_buff *skb; + struct ieee80211_hdr *ieee80211hdr; + unsigned long flags; + + pkt_rc = val & 0xFF; + tok = val & (1 << 15); + seq_no = (val >> 16) & 0xFFF; + + spin_lock_irqsave(&priv->b_tx_status.queue.lock, flags); + skb_queue_reverse_walk(&priv->b_tx_status.queue, skb) { + ieee80211hdr = (struct ieee80211_hdr *)skb->data; + + /* + * While testing, it was discovered that the seq_no + * doesn't actually contains the sequence number. + * Instead of returning just the 12 bits of sequence + * number, hardware is returning entire sequence control + * (fragment number plus sequence number) in a 12 bit + * only field overflowing after some time. As a + * workaround, just consider the lower bits, and expect + * it's unlikely we wrongly ack some sent data + */ + if ((le16_to_cpu(ieee80211hdr->seq_ctrl) + & 0xFFF) == seq_no) + break; + } + if (skb != (struct sk_buff *) &priv->b_tx_status.queue) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + __skb_unlink(skb, &priv->b_tx_status.queue); + if (tok) + info->flags |= IEEE80211_TX_STAT_ACK; + info->status.rates[0].count = pkt_rc; + + ieee80211_tx_status_irqsafe(hw, skb); + } + spin_unlock_irqrestore(&priv->b_tx_status.queue.lock, flags); + } + + usb_submit_urb(urb, GFP_ATOMIC); +} + +static int rtl8187b_init_status_urb(struct ieee80211_hw *dev) +{ + struct rtl8187_priv *priv = dev->priv; + struct urb *entry; + + entry = usb_alloc_urb(0, GFP_KERNEL); + if (!entry) + return -ENOMEM; + priv->b_tx_status.urb = entry; + + usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, 9), + &priv->b_tx_status.buf, sizeof(priv->b_tx_status.buf), + rtl8187b_status_cb, dev); + + usb_submit_urb(entry, GFP_KERNEL); + + return 0; +} + static int rtl8187_cmd_reset(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; @@ -755,6 +877,7 @@ static int rtl8187_start(struct ieee80211_hw *dev) (7 << 0 /* long retry limit */) | (7 << 21 /* MAX TX DMA */)); rtl8187_init_urbs(dev); + rtl8187b_init_status_urb(dev); mutex_unlock(&priv->conf_mutex); return 0; } @@ -831,6 +954,9 @@ static void rtl8187_stop(struct ieee80211_hw *dev) usb_kill_urb(info->urb); kfree_skb(skb); } + while ((skb = skb_dequeue(&priv->b_tx_status.queue))) + dev_kfree_skb_any(skb); + usb_kill_urb(priv->b_tx_status.urb); mutex_unlock(&priv->conf_mutex); } @@ -1317,6 +1443,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, goto err_free_dev; } mutex_init(&priv->conf_mutex); + skb_queue_head_init(&priv->b_tx_status.queue); printk(KERN_INFO "%s: hwaddr %pM, %s V%d + %s\n", wiphy_name(dev->wiphy), dev->wiphy->perm_addr, -- cgit v1.2.3 From c1db52b9d27ee6e15a7136e67e4a21dc916cd07f Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 9 Dec 2008 23:34:27 -0600 Subject: rtl8187: Use usb anchor facilities to manage urbs When SLUB debugging is enabled in the kernel, and the boot command includes the option "slub_debug=P", rtl8187 encounters a GPF due to a read-after-free of a urb. Following the example of changes in p54usb to fix the same problem, the code has been modified to use the usb_anchor_urb() method. With this change, the USB core handles the freeing of urb's. This patch fixes the problem reported in Kernel Bugzilla #12185 (http://bugzilla.kernel.org/show_bug.cgi?id=12185). Signed-off-by: Larry Finger Tested-by: Hin-Tak Leung Signed-off-by: John W. Linville --- drivers/net/wireless/rtl818x/rtl8187.h | 2 +- drivers/net/wireless/rtl818x/rtl8187_dev.c | 75 ++++++++++++++++++++---------- 2 files changed, 52 insertions(+), 25 deletions(-) (limited to 'drivers/net/wireless/rtl818x/rtl8187.h') diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h index c385407a9941..3b1e1c2aad26 100644 --- a/drivers/net/wireless/rtl818x/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187.h @@ -99,6 +99,7 @@ struct rtl8187_priv { struct ieee80211_supported_band band; struct usb_device *udev; u32 rx_conf; + struct usb_anchor anchored; u16 txpwr_base; u8 asic_rev; u8 is_rtl8187b; @@ -115,7 +116,6 @@ struct rtl8187_priv { u8 aifsn[4]; struct { __le64 buf; - struct urb *urb; struct sk_buff_head queue; } b_tx_status; }; diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index 417a2d7b5764..74f5449b7924 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -99,7 +99,6 @@ static const struct ieee80211_channel rtl818x_channels[] = { static void rtl8187_iowrite_async_cb(struct urb *urb) { kfree(urb->context); - usb_free_urb(urb); } static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr, @@ -136,11 +135,13 @@ static void rtl8187_iowrite_async(struct rtl8187_priv *priv, __le16 addr, usb_fill_control_urb(urb, priv->udev, usb_sndctrlpipe(priv->udev, 0), (unsigned char *)dr, buf, len, rtl8187_iowrite_async_cb, buf); + usb_anchor_urb(urb, &priv->anchored); rc = usb_submit_urb(urb, GFP_ATOMIC); if (rc < 0) { kfree(buf); - usb_free_urb(urb); + usb_unanchor_urb(urb); } + usb_free_urb(urb); } static inline void rtl818x_iowrite32_async(struct rtl8187_priv *priv, @@ -172,7 +173,6 @@ static void rtl8187_tx_cb(struct urb *urb) struct ieee80211_hw *hw = info->rate_driver_data[0]; struct rtl8187_priv *priv = hw->priv; - usb_free_urb(info->rate_driver_data[1]); skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) : sizeof(struct rtl8187_tx_hdr)); ieee80211_tx_info_clear_status(info); @@ -273,11 +273,13 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, ep), buf, skb->len, rtl8187_tx_cb, skb); + usb_anchor_urb(urb, &priv->anchored); rc = usb_submit_urb(urb, GFP_ATOMIC); if (rc < 0) { - usb_free_urb(urb); + usb_unanchor_urb(urb); kfree_skb(skb); } + usb_free_urb(urb); return 0; } @@ -301,14 +303,13 @@ static void rtl8187_rx_cb(struct urb *urb) return; } spin_unlock(&priv->rx_queue.lock); + skb_put(skb, urb->actual_length); if (unlikely(urb->status)) { - usb_free_urb(urb); dev_kfree_skb_irq(skb); return; } - skb_put(skb, urb->actual_length); if (!priv->is_rtl8187b) { struct rtl8187_rx_hdr *hdr = (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); @@ -361,7 +362,6 @@ static void rtl8187_rx_cb(struct urb *urb) skb = dev_alloc_skb(RTL8187_MAX_RX); if (unlikely(!skb)) { - usb_free_urb(urb); /* TODO check rx queue length and refill *somewhere* */ return; } @@ -373,24 +373,32 @@ static void rtl8187_rx_cb(struct urb *urb) urb->context = skb; skb_queue_tail(&priv->rx_queue, skb); - usb_submit_urb(urb, GFP_ATOMIC); + usb_anchor_urb(urb, &priv->anchored); + if (usb_submit_urb(urb, GFP_ATOMIC)) { + usb_unanchor_urb(urb); + skb_unlink(skb, &priv->rx_queue); + dev_kfree_skb_irq(skb); + } } static int rtl8187_init_urbs(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; - struct urb *entry; + struct urb *entry = NULL; struct sk_buff *skb; struct rtl8187_rx_info *info; + int ret = 0; while (skb_queue_len(&priv->rx_queue) < 8) { skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL); - if (!skb) - break; + if (!skb) { + ret = -ENOMEM; + goto err; + } entry = usb_alloc_urb(0, GFP_KERNEL); if (!entry) { - kfree_skb(skb); - break; + ret = -ENOMEM; + goto err; } usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, @@ -401,10 +409,22 @@ static int rtl8187_init_urbs(struct ieee80211_hw *dev) info->urb = entry; info->dev = dev; skb_queue_tail(&priv->rx_queue, skb); - usb_submit_urb(entry, GFP_KERNEL); + usb_anchor_urb(entry, &priv->anchored); + ret = usb_submit_urb(entry, GFP_KERNEL); + if (ret) { + skb_unlink(skb, &priv->rx_queue); + usb_unanchor_urb(entry); + goto err; + } + usb_free_urb(entry); } + return ret; - return 0; +err: + usb_free_urb(entry); + kfree_skb(skb); + usb_kill_anchored_urbs(&priv->anchored); + return ret; } static void rtl8187b_status_cb(struct urb *urb) @@ -414,10 +434,8 @@ static void rtl8187b_status_cb(struct urb *urb) u64 val; unsigned int cmd_type; - if (unlikely(urb->status)) { - usb_free_urb(urb); + if (unlikely(urb->status)) return; - } /* * Read from status buffer: @@ -488,26 +506,32 @@ static void rtl8187b_status_cb(struct urb *urb) spin_unlock_irqrestore(&priv->b_tx_status.queue.lock, flags); } - usb_submit_urb(urb, GFP_ATOMIC); + usb_anchor_urb(urb, &priv->anchored); + if (usb_submit_urb(urb, GFP_ATOMIC)) + usb_unanchor_urb(urb); } static int rtl8187b_init_status_urb(struct ieee80211_hw *dev) { struct rtl8187_priv *priv = dev->priv; struct urb *entry; + int ret = 0; entry = usb_alloc_urb(0, GFP_KERNEL); if (!entry) return -ENOMEM; - priv->b_tx_status.urb = entry; usb_fill_bulk_urb(entry, priv->udev, usb_rcvbulkpipe(priv->udev, 9), &priv->b_tx_status.buf, sizeof(priv->b_tx_status.buf), rtl8187b_status_cb, dev); - usb_submit_urb(entry, GFP_KERNEL); + usb_anchor_urb(entry, &priv->anchored); + ret = usb_submit_urb(entry, GFP_KERNEL); + if (ret) + usb_unanchor_urb(entry); + usb_free_urb(entry); - return 0; + return ret; } static int rtl8187_cmd_reset(struct ieee80211_hw *dev) @@ -841,6 +865,9 @@ static int rtl8187_start(struct ieee80211_hw *dev) return ret; mutex_lock(&priv->conf_mutex); + + init_usb_anchor(&priv->anchored); + if (priv->is_rtl8187b) { reg = RTL818X_RX_CONF_MGMT | RTL818X_RX_CONF_DATA | @@ -936,12 +963,12 @@ static void rtl8187_stop(struct ieee80211_hw *dev) while ((skb = skb_dequeue(&priv->rx_queue))) { info = (struct rtl8187_rx_info *)skb->cb; - usb_kill_urb(info->urb); kfree_skb(skb); } while ((skb = skb_dequeue(&priv->b_tx_status.queue))) dev_kfree_skb_any(skb); - usb_kill_urb(priv->b_tx_status.urb); + + usb_kill_anchored_urbs(&priv->anchored); mutex_unlock(&priv->conf_mutex); } -- cgit v1.2.3