summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/rt2x00
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/rt2x00')
-rw-r--r--drivers/net/wireless/rt2x00/rt2400pci.c33
-rw-r--r--drivers/net/wireless/rt2x00/rt2500pci.c39
-rw-r--r--drivers/net/wireless/rt2x00/rt2500usb.c37
-rw-r--r--drivers/net/wireless/rt2x00/rt2800.h38
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.c389
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.h24
-rw-r--r--drivers/net/wireless/rt2x00/rt2800pci.c233
-rw-r--r--drivers/net/wireless/rt2x00/rt2800usb.c159
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00.h31
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00config.c12
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00crypto.c17
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00debug.c11
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c71
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00firmware.c3
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00ht.c10
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c82
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.h56
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00usb.c298
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00usb.h12
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.c69
-rw-r--r--drivers/net/wireless/rt2x00/rt73usb.c41
21 files changed, 975 insertions, 690 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c
index 5063e01410e5..103c71164f10 100644
--- a/drivers/net/wireless/rt2x00/rt2400pci.c
+++ b/drivers/net/wireless/rt2x00/rt2400pci.c
@@ -1007,12 +1007,11 @@ static int rt2400pci_set_device_state(struct rt2x00_dev *rt2x00dev,
/*
* TX descriptor initialization
*/
-static void rt2400pci_write_tx_desc(struct rt2x00_dev *rt2x00dev,
- struct sk_buff *skb,
+static void rt2400pci_write_tx_desc(struct queue_entry *entry,
struct txentry_desc *txdesc)
{
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
- struct queue_entry_priv_pci *entry_priv = skbdesc->entry->priv_data;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+ struct queue_entry_priv_pci *entry_priv = entry->priv_data;
__le32 *txd = entry_priv->desc;
u32 word;
@@ -1096,7 +1095,7 @@ static void rt2400pci_write_beacon(struct queue_entry *entry,
/*
* Write the TX descriptor for the beacon.
*/
- rt2400pci_write_tx_desc(rt2x00dev, entry->skb, txdesc);
+ rt2400pci_write_tx_desc(entry, txdesc);
/*
* Dump beacon to userspace through debugfs.
@@ -1112,24 +1111,24 @@ static void rt2400pci_write_beacon(struct queue_entry *entry,
rt2x00pci_register_write(rt2x00dev, CSR14, reg);
}
-static void rt2400pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid queue)
+static void rt2400pci_kick_tx_queue(struct data_queue *queue)
{
+ struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
u32 reg;
rt2x00pci_register_read(rt2x00dev, TXCSR0, &reg);
- rt2x00_set_field32(&reg, TXCSR0_KICK_PRIO, (queue == QID_AC_BE));
- rt2x00_set_field32(&reg, TXCSR0_KICK_TX, (queue == QID_AC_BK));
- rt2x00_set_field32(&reg, TXCSR0_KICK_ATIM, (queue == QID_ATIM));
+ rt2x00_set_field32(&reg, TXCSR0_KICK_PRIO, (queue->qid == QID_AC_BE));
+ rt2x00_set_field32(&reg, TXCSR0_KICK_TX, (queue->qid == QID_AC_BK));
+ rt2x00_set_field32(&reg, TXCSR0_KICK_ATIM, (queue->qid == QID_ATIM));
rt2x00pci_register_write(rt2x00dev, TXCSR0, reg);
}
-static void rt2400pci_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid qid)
+static void rt2400pci_kill_tx_queue(struct data_queue *queue)
{
+ struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
u32 reg;
- if (qid == QID_BEACON) {
+ if (queue->qid == QID_BEACON) {
rt2x00pci_register_write(rt2x00dev, CSR14, 0);
} else {
rt2x00pci_register_read(rt2x00dev, TXCSR0, &reg);
@@ -1481,15 +1480,17 @@ static int rt2400pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
/*
* Create channel information array
*/
- info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL);
+ info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
spec->channels_info = info;
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START);
- for (i = 0; i < 14; i++)
- info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ for (i = 0; i < 14; i++) {
+ info[i].max_power = TXPOWER_FROM_DEV(MAX_TXPOWER);
+ info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ }
return 0;
}
diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c
index c2a555d5376b..ab0507110e42 100644
--- a/drivers/net/wireless/rt2x00/rt2500pci.c
+++ b/drivers/net/wireless/rt2x00/rt2500pci.c
@@ -1161,12 +1161,11 @@ static int rt2500pci_set_device_state(struct rt2x00_dev *rt2x00dev,
/*
* TX descriptor initialization
*/
-static void rt2500pci_write_tx_desc(struct rt2x00_dev *rt2x00dev,
- struct sk_buff *skb,
+static void rt2500pci_write_tx_desc(struct queue_entry *entry,
struct txentry_desc *txdesc)
{
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
- struct queue_entry_priv_pci *entry_priv = skbdesc->entry->priv_data;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+ struct queue_entry_priv_pci *entry_priv = entry->priv_data;
__le32 *txd = entry_priv->desc;
u32 word;
@@ -1249,7 +1248,7 @@ static void rt2500pci_write_beacon(struct queue_entry *entry,
/*
* Write the TX descriptor for the beacon.
*/
- rt2500pci_write_tx_desc(rt2x00dev, entry->skb, txdesc);
+ rt2500pci_write_tx_desc(entry, txdesc);
/*
* Dump beacon to userspace through debugfs.
@@ -1265,24 +1264,24 @@ static void rt2500pci_write_beacon(struct queue_entry *entry,
rt2x00pci_register_write(rt2x00dev, CSR14, reg);
}
-static void rt2500pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid queue)
+static void rt2500pci_kick_tx_queue(struct data_queue *queue)
{
+ struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
u32 reg;
rt2x00pci_register_read(rt2x00dev, TXCSR0, &reg);
- rt2x00_set_field32(&reg, TXCSR0_KICK_PRIO, (queue == QID_AC_BE));
- rt2x00_set_field32(&reg, TXCSR0_KICK_TX, (queue == QID_AC_BK));
- rt2x00_set_field32(&reg, TXCSR0_KICK_ATIM, (queue == QID_ATIM));
+ rt2x00_set_field32(&reg, TXCSR0_KICK_PRIO, (queue->qid == QID_AC_BE));
+ rt2x00_set_field32(&reg, TXCSR0_KICK_TX, (queue->qid == QID_AC_BK));
+ rt2x00_set_field32(&reg, TXCSR0_KICK_ATIM, (queue->qid == QID_ATIM));
rt2x00pci_register_write(rt2x00dev, TXCSR0, reg);
}
-static void rt2500pci_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid qid)
+static void rt2500pci_kill_tx_queue(struct data_queue *queue)
{
+ struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
u32 reg;
- if (qid == QID_BEACON) {
+ if (queue->qid == QID_BEACON) {
rt2x00pci_register_write(rt2x00dev, CSR14, 0);
} else {
rt2x00pci_register_read(rt2x00dev, TXCSR0, &reg);
@@ -1795,19 +1794,23 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
/*
* Create channel information array
*/
- info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL);
+ info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
spec->channels_info = info;
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START);
- for (i = 0; i < 14; i++)
- info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ for (i = 0; i < 14; i++) {
+ info[i].max_power = MAX_TXPOWER;
+ info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ }
if (spec->num_channels > 14) {
- for (i = 14; i < spec->num_channels; i++)
- info[i].tx_power1 = DEFAULT_TXPOWER;
+ for (i = 14; i < spec->num_channels; i++) {
+ info[i].max_power = MAX_TXPOWER;
+ info[i].default_power1 = DEFAULT_TXPOWER;
+ }
}
return 0;
diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c
index cdaf93f48263..db64df4267d8 100644
--- a/drivers/net/wireless/rt2x00/rt2500usb.c
+++ b/drivers/net/wireless/rt2x00/rt2500usb.c
@@ -355,7 +355,9 @@ static int rt2500usb_config_key(struct rt2x00_dev *rt2x00dev,
* it is known that not work at least on some hardware.
* SW crypto will be used in that case.
*/
- if (key->alg == ALG_WEP && key->keyidx != 0)
+ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ key->cipher == WLAN_CIPHER_SUITE_WEP104) &&
+ key->keyidx != 0)
return -EOPNOTSUPP;
/*
@@ -1039,12 +1041,11 @@ static int rt2500usb_set_device_state(struct rt2x00_dev *rt2x00dev,
/*
* TX descriptor initialization
*/
-static void rt2500usb_write_tx_desc(struct rt2x00_dev *rt2x00dev,
- struct sk_buff *skb,
+static void rt2500usb_write_tx_desc(struct queue_entry *entry,
struct txentry_desc *txdesc)
{
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
- __le32 *txd = (__le32 *) skb->data;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+ __le32 *txd = (__le32 *) entry->skb->data;
u32 word;
/*
@@ -1127,7 +1128,7 @@ static void rt2500usb_write_beacon(struct queue_entry *entry,
/*
* Write the TX descriptor for the beacon.
*/
- rt2500usb_write_tx_desc(rt2x00dev, entry->skb, txdesc);
+ rt2500usb_write_tx_desc(entry, txdesc);
/*
* Dump beacon to userspace through debugfs.
@@ -1195,6 +1196,14 @@ static int rt2500usb_get_tx_data_len(struct queue_entry *entry)
return length;
}
+static void rt2500usb_kill_tx_queue(struct data_queue *queue)
+{
+ if (queue->qid == QID_BEACON)
+ rt2500usb_register_write(queue->rt2x00dev, TXRX_CSR19, 0);
+
+ rt2x00usb_kill_tx_queue(queue);
+}
+
/*
* RX control handlers
*/
@@ -1698,19 +1707,23 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
/*
* Create channel information array
*/
- info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL);
+ info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
spec->channels_info = info;
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_START);
- for (i = 0; i < 14; i++)
- info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ for (i = 0; i < 14; i++) {
+ info[i].max_power = MAX_TXPOWER;
+ info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ }
if (spec->num_channels > 14) {
- for (i = 14; i < spec->num_channels; i++)
- info[i].tx_power1 = DEFAULT_TXPOWER;
+ for (i = 14; i < spec->num_channels; i++) {
+ info[i].max_power = MAX_TXPOWER;
+ info[i].default_power1 = DEFAULT_TXPOWER;
+ }
}
return 0;
@@ -1789,7 +1802,7 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
.write_beacon = rt2500usb_write_beacon,
.get_tx_data_len = rt2500usb_get_tx_data_len,
.kick_tx_queue = rt2x00usb_kick_tx_queue,
- .kill_tx_queue = rt2x00usb_kill_tx_queue,
+ .kill_tx_queue = rt2500usb_kill_tx_queue,
.fill_rxdone = rt2500usb_fill_rxdone,
.config_shared_key = rt2500usb_config_key,
.config_pairwise_key = rt2500usb_config_key,
diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h
index ed4ebcdde7c9..70a5cb86405b 100644
--- a/drivers/net/wireless/rt2x00/rt2800.h
+++ b/drivers/net/wireless/rt2x00/rt2800.h
@@ -1318,7 +1318,25 @@
#define TX_STA_CNT2_TX_UNDER_FLOW_COUNT FIELD32(0xffff0000)
/*
- * TX_STA_FIFO: TX Result for specific PID status fifo register
+ * TX_STA_FIFO: TX Result for specific PID status fifo register.
+ *
+ * This register is implemented as FIFO with 16 entries in the HW. Each
+ * register read fetches the next tx result. If the FIFO is full because
+ * it wasn't read fast enough after the according interrupt (TX_FIFO_STATUS)
+ * triggered, the hw seems to simply drop further tx results.
+ *
+ * VALID: 1: this tx result is valid
+ * 0: no valid tx result -> driver should stop reading
+ * PID_TYPE: The PID latched from the PID field in the TXWI, can be used
+ * to match a frame with its tx result (even though the PID is
+ * only 4 bits wide).
+ * TX_SUCCESS: Indicates tx success (1) or failure (0)
+ * TX_AGGRE: Indicates if the frame was part of an aggregate (1) or not (0)
+ * TX_ACK_REQUIRED: Indicates if the frame needed to get ack'ed (1) or not (0)
+ * WCID: The wireless client ID.
+ * MCS: The tx rate used during the last transmission of this frame, be it
+ * successful or not.
+ * PHYMODE: The phymode used for the transmission.
*/
#define TX_STA_FIFO 0x1718
#define TX_STA_FIFO_VALID FIELD32(0x00000001)
@@ -1841,6 +1859,13 @@ struct mac_iveiv_entry {
#define EEPROM_RSSI_A2_LNA_A2 FIELD16(0xff00)
/*
+ * EEPROM Maximum TX power values
+ */
+#define EEPROM_MAX_TX_POWER 0x0027
+#define EEPROM_MAX_TX_POWER_24GHZ FIELD16(0x00ff)
+#define EEPROM_MAX_TX_POWER_5GHZ FIELD16(0xff00)
+
+/*
* EEPROM TXpower delta: 20MHZ AND 40 MHZ use different power.
* This is delta in 40MHZ.
* VALUE: Tx Power dalta value (MAX=4)
@@ -1928,6 +1953,8 @@ struct mac_iveiv_entry {
* TX_OP: 0:HT TXOP rule , 1:PIFS TX ,2:Backoff, 3:sifs
* BW: Channel bandwidth 20MHz or 40 MHz
* STBC: 1: STBC support MCS =0-7, 2,3 : RESERVED
+ * AMPDU: 1: this frame is eligible for AMPDU aggregation, the hw will
+ * aggregate consecutive frames with the same RA and QoS TID.
*/
#define TXWI_W0_FRAG FIELD32(0x00000001)
#define TXWI_W0_MIMO_PS FIELD32(0x00000002)
@@ -1945,6 +1972,15 @@ struct mac_iveiv_entry {
/*
* Word1
+ * ACK: 0: No Ack needed, 1: Ack needed
+ * NSEQ: 0: Don't assign hw sequence number, 1: Assign hw sequence number
+ * BW_WIN_SIZE: BA windows size of the recipient
+ * WIRELESS_CLI_ID: Client ID for WCID table access
+ * MPDU_TOTAL_BYTE_COUNT: Length of 802.11 frame
+ * PACKETID: Will be latched into the TX_STA_FIFO register once the according
+ * frame was processed. If multiple frames are aggregated together
+ * (AMPDU==1) the reported tx status will always contain the packet
+ * id of the first frame. 0: Don't report tx status for this frame.
*/
#define TXWI_W1_ACK FIELD32(0x00000001)
#define TXWI_W1_NSEQ FIELD32(0x00000002)
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index b66e0fd8f0fa..27a6e225083c 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -1,4 +1,5 @@
/*
+ Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
Copyright (C) 2010 Ivo van Doorn <IvDoorn@gmail.com>
Copyright (C) 2009 Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Copyright (C) 2009 Gertjan van Wingerde <gwingerde@gmail.com>
@@ -254,6 +255,23 @@ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
}
EXPORT_SYMBOL_GPL(rt2800_mcu_request);
+int rt2800_wait_csr_ready(struct rt2x00_dev *rt2x00dev)
+{
+ unsigned int i = 0;
+ u32 reg;
+
+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
+ if (reg && reg != ~0)
+ return 0;
+ msleep(1);
+ }
+
+ ERROR(rt2x00dev, "Unstable hardware.\n");
+ return -EBUSY;
+}
+EXPORT_SYMBOL_GPL(rt2800_wait_csr_ready);
+
int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev)
{
unsigned int i;
@@ -367,19 +385,16 @@ int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
u32 reg;
/*
- * Wait for stable hardware.
+ * If driver doesn't wake up firmware here,
+ * rt2800_load_firmware will hang forever when interface is up again.
*/
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
- if (reg && reg != ~0)
- break;
- msleep(1);
- }
+ rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000);
- if (i == REGISTER_BUSY_COUNT) {
- ERROR(rt2x00dev, "Unstable hardware.\n");
+ /*
+ * Wait for stable hardware.
+ */
+ if (rt2800_wait_csr_ready(rt2x00dev))
return -EBUSY;
- }
if (rt2x00_is_pci(rt2x00dev))
rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000002);
@@ -427,8 +442,10 @@ int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
}
EXPORT_SYMBOL_GPL(rt2800_load_firmware);
-void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
+void rt2800_write_tx_data(struct queue_entry *entry,
+ struct txentry_desc *txdesc)
{
+ __le32 *txwi = rt2800_drv_get_txwi(entry);
u32 word;
/*
@@ -437,7 +454,8 @@ void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
rt2x00_desc_read(txwi, 0, &word);
rt2x00_set_field32(&word, TXWI_W0_FRAG,
test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
- rt2x00_set_field32(&word, TXWI_W0_MIMO_PS, 0);
+ rt2x00_set_field32(&word, TXWI_W0_MIMO_PS,
+ test_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags));
rt2x00_set_field32(&word, TXWI_W0_CF_ACK, 0);
rt2x00_set_field32(&word, TXWI_W0_TS,
test_bit(ENTRY_TXD_REQ_TIMESTAMP, &txdesc->flags));
@@ -465,7 +483,7 @@ void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
txdesc->key_idx : 0xff);
rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT,
txdesc->length);
- rt2x00_set_field32(&word, TXWI_W1_PACKETID, txdesc->queue + 1);
+ rt2x00_set_field32(&word, TXWI_W1_PACKETID, txdesc->qid + 1);
rt2x00_desc_write(txwi, 1, word);
/*
@@ -478,7 +496,7 @@ void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
_rt2x00_desc_write(txwi, 2, 0 /* skbdesc->iv[0] */);
_rt2x00_desc_write(txwi, 3, 0 /* skbdesc->iv[1] */);
}
-EXPORT_SYMBOL_GPL(rt2800_write_txwi);
+EXPORT_SYMBOL_GPL(rt2800_write_tx_data);
static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxwi_w2)
{
@@ -490,7 +508,7 @@ static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxwi_w2)
u8 offset1;
u8 offset2;
- if (rt2x00dev->rx_status.band == IEEE80211_BAND_2GHZ) {
+ if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom);
offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0);
offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET1);
@@ -569,6 +587,148 @@ void rt2800_process_rxwi(struct queue_entry *entry,
}
EXPORT_SYMBOL_GPL(rt2800_process_rxwi);
+static bool rt2800_txdone_entry_check(struct queue_entry *entry, u32 reg)
+{
+ __le32 *txwi;
+ u32 word;
+ int wcid, ack, pid;
+ int tx_wcid, tx_ack, tx_pid;
+
+ wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID);
+ ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED);
+ pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE);
+
+ /*
+ * This frames has returned with an IO error,
+ * so the status report is not intended for this
+ * frame.
+ */
+ if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags)) {
+ rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
+ return false;
+ }
+
+ /*
+ * Validate if this TX status report is intended for
+ * this entry by comparing the WCID/ACK/PID fields.
+ */
+ txwi = rt2800_drv_get_txwi(entry);
+
+ rt2x00_desc_read(txwi, 1, &word);
+ tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
+ tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK);
+ tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID);
+
+ if ((wcid != tx_wcid) || (ack != tx_ack) || (pid != tx_pid)) {
+ WARNING(entry->queue->rt2x00dev,
+ "TX status report missed for queue %d entry %d\n",
+ entry->queue->qid, entry->entry_idx);
+ rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN);
+ return false;
+ }
+
+ return true;
+}
+
+void rt2800_txdone(struct rt2x00_dev *rt2x00dev)
+{
+ struct data_queue *queue;
+ struct queue_entry *entry;
+ __le32 *txwi;
+ struct txdone_entry_desc txdesc;
+ u32 word;
+ u32 reg;
+ u16 mcs, real_mcs;
+ u8 pid;
+ int i;
+
+ /*
+ * TX_STA_FIFO is a stack of X entries, hence read TX_STA_FIFO
+ * at most X times and also stop processing once the TX_STA_FIFO_VALID
+ * flag is not set anymore.
+ *
+ * The legacy drivers use X=TX_RING_SIZE but state in a comment
+ * that the TX_STA_FIFO stack has a size of 16. We stick to our
+ * tx ring size for now.
+ */
+ for (i = 0; i < TX_ENTRIES; i++) {
+ rt2800_register_read(rt2x00dev, TX_STA_FIFO, &reg);
+ if (!rt2x00_get_field32(reg, TX_STA_FIFO_VALID))
+ break;
+
+ /*
+ * Skip this entry when it contains an invalid
+ * queue identication number.
+ */
+ pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE) - 1;
+ if (pid >= QID_RX)
+ continue;
+
+ queue = rt2x00queue_get_queue(rt2x00dev, pid);
+ if (unlikely(!queue))
+ continue;
+
+ /*
+ * Inside each queue, we process each entry in a chronological
+ * order. We first check that the queue is not empty.
+ */
+ entry = NULL;
+ txwi = NULL;
+ while (!rt2x00queue_empty(queue)) {
+ entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
+ if (rt2800_txdone_entry_check(entry, reg))
+ break;
+ }
+
+ if (!entry || rt2x00queue_empty(queue))
+ break;
+
+
+ /*
+ * Obtain the status about this packet.
+ */
+ txdesc.flags = 0;
+ txwi = rt2800_drv_get_txwi(entry);
+ rt2x00_desc_read(txwi, 0, &word);
+ mcs = rt2x00_get_field32(word, TXWI_W0_MCS);
+ real_mcs = rt2x00_get_field32(reg, TX_STA_FIFO_MCS);
+
+ /*
+ * Ralink has a retry mechanism using a global fallback
+ * table. We setup this fallback table to try the immediate
+ * lower rate for all rates. In the TX_STA_FIFO, the MCS field
+ * always contains the MCS used for the last transmission, be
+ * it successful or not.
+ */
+ if (rt2x00_get_field32(reg, TX_STA_FIFO_TX_SUCCESS)) {
+ /*
+ * Transmission succeeded. The number of retries is
+ * mcs - real_mcs
+ */
+ __set_bit(TXDONE_SUCCESS, &txdesc.flags);
+ txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0);
+ } else {
+ /*
+ * Transmission failed. The number of retries is
+ * always 7 in this case (for a total number of 8
+ * frames sent).
+ */
+ __set_bit(TXDONE_FAILURE, &txdesc.flags);
+ txdesc.retry = rt2x00dev->long_retry;
+ }
+
+ /*
+ * the frame was retried at least once
+ * -> hw used fallback rates
+ */
+ if (txdesc.retry)
+ __set_bit(TXDONE_FALLBACK, &txdesc.flags);
+
+ rt2x00lib_txdone(entry, &txdesc);
+ }
+}
+EXPORT_SYMBOL_GPL(rt2800_txdone);
+
void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
{
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
@@ -600,7 +760,7 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
/*
* Add the TXWI for the beacon to the skb.
*/
- rt2800_write_txwi((__le32 *)entry->skb->data, txdesc);
+ rt2800_write_tx_data(entry, txdesc);
/*
* Dump beacon to userspace through debugfs.
@@ -975,19 +1135,23 @@ void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
}
if (flags & CONFIG_UPDATE_MAC) {
- reg = le32_to_cpu(conf->mac[1]);
- rt2x00_set_field32(&reg, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff);
- conf->mac[1] = cpu_to_le32(reg);
+ if (!is_zero_ether_addr((const u8 *)conf->mac)) {
+ reg = le32_to_cpu(conf->mac[1]);
+ rt2x00_set_field32(&reg, MAC_ADDR_DW1_UNICAST_TO_ME_MASK, 0xff);
+ conf->mac[1] = cpu_to_le32(reg);
+ }
rt2800_register_multiwrite(rt2x00dev, MAC_ADDR_DW0,
conf->mac, sizeof(conf->mac));
}
if (flags & CONFIG_UPDATE_BSSID) {
- reg = le32_to_cpu(conf->bssid[1]);
- rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_ID_MASK, 3);
- rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 7);
- conf->bssid[1] = cpu_to_le32(reg);
+ if (!is_zero_ether_addr((const u8 *)conf->bssid)) {
+ reg = le32_to_cpu(conf->bssid[1]);
+ rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_ID_MASK, 3);
+ rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 7);
+ conf->bssid[1] = cpu_to_le32(reg);
+ }
rt2800_register_multiwrite(rt2x00dev, MAC_BSSID_DW0,
conf->bssid, sizeof(conf->bssid));
@@ -1120,27 +1284,23 @@ static void rt2800_config_channel_rf2xxx(struct rt2x00_dev *rt2x00dev,
* double meaning, and we should set a 7DBm boost flag.
*/
rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A_7DBM_BOOST,
- (info->tx_power1 >= 0));
+ (info->default_power1 >= 0));
- if (info->tx_power1 < 0)
- info->tx_power1 += 7;
+ if (info->default_power1 < 0)
+ info->default_power1 += 7;
- rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A,
- TXPOWER_A_TO_DEV(info->tx_power1));
+ rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_A, info->default_power1);
rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A_7DBM_BOOST,
- (info->tx_power2 >= 0));
+ (info->default_power2 >= 0));
- if (info->tx_power2 < 0)
- info->tx_power2 += 7;
+ if (info->default_power2 < 0)
+ info->default_power2 += 7;
- rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A,
- TXPOWER_A_TO_DEV(info->tx_power2));
+ rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_A, info->default_power2);
} else {
- rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G,
- TXPOWER_G_TO_DEV(info->tx_power1));
- rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G,
- TXPOWER_G_TO_DEV(info->tx_power2));
+ rt2x00_set_field32(&rf->rf3, RF3_TXPOWER_G, info->default_power1);
+ rt2x00_set_field32(&rf->rf4, RF4_TXPOWER_G, info->default_power2);
}
rt2x00_set_field32(&rf->rf4, RF4_HT40, conf_is_ht40(conf));
@@ -1180,13 +1340,11 @@ static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev,
rt2800_rfcsr_write(rt2x00dev, 6, rfcsr);
rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr);
- rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER,
- TXPOWER_G_TO_DEV(info->tx_power1));
+ rt2x00_set_field8(&rfcsr, RFCSR12_TX_POWER, info->default_power1);
rt2800_rfcsr_write(rt2x00dev, 12, rfcsr);
rt2800_rfcsr_read(rt2x00dev, 13, &rfcsr);
- rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER,
- TXPOWER_G_TO_DEV(info->tx_power2));
+ rt2x00_set_field8(&rfcsr, RFCSR13_TX_POWER, info->default_power2);
rt2800_rfcsr_write(rt2x00dev, 13, rfcsr);
rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr);
@@ -1210,10 +1368,19 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
unsigned int tx_pin;
u8 bbp;
+ if (rf->channel <= 14) {
+ info->default_power1 = TXPOWER_G_TO_DEV(info->default_power1);
+ info->default_power2 = TXPOWER_G_TO_DEV(info->default_power2);
+ } else {
+ info->default_power1 = TXPOWER_A_TO_DEV(info->default_power1);
+ info->default_power2 = TXPOWER_A_TO_DEV(info->default_power2);
+ }
+
if (rt2x00_rf(rt2x00dev, RF2020) ||
rt2x00_rf(rt2x00dev, RF3020) ||
rt2x00_rf(rt2x00dev, RF3021) ||
- rt2x00_rf(rt2x00dev, RF3022))
+ rt2x00_rf(rt2x00dev, RF3022) ||
+ rt2x00_rf(rt2x00dev, RF3052))
rt2800_config_channel_rf3xxx(rt2x00dev, conf, rf, info);
else
rt2800_config_channel_rf2xxx(rt2x00dev, conf, rf, info);
@@ -1536,7 +1703,7 @@ EXPORT_SYMBOL_GPL(rt2800_link_tuner);
/*
* Initialization functions.
*/
-int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
+static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
{
u32 reg;
u16 eeprom;
@@ -1906,7 +2073,6 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
return 0;
}
-EXPORT_SYMBOL_GPL(rt2800_init_registers);
static int rt2800_wait_bbp_rf_ready(struct rt2x00_dev *rt2x00dev)
{
@@ -1949,7 +2115,7 @@ static int rt2800_wait_bbp_ready(struct rt2x00_dev *rt2x00dev)
return -EACCES;
}
-int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
+static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
{
unsigned int i;
u16 eeprom;
@@ -2044,7 +2210,6 @@ int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev)
return 0;
}
-EXPORT_SYMBOL_GPL(rt2800_init_bbp);
static u8 rt2800_init_rx_filter(struct rt2x00_dev *rt2x00dev,
bool bw40, u8 rfcsr24, u8 filter_target)
@@ -2106,7 +2271,7 @@ static u8 rt2800_init_rx_filter(struct rt2x00_dev *rt2x00dev,
return rfcsr24;
}
-int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
+static int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
{
u8 rfcsr;
u8 bbp;
@@ -2360,7 +2525,100 @@ int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev)
return 0;
}
-EXPORT_SYMBOL_GPL(rt2800_init_rfcsr);
+
+int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev)
+{
+ u32 reg;
+ u16 word;
+
+ /*
+ * Initialize all registers.
+ */
+ if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) ||
+ rt2800_init_registers(rt2x00dev) ||
+ rt2800_init_bbp(rt2x00dev) ||
+ rt2800_init_rfcsr(rt2x00dev)))
+ return -EIO;
+
+ /*
+ * Send signal to firmware during boot time.
+ */
+ rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0);
+
+ if (rt2x00_is_usb(rt2x00dev) &&
+ (rt2x00_rt(rt2x00dev, RT3070) ||
+ rt2x00_rt(rt2x00dev, RT3071) ||
+ rt2x00_rt(rt2x00dev, RT3572))) {
+ udelay(200);
+ rt2800_mcu_request(rt2x00dev, MCU_CURRENT, 0, 0, 0);
+ udelay(10);
+ }
+
+ /*
+ * Enable RX.
+ */
+ rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+ rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 1);
+ rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 0);
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+
+ udelay(50);
+
+ rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 1);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 1);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 2);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
+ rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
+
+ rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+ rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 1);
+ rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 1);
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+
+ /*
+ * Initialize LED control
+ */
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_LED1, &word);
+ rt2800_mcu_request(rt2x00dev, MCU_LED_1, 0xff,
+ word & 0xff, (word >> 8) & 0xff);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_LED2, &word);
+ rt2800_mcu_request(rt2x00dev, MCU_LED_2, 0xff,
+ word & 0xff, (word >> 8) & 0xff);
+
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_LED3, &word);
+ rt2800_mcu_request(rt2x00dev, MCU_LED_3, 0xff,
+ word & 0xff, (word >> 8) & 0xff);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt2800_enable_radio);
+
+void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev)
+{
+ u32 reg;
+
+ rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_DMA_BUSY, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_RX_DMA_BUSY, 0);
+ rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
+ rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
+
+ /* Wait for DMA, ignore error */
+ rt2800_wait_wpdma_ready(rt2x00dev);
+
+ rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
+ rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 0);
+ rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 0);
+ rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
+
+ rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0);
+ rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0);
+}
+EXPORT_SYMBOL_GPL(rt2800_disable_radio);
int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev)
{
@@ -2516,6 +2774,13 @@ int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
default_lna_gain);
rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word);
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_MAX_TX_POWER, &word);
+ if (rt2x00_get_field16(word, EEPROM_MAX_TX_POWER_24GHZ) == 0xff)
+ rt2x00_set_field16(&word, EEPROM_MAX_TX_POWER_24GHZ, MAX_G_TXPOWER);
+ if (rt2x00_get_field16(word, EEPROM_MAX_TX_POWER_5GHZ) == 0xff)
+ rt2x00_set_field16(&word, EEPROM_MAX_TX_POWER_5GHZ, MAX_A_TXPOWER);
+ rt2x00_eeprom_write(rt2x00dev, EEPROM_MAX_TX_POWER, word);
+
return 0;
}
EXPORT_SYMBOL_GPL(rt2800_validate_eeprom);
@@ -2755,9 +3020,10 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
{
struct hw_mode_spec *spec = &rt2x00dev->spec;
struct channel_info *info;
- char *tx_power1;
- char *tx_power2;
+ char *default_power1;
+ char *default_power2;
unsigned int i;
+ unsigned short max_power;
u16 eeprom;
/*
@@ -2865,27 +3131,32 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
/*
* Create channel information array
*/
- info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL);
+ info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
spec->channels_info = info;
- tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1);
- tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2);
+ rt2x00_eeprom_read(rt2x00dev, EEPROM_MAX_TX_POWER, &eeprom);
+ max_power = rt2x00_get_field16(eeprom, EEPROM_MAX_TX_POWER_24GHZ);
+ default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1);
+ default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2);
for (i = 0; i < 14; i++) {
- info[i].tx_power1 = TXPOWER_G_FROM_DEV(tx_power1[i]);
- info[i].tx_power2 = TXPOWER_G_FROM_DEV(tx_power2[i]);
+ info[i].max_power = max_power;
+ info[i].default_power1 = TXPOWER_G_FROM_DEV(default_power1[i]);
+ info[i].default_power2 = TXPOWER_G_FROM_DEV(default_power2[i]);
}
if (spec->num_channels > 14) {
- tx_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1);
- tx_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2);
+ max_power = rt2x00_get_field16(eeprom, EEPROM_MAX_TX_POWER_5GHZ);
+ default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1);
+ default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2);
for (i = 14; i < spec->num_channels; i++) {
- info[i].tx_power1 = TXPOWER_A_FROM_DEV(tx_power1[i]);
- info[i].tx_power2 = TXPOWER_A_FROM_DEV(tx_power2[i]);
+ info[i].max_power = max_power;
+ info[i].default_power1 = TXPOWER_A_FROM_DEV(default_power1[i]);
+ info[i].default_power2 = TXPOWER_A_FROM_DEV(default_power2[i]);
}
}
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h
index 091641e3c5e2..986229c06c19 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.h
+++ b/drivers/net/wireless/rt2x00/rt2800lib.h
@@ -1,4 +1,6 @@
/*
+ Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
+ Copyright (C) 2010 Ivo van Doorn <IvDoorn@gmail.com>
Copyright (C) 2009 Bartlomiej Zolnierkiewicz
This program is free software; you can redistribute it and/or modify
@@ -44,6 +46,7 @@ struct rt2800_ops {
int (*drv_write_firmware)(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len);
int (*drv_init_registers)(struct rt2x00_dev *rt2x00dev);
+ __le32 *(*drv_get_txwi)(struct queue_entry *entry);
};
static inline void rt2800_register_read(struct rt2x00_dev *rt2x00dev,
@@ -126,18 +129,31 @@ static inline int rt2800_drv_init_registers(struct rt2x00_dev *rt2x00dev)
return rt2800ops->drv_init_registers(rt2x00dev);
}
+static inline __le32 *rt2800_drv_get_txwi(struct queue_entry *entry)
+{
+ const struct rt2800_ops *rt2800ops = entry->queue->rt2x00dev->ops->drv;
+
+ return rt2800ops->drv_get_txwi(entry);
+}
+
void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
const u8 command, const u8 token,
const u8 arg0, const u8 arg1);
+int rt2800_wait_csr_ready(struct rt2x00_dev *rt2x00dev);
+int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev);
+
int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len);
int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len);
-void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc);
+void rt2800_write_tx_data(struct queue_entry *entry,
+ struct txentry_desc *txdesc);
void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *txdesc);
+void rt2800_txdone(struct rt2x00_dev *rt2x00dev);
+
void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc);
extern const struct rt2x00debug rt2800_rt2x00debug;
@@ -163,10 +179,8 @@ void rt2800_reset_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual);
void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual,
const u32 count);
-int rt2800_init_registers(struct rt2x00_dev *rt2x00dev);
-int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev);
-int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev);
-int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev);
+int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev);
+void rt2800_disable_radio(struct rt2x00_dev *rt2x00dev);
int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev);
void rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev);
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index 39b3846fa340..2bcb1507e3ac 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com>
Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com>
@@ -196,8 +196,6 @@ static int rt2800pci_write_firmware(struct rt2x00_dev *rt2x00dev,
{
u32 reg;
- rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000);
-
/*
* enable Host program ram write selection
*/
@@ -399,78 +397,18 @@ static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev)
static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev)
{
- u32 reg;
- u16 word;
-
- /*
- * Initialize all registers.
- */
if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) ||
- rt2800pci_init_queues(rt2x00dev) ||
- rt2800_init_registers(rt2x00dev) ||
- rt2800_wait_wpdma_ready(rt2x00dev) ||
- rt2800_init_bbp(rt2x00dev) ||
- rt2800_init_rfcsr(rt2x00dev)))
+ rt2800pci_init_queues(rt2x00dev)))
return -EIO;
- /*
- * Send signal to firmware during boot time.
- */
- rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0);
-
- /*
- * Enable RX.
- */
- rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
- rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 1);
- rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 0);
- rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
-
- rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 1);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 1);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_WP_DMA_BURST_SIZE, 2);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
- rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
-
- rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
- rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 1);
- rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 1);
- rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
-
- /*
- * Initialize LED control
- */
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LED1, &word);
- rt2800_mcu_request(rt2x00dev, MCU_LED_1, 0xff,
- word & 0xff, (word >> 8) & 0xff);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LED2, &word);
- rt2800_mcu_request(rt2x00dev, MCU_LED_2, 0xff,
- word & 0xff, (word >> 8) & 0xff);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LED3, &word);
- rt2800_mcu_request(rt2x00dev, MCU_LED_3, 0xff,
- word & 0xff, (word >> 8) & 0xff);
-
- return 0;
+ return rt2800_enable_radio(rt2x00dev);
}
static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev)
{
u32 reg;
- rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_DMA_BUSY, 0);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_RX_DMA_BUSY, 0);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
- rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
-
- rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0);
- rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0);
- rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0);
+ rt2800_disable_radio(rt2x00dev);
rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001280);
@@ -486,9 +424,6 @@ static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev)
rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f);
rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00);
-
- /* Wait for DMA, ignore error */
- rt2800_wait_wpdma_ready(rt2x00dev);
}
static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev,
@@ -566,21 +501,16 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev,
/*
* TX descriptor initialization
*/
-static void rt2800pci_write_tx_data(struct queue_entry* entry,
- struct txentry_desc *txdesc)
+static __le32 *rt2800pci_get_txwi(struct queue_entry *entry)
{
- __le32 *txwi = (__le32 *) entry->skb->data;
-
- rt2800_write_txwi(txwi, txdesc);
+ return (__le32 *) entry->skb->data;
}
-
-static void rt2800pci_write_tx_desc(struct rt2x00_dev *rt2x00dev,
- struct sk_buff *skb,
+static void rt2800pci_write_tx_desc(struct queue_entry *entry,
struct txentry_desc *txdesc)
{
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
- struct queue_entry_priv_pci *entry_priv = skbdesc->entry->priv_data;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+ struct queue_entry_priv_pci *entry_priv = entry->priv_data;
__le32 *txd = entry_priv->desc;
u32 word;
@@ -600,7 +530,7 @@ static void rt2800pci_write_tx_desc(struct rt2x00_dev *rt2x00dev,
rt2x00_desc_write(txd, 0, word);
rt2x00_desc_read(txd, 1, &word);
- rt2x00_set_field32(&word, TXD_W1_SD_LEN1, skb->len);
+ rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len);
rt2x00_set_field32(&word, TXD_W1_LAST_SEC1,
!test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags));
rt2x00_set_field32(&word, TXD_W1_BURST,
@@ -631,41 +561,35 @@ static void rt2800pci_write_tx_desc(struct rt2x00_dev *rt2x00dev,
/*
* TX data initialization
*/
-static void rt2800pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid queue_idx)
+static void rt2800pci_kick_tx_queue(struct data_queue *queue)
{
- struct data_queue *queue;
- unsigned int idx, qidx = 0;
-
- if (queue_idx > QID_HCCA && queue_idx != QID_MGMT)
- return;
-
- queue = rt2x00queue_get_queue(rt2x00dev, queue_idx);
- idx = queue->index[Q_INDEX];
+ struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
+ struct queue_entry *entry = rt2x00queue_get_entry(queue, Q_INDEX);
+ unsigned int qidx = 0;
- if (queue_idx == QID_MGMT)
+ if (queue->qid == QID_MGMT)
qidx = 5;
else
- qidx = queue_idx;
+ qidx = queue->qid;
- rt2800_register_write(rt2x00dev, TX_CTX_IDX(qidx), idx);
+ rt2800_register_write(rt2x00dev, TX_CTX_IDX(qidx), entry->entry_idx);
}
-static void rt2800pci_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid qid)
+static void rt2800pci_kill_tx_queue(struct data_queue *queue)
{
+ struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
u32 reg;
- if (qid == QID_BEACON) {
+ if (queue->qid == QID_BEACON) {
rt2800_register_write(rt2x00dev, BCN_TIME_CFG, 0);
return;
}
rt2800_register_read(rt2x00dev, WPDMA_RST_IDX, &reg);
- rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX0, (qid == QID_AC_BE));
- rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX1, (qid == QID_AC_BK));
- rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX2, (qid == QID_AC_VI));
- rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX3, (qid == QID_AC_VO));
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX0, (queue->qid == QID_AC_BE));
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX1, (queue->qid == QID_AC_BK));
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX2, (queue->qid == QID_AC_VI));
+ rt2x00_set_field32(&reg, WPDMA_RST_IDX_DTX_IDX3, (queue->qid == QID_AC_VO));
rt2800_register_write(rt2x00dev, WPDMA_RST_IDX, reg);
}
@@ -728,110 +652,6 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry,
/*
* Interrupt functions.
*/
-static void rt2800pci_txdone(struct rt2x00_dev *rt2x00dev)
-{
- struct data_queue *queue;
- struct queue_entry *entry;
- __le32 *txwi;
- struct txdone_entry_desc txdesc;
- u32 word;
- u32 reg;
- int wcid, ack, pid, tx_wcid, tx_ack, tx_pid;
- u16 mcs, real_mcs;
- int i;
-
- /*
- * TX_STA_FIFO is a stack of X entries, hence read TX_STA_FIFO
- * at most X times and also stop processing once the TX_STA_FIFO_VALID
- * flag is not set anymore.
- *
- * The legacy drivers use X=TX_RING_SIZE but state in a comment
- * that the TX_STA_FIFO stack has a size of 16. We stick to our
- * tx ring size for now.
- */
- for (i = 0; i < TX_ENTRIES; i++) {
- rt2800_register_read(rt2x00dev, TX_STA_FIFO, &reg);
- if (!rt2x00_get_field32(reg, TX_STA_FIFO_VALID))
- break;
-
- wcid = rt2x00_get_field32(reg, TX_STA_FIFO_WCID);
- ack = rt2x00_get_field32(reg, TX_STA_FIFO_TX_ACK_REQUIRED);
- pid = rt2x00_get_field32(reg, TX_STA_FIFO_PID_TYPE);
-
- /*
- * Skip this entry when it contains an invalid
- * queue identication number.
- */
- if (pid <= 0 || pid > QID_RX)
- continue;
-
- queue = rt2x00queue_get_queue(rt2x00dev, pid - 1);
- if (unlikely(!queue))
- continue;
-
- /*
- * Inside each queue, we process each entry in a chronological
- * order. We first check that the queue is not empty.
- */
- if (rt2x00queue_empty(queue))
- continue;
- entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
-
- /* Check if we got a match by looking at WCID/ACK/PID
- * fields */
- txwi = (__le32 *) entry->skb->data;
-
- rt2x00_desc_read(txwi, 1, &word);
- tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID);
- tx_ack = rt2x00_get_field32(word, TXWI_W1_ACK);
- tx_pid = rt2x00_get_field32(word, TXWI_W1_PACKETID);
-
- if ((wcid != tx_wcid) || (ack != tx_ack) || (pid != tx_pid))
- WARNING(rt2x00dev, "invalid TX_STA_FIFO content\n");
-
- /*
- * Obtain the status about this packet.
- */
- txdesc.flags = 0;
- rt2x00_desc_read(txwi, 0, &word);
- mcs = rt2x00_get_field32(word, TXWI_W0_MCS);
- real_mcs = rt2x00_get_field32(reg, TX_STA_FIFO_MCS);
-
- /*
- * Ralink has a retry mechanism using a global fallback
- * table. We setup this fallback table to try the immediate
- * lower rate for all rates. In the TX_STA_FIFO, the MCS field
- * always contains the MCS used for the last transmission, be
- * it successful or not.
- */
- if (rt2x00_get_field32(reg, TX_STA_FIFO_TX_SUCCESS)) {
- /*
- * Transmission succeeded. The number of retries is
- * mcs - real_mcs
- */
- __set_bit(TXDONE_SUCCESS, &txdesc.flags);
- txdesc.retry = ((mcs > real_mcs) ? mcs - real_mcs : 0);
- } else {
- /*
- * Transmission failed. The number of retries is
- * always 7 in this case (for a total number of 8
- * frames sent).
- */
- __set_bit(TXDONE_FAILURE, &txdesc.flags);
- txdesc.retry = 7;
- }
-
- /*
- * the frame was retried at least once
- * -> hw used fallback rates
- */
- if (txdesc.retry)
- __set_bit(TXDONE_FALLBACK, &txdesc.flags);
-
- rt2x00lib_txdone(entry, &txdesc);
- }
-}
-
static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev)
{
struct ieee80211_conf conf = { .flags = 0 };
@@ -867,7 +687,7 @@ static irqreturn_t rt2800pci_interrupt_thread(int irq, void *dev_instance)
* 4 - Tx done interrupt.
*/
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS))
- rt2800pci_txdone(rt2x00dev);
+ rt2800_txdone(rt2x00dev);
/*
* 5 - Auto wakeup interrupt.
@@ -1011,6 +831,7 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = {
.regbusy_read = rt2x00pci_regbusy_read,
.drv_write_firmware = rt2800pci_write_firmware,
.drv_init_registers = rt2800pci_init_registers,
+ .drv_get_txwi = rt2800pci_get_txwi,
};
static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
@@ -1030,7 +851,7 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
.reset_tuner = rt2800_reset_tuner,
.link_tuner = rt2800_link_tuner,
.write_tx_desc = rt2800pci_write_tx_desc,
- .write_tx_data = rt2800pci_write_tx_data,
+ .write_tx_data = rt2800_write_tx_data,
.write_beacon = rt2800_write_beacon,
.kick_tx_queue = rt2800pci_kick_tx_queue,
.kill_tx_queue = rt2800pci_kill_tx_queue,
diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 5a2dfe87c6b6..3dff56ec195a 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -1,5 +1,6 @@
/*
- Copyright (C) 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
+ Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de>
Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com>
@@ -100,19 +101,6 @@ static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev,
msleep(10);
rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
- /*
- * Send signal to firmware during boot time.
- */
- rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0);
-
- if (rt2x00_rt(rt2x00dev, RT3070) ||
- rt2x00_rt(rt2x00dev, RT3071) ||
- rt2x00_rt(rt2x00dev, RT3572)) {
- udelay(200);
- rt2800_mcu_request(rt2x00dev, MCU_CURRENT, 0, 0, 0);
- udelay(10);
- }
-
return 0;
}
@@ -134,26 +122,18 @@ static void rt2800usb_toggle_rx(struct rt2x00_dev *rt2x00dev,
static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev)
{
u32 reg;
- int i;
/*
* Wait until BBP and RF are ready.
*/
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
- if (reg && reg != ~0)
- break;
- msleep(1);
- }
-
- if (i == REGISTER_BUSY_COUNT) {
- ERROR(rt2x00dev, "Unstable hardware.\n");
+ if (rt2800_wait_csr_ready(rt2x00dev))
return -EBUSY;
- }
rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, &reg);
rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, reg & ~0x00002000);
+ rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003);
+
rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_CSR, 1);
rt2x00_set_field32(&reg, MAC_SYS_CTRL_RESET_BBP, 1);
@@ -172,30 +152,10 @@ static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev)
static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev)
{
u32 reg;
- u16 word;
- /*
- * Initialize all registers.
- */
- if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) ||
- rt2800_init_registers(rt2x00dev) ||
- rt2800_init_bbp(rt2x00dev) ||
- rt2800_init_rfcsr(rt2x00dev)))
+ if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev)))
return -EIO;
- rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
- rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 1);
- rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
-
- udelay(50);
-
- rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 1);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 1);
- rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
-
-
rt2800_register_read(rt2x00dev, USB_DMA_CFG, &reg);
rt2x00_set_field32(&reg, USB_DMA_CFG_PHY_CLEAR, 0);
rt2x00_set_field32(&reg, USB_DMA_CFG_RX_BULK_AGG_EN, 0);
@@ -210,45 +170,12 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev)
rt2x00_set_field32(&reg, USB_DMA_CFG_TX_BULK_EN, 1);
rt2800_register_write(rt2x00dev, USB_DMA_CFG, reg);
- rt2800_register_read(rt2x00dev, MAC_SYS_CTRL, &reg);
- rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_TX, 1);
- rt2x00_set_field32(&reg, MAC_SYS_CTRL_ENABLE_RX, 1);
- rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg);
-
- /*
- * Initialize LED control
- */
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LED1, &word);
- rt2800_mcu_request(rt2x00dev, MCU_LED_1, 0xff,
- word & 0xff, (word >> 8) & 0xff);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LED2, &word);
- rt2800_mcu_request(rt2x00dev, MCU_LED_2, 0xff,
- word & 0xff, (word >> 8) & 0xff);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LED3, &word);
- rt2800_mcu_request(rt2x00dev, MCU_LED_3, 0xff,
- word & 0xff, (word >> 8) & 0xff);
-
- return 0;
+ return rt2800_enable_radio(rt2x00dev);
}
static void rt2800usb_disable_radio(struct rt2x00_dev *rt2x00dev)
{
- u32 reg;
-
- rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
- rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
- rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
-
- rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0);
- rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0);
- rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0);
-
- /* Wait for DMA, ignore error */
- rt2800_wait_wpdma_ready(rt2x00dev);
-
+ rt2800_disable_radio(rt2x00dev);
rt2x00usb_disable_radio(rt2x00dev);
}
@@ -320,21 +247,19 @@ static int rt2800usb_set_device_state(struct rt2x00_dev *rt2x00dev,
/*
* TX descriptor initialization
*/
-static void rt2800usb_write_tx_data(struct queue_entry* entry,
- struct txentry_desc *txdesc)
+static __le32 *rt2800usb_get_txwi(struct queue_entry *entry)
{
- __le32 *txwi = (__le32 *) (entry->skb->data + TXINFO_DESC_SIZE);
-
- rt2800_write_txwi(txwi, txdesc);
+ if (entry->queue->qid == QID_BEACON)
+ return (__le32 *) (entry->skb->data);
+ else
+ return (__le32 *) (entry->skb->data + TXINFO_DESC_SIZE);
}
-
-static void rt2800usb_write_tx_desc(struct rt2x00_dev *rt2x00dev,
- struct sk_buff *skb,
+static void rt2800usb_write_tx_desc(struct queue_entry *entry,
struct txentry_desc *txdesc)
{
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
- __le32 *txi = (__le32 *) skb->data;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+ __le32 *txi = (__le32 *) entry->skb->data;
u32 word;
/*
@@ -342,7 +267,7 @@ static void rt2800usb_write_tx_desc(struct rt2x00_dev *rt2x00dev,
*/
rt2x00_desc_read(txi, 0, &word);
rt2x00_set_field32(&word, TXINFO_W0_USB_DMA_TX_PKT_LEN,
- skb->len - TXINFO_DESC_SIZE);
+ entry->skb->len - TXINFO_DESC_SIZE);
rt2x00_set_field32(&word, TXINFO_W0_WIV,
!test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags));
rt2x00_set_field32(&word, TXINFO_W0_QSEL, 2);
@@ -379,6 +304,46 @@ static int rt2800usb_get_tx_data_len(struct queue_entry *entry)
}
/*
+ * TX control handlers
+ */
+static void rt2800usb_work_txdone(struct work_struct *work)
+{
+ struct rt2x00_dev *rt2x00dev =
+ container_of(work, struct rt2x00_dev, txdone_work);
+ struct data_queue *queue;
+ struct queue_entry *entry;
+
+ rt2800_txdone(rt2x00dev);
+
+ /*
+ * Process any trailing TX status reports for IO failures,
+ * we loop until we find the first non-IO error entry. This
+ * can either be a frame which is free, is being uploaded,
+ * or has completed the upload but didn't have an entry
+ * in the TX_STAT_FIFO register yet.
+ */
+ tx_queue_for_each(rt2x00dev, queue) {
+ while (!rt2x00queue_empty(queue)) {
+ entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
+
+ if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags) ||
+ !test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
+ break;
+
+ rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
+ }
+ }
+}
+
+static void rt2800usb_kill_tx_queue(struct data_queue *queue)
+{
+ if (queue->qid == QID_BEACON)
+ rt2x00usb_register_write(queue->rt2x00dev, BCN_TIME_CFG, 0);
+
+ rt2x00usb_kill_tx_queue(queue);
+}
+
+/*
* RX control handlers
*/
static void rt2800usb_fill_rxdone(struct queue_entry *entry,
@@ -514,6 +479,11 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
*/
rt2x00dev->rssi_offset = DEFAULT_RSSI_OFFSET;
+ /*
+ * Overwrite TX done handler
+ */
+ PREPARE_WORK(&rt2x00dev->txdone_work, rt2800usb_work_txdone);
+
return 0;
}
@@ -549,6 +519,7 @@ static const struct rt2800_ops rt2800usb_rt2800_ops = {
.regbusy_read = rt2x00usb_regbusy_read,
.drv_write_firmware = rt2800usb_write_firmware,
.drv_init_registers = rt2800usb_init_registers,
+ .drv_get_txwi = rt2800usb_get_txwi,
};
static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
@@ -566,11 +537,11 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
.link_tuner = rt2800_link_tuner,
.watchdog = rt2x00usb_watchdog,
.write_tx_desc = rt2800usb_write_tx_desc,
- .write_tx_data = rt2800usb_write_tx_data,
+ .write_tx_data = rt2800_write_tx_data,
.write_beacon = rt2800_write_beacon,
.get_tx_data_len = rt2800usb_get_tx_data_len,
.kick_tx_queue = rt2x00usb_kick_tx_queue,
- .kill_tx_queue = rt2x00usb_kill_tx_queue,
+ .kill_tx_queue = rt2800usb_kill_tx_queue,
.fill_rxdone = rt2800usb_fill_rxdone,
.config_shared_key = rt2800_config_shared_key,
.config_pairwise_key = rt2800_config_pairwise_key,
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index c21af38cc5af..0ae942cb66df 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -1,5 +1,6 @@
/*
- Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
+ Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
<http://rt2x00.serialmonkey.com>
@@ -212,8 +213,9 @@ struct channel_info {
unsigned int flags;
#define GEOGRAPHY_ALLOWED 0x00000001
- short tx_power1;
- short tx_power2;
+ short max_power;
+ short default_power1;
+ short default_power2;
};
/*
@@ -558,18 +560,15 @@ struct rt2x00lib_ops {
/*
* TX control handlers
*/
- void (*write_tx_desc) (struct rt2x00_dev *rt2x00dev,
- struct sk_buff *skb,
+ void (*write_tx_desc) (struct queue_entry *entry,
struct txentry_desc *txdesc);
void (*write_tx_data) (struct queue_entry *entry,
struct txentry_desc *txdesc);
void (*write_beacon) (struct queue_entry *entry,
struct txentry_desc *txdesc);
int (*get_tx_data_len) (struct queue_entry *entry);
- void (*kick_tx_queue) (struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid queue);
- void (*kill_tx_queue) (struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid queue);
+ void (*kick_tx_queue) (struct data_queue *queue);
+ void (*kill_tx_queue) (struct data_queue *queue);
/*
* RX control handlers
@@ -698,6 +697,7 @@ struct rt2x00_dev {
struct ieee80211_hw *hw;
struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS];
enum ieee80211_band curr_band;
+ int curr_freq;
/*
* If enabled, the debugfs interface structures
@@ -850,11 +850,6 @@ struct rt2x00_dev {
struct ieee80211_low_level_stats low_level_stats;
/*
- * RX configuration information.
- */
- struct ieee80211_rx_status rx_status;
-
- /*
* Scheduled work.
* NOTE: intf_work will use ieee80211_iterate_active_interfaces()
* which means it cannot be placed on the hw->workqueue
@@ -862,6 +857,12 @@ struct rt2x00_dev {
*/
struct work_struct intf_work;
+ /**
+ * Scheduled work for TX/RX done handling (USB devices)
+ */
+ struct work_struct rxdone_work;
+ struct work_struct txdone_work;
+
/*
* Data queue arrays for RX, TX and Beacon.
* The Beacon array also contains the Atim queue
@@ -1069,8 +1070,10 @@ static inline void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev,
*/
void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev);
void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev);
+void rt2x00lib_dmadone(struct queue_entry *entry);
void rt2x00lib_txdone(struct queue_entry *entry,
struct txdone_entry_desc *txdesc);
+void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status);
void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
struct queue_entry *entry);
diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c
index 953dc4f2c6af..34f34fa7f53a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00config.c
+++ b/drivers/net/wireless/rt2x00/rt2x00config.c
@@ -126,11 +126,6 @@ void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
* ANTENNA_SW_DIVERSITY state to the driver.
* If that happens, fallback to hardware defaults,
* or our own default.
- * If diversity handling is active for a particular antenna,
- * we shouldn't overwrite that antenna.
- * The calls to rt2x00lib_config_antenna_check()
- * might have caused that we restore back to the already
- * active setting. If that has happened we can quit.
*/
if (!(ant->flags & ANTENNA_RX_DIVERSITY))
config.rx = rt2x00lib_config_antenna_check(config.rx, def->rx);
@@ -142,9 +137,6 @@ void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
else
config.tx = active->tx;
- if (config.rx == active->rx && config.tx == active->tx)
- return;
-
/*
* Antenna setup changes require the RX to be disabled,
* else the changes will be ignored by the device.
@@ -209,10 +201,8 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
rt2x00link_reset_tuner(rt2x00dev, false);
rt2x00dev->curr_band = conf->channel->band;
+ rt2x00dev->curr_freq = conf->channel->center_freq;
rt2x00dev->tx_power = conf->power_level;
rt2x00dev->short_retry = conf->short_frame_max_tx_count;
rt2x00dev->long_retry = conf->long_frame_max_tx_count;
-
- rt2x00dev->rx_status.band = conf->channel->band;
- rt2x00dev->rx_status.freq = conf->channel->center_freq;
}
diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c
index 583dacd8d241..5e9074bf2b8e 100644
--- a/drivers/net/wireless/rt2x00/rt2x00crypto.c
+++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c
@@ -31,15 +31,14 @@
enum cipher rt2x00crypto_key_to_cipher(struct ieee80211_key_conf *key)
{
- switch (key->alg) {
- case ALG_WEP:
- if (key->keylen == WLAN_KEY_LEN_WEP40)
- return CIPHER_WEP64;
- else
- return CIPHER_WEP128;
- case ALG_TKIP:
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ return CIPHER_WEP64;
+ case WLAN_CIPHER_SUITE_WEP104:
+ return CIPHER_WEP128;
+ case WLAN_CIPHER_SUITE_TKIP:
return CIPHER_TKIP;
- case ALG_CCMP:
+ case WLAN_CIPHER_SUITE_CCMP:
return CIPHER_AES;
default:
return CIPHER_NONE;
@@ -95,7 +94,7 @@ unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev,
overhead += key->iv_len;
if (!(key->flags & IEEE80211_KEY_FLAG_GENERATE_MMIC)) {
- if (key->alg == ALG_TKIP)
+ if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
overhead += 8;
}
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c
index b0498e7e7aae..b8cf45c4e9f5 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.c
+++ b/drivers/net/wireless/rt2x00/rt2x00debug.c
@@ -333,12 +333,12 @@ static ssize_t rt2x00debug_read_queue_stats(struct file *file,
if (*offset)
return 0;
- data = kzalloc(lines * MAX_LINE_LENGTH, GFP_KERNEL);
+ data = kcalloc(lines, MAX_LINE_LENGTH, GFP_KERNEL);
if (!data)
return -ENOMEM;
temp = data +
- sprintf(data, "qid\tcount\tlimit\tlength\tindex\tdone\tcrypto\n");
+ sprintf(data, "qid\tcount\tlimit\tlength\tindex\tdma done\tdone\n");
queue_for_each(intf->rt2x00dev, queue) {
spin_lock_irqsave(&queue->lock, irqflags);
@@ -346,8 +346,8 @@ static ssize_t rt2x00debug_read_queue_stats(struct file *file,
temp += sprintf(temp, "%d\t%d\t%d\t%d\t%d\t%d\t%d\n", queue->qid,
queue->count, queue->limit, queue->length,
queue->index[Q_INDEX],
- queue->index[Q_INDEX_DONE],
- queue->index[Q_INDEX_CRYPTO]);
+ queue->index[Q_INDEX_DMA_DONE],
+ queue->index[Q_INDEX_DONE]);
spin_unlock_irqrestore(&queue->lock, irqflags);
}
@@ -481,6 +481,9 @@ static ssize_t rt2x00debug_write_##__name(struct file *file, \
if (index >= debug->__name.word_count) \
return -EINVAL; \
\
+ if (length > sizeof(line)) \
+ return -EINVAL; \
+ \
if (copy_from_user(line, buf, length)) \
return -EFAULT; \
\
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 585e8166f22a..053fdd3bd720 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -1,5 +1,6 @@
/*
- Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
+ Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -250,6 +251,12 @@ void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev)
}
EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt);
+void rt2x00lib_dmadone(struct queue_entry *entry)
+{
+ rt2x00queue_index_inc(entry->queue, Q_INDEX_DMA_DONE);
+}
+EXPORT_SYMBOL_GPL(rt2x00lib_dmadone);
+
void rt2x00lib_txdone(struct queue_entry *entry,
struct txdone_entry_desc *txdesc)
{
@@ -383,15 +390,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
* send the status report back.
*/
if (!(skbdesc_flags & SKBDESC_NOT_MAC80211))
- /*
- * Only PCI and SOC devices process the tx status in process
- * context. Hence use ieee80211_tx_status for PCI and SOC
- * devices and stick to ieee80211_tx_status_irqsafe for USB.
- */
- if (rt2x00_is_usb(rt2x00dev))
- ieee80211_tx_status_irqsafe(rt2x00dev->hw, entry->skb);
- else
- ieee80211_tx_status(rt2x00dev->hw, entry->skb);
+ ieee80211_tx_status(rt2x00dev->hw, entry->skb);
else
dev_kfree_skb_any(entry->skb);
@@ -403,7 +402,6 @@ void rt2x00lib_txdone(struct queue_entry *entry,
rt2x00dev->ops->lib->clear_entry(entry);
- clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags);
rt2x00queue_index_inc(entry->queue, Q_INDEX_DONE);
/*
@@ -416,6 +414,18 @@ void rt2x00lib_txdone(struct queue_entry *entry,
}
EXPORT_SYMBOL_GPL(rt2x00lib_txdone);
+void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status)
+{
+ struct txdone_entry_desc txdesc;
+
+ txdesc.flags = 0;
+ __set_bit(status, &txdesc.flags);
+ txdesc.retry = 0;
+
+ rt2x00lib_txdone(entry, &txdesc);
+}
+EXPORT_SYMBOL_GPL(rt2x00lib_txdone_noinfo);
+
static int rt2x00lib_rxdone_read_signal(struct rt2x00_dev *rt2x00dev,
struct rxdone_entry_desc *rxdesc)
{
@@ -460,9 +470,13 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
{
struct rxdone_entry_desc rxdesc;
struct sk_buff *skb;
- struct ieee80211_rx_status *rx_status = &rt2x00dev->rx_status;
+ struct ieee80211_rx_status *rx_status;
unsigned int header_length;
int rate_idx;
+
+ if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
+ goto submit_entry;
+
/*
* Allocate a new sk_buffer. If no new buffer available, drop the
* received frame and reuse the existing buffer.
@@ -527,39 +541,32 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
*/
rt2x00link_update_stats(rt2x00dev, entry->skb, &rxdesc);
rt2x00debug_update_crypto(rt2x00dev, &rxdesc);
+ rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb);
+ /*
+ * Initialize RX status information, and send frame
+ * to mac80211.
+ */
+ rx_status = IEEE80211_SKB_RXCB(entry->skb);
rx_status->mactime = rxdesc.timestamp;
+ rx_status->band = rt2x00dev->curr_band;
+ rx_status->freq = rt2x00dev->curr_freq;
rx_status->rate_idx = rate_idx;
rx_status->signal = rxdesc.rssi;
rx_status->flag = rxdesc.flags;
rx_status->antenna = rt2x00dev->link.ant.active.rx;
- /*
- * Send frame to mac80211 & debugfs.
- * mac80211 will clean up the skb structure.
- */
- rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb);
- memcpy(IEEE80211_SKB_RXCB(entry->skb), rx_status, sizeof(*rx_status));
-
- /*
- * Currently only PCI and SOC devices handle rx interrupts in process
- * context. Hence, use ieee80211_rx_irqsafe for USB and ieee80211_rx_ni
- * for PCI and SOC devices.
- */
- if (rt2x00_is_usb(rt2x00dev))
- ieee80211_rx_irqsafe(rt2x00dev->hw, entry->skb);
- else
- ieee80211_rx_ni(rt2x00dev->hw, entry->skb);
+ ieee80211_rx_ni(rt2x00dev->hw, entry->skb);
/*
* Replace the skb with the freshly allocated one.
*/
entry->skb = skb;
- entry->flags = 0;
+submit_entry:
rt2x00dev->ops->lib->clear_entry(entry);
-
rt2x00queue_index_inc(entry->queue, Q_INDEX);
+ rt2x00queue_index_inc(entry->queue, Q_INDEX_DONE);
}
EXPORT_SYMBOL_GPL(rt2x00lib_rxdone);
@@ -710,7 +717,7 @@ static int rt2x00lib_probe_hw_modes(struct rt2x00_dev *rt2x00dev,
for (i = 0; i < spec->num_channels; i++) {
rt2x00lib_channel(&channels[i],
spec->channels[i].channel,
- spec->channels_info[i].tx_power1, i);
+ spec->channels_info[i].max_power, i);
}
/*
@@ -1017,6 +1024,8 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
* Stop all work.
*/
cancel_work_sync(&rt2x00dev->intf_work);
+ cancel_work_sync(&rt2x00dev->rxdone_work);
+ cancel_work_sync(&rt2x00dev->txdone_work);
/*
* Uninitialize device.
diff --git a/drivers/net/wireless/rt2x00/rt2x00firmware.c b/drivers/net/wireless/rt2x00/rt2x00firmware.c
index b818a43c4672..f0e1eb72befc 100644
--- a/drivers/net/wireless/rt2x00/rt2x00firmware.c
+++ b/drivers/net/wireless/rt2x00/rt2x00firmware.c
@@ -63,6 +63,9 @@ static int rt2x00lib_request_firmware(struct rt2x00_dev *rt2x00dev)
INFO(rt2x00dev, "Firmware detected - version: %d.%d.\n",
fw->data[fw->size - 4], fw->data[fw->size - 3]);
+ snprintf(rt2x00dev->hw->wiphy->fw_version,
+ sizeof(rt2x00dev->hw->wiphy->fw_version), "%d.%d",
+ fw->data[fw->size - 4], fw->data[fw->size - 3]);
retval = rt2x00dev->ops->lib->check_firmware(rt2x00dev, fw->data, fw->size);
switch (retval) {
diff --git a/drivers/net/wireless/rt2x00/rt2x00ht.c b/drivers/net/wireless/rt2x00/rt2x00ht.c
index c004cd3a8847..ad3c7ff4837b 100644
--- a/drivers/net/wireless/rt2x00/rt2x00ht.c
+++ b/drivers/net/wireless/rt2x00/rt2x00ht.c
@@ -54,6 +54,16 @@ void rt2x00ht_create_tx_descriptor(struct queue_entry *entry,
*/
if (txrate->flags & IEEE80211_TX_RC_MCS) {
txdesc->mcs = txrate->idx;
+
+ /*
+ * MIMO PS should be set to 1 for STA's using dynamic SM PS
+ * when using more then one tx stream (>MCS7).
+ */
+ if (tx_info->control.sta && txdesc->mcs > 7 &&
+ (tx_info->control.sta->ht_cap.cap &
+ (WLAN_HT_CAP_SM_PS_DYNAMIC <<
+ IEEE80211_HT_CAP_SM_PS_SHIFT)))
+ __set_bit(ENTRY_TXD_HT_MIMO_PS, &txdesc->flags);
} else {
txdesc->mcs = rt2x00_get_rate_mcs(hwrate->mcs);
if (txrate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index a3401d301058..eede99939db9 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -1,5 +1,6 @@
/*
- Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
+ Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
Copyright (C) 2004 - 2009 Gertjan van Wingerde <gwingerde@gmail.com>
<http://rt2x00.serialmonkey.com>
@@ -311,7 +312,7 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry,
/*
* Initialize information from queue
*/
- txdesc->queue = entry->queue->qid;
+ txdesc->qid = entry->queue->qid;
txdesc->cw_min = entry->queue->cw_min;
txdesc->cw_max = entry->queue->cw_max;
txdesc->aifs = entry->queue->aifs;
@@ -448,15 +449,14 @@ static void rt2x00queue_write_tx_descriptor(struct queue_entry *entry,
struct txentry_desc *txdesc)
{
struct data_queue *queue = entry->queue;
- struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
- rt2x00dev->ops->lib->write_tx_desc(rt2x00dev, entry->skb, txdesc);
+ queue->rt2x00dev->ops->lib->write_tx_desc(entry, txdesc);
/*
* All processing on the frame has been completed, this means
* it is now ready to be dumped to userspace through debugfs.
*/
- rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_TX, entry->skb);
+ rt2x00debug_dump_frame(queue->rt2x00dev, DUMP_FRAME_TX, entry->skb);
}
static void rt2x00queue_kick_tx_queue(struct queue_entry *entry,
@@ -476,7 +476,7 @@ static void rt2x00queue_kick_tx_queue(struct queue_entry *entry,
*/
if (rt2x00queue_threshold(queue) ||
!test_bit(ENTRY_TXD_BURST, &txdesc->flags))
- rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, queue->qid);
+ rt2x00dev->ops->lib->kick_tx_queue(queue);
}
int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb,
@@ -590,7 +590,7 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
intf->beacon->skb = NULL;
if (!enable_beacon) {
- rt2x00dev->ops->lib->kill_tx_queue(rt2x00dev, QID_BEACON);
+ rt2x00dev->ops->lib->kill_tx_queue(intf->beacon->queue);
mutex_unlock(&intf->beacon_skb_mutex);
return 0;
}
@@ -625,6 +625,51 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
return 0;
}
+void rt2x00queue_for_each_entry(struct data_queue *queue,
+ enum queue_index start,
+ enum queue_index end,
+ void (*fn)(struct queue_entry *entry))
+{
+ unsigned long irqflags;
+ unsigned int index_start;
+ unsigned int index_end;
+ unsigned int i;
+
+ if (unlikely(start >= Q_INDEX_MAX || end >= Q_INDEX_MAX)) {
+ ERROR(queue->rt2x00dev,
+ "Entry requested from invalid index range (%d - %d)\n",
+ start, end);
+ return;
+ }
+
+ /*
+ * Only protect the range we are going to loop over,
+ * if during our loop a extra entry is set to pending
+ * it should not be kicked during this run, since it
+ * is part of another TX operation.
+ */
+ spin_lock_irqsave(&queue->lock, irqflags);
+ index_start = queue->index[start];
+ index_end = queue->index[end];
+ spin_unlock_irqrestore(&queue->lock, irqflags);
+
+ /*
+ * Start from the TX done pointer, this guarentees that we will
+ * send out all frames in the correct order.
+ */
+ if (index_start < index_end) {
+ for (i = index_start; i < index_end; i++)
+ fn(&queue->entries[i]);
+ } else {
+ for (i = index_start; i < queue->limit; i++)
+ fn(&queue->entries[i]);
+
+ for (i = 0; i < index_end; i++)
+ fn(&queue->entries[i]);
+ }
+}
+EXPORT_SYMBOL_GPL(rt2x00queue_for_each_entry);
+
struct data_queue *rt2x00queue_get_queue(struct rt2x00_dev *rt2x00dev,
const enum data_queue_qid queue)
{
@@ -686,13 +731,13 @@ void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index)
if (queue->index[index] >= queue->limit)
queue->index[index] = 0;
+ queue->last_action[index] = jiffies;
+
if (index == Q_INDEX) {
queue->length++;
- queue->last_index = jiffies;
} else if (index == Q_INDEX_DONE) {
queue->length--;
queue->count++;
- queue->last_index_done = jiffies;
}
spin_unlock_irqrestore(&queue->lock, irqflags);
@@ -701,14 +746,17 @@ void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index)
static void rt2x00queue_reset(struct data_queue *queue)
{
unsigned long irqflags;
+ unsigned int i;
spin_lock_irqsave(&queue->lock, irqflags);
queue->count = 0;
queue->length = 0;
- queue->last_index = jiffies;
- queue->last_index_done = jiffies;
- memset(queue->index, 0, sizeof(queue->index));
+
+ for (i = 0; i < Q_INDEX_MAX; i++) {
+ queue->index[i] = 0;
+ queue->last_action[i] = jiffies;
+ }
spin_unlock_irqrestore(&queue->lock, irqflags);
}
@@ -718,7 +766,7 @@ void rt2x00queue_stop_queues(struct rt2x00_dev *rt2x00dev)
struct data_queue *queue;
txall_queue_for_each(rt2x00dev, queue)
- rt2x00dev->ops->lib->kill_tx_queue(rt2x00dev, queue->qid);
+ rt2x00dev->ops->lib->kill_tx_queue(queue);
}
void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev)
@@ -730,9 +778,9 @@ void rt2x00queue_init_queues(struct rt2x00_dev *rt2x00dev)
rt2x00queue_reset(queue);
for (i = 0; i < queue->limit; i++) {
- queue->entries[i].flags = 0;
-
rt2x00dev->ops->lib->clear_entry(&queue->entries[i]);
+ if (queue->qid == QID_RX)
+ rt2x00queue_index_inc(queue, Q_INDEX);
}
}
}
@@ -755,7 +803,7 @@ static int rt2x00queue_alloc_entries(struct data_queue *queue,
* Allocate all queue entries.
*/
entry_size = sizeof(*entries) + qdesc->priv_size;
- entries = kzalloc(queue->limit * entry_size, GFP_KERNEL);
+ entries = kcalloc(queue->limit, entry_size, GFP_KERNEL);
if (!entries)
return -ENOMEM;
@@ -891,7 +939,7 @@ int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev)
*/
rt2x00dev->data_queues = 2 + rt2x00dev->ops->tx_queues + req_atim;
- queue = kzalloc(rt2x00dev->data_queues * sizeof(*queue), GFP_KERNEL);
+ queue = kcalloc(rt2x00dev->data_queues, sizeof(*queue), GFP_KERNEL);
if (!queue) {
ERROR(rt2x00dev, "Queue allocation failed.\n");
return -ENOMEM;
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index 191e7775a9c0..d81d85f34866 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -268,6 +268,7 @@ struct txdone_entry_desc {
* @ENTRY_TXD_HT_AMPDU: This frame is part of an AMPDU.
* @ENTRY_TXD_HT_BW_40: Use 40MHz Bandwidth.
* @ENTRY_TXD_HT_SHORT_GI: Use short GI.
+ * @ENTRY_TXD_HT_MIMO_PS: The receiving STA is in dynamic SM PS mode.
*/
enum txentry_desc_flags {
ENTRY_TXD_RTS_FRAME,
@@ -286,6 +287,7 @@ enum txentry_desc_flags {
ENTRY_TXD_HT_AMPDU,
ENTRY_TXD_HT_BW_40,
ENTRY_TXD_HT_SHORT_GI,
+ ENTRY_TXD_HT_MIMO_PS,
};
/**
@@ -294,7 +296,7 @@ enum txentry_desc_flags {
* Summary of information for the frame descriptor before sending a TX frame.
*
* @flags: Descriptor flags (See &enum queue_entry_flags).
- * @queue: Queue identification (See &enum data_queue_qid).
+ * @qid: Queue identification (See &enum data_queue_qid).
* @length: Length of the entire frame.
* @header_length: Length of 802.11 header.
* @length_high: PLCP length high word.
@@ -320,7 +322,7 @@ enum txentry_desc_flags {
struct txentry_desc {
unsigned long flags;
- enum data_queue_qid queue;
+ enum data_queue_qid qid;
u16 length;
u16 header_length;
@@ -358,17 +360,17 @@ struct txentry_desc {
* @ENTRY_OWNER_DEVICE_DATA: This entry is owned by the device for data
* transfer (either TX or RX depending on the queue). The entry should
* only be touched after the device has signaled it is done with it.
- * @ENTRY_OWNER_DEVICE_CRYPTO: This entry is owned by the device for data
- * encryption or decryption. The entry should only be touched after
- * the device has signaled it is done with it.
* @ENTRY_DATA_PENDING: This entry contains a valid frame and is waiting
* for the signal to start sending.
+ * @ENTRY_DATA_IO_FAILED: Hardware indicated that an IO error occured
+ * while transfering the data to the hardware. No TX status report will
+ * be expected from the hardware.
*/
enum queue_entry_flags {
ENTRY_BCN_ASSIGNED,
ENTRY_OWNER_DEVICE_DATA,
- ENTRY_OWNER_DEVICE_CRYPTO,
ENTRY_DATA_PENDING,
+ ENTRY_DATA_IO_FAILED
};
/**
@@ -399,18 +401,18 @@ struct queue_entry {
*
* @Q_INDEX: Index pointer to the current entry in the queue, if this entry is
* owned by the hardware then the queue is considered to be full.
+ * @Q_INDEX_DMA_DONE: Index pointer for the next entry which will have been
+ * transfered to the hardware.
* @Q_INDEX_DONE: Index pointer to the next entry which will be completed by
* the hardware and for which we need to run the txdone handler. If this
* entry is not owned by the hardware the queue is considered to be empty.
- * @Q_INDEX_CRYPTO: Index pointer to the next entry which encryption/decription
- * will be completed by the hardware next.
* @Q_INDEX_MAX: Keep last, used in &struct data_queue to determine the size
* of the index array.
*/
enum queue_index {
Q_INDEX,
+ Q_INDEX_DMA_DONE,
Q_INDEX_DONE,
- Q_INDEX_CRYPTO,
Q_INDEX_MAX,
};
@@ -446,13 +448,12 @@ struct data_queue {
enum data_queue_qid qid;
spinlock_t lock;
- unsigned long last_index;
- unsigned long last_index_done;
unsigned int count;
unsigned short limit;
unsigned short threshold;
unsigned short length;
unsigned short index[Q_INDEX_MAX];
+ unsigned long last_action[Q_INDEX_MAX];
unsigned short txop;
unsigned short aifs;
@@ -565,6 +566,22 @@ struct data_queue_desc {
queue_loop(__entry, (__dev)->tx, queue_end(__dev))
/**
+ * rt2x00queue_for_each_entry - Loop through all entries in the queue
+ * @queue: Pointer to @data_queue
+ * @start: &enum queue_index Pointer to start index
+ * @end: &enum queue_index Pointer to end index
+ * @fn: The function to call for each &struct queue_entry
+ *
+ * This will walk through all entries in the queue, in chronological
+ * order. This means it will start at the current @start pointer
+ * and will walk through the queue until it reaches the @end pointer.
+ */
+void rt2x00queue_for_each_entry(struct data_queue *queue,
+ enum queue_index start,
+ enum queue_index end,
+ void (*fn)(struct queue_entry *entry));
+
+/**
* rt2x00queue_empty - Check if the queue is empty.
* @queue: Queue to check if empty.
*/
@@ -601,12 +618,23 @@ static inline int rt2x00queue_threshold(struct data_queue *queue)
}
/**
- * rt2x00queue_timeout - Check if a timeout occured for this queue
+ * rt2x00queue_timeout - Check if a timeout occured for STATUS reorts
* @queue: Queue to check.
*/
static inline int rt2x00queue_timeout(struct data_queue *queue)
{
- return time_after(queue->last_index, queue->last_index_done + (HZ / 10));
+ return time_after(queue->last_action[Q_INDEX_DMA_DONE],
+ queue->last_action[Q_INDEX_DONE] + (HZ / 10));
+}
+
+/**
+ * rt2x00queue_timeout - Check if a timeout occured for DMA transfers
+ * @queue: Queue to check.
+ */
+static inline int rt2x00queue_dma_timeout(struct data_queue *queue)
+{
+ return time_after(queue->last_action[Q_INDEX],
+ queue->last_action[Q_INDEX_DMA_DONE] + (HZ / 10));
}
/**
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c
index ff3a36622d1b..4c5ae3d45625 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -1,5 +1,6 @@
/*
- Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
+ Copyright (C) 2010 Willow Garage <http://www.willowgarage.com>
+ Copyright (C) 2004 - 2010 Ivo van Doorn <IvDoorn@gmail.com>
<http://rt2x00.serialmonkey.com>
This program is free software; you can redistribute it and/or modify
@@ -167,137 +168,142 @@ EXPORT_SYMBOL_GPL(rt2x00usb_regbusy_read);
/*
* TX data handlers.
*/
-static void rt2x00usb_interrupt_txdone(struct urb *urb)
+static void rt2x00usb_work_txdone_entry(struct queue_entry *entry)
{
- struct queue_entry *entry = (struct queue_entry *)urb->context;
- struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
- struct txdone_entry_desc txdesc;
-
- if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) ||
- !test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
- return;
-
/*
- * Obtain the status about this packet.
- * Note that when the status is 0 it does not mean the
+ * If the transfer to hardware succeeded, 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).
*/
- txdesc.flags = 0;
- if (!urb->status)
- __set_bit(TXDONE_UNKNOWN, &txdesc.flags);
+ if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
+ rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
else
- __set_bit(TXDONE_FAILURE, &txdesc.flags);
- txdesc.retry = 0;
-
- rt2x00lib_txdone(entry, &txdesc);
+ rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN);
}
-static inline void rt2x00usb_kick_tx_entry(struct queue_entry *entry)
+static void rt2x00usb_work_txdone(struct work_struct *work)
{
- struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
- struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev);
- struct queue_entry_priv_usb *entry_priv = entry->priv_data;
- u32 length;
+ struct rt2x00_dev *rt2x00dev =
+ container_of(work, struct rt2x00_dev, txdone_work);
+ struct data_queue *queue;
+ struct queue_entry *entry;
- if (test_and_clear_bit(ENTRY_DATA_PENDING, &entry->flags)) {
- /*
- * USB devices cannot blindly pass the skb->len as the
- * length of the data to usb_fill_bulk_urb. Pass the skb
- * to the driver to determine what the length should be.
- */
- length = rt2x00dev->ops->lib->get_tx_data_len(entry);
+ tx_queue_for_each(rt2x00dev, queue) {
+ while (!rt2x00queue_empty(queue)) {
+ entry = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
- usb_fill_bulk_urb(entry_priv->urb, usb_dev,
- usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint),
- entry->skb->data, length,
- rt2x00usb_interrupt_txdone, entry);
+ if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
+ break;
- usb_submit_urb(entry_priv->urb, GFP_ATOMIC);
+ rt2x00usb_work_txdone_entry(entry);
+ }
}
}
-void rt2x00usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid qid)
+static void rt2x00usb_interrupt_txdone(struct urb *urb)
{
- struct data_queue *queue = rt2x00queue_get_queue(rt2x00dev, qid);
- unsigned long irqflags;
- unsigned int index;
- unsigned int index_done;
- unsigned int i;
+ struct queue_entry *entry = (struct queue_entry *)urb->context;
+ struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+
+ if (!__test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
+ return;
/*
- * Only protect the range we are going to loop over,
- * if during our loop a extra entry is set to pending
- * it should not be kicked during this run, since it
- * is part of another TX operation.
+ * Report the frame as DMA done
*/
- spin_lock_irqsave(&queue->lock, irqflags);
- index = queue->index[Q_INDEX];
- index_done = queue->index[Q_INDEX_DONE];
- spin_unlock_irqrestore(&queue->lock, irqflags);
+ rt2x00lib_dmadone(entry);
/*
- * Start from the TX done pointer, this guarentees that we will
- * send out all frames in the correct order.
+ * Check if the frame was correctly uploaded
*/
- if (index_done < index) {
- for (i = index_done; i < index; i++)
- rt2x00usb_kick_tx_entry(&queue->entries[i]);
- } else {
- for (i = index_done; i < queue->limit; i++)
- rt2x00usb_kick_tx_entry(&queue->entries[i]);
+ if (urb->status)
+ __set_bit(ENTRY_DATA_IO_FAILED, &entry->flags);
- for (i = 0; i < index; i++)
- rt2x00usb_kick_tx_entry(&queue->entries[i]);
- }
+ /*
+ * Schedule the delayed work for reading the TX status
+ * from the device.
+ */
+ if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
+ test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+ ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->txdone_work);
+}
+
+static void rt2x00usb_kick_tx_entry(struct queue_entry *entry)
+{
+ struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+ struct usb_device *usb_dev = to_usb_device_intf(rt2x00dev->dev);
+ struct queue_entry_priv_usb *entry_priv = entry->priv_data;
+ u32 length;
+
+ if (!test_and_clear_bit(ENTRY_DATA_PENDING, &entry->flags))
+ return;
+
+ /*
+ * USB devices cannot blindly pass the skb->len as the
+ * length of the data to usb_fill_bulk_urb. Pass the skb
+ * to the driver to determine what the length should be.
+ */
+ length = rt2x00dev->ops->lib->get_tx_data_len(entry);
+
+ usb_fill_bulk_urb(entry_priv->urb, usb_dev,
+ usb_sndbulkpipe(usb_dev, entry->queue->usb_endpoint),
+ entry->skb->data, length,
+ rt2x00usb_interrupt_txdone, entry);
+
+ usb_submit_urb(entry_priv->urb, GFP_ATOMIC);
+}
+
+void rt2x00usb_kick_tx_queue(struct data_queue *queue)
+{
+ rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX,
+ rt2x00usb_kick_tx_entry);
}
EXPORT_SYMBOL_GPL(rt2x00usb_kick_tx_queue);
-void rt2x00usb_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid qid)
+static void rt2x00usb_kill_tx_entry(struct queue_entry *entry)
{
- struct data_queue *queue = rt2x00queue_get_queue(rt2x00dev, qid);
- struct queue_entry_priv_usb *entry_priv;
- struct queue_entry_priv_usb_bcn *bcn_priv;
- unsigned int i;
- bool kill_guard;
+ struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
+ struct queue_entry_priv_usb *entry_priv = entry->priv_data;
+ struct queue_entry_priv_usb_bcn *bcn_priv = entry->priv_data;
+
+ if (!test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
+ return;
+
+ usb_kill_urb(entry_priv->urb);
/*
- * When killing the beacon queue, we must also kill
- * the beacon guard byte.
+ * Kill guardian urb (if required by driver).
*/
- kill_guard =
- (qid == QID_BEACON) &&
- (test_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags));
+ if ((entry->queue->qid == QID_BEACON) &&
+ (test_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags)))
+ usb_kill_urb(bcn_priv->guardian_urb);
/*
- * Cancel all entries.
+ * We need a short delay here to wait for
+ * the URB to be canceled
*/
- for (i = 0; i < queue->limit; i++) {
- entry_priv = queue->entries[i].priv_data;
- usb_kill_urb(entry_priv->urb);
+ do {
+ udelay(100);
+ } while (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags));
+}
- /*
- * Kill guardian urb (if required by driver).
- */
- if (kill_guard) {
- bcn_priv = queue->entries[i].priv_data;
- usb_kill_urb(bcn_priv->guardian_urb);
- }
- }
+void rt2x00usb_kill_tx_queue(struct data_queue *queue)
+{
+ rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, Q_INDEX,
+ rt2x00usb_kill_tx_entry);
}
EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue);
-static void rt2x00usb_watchdog_reset_tx(struct data_queue *queue)
+static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
{
- struct queue_entry_priv_usb *entry_priv;
+ struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
unsigned short threshold = queue->threshold;
- WARNING(queue->rt2x00dev, "TX queue %d timed out, invoke reset", queue->qid);
+ WARNING(queue->rt2x00dev, "TX queue %d DMA timed out,"
+ " invoke forced forced reset", queue->qid);
/*
* Temporarily disable the TX queue, this will force mac80211
@@ -307,20 +313,33 @@ static void rt2x00usb_watchdog_reset_tx(struct data_queue *queue)
* queue from being enabled during the txdone handler.
*/
queue->threshold = queue->limit;
- ieee80211_stop_queue(queue->rt2x00dev->hw, queue->qid);
+ ieee80211_stop_queue(rt2x00dev->hw, queue->qid);
/*
- * Reset all currently uploaded TX frames.
+ * Kill all entries in the queue, afterwards we need to
+ * wait a bit for all URBs to be cancelled.
*/
- while (!rt2x00queue_empty(queue)) {
- entry_priv = rt2x00queue_get_entry(queue, Q_INDEX_DONE)->priv_data;
- usb_kill_urb(entry_priv->urb);
+ rt2x00usb_kill_tx_queue(queue);
- /*
- * We need a short delay here to wait for
- * the URB to be canceled and invoked the tx_done handler.
- */
- udelay(200);
+ /*
+ * In case that a driver has overriden the txdone_work
+ * function, we invoke the TX done through there.
+ */
+ rt2x00dev->txdone_work.func(&rt2x00dev->txdone_work);
+
+ /*
+ * Security measure: if the driver did override the
+ * txdone_work function, and the hardware did arrive
+ * in a state which causes it to malfunction, it is
+ * possible that the driver couldn't handle the txdone
+ * event correctly. So after giving the driver the
+ * chance to cleanup, we now force a cleanup of any
+ * leftovers.
+ */
+ if (!rt2x00queue_empty(queue)) {
+ WARNING(queue->rt2x00dev, "TX queue %d DMA timed out,"
+ " status handling failed, invoke hard reset", queue->qid);
+ rt2x00usb_work_txdone(&rt2x00dev->txdone_work);
}
/*
@@ -328,7 +347,15 @@ static void rt2x00usb_watchdog_reset_tx(struct data_queue *queue)
* queue again.
*/
queue->threshold = threshold;
- ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid);
+ ieee80211_wake_queue(rt2x00dev->hw, queue->qid);
+}
+
+static void rt2x00usb_watchdog_tx_status(struct data_queue *queue)
+{
+ WARNING(queue->rt2x00dev, "TX queue %d status timed out,"
+ " invoke forced tx handler", queue->qid);
+
+ ieee80211_queue_work(queue->rt2x00dev->hw, &queue->rt2x00dev->txdone_work);
}
void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev)
@@ -336,8 +363,10 @@ void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev)
struct data_queue *queue;
tx_queue_for_each(rt2x00dev, queue) {
+ if (rt2x00queue_dma_timeout(queue))
+ rt2x00usb_watchdog_tx_dma(queue);
if (rt2x00queue_timeout(queue))
- rt2x00usb_watchdog_reset_tx(queue);
+ rt2x00usb_watchdog_tx_status(queue);
}
}
EXPORT_SYMBOL_GPL(rt2x00usb_watchdog);
@@ -345,38 +374,62 @@ EXPORT_SYMBOL_GPL(rt2x00usb_watchdog);
/*
* RX data handlers.
*/
+static void rt2x00usb_work_rxdone(struct work_struct *work)
+{
+ struct rt2x00_dev *rt2x00dev =
+ container_of(work, struct rt2x00_dev, rxdone_work);
+ struct queue_entry *entry;
+ struct skb_frame_desc *skbdesc;
+ u8 rxd[32];
+
+ while (!rt2x00queue_empty(rt2x00dev->rx)) {
+ entry = rt2x00queue_get_entry(rt2x00dev->rx, Q_INDEX_DONE);
+
+ if (test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
+ break;
+
+ /*
+ * Fill in desc fields of the skb descriptor
+ */
+ skbdesc = get_skb_frame_desc(entry->skb);
+ skbdesc->desc = rxd;
+ skbdesc->desc_len = entry->queue->desc_size;
+
+ /*
+ * Send the frame to rt2x00lib for further processing.
+ */
+ rt2x00lib_rxdone(rt2x00dev, entry);
+ }
+}
+
static void rt2x00usb_interrupt_rxdone(struct urb *urb)
{
struct queue_entry *entry = (struct queue_entry *)urb->context;
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
- u8 rxd[32];
- if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) ||
- !test_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
+ if (!__test_and_clear_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags))
return;
/*
- * Check if the received data is simply too small
- * to be actually valid, or if the urb is signaling
- * a problem.
+ * Report the frame as DMA done
*/
- if (urb->actual_length < entry->queue->desc_size || urb->status) {
- set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags);
- usb_submit_urb(urb, GFP_ATOMIC);
- return;
- }
+ rt2x00lib_dmadone(entry);
/*
- * Fill in desc fields of the skb descriptor
+ * Check if the received data is simply too small
+ * to be actually valid, or if the urb is signaling
+ * a problem.
*/
- skbdesc->desc = rxd;
- skbdesc->desc_len = entry->queue->desc_size;
+ if (urb->actual_length < entry->queue->desc_size || urb->status)
+ __set_bit(ENTRY_DATA_IO_FAILED, &entry->flags);
/*
- * Send the frame to rt2x00lib for further processing.
+ * Schedule the delayed work for reading the RX status
+ * from the device.
*/
- rt2x00lib_rxdone(rt2x00dev, entry);
+ if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
+ test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
+ ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->rxdone_work);
}
/*
@@ -391,7 +444,7 @@ void rt2x00usb_disable_radio(struct rt2x00_dev *rt2x00dev)
* The USB version of kill_tx_queue also works
* on the RX queue.
*/
- rt2x00dev->ops->lib->kill_tx_queue(rt2x00dev, QID_RX);
+ rt2x00dev->ops->lib->kill_tx_queue(rt2x00dev->rx);
}
EXPORT_SYMBOL_GPL(rt2x00usb_disable_radio);
@@ -405,6 +458,8 @@ void rt2x00usb_clear_entry(struct queue_entry *entry)
struct queue_entry_priv_usb *entry_priv = entry->priv_data;
int pipe;
+ entry->flags = 0;
+
if (entry->queue->qid == QID_RX) {
pipe = usb_rcvbulkpipe(usb_dev, entry->queue->usb_endpoint);
usb_fill_bulk_urb(entry_priv->urb, usb_dev, pipe,
@@ -413,8 +468,6 @@ void rt2x00usb_clear_entry(struct queue_entry *entry)
set_bit(ENTRY_OWNER_DEVICE_DATA, &entry->flags);
usb_submit_urb(entry_priv->urb, GFP_ATOMIC);
- } else {
- entry->flags = 0;
}
}
EXPORT_SYMBOL_GPL(rt2x00usb_clear_entry);
@@ -659,6 +712,9 @@ int rt2x00usb_probe(struct usb_interface *usb_intf,
rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_USB);
+ INIT_WORK(&rt2x00dev->rxdone_work, rt2x00usb_work_rxdone);
+ INIT_WORK(&rt2x00dev->txdone_work, rt2x00usb_work_txdone);
+
retval = rt2x00usb_alloc_reg(rt2x00dev);
if (retval)
goto exit_free_device;
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h
index d3d3ddc40875..c2d997f67b3e 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.h
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.h
@@ -379,25 +379,21 @@ struct queue_entry_priv_usb_bcn {
/**
* rt2x00usb_kick_tx_queue - Kick data queue
- * @rt2x00dev: Pointer to &struct rt2x00_dev
- * @qid: Data queue to kick
+ * @queue: Data queue to kick
*
* This will walk through all entries of the queue and push all pending
* frames to the hardware as a single burst.
*/
-void rt2x00usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid qid);
+void rt2x00usb_kick_tx_queue(struct data_queue *queue);
/**
* rt2x00usb_kill_tx_queue - Kill data queue
- * @rt2x00dev: Pointer to &struct rt2x00_dev
- * @qid: Data queue to kill
+ * @queue: Data queue to kill
*
* This will walk through all entries of the queue and kill all
* previously kicked frames before they can be send.
*/
-void rt2x00usb_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid qid);
+void rt2x00usb_kill_tx_queue(struct data_queue *queue);
/**
* rt2x00usb_watchdog - Watchdog for USB communication
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index e539c6cb636f..3a7759929190 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -1050,7 +1050,7 @@ static void rt61pci_link_tuner(struct rt2x00_dev *rt2x00dev,
/*
* Determine r17 bounds.
*/
- if (rt2x00dev->rx_status.band == IEEE80211_BAND_5GHZ) {
+ if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
low_bound = 0x28;
up_bound = 0x48;
if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags)) {
@@ -1766,12 +1766,11 @@ static int rt61pci_set_device_state(struct rt2x00_dev *rt2x00dev,
/*
* TX descriptor initialization
*/
-static void rt61pci_write_tx_desc(struct rt2x00_dev *rt2x00dev,
- struct sk_buff *skb,
+static void rt61pci_write_tx_desc(struct queue_entry *entry,
struct txentry_desc *txdesc)
{
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
- struct queue_entry_priv_pci *entry_priv = skbdesc->entry->priv_data;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+ struct queue_entry_priv_pci *entry_priv = entry->priv_data;
__le32 *txd = entry_priv->desc;
u32 word;
@@ -1779,7 +1778,7 @@ static void rt61pci_write_tx_desc(struct rt2x00_dev *rt2x00dev,
* Start writing the descriptor words.
*/
rt2x00_desc_read(txd, 1, &word);
- rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, txdesc->queue);
+ rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, txdesc->qid);
rt2x00_set_field32(&word, TXD_W1_AIFSN, txdesc->aifs);
rt2x00_set_field32(&word, TXD_W1_CWMIN, txdesc->cw_min);
rt2x00_set_field32(&word, TXD_W1_CWMAX, txdesc->cw_max);
@@ -1802,15 +1801,15 @@ static void rt61pci_write_tx_desc(struct rt2x00_dev *rt2x00dev,
}
rt2x00_desc_read(txd, 5, &word);
- rt2x00_set_field32(&word, TXD_W5_PID_TYPE, skbdesc->entry->queue->qid);
+ rt2x00_set_field32(&word, TXD_W5_PID_TYPE, entry->queue->qid);
rt2x00_set_field32(&word, TXD_W5_PID_SUBTYPE,
skbdesc->entry->entry_idx);
rt2x00_set_field32(&word, TXD_W5_TX_POWER,
- TXPOWER_TO_DEV(rt2x00dev->tx_power));
+ TXPOWER_TO_DEV(entry->queue->rt2x00dev->tx_power));
rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1);
rt2x00_desc_write(txd, 5, word);
- if (txdesc->queue != QID_BEACON) {
+ if (txdesc->qid != QID_BEACON) {
rt2x00_desc_read(txd, 6, &word);
rt2x00_set_field32(&word, TXD_W6_BUFFER_PHYSICAL_ADDRESS,
skbdesc->skb_dma);
@@ -1857,7 +1856,7 @@ static void rt61pci_write_tx_desc(struct rt2x00_dev *rt2x00dev,
*/
skbdesc->desc = txd;
skbdesc->desc_len =
- (txdesc->queue == QID_BEACON) ? TXINFO_SIZE : TXD_DESC_SIZE;
+ (txdesc->qid == QID_BEACON) ? TXINFO_SIZE : TXD_DESC_SIZE;
}
/*
@@ -1882,7 +1881,7 @@ static void rt61pci_write_beacon(struct queue_entry *entry,
/*
* Write the TX descriptor for the beacon.
*/
- rt61pci_write_tx_desc(rt2x00dev, entry->skb, txdesc);
+ rt61pci_write_tx_desc(entry, txdesc);
/*
* Dump beacon to userspace through debugfs.
@@ -1918,34 +1917,34 @@ static void rt61pci_write_beacon(struct queue_entry *entry,
entry->skb = NULL;
}
-static void rt61pci_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid queue)
+static void rt61pci_kick_tx_queue(struct data_queue *queue)
{
+ struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
u32 reg;
rt2x00pci_register_read(rt2x00dev, TX_CNTL_CSR, &reg);
- rt2x00_set_field32(&reg, TX_CNTL_CSR_KICK_TX_AC0, (queue == QID_AC_BE));
- rt2x00_set_field32(&reg, TX_CNTL_CSR_KICK_TX_AC1, (queue == QID_AC_BK));
- rt2x00_set_field32(&reg, TX_CNTL_CSR_KICK_TX_AC2, (queue == QID_AC_VI));
- rt2x00_set_field32(&reg, TX_CNTL_CSR_KICK_TX_AC3, (queue == QID_AC_VO));
+ rt2x00_set_field32(&reg, TX_CNTL_CSR_KICK_TX_AC0, (queue->qid == QID_AC_BE));
+ rt2x00_set_field32(&reg, TX_CNTL_CSR_KICK_TX_AC1, (queue->qid == QID_AC_BK));
+ rt2x00_set_field32(&reg, TX_CNTL_CSR_KICK_TX_AC2, (queue->qid == QID_AC_VI));
+ rt2x00_set_field32(&reg, TX_CNTL_CSR_KICK_TX_AC3, (queue->qid == QID_AC_VO));
rt2x00pci_register_write(rt2x00dev, TX_CNTL_CSR, reg);
}
-static void rt61pci_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
- const enum data_queue_qid qid)
+static void rt61pci_kill_tx_queue(struct data_queue *queue)
{
+ struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
u32 reg;
- if (qid == QID_BEACON) {
+ if (queue->qid == QID_BEACON) {
rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, 0);
return;
}
rt2x00pci_register_read(rt2x00dev, TX_CNTL_CSR, &reg);
- rt2x00_set_field32(&reg, TX_CNTL_CSR_ABORT_TX_AC0, (qid == QID_AC_BE));
- rt2x00_set_field32(&reg, TX_CNTL_CSR_ABORT_TX_AC1, (qid == QID_AC_BK));
- rt2x00_set_field32(&reg, TX_CNTL_CSR_ABORT_TX_AC2, (qid == QID_AC_VI));
- rt2x00_set_field32(&reg, TX_CNTL_CSR_ABORT_TX_AC3, (qid == QID_AC_VO));
+ rt2x00_set_field32(&reg, TX_CNTL_CSR_ABORT_TX_AC0, (queue->qid == QID_AC_BE));
+ rt2x00_set_field32(&reg, TX_CNTL_CSR_ABORT_TX_AC1, (queue->qid == QID_AC_BK));
+ rt2x00_set_field32(&reg, TX_CNTL_CSR_ABORT_TX_AC2, (queue->qid == QID_AC_VI));
+ rt2x00_set_field32(&reg, TX_CNTL_CSR_ABORT_TX_AC3, (queue->qid == QID_AC_VO));
rt2x00pci_register_write(rt2x00dev, TX_CNTL_CSR, reg);
}
@@ -1972,7 +1971,7 @@ static int rt61pci_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1)
return 0;
}
- if (rt2x00dev->rx_status.band == IEEE80211_BAND_5GHZ) {
+ if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
if (lna == 3 || lna == 2)
offset += 10;
}
@@ -2107,11 +2106,7 @@ static void rt61pci_txdone(struct rt2x00_dev *rt2x00dev)
"TX status report missed for entry %d\n",
entry_done->entry_idx);
- txdesc.flags = 0;
- __set_bit(TXDONE_UNKNOWN, &txdesc.flags);
- txdesc.retry = 0;
-
- rt2x00lib_txdone(entry_done, &txdesc);
+ rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN);
entry_done = rt2x00queue_get_entry(queue, Q_INDEX_DONE);
}
@@ -2654,20 +2649,24 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
/*
* Create channel information array
*/
- info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL);
+ info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
spec->channels_info = info;
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START);
- for (i = 0; i < 14; i++)
- info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ for (i = 0; i < 14; i++) {
+ info[i].max_power = MAX_TXPOWER;
+ info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ }
if (spec->num_channels > 14) {
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START);
- for (i = 14; i < spec->num_channels; i++)
- info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ for (i = 14; i < spec->num_channels; i++) {
+ info[i].max_power = MAX_TXPOWER;
+ info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ }
}
return 0;
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index aa9de18fd410..87fb2201537b 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -929,7 +929,7 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev,
/*
* Determine r17 bounds.
*/
- if (rt2x00dev->rx_status.band == IEEE80211_BAND_5GHZ) {
+ if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
low_bound = 0x28;
up_bound = 0x48;
@@ -1426,12 +1426,11 @@ static int rt73usb_set_device_state(struct rt2x00_dev *rt2x00dev,
/*
* TX descriptor initialization
*/
-static void rt73usb_write_tx_desc(struct rt2x00_dev *rt2x00dev,
- struct sk_buff *skb,
+static void rt73usb_write_tx_desc(struct queue_entry *entry,
struct txentry_desc *txdesc)
{
- struct skb_frame_desc *skbdesc = get_skb_frame_desc(skb);
- __le32 *txd = (__le32 *) skb->data;
+ struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb);
+ __le32 *txd = (__le32 *) entry->skb->data;
u32 word;
/*
@@ -1464,7 +1463,7 @@ static void rt73usb_write_tx_desc(struct rt2x00_dev *rt2x00dev,
rt2x00_desc_write(txd, 0, word);
rt2x00_desc_read(txd, 1, &word);
- rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, txdesc->queue);
+ rt2x00_set_field32(&word, TXD_W1_HOST_Q_ID, txdesc->qid);
rt2x00_set_field32(&word, TXD_W1_AIFSN, txdesc->aifs);
rt2x00_set_field32(&word, TXD_W1_CWMIN, txdesc->cw_min);
rt2x00_set_field32(&word, TXD_W1_CWMAX, txdesc->cw_max);
@@ -1487,7 +1486,7 @@ static void rt73usb_write_tx_desc(struct rt2x00_dev *rt2x00dev,
rt2x00_desc_read(txd, 5, &word);
rt2x00_set_field32(&word, TXD_W5_TX_POWER,
- TXPOWER_TO_DEV(rt2x00dev->tx_power));
+ TXPOWER_TO_DEV(entry->queue->rt2x00dev->tx_power));
rt2x00_set_field32(&word, TXD_W5_WAITING_DMA_DONE_INT, 1);
rt2x00_desc_write(txd, 5, word);
@@ -1526,7 +1525,7 @@ static void rt73usb_write_beacon(struct queue_entry *entry,
/*
* Write the TX descriptor for the beacon.
*/
- rt73usb_write_tx_desc(rt2x00dev, entry->skb, txdesc);
+ rt73usb_write_tx_desc(entry, txdesc);
/*
* Dump beacon to userspace through debugfs.
@@ -1574,6 +1573,14 @@ static int rt73usb_get_tx_data_len(struct queue_entry *entry)
return length;
}
+static void rt73usb_kill_tx_queue(struct data_queue *queue)
+{
+ if (queue->qid == QID_BEACON)
+ rt2x00usb_register_write(queue->rt2x00dev, TXRX_CSR9, 0);
+
+ rt2x00usb_kill_tx_queue(queue);
+}
+
/*
* RX control handlers
*/
@@ -1597,7 +1604,7 @@ static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1)
return 0;
}
- if (rt2x00dev->rx_status.band == IEEE80211_BAND_5GHZ) {
+ if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
if (test_bit(CONFIG_EXTERNAL_LNA_A, &rt2x00dev->flags)) {
if (lna == 3 || lna == 2)
offset += 10;
@@ -2084,20 +2091,24 @@ static int rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
/*
* Create channel information array
*/
- info = kzalloc(spec->num_channels * sizeof(*info), GFP_KERNEL);
+ info = kcalloc(spec->num_channels, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
spec->channels_info = info;
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_G_START);
- for (i = 0; i < 14; i++)
- info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ for (i = 0; i < 14; i++) {
+ info[i].max_power = MAX_TXPOWER;
+ info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ }
if (spec->num_channels > 14) {
tx_power = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A_START);
- for (i = 14; i < spec->num_channels; i++)
- info[i].tx_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ for (i = 14; i < spec->num_channels; i++) {
+ info[i].max_power = MAX_TXPOWER;
+ info[i].default_power1 = TXPOWER_FROM_DEV(tx_power[i]);
+ }
}
return 0;
@@ -2259,7 +2270,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 = rt2x00usb_kick_tx_queue,
- .kill_tx_queue = rt2x00usb_kill_tx_queue,
+ .kill_tx_queue = rt73usb_kill_tx_queue,
.fill_rxdone = rt73usb_fill_rxdone,
.config_shared_key = rt73usb_config_shared_key,
.config_pairwise_key = rt73usb_config_pairwise_key,