Skip to content

Commit

Permalink
zd1211rw: Fix TX status reporting in order to have proper rate control
Browse files Browse the repository at this point in the history
First, we reduce the number of hardware retries to 0 (ie 2 real retries
for each rate). Next, when we report the retries to mac80211, we always
report a retry count of 1 (it seems to be 2 in fact, but using 2 seems
to lead to wrong performance for some reason). We use a state machine to
determine the real fate of a packet based on the 802.11 ACK and what the
Zydas hardware is saying when a real retry occurs. The real retry rates
are encoded in a static array. It has been tested with both zd1211 and
zd1211b hardware. Of course, since the Zydas hardware is not reporting
retries accurately, we are just doing our best in order to get the best
performance (ie higher throughput).

Signed-off-by: Benoit PAPILLAULT <benoit.papillault@free.fr>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Benoit PAPILLAULT authored and John W. Linville committed Oct 27, 2009
1 parent 287d374 commit 7f4013f
Show file tree
Hide file tree
Showing 5 changed files with 227 additions and 26 deletions.
4 changes: 2 additions & 2 deletions drivers/net/wireless/zd1211rw/zd_chip.c
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,7 @@ static int hw_reset_phy(struct zd_chip *chip)
static int zd1211_hw_init_hmac(struct zd_chip *chip)
{
static const struct zd_ioreq32 ioreqs[] = {
{ CR_ZD1211_RETRY_MAX, 0x2 },
{ CR_ZD1211_RETRY_MAX, ZD1211_RETRY_COUNT },
{ CR_RX_THRESHOLD, 0x000c0640 },
};

Expand All @@ -767,7 +767,7 @@ static int zd1211_hw_init_hmac(struct zd_chip *chip)
static int zd1211b_hw_init_hmac(struct zd_chip *chip)
{
static const struct zd_ioreq32 ioreqs[] = {
{ CR_ZD1211B_RETRY_MAX, 0x02020202 },
{ CR_ZD1211B_RETRY_MAX, ZD1211B_RETRY_COUNT },
{ CR_ZD1211B_CWIN_MAX_MIN_AC0, 0x007f003f },
{ CR_ZD1211B_CWIN_MAX_MIN_AC1, 0x007f003f },
{ CR_ZD1211B_CWIN_MAX_MIN_AC2, 0x003f001f },
Expand Down
18 changes: 17 additions & 1 deletion drivers/net/wireless/zd1211rw/zd_chip.h
Original file line number Diff line number Diff line change
Expand Up @@ -642,13 +642,29 @@ enum {
#define CR_ZD1211B_TXOP CTL_REG(0x0b20)
#define CR_ZD1211B_RETRY_MAX CTL_REG(0x0b28)

/* Value for CR_ZD1211_RETRY_MAX & CR_ZD1211B_RETRY_MAX. Vendor driver uses 2,
* we use 0. The first rate is tried (count+2), then all next rates are tried
* twice, until 1 Mbits is tried. */
#define ZD1211_RETRY_COUNT 0
#define ZD1211B_RETRY_COUNT \
(ZD1211_RETRY_COUNT << 0)| \
(ZD1211_RETRY_COUNT << 8)| \
(ZD1211_RETRY_COUNT << 16)| \
(ZD1211_RETRY_COUNT << 24)

/* Used to detect PLL lock */
#define UW2453_INTR_REG ((zd_addr_t)0x85c1)

#define CWIN_SIZE 0x007f043f


#define HWINT_ENABLED 0x004f0000
#define HWINT_ENABLED \
(INT_TX_COMPLETE_EN| \
INT_RX_COMPLETE_EN| \
INT_RETRY_FAIL_EN| \
INT_WAKEUP_EN| \
INT_CFG_NEXT_BCN_EN)

#define HWINT_DISABLED 0

#define E2P_PWR_INT_GUARD 8
Expand Down
202 changes: 182 additions & 20 deletions drivers/net/wireless/zd1211rw/zd_mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,34 @@ static const struct ieee80211_rate zd_rates[] = {
.flags = 0 },
};

/*
* Zydas retry rates table. Each line is listed in the same order as
* in zd_rates[] and contains all the rate used when a packet is sent
* starting with a given rates. Let's consider an example :
*
* "11 Mbits : 4, 3, 2, 1, 0" means :
* - packet is sent using 4 different rates
* - 1st rate is index 3 (ie 11 Mbits)
* - 2nd rate is index 2 (ie 5.5 Mbits)
* - 3rd rate is index 1 (ie 2 Mbits)
* - 4th rate is index 0 (ie 1 Mbits)
*/

static const struct tx_retry_rate zd_retry_rates[] = {
{ /* 1 Mbits */ 1, { 0 }},
{ /* 2 Mbits */ 2, { 1, 0 }},
{ /* 5.5 Mbits */ 3, { 2, 1, 0 }},
{ /* 11 Mbits */ 4, { 3, 2, 1, 0 }},
{ /* 6 Mbits */ 5, { 4, 3, 2, 1, 0 }},
{ /* 9 Mbits */ 6, { 5, 4, 3, 2, 1, 0}},
{ /* 12 Mbits */ 5, { 6, 3, 2, 1, 0 }},
{ /* 18 Mbits */ 6, { 7, 6, 3, 2, 1, 0 }},
{ /* 24 Mbits */ 6, { 8, 6, 3, 2, 1, 0 }},
{ /* 36 Mbits */ 7, { 9, 8, 6, 3, 2, 1, 0 }},
{ /* 48 Mbits */ 8, {10, 9, 8, 6, 3, 2, 1, 0 }},
{ /* 54 Mbits */ 9, {11, 10, 9, 8, 6, 3, 2, 1, 0 }}
};

static const struct ieee80211_channel zd_channels[] = {
{ .center_freq = 2412, .hw_value = 1 },
{ .center_freq = 2417, .hw_value = 2 },
Expand Down Expand Up @@ -282,7 +310,7 @@ static void zd_op_stop(struct ieee80211_hw *hw)
}

/**
* tx_status - reports tx status of a packet if required
* zd_mac_tx_status - reports tx status of a packet if required
* @hw - a &struct ieee80211_hw pointer
* @skb - a sk-buffer
* @flags: extra flags to set in the TX status info
Expand All @@ -295,15 +323,49 @@ static void zd_op_stop(struct ieee80211_hw *hw)
*
* If no status information has been requested, the skb is freed.
*/
static void tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
int ackssi, bool success)
static void zd_mac_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
int ackssi, struct tx_status *tx_status)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int i;
int success = 1, retry = 1;
int first_idx;
const struct tx_retry_rate *retries;

ieee80211_tx_info_clear_status(info);

if (success)
if (tx_status) {
success = !tx_status->failure;
retry = tx_status->retry + success;
}

if (success) {
/* success */
info->flags |= IEEE80211_TX_STAT_ACK;
} else {
/* failure */
info->flags &= ~IEEE80211_TX_STAT_ACK;
}

first_idx = info->status.rates[0].idx;
ZD_ASSERT(0<=first_idx && first_idx<ARRAY_SIZE(zd_retry_rates));
retries = &zd_retry_rates[first_idx];
ZD_ASSERT(0<=retry && retry<=retries->count);

info->status.rates[0].idx = retries->rate[0];
info->status.rates[0].count = 1; // (retry > 1 ? 2 : 1);

for (i=1; i<IEEE80211_TX_MAX_RATES-1 && i<retry; i++) {
info->status.rates[i].idx = retries->rate[i];
info->status.rates[i].count = 1; // ((i==retry-1) && success ? 1:2);
}
for (; i<IEEE80211_TX_MAX_RATES && i<retry; i++) {
info->status.rates[i].idx = retries->rate[retry-1];
info->status.rates[i].count = 1; // (success ? 1:2);
}
if (i<IEEE80211_TX_MAX_RATES)
info->status.rates[i].idx = -1; /* terminate */

info->status.ack_signal = ackssi;
ieee80211_tx_status_irqsafe(hw, skb);
}
Expand All @@ -316,16 +378,79 @@ static void tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
* transferred. The first frame from the tx queue, will be selected and
* reported as error to the upper layers.
*/
void zd_mac_tx_failed(struct ieee80211_hw *hw)
void zd_mac_tx_failed(struct urb *urb)
{
struct sk_buff_head *q = &zd_hw_mac(hw)->ack_wait_queue;
struct ieee80211_hw * hw = zd_usb_to_hw(urb->context);
struct zd_mac *mac = zd_hw_mac(hw);
struct sk_buff_head *q = &mac->ack_wait_queue;
struct sk_buff *skb;
struct tx_status *tx_status = (struct tx_status *)urb->transfer_buffer;
unsigned long flags;
int success = !tx_status->failure;
int retry = tx_status->retry + success;
int found = 0;
int i, position = 0;

skb = skb_dequeue(q);
if (skb == NULL)
return;
q = &mac->ack_wait_queue;
spin_lock_irqsave(&q->lock, flags);

skb_queue_walk(q, skb) {
struct ieee80211_hdr *tx_hdr;
struct ieee80211_tx_info *info;
int first_idx, final_idx;
const struct tx_retry_rate *retries;
u8 final_rate;

position ++;

/* if the hardware reports a failure and we had a 802.11 ACK
* pending, then we skip the first skb when searching for a
* matching frame */
if (tx_status->failure && mac->ack_pending &&
skb_queue_is_first(q, skb)) {
continue;
}

tx_hdr = (struct ieee80211_hdr *)skb->data;

/* we skip all frames not matching the reported destination */
if (unlikely(memcmp(tx_hdr->addr1, tx_status->mac, ETH_ALEN))) {
continue;
}

/* we skip all frames not matching the reported final rate */

tx_status(hw, skb, 0, 0);
info = IEEE80211_SKB_CB(skb);
first_idx = info->status.rates[0].idx;
ZD_ASSERT(0<=first_idx && first_idx<ARRAY_SIZE(zd_retry_rates));
retries = &zd_retry_rates[first_idx];
if (retry < 0 || retry > retries->count) {
continue;
}

ZD_ASSERT(0<=retry && retry<=retries->count);
final_idx = retries->rate[retry-1];
final_rate = zd_rates[final_idx].hw_value;

if (final_rate != tx_status->rate) {
continue;
}

found = 1;
break;
}

if (found) {
for (i=1; i<=position; i++) {
skb = __skb_dequeue(q);
zd_mac_tx_status(hw, skb,
mac->ack_pending ? mac->ack_signal : 0,
i == position ? tx_status : NULL);
mac->ack_pending = 0;
}
}

spin_unlock_irqrestore(&q->lock, flags);
}

/**
Expand All @@ -342,18 +467,27 @@ void zd_mac_tx_to_dev(struct sk_buff *skb, int error)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hw *hw = info->rate_driver_data[0];
struct zd_mac *mac = zd_hw_mac(hw);

ieee80211_tx_info_clear_status(info);

skb_pull(skb, sizeof(struct zd_ctrlset));
if (unlikely(error ||
(info->flags & IEEE80211_TX_CTL_NO_ACK))) {
tx_status(hw, skb, 0, !error);
/*
* FIXME : do we need to fill in anything ?
*/
ieee80211_tx_status_irqsafe(hw, skb);
} else {
struct sk_buff_head *q =
&zd_hw_mac(hw)->ack_wait_queue;
struct sk_buff_head *q = &mac->ack_wait_queue;

skb_queue_tail(q, skb);
while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS)
zd_mac_tx_failed(hw);
while (skb_queue_len(q) > ZD_MAC_MAX_ACK_WAITERS) {
zd_mac_tx_status(hw, skb_dequeue(q),
mac->ack_pending ? mac->ack_signal : 0,
NULL);
mac->ack_pending = 0;
}
}
}

Expand Down Expand Up @@ -606,27 +740,47 @@ static int zd_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr,
struct ieee80211_rx_status *stats)
{
struct zd_mac *mac = zd_hw_mac(hw);
struct sk_buff *skb;
struct sk_buff_head *q;
unsigned long flags;
int found = 0;
int i, position = 0;

if (!ieee80211_is_ack(rx_hdr->frame_control))
return 0;

q = &zd_hw_mac(hw)->ack_wait_queue;
q = &mac->ack_wait_queue;
spin_lock_irqsave(&q->lock, flags);
skb_queue_walk(q, skb) {
struct ieee80211_hdr *tx_hdr;

position ++;

if (mac->ack_pending && skb_queue_is_first(q, skb))
continue;

tx_hdr = (struct ieee80211_hdr *)skb->data;
if (likely(!memcmp(tx_hdr->addr2, rx_hdr->addr1, ETH_ALEN)))
{
__skb_unlink(skb, q);
tx_status(hw, skb, stats->signal, 1);
goto out;
found = 1;
break;
}
}
out:

if (found) {
for (i=1; i<position; i++) {
skb = __skb_dequeue(q);
zd_mac_tx_status(hw, skb,
mac->ack_pending ? mac->ack_signal : 0,
NULL);
mac->ack_pending = 0;
}

mac->ack_pending = 1;
mac->ack_signal = stats->signal;
}

spin_unlock_irqrestore(&q->lock, flags);
return 1;
}
Expand Down Expand Up @@ -709,6 +863,7 @@ int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length)
skb_reserve(skb, 2);
}

/* FIXME : could we avoid this big memcpy ? */
memcpy(skb_put(skb, length), buffer, length);

memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats));
Expand Down Expand Up @@ -999,7 +1154,14 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
hw->queues = 1;
hw->extra_tx_headroom = sizeof(struct zd_ctrlset);

/*
* Tell mac80211 that we support multi rate retries
*/
hw->max_rates = IEEE80211_TX_MAX_RATES;
hw->max_rate_tries = 18; /* 9 rates * 2 retries/rate */

skb_queue_head_init(&mac->ack_wait_queue);
mac->ack_pending = 0;

zd_chip_init(&mac->chip, hw, intf);
housekeeping_init(mac);
Expand Down
25 changes: 23 additions & 2 deletions drivers/net/wireless/zd1211rw/zd_mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,21 @@ struct rx_status {
#define ZD_RX_CRC16_ERROR 0x40
#define ZD_RX_ERROR 0x80

struct tx_retry_rate {
int count; /* number of valid element in rate[] array */
int rate[10]; /* retry rates, described by an index in zd_rates[] */
};

struct tx_status {
u8 type; /* must always be 0x01 : USB_INT_TYPE */
u8 id; /* must always be 0xa0 : USB_INT_ID_RETRY_FAILED */
u8 rate;
u8 pad;
u8 mac[ETH_ALEN];
u8 retry;
u8 failure;
} __attribute__((packed));

enum mac_flags {
MAC_FIXED_CHANNEL = 0x01,
};
Expand All @@ -150,7 +165,7 @@ struct housekeeping {

#define ZD_MAC_STATS_BUFFER_SIZE 16

#define ZD_MAC_MAX_ACK_WAITERS 10
#define ZD_MAC_MAX_ACK_WAITERS 50

struct zd_mac {
struct zd_chip chip;
Expand Down Expand Up @@ -184,6 +199,12 @@ struct zd_mac {

/* whether to pass control frames to stack */
unsigned int pass_ctrl:1;

/* whether we have received a 802.11 ACK that is pending */
unsigned int ack_pending:1;

/* signal strength of the last 802.11 ACK received */
int ack_signal;
};

#define ZD_REGDOMAIN_FCC 0x10
Expand Down Expand Up @@ -279,7 +300,7 @@ int zd_mac_preinit_hw(struct ieee80211_hw *hw);
int zd_mac_init_hw(struct ieee80211_hw *hw);

int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length);
void zd_mac_tx_failed(struct ieee80211_hw *hw);
void zd_mac_tx_failed(struct urb *urb);
void zd_mac_tx_to_dev(struct sk_buff *skb, int error);

#ifdef DEBUG
Expand Down
Loading

0 comments on commit 7f4013f

Please sign in to comment.