Subject: [PATCH] Add Realtek 8187B support

Hi, this patch (made against wireless-testing repository) adds support for
8187B to the rtl8187 module. It is based on code made by Realtek in their
open source driver, plus contains code by initial patch made by John W.
Linville and feedback/fixes to his initial patch by Pavel Roskin (thus I'm
adding them to Signed-offs).

Also I fixed a bug of a missing "priv->vif = vif" in rtl8187_config_interface
that makes kernel oops in transmit code in ieee80211_generic_frame_duration
function.

Signed-off-by: Herton Ronaldo Krzesinski <[email protected]>
Signed-off-by: John W. Linville <[email protected]>
Signed-off-by: Pavel Roskin <[email protected]>

diff --git a/drivers/net/wireless/rtl8180_rtl8225.c b/drivers/net/wireless/rtl8180_rtl8225.c
index cd22781..b4c6ae0 100644
--- a/drivers/net/wireless/rtl8180_rtl8225.c
+++ b/drivers/net/wireless/rtl8180_rtl8225.c
@@ -718,10 +718,7 @@ static void rtl8225_rf_set_channel(struct ieee80211_hw *dev,
struct rtl8180_priv *priv = dev->priv;
int chan = ieee80211_frequency_to_channel(conf->channel->center_freq);

- if (priv->rf->init == rtl8225_rf_init)
- rtl8225_rf_set_tx_power(dev, chan);
- else
- rtl8225z2_rf_set_tx_power(dev, chan);
+ priv->rf->set_txpwr(dev, chan);

rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]);
msleep(10);
@@ -745,14 +742,16 @@ static const struct rtl818x_rf_ops rtl8225_ops = {
.name = "rtl8225",
.init = rtl8225_rf_init,
.stop = rtl8225_rf_stop,
- .set_chan = rtl8225_rf_set_channel
+ .set_chan = rtl8225_rf_set_channel,
+ .set_txpwr = rtl8225_rf_set_tx_power
};

static const struct rtl818x_rf_ops rtl8225z2_ops = {
.name = "rtl8225z2",
.init = rtl8225z2_rf_init,
.stop = rtl8225_rf_stop,
- .set_chan = rtl8225_rf_set_channel
+ .set_chan = rtl8225_rf_set_channel,
+ .set_txpwr = rtl8225z2_rf_set_tx_power
};

const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *dev)
diff --git a/drivers/net/wireless/rtl8187.h b/drivers/net/wireless/rtl8187.h
index 076d88b..9091c53 100644
--- a/drivers/net/wireless/rtl8187.h
+++ b/drivers/net/wireless/rtl8187.h
@@ -44,23 +44,50 @@ struct rtl8187_rx_hdr {
__le64 mac_time;
} __attribute__((packed));

+struct rtl8187b_rx_hdr {
+ __le32 flags;
+ __le64 mac_time;
+ u8 noise;
+ u8 signal;
+ u8 agc;
+ u8 reserved;
+ __le32 unused;
+} __attribute__((packed));
+
struct rtl8187_tx_info {
struct ieee80211_tx_control *control;
struct urb *urb;
struct ieee80211_hw *dev;
};

-struct rtl8187_tx_hdr {
- __le32 flags;
+/* Tx flags are common between rtl8187 and rtl8187b */
#define RTL8187_TX_FLAG_NO_ENCRYPT (1 << 15)
#define RTL8187_TX_FLAG_MORE_FRAG (1 << 17)
#define RTL8187_TX_FLAG_CTS (1 << 18)
#define RTL8187_TX_FLAG_RTS (1 << 23)
+
+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));
+
+#define DEVICE_RTL8187 0
+#define DEVICE_RTL8187B 1
+
struct rtl8187_priv {
/* common between rtl818x drivers */
struct rtl818x_csr *map;
@@ -76,70 +103,100 @@ struct rtl8187_priv {
u32 rx_conf;
u16 txpwr_base;
u8 asic_rev;
+ u8 is_rtl8187b;
+ enum {
+ RTL8187BvB,
+ RTL8187BvD,
+ RTL8187BvE
+ } hw_rev;
struct sk_buff_head rx_queue;
};

void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data);

-static inline u8 rtl818x_ioread8(struct rtl8187_priv *priv, u8 *addr)
+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, 0, &val, sizeof(val), HZ / 2);
+ (unsigned long)addr, idx & 0x03, &val,
+ sizeof(val), HZ / 2);

return val;
}

-static inline u16 rtl818x_ioread16(struct rtl8187_priv *priv, __le16 *addr)
+#define rtl818x_ioread8(priv, addr) 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, 0, &val, sizeof(val), HZ / 2);
+ (unsigned long)addr, idx & 0x03, &val,
+ sizeof(val), HZ / 2);

return le16_to_cpu(val);
}

-static inline u32 rtl818x_ioread32(struct rtl8187_priv *priv, __le32 *addr)
+#define rtl818x_ioread16(priv, addr) 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, 0, &val, sizeof(val), HZ / 2);
+ (unsigned long)addr, idx & 0x03, &val,
+ sizeof(val), HZ / 2);

return le32_to_cpu(val);
}

-static inline void rtl818x_iowrite8(struct rtl8187_priv *priv,
- u8 *addr, u8 val)
+#define rtl818x_ioread32(priv, addr) 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, 0, &val, sizeof(val), HZ / 2);
+ (unsigned long)addr, idx & 0x03, &val,
+ sizeof(val), HZ / 2);
}

-static inline void rtl818x_iowrite16(struct rtl8187_priv *priv,
- __le16 *addr, u16 val)
+#define rtl818x_iowrite8(priv, addr, 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, 0, &buf, sizeof(buf), HZ / 2);
+ (unsigned long)addr, idx & 0x03, &buf, sizeof(buf),
+ HZ / 2);
}

-static inline void rtl818x_iowrite32(struct rtl8187_priv *priv,
- __le32 *addr, u32 val)
+#define rtl818x_iowrite16(priv, addr, 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, 0, &buf, sizeof(buf), HZ / 2);
+ (unsigned long)addr, idx & 0x03, &buf, sizeof(buf),
+ HZ / 2);
}

+#define rtl818x_iowrite32(priv, addr, val) \
+ rtl818x_iowrite32_idx(priv, addr, val, 0)
+
#endif /* RTL8187_H */
diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c
index c03834d..f6ce612 100644
--- a/drivers/net/wireless/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl8187_dev.c
@@ -27,19 +27,21 @@

MODULE_AUTHOR("Michael Wu <[email protected]>");
MODULE_AUTHOR("Andrea Merello <[email protected]>");
-MODULE_DESCRIPTION("RTL8187 USB wireless driver");
+MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver");
MODULE_LICENSE("GPL");

static struct usb_device_id rtl8187_table[] __devinitdata = {
- /* Realtek */
- {USB_DEVICE(0x0bda, 0x8187)},
+ /* Realtek 8187 */
+ {USB_DEVICE(0x0bda, 0x8187), .driver_info = DEVICE_RTL8187},
/* Netgear */
- {USB_DEVICE(0x0846, 0x6100)},
- {USB_DEVICE(0x0846, 0x6a00)},
+ {USB_DEVICE(0x0846, 0x6100), .driver_info = DEVICE_RTL8187},
+ {USB_DEVICE(0x0846, 0x6a00), .driver_info = DEVICE_RTL8187},
/* HP */
- {USB_DEVICE(0x03f0, 0xca02)},
+ {USB_DEVICE(0x03f0, 0xca02), .driver_info = DEVICE_RTL8187},
/* Sitecom */
- {USB_DEVICE(0x0df6, 0x000d)},
+ {USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187},
+ /* Realtek 8187B */
+ {USB_DEVICE(0x0bda, 0x8189), .driver_info = DEVICE_RTL8187B},
{}
};

@@ -148,6 +150,8 @@ static void rtl8187_tx_cb(struct urb *urb)
struct ieee80211_tx_status status;
struct sk_buff *skb = (struct sk_buff *)urb->context;
struct rtl8187_tx_info *info = (struct rtl8187_tx_info *)skb->cb;
+ struct ieee80211_hw *dev = info->dev;
+ struct rtl8187_priv *priv = dev->priv;

memset(&status, 0, sizeof(status));

@@ -155,7 +159,8 @@ static void rtl8187_tx_cb(struct urb *urb)
if (info->control)
memcpy(&status.control, info->control, sizeof(status.control));
kfree(info->control);
- skb_pull(skb, sizeof(struct rtl8187_tx_hdr));
+ skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) :
+ sizeof(struct rtl8187_tx_hdr));
status.flags |= IEEE80211_TX_STATUS_ACK;
ieee80211_tx_status_irqsafe(info->dev, skb, &status);
}
@@ -164,11 +169,13 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb,
struct ieee80211_tx_control *control)
{
struct rtl8187_priv *priv = dev->priv;
- struct rtl8187_tx_hdr *hdr;
+ struct rtl8187_tx_hdr *hdr = NULL;
+ struct rtl8187b_tx_hdr *hdr_b = NULL;
struct rtl8187_tx_info *info;
struct urb *urb;
__le16 rts_dur = 0;
u32 flags;
+ unsigned int ep[4] = { 6, 7, 5, 4 };

urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
@@ -196,18 +203,34 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb,
flags |= control->rts_cts_rate->hw_value << 19;
}

- hdr = (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr));
- hdr->flags = cpu_to_le32(flags);
- hdr->len = 0;
- hdr->rts_duration = rts_dur;
- hdr->retry = cpu_to_le32(control->retry_limit << 8);
+ if (!priv->is_rtl8187b) {
+ hdr = (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr));
+ hdr->flags = cpu_to_le32(flags);
+ hdr->len = 0;
+ hdr->rts_duration = rts_dur;
+ hdr->retry = cpu_to_le32(control->retry_limit << 8);
+ } else {
+ hdr_b = (struct rtl8187b_tx_hdr *)skb_push(skb, sizeof(*hdr_b));
+ memset(hdr_b, 0, sizeof(*hdr_b));
+ hdr_b->flags = cpu_to_le32(flags);
+ hdr_b->rts_duration = rts_dur;
+ hdr_b->retry = cpu_to_le32(control->retry_limit << 8);
+ hdr_b->tx_duration = ieee80211_generic_frame_duration(
+ dev,
+ priv->vif,
+ skb->len,
+ control->tx_rate);
+ }

info = (struct rtl8187_tx_info *)skb->cb;
info->control = kmemdup(control, sizeof(*control), GFP_ATOMIC);
info->urb = urb;
info->dev = dev;
- usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, 2),
- hdr, skb->len, rtl8187_tx_cb, skb);
+ usb_fill_bulk_urb(urb, priv->udev,
+ usb_sndbulkpipe(priv->udev,
+ priv->is_rtl8187b ? ep[skb->priority] : 2),
+ priv->is_rtl8187b ? (void *)hdr_b : (void *)hdr,
+ skb->len, rtl8187_tx_cb, skb);
usb_submit_urb(urb, GFP_ATOMIC);

return 0;
@@ -220,6 +243,7 @@ static void rtl8187_rx_cb(struct urb *urb)
struct ieee80211_hw *dev = info->dev;
struct rtl8187_priv *priv = dev->priv;
struct rtl8187_rx_hdr *hdr;
+ struct rtl8187b_rx_hdr *hdr_b;
struct ieee80211_rx_status rx_status = { 0 };
int rate, signal;
u32 flags;
@@ -240,11 +264,25 @@ static void rtl8187_rx_cb(struct urb *urb)
}

skb_put(skb, urb->actual_length);
- hdr = (struct rtl8187_rx_hdr *)(skb_tail_pointer(skb) - sizeof(*hdr));
- flags = le32_to_cpu(hdr->flags);
- skb_trim(skb, flags & 0x0FFF);
+ if (!priv->is_rtl8187b) {
+ hdr = (struct rtl8187_rx_hdr *)
+ (skb_tail_pointer(skb) - sizeof(*hdr));
+ flags = le32_to_cpu(hdr->flags);
+ signal = hdr->agc >> 1;
+ rx_status.antenna = (hdr->signal >> 7) & 1;
+ rx_status.signal = 64 - min(hdr->noise, (u8)64);
+ rx_status.mactime = le64_to_cpu(hdr->mac_time);
+ } else {
+ hdr_b = (struct rtl8187b_rx_hdr *)
+ (skb_tail_pointer(skb) - sizeof(*hdr_b));
+ flags = le32_to_cpu(hdr_b->flags);
+ signal = hdr_b->agc >> 1;
+ rx_status.antenna = (hdr_b->signal >> 7) & 1;
+ rx_status.signal = 64 - min(hdr_b->noise, (u8)64);
+ rx_status.mactime = le64_to_cpu(hdr_b->mac_time);
+ }

- signal = hdr->agc >> 1;
+ skb_trim(skb, flags & 0x0FFF);
rate = (flags >> 20) & 0xF;
if (rate > 3) { /* OFDM rate */
if (signal > 90)
@@ -260,13 +298,10 @@ static void rtl8187_rx_cb(struct urb *urb)
signal = 95 - signal;
}

- rx_status.antenna = (hdr->signal >> 7) & 1;
- rx_status.signal = 64 - min(hdr->noise, (u8)64);
rx_status.ssi = signal;
rx_status.rate_idx = rate;
rx_status.freq = dev->conf.channel->center_freq;
rx_status.band = dev->conf.channel->band;
- rx_status.mactime = le64_to_cpu(hdr->mac_time);
rx_status.flag |= RX_FLAG_TSFT;
if (flags & (1 << 13))
rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
@@ -306,7 +341,8 @@ static int rtl8187_init_urbs(struct ieee80211_hw *dev)
break;
}
usb_fill_bulk_urb(entry, priv->udev,
- usb_rcvbulkpipe(priv->udev, 1),
+ usb_rcvbulkpipe(priv->udev,
+ priv->is_rtl8187b ? 3 : 1),
skb_tail_pointer(skb),
RTL8187_MAX_RX, rtl8187_rx_cb, skb);
info = (struct rtl8187_rx_info *)skb->cb;
@@ -319,29 +355,12 @@ static int rtl8187_init_urbs(struct ieee80211_hw *dev)
return 0;
}

-static int rtl8187_init_hw(struct ieee80211_hw *dev)
+static int rtl8187_cmd_reset(struct ieee80211_hw *dev)
{
struct rtl8187_priv *priv = dev->priv;
u8 reg;
int i;

- /* reset */
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_ON);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
-
- msleep(200);
- rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10);
- rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11);
- rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00);
- msleep(200);
-
reg = rtl818x_ioread8(priv, &priv->map->CMD);
reg &= (1 << 1);
reg |= RTL818X_CMD_RESET;
@@ -377,6 +396,36 @@ static int rtl8187_init_hw(struct ieee80211_hw *dev)
return -ETIMEDOUT;
}

+ return 0;
+}
+
+static int rtl8187_init_hw(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u8 reg;
+ int res;
+
+ /* reset */
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_ON);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
+
+ msleep(200);
+ rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10);
+ rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11);
+ rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00);
+ msleep(200);
+
+ res = rtl8187_cmd_reset(dev);
+ if (res)
+ return res;
+
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
@@ -446,16 +495,191 @@ static int rtl8187_init_hw(struct ieee80211_hw *dev)
return 0;
}

+static const u8 rtl8187b_reg_table[][3] = {
+ {0xF0, 0x32, 0}, {0xF1, 0x32, 0}, {0xF2, 0x00, 0}, {0xF3, 0x00, 0},
+ {0xF4, 0x32, 0}, {0xF5, 0x43, 0}, {0xF6, 0x00, 0}, {0xF7, 0x00, 0},
+ {0xF8, 0x46, 0}, {0xF9, 0xA4, 0}, {0xFA, 0x00, 0}, {0xFB, 0x00, 0},
+ {0xFC, 0x96, 0}, {0xFD, 0xA4, 0}, {0xFE, 0x00, 0}, {0xFF, 0x00, 0},
+
+ {0x58, 0x4B, 1}, {0x59, 0x00, 1}, {0x5A, 0x4B, 1}, {0x5B, 0x00, 1},
+ {0x60, 0x4B, 1}, {0x61, 0x09, 1}, {0x62, 0x4B, 1}, {0x63, 0x09, 1},
+ {0xCE, 0x0F, 1}, {0xCF, 0x00, 1}, {0xE0, 0xFF, 1}, {0xE1, 0x0F, 1},
+ {0xE2, 0x00, 1}, {0xF0, 0x4E, 1}, {0xF1, 0x01, 1}, {0xF2, 0x02, 1},
+ {0xF3, 0x03, 1}, {0xF4, 0x04, 1}, {0xF5, 0x05, 1}, {0xF6, 0x06, 1},
+ {0xF7, 0x07, 1}, {0xF8, 0x08, 1},
+
+ {0x4E, 0x00, 2}, {0x0C, 0x04, 2}, {0x21, 0x61, 2}, {0x22, 0x68, 2},
+ {0x23, 0x6F, 2}, {0x24, 0x76, 2}, {0x25, 0x7D, 2}, {0x26, 0x84, 2},
+ {0x27, 0x8D, 2}, {0x4D, 0x08, 2}, {0x50, 0x05, 2}, {0x51, 0xF5, 2},
+ {0x52, 0x04, 2}, {0x53, 0xA0, 2}, {0x54, 0x1F, 2}, {0x55, 0x23, 2},
+ {0x56, 0x45, 2}, {0x57, 0x67, 2}, {0x58, 0x08, 2}, {0x59, 0x08, 2},
+ {0x5A, 0x08, 2}, {0x5B, 0x08, 2}, {0x60, 0x08, 2}, {0x61, 0x08, 2},
+ {0x62, 0x08, 2}, {0x63, 0x08, 2}, {0x64, 0xCF, 2}, {0x72, 0x56, 2},
+ {0x73, 0x9A, 2},
+
+ {0x34, 0xF0, 0}, {0x35, 0x0F, 0}, {0x5B, 0x40, 0}, {0x84, 0x88, 0},
+ {0x85, 0x24, 0}, {0x88, 0x54, 0}, {0x8B, 0xB8, 0}, {0x8C, 0x07, 0},
+ {0x8D, 0x00, 0}, {0x94, 0x1B, 0}, {0x95, 0x12, 0}, {0x96, 0x00, 0},
+ {0x97, 0x06, 0}, {0x9D, 0x1A, 0}, {0x9F, 0x10, 0}, {0xB4, 0x22, 0},
+ {0xBE, 0x80, 0}, {0xDB, 0x00, 0}, {0xEE, 0x00, 0}, {0x91, 0x03, 0},
+
+ {0x4C, 0x00, 2}, {0x9F, 0x00, 3}, {0x8C, 0x01, 0}, {0x8D, 0x10, 0},
+ {0x8E, 0x08, 0}, {0x8F, 0x00, 0}
+};
+
+static int rtl8187b_init_hw(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ int res, i;
+ u8 reg;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ reg |= RTL818X_CONFIG3_ANAPARAM_WRITE | RTL818X_CONFIG3_GNT_SELECT;
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, 0x727f3f52);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM, 0x45090658);
+ rtl818x_iowrite8(priv, &priv->map->ANAPARAM3, 0);
+
+ rtl818x_iowrite8(priv, (u8 *)0xFF61, 0x10);
+ reg = rtl818x_ioread8(priv, (u8 *)0xFF62);
+ rtl818x_iowrite8(priv, (u8 *)0xFF62, reg & ~(1 << 5));
+ rtl818x_iowrite8(priv, (u8 *)0xFF62, reg | (1 << 5));
+
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ reg &= ~RTL818X_CONFIG3_ANAPARAM_WRITE;
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ res = rtl8187_cmd_reset(dev);
+ if (res)
+ return res;
+
+ rtl818x_iowrite16(priv, (__le16 *)0xFF2D, 0x0FFF);
+ reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
+ reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
+ rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
+ reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
+ reg |= RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT |
+ RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
+ 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);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFFD4, 0xFFFF, 1);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG1);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG1, (reg & 0x3F) | 0x80);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0);
+ for (i = 0; i < ARRAY_SIZE(rtl8187b_reg_table); i++) {
+ rtl818x_iowrite8_idx(priv,
+ (u8 *) (rtl8187b_reg_table[i][0] | 0xFF00),
+ rtl8187b_reg_table[i][1],
+ rtl8187b_reg_table[i][2]);
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->TID_AC_MAP, 0xFA50);
+ rtl818x_iowrite16(priv, &priv->map->INT_MIG, 0);
+
+ rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF0, 0, 1);
+ rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF4, 0, 1);
+ rtl818x_iowrite8_idx(priv, (u8 *)0xFFF8, 0, 1);
+
+ rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00004001);
+
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x569A, 2);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ reg |= RTL818X_CONFIG3_ANAPARAM_WRITE;
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x2488);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+ msleep(1100);
+
+ priv->rf->init(dev);
+
+ reg = RTL818X_CMD_TX_ENABLE | RTL818X_CMD_RX_ENABLE;
+ rtl818x_iowrite8(priv, &priv->map->CMD, reg);
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF);
+
+ rtl818x_iowrite8(priv, (u8 *)0xFE41, 0xF4);
+ rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x00);
+ rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00);
+ rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01);
+ rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x0F);
+ rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00);
+ rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01);
+
+ reg = rtl818x_ioread8(priv, (u8 *)0xFFDB);
+ rtl818x_iowrite8(priv, (u8 *)0xFFDB, reg | (1 << 2));
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x59FA, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF74, 0x59D2, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF76, 0x59D2, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF78, 0x19FA, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7A, 0x19FA, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7C, 0x00D0, 3);
+ rtl818x_iowrite8(priv, (u8 *)0xFF61, 0);
+ rtl818x_iowrite8_idx(priv, (u8 *)0xFF80, 0x0F, 1);
+ rtl818x_iowrite8_idx(priv, (u8 *)0xFF83, 0x03, 1);
+ rtl818x_iowrite8(priv, (u8 *)0xFFDA, 0x10);
+ rtl818x_iowrite8_idx(priv, (u8 *)0xFF4D, 0x08, 2);
+
+ rtl818x_iowrite32(priv, &priv->map->HSSI_PARA, 0x0600321B);
+
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFFEC, 0x0800, 1);
+
+ return 0;
+}
+
static int rtl8187_start(struct ieee80211_hw *dev)
{
struct rtl8187_priv *priv = dev->priv;
u32 reg;
int ret;

- ret = rtl8187_init_hw(dev);
+ ret = (!priv->is_rtl8187b) ? rtl8187_init_hw(dev) :
+ rtl8187b_init_hw(dev);
if (ret)
return ret;

+ if (priv->is_rtl8187b) {
+ reg = RTL818X_RX_CONF_MGMT |
+ RTL818X_RX_CONF_DATA |
+ RTL818X_RX_CONF_BROADCAST |
+ RTL818X_RX_CONF_NICMAC |
+ RTL818X_RX_CONF_BSSID |
+ (7 << 13 /* RX FIFO threshold NONE */) |
+ (7 << 10 /* MAX RX DMA */) |
+ RTL818X_RX_CONF_RX_AUTORESETPHY |
+ RTL818X_RX_CONF_ONLYERLPKT |
+ RTL818X_RX_CONF_MULTICAST;
+ priv->rx_conf = reg;
+ rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
+
+ rtl818x_iowrite32(priv, &priv->map->TX_CONF,
+ RTL818X_TX_CONF_HW_SEQNUM |
+ RTL818X_TX_CONF_DISREQQSIZE |
+ (7 << 8 /* short retry limit */) |
+ (7 << 0 /* long retry limit */) |
+ (7 << 21 /* MAX TX DMA */));
+ rtl8187_init_urbs(dev);
+ return 0;
+ }
+
rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF);

rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0);
@@ -579,18 +803,20 @@ static int rtl8187_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf)
msleep(10);
rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg);

- rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
-
- if (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) {
- 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);
+ if (!priv->is_rtl8187b) {
+ rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
+
+ if (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) {
+ 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);
+ }
}

rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2);
@@ -606,14 +832,22 @@ static int rtl8187_config_interface(struct ieee80211_hw *dev,
{
struct rtl8187_priv *priv = dev->priv;
int i;
+ u8 reg;
+
+ priv->vif = vif;

for (i = 0; i < ETH_ALEN; i++)
rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]);

- if (is_valid_ether_addr(conf->bssid))
- rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_INFRA);
- else
- rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_NO_LINK);
+ if (is_valid_ether_addr(conf->bssid)) {
+ reg = RTL818X_MSR_INFRA;
+ if (priv->is_rtl8187b)
+ reg |= RTL818X_MSR_ENEDCA;
+ rtl818x_iowrite8(priv, &priv->map->MSR, reg);
+ } else {
+ reg = RTL818X_MSR_NO_LINK;
+ rtl818x_iowrite8(priv, &priv->map->MSR, reg);
+ }

return 0;
}
@@ -700,6 +934,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
struct rtl8187_priv *priv;
struct eeprom_93cx6 eeprom;
struct ieee80211_channel *channel;
+ const char *chip_name;
u16 txpwr, reg;
int err, i;
DECLARE_MAC_BUF(mac);
@@ -711,6 +946,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
}

priv = dev->priv;
+ priv->is_rtl8187b = (id->driver_info == DEVICE_RTL8187B);

SET_IEEE80211_DEV(dev, &intf->dev);
usb_set_intfdata(intf, dev);
@@ -738,7 +974,9 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
priv->mode = IEEE80211_IF_TYPE_MNTR;
dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
IEEE80211_HW_RX_INCLUDES_FCS;
- dev->extra_tx_headroom = sizeof(struct rtl8187_tx_hdr);
+ dev->extra_tx_headroom = (!priv->is_rtl8187b) ?
+ sizeof(struct rtl8187_tx_hdr) :
+ sizeof(struct rtl8187b_tx_hdr);
dev->queues = 1;
dev->max_rssi = 65;
dev->max_signal = 64;
@@ -775,10 +1013,24 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
(*channel++).hw_value = txpwr & 0xFF;
(*channel++).hw_value = txpwr >> 8;
}
- for (i = 0; i < 2; i++) {
- eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_6 + i,
+ if (!priv->is_rtl8187b) {
+ for (i = 0; i < 2; i++) {
+ eeprom_93cx6_read(&eeprom,
+ RTL8187_EEPROM_TXPWR_CHAN_6 + i,
+ &txpwr);
+ (*channel++).hw_value = txpwr & 0xFF;
+ (*channel++).hw_value = txpwr >> 8;
+ }
+ } else {
+ eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_6,
&txpwr);
(*channel++).hw_value = txpwr & 0xFF;
+
+ eeprom_93cx6_read(&eeprom, 0x0A, &txpwr);
+ (*channel++).hw_value = txpwr & 0xFF;
+
+ eeprom_93cx6_read(&eeprom, 0x1C, &txpwr);
+ (*channel++).hw_value = txpwr & 0xFF;
(*channel++).hw_value = txpwr >> 8;
}

@@ -794,6 +1046,38 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);

+ if (!priv->is_rtl8187b) {
+ u32 reg32;
+ reg32 = rtl818x_ioread32(priv, &priv->map->TX_CONF);
+ reg32 &= RTL818X_TX_CONF_HWVER_MASK;
+ switch (reg32) {
+ case RTL818X_TX_CONF_R8187vD_1:
+ case RTL818X_TX_CONF_R8187vD_2:
+ chip_name = "RTL8187vD";
+ break;
+ default:
+ chip_name = "RTL8187vB (default)";
+ }
+ } else {
+ switch (rtl818x_ioread8(priv, (u8 *)0xFFE1)) {
+ case RTL818X_R8187B_B:
+ chip_name = "RTL8187BvB";
+ priv->hw_rev = RTL8187BvB;
+ break;
+ case RTL818X_R8187B_D:
+ chip_name = "RTL8187BvD";
+ priv->hw_rev = RTL8187BvD;
+ break;
+ case RTL818X_R8187B_E:
+ chip_name = "RTL8187BvE";
+ priv->hw_rev = RTL8187BvE;
+ break;
+ default:
+ chip_name = "RTL8187BvB (default)";
+ priv->hw_rev = RTL8187BvB;
+ }
+ }
+
priv->rf = rtl8187_detect_rf(dev);

err = ieee80211_register_hw(dev);
@@ -802,9 +1086,9 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
goto err_free_dev;
}

- printk(KERN_INFO "%s: hwaddr %s, rtl8187 V%d + %s\n",
+ printk(KERN_INFO "%s: hwaddr %s, %s V%d + %s\n",
wiphy_name(dev->wiphy), print_mac(mac, dev->wiphy->perm_addr),
- priv->asic_rev, priv->rf->name);
+ chip_name, priv->asic_rev, priv->rf->name);

return 0;

diff --git a/drivers/net/wireless/rtl8187_rtl8225.c b/drivers/net/wireless/rtl8187_rtl8225.c
index 9146387..c9419e0 100644
--- a/drivers/net/wireless/rtl8187_rtl8225.c
+++ b/drivers/net/wireless/rtl8187_rtl8225.c
@@ -471,12 +471,42 @@ static void rtl8225_rf_init(struct ieee80211_hw *dev)
rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[2]);
}

+static const u8 rtl8225z2_agc[] = {
+ 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51, 0x4f,
+ 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37,
+ 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f,
+ 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, 0x0d, 0x0b, 0x09, 0x07,
+ 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28,
+ 0x28, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31
+};
+static const u8 rtl8225z2_ofdm[] = {
+ 0x10, 0x0d, 0x01, 0x00, 0x14, 0xfb, 0xfb, 0x60,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00,
+ 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0xa8, 0x26,
+ 0x32, 0x33, 0x07, 0xa5, 0x6f, 0x55, 0xc8, 0xb3,
+ 0x0a, 0xe1, 0x2C, 0x8a, 0x86, 0x83, 0x34, 0x0f,
+ 0x4f, 0x24, 0x6f, 0xc2, 0x6b, 0x40, 0x80, 0x00,
+ 0xc0, 0xc1, 0x58, 0xf1, 0x00, 0xe4, 0x90, 0x3e,
+ 0x6d, 0x3c, 0xfb, 0x07
+};
+
static const u8 rtl8225z2_tx_power_cck_ch14[] = {
- 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00
+ 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00
};

static const u8 rtl8225z2_tx_power_cck[] = {
- 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04
+ 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04,
+ 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03,
+ 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03,
+ 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03
};

static const u8 rtl8225z2_tx_power_ofdm[] = {
@@ -542,6 +572,85 @@ static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
msleep(1);
}

+void rtl8225z2_b_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u8 cck_power, ofdm_power;
+ const u8 *tmp;
+ int i;
+
+ cck_power = priv->channels[channel - 1].hw_value & 0xF;
+ ofdm_power = priv->channels[channel - 1].hw_value >> 4;
+
+ if (cck_power > 15)
+ cck_power = (priv->hw_rev == RTL8187BvB) ? 15 : 22;
+ else
+ cck_power += (priv->hw_rev == RTL8187BvB) ? 0 : 7;
+ cck_power += priv->txpwr_base & 0xF;
+ cck_power = min(cck_power, (u8)35);
+
+ if (ofdm_power > 15)
+ ofdm_power = (priv->hw_rev == RTL8187BvB) ? 17 : 25;
+ else
+ ofdm_power += (priv->hw_rev == RTL8187BvB) ? 2 : 10;
+ ofdm_power += (priv->txpwr_base >> 4) & 0xF;
+ ofdm_power = min(ofdm_power, (u8)35);
+
+ if (channel == 14)
+ tmp = rtl8225z2_tx_power_cck_ch14;
+ else
+ tmp = rtl8225z2_tx_power_cck;
+
+ if (priv->hw_rev == RTL8187BvB) {
+ if (cck_power <= 6)
+ ; /* do nothing */
+ else if (cck_power <= 11)
+ tmp += 8;
+ else
+ tmp += 16;
+ } else {
+ if (cck_power <= 5)
+ ; /* do nothing */
+ else if (cck_power <= 11)
+ tmp += 8;
+ else if (cck_power <= 17)
+ tmp += 16;
+ else
+ tmp += 24;
+ }
+
+ for (i = 0; i < 8; i++)
+ rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
+ rtl8225z2_tx_gain_cck_ofdm[cck_power]);
+ msleep(1);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
+ rtl8225z2_tx_gain_cck_ofdm[ofdm_power] << 1);
+ if (priv->hw_rev == RTL8187BvB) {
+ if (ofdm_power <= 11) {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x60);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x60);
+ } else {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x5c);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x5c);
+ }
+ } else {
+ if (ofdm_power <= 11) {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x5c);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x5c);
+ } else if (ofdm_power <= 17) {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x54);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x54);
+ } else {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x50);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x50);
+ }
+ }
+ msleep(1);
+}
+
static const u16 rtl8225z2_rxgain[] = {
0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
@@ -715,6 +824,81 @@ static void rtl8225z2_rf_init(struct ieee80211_hw *dev)
rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002);
}

+void rtl8225z2_b_rf_init(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ int i;
+
+ rtl8225_write(dev, 0x0, 0x0B7); msleep(1);
+ rtl8225_write(dev, 0x1, 0xEE0); msleep(1);
+ rtl8225_write(dev, 0x2, 0x44D); msleep(1);
+ rtl8225_write(dev, 0x3, 0x441); msleep(1);
+ rtl8225_write(dev, 0x4, 0x8C3); msleep(1);
+ rtl8225_write(dev, 0x5, 0xC72); msleep(1);
+ rtl8225_write(dev, 0x6, 0x0E6); msleep(1);
+ rtl8225_write(dev, 0x7, 0x82A); msleep(1);
+ rtl8225_write(dev, 0x8, 0x03F); msleep(1);
+ rtl8225_write(dev, 0x9, 0x335); msleep(1);
+ rtl8225_write(dev, 0xa, 0x9D4); msleep(1);
+ rtl8225_write(dev, 0xb, 0x7BB); msleep(1);
+ rtl8225_write(dev, 0xc, 0x850); msleep(1);
+ rtl8225_write(dev, 0xd, 0xCDF); msleep(1);
+ rtl8225_write(dev, 0xe, 0x02B); msleep(1);
+ rtl8225_write(dev, 0xf, 0x114); msleep(1);
+
+ rtl8225_write(dev, 0x0, 0x1B7); msleep(1);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) {
+ rtl8225_write(dev, 0x1, i + 1); msleep(1);
+ rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); msleep(1);
+ }
+
+ rtl8225_write(dev, 0x3, 0x080); msleep(1);
+ rtl8225_write(dev, 0x5, 0x004); msleep(1);
+ rtl8225_write(dev, 0x0, 0x0B7); msleep(1);
+ msleep(3000);
+
+ rtl8225_write(dev, 0x2, 0xC4D); msleep(1);
+ msleep(2000);
+
+ rtl8225_write(dev, 0x2, 0x44D); msleep(1);
+ rtl8225_write(dev, 0x0, 0x2BF); msleep(1);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, 0x03);
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, 0x07);
+ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03);
+
+ rtl8225_write_phy_ofdm(dev, 0x80, 0x12);
+ for (i = 0; i < ARRAY_SIZE(rtl8225z2_agc); i++) {
+ rtl8225_write_phy_ofdm(dev, 0xF, rtl8225z2_agc[i]);
+ rtl8225_write_phy_ofdm(dev, 0xE, 0x80 + i);
+ rtl8225_write_phy_ofdm(dev, 0xE, 0);
+ }
+ rtl8225_write_phy_ofdm(dev, 0x80, 0x10);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225z2_ofdm); i++)
+ rtl8225_write_phy_ofdm(dev, i, rtl8225z2_ofdm[i]);
+
+ rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
+ rtl818x_iowrite8(priv, &priv->map->SLOT, 9);
+ rtl818x_iowrite8(priv, (u8 *)0xFFF0, 28);
+ rtl818x_iowrite8(priv, (u8 *)0xFFF4, 28);
+ rtl818x_iowrite8(priv, (u8 *)0xFFF8, 28);
+ rtl818x_iowrite8(priv, (u8 *)0xFFFC, 28);
+ rtl818x_iowrite8(priv, (u8 *)0xFF2D, 0x5B);
+ rtl818x_iowrite8(priv, (u8 *)0xFF79, 0x5B);
+ 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); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0xa4, 0xb6); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x85, 0xfc); msleep(1);
+ rtl8225_write_phy_cck(dev, 0xc1, 0x88); msleep(1);
+}
+
static void rtl8225_rf_stop(struct ieee80211_hw *dev)
{
u8 reg;
@@ -737,10 +921,7 @@ static void rtl8225_rf_set_channel(struct ieee80211_hw *dev,
struct rtl8187_priv *priv = dev->priv;
int chan = ieee80211_frequency_to_channel(conf->channel->center_freq);

- if (priv->rf->init == rtl8225_rf_init)
- rtl8225_rf_set_tx_power(dev, chan);
- else
- rtl8225z2_rf_set_tx_power(dev, chan);
+ priv->rf->set_txpwr(dev, chan);

rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]);
msleep(10);
@@ -750,29 +931,43 @@ static const struct rtl818x_rf_ops rtl8225_ops = {
.name = "rtl8225",
.init = rtl8225_rf_init,
.stop = rtl8225_rf_stop,
- .set_chan = rtl8225_rf_set_channel
+ .set_chan = rtl8225_rf_set_channel,
+ .set_txpwr = rtl8225_rf_set_tx_power
};

static const struct rtl818x_rf_ops rtl8225z2_ops = {
.name = "rtl8225z2",
.init = rtl8225z2_rf_init,
.stop = rtl8225_rf_stop,
- .set_chan = rtl8225_rf_set_channel
+ .set_chan = rtl8225_rf_set_channel,
+ .set_txpwr = rtl8225z2_rf_set_tx_power
+};
+
+static const struct rtl818x_rf_ops rtl8225z2_b_ops = {
+ .name = "rtl8225z2",
+ .init = rtl8225z2_b_rf_init,
+ .stop = rtl8225_rf_stop,
+ .set_chan = rtl8225_rf_set_channel,
+ .set_txpwr = rtl8225z2_b_rf_set_tx_power
};

const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *dev)
{
u16 reg8, reg9;
+ struct rtl8187_priv *priv = dev->priv;

- rtl8225_write(dev, 0, 0x1B7);
+ if (!priv->is_rtl8187b) {
+ rtl8225_write(dev, 0, 0x1B7);

- reg8 = rtl8225_read(dev, 8);
- reg9 = rtl8225_read(dev, 9);
+ reg8 = rtl8225_read(dev, 8);
+ reg9 = rtl8225_read(dev, 9);

- rtl8225_write(dev, 0, 0x0B7);
+ rtl8225_write(dev, 0, 0x0B7);

- if (reg8 != 0x588 || reg9 != 0x700)
- return &rtl8225_ops;
+ if (reg8 != 0x588 || reg9 != 0x700)
+ return &rtl8225_ops;

- return &rtl8225z2_ops;
+ return &rtl8225z2_ops;
+ } else
+ return &rtl8225z2_b_ops;
}
diff --git a/drivers/net/wireless/rtl818x.h b/drivers/net/wireless/rtl818x.h
index 4f7d38f..763601a 100644
--- a/drivers/net/wireless/rtl818x.h
+++ b/drivers/net/wireless/rtl818x.h
@@ -66,7 +66,10 @@ struct rtl818x_csr {
#define RTL818X_TX_CONF_R8180_F (3 << 25)
#define RTL818X_TX_CONF_R8185_ABC (4 << 25)
#define RTL818X_TX_CONF_R8185_D (5 << 25)
+#define RTL818X_TX_CONF_R8187vD_1 (5 << 25)
+#define RTL818X_TX_CONF_R8187vD_2 (6 << 25)
#define RTL818X_TX_CONF_HWVER_MASK (7 << 25)
+#define RTL818X_TX_CONF_DISREQQSIZE (1 << 28)
#define RTL818X_TX_CONF_PROBE_DTS (1 << 29)
#define RTL818X_TX_CONF_HW_SEQNUM (1 << 30)
#define RTL818X_TX_CONF_CW_MIN (1 << 31)
@@ -106,8 +109,11 @@ struct rtl818x_csr {
#define RTL818X_MSR_NO_LINK (0 << 2)
#define RTL818X_MSR_ADHOC (1 << 2)
#define RTL818X_MSR_INFRA (2 << 2)
+#define RTL818X_MSR_MASTER (3 << 2)
+#define RTL818X_MSR_ENEDCA (4 << 2)
u8 CONFIG3;
#define RTL818X_CONFIG3_ANAPARAM_WRITE (1 << 6)
+#define RTL818X_CONFIG3_GNT_SELECT (1 << 7)
u8 CONFIG4;
#define RTL818X_CONFIG4_POWEROFF (1 << 6)
#define RTL818X_CONFIG4_VCOOFF (1 << 7)
@@ -133,7 +139,9 @@ struct rtl818x_csr {
__le32 RF_TIMING;
u8 GP_ENABLE;
u8 GPIO;
- u8 reserved_12[10];
+ u8 reserved_12[2];
+ __le32 HSSI_PARA;
+ u8 reserved_13[4];
u8 TX_AGC_CTL;
#define RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT (1 << 0)
#define RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT (1 << 1)
@@ -141,29 +149,38 @@ struct rtl818x_csr {
u8 TX_GAIN_CCK;
u8 TX_GAIN_OFDM;
u8 TX_ANTENNA;
- u8 reserved_13[16];
+ u8 reserved_14[16];
u8 WPA_CONF;
- u8 reserved_14[3];
+ u8 reserved_15[3];
u8 SIFS;
u8 DIFS;
u8 SLOT;
- u8 reserved_15[5];
+ u8 reserved_16[5];
u8 CW_CONF;
#define RTL818X_CW_CONF_PERPACKET_CW_SHIFT (1 << 0)
#define RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT (1 << 1)
u8 CW_VAL;
u8 RATE_FALLBACK;
- u8 reserved_16[25];
+#define RTL818X_RATE_FALLBACK_ENABLE (1 << 7)
+ u8 ACM_CONTROL;
+ u8 reserved_17[24];
u8 CONFIG5;
u8 TX_DMA_POLLING;
- u8 reserved_17[2];
+ u8 reserved_18[2];
__le16 CWR;
u8 RETRY_CTR;
- u8 reserved_18[5];
+ u8 reserved_19[3];
+ __le16 INT_MIG;
+#define RTL818X_R8187B_B 0
+#define RTL818X_R8187B_D 1
+#define RTL818X_R8187B_E 2
__le32 RDSAR;
- u8 reserved_19[12];
- __le16 FEMR;
+ __le16 TID_AC_MAP;
u8 reserved_20[4];
+ u8 ANAPARAM3;
+ u8 reserved_21[5];
+ __le16 FEMR;
+ u8 reserved_22[4];
__le16 TALLY_CNT;
u8 TALLY_SEL;
} __attribute__((packed));
@@ -173,6 +190,7 @@ struct rtl818x_rf_ops {
void (*init)(struct ieee80211_hw *);
void (*stop)(struct ieee80211_hw *);
void (*set_chan)(struct ieee80211_hw *, struct ieee80211_conf *);
+ void (*set_txpwr)(struct ieee80211_hw *, int);
};

#endif /* RTL818X_H */


--
[]'s
Herton


Subject: Re: [PATCH] Add Realtek 8187B support

Em Wednesday 09 April 2008 12:46:53 Larry Finger escreveu:
> Herton Ronaldo Krzesinski wrote:
> > Hi, this patch (made against wireless-testing repository) adds support for
> > 8187B to the rtl8187 module.
>
> Thanks for your efforts. If this driver had been available two months
> ago, it would have made my life much simpler.
>
> Don't get too discouraged about the negative comments - that kind of
> diligence is what makes Linux so robust, and keeps this open-source
> project from chaos.

Thanks, ok no problem :)

>
> Once your patches get more stabilized, I will be submitting one to
> handle my hardware, which is a LevelOne WNC-0301USB. For some reason,
> it has an idProduct code of 0x8187, not 0x8189, and one has to look at
> the iProduct value. For early testing, I am just forcing it to be an
> 8187B. For my information, what is the iProduct value for your device?

Mine here reports iProduct = 2 RTL8187B_WLAN_Adapter
It's a Quanta IL1 mini-notebook with 8187b builtin.

>
> I did only minimal testing. The USB device was only about 1 meter from
> the AP. It connected with a WPA-encrypted network in about the same
> time as my BCM43xx devices do.
>
> I do have one minor comment - I prefer to see signal and noise values
> in dBm, not as xx/65.

It's using the same signal/noise reporting as original rtl8187, we can change
it, I also found it was strange this way.

>
> Good job,
>
> Larry

--
[]'s
Herton

Subject: Re: [PATCH] Add Realtek 8187B support

Em Wednesday 09 April 2008 03:31:40 Pavel Roskin escreveu:
> On Tue, 2008-04-08 at 19:31 -0300, Herton Ronaldo Krzesinski wrote:
>
> P.S. More stuff as I'm going through the patch. Sorry, I had to post
> the first reply quickly to disavow my Signed-off-by.
>
> > Also I fixed a bug of a missing "priv->vif = vif" in rtl8187_config_interface
> > that makes kernel oops in transmit code in ieee80211_generic_frame_duration
> > function.
>
> This should be a separate patch. In the kernel, it's very important to
> be able to pinpoint which change broke things. And git is well suited
> for working with multiple patches (my personal preference is StGIT,
> which is not just suited for that - it's the main thing StGIT does).

I'll submit a separate patch.

>
> > diff --git a/drivers/net/wireless/rtl8180_rtl8225.c b/drivers/net/wireless/rtl8180_rtl8225.c
> > index cd22781..b4c6ae0 100644
> > --- a/drivers/net/wireless/rtl8180_rtl8225.c
> > +++ b/drivers/net/wireless/rtl8180_rtl8225.c
> > @@ -718,10 +718,7 @@ static void rtl8225_rf_set_channel(struct ieee80211_hw *dev,
> > struct rtl8180_priv *priv = dev->priv;
> > int chan = ieee80211_frequency_to_channel(conf->channel->center_freq);
> >
> > - if (priv->rf->init == rtl8225_rf_init)
> > - rtl8225_rf_set_tx_power(dev, chan);
> > - else
> > - rtl8225z2_rf_set_tx_power(dev, chan);
> > + priv->rf->set_txpwr(dev, chan);
>
> It seems to me it's unrelated to rtl8187b support. You are doing some
> unrelated refactoring here, which could be done in a separate patch.

The problem is that if I don't do this, in rtl8225_rf_set_channel in
rtl8187_rtl8225.c I would have to add more checks for what function to call,
so I introduced this that seemed more cleaner. I'll provide too a separate
patch for that then if people find to be better.

>
> > - .set_chan = rtl8225_rf_set_channel
> > + .set_chan = rtl8225_rf_set_channel,
> > + .set_txpwr = rtl8225_rf_set_tx_power
>
> Same thing here. If you need extra infrastructure, add it in one patch
> and leave hardware support for another.
>
> > +struct rtl8187b_rx_hdr {
> > + __le32 flags;
> > + __le64 mac_time;
> > + u8 noise;
> > + u8 signal;
> > + u8 agc;
> > + u8 reserved;
> > + __le32 unused;
> > +} __attribute__((packed));
>
> I know, you are copying parts from the rtl8187 header. But looking at
> the vendor driver, "reserved" and "unused" are better described. Nobody
> would guess it by looking at this header. It would be great if you at
> least give a hint that the vendor driver describes the structure of
> those fields. That should be a separate patch, of course.

I used the same style used already in original rtl8187: if there is reserved
bitfields just use reserved, otherwise unused. The vendor drivers uses this for
tx/rx:

struct tx_desc {

#ifdef _LINUX_BYTEORDER_LITTLE_ENDIAN_H

//dword 0
unsigned int tpktsize:12;
unsigned int rsvd0:3;
unsigned int no_encrypt:1;
unsigned int splcp:1;
unsigned int morefrag:1;
unsigned int ctsen:1;
unsigned int rtsrate:4;
unsigned int rtsen:1;
unsigned int txrate:4;
unsigned int last:1;
unsigned int first:1;
unsigned int dmaok:1;
unsigned int own:1;

//dword 1
unsigned short rtsdur;
unsigned short length:15;
unsigned short l_ext:1;

//dword 2
unsigned int bufaddr;

//dword 3
unsigned short rxlen:12;
unsigned short rsvd1:3;
unsigned short miccal:1;
unsigned short dur;

//dword 4
unsigned int nextdescaddr;

//dword 5
unsigned char rtsagc;
unsigned char retrylimit;
unsigned short rtdb:1;
unsigned short noacm:1;
unsigned short pifs:1;
unsigned short rsvd2:4;
unsigned short rtsratefallback:4;
unsigned short ratefallback:5;

//dword 6
unsigned short delaybound;
unsigned short rsvd3:4;
unsigned short agc:8;
unsigned short antenna:1;
unsigned short spc:2;
unsigned short rsvd4:1;

//dword 7
unsigned char len_adjust:2;
unsigned char rsvd5:1;
unsigned char tpcdesen:1;
unsigned char tpcpolarity:2;
unsigned char tpcen:1;
unsigned char pten:1;

unsigned char bckey:6;
unsigned char enbckey:1;
unsigned char enpmpd:1;
unsigned short fragqsz;

#else

#error "please modify tx_desc to your own\n"

#endif

} __attribute__((packed));

struct rx_desc_rtl8187b {

#ifdef _LINUX_BYTEORDER_LITTLE_ENDIAN_H

//dword 0
unsigned int rxlen:12;
unsigned int icv:1;
unsigned int crc32:1;
unsigned int pwrmgt:1;
unsigned int res:1;
unsigned int bar:1;
unsigned int pam:1;
unsigned int mar:1;
unsigned int qos:1;
unsigned int rxrate:4;
unsigned int trsw:1;
unsigned int splcp:1;
unsigned int fovf:1;
unsigned int dmafail:1;
unsigned int last:1;
unsigned int first:1;
unsigned int eor:1;
unsigned int own:1;

//dword 1
unsigned int tsftl;

//dword 2
unsigned int tsfth;

//dword 3
unsigned char sq;
unsigned char rssi:7;
unsigned char antenna:1;

unsigned char agc;
unsigned char decrypted:1;
unsigned char wakeup:1;
unsigned char shift:1;
unsigned char rsvd0:5;

//dword 4
unsigned int num_mcsi:4;
unsigned int snr_long2end:6;
unsigned int cfo_bias:6;

unsigned int pwdb_g12:8;
unsigned int fot:8;

#else

#error "please modify tx_desc to your own\n"

#endif

}__attribute__((packed));

struct rx_desc_rtl8187 {

#ifdef _LINUX_BYTEORDER_LITTLE_ENDIAN_H

//dword 0
unsigned int rxlen:12;
unsigned int icv:1;
unsigned int crc32:1;
unsigned int pwrmgt:1;
unsigned int res:1;
unsigned int bar:1;
unsigned int pam:1;
unsigned int mar:1;
unsigned int qos:1;
unsigned int rxrate:4;
unsigned int trsw:1;
unsigned int splcp:1;
unsigned int fovf:1;
unsigned int dmafail:1;
unsigned int last:1;
unsigned int first:1;
unsigned int eor:1;
unsigned int own:1;

//dword 1
unsigned char sq;
unsigned char rssi:7;
unsigned char antenna:1;

unsigned char agc;
unsigned char decrypted:1;
unsigned char wakeup:1;
unsigned char shift:1;
unsigned char rsvd0:5;

//dword 2
unsigned int tsftl;

//dword 3
unsigned int tsfth;

#else

#error "please modify tx_desc to your own\n"

#endif

}__attribute__((packed));

I can just then define all fields/bits like the original driver, but anyway
would not be useful, as we don't know what all these bits do (no documentation)
and even Realtek doesn't uses them in the driver.

>
> > +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));
>
> Those are indeed "unused in" the vendor driver.
>
> > +#define DEVICE_RTL8187 0
> > +#define DEVICE_RTL8187B 1
>
> I prefer enums here, but I don't feel strongly about it.
>
> > struct rtl8187_priv {
> > /* common between rtl818x drivers */
> > struct rtl818x_csr *map;
> > @@ -76,70 +103,100 @@ struct rtl8187_priv {
> > u32 rx_conf;
> > u16 txpwr_base;
> > u8 asic_rev;
> > + u8 is_rtl8187b;
> > + enum {
> > + RTL8187BvB,
> > + RTL8187BvD,
> > + RTL8187BvE
> > + } hw_rev;
>
> Or maybe we could have one enum here, and use (hw_rev > DEVICE_RTL8187)
> instead of is_rtl8187b. Maybe there will be future revisions that
> change something else. The standard approaches are flags that are ORed
> or enums that are compared. is_something doesn't scale well.

Ok, I'll merge and compare using '>'

>
> > -static inline u8 rtl818x_ioread8(struct rtl8187_priv *priv, u8 *addr)
> > +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, 0, &val, sizeof(val), HZ / 2);
> > + (unsigned long)addr, idx & 0x03, &val,
> > + sizeof(val), HZ / 2);
>
> As I said, "addr" should not be a pointer. In fact, the fifth argument
> is __u16. There is no way a valid pointer can even be passed to
> usb_control_msg() without truncating it. Make it __u16 and remove all
> casts unless they are needed and you know why.

I don't understand how it could be truncated, but indeed this casts are not
needed at all. I'll make another patch, as like I said at the other mail I just
followed what was already on rtl8187.

>
> > -static inline u16 rtl818x_ioread16(struct rtl8187_priv *priv, __le16 *addr)
> > +#define rtl818x_ioread8(priv, addr) rtl818x_ioread8_idx(priv, addr, 0)
>
> Inline functions are preferred for that.

Will do as inline function.

>
> > +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, 0, &val, sizeof(val), HZ / 2);
> > + (unsigned long)addr, idx & 0x03, &val,
> > + sizeof(val), HZ / 2);
>
> More of the same stuff.
>
> > static struct usb_device_id rtl8187_table[] __devinitdata = {
> > - /* Realtek */
> > - {USB_DEVICE(0x0bda, 0x8187)},
> > + /* Realtek 8187 */
> > + {USB_DEVICE(0x0bda, 0x8187), .driver_info = DEVICE_RTL8187},
>
> You are mixing to things in the comment. DEVICE_RTL8187 already
> indicates that it's 8187. Better keep Realtek devices together, and
> leave the details to the code.

I'll group them together on the same entry.

>
> > - hdr = (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr));
> > - hdr->flags = cpu_to_le32(flags);
> > - hdr->len = 0;
> > - hdr->rts_duration = rts_dur;
> > - hdr->retry = cpu_to_le32(control->retry_limit << 8);
> > + if (!priv->is_rtl8187b) {
>
> I would explore the possibility of splitting the hardware specific parts
> of rtl8187_tx() into two separate functions.

I'll split them.

>
> > @@ -240,11 +264,25 @@ static void rtl8187_rx_cb(struct urb *urb)
>
> And the same on the rx side. gcc is good at inlining static functions
> that are used once.
>
> > + rx_status.mactime = le64_to_cpu(hdr_b->mac_time);
>
> Careful here! mac_time is not size-aligned in rtl8187b_rx_hdr. I would
> ask our alignment gurus it it's OK.

I don't know much about alignment or possible problems in this, seems to be ok,
who can verify this?

>
> > -static int rtl8187_init_hw(struct ieee80211_hw *dev)
> > +static int rtl8187_cmd_reset(struct ieee80211_hw *dev)
> > {
> > struct rtl8187_priv *priv = dev->priv;
> > u8 reg;
> > int i;
>
> It seems to me that there is too much in common between those functions.
> Please consider refactoring. I would also suggest that you submit it
> separately.

? In fact what was in common was reset code, that I already splitted. About
the rest of hardware initialization they are very different, some commands
are equal but may be the hardware requires a specific sequence, we can't be
sure as there is no documentation.

>
> Skipping lots of stuff for a later review, as it's getting late, and the
> end it far away. I hope you got the idea.
>
> > u8 reserved_19[3];
> > + __le16 INT_MIG;
> > +#define RTL818X_R8187B_B 0
> > +#define RTL818X_R8187B_D 1
> > +#define RTL818X_R8187B_E 2
>
> You are inconsistent here. You were using an enum in another structure
> essentially for the same thing.
>

Nope, these are specific values returned by hardware (see rtl8187_probe where
these are used).

--
[]'s
Herton

2008-04-09 06:31:46

by Pavel Roskin

[permalink] [raw]
Subject: Re: [PATCH] Add Realtek 8187B support

On Tue, 2008-04-08 at 19:31 -0300, Herton Ronaldo Krzesinski wrote:

P.S. More stuff as I'm going through the patch. Sorry, I had to post
the first reply quickly to disavow my Signed-off-by.

> Also I fixed a bug of a missing "priv->vif = vif" in rtl8187_config_interface
> that makes kernel oops in transmit code in ieee80211_generic_frame_duration
> function.

This should be a separate patch. In the kernel, it's very important to
be able to pinpoint which change broke things. And git is well suited
for working with multiple patches (my personal preference is StGIT,
which is not just suited for that - it's the main thing StGIT does).

> diff --git a/drivers/net/wireless/rtl8180_rtl8225.c b/drivers/net/wireless/rtl8180_rtl8225.c
> index cd22781..b4c6ae0 100644
> --- a/drivers/net/wireless/rtl8180_rtl8225.c
> +++ b/drivers/net/wireless/rtl8180_rtl8225.c
> @@ -718,10 +718,7 @@ static void rtl8225_rf_set_channel(struct ieee80211_hw *dev,
> struct rtl8180_priv *priv = dev->priv;
> int chan = ieee80211_frequency_to_channel(conf->channel->center_freq);
>
> - if (priv->rf->init == rtl8225_rf_init)
> - rtl8225_rf_set_tx_power(dev, chan);
> - else
> - rtl8225z2_rf_set_tx_power(dev, chan);
> + priv->rf->set_txpwr(dev, chan);

It seems to me it's unrelated to rtl8187b support. You are doing some
unrelated refactoring here, which could be done in a separate patch.

> - .set_chan = rtl8225_rf_set_channel
> + .set_chan = rtl8225_rf_set_channel,
> + .set_txpwr = rtl8225_rf_set_tx_power

Same thing here. If you need extra infrastructure, add it in one patch
and leave hardware support for another.

> +struct rtl8187b_rx_hdr {
> + __le32 flags;
> + __le64 mac_time;
> + u8 noise;
> + u8 signal;
> + u8 agc;
> + u8 reserved;
> + __le32 unused;
> +} __attribute__((packed));

I know, you are copying parts from the rtl8187 header. But looking at
the vendor driver, "reserved" and "unused" are better described. Nobody
would guess it by looking at this header. It would be great if you at
least give a hint that the vendor driver describes the structure of
those fields. That should be a separate patch, of course.

> +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));

Those are indeed "unused in" the vendor driver.

> +#define DEVICE_RTL8187 0
> +#define DEVICE_RTL8187B 1

I prefer enums here, but I don't feel strongly about it.

> struct rtl8187_priv {
> /* common between rtl818x drivers */
> struct rtl818x_csr *map;
> @@ -76,70 +103,100 @@ struct rtl8187_priv {
> u32 rx_conf;
> u16 txpwr_base;
> u8 asic_rev;
> + u8 is_rtl8187b;
> + enum {
> + RTL8187BvB,
> + RTL8187BvD,
> + RTL8187BvE
> + } hw_rev;

Or maybe we could have one enum here, and use (hw_rev > DEVICE_RTL8187)
instead of is_rtl8187b. Maybe there will be future revisions that
change something else. The standard approaches are flags that are ORed
or enums that are compared. is_something doesn't scale well.

> -static inline u8 rtl818x_ioread8(struct rtl8187_priv *priv, u8 *addr)
> +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, 0, &val, sizeof(val), HZ / 2);
> + (unsigned long)addr, idx & 0x03, &val,
> + sizeof(val), HZ / 2);

As I said, "addr" should not be a pointer. In fact, the fifth argument
is __u16. There is no way a valid pointer can even be passed to
usb_control_msg() without truncating it. Make it __u16 and remove all
casts unless they are needed and you know why.

> -static inline u16 rtl818x_ioread16(struct rtl8187_priv *priv, __le16 *addr)
> +#define rtl818x_ioread8(priv, addr) rtl818x_ioread8_idx(priv, addr, 0)

Inline functions are preferred for that.

> +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, 0, &val, sizeof(val), HZ / 2);
> + (unsigned long)addr, idx & 0x03, &val,
> + sizeof(val), HZ / 2);

More of the same stuff.

> static struct usb_device_id rtl8187_table[] __devinitdata = {
> - /* Realtek */
> - {USB_DEVICE(0x0bda, 0x8187)},
> + /* Realtek 8187 */
> + {USB_DEVICE(0x0bda, 0x8187), .driver_info = DEVICE_RTL8187},

You are mixing to things in the comment. DEVICE_RTL8187 already
indicates that it's 8187. Better keep Realtek devices together, and
leave the details to the code.

> - hdr = (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr));
> - hdr->flags = cpu_to_le32(flags);
> - hdr->len = 0;
> - hdr->rts_duration = rts_dur;
> - hdr->retry = cpu_to_le32(control->retry_limit << 8);
> + if (!priv->is_rtl8187b) {

I would explore the possibility of splitting the hardware specific parts
of rtl8187_tx() into two separate functions.

> @@ -240,11 +264,25 @@ static void rtl8187_rx_cb(struct urb *urb)

And the same on the rx side. gcc is good at inlining static functions
that are used once.

> + rx_status.mactime = le64_to_cpu(hdr_b->mac_time);

Careful here! mac_time is not size-aligned in rtl8187b_rx_hdr. I would
ask our alignment gurus it it's OK.

> -static int rtl8187_init_hw(struct ieee80211_hw *dev)
> +static int rtl8187_cmd_reset(struct ieee80211_hw *dev)
> {
> struct rtl8187_priv *priv = dev->priv;
> u8 reg;
> int i;

It seems to me that there is too much in common between those functions.
Please consider refactoring. I would also suggest that you submit it
separately.

Skipping lots of stuff for a later review, as it's getting late, and the
end it far away. I hope you got the idea.

> u8 reserved_19[3];
> + __le16 INT_MIG;
> +#define RTL818X_R8187B_B 0
> +#define RTL818X_R8187B_D 1
> +#define RTL818X_R8187B_E 2

You are inconsistent here. You were using an enum in another structure
essentially for the same thing.

--
Regards,
Pavel Roskin

2008-04-11 04:58:02

by Larry Finger

[permalink] [raw]
Subject: Re: [PATCH] Add Realtek 8187B support

Herton Ronaldo Krzesinski wrote:
>
> Can you open your dongle and see if you really have a RTL8187B? (as you
> said you had to force the ID). May be it wasn't really a 8187b device but
> another variant. I did many tests this week with a 8187b that continues to
> work ok.

I don't want to open it as it should still be under warranty. I'm
pretty sure it is an 8187b. When I tried it with the 8187 driver, it
gets an rf calibration error. By using your driver with the patch
below, it loaded and worked, at least for a while. It probably was a
natural death, but I felt I should issue a warning, jest in case.

I was going to hold the following patch until I had more to
contribute; however, I don't know if I will be able to participate.
Please apply the patch, as it lets devices like mine, where the
iProduct string identifies is as an RTL8187B device, but the idProduct
value is 0x8187, not 0x8189.

Thanks,

Larry

Index: wireless-testing/drivers/net/wireless/rtl8187_dev.c
===================================================================
--- wireless-testing.orig/drivers/net/wireless/rtl8187_dev.c
+++ wireless-testing/drivers/net/wireless/rtl8187_dev.c
@@ -946,7 +946,8 @@ static int __devinit rtl8187_probe(struc
}

priv = dev->priv;
- priv->is_rtl8187b = (id->driver_info == DEVICE_RTL8187B);
+ priv->is_rtl8187b = (id->driver_info == DEVICE_RTL8187B) ||
+ !memcmp(udev->product, "RTL8187B", 8);

SET_IEEE80211_DEV(dev, &intf->dev);
usb_set_intfdata(intf, dev);




Subject: Re: [PATCH] Add Realtek 8187B support

Em Wednesday 09 April 2008 01:22:03 Pavel Roskin escreveu:
> Hello!
>
> On Tue, 2008-04-08 at 19:31 -0300, Herton Ronaldo Krzesinski wrote:
> > Hi, this patch (made against wireless-testing repository) adds support for
> > 8187B to the rtl8187 module. It is based on code made by Realtek in their
> > open source driver, plus contains code by initial patch made by John W.
> > Linville and feedback/fixes to his initial patch by Pavel Roskin (thus I'm
> > adding them to Signed-offs).
>
> I appreciate your efforts, but I have to point out some mistakes.
>
> I have never tested your patch. Therefore, you cannot just put my name
> on it. Also, Signed-off-by has a certain legal meaning, and should be
> generally used only by authors of the code and those forwarding it:
> http://kerneltrap.org/taxonomy/term/245

Hi, I thought Signed-off was more of a credit assigment, and as you had placed
some code in response to John's patch I included you (I used the same fixes),
no problem, I'll read the above link and request that people place their
Signed-offs if they want in next patch (and use my signed-off only).

>
> Patches should start with the driver name followed by the semicolon. At
> this stage you should probably be posting an RFT (request for testing).
> http://linuxwireless.org/en/developers/Documentation/SubmittingPatches

Will fix in next submit.

>
> The patch produces a warning on x64_64:
>
> /home/proski/src/linux-2.6/drivers/net/wireless/rtl8187_dev.c: In
> function 'rtl8187b_init_hw':
> /home/proski/src/linux-2.6/drivers/net/wireless/rtl8187_dev.c:586:
> warning: cast to pointer from integer of different size
>
> I see you introduce function rtl818x_ioread8_idx(), which takes pointer
> to u8 as the second argument, but you cast u16 values to (u8*) in so
> many places, that I wonder if that function should take u16 instead.

In fact I didn't introduce the function, I just extended it with one more
argument as was needed by rtl8187b setup (that needs an index to be passed
to usb_control_msg != 0). The function was already before using u8*. I agree
that the casts aren't needed, for now I just followed the style that was on the
driver (iowrite functions could well use just u16 instead of u8*, __le16*,
__le32*).

Before my patch there are the same type casts:
$ grep '(u8 \*)' rtl*
rtl8187_dev.c: rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10);
rtl8187_dev.c: rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11);
rtl8187_dev.c: rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00);
rtl8187_dev.c: reg = rtl818x_ioread8(priv, (u8 *)0xFE53);
rtl8187_dev.c: rtl818x_iowrite8(priv, (u8 *)0xFE53, reg | (1 << 7));
rtl8187_dev.c: rtl818x_iowrite8(priv, (u8 *)0xFFFF, 0x60);
rtl8187_dev.c: ((u8 *)conf->mac_addr)[i]);
rtl8187_dev.c: priv->asic_rev = rtl818x_ioread8(priv, (u8 *)0xFFFE) & 0x3;
rtl8187_rtl8225.c: rtl818x_iowrite8(priv, (u8 *)0xFF5B, 0x0D); msleep(1);

The warning on x86_64 seems related to sign extension, I found this reference:
http://gcc.gnu.org/ml/gcc-patches/2005-12/msg00942.html
I'll fix it making a double cast for now:
(u8 *)(uintptr_t)(rtl8187b_reg_table[i][0] | 0xFF00)
instead of
(u8 *)(rtl8187b_reg_table[i][0] | 0xFF00)

This would gone of course if we were just using u16, I can make a patch
following this, test and submit it.

>
> checkpatch.pl complains about "line over 80 characters" in many places.
> I know that many developers don't like this limitation, but it's trivial
> to fix in your case. Thus, checkpatch.pl is only showing that you
> didn't run it :-)

I run checkpatch.pl, I just didn't fixed these because they were lines already
this way, my patch got these because I moved reset code common to rtl8187b
and rtl8187 (new rtl8187_cmd_reset function) out of rtl8187_init_hw. Also I
introduced some new ones, these lines are over 80 characters:
rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);

Should I fix all of these ocurrences in rtl8187b patch or in a new patch?

>
> Then there are 2 sparse warnings introduced by your patch:
>
> /home/proski/src/linux-2.6/drivers/net/wireless/rtl8187_rtl8225.c:575:6:
> warning: symbol 'rtl8225z2_b_rf_set_tx_power' was not declared. Should
> it be static?
> /home/proski/src/linux-2.6/drivers/net/wireless/rtl8187_rtl8225.c:827:6:
> warning: symbol 'rtl8225z2_b_rf_init' was not declared. Should it be
> static?

Will fix making functions static.

>
> Finally, I tried your patch on my hardware (Trendnet TEW-424UB, USB ID
> 0bda:8189). The driver loads and works:
>
> phy2: Selected rate control algorithm 'pid'
> phy2: hwaddr 00:14:d1:45:a9:0b, RTL8187BvE V0 + rtl8225z2
> usbcore: registered new interface driver rtl8187
>
> But bringing the interface up is very slow - it takes whole 28 seconds:
>
> # time ifconfig wlan1 up
>
> real 0m28.354s
> user 0m0.000s
> sys 0m0.140s

Wow, 28 seconds is too much... indeed it's slow, but should be not as much as 28
seconds, here I get 11/12 seconds. The delay is huge as there are many sleeps
in Realtek initialization.

>
> And the connection is rather bad. I'm connecting to an AP in the next
> room (one wall and 5 meters distance at most). It starts rather fine at
> 1Mbps, but then it goes quickly to 54Mbsp, and I get 41% packet loss.
> The rate never goes down. The rate control algorithm is pid.
>

I forgot to say about this when I submitted the patch, indeed rate control
doesn't work for 8187b, for 8187 it never worked too. If you set rate manually
to something lower (I found that for 2-3 meters from AP 11M seems to be the
best setting) it works much better (there aren't stalls/packet drops). I'll try to
take a look on this problem and see if I can enhance startup time and add rate
control, unfortunately in these two areas there isn't much that I can do as I
did this work only basing on opensource realtek driver and I don't have any
documentation for the hardware. The same realtek driver from where I based the
work also has the two problems (no rate control, long startup time).

--
[]'s
Herton

2008-04-09 15:47:00

by Larry Finger

[permalink] [raw]
Subject: Re: [PATCH] Add Realtek 8187B support

Herton Ronaldo Krzesinski wrote:
> Hi, this patch (made against wireless-testing repository) adds support for
> 8187B to the rtl8187 module.

Thanks for your efforts. If this driver had been available two months
ago, it would have made my life much simpler.

Don't get too discouraged about the negative comments - that kind of
diligence is what makes Linux so robust, and keeps this open-source
project from chaos.

Once your patches get more stabilized, I will be submitting one to
handle my hardware, which is a LevelOne WNC-0301USB. For some reason,
it has an idProduct code of 0x8187, not 0x8189, and one has to look at
the iProduct value. For early testing, I am just forcing it to be an
8187B. For my information, what is the iProduct value for your device?

I did only minimal testing. The USB device was only about 1 meter from
the AP. It connected with a WPA-encrypted network in about the same
time as my BCM43xx devices do.

I do have one minor comment - I prefer to see signal and noise values
in dBm, not as xx/65.

Good job,

Larry

2008-04-10 03:40:06

by Larry Finger

[permalink] [raw]
Subject: Re: [PATCH] Add Realtek 8187B support

Herton Ronaldo Krzesinski wrote:
> Hi, this patch (made against wireless-testing repository) adds support for
> 8187B to the rtl8187 module.

I want to give a "heads up" to the users of this driver. This
afternoon, I was using flood pings on my RTL8187B at different
transmission rates to determine how well the device worked. In the
middle of the 3rd test, the device failed, and no longer has the
ability to transmit. I have no way of knowing if that was just the
time that it failed, or if the driver is overpowering the transmitter.
In any case, you should watch for problems.

Larry

2008-04-09 04:22:08

by Pavel Roskin

[permalink] [raw]
Subject: Re: [PATCH] Add Realtek 8187B support

Hello!

On Tue, 2008-04-08 at 19:31 -0300, Herton Ronaldo Krzesinski wrote:
> Hi, this patch (made against wireless-testing repository) adds support for
> 8187B to the rtl8187 module. It is based on code made by Realtek in their
> open source driver, plus contains code by initial patch made by John W.
> Linville and feedback/fixes to his initial patch by Pavel Roskin (thus I'm
> adding them to Signed-offs).

I appreciate your efforts, but I have to point out some mistakes.

I have never tested your patch. Therefore, you cannot just put my name
on it. Also, Signed-off-by has a certain legal meaning, and should be
generally used only by authors of the code and those forwarding it:
http://kerneltrap.org/taxonomy/term/245

Patches should start with the driver name followed by the semicolon. At
this stage you should probably be posting an RFT (request for testing).
http://linuxwireless.org/en/developers/Documentation/SubmittingPatches

The patch produces a warning on x64_64:

/home/proski/src/linux-2.6/drivers/net/wireless/rtl8187_dev.c: In
function 'rtl8187b_init_hw':
/home/proski/src/linux-2.6/drivers/net/wireless/rtl8187_dev.c:586:
warning: cast to pointer from integer of different size

I see you introduce function rtl818x_ioread8_idx(), which takes pointer
to u8 as the second argument, but you cast u16 values to (u8*) in so
many places, that I wonder if that function should take u16 instead.

checkpatch.pl complains about "line over 80 characters" in many places.
I know that many developers don't like this limitation, but it's trivial
to fix in your case. Thus, checkpatch.pl is only showing that you
didn't run it :-)

Then there are 2 sparse warnings introduced by your patch:

/home/proski/src/linux-2.6/drivers/net/wireless/rtl8187_rtl8225.c:575:6:
warning: symbol 'rtl8225z2_b_rf_set_tx_power' was not declared. Should
it be static?
/home/proski/src/linux-2.6/drivers/net/wireless/rtl8187_rtl8225.c:827:6:
warning: symbol 'rtl8225z2_b_rf_init' was not declared. Should it be
static?

Finally, I tried your patch on my hardware (Trendnet TEW-424UB, USB ID
0bda:8189). The driver loads and works:

phy2: Selected rate control algorithm 'pid'
phy2: hwaddr 00:14:d1:45:a9:0b, RTL8187BvE V0 + rtl8225z2
usbcore: registered new interface driver rtl8187

But bringing the interface up is very slow - it takes whole 28 seconds:

# time ifconfig wlan1 up

real 0m28.354s
user 0m0.000s
sys 0m0.140s

And the connection is rather bad. I'm connecting to an AP in the next
room (one wall and 5 meters distance at most). It starts rather fine at
1Mbps, but then it goes quickly to 54Mbsp, and I get 41% packet loss.
The rate never goes down. The rate control algorithm is pid.

--
Regards,
Pavel Roskin

2008-04-09 18:59:25

by Pavel Roskin

[permalink] [raw]
Subject: Re: [PATCH] Add Realtek 8187B support

On Wed, 2008-04-09 at 14:36 -0300, Herton Ronaldo Krzesinski wrote:

> I run checkpatch.pl, I just didn't fixed these because they were lines already
> this way, my patch got these because I moved reset code common to rtl8187b
> and rtl8187 (new rtl8187_cmd_reset function) out of rtl8187_init_hw. Also I
> introduced some new ones, these lines are over 80 characters:
> rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
> rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
>
> Should I fix all of these ocurrences in rtl8187b patch or in a new patch?

I feel bad that you have to deal with sloppy code left by others, but
these are the rules of the game. I suggest that you do all cleanups
first as separate patches, and then the user-visible changes. The only
exception is then your user visible changes replace the old code.

I could do some cleanups myself, but I think it would be easier for you
to do it at once rather than merge my patches.

Or maybe we should ask the existing maintainers to prepare a clean
playground for new contributors? Michael? Andrea?

--
Regards,
Pavel Roskin

2008-04-12 22:49:41

by Larry Finger

[permalink] [raw]
Subject: Re: [PATCH] Add Realtek 8187B support

Pavel Roskin wrote:
> On Fri, 2008-04-11 at 23:36 -0300, Herton Ronaldo Krzesinski wrote:
>
>> Yes, I'm too concerned about the fact that could be the patch that fried your
>> dongle.
>
> I think you understood Larry too literally. I assume the dongle is
> still working.

No, the transmitter failed. It could still receive, but nothing ever
was sent out. It happened while I was doing a ping -f with the rate
set at 1Mbs.

I have received an RMA and will be sending the dongle back for
replacement on Monday.

Larry



2008-04-12 17:29:07

by Pavel Roskin

[permalink] [raw]
Subject: Re: [PATCH] Add Realtek 8187B support

On Fri, 2008-04-11 at 23:36 -0300, Herton Ronaldo Krzesinski wrote:

> Yes, I'm too concerned about the fact that could be the patch that fried your
> dongle.

I think you understood Larry too literally. I assume the dongle is
still working.

--
Regards,
Pavel Roskin

Subject: Re: [PATCH] Add Realtek 8187B support

Em Wednesday 09 April 2008 15:07:10 Herton Ronaldo Krzesinski escreveu:
> > > +#define DEVICE_RTL8187 0
> > > +#define DEVICE_RTL8187B 1
> >
> > I prefer enums here, but I don't feel strongly about it.
> >
> > > struct rtl8187_priv {
> > > /* common between rtl818x drivers */
> > > struct rtl818x_csr *map;
> > > @@ -76,70 +103,100 @@ struct rtl8187_priv {
> > > u32 rx_conf;
> > > u16 txpwr_base;
> > > u8 asic_rev;
> > > + u8 is_rtl8187b;
> > > + enum {
> > > + RTL8187BvB,
> > > + RTL8187BvD,
> > > + RTL8187BvE
> > > + } hw_rev;
> >
> > Or maybe we could have one enum here, and use (hw_rev > DEVICE_RTL8187)
> > instead of is_rtl8187b. Maybe there will be future revisions that
> > change something else. The standard approaches are flags that are ORed
> > or enums that are compared. is_something doesn't scale well.
>
> Ok, I'll merge and compare using '>'

I started to look into this, but the conclusion is that there is no way to
merge this now, as there is no way to know if a device is 8187 or 8187b at
runtime that I know of (you must know if the device is 8187 or 8187b before
probing its variants, by the driver_info field).

> >
> > > - hdr = (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr));
> > > - hdr->flags = cpu_to_le32(flags);
> > > - hdr->len = 0;
> > > - hdr->rts_duration = rts_dur;
> > > - hdr->retry = cpu_to_le32(control->retry_limit << 8);
> > > + if (!priv->is_rtl8187b) {
> >
> > I would explore the possibility of splitting the hardware specific parts
> > of rtl8187_tx() into two separate functions.
>
> I'll split them.
>
> >
> > > @@ -240,11 +264,25 @@ static void rtl8187_rx_cb(struct urb *urb)
> >
> > And the same on the rx side. gcc is good at inlining static functions
> > that are used once.

I splitted tx function. Please take a look at the new patch below and see if
it's ok or seems better. For me there is not much difference in splitting it or
not, for rx would be worse, as I either would have to make functions return
a struct with fields or pass rx_status as parameter and would become strange,
as having to set some rx_status fields inside functions and others outside,
wouldn't this compromise some of the readability of the code? (for now I just
didn't split rx)

Here it's updated patch with most of previous issues pointed fixed, just for
review (missing are what I told before plus the pointer casts cleanups etc.
that I don't know how we will handle). Only needs cleanup in tx code, I kept
commented old code just to compare the before/after split.

Signed-off-by: Herton Ronaldo Krzesinski <[email protected]>
diff --git a/drivers/net/wireless/rtl8187.h b/drivers/net/wireless/rtl8187.h
index 076d88b..18d1c8e 100644
--- a/drivers/net/wireless/rtl8187.h
+++ b/drivers/net/wireless/rtl8187.h
@@ -44,23 +44,50 @@ struct rtl8187_rx_hdr {
__le64 mac_time;
} __attribute__((packed));

+struct rtl8187b_rx_hdr {
+ __le32 flags;
+ __le64 mac_time;
+ u8 noise;
+ u8 signal;
+ u8 agc;
+ u8 reserved;
+ __le32 unused;
+} __attribute__((packed));
+
struct rtl8187_tx_info {
struct ieee80211_tx_control *control;
struct urb *urb;
struct ieee80211_hw *dev;
};

-struct rtl8187_tx_hdr {
- __le32 flags;
+/* Tx flags are common between rtl8187 and rtl8187b */
#define RTL8187_TX_FLAG_NO_ENCRYPT (1 << 15)
#define RTL8187_TX_FLAG_MORE_FRAG (1 << 17)
#define RTL8187_TX_FLAG_CTS (1 << 18)
#define RTL8187_TX_FLAG_RTS (1 << 23)
+
+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));
+
+#define DEVICE_RTL8187 0
+#define DEVICE_RTL8187B 1
+
struct rtl8187_priv {
/* common between rtl818x drivers */
struct rtl818x_csr *map;
@@ -76,70 +103,117 @@ struct rtl8187_priv {
u32 rx_conf;
u16 txpwr_base;
u8 asic_rev;
+ u8 is_rtl8187b;
+ enum {
+ RTL8187BvB,
+ RTL8187BvD,
+ RTL8187BvE
+ } hw_rev;
struct sk_buff_head rx_queue;
};

void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data);

-static inline u8 rtl818x_ioread8(struct rtl8187_priv *priv, u8 *addr)
+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, 0, &val, sizeof(val), HZ / 2);
+ (unsigned long)addr, idx & 0x03, &val,
+ sizeof(val), HZ / 2);

return val;
}

-static inline u16 rtl818x_ioread16(struct rtl8187_priv *priv, __le16 *addr)
+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, 0, &val, sizeof(val), HZ / 2);
+ (unsigned long)addr, idx & 0x03, &val,
+ sizeof(val), HZ / 2);

return le16_to_cpu(val);
}

-static inline u32 rtl818x_ioread32(struct rtl8187_priv *priv, __le32 *addr)
+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, 0, &val, sizeof(val), HZ / 2);
+ (unsigned long)addr, idx & 0x03, &val,
+ sizeof(val), HZ / 2);

return le32_to_cpu(val);
}

-static inline void rtl818x_iowrite8(struct rtl8187_priv *priv,
- u8 *addr, u8 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, 0, &val, sizeof(val), HZ / 2);
+ (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(struct rtl8187_priv *priv,
- __le16 *addr, u16 val)
+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, 0, &buf, sizeof(buf), HZ / 2);
+ (unsigned long)addr, idx & 0x03, &buf, sizeof(buf),
+ HZ / 2);
}

-static inline void rtl818x_iowrite32(struct rtl8187_priv *priv,
- __le32 *addr, u32 val)
+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, 0, &buf, sizeof(buf), HZ / 2);
+ (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 */
diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c
index d5787b3..48e51a1 100644
--- a/drivers/net/wireless/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl8187_dev.c
@@ -27,19 +27,20 @@

MODULE_AUTHOR("Michael Wu <[email protected]>");
MODULE_AUTHOR("Andrea Merello <[email protected]>");
-MODULE_DESCRIPTION("RTL8187 USB wireless driver");
+MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver");
MODULE_LICENSE("GPL");

static struct usb_device_id rtl8187_table[] __devinitdata = {
/* Realtek */
- {USB_DEVICE(0x0bda, 0x8187)},
+ {USB_DEVICE(0x0bda, 0x8187), .driver_info = DEVICE_RTL8187},
+ {USB_DEVICE(0x0bda, 0x8189), .driver_info = DEVICE_RTL8187B},
/* Netgear */
- {USB_DEVICE(0x0846, 0x6100)},
- {USB_DEVICE(0x0846, 0x6a00)},
+ {USB_DEVICE(0x0846, 0x6100), .driver_info = DEVICE_RTL8187},
+ {USB_DEVICE(0x0846, 0x6a00), .driver_info = DEVICE_RTL8187},
/* HP */
- {USB_DEVICE(0x03f0, 0xca02)},
+ {USB_DEVICE(0x03f0, 0xca02), .driver_info = DEVICE_RTL8187},
/* Sitecom */
- {USB_DEVICE(0x0df6, 0x000d)},
+ {USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187},
{}
};

@@ -148,6 +149,8 @@ static void rtl8187_tx_cb(struct urb *urb)
struct ieee80211_tx_status status;
struct sk_buff *skb = (struct sk_buff *)urb->context;
struct rtl8187_tx_info *info = (struct rtl8187_tx_info *)skb->cb;
+ struct ieee80211_hw *dev = info->dev;
+ struct rtl8187_priv *priv = dev->priv;

memset(&status, 0, sizeof(status));

@@ -155,20 +158,54 @@ static void rtl8187_tx_cb(struct urb *urb)
if (info->control)
memcpy(&status.control, info->control, sizeof(status.control));
kfree(info->control);
- skb_pull(skb, sizeof(struct rtl8187_tx_hdr));
+ skb_pull(skb, priv->is_rtl8187b ? sizeof(struct rtl8187b_tx_hdr) :
+ sizeof(struct rtl8187_tx_hdr));
status.flags |= IEEE80211_TX_STATUS_ACK;
ieee80211_tx_status_irqsafe(info->dev, skb, &status);
}

+static void *rtl8187_tx_buf(struct sk_buff *skb, __le32 flags, __le16 rts_dur,
+ __le32 retry)
+{
+ struct rtl8187_tx_hdr *hdr;
+
+ hdr = (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr));
+ hdr->flags = flags;
+ hdr->len = 0;
+ hdr->rts_duration = rts_dur;
+ hdr->retry = retry;
+
+ return hdr;
+}
+
+static void *rtl8187b_tx_buf(struct sk_buff *skb, __le32 flags,
+ __le16 rts_dur, __le32 retry,
+ __le16 tx_dur)
+{
+ struct rtl8187b_tx_hdr *hdr;
+
+ hdr = (struct rtl8187b_tx_hdr *)skb_push(skb, sizeof(*hdr));
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->flags = flags;
+ hdr->rts_duration = rts_dur;
+ hdr->retry = retry;
+ hdr->tx_duration = tx_dur;
+
+ return hdr;
+}
+
static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb,
struct ieee80211_tx_control *control)
{
struct rtl8187_priv *priv = dev->priv;
- struct rtl8187_tx_hdr *hdr;
+ /*struct rtl8187_tx_hdr *hdr = NULL;
+ struct rtl8187b_tx_hdr *hdr_b = NULL;*/
+ void *buf;
struct rtl8187_tx_info *info;
struct urb *urb;
__le16 rts_dur = 0;
u32 flags;
+ unsigned int ep[4] = { 6, 7, 5, 4 };

urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
@@ -196,18 +233,46 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb,
flags |= control->rts_cts_rate->hw_value << 19;
}

- hdr = (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr));
- hdr->flags = cpu_to_le32(flags);
- hdr->len = 0;
- hdr->rts_duration = rts_dur;
- hdr->retry = cpu_to_le32(control->retry_limit << 8);
+ if (!priv->is_rtl8187b)
+ buf = rtl8187_tx_buf(skb, cpu_to_le32(flags), rts_dur,
+ cpu_to_le32(control->retry_limit << 8));
+ else
+ buf = rtl8187b_tx_buf(skb, cpu_to_le32(flags), rts_dur,
+ cpu_to_le32(control->retry_limit << 8),
+ ieee80211_generic_frame_duration(
+ dev,
+ priv->vif,
+ skb->len,
+ control->tx_rate));
+
+ /*if (!priv->is_rtl8187b) {
+ hdr = (struct rtl8187_tx_hdr *)skb_push(skb, sizeof(*hdr));
+ hdr->flags = cpu_to_le32(flags);
+ hdr->len = 0;
+ hdr->rts_duration = rts_dur;
+ hdr->retry = cpu_to_le32(control->retry_limit << 8);
+ } else {
+ hdr_b = (struct rtl8187b_tx_hdr *)skb_push(skb, sizeof(*hdr_b));
+ memset(hdr_b, 0, sizeof(*hdr_b));
+ hdr_b->flags = cpu_to_le32(flags);
+ hdr_b->rts_duration = rts_dur;
+ hdr_b->retry = cpu_to_le32(control->retry_limit << 8);
+ hdr_b->tx_duration = ieee80211_generic_frame_duration(
+ dev,
+ priv->vif,
+ skb->len,
+ control->tx_rate);
+ }*/

info = (struct rtl8187_tx_info *)skb->cb;
info->control = kmemdup(control, sizeof(*control), GFP_ATOMIC);
info->urb = urb;
info->dev = dev;
- usb_fill_bulk_urb(urb, priv->udev, usb_sndbulkpipe(priv->udev, 2),
- hdr, skb->len, rtl8187_tx_cb, skb);
+ usb_fill_bulk_urb(urb, priv->udev,
+ usb_sndbulkpipe(priv->udev,
+ priv->is_rtl8187b ? ep[skb->priority] : 2),
+ /*priv->is_rtl8187b ? (void *)hdr_b : (void *)hdr,*/
+ buf, skb->len, rtl8187_tx_cb, skb);
usb_submit_urb(urb, GFP_ATOMIC);

return 0;
@@ -220,6 +285,7 @@ static void rtl8187_rx_cb(struct urb *urb)
struct ieee80211_hw *dev = info->dev;
struct rtl8187_priv *priv = dev->priv;
struct rtl8187_rx_hdr *hdr;
+ struct rtl8187b_rx_hdr *hdr_b;
struct ieee80211_rx_status rx_status = { 0 };
int rate, signal;
u32 flags;
@@ -240,11 +306,25 @@ static void rtl8187_rx_cb(struct urb *urb)
}

skb_put(skb, urb->actual_length);
- hdr = (struct rtl8187_rx_hdr *)(skb_tail_pointer(skb) - sizeof(*hdr));
- flags = le32_to_cpu(hdr->flags);
- skb_trim(skb, flags & 0x0FFF);
+ if (!priv->is_rtl8187b) {
+ hdr = (struct rtl8187_rx_hdr *)
+ (skb_tail_pointer(skb) - sizeof(*hdr));
+ flags = le32_to_cpu(hdr->flags);
+ signal = hdr->agc >> 1;
+ rx_status.antenna = (hdr->signal >> 7) & 1;
+ rx_status.signal = 64 - min(hdr->noise, (u8)64);
+ rx_status.mactime = le64_to_cpu(hdr->mac_time);
+ } else {
+ hdr_b = (struct rtl8187b_rx_hdr *)
+ (skb_tail_pointer(skb) - sizeof(*hdr_b));
+ flags = le32_to_cpu(hdr_b->flags);
+ signal = hdr_b->agc >> 1;
+ rx_status.antenna = (hdr_b->signal >> 7) & 1;
+ rx_status.signal = 64 - min(hdr_b->noise, (u8)64);
+ rx_status.mactime = le64_to_cpu(hdr_b->mac_time);
+ }

- signal = hdr->agc >> 1;
+ skb_trim(skb, flags & 0x0FFF);
rate = (flags >> 20) & 0xF;
if (rate > 3) { /* OFDM rate */
if (signal > 90)
@@ -260,13 +340,10 @@ static void rtl8187_rx_cb(struct urb *urb)
signal = 95 - signal;
}

- rx_status.antenna = (hdr->signal >> 7) & 1;
- rx_status.signal = 64 - min(hdr->noise, (u8)64);
rx_status.ssi = signal;
rx_status.rate_idx = rate;
rx_status.freq = dev->conf.channel->center_freq;
rx_status.band = dev->conf.channel->band;
- rx_status.mactime = le64_to_cpu(hdr->mac_time);
rx_status.flag |= RX_FLAG_TSFT;
if (flags & (1 << 13))
rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
@@ -306,7 +383,8 @@ static int rtl8187_init_urbs(struct ieee80211_hw *dev)
break;
}
usb_fill_bulk_urb(entry, priv->udev,
- usb_rcvbulkpipe(priv->udev, 1),
+ usb_rcvbulkpipe(priv->udev,
+ priv->is_rtl8187b ? 3 : 1),
skb_tail_pointer(skb),
RTL8187_MAX_RX, rtl8187_rx_cb, skb);
info = (struct rtl8187_rx_info *)skb->cb;
@@ -319,29 +397,12 @@ static int rtl8187_init_urbs(struct ieee80211_hw *dev)
return 0;
}

-static int rtl8187_init_hw(struct ieee80211_hw *dev)
+static int rtl8187_cmd_reset(struct ieee80211_hw *dev)
{
struct rtl8187_priv *priv = dev->priv;
u8 reg;
int i;

- /* reset */
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
- reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_ON);
- rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON);
- rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
- rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
-
- rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
-
- msleep(200);
- rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10);
- rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11);
- rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00);
- msleep(200);
-
reg = rtl818x_ioread8(priv, &priv->map->CMD);
reg &= (1 << 1);
reg |= RTL818X_CMD_RESET;
@@ -377,6 +438,36 @@ static int rtl8187_init_hw(struct ieee80211_hw *dev)
return -ETIMEDOUT;
}

+ return 0;
+}
+
+static int rtl8187_init_hw(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u8 reg;
+ int res;
+
+ /* reset */
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_ON);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0);
+
+ msleep(200);
+ rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x10);
+ rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x11);
+ rtl818x_iowrite8(priv, (u8 *)0xFE18, 0x00);
+ msleep(200);
+
+ res = rtl8187_cmd_reset(dev);
+ if (res)
+ return res;
+
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
@@ -446,16 +537,192 @@ static int rtl8187_init_hw(struct ieee80211_hw *dev)
return 0;
}

+static const u8 rtl8187b_reg_table[][3] = {
+ {0xF0, 0x32, 0}, {0xF1, 0x32, 0}, {0xF2, 0x00, 0}, {0xF3, 0x00, 0},
+ {0xF4, 0x32, 0}, {0xF5, 0x43, 0}, {0xF6, 0x00, 0}, {0xF7, 0x00, 0},
+ {0xF8, 0x46, 0}, {0xF9, 0xA4, 0}, {0xFA, 0x00, 0}, {0xFB, 0x00, 0},
+ {0xFC, 0x96, 0}, {0xFD, 0xA4, 0}, {0xFE, 0x00, 0}, {0xFF, 0x00, 0},
+
+ {0x58, 0x4B, 1}, {0x59, 0x00, 1}, {0x5A, 0x4B, 1}, {0x5B, 0x00, 1},
+ {0x60, 0x4B, 1}, {0x61, 0x09, 1}, {0x62, 0x4B, 1}, {0x63, 0x09, 1},
+ {0xCE, 0x0F, 1}, {0xCF, 0x00, 1}, {0xE0, 0xFF, 1}, {0xE1, 0x0F, 1},
+ {0xE2, 0x00, 1}, {0xF0, 0x4E, 1}, {0xF1, 0x01, 1}, {0xF2, 0x02, 1},
+ {0xF3, 0x03, 1}, {0xF4, 0x04, 1}, {0xF5, 0x05, 1}, {0xF6, 0x06, 1},
+ {0xF7, 0x07, 1}, {0xF8, 0x08, 1},
+
+ {0x4E, 0x00, 2}, {0x0C, 0x04, 2}, {0x21, 0x61, 2}, {0x22, 0x68, 2},
+ {0x23, 0x6F, 2}, {0x24, 0x76, 2}, {0x25, 0x7D, 2}, {0x26, 0x84, 2},
+ {0x27, 0x8D, 2}, {0x4D, 0x08, 2}, {0x50, 0x05, 2}, {0x51, 0xF5, 2},
+ {0x52, 0x04, 2}, {0x53, 0xA0, 2}, {0x54, 0x1F, 2}, {0x55, 0x23, 2},
+ {0x56, 0x45, 2}, {0x57, 0x67, 2}, {0x58, 0x08, 2}, {0x59, 0x08, 2},
+ {0x5A, 0x08, 2}, {0x5B, 0x08, 2}, {0x60, 0x08, 2}, {0x61, 0x08, 2},
+ {0x62, 0x08, 2}, {0x63, 0x08, 2}, {0x64, 0xCF, 2}, {0x72, 0x56, 2},
+ {0x73, 0x9A, 2},
+
+ {0x34, 0xF0, 0}, {0x35, 0x0F, 0}, {0x5B, 0x40, 0}, {0x84, 0x88, 0},
+ {0x85, 0x24, 0}, {0x88, 0x54, 0}, {0x8B, 0xB8, 0}, {0x8C, 0x07, 0},
+ {0x8D, 0x00, 0}, {0x94, 0x1B, 0}, {0x95, 0x12, 0}, {0x96, 0x00, 0},
+ {0x97, 0x06, 0}, {0x9D, 0x1A, 0}, {0x9F, 0x10, 0}, {0xB4, 0x22, 0},
+ {0xBE, 0x80, 0}, {0xDB, 0x00, 0}, {0xEE, 0x00, 0}, {0x91, 0x03, 0},
+
+ {0x4C, 0x00, 2}, {0x9F, 0x00, 3}, {0x8C, 0x01, 0}, {0x8D, 0x10, 0},
+ {0x8E, 0x08, 0}, {0x8F, 0x00, 0}
+};
+
+static int rtl8187b_init_hw(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ int res, i;
+ u8 reg;
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ reg |= RTL818X_CONFIG3_ANAPARAM_WRITE | RTL818X_CONFIG3_GNT_SELECT;
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, 0x727f3f52);
+ rtl818x_iowrite32(priv, &priv->map->ANAPARAM, 0x45090658);
+ rtl818x_iowrite8(priv, &priv->map->ANAPARAM3, 0);
+
+ rtl818x_iowrite8(priv, (u8 *)0xFF61, 0x10);
+ reg = rtl818x_ioread8(priv, (u8 *)0xFF62);
+ rtl818x_iowrite8(priv, (u8 *)0xFF62, reg & ~(1 << 5));
+ rtl818x_iowrite8(priv, (u8 *)0xFF62, reg | (1 << 5));
+
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ reg &= ~RTL818X_CONFIG3_ANAPARAM_WRITE;
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ res = rtl8187_cmd_reset(dev);
+ if (res)
+ return res;
+
+ rtl818x_iowrite16(priv, (__le16 *)0xFF2D, 0x0FFF);
+ reg = rtl818x_ioread8(priv, &priv->map->CW_CONF);
+ reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT;
+ rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg);
+ reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL);
+ reg |= RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT |
+ RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT;
+ 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);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFFD4, 0xFFFF, 1);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG1);
+ rtl818x_iowrite8(priv, &priv->map->CONFIG1, (reg & 0x3F) | 0x80);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0);
+ for (i = 0; i < ARRAY_SIZE(rtl8187b_reg_table); i++) {
+ rtl818x_iowrite8_idx(priv,
+ (u8 *)(uintptr_t)
+ (rtl8187b_reg_table[i][0] | 0xFF00),
+ rtl8187b_reg_table[i][1],
+ rtl8187b_reg_table[i][2]);
+ }
+
+ rtl818x_iowrite16(priv, &priv->map->TID_AC_MAP, 0xFA50);
+ rtl818x_iowrite16(priv, &priv->map->INT_MIG, 0);
+
+ rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF0, 0, 1);
+ rtl818x_iowrite32_idx(priv, (__le32 *)0xFFF4, 0, 1);
+ rtl818x_iowrite8_idx(priv, (u8 *)0xFFF8, 0, 1);
+
+ rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00004001);
+
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x569A, 2);
+
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
+ reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
+ reg |= RTL818X_CONFIG3_ANAPARAM_WRITE;
+ rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg);
+ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+
+ rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x2488);
+ rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
+ msleep(1100);
+
+ priv->rf->init(dev);
+
+ reg = RTL818X_CMD_TX_ENABLE | RTL818X_CMD_RX_ENABLE;
+ rtl818x_iowrite8(priv, &priv->map->CMD, reg);
+ rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF);
+
+ rtl818x_iowrite8(priv, (u8 *)0xFE41, 0xF4);
+ rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x00);
+ rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00);
+ rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01);
+ rtl818x_iowrite8(priv, (u8 *)0xFE40, 0x0F);
+ rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x00);
+ rtl818x_iowrite8(priv, (u8 *)0xFE42, 0x01);
+
+ reg = rtl818x_ioread8(priv, (u8 *)0xFFDB);
+ rtl818x_iowrite8(priv, (u8 *)0xFFDB, reg | (1 << 2));
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF72, 0x59FA, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF74, 0x59D2, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF76, 0x59D2, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF78, 0x19FA, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7A, 0x19FA, 3);
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFF7C, 0x00D0, 3);
+ rtl818x_iowrite8(priv, (u8 *)0xFF61, 0);
+ rtl818x_iowrite8_idx(priv, (u8 *)0xFF80, 0x0F, 1);
+ rtl818x_iowrite8_idx(priv, (u8 *)0xFF83, 0x03, 1);
+ rtl818x_iowrite8(priv, (u8 *)0xFFDA, 0x10);
+ rtl818x_iowrite8_idx(priv, (u8 *)0xFF4D, 0x08, 2);
+
+ rtl818x_iowrite32(priv, &priv->map->HSSI_PARA, 0x0600321B);
+
+ rtl818x_iowrite16_idx(priv, (__le16 *)0xFFEC, 0x0800, 1);
+
+ return 0;
+}
+
static int rtl8187_start(struct ieee80211_hw *dev)
{
struct rtl8187_priv *priv = dev->priv;
u32 reg;
int ret;

- ret = rtl8187_init_hw(dev);
+ ret = (!priv->is_rtl8187b) ? rtl8187_init_hw(dev) :
+ rtl8187b_init_hw(dev);
if (ret)
return ret;

+ if (priv->is_rtl8187b) {
+ reg = RTL818X_RX_CONF_MGMT |
+ RTL818X_RX_CONF_DATA |
+ RTL818X_RX_CONF_BROADCAST |
+ RTL818X_RX_CONF_NICMAC |
+ RTL818X_RX_CONF_BSSID |
+ (7 << 13 /* RX FIFO threshold NONE */) |
+ (7 << 10 /* MAX RX DMA */) |
+ RTL818X_RX_CONF_RX_AUTORESETPHY |
+ RTL818X_RX_CONF_ONLYERLPKT |
+ RTL818X_RX_CONF_MULTICAST;
+ priv->rx_conf = reg;
+ rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg);
+
+ rtl818x_iowrite32(priv, &priv->map->TX_CONF,
+ RTL818X_TX_CONF_HW_SEQNUM |
+ RTL818X_TX_CONF_DISREQQSIZE |
+ (7 << 8 /* short retry limit */) |
+ (7 << 0 /* long retry limit */) |
+ (7 << 21 /* MAX TX DMA */));
+ rtl8187_init_urbs(dev);
+ return 0;
+ }
+
rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF);

rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0);
@@ -582,18 +849,20 @@ static int rtl8187_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf)
msleep(10);
rtl818x_iowrite32(priv, &priv->map->TX_CONF, reg);

- rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
-
- if (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) {
- 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);
+ if (!priv->is_rtl8187b) {
+ rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
+
+ if (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) {
+ 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);
+ }
}

rtl818x_iowrite16(priv, &priv->map->ATIM_WND, 2);
@@ -609,14 +878,20 @@ static int rtl8187_config_interface(struct ieee80211_hw *dev,
{
struct rtl8187_priv *priv = dev->priv;
int i;
+ u8 reg;

for (i = 0; i < ETH_ALEN; i++)
rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]);

- if (is_valid_ether_addr(conf->bssid))
- rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_INFRA);
- else
- rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_NO_LINK);
+ if (is_valid_ether_addr(conf->bssid)) {
+ reg = RTL818X_MSR_INFRA;
+ if (priv->is_rtl8187b)
+ reg |= RTL818X_MSR_ENEDCA;
+ rtl818x_iowrite8(priv, &priv->map->MSR, reg);
+ } else {
+ reg = RTL818X_MSR_NO_LINK;
+ rtl818x_iowrite8(priv, &priv->map->MSR, reg);
+ }

return 0;
}
@@ -703,6 +978,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
struct rtl8187_priv *priv;
struct eeprom_93cx6 eeprom;
struct ieee80211_channel *channel;
+ const char *chip_name;
u16 txpwr, reg;
int err, i;
DECLARE_MAC_BUF(mac);
@@ -714,6 +990,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
}

priv = dev->priv;
+ priv->is_rtl8187b = (id->driver_info == DEVICE_RTL8187B);

SET_IEEE80211_DEV(dev, &intf->dev);
usb_set_intfdata(intf, dev);
@@ -741,7 +1018,9 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
priv->mode = IEEE80211_IF_TYPE_MNTR;
dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
IEEE80211_HW_RX_INCLUDES_FCS;
- dev->extra_tx_headroom = sizeof(struct rtl8187_tx_hdr);
+ dev->extra_tx_headroom = (!priv->is_rtl8187b) ?
+ sizeof(struct rtl8187_tx_hdr) :
+ sizeof(struct rtl8187b_tx_hdr);
dev->queues = 1;
dev->max_rssi = 65;
dev->max_signal = 64;
@@ -778,10 +1057,24 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
(*channel++).hw_value = txpwr & 0xFF;
(*channel++).hw_value = txpwr >> 8;
}
- for (i = 0; i < 2; i++) {
- eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_6 + i,
+ if (!priv->is_rtl8187b) {
+ for (i = 0; i < 2; i++) {
+ eeprom_93cx6_read(&eeprom,
+ RTL8187_EEPROM_TXPWR_CHAN_6 + i,
+ &txpwr);
+ (*channel++).hw_value = txpwr & 0xFF;
+ (*channel++).hw_value = txpwr >> 8;
+ }
+ } else {
+ eeprom_93cx6_read(&eeprom, RTL8187_EEPROM_TXPWR_CHAN_6,
&txpwr);
(*channel++).hw_value = txpwr & 0xFF;
+
+ eeprom_93cx6_read(&eeprom, 0x0A, &txpwr);
+ (*channel++).hw_value = txpwr & 0xFF;
+
+ eeprom_93cx6_read(&eeprom, 0x1C, &txpwr);
+ (*channel++).hw_value = txpwr & 0xFF;
(*channel++).hw_value = txpwr >> 8;
}

@@ -797,6 +1090,38 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);

+ if (!priv->is_rtl8187b) {
+ u32 reg32;
+ reg32 = rtl818x_ioread32(priv, &priv->map->TX_CONF);
+ reg32 &= RTL818X_TX_CONF_HWVER_MASK;
+ switch (reg32) {
+ case RTL818X_TX_CONF_R8187vD_1:
+ case RTL818X_TX_CONF_R8187vD_2:
+ chip_name = "RTL8187vD";
+ break;
+ default:
+ chip_name = "RTL8187vB (default)";
+ }
+ } else {
+ switch (rtl818x_ioread8(priv, (u8 *)0xFFE1)) {
+ case RTL818X_R8187B_B:
+ chip_name = "RTL8187BvB";
+ priv->hw_rev = RTL8187BvB;
+ break;
+ case RTL818X_R8187B_D:
+ chip_name = "RTL8187BvD";
+ priv->hw_rev = RTL8187BvD;
+ break;
+ case RTL818X_R8187B_E:
+ chip_name = "RTL8187BvE";
+ priv->hw_rev = RTL8187BvE;
+ break;
+ default:
+ chip_name = "RTL8187BvB (default)";
+ priv->hw_rev = RTL8187BvB;
+ }
+ }
+
priv->rf = rtl8187_detect_rf(dev);

err = ieee80211_register_hw(dev);
@@ -805,9 +1130,9 @@ static int __devinit rtl8187_probe(struct usb_interface *intf,
goto err_free_dev;
}

- printk(KERN_INFO "%s: hwaddr %s, rtl8187 V%d + %s\n",
+ printk(KERN_INFO "%s: hwaddr %s, %s V%d + %s\n",
wiphy_name(dev->wiphy), print_mac(mac, dev->wiphy->perm_addr),
- priv->asic_rev, priv->rf->name);
+ chip_name, priv->asic_rev, priv->rf->name);

return 0;

diff --git a/drivers/net/wireless/rtl8187_rtl8225.c b/drivers/net/wireless/rtl8187_rtl8225.c
index 9146387..2a21ddb 100644
--- a/drivers/net/wireless/rtl8187_rtl8225.c
+++ b/drivers/net/wireless/rtl8187_rtl8225.c
@@ -471,12 +471,42 @@ static void rtl8225_rf_init(struct ieee80211_hw *dev)
rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[2]);
}

+static const u8 rtl8225z2_agc[] = {
+ 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51, 0x4f,
+ 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x39, 0x37,
+ 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f,
+ 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, 0x0d, 0x0b, 0x09, 0x07,
+ 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19,
+ 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28,
+ 0x28, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d,
+ 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
+ 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31
+};
+static const u8 rtl8225z2_ofdm[] = {
+ 0x10, 0x0d, 0x01, 0x00, 0x14, 0xfb, 0xfb, 0x60,
+ 0x00, 0x60, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00,
+ 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0xa8, 0x26,
+ 0x32, 0x33, 0x07, 0xa5, 0x6f, 0x55, 0xc8, 0xb3,
+ 0x0a, 0xe1, 0x2C, 0x8a, 0x86, 0x83, 0x34, 0x0f,
+ 0x4f, 0x24, 0x6f, 0xc2, 0x6b, 0x40, 0x80, 0x00,
+ 0xc0, 0xc1, 0x58, 0xf1, 0x00, 0xe4, 0x90, 0x3e,
+ 0x6d, 0x3c, 0xfb, 0x07
+};
+
static const u8 rtl8225z2_tx_power_cck_ch14[] = {
- 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00
+ 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00
};

static const u8 rtl8225z2_tx_power_cck[] = {
- 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04
+ 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04,
+ 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03,
+ 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03,
+ 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03
};

static const u8 rtl8225z2_tx_power_ofdm[] = {
@@ -542,6 +572,85 @@ static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
msleep(1);
}

+static void rtl8225z2_b_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ u8 cck_power, ofdm_power;
+ const u8 *tmp;
+ int i;
+
+ cck_power = priv->channels[channel - 1].hw_value & 0xF;
+ ofdm_power = priv->channels[channel - 1].hw_value >> 4;
+
+ if (cck_power > 15)
+ cck_power = (priv->hw_rev == RTL8187BvB) ? 15 : 22;
+ else
+ cck_power += (priv->hw_rev == RTL8187BvB) ? 0 : 7;
+ cck_power += priv->txpwr_base & 0xF;
+ cck_power = min(cck_power, (u8)35);
+
+ if (ofdm_power > 15)
+ ofdm_power = (priv->hw_rev == RTL8187BvB) ? 17 : 25;
+ else
+ ofdm_power += (priv->hw_rev == RTL8187BvB) ? 2 : 10;
+ ofdm_power += (priv->txpwr_base >> 4) & 0xF;
+ ofdm_power = min(ofdm_power, (u8)35);
+
+ if (channel == 14)
+ tmp = rtl8225z2_tx_power_cck_ch14;
+ else
+ tmp = rtl8225z2_tx_power_cck;
+
+ if (priv->hw_rev == RTL8187BvB) {
+ if (cck_power <= 6)
+ ; /* do nothing */
+ else if (cck_power <= 11)
+ tmp += 8;
+ else
+ tmp += 16;
+ } else {
+ if (cck_power <= 5)
+ ; /* do nothing */
+ else if (cck_power <= 11)
+ tmp += 8;
+ else if (cck_power <= 17)
+ tmp += 16;
+ else
+ tmp += 24;
+ }
+
+ for (i = 0; i < 8; i++)
+ rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
+ rtl8225z2_tx_gain_cck_ofdm[cck_power]);
+ msleep(1);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
+ rtl8225z2_tx_gain_cck_ofdm[ofdm_power] << 1);
+ if (priv->hw_rev == RTL8187BvB) {
+ if (ofdm_power <= 11) {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x60);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x60);
+ } else {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x5c);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x5c);
+ }
+ } else {
+ if (ofdm_power <= 11) {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x5c);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x5c);
+ } else if (ofdm_power <= 17) {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x54);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x54);
+ } else {
+ rtl8225_write_phy_ofdm(dev, 0x87, 0x50);
+ rtl8225_write_phy_ofdm(dev, 0x89, 0x50);
+ }
+ }
+ msleep(1);
+}
+
static const u16 rtl8225z2_rxgain[] = {
0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
@@ -715,6 +824,81 @@ static void rtl8225z2_rf_init(struct ieee80211_hw *dev)
rtl818x_iowrite32(priv, (__le32 *)0xFF94, 0x3dc00002);
}

+static void rtl8225z2_b_rf_init(struct ieee80211_hw *dev)
+{
+ struct rtl8187_priv *priv = dev->priv;
+ int i;
+
+ rtl8225_write(dev, 0x0, 0x0B7); msleep(1);
+ rtl8225_write(dev, 0x1, 0xEE0); msleep(1);
+ rtl8225_write(dev, 0x2, 0x44D); msleep(1);
+ rtl8225_write(dev, 0x3, 0x441); msleep(1);
+ rtl8225_write(dev, 0x4, 0x8C3); msleep(1);
+ rtl8225_write(dev, 0x5, 0xC72); msleep(1);
+ rtl8225_write(dev, 0x6, 0x0E6); msleep(1);
+ rtl8225_write(dev, 0x7, 0x82A); msleep(1);
+ rtl8225_write(dev, 0x8, 0x03F); msleep(1);
+ rtl8225_write(dev, 0x9, 0x335); msleep(1);
+ rtl8225_write(dev, 0xa, 0x9D4); msleep(1);
+ rtl8225_write(dev, 0xb, 0x7BB); msleep(1);
+ rtl8225_write(dev, 0xc, 0x850); msleep(1);
+ rtl8225_write(dev, 0xd, 0xCDF); msleep(1);
+ rtl8225_write(dev, 0xe, 0x02B); msleep(1);
+ rtl8225_write(dev, 0xf, 0x114); msleep(1);
+
+ rtl8225_write(dev, 0x0, 0x1B7); msleep(1);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) {
+ rtl8225_write(dev, 0x1, i + 1); msleep(1);
+ rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]); msleep(1);
+ }
+
+ rtl8225_write(dev, 0x3, 0x080); msleep(1);
+ rtl8225_write(dev, 0x5, 0x004); msleep(1);
+ rtl8225_write(dev, 0x0, 0x0B7); msleep(1);
+ msleep(3000);
+
+ rtl8225_write(dev, 0x2, 0xC4D); msleep(1);
+ msleep(2000);
+
+ rtl8225_write(dev, 0x2, 0x44D); msleep(1);
+ rtl8225_write(dev, 0x0, 0x2BF); msleep(1);
+
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, 0x03);
+ rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, 0x07);
+ rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03);
+
+ rtl8225_write_phy_ofdm(dev, 0x80, 0x12);
+ for (i = 0; i < ARRAY_SIZE(rtl8225z2_agc); i++) {
+ rtl8225_write_phy_ofdm(dev, 0xF, rtl8225z2_agc[i]);
+ rtl8225_write_phy_ofdm(dev, 0xE, 0x80 + i);
+ rtl8225_write_phy_ofdm(dev, 0xE, 0);
+ }
+ rtl8225_write_phy_ofdm(dev, 0x80, 0x10);
+
+ for (i = 0; i < ARRAY_SIZE(rtl8225z2_ofdm); i++)
+ rtl8225_write_phy_ofdm(dev, i, rtl8225z2_ofdm[i]);
+
+ rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
+ rtl818x_iowrite8(priv, &priv->map->SLOT, 9);
+ rtl818x_iowrite8(priv, (u8 *)0xFFF0, 28);
+ rtl818x_iowrite8(priv, (u8 *)0xFFF4, 28);
+ rtl818x_iowrite8(priv, (u8 *)0xFFF8, 28);
+ rtl818x_iowrite8(priv, (u8 *)0xFFFC, 28);
+ rtl818x_iowrite8(priv, (u8 *)0xFF2D, 0x5B);
+ rtl818x_iowrite8(priv, (u8 *)0xFF79, 0x5B);
+ 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); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0xa4, 0xb6); msleep(1);
+ rtl8225_write_phy_ofdm(dev, 0x85, 0xfc); msleep(1);
+ rtl8225_write_phy_cck(dev, 0xc1, 0x88); msleep(1);
+}
+
static void rtl8225_rf_stop(struct ieee80211_hw *dev)
{
u8 reg;
@@ -739,8 +923,10 @@ static void rtl8225_rf_set_channel(struct ieee80211_hw *dev,

if (priv->rf->init == rtl8225_rf_init)
rtl8225_rf_set_tx_power(dev, chan);
- else
+ else if (priv->rf->init == rtl8225z2_rf_init)
rtl8225z2_rf_set_tx_power(dev, chan);
+ else
+ rtl8225z2_b_rf_set_tx_power(dev, chan);

rtl8225_write(dev, 0x7, rtl8225_chan[chan - 1]);
msleep(10);
@@ -760,19 +946,30 @@ static const struct rtl818x_rf_ops rtl8225z2_ops = {
.set_chan = rtl8225_rf_set_channel
};

+static const struct rtl818x_rf_ops rtl8225z2_b_ops = {
+ .name = "rtl8225z2",
+ .init = rtl8225z2_b_rf_init,
+ .stop = rtl8225_rf_stop,
+ .set_chan = rtl8225_rf_set_channel
+};
+
const struct rtl818x_rf_ops * rtl8187_detect_rf(struct ieee80211_hw *dev)
{
u16 reg8, reg9;
+ struct rtl8187_priv *priv = dev->priv;

- rtl8225_write(dev, 0, 0x1B7);
+ if (!priv->is_rtl8187b) {
+ rtl8225_write(dev, 0, 0x1B7);

- reg8 = rtl8225_read(dev, 8);
- reg9 = rtl8225_read(dev, 9);
+ reg8 = rtl8225_read(dev, 8);
+ reg9 = rtl8225_read(dev, 9);

- rtl8225_write(dev, 0, 0x0B7);
+ rtl8225_write(dev, 0, 0x0B7);

- if (reg8 != 0x588 || reg9 != 0x700)
- return &rtl8225_ops;
+ if (reg8 != 0x588 || reg9 != 0x700)
+ return &rtl8225_ops;

- return &rtl8225z2_ops;
+ return &rtl8225z2_ops;
+ } else
+ return &rtl8225z2_b_ops;
}
diff --git a/drivers/net/wireless/rtl818x.h b/drivers/net/wireless/rtl818x.h
index 4f7d38f..e497fea 100644
--- a/drivers/net/wireless/rtl818x.h
+++ b/drivers/net/wireless/rtl818x.h
@@ -66,7 +66,10 @@ struct rtl818x_csr {
#define RTL818X_TX_CONF_R8180_F (3 << 25)
#define RTL818X_TX_CONF_R8185_ABC (4 << 25)
#define RTL818X_TX_CONF_R8185_D (5 << 25)
+#define RTL818X_TX_CONF_R8187vD_1 (5 << 25)
+#define RTL818X_TX_CONF_R8187vD_2 (6 << 25)
#define RTL818X_TX_CONF_HWVER_MASK (7 << 25)
+#define RTL818X_TX_CONF_DISREQQSIZE (1 << 28)
#define RTL818X_TX_CONF_PROBE_DTS (1 << 29)
#define RTL818X_TX_CONF_HW_SEQNUM (1 << 30)
#define RTL818X_TX_CONF_CW_MIN (1 << 31)
@@ -106,8 +109,11 @@ struct rtl818x_csr {
#define RTL818X_MSR_NO_LINK (0 << 2)
#define RTL818X_MSR_ADHOC (1 << 2)
#define RTL818X_MSR_INFRA (2 << 2)
+#define RTL818X_MSR_MASTER (3 << 2)
+#define RTL818X_MSR_ENEDCA (4 << 2)
u8 CONFIG3;
#define RTL818X_CONFIG3_ANAPARAM_WRITE (1 << 6)
+#define RTL818X_CONFIG3_GNT_SELECT (1 << 7)
u8 CONFIG4;
#define RTL818X_CONFIG4_POWEROFF (1 << 6)
#define RTL818X_CONFIG4_VCOOFF (1 << 7)
@@ -133,7 +139,9 @@ struct rtl818x_csr {
__le32 RF_TIMING;
u8 GP_ENABLE;
u8 GPIO;
- u8 reserved_12[10];
+ u8 reserved_12[2];
+ __le32 HSSI_PARA;
+ u8 reserved_13[4];
u8 TX_AGC_CTL;
#define RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT (1 << 0)
#define RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT (1 << 1)
@@ -141,29 +149,38 @@ struct rtl818x_csr {
u8 TX_GAIN_CCK;
u8 TX_GAIN_OFDM;
u8 TX_ANTENNA;
- u8 reserved_13[16];
+ u8 reserved_14[16];
u8 WPA_CONF;
- u8 reserved_14[3];
+ u8 reserved_15[3];
u8 SIFS;
u8 DIFS;
u8 SLOT;
- u8 reserved_15[5];
+ u8 reserved_16[5];
u8 CW_CONF;
#define RTL818X_CW_CONF_PERPACKET_CW_SHIFT (1 << 0)
#define RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT (1 << 1)
u8 CW_VAL;
u8 RATE_FALLBACK;
- u8 reserved_16[25];
+#define RTL818X_RATE_FALLBACK_ENABLE (1 << 7)
+ u8 ACM_CONTROL;
+ u8 reserved_17[24];
u8 CONFIG5;
u8 TX_DMA_POLLING;
- u8 reserved_17[2];
+ u8 reserved_18[2];
__le16 CWR;
u8 RETRY_CTR;
- u8 reserved_18[5];
+ u8 reserved_19[3];
+ __le16 INT_MIG;
+#define RTL818X_R8187B_B 0
+#define RTL818X_R8187B_D 1
+#define RTL818X_R8187B_E 2
__le32 RDSAR;
- u8 reserved_19[12];
- __le16 FEMR;
+ __le16 TID_AC_MAP;
u8 reserved_20[4];
+ u8 ANAPARAM3;
+ u8 reserved_21[5];
+ __le16 FEMR;
+ u8 reserved_22[4];
__le16 TALLY_CNT;
u8 TALLY_SEL;
} __attribute__((packed));


--
[]'s
Herton

Subject: Re: [PATCH] Add Realtek 8187B support

Em Thursday 10 April 2008 00:39:58 Larry Finger escreveu:
> Herton Ronaldo Krzesinski wrote:
> > Hi, this patch (made against wireless-testing repository) adds support for
> > 8187B to the rtl8187 module.
>
> I want to give a "heads up" to the users of this driver. This
> afternoon, I was using flood pings on my RTL8187B at different
> transmission rates to determine how well the device worked. In the
> middle of the 3rd test, the device failed, and no longer has the
> ability to transmit. I have no way of knowing if that was just the
> time that it failed, or if the driver is overpowering the transmitter.
> In any case, you should watch for problems.

Can you open your dongle and see if you really have a RTL8187B? (as you
said you had to force the ID). May be it wasn't really a 8187b device but
another variant. I did many tests this week with a 8187b that continues to
work ok.

>
> Larry
>

--
[]'s
Herton

Subject: Re: [PATCH] Add Realtek 8187B support

Em Friday 11 April 2008 01:57:50 Larry Finger escreveu:
> Herton Ronaldo Krzesinski wrote:
> >
> > Can you open your dongle and see if you really have a RTL8187B? (as you
> > said you had to force the ID). May be it wasn't really a 8187b device but
> > another variant. I did many tests this week with a 8187b that continues to
> > work ok.
>
> I don't want to open it as it should still be under warranty. I'm
> pretty sure it is an 8187b. When I tried it with the 8187 driver, it
> gets an rf calibration error. By using your driver with the patch
> below, it loaded and worked, at least for a while. It probably was a
> natural death, but I felt I should issue a warning, jest in case.

Yes, I'm too concerned about the fact that could be the patch that fried your
dongle. So any people playing with my patch should be careful. It prompted
me to try a long ping -f session from the machine I have with a rtl8187b, mine
continues to work ok (after one day of ping -f), but any sign of problem I will
report. I think you have a real 8187b, but anyway could be there a possibility
that it's a variant that requires different code (could explain the different id),
or is just the patch, or just was a natural death.

Anyway would be great if someone could review my patch and compare to
Realtek code, I did the same initialization/set tx power code, may be something
is wrong/missing.

>
> I was going to hold the following patch until I had more to
> contribute; however, I don't know if I will be able to participate.
> Please apply the patch, as it lets devices like mine, where the
> iProduct string identifies is as an RTL8187B device, but the idProduct
> value is 0x8187, not 0x8189.

Thanks for the patch, I'll include your changes. Also I found today another
minor thing missing from my patch, I need to set "dev->queues = 4" for rtl8187b,
as it has qos (the four endpoints in tx code). Anyone getting current patch can
fix this for now. Sometime I'll post an updated patch.

>
> Thanks,
>
> Larry

--
[]'s
Herton