Message ID | 1254835462-9866-1-git-send-email-ike.pan@canonical.com |
---|---|
State | Accepted |
Headers | show |
Ike Panhc wrote: > BugLink: https://bugs.launchpad.net/bugs/444377 > > mac80211 needs driver to report the TX retry/fail count for bitrate control. > It is the hardware mac to re-transmit frames, so we need to collect the count > after the transmission has done. > > Signed-off-by: Colin Ian King <colin.king@canonical.com> > Signed-off-by: Ike Panhc <ike.pan@canonical.com> > --- > .../drivers/net/wireless/rt2x00/rt2x00.h | 7 ++ > .../drivers/net/wireless/rt2x00/rt2x00usb.c | 31 ++++++--- > .../drivers/net/wireless/rt2x00/rt73usb.c | 66 ++++++++++++++++++++ > .../drivers/net/wireless/rt2x00/rt73usb.h | 4 +- > 4 files changed, 95 insertions(+), 13 deletions(-) > > diff --git a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00.h b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00.h > index 427d01e..311b8d0 100644 > --- a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00.h > +++ b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00.h > @@ -525,6 +525,8 @@ struct rt2x00lib_ops { > struct sk_buff *skb); > void (*kick_tx_queue) (struct rt2x00_dev *rt2x00dev, > const enum data_queue_qid queue); > + void (*get_tx_status) (struct rt2x00_dev *rt2x00dev, > + struct txdone_entry_desc *txdesc); > > /* > * RX control handlers > @@ -809,6 +811,11 @@ struct rt2x00_dev { > * Firmware image. > */ > const struct firmware *fw; > + > + /* > + * Cached TX stats, rt73usb > + */ > + int tx_retry_count; > }; > > /* > diff --git a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00usb.c b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00usb.c > index 83862e7..7a08f8b 100644 > --- a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00usb.c > +++ b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00usb.c > @@ -135,6 +135,8 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) > !test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) > return; > > + memset(&txdesc, 0, sizeof(struct txdone_entry_desc)); > + > /* > * Remove the descriptor data from the buffer. > */ > @@ -142,18 +144,25 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) > > /* > * Obtain the status about this packet. > - * Note that when the status is 0 it does not mean the > - * frame was send out correctly. It only means the frame > - * was succesfully pushed to the hardware, we have no > - * way to determine the transmission status right now. > - * (Only indirectly by looking at the failed TX counters > - * in the register). > */ > - if (!urb->status) > - __set_bit(TXDONE_UNKNOWN, &txdesc.flags); > - else > - __set_bit(TXDONE_FAILURE, &txdesc.flags); > - txdesc.retry = 0; > + if (rt2x00dev->ops->lib->get_tx_status) { > + rt2x00dev->ops->lib->get_tx_status(rt2x00dev, &txdesc); > + } else { > + /* > + * Note that when the status is 0 it does not mean the > + * frame was send out correctly. It only means the frame > + * was succesfully pushed to the hardware, we have no > + * way to determine the transmission status right now. > + * (Only indirectly by looking at the failed TX counters > + * in the register). > + */ > + if (!urb->status) > + __set_bit(TXDONE_UNKNOWN, &txdesc.flags); > + else > + __set_bit(TXDONE_FAILURE, &txdesc.flags); > + > + txdesc.retry = 0; > + } > > rt2x00lib_txdone(entry, &txdesc); > } > diff --git a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.c b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.c > index 2521a08..3dde950 100644 > --- a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.c > +++ b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.c > @@ -36,6 +36,13 @@ > #include "rt2x00usb.h" > #include "rt73usb.h" > > +struct rt73_work_info { > + struct work_struct work; > + struct rt2x00_dev *rt2x00dev; > +}; > + > +struct rt73_work_info rt73_work; > + > /* > * Register access. > * All access to the CSR registers will go through the methods > @@ -1437,6 +1444,62 @@ static void rt73usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev, > } > } > > +static void rt73usb_tx_status_work(struct work_struct *work) > +{ > + u32 reg4; > + u32 reg5; > + int retry_fails; > + struct rt73_work_info *rt73_work_item = > + container_of(work, struct rt73_work_info, work); > + struct rt2x00_dev *rt2x00dev = rt73_work_item->rt2x00dev; > + > + rt73usb_register_read(rt2x00dev, STA_CSR4, ®4); > + rt73usb_register_read(rt2x00dev, STA_CSR5, ®5); > + > + retry_fails = rt2x00_get_field32(reg5, STA_CSR5_TX_RETRY_FAIL_COUNT); > + > + if (retry_fails > 0) > + /* -ve retry count indicates a failure */ > + rt2x00dev->tx_retry_count = -retry_fails; > + else > + if (rt2x00_get_field32(reg4, STA_CSR4_TX_NO_RETRY_COUNT) > 0) > + rt2x00dev->tx_retry_count = 0; /* Success */ > + else if (rt2x00_get_field32(reg4, STA_CSR4_TX_ONE_RETRY_COUNT) > 0) > + rt2x00dev->tx_retry_count = 1; /* Single retry */ > + else { > + int retries = rt2x00_get_field32(reg5, STA_CSR5_TX_MULTI_RETRY_COUNT); > + if (retries > 0) > + rt2x00dev->tx_retry_count = retries; > + else > + rt2x00dev->tx_retry_count = 0; /* Unknown */ > + } > +} > + > +static void rt73usb_get_tx_status(struct rt2x00_dev *rt2x00dev, > + struct txdone_entry_desc *txdesc) > +{ > + rt73_work.rt2x00dev = rt2x00dev; > + > + /* > + * Caution: This is being called from withing an interrupt context. > + * We cannot gather statistics on the current transmitted packets > + * inside the interrupt context because we need to do USB register > + * reads. Instead, we shedule it to be run on a work queue to gather > + * statistics for the next packet. It's not 100% perfect solution > + * but it does at least supply some information back up the stack > + * to allow bitrate control to work. > + */ > + schedule_work(&rt73_work.work); > + > + if (rt2x00dev->tx_retry_count < 0) { > + __set_bit(TXDONE_FAILURE, &txdesc->flags); > + txdesc->retry = 0; > + } else { > + __set_bit(TXDONE_SUCCESS, &txdesc->flags); > + txdesc->retry = rt2x00dev->tx_retry_count; > + } > +} > + > /* > * RX control handlers > */ > @@ -1938,6 +2001,8 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) > { > int retval; > > + INIT_WORK(&rt73_work.work, rt73usb_tx_status_work); > + > /* > * Allocate eeprom data. > */ > @@ -2044,6 +2109,7 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = { > .write_beacon = rt73usb_write_beacon, > .get_tx_data_len = rt73usb_get_tx_data_len, > .kick_tx_queue = rt73usb_kick_tx_queue, > + .get_tx_status = rt73usb_get_tx_status, > .fill_rxdone = rt73usb_fill_rxdone, > .config_filter = rt73usb_config_filter, > .config_intf = rt73usb_config_intf, > diff --git a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.h b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.h > index 1484935..60f98be 100644 > --- a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.h > +++ b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.h > @@ -619,8 +619,8 @@ struct hw_pairwise_ta_entry { > * STA_CSR5: TX Retry count. > */ > #define STA_CSR5 0x30d4 > -#define STA_CSR4_TX_MULTI_RETRY_COUNT FIELD32(0x0000ffff) > -#define STA_CSR4_TX_RETRY_FAIL_COUNT FIELD32(0xffff0000) > +#define STA_CSR5_TX_MULTI_RETRY_COUNT FIELD32(0x0000ffff) > +#define STA_CSR5_TX_RETRY_FAIL_COUNT FIELD32(0xffff0000) > > /* > * QOS control registers. > ACK
diff --git a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00.h b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00.h index 427d01e..311b8d0 100644 --- a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00.h +++ b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00.h @@ -525,6 +525,8 @@ struct rt2x00lib_ops { struct sk_buff *skb); void (*kick_tx_queue) (struct rt2x00_dev *rt2x00dev, const enum data_queue_qid queue); + void (*get_tx_status) (struct rt2x00_dev *rt2x00dev, + struct txdone_entry_desc *txdesc); /* * RX control handlers @@ -809,6 +811,11 @@ struct rt2x00_dev { * Firmware image. */ const struct firmware *fw; + + /* + * Cached TX stats, rt73usb + */ + int tx_retry_count; }; /* diff --git a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00usb.c b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00usb.c index 83862e7..7a08f8b 100644 --- a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -135,6 +135,8 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) !test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags)) return; + memset(&txdesc, 0, sizeof(struct txdone_entry_desc)); + /* * Remove the descriptor data from the buffer. */ @@ -142,18 +144,25 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) /* * Obtain the status about this packet. - * Note that when the status is 0 it does not mean the - * frame was send out correctly. It only means the frame - * was succesfully pushed to the hardware, we have no - * way to determine the transmission status right now. - * (Only indirectly by looking at the failed TX counters - * in the register). */ - if (!urb->status) - __set_bit(TXDONE_UNKNOWN, &txdesc.flags); - else - __set_bit(TXDONE_FAILURE, &txdesc.flags); - txdesc.retry = 0; + if (rt2x00dev->ops->lib->get_tx_status) { + rt2x00dev->ops->lib->get_tx_status(rt2x00dev, &txdesc); + } else { + /* + * Note that when the status is 0 it does not mean the + * frame was send out correctly. It only means the frame + * was succesfully pushed to the hardware, we have no + * way to determine the transmission status right now. + * (Only indirectly by looking at the failed TX counters + * in the register). + */ + if (!urb->status) + __set_bit(TXDONE_UNKNOWN, &txdesc.flags); + else + __set_bit(TXDONE_FAILURE, &txdesc.flags); + + txdesc.retry = 0; + } rt2x00lib_txdone(entry, &txdesc); } diff --git a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.c b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.c index 2521a08..3dde950 100644 --- a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.c +++ b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.c @@ -36,6 +36,13 @@ #include "rt2x00usb.h" #include "rt73usb.h" +struct rt73_work_info { + struct work_struct work; + struct rt2x00_dev *rt2x00dev; +}; + +struct rt73_work_info rt73_work; + /* * Register access. * All access to the CSR registers will go through the methods @@ -1437,6 +1444,62 @@ static void rt73usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev, } } +static void rt73usb_tx_status_work(struct work_struct *work) +{ + u32 reg4; + u32 reg5; + int retry_fails; + struct rt73_work_info *rt73_work_item = + container_of(work, struct rt73_work_info, work); + struct rt2x00_dev *rt2x00dev = rt73_work_item->rt2x00dev; + + rt73usb_register_read(rt2x00dev, STA_CSR4, ®4); + rt73usb_register_read(rt2x00dev, STA_CSR5, ®5); + + retry_fails = rt2x00_get_field32(reg5, STA_CSR5_TX_RETRY_FAIL_COUNT); + + if (retry_fails > 0) + /* -ve retry count indicates a failure */ + rt2x00dev->tx_retry_count = -retry_fails; + else + if (rt2x00_get_field32(reg4, STA_CSR4_TX_NO_RETRY_COUNT) > 0) + rt2x00dev->tx_retry_count = 0; /* Success */ + else if (rt2x00_get_field32(reg4, STA_CSR4_TX_ONE_RETRY_COUNT) > 0) + rt2x00dev->tx_retry_count = 1; /* Single retry */ + else { + int retries = rt2x00_get_field32(reg5, STA_CSR5_TX_MULTI_RETRY_COUNT); + if (retries > 0) + rt2x00dev->tx_retry_count = retries; + else + rt2x00dev->tx_retry_count = 0; /* Unknown */ + } +} + +static void rt73usb_get_tx_status(struct rt2x00_dev *rt2x00dev, + struct txdone_entry_desc *txdesc) +{ + rt73_work.rt2x00dev = rt2x00dev; + + /* + * Caution: This is being called from withing an interrupt context. + * We cannot gather statistics on the current transmitted packets + * inside the interrupt context because we need to do USB register + * reads. Instead, we shedule it to be run on a work queue to gather + * statistics for the next packet. It's not 100% perfect solution + * but it does at least supply some information back up the stack + * to allow bitrate control to work. + */ + schedule_work(&rt73_work.work); + + if (rt2x00dev->tx_retry_count < 0) { + __set_bit(TXDONE_FAILURE, &txdesc->flags); + txdesc->retry = 0; + } else { + __set_bit(TXDONE_SUCCESS, &txdesc->flags); + txdesc->retry = rt2x00dev->tx_retry_count; + } +} + /* * RX control handlers */ @@ -1938,6 +2001,8 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) { int retval; + INIT_WORK(&rt73_work.work, rt73usb_tx_status_work); + /* * Allocate eeprom data. */ @@ -2044,6 +2109,7 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = { .write_beacon = rt73usb_write_beacon, .get_tx_data_len = rt73usb_get_tx_data_len, .kick_tx_queue = rt73usb_kick_tx_queue, + .get_tx_status = rt73usb_get_tx_status, .fill_rxdone = rt73usb_fill_rxdone, .config_filter = rt73usb_config_filter, .config_intf = rt73usb_config_intf, diff --git a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.h b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.h index 1484935..60f98be 100644 --- a/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.h +++ b/updates/compat-wireless-2.6/drivers/net/wireless/rt2x00/rt73usb.h @@ -619,8 +619,8 @@ struct hw_pairwise_ta_entry { * STA_CSR5: TX Retry count. */ #define STA_CSR5 0x30d4 -#define STA_CSR4_TX_MULTI_RETRY_COUNT FIELD32(0x0000ffff) -#define STA_CSR4_TX_RETRY_FAIL_COUNT FIELD32(0xffff0000) +#define STA_CSR5_TX_MULTI_RETRY_COUNT FIELD32(0x0000ffff) +#define STA_CSR5_TX_RETRY_FAIL_COUNT FIELD32(0xffff0000) /* * QOS control registers.