From 605a0bd66d9d55e9ba46da1a9e5140c68bdf6d85 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 15 Jul 2008 10:10:01 +0200 Subject: mac80211: remove IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE flag I forgot this in the previous patch that made it unused. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 3 +-- drivers/net/wireless/b43legacy/main.c | 3 +-- drivers/net/wireless/iwlwifi/iwl-core.c | 3 +-- drivers/net/wireless/iwlwifi/iwl3945-base.c | 3 +-- drivers/net/wireless/rt2x00/rt2500usb.c | 1 - drivers/net/wireless/rt2x00/rt61pci.c | 1 - drivers/net/wireless/rt2x00/rt73usb.c | 1 - drivers/net/wireless/zd1211rw/zd_mac.c | 1 - 8 files changed, 4 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index e78319aa47c1..3bf3a869361f 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4645,8 +4645,7 @@ static int b43_wireless_init(struct ssb_device *dev) } /* fill hw info */ - hw->flags = IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | - IEEE80211_HW_RX_INCLUDES_FCS | + hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index a1b8bf3ee732..cb5ad4f7fb2b 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -3702,8 +3702,7 @@ static int b43legacy_wireless_init(struct ssb_device *dev) } /* fill hw info */ - hw->flags = IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | - IEEE80211_HW_RX_INCLUDES_FCS | + hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; hw->queues = 1; /* FIXME: hardware has more queues */ diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index a44188bf4459..e3427c205ccf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -818,8 +818,7 @@ int iwl_setup_mac(struct iwl_priv *priv) hw->rate_control_algorithm = "iwl-4965-rs"; /* Tell mac80211 our characteristics */ - hw->flags = IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | - IEEE80211_HW_SIGNAL_DBM | + hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; /* Default value; 4 EDCA QOS priorities */ hw->queues = 4; diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 4a22d3fba75b..05121f395c4e 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -7899,8 +7899,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e priv->ibss_beacon = NULL; /* Tell mac80211 our characteristics */ - hw->flags = IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | - IEEE80211_HW_SIGNAL_DBM | + hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; /* 4 EDCA QOS priorities */ diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index 3558cb210747..449c8d393484 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1650,7 +1650,6 @@ static void rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) * Initialize all hw fields. */ rt2x00dev->hw->flags = - IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM; diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index f7c1f92c1448..ec4ec65d94fd 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -2278,7 +2278,6 @@ static void rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) * Initialize all hw fields. */ rt2x00dev->hw->flags = - IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM; rt2x00dev->hw->extra_tx_headroom = 0; diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index d383735ab8f2..b9efa7f76c9e 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -1871,7 +1871,6 @@ static void rt73usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) * Initialize all hw fields. */ rt2x00dev->hw->flags = - IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM; rt2x00dev->hw->extra_tx_headroom = TXD_DESC_SIZE; diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index fcc532bb6a7e..4d7b98b05030 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -935,7 +935,6 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf) hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mac->band; hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE | IEEE80211_HW_SIGNAL_DB; hw->max_signal = 100; -- cgit v1.2.1 From 3a0f2c871849f23c1070965bf94dec3f9c0b479d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 15 Jul 2008 17:44:18 +0200 Subject: Ath5k: fix memory corruption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When signal is noisy, hardware can use all RX buffers and since the last entry in the list is self-linked, it overwrites the entry until we link new buffers. Ensure that we don't free this last one until we are 100% sure that it is not used by the hardware anymore to not cause memory curruption as can be seen below. This is done by checking next buffer in the list. Even after that we know that the hardware refetched the new link and proceeded further (the next buffer is ready) we can finally free the overwritten buffer. We discard it since the status in its descriptor is overwritten (OR-ed by new status) too. ============================================================================= BUG kmalloc-4096: Poison overwritten ----------------------------------------------------------------------------- INFO: 0xffff810067419060-0xffff810067419667. First byte 0x8 instead of 0x6b INFO: Allocated in dev_alloc_skb+0x18/0x30 age=1118 cpu=1 pid=0 INFO: Freed in skb_release_data+0x85/0xd0 age=1105 cpu=1 pid=3718 INFO: Slab 0xffffe200019d0600 objects=7 used=0 fp=0xffff810067419048 flags=0x40000000000020c3 INFO: Object 0xffff810067419048 @offset=4168 fp=0xffff81006741c120 Bytes b4 0xffff810067419038: 4f 0b 02 00 01 00 00 00 5a 5a 5a 5a 5a 5a 5a 5a O.......ZZZZZZZZ Object 0xffff810067419048: 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk Object 0xffff810067419058: 6b 6b 6b 6b 6b 6b 6b 6b 08 42 30 00 00 0b 6b 80 kkkkkkkk.B0...k. Object 0xffff810067419068: f0 5d 00 4f 62 08 a3 64 00 0c 42 16 52 e4 f0 5a 360].Ob.243d..B.R344360Z Object 0xffff810067419078: 68 81 00 00 7b a5 b4 be 7d 3b 8f 53 cd d5 de 12 h...{245264276};.S315325336. Object 0xffff810067419088: 96 10 0b 89 48 54 23 41 0f 4e 2d b9 37 c3 cb 29 ....HT#A.N-2717303313) Object 0xffff810067419098: d1 e0 de 14 8a 57 2a cc 3b 44 0d 78 7a 19 12 15 321340336..W*314;D.xz... Object 0xffff8100674190a8: a9 ec d4 35 a8 10 ec 8c 40 a7 06 0a 51 a7 48 bb 2513543245250.354.@247..Q247H273 Object 0xffff8100674190b8: 3e cf a1 c7 38 60 63 3f 51 15 c7 20 eb ba 65 30 >ϡ3078`c?Q.307.353272e0 Redzone 0xffff81006741a048: bb bb bb bb bb bb bb bb 273273273273273273273273 Padding 0xffff81006741a088: 5a 5a 5a 5a 5a 5a 5a 5a ZZZZZZZZ Pid: 3297, comm: ath5k_pci Not tainted 2.6.26-rc8-mm1_64 #427 Call Trace: [] print_trailer+0xf6/0x150 [] check_bytes_and_report+0x125/0x180 [] check_object+0xac/0x260 [] __slab_alloc+0x368/0x6d0 [] ? wireless_send_event+0x142/0x310 [] ? __alloc_skb+0x44/0x150 [] ? wireless_send_event+0x142/0x310 [] __kmalloc_track_caller+0xc3/0xf0 [] __alloc_skb+0x6e/0x150 [... stack snipped] FIX kmalloc-4096: Restoring 0xffff810067419060-0xffff810067419667=0x6b FIX kmalloc-4096: Marking all objects used Signed-off-by: Jiri Slaby Acked-by: Nick Kossifidis Cc: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 32 +++++++++++++++++++++++++------- drivers/net/wireless/ath5k/base.h | 2 +- 2 files changed, 26 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index d9769c527346..ed51c4a69d43 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1682,20 +1682,21 @@ ath5k_tasklet_rx(unsigned long data) struct ath5k_rx_status rs = {}; struct sk_buff *skb; struct ath5k_softc *sc = (void *)data; - struct ath5k_buf *bf; + struct ath5k_buf *bf, *bf_last; struct ath5k_desc *ds; int ret; int hdrlen; int pad; spin_lock(&sc->rxbuflock); + if (list_empty(&sc->rxbuf)) { + ATH5K_WARN(sc, "empty rx buf pool\n"); + goto unlock; + } + bf_last = list_entry(sc->rxbuf.prev, struct ath5k_buf, list); do { rxs.flag = 0; - if (unlikely(list_empty(&sc->rxbuf))) { - ATH5K_WARN(sc, "empty rx buf pool\n"); - break; - } bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list); BUG_ON(bf->skb == NULL); skb = bf->skb; @@ -1705,8 +1706,24 @@ ath5k_tasklet_rx(unsigned long data) pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr, sc->desc_len, PCI_DMA_FROMDEVICE); - if (unlikely(ds->ds_link == bf->daddr)) /* this is the end */ - break; + /* + * last buffer must not be freed to ensure proper hardware + * function. When the hardware finishes also a packet next to + * it, we are sure, it doesn't use it anymore and we can go on. + */ + if (bf_last == bf) + bf->flags |= 1; + if (bf->flags) { + struct ath5k_buf *bf_next = list_entry(bf->list.next, + struct ath5k_buf, list); + ret = sc->ah->ah_proc_rx_desc(sc->ah, bf_next->desc, + &rs); + if (ret) + break; + bf->flags &= ~1; + /* skip the overwritten one (even status is martian) */ + goto next; + } ret = sc->ah->ah_proc_rx_desc(sc->ah, ds, &rs); if (unlikely(ret == -EINPROGRESS)) @@ -1816,6 +1833,7 @@ accept: next: list_move_tail(&bf->list, &sc->rxbuf); } while (ath5k_rxbuf_setup(sc, bf) == 0); +unlock: spin_unlock(&sc->rxbuflock); } diff --git a/drivers/net/wireless/ath5k/base.h b/drivers/net/wireless/ath5k/base.h index 47f414b09e67..d7e03e6b8271 100644 --- a/drivers/net/wireless/ath5k/base.h +++ b/drivers/net/wireless/ath5k/base.h @@ -56,7 +56,7 @@ struct ath5k_buf { struct list_head list; - unsigned int flags; /* tx descriptor flags */ + unsigned int flags; /* rx descriptor flags */ struct ath5k_desc *desc; /* virtual addr of desc */ dma_addr_t daddr; /* physical addr of desc */ struct sk_buff *skb; /* skbuff for buf */ -- cgit v1.2.1 From 10488f8ad62be3b860bad74e60b4fe6ab87aece3 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 15 Jul 2008 17:44:19 +0200 Subject: Ath5k: kill tasklets on shutdown Don't forget to kill tasklets on stop to not panic if they fire after freeing some structures. Signed-off-by: Jiri Slaby Acked-by: Nick Kossifidis Cc: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index ed51c4a69d43..c5bf8a2ef5cd 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2342,6 +2342,9 @@ ath5k_stop_hw(struct ath5k_softc *sc) mutex_unlock(&sc->lock); del_timer_sync(&sc->calib_tim); + tasklet_kill(&sc->rxtq); + tasklet_kill(&sc->txtq); + tasklet_kill(&sc->restq); return ret; } -- cgit v1.2.1 From 274c7c3638cd027b46f76d0caef96c1bad8b6701 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 15 Jul 2008 17:44:20 +0200 Subject: Ath5k: flush work Make sure that the irq is not in progress after stop. This means two things: - ensure the intr setting register is set by flushing posted values - call synchronize_irq() after that Also flush stop tx write, inform callers of the tx stop about still pending transfers (unsuccessful stop) and finally don't wait another 3ms in ath5k_rx_stop, since ath5k_hw_stop_rx_dma ensures transfer to be finished. Make sure all writes will be ordered in respect to locks by mmiowb(). Signed-off-by: Jiri Slaby Acked-by: Nick Kossifidis Cc: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 13 +++++++++++-- drivers/net/wireless/ath5k/hw.c | 4 ++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index c5bf8a2ef5cd..8e9afb3b3a56 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -43,7 +43,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -1249,6 +1251,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) txq->link = &ds->ds_link; ath5k_hw_tx_start(ah, txq->qnum); + mmiowb(); spin_unlock_bh(&txq->lock); return 0; @@ -1583,7 +1586,6 @@ ath5k_rx_stop(struct ath5k_softc *sc) ath5k_hw_stop_pcu_recv(ah); /* disable PCU */ ath5k_hw_set_rx_filter(ah, 0); /* clear recv filter */ ath5k_hw_stop_rx_dma(ah); /* disable DMA engine */ - mdelay(3); /* 3ms is long enough for 1 frame */ ath5k_debug_printrxbuffs(sc, ah); @@ -2258,6 +2260,7 @@ ath5k_init(struct ath5k_softc *sc) ret = 0; done: + mmiowb(); mutex_unlock(&sc->lock); return ret; } @@ -2290,6 +2293,7 @@ ath5k_stop_locked(struct ath5k_softc *sc) if (!test_bit(ATH_STAT_INVALID, sc->status)) { ath5k_led_off(sc); ath5k_hw_set_intr(ah, 0); + synchronize_irq(sc->pdev->irq); } ath5k_txq_cleanup(sc); if (!test_bit(ATH_STAT_INVALID, sc->status)) { @@ -2339,6 +2343,7 @@ ath5k_stop_hw(struct ath5k_softc *sc) } } ath5k_txbuf_free(sc, sc->bbuf); + mmiowb(); mutex_unlock(&sc->lock); del_timer_sync(&sc->calib_tim); @@ -2804,6 +2809,7 @@ ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, /* XXX: assoc id is set to 0 for now, mac80211 doesn't have * a clean way of letting us retrieve this yet. */ ath5k_hw_set_associd(ah, ah->ah_bssid, 0); + mmiowb(); } if (conf->changed & IEEE80211_IFCC_BEACON && @@ -2992,6 +2998,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, } unlock: + mmiowb(); mutex_unlock(&sc->lock); return ret; } @@ -3065,8 +3072,10 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb) ret = ath5k_beacon_setup(sc, sc->bbuf); if (ret) sc->bbuf->skb = NULL; - else + else { ath5k_beacon_config(sc); + mmiowb(); + } end: mutex_unlock(&sc->lock); diff --git a/drivers/net/wireless/ath5k/hw.c b/drivers/net/wireless/ath5k/hw.c index c6d12c53bda4..7ca87a557312 100644 --- a/drivers/net/wireless/ath5k/hw.c +++ b/drivers/net/wireless/ath5k/hw.c @@ -1440,6 +1440,7 @@ int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue) /* Stop queue */ ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); + ath5k_hw_reg_read(ah, AR5K_CR); } else { /* * Schedule TX disable and wait until queue is empty @@ -1456,6 +1457,8 @@ int ath5k_hw_stop_tx_dma(struct ath5k_hw *ah, unsigned int queue) /* Clear register */ ath5k_hw_reg_write(ah, 0, AR5K_QCU_TXD); + if (pending) + return -EBUSY; } /* TODO: Check for success else return error */ @@ -1716,6 +1719,7 @@ enum ath5k_int ath5k_hw_set_intr(struct ath5k_hw *ah, enum ath5k_int new_mask) /* ..re-enable interrupts */ ath5k_hw_reg_write(ah, AR5K_IER_ENABLE, AR5K_IER); + ath5k_hw_reg_read(ah, AR5K_IER); return old_mask; } -- cgit v1.2.1 From e86600c7b4e9b9b22ba51620613d6159bf5cf504 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 15 Jul 2008 17:44:43 +0200 Subject: Ath5k: fix dma operation Don't sync - coherent mapping (descriptors) - before unmap, it's useless - (wrongly anyway -- for_cpu) beacon skb, it's just mapped, so by the device yet Signed-off-by: Jiri Slaby Acked-by: Nick Kossifidis Cc: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 8e9afb3b3a56..3aa22dc71651 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1704,10 +1704,6 @@ ath5k_tasklet_rx(unsigned long data) skb = bf->skb; ds = bf->desc; - /* TODO only one segment */ - pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr, - sc->desc_len, PCI_DMA_FROMDEVICE); - /* * last buffer must not be freed to ensure proper hardware * function. When the hardware finishes also a packet next to @@ -1771,8 +1767,6 @@ ath5k_tasklet_rx(unsigned long data) goto next; } accept: - pci_dma_sync_single_for_cpu(sc->pdev, bf->skbaddr, - rs.rs_datalen, PCI_DMA_FROMDEVICE); pci_unmap_single(sc->pdev, bf->skbaddr, sc->rxbufsize, PCI_DMA_FROMDEVICE); bf->skb = NULL; @@ -1860,9 +1854,6 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) list_for_each_entry_safe(bf, bf0, &txq->q, list) { ds = bf->desc; - /* TODO only one segment */ - pci_dma_sync_single_for_cpu(sc->pdev, sc->desc_daddr, - sc->desc_len, PCI_DMA_FROMDEVICE); ret = sc->ah->ah_proc_tx_desc(sc->ah, ds, &ts); if (unlikely(ret == -EINPROGRESS)) break; @@ -2035,8 +2026,6 @@ ath5k_beacon_send(struct ath5k_softc *sc) ATH5K_WARN(sc, "beacon queue %u didn't stop?\n", sc->bhalq); /* NB: hw still stops DMA, so proceed */ } - pci_dma_sync_single_for_cpu(sc->pdev, bf->skbaddr, bf->skb->len, - PCI_DMA_TODEVICE); ath5k_hw_put_tx_buf(ah, sc->bhalq, bf->daddr); ath5k_hw_tx_start(ah, sc->bhalq); -- cgit v1.2.1 From 3e4242b99ce46fed82aa7f40ad5a1817a2b3bd45 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 15 Jul 2008 17:44:21 +0200 Subject: Ath5k: suspend/resume fixes - free and re-request irq since it might have changed during suspend - disable and enable msi - don't set D0 state of the device, it's already done by the PCI layer - do restore_state before enable_device, it's safer - check ath5k_init return value Signed-off-by: Jiri Slaby Acked-by: Nick Kossifidis Cc: Luis R. Rodriguez Cc: Jesse Barnes Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 3aa22dc71651..7273e9f5c6d5 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -592,6 +592,9 @@ ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state) ath5k_led_off(sc); ath5k_stop_hw(sc); + + free_irq(pdev->irq, sc); + pci_disable_msi(pdev); pci_save_state(pdev); pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); @@ -607,15 +610,12 @@ ath5k_pci_resume(struct pci_dev *pdev) struct ath5k_hw *ah = sc->ah; int i, err; - err = pci_set_power_state(pdev, PCI_D0); - if (err) - return err; + pci_restore_state(pdev); err = pci_enable_device(pdev); if (err) return err; - pci_restore_state(pdev); /* * Suspend/Resume resets the PCI configuration space, so we have to * re-disable the RETRY_TIMEOUT register (0x41) to keep @@ -623,7 +623,17 @@ ath5k_pci_resume(struct pci_dev *pdev) */ pci_write_config_byte(pdev, 0x41, 0); - ath5k_init(sc); + pci_enable_msi(pdev); + + err = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc); + if (err) { + ATH5K_ERR(sc, "request_irq failed\n"); + goto err_msi; + } + + err = ath5k_init(sc); + if (err) + goto err_irq; ath5k_led_enable(sc); /* @@ -637,6 +647,12 @@ ath5k_pci_resume(struct pci_dev *pdev) ath5k_hw_reset_key(ah, i); return 0; +err_irq: + free_irq(pdev->irq, sc); +err_msi: + pci_disable_msi(pdev); + pci_disable_device(pdev); + return err; } #endif /* CONFIG_PM */ -- cgit v1.2.1 From 734b5aa911dc65f4563048f069dfc631c9aa7de7 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Tue, 15 Jul 2008 13:07:16 -0400 Subject: ath5k: use positive logic for HP laptop LEDs Helge Deller reports that HP laptops (NC4010 and NC6000) use active- high signals to turn on the LEDs. Previous code used active-low for all devices. Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 7273e9f5c6d5..359795d8b482 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2581,8 +2581,6 @@ ath5k_init_leds(struct ath5k_softc *sc) struct pci_dev *pdev = sc->pdev; char name[ATH5K_LED_MAX_NAME_LEN + 1]; - sc->led_on = 0; /* active low */ - /* * Auto-enable soft led processing for IBM cards and for * 5211 minipci cards. @@ -2591,11 +2589,13 @@ ath5k_init_leds(struct ath5k_softc *sc) pdev->device == PCI_DEVICE_ID_ATHEROS_AR5211) { __set_bit(ATH_STAT_LEDSOFT, sc->status); sc->led_pin = 0; + sc->led_on = 0; /* active low */ } /* Enable softled on PIN1 on HP Compaq nc6xx, nc4000 & nx5000 laptops */ if (pdev->subsystem_vendor == PCI_VENDOR_ID_COMPAQ) { __set_bit(ATH_STAT_LEDSOFT, sc->status); sc->led_pin = 1; + sc->led_on = 1; /* active high */ } if (!test_bit(ATH_STAT_LEDSOFT, sc->status)) goto out; -- cgit v1.2.1 From 699669f331a9e459713e4327a468db8fbb8cd60f Mon Sep 17 00:00:00 2001 From: Rami Rosen Date: Wed, 16 Jul 2008 16:39:56 +0300 Subject: iwl-3945: add #ifdef CONFIG_IWL3945_LEDS to avoid compile warning. When building the wireless-next-2.6 tree with CONFIG_IWL3945 (for building iwl-3945 driver) and where CONFIG_IWL3945_LEDS is not set, we get this warning: drivers/net/wireless/iwlwifi/iwl-3945.c: In function 'iwl3945_pass_packet_to_mac80211': drivers/net/wireless/iwlwifi/iwl-3945.c:633: warning: unused variable 'hdr' This patch adds #ifdef to iwl3945_pass_packet_to_mac80211() to avoid this warning. (The variable 'hdr' is used only if CONFIG_IWL3945_LEDS is set) Signed-off-by: Rami Rosen Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index c2a76785b665..a51e0eaa1334 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -630,7 +630,9 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl3945_priv *priv, struct ieee80211_rx_status *stats) { struct iwl3945_rx_packet *pkt = (struct iwl3945_rx_packet *)rxb->skb->data; +#ifdef CONFIG_IWL3945_LEDS struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)IWL_RX_DATA(pkt); +#endif struct iwl3945_rx_frame_hdr *rx_hdr = IWL_RX_HDR(pkt); struct iwl3945_rx_frame_end *rx_end = IWL_RX_END(pkt); short len = le16_to_cpu(rx_hdr->len); -- cgit v1.2.1 From fb904907fb1a02a64af9f2d1fb1ef35d963231f9 Mon Sep 17 00:00:00 2001 From: Brian Cavagnolo Date: Wed, 16 Jul 2008 12:15:26 -0700 Subject: libertas: check bounds and only use decimal for sysfs persistent features. Some persistent settings were using hex and others decimal. In some cases, values were set in hex but reported in decimal. Confusing. Signed-off-by: Brian Cavagnolo Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/persistcfg.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/libertas/persistcfg.c b/drivers/net/wireless/libertas/persistcfg.c index 6d0ff8decaf7..3309a9c3cfef 100644 --- a/drivers/net/wireless/libertas/persistcfg.c +++ b/drivers/net/wireless/libertas/persistcfg.c @@ -48,7 +48,7 @@ static ssize_t bootflag_get(struct device *dev, if (ret) return ret; - return snprintf(buf, 12, "0x%x\n", le32_to_cpu(defs.bootflag)); + return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag)); } /** @@ -63,8 +63,8 @@ static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr, int ret; memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%x", &datum); - if (ret != 1) + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 1)) return -EINVAL; *((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum); @@ -91,7 +91,7 @@ static ssize_t boottime_get(struct device *dev, if (ret) return ret; - return snprintf(buf, 12, "0x%x\n", defs.boottime); + return snprintf(buf, 12, "%d\n", defs.boottime); } /** @@ -106,8 +106,8 @@ static ssize_t boottime_set(struct device *dev, int ret; memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%x", &datum); - if (ret != 1) + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) return -EINVAL; /* A too small boot time will result in the device booting into @@ -143,7 +143,7 @@ static ssize_t channel_get(struct device *dev, if (ret) return ret; - return snprintf(buf, 12, "0x%x\n", le16_to_cpu(defs.channel)); + return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel)); } /** @@ -154,11 +154,11 @@ static ssize_t channel_set(struct device *dev, struct device_attribute *attr, { struct lbs_private *priv = to_net_dev(dev)->priv; struct cmd_ds_mesh_config cmd; - uint16_t datum; + uint32_t datum; int ret; memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%hx", &datum); + ret = sscanf(buf, "%d", &datum); if (ret != 1 || datum < 1 || datum > 11) return -EINVAL; @@ -274,8 +274,8 @@ static ssize_t protocol_id_set(struct device *dev, int ret; memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%x", &datum); - if (ret != 1) + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) return -EINVAL; /* fetch all other Information Element parameters */ @@ -328,8 +328,8 @@ static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr, int ret; memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%x", &datum); - if (ret != 1) + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) return -EINVAL; /* fetch all other Information Element parameters */ @@ -382,8 +382,8 @@ static ssize_t capability_set(struct device *dev, struct device_attribute *attr, int ret; memset(&cmd, 0, sizeof(cmd)); - ret = sscanf(buf, "%x", &datum); - if (ret != 1) + ret = sscanf(buf, "%d", &datum); + if ((ret != 1) || (datum > 255)) return -EINVAL; /* fetch all other Information Element parameters */ -- cgit v1.2.1 From c0b6a1c9be3acac0f600072ebc2bc6ad3d8c8f76 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 17 Jul 2008 13:19:24 +0400 Subject: iwlwifi: compilation error when CONFIG_IWLWIFI_DEBUG is not set CC [M] drivers/net/wireless/iwlwifi/iwl-rfkill.o drivers/net/wireless/iwlwifi/iwl-led.c: In function 'iwl_led_brightness_set': drivers/net/wireless/iwlwifi/iwl-led.c:198: error: 'led_type_str' undeclared (first use in this function) drivers/net/wireless/iwlwifi/iwl-led.c:198: error: (Each undeclared identifier is reported only once drivers/net/wireless/iwlwifi/iwl-led.c:198: error: for each function it appears in.) The problem is that led_type_str is defined under CONFIG_IWLWIFI_DEBUG while IWL_DEBUG is a static inline function in this case. Replace it with macro. Signed-off-by: Denis V. Lunev Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-debug.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index 58384805a494..d6d729e86bdb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -68,12 +68,8 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv); #endif #else -static inline void IWL_DEBUG(int level, const char *fmt, ...) -{ -} -static inline void IWL_DEBUG_LIMIT(int level, const char *fmt, ...) -{ -} +#define IWL_DEBUG(level, fmt, args...) +#define IWL_DEBUG_LIMIT(level, fmt, args...) #endif /* CONFIG_IWLWIFI_DEBUG */ -- cgit v1.2.1 From cb9289cb798502a5010c8f1d8d003842cd1449a4 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Fri, 18 Jul 2008 10:56:12 +0400 Subject: iwlwifi: small compile warnings without CONFIG_IWLWIFI_DEBUG CC [M] drivers/net/wireless/iwlwifi/iwl-scan.o drivers/net/wireless/iwlwifi/iwl-scan.c: In function 'iwl_rx_scan_complete_notif': drivers/net/wireless/iwlwifi/iwl-scan.c:274: warning: unused variable 'scan_notif' Signed-off-by: Denis V. Lunev Acked-by: Tomas Winkler Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-scan.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index efc750d2fc5c..5a00ac23e2d0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -270,6 +270,7 @@ static void iwl_rx_scan_results_notif(struct iwl_priv *priv, static void iwl_rx_scan_complete_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { +#ifdef CONFIG_IWLWIFI_DEBUG struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; struct iwl_scancomplete_notification *scan_notif = (void *)pkt->u.raw; @@ -277,6 +278,7 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv, scan_notif->scanned_channels, scan_notif->tsf_low, scan_notif->tsf_high, scan_notif->status); +#endif /* The HW is no longer scanning */ clear_bit(STATUS_SCAN_HW, &priv->status); -- cgit v1.2.1 From bc05116ab33d30342e2b4b1bcc6d6e1184e9df97 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Fri, 18 Jul 2008 11:11:21 -0400 Subject: ath5k: fix recursive locking in ath5k_beacon_update ath5k_beacon_update takes sc->lock upon entry. However, it is only called from within ath5k_config_interface, which already holds the lock. Remove the unnecessary locking from ath5k_beacon_update. Signed-off-by: Bob Copeland Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 359795d8b482..3285032727b3 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -3065,8 +3065,6 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb) ath5k_debug_dump_skb(sc, skb, "BC ", 1); - mutex_lock(&sc->lock); - if (sc->opmode != IEEE80211_IF_TYPE_IBSS) { ret = -EIO; goto end; @@ -3083,7 +3081,6 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb) } end: - mutex_unlock(&sc->lock); return ret; } -- cgit v1.2.1 From 256b152b005e319f985f50f2a910a75ba0def74f Mon Sep 17 00:00:00 2001 From: Pavel Roskin Date: Fri, 18 Jul 2008 12:56:59 -0400 Subject: ath5k: don't enable MSI, we cannot handle it yet MSI is a nice thing, but we cannot enable it without changing the interrupt handler. If we do it, we break MSI capable hardware, specifically AR5006 chipset. Signed-off-by: Pavel Roskin Acked-by: Nick Kossifidis Cc: stable Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 3285032727b3..1106d1c06298 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -473,9 +473,6 @@ ath5k_pci_probe(struct pci_dev *pdev, /* Set private data */ pci_set_drvdata(pdev, hw); - /* Enable msi for devices that support it */ - pci_enable_msi(pdev); - /* Setup interrupt handler */ ret = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc); if (ret) { @@ -553,7 +550,6 @@ err_ah: err_irq: free_irq(pdev->irq, sc); err_free: - pci_disable_msi(pdev); ieee80211_free_hw(hw); err_map: pci_iounmap(pdev, mem); @@ -575,7 +571,6 @@ ath5k_pci_remove(struct pci_dev *pdev) ath5k_detach(pdev, hw); ath5k_hw_detach(sc->ah); free_irq(pdev->irq, sc); - pci_disable_msi(pdev); pci_iounmap(pdev, sc->iobase); pci_release_region(pdev, 0); pci_disable_device(pdev); -- cgit v1.2.1 From ed06387b44f0501f7298b559dc8ddfcd410c8fa0 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Sat, 19 Jul 2008 16:15:42 +0200 Subject: rt2x00: Remove duplicate declaration rt2x00queue_free_skb() was declared twice. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00lib.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h index f2c9b0e79b5f..c5fb3a72cf37 100644 --- a/drivers/net/wireless/rt2x00/rt2x00lib.h +++ b/drivers/net/wireless/rt2x00/rt2x00lib.h @@ -124,13 +124,6 @@ void rt2x00queue_unmap_skb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb); */ void rt2x00queue_free_skb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb); -/** - * rt2x00queue_free_skb - free a skb - * @rt2x00dev: Pointer to &struct rt2x00_dev. - * @skb: The skb to free. - */ -void rt2x00queue_free_skb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb); - /** * rt2x00queue_write_tx_frame - Write TX frame to hardware * @queue: Queue over which the frame should be send -- cgit v1.2.1 From f2fdbc4847e0d3991474949f21aa439c361391db Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Sat, 19 Jul 2008 16:16:12 +0200 Subject: rt2x00: Fix EIFS timing value Olivier reported a difference between the EIFS values used in the legacy driver and the one in the rt2x00 drivers. In rt2x00 the value was ( SIFS + (8 * (IEEE80211_HEADER + ACK_SIZE)) ) which comes down to 314us while the legacy driver uses the value 364us This was caused because EIFS is: SIFS + DIFS + AckTime This patch will fix this by adding the DIFS by the above value, and creating a SHORT_EIFS define which uses the SHORT_DIFS. Reported-by: Olivier Cornu Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00.h | 5 ++++- drivers/net/wireless/rt2x00/rt2x00config.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 07b03b3c7ef1..73495f0ee139 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -108,7 +108,10 @@ #define SHORT_PIFS ( SIFS + SHORT_SLOT_TIME ) #define DIFS ( PIFS + SLOT_TIME ) #define SHORT_DIFS ( SHORT_PIFS + SHORT_SLOT_TIME ) -#define EIFS ( SIFS + (8 * (IEEE80211_HEADER + ACK_SIZE)) ) +#define EIFS ( SIFS + DIFS + \ + (8 * (IEEE80211_HEADER + ACK_SIZE)) ) +#define SHORT_EIFS ( SIFS + SHORT_DIFS + \ + (8 * (IEEE80211_HEADER + ACK_SIZE)) ) /* * Chipset identification diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index f20ca712504f..3f89516e8332 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c @@ -271,7 +271,7 @@ config: libconf.sifs = SIFS; libconf.pifs = short_slot_time ? SHORT_PIFS : PIFS; libconf.difs = short_slot_time ? SHORT_DIFS : DIFS; - libconf.eifs = EIFS; + libconf.eifs = short_slot_time ? SHORT_EIFS : EIFS; } libconf.conf = conf; -- cgit v1.2.1 From ed0dbeeb92bdb1030bcec67e20b294bd2020cb31 Mon Sep 17 00:00:00 2001 From: Iwo Mergler Date: Sat, 19 Jul 2008 16:16:54 +0200 Subject: rt2x00: Support for large vendor requests Adds an extra rt2x00 vendor request function to support register transfers beyond the CSR_CACHE_SIZE / USB packet size limit. This is useful for firmware uploads, beacon templates and keys, all of which are to large to do with a single USB request. Signed-off-by: Iwo Mergler Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.c | 32 ++++++++++++++++++++++++++++++++ drivers/net/wireless/rt2x00/rt2x00usb.h | 21 ++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 83862e7f7aec..933e6cc9359d 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -122,6 +122,38 @@ int rt2x00usb_vendor_request_buff(struct rt2x00_dev *rt2x00dev, } EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_buff); +int rt2x00usb_vendor_request_large_buff(struct rt2x00_dev *rt2x00dev, + const u8 request, const u8 requesttype, + const u16 offset, void *buffer, + const u16 buffer_length, + const int timeout) +{ + int status = 0; + unsigned char *tb; + u16 off, len, bsize; + + mutex_lock(&rt2x00dev->usb_cache_mutex); + + tb = buffer; + off = offset; + len = buffer_length; + while (len && !status) { + bsize = min_t(u16, CSR_CACHE_SIZE, len); + status = rt2x00usb_vendor_req_buff_lock(rt2x00dev, request, + requesttype, off, tb, + bsize, timeout); + + tb += bsize; + len -= bsize; + off += bsize; + } + + mutex_unlock(&rt2x00dev->usb_cache_mutex); + + return status; +} +EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_large_buff); + /* * TX data handlers. */ diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h index aad794adf52c..8d5d4272e88f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.h +++ b/drivers/net/wireless/rt2x00/rt2x00usb.h @@ -70,7 +70,7 @@ /* * Cache size */ -#define CSR_CACHE_SIZE 8 +#define CSR_CACHE_SIZE 64 #define CSR_CACHE_SIZE_FIRMWARE 64 /* @@ -171,6 +171,25 @@ int rt2x00usb_vendor_req_buff_lock(struct rt2x00_dev *rt2x00dev, const u16 offset, void *buffer, const u16 buffer_length, const int timeout); +/** + * rt2x00usb_vendor_request_large_buff - Send register command to device (buffered) + * @rt2x00dev: Pointer to &struct rt2x00_dev + * @request: USB vendor command (See &enum rt2x00usb_vendor_request) + * @requesttype: Request type &USB_VENDOR_REQUEST_* + * @offset: Register start offset to perform action on + * @buffer: Buffer where information will be read/written to by device + * @buffer_length: Size of &buffer + * @timeout: Operation timeout + * + * This function is used to transfer register data in blocks larger + * then CSR_CACHE_SIZE. Use for firmware upload, keys and beacons. + */ +int rt2x00usb_vendor_request_large_buff(struct rt2x00_dev *rt2x00dev, + const u8 request, const u8 requesttype, + const u16 offset, void *buffer, + const u16 buffer_length, + const int timeout); + /** * rt2x00usb_vendor_request_sw - Send single register command to device * @rt2x00dev: Pointer to &struct rt2x00_dev -- cgit v1.2.1 From 3e0c1abe748a30bc705a55f71bca8e04a83820f1 Mon Sep 17 00:00:00 2001 From: Iwo Mergler Date: Sat, 19 Jul 2008 16:17:16 +0200 Subject: rt2x00: Large vendor requests for rt73usb firmware upload and beacons Switches rt73usb to use large vendor requests for firmware and beacons. This also fixes the garbled beacon bug. Signed-off-by: Iwo Mergler Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00usb.h | 1 - drivers/net/wireless/rt2x00/rt73usb.c | 40 ++++++++------------------------- 2 files changed, 9 insertions(+), 32 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h index 8d5d4272e88f..ee3875f894aa 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.h +++ b/drivers/net/wireless/rt2x00/rt2x00usb.h @@ -71,7 +71,6 @@ * Cache size */ #define CSR_CACHE_SIZE 64 -#define CSR_CACHE_SIZE_FIRMWARE 64 /* * USB request types. diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index b9efa7f76c9e..fc320c0409db 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -890,9 +890,6 @@ static int rt73usb_load_firmware(struct rt2x00_dev *rt2x00dev, const void *data, unsigned int i; int status; u32 reg; - const char *ptr = data; - char *cache; - int buflen; /* * Wait for stable hardware. @@ -911,31 +908,12 @@ static int rt73usb_load_firmware(struct rt2x00_dev *rt2x00dev, const void *data, /* * Write firmware to device. - * We setup a seperate cache for this action, - * since we are going to write larger chunks of data - * then normally used cache size. */ - cache = kmalloc(CSR_CACHE_SIZE_FIRMWARE, GFP_KERNEL); - if (!cache) { - ERROR(rt2x00dev, "Failed to allocate firmware cache.\n"); - return -ENOMEM; - } - - for (i = 0; i < len; i += CSR_CACHE_SIZE_FIRMWARE) { - buflen = min_t(int, len - i, CSR_CACHE_SIZE_FIRMWARE); - - memcpy(cache, ptr, buflen); - - rt2x00usb_vendor_request(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, - FIRMWARE_IMAGE_BASE + i, 0, - cache, buflen, - REGISTER_TIMEOUT32(buflen)); - - ptr += buflen; - } - - kfree(cache); + rt2x00usb_vendor_request_large_buff(rt2x00dev, USB_MULTI_WRITE, + USB_VENDOR_REQUEST_OUT, + FIRMWARE_IMAGE_BASE, + data, len, + REGISTER_TIMEOUT32(len)); /* * Send firmware request to device to load firmware, @@ -1374,10 +1352,10 @@ static void rt73usb_write_beacon(struct queue_entry *entry) * Write entire beacon with descriptor to register. */ beacon_base = HW_BEACON_OFFSET(entry->entry_idx); - rt2x00usb_vendor_request(rt2x00dev, USB_MULTI_WRITE, - USB_VENDOR_REQUEST_OUT, beacon_base, 0, - entry->skb->data, entry->skb->len, - REGISTER_TIMEOUT32(entry->skb->len)); + rt2x00usb_vendor_request_large_buff(rt2x00dev, USB_MULTI_WRITE, + USB_VENDOR_REQUEST_OUT, beacon_base, + entry->skb->data, entry->skb->len, + REGISTER_TIMEOUT32(entry->skb->len)); /* * Clean up the beacon skb. -- cgit v1.2.1 From b93ce437eba7e0232683326f30d9d1167a872fad Mon Sep 17 00:00:00 2001 From: Iwo Mergler Date: Sat, 19 Jul 2008 16:17:40 +0200 Subject: rt2x00: Fix the beacon length bug When setting up a beacon template, the length of the beacon is calculated with the assumption that the SKB already contains the Tx descriptor. In the case of beacons it doesn't. This patch undoes the damage by adding the Tx descriptor length to the beacon length. This is safe, because the shortest possible beacon is longer than the Tx header. Signed-off-by: Iwo Mergler Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2500usb.c | 12 ++++++++++++ drivers/net/wireless/rt2x00/rt73usb.c | 12 ++++++++++++ 2 files changed, 24 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index 449c8d393484..3078417b326b 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1121,6 +1121,7 @@ static void rt2500usb_write_beacon(struct queue_entry *entry) int pipe = usb_sndbulkpipe(usb_dev, 1); int length; u16 reg; + u32 word, len; /* * Add the descriptor in front of the skb. @@ -1129,6 +1130,17 @@ static void rt2500usb_write_beacon(struct queue_entry *entry) memcpy(entry->skb->data, skbdesc->desc, skbdesc->desc_len); skbdesc->desc = entry->skb->data; + /* + * Adjust the beacon databyte count. The current number is + * calculated before this function gets called, but falsely + * assumes that the descriptor was already present in the SKB. + */ + rt2x00_desc_read(skbdesc->desc, 0, &word); + len = rt2x00_get_field32(word, TXD_W0_DATABYTE_COUNT); + len += skbdesc->desc_len; + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, len); + rt2x00_desc_write(skbdesc->desc, 0, word); + /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index fc320c0409db..866504612e8a 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -1330,6 +1330,7 @@ static void rt73usb_write_beacon(struct queue_entry *entry) struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); unsigned int beacon_base; u32 reg; + u32 word, len; /* * Add the descriptor in front of the skb. @@ -1338,6 +1339,17 @@ static void rt73usb_write_beacon(struct queue_entry *entry) memcpy(entry->skb->data, skbdesc->desc, skbdesc->desc_len); skbdesc->desc = entry->skb->data; + /* + * Adjust the beacon databyte count. The current number is + * calculated before this function gets called, but falsely + * assumes that the descriptor was already present in the SKB. + */ + rt2x00_desc_read(skbdesc->desc, 0, &word); + len = rt2x00_get_field32(word, TXD_W0_DATABYTE_COUNT); + len += skbdesc->desc_len; + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, len); + rt2x00_desc_write(skbdesc->desc, 0, word); + /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. -- cgit v1.2.1 From 5adf6d63c1697ce1835daf2b5393488a71ee0dca Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Sun, 20 Jul 2008 18:03:38 +0200 Subject: rt2x00: Fix QOS sequence counting When IEEE80211_TX_CTL_ASSIGN_SEQ is not set, the driver should disable hardware sequence counting to make sure the mac80211 provided counter is used. This fixes QOS sequence counting, since that is one of the cases where mac80211 provides a seperate sequence counter. By moving the sequence counting code to rt2x00queue we make sure that _all_ frames get the sequence counter, including RTS/CTS and Beacon frames. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00mac.c | 13 ------------- drivers/net/wireless/rt2x00/rt2x00queue.c | 26 ++++++++++++++++++++++++++ drivers/net/wireless/rt2x00/rt2x00queue.h | 2 ++ drivers/net/wireless/rt2x00/rt61pci.c | 3 ++- drivers/net/wireless/rt2x00/rt73usb.c | 3 ++- 5 files changed, 32 insertions(+), 15 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index f1dcbaa80c3c..9d346bd2db0e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -96,7 +96,6 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data; enum data_queue_qid qid = skb_get_queue_mapping(skb); - struct rt2x00_intf *intf = vif_to_intf(tx_info->control.vif); struct data_queue *queue; u16 frame_control; @@ -152,18 +151,6 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) } } - /* - * XXX: This is as wrong as the old mac80211 code was, - * due to beacons not getting sequence numbers assigned - * properly. - */ - if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - if (tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) - intf->seqno += 0x10; - ieee80211hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - ieee80211hdr->seq_ctrl |= cpu_to_le16(intf->seqno); - } - if (rt2x00queue_write_tx_frame(queue, skb)) { ieee80211_stop_queue(rt2x00dev->hw, qid); return NETDEV_TX_BUSY; diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 7f442030f5ad..7b581a370fde 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -120,6 +120,7 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb); + struct rt2x00_intf *intf = vif_to_intf(tx_info->control.vif); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)entry->skb->data; struct ieee80211_rate *rate = ieee80211_get_tx_rate(rt2x00dev->hw, tx_info); @@ -199,6 +200,31 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, txdesc->ifs = IFS_SIFS; } + /* + * Hardware should insert sequence counter. + * FIXME: We insert a software sequence counter first for + * hardware that doesn't support hardware sequence counting. + * + * This is wrong because beacons are not getting sequence + * numbers assigned properly. + * + * A secondary problem exists for drivers that cannot toggle + * sequence counting per-frame, since those will override the + * sequence counter given by mac80211. + */ + if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + spin_lock(&intf->lock); + + if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)) + intf->seqno += 0x10; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(intf->seqno); + + spin_unlock(&intf->lock); + + __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); + } + /* * PLCP setup * Length calculation depends on OFDM/CCK rate. diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h index 8945945c892e..a4a8c57004db 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/rt2x00/rt2x00queue.h @@ -199,6 +199,7 @@ struct txdone_entry_desc { * @ENTRY_TXD_RTS_FRAME: This frame is a RTS frame. * @ENTRY_TXD_CTS_FRAME: This frame is a CTS-to-self frame. * @ENTRY_TXD_OFDM_RATE: This frame is send out with an OFDM rate. + * @ENTRY_TXD_GENERATE_SEQ: This frame requires sequence counter. * @ENTRY_TXD_FIRST_FRAGMENT: This is the first frame. * @ENTRY_TXD_MORE_FRAG: This frame is followed by another fragment. * @ENTRY_TXD_REQ_TIMESTAMP: Require timestamp to be inserted. @@ -210,6 +211,7 @@ enum txentry_desc_flags { ENTRY_TXD_RTS_FRAME, ENTRY_TXD_CTS_FRAME, ENTRY_TXD_OFDM_RATE, + ENTRY_TXD_GENERATE_SEQ, ENTRY_TXD_FIRST_FRAGMENT, ENTRY_TXD_MORE_FRAG, ENTRY_TXD_REQ_TIMESTAMP, diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index ec4ec65d94fd..fbe2a652e014 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -1544,7 +1544,8 @@ static void rt61pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&word, TXD_W1_CWMIN, txdesc->cw_min); rt2x00_set_field32(&word, TXD_W1_CWMAX, txdesc->cw_max); rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, IEEE80211_HEADER); - rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, 1); + rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, + test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); rt2x00_set_field32(&word, TXD_W1_BUFFER_COUNT, 1); rt2x00_desc_write(txd, 1, word); diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 866504612e8a..9761eaaa08be 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -1281,7 +1281,8 @@ static void rt73usb_write_tx_desc(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&word, TXD_W1_CWMIN, txdesc->cw_min); rt2x00_set_field32(&word, TXD_W1_CWMAX, txdesc->cw_max); rt2x00_set_field32(&word, TXD_W1_IV_OFFSET, IEEE80211_HEADER); - rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, 1); + rt2x00_set_field32(&word, TXD_W1_HW_SEQUENCE, + test_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags)); rt2x00_desc_write(txd, 1, word); rt2x00_desc_read(txd, 2, &word); -- cgit v1.2.1 From e7087a828f8714e464fff18d93618727530dfd89 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Sun, 20 Jul 2008 18:03:58 +0200 Subject: rt2x00: Fix memleak when RTS/CTS fails When sending the RTS/CTS frame fails, we should free the skb buffer which was created. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00mac.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 9d346bd2db0e..1f83d5fbf6b0 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -83,6 +83,7 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, (struct ieee80211_rts *)(skb->data)); if (rt2x00queue_write_tx_frame(queue, skb)) { + dev_kfree_skb_any(skb); WARNING(rt2x00dev, "Failed to send RTS/CTS frame.\n"); return NETDEV_TX_BUSY; } -- cgit v1.2.1 From 80c42affad970c8ebc5ebec4681aef8dadf21c32 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 21 Jul 2008 09:58:11 +0200 Subject: drivers/net/wireless/ipw2100.c: Release mutex in error handling code The mutex is released on a successful return, so it would seem that it should be released on an error return as well. The semantic patch finds this problem is as follows: (http://www.emn.fr/x-info/coccinelle/) // @@ expression l; @@ mutex_lock(l); ... when != mutex_unlock(l) when any when strict ( if (...) { ... when != mutex_unlock(l) + mutex_unlock(l); return ...; } | mutex_unlock(l); ) // Signed-off-by: Julia Lawall Signed-off-by: John W. Linville --- drivers/net/wireless/ipw2100.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c index 5bf9e00b070c..c6f886ec08a3 100644 --- a/drivers/net/wireless/ipw2100.c +++ b/drivers/net/wireless/ipw2100.c @@ -6442,6 +6442,7 @@ static int ipw2100_resume(struct pci_dev *pci_dev) if (err) { printk(KERN_ERR "%s: pci_enable_device failed on resume\n", dev->name); + mutex_unlock(&priv->action_mutex); return err; } pci_restore_state(pci_dev); @@ -7146,7 +7147,7 @@ static int ipw2100_wx_get_rate(struct net_device *dev, err = ipw2100_get_ordinal(priv, IPW_ORD_CURRENT_TX_RATE, &val, &len); if (err) { IPW_DEBUG_WX("failed querying ordinals.\n"); - return err; + goto done; } switch (val & TX_RATE_MASK) { -- cgit v1.2.1 From 4104863fb4a724723d1d5f3cba9d3c5084087e45 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 21 Jul 2008 11:29:34 +0200 Subject: b43legacy: Release mutex in error handling code The mutex is released on a successful return, so it would seem that it should be released on an error return as well. The semantic patch finds this problem is as follows: (http://www.emn.fr/x-info/coccinelle/) // @@ expression l; @@ mutex_lock(l); ... when != mutex_unlock(l) when any when strict ( if (...) { ... when != mutex_unlock(l) + mutex_unlock(l); return ...; } | mutex_unlock(l); ) // Signed-off-by: Julia Lawall Signed-off-by: Michael Buesch Cc: stable Signed-off-by: John W. Linville --- drivers/net/wireless/b43legacy/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index cb5ad4f7fb2b..2541c81932f0 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -3845,10 +3845,10 @@ static int b43legacy_resume(struct ssb_device *dev) goto out; } } - mutex_unlock(&wl->mutex); b43legacydbg(wl, "Device resumed.\n"); out: + mutex_unlock(&wl->mutex); return err; } -- cgit v1.2.1 From 74c0ee9b59bdaa81a666d5d58022f847390e4b0c Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 21 Jul 2008 11:52:44 +0200 Subject: rt2x00: Force full register config after start() rt2x00 will only perform configuration changes from mac80211 when the configuration option has changed. This means it keeps track of the current active configuration and will check these values when the config() callback function is used. However this causes breakage when the interface has been brought down and up again, since all stored active values aren't reset while the registers might have. This is for example the case with rt61pci antenna registers which will jump to invalid values when the interface has been started. To make sure a full configuration takes place after the start() callback function, a new flag is added which will be checked during config() and skips the "what's changed" phase. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00.h | 1 + drivers/net/wireless/rt2x00/rt2x00dev.c | 7 ++++--- drivers/net/wireless/rt2x00/rt2x00mac.c | 13 ++++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 73495f0ee139..db2dc976d831 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -600,6 +600,7 @@ enum rt2x00_flags { DEVICE_STARTED_SUSPEND, DEVICE_ENABLED_RADIO, DEVICE_DISABLED_RADIO_HW, + DEVICE_DIRTY_CONFIG, /* * Driver features diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 8c93eb8353b0..f42283ad7b02 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1013,6 +1013,7 @@ int rt2x00lib_start(struct rt2x00_dev *rt2x00dev) rt2x00dev->intf_associated = 0; __set_bit(DEVICE_STARTED, &rt2x00dev->flags); + __set_bit(DEVICE_DIRTY_CONFIG, &rt2x00dev->flags); return 0; } @@ -1237,9 +1238,9 @@ int rt2x00lib_resume(struct rt2x00_dev *rt2x00dev) /* * Reconfigure device. */ - rt2x00lib_config(rt2x00dev, &rt2x00dev->hw->conf, 1); - if (!rt2x00dev->hw->conf.radio_enabled) - rt2x00lib_disable_radio(rt2x00dev); + retval = rt2x00mac_config(rt2x00dev->hw, &rt2x00dev->hw->conf); + if (retval) + goto exit; /* * Iterator over each active interface to diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 1f83d5fbf6b0..042ab00d8bd2 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -310,6 +310,7 @@ EXPORT_SYMBOL_GPL(rt2x00mac_remove_interface); int rt2x00mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) { struct rt2x00_dev *rt2x00dev = hw->priv; + int force_reconfig; /* * Mac80211 might be calling this function while we are trying @@ -329,7 +330,17 @@ int rt2x00mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); } - rt2x00lib_config(rt2x00dev, conf, 0); + /* + * When the DEVICE_DIRTY_CONFIG flag is set, the device has recently + * been started and the configuration must be forced upon the hardware. + * Otherwise registers will not be intialized correctly and could + * result in non-working hardware because essential registers aren't + * initialized. + */ + force_reconfig = + __test_and_clear_bit(DEVICE_DIRTY_CONFIG, &rt2x00dev->flags); + + rt2x00lib_config(rt2x00dev, conf, force_reconfig); /* * Reenable RX only if the radio should be on. -- cgit v1.2.1 From 9c0ab712c7e40b61063431cae74a3e763535a4e7 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 21 Jul 2008 19:06:02 +0200 Subject: rt2x00: Clear queue entry flags during initialization When the queues are being initialized the entry flags fields must be reset to 0. When this does not happen some entries might still be marked as "occupied" after an ifdown & ifup cycle which would trigger errors when the entry is being accessed: phy0 -> rt2x00queue_write_tx_frame: Error - Arrived at non-free entry in the non-full queue 0. Please file bug report to http://rt2x00.serialmonkey.com. This also fixes the mac80211 warning: ------------[ cut here ]------------ WARNING: at net/mac80211/tx.c:1238 ieee80211_master_start_xmit+0x30a/0x350 [mac80211]() which was triggered by the queue error. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00queue.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 7b581a370fde..3b27f6aa860c 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -492,9 +492,12 @@ void rt2x00queue_init_rx(struct rt2x00_dev *rt2x00dev) if (!rt2x00dev->ops->lib->init_rxentry) return; - for (i = 0; i < queue->limit; i++) + for (i = 0; i < queue->limit; i++) { + queue->entries[i].flags = 0; + rt2x00dev->ops->lib->init_rxentry(rt2x00dev, &queue->entries[i]); + } } void rt2x00queue_init_tx(struct rt2x00_dev *rt2x00dev) @@ -508,9 +511,12 @@ void rt2x00queue_init_tx(struct rt2x00_dev *rt2x00dev) if (!rt2x00dev->ops->lib->init_txentry) continue; - for (i = 0; i < queue->limit; i++) + for (i = 0; i < queue->limit; i++) { + queue->entries[i].flags = 0; + rt2x00dev->ops->lib->init_txentry(rt2x00dev, &queue->entries[i]); + } } } -- cgit v1.2.1 From 031211049b71619f7e776521963c082ca453d9fd Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 22 Jul 2008 23:50:04 -0700 Subject: drivers/net/wireless/iwlwifi/iwl-led.c: printk fix ia64: drivers/net/wireless/iwlwifi/iwl-led.c: In function `iwl_get_blink_rate': drivers/net/wireless/iwlwifi/iwl-led.c:271: warning: long long int format, s64 arg (arg 6) drivers/net/wireless/iwlwifi/iwl-led.c:271: warning: long long int format, u64 arg (arg 7) We do not know what type the architecture uses to impement u64 and s64, hence we must cast the variables for printing. Cc: Tomas Winkler Cc: John W. Linville Cc: Zhu Yi Signed-off-by: Andrew Morton Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-led.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c index 899d7a2567a8..61250e6a7d1b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-led.c @@ -268,7 +268,9 @@ static int iwl_get_blink_rate(struct iwl_priv *priv) if (tpt < 0) /* wrapparound */ tpt = -tpt; - IWL_DEBUG_LED("tpt %lld current_tpt %lld\n", tpt, current_tpt); + IWL_DEBUG_LED("tpt %lld current_tpt %llu\n", + (long long)tpt, + (unsigned long long)current_tpt); priv->led_tpt = current_tpt; if (!priv->allow_blinking) -- cgit v1.2.1 From 0b06b2ae0e474fc6378117c832bcd94785a9e975 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Wed, 23 Jul 2008 18:36:38 -0700 Subject: mac80211: fix sparse integer as NULL pointer warning drivers/net/wireless/mac80211_hwsim.c:503:20: warning: Using plain integer as NULL pointer Signed-off-by: Harvey Harrison Signed-off-by: John W. Linville --- drivers/net/wireless/mac80211_hwsim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 5816230d58f8..248d31a7aa33 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -500,7 +500,7 @@ failed_hw: device_unregister(data->dev); failed_drvdata: ieee80211_free_hw(hw); - hwsim_radios[i] = 0; + hwsim_radios[i] = NULL; failed: mac80211_hwsim_free(); return err; -- cgit v1.2.1 From 1f690d7b549ef9c7424536475501885dd5b54930 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 28 Jul 2008 22:08:18 -0500 Subject: rtl8187: Fix for TX sequence number problem "mac80211: fix TX sequence numbers" broke rtl8187. This patch makes the same kind of fix that was done for rt2x00. Note that this code will have to be reworked for proper sequence numbers on beacons. In addition, the sequence number has been placed in the hardware state, not the vif state. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtl8187.h | 1 + drivers/net/wireless/rtl8187_dev.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rtl8187.h b/drivers/net/wireless/rtl8187.h index 3afb49f8866a..8961901b5c04 100644 --- a/drivers/net/wireless/rtl8187.h +++ b/drivers/net/wireless/rtl8187.h @@ -100,6 +100,7 @@ struct rtl8187_priv { struct usb_device *udev; u32 rx_conf; u16 txpwr_base; + u16 seqno; u8 asic_rev; u8 is_rtl8187b; enum { diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c index d3067b1216ca..7ff03aca2518 100644 --- a/drivers/net/wireless/rtl8187_dev.c +++ b/drivers/net/wireless/rtl8187_dev.c @@ -169,6 +169,7 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) { struct rtl8187_priv *priv = dev->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data; unsigned int ep; void *buf; struct urb *urb; @@ -234,6 +235,20 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) ep = epmap[skb_get_queue_mapping(skb)]; } + /* FIXME: The sequence that follows is needed for this driver to + * work with mac80211 since "mac80211: fix TX sequence numbers". + * As with the temporary code in rt2x00, changes will be needed + * to get proper sequence numbers on beacons. In addition, this + * patch places the sequence number in the hardware state, which + * limits us to a single virtual state. + */ + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) + priv->seqno += 0x10; + ieee80211hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + ieee80211hdr->seq_ctrl |= cpu_to_le16(priv->seqno); + } + info->driver_data[0] = dev; info->driver_data[1] = urb; -- cgit v1.2.1 From 0ccd58fc03f40529f66190b1a41e92a732d2bda8 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Mon, 28 Jul 2008 22:25:08 -0500 Subject: rtl8187: Improve wireless statistics for RTL8187B Wireless statistics produced by the RTL8187B driver are not particularly informative about the strength of the received signal. From the data sheet provided by Realtek, I discovered that certain parts of the RX header should have the information necessary to calculate signal quality and strength. With testing, it became clear that most of these quantities were very jittery - only the AGC correlated with the signals expected from nearby AP's. As a result, the quality and strength are derived from the agc value. The scaling has been determined so that the numbers are close to those obtained by b43 under the same conditions. The results are qualitatively correct. Statistics derived for the RTL8187 have not been changed. The RX header variables have been renamed to match the quantites described in the Realtek data sheet. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtl8187.h | 10 +++-- drivers/net/wireless/rtl8187_dev.c | 78 +++++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 34 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rtl8187.h b/drivers/net/wireless/rtl8187.h index 8961901b5c04..1b0d750f6623 100644 --- a/drivers/net/wireless/rtl8187.h +++ b/drivers/net/wireless/rtl8187.h @@ -47,11 +47,13 @@ struct rtl8187_rx_hdr { struct rtl8187b_rx_hdr { __le32 flags; __le64 mac_time; - u8 noise; - u8 signal; + u8 sq; + u8 rssi; u8 agc; - u8 reserved; - __le32 unused; + u8 flags2; + __le16 snr_long2end; + s8 pwdb_g12; + u8 fot; } __attribute__((packed)); /* {rtl8187,rtl8187b}_tx_info is in skb */ diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c index 7ff03aca2518..177988efd660 100644 --- a/drivers/net/wireless/rtl8187_dev.c +++ b/drivers/net/wireless/rtl8187_dev.c @@ -272,6 +272,7 @@ static void rtl8187_rx_cb(struct urb *urb) struct ieee80211_rx_status rx_status = { 0 }; int rate, signal; u32 flags; + u32 quality; spin_lock(&priv->rx_queue.lock); if (skb->next) @@ -295,44 +296,57 @@ static void rtl8187_rx_cb(struct urb *urb) flags = le32_to_cpu(hdr->flags); signal = hdr->signal & 0x7f; rx_status.antenna = (hdr->signal >> 7) & 1; - rx_status.signal = signal; rx_status.noise = hdr->noise; rx_status.mactime = le64_to_cpu(hdr->mac_time); - priv->signal = signal; priv->quality = signal; + rx_status.qual = priv->quality; priv->noise = hdr->noise; + rate = (flags >> 20) & 0xF; + if (rate > 3) { /* OFDM rate */ + if (signal > 90) + signal = 90; + else if (signal < 25) + signal = 25; + signal = 90 - signal; + } else { /* CCK rate */ + if (signal > 95) + signal = 95; + else if (signal < 30) + signal = 30; + signal = 95 - signal; + } + rx_status.signal = signal; + priv->signal = signal; } else { struct rtl8187b_rx_hdr *hdr = (typeof(hdr))(skb_tail_pointer(skb) - sizeof(*hdr)); + /* The Realtek datasheet for the RTL8187B shows that the RX + * header contains the following quantities: signal quality, + * RSSI, AGC, the received power in dB, and the measured SNR. + * In testing, none of these quantities show qualitative + * agreement with AP signal strength, except for the AGC, + * which is inversely proportional to the strength of the + * signal. In the following, the quality and signal strength + * are derived from the AGC. The arbitrary scaling constants + * are chosen to make the results close to the values obtained + * for a BCM4312 using b43 as the driver. The noise is ignored + * for now. + */ flags = le32_to_cpu(hdr->flags); - signal = hdr->agc >> 1; - rx_status.antenna = (hdr->signal >> 7) & 1; - rx_status.signal = 64 - min(hdr->noise, (u8)64); - rx_status.noise = hdr->noise; + quality = 170 - hdr->agc; + if (quality > 100) + quality = 100; + signal = 14 - hdr->agc / 2; + rx_status.qual = quality; + priv->quality = quality; + rx_status.signal = signal; + priv->signal = signal; + rx_status.antenna = (hdr->rssi >> 7) & 1; rx_status.mactime = le64_to_cpu(hdr->mac_time); - priv->signal = hdr->signal; - priv->quality = hdr->agc >> 1; - priv->noise = hdr->noise; + rate = (flags >> 20) & 0xF; } skb_trim(skb, flags & 0x0FFF); - rate = (flags >> 20) & 0xF; - if (rate > 3) { /* OFDM rate */ - if (signal > 90) - signal = 90; - else if (signal < 25) - signal = 25; - signal = 90 - signal; - } else { /* CCK rate */ - if (signal > 95) - signal = 95; - else if (signal < 30) - signal = 30; - signal = 95 - signal; - } - - rx_status.qual = priv->quality; - rx_status.signal = signal; rx_status.rate_idx = rate; rx_status.freq = dev->conf.channel->center_freq; rx_status.band = dev->conf.channel->band; @@ -1030,9 +1044,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, priv->mode = IEEE80211_IF_TYPE_MNTR; dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_SIGNAL_UNSPEC; - dev->max_signal = 65; + IEEE80211_HW_RX_INCLUDES_FCS; eeprom.data = dev; eeprom.register_read = rtl8187_eeprom_register_read; @@ -1147,10 +1159,16 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, (*channel++).hw_value = txpwr >> 8; } - if (priv->is_rtl8187b) + if (priv->is_rtl8187b) { printk(KERN_WARNING "rtl8187: 8187B chip detected. Support " "is EXPERIMENTAL, and could damage your\n" " hardware, use at your own risk\n"); + dev->flags |= IEEE80211_HW_SIGNAL_DBM; + } else { + dev->flags |= IEEE80211_HW_SIGNAL_UNSPEC; + dev->max_signal = 65; + } + if ((id->driver_info == DEVICE_RTL8187) && priv->is_rtl8187b) printk(KERN_INFO "rtl8187: inconsistency between id with OEM" " info!\n"); -- cgit v1.2.1 From d0f09804144fd9471a13cf4d80e66842c7fa114f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 29 Jul 2008 11:32:07 +0200 Subject: mac80211: partially fix skb->cb use This patch fixes mac80211 to not use the skb->cb over the queue step from virtual interfaces to the master. The patch also, for now, disables aggregation because that would still require requeuing, will fix that in a separate patch. There are two other places (software requeue and powersaving stations) where requeue can happen, but that is not currently used by any drivers/not possible to use respectively. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 2 +- drivers/net/wireless/b43/xmit.c | 2 +- drivers/net/wireless/b43legacy/xmit.c | 2 +- drivers/net/wireless/iwlwifi/iwl-tx.c | 2 +- drivers/net/wireless/iwlwifi/iwl3945-base.c | 2 +- drivers/net/wireless/rt2x00/rt2x00mac.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 1106d1c06298..ff3fad794b61 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -1237,7 +1237,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) pktlen = skb->len; - if (!(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT)) { + if (info->control.hw_key) { keyidx = info->control.hw_key->hw_key_idx; pktlen += info->control.icv_len; } diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 8d54502222a6..9dda8169f7cc 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -192,7 +192,7 @@ int b43_generate_txhdr(struct b43_wldev *dev, const struct b43_phy *phy = &dev->phy; const struct ieee80211_hdr *wlhdr = (const struct ieee80211_hdr *)fragment_data; - int use_encryption = (!(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT)); + int use_encryption = !!info->control.hw_key; __le16 fctl = wlhdr->frame_control; struct ieee80211_rate *fbrate; u8 rate, rate_fb; diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c index e969ed8d412d..68e1f8c78727 100644 --- a/drivers/net/wireless/b43legacy/xmit.c +++ b/drivers/net/wireless/b43legacy/xmit.c @@ -192,7 +192,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev, u16 cookie) { const struct ieee80211_hdr *wlhdr; - int use_encryption = (!(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT)); + int use_encryption = !!info->control.hw_key; u16 fctl; u8 rate; struct ieee80211_rate *rate_fb; diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 9b50b1052b09..f72cd0bf6aa3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -906,7 +906,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) * first entry */ iwl_hw_txq_attach_buf_to_tfd(priv, tfd, txcmd_phys, len); - if (!(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT)) + if (info->control.hw_key) iwl_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb, sta_id); /* Set up TFD's 2nd entry to point directly to remainder of skb, diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 05121f395c4e..7c82ecfa30a4 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2667,7 +2667,7 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) * first entry */ iwl3945_hw_txq_attach_buf_to_tfd(priv, tfd, txcmd_phys, len); - if (!(info->flags & IEEE80211_TX_CTL_DO_NOT_ENCRYPT)) + if (info->control.hw_key) iwl3945_build_tx_cmd_hwcrypto(priv, info, out_cmd, skb, 0); /* Set up TFD's 2nd entry to point directly to remainder of skb, diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 042ab00d8bd2..c3ee4ecba792 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -63,7 +63,7 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, */ memcpy(skb->cb, frag_skb->cb, sizeof(skb->cb)); rts_info = IEEE80211_SKB_CB(skb); - rts_info->flags |= IEEE80211_TX_CTL_DO_NOT_ENCRYPT; + rts_info->control.hw_key = NULL; rts_info->flags &= ~IEEE80211_TX_CTL_USE_RTS_CTS; rts_info->flags &= ~IEEE80211_TX_CTL_USE_CTS_PROTECT; rts_info->flags &= ~IEEE80211_TX_CTL_REQ_TX_STATUS; -- cgit v1.2.1 From 0bacdf303f72a3ed34252934114bc04e79222687 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Wed, 30 Jul 2008 13:18:59 +0300 Subject: ath5k: Update register list * Update list of registers * Use updated register macros inside hw.c, initvals.c and debug.c Changes-licensed-under: ISC Signed-off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/debug.c | 2 +- drivers/net/wireless/ath5k/hw.c | 43 +- drivers/net/wireless/ath5k/initvals.c | 4 +- drivers/net/wireless/ath5k/reg.h | 934 +++++++++++++++++++++++++--------- 4 files changed, 720 insertions(+), 263 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/debug.c b/drivers/net/wireless/ath5k/debug.c index 41d5fa34b544..6fa6c8e04ff0 100644 --- a/drivers/net/wireless/ath5k/debug.c +++ b/drivers/net/wireless/ath5k/debug.c @@ -129,7 +129,7 @@ static struct reg regs[] = { REG_STRUCT_INIT(AR5K_CPC1), REG_STRUCT_INIT(AR5K_CPC2), REG_STRUCT_INIT(AR5K_CPC3), - REG_STRUCT_INIT(AR5K_CPCORN), + REG_STRUCT_INIT(AR5K_CPCOVF), REG_STRUCT_INIT(AR5K_RESET_CTL), REG_STRUCT_INIT(AR5K_SLEEP_CTL), REG_STRUCT_INIT(AR5K_INTPEND), diff --git a/drivers/net/wireless/ath5k/hw.c b/drivers/net/wireless/ath5k/hw.c index 7ca87a557312..42ef41ed5d18 100644 --- a/drivers/net/wireless/ath5k/hw.c +++ b/drivers/net/wireless/ath5k/hw.c @@ -843,27 +843,26 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, * Write some more initial register settings */ if (ah->ah_version == AR5K_AR5212) { - ath5k_hw_reg_write(ah, 0x0002a002, AR5K_PHY(11)); + ath5k_hw_reg_write(ah, 0x0002a002, 0x982c); if (channel->hw_value == CHANNEL_G) if (ah->ah_mac_srev < AR5K_SREV_VER_AR2413) ath5k_hw_reg_write(ah, 0x00f80d80, - AR5K_PHY(83)); + 0x994c); else if (ah->ah_mac_srev < AR5K_SREV_VER_AR2424) ath5k_hw_reg_write(ah, 0x00380140, - AR5K_PHY(83)); + 0x994c); else if (ah->ah_mac_srev < AR5K_SREV_VER_AR2425) ath5k_hw_reg_write(ah, 0x00fc0ec0, - AR5K_PHY(83)); + 0x994c); else /* 2425 */ ath5k_hw_reg_write(ah, 0x00fc0fc0, - AR5K_PHY(83)); + 0x994c); else - ath5k_hw_reg_write(ah, 0x00000000, - AR5K_PHY(83)); + ath5k_hw_reg_write(ah, 0x00000000, 0x994c); ath5k_hw_reg_write(ah, 0x000009b5, 0xa228); - ath5k_hw_reg_write(ah, 0x0000000f, 0x8060); + ath5k_hw_reg_write(ah, 0x0000000f, AR5K_SEQ_MASK); ath5k_hw_reg_write(ah, 0x00000000, 0xa254); ath5k_hw_reg_write(ah, 0x0000000e, AR5K_PHY_SCAL); } @@ -935,7 +934,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, return ret; /* Set antenna mode */ - AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x44), + AR5K_REG_MASKED_BITS(ah, AR5K_PHY_ANT_CTL, ah->ah_antenna[ee_mode][0], 0xfffffc06); /* @@ -965,15 +964,15 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, ath5k_hw_reg_write(ah, AR5K_PHY_NF_SVAL(ee->ee_noise_floor_thr[ee_mode]), - AR5K_PHY(0x5a)); + AR5K_PHY_NFTHRES); - AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x11), + AR5K_REG_MASKED_BITS(ah, AR5K_PHY_SETTLING, (ee->ee_switch_settling[ee_mode] << 7) & 0x3f80, 0xffffc07f); - AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x12), + AR5K_REG_MASKED_BITS(ah, AR5K_PHY_GAIN, (ee->ee_ant_tx_rx[ee_mode] << 12) & 0x3f000, 0xfffc0fff); - AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x14), + AR5K_REG_MASKED_BITS(ah, AR5K_PHY_DESIRED_SIZE, (ee->ee_adc_desired_size[ee_mode] & 0x00ff) | ((ee->ee_pga_desired_size[ee_mode] << 8) & 0xff00), 0xffff0000); @@ -982,13 +981,13 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, (ee->ee_tx_end2xpa_disable[ee_mode] << 24) | (ee->ee_tx_end2xpa_disable[ee_mode] << 16) | (ee->ee_tx_frm2xpa_enable[ee_mode] << 8) | - (ee->ee_tx_frm2xpa_enable[ee_mode]), AR5K_PHY(0x0d)); + (ee->ee_tx_frm2xpa_enable[ee_mode]), AR5K_PHY_RF_CTL4); - AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x0a), + AR5K_REG_MASKED_BITS(ah, AR5K_PHY_RF_CTL3, ee->ee_tx_end2xlna_enable[ee_mode] << 8, 0xffff00ff); - AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x19), + AR5K_REG_MASKED_BITS(ah, AR5K_PHY_NF, (ee->ee_thr_62[ee_mode] << 12) & 0x7f000, 0xfff80fff); - AR5K_REG_MASKED_BITS(ah, AR5K_PHY(0x49), 4, 0xffffff01); + AR5K_REG_MASKED_BITS(ah, AR5K_PHY_OFDM_SELFCORR, 4, 0xffffff01); AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE | @@ -3363,11 +3362,13 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) ath5k_hw_reg_write(ah, ah->ah_turbo ? AR5K_INIT_PROTO_TIME_CNTRL_TURBO : AR5K_INIT_PROTO_TIME_CNTRL, AR5K_IFS1); - /* Set PHY register 0x9844 (??) */ + /* Set AR5K_PHY_SETTLING */ ath5k_hw_reg_write(ah, ah->ah_turbo ? - (ath5k_hw_reg_read(ah, AR5K_PHY(17)) & ~0x7F) | 0x38 : - (ath5k_hw_reg_read(ah, AR5K_PHY(17)) & ~0x7F) | 0x1C, - AR5K_PHY(17)); + (ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F) + | 0x38 : + (ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F) + | 0x1C, + AR5K_PHY_SETTLING); /* Set Frame Control Register */ ath5k_hw_reg_write(ah, ah->ah_turbo ? (AR5K_PHY_FRAME_CTL_INI | AR5K_PHY_TURBO_MODE | diff --git a/drivers/net/wireless/ath5k/initvals.c b/drivers/net/wireless/ath5k/initvals.c index 04c84e9da89d..2806b21bf90b 100644 --- a/drivers/net/wireless/ath5k/initvals.c +++ b/drivers/net/wireless/ath5k/initvals.c @@ -489,7 +489,7 @@ static const struct ath5k_ini ar5212_ini[] = { { AR5K_QUEUE_TXDP(9), 0x00000000 }, { AR5K_DCU_FP, 0x00000000 }, { AR5K_DCU_TXP, 0x00000000 }, - { AR5K_DCU_TX_FILTER, 0x00000000 }, + { AR5K_DCU_TX_FILTER_0_BASE, 0x00000000 }, /* Unknown table */ { 0x1078, 0x00000000 }, { 0x10b8, 0x00000000 }, @@ -679,7 +679,7 @@ static const struct ath5k_ini ar5212_ini[] = { { AR5K_PHY(645), 0x00106c10 }, { AR5K_PHY(646), 0x009c4060 }, { AR5K_PHY(647), 0x1483800a }, - /* { AR5K_PHY(648), 0x018830c6 },*/ /* 2413 */ + /* { AR5K_PHY(648), 0x018830c6 },*/ /* 2413/2425 */ { AR5K_PHY(648), 0x01831061 }, { AR5K_PHY(649), 0x00000400 }, /*{ AR5K_PHY(650), 0x000001b5 },*/ diff --git a/drivers/net/wireless/ath5k/reg.h b/drivers/net/wireless/ath5k/reg.h index 30629b3e37c2..7562bf173d3e 100644 --- a/drivers/net/wireless/ath5k/reg.h +++ b/drivers/net/wireless/ath5k/reg.h @@ -53,7 +53,7 @@ #define AR5K_CR_TXD0 0x00000008 /* TX Disable for queue 0 on 5210 */ #define AR5K_CR_TXD1 0x00000010 /* TX Disable for queue 1 on 5210 */ #define AR5K_CR_RXD 0x00000020 /* RX Disable */ -#define AR5K_CR_SWI 0x00000040 +#define AR5K_CR_SWI 0x00000040 /* Software Interrupt */ /* * RX Descriptor Pointer register @@ -65,19 +65,19 @@ */ #define AR5K_CFG 0x0014 /* Register Address */ #define AR5K_CFG_SWTD 0x00000001 /* Byte-swap TX descriptor (for big endian archs) */ -#define AR5K_CFG_SWTB 0x00000002 /* Byte-swap TX buffer (?) */ +#define AR5K_CFG_SWTB 0x00000002 /* Byte-swap TX buffer */ #define AR5K_CFG_SWRD 0x00000004 /* Byte-swap RX descriptor */ -#define AR5K_CFG_SWRB 0x00000008 /* Byte-swap RX buffer (?) */ -#define AR5K_CFG_SWRG 0x00000010 /* Byte-swap Register values (?) */ -#define AR5K_CFG_ADHOC 0x00000020 /* [5211+] */ +#define AR5K_CFG_SWRB 0x00000008 /* Byte-swap RX buffer */ +#define AR5K_CFG_SWRG 0x00000010 /* Byte-swap Register access */ +#define AR5K_CFG_ADHOC 0x00000020 /* AP/Adhoc indication [5211+] */ #define AR5K_CFG_PHY_OK 0x00000100 /* [5211+] */ #define AR5K_CFG_EEBS 0x00000200 /* EEPROM is busy */ -#define AR5K_CFG_CLKGD 0x00000400 /* Clock gated (?) */ +#define AR5K_CFG_CLKGD 0x00000400 /* Clock gated (Disable dynamic clock) */ #define AR5K_CFG_TXCNT 0x00007800 /* Tx frame count (?) [5210] */ #define AR5K_CFG_TXCNT_S 11 #define AR5K_CFG_TXFSTAT 0x00008000 /* Tx frame status (?) [5210] */ #define AR5K_CFG_TXFSTRT 0x00010000 /* [5210] */ -#define AR5K_CFG_PCI_THRES 0x00060000 /* [5211+] */ +#define AR5K_CFG_PCI_THRES 0x00060000 /* PCI Master req q threshold [5211+] */ #define AR5K_CFG_PCI_THRES_S 17 /* @@ -162,35 +162,40 @@ /* * Transmit configuration register */ -#define AR5K_TXCFG 0x0030 /* Register Address */ -#define AR5K_TXCFG_SDMAMR 0x00000007 /* DMA size */ -#define AR5K_TXCFG_SDMAMR_S 0 -#define AR5K_TXCFG_B_MODE 0x00000008 /* Set b mode for 5111 (enable 2111) */ -#define AR5K_TXCFG_TXFSTP 0x00000008 /* TX DMA full Stop [5210] */ -#define AR5K_TXCFG_TXFULL 0x000003f0 /* TX Triger level mask */ -#define AR5K_TXCFG_TXFULL_S 4 -#define AR5K_TXCFG_TXFULL_0B 0x00000000 -#define AR5K_TXCFG_TXFULL_64B 0x00000010 -#define AR5K_TXCFG_TXFULL_128B 0x00000020 -#define AR5K_TXCFG_TXFULL_192B 0x00000030 -#define AR5K_TXCFG_TXFULL_256B 0x00000040 -#define AR5K_TXCFG_TXCONT_EN 0x00000080 -#define AR5K_TXCFG_DMASIZE 0x00000100 /* Flag for passing DMA size [5210] */ -#define AR5K_TXCFG_JUMBO_TXE 0x00000400 /* Enable jumbo frames transmition (?) [5211+] */ -#define AR5K_TXCFG_RTSRND 0x00001000 /* [5211+] */ -#define AR5K_TXCFG_FRMPAD_DIS 0x00002000 /* [5211+] */ -#define AR5K_TXCFG_RDY_DIS 0x00004000 /* [5211+] */ +#define AR5K_TXCFG 0x0030 /* Register Address */ +#define AR5K_TXCFG_SDMAMR 0x00000007 /* DMA size (read) */ +#define AR5K_TXCFG_SDMAMR_S 0 +#define AR5K_TXCFG_B_MODE 0x00000008 /* Set b mode for 5111 (enable 2111) */ +#define AR5K_TXCFG_TXFSTP 0x00000008 /* TX DMA full Stop [5210] */ +#define AR5K_TXCFG_TXFULL 0x000003f0 /* TX Triger level mask */ +#define AR5K_TXCFG_TXFULL_S 4 +#define AR5K_TXCFG_TXFULL_0B 0x00000000 +#define AR5K_TXCFG_TXFULL_64B 0x00000010 +#define AR5K_TXCFG_TXFULL_128B 0x00000020 +#define AR5K_TXCFG_TXFULL_192B 0x00000030 +#define AR5K_TXCFG_TXFULL_256B 0x00000040 +#define AR5K_TXCFG_TXCONT_EN 0x00000080 +#define AR5K_TXCFG_DMASIZE 0x00000100 /* Flag for passing DMA size [5210] */ +#define AR5K_TXCFG_JUMBO_DESC_EN 0x00000400 /* Enable jumbo tx descriptors [5211+] */ +#define AR5K_TXCFG_ADHOC_BCN_ATIM 0x00000800 /* Adhoc Beacon ATIM Policy */ +#define AR5K_TXCFG_ATIM_WINDOW_DEF_DIS 0x00001000 /* Disable ATIM window defer [5211+] */ +#define AR5K_TXCFG_RTSRND 0x00001000 /* [5211+] */ +#define AR5K_TXCFG_FRMPAD_DIS 0x00002000 /* [5211+] */ +#define AR5K_TXCFG_RDY_CBR_DIS 0x00004000 /* Ready time CBR disable [5211+] */ +#define AR5K_TXCFG_JUMBO_FRM_MODE 0x00008000 /* Jumbo frame mode [5211+] */ +#define AR5K_TXCFG_DCU_CACHING_DIS 0x00010000 /* Disable DCU caching */ /* * Receive configuration register */ #define AR5K_RXCFG 0x0034 /* Register Address */ -#define AR5K_RXCFG_SDMAMW 0x00000007 /* DMA size */ +#define AR5K_RXCFG_SDMAMW 0x00000007 /* DMA size (write) */ #define AR5K_RXCFG_SDMAMW_S 0 -#define AR5K_RXCFG_DEF_ANTENNA 0x00000008 /* Default antenna */ -#define AR5K_RXCFG_ZLFDMA 0x00000010 /* Zero-length DMA */ -#define AR5K_RXCFG_JUMBO_RXE 0x00000020 /* Enable jumbo frames reception (?) [5211+] */ -#define AR5K_RXCFG_JUMBO_WRAP 0x00000040 /* Wrap jumbo frames (?) [5211+] */ +#define AR5K_RXCFG_ZLFDMA 0x00000008 /* Enable Zero-length frame DMA */ +#define AR5K_RXCFG_DEF_ANTENNA 0x00000010 /* Default antenna (?) */ +#define AR5K_RXCFG_JUMBO_RXE 0x00000020 /* Enable jumbo rx descriptors [5211+] */ +#define AR5K_RXCFG_JUMBO_WRAP 0x00000040 /* Wrap jumbo frames [5211+] */ +#define AR5K_RXCFG_SLE_ENTRY 0x00000080 /* Sleep entry policy */ /* * Receive jumbo descriptor last address register @@ -202,35 +207,35 @@ * MIB control register */ #define AR5K_MIBC 0x0040 /* Register Address */ -#define AR5K_MIBC_COW 0x00000001 -#define AR5K_MIBC_FMC 0x00000002 /* Freeze Mib Counters (?) */ -#define AR5K_MIBC_CMC 0x00000004 /* Clean Mib Counters (?) */ -#define AR5K_MIBC_MCS 0x00000008 +#define AR5K_MIBC_COW 0x00000001 /* Warn test indicator */ +#define AR5K_MIBC_FMC 0x00000002 /* Freeze MIB Counters */ +#define AR5K_MIBC_CMC 0x00000004 /* Clean MIB Counters */ +#define AR5K_MIBC_MCS 0x00000008 /* MIB counter strobe */ /* * Timeout prescale register */ #define AR5K_TOPS 0x0044 -#define AR5K_TOPS_M 0x0000ffff /* [5211+] (?) */ +#define AR5K_TOPS_M 0x0000ffff /* * Receive timeout register (no frame received) */ #define AR5K_RXNOFRM 0x0048 -#define AR5K_RXNOFRM_M 0x000003ff /* [5211+] (?) */ +#define AR5K_RXNOFRM_M 0x000003ff /* * Transmit timeout register (no frame sent) */ #define AR5K_TXNOFRM 0x004c -#define AR5K_TXNOFRM_M 0x000003ff /* [5211+] (?) */ -#define AR5K_TXNOFRM_QCU 0x000ffc00 /* [5211+] (?) */ +#define AR5K_TXNOFRM_M 0x000003ff +#define AR5K_TXNOFRM_QCU 0x000ffc00 /* * Receive frame gap timeout register */ #define AR5K_RPGTO 0x0050 -#define AR5K_RPGTO_M 0x000003ff /* [5211+] (?) */ +#define AR5K_RPGTO_M 0x000003ff /* * Receive frame count limit register @@ -241,6 +246,7 @@ /* * Misc settings register + * (reserved0-3) */ #define AR5K_MISC 0x0058 /* Register Address */ #define AR5K_MISC_DMA_OBS_M 0x000001e0 @@ -256,6 +262,7 @@ /* * QCU/DCU clock gating register (5311) + * (reserved4-5) */ #define AR5K_QCUDCU_CLKGT 0x005c /* Register Address (?) */ #define AR5K_QCUDCU_CLKGT_QCU 0x0000ffff /* Mask for QCU clock */ @@ -284,18 +291,18 @@ #define AR5K_ISR_TXEOL 0x00000400 /* Empty TX descriptor */ #define AR5K_ISR_TXURN 0x00000800 /* Transmit FIFO underrun */ #define AR5K_ISR_MIB 0x00001000 /* Update MIB counters */ -#define AR5K_ISR_SWI 0x00002000 /* Software interrupt (?) */ +#define AR5K_ISR_SWI 0x00002000 /* Software interrupt */ #define AR5K_ISR_RXPHY 0x00004000 /* PHY error */ -#define AR5K_ISR_RXKCM 0x00008000 +#define AR5K_ISR_RXKCM 0x00008000 /* RX Key cache miss */ #define AR5K_ISR_SWBA 0x00010000 /* Software beacon alert */ #define AR5K_ISR_BRSSI 0x00020000 #define AR5K_ISR_BMISS 0x00040000 /* Beacon missed */ #define AR5K_ISR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] */ #define AR5K_ISR_BNR 0x00100000 /* Beacon not ready [5211+] */ -#define AR5K_ISR_MCABT 0x00100000 /* [5210] */ -#define AR5K_ISR_RXCHIRP 0x00200000 /* [5212+] */ -#define AR5K_ISR_SSERR 0x00200000 /* [5210] */ -#define AR5K_ISR_DPERR 0x00400000 /* [5210] */ +#define AR5K_ISR_MCABT 0x00100000 /* Master Cycle Abort [5210] */ +#define AR5K_ISR_RXCHIRP 0x00200000 /* CHIRP Received [5212+] */ +#define AR5K_ISR_SSERR 0x00200000 /* Signaled System Error [5210] */ +#define AR5K_ISR_DPERR 0x00400000 /* Det par Error (?) [5210] */ #define AR5K_ISR_TIM 0x00800000 /* [5210] */ #define AR5K_ISR_BCNMISC 0x00800000 /* [5212+] */ #define AR5K_ISR_GPIO 0x01000000 /* GPIO (rf kill)*/ @@ -320,14 +327,14 @@ #define AR5K_SISR2 0x008c /* Register Address [5211+] */ #define AR5K_SISR2_QCU_TXURN 0x000003ff /* Mask for QCU_TXURN */ -#define AR5K_SISR2_MCABT 0x00100000 -#define AR5K_SISR2_SSERR 0x00200000 -#define AR5K_SISR2_DPERR 0x00400000 +#define AR5K_SISR2_MCABT 0x00100000 /* Master Cycle Abort */ +#define AR5K_SISR2_SSERR 0x00200000 /* Signaled System Error */ +#define AR5K_SISR2_DPERR 0x00400000 /* Det par Error (?) */ #define AR5K_SISR2_TIM 0x01000000 /* [5212+] */ #define AR5K_SISR2_CAB_END 0x02000000 /* [5212+] */ -#define AR5K_SISR2_DTIM_SYNC 0x04000000 /* [5212+] */ -#define AR5K_SISR2_BCN_TIMEOUT 0x08000000 /* [5212+] */ -#define AR5K_SISR2_CAB_TIMEOUT 0x10000000 /* [5212+] */ +#define AR5K_SISR2_DTIM_SYNC 0x04000000 /* DTIM sync lost [5212+] */ +#define AR5K_SISR2_BCN_TIMEOUT 0x08000000 /* Beacon Timeout [5212+] */ +#define AR5K_SISR2_CAB_TIMEOUT 0x10000000 /* CAB Timeout [5212+] */ #define AR5K_SISR2_DTIM 0x20000000 /* [5212+] */ #define AR5K_SISR3 0x0090 /* Register Address [5211+] */ @@ -368,18 +375,18 @@ #define AR5K_IMR_TXEOL 0x00000400 /* Empty TX descriptor*/ #define AR5K_IMR_TXURN 0x00000800 /* Transmit FIFO underrun*/ #define AR5K_IMR_MIB 0x00001000 /* Update MIB counters*/ -#define AR5K_IMR_SWI 0x00002000 +#define AR5K_IMR_SWI 0x00002000 /* Software interrupt */ #define AR5K_IMR_RXPHY 0x00004000 /* PHY error*/ -#define AR5K_IMR_RXKCM 0x00008000 +#define AR5K_IMR_RXKCM 0x00008000 /* RX Key cache miss */ #define AR5K_IMR_SWBA 0x00010000 /* Software beacon alert*/ #define AR5K_IMR_BRSSI 0x00020000 #define AR5K_IMR_BMISS 0x00040000 /* Beacon missed*/ #define AR5K_IMR_HIUERR 0x00080000 /* Host Interface Unit error [5211+] */ #define AR5K_IMR_BNR 0x00100000 /* Beacon not ready [5211+] */ -#define AR5K_IMR_MCABT 0x00100000 /* [5210] */ -#define AR5K_IMR_RXCHIRP 0x00200000 /* [5212+]*/ -#define AR5K_IMR_SSERR 0x00200000 /* [5210] */ -#define AR5K_IMR_DPERR 0x00400000 /* [5210] */ +#define AR5K_IMR_MCABT 0x00100000 /* Master Cycle Abort [5210] */ +#define AR5K_IMR_RXCHIRP 0x00200000 /* CHIRP Received [5212+]*/ +#define AR5K_IMR_SSERR 0x00200000 /* Signaled System Error [5210] */ +#define AR5K_IMR_DPERR 0x00400000 /* Det par Error (?) [5210] */ #define AR5K_IMR_TIM 0x00800000 /* [5211+] */ #define AR5K_IMR_BCNMISC 0x00800000 /* [5212+] */ #define AR5K_IMR_GPIO 0x01000000 /* GPIO (rf kill)*/ @@ -405,14 +412,14 @@ #define AR5K_SIMR2 0x00ac /* Register Address [5211+] */ #define AR5K_SIMR2_QCU_TXURN 0x000003ff /* Mask for QCU_TXURN */ #define AR5K_SIMR2_QCU_TXURN_S 0 -#define AR5K_SIMR2_MCABT 0x00100000 -#define AR5K_SIMR2_SSERR 0x00200000 -#define AR5K_SIMR2_DPERR 0x00400000 +#define AR5K_SIMR2_MCABT 0x00100000 /* Master Cycle Abort */ +#define AR5K_SIMR2_SSERR 0x00200000 /* Signaled System Error */ +#define AR5K_SIMR2_DPERR 0x00400000 /* Det par Error (?) */ #define AR5K_SIMR2_TIM 0x01000000 /* [5212+] */ #define AR5K_SIMR2_CAB_END 0x02000000 /* [5212+] */ -#define AR5K_SIMR2_DTIM_SYNC 0x04000000 /* [5212+] */ -#define AR5K_SIMR2_BCN_TIMEOUT 0x08000000 /* [5212+] */ -#define AR5K_SIMR2_CAB_TIMEOUT 0x10000000 /* [5212+] */ +#define AR5K_SIMR2_DTIM_SYNC 0x04000000 /* DTIM Sync lost [5212+] */ +#define AR5K_SIMR2_BCN_TIMEOUT 0x08000000 /* Beacon Timeout [5212+] */ +#define AR5K_SIMR2_CAB_TIMEOUT 0x10000000 /* CAB Timeout [5212+] */ #define AR5K_SIMR2_DTIM 0x20000000 /* [5212+] */ #define AR5K_SIMR3 0x00b0 /* Register Address [5211+] */ @@ -425,23 +432,69 @@ #define AR5K_SIMR4_QTRIG 0x000003ff /* Mask for QTRIG */ #define AR5K_SIMR4_QTRIG_S 0 +/* + * DMA Debug registers 0-7 + * 0xe0 - 0xfc + */ /* * Decompression mask registers [5212+] */ -#define AR5K_DCM_ADDR 0x0400 /*Decompression mask address (?)*/ -#define AR5K_DCM_DATA 0x0404 /*Decompression mask data (?)*/ +#define AR5K_DCM_ADDR 0x0400 /*Decompression mask address (index) */ +#define AR5K_DCM_DATA 0x0404 /*Decompression mask data */ + +/* + * Wake On Wireless pattern control register [5212+] + */ +#define AR5K_WOW_PCFG 0x0410 /* Register Address */ +#define AR5K_WOW_PCFG_PAT_MATCH_EN 0x00000001 /* Pattern match enable */ +#define AR5K_WOW_PCFG_LONG_FRAME_POL 0x00000002 /* Long frame policy */ +#define AR5K_WOW_PCFG_WOBMISS 0x00000004 /* Wake on bea(con) miss (?) */ +#define AR5K_WOW_PCFG_PAT_0_EN 0x00000100 /* Enable pattern 0 */ +#define AR5K_WOW_PCFG_PAT_1_EN 0x00000200 /* Enable pattern 1 */ +#define AR5K_WOW_PCFG_PAT_2_EN 0x00000400 /* Enable pattern 2 */ +#define AR5K_WOW_PCFG_PAT_3_EN 0x00000800 /* Enable pattern 3 */ +#define AR5K_WOW_PCFG_PAT_4_EN 0x00001000 /* Enable pattern 4 */ +#define AR5K_WOW_PCFG_PAT_5_EN 0x00002000 /* Enable pattern 5 */ + +/* + * Wake On Wireless pattern index register (?) [5212+] + */ +#define AR5K_WOW_PAT_IDX 0x0414 + +/* + * Wake On Wireless pattern data register [5212+] + */ +#define AR5K_WOW_PAT_DATA 0x0418 /* Register Address */ +#define AR5K_WOW_PAT_DATA_0_3_V 0x00000001 /* Pattern 0, 3 value */ +#define AR5K_WOW_PAT_DATA_1_4_V 0x00000100 /* Pattern 1, 4 value */ +#define AR5K_WOW_PAT_DATA_2_5_V 0x00010000 /* Pattern 2, 5 value */ +#define AR5K_WOW_PAT_DATA_0_3_M 0x01000000 /* Pattern 0, 3 mask */ +#define AR5K_WOW_PAT_DATA_1_4_M 0x04000000 /* Pattern 1, 4 mask */ +#define AR5K_WOW_PAT_DATA_2_5_M 0x10000000 /* Pattern 2, 5 mask */ /* * Decompression configuration registers [5212+] */ -#define AR5K_DCCFG 0x0420 +#define AR5K_DCCFG 0x0420 /* Register Address */ +#define AR5K_DCCFG_GLOBAL_EN 0x00000001 /* Enable decompression on all queues */ +#define AR5K_DCCFG_BYPASS_EN 0x00000002 /* Bypass decompression */ +#define AR5K_DCCFG_BCAST_EN 0x00000004 /* Enable decompression for bcast frames */ +#define AR5K_DCCFG_MCAST_EN 0x00000008 /* Enable decompression for mcast frames */ /* * Compression configuration registers [5212+] */ -#define AR5K_CCFG 0x0600 -#define AR5K_CCFG_CUP 0x0604 +#define AR5K_CCFG 0x0600 /* Register Address */ +#define AR5K_CCFG_WINDOW_SIZE 0x00000007 /* Compression window size */ +#define AR5K_CCFG_CPC_EN 0x00000008 /* Enable performance counters */ + +#define AR5K_CCFG_CCU 0x0604 /* Register Address */ +#define AR5K_CCFG_CCU_CUP_EN 0x00000001 /* CCU Catchup enable */ +#define AR5K_CCFG_CCU_CREDIT 0x00000002 /* CCU Credit (field) */ +#define AR5K_CCFG_CCU_CD_THRES 0x00000080 /* CCU Cyc(lic?) debt threshold (field) */ +#define AR5K_CCFG_CCU_CUP_LCNT 0x00010000 /* CCU Catchup lit(?) count */ +#define AR5K_CCFG_CCU_INIT 0x00100200 /* Initial value during reset */ /* * Compression performance counter registers [5212+] @@ -450,7 +503,7 @@ #define AR5K_CPC1 0x0614 /* Compression performance counter 1*/ #define AR5K_CPC2 0x0618 /* Compression performance counter 2 */ #define AR5K_CPC3 0x061c /* Compression performance counter 3 */ -#define AR5K_CPCORN 0x0620 /* Compression performance overrun (?) */ +#define AR5K_CPCOVF 0x0620 /* Compression performance overflow */ /* @@ -466,8 +519,6 @@ * set/clear, which contain status for all queues (we shift by 1 for each * queue). To access these registers easily we define some macros here * that are used inside HAL. For more infos check out *_tx_queue functs. - * - * TODO: Boundary checking on macros (here?) */ /* @@ -513,7 +564,6 @@ #define AR5K_QCU_RDYTIMECFG_BASE 0x0900 /* Register Address - Queue0 RDYTIMECFG */ #define AR5K_QCU_RDYTIMECFG_INTVAL 0x00ffffff /* Ready time interval mask */ #define AR5K_QCU_RDYTIMECFG_INTVAL_S 0 -#define AR5K_QCU_RDYTIMECFG_DURATION 0x00ffffff /* Ready time duration mask */ #define AR5K_QCU_RDYTIMECFG_ENABLE 0x01000000 /* Ready time enable mask */ #define AR5K_QUEUE_RDYTIMECFG(_q) AR5K_QUEUE_REG(AR5K_QCU_RDYTIMECFG_BASE, _q) @@ -534,19 +584,20 @@ */ #define AR5K_QCU_MISC_BASE 0x09c0 /* Register Address -Queue0 MISC */ #define AR5K_QCU_MISC_FRSHED_M 0x0000000f /* Frame sheduling mask */ -#define AR5K_QCU_MISC_FRSHED_ASAP 0 /* ASAP */ -#define AR5K_QCU_MISC_FRSHED_CBR 1 /* Constant Bit Rate */ -#define AR5K_QCU_MISC_FRSHED_DBA_GT 2 /* DMA Beacon alert gated (?) */ -#define AR5K_QCU_MISC_FRSHED_TIM_GT 3 /* Time gated (?) */ +#define AR5K_QCU_MISC_FRSHED_ASAP 0 /* ASAP */ +#define AR5K_QCU_MISC_FRSHED_CBR 1 /* Constant Bit Rate */ +#define AR5K_QCU_MISC_FRSHED_DBA_GT 2 /* DMA Beacon alert gated (?) */ +#define AR5K_QCU_MISC_FRSHED_TIM_GT 3 /* Time gated (?) */ #define AR5K_QCU_MISC_FRSHED_BCN_SENT_GT 4 /* Beacon sent gated (?) */ #define AR5K_QCU_MISC_ONESHOT_ENABLE 0x00000010 /* Oneshot enable */ #define AR5K_QCU_MISC_CBREXP 0x00000020 /* CBR expired (normal queue) */ #define AR5K_QCU_MISC_CBREXP_BCN 0x00000040 /* CBR expired (beacon queue) */ -#define AR5K_QCU_MISC_BCN_ENABLE 0x00000080 /* Beacons enabled */ -#define AR5K_QCU_MISC_CBR_THRES_ENABLE 0x00000100 /* CBR threshold enabled (?) */ -#define AR5K_QCU_MISC_TXE 0x00000200 /* TXE reset when RDYTIME enalbed (?) */ -#define AR5K_QCU_MISC_CBR 0x00000400 /* CBR threshold reset (?) */ -#define AR5K_QCU_MISC_DCU_EARLY 0x00000800 /* DCU reset (?) */ +#define AR5K_QCU_MISC_BCN_ENABLE 0x00000080 /* Enable Beacon use */ +#define AR5K_QCU_MISC_CBR_THRES_ENABLE 0x00000100 /* CBR threshold enabled */ +#define AR5K_QCU_MISC_RDY_VEOL_POLICY 0x00000200 /* TXE reset when RDYTIME enalbed */ +#define AR5K_QCU_MISC_CBR_RESET_CNT 0x00000400 /* CBR threshold (counter) reset */ +#define AR5K_QCU_MISC_DCU_EARLY 0x00000800 /* DCU early termination */ +#define AR5K_QCU_MISC_DCU_CMP_EN 0x00001000 /* Enable frame compression */ #define AR5K_QUEUE_MISC(_q) AR5K_QUEUE_REG(AR5K_QCU_MISC_BASE, _q) @@ -555,7 +606,7 @@ */ #define AR5K_QCU_STS_BASE 0x0a00 /* Register Address - Queue0 STS */ #define AR5K_QCU_STS_FRMPENDCNT 0x00000003 /* Frames pending counter */ -#define AR5K_QCU_STS_CBREXPCNT 0x0000ff00 /* CBR expired counter (?) */ +#define AR5K_QCU_STS_CBREXPCNT 0x0000ff00 /* CBR expired counter */ #define AR5K_QUEUE_STATUS(_q) AR5K_QUEUE_REG(AR5K_QCU_STS_BASE, _q) /* @@ -569,9 +620,11 @@ */ #define AR5K_QCU_CBB_SELECT 0x0b00 #define AR5K_QCU_CBB_ADDR 0x0b04 +#define AR5K_QCU_CBB_ADDR_S 9 /* * QCU compression buffer configuration register [5212+] + * (buffer size) */ #define AR5K_QCU_CBCFG 0x0b08 @@ -652,80 +705,100 @@ * No lockout means there is no special handling. */ #define AR5K_DCU_MISC_BASE 0x1100 /* Register Address -Queue0 DCU_MISC */ -#define AR5K_DCU_MISC_BACKOFF 0x000007ff /* Mask for backoff setting (?) */ +#define AR5K_DCU_MISC_BACKOFF 0x000007ff /* Mask for backoff threshold */ #define AR5K_DCU_MISC_BACKOFF_FRAG 0x00000200 /* Enable backoff while bursting */ -#define AR5K_DCU_MISC_HCFPOLL_ENABLE 0x00000800 /* CF - Poll (?) */ -#define AR5K_DCU_MISC_BACKOFF_PERSIST 0x00001000 /* Persistent backoff (?) */ -#define AR5K_DCU_MISC_FRMPRFTCH_ENABLE 0x00002000 /* Enable frame pre-fetch (?) */ +#define AR5K_DCU_MISC_HCFPOLL_ENABLE 0x00000800 /* CF - Poll enable */ +#define AR5K_DCU_MISC_BACKOFF_PERSIST 0x00001000 /* Persistent backoff */ +#define AR5K_DCU_MISC_FRMPRFTCH_ENABLE 0x00002000 /* Enable frame pre-fetch */ #define AR5K_DCU_MISC_VIRTCOL 0x0000c000 /* Mask for Virtual Collision (?) */ -#define AR5K_DCU_MISC_VIRTCOL_NORMAL 0 -#define AR5K_DCU_MISC_VIRTCOL_MODIFIED 1 -#define AR5K_DCU_MISC_VIRTCOL_IGNORE 2 -#define AR5K_DCU_MISC_BCN_ENABLE 0x00010000 /* Beacon enable (?) */ +#define AR5K_DCU_MISC_VIRTCOL_NORMAL 0 +#define AR5K_DCU_MISC_VIRTCOL_MODIFIED 1 +#define AR5K_DCU_MISC_VIRTCOL_IGNORE 2 +#define AR5K_DCU_MISC_BCN_ENABLE 0x00010000 /* Enable Beacon use */ #define AR5K_DCU_MISC_ARBLOCK_CTL 0x00060000 /* Arbiter lockout control mask */ #define AR5K_DCU_MISC_ARBLOCK_CTL_S 17 -#define AR5K_DCU_MISC_ARBLOCK_CTL_NONE 0 /* No arbiter lockout */ +#define AR5K_DCU_MISC_ARBLOCK_CTL_NONE 0 /* No arbiter lockout */ #define AR5K_DCU_MISC_ARBLOCK_CTL_INTFRM 1 /* Intra-frame lockout */ #define AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL 2 /* Global lockout */ -#define AR5K_DCU_MISC_ARBLOCK_IGNORE 0x00080000 -#define AR5K_DCU_MISC_SEQ_NUM_INCR_DIS 0x00100000 /* Disable sequence number increment (?) */ -#define AR5K_DCU_MISC_POST_FR_BKOFF_DIS 0x00200000 /* Disable post-frame backoff (?) */ -#define AR5K_DCU_MISC_VIRT_COLL_POLICY 0x00400000 /* Virtual Collision policy (?) */ -#define AR5K_DCU_MISC_BLOWN_IFS_POLICY 0x00800000 +#define AR5K_DCU_MISC_ARBLOCK_IGNORE 0x00080000 /* Ignore Arbiter lockout */ +#define AR5K_DCU_MISC_SEQ_NUM_INCR_DIS 0x00100000 /* Disable sequence number increment */ +#define AR5K_DCU_MISC_POST_FR_BKOFF_DIS 0x00200000 /* Disable post-frame backoff */ +#define AR5K_DCU_MISC_VIRT_COLL_POLICY 0x00400000 /* Virtual Collision cw policy */ +#define AR5K_DCU_MISC_BLOWN_IFS_POLICY 0x00800000 /* Blown IFS policy (?) */ #define AR5K_DCU_MISC_SEQNUM_CTL 0x01000000 /* Sequence number control (?) */ #define AR5K_QUEUE_DFS_MISC(_q) AR5K_QUEUE_REG(AR5K_DCU_MISC_BASE, _q) /* * DCU frame sequence number registers */ -#define AR5K_DCU_SEQNUM_BASE 0x1140 -#define AR5K_DCU_SEQNUM_M 0x00000fff +#define AR5K_DCU_SEQNUM_BASE 0x1140 +#define AR5K_DCU_SEQNUM_M 0x00000fff #define AR5K_QUEUE_DFS_SEQNUM(_q) AR5K_QUEUE_REG(AR5K_DCU_SEQNUM_BASE, _q) /* - * DCU global IFS SIFS registers + * DCU global IFS SIFS register */ #define AR5K_DCU_GBL_IFS_SIFS 0x1030 #define AR5K_DCU_GBL_IFS_SIFS_M 0x0000ffff /* - * DCU global IFS slot interval registers + * DCU global IFS slot interval register */ #define AR5K_DCU_GBL_IFS_SLOT 0x1070 #define AR5K_DCU_GBL_IFS_SLOT_M 0x0000ffff /* - * DCU global IFS EIFS registers + * DCU global IFS EIFS register */ #define AR5K_DCU_GBL_IFS_EIFS 0x10b0 #define AR5K_DCU_GBL_IFS_EIFS_M 0x0000ffff /* - * DCU global IFS misc registers + * DCU global IFS misc register + * + * LFSR stands for Linear Feedback Shift Register + * and it's used for generating pseudo-random + * number sequences. + * + * (If i understand corectly, random numbers are + * used for idle sensing -multiplied with cwmin/max etc-) */ #define AR5K_DCU_GBL_IFS_MISC 0x10f0 /* Register Address */ -#define AR5K_DCU_GBL_IFS_MISC_LFSR_SLICE 0x00000007 -#define AR5K_DCU_GBL_IFS_MISC_TURBO_MODE 0x00000008 /* Turbo mode (?) */ -#define AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC 0x000003f0 /* SIFS Duration mask (?) */ -#define AR5K_DCU_GBL_IFS_MISC_USEC_DUR 0x000ffc00 -#define AR5K_DCU_GBL_IFS_MISC_DCU_ARB_DELAY 0x00300000 +#define AR5K_DCU_GBL_IFS_MISC_LFSR_SLICE 0x00000007 /* LFSR Slice Select */ +#define AR5K_DCU_GBL_IFS_MISC_TURBO_MODE 0x00000008 /* Turbo mode */ +#define AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC 0x000003f0 /* SIFS Duration mask */ +#define AR5K_DCU_GBL_IFS_MISC_USEC_DUR 0x000ffc00 /* USEC Duration mask */ +#define AR5K_DCU_GBL_IFS_MISC_DCU_ARB_DELAY 0x00300000 /* DCU Arbiter delay mask */ +#define AR5K_DCU_GBL_IFS_MISC_SIFS_CNT_RST 0x00400000 /* SIFC cnt reset policy (?) */ +#define AR5K_DCU_GBL_IFS_MISC_AIFS_CNT_RST 0x00800000 /* AIFS cnt reset policy (?) */ +#define AR5K_DCU_GBL_IFS_MISC_RND_LFSR_SL_DIS 0x01000000 /* Disable random LFSR slice */ /* * DCU frame prefetch control register */ -#define AR5K_DCU_FP 0x1230 +#define AR5K_DCU_FP 0x1230 /* Register Address */ +#define AR5K_DCU_FP_NOBURST_DCU_EN 0x00000001 /* Enable non-burst prefetch on DCU (?) */ +#define AR5K_DCU_FP_NOBURST_EN 0x00000010 /* Enable non-burst prefetch (?) */ +#define AR5K_DCU_FP_BURST_DCU_EN 0x00000020 /* Enable burst prefetch on DCU (?) */ /* * DCU transmit pause control/status register */ #define AR5K_DCU_TXP 0x1270 /* Register Address */ -#define AR5K_DCU_TXP_M 0x000003ff /* Tx pause mask (?) */ -#define AR5K_DCU_TXP_STATUS 0x00010000 /* Tx pause status (?) */ +#define AR5K_DCU_TXP_M 0x000003ff /* Tx pause mask */ +#define AR5K_DCU_TXP_STATUS 0x00010000 /* Tx pause status */ + +/* + * DCU transmit filter table 0 (32 entries) + */ +#define AR5K_DCU_TX_FILTER_0_BASE 0x1038 +#define AR5K_DCU_TX_FILTER_0(_n) (AR5K_DCU_TX_FILTER_0_BASE + (_n * 64)) /* - * DCU transmit filter register + * DCU transmit filter table 1 (16 entries) */ -#define AR5K_DCU_TX_FILTER 0x1038 +#define AR5K_DCU_TX_FILTER_1_BASE 0x103c +#define AR5K_DCU_TX_FILTER_1(_n) (AR5K_DCU_TX_FILTER_1_BASE + ((_n - 32) * 64)) /* * DCU clear transmit filter register @@ -739,9 +812,6 @@ /* * Reset control register - * - * 4 and 8 are not used in 5211/5212 and - * 2 means "baseband reset" on 5211/5212. */ #define AR5K_RESET_CTL 0x4000 /* Register Address */ #define AR5K_RESET_CTL_PCU 0x00000001 /* Protocol Control Unit reset */ @@ -765,6 +835,7 @@ #define AR5K_SLEEP_CTL_SLE_SLP 0x00010000 /* Force chip sleep */ #define AR5K_SLEEP_CTL_SLE_ALLOW 0x00020000 #define AR5K_SLEEP_CTL_SLE_UNITS 0x00000008 /* [5211+] */ +/* more bits */ /* * Interrupt pending register @@ -776,13 +847,14 @@ * Sleep force register */ #define AR5K_SFR 0x400c -#define AR5K_SFR_M 0x00000001 +#define AR5K_SFR_EN 0x00000001 /* * PCI configuration register */ #define AR5K_PCICFG 0x4010 /* Register Address */ #define AR5K_PCICFG_EEAE 0x00000001 /* Eeprom access enable [5210] */ +#define AR5K_PCICFG_SLEEP_CLOCK_EN 0x00000002 /* Enable sleep clock (?) */ #define AR5K_PCICFG_CLKRUNEN 0x00000004 /* CLKRUN enable [5211+] */ #define AR5K_PCICFG_EESIZE 0x00000018 /* Mask for EEPROM size [5211+] */ #define AR5K_PCICFG_EESIZE_S 3 @@ -798,19 +870,21 @@ #define AR5K_PCICFG_CBEFIX_DIS 0x00000400 /* Disable CBE fix (?) */ #define AR5K_PCICFG_SL_INTEN 0x00000800 /* Enable interrupts when asleep (?) */ #define AR5K_PCICFG_LED_BCTL 0x00001000 /* Led blink (?) [5210] */ -#define AR5K_PCICFG_SL_INPEN 0x00002800 /* Sleep even whith pending interrupts (?) */ +#define AR5K_PCICFG_UNK 0x00001000 /* Passed on some parts durring attach (?) */ +#define AR5K_PCICFG_SL_INPEN 0x00002000 /* Sleep even whith pending interrupts (?) */ #define AR5K_PCICFG_SPWR_DN 0x00010000 /* Mask for power status */ #define AR5K_PCICFG_LEDMODE 0x000e0000 /* Ledmode [5211+] */ #define AR5K_PCICFG_LEDMODE_PROP 0x00000000 /* Blink on standard traffic [5211+] */ #define AR5K_PCICFG_LEDMODE_PROM 0x00020000 /* Default mode (blink on any traffic) [5211+] */ #define AR5K_PCICFG_LEDMODE_PWR 0x00040000 /* Some other blinking mode (?) [5211+] */ #define AR5K_PCICFG_LEDMODE_RAND 0x00060000 /* Random blinking (?) [5211+] */ -#define AR5K_PCICFG_LEDBLINK 0x00700000 +#define AR5K_PCICFG_LEDBLINK 0x00700000 /* Led blink rate */ #define AR5K_PCICFG_LEDBLINK_S 20 -#define AR5K_PCICFG_LEDSLOW 0x00800000 /* Slow led blink rate (?) [5211+] */ +#define AR5K_PCICFG_LEDSLOW 0x00800000 /* Slowest led blink rate [5211+] */ #define AR5K_PCICFG_LEDSTATE \ (AR5K_PCICFG_LED | AR5K_PCICFG_LEDMODE | \ AR5K_PCICFG_LEDBLINK | AR5K_PCICFG_LEDSLOW) +#define AR5K_PCICFG_SLEEP_CLOCK_RATE 0x03000000 /* Sleep clock rate (field) */ /* * "General Purpose Input/Output" (GPIO) control register @@ -947,7 +1021,7 @@ #define AR5K_EEPROM_VERSION_4_4 0x4004 #define AR5K_EEPROM_VERSION_4_5 0x4005 #define AR5K_EEPROM_VERSION_4_6 0x4006 /* has ee_scaled_cck_delta */ -#define AR5K_EEPROM_VERSION_4_7 0x3007 +#define AR5K_EEPROM_VERSION_4_7 0x4007 #define AR5K_EEPROM_MODE_11A 0 #define AR5K_EEPROM_MODE_11B 1 @@ -1023,10 +1097,14 @@ #define AR5K_EEPROM_STAT_WRDONE 0x00000008 /* EEPROM write successful */ /* - * EEPROM config register (?) + * EEPROM config register */ -#define AR5K_EEPROM_CFG 0x6010 - +#define AR5K_EEPROM_CFG 0x6010 /* Register Addres */ +#define AR5K_EEPROM_CFG_SIZE_OVR 0x00000001 +#define AR5K_EEPROM_CFG_WR_WAIT_DIS 0x00000004 /* Disable write wait */ +#define AR5K_EEPROM_CFG_CLK_RATE 0x00000018 /* Clock rate */ +#define AR5K_EEPROM_CFG_PROT_KEY 0x00ffff00 /* Protectio key */ +#define AR5K_EEPROM_CFG_LIND_EN 0x01000000 /* Enable length indicator (?) */ /* @@ -1050,7 +1128,7 @@ #define AR5K_STA_ID1 0x8004 /* Register Address */ #define AR5K_STA_ID1_AP 0x00010000 /* Set AP mode */ #define AR5K_STA_ID1_ADHOC 0x00020000 /* Set Ad-Hoc mode */ -#define AR5K_STA_ID1_PWR_SV 0x00040000 /* Power save reporting (?) */ +#define AR5K_STA_ID1_PWR_SV 0x00040000 /* Power save reporting */ #define AR5K_STA_ID1_NO_KEYSRCH 0x00080000 /* No key search */ #define AR5K_STA_ID1_NO_PSPOLL 0x00100000 /* No power save polling [5210] */ #define AR5K_STA_ID1_PCF_5211 0x00100000 /* Enable PCF on [5211+] */ @@ -1059,9 +1137,13 @@ AR5K_STA_ID1_PCF_5210 : AR5K_STA_ID1_PCF_5211) #define AR5K_STA_ID1_DEFAULT_ANTENNA 0x00200000 /* Use default antenna */ #define AR5K_STA_ID1_DESC_ANTENNA 0x00400000 /* Update antenna from descriptor */ -#define AR5K_STA_ID1_RTS_DEF_ANTENNA 0x00800000 /* Use default antenna for RTS (?) */ -#define AR5K_STA_ID1_ACKCTS_6MB 0x01000000 /* Use 6Mbit/s for ACK/CTS (?) */ +#define AR5K_STA_ID1_RTS_DEF_ANTENNA 0x00800000 /* Use default antenna for RTS */ +#define AR5K_STA_ID1_ACKCTS_6MB 0x01000000 /* Use 6Mbit/s for ACK/CTS */ #define AR5K_STA_ID1_BASE_RATE_11B 0x02000000 /* Use 11b base rate (for ACK/CTS ?) [5211+] */ +#define AR5K_STA_ID1_SELF_GEN_SECTORE 0x04000000 /* Self generate sectore (?) */ +#define AR5K_STA_ID1_CRYPT_MIC_EN 0x08000000 /* Enable MIC */ +#define AR5K_STA_ID1_KEYSRCH_MODE 0x10000000 /* Keysearch mode (?) */ +#define AR5K_STA_ID1_PRESERVE_SEQ_NUM 0x20000000 /* Preserve sequence number */ /* * First BSSID register (MAC address, lower 32bits) @@ -1117,7 +1199,7 @@ * * Retry limit register for 5210 (no QCU/DCU so it's done in PCU) */ -#define AR5K_NODCU_RETRY_LMT 0x801c /*Register Address */ +#define AR5K_NODCU_RETRY_LMT 0x801c /* Register Address */ #define AR5K_NODCU_RETRY_LMT_SH_RETRY 0x0000000f /* Short retry limit mask */ #define AR5K_NODCU_RETRY_LMT_SH_RETRY_S 0 #define AR5K_NODCU_RETRY_LMT_LG_RETRY 0x000000f0 /* Long retry mask */ @@ -1136,9 +1218,9 @@ #define AR5K_USEC_5211 0x801c /* Register Address [5211+] */ #define AR5K_USEC (ah->ah_version == AR5K_AR5210 ? \ AR5K_USEC_5210 : AR5K_USEC_5211) -#define AR5K_USEC_1 0x0000007f +#define AR5K_USEC_1 0x0000007f /* clock cycles for 1us */ #define AR5K_USEC_1_S 0 -#define AR5K_USEC_32 0x00003f80 +#define AR5K_USEC_32 0x00003f80 /* clock cycles for 1us while on 32Mhz clock */ #define AR5K_USEC_32_S 7 #define AR5K_USEC_TX_LATENCY_5211 0x007fc000 #define AR5K_USEC_TX_LATENCY_5211_S 14 @@ -1152,16 +1234,16 @@ /* * PCU beacon control register */ -#define AR5K_BEACON_5210 0x8024 -#define AR5K_BEACON_5211 0x8020 +#define AR5K_BEACON_5210 0x8024 /*Register Address [5210] */ +#define AR5K_BEACON_5211 0x8020 /*Register Address [5211+] */ #define AR5K_BEACON (ah->ah_version == AR5K_AR5210 ? \ AR5K_BEACON_5210 : AR5K_BEACON_5211) -#define AR5K_BEACON_PERIOD 0x0000ffff +#define AR5K_BEACON_PERIOD 0x0000ffff /* Mask for beacon period */ #define AR5K_BEACON_PERIOD_S 0 -#define AR5K_BEACON_TIM 0x007f0000 +#define AR5K_BEACON_TIM 0x007f0000 /* Mask for TIM offset */ #define AR5K_BEACON_TIM_S 16 -#define AR5K_BEACON_ENABLE 0x00800000 -#define AR5K_BEACON_RESET_TSF 0x01000000 +#define AR5K_BEACON_ENABLE 0x00800000 /* Enable beacons */ +#define AR5K_BEACON_RESET_TSF 0x01000000 /* Force TSF reset */ /* * CFP period register @@ -1234,7 +1316,6 @@ /* * Receive filter register - * TODO: Get these out of ar5xxx.h on ath5k */ #define AR5K_RX_FILTER_5210 0x804c /* Register Address [5210] */ #define AR5K_RX_FILTER_5211 0x803c /* Register Address [5211+] */ @@ -1307,11 +1388,11 @@ #define AR5K_DIAG_SW_5211 0x8048 /* Register Address [5211+] */ #define AR5K_DIAG_SW (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_5210 : AR5K_DIAG_SW_5211) -#define AR5K_DIAG_SW_DIS_WEP_ACK 0x00000001 -#define AR5K_DIAG_SW_DIS_ACK 0x00000002 /* Disable ACKs (?) */ -#define AR5K_DIAG_SW_DIS_CTS 0x00000004 /* Disable CTSs (?) */ -#define AR5K_DIAG_SW_DIS_ENC 0x00000008 /* Disable encryption (?) */ -#define AR5K_DIAG_SW_DIS_DEC 0x00000010 /* Disable decryption (?) */ +#define AR5K_DIAG_SW_DIS_WEP_ACK 0x00000001 /* Disable ACKs if WEP key is invalid */ +#define AR5K_DIAG_SW_DIS_ACK 0x00000002 /* Disable ACKs */ +#define AR5K_DIAG_SW_DIS_CTS 0x00000004 /* Disable CTSs */ +#define AR5K_DIAG_SW_DIS_ENC 0x00000008 /* Disable encryption */ +#define AR5K_DIAG_SW_DIS_DEC 0x00000010 /* Disable decryption */ #define AR5K_DIAG_SW_DIS_TX 0x00000020 /* Disable transmit [5210] */ #define AR5K_DIAG_SW_DIS_RX_5210 0x00000040 /* Disable recieve */ #define AR5K_DIAG_SW_DIS_RX_5211 0x00000020 @@ -1329,13 +1410,13 @@ #define AR5K_DIAG_SW_CHAN_INFO_5211 0x00000100 #define AR5K_DIAG_SW_CHAN_INFO (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_CHAN_INFO_5210 : AR5K_DIAG_SW_CHAN_INFO_5211) -#define AR5K_DIAG_SW_EN_SCRAM_SEED_5211 0x00000200 /* Scrambler seed (?) */ +#define AR5K_DIAG_SW_EN_SCRAM_SEED_5211 0x00000200 /* Enable scrambler seed */ #define AR5K_DIAG_SW_EN_SCRAM_SEED_5210 0x00000400 #define AR5K_DIAG_SW_EN_SCRAM_SEED (ah->ah_version == AR5K_AR5210 ? \ AR5K_DIAG_SW_EN_SCRAM_SEED_5210 : AR5K_DIAG_SW_EN_SCRAM_SEED_5211) #define AR5K_DIAG_SW_ECO_ENABLE 0x00000400 /* [5211+] */ #define AR5K_DIAG_SW_SCVRAM_SEED 0x0003f800 /* [5210] */ -#define AR5K_DIAG_SW_SCRAM_SEED_M 0x0001fc00 /* Scrambler seed mask (?) */ +#define AR5K_DIAG_SW_SCRAM_SEED_M 0x0001fc00 /* Scrambler seed mask */ #define AR5K_DIAG_SW_SCRAM_SEED_S 10 #define AR5K_DIAG_SW_DIS_SEQ_INC 0x00040000 /* Disable seqnum increment (?)[5210] */ #define AR5K_DIAG_SW_FRAME_NV0_5210 0x00080000 @@ -1344,6 +1425,7 @@ AR5K_DIAG_SW_FRAME_NV0_5210 : AR5K_DIAG_SW_FRAME_NV0_5211) #define AR5K_DIAG_SW_OBSPT_M 0x000c0000 #define AR5K_DIAG_SW_OBSPT_S 18 +/* more bits */ /* * TSF (clock) register (lower 32 bits) @@ -1369,15 +1451,34 @@ /* * ADDAC test register [5211+] */ -#define AR5K_ADDAC_TEST 0x8054 -#define AR5K_ADDAC_TEST_TXCONT 0x00000001 +#define AR5K_ADDAC_TEST 0x8054 /* Register Address */ +#define AR5K_ADDAC_TEST_TXCONT 0x00000001 /* Test continuous tx */ +#define AR5K_ADDAC_TEST_TST_MODE 0x00000002 /* Test mode */ +#define AR5K_ADDAC_TEST_LOOP_EN 0x00000004 /* Enable loop */ +#define AR5K_ADDAC_TEST_LOOP_LEN 0x00000008 /* Loop length (field) */ +#define AR5K_ADDAC_TEST_USE_U8 0x00004000 /* Use upper 8 bits */ +#define AR5K_ADDAC_TEST_MSB 0x00008000 /* State of MSB */ +#define AR5K_ADDAC_TEST_TRIG_SEL 0x00010000 /* Trigger select */ +#define AR5K_ADDAC_TEST_TRIG_PTY 0x00020000 /* Trigger polarity */ +#define AR5K_ADDAC_TEST_RXCONT 0x00040000 /* Continuous capture */ +#define AR5K_ADDAC_TEST_CAPTURE 0x00080000 /* Begin capture */ +#define AR5K_ADDAC_TEST_TST_ARM 0x00100000 /* Test ARM (Adaptive Radio Mode ?) */ /* * Default antenna register [5211+] */ #define AR5K_DEFAULT_ANTENNA 0x8058 +/* + * Frame control QoS mask register (?) [5211+] + * (FC_QOS_MASK) + */ +#define AR5K_FRAME_CTL_QOSM 0x805c +/* + * Seq mask register (?) [5211+] + */ +#define AR5K_SEQ_MASK 0x8060 /* * Retry count register [5210] @@ -1449,124 +1550,242 @@ /* * XR (eXtended Range) mode register */ -#define AR5K_XRMODE 0x80c0 -#define AR5K_XRMODE_POLL_TYPE_M 0x0000003f +#define AR5K_XRMODE 0x80c0 /* Register Address */ +#define AR5K_XRMODE_POLL_TYPE_M 0x0000003f /* Mask for Poll type (?) */ #define AR5K_XRMODE_POLL_TYPE_S 0 -#define AR5K_XRMODE_POLL_SUBTYPE_M 0x0000003c +#define AR5K_XRMODE_POLL_SUBTYPE_M 0x0000003c /* Mask for Poll subtype (?) */ #define AR5K_XRMODE_POLL_SUBTYPE_S 2 -#define AR5K_XRMODE_POLL_WAIT_ALL 0x00000080 -#define AR5K_XRMODE_SIFS_DELAY 0x000fff00 -#define AR5K_XRMODE_FRAME_HOLD_M 0xfff00000 +#define AR5K_XRMODE_POLL_WAIT_ALL 0x00000080 /* Wait for poll */ +#define AR5K_XRMODE_SIFS_DELAY 0x000fff00 /* Mask for SIFS delay */ +#define AR5K_XRMODE_FRAME_HOLD_M 0xfff00000 /* Mask for frame hold (?) */ #define AR5K_XRMODE_FRAME_HOLD_S 20 /* * XR delay register */ -#define AR5K_XRDELAY 0x80c4 -#define AR5K_XRDELAY_SLOT_DELAY_M 0x0000ffff +#define AR5K_XRDELAY 0x80c4 /* Register Address */ +#define AR5K_XRDELAY_SLOT_DELAY_M 0x0000ffff /* Mask for slot delay */ #define AR5K_XRDELAY_SLOT_DELAY_S 0 -#define AR5K_XRDELAY_CHIRP_DELAY_M 0xffff0000 +#define AR5K_XRDELAY_CHIRP_DELAY_M 0xffff0000 /* Mask for CHIRP data delay */ #define AR5K_XRDELAY_CHIRP_DELAY_S 16 /* * XR timeout register */ -#define AR5K_XRTIMEOUT 0x80c8 -#define AR5K_XRTIMEOUT_CHIRP_M 0x0000ffff +#define AR5K_XRTIMEOUT 0x80c8 /* Register Address */ +#define AR5K_XRTIMEOUT_CHIRP_M 0x0000ffff /* Mask for CHIRP timeout */ #define AR5K_XRTIMEOUT_CHIRP_S 0 -#define AR5K_XRTIMEOUT_POLL_M 0xffff0000 +#define AR5K_XRTIMEOUT_POLL_M 0xffff0000 /* Mask for Poll timeout */ #define AR5K_XRTIMEOUT_POLL_S 16 /* * XR chirp register */ -#define AR5K_XRCHIRP 0x80cc -#define AR5K_XRCHIRP_SEND 0x00000001 -#define AR5K_XRCHIRP_GAP 0xffff0000 +#define AR5K_XRCHIRP 0x80cc /* Register Address */ +#define AR5K_XRCHIRP_SEND 0x00000001 /* Send CHIRP */ +#define AR5K_XRCHIRP_GAP 0xffff0000 /* Mask for CHIRP gap (?) */ /* * XR stomp register */ -#define AR5K_XRSTOMP 0x80d0 -#define AR5K_XRSTOMP_TX 0x00000001 -#define AR5K_XRSTOMP_RX_ABORT 0x00000002 -#define AR5K_XRSTOMP_RSSI_THRES 0x0000ff00 +#define AR5K_XRSTOMP 0x80d0 /* Register Address */ +#define AR5K_XRSTOMP_TX 0x00000001 /* Stomp Tx (?) */ +#define AR5K_XRSTOMP_RX 0x00000002 /* Stomp Rx (?) */ +#define AR5K_XRSTOMP_TX_RSSI 0x00000004 /* Stomp Tx RSSI (?) */ +#define AR5K_XRSTOMP_TX_BSSID 0x00000008 /* Stomp Tx BSSID (?) */ +#define AR5K_XRSTOMP_DATA 0x00000010 /* Stomp data (?)*/ +#define AR5K_XRSTOMP_RSSI_THRES 0x0000ff00 /* Mask for XR RSSI threshold */ /* * First enhanced sleep register */ -#define AR5K_SLEEP0 0x80d4 -#define AR5K_SLEEP0_NEXT_DTIM 0x0007ffff +#define AR5K_SLEEP0 0x80d4 /* Register Address */ +#define AR5K_SLEEP0_NEXT_DTIM 0x0007ffff /* Mask for next DTIM (?) */ #define AR5K_SLEEP0_NEXT_DTIM_S 0 -#define AR5K_SLEEP0_ASSUME_DTIM 0x00080000 -#define AR5K_SLEEP0_ENH_SLEEP_EN 0x00100000 -#define AR5K_SLEEP0_CABTO 0xff000000 +#define AR5K_SLEEP0_ASSUME_DTIM 0x00080000 /* Assume DTIM */ +#define AR5K_SLEEP0_ENH_SLEEP_EN 0x00100000 /* Enable enchanced sleep control */ +#define AR5K_SLEEP0_CABTO 0xff000000 /* Mask for CAB Time Out */ #define AR5K_SLEEP0_CABTO_S 24 /* * Second enhanced sleep register */ -#define AR5K_SLEEP1 0x80d8 -#define AR5K_SLEEP1_NEXT_TIM 0x0007ffff +#define AR5K_SLEEP1 0x80d8 /* Register Address */ +#define AR5K_SLEEP1_NEXT_TIM 0x0007ffff /* Mask for next TIM (?) */ #define AR5K_SLEEP1_NEXT_TIM_S 0 -#define AR5K_SLEEP1_BEACON_TO 0xff000000 +#define AR5K_SLEEP1_BEACON_TO 0xff000000 /* Mask for Beacon Time Out */ #define AR5K_SLEEP1_BEACON_TO_S 24 /* * Third enhanced sleep register */ -#define AR5K_SLEEP2 0x80dc -#define AR5K_SLEEP2_TIM_PER 0x0000ffff +#define AR5K_SLEEP2 0x80dc /* Register Address */ +#define AR5K_SLEEP2_TIM_PER 0x0000ffff /* Mask for TIM period (?) */ #define AR5K_SLEEP2_TIM_PER_S 0 -#define AR5K_SLEEP2_DTIM_PER 0xffff0000 +#define AR5K_SLEEP2_DTIM_PER 0xffff0000 /* Mask for DTIM period (?) */ #define AR5K_SLEEP2_DTIM_PER_S 16 /* * BSSID mask registers */ -#define AR5K_BSS_IDM0 0x80e0 -#define AR5K_BSS_IDM1 0x80e4 +#define AR5K_BSS_IDM0 0x80e0 /* Upper bits */ +#define AR5K_BSS_IDM1 0x80e4 /* Lower bits */ /* * TX power control (TPC) register + * + * XXX: PCDAC steps (0.5dbm) or DBM ? + * + * XXX: Mask changes for newer chips to 7f + * like tx power table ? */ -#define AR5K_TXPC 0x80e8 -#define AR5K_TXPC_ACK_M 0x0000003f +#define AR5K_TXPC 0x80e8 /* Register Address */ +#define AR5K_TXPC_ACK_M 0x0000003f /* Mask for ACK tx power */ #define AR5K_TXPC_ACK_S 0 -#define AR5K_TXPC_CTS_M 0x00003f00 +#define AR5K_TXPC_CTS_M 0x00003f00 /* Mask for CTS tx power */ #define AR5K_TXPC_CTS_S 8 -#define AR5K_TXPC_CHIRP_M 0x003f0000 +#define AR5K_TXPC_CHIRP_M 0x003f0000 /* Mask for CHIRP tx power */ #define AR5K_TXPC_CHIRP_S 22 /* * Profile count registers */ -#define AR5K_PROFCNT_TX 0x80ec -#define AR5K_PROFCNT_RX 0x80f0 -#define AR5K_PROFCNT_RXCLR 0x80f4 -#define AR5K_PROFCNT_CYCLE 0x80f8 +#define AR5K_PROFCNT_TX 0x80ec /* Tx count */ +#define AR5K_PROFCNT_RX 0x80f0 /* Rx count */ +#define AR5K_PROFCNT_RXCLR 0x80f4 /* Clear Rx count */ +#define AR5K_PROFCNT_CYCLE 0x80f8 /* Cycle count (?) */ + +/* + * Quiet (period) control registers (?) + */ +#define AR5K_QUIET_CTL1 0x80fc /* Register Address */ +#define AR5K_QUIET_CTL1_NEXT_QT 0x0000ffff /* Mask for next quiet (period?) (?) */ +#define AR5K_QUIET_CTL1_QT_EN 0x00010000 /* Enable quiet (period?) */ +#define AR5K_QUIET_CTL2 0x8100 /* Register Address */ +#define AR5K_QUIET_CTL2_QT_PER 0x0000ffff /* Mask for quiet period (?) */ +#define AR5K_QUIET_CTL2_QT_DUR 0xffff0000 /* Mask for quiet duration (?) */ /* * TSF parameter register */ -#define AR5K_TSF_PARM 0x8104 -#define AR5K_TSF_PARM_INC_M 0x000000ff +#define AR5K_TSF_PARM 0x8104 /* Register Address */ +#define AR5K_TSF_PARM_INC_M 0x000000ff /* Mask for TSF increment */ #define AR5K_TSF_PARM_INC_S 0 +/* + * QoS register (?) + */ +#define AR5K_QOS 0x8108 /* Register Address */ +#define AR5K_QOS_NOACK_2BIT_VALUES 0x00000000 /* (field) */ +#define AR5K_QOS_NOACK_BIT_OFFSET 0x00000020 /* (field) */ +#define AR5K_QOS_NOACK_BYTE_OFFSET 0x00000080 /* (field) */ + /* * PHY error filter register */ #define AR5K_PHY_ERR_FIL 0x810c -#define AR5K_PHY_ERR_FIL_RADAR 0x00000020 -#define AR5K_PHY_ERR_FIL_OFDM 0x00020000 -#define AR5K_PHY_ERR_FIL_CCK 0x02000000 +#define AR5K_PHY_ERR_FIL_RADAR 0x00000020 /* Radar signal */ +#define AR5K_PHY_ERR_FIL_OFDM 0x00020000 /* OFDM false detect (ANI) */ +#define AR5K_PHY_ERR_FIL_CCK 0x02000000 /* CCK false detect (ANI) */ + +/* + * XR latency register + */ +#define AR5K_XRLAT_TX 0x8110 /* - * Rate duration register + * ACK SIFS register + */ +#define AR5K_ACKSIFS 0x8114 /* Register Address */ +#define AR5K_ACKSIFS_INC 0x00000000 /* ACK SIFS Increment (field) */ + +/* + * MIC QoS control register (?) + */ +#define AR5K_MIC_QOS_CTL 0x8118 /* Register Address */ +#define AR5K_MIC_QOS_CTL_0 0x00000001 /* MIC QoS control 0 (?) */ +#define AR5K_MIC_QOS_CTL_1 0x00000004 /* MIC QoS control 1 (?) */ +#define AR5K_MIC_QOS_CTL_2 0x00000010 /* MIC QoS control 2 (?) */ +#define AR5K_MIC_QOS_CTL_3 0x00000040 /* MIC QoS control 3 (?) */ +#define AR5K_MIC_QOS_CTL_4 0x00000100 /* MIC QoS control 4 (?) */ +#define AR5K_MIC_QOS_CTL_5 0x00000400 /* MIC QoS control 5 (?) */ +#define AR5K_MIC_QOS_CTL_6 0x00001000 /* MIC QoS control 6 (?) */ +#define AR5K_MIC_QOS_CTL_7 0x00004000 /* MIC QoS control 7 (?) */ +#define AR5K_MIC_QOS_CTL_MQ_EN 0x00010000 /* Enable MIC QoS */ + +/* + * MIC QoS select register (?) + */ +#define AR5K_MIC_QOS_SEL 0x811c +#define AR5K_MIC_QOS_SEL_0 0x00000001 +#define AR5K_MIC_QOS_SEL_1 0x00000010 +#define AR5K_MIC_QOS_SEL_2 0x00000100 +#define AR5K_MIC_QOS_SEL_3 0x00001000 +#define AR5K_MIC_QOS_SEL_4 0x00010000 +#define AR5K_MIC_QOS_SEL_5 0x00100000 +#define AR5K_MIC_QOS_SEL_6 0x01000000 +#define AR5K_MIC_QOS_SEL_7 0x10000000 + +/* + * Misc mode control register (?) + */ +#define AR5K_MISC_MODE 0x8120 /* Register Address */ +#define AR5K_MISC_MODE_FBSSID_MATCH 0x00000001 /* Force BSSID match */ +#define AR5K_MISC_MODE_ACKSIFS_MEM 0x00000002 /* ACK SIFS memory (?) */ +/* more bits */ + +/* + * OFDM Filter counter + */ +#define AR5K_OFDM_FIL_CNT 0x8124 + +/* + * CCK Filter counter + */ +#define AR5K_CCK_FIL_CNT 0x8128 + +/* + * PHY Error Counters (?) + */ +#define AR5K_PHYERR_CNT1 0x812c +#define AR5K_PHYERR_CNT1_MASK 0x8130 + +#define AR5K_PHYERR_CNT2 0x8134 +#define AR5K_PHYERR_CNT2_MASK 0x8138 + +/* + * TSF Threshold register (?) + */ +#define AR5K_TSF_THRES 0x813c + +/* + * Rate -> ACK SIFS mapping table (32 entries) + */ +#define AR5K_RATE_ACKSIFS_BASE 0x8680 /* Register Address */ +#define AR5K_RATE_ACKSIFS(_n) (AR5K_RATE_ACKSIFS_BSE + ((_n) << 2)) +#define AR5K_RATE_ACKSIFS_NORMAL 0x00000001 /* Normal SIFS (field) */ +#define AR5K_RATE_ACKSIFS_TURBO 0x00000400 /* Turbo SIFS (field) */ + +/* + * Rate -> duration mapping table (32 entries) */ #define AR5K_RATE_DUR_BASE 0x8700 #define AR5K_RATE_DUR(_n) (AR5K_RATE_DUR_BASE + ((_n) << 2)) +/* + * Rate -> db mapping table + * (8 entries, each one has 4 8bit fields) + */ +#define AR5K_RATE2DB_BASE 0x87c0 +#define AR5K_RATE2DB(_n) (AR5K_RATE2DB_BASE + ((_n) << 2)) + +/* + * db -> Rate mapping table + * (8 entries, each one has 4 8bit fields) + */ +#define AR5K_DB2RATE_BASE 0x87e0 +#define AR5K_DB2RATE(_n) (AR5K_DB2RATE_BASE + ((_n) << 2)) + /*===5212 end===*/ /* @@ -1613,12 +1832,34 @@ /*===PHY REGISTERS===*/ /* - * PHY register + * PHY registers start */ #define AR5K_PHY_BASE 0x9800 #define AR5K_PHY(_n) (AR5K_PHY_BASE + ((_n) << 2)) -#define AR5K_PHY_SHIFT_2GHZ 0x00004007 -#define AR5K_PHY_SHIFT_5GHZ 0x00000007 + +/* + * TST_2 (Misc config parameters) + */ +#define AR5K_PHY_TST2 0x9800 /* Register Address */ +#define AR5K_PHY_TST2_TRIG_SEL 0x00000001 /* Trigger select (?) (field ?) */ +#define AR5K_PHY_TST2_TRIG 0x00000010 /* Trigger (?) (field ?) */ +#define AR5K_PHY_TST2_CBUS_MODE 0x00000100 /* Cardbus mode (?) */ +/* bit reserved */ +#define AR5K_PHY_TST2_CLK32 0x00000400 /* CLK_OUT is CLK32 (32Khz external) */ +#define AR5K_PHY_TST2_CHANCOR_DUMP_EN 0x00000800 /* Enable Chancor dump (?) */ +#define AR5K_PHY_TST2_EVEN_CHANCOR_DUMP 0x00001000 /* Even Chancor dump (?) */ +#define AR5K_PHY_TST2_RFSILENT_EN 0x00002000 /* Enable RFSILENT */ +#define AR5K_PHY_TST2_ALT_RFDATA 0x00004000 /* Alternate RFDATA (5-2GHz switch) */ +#define AR5K_PHY_TST2_MINI_OBS_EN 0x00008000 /* Enable mini OBS (?) */ +#define AR5K_PHY_TST2_RX2_IS_RX5_INV 0x00010000 /* 2GHz rx path is the 5GHz path inverted (?) */ +#define AR5K_PHY_TST2_SLOW_CLK160 0x00020000 /* Slow CLK160 (?) */ +#define AR5K_PHY_TST2_AGC_OBS_SEL_3 0x00040000 /* AGC OBS Select 3 (?) */ +#define AR5K_PHY_TST2_BBB_OBS_SEL 0x00080000 /* BB OBS Select (field ?) */ +#define AR5K_PHY_TST2_ADC_OBS_SEL 0x00800000 /* ADC OBS Select (field ?) */ +#define AR5K_PHY_TST2_RX_CLR_SEL 0x08000000 /* RX Clear Select (?) */ +#define AR5K_PHY_TST2_FORCE_AGC_CLR 0x10000000 /* Force AGC clear (?) */ +#define AR5K_PHY_SHIFT_2GHZ 0x00004007 /* Used to access 2GHz radios */ +#define AR5K_PHY_SHIFT_5GHZ 0x00000007 /* Used to access 5GHz radios (default) */ /* * PHY frame control register [5110] /turbo mode register [5111+] @@ -1630,18 +1871,21 @@ * a "turbo mode register" for 5110. We treat this one as * a frame control register for 5110 below. */ -#define AR5K_PHY_TURBO 0x9804 -#define AR5K_PHY_TURBO_MODE 0x00000001 -#define AR5K_PHY_TURBO_SHORT 0x00000002 +#define AR5K_PHY_TURBO 0x9804 /* Register Address */ +#define AR5K_PHY_TURBO_MODE 0x00000001 /* Enable turbo mode */ +#define AR5K_PHY_TURBO_SHORT 0x00000002 /* Short mode (20Mhz channels) (?) */ /* * PHY agility command register + * (aka TST_1) */ -#define AR5K_PHY_AGC 0x9808 -#define AR5K_PHY_AGC_DISABLE 0x08000000 +#define AR5K_PHY_AGC 0x9808 /* Register Address */ +#define AR5K_PHY_TST1 0x9808 +#define AR5K_PHY_AGC_DISABLE 0x08000000 /* Disable AGC to A2 (?)*/ +#define AR5K_PHY_TST1_TXHOLD 0x00003800 /* Set tx hold (?) */ /* - * PHY timing register [5112+] + * PHY timing register 3 [5112+] */ #define AR5K_PHY_TIMING_3 0x9814 #define AR5K_PHY_TIMING_3_DSC_MAN 0xfffe0000 @@ -1657,26 +1901,81 @@ /* * PHY activation register */ -#define AR5K_PHY_ACT 0x981c -#define AR5K_PHY_ACT_ENABLE 0x00000001 -#define AR5K_PHY_ACT_DISABLE 0x00000002 +#define AR5K_PHY_ACT 0x981c /* Register Address */ +#define AR5K_PHY_ACT_ENABLE 0x00000001 /* Activate PHY */ +#define AR5K_PHY_ACT_DISABLE 0x00000002 /* Deactivate PHY */ + +/* + * PHY RF control registers + * (i think these are delay times, + * these calibration values exist + * in EEPROM) + */ +#define AR5K_PHY_RF_CTL2 0x9824 /* Register Address */ +#define AR5K_PHY_RF_CTL2_TXF2TXD_START 0x0000000f /* Mask for TX frame to TX d(esc?) start */ + +#define AR5K_PHY_RF_CTL3 0x9828 /* Register Address */ +#define AR5K_PHY_RF_CTL3_TXE2XLNA_ON 0x0000000f /* Mask for TX end to XLNA on */ + +#define AR5K_PHY_RF_CTL4 0x9834 /* Register Address */ +#define AR5K_PHY_RF_CTL4_TXF2XPA_A_ON 0x00000001 /* TX frame to XPA A on (field) */ +#define AR5K_PHY_RF_CTL4_TXF2XPA_B_ON 0x00000100 /* TX frame to XPA B on (field) */ +#define AR5K_PHY_RF_CTL4_TXE2XPA_A_OFF 0x00010000 /* TX end to XPA A off (field) */ +#define AR5K_PHY_RF_CTL4_TXE2XPA_B_OFF 0x01000000 /* TX end to XPA B off (field) */ + +/* + * Pre-Amplifier control register + * (XPA -> external pre-amplifier) + */ +#define AR5K_PHY_PA_CTL 0x9838 /* Register Address */ +#define AR5K_PHY_PA_CTL_XPA_A_HI 0x00000001 /* XPA A high (?) */ +#define AR5K_PHY_PA_CTL_XPA_B_HI 0x00000002 /* XPA B high (?) */ +#define AR5K_PHY_PA_CTL_XPA_A_EN 0x00000004 /* Enable XPA A */ +#define AR5K_PHY_PA_CTL_XPA_B_EN 0x00000008 /* Enable XPA B */ + +/* + * PHY settling register + */ +#define AR5K_PHY_SETTLING 0x9844 /* Register Address */ +#define AR5K_PHY_SETTLING_AGC 0x0000007f /* Mask for AGC settling time */ +#define AR5K_PHY_SETTLING_SWITCH 0x00003f80 /* Mask for Switch settlig time */ + +/* + * PHY Gain registers + */ +#define AR5K_PHY_GAIN 0x9848 /* Register Address */ +#define AR5K_PHY_GAIN_TXRX_ATTEN 0x0003f000 /* Mask for TX-RX Attenuation */ + +#define AR5K_PHY_GAIN_OFFSET 0x984c /* Register Address */ +#define AR5K_PHY_GAIN_OFFSET_RXTX_FLAG 0x00020000 /* RX-TX flag (?) */ + +/* + * Desired size register + * (for more infos read ANI patent) + */ +#define AR5K_PHY_DESIRED_SIZE 0x9850 /* Register Address */ +#define AR5K_PHY_DESIRED_SIZE_ADC 0x000000ff /* Mask for ADC desired size */ +#define AR5K_PHY_DESIRED_SIZE_PGA 0x0000ff00 /* Mask for PGA desired size */ +#define AR5K_PHY_DESIRED_SIZE_TOT 0x0ff00000 /* Mask for Total desired size (?) */ /* * PHY signal register + * (for more infos read ANI patent) */ -#define AR5K_PHY_SIG 0x9858 -#define AR5K_PHY_SIG_FIRSTEP 0x0003f000 +#define AR5K_PHY_SIG 0x9858 /* Register Address */ +#define AR5K_PHY_SIG_FIRSTEP 0x0003f000 /* Mask for FIRSTEP */ #define AR5K_PHY_SIG_FIRSTEP_S 12 -#define AR5K_PHY_SIG_FIRPWR 0x03fc0000 +#define AR5K_PHY_SIG_FIRPWR 0x03fc0000 /* Mask for FIPWR */ #define AR5K_PHY_SIG_FIRPWR_S 18 /* * PHY coarse agility control register + * (for more infos read ANI patent) */ -#define AR5K_PHY_AGCCOARSE 0x985c -#define AR5K_PHY_AGCCOARSE_LO 0x00007f80 +#define AR5K_PHY_AGCCOARSE 0x985c /* Register Address */ +#define AR5K_PHY_AGCCOARSE_LO 0x00007f80 /* Mask for AGC Coarse low */ #define AR5K_PHY_AGCCOARSE_LO_S 7 -#define AR5K_PHY_AGCCOARSE_HI 0x003f8000 +#define AR5K_PHY_AGCCOARSE_HI 0x003f8000 /* Mask for AGC Coarse high */ #define AR5K_PHY_AGCCOARSE_HI_S 15 /* @@ -1689,12 +1988,13 @@ /* * PHY noise floor status register */ -#define AR5K_PHY_NF 0x9864 -#define AR5K_PHY_NF_M 0x000001ff -#define AR5K_PHY_NF_ACTIVE 0x00000100 +#define AR5K_PHY_NF 0x9864 /* Register address */ +#define AR5K_PHY_NF_M 0x000001ff /* Noise floor mask */ +#define AR5K_PHY_NF_ACTIVE 0x00000100 /* Noise floor calibration still active */ #define AR5K_PHY_NF_RVAL(_n) (((_n) >> 19) & AR5K_PHY_NF_M) #define AR5K_PHY_NF_AVAL(_n) (-((_n) ^ AR5K_PHY_NF_M) + 1) #define AR5K_PHY_NF_SVAL(_n) (((_n) & AR5K_PHY_NF_M) | (1 << 9)) +#define AR5K_PHY_NF_THRESH62 0x00001000 /* Thresh62 -check ANI patent- (field) */ /* * PHY ADC saturation register [5110] @@ -1705,6 +2005,30 @@ #define AR5K_PHY_ADCSAT_THR 0x000007e0 #define AR5K_PHY_ADCSAT_THR_S 5 +/* + * PHY Weak ofdm signal detection threshold registers (ANI) [5212+] + */ + +/* High thresholds */ +#define AR5K_PHY_WEAK_OFDM_HIGH_THR 0x9868 +#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT 0x0000001f +#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT_S 0 +#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M1 0x00fe0000 +#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M1_S 17 +#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2 0x7f000000 +#define AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_S 24 + +/* Low thresholds */ +#define AR5K_PHY_WEAK_OFDM_LOW_THR 0x986c +#define AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN 0x00000001 +#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT 0x00003f00 +#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT_S 8 +#define AR5K_PHY_WEAK_OFDM_LOW_THR_M1 0x001fc000 +#define AR5K_PHY_WEAK_OFDM_LOW_THR_M1_S 14 +#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2 0x0fe00000 +#define AR5K_PHY_WEAK_OFDM_LOW_THR_M2_S 21 + + /* * PHY sleep registers [5112+] */ @@ -1730,6 +2054,8 @@ AR5K_PHY_PLL_44MHZ_5211 : AR5K_PHY_PLL_44MHZ_5212) #define AR5K_PHY_PLL_RF5111 0x00000000 #define AR5K_PHY_PLL_RF5112 0x00000040 +#define AR5K_PHY_PLL_HALF_RATE 0x00000100 +#define AR5K_PHY_PLL_QUARTER_RATE 0x00000200 /* * RF Buffer register @@ -1791,24 +2117,75 @@ #define AR5K_PHY_RFSTG 0x98d4 #define AR5K_PHY_RFSTG_DISABLE 0x00000021 +/* + * PHY Antenna control register + */ +#define AR5K_PHY_ANT_CTL 0x9910 /* Register Address */ +#define AR5K_PHY_ANT_CTL_TXRX_EN 0x00000001 /* Enable TX/RX (?) */ +#define AR5K_PHY_ANT_CTL_SECTORED_ANT 0x00000004 /* Sectored Antenna */ +#define AR5K_PHY_ANT_CTL_HITUNE5 0x00000008 /* Hitune5 (?) */ +#define AR5K_PHY_ANT_CTL_SWTABLE_IDLE 0x00000010 /* Switch table idle (?) */ + /* * PHY receiver delay register [5111+] */ -#define AR5K_PHY_RX_DELAY 0x9914 -#define AR5K_PHY_RX_DELAY_M 0x00003fff +#define AR5K_PHY_RX_DELAY 0x9914 /* Register Address */ +#define AR5K_PHY_RX_DELAY_M 0x00003fff /* Mask for RX activate to receive delay (/100ns) */ + +/* + * PHY max rx length register (?) [5111] + */ +#define AR5K_PHY_MAX_RX_LEN 0x991c /* - * PHY timing I(nphase) Q(adrature) control register [5111+] + * PHY timing register 4 + * I(nphase)/Q(adrature) calibration register [5111+] */ -#define AR5K_PHY_IQ 0x9920 /* Register address */ +#define AR5K_PHY_IQ 0x9920 /* Register Address */ #define AR5K_PHY_IQ_CORR_Q_Q_COFF 0x0000001f /* Mask for q correction info */ #define AR5K_PHY_IQ_CORR_Q_I_COFF 0x000007e0 /* Mask for i correction info */ #define AR5K_PHY_IQ_CORR_Q_I_COFF_S 5 #define AR5K_PHY_IQ_CORR_ENABLE 0x00000800 /* Enable i/q correction */ -#define AR5K_PHY_IQ_CAL_NUM_LOG_MAX 0x0000f000 +#define AR5K_PHY_IQ_CAL_NUM_LOG_MAX 0x0000f000 /* Mask for max number of samples in log scale */ #define AR5K_PHY_IQ_CAL_NUM_LOG_MAX_S 12 #define AR5K_PHY_IQ_RUN 0x00010000 /* Run i/q calibration */ +#define AR5K_PHY_IQ_USE_PT_DF 0x00020000 /* Use pilot track df (?) */ +#define AR5K_PHY_IQ_EARLY_TRIG_THR 0x00200000 /* Early trigger threshold (?) (field) */ +#define AR5K_PHY_IQ_PILOT_MASK_EN 0x10000000 /* Enable pilot mask (?) */ +#define AR5K_PHY_IQ_CHAN_MASK_EN 0x20000000 /* Enable channel mask (?) */ +#define AR5K_PHY_IQ_SPUR_FILT_EN 0x40000000 /* Enable spur filter */ +#define AR5K_PHY_IQ_SPUR_RSSI_EN 0x80000000 /* Enable spur rssi */ +/* + * PHY timing register 5 + * OFDM Self-correlator Cyclic RSSI threshold params + * (Check out bb_cycpwr_thr1 on ANI patent) + */ +#define AR5K_PHY_OFDM_SELFCORR 0x9924 /* Register Address */ +#define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1_EN 0x00000001 /* Enable cyclic RSSI thr 1 */ +#define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1 0x000000fe /* Mask for Cyclic RSSI threshold 1 */ +#define AR5K_PHY_OFDM_SELFCORR_CYPWR_THR3 0x00000100 /* Cyclic RSSI threshold 3 (field) (?) */ +#define AR5K_PHY_OFDM_SELFCORR_RSSI_1ATHR_EN 0x00008000 /* Enable 1A RSSI threshold (?) */ +#define AR5K_PHY_OFDM_SELFCORR_RSSI_1ATHR 0x00010000 /* 1A RSSI threshold (field) (?) */ +#define AR5K_PHY_OFDM_SELFCORR_LSCTHR_HIRSSI 0x00800000 /* Long sc threshold hi rssi (?) */ + +/* + * PHY-only warm reset register + */ +#define AR5K_PHY_WARM_RESET 0x9928 + +/* + * PHY-only control register + */ +#define AR5K_PHY_CTL 0x992c /* Register Address */ +#define AR5K_PHY_CTL_RX_DRAIN_RATE 0x00000001 /* RX drain rate (?) */ +#define AR5K_PHY_CTL_LATE_TX_SIG_SYM 0x00000002 /* Late tx signal symbol (?) */ +#define AR5K_PHY_CTL_GEN_SCRAMBLER 0x00000004 /* Generate scrambler */ +#define AR5K_PHY_CTL_TX_ANT_SEL 0x00000008 /* TX antenna select */ +#define AR5K_PHY_CTL_TX_ANT_STATIC 0x00000010 /* Static TX antenna */ +#define AR5K_PHY_CTL_RX_ANT_SEL 0x00000020 /* RX antenna select */ +#define AR5K_PHY_CTL_RX_ANT_STATIC 0x00000040 /* Static RX antenna */ +#define AR5K_PHY_CTL_LOW_FREQ_SLE_EN 0x00000080 /* Enable low freq sleep */ /* * PHY PAPD probe register [5111+ (?)] @@ -1816,9 +2193,13 @@ * Because it's always 0 in 5211 initialization code */ #define AR5K_PHY_PAPD_PROBE 0x9930 +#define AR5K_PHY_PAPD_PROBE_SH_HI_PAR 0x00000001 +#define AR5K_PHY_PAPD_PROBE_PCDAC_BIAS 0x00000002 +#define AR5K_PHY_PAPD_PROBE_COMP_GAIN 0x00000040 #define AR5K_PHY_PAPD_PROBE_TXPOWER 0x00007e00 #define AR5K_PHY_PAPD_PROBE_TXPOWER_S 9 #define AR5K_PHY_PAPD_PROBE_TX_NEXT 0x00008000 +#define AR5K_PHY_PAPD_PROBE_PREDIST_EN 0x00010000 #define AR5K_PHY_PAPD_PROBE_TYPE 0x01800000 /* [5112+] */ #define AR5K_PHY_PAPD_PROBE_TYPE_S 23 #define AR5K_PHY_PAPD_PROBE_TYPE_OFDM 0 @@ -1848,15 +2229,16 @@ #define AR5K_PHY_FRAME_CTL (ah->ah_version == AR5K_AR5210 ? \ AR5K_PHY_FRAME_CTL_5210 : AR5K_PHY_FRAME_CTL_5211) /*---[5111+]---*/ -#define AR5K_PHY_FRAME_CTL_TX_CLIP 0x00000038 +#define AR5K_PHY_FRAME_CTL_TX_CLIP 0x00000038 /* Mask for tx clip (?) */ #define AR5K_PHY_FRAME_CTL_TX_CLIP_S 3 +#define AR5K_PHY_FRAME_CTL_PREP_CHINFO 0x00010000 /* Prepend chan info */ /*---[5110/5111]---*/ -#define AR5K_PHY_FRAME_CTL_TIMING_ERR 0x01000000 -#define AR5K_PHY_FRAME_CTL_PARITY_ERR 0x02000000 -#define AR5K_PHY_FRAME_CTL_ILLRATE_ERR 0x04000000 /* illegal rate */ -#define AR5K_PHY_FRAME_CTL_ILLLEN_ERR 0x08000000 /* illegal length */ +#define AR5K_PHY_FRAME_CTL_TIMING_ERR 0x01000000 /* PHY timing error */ +#define AR5K_PHY_FRAME_CTL_PARITY_ERR 0x02000000 /* Parity error */ +#define AR5K_PHY_FRAME_CTL_ILLRATE_ERR 0x04000000 /* Illegal rate */ +#define AR5K_PHY_FRAME_CTL_ILLLEN_ERR 0x08000000 /* Illegal length */ #define AR5K_PHY_FRAME_CTL_SERVICE_ERR 0x20000000 -#define AR5K_PHY_FRAME_CTL_TXURN_ERR 0x40000000 /* tx underrun */ +#define AR5K_PHY_FRAME_CTL_TXURN_ERR 0x40000000 /* TX underrun */ #define AR5K_PHY_FRAME_CTL_INI AR5K_PHY_FRAME_CTL_SERVICE_ERR | \ AR5K_PHY_FRAME_CTL_TXURN_ERR | \ AR5K_PHY_FRAME_CTL_ILLLEN_ERR | \ @@ -1914,6 +2296,11 @@ after DFS is enabled */ #define AR5K_PHY_ANT_SWITCH_TABLE_0 0x9960 #define AR5K_PHY_ANT_SWITCH_TABLE_1 0x9964 +/* + * PHY Noise floor threshold + */ +#define AR5K_PHY_NFTHRES 0x9968 + /* * PHY clock sleep registers [5112+] */ @@ -1922,56 +2309,116 @@ after DFS is enabled */ #define AR5K_PHY_SDELAY 0x99f4 #define AR5K_PHY_SDELAY_32MHZ 0x000000ff #define AR5K_PHY_SPENDING 0x99f8 +#define AR5K_PHY_SPENDING_14 0x00000014 +#define AR5K_PHY_SPENDING_18 0x00000018 #define AR5K_PHY_SPENDING_RF5111 0x00000018 -#define AR5K_PHY_SPENDING_RF5112 0x00000014 /* <- i 've only seen this on 2425 dumps ! */ -#define AR5K_PHY_SPENDING_RF5112A 0x0000000e /* but since i only have 5112A-based chips */ -#define AR5K_PHY_SPENDING_RF5424 0x00000012 /* to test it might be also for old 5112. */ +#define AR5K_PHY_SPENDING_RF5112 0x00000014 +/* #define AR5K_PHY_SPENDING_RF5112A 0x0000000e */ +/* #define AR5K_PHY_SPENDING_RF5424 0x00000012 */ +#define AR5K_PHY_SPENDING_RF5413 0x00000014 +#define AR5K_PHY_SPENDING_RF2413 0x00000014 +#define AR5K_PHY_SPENDING_RF2425 0x00000018 /* * Misc PHY/radio registers [5110 - 5111] */ -#define AR5K_BB_GAIN_BASE 0x9b00 /* BaseBand Amplifier Gain table base address */ +#define AR5K_BB_GAIN_BASE 0x9b00 /* BaseBand Amplifier Gain table base address */ #define AR5K_BB_GAIN(_n) (AR5K_BB_GAIN_BASE + ((_n) << 2)) -#define AR5K_RF_GAIN_BASE 0x9a00 /* RF Amplrifier Gain table base address */ +#define AR5K_RF_GAIN_BASE 0x9a00 /* RF Amplrifier Gain table base address */ #define AR5K_RF_GAIN(_n) (AR5K_RF_GAIN_BASE + ((_n) << 2)) /* * PHY timing IQ calibration result register [5111+] */ -#define AR5K_PHY_IQRES_CAL_PWR_I 0x9c10 /* I (Inphase) power value */ -#define AR5K_PHY_IQRES_CAL_PWR_Q 0x9c14 /* Q (Quadrature) power value */ +#define AR5K_PHY_IQRES_CAL_PWR_I 0x9c10 /* I (Inphase) power value */ +#define AR5K_PHY_IQRES_CAL_PWR_Q 0x9c14 /* Q (Quadrature) power value */ #define AR5K_PHY_IQRES_CAL_CORR 0x9c18 /* I/Q Correlation */ /* * PHY current RSSI register [5111+] */ -#define AR5K_PHY_CURRENT_RSSI 0x9c1c +#define AR5K_PHY_CURRENT_RSSI 0x9c1c + +/* + * PHY RF Bus grant register (?) + */ +#define AR5K_PHY_RFBUS_GRANT 0x9c20 + +/* + * PHY ADC test register + */ +#define AR5K_PHY_ADC_TEST 0x9c24 +#define AR5K_PHY_ADC_TEST_I 0x00000001 +#define AR5K_PHY_ADC_TEST_Q 0x00000200 + +/* + * PHY DAC test register + */ +#define AR5K_PHY_DAC_TEST 0x9c28 +#define AR5K_PHY_DAC_TEST_I 0x00000001 +#define AR5K_PHY_DAC_TEST_Q 0x00000200 + +/* + * PHY PTAT register (?) + */ +#define AR5K_PHY_PTAT 0x9c2c + +/* + * PHY Illegal TX rate register [5112+] + */ +#define AR5K_PHY_BAD_TX_RATE 0x9c30 + +/* + * PHY SPUR Power register [5112+] + */ +#define AR5K_PHY_SPUR_PWR 0x9c34 /* Register Address */ +#define AR5K_PHY_SPUR_PWR_I 0x00000001 /* SPUR Power estimate for I (field) */ +#define AR5K_PHY_SPUR_PWR_Q 0x00000100 /* SPUR Power estimate for Q (field) */ +#define AR5K_PHY_SPUR_PWR_FILT 0x00010000 /* Power with SPUR removed (field) */ + +/* + * PHY Channel status register [5112+] (?) + */ +#define AR5K_PHY_CHAN_STATUS 0x9c38 +#define AR5K_PHY_CHAN_STATUS_BT_ACT 0x00000001 +#define AR5K_PHY_CHAN_STATUS_RX_CLR_RAW 0x00000002 +#define AR5K_PHY_CHAN_STATUS_RX_CLR_MAC 0x00000004 +#define AR5K_PHY_CHAN_STATUS_RX_CLR_PAP 0x00000008 + +/* + * PHY PAPD I (power?) table (?) + * (92! entries) + */ +#define AR5K_PHY_PAPD_I_BASE 0xa000 +#define AR5K_PHY_PAPD_I(_n) (AR5K_PHY_PAPD_I_BASE + ((_n) << 2)) /* * PHY PCDAC TX power table */ #define AR5K_PHY_PCDAC_TXPOWER_BASE_5211 0xa180 -#define AR5K_PHY_PCDAC_TXPOWER_BASE_5413 0xa280 -#define AR5K_PHY_PCDAC_TXPOWER_BASE (ah->ah_radio >= AR5K_RF5413 ? \ - AR5K_PHY_PCDAC_TXPOWER_BASE_5413 :\ +#define AR5K_PHY_PCDAC_TXPOWER_BASE_2413 0xa280 +#define AR5K_PHY_PCDAC_TXPOWER_BASE (ah->ah_radio >= AR5K_RF2413 ? \ + AR5K_PHY_PCDAC_TXPOWER_BASE_2413 :\ AR5K_PHY_PCDAC_TXPOWER_BASE_5211) #define AR5K_PHY_PCDAC_TXPOWER(_n) (AR5K_PHY_PCDAC_TXPOWER_BASE + ((_n) << 2)) /* * PHY mode register [5111+] */ -#define AR5K_PHY_MODE 0x0a200 /* Register address */ -#define AR5K_PHY_MODE_MOD 0x00000001 /* PHY Modulation mask*/ +#define AR5K_PHY_MODE 0x0a200 /* Register Address */ +#define AR5K_PHY_MODE_MOD 0x00000001 /* PHY Modulation bit */ #define AR5K_PHY_MODE_MOD_OFDM 0 #define AR5K_PHY_MODE_MOD_CCK 1 -#define AR5K_PHY_MODE_FREQ 0x00000002 /* Freq mode mask */ +#define AR5K_PHY_MODE_FREQ 0x00000002 /* Freq mode bit */ #define AR5K_PHY_MODE_FREQ_5GHZ 0 #define AR5K_PHY_MODE_FREQ_2GHZ 2 -#define AR5K_PHY_MODE_MOD_DYN 0x00000004 /* Dynamic OFDM/CCK mode mask [5112+] */ +#define AR5K_PHY_MODE_MOD_DYN 0x00000004 /* Enable Dynamic OFDM/CCK mode [5112+] */ #define AR5K_PHY_MODE_RAD 0x00000008 /* [5212+] */ #define AR5K_PHY_MODE_RAD_RF5111 0 #define AR5K_PHY_MODE_RAD_RF5112 8 -#define AR5K_PHY_MODE_XR 0x00000010 /* [5112+] */ +#define AR5K_PHY_MODE_XR 0x00000010 /* Enable XR mode [5112+] */ +#define AR5K_PHY_MODE_HALF_RATE 0x00000020 /* Enable Half rate (test) */ +#define AR5K_PHY_MODE_QUARTER_RATE 0x00000040 /* Enable Quarter rat (test) */ /* * PHY CCK transmit control register [5111+ (?)] @@ -1979,6 +2426,15 @@ after DFS is enabled */ #define AR5K_PHY_CCKTXCTL 0xa204 #define AR5K_PHY_CCKTXCTL_WORLD 0x00000000 #define AR5K_PHY_CCKTXCTL_JAPAN 0x00000010 +#define AR5K_PHY_CCKTXCTL_SCRAMBLER_DIS 0x00000001 +#define AR5K_PHY_CCKTXCTK_DAC_SCALE 0x00000004 + +/* + * PHY CCK Cross-correlator Barker RSSI threshold register [5212+] + */ +#define AR5K_PHY_CCK_CROSSCORR 0xa208 +#define AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR 0x0000000f +#define AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR_S 0 /* * PHY 2GHz gain register [5111+] -- cgit v1.2.1 From ba37746e547e14703a5ac86560c6e056620bc4cf Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 20 Jul 2008 06:32:32 +0300 Subject: ath5k: Restore saved initval after POST * Restore saved initial value after POST Changes-licensed-under: ISC Signed-off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/hw.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/hw.c b/drivers/net/wireless/ath5k/hw.c index 42ef41ed5d18..b3b9baa551bc 100644 --- a/drivers/net/wireless/ath5k/hw.c +++ b/drivers/net/wireless/ath5k/hw.c @@ -139,6 +139,8 @@ static int ath5k_hw_post(struct ath5k_hw *ah) for (c = 0; c < 2; c++) { cur_reg = regs[c]; + + /* Save previous value */ init_val = ath5k_hw_reg_read(ah, cur_reg); for (i = 0; i < 256; i++) { @@ -170,6 +172,10 @@ static int ath5k_hw_post(struct ath5k_hw *ah) var_pattern = 0x003b080f; ath5k_hw_reg_write(ah, var_pattern, cur_reg); } + + /* Restore previous value */ + ath5k_hw_reg_write(ah, init_val, cur_reg); + } return 0; -- cgit v1.2.1 From e5a4ad0dda8f79a984ba6391af65274b482b6703 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 20 Jul 2008 06:34:39 +0300 Subject: ath5k: Misc hw_attach fixes * Correctly attach RF2425 * Update SREV values for Radio chips * Update hw_attach to use new SPENDING values * Write a bit after POST for some chips Changes-licensed-under: ISC Signed-off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/ath5k.h | 8 +++-- drivers/net/wireless/ath5k/hw.c | 62 +++++++++++++------------------------- 2 files changed, 26 insertions(+), 44 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/ath5k.h b/drivers/net/wireless/ath5k/ath5k.h index ba35c30d203c..9102eea3c8bf 100644 --- a/drivers/net/wireless/ath5k/ath5k.h +++ b/drivers/net/wireless/ath5k/ath5k.h @@ -186,11 +186,13 @@ struct ath5k_srev_name { #define AR5K_SREV_RAD_2111 0x20 #define AR5K_SREV_RAD_5112 0x30 #define AR5K_SREV_RAD_5112A 0x35 +#define AR5K_SREV_RAD_5112B 0x36 #define AR5K_SREV_RAD_2112 0x40 #define AR5K_SREV_RAD_2112A 0x45 -#define AR5K_SREV_RAD_SC0 0x56 /* Found on 2413/2414 */ -#define AR5K_SREV_RAD_SC1 0x63 /* Found on 5413/5414 */ -#define AR5K_SREV_RAD_SC2 0xa2 /* Found on 2424-5/5424 */ +#define AR5K_SREV_RAD_2112B 0x46 +#define AR5K_SREV_RAD_SC0 0x50 /* Found on 2413/2414 */ +#define AR5K_SREV_RAD_SC1 0x60 /* Found on 5413/5414 */ +#define AR5K_SREV_RAD_SC2 0xa0 /* Found on 2424-5/5424 */ #define AR5K_SREV_RAD_5133 0xc0 /* MIMO found on 5418 */ /* IEEE defs */ diff --git a/drivers/net/wireless/ath5k/hw.c b/drivers/net/wireless/ath5k/hw.c index b3b9baa551bc..8cd8659e9128 100644 --- a/drivers/net/wireless/ath5k/hw.c +++ b/drivers/net/wireless/ath5k/hw.c @@ -293,67 +293,42 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) /* Identify the radio chip*/ if (ah->ah_version == AR5K_AR5210) { ah->ah_radio = AR5K_RF5110; + /* + * Register returns 0x0/0x04 for radio revision + * so ath5k_hw_radio_revision doesn't parse the value + * correctly. For now we are based on mac's srev to + * identify RF2425 radio. + */ + } else if (srev == AR5K_SREV_VER_AR2425) { + ah->ah_radio = AR5K_RF2425; + ah->ah_phy_spending = AR5K_PHY_SPENDING_RF2425; } else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112) { ah->ah_radio = AR5K_RF5111; ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5111; } else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_SC0) { - ah->ah_radio = AR5K_RF5112; - - if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5112A) { - ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112; - } else { - ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112A; - } - + ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112; } else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_SC1) { ah->ah_radio = AR5K_RF2413; - ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112A; + ah->ah_phy_spending = AR5K_PHY_SPENDING_RF2413; } else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_SC2) { ah->ah_radio = AR5K_RF5413; - ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112A; + ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5413; } else if (ah->ah_radio_5ghz_revision < AR5K_SREV_RAD_5133) { - /* AR5424 */ if (srev >= AR5K_SREV_VER_AR5424) { ah->ah_radio = AR5K_RF5413; - ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5424; + ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5413; /* AR2424 */ } else { ah->ah_radio = AR5K_RF2413; /* For testing */ - ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112A; + ah->ah_phy_spending = AR5K_PHY_SPENDING_RF2413; } - - /* - * Register returns 0x4 for radio revision - * so ath5k_hw_radio_revision doesn't parse the value - * correctly. For now we are based on mac's srev to - * identify RF2425 radio. - */ - } else if (srev == AR5K_SREV_VER_AR2425) { - ah->ah_radio = AR5K_RF2425; - ah->ah_phy_spending = AR5K_PHY_SPENDING_RF5112; } - ah->ah_phy = AR5K_PHY(0); /* - * Identify AR5212-based PCI-E cards - * And write some initial settings. - * - * (doing a "strings" on ndis driver - * -ar5211.sys- reveals the following - * pci-e related functions: - * - * pcieClockReq - * pcieRxErrNotify - * pcieL1SKPEnable - * pcieAspm - * pcieDisableAspmOnRfWake - * pciePowerSaveEnable - * - * I guess these point to ClockReq but - * i'm not sure.) + * Write PCI-E power save settings */ if ((ah->ah_version == AR5K_AR5212) && (pdev->is_pcie)) { ath5k_hw_reg_write(ah, 0x9248fc00, 0x4080); @@ -375,10 +350,15 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) if (ret) goto err_free; + /* Write AR5K_PCICFG_UNK on 2112B and later chips */ + if (ah->ah_radio_5ghz_revision > AR5K_SREV_RAD_2112B || + srev > AR5K_SREV_VER_AR2413) { + ath5k_hw_reg_write(ah, AR5K_PCICFG_UNK, AR5K_PCICFG); + } + /* * Get card capabilities, values, ... */ - ret = ath5k_eeprom_init(ah); if (ret) { ATH5K_ERR(sc, "unable to init EEPROM\n"); -- cgit v1.2.1 From 2203d6be7ed17af81a1dc35a0af9806086743b02 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 20 Jul 2008 06:36:52 +0300 Subject: ath5k: Misc hw_reset updates * Update hw_reset to calculate some of the values we were using as static * Increase activation to rx delay Changes-licensed-under: ISC Signed-off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/hw.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/hw.c b/drivers/net/wireless/ath5k/hw.c index 8cd8659e9128..dc51b844c62d 100644 --- a/drivers/net/wireless/ath5k/hw.c +++ b/drivers/net/wireless/ath5k/hw.c @@ -847,7 +847,22 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, else ath5k_hw_reg_write(ah, 0x00000000, 0x994c); - ath5k_hw_reg_write(ah, 0x000009b5, 0xa228); + /* Some bits are disabled here, we know nothing about + * register 0xa228 yet, most of the times this ends up + * with a value 0x9b5 -haven't seen any dump with + * a different value- */ + /* Got this from decompiling binary HAL */ + data = ath5k_hw_reg_read(ah, 0xa228); + data &= 0xfffffdff; + ath5k_hw_reg_write(ah, data, 0xa228); + + data = ath5k_hw_reg_read(ah, 0xa228); + data &= 0xfffe03ff; + ath5k_hw_reg_write(ah, data, 0xa228); + data = 0; + + /* Just write 0x9b5 ? */ + /* ath5k_hw_reg_write(ah, 0x000009b5, 0xa228); */ ath5k_hw_reg_write(ah, 0x0000000f, AR5K_SEQ_MASK); ath5k_hw_reg_write(ah, 0x00000000, 0xa254); ath5k_hw_reg_write(ah, 0x0000000e, AR5K_PHY_SCAL); @@ -864,6 +879,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, else data = 0xffb80d20; ath5k_hw_reg_write(ah, data, AR5K_PHY_FRAME_CTL); + data = 0; } /* @@ -883,7 +899,6 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, /* * Write RF registers - * TODO:Does this work on 5211 (5111) ? */ ret = ath5k_hw_rfregs(ah, channel, mode); if (ret) @@ -1048,7 +1063,8 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, ath5k_hw_reg_write(ah, AR5K_PHY_ACT_ENABLE, AR5K_PHY_ACT); /* - * 5111/5112 Specific + * On 5211+ read activation -> rx delay + * and use it. */ if (ah->ah_version != AR5K_AR5210) { data = ath5k_hw_reg_read(ah, AR5K_PHY_RX_DELAY) & @@ -1056,7 +1072,8 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, data = (channel->hw_value & CHANNEL_CCK) ? ((data << 2) / 22) : (data / 10); - udelay(100 + data); + udelay(100 + (2 * data)); + data = 0; } else { mdelay(1); } @@ -1139,6 +1156,12 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, ath5k_hw_reg_write(ah, AR5K_PHY_SCLOCK_32MHZ, AR5K_PHY_SCLOCK); ath5k_hw_reg_write(ah, AR5K_PHY_SDELAY_32MHZ, AR5K_PHY_SDELAY); ath5k_hw_reg_write(ah, ah->ah_phy_spending, AR5K_PHY_SPENDING); + + data = ath5k_hw_reg_read(ah, AR5K_USEC_5211) & 0xffffc07f ; + data |= (ah->ah_phy_spending == AR5K_PHY_SPENDING_18) ? + 0x00000f80 : 0x00001380 ; + ath5k_hw_reg_write(ah, data, AR5K_USEC_5211); + data = 0; } if (ah->ah_version == AR5K_AR5212) { -- cgit v1.2.1 From e2a0ccebc4ffabc1c7234cfd324299b5a936e0f2 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 20 Jul 2008 06:38:16 +0300 Subject: ath5k: Do ADC test during reset * Do an ADC test during reset to match recent regdumps Changes-licensed-under: ISC Signed-off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/hw.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/hw.c b/drivers/net/wireless/ath5k/hw.c index dc51b844c62d..3937e46e4735 100644 --- a/drivers/net/wireless/ath5k/hw.c +++ b/drivers/net/wireless/ath5k/hw.c @@ -1078,6 +1078,19 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, mdelay(1); } + /* + * Perform ADC test (?) + */ + data = ath5k_hw_reg_read(ah, AR5K_PHY_TST1); + ath5k_hw_reg_write(ah, AR5K_PHY_TST1_TXHOLD, AR5K_PHY_TST1); + for (i = 0; i <= 20; i++) { + if (!(ath5k_hw_reg_read(ah, AR5K_PHY_ADC_TEST) & 0x10)) + break; + udelay(200); + } + ath5k_hw_reg_write(ah, data, AR5K_PHY_TST1); + data = 0; + /* * Enable calibration and wait until completion */ -- cgit v1.2.1 From df75dcddf99647d68f3b6b874effe5365c5024d9 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 20 Jul 2008 06:41:26 +0300 Subject: ath5k: Reorder calibration calls during reset and update hw_set_power * Update ath5k_hw_reset and add some more documentation about PHY calibration * Fix ath5k_hw_set_power to use AR5K_SLEEP_CTL_SLE_ALLOW for Network sleep * Preserve sleep duration field while setting AR5K_SLEEP_CTL and reduce delays & checks for register's status (got this from decompiling & dumps, it works for me but it needs testing) Changes-licensed-under: ISC Signed-off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/hw.c | 82 +++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 23 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/hw.c b/drivers/net/wireless/ath5k/hw.c index 3937e46e4735..ad1a5b422c8c 100644 --- a/drivers/net/wireless/ath5k/hw.c +++ b/drivers/net/wireless/ath5k/hw.c @@ -1092,34 +1092,57 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum ieee80211_if_types op_mode, data = 0; /* - * Enable calibration and wait until completion + * Start automatic gain calibration + * + * During AGC calibration RX path is re-routed to + * a signal detector so we don't receive anything. + * + * This method is used to calibrate some static offsets + * used together with on-the fly I/Q calibration (the + * one performed via ath5k_hw_phy_calibrate), that doesn't + * interrupt rx path. + * + * If we are in a noisy environment AGC calibration may time + * out. */ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL); + /* At the same time start I/Q calibration for QAM constellation + * -no need for CCK- */ + ah->ah_calibration = false; + if (!(mode == AR5K_MODE_11B)) { + ah->ah_calibration = true; + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, + AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, + AR5K_PHY_IQ_RUN); + } + + /* Wait for gain calibration to finish (we check for I/Q calibration + * during ath5k_phy_calibrate) */ if (ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_CAL, 0, false)) { - ATH5K_ERR(ah->ah_sc, "calibration timeout (%uMHz)\n", + ATH5K_ERR(ah->ah_sc, "gain calibration timeout (%uMHz)\n", channel->center_freq); return -EAGAIN; } + /* + * Start noise floor calibration + * + * If we run NF calibration before AGC, it always times out. + * Binary HAL starts NF and AGC calibration at the same time + * and only waits for AGC to finish. I believe that's wrong because + * during NF calibration, rx path is also routed to a detector, so if + * it doesn't finish we won't have RX. + * + * XXX: Find an interval that's OK for all cards... + */ ret = ath5k_hw_noise_floor_calibration(ah, channel->center_freq); if (ret) return ret; - ah->ah_calibration = false; - - /* A and G modes can use QAM modulation which requires enabling - * I and Q calibration. Don't bother in B mode. */ - if (!(mode == AR5K_MODE_11B)) { - ah->ah_calibration = true; - AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, - AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); - AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, - AR5K_PHY_IQ_RUN); - } - /* * Reset queues and start beacon timers at the end of the reset routine */ @@ -1247,7 +1270,7 @@ int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, bool set_chip, u16 sleep_duration) { unsigned int i; - u32 staid; + u32 staid, data; ATH5K_TRACE(ah->ah_sc); staid = ath5k_hw_reg_read(ah, AR5K_STA_ID1); @@ -1259,7 +1282,8 @@ int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, case AR5K_PM_NETWORK_SLEEP: if (set_chip) ath5k_hw_reg_write(ah, - AR5K_SLEEP_CTL_SLE | sleep_duration, + AR5K_SLEEP_CTL_SLE_ALLOW | + sleep_duration, AR5K_SLEEP_CTL); staid |= AR5K_STA_ID1_PWR_SV; @@ -1274,13 +1298,24 @@ int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, break; case AR5K_PM_AWAKE: + + staid &= ~AR5K_STA_ID1_PWR_SV; + if (!set_chip) goto commit; - ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_WAKE, - AR5K_SLEEP_CTL); + /* Preserve sleep duration */ + data = ath5k_hw_reg_read(ah, AR5K_SLEEP_CTL); + if( data & 0xffc00000 ){ + data = 0; + } else { + data = data & 0xfffcffff; + } + + ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL); + udelay(15); - for (i = 5000; i > 0; i--) { + for (i = 50; i > 0; i--) { /* Check if the chip did wake up */ if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) & AR5K_PCICFG_SPWR_DN) == 0) @@ -1288,15 +1323,13 @@ int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, /* Wait a bit and retry */ udelay(200); - ath5k_hw_reg_write(ah, AR5K_SLEEP_CTL_SLE_WAKE, - AR5K_SLEEP_CTL); + ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL); } /* Fail if the chip didn't wake up */ if (i <= 0) return -EIO; - staid &= ~AR5K_STA_ID1_PWR_SV; break; default: @@ -1325,6 +1358,7 @@ void ath5k_hw_start_rx(struct ath5k_hw *ah) { ATH5K_TRACE(ah->ah_sc); ath5k_hw_reg_write(ah, AR5K_CR_RXE, AR5K_CR); + ath5k_hw_reg_read(ah, AR5K_CR); } /* @@ -1411,6 +1445,7 @@ int ath5k_hw_tx_start(struct ath5k_hw *ah, unsigned int queue) } /* Start queue */ ath5k_hw_reg_write(ah, tx_queue, AR5K_CR); + ath5k_hw_reg_read(ah, AR5K_CR); } else { /* Return if queue is disabled */ if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXD, queue)) @@ -1708,6 +1743,7 @@ enum ath5k_int ath5k_hw_set_intr(struct ath5k_hw *ah, enum ath5k_int new_mask) * (they will be re-enabled afterwards). */ ath5k_hw_reg_write(ah, AR5K_IER_DISABLE, AR5K_IER); + ath5k_hw_reg_read(ah, AR5K_IER); old_mask = ah->ah_imr; @@ -3511,7 +3547,7 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) if (tq->tqi_flags & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE) AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), - AR5K_QCU_MISC_TXE); + AR5K_QCU_MISC_RDY_VEOL_POLICY); } if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE) -- cgit v1.2.1 From 27bcdeed320c8c7dc0f502df43f6465f0d9840f1 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 20 Jul 2008 06:42:47 +0300 Subject: ath5k: Add RF2425 initial rfgain values * Add initial RF gain settings for RF2425 Changes-licensed-under: ISC Signed-off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/phy.c | 72 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/phy.c b/drivers/net/wireless/ath5k/phy.c index afd8689e5c03..66af70bd14e7 100644 --- a/drivers/net/wireless/ath5k/phy.c +++ b/drivers/net/wireless/ath5k/phy.c @@ -1020,6 +1020,74 @@ static const struct ath5k_ini_rfgain rfgain_2413[] = { { AR5K_RF_GAIN(63), { 0x000000f9 } }, }; +/* Initial RF Gain settings for RF2425 */ +static const struct ath5k_ini_rfgain rfgain_2425[] = { + { AR5K_RF_GAIN(0), { 0x00000000 } }, + { AR5K_RF_GAIN(1), { 0x00000040 } }, + { AR5K_RF_GAIN(2), { 0x00000080 } }, + { AR5K_RF_GAIN(3), { 0x00000181 } }, + { AR5K_RF_GAIN(4), { 0x000001c1 } }, + { AR5K_RF_GAIN(5), { 0x00000001 } }, + { AR5K_RF_GAIN(6), { 0x00000041 } }, + { AR5K_RF_GAIN(7), { 0x00000081 } }, + { AR5K_RF_GAIN(8), { 0x00000188 } }, + { AR5K_RF_GAIN(9), { 0x000001c8 } }, + { AR5K_RF_GAIN(10), { 0x00000008 } }, + { AR5K_RF_GAIN(11), { 0x00000048 } }, + { AR5K_RF_GAIN(12), { 0x00000088 } }, + { AR5K_RF_GAIN(13), { 0x00000189 } }, + { AR5K_RF_GAIN(14), { 0x000001c9 } }, + { AR5K_RF_GAIN(15), { 0x00000009 } }, + { AR5K_RF_GAIN(16), { 0x00000049 } }, + { AR5K_RF_GAIN(17), { 0x00000089 } }, + { AR5K_RF_GAIN(18), { 0x000001b0 } }, + { AR5K_RF_GAIN(19), { 0x000001f0 } }, + { AR5K_RF_GAIN(20), { 0x00000030 } }, + { AR5K_RF_GAIN(21), { 0x00000070 } }, + { AR5K_RF_GAIN(22), { 0x00000171 } }, + { AR5K_RF_GAIN(23), { 0x000001b1 } }, + { AR5K_RF_GAIN(24), { 0x000001f1 } }, + { AR5K_RF_GAIN(25), { 0x00000031 } }, + { AR5K_RF_GAIN(26), { 0x00000071 } }, + { AR5K_RF_GAIN(27), { 0x000001b8 } }, + { AR5K_RF_GAIN(28), { 0x000001f8 } }, + { AR5K_RF_GAIN(29), { 0x00000038 } }, + { AR5K_RF_GAIN(30), { 0x00000078 } }, + { AR5K_RF_GAIN(31), { 0x000000b8 } }, + { AR5K_RF_GAIN(32), { 0x000001b9 } }, + { AR5K_RF_GAIN(33), { 0x000001f9 } }, + { AR5K_RF_GAIN(34), { 0x00000039 } }, + { AR5K_RF_GAIN(35), { 0x00000079 } }, + { AR5K_RF_GAIN(36), { 0x000000b9 } }, + { AR5K_RF_GAIN(37), { 0x000000f9 } }, + { AR5K_RF_GAIN(38), { 0x000000f9 } }, + { AR5K_RF_GAIN(39), { 0x000000f9 } }, + { AR5K_RF_GAIN(40), { 0x000000f9 } }, + { AR5K_RF_GAIN(41), { 0x000000f9 } }, + { AR5K_RF_GAIN(42), { 0x000000f9 } }, + { AR5K_RF_GAIN(43), { 0x000000f9 } }, + { AR5K_RF_GAIN(44), { 0x000000f9 } }, + { AR5K_RF_GAIN(45), { 0x000000f9 } }, + { AR5K_RF_GAIN(46), { 0x000000f9 } }, + { AR5K_RF_GAIN(47), { 0x000000f9 } }, + { AR5K_RF_GAIN(48), { 0x000000f9 } }, + { AR5K_RF_GAIN(49), { 0x000000f9 } }, + { AR5K_RF_GAIN(50), { 0x000000f9 } }, + { AR5K_RF_GAIN(51), { 0x000000f9 } }, + { AR5K_RF_GAIN(52), { 0x000000f9 } }, + { AR5K_RF_GAIN(53), { 0x000000f9 } }, + { AR5K_RF_GAIN(54), { 0x000000f9 } }, + { AR5K_RF_GAIN(55), { 0x000000f9 } }, + { AR5K_RF_GAIN(56), { 0x000000f9 } }, + { AR5K_RF_GAIN(57), { 0x000000f9 } }, + { AR5K_RF_GAIN(58), { 0x000000f9 } }, + { AR5K_RF_GAIN(59), { 0x000000f9 } }, + { AR5K_RF_GAIN(60), { 0x000000f9 } }, + { AR5K_RF_GAIN(61), { 0x000000f9 } }, + { AR5K_RF_GAIN(62), { 0x000000f9 } }, + { AR5K_RF_GAIN(63), { 0x000000f9 } }, +}; + static const struct ath5k_gain_opt rfgain_opt_5112 = { 1, 8, @@ -1588,8 +1656,8 @@ int ath5k_hw_rfgain(struct ath5k_hw *ah, unsigned int freq) freq = 0; /* only 2Ghz */ break; case AR5K_RF2425: - ath5k_rfg = rfgain_2413; - size = ARRAY_SIZE(rfgain_2413); + ath5k_rfg = rfgain_2425; + size = ARRAY_SIZE(rfgain_2425); freq = 0; /* only 2Ghz */ break; default: -- cgit v1.2.1 From cc6323c7d8c231d83e592ff9f7acf2cac5e016f7 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 20 Jul 2008 06:44:43 +0300 Subject: ath5k: Update channel functions * Add channel function for RF2425 (got this from decompiling binary HAL, i have no idea why there is a 5GHz section but i'm looking into it) * Update RF5112 channel function (also got this from decompiling binary HAL) * Set JAPAN setting for channel 14 on all PHY chips Changes-licensed-under: ISC Signed-off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/phy.c | 59 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/phy.c b/drivers/net/wireless/ath5k/phy.c index 66af70bd14e7..cbc362d20719 100644 --- a/drivers/net/wireless/ath5k/phy.c +++ b/drivers/net/wireless/ath5k/phy.c @@ -1898,9 +1898,6 @@ static int ath5k_hw_rf5112_channel(struct ath5k_hw *ah, data = data0 = data1 = data2 = 0; c = channel->center_freq; - /* - * Set the channel on the RF5112 or newer - */ if (c < 4800) { if (!((c - 2224) % 5)) { data0 = ((2 * (c - 704)) - 3040) / 10; @@ -1912,7 +1909,7 @@ static int ath5k_hw_rf5112_channel(struct ath5k_hw *ah, return -EINVAL; data0 = ath5k_hw_bitswap((data0 << 2) & 0xff, 8); - } else { + } else if ((c - (c % 5)) != 2 || c > 5435) { if (!(c % 20) && c >= 5120) { data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8); data2 = ath5k_hw_bitswap(3, 2); @@ -1924,6 +1921,9 @@ static int ath5k_hw_rf5112_channel(struct ath5k_hw *ah, data2 = ath5k_hw_bitswap(1, 2); } else return -EINVAL; + } else { + data0 = ath5k_hw_bitswap((10 * (c - 2) - 4800) / 25 + 1, 8); + data2 = ath5k_hw_bitswap(0, 2); } data = (data0 << 4) | (data1 << 1) | (data2 << 2) | 0x1001; @@ -1934,6 +1934,45 @@ static int ath5k_hw_rf5112_channel(struct ath5k_hw *ah, return 0; } +/* + * Set the channel on the RF2425 + */ +static int ath5k_hw_rf2425_channel(struct ath5k_hw *ah, + struct ieee80211_channel *channel) +{ + u32 data, data0, data2; + u16 c; + + data = data0 = data2 = 0; + c = channel->center_freq; + + if (c < 4800) { + data0 = ath5k_hw_bitswap((c - 2272), 8); + data2 = 0; + /* ? 5GHz ? */ + } else if ((c - (c % 5)) != 2 || c > 5435) { + if (!(c % 20) && c < 5120) + data0 = ath5k_hw_bitswap(((c - 4800) / 20 << 2), 8); + else if (!(c % 10)) + data0 = ath5k_hw_bitswap(((c - 4800) / 10 << 1), 8); + else if (!(c % 5)) + data0 = ath5k_hw_bitswap((c - 4800) / 5, 8); + else + return -EINVAL; + data2 = ath5k_hw_bitswap(1, 2); + } else { + data0 = ath5k_hw_bitswap((10 * (c - 2) - 4800) / 25 + 1, 8); + data2 = ath5k_hw_bitswap(0, 2); + } + + data = (data0 << 4) | data2 << 2 | 0x1001; + + ath5k_hw_reg_write(ah, data & 0xff, AR5K_RF_BUFFER); + ath5k_hw_reg_write(ah, (data >> 8) & 0x7f, AR5K_RF_BUFFER_CONTROL_5); + + return 0; +} + /* * Set a channel on the radio chip */ @@ -1963,6 +2002,9 @@ int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) case AR5K_RF5111: ret = ath5k_hw_rf5111_channel(ah, channel); break; + case AR5K_RF2425: + ret = ath5k_hw_rf2425_channel(ah, channel); + break; default: ret = ath5k_hw_rf5112_channel(ah, channel); break; @@ -1971,6 +2013,15 @@ int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) if (ret) return ret; + /* Set JAPAN setting for channel 14 */ + if (channel->center_freq == 2484) { + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL, + AR5K_PHY_CCKTXCTL_JAPAN); + } else { + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_CCKTXCTL, + AR5K_PHY_CCKTXCTL_WORLD); + } + ah->ah_current_channel.center_freq = channel->center_freq; ah->ah_current_channel.hw_value = channel->hw_value; ah->ah_turbo = channel->hw_value == CHANNEL_T ? true : false; -- cgit v1.2.1 From f860ee26db51c478fd70039bd4902912a8d93993 Mon Sep 17 00:00:00 2001 From: Nick Kossifidis Date: Sun, 20 Jul 2008 06:47:12 +0300 Subject: ath5k: Update phy calibration functions * Enable I/Q calibration each time we have correction results (we were only enabling calibration during reset). If we don't we commit the same results each time calibration routine is called. * Add some documentation and a TODO on nf calibration * Return -EAGAIN on noise floor timeout/failure Changes-licensed-under: ISC Signed-off-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/phy.c | 54 +++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/phy.c b/drivers/net/wireless/ath5k/phy.c index cbc362d20719..fa0d47faf574 100644 --- a/drivers/net/wireless/ath5k/phy.c +++ b/drivers/net/wireless/ath5k/phy.c @@ -2052,6 +2052,8 @@ int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) * http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL \ * &p=1&u=%2Fnetahtml%2FPTO%2Fsrchnum.htm&r=1&f=G&l=50&s1=7245893.PN.&OS=PN/7 * + * XXX: Since during noise floor calibration antennas are detached according to + * the patent, we should stop tx queues here. */ int ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq) @@ -2061,7 +2063,7 @@ ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq) s32 noise_floor; /* - * Enable noise floor calibration and wait until completion + * Enable noise floor calibration */ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF); @@ -2071,7 +2073,7 @@ ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq) if (ret) { ATH5K_ERR(ah->ah_sc, "noise floor calibration timeout (%uMHz)\n", freq); - return ret; + return -EAGAIN; } /* Wait until the noise floor is calibrated and read the value */ @@ -2093,7 +2095,7 @@ ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq) if (noise_floor > AR5K_TUNE_NOISE_FLOOR) { ATH5K_ERR(ah->ah_sc, "noise floor calibration failed (%uMHz)\n", freq); - return -EIO; + return -EAGAIN; } ah->ah_noise_floor = noise_floor; @@ -2206,38 +2208,66 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, } /* - * Perform a PHY calibration on RF5111/5112 + * Perform a PHY calibration on RF5111/5112 and newer chips */ static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel) { u32 i_pwr, q_pwr; s32 iq_corr, i_coff, i_coffd, q_coff, q_coffd; + int i; ATH5K_TRACE(ah->ah_sc); if (!ah->ah_calibration || - ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN) + ath5k_hw_reg_read(ah, AR5K_PHY_IQ) & AR5K_PHY_IQ_RUN) goto done; - ah->ah_calibration = false; + /* Calibration has finished, get the results and re-run */ + for (i = 0; i <= 10; i++) { + iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR); + i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I); + q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q); + } - iq_corr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_CORR); - i_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_I); - q_pwr = ath5k_hw_reg_read(ah, AR5K_PHY_IQRES_CAL_PWR_Q); i_coffd = ((i_pwr >> 1) + (q_pwr >> 1)) >> 7; - q_coffd = q_pwr >> 6; + q_coffd = q_pwr >> 7; + /* No correction */ if (i_coffd == 0 || q_coffd == 0) goto done; i_coff = ((-iq_corr) / i_coffd) & 0x3f; - q_coff = (((s32)i_pwr / q_coffd) - 64) & 0x1f; - /* Commit new IQ value */ + /* Boundary check */ + if (i_coff > 31) + i_coff = 31; + if (i_coff < -32) + i_coff = -32; + + q_coff = (((s32)i_pwr / q_coffd) - 128) & 0x1f; + + /* Boundary check */ + if (q_coff > 15) + q_coff = 15; + if (q_coff < -16) + q_coff = -16; + + /* Commit new I/Q value */ AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_CORR_ENABLE | ((u32)q_coff) | ((u32)i_coff << AR5K_PHY_IQ_CORR_Q_I_COFF_S)); + /* Re-enable calibration -if we don't we'll commit + * the same values again and again */ + AR5K_REG_WRITE_BITS(ah, AR5K_PHY_IQ, + AR5K_PHY_IQ_CAL_NUM_LOG_MAX, 15); + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ, AR5K_PHY_IQ_RUN); + done: + + /* TODO: Separate noise floor calibration from I/Q calibration + * since noise floor calibration interrupts rx path while I/Q + * calibration doesn't. We don't need to run noise floor calibration + * as often as I/Q calibration.*/ ath5k_hw_noise_floor_calibration(ah, channel->center_freq); /* Request RF gain */ -- cgit v1.2.1 From dc1968e7b7862bcd2d358c1be6119c011992bdd2 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 23 Jul 2008 13:17:34 +0200 Subject: Ath5k: mask out unneeded interrupts Mask out previously demanded interrupt flags because we set new ones. Don't allow mixing them after switch from sta to ibss and vice versa. Signed-off-by: Jiri Slaby Cc: Nick Kossifidis Cc: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index ff3fad794b61..ebf19bc11f5b 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2170,6 +2170,7 @@ ath5k_beacon_config(struct ath5k_softc *sc) ath5k_hw_set_intr(ah, 0); sc->bmisscount = 0; + sc->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA); if (sc->opmode == IEEE80211_IF_TYPE_STA) { sc->imask |= AR5K_INT_BMISS; -- cgit v1.2.1 From 8de394f60235a825b32f30441290a44251eca45d Mon Sep 17 00:00:00 2001 From: Helmut Schaa Date: Thu, 24 Jul 2008 18:22:55 +0200 Subject: ath5k: remove obsolete declaration of struct ieee80211_hw_mode Signed-off-by: Helmut Schaa Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/debug.h | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/debug.h b/drivers/net/wireless/ath5k/debug.h index 2cf8d18b10e3..ffc529393306 100644 --- a/drivers/net/wireless/ath5k/debug.h +++ b/drivers/net/wireless/ath5k/debug.h @@ -63,7 +63,6 @@ struct ath5k_softc; struct ath5k_hw; -struct ieee80211_hw_mode; struct sk_buff; struct ath5k_buf; -- cgit v1.2.1 From 143b09efb74efd3328f57d7a4bd6d7663c1d6497 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 24 Jul 2008 21:33:42 +0300 Subject: iwlwifi: don't stop queue in the middle of fragmented packet This patch avoids stopping queue in the middle of the fragmented packet. It is required that there will be ~10 (max packet/min fragment) or 16 (4 bits of frag number) free tfds all the time. Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-tx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index f72cd0bf6aa3..0182e4da8e35 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -962,16 +962,16 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) if (ret) return ret; - if ((iwl_queue_space(q) < q->high_mark) - && priv->mac80211_registered) { + if ((iwl_queue_space(q) < q->high_mark) && priv->mac80211_registered) { if (wait_write_ptr) { spin_lock_irqsave(&priv->lock, flags); txq->need_update = 1; iwl_txq_update_write_ptr(priv, txq); spin_unlock_irqrestore(&priv->lock, flags); + } else { + ieee80211_stop_queue(priv->hw, + skb_get_queue_mapping(skb)); } - - ieee80211_stop_queue(priv->hw, skb_get_queue_mapping(skb)); } return 0; -- cgit v1.2.1 From 7c7e6af37dad30632103497a72a1273d18ec55fe Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Fri, 25 Jul 2008 19:08:11 +0200 Subject: Rtl8187 PATCH add usb ID for asus wireless link This patch from Davide Cavalca adds a usb ID for an rtl8187L device. Signed-off-by: John W. Linville --- drivers/net/wireless/Kconfig | 1 + drivers/net/wireless/rtl8187_dev.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 91fc2c765d90..4c7ff61a1a9c 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -649,6 +649,7 @@ config RTL8187 Trendnet TEW-424UB ASUS P5B Deluxe Toshiba Satellite Pro series of laptops + Asus Wireless Link Thanks to Realtek for their support! diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c index 177988efd660..461aa26164c2 100644 --- a/drivers/net/wireless/rtl8187_dev.c +++ b/drivers/net/wireless/rtl8187_dev.c @@ -31,6 +31,8 @@ MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver"); MODULE_LICENSE("GPL"); static struct usb_device_id rtl8187_table[] __devinitdata = { + /* Asus */ + {USB_DEVICE(0x0b05, 0x171d), .driver_info = DEVICE_RTL8187}, /* Realtek */ {USB_DEVICE(0x0bda, 0x8187), .driver_info = DEVICE_RTL8187}, {USB_DEVICE(0x0bda, 0x8189), .driver_info = DEVICE_RTL8187B}, -- cgit v1.2.1 From d2b690714cd7d328561bfb9bf941edd6a3316a85 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Sun, 27 Jul 2008 15:06:05 +0200 Subject: rt2x00: Fix access permissions on debugfs files Although most rt2x00 debugfs files don't contain information which could compromise network security, it is better to set the access permissions to root only. This will be required when HW crypto is implemented, because it could be possible to read the HW key from the registers. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00debug.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c index 300cf061035f..6bee1d611bbf 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/rt2x00/rt2x00debug.c @@ -372,9 +372,6 @@ static ssize_t rt2x00debug_write_##__name(struct file *file, \ if (*offset) \ return 0; \ \ - if (!capable(CAP_NET_ADMIN)) \ - return -EPERM; \ - \ if (intf->offset_##__name >= debug->__name.word_count) \ return -EINVAL; \ \ @@ -454,7 +451,7 @@ static struct dentry *rt2x00debug_create_file_driver(const char *name, data += sprintf(data, "compiled: %s %s\n", __DATE__, __TIME__); blob->size = strlen(blob->data); - return debugfs_create_blob(name, S_IRUGO, intf->driver_folder, blob); + return debugfs_create_blob(name, S_IRUSR, intf->driver_folder, blob); } static struct dentry *rt2x00debug_create_file_chipset(const char *name, @@ -482,7 +479,7 @@ static struct dentry *rt2x00debug_create_file_chipset(const char *name, data += sprintf(data, "rf length: %d\n", debug->rf.word_count); blob->size = strlen(blob->data); - return debugfs_create_blob(name, S_IRUGO, intf->driver_folder, blob); + return debugfs_create_blob(name, S_IRUSR, intf->driver_folder, blob); } void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) @@ -517,7 +514,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) if (IS_ERR(intf->chipset_entry)) goto exit; - intf->dev_flags = debugfs_create_file("dev_flags", S_IRUGO, + intf->dev_flags = debugfs_create_file("dev_flags", S_IRUSR, intf->driver_folder, intf, &rt2x00debug_fop_dev_flags); if (IS_ERR(intf->dev_flags)) @@ -532,7 +529,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) ({ \ (__intf)->__name##_off_entry = \ debugfs_create_u32(__stringify(__name) "_offset", \ - S_IRUGO | S_IWUSR, \ + S_IRUSR | S_IWUSR, \ (__intf)->register_folder, \ &(__intf)->offset_##__name); \ if (IS_ERR((__intf)->__name##_off_entry)) \ @@ -540,7 +537,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) \ (__intf)->__name##_val_entry = \ debugfs_create_file(__stringify(__name) "_value", \ - S_IRUGO | S_IWUSR, \ + S_IRUSR | S_IWUSR, \ (__intf)->register_folder, \ (__intf), &rt2x00debug_fop_##__name);\ if (IS_ERR((__intf)->__name##_val_entry)) \ @@ -560,7 +557,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) goto exit; intf->queue_frame_dump_entry = - debugfs_create_file("dump", S_IRUGO, intf->queue_folder, + debugfs_create_file("dump", S_IRUSR, intf->queue_folder, intf, &rt2x00debug_fop_queue_dump); if (IS_ERR(intf->queue_frame_dump_entry)) goto exit; @@ -569,7 +566,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) init_waitqueue_head(&intf->frame_dump_waitqueue); intf->queue_stats_entry = - debugfs_create_file("queue", S_IRUGO, intf->queue_folder, + debugfs_create_file("queue", S_IRUSR, intf->queue_folder, intf, &rt2x00debug_fop_queue_stats); return; -- cgit v1.2.1 From ada662f3eb6231ab27f5e6366d4e5c395d25edd3 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Sun, 27 Jul 2008 15:06:21 +0200 Subject: rt2x00: Fix partial antenna configuration The if-statement to determine the new TX/RX antenna configuration was incomplete. It lacks the general else-clause when the antenna wasn't changed. This is a correct event, since it can occur when only one of the antenna's has been changed or when the new configuration is being forced (like when the interface has just been added). Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00config.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index 3f89516e8332..d134c3be539a 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c @@ -254,6 +254,8 @@ config: libconf.ant.rx = default_ant->rx; else if (active_ant->rx == ANTENNA_SW_DIVERSITY) libconf.ant.rx = ANTENNA_B; + else + libconf.ant.rx = active_ant->rx; if (conf->antenna_sel_tx) libconf.ant.tx = conf->antenna_sel_tx; @@ -261,6 +263,8 @@ config: libconf.ant.tx = default_ant->tx; else if (active_ant->tx == ANTENNA_SW_DIVERSITY) libconf.ant.tx = ANTENNA_B; + else + libconf.ant.tx = active_ant->tx; } if (flags & CONFIG_UPDATE_SLOT_TIME) { -- cgit v1.2.1 From e6d3e902088ac5da77b074f513e3cb80422ff471 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Sun, 27 Jul 2008 15:06:50 +0200 Subject: rt2x00: rt61pci needs another millisecond after firmware upload After the hardware has indicated the firmware upload has completed and the device is ready, we should wait another millisecond to make sure the device is really ready to continue. Without this timout, bringing the interface down and up again will fail due to incorrect register initialization. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt61pci.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index fbe2a652e014..087e90b328cd 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -1003,6 +1003,11 @@ static int rt61pci_load_firmware(struct rt2x00_dev *rt2x00dev, const void *data, return -EBUSY; } + /* + * Hardware needs another millisecond before it is ready. + */ + msleep(1); + /* * Reset MAC and BBP registers. */ -- cgit v1.2.1 From 8d8acd46fb7e962ac04baef5a118d431fae6b0f6 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 28 Jul 2008 10:20:12 +0200 Subject: rt2x00: Fix VGC lower bound initialization When the EEPROM_BBPTUNE_VGC word is valid, we should override EEPROM_BBPTUNE_VGCLOWER field with the BBP value. And we should _not_ do that when EEPROM_BBPTUNE_R17 is valid. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2500usb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index 3078417b326b..c6f6eb6e17a1 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1376,6 +1376,9 @@ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word); EEPROM(rt2x00dev, "BBPtune vgc: 0x%04x\n", word); + } else { + rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp); + rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R17, &word); @@ -1384,9 +1387,6 @@ static int rt2500usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) rt2x00_set_field16(&word, EEPROM_BBPTUNE_R17_HIGH, 0x41); rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_R17, word); EEPROM(rt2x00dev, "BBPtune r17: 0x%04x\n", word); - } else { - rt2x00_set_field16(&word, EEPROM_BBPTUNE_VGCLOWER, bbp); - rt2x00_eeprom_write(rt2x00dev, EEPROM_BBPTUNE_VGC, word); } rt2x00_eeprom_read(rt2x00dev, EEPROM_BBPTUNE_R24, &word); -- cgit v1.2.1 From d4764b29b6e0f1608e397930677928e5a3f62bba Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Mon, 28 Jul 2008 10:21:16 +0200 Subject: rt2x00: Sequence counter should be protected in irqsave The sequence counter can be accessed in IRQ context, which means the lock protecting the counter should be irqsave. To prevent making the entire intf->lock irqsave without reason, create a new lock which only protects the sequence counter. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00.h | 6 ++++++ drivers/net/wireless/rt2x00/rt2x00mac.c | 1 + drivers/net/wireless/rt2x00/rt2x00queue.c | 5 +++-- 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index db2dc976d831..8b10ea41b204 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -368,6 +368,12 @@ struct rt2x00_intf { #define DELAYED_CONFIG_ERP 0x00000002 #define DELAYED_LED_ASSOC 0x00000004 + /* + * Software sequence counter, this is only required + * for hardware which doesn't support hardware + * sequence counting. + */ + spinlock_t seqlock; u16 seqno; }; diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index c3ee4ecba792..bd422fd6a894 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -247,6 +247,7 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw, rt2x00dev->intf_sta_count++; spin_lock_init(&intf->lock); + spin_lock_init(&intf->seqlock); intf->beacon = entry; if (conf->type == IEEE80211_IF_TYPE_AP) diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 3b27f6aa860c..898cdd7f57d9 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -128,6 +128,7 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, unsigned int data_length; unsigned int duration; unsigned int residual; + unsigned long irqflags; memset(txdesc, 0, sizeof(*txdesc)); @@ -213,14 +214,14 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, * sequence counter given by mac80211. */ if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - spin_lock(&intf->lock); + spin_lock_irqsave(&intf->seqlock, irqflags); if (test_bit(ENTRY_TXD_FIRST_FRAGMENT, &txdesc->flags)) intf->seqno += 0x10; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(intf->seqno); - spin_unlock(&intf->lock); + spin_unlock_irqrestore(&intf->seqlock, irqflags); __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); } -- cgit v1.2.1 From 3b72b01d3ab623c296df49f2d71d40a38bcfb4b3 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 29 Jul 2008 13:50:39 -0400 Subject: libertas: only enable rtap with mesh firmware Since only mesh-enabled firmware has the CMD_802_11_MONITOR_MODE on which the rtap functionality depends, only expose the rtap functionality when mesh is also available. Signed-off-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/main.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 14d5d61cec4c..bd32ac0b4e07 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -297,9 +297,7 @@ static ssize_t lbs_rtap_set(struct device *dev, lbs_add_rtap(priv); } priv->monitormode = monitor_mode; - } - - else { + } else { if (!priv->monitormode) return strlen(buf); priv->monitormode = 0; @@ -1242,8 +1240,6 @@ int lbs_start_card(struct lbs_private *priv) lbs_pr_err("cannot register ethX device\n"); goto done; } - if (device_create_file(&dev->dev, &dev_attr_lbs_rtap)) - lbs_pr_err("cannot register lbs_rtap attribute\n"); lbs_update_channel(priv); @@ -1275,6 +1271,13 @@ int lbs_start_card(struct lbs_private *priv) if (device_create_file(&dev->dev, &dev_attr_lbs_mesh)) lbs_pr_err("cannot register lbs_mesh attribute\n"); + + /* While rtap isn't related to mesh, only mesh-enabled + * firmware implements the rtap functionality via + * CMD_802_11_MONITOR_MODE. + */ + if (device_create_file(&dev->dev, &dev_attr_lbs_rtap)) + lbs_pr_err("cannot register lbs_rtap attribute\n"); } } @@ -1306,9 +1309,9 @@ void lbs_stop_card(struct lbs_private *priv) netif_carrier_off(priv->dev); lbs_debugfs_remove_one(priv); - device_remove_file(&dev->dev, &dev_attr_lbs_rtap); if (priv->mesh_tlv) { device_remove_file(&dev->dev, &dev_attr_lbs_mesh); + device_remove_file(&dev->dev, &dev_attr_lbs_rtap); } /* Flush pending command nodes */ -- cgit v1.2.1 From bf4634afd8bb72936d2d56425ec792ca1bfa92a2 Mon Sep 17 00:00:00 2001 From: Peter Chubb Date: Thu, 31 Jul 2008 10:56:34 +1000 Subject: rt2500pci: restoring missing line In kernel version 2.6.26-rc9 my wireless LAN card worked; but in the released 2.6.26, my RaLink rt2500 card wouldn't associate. Git-bisect led me to this patch: 61486e0f68d1f8966c09b734566a187d42d65c54 rt2x00: Remove ieee80211_tx_control argument from write_tx_desc() I believe that there is a problem with that patch --- it (inadvertantly) removes an extra line of code, that used to set the DATABYTE_COUNT field. This patch reinstates that line, and with it my card works again. Signed-off-by: Peter Chubb Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2500pci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index aa6dfb811c71..181a146b4768 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -1220,6 +1220,7 @@ static void rt2500pci_write_tx_desc(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&word, TXD_W0_IFS, txdesc->ifs); rt2x00_set_field32(&word, TXD_W0_RETRY_MODE, test_bit(ENTRY_TXD_RETRY_MODE, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W0_DATABYTE_COUNT, skb->len); rt2x00_set_field32(&word, TXD_W0_CIPHER_ALG, CIPHER_NONE); rt2x00_desc_write(txd, 0, word); } -- cgit v1.2.1 From 7dcdd073bf78bb6958bbc12a1a47754a0f3c4721 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 31 Jul 2008 19:30:48 -0500 Subject: rtl8187: Fix lockups due to concurrent access to config routine Some users of the RTL8187B have experienced difficulties since commit 49292d56352a6ab90d04c3448dd8b6106dfef2d6 that introduced the power management wext hooks. This difficulty has not made much sense until it was realized that it was possible for mac80211 to make a call to the config routine while that routine was already being executed. On this device, it is necessary to loopback the TX when changing channels. Unless this is properly restored, the device will lockup. A mutex now protects the device state, and the private data in several places. The problem was found by Herton Ronaldo Krzesinski , who also suggested this type of fix. Signed-off-by: Larry Finger Acked-by: Herton Ronaldo Krzesinski Acked-by: Hin-Tak Leung Signed-off-by: John W. Linville --- drivers/net/wireless/rtl8187.h | 4 ++++ drivers/net/wireless/rtl8187_dev.c | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rtl8187.h b/drivers/net/wireless/rtl8187.h index 1b0d750f6623..5a9515c99960 100644 --- a/drivers/net/wireless/rtl8187.h +++ b/drivers/net/wireless/rtl8187.h @@ -94,6 +94,10 @@ struct rtl8187_priv { const struct rtl818x_rf_ops *rf; struct ieee80211_vif *vif; int mode; + /* The mutex protects the TX loopback state. + * Any attempt to set channels concurrently locks the device. + */ + struct mutex conf_mutex; /* rtl8187 specific */ struct ieee80211_channel channels[14]; diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c index 461aa26164c2..57376fb993ed 100644 --- a/drivers/net/wireless/rtl8187_dev.c +++ b/drivers/net/wireless/rtl8187_dev.c @@ -728,6 +728,7 @@ static int rtl8187_start(struct ieee80211_hw *dev) if (ret) return ret; + mutex_lock(&priv->conf_mutex); if (priv->is_rtl8187b) { reg = RTL818X_RX_CONF_MGMT | RTL818X_RX_CONF_DATA | @@ -749,6 +750,7 @@ static int rtl8187_start(struct ieee80211_hw *dev) (7 << 0 /* long retry limit */) | (7 << 21 /* MAX TX DMA */)); rtl8187_init_urbs(dev); + mutex_unlock(&priv->conf_mutex); return 0; } @@ -792,6 +794,7 @@ static int rtl8187_start(struct ieee80211_hw *dev) reg |= RTL818X_CMD_TX_ENABLE; reg |= RTL818X_CMD_RX_ENABLE; rtl818x_iowrite8(priv, &priv->map->CMD, reg); + mutex_unlock(&priv->conf_mutex); return 0; } @@ -803,6 +806,7 @@ static void rtl8187_stop(struct ieee80211_hw *dev) struct sk_buff *skb; u32 reg; + mutex_lock(&priv->conf_mutex); rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); reg = rtl818x_ioread8(priv, &priv->map->CMD); @@ -822,7 +826,7 @@ static void rtl8187_stop(struct ieee80211_hw *dev) usb_kill_urb(info->urb); kfree_skb(skb); } - return; + mutex_unlock(&priv->conf_mutex); } static int rtl8187_add_interface(struct ieee80211_hw *dev, @@ -842,6 +846,7 @@ static int rtl8187_add_interface(struct ieee80211_hw *dev, return -EOPNOTSUPP; } + mutex_lock(&priv->conf_mutex); priv->vif = conf->vif; rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); @@ -850,6 +855,7 @@ static int rtl8187_add_interface(struct ieee80211_hw *dev, ((u8 *)conf->mac_addr)[i]); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); + mutex_unlock(&priv->conf_mutex); return 0; } @@ -857,8 +863,10 @@ static void rtl8187_remove_interface(struct ieee80211_hw *dev, struct ieee80211_if_init_conf *conf) { struct rtl8187_priv *priv = dev->priv; + mutex_lock(&priv->conf_mutex); priv->mode = IEEE80211_IF_TYPE_MNTR; priv->vif = NULL; + mutex_unlock(&priv->conf_mutex); } static int rtl8187_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf) @@ -866,6 +874,7 @@ static int rtl8187_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf) struct rtl8187_priv *priv = dev->priv; u32 reg; + mutex_lock(&priv->conf_mutex); reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); /* Enable TX loopback on MAC level to avoid TX during channel * changes, as this has be seen to causes problems and the @@ -898,6 +907,7 @@ static int rtl8187_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf) rtl818x_iowrite16(priv, &priv->map->ATIMTR_INTERVAL, 100); rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL, 100); rtl818x_iowrite16(priv, &priv->map->BEACON_INTERVAL_TIME, 100); + mutex_unlock(&priv->conf_mutex); return 0; } @@ -909,6 +919,7 @@ static int rtl8187_config_interface(struct ieee80211_hw *dev, int i; u8 reg; + mutex_lock(&priv->conf_mutex); for (i = 0; i < ETH_ALEN; i++) rtl818x_iowrite8(priv, &priv->map->BSSID[i], conf->bssid[i]); @@ -922,6 +933,7 @@ static int rtl8187_config_interface(struct ieee80211_hw *dev, rtl818x_iowrite8(priv, &priv->map->MSR, reg); } + mutex_unlock(&priv->conf_mutex); return 0; } @@ -1189,6 +1201,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, printk(KERN_ERR "rtl8187: Cannot register device\n"); goto err_free_dev; } + mutex_init(&priv->conf_mutex); printk(KERN_INFO "%s: hwaddr %s, %s V%d + %s\n", wiphy_name(dev->wiphy), print_mac(mac, dev->wiphy->perm_addr), -- cgit v1.2.1 From fb55d887c5bd9054ec069534e1ef9eb8d9a983c6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 31 Jul 2008 19:02:06 +0200 Subject: ipw2200 - Fix bad ipw_write8() macro ipw_write8() can't be used alone with a loop because of a wrong definition. CC [M] drivers/net/wireless/ipw2200.o drivers/net/wireless/ipw2200.c: In function 'ipw_ethtool_set_eeprom': drivers/net/wireless/ipw2200.c:10579: warning: array subscript is above array bounds drivers/net/wireless/ipw2200.c: In function 'ipw_load': drivers/net/wireless/ipw2200.c:2663: warning: array subscript is above array bounds Add missing do {} while (0) to fix them. Signed-off-by: Takashi Iwai Signed-off-by: John W. Linville --- drivers/net/wireless/ipw2200.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c index 1acfbcd3703c..9509fd2a25f8 100644 --- a/drivers/net/wireless/ipw2200.c +++ b/drivers/net/wireless/ipw2200.c @@ -305,9 +305,10 @@ static inline void ipw_write_reg32(struct ipw_priv *a, u32 b, u32 c) #define _ipw_write8(ipw, ofs, val) writeb((val), (ipw)->hw_base + (ofs)) /* 8-bit direct write (for low 4K of SRAM/regs), with debug wrapper */ -#define ipw_write8(ipw, ofs, val) \ +#define ipw_write8(ipw, ofs, val) do { \ IPW_DEBUG_IO("%s %d: write_direct8(0x%08X, 0x%08X)\n", __FILE__, __LINE__, (u32)(ofs), (u32)(val)); \ - _ipw_write8(ipw, ofs, val) + _ipw_write8(ipw, ofs, val); \ + } while (0) /* 16-bit direct write (low 4K) */ #define _ipw_write16(ipw, ofs, val) writew((val), (ipw)->hw_base + (ofs)) -- cgit v1.2.1 From 3d0f823953e6b5aa36fc098de2d27e15da220974 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 31 Jul 2008 19:03:10 +0200 Subject: prism54 - Use offsetof() Use the standard offsetof() macro to fix a compile warning below: CC [M] drivers/net/wireless/prism54/isl_ioctl.o drivers/net/wireless/prism54/isl_ioctl.c: In function 'prism2_ioctl_set_generic_element': drivers/net/wireless/prism54/isl_ioctl.c:2658: warning: cast from pointer to integer of different size Signed-off-by: Takashi Iwai Signed-off-by: John W. Linville --- drivers/net/wireless/prism54/isl_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c index 97fa14e0a479..3d75a7137d3c 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -2518,7 +2518,7 @@ enum { #define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 #define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ -((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) + offsetof(struct prism2_hostapd_param, u.generic_elem.data) /* Maximum length for algorithm names (-1 for nul termination) * used in ioctl() */ -- cgit v1.2.1 From 56decd3c5758b0d776c073f65f777beb7a05ac0a Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Fri, 1 Aug 2008 12:54:27 +0300 Subject: iwl3945: Fix statistics in monitor mode iwl3945_rx_reply_rx was sending packets too early to mac80211, before updating signal strength/quality. This resulted in garbage power levels. Signed-off-by: Maxim Levitsky Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index a51e0eaa1334..56a9361a847f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -710,10 +710,7 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv, return; } - if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) { - iwl3945_pass_packet_to_mac80211(priv, rxb, &rx_status); - return; - } + /* Convert 3945's rssi indicator to dBm */ rx_status.signal = rx_stats->rssi - IWL_RSSI_OFFSET; @@ -775,6 +772,11 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv, priv->last_rx_noise = rx_status.noise; } + if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) { + iwl3945_pass_packet_to_mac80211(priv, rxb, &rx_status); + return; + } + switch (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FTYPE) { case IEEE80211_FTYPE_MGMT: switch (le16_to_cpu(header->frame_control) & -- cgit v1.2.1 From 61a2d07d3fb1ac34d142b9b62d4cd60a0f8c229e Mon Sep 17 00:00:00 2001 From: Niels de Vos Date: Thu, 31 Jul 2008 00:07:23 -0700 Subject: Remove newline from the description of module parameters Some module parameters with only one line have the '\n' at the end of the description. This is not needed nor wanted as after the description the type (i.e. int) is followed by a newline. Some modules contain a multi-line description, these are not affected by this patch. Signed-off-by: Niels de Vos Acked-by: Randy Dunlap Cc: John W. Linville Cc: Ed L. Cashin Cc: Dave Airlie Cc: Roland Dreier Acked-by: Mauro Carvalho Chehab Cc: Jeff Garzik Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/net/wireless/ipw2200.c | 2 +- drivers/net/wireless/iwlwifi/iwl-4965.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c index 1acfbcd3703c..846a7d051851 100644 --- a/drivers/net/wireless/ipw2200.c +++ b/drivers/net/wireless/ipw2200.c @@ -11946,7 +11946,7 @@ module_param(auto_create, int, 0444); MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)"); module_param(led, int, 0444); -MODULE_PARM_DESC(led, "enable led control on some systems (default 0 off)\n"); +MODULE_PARM_DESC(led, "enable led control on some systems (default 0 off)"); module_param(debug, int, 0444); MODULE_PARM_DESC(debug, "debug output mask"); diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 9afecb813716..ba2df1ba32d2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2469,7 +2469,7 @@ MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); module_param_named(disable, iwl4965_mod_params.disable, int, 0444); MODULE_PARM_DESC(disable, "manually disable the radio (default 0 [radio on])"); module_param_named(swcrypto, iwl4965_mod_params.sw_crypto, int, 0444); -MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])\n"); +MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); module_param_named(debug, iwl4965_mod_params.debug, int, 0444); MODULE_PARM_DESC(debug, "debug output mask"); module_param_named( -- cgit v1.2.1 From 82f97b8d3cb3982ec97e081598c671fab2c321b0 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Sat, 2 Aug 2008 01:31:09 -0700 Subject: rt2x00: Fix compile warning rt2x00usb_vendor_request_large_buff is write-only, so it is safe to make the argument a const. Fixes compile warning: drivers/net/wireless/rt2x00/rt73usb.c: In function 'rt73usb_load_firmware': drivers/net/wireless/rt2x00/rt73usb.c:916: warning: passing argument 5 of 'rt2x00usb_vendor_request_large_buff' discards qualifiers from pointer target typ Signed-off-by: Ivo van Doorn Signed-off-by: David S. Miller --- drivers/net/wireless/rt2x00/rt2x00usb.c | 4 ++-- drivers/net/wireless/rt2x00/rt2x00usb.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 933e6cc9359d..8d76bb2e0312 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -124,7 +124,7 @@ EXPORT_SYMBOL_GPL(rt2x00usb_vendor_request_buff); int rt2x00usb_vendor_request_large_buff(struct rt2x00_dev *rt2x00dev, const u8 request, const u8 requesttype, - const u16 offset, void *buffer, + const u16 offset, const void *buffer, const u16 buffer_length, const int timeout) { @@ -134,7 +134,7 @@ int rt2x00usb_vendor_request_large_buff(struct rt2x00_dev *rt2x00dev, mutex_lock(&rt2x00dev->usb_cache_mutex); - tb = buffer; + tb = (char *)buffer; off = offset; len = buffer_length; while (len && !status) { diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h index ee3875f894aa..3b4a67417f95 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.h +++ b/drivers/net/wireless/rt2x00/rt2x00usb.h @@ -185,7 +185,7 @@ int rt2x00usb_vendor_req_buff_lock(struct rt2x00_dev *rt2x00dev, */ int rt2x00usb_vendor_request_large_buff(struct rt2x00_dev *rt2x00dev, const u8 request, const u8 requesttype, - const u16 offset, void *buffer, + const u16 offset, const void *buffer, const u16 buffer_length, const int timeout); -- cgit v1.2.1 From ca579617d81baf5865498eb5fae58e453ee77c2c Mon Sep 17 00:00:00 2001 From: Mohamed Abbas Date: Fri, 18 Jul 2008 13:52:57 +0800 Subject: iwlwifi: add power save to 5000 HW This patch adds support for power save for 5000 HW. Signed-off-by: Mohamed Abbas Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-4965.c | 13 ------------- drivers/net/wireless/iwlwifi/iwl-5000.c | 1 + drivers/net/wireless/iwlwifi/iwl-commands.h | 4 ++-- drivers/net/wireless/iwlwifi/iwl-core.h | 1 - drivers/net/wireless/iwlwifi/iwl-dev.h | 3 ++- drivers/net/wireless/iwlwifi/iwl-power.c | 18 ++++++++++++------ drivers/net/wireless/iwlwifi/iwl-power.h | 2 +- 7 files changed, 18 insertions(+), 24 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index ba2df1ba32d2..ea23c7629575 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -875,18 +875,6 @@ static int iwl4965_hw_set_hw_params(struct iwl_priv *priv) return 0; } -/* set card power command */ -static int iwl4965_set_power(struct iwl_priv *priv, - void *cmd) -{ - int ret = 0; - - ret = iwl_send_cmd_pdu_async(priv, POWER_TABLE_CMD, - sizeof(struct iwl4965_powertable_cmd), - cmd, NULL); - return ret; -} - static s32 iwl4965_math_div_round(s32 num, s32 denom, s32 *res) { s32 sign = 1; @@ -2440,7 +2428,6 @@ static struct iwl_lib_ops iwl4965_lib = { .check_version = iwl4965_eeprom_check_version, .query_addr = iwlcore_eeprom_query_addr, }, - .set_power = iwl4965_set_power, .send_tx_power = iwl4965_send_tx_power, .update_chain_flags = iwl4965_update_chain_flags, .temperature = iwl4965_temperature_calib, diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 878d6193b232..f91c54b5ff53 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1474,6 +1474,7 @@ static struct iwl_lib_ops iwl5000_lib = { .alive_notify = iwl5000_alive_notify, .send_tx_power = iwl5000_send_tx_power, .temperature = iwl5000_temperature, + .update_chain_flags = iwl4965_update_chain_flags, .apm_ops = { .init = iwl5000_apm_init, .reset = iwl5000_apm_reset, diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index e9bb1de0ce3f..6f3555ffe527 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -1993,7 +1993,7 @@ struct iwl4965_spectrum_notification { *****************************************************************************/ /** - * struct iwl4965_powertable_cmd - Power Table Command + * struct iwl_powertable_cmd - Power Table Command * @flags: See below: * * POWER_TABLE_CMD = 0x77 (command, has simple generic response) @@ -2027,7 +2027,7 @@ struct iwl4965_spectrum_notification { #define IWL_POWER_PCI_PM_MSK __constant_cpu_to_le16(1 << 3) #define IWL_POWER_FAST_PD __constant_cpu_to_le16(1 << 4) -struct iwl4965_powertable_cmd { +struct iwl_powertable_cmd { __le16 flags; u8 keep_alive_seconds; u8 debug_flags; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index db66114f1e56..eaefa42f37c5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -139,7 +139,6 @@ struct iwl_lib_ops { int (*set_pwr_src)(struct iwl_priv *priv, enum iwl_pwr_src src); } apm_ops; /* power */ - int (*set_power)(struct iwl_priv *priv, void *cmd); int (*send_tx_power) (struct iwl_priv *priv); void (*update_chain_flags)(struct iwl_priv *priv); void (*temperature) (struct iwl_priv *priv); diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 4d789e353e3a..010ed69e0e50 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -283,7 +283,7 @@ struct iwl_cmd { u32 val32; struct iwl4965_bt_cmd bt; struct iwl4965_rxon_time_cmd rxon_time; - struct iwl4965_powertable_cmd powertable; + struct iwl_powertable_cmd powertable; struct iwl_qosparam_cmd qosparam; struct iwl_tx_cmd tx; struct iwl4965_tx_beacon_cmd tx_beacon; @@ -590,6 +590,7 @@ extern unsigned int iwl4965_fill_beacon_frame(struct iwl_priv *priv, const u8 *dest, int left); extern void iwl4965_update_chain_flags(struct iwl_priv *priv); int iwl4965_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src); +extern int iwl4965_set_power(struct iwl_priv *priv, void *cmd); extern const u8 iwl_bcast_addr[ETH_ALEN]; diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c index 2e71803e09ba..e3c71beb01ef 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.c +++ b/drivers/net/wireless/iwlwifi/iwl-power.c @@ -112,6 +112,13 @@ static struct iwl_power_vec_entry range_2[IWL_POWER_AC] = { {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0} }; +/* set card power command */ +static int iwl_set_power(struct iwl_priv *priv, void *cmd) +{ + return iwl_send_cmd_pdu_async(priv, POWER_TABLE_CMD, + sizeof(struct iwl_powertable_cmd), + cmd, NULL); +} /* decide the right power level according to association status * and battery status */ @@ -162,7 +169,7 @@ static int iwl_power_init_handle(struct iwl_priv *priv) if (ret != 0) return 0; else { - struct iwl4965_powertable_cmd *cmd; + struct iwl_powertable_cmd *cmd; IWL_DEBUG_POWER("adjust power command flags\n"); @@ -180,7 +187,7 @@ static int iwl_power_init_handle(struct iwl_priv *priv) /* adjust power command according to dtim period and power level*/ static int iwl_update_power_command(struct iwl_priv *priv, - struct iwl4965_powertable_cmd *cmd, + struct iwl_powertable_cmd *cmd, u16 mode) { int ret = 0, i; @@ -204,7 +211,7 @@ static int iwl_update_power_command(struct iwl_priv *priv, range = &pow_data->pwr_range_2[0]; period = pow_data->dtim_period; - memcpy(cmd, &range[mode].cmd, sizeof(struct iwl4965_powertable_cmd)); + memcpy(cmd, &range[mode].cmd, sizeof(struct iwl_powertable_cmd)); if (period == 0) { period = 1; @@ -280,7 +287,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, u8 refresh) if (!iwl_is_rfkill(priv) && !setting->power_disabled && ((setting->power_mode != final_mode) || refresh)) { - struct iwl4965_powertable_cmd cmd; + struct iwl_powertable_cmd cmd; if (final_mode != IWL_POWER_MODE_CAM) set_bit(STATUS_POWER_PMI, &priv->status); @@ -291,8 +298,7 @@ int iwl_power_update_mode(struct iwl_priv *priv, u8 refresh) if (final_mode == IWL_POWER_INDEX_5) cmd.flags |= IWL_POWER_FAST_PD; - if (priv->cfg->ops->lib->set_power) - ret = priv->cfg->ops->lib->set_power(priv, &cmd); + ret = iwl_set_power(priv, &cmd); if (final_mode == IWL_POWER_MODE_CAM) clear_bit(STATUS_POWER_PMI, &priv->status); diff --git a/drivers/net/wireless/iwlwifi/iwl-power.h b/drivers/net/wireless/iwlwifi/iwl-power.h index b066724a1c2b..801f6143a42c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.h +++ b/drivers/net/wireless/iwlwifi/iwl-power.h @@ -46,7 +46,7 @@ struct iwl_priv; /* Power management (not Tx power) structures */ struct iwl_power_vec_entry { - struct iwl4965_powertable_cmd cmd; + struct iwl_powertable_cmd cmd; u8 no_dtim; }; -- cgit v1.2.1 From 298df1f62aa69881528bf0f1c3c14395bc447846 Mon Sep 17 00:00:00 2001 From: Esti Kummer Date: Fri, 18 Jul 2008 13:52:58 +0800 Subject: iwlwifi: corrects power_level in sysfs This patch corrects power_level in sysfs. Signed-off-by: Esti Kummer Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-power.c | 27 ++++++++----- drivers/net/wireless/iwlwifi/iwl-power.h | 31 +++++++++----- drivers/net/wireless/iwlwifi/iwl4965-base.c | 63 +++++++++-------------------- 3 files changed, 58 insertions(+), 63 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c index e3c71beb01ef..028e3053c0ca 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.c +++ b/drivers/net/wireless/iwlwifi/iwl-power.c @@ -82,7 +82,7 @@ /* default power management (not Tx power) table values */ /* for tim 0-10 */ -static struct iwl_power_vec_entry range_0[IWL_POWER_AC] = { +static struct iwl_power_vec_entry range_0[IWL_POWER_MAX] = { {{NOSLP, SLP_TOUT(0), SLP_TOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, @@ -93,7 +93,7 @@ static struct iwl_power_vec_entry range_0[IWL_POWER_AC] = { /* for tim = 3-10 */ -static struct iwl_power_vec_entry range_1[IWL_POWER_AC] = { +static struct iwl_power_vec_entry range_1[IWL_POWER_MAX] = { {{NOSLP, SLP_TOUT(0), SLP_TOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0}, @@ -103,7 +103,7 @@ static struct iwl_power_vec_entry range_1[IWL_POWER_AC] = { }; /* for tim > 11 */ -static struct iwl_power_vec_entry range_2[IWL_POWER_AC] = { +static struct iwl_power_vec_entry range_2[IWL_POWER_MAX] = { {{NOSLP, SLP_TOUT(0), SLP_TOUT(0), SLP_VEC(0, 0, 0, 0, 0)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0}, {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0}, @@ -124,7 +124,7 @@ static int iwl_set_power(struct iwl_priv *priv, void *cmd) */ static u16 iwl_get_auto_power_mode(struct iwl_priv *priv) { - u16 mode = priv->power_data.user_power_setting; + u16 mode; switch (priv->power_data.user_power_setting) { case IWL_POWER_AUTO: @@ -136,12 +136,16 @@ static u16 iwl_get_auto_power_mode(struct iwl_priv *priv) else mode = IWL_POWER_ON_AC_DISASSOC; break; + /* FIXME: remove battery and ac from here */ case IWL_POWER_BATTERY: mode = IWL_POWER_INDEX_3; break; case IWL_POWER_AC: mode = IWL_POWER_MODE_CAM; break; + default: + mode = priv->power_data.user_power_setting; + break; } return mode; } @@ -151,7 +155,7 @@ static int iwl_power_init_handle(struct iwl_priv *priv) { int ret = 0, i; struct iwl_power_mgr *pow_data; - int size = sizeof(struct iwl_power_vec_entry) * IWL_POWER_AC; + int size = sizeof(struct iwl_power_vec_entry) * IWL_POWER_MAX; u16 pci_pm; IWL_DEBUG_POWER("Initialize power \n"); @@ -173,7 +177,7 @@ static int iwl_power_init_handle(struct iwl_priv *priv) IWL_DEBUG_POWER("adjust power command flags\n"); - for (i = 0; i < IWL_POWER_AC; i++) { + for (i = 0; i < IWL_POWER_MAX; i++) { cmd = &pow_data->pwr_range_0[i].cmd; if (pci_pm & 0x1) @@ -265,17 +269,18 @@ int iwl_power_update_mode(struct iwl_priv *priv, u8 refresh) * else user level */ switch (setting->system_power_setting) { - case IWL_POWER_AUTO: + case IWL_POWER_SYS_AUTO: final_mode = iwl_get_auto_power_mode(priv); break; - case IWL_POWER_BATTERY: + case IWL_POWER_SYS_BATTERY: final_mode = IWL_POWER_INDEX_3; break; - case IWL_POWER_AC: + case IWL_POWER_SYS_AC: final_mode = IWL_POWER_MODE_CAM; break; default: - final_mode = setting->system_power_setting; + final_mode = IWL_POWER_INDEX_3; + WARN_ON(1); } if (setting->critical_power_setting > final_mode) @@ -394,7 +399,7 @@ void iwl_power_initialize(struct iwl_priv *priv) iwl_power_init_handle(priv); priv->power_data.user_power_setting = IWL_POWER_AUTO; priv->power_data.power_disabled = 0; - priv->power_data.system_power_setting = IWL_POWER_AUTO; + priv->power_data.system_power_setting = IWL_POWER_SYS_AUTO; priv->power_data.is_battery_active = 0; priv->power_data.power_disabled = 0; priv->power_data.critical_power_setting = 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-power.h b/drivers/net/wireless/iwlwifi/iwl-power.h index 801f6143a42c..abcbbf96a84e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.h +++ b/drivers/net/wireless/iwlwifi/iwl-power.h @@ -33,12 +33,25 @@ struct iwl_priv; -#define IWL_POWER_MODE_CAM 0x00 /* Continuously Aware Mode, always on */ -#define IWL_POWER_INDEX_3 0x03 -#define IWL_POWER_INDEX_5 0x05 -#define IWL_POWER_AC 0x06 -#define IWL_POWER_BATTERY 0x07 -#define IWL_POWER_AUTO 0x08 +enum { + IWL_POWER_MODE_CAM, /* Continuously Aware Mode, always on */ + IWL_POWER_INDEX_1, + IWL_POWER_INDEX_2, + IWL_POWER_INDEX_3, + IWL_POWER_INDEX_4, + IWL_POWER_INDEX_5, + IWL_POWER_AUTO, + IWL_POWER_MAX = IWL_POWER_AUTO, + IWL_POWER_AC, + IWL_POWER_BATTERY, +}; + +enum { + IWL_POWER_SYS_AUTO, + IWL_POWER_SYS_AC, + IWL_POWER_SYS_BATTERY, +}; + #define IWL_POWER_LIMIT 0x08 #define IWL_POWER_MASK 0x0F #define IWL_POWER_ENABLED 0x10 @@ -52,9 +65,9 @@ struct iwl_power_vec_entry { struct iwl_power_mgr { spinlock_t lock; - struct iwl_power_vec_entry pwr_range_0[IWL_POWER_AC]; - struct iwl_power_vec_entry pwr_range_1[IWL_POWER_AC]; - struct iwl_power_vec_entry pwr_range_2[IWL_POWER_AC]; + struct iwl_power_vec_entry pwr_range_0[IWL_POWER_MAX]; + struct iwl_power_vec_entry pwr_range_1[IWL_POWER_MAX]; + struct iwl_power_vec_entry pwr_range_2[IWL_POWER_MAX]; u32 dtim_period; /* final power level that used to calculate final power command */ u8 power_mode; diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 71f5da3fe5c4..2001b09738ff 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -3800,76 +3800,53 @@ static ssize_t store_power_level(struct device *d, const char *buf, size_t count) { struct iwl_priv *priv = dev_get_drvdata(d); - int rc; + int ret; int mode; mode = simple_strtoul(buf, NULL, 0); mutex_lock(&priv->mutex); if (!iwl_is_ready(priv)) { - rc = -EAGAIN; + ret = -EAGAIN; goto out; } - rc = iwl_power_set_user_mode(priv, mode); - if (rc) { + ret = iwl_power_set_user_mode(priv, mode); + if (ret) { IWL_DEBUG_MAC80211("failed setting power mode.\n"); goto out; } - rc = count; + ret = count; out: mutex_unlock(&priv->mutex); - return rc; + return ret; } -#define MAX_WX_STRING 80 - -/* Values are in microsecond */ -static const s32 timeout_duration[] = { - 350000, - 250000, - 75000, - 37000, - 25000, -}; -static const s32 period_duration[] = { - 400000, - 700000, - 1000000, - 1000000, - 1000000 -}; - static ssize_t show_power_level(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); + int mode = priv->power_data.user_power_setting; + int system = priv->power_data.system_power_setting; int level = priv->power_data.power_mode; char *p = buf; - p += sprintf(p, "%d ", level); - switch (level) { - case IWL_POWER_MODE_CAM: - case IWL_POWER_AC: - p += sprintf(p, "(AC)"); + switch (system) { + case IWL_POWER_SYS_AUTO: + p += sprintf(p, "SYSTEM:auto"); break; - case IWL_POWER_BATTERY: - p += sprintf(p, "(BATTERY)"); + case IWL_POWER_SYS_AC: + p += sprintf(p, "SYSTEM:ac"); + break; + case IWL_POWER_SYS_BATTERY: + p += sprintf(p, "SYSTEM:battery"); break; - default: - p += sprintf(p, - "(Timeout %dms, Period %dms)", - timeout_duration[level - 1] / 1000, - period_duration[level - 1] / 1000); } -/* - if (!(priv->power_mode & IWL_POWER_ENABLED)) - p += sprintf(p, " OFF\n"); - else - p += sprintf(p, " \n"); -*/ - p += sprintf(p, " \n"); + + p += sprintf(p, "\tMODE:%s", (mode < IWL_POWER_AUTO)?"fixed":"auto"); + p += sprintf(p, "\tINDEX:%d", level); + p += sprintf(p, "\n"); return (p - buf + 1); } -- cgit v1.2.1 From d783b061077f92af55244aef1df8780b0f46b5af Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 18 Jul 2008 13:53:02 +0800 Subject: iwlwifi: move iwl4965_mac_ampdu_action to iwl4965-base.c This patch moves iwl4965_mac_ampdu_action to iwl4965-base.c. Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-4965.c | 33 --------------------------- drivers/net/wireless/iwlwifi/iwl-dev.h | 4 ---- drivers/net/wireless/iwlwifi/iwl4965-base.c | 35 ++++++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 38 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index ea23c7629575..3cc6f00d96cd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2067,39 +2067,6 @@ static int iwl4965_txq_agg_enable(struct iwl_priv *priv, int txq_id, return 0; } -int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw, - enum ieee80211_ampdu_mlme_action action, - const u8 *addr, u16 tid, u16 *ssn) -{ - struct iwl_priv *priv = hw->priv; - DECLARE_MAC_BUF(mac); - - IWL_DEBUG_HT("A-MPDU action on addr %s tid %d\n", - print_mac(mac, addr), tid); - - if (!(priv->cfg->sku & IWL_SKU_N)) - return -EACCES; - - switch (action) { - case IEEE80211_AMPDU_RX_START: - IWL_DEBUG_HT("start Rx\n"); - return iwl_rx_agg_start(priv, addr, tid, *ssn); - case IEEE80211_AMPDU_RX_STOP: - IWL_DEBUG_HT("stop Rx\n"); - return iwl_rx_agg_stop(priv, addr, tid); - case IEEE80211_AMPDU_TX_START: - IWL_DEBUG_HT("start Tx\n"); - return iwl_tx_agg_start(priv, addr, tid, ssn); - case IEEE80211_AMPDU_TX_STOP: - IWL_DEBUG_HT("stop Tx\n"); - return iwl_tx_agg_stop(priv, addr, tid); - default: - IWL_DEBUG_HT("unknown\n"); - return -EINVAL; - break; - } - return 0; -} static u16 iwl4965_get_hcmd_size(u8 cmd_id, u16 len) { diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 010ed69e0e50..d2d4beab82a0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -643,10 +643,6 @@ struct iwl_priv; * Forward declare iwl-4965.c functions for iwl-base.c */ extern void iwl4965_rf_kill_ct_config(struct iwl_priv *priv); - -int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw, - enum ieee80211_ampdu_mlme_action action, - const u8 *addr, u16 tid, u16 *ssn); int iwl4965_check_empty_hw_queue(struct iwl_priv *priv, int sta_id, u8 tid, int txq_id); diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 2001b09738ff..a34280f65c1a 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -65,7 +65,7 @@ * NOTE: DRV_NAME is defined in iwlwifi.h for use by iwl-debug.h and printk */ -#define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link 4965AGN driver for Linux" +#define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link AGN driver for Linux" #ifdef CONFIG_IWLWIFI_DEBUG #define VD "d" @@ -3345,6 +3345,39 @@ static int iwl4965_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, return 0; } +static int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw, + enum ieee80211_ampdu_mlme_action action, + const u8 *addr, u16 tid, u16 *ssn) +{ + struct iwl_priv *priv = hw->priv; + DECLARE_MAC_BUF(mac); + + IWL_DEBUG_HT("A-MPDU action on addr %s tid %d\n", + print_mac(mac, addr), tid); + + if (!(priv->cfg->sku & IWL_SKU_N)) + return -EACCES; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + IWL_DEBUG_HT("start Rx\n"); + return iwl_rx_agg_start(priv, addr, tid, *ssn); + case IEEE80211_AMPDU_RX_STOP: + IWL_DEBUG_HT("stop Rx\n"); + return iwl_rx_agg_stop(priv, addr, tid); + case IEEE80211_AMPDU_TX_START: + IWL_DEBUG_HT("start Tx\n"); + return iwl_tx_agg_start(priv, addr, tid, ssn); + case IEEE80211_AMPDU_TX_STOP: + IWL_DEBUG_HT("stop Tx\n"); + return iwl_tx_agg_stop(priv, addr, tid); + default: + IWL_DEBUG_HT("unknown\n"); + return -EINVAL; + break; + } + return 0; +} static int iwl4965_mac_get_tx_stats(struct ieee80211_hw *hw, struct ieee80211_tx_queue_stats *stats) { -- cgit v1.2.1 From 4bf64efd26f5610cde4fb7846e2f37bd1f62d3a9 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 18 Jul 2008 13:53:03 +0800 Subject: iwlwifi: move beacon handling to iwl4965-base.c This patch concentrates becaon handling in iwl4965-base.c. Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-4965.c | 32 ----------------------- drivers/net/wireless/iwlwifi/iwl-commands.h | 2 +- drivers/net/wireless/iwlwifi/iwl-dev.h | 3 +-- drivers/net/wireless/iwlwifi/iwl4965-base.c | 39 ++++++++++++++++++++++++++--- 4 files changed, 37 insertions(+), 39 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 3cc6f00d96cd..9ae8525b9b60 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -1689,38 +1689,6 @@ static int iwl4965_shared_mem_rx_idx(struct iwl_priv *priv) return le32_to_cpu(s->rb_closed) & 0xFFF; } -unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv, - struct iwl_frame *frame, u8 rate) -{ - struct iwl4965_tx_beacon_cmd *tx_beacon_cmd; - unsigned int frame_size; - - tx_beacon_cmd = &frame->u.beacon; - memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); - - tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id; - tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - - frame_size = iwl4965_fill_beacon_frame(priv, - tx_beacon_cmd->frame, - iwl_bcast_addr, - sizeof(frame->u) - sizeof(*tx_beacon_cmd)); - - BUG_ON(frame_size > MAX_MPDU_SIZE); - tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size); - - if ((rate == IWL_RATE_1M_PLCP) || (rate >= IWL_RATE_2M_PLCP)) - tx_beacon_cmd->tx.rate_n_flags = - iwl_hw_set_rate_n_flags(rate, RATE_MCS_CCK_MSK); - else - tx_beacon_cmd->tx.rate_n_flags = - iwl_hw_set_rate_n_flags(rate, 0); - - tx_beacon_cmd->tx.tx_flags = (TX_CMD_FLG_SEQ_CTL_MSK | - TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK); - return (sizeof(*tx_beacon_cmd) + frame_size); -} - static int iwl4965_alloc_shared_mem(struct iwl_priv *priv) { priv->shared_virt = pci_alloc_consistent(priv->pci_dev, diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 6f3555ffe527..cd6d668f4e00 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -2324,7 +2324,7 @@ struct iwl4965_beacon_notif { /* * REPLY_TX_BEACON = 0x91 (command, has simple generic response) */ -struct iwl4965_tx_beacon_cmd { +struct iwl_tx_beacon_cmd { struct iwl_tx_cmd tx; __le16 tim_idx; u8 tim_size; diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index d2d4beab82a0..ff16cca2b8c7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -219,7 +219,7 @@ enum iwl_pwr_src { struct iwl_frame { union { struct ieee80211_hdr frame; - struct iwl4965_tx_beacon_cmd beacon; + struct iwl_tx_beacon_cmd beacon; u8 raw[IEEE80211_FRAME_LEN]; u8 cmd[360]; } u; @@ -286,7 +286,6 @@ struct iwl_cmd { struct iwl_powertable_cmd powertable; struct iwl_qosparam_cmd qosparam; struct iwl_tx_cmd tx; - struct iwl4965_tx_beacon_cmd tx_beacon; struct iwl4965_rxon_assoc_cmd rxon_assoc; struct iwl_rem_sta_cmd rm_sta; u8 *indirect; diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index a34280f65c1a..94ce026ba602 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -444,11 +444,10 @@ static void iwl_free_frame(struct iwl_priv *priv, struct iwl_frame *frame) list_add(&frame->list, &priv->free_frames); } -unsigned int iwl4965_fill_beacon_frame(struct iwl_priv *priv, - struct ieee80211_hdr *hdr, - const u8 *dest, int left) +static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv, + struct ieee80211_hdr *hdr, + const u8 *dest, int left) { - if (!iwl_is_associated(priv) || !priv->ibss_beacon || ((priv->iw_mode != IEEE80211_IF_TYPE_IBSS) && (priv->iw_mode != IEEE80211_IF_TYPE_AP))) @@ -487,6 +486,38 @@ static u8 iwl4965_rate_get_lowest_plcp(struct iwl_priv *priv) return IWL_RATE_6M_PLCP; } +unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv, + struct iwl_frame *frame, u8 rate) +{ + struct iwl_tx_beacon_cmd *tx_beacon_cmd; + unsigned int frame_size; + + tx_beacon_cmd = &frame->u.beacon; + memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); + + tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id; + tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + + frame_size = iwl_fill_beacon_frame(priv, tx_beacon_cmd->frame, + iwl_bcast_addr, + sizeof(frame->u) - sizeof(*tx_beacon_cmd)); + + BUG_ON(frame_size > MAX_MPDU_SIZE); + tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size); + + if ((rate == IWL_RATE_1M_PLCP) || (rate >= IWL_RATE_2M_PLCP)) + tx_beacon_cmd->tx.rate_n_flags = + iwl_hw_set_rate_n_flags(rate, RATE_MCS_CCK_MSK); + else + tx_beacon_cmd->tx.rate_n_flags = + iwl_hw_set_rate_n_flags(rate, 0); + + tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | + TX_CMD_FLG_TSF_MSK | + TX_CMD_FLG_STA_RATE_MSK; + + return sizeof(*tx_beacon_cmd) + frame_size; +} static int iwl4965_send_beacon_cmd(struct iwl_priv *priv) { struct iwl_frame *frame; -- cgit v1.2.1 From e2e3c57b271d74ed8fd4d378f1517525ef7e5921 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 18 Jul 2008 13:53:04 +0800 Subject: iwlwifi: move iwl4965_set_pwr_src to iwl4965-base.c This patch moves iwl4965_set_pwr_src to iwl4965-base.c. Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-4965.c | 33 ----------------------------- drivers/net/wireless/iwlwifi/iwl4965-base.c | 31 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 33 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 9ae8525b9b60..c0ba28fa6724 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -341,39 +341,6 @@ err: return -EINVAL; } -int iwl4965_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src) -{ - int ret; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - ret = iwl_grab_nic_access(priv); - if (ret) { - spin_unlock_irqrestore(&priv->lock, flags); - return ret; - } - - if (src == IWL_PWR_SRC_VAUX) { - u32 val; - ret = pci_read_config_dword(priv->pci_dev, PCI_POWER_SOURCE, - &val); - - if (val & PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT) { - iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VAUX, - ~APMG_PS_CTRL_MSK_PWR_SRC); - } - } else { - iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, - ~APMG_PS_CTRL_MSK_PWR_SRC); - } - - iwl_release_nic_access(priv); - spin_unlock_irqrestore(&priv->lock, flags); - - return ret; -} /* * Activate/Deactivat Tx DMA/FIFO channels according tx fifos mask diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 94ce026ba602..ac02342d2281 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -1262,6 +1262,37 @@ static void iwl4965_rx_card_state_notif(struct iwl_priv *priv, wake_up_interruptible(&priv->wait_command_queue); } +int iwl4965_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + ret = iwl_grab_nic_access(priv); + if (ret) + goto err; + + if (src == IWL_PWR_SRC_VAUX) { + u32 val; + ret = pci_read_config_dword(priv->pci_dev, PCI_POWER_SOURCE, + &val); + + if (val & PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT) + iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VAUX, + ~APMG_PS_CTRL_MSK_PWR_SRC); + } else { + iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, + ~APMG_PS_CTRL_MSK_PWR_SRC); + } + + iwl_release_nic_access(priv); +err: + spin_unlock_irqrestore(&priv->lock, flags); + return ret; +} + /** * iwl4965_setup_rx_handlers - Initialize Rx handler callbacks * -- cgit v1.2.1 From e227ceac8429ecd775c213838f0415700727b7b4 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 18 Jul 2008 13:53:05 +0800 Subject: iwlwifi: rename iwl-4695-rs to iwl-agn-rs This patch renames iwl-4965-rs to iwl-agn-rs as it provides rate scale capability for all AGN capable iwlwifi drivers. Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/Makefile | 2 +- drivers/net/wireless/iwlwifi/iwl-4965-rs.c | 2713 --------------------------- drivers/net/wireless/iwlwifi/iwl-4965-rs.h | 318 ---- drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 2704 ++++++++++++++++++++++++++ drivers/net/wireless/iwlwifi/iwl-agn-rs.h | 318 ++++ drivers/net/wireless/iwlwifi/iwl-core.c | 2 +- drivers/net/wireless/iwlwifi/iwl-dev.h | 2 +- drivers/net/wireless/iwlwifi/iwl-sta.c | 2 +- drivers/net/wireless/iwlwifi/iwl4965-base.c | 6 +- 9 files changed, 3029 insertions(+), 3038 deletions(-) delete mode 100644 drivers/net/wireless/iwlwifi/iwl-4965-rs.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl-4965-rs.h create mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-rs.c create mode 100644 drivers/net/wireless/iwlwifi/iwl-agn-rs.h (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 1f52b92f08b5..f50d2f8fd5c9 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -11,7 +11,7 @@ iwl3945-objs := iwl3945-base.o iwl-3945.o iwl-3945-rs.o iwl3945-$(CONFIG_IWL3945_LEDS) += iwl-3945-led.o obj-$(CONFIG_IWL4965) += iwl4965.o -iwl4965-objs := iwl4965-base.o iwl-4965.o iwl-4965-rs.o +iwl4965-objs := iwl4965-base.o iwl-4965.o iwl-agn-rs.o ifeq ($(CONFIG_IWL5000),y) iwl4965-objs += iwl-5000.o diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c deleted file mode 100644 index 3ccb84aa5dbc..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c +++ /dev/null @@ -1,2713 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * James P. Ketrenos - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "../net/mac80211/rate.h" - -#include "iwl-dev.h" -#include "iwl-sta.h" -#include "iwl-core.h" -#include "iwl-helpers.h" - -#define RS_NAME "iwl-4965-rs" - -#define NUM_TRY_BEFORE_ANT_TOGGLE 1 -#define IWL_NUMBER_TRY 1 -#define IWL_HT_NUMBER_TRY 3 - -#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ -#define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ -#define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ - -/* max time to accum history 2 seconds */ -#define IWL_RATE_SCALE_FLUSH_INTVL (2*HZ) - -static u8 rs_ht_to_legacy[] = { - IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, - IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, - IWL_RATE_6M_INDEX, - IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX, - IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, - IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, - IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX -}; - -static const u8 ant_toggle_lookup[] = { - /*ANT_NONE -> */ ANT_NONE, - /*ANT_A -> */ ANT_B, - /*ANT_B -> */ ANT_C, - /*ANT_AB -> */ ANT_BC, - /*ANT_C -> */ ANT_A, - /*ANT_AC -> */ ANT_AB, - /*ANT_BC -> */ ANT_AC, - /*ANT_ABC -> */ ANT_ABC, -}; - -/** - * struct iwl4965_rate_scale_data -- tx success history for one rate - */ -struct iwl4965_rate_scale_data { - u64 data; /* bitmap of successful frames */ - s32 success_counter; /* number of frames successful */ - s32 success_ratio; /* per-cent * 128 */ - s32 counter; /* number of frames attempted */ - s32 average_tpt; /* success ratio * expected throughput */ - unsigned long stamp; -}; - -/** - * struct iwl4965_scale_tbl_info -- tx params and success history for all rates - * - * There are two of these in struct iwl4965_lq_sta, - * one for "active", and one for "search". - */ -struct iwl4965_scale_tbl_info { - enum iwl_table_type lq_type; - u8 ant_type; - u8 is_SGI; /* 1 = short guard interval */ - u8 is_fat; /* 1 = 40 MHz channel width */ - u8 is_dup; /* 1 = duplicated data streams */ - u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ - s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ - u32 current_rate; /* rate_n_flags, uCode API format */ - struct iwl4965_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ -}; - -struct iwl4965_traffic_load { - unsigned long time_stamp; /* age of the oldest statistics */ - u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time - * slice */ - u32 total; /* total num of packets during the - * last TID_MAX_TIME_DIFF */ - u8 queue_count; /* number of queues that has - * been used since the last cleanup */ - u8 head; /* start of the circular buffer */ -}; - -/** - * struct iwl4965_lq_sta -- driver's rate scaling private structure - * - * Pointer to this gets passed back and forth between driver and mac80211. - */ -struct iwl4965_lq_sta { - u8 active_tbl; /* index of active table, range 0-1 */ - u8 enable_counter; /* indicates HT mode */ - u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ - u8 search_better_tbl; /* 1: currently trying alternate mode */ - s32 last_tpt; - - /* The following determine when to search for a new mode */ - u32 table_count_limit; - u32 max_failure_limit; /* # failed frames before new search */ - u32 max_success_limit; /* # successful frames before new search */ - u32 table_count; - u32 total_failed; /* total failed frames, any/all rates */ - u32 total_success; /* total successful frames, any/all rates */ - u32 flush_timer; /* time staying in mode before new search */ - - u8 action_counter; /* # mode-switch actions tried */ - u8 is_green; - u8 is_dup; - enum ieee80211_band band; - u8 ibss_sta_added; - - /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ - u32 supp_rates; - u16 active_legacy_rate; - u16 active_siso_rate; - u16 active_mimo2_rate; - u16 active_mimo3_rate; - u16 active_rate_basic; - - struct iwl_link_quality_cmd lq; - struct iwl4965_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ - struct iwl4965_traffic_load load[TID_MAX_LOAD_COUNT]; - u8 tx_agg_tid_en; -#ifdef CONFIG_MAC80211_DEBUGFS - struct dentry *rs_sta_dbgfs_scale_table_file; - struct dentry *rs_sta_dbgfs_stats_table_file; - struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; - u32 dbg_fixed_rate; -#endif - struct iwl_priv *drv; -}; - -static void rs_rate_scale_perform(struct iwl_priv *priv, - struct net_device *dev, - struct ieee80211_hdr *hdr, - struct sta_info *sta); -static void rs_fill_link_cmd(const struct iwl_priv *priv, - struct iwl4965_lq_sta *lq_sta, - u32 rate_n_flags); - - -#ifdef CONFIG_MAC80211_DEBUGFS -static void rs_dbgfs_set_mcs(struct iwl4965_lq_sta *lq_sta, - u32 *rate_n_flags, int index); -#else -static void rs_dbgfs_set_mcs(struct iwl4965_lq_sta *lq_sta, - u32 *rate_n_flags, int index) -{} -#endif - -/* - * Expected throughput metrics for following rates: - * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits - * "G" is the only table that supports CCK (the first 4 rates). - */ -/*FIXME:RS:need to spearate tables for MIMO2/MIMO3*/ -static s32 expected_tpt_A[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186, 186 -}; - -static s32 expected_tpt_G[IWL_RATE_COUNT] = { - 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 186 -}; - -static s32 expected_tpt_siso20MHz[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 42, 42, 76, 102, 124, 159, 183, 193, 202 -}; - -static s32 expected_tpt_siso20MHzSGI[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 46, 46, 82, 110, 132, 168, 192, 202, 211 -}; - -static s32 expected_tpt_mimo20MHz[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 74, 74, 123, 155, 179, 214, 236, 244, 251 -}; - -static s32 expected_tpt_mimo20MHzSGI[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 81, 81, 131, 164, 188, 222, 243, 251, 257 -}; - -static s32 expected_tpt_siso40MHz[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 77, 77, 127, 160, 184, 220, 242, 250, 257 -}; - -static s32 expected_tpt_siso40MHzSGI[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 83, 83, 135, 169, 193, 229, 250, 257, 264 -}; - -static s32 expected_tpt_mimo40MHz[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 123, 123, 182, 214, 235, 264, 279, 285, 289 -}; - -static s32 expected_tpt_mimo40MHzSGI[IWL_RATE_COUNT] = { - 0, 0, 0, 0, 131, 131, 191, 222, 242, 270, 284, 289, 293 -}; - -static inline u8 rs_extract_rate(u32 rate_n_flags) -{ - return (u8)(rate_n_flags & 0xFF); -} - -static void rs_rate_scale_clear_window(struct iwl4965_rate_scale_data *window) -{ - window->data = 0; - window->success_counter = 0; - window->success_ratio = IWL_INVALID_VALUE; - window->counter = 0; - window->average_tpt = IWL_INVALID_VALUE; - window->stamp = 0; -} - -static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) -{ - return ((ant_type & valid_antenna) == ant_type); -} - -/* - * removes the old data from the statistics. All data that is older than - * TID_MAX_TIME_DIFF, will be deleted. - */ -static void rs_tl_rm_old_stats(struct iwl4965_traffic_load *tl, u32 curr_time) -{ - /* The oldest age we want to keep */ - u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; - - while (tl->queue_count && - (tl->time_stamp < oldest_time)) { - tl->total -= tl->packet_count[tl->head]; - tl->packet_count[tl->head] = 0; - tl->time_stamp += TID_QUEUE_CELL_SPACING; - tl->queue_count--; - tl->head++; - if (tl->head >= TID_QUEUE_MAX_SIZE) - tl->head = 0; - } -} - -/* - * increment traffic load value for tid and also remove - * any old values if passed the certain time period - */ -static u8 rs_tl_add_packet(struct iwl4965_lq_sta *lq_data, - struct ieee80211_hdr *hdr) -{ - u32 curr_time = jiffies_to_msecs(jiffies); - u32 time_diff; - s32 index; - struct iwl4965_traffic_load *tl = NULL; - __le16 fc = hdr->frame_control; - u8 tid; - - if (ieee80211_is_data_qos(fc)) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & 0xf; - } else - return MAX_TID_COUNT; - - tl = &lq_data->load[tid]; - - curr_time -= curr_time % TID_ROUND_VALUE; - - /* Happens only for the first packet. Initialize the data */ - if (!(tl->queue_count)) { - tl->total = 1; - tl->time_stamp = curr_time; - tl->queue_count = 1; - tl->head = 0; - tl->packet_count[0] = 1; - return MAX_TID_COUNT; - } - - time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); - index = time_diff / TID_QUEUE_CELL_SPACING; - - /* The history is too long: remove data that is older than */ - /* TID_MAX_TIME_DIFF */ - if (index >= TID_QUEUE_MAX_SIZE) - rs_tl_rm_old_stats(tl, curr_time); - - index = (tl->head + index) % TID_QUEUE_MAX_SIZE; - tl->packet_count[index] = tl->packet_count[index] + 1; - tl->total = tl->total + 1; - - if ((index + 1) > tl->queue_count) - tl->queue_count = index + 1; - - return tid; -} - -/* - get the traffic load value for tid -*/ -static u32 rs_tl_get_load(struct iwl4965_lq_sta *lq_data, u8 tid) -{ - u32 curr_time = jiffies_to_msecs(jiffies); - u32 time_diff; - s32 index; - struct iwl4965_traffic_load *tl = NULL; - - if (tid >= TID_MAX_LOAD_COUNT) - return 0; - - tl = &(lq_data->load[tid]); - - curr_time -= curr_time % TID_ROUND_VALUE; - - if (!(tl->queue_count)) - return 0; - - time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); - index = time_diff / TID_QUEUE_CELL_SPACING; - - /* The history is too long: remove data that is older than */ - /* TID_MAX_TIME_DIFF */ - if (index >= TID_QUEUE_MAX_SIZE) - rs_tl_rm_old_stats(tl, curr_time); - - return tl->total; -} - -static void rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, - struct iwl4965_lq_sta *lq_data, u8 tid, - struct sta_info *sta) -{ - unsigned long state; - DECLARE_MAC_BUF(mac); - - spin_lock_bh(&sta->lock); - state = sta->ampdu_mlme.tid_state_tx[tid]; - spin_unlock_bh(&sta->lock); - - if (state == HT_AGG_STATE_IDLE && - rs_tl_get_load(lq_data, tid) > IWL_AGG_LOAD_THRESHOLD) { - IWL_DEBUG_HT("Starting Tx agg: STA: %s tid: %d\n", - print_mac(mac, sta->addr), tid); - ieee80211_start_tx_ba_session(priv->hw, sta->addr, tid); - } -} - -static void rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid, - struct iwl4965_lq_sta *lq_data, - struct sta_info *sta) -{ - if ((tid < TID_MAX_LOAD_COUNT)) - rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); - else if (tid == IWL_AGG_ALL_TID) - for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++) - rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); -} - -static inline int get_num_of_ant_from_rate(u32 rate_n_flags) -{ - return (!!(rate_n_flags & RATE_MCS_ANT_A_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_C_MSK)); -} - -/** - * rs_collect_tx_data - Update the success/failure sliding window - * - * We keep a sliding window of the last 62 packets transmitted - * at this rate. window->data contains the bitmask of successful - * packets. - */ -static int rs_collect_tx_data(struct iwl4965_rate_scale_data *windows, - int scale_index, s32 tpt, int retries, - int successes) -{ - struct iwl4965_rate_scale_data *window = NULL; - static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); - s32 fail_count; - - if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) - return -EINVAL; - - /* Select data for current tx bit rate */ - window = &(windows[scale_index]); - - /* - * Keep track of only the latest 62 tx frame attempts in this rate's - * history window; anything older isn't really relevant any more. - * If we have filled up the sliding window, drop the oldest attempt; - * if the oldest attempt (highest bit in bitmap) shows "success", - * subtract "1" from the success counter (this is the main reason - * we keep these bitmaps!). - */ - while (retries > 0) { - if (window->counter >= IWL_RATE_MAX_WINDOW) { - - /* remove earliest */ - window->counter = IWL_RATE_MAX_WINDOW - 1; - - if (window->data & mask) { - window->data &= ~mask; - window->success_counter--; - } - } - - /* Increment frames-attempted counter */ - window->counter++; - - /* Shift bitmap by one frame (throw away oldest history), - * OR in "1", and increment "success" if this - * frame was successful. */ - window->data <<= 1;; - if (successes > 0) { - window->success_counter++; - window->data |= 0x1; - successes--; - } - - retries--; - } - - /* Calculate current success ratio, avoid divide-by-0! */ - if (window->counter > 0) - window->success_ratio = 128 * (100 * window->success_counter) - / window->counter; - else - window->success_ratio = IWL_INVALID_VALUE; - - fail_count = window->counter - window->success_counter; - - /* Calculate average throughput, if we have enough history. */ - if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) || - (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH)) - window->average_tpt = (window->success_ratio * tpt + 64) / 128; - else - window->average_tpt = IWL_INVALID_VALUE; - - /* Tag this window as having been updated */ - window->stamp = jiffies; - - return 0; -} - -/* - * Fill uCode API rate_n_flags field, based on "search" or "active" table. - */ -/* FIXME:RS:remove this function and put the flags statically in the table */ -static u32 rate_n_flags_from_tbl(struct iwl4965_scale_tbl_info *tbl, - int index, u8 use_green) -{ - u32 rate_n_flags = 0; - - if (is_legacy(tbl->lq_type)) { - rate_n_flags = iwl_rates[index].plcp; - if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) - rate_n_flags |= RATE_MCS_CCK_MSK; - - } else if (is_Ht(tbl->lq_type)) { - if (index > IWL_LAST_OFDM_RATE) { - IWL_ERROR("invalid HT rate index %d\n", index); - index = IWL_LAST_OFDM_RATE; - } - rate_n_flags = RATE_MCS_HT_MSK; - - if (is_siso(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_siso; - else if (is_mimo2(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_mimo2; - else - rate_n_flags |= iwl_rates[index].plcp_mimo3; - } else { - IWL_ERROR("Invalid tbl->lq_type %d\n", tbl->lq_type); - } - - rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & - RATE_MCS_ANT_ABC_MSK); - - if (is_Ht(tbl->lq_type)) { - if (tbl->is_fat) { - if (tbl->is_dup) - rate_n_flags |= RATE_MCS_DUP_MSK; - else - rate_n_flags |= RATE_MCS_FAT_MSK; - } - if (tbl->is_SGI) - rate_n_flags |= RATE_MCS_SGI_MSK; - - if (use_green) { - rate_n_flags |= RATE_MCS_GF_MSK; - if (is_siso(tbl->lq_type) && tbl->is_SGI) { - rate_n_flags &= ~RATE_MCS_SGI_MSK; - IWL_ERROR("GF was set with SGI:SISO\n"); - } - } - } - return rate_n_flags; -} - -/* - * Interpret uCode API's rate_n_flags format, - * fill "search" or "active" tx mode table. - */ -static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, - enum ieee80211_band band, - struct iwl4965_scale_tbl_info *tbl, - int *rate_idx) -{ - u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); - u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags); - u8 mcs; - - *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); - - if (*rate_idx == IWL_RATE_INVALID) { - *rate_idx = -1; - return -EINVAL; - } - tbl->is_SGI = 0; /* default legacy setup */ - tbl->is_fat = 0; - tbl->is_dup = 0; - tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); - tbl->lq_type = LQ_NONE; - - /* legacy rate format */ - if (!(rate_n_flags & RATE_MCS_HT_MSK)) { - if (num_of_ant == 1) { - if (band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_A; - else - tbl->lq_type = LQ_G; - } - /* HT rate format */ - } else { - if (rate_n_flags & RATE_MCS_SGI_MSK) - tbl->is_SGI = 1; - - if ((rate_n_flags & RATE_MCS_FAT_MSK) || - (rate_n_flags & RATE_MCS_DUP_MSK)) - tbl->is_fat = 1; - - if (rate_n_flags & RATE_MCS_DUP_MSK) - tbl->is_dup = 1; - - mcs = rs_extract_rate(rate_n_flags); - - /* SISO */ - if (mcs <= IWL_RATE_SISO_60M_PLCP) { - if (num_of_ant == 1) - tbl->lq_type = LQ_SISO; /*else NONE*/ - /* MIMO2 */ - } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) { - if (num_of_ant == 2) - tbl->lq_type = LQ_MIMO2; - /* MIMO3 */ - } else { - if (num_of_ant == 3) - tbl->lq_type = LQ_MIMO3; - } - } - return 0; -} - -/* switch to another antenna/antennas and return 1 */ -/* if no other valid antenna found, return 0 */ -static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, - struct iwl4965_scale_tbl_info *tbl) -{ - u8 new_ant_type; - - if (!tbl->ant_type || tbl->ant_type > ANT_ABC) - return 0; - - if (!rs_is_valid_ant(valid_ant, tbl->ant_type)) - return 0; - - new_ant_type = ant_toggle_lookup[tbl->ant_type]; - - while ((new_ant_type != tbl->ant_type) && - !rs_is_valid_ant(valid_ant, new_ant_type)) - new_ant_type = ant_toggle_lookup[new_ant_type]; - - if (new_ant_type == tbl->ant_type) - return 0; - - tbl->ant_type = new_ant_type; - *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; - *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; - return 1; -} - -/* FIXME:RS: in 4965 we don't use greenfield at all */ -/* FIXME:RS: don't use greenfield for now in TX */ -#if 0 -static inline u8 rs_use_green(struct iwl_priv *priv, struct ieee80211_conf *conf) -{ - return ((conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) && - priv->current_ht_config.is_green_field && - !priv->current_ht_config.non_GF_STA_present); -} -#endif -static inline u8 rs_use_green(struct iwl_priv *priv, struct ieee80211_conf *conf) -{ - return 0; -} - -/** - * rs_get_supported_rates - get the available rates - * - * if management frame or broadcast frame only return - * basic available rates. - * - */ -static u16 rs_get_supported_rates(struct iwl4965_lq_sta *lq_sta, - struct ieee80211_hdr *hdr, - enum iwl_table_type rate_type) -{ - if (hdr && is_multicast_ether_addr(hdr->addr1) && - lq_sta->active_rate_basic) - return lq_sta->active_rate_basic; - - if (is_legacy(rate_type)) { - return lq_sta->active_legacy_rate; - } else { - if (is_siso(rate_type)) - return lq_sta->active_siso_rate; - else if (is_mimo2(rate_type)) - return lq_sta->active_mimo2_rate; - else - return lq_sta->active_mimo3_rate; - } -} - -static u16 rs_get_adjacent_rate(struct iwl_priv *priv, u8 index, u16 rate_mask, - int rate_type) -{ - u8 high = IWL_RATE_INVALID; - u8 low = IWL_RATE_INVALID; - - /* 802.11A or ht walks to the next literal adjacent rate in - * the rate table */ - if (is_a_band(rate_type) || !is_legacy(rate_type)) { - int i; - u32 mask; - - /* Find the previous rate that is in the rate mask */ - i = index - 1; - for (mask = (1 << i); i >= 0; i--, mask >>= 1) { - if (rate_mask & mask) { - low = i; - break; - } - } - - /* Find the next rate that is in the rate mask */ - i = index + 1; - for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { - if (rate_mask & mask) { - high = i; - break; - } - } - - return (high << 8) | low; - } - - low = index; - while (low != IWL_RATE_INVALID) { - low = iwl_rates[low].prev_rs; - if (low == IWL_RATE_INVALID) - break; - if (rate_mask & (1 << low)) - break; - IWL_DEBUG_RATE("Skipping masked lower rate: %d\n", low); - } - - high = index; - while (high != IWL_RATE_INVALID) { - high = iwl_rates[high].next_rs; - if (high == IWL_RATE_INVALID) - break; - if (rate_mask & (1 << high)) - break; - IWL_DEBUG_RATE("Skipping masked higher rate: %d\n", high); - } - - return (high << 8) | low; -} - -static u32 rs_get_lower_rate(struct iwl4965_lq_sta *lq_sta, - struct iwl4965_scale_tbl_info *tbl, u8 scale_index, - u8 ht_possible) -{ - s32 low; - u16 rate_mask; - u16 high_low; - u8 switch_to_legacy = 0; - u8 is_green = lq_sta->is_green; - - /* check if we need to switch from HT to legacy rates. - * assumption is that mandatory rates (1Mbps or 6Mbps) - * are always supported (spec demand) */ - if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) { - switch_to_legacy = 1; - scale_index = rs_ht_to_legacy[scale_index]; - if (lq_sta->band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_A; - else - tbl->lq_type = LQ_G; - - if (num_of_ant(tbl->ant_type) > 1) - tbl->ant_type = ANT_A;/*FIXME:RS*/ - - tbl->is_fat = 0; - tbl->is_SGI = 0; - } - - rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); - - /* Mask with station rate restriction */ - if (is_legacy(tbl->lq_type)) { - /* supp_rates has no CCK bits in A mode */ - if (lq_sta->band == IEEE80211_BAND_5GHZ) - rate_mask = (u16)(rate_mask & - (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); - else - rate_mask = (u16)(rate_mask & lq_sta->supp_rates); - } - - /* If we switched from HT to legacy, check current rate */ - if (switch_to_legacy && (rate_mask & (1 << scale_index))) { - low = scale_index; - goto out; - } - - high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask, - tbl->lq_type); - low = high_low & 0xff; - - if (low == IWL_RATE_INVALID) - low = scale_index; - -out: - return rate_n_flags_from_tbl(tbl, low, is_green); -} - -/* - * mac80211 sends us Tx status - */ -static void rs_tx_status(void *priv_rate, struct net_device *dev, - struct sk_buff *skb) -{ - int status; - u8 retries; - int rs_index, index = 0; - struct iwl4965_lq_sta *lq_sta; - struct iwl_link_quality_cmd *table; - struct sta_info *sta; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct iwl_priv *priv = (struct iwl_priv *)priv_rate; - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_hw *hw = local_to_hw(local); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl4965_rate_scale_data *window = NULL; - struct iwl4965_rate_scale_data *search_win = NULL; - u32 tx_rate; - struct iwl4965_scale_tbl_info tbl_type; - struct iwl4965_scale_tbl_info *curr_tbl, *search_tbl; - u8 active_index = 0; - __le16 fc = hdr->frame_control; - s32 tpt = 0; - - IWL_DEBUG_RATE_LIMIT("get frame ack response, update rate scale window\n"); - - if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) - return; - - /* This packet was aggregated but doesn't carry rate scale info */ - if ((info->flags & IEEE80211_TX_CTL_AMPDU) && - !(info->flags & IEEE80211_TX_STAT_AMPDU)) - return; - - retries = info->status.retry_count; - - if (retries > 15) - retries = 15; - - rcu_read_lock(); - - sta = sta_info_get(local, hdr->addr1); - - if (!sta || !sta->rate_ctrl_priv) - goto out; - - - lq_sta = (struct iwl4965_lq_sta *)sta->rate_ctrl_priv; - - if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && - !lq_sta->ibss_sta_added) - goto out; - - table = &lq_sta->lq; - active_index = lq_sta->active_tbl; - - curr_tbl = &(lq_sta->lq_info[active_index]); - search_tbl = &(lq_sta->lq_info[(1 - active_index)]); - window = (struct iwl4965_rate_scale_data *) - &(curr_tbl->win[0]); - search_win = (struct iwl4965_rate_scale_data *) - &(search_tbl->win[0]); - - /* - * Ignore this Tx frame response if its initial rate doesn't match - * that of latest Link Quality command. There may be stragglers - * from a previous Link Quality command, but we're no longer interested - * in those; they're either from the "active" mode while we're trying - * to check "search" mode, or a prior "search" mode after we've moved - * to a new "search" mode (which might become the new "active" mode). - */ - tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); - rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); - if (priv->band == IEEE80211_BAND_5GHZ) - rs_index -= IWL_FIRST_OFDM_RATE; - - if ((info->tx_rate_idx < 0) || - (tbl_type.is_SGI ^ - !!(info->flags & IEEE80211_TX_CTL_SHORT_GI)) || - (tbl_type.is_fat ^ - !!(info->flags & IEEE80211_TX_CTL_40_MHZ_WIDTH)) || - (tbl_type.is_dup ^ - !!(info->flags & IEEE80211_TX_CTL_DUP_DATA)) || - (tbl_type.ant_type ^ info->antenna_sel_tx) || - (!!(tx_rate & RATE_MCS_HT_MSK) ^ - !!(info->flags & IEEE80211_TX_CTL_OFDM_HT)) || - (!!(tx_rate & RATE_MCS_GF_MSK) ^ - !!(info->flags & IEEE80211_TX_CTL_GREEN_FIELD)) || - (hw->wiphy->bands[priv->band]->bitrates[rs_index].bitrate != - hw->wiphy->bands[info->band]->bitrates[info->tx_rate_idx].bitrate)) { - IWL_DEBUG_RATE("initial rate does not match 0x%x\n", tx_rate); - goto out; - } - - /* Update frame history window with "failure" for each Tx retry. */ - while (retries) { - /* Look up the rate and other info used for each tx attempt. - * Each tx attempt steps one entry deeper in the rate table. */ - tx_rate = le32_to_cpu(table->rs_table[index].rate_n_flags); - rs_get_tbl_info_from_mcs(tx_rate, priv->band, - &tbl_type, &rs_index); - - /* If type matches "search" table, - * add failure to "search" history */ - if ((tbl_type.lq_type == search_tbl->lq_type) && - (tbl_type.ant_type == search_tbl->ant_type) && - (tbl_type.is_SGI == search_tbl->is_SGI)) { - if (search_tbl->expected_tpt) - tpt = search_tbl->expected_tpt[rs_index]; - else - tpt = 0; - rs_collect_tx_data(search_win, rs_index, tpt, 1, 0); - - /* Else if type matches "current/active" table, - * add failure to "current/active" history */ - } else if ((tbl_type.lq_type == curr_tbl->lq_type) && - (tbl_type.ant_type == curr_tbl->ant_type) && - (tbl_type.is_SGI == curr_tbl->is_SGI)) { - if (curr_tbl->expected_tpt) - tpt = curr_tbl->expected_tpt[rs_index]; - else - tpt = 0; - rs_collect_tx_data(window, rs_index, tpt, 1, 0); - } - - /* If not searching for a new mode, increment failed counter - * ... this helps determine when to start searching again */ - if (lq_sta->stay_in_tbl) - lq_sta->total_failed++; - --retries; - index++; - - } - - /* - * Find (by rate) the history window to update with final Tx attempt; - * if Tx was successful first try, use original rate, - * else look up the rate that was, finally, successful. - */ - tx_rate = le32_to_cpu(table->rs_table[index].rate_n_flags); - rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); - - /* Update frame history window with "success" if Tx got ACKed ... */ - status = !!(info->flags & IEEE80211_TX_STAT_ACK); - - /* If type matches "search" table, - * add final tx status to "search" history */ - if ((tbl_type.lq_type == search_tbl->lq_type) && - (tbl_type.ant_type == search_tbl->ant_type) && - (tbl_type.is_SGI == search_tbl->is_SGI)) { - if (search_tbl->expected_tpt) - tpt = search_tbl->expected_tpt[rs_index]; - else - tpt = 0; - if (info->flags & IEEE80211_TX_CTL_AMPDU) - rs_collect_tx_data(search_win, rs_index, tpt, - info->status.ampdu_ack_len, - info->status.ampdu_ack_map); - else - rs_collect_tx_data(search_win, rs_index, tpt, - 1, status); - /* Else if type matches "current/active" table, - * add final tx status to "current/active" history */ - } else if ((tbl_type.lq_type == curr_tbl->lq_type) && - (tbl_type.ant_type == curr_tbl->ant_type) && - (tbl_type.is_SGI == curr_tbl->is_SGI)) { - if (curr_tbl->expected_tpt) - tpt = curr_tbl->expected_tpt[rs_index]; - else - tpt = 0; - if (info->flags & IEEE80211_TX_CTL_AMPDU) - rs_collect_tx_data(window, rs_index, tpt, - info->status.ampdu_ack_len, - info->status.ampdu_ack_map); - else - rs_collect_tx_data(window, rs_index, tpt, - 1, status); - } - - /* If not searching for new mode, increment success/failed counter - * ... these help determine when to start searching again */ - if (lq_sta->stay_in_tbl) { - if (info->flags & IEEE80211_TX_CTL_AMPDU) { - lq_sta->total_success += info->status.ampdu_ack_map; - lq_sta->total_failed += - (info->status.ampdu_ack_len - info->status.ampdu_ack_map); - } else { - if (status) - lq_sta->total_success++; - else - lq_sta->total_failed++; - } - } - - /* See if there's a better rate or modulation mode to try. */ - rs_rate_scale_perform(priv, dev, hdr, sta); -out: - rcu_read_unlock(); - return; -} - -/* - * Begin a period of staying with a selected modulation mode. - * Set "stay_in_tbl" flag to prevent any mode switches. - * Set frame tx success limits according to legacy vs. high-throughput, - * and reset overall (spanning all rates) tx success history statistics. - * These control how long we stay using same modulation mode before - * searching for a new mode. - */ -static void rs_set_stay_in_table(struct iwl_priv *priv, u8 is_legacy, - struct iwl4965_lq_sta *lq_sta) -{ - IWL_DEBUG_RATE("we are staying in the same table\n"); - lq_sta->stay_in_tbl = 1; /* only place this gets set */ - if (is_legacy) { - lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT; - } else { - lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT; - } - lq_sta->table_count = 0; - lq_sta->total_failed = 0; - lq_sta->total_success = 0; -} - -/* - * Find correct throughput table for given mode of modulation - */ -static void rs_set_expected_tpt_table(struct iwl4965_lq_sta *lq_sta, - struct iwl4965_scale_tbl_info *tbl) -{ - if (is_legacy(tbl->lq_type)) { - if (!is_a_band(tbl->lq_type)) - tbl->expected_tpt = expected_tpt_G; - else - tbl->expected_tpt = expected_tpt_A; - } else if (is_siso(tbl->lq_type)) { - if (tbl->is_fat && !lq_sta->is_dup) - if (tbl->is_SGI) - tbl->expected_tpt = expected_tpt_siso40MHzSGI; - else - tbl->expected_tpt = expected_tpt_siso40MHz; - else if (tbl->is_SGI) - tbl->expected_tpt = expected_tpt_siso20MHzSGI; - else - tbl->expected_tpt = expected_tpt_siso20MHz; - - } else if (is_mimo(tbl->lq_type)) { /* FIXME:need to separate mimo2/3 */ - if (tbl->is_fat && !lq_sta->is_dup) - if (tbl->is_SGI) - tbl->expected_tpt = expected_tpt_mimo40MHzSGI; - else - tbl->expected_tpt = expected_tpt_mimo40MHz; - else if (tbl->is_SGI) - tbl->expected_tpt = expected_tpt_mimo20MHzSGI; - else - tbl->expected_tpt = expected_tpt_mimo20MHz; - } else - tbl->expected_tpt = expected_tpt_G; -} - -/* - * Find starting rate for new "search" high-throughput mode of modulation. - * Goal is to find lowest expected rate (under perfect conditions) that is - * above the current measured throughput of "active" mode, to give new mode - * a fair chance to prove itself without too many challenges. - * - * This gets called when transitioning to more aggressive modulation - * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive - * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need - * to decrease to match "active" throughput. When moving from MIMO to SISO, - * bit rate will typically need to increase, but not if performance was bad. - */ -static s32 rs_get_best_rate(struct iwl_priv *priv, - struct iwl4965_lq_sta *lq_sta, - struct iwl4965_scale_tbl_info *tbl, /* "search" */ - u16 rate_mask, s8 index) -{ - /* "active" values */ - struct iwl4965_scale_tbl_info *active_tbl = - &(lq_sta->lq_info[lq_sta->active_tbl]); - s32 active_sr = active_tbl->win[index].success_ratio; - s32 active_tpt = active_tbl->expected_tpt[index]; - - /* expected "search" throughput */ - s32 *tpt_tbl = tbl->expected_tpt; - - s32 new_rate, high, low, start_hi; - u16 high_low; - s8 rate = index; - - new_rate = high = low = start_hi = IWL_RATE_INVALID; - - for (; ;) { - high_low = rs_get_adjacent_rate(priv, rate, rate_mask, - tbl->lq_type); - - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; - - /* - * Lower the "search" bit rate, to give new "search" mode - * approximately the same throughput as "active" if: - * - * 1) "Active" mode has been working modestly well (but not - * great), and expected "search" throughput (under perfect - * conditions) at candidate rate is above the actual - * measured "active" throughput (but less than expected - * "active" throughput under perfect conditions). - * OR - * 2) "Active" mode has been working perfectly or very well - * and expected "search" throughput (under perfect - * conditions) at candidate rate is above expected - * "active" throughput (under perfect conditions). - */ - if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) && - ((active_sr > IWL_RATE_DECREASE_TH) && - (active_sr <= IWL_RATE_HIGH_TH) && - (tpt_tbl[rate] <= active_tpt))) || - ((active_sr >= IWL_RATE_SCALE_SWITCH) && - (tpt_tbl[rate] > active_tpt))) { - - /* (2nd or later pass) - * If we've already tried to raise the rate, and are - * now trying to lower it, use the higher rate. */ - if (start_hi != IWL_RATE_INVALID) { - new_rate = start_hi; - break; - } - - new_rate = rate; - - /* Loop again with lower rate */ - if (low != IWL_RATE_INVALID) - rate = low; - - /* Lower rate not available, use the original */ - else - break; - - /* Else try to raise the "search" rate to match "active" */ - } else { - /* (2nd or later pass) - * If we've already tried to lower the rate, and are - * now trying to raise it, use the lower rate. */ - if (new_rate != IWL_RATE_INVALID) - break; - - /* Loop again with higher rate */ - else if (high != IWL_RATE_INVALID) { - start_hi = high; - rate = high; - - /* Higher rate not available, use the original */ - } else { - break; - } - } - } - - return new_rate; -} - -/* - * Set up search table for MIMO - */ -static int rs_switch_to_mimo2(struct iwl_priv *priv, - struct iwl4965_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct sta_info *sta, - struct iwl4965_scale_tbl_info *tbl, int index) -{ - u16 rate_mask; - s32 rate; - s8 is_green = lq_sta->is_green; - - if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) || - !sta->ht_info.ht_supported) - return -1; - - if (priv->current_ht_config.tx_mimo_ps_mode == IWL_MIMO_PS_STATIC) - return -1; - - /* Need both Tx chains/antennas to support MIMO */ - if (priv->hw_params.tx_chains_num < 2) - return -1; - - IWL_DEBUG_RATE("LQ: try to switch to MIMO2\n"); - - tbl->lq_type = LQ_MIMO2; - tbl->is_dup = lq_sta->is_dup; - tbl->action = 0; - rate_mask = lq_sta->active_mimo2_rate; - - if (priv->current_ht_config.supported_chan_width - == IWL_CHANNEL_WIDTH_40MHZ) - tbl->is_fat = 1; - else - tbl->is_fat = 0; - - /* FIXME: - don't toggle SGI here - if (tbl->is_fat) { - if (priv->current_ht_config.sgf & HT_SHORT_GI_40MHZ_ONLY) - tbl->is_SGI = 1; - else - tbl->is_SGI = 0; - } else if (priv->current_ht_config.sgf & HT_SHORT_GI_20MHZ_ONLY) - tbl->is_SGI = 1; - else - tbl->is_SGI = 0; - */ - - rs_set_expected_tpt_table(lq_sta, tbl); - - rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); - - IWL_DEBUG_RATE("LQ: MIMO2 best rate %d mask %X\n", rate, rate_mask); - - if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { - IWL_DEBUG_RATE("Can't switch with index %d rate mask %x\n", - rate, rate_mask); - return -1; - } - tbl->current_rate = rate_n_flags_from_tbl(tbl, rate, is_green); - - IWL_DEBUG_RATE("LQ: Switch to new mcs %X index is green %X\n", - tbl->current_rate, is_green); - return 0; -} - -/* - * Set up search table for SISO - */ -static int rs_switch_to_siso(struct iwl_priv *priv, - struct iwl4965_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct sta_info *sta, - struct iwl4965_scale_tbl_info *tbl, int index) -{ - u16 rate_mask; - u8 is_green = lq_sta->is_green; - s32 rate; - - if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) || - !sta->ht_info.ht_supported) - return -1; - - IWL_DEBUG_RATE("LQ: try to switch to SISO\n"); - - tbl->is_dup = lq_sta->is_dup; - tbl->lq_type = LQ_SISO; - tbl->action = 0; - rate_mask = lq_sta->active_siso_rate; - - if (priv->current_ht_config.supported_chan_width - == IWL_CHANNEL_WIDTH_40MHZ) - tbl->is_fat = 1; - else - tbl->is_fat = 0; - - /* FIXME: - don't toggle SGI here - if (tbl->is_fat) { - if (priv->current_ht_config.sgf & HT_SHORT_GI_40MHZ_ONLY) - tbl->is_SGI = 1; - else - tbl->is_SGI = 0; - } else if (priv->current_ht_config.sgf & HT_SHORT_GI_20MHZ_ONLY) - tbl->is_SGI = 1; - else - tbl->is_SGI = 0; - */ - - if (is_green) - tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/ - - rs_set_expected_tpt_table(lq_sta, tbl); - rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); - - IWL_DEBUG_RATE("LQ: get best rate %d mask %X\n", rate, rate_mask); - if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { - IWL_DEBUG_RATE("can not switch with index %d rate mask %x\n", - rate, rate_mask); - return -1; - } - tbl->current_rate = rate_n_flags_from_tbl(tbl, rate, is_green); - IWL_DEBUG_RATE("LQ: Switch to new mcs %X index is green %X\n", - tbl->current_rate, is_green); - return 0; -} - -/* - * Try to switch to new modulation mode from legacy - */ -static int rs_move_legacy_other(struct iwl_priv *priv, - struct iwl4965_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct sta_info *sta, - int index) -{ - struct iwl4965_scale_tbl_info *tbl = - &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl4965_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl4965_rate_scale_data *window = &(tbl->win[index]); - u32 sz = (sizeof(struct iwl4965_scale_tbl_info) - - (sizeof(struct iwl4965_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action = tbl->action; - u8 valid_tx_ant = priv->hw_params.valid_tx_ant; - int ret = 0; - - for (; ;) { - switch (tbl->action) { - case IWL_LEGACY_SWITCH_ANTENNA: - IWL_DEBUG_RATE("LQ: Legacy toggle Antenna\n"); - - lq_sta->action_counter++; - - /* Don't change antenna if success has been great */ - if (window->success_ratio >= IWL_RS_GOOD_RATIO) - break; - - /* Set up search table to try other antenna */ - memcpy(search_tbl, tbl, sz); - - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, search_tbl)) { - lq_sta->search_better_tbl = 1; - goto out; - } - break; - case IWL_LEGACY_SWITCH_SISO: - IWL_DEBUG_RATE("LQ: Legacy switch to SISO\n"); - - /* Set up search table to try SISO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - ret = rs_switch_to_siso(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) { - lq_sta->search_better_tbl = 1; - lq_sta->action_counter = 0; - goto out; - } - - break; - case IWL_LEGACY_SWITCH_MIMO2: - IWL_DEBUG_RATE("LQ: Legacy switch to MIMO2\n"); - - /* Set up search table to try MIMO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - search_tbl->ant_type = ANT_AB;/*FIXME:RS*/ - /*FIXME:RS:need to check ant validity*/ - ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) { - lq_sta->search_better_tbl = 1; - lq_sta->action_counter = 0; - goto out; - } - break; - } - tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO2) - tbl->action = IWL_LEGACY_SWITCH_ANTENNA; - - if (tbl->action == start_action) - break; - - } - return 0; - - out: - tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO2) - tbl->action = IWL_LEGACY_SWITCH_ANTENNA; - return 0; - -} - -/* - * Try to switch to new modulation mode from SISO - */ -static int rs_move_siso_to_other(struct iwl_priv *priv, - struct iwl4965_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct sta_info *sta, - int index) -{ - u8 is_green = lq_sta->is_green; - struct iwl4965_scale_tbl_info *tbl = - &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl4965_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl4965_rate_scale_data *window = &(tbl->win[index]); - u32 sz = (sizeof(struct iwl4965_scale_tbl_info) - - (sizeof(struct iwl4965_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action = tbl->action; - u8 valid_tx_ant = priv->hw_params.valid_tx_ant; - int ret; - - for (;;) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_SISO_SWITCH_ANTENNA: - IWL_DEBUG_RATE("LQ: SISO toggle Antenna\n"); - if (window->success_ratio >= IWL_RS_GOOD_RATIO) - break; - - memcpy(search_tbl, tbl, sz); - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, search_tbl)) { - lq_sta->search_better_tbl = 1; - goto out; - } - break; - case IWL_SISO_SWITCH_MIMO2: - IWL_DEBUG_RATE("LQ: SISO switch to MIMO2\n"); - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - search_tbl->ant_type = ANT_AB; /*FIXME:RS*/ - ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) { - lq_sta->search_better_tbl = 1; - goto out; - } - break; - case IWL_SISO_SWITCH_GI: - if (!tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_20MHZ)) - break; - if (tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_40MHZ)) - break; - - IWL_DEBUG_RATE("LQ: SISO toggle SGI/NGI\n"); - - memcpy(search_tbl, tbl, sz); - if (is_green) { - if (!tbl->is_SGI) - break; - else - IWL_ERROR("SGI was set in GF+SISO\n"); - } - search_tbl->is_SGI = !tbl->is_SGI; - rs_set_expected_tpt_table(lq_sta, search_tbl); - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[index]) - break; - } - search_tbl->current_rate = rate_n_flags_from_tbl( - search_tbl, index, is_green); - lq_sta->search_better_tbl = 1; - goto out; - } - tbl->action++; - if (tbl->action > IWL_SISO_SWITCH_GI) - tbl->action = IWL_SISO_SWITCH_ANTENNA; - - if (tbl->action == start_action) - break; - } - return 0; - - out: - tbl->action++; - if (tbl->action > IWL_SISO_SWITCH_GI) - tbl->action = IWL_SISO_SWITCH_ANTENNA; - return 0; -} - -/* - * Try to switch to new modulation mode from MIMO - */ -static int rs_move_mimo_to_other(struct iwl_priv *priv, - struct iwl4965_lq_sta *lq_sta, - struct ieee80211_conf *conf, - struct sta_info *sta, - int index) -{ - s8 is_green = lq_sta->is_green; - struct iwl4965_scale_tbl_info *tbl = - &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl4965_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - u32 sz = (sizeof(struct iwl4965_scale_tbl_info) - - (sizeof(struct iwl4965_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action = tbl->action; - /*u8 valid_tx_ant = priv->hw_params.valid_tx_ant;*/ - int ret; - - for (;;) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_MIMO_SWITCH_ANTENNA_A: - case IWL_MIMO_SWITCH_ANTENNA_B: - IWL_DEBUG_RATE("LQ: MIMO2 switch to SISO\n"); - - /* Set up new search table for SISO */ - memcpy(search_tbl, tbl, sz); - - /*FIXME:RS:need to check ant validity + C*/ - if (tbl->action == IWL_MIMO_SWITCH_ANTENNA_A) - search_tbl->ant_type = ANT_A; - else - search_tbl->ant_type = ANT_B; - - ret = rs_switch_to_siso(priv, lq_sta, conf, sta, - search_tbl, index); - if (!ret) { - lq_sta->search_better_tbl = 1; - goto out; - } - break; - - case IWL_MIMO_SWITCH_GI: - if (!tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_20MHZ)) - break; - if (tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_40MHZ)) - break; - - IWL_DEBUG_RATE("LQ: MIMO toggle SGI/NGI\n"); - - /* Set up new search table for MIMO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = !tbl->is_SGI; - rs_set_expected_tpt_table(lq_sta, search_tbl); - /* - * If active table already uses the fastest possible - * modulation (dual stream with short guard interval), - * and it's working well, there's no need to look - * for a better type of modulation! - */ - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[index]) - break; - } - search_tbl->current_rate = rate_n_flags_from_tbl( - search_tbl, index, is_green); - lq_sta->search_better_tbl = 1; - goto out; - - } - tbl->action++; - if (tbl->action > IWL_MIMO_SWITCH_GI) - tbl->action = IWL_MIMO_SWITCH_ANTENNA_A; - - if (tbl->action == start_action) - break; - } - - return 0; - out: - tbl->action++; - if (tbl->action > IWL_MIMO_SWITCH_GI) - tbl->action = IWL_MIMO_SWITCH_ANTENNA_A; - return 0; - -} - -/* - * Check whether we should continue using same modulation mode, or - * begin search for a new mode, based on: - * 1) # tx successes or failures while using this mode - * 2) # times calling this function - * 3) elapsed time in this mode (not used, for now) - */ -static void rs_stay_in_table(struct iwl4965_lq_sta *lq_sta) -{ - struct iwl4965_scale_tbl_info *tbl; - int i; - int active_tbl; - int flush_interval_passed = 0; - struct iwl_priv *priv; - - priv = lq_sta->drv; - active_tbl = lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - - /* If we've been disallowing search, see if we should now allow it */ - if (lq_sta->stay_in_tbl) { - - /* Elapsed time using current modulation mode */ - if (lq_sta->flush_timer) - flush_interval_passed = - time_after(jiffies, - (unsigned long)(lq_sta->flush_timer + - IWL_RATE_SCALE_FLUSH_INTVL)); - - /* - * Check if we should allow search for new modulation mode. - * If many frames have failed or succeeded, or we've used - * this same modulation for a long time, allow search, and - * reset history stats that keep track of whether we should - * allow a new search. Also (below) reset all bitmaps and - * stats in active history. - */ - if ((lq_sta->total_failed > lq_sta->max_failure_limit) || - (lq_sta->total_success > lq_sta->max_success_limit) || - ((!lq_sta->search_better_tbl) && (lq_sta->flush_timer) - && (flush_interval_passed))) { - IWL_DEBUG_RATE("LQ: stay is expired %d %d %d\n:", - lq_sta->total_failed, - lq_sta->total_success, - flush_interval_passed); - - /* Allow search for new mode */ - lq_sta->stay_in_tbl = 0; /* only place reset */ - lq_sta->total_failed = 0; - lq_sta->total_success = 0; - lq_sta->flush_timer = 0; - - /* - * Else if we've used this modulation mode enough repetitions - * (regardless of elapsed time or success/failure), reset - * history bitmaps and rate-specific stats for all rates in - * active table. - */ - } else { - lq_sta->table_count++; - if (lq_sta->table_count >= - lq_sta->table_count_limit) { - lq_sta->table_count = 0; - - IWL_DEBUG_RATE("LQ: stay in table clear win\n"); - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window( - &(tbl->win[i])); - } - } - - /* If transitioning to allow "search", reset all history - * bitmaps and stats in active table (this will become the new - * "search" table). */ - if (!lq_sta->stay_in_tbl) { - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&(tbl->win[i])); - } - } -} - -/* - * Do rate scaling and search for new modulation mode. - */ -static void rs_rate_scale_perform(struct iwl_priv *priv, - struct net_device *dev, - struct ieee80211_hdr *hdr, - struct sta_info *sta) -{ - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_hw *hw = local_to_hw(local); - struct ieee80211_conf *conf = &hw->conf; - int low = IWL_RATE_INVALID; - int high = IWL_RATE_INVALID; - int index; - int i; - struct iwl4965_rate_scale_data *window = NULL; - int current_tpt = IWL_INVALID_VALUE; - int low_tpt = IWL_INVALID_VALUE; - int high_tpt = IWL_INVALID_VALUE; - u32 fail_count; - s8 scale_action = 0; - __le16 fc; - u16 rate_mask; - u8 update_lq = 0; - struct iwl4965_lq_sta *lq_sta; - struct iwl4965_scale_tbl_info *tbl, *tbl1; - u16 rate_scale_index_msk = 0; - u32 rate; - u8 is_green = 0; - u8 active_tbl = 0; - u8 done_search = 0; - u16 high_low; - s32 sr; - u8 tid = MAX_TID_COUNT; - - IWL_DEBUG_RATE("rate scale calculate new rate for skb\n"); - - fc = hdr->frame_control; - if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) { - /* Send management frames and broadcast/multicast data using - * lowest rate. */ - /* TODO: this could probably be improved.. */ - return; - } - - if (!sta || !sta->rate_ctrl_priv) - return; - - lq_sta = (struct iwl4965_lq_sta *)sta->rate_ctrl_priv; - - tid = rs_tl_add_packet(lq_sta, hdr); - - /* - * Select rate-scale / modulation-mode table to work with in - * the rest of this function: "search" if searching for better - * modulation mode, or "active" if doing rate scaling within a mode. - */ - if (!lq_sta->search_better_tbl) - active_tbl = lq_sta->active_tbl; - else - active_tbl = 1 - lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - is_green = lq_sta->is_green; - - /* current tx rate */ - index = sta->last_txrate_idx; - - IWL_DEBUG_RATE("Rate scale index %d for type %d\n", index, - tbl->lq_type); - - /* rates available for this association, and for modulation mode */ - rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); - - IWL_DEBUG_RATE("mask 0x%04X \n", rate_mask); - - /* mask with station rate restriction */ - if (is_legacy(tbl->lq_type)) { - if (lq_sta->band == IEEE80211_BAND_5GHZ) - /* supp_rates has no CCK bits in A mode */ - rate_scale_index_msk = (u16) (rate_mask & - (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); - else - rate_scale_index_msk = (u16) (rate_mask & - lq_sta->supp_rates); - - } else - rate_scale_index_msk = rate_mask; - - if (!rate_scale_index_msk) - rate_scale_index_msk = rate_mask; - - if (!((1 << index) & rate_scale_index_msk)) { - IWL_ERROR("Current Rate is not valid\n"); - return; - } - - /* Get expected throughput table and history window for current rate */ - if (!tbl->expected_tpt) { - IWL_ERROR("tbl->expected_tpt is NULL\n"); - return; - } - - window = &(tbl->win[index]); - - /* - * If there is not enough history to calculate actual average - * throughput, keep analyzing results of more tx frames, without - * changing rate or mode (bypass most of the rest of this function). - * Set up new rate table in uCode only if old rate is not supported - * in current association (use new rate found above). - */ - fail_count = window->counter - window->success_counter; - if ((fail_count < IWL_RATE_MIN_FAILURE_TH) && - (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) { - IWL_DEBUG_RATE("LQ: still below TH. succ=%d total=%d " - "for index %d\n", - window->success_counter, window->counter, index); - - /* Can't calculate this yet; not enough history */ - window->average_tpt = IWL_INVALID_VALUE; - - /* Should we stay with this modulation mode, - * or search for a new one? */ - rs_stay_in_table(lq_sta); - - goto out; - - /* Else we have enough samples; calculate estimate of - * actual average throughput */ - } else { - /*FIXME:RS remove this else if we don't get this error*/ - if (window->average_tpt != ((window->success_ratio * - tbl->expected_tpt[index] + 64) / 128)) { - IWL_ERROR("expected_tpt should have been calculated" - " by now\n"); - window->average_tpt = ((window->success_ratio * - tbl->expected_tpt[index] + 64) / 128); - } - } - - /* If we are searching for better modulation mode, check success. */ - if (lq_sta->search_better_tbl) { - - /* If good success, continue using the "search" mode; - * no need to send new link quality command, since we're - * continuing to use the setup that we've been trying. */ - if (window->average_tpt > lq_sta->last_tpt) { - - IWL_DEBUG_RATE("LQ: SWITCHING TO CURRENT TABLE " - "suc=%d cur-tpt=%d old-tpt=%d\n", - window->success_ratio, - window->average_tpt, - lq_sta->last_tpt); - - if (!is_legacy(tbl->lq_type)) - lq_sta->enable_counter = 1; - - /* Swap tables; "search" becomes "active" */ - lq_sta->active_tbl = active_tbl; - current_tpt = window->average_tpt; - - /* Else poor success; go back to mode in "active" table */ - } else { - - IWL_DEBUG_RATE("LQ: GOING BACK TO THE OLD TABLE " - "suc=%d cur-tpt=%d old-tpt=%d\n", - window->success_ratio, - window->average_tpt, - lq_sta->last_tpt); - - /* Nullify "search" table */ - tbl->lq_type = LQ_NONE; - - /* Revert to "active" table */ - active_tbl = lq_sta->active_tbl; - tbl = &(lq_sta->lq_info[active_tbl]); - - /* Revert to "active" rate and throughput info */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); - current_tpt = lq_sta->last_tpt; - - /* Need to set up a new rate table in uCode */ - update_lq = 1; - } - - /* Either way, we've made a decision; modulation mode - * search is done, allow rate adjustment next time. */ - lq_sta->search_better_tbl = 0; - done_search = 1; /* Don't switch modes below! */ - goto lq_update; - } - - /* (Else) not in search of better modulation mode, try for better - * starting rate, while staying in this mode. */ - high_low = rs_get_adjacent_rate(priv, index, rate_scale_index_msk, - tbl->lq_type); - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; - - sr = window->success_ratio; - - /* Collect measured throughputs for current and adjacent rates */ - current_tpt = window->average_tpt; - if (low != IWL_RATE_INVALID) - low_tpt = tbl->win[low].average_tpt; - if (high != IWL_RATE_INVALID) - high_tpt = tbl->win[high].average_tpt; - - scale_action = 0; - - /* Too many failures, decrease rate */ - if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) { - IWL_DEBUG_RATE("decrease rate because of low success_ratio\n"); - scale_action = -1; - - /* No throughput measured yet for adjacent rates; try increase. */ - } else if ((low_tpt == IWL_INVALID_VALUE) && - (high_tpt == IWL_INVALID_VALUE)) { - - if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) - scale_action = 1; - else if (low != IWL_RATE_INVALID) - scale_action = -1; - } - - /* Both adjacent throughputs are measured, but neither one has better - * throughput; we're using the best rate, don't change it! */ - else if ((low_tpt != IWL_INVALID_VALUE) && - (high_tpt != IWL_INVALID_VALUE) && - (low_tpt < current_tpt) && - (high_tpt < current_tpt)) - scale_action = 0; - - /* At least one adjacent rate's throughput is measured, - * and may have better performance. */ - else { - /* Higher adjacent rate's throughput is measured */ - if (high_tpt != IWL_INVALID_VALUE) { - /* Higher rate has better throughput */ - if (high_tpt > current_tpt && - sr >= IWL_RATE_INCREASE_TH) { - scale_action = 1; - } else { - IWL_DEBUG_RATE - ("decrease rate because of high tpt\n"); - scale_action = -1; - } - - /* Lower adjacent rate's throughput is measured */ - } else if (low_tpt != IWL_INVALID_VALUE) { - /* Lower rate has better throughput */ - if (low_tpt > current_tpt) { - IWL_DEBUG_RATE - ("decrease rate because of low tpt\n"); - scale_action = -1; - } else if (sr >= IWL_RATE_INCREASE_TH) { - scale_action = 1; - } - } - } - - /* Sanity check; asked for decrease, but success rate or throughput - * has been good at old rate. Don't change it. */ - if ((scale_action == -1) && (low != IWL_RATE_INVALID) && - ((sr > IWL_RATE_HIGH_TH) || - (current_tpt > (100 * tbl->expected_tpt[low])))) - scale_action = 0; - - switch (scale_action) { - case -1: - /* Decrease starting rate, update uCode's rate table */ - if (low != IWL_RATE_INVALID) { - update_lq = 1; - index = low; - } - break; - case 1: - /* Increase starting rate, update uCode's rate table */ - if (high != IWL_RATE_INVALID) { - update_lq = 1; - index = high; - } - - break; - case 0: - /* No change */ - default: - break; - } - - IWL_DEBUG_RATE("choose rate scale index %d action %d low %d " - "high %d type %d\n", - index, scale_action, low, high, tbl->lq_type); - -lq_update: - /* Replace uCode's rate table for the destination station. */ - if (update_lq) { - rate = rate_n_flags_from_tbl(tbl, index, is_green); - rs_fill_link_cmd(priv, lq_sta, rate); - iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); - } - - /* Should we stay with this modulation mode, or search for a new one? */ - rs_stay_in_table(lq_sta); - - /* - * Search for new modulation mode if we're: - * 1) Not changing rates right now - * 2) Not just finishing up a search - * 3) Allowing a new search - */ - if (!update_lq && !done_search && !lq_sta->stay_in_tbl && window->counter) { - /* Save current throughput to compare with "search" throughput*/ - lq_sta->last_tpt = current_tpt; - - /* Select a new "search" modulation mode to try. - * If one is found, set up the new "search" table. */ - if (is_legacy(tbl->lq_type)) - rs_move_legacy_other(priv, lq_sta, conf, sta, index); - else if (is_siso(tbl->lq_type)) - rs_move_siso_to_other(priv, lq_sta, conf, sta, index); - else - rs_move_mimo_to_other(priv, lq_sta, conf, sta, index); - - /* If new "search" mode was selected, set up in uCode table */ - if (lq_sta->search_better_tbl) { - /* Access the "search" table, clear its history. */ - tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&(tbl->win[i])); - - /* Use new "search" start rate */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); - - IWL_DEBUG_RATE("Switch current mcs: %X index: %d\n", - tbl->current_rate, index); - rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); - iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); - } - - /* If the "active" (non-search) mode was legacy, - * and we've tried switching antennas, - * but we haven't been able to try HT modes (not available), - * stay with best antenna legacy modulation for a while - * before next round of mode comparisons. */ - tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); - if (is_legacy(tbl1->lq_type) && - (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)) && - (lq_sta->action_counter >= 1)) { - lq_sta->action_counter = 0; - IWL_DEBUG_RATE("LQ: STAY in legacy table\n"); - rs_set_stay_in_table(priv, 1, lq_sta); - } - - /* If we're in an HT mode, and all 3 mode switch actions - * have been tried and compared, stay in this best modulation - * mode for a while before next round of mode comparisons. */ - if (lq_sta->enable_counter && - (lq_sta->action_counter >= IWL_ACTION_LIMIT)) { - if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && - (lq_sta->tx_agg_tid_en & (1 << tid)) && - (tid != MAX_TID_COUNT)) { - IWL_DEBUG_RATE("try to aggregate tid %d\n", tid); - rs_tl_turn_on_agg(priv, tid, lq_sta, sta); - } - lq_sta->action_counter = 0; - rs_set_stay_in_table(priv, 0, lq_sta); - } - - /* - * Else, don't search for a new modulation mode. - * Put new timestamp in stay-in-modulation-mode flush timer if: - * 1) Not changing rates right now - * 2) Not just finishing up a search - * 3) flush timer is empty - */ - } else { - if ((!update_lq) && (!done_search) && (!lq_sta->flush_timer)) - lq_sta->flush_timer = jiffies; - } - -out: - tbl->current_rate = rate_n_flags_from_tbl(tbl, index, is_green); - i = index; - sta->last_txrate_idx = i; - - /* sta->txrate_idx is an index to A mode rates which start - * at IWL_FIRST_OFDM_RATE - */ - if (lq_sta->band == IEEE80211_BAND_5GHZ) - sta->txrate_idx = i - IWL_FIRST_OFDM_RATE; - else - sta->txrate_idx = i; - - return; -} - - -static void rs_initialize_lq(struct iwl_priv *priv, - struct ieee80211_conf *conf, - struct sta_info *sta) -{ - struct iwl4965_lq_sta *lq_sta; - struct iwl4965_scale_tbl_info *tbl; - int rate_idx; - int i; - u32 rate; - u8 use_green = rs_use_green(priv, conf); - u8 active_tbl = 0; - u8 valid_tx_ant; - - if (!sta || !sta->rate_ctrl_priv) - goto out; - - lq_sta = (struct iwl4965_lq_sta *)sta->rate_ctrl_priv; - i = sta->last_txrate_idx; - - if ((lq_sta->lq.sta_id == 0xff) && - (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)) - goto out; - - valid_tx_ant = priv->hw_params.valid_tx_ant; - - if (!lq_sta->search_better_tbl) - active_tbl = lq_sta->active_tbl; - else - active_tbl = 1 - lq_sta->active_tbl; - - tbl = &(lq_sta->lq_info[active_tbl]); - - if ((i < 0) || (i >= IWL_RATE_COUNT)) - i = 0; - - /* FIXME:RS: This is also wrong in 4965 */ - rate = iwl_rates[i].plcp; - rate |= RATE_MCS_ANT_B_MSK; - rate &= ~RATE_MCS_ANT_A_MSK; - - if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE) - rate |= RATE_MCS_CCK_MSK; - - tbl->ant_type = ANT_B; - rs_get_tbl_info_from_mcs(rate, priv->band, tbl, &rate_idx); - if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) - rs_toggle_antenna(valid_tx_ant, &rate, tbl); - - rate = rate_n_flags_from_tbl(tbl, rate_idx, use_green); - tbl->current_rate = rate; - rs_set_expected_tpt_table(lq_sta, tbl); - rs_fill_link_cmd(NULL, lq_sta, rate); - iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); - out: - return; -} - -static void rs_get_rate(void *priv_rate, struct net_device *dev, - struct ieee80211_supported_band *sband, - struct sk_buff *skb, - struct rate_selection *sel) -{ - - int i; - struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); - struct ieee80211_conf *conf = &local->hw.conf; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct sta_info *sta; - __le16 fc; - struct iwl_priv *priv = (struct iwl_priv *)priv_rate; - struct iwl4965_lq_sta *lq_sta; - - IWL_DEBUG_RATE_LIMIT("rate scale calculate new rate for skb\n"); - - rcu_read_lock(); - - sta = sta_info_get(local, hdr->addr1); - - /* Send management frames and broadcast/multicast data using lowest - * rate. */ - fc = hdr->frame_control; - if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) || - !sta || !sta->rate_ctrl_priv) { - sel->rate_idx = rate_lowest_index(local, sband, sta); - goto out; - } - - lq_sta = (struct iwl4965_lq_sta *)sta->rate_ctrl_priv; - i = sta->last_txrate_idx; - - if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && - !lq_sta->ibss_sta_added) { - u8 sta_id = iwl_find_station(priv, hdr->addr1); - DECLARE_MAC_BUF(mac); - - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_RATE("LQ: ADD station %s\n", - print_mac(mac, hdr->addr1)); - sta_id = iwl_add_station_flags(priv, hdr->addr1, - 0, CMD_ASYNC, NULL); - } - if ((sta_id != IWL_INVALID_STATION)) { - lq_sta->lq.sta_id = sta_id; - lq_sta->lq.rs_table[0].rate_n_flags = 0; - lq_sta->ibss_sta_added = 1; - rs_initialize_lq(priv, conf, sta); - } - } - - if ((i < 0) || (i > IWL_RATE_COUNT)) { - sel->rate_idx = rate_lowest_index(local, sband, sta); - goto out; - } - - if (sband->band == IEEE80211_BAND_5GHZ) - i -= IWL_FIRST_OFDM_RATE; - sel->rate_idx = i; -out: - rcu_read_unlock(); -} - -static void *rs_alloc_sta(void *priv_rate, gfp_t gfp) -{ - struct iwl4965_lq_sta *lq_sta; - struct iwl_priv *priv; - int i, j; - - priv = (struct iwl_priv *)priv_rate; - IWL_DEBUG_RATE("create station rate scale window\n"); - - lq_sta = kzalloc(sizeof(struct iwl4965_lq_sta), gfp); - - if (lq_sta == NULL) - return NULL; - lq_sta->lq.sta_id = 0xff; - - - for (j = 0; j < LQ_SIZE; j++) - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&(lq_sta->lq_info[j].win[i])); - - return lq_sta; -} - -static void rs_rate_init(void *priv_rate, void *priv_sta, - struct ieee80211_local *local, - struct sta_info *sta) -{ - int i, j; - struct ieee80211_conf *conf = &local->hw.conf; - struct ieee80211_supported_band *sband; - struct iwl_priv *priv = (struct iwl_priv *)priv_rate; - struct iwl4965_lq_sta *lq_sta = priv_sta; - - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - - lq_sta->flush_timer = 0; - lq_sta->supp_rates = sta->supp_rates[sband->band]; - sta->txrate_idx = 3; - for (j = 0; j < LQ_SIZE; j++) - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&(lq_sta->lq_info[j].win[i])); - - IWL_DEBUG_RATE("LQ: *** rate scale global init ***\n"); - /* TODO: what is a good starting rate for STA? About middle? Maybe not - * the lowest or the highest rate.. Could consider using RSSI from - * previous packets? Need to have IEEE 802.1X auth succeed immediately - * after assoc.. */ - - lq_sta->ibss_sta_added = 0; - if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { - u8 sta_id = iwl_find_station(priv, sta->addr); - DECLARE_MAC_BUF(mac); - - /* for IBSS the call are from tasklet */ - IWL_DEBUG_RATE("LQ: ADD station %s\n", - print_mac(mac, sta->addr)); - - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_RATE("LQ: ADD station %s\n", - print_mac(mac, sta->addr)); - sta_id = iwl_add_station_flags(priv, sta->addr, - 0, CMD_ASYNC, NULL); - } - if ((sta_id != IWL_INVALID_STATION)) { - lq_sta->lq.sta_id = sta_id; - lq_sta->lq.rs_table[0].rate_n_flags = 0; - } - /* FIXME: this is w/a remove it later */ - priv->assoc_station_added = 1; - } - - /* Find highest tx rate supported by hardware and destination station */ - for (i = 0; i < sband->n_bitrates; i++) - if (sta->supp_rates[sband->band] & BIT(i)) - sta->txrate_idx = i; - - sta->last_txrate_idx = sta->txrate_idx; - /* WTF is with this bogus comment? A doesn't have cck rates */ - /* For MODE_IEEE80211A, cck rates are at end of rate table */ - if (local->hw.conf.channel->band == IEEE80211_BAND_5GHZ) - sta->last_txrate_idx += IWL_FIRST_OFDM_RATE; - - lq_sta->is_dup = 0; - lq_sta->is_green = rs_use_green(priv, conf); - lq_sta->active_legacy_rate = priv->active_rate & ~(0x1000); - lq_sta->active_rate_basic = priv->active_rate_basic; - lq_sta->band = priv->band; - /* - * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), - * supp_rates[] does not; shift to convert format, force 9 MBits off. - */ - lq_sta->active_siso_rate = conf->ht_conf.supp_mcs_set[0] << 1; - lq_sta->active_siso_rate |= conf->ht_conf.supp_mcs_set[0] & 0x1; - lq_sta->active_siso_rate &= ~((u16)0x2); - lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; - - /* Same here */ - lq_sta->active_mimo2_rate = conf->ht_conf.supp_mcs_set[1] << 1; - lq_sta->active_mimo2_rate |= conf->ht_conf.supp_mcs_set[1] & 0x1; - lq_sta->active_mimo2_rate &= ~((u16)0x2); - lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; - - lq_sta->active_mimo3_rate = conf->ht_conf.supp_mcs_set[2] << 1; - lq_sta->active_mimo3_rate |= conf->ht_conf.supp_mcs_set[2] & 0x1; - lq_sta->active_mimo3_rate &= ~((u16)0x2); - lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE; - - IWL_DEBUG_RATE("SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n", - lq_sta->active_siso_rate, - lq_sta->active_mimo2_rate, - lq_sta->active_mimo3_rate); - - /* These values will be overriden later */ - lq_sta->lq.general_params.single_stream_ant_msk = ANT_A; - lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB; - - /* as default allow aggregation for all tids */ - lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; - lq_sta->drv = priv; - - rs_initialize_lq(priv, conf, sta); -} - -static void rs_fill_link_cmd(const struct iwl_priv *priv, - struct iwl4965_lq_sta *lq_sta, - u32 new_rate) -{ - struct iwl4965_scale_tbl_info tbl_type; - int index = 0; - int rate_idx; - int repeat_rate = 0; - u8 ant_toggle_cnt = 0; - u8 use_ht_possible = 1; - u8 valid_tx_ant = 0; - struct iwl_link_quality_cmd *lq_cmd = &lq_sta->lq; - - /* Override starting rate (index 0) if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate, index); - - /* Interpret new_rate (rate_n_flags) */ - memset(&tbl_type, 0, sizeof(tbl_type)); - rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, - &tbl_type, &rate_idx); - - /* How many times should we repeat the initial rate? */ - if (is_legacy(tbl_type.lq_type)) { - ant_toggle_cnt = 1; - repeat_rate = IWL_NUMBER_TRY; - } else { - repeat_rate = IWL_HT_NUMBER_TRY; - } - - lq_cmd->general_params.mimo_delimiter = - is_mimo(tbl_type.lq_type) ? 1 : 0; - - /* Fill 1st table entry (index 0) */ - lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); - - if (num_of_ant(tbl_type.ant_type) == 1) { - lq_cmd->general_params.single_stream_ant_msk = - tbl_type.ant_type; - } else if (num_of_ant(tbl_type.ant_type) == 2) { - lq_cmd->general_params.dual_stream_ant_msk = - tbl_type.ant_type; - } /* otherwise we don't modify the existing value */ - - index++; - repeat_rate--; - - if (priv) - valid_tx_ant = priv->hw_params.valid_tx_ant; - - /* Fill rest of rate table */ - while (index < LINK_QUAL_MAX_RETRY_NUM) { - /* Repeat initial/next rate. - * For legacy IWL_NUMBER_TRY == 1, this loop will not execute. - * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */ - while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) { - if (is_legacy(tbl_type.lq_type)) { - if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) - ant_toggle_cnt++; - else if (priv && - rs_toggle_antenna(valid_tx_ant, - &new_rate, &tbl_type)) - ant_toggle_cnt = 1; -} - - /* Override next rate if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate, index); - - /* Fill next table entry */ - lq_cmd->rs_table[index].rate_n_flags = - cpu_to_le32(new_rate); - repeat_rate--; - index++; - } - - rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, - &rate_idx); - - /* Indicate to uCode which entries might be MIMO. - * If initial rate was MIMO, this will finally end up - * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ - if (is_mimo(tbl_type.lq_type)) - lq_cmd->general_params.mimo_delimiter = index; - - /* Get next rate */ - new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx, - use_ht_possible); - - /* How many times should we repeat the next rate? */ - if (is_legacy(tbl_type.lq_type)) { - if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) - ant_toggle_cnt++; - else if (priv && - rs_toggle_antenna(valid_tx_ant, - &new_rate, &tbl_type)) - ant_toggle_cnt = 1; - - repeat_rate = IWL_NUMBER_TRY; - } else { - repeat_rate = IWL_HT_NUMBER_TRY; - } - - /* Don't allow HT rates after next pass. - * rs_get_lower_rate() will change type to LQ_A or LQ_G. */ - use_ht_possible = 0; - - /* Override next rate if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate, index); - - /* Fill next table entry */ - lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); - - index++; - repeat_rate--; - } - - lq_cmd->agg_params.agg_frame_cnt_limit = 64; - lq_cmd->agg_params.agg_dis_start_th = 3; - lq_cmd->agg_params.agg_time_limit = cpu_to_le16(4000); -} - -static void *rs_alloc(struct ieee80211_local *local) -{ - return local->hw.priv; -} -/* rate scale requires free function to be implemented */ -static void rs_free(void *priv_rate) -{ - return; -} - -static void rs_clear(void *priv_rate) -{ - struct iwl_priv *priv = (struct iwl_priv *) priv_rate; - - IWL_DEBUG_RATE("enter\n"); - - /* TODO - add rate scale state reset */ - - IWL_DEBUG_RATE("leave\n"); -} - -static void rs_free_sta(void *priv_rate, void *priv_sta) -{ - struct iwl4965_lq_sta *lq_sta = priv_sta; - struct iwl_priv *priv; - - priv = (struct iwl_priv *)priv_rate; - IWL_DEBUG_RATE("enter\n"); - kfree(lq_sta); - IWL_DEBUG_RATE("leave\n"); -} - - -#ifdef CONFIG_MAC80211_DEBUGFS -static int open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} -static void rs_dbgfs_set_mcs(struct iwl4965_lq_sta *lq_sta, - u32 *rate_n_flags, int index) -{ - struct iwl_priv *priv; - - priv = lq_sta->drv; - if (lq_sta->dbg_fixed_rate) { - if (index < 12) { - *rate_n_flags = lq_sta->dbg_fixed_rate; - } else { - if (lq_sta->band == IEEE80211_BAND_5GHZ) - *rate_n_flags = 0x800D; - else - *rate_n_flags = 0x820A; - } - IWL_DEBUG_RATE("Fixed rate ON\n"); - } else { - IWL_DEBUG_RATE("Fixed rate OFF\n"); - } -} - -static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct iwl4965_lq_sta *lq_sta = file->private_data; - struct iwl_priv *priv; - char buf[64]; - int buf_size; - u32 parsed_rate; - - priv = lq_sta->drv; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - if (sscanf(buf, "%x", &parsed_rate) == 1) - lq_sta->dbg_fixed_rate = parsed_rate; - else - lq_sta->dbg_fixed_rate = 0; - - lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ - lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - - IWL_DEBUG_RATE("sta_id %d rate 0x%X\n", - lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); - - if (lq_sta->dbg_fixed_rate) { - rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); - iwl_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC); - } - - return count; -} - -static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - char buff[1024]; - int desc = 0; - int i = 0; - - struct iwl4965_lq_sta *lq_sta = file->private_data; - - desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id); - desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n", - lq_sta->total_failed, lq_sta->total_success, - lq_sta->active_legacy_rate); - desc += sprintf(buff+desc, "fixed rate 0x%X\n", - lq_sta->dbg_fixed_rate); - desc += sprintf(buff+desc, "general:" - "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", - lq_sta->lq.general_params.flags, - lq_sta->lq.general_params.mimo_delimiter, - lq_sta->lq.general_params.single_stream_ant_msk, - lq_sta->lq.general_params.dual_stream_ant_msk); - - desc += sprintf(buff+desc, "agg:" - "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", - le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit), - lq_sta->lq.agg_params.agg_dis_start_th, - lq_sta->lq.agg_params.agg_frame_cnt_limit); - - desc += sprintf(buff+desc, - "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", - lq_sta->lq.general_params.start_rate_index[0], - lq_sta->lq.general_params.start_rate_index[1], - lq_sta->lq.general_params.start_rate_index[2], - lq_sta->lq.general_params.start_rate_index[3]); - - - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) - desc += sprintf(buff+desc, " rate[%d] 0x%X\n", - i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags)); - - return simple_read_from_buffer(user_buf, count, ppos, buff, desc); -} - -static const struct file_operations rs_sta_dbgfs_scale_table_ops = { - .write = rs_sta_dbgfs_scale_table_write, - .read = rs_sta_dbgfs_scale_table_read, - .open = open_file_generic, -}; -static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, - char __user *user_buf, size_t count, loff_t *ppos) -{ - char buff[1024]; - int desc = 0; - int i, j; - - struct iwl4965_lq_sta *lq_sta = file->private_data; - for (i = 0; i < LQ_SIZE; i++) { - desc += sprintf(buff+desc, "%s type=%d SGI=%d FAT=%d DUP=%d\n" - "rate=0x%X\n", - lq_sta->active_tbl == i?"*":"x", - lq_sta->lq_info[i].lq_type, - lq_sta->lq_info[i].is_SGI, - lq_sta->lq_info[i].is_fat, - lq_sta->lq_info[i].is_dup, - lq_sta->lq_info[i].current_rate); - for (j = 0; j < IWL_RATE_COUNT; j++) { - desc += sprintf(buff+desc, - "counter=%d success=%d %%=%d\n", - lq_sta->lq_info[i].win[j].counter, - lq_sta->lq_info[i].win[j].success_counter, - lq_sta->lq_info[i].win[j].success_ratio); - } - } - return simple_read_from_buffer(user_buf, count, ppos, buff, desc); -} - -static const struct file_operations rs_sta_dbgfs_stats_table_ops = { - .read = rs_sta_dbgfs_stats_table_read, - .open = open_file_generic, -}; - -static void rs_add_debugfs(void *priv, void *priv_sta, - struct dentry *dir) -{ - struct iwl4965_lq_sta *lq_sta = priv_sta; - lq_sta->rs_sta_dbgfs_scale_table_file = - debugfs_create_file("rate_scale_table", 0600, dir, - lq_sta, &rs_sta_dbgfs_scale_table_ops); - lq_sta->rs_sta_dbgfs_stats_table_file = - debugfs_create_file("rate_stats_table", 0600, dir, - lq_sta, &rs_sta_dbgfs_stats_table_ops); - lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = - debugfs_create_u8("tx_agg_tid_enable", 0600, dir, - &lq_sta->tx_agg_tid_en); - -} - -static void rs_remove_debugfs(void *priv, void *priv_sta) -{ - struct iwl4965_lq_sta *lq_sta = priv_sta; - debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); - debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); -} -#endif - -static struct rate_control_ops rs_ops = { - .module = NULL, - .name = RS_NAME, - .tx_status = rs_tx_status, - .get_rate = rs_get_rate, - .rate_init = rs_rate_init, - .clear = rs_clear, - .alloc = rs_alloc, - .free = rs_free, - .alloc_sta = rs_alloc_sta, - .free_sta = rs_free_sta, -#ifdef CONFIG_MAC80211_DEBUGFS - .add_sta_debugfs = rs_add_debugfs, - .remove_sta_debugfs = rs_remove_debugfs, -#endif -}; - -int iwl4965_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) -{ - struct ieee80211_local *local = hw_to_local(hw); - struct iwl_priv *priv = hw->priv; - struct iwl4965_lq_sta *lq_sta; - struct sta_info *sta; - int cnt = 0, i; - u32 samples = 0, success = 0, good = 0; - unsigned long now = jiffies; - u32 max_time = 0; - u8 lq_type, antenna; - - rcu_read_lock(); - - sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr); - if (!sta || !sta->rate_ctrl_priv) { - if (sta) - IWL_DEBUG_RATE("leave - no private rate data!\n"); - else - IWL_DEBUG_RATE("leave - no station!\n"); - rcu_read_unlock(); - return sprintf(buf, "station %d not found\n", sta_id); - } - - lq_sta = (void *)sta->rate_ctrl_priv; - - lq_type = lq_sta->lq_info[lq_sta->active_tbl].lq_type; - antenna = lq_sta->lq_info[lq_sta->active_tbl].ant_type; - - if (is_legacy(lq_type)) - i = IWL_RATE_54M_INDEX; - else - i = IWL_RATE_60M_INDEX; - while (1) { - u64 mask; - int j; - int active = lq_sta->active_tbl; - - cnt += - sprintf(&buf[cnt], " %2dMbs: ", iwl_rates[i].ieee / 2); - - mask = (1ULL << (IWL_RATE_MAX_WINDOW - 1)); - for (j = 0; j < IWL_RATE_MAX_WINDOW; j++, mask >>= 1) - buf[cnt++] = - (lq_sta->lq_info[active].win[i].data & mask) - ? '1' : '0'; - - samples += lq_sta->lq_info[active].win[i].counter; - good += lq_sta->lq_info[active].win[i].success_counter; - success += lq_sta->lq_info[active].win[i].success_counter * - iwl_rates[i].ieee; - - if (lq_sta->lq_info[active].win[i].stamp) { - int delta = - jiffies_to_msecs(now - - lq_sta->lq_info[active].win[i].stamp); - - if (delta > max_time) - max_time = delta; - - cnt += sprintf(&buf[cnt], "%5dms\n", delta); - } else - buf[cnt++] = '\n'; - - j = iwl4965_get_prev_ieee_rate(i); - if (j == i) - break; - i = j; - } - - /* - * Display the average rate of all samples taken. - * NOTE: We multiply # of samples by 2 since the IEEE measurement - * added from iwl_rates is actually 2X the rate. - */ - if (samples) - cnt += sprintf(&buf[cnt], - "\nAverage rate is %3d.%02dMbs over last %4dms\n" - "%3d%% success (%d good packets over %d tries)\n", - success / (2 * samples), (success * 5 / samples) % 10, - max_time, good * 100 / samples, good, samples); - else - cnt += sprintf(&buf[cnt], "\nAverage rate: 0Mbs\n"); - - cnt += sprintf(&buf[cnt], "\nrate scale type %d antenna %d " - "active_search %d rate index %d\n", lq_type, antenna, - lq_sta->search_better_tbl, sta->last_txrate_idx); - - rcu_read_unlock(); - return cnt; -} - -int iwl4965_rate_control_register(void) -{ - return ieee80211_rate_control_register(&rs_ops); -} - -void iwl4965_rate_control_unregister(void) -{ - ieee80211_rate_control_unregister(&rs_ops); -} - diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-rs.h b/drivers/net/wireless/iwlwifi/iwl-4965-rs.h deleted file mode 100644 index 9b9972885aa5..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl-4965-rs.h +++ /dev/null @@ -1,318 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2008 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * James P. Ketrenos - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#ifndef __iwl_4965_rs_h__ -#define __iwl_4965_rs_h__ - -#include "iwl-dev.h" - -struct iwl_rate_info { - u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ - u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ - u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ - u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */ - u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ - u8 prev_ieee; /* previous rate in IEEE speeds */ - u8 next_ieee; /* next rate in IEEE speeds */ - u8 prev_rs; /* previous rate used in rs algo */ - u8 next_rs; /* next rate used in rs algo */ - u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ - u8 next_rs_tgg; /* next rate used in TGG rs algo */ -}; - -/* - * These serve as indexes into - * struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; - */ -enum { - IWL_RATE_1M_INDEX = 0, - IWL_RATE_2M_INDEX, - IWL_RATE_5M_INDEX, - IWL_RATE_11M_INDEX, - IWL_RATE_6M_INDEX, - IWL_RATE_9M_INDEX, - IWL_RATE_12M_INDEX, - IWL_RATE_18M_INDEX, - IWL_RATE_24M_INDEX, - IWL_RATE_36M_INDEX, - IWL_RATE_48M_INDEX, - IWL_RATE_54M_INDEX, - IWL_RATE_60M_INDEX, - IWL_RATE_COUNT, /*FIXME:RS:change to IWL_RATE_INDEX_COUNT,*/ - IWL_RATE_INVM_INDEX = IWL_RATE_COUNT, - IWL_RATE_INVALID = IWL_RATE_COUNT, -}; - -enum { - IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, - IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX, - IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX, - IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, -}; - -/* #define vs. enum to keep from defaulting to 'large integer' */ -#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX) -#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX) -#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX) -#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX) -#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX) -#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX) -#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX) -#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX) -#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX) -#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX) -#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX) -#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX) -#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX) - -/* 4965 uCode API values for legacy bit rates, both OFDM and CCK */ -enum { - IWL_RATE_6M_PLCP = 13, - IWL_RATE_9M_PLCP = 15, - IWL_RATE_12M_PLCP = 5, - IWL_RATE_18M_PLCP = 7, - IWL_RATE_24M_PLCP = 9, - IWL_RATE_36M_PLCP = 11, - IWL_RATE_48M_PLCP = 1, - IWL_RATE_54M_PLCP = 3, - IWL_RATE_60M_PLCP = 3,/*FIXME:RS:should be removed*/ - IWL_RATE_1M_PLCP = 10, - IWL_RATE_2M_PLCP = 20, - IWL_RATE_5M_PLCP = 55, - IWL_RATE_11M_PLCP = 110, - /*FIXME:RS:change to IWL_RATE_LEGACY_??M_PLCP */ - /*FIXME:RS:add IWL_RATE_LEGACY_INVM_PLCP = 0,*/ -}; - -/* 4965 uCode API values for OFDM high-throughput (HT) bit rates */ -enum { - IWL_RATE_SISO_6M_PLCP = 0, - IWL_RATE_SISO_12M_PLCP = 1, - IWL_RATE_SISO_18M_PLCP = 2, - IWL_RATE_SISO_24M_PLCP = 3, - IWL_RATE_SISO_36M_PLCP = 4, - IWL_RATE_SISO_48M_PLCP = 5, - IWL_RATE_SISO_54M_PLCP = 6, - IWL_RATE_SISO_60M_PLCP = 7, - IWL_RATE_MIMO2_6M_PLCP = 0x8, - IWL_RATE_MIMO2_12M_PLCP = 0x9, - IWL_RATE_MIMO2_18M_PLCP = 0xa, - IWL_RATE_MIMO2_24M_PLCP = 0xb, - IWL_RATE_MIMO2_36M_PLCP = 0xc, - IWL_RATE_MIMO2_48M_PLCP = 0xd, - IWL_RATE_MIMO2_54M_PLCP = 0xe, - IWL_RATE_MIMO2_60M_PLCP = 0xf, - IWL_RATE_MIMO3_6M_PLCP = 0x10, - IWL_RATE_MIMO3_12M_PLCP = 0x11, - IWL_RATE_MIMO3_18M_PLCP = 0x12, - IWL_RATE_MIMO3_24M_PLCP = 0x13, - IWL_RATE_MIMO3_36M_PLCP = 0x14, - IWL_RATE_MIMO3_48M_PLCP = 0x15, - IWL_RATE_MIMO3_54M_PLCP = 0x16, - IWL_RATE_MIMO3_60M_PLCP = 0x17, - IWL_RATE_SISO_INVM_PLCP, - IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, - IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, -}; - -/* MAC header values for bit rates */ -enum { - IWL_RATE_6M_IEEE = 12, - IWL_RATE_9M_IEEE = 18, - IWL_RATE_12M_IEEE = 24, - IWL_RATE_18M_IEEE = 36, - IWL_RATE_24M_IEEE = 48, - IWL_RATE_36M_IEEE = 72, - IWL_RATE_48M_IEEE = 96, - IWL_RATE_54M_IEEE = 108, - IWL_RATE_60M_IEEE = 120, - IWL_RATE_1M_IEEE = 2, - IWL_RATE_2M_IEEE = 4, - IWL_RATE_5M_IEEE = 11, - IWL_RATE_11M_IEEE = 22, -}; - -#define IWL_CCK_BASIC_RATES_MASK \ - (IWL_RATE_1M_MASK | \ - IWL_RATE_2M_MASK) - -#define IWL_CCK_RATES_MASK \ - (IWL_BASIC_RATES_MASK | \ - IWL_RATE_5M_MASK | \ - IWL_RATE_11M_MASK) - -#define IWL_OFDM_BASIC_RATES_MASK \ - (IWL_RATE_6M_MASK | \ - IWL_RATE_12M_MASK | \ - IWL_RATE_24M_MASK) - -#define IWL_OFDM_RATES_MASK \ - (IWL_OFDM_BASIC_RATES_MASK | \ - IWL_RATE_9M_MASK | \ - IWL_RATE_18M_MASK | \ - IWL_RATE_36M_MASK | \ - IWL_RATE_48M_MASK | \ - IWL_RATE_54M_MASK) - -#define IWL_BASIC_RATES_MASK \ - (IWL_OFDM_BASIC_RATES_MASK | \ - IWL_CCK_BASIC_RATES_MASK) - -#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) - -#define IWL_INVALID_VALUE -1 - -#define IWL_MIN_RSSI_VAL -100 -#define IWL_MAX_RSSI_VAL 0 - -/* These values specify how many Tx frame attempts before - * searching for a new modulation mode */ -#define IWL_LEGACY_FAILURE_LIMIT 160 -#define IWL_LEGACY_SUCCESS_LIMIT 480 -#define IWL_LEGACY_TABLE_COUNT 160 - -#define IWL_NONE_LEGACY_FAILURE_LIMIT 400 -#define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500 -#define IWL_NONE_LEGACY_TABLE_COUNT 1500 - -/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ -#define IWL_RS_GOOD_RATIO 12800 /* 100% */ -#define IWL_RATE_SCALE_SWITCH 10880 /* 85% */ -#define IWL_RATE_HIGH_TH 10880 /* 85% */ -#define IWL_RATE_INCREASE_TH 8960 /* 70% */ -#define IWL_RATE_DECREASE_TH 1920 /* 15% */ - -/* possible actions when in legacy mode */ -#define IWL_LEGACY_SWITCH_ANTENNA 0 -#define IWL_LEGACY_SWITCH_SISO 1 -#define IWL_LEGACY_SWITCH_MIMO2 2 - -/* possible actions when in siso mode */ -#define IWL_SISO_SWITCH_ANTENNA 0 -#define IWL_SISO_SWITCH_MIMO2 1 -#define IWL_SISO_SWITCH_GI 2 - -/* possible actions when in mimo mode */ -#define IWL_MIMO_SWITCH_ANTENNA_A 0 -#define IWL_MIMO_SWITCH_ANTENNA_B 1 -#define IWL_MIMO_SWITCH_GI 2 - -/*FIXME:RS:separate MIMO2/3 transitions*/ - -/*FIXME:RS:add posible acctions for MIMO3*/ - -#define IWL_ACTION_LIMIT 3 /* # possible actions */ - -#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ - -/* load per tid defines for A-MPDU activation */ -#define IWL_AGG_TPT_THREHOLD 0 -#define IWL_AGG_LOAD_THRESHOLD 10 -#define IWL_AGG_ALL_TID 0xff -#define TID_QUEUE_CELL_SPACING 50 /*mS */ -#define TID_QUEUE_MAX_SIZE 20 -#define TID_ROUND_VALUE 5 /* mS */ -#define TID_MAX_LOAD_COUNT 8 - -#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) -#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) - -extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; - -enum iwl_table_type { - LQ_NONE, - LQ_G, /* legacy types */ - LQ_A, - LQ_SISO, /* high-throughput types */ - LQ_MIMO2, - LQ_MIMO3, - LQ_MAX, -}; - -#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A)) -#define is_siso(tbl) ((tbl) == LQ_SISO) -#define is_mimo2(tbl) ((tbl) == LQ_MIMO2) -#define is_mimo3(tbl) ((tbl) == LQ_MIMO3) -#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl)) -#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) -#define is_a_band(tbl) ((tbl) == LQ_A) -#define is_g_and(tbl) ((tbl) == LQ_G) - -#define ANT_NONE 0x0 -#define ANT_A BIT(0) -#define ANT_B BIT(1) -#define ANT_AB (ANT_A | ANT_B) -#define ANT_C BIT(2) -#define ANT_AC (ANT_A | ANT_C) -#define ANT_BC (ANT_B | ANT_C) -#define ANT_ABC (ANT_AB | ANT_C) - -static inline u8 num_of_ant(u8 mask) -{ - return !!((mask) & ANT_A) + - !!((mask) & ANT_B) + - !!((mask) & ANT_C); -} - -static inline u8 iwl4965_get_prev_ieee_rate(u8 rate_index) -{ - u8 rate = iwl_rates[rate_index].prev_ieee; - - if (rate == IWL_RATE_INVALID) - rate = rate_index; - return rate; -} - -/** - * iwl4965_fill_rs_info - Fill an output text buffer with the rate representation - * - * NOTE: This is provided as a quick mechanism for a user to visualize - * the performance of the rate control algorithm and is not meant to be - * parsed software. - */ -extern int iwl4965_fill_rs_info(struct ieee80211_hw *, char *buf, u8 sta_id); - -/** - * iwl4965_rate_control_register - Register the rate control algorithm callbacks - * - * Since the rate control algorithm is hardware specific, there is no need - * or reason to place it as a stand alone module. The driver can call - * iwl4965_rate_control_register in order to register the rate control callbacks - * with the mac80211 subsystem. This should be performed prior to calling - * ieee80211_register_hw - * - */ -extern int iwl4965_rate_control_register(void); - -/** - * iwl4965_rate_control_unregister - Unregister the rate control callbacks - * - * This should be called after calling ieee80211_unregister_hw, but before - * the driver is unloaded. - */ -extern void iwl4965_rate_control_unregister(void); - -#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c new file mode 100644 index 000000000000..eee22c6dec73 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -0,0 +1,2704 @@ +/****************************************************************************** + * + * Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * James P. Ketrenos + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "../net/mac80211/rate.h" + +#include "iwl-dev.h" +#include "iwl-sta.h" +#include "iwl-core.h" +#include "iwl-helpers.h" + +#define RS_NAME "iwl-agn-rs" + +#define NUM_TRY_BEFORE_ANT_TOGGLE 1 +#define IWL_NUMBER_TRY 1 +#define IWL_HT_NUMBER_TRY 3 + +#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ +#define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ +#define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ + +/* max time to accum history 2 seconds */ +#define IWL_RATE_SCALE_FLUSH_INTVL (2*HZ) + +static u8 rs_ht_to_legacy[] = { + IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, + IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, + IWL_RATE_6M_INDEX, + IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX, + IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, + IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, + IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX +}; + +static const u8 ant_toggle_lookup[] = { + /*ANT_NONE -> */ ANT_NONE, + /*ANT_A -> */ ANT_B, + /*ANT_B -> */ ANT_C, + /*ANT_AB -> */ ANT_BC, + /*ANT_C -> */ ANT_A, + /*ANT_AC -> */ ANT_AB, + /*ANT_BC -> */ ANT_AC, + /*ANT_ABC -> */ ANT_ABC, +}; + +/** + * struct iwl_rate_scale_data -- tx success history for one rate + */ +struct iwl_rate_scale_data { + u64 data; /* bitmap of successful frames */ + s32 success_counter; /* number of frames successful */ + s32 success_ratio; /* per-cent * 128 */ + s32 counter; /* number of frames attempted */ + s32 average_tpt; /* success ratio * expected throughput */ + unsigned long stamp; +}; + +/** + * struct iwl_scale_tbl_info -- tx params and success history for all rates + * + * There are two of these in struct iwl_lq_sta, + * one for "active", and one for "search". + */ +struct iwl_scale_tbl_info { + enum iwl_table_type lq_type; + u8 ant_type; + u8 is_SGI; /* 1 = short guard interval */ + u8 is_fat; /* 1 = 40 MHz channel width */ + u8 is_dup; /* 1 = duplicated data streams */ + u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ + s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ + u32 current_rate; /* rate_n_flags, uCode API format */ + struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ +}; + +struct iwl_traffic_load { + unsigned long time_stamp; /* age of the oldest statistics */ + u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time + * slice */ + u32 total; /* total num of packets during the + * last TID_MAX_TIME_DIFF */ + u8 queue_count; /* number of queues that has + * been used since the last cleanup */ + u8 head; /* start of the circular buffer */ +}; + +/** + * struct iwl_lq_sta -- driver's rate scaling private structure + * + * Pointer to this gets passed back and forth between driver and mac80211. + */ +struct iwl_lq_sta { + u8 active_tbl; /* index of active table, range 0-1 */ + u8 enable_counter; /* indicates HT mode */ + u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ + u8 search_better_tbl; /* 1: currently trying alternate mode */ + s32 last_tpt; + + /* The following determine when to search for a new mode */ + u32 table_count_limit; + u32 max_failure_limit; /* # failed frames before new search */ + u32 max_success_limit; /* # successful frames before new search */ + u32 table_count; + u32 total_failed; /* total failed frames, any/all rates */ + u32 total_success; /* total successful frames, any/all rates */ + u32 flush_timer; /* time staying in mode before new search */ + + u8 action_counter; /* # mode-switch actions tried */ + u8 is_green; + u8 is_dup; + enum ieee80211_band band; + u8 ibss_sta_added; + + /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ + u32 supp_rates; + u16 active_legacy_rate; + u16 active_siso_rate; + u16 active_mimo2_rate; + u16 active_mimo3_rate; + u16 active_rate_basic; + + struct iwl_link_quality_cmd lq; + struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ + struct iwl_traffic_load load[TID_MAX_LOAD_COUNT]; + u8 tx_agg_tid_en; +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *rs_sta_dbgfs_scale_table_file; + struct dentry *rs_sta_dbgfs_stats_table_file; + struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; + u32 dbg_fixed_rate; +#endif + struct iwl_priv *drv; +}; + +static void rs_rate_scale_perform(struct iwl_priv *priv, + struct net_device *dev, + struct ieee80211_hdr *hdr, + struct sta_info *sta); +static void rs_fill_link_cmd(const struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, u32 rate_n_flags); + + +#ifdef CONFIG_MAC80211_DEBUGFS +static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, + u32 *rate_n_flags, int index); +#else +static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, + u32 *rate_n_flags, int index) +{} +#endif + +/* + * Expected throughput metrics for following rates: + * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits + * "G" is the only table that supports CCK (the first 4 rates). + */ +/*FIXME:RS:need to spearate tables for MIMO2/MIMO3*/ +static s32 expected_tpt_A[IWL_RATE_COUNT] = { + 0, 0, 0, 0, 40, 57, 72, 98, 121, 154, 177, 186, 186 +}; + +static s32 expected_tpt_G[IWL_RATE_COUNT] = { + 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 186 +}; + +static s32 expected_tpt_siso20MHz[IWL_RATE_COUNT] = { + 0, 0, 0, 0, 42, 42, 76, 102, 124, 159, 183, 193, 202 +}; + +static s32 expected_tpt_siso20MHzSGI[IWL_RATE_COUNT] = { + 0, 0, 0, 0, 46, 46, 82, 110, 132, 168, 192, 202, 211 +}; + +static s32 expected_tpt_mimo20MHz[IWL_RATE_COUNT] = { + 0, 0, 0, 0, 74, 74, 123, 155, 179, 214, 236, 244, 251 +}; + +static s32 expected_tpt_mimo20MHzSGI[IWL_RATE_COUNT] = { + 0, 0, 0, 0, 81, 81, 131, 164, 188, 222, 243, 251, 257 +}; + +static s32 expected_tpt_siso40MHz[IWL_RATE_COUNT] = { + 0, 0, 0, 0, 77, 77, 127, 160, 184, 220, 242, 250, 257 +}; + +static s32 expected_tpt_siso40MHzSGI[IWL_RATE_COUNT] = { + 0, 0, 0, 0, 83, 83, 135, 169, 193, 229, 250, 257, 264 +}; + +static s32 expected_tpt_mimo40MHz[IWL_RATE_COUNT] = { + 0, 0, 0, 0, 123, 123, 182, 214, 235, 264, 279, 285, 289 +}; + +static s32 expected_tpt_mimo40MHzSGI[IWL_RATE_COUNT] = { + 0, 0, 0, 0, 131, 131, 191, 222, 242, 270, 284, 289, 293 +}; + +static inline u8 rs_extract_rate(u32 rate_n_flags) +{ + return (u8)(rate_n_flags & 0xFF); +} + +static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) +{ + window->data = 0; + window->success_counter = 0; + window->success_ratio = IWL_INVALID_VALUE; + window->counter = 0; + window->average_tpt = IWL_INVALID_VALUE; + window->stamp = 0; +} + +static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) +{ + return ((ant_type & valid_antenna) == ant_type); +} + +/* + * removes the old data from the statistics. All data that is older than + * TID_MAX_TIME_DIFF, will be deleted. + */ +static void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time) +{ + /* The oldest age we want to keep */ + u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; + + while (tl->queue_count && + (tl->time_stamp < oldest_time)) { + tl->total -= tl->packet_count[tl->head]; + tl->packet_count[tl->head] = 0; + tl->time_stamp += TID_QUEUE_CELL_SPACING; + tl->queue_count--; + tl->head++; + if (tl->head >= TID_QUEUE_MAX_SIZE) + tl->head = 0; + } +} + +/* + * increment traffic load value for tid and also remove + * any old values if passed the certain time period + */ +static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data, + struct ieee80211_hdr *hdr) +{ + u32 curr_time = jiffies_to_msecs(jiffies); + u32 time_diff; + s32 index; + struct iwl_traffic_load *tl = NULL; + __le16 fc = hdr->frame_control; + u8 tid; + + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + } else + return MAX_TID_COUNT; + + tl = &lq_data->load[tid]; + + curr_time -= curr_time % TID_ROUND_VALUE; + + /* Happens only for the first packet. Initialize the data */ + if (!(tl->queue_count)) { + tl->total = 1; + tl->time_stamp = curr_time; + tl->queue_count = 1; + tl->head = 0; + tl->packet_count[0] = 1; + return MAX_TID_COUNT; + } + + time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); + index = time_diff / TID_QUEUE_CELL_SPACING; + + /* The history is too long: remove data that is older than */ + /* TID_MAX_TIME_DIFF */ + if (index >= TID_QUEUE_MAX_SIZE) + rs_tl_rm_old_stats(tl, curr_time); + + index = (tl->head + index) % TID_QUEUE_MAX_SIZE; + tl->packet_count[index] = tl->packet_count[index] + 1; + tl->total = tl->total + 1; + + if ((index + 1) > tl->queue_count) + tl->queue_count = index + 1; + + return tid; +} + +/* + get the traffic load value for tid +*/ +static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid) +{ + u32 curr_time = jiffies_to_msecs(jiffies); + u32 time_diff; + s32 index; + struct iwl_traffic_load *tl = NULL; + + if (tid >= TID_MAX_LOAD_COUNT) + return 0; + + tl = &(lq_data->load[tid]); + + curr_time -= curr_time % TID_ROUND_VALUE; + + if (!(tl->queue_count)) + return 0; + + time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); + index = time_diff / TID_QUEUE_CELL_SPACING; + + /* The history is too long: remove data that is older than */ + /* TID_MAX_TIME_DIFF */ + if (index >= TID_QUEUE_MAX_SIZE) + rs_tl_rm_old_stats(tl, curr_time); + + return tl->total; +} + +static void rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, + struct iwl_lq_sta *lq_data, u8 tid, + struct sta_info *sta) +{ + unsigned long state; + DECLARE_MAC_BUF(mac); + + spin_lock_bh(&sta->lock); + state = sta->ampdu_mlme.tid_state_tx[tid]; + spin_unlock_bh(&sta->lock); + + if (state == HT_AGG_STATE_IDLE && + rs_tl_get_load(lq_data, tid) > IWL_AGG_LOAD_THRESHOLD) { + IWL_DEBUG_HT("Starting Tx agg: STA: %s tid: %d\n", + print_mac(mac, sta->addr), tid); + ieee80211_start_tx_ba_session(priv->hw, sta->addr, tid); + } +} + +static void rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid, + struct iwl_lq_sta *lq_data, + struct sta_info *sta) +{ + if ((tid < TID_MAX_LOAD_COUNT)) + rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); + else if (tid == IWL_AGG_ALL_TID) + for (tid = 0; tid < TID_MAX_LOAD_COUNT; tid++) + rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); +} + +static inline int get_num_of_ant_from_rate(u32 rate_n_flags) +{ + return (!!(rate_n_flags & RATE_MCS_ANT_A_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_C_MSK)); +} + +/** + * rs_collect_tx_data - Update the success/failure sliding window + * + * We keep a sliding window of the last 62 packets transmitted + * at this rate. window->data contains the bitmask of successful + * packets. + */ +static int rs_collect_tx_data(struct iwl_rate_scale_data *windows, + int scale_index, s32 tpt, int retries, + int successes) +{ + struct iwl_rate_scale_data *window = NULL; + static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); + s32 fail_count; + + if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) + return -EINVAL; + + /* Select data for current tx bit rate */ + window = &(windows[scale_index]); + + /* + * Keep track of only the latest 62 tx frame attempts in this rate's + * history window; anything older isn't really relevant any more. + * If we have filled up the sliding window, drop the oldest attempt; + * if the oldest attempt (highest bit in bitmap) shows "success", + * subtract "1" from the success counter (this is the main reason + * we keep these bitmaps!). + */ + while (retries > 0) { + if (window->counter >= IWL_RATE_MAX_WINDOW) { + + /* remove earliest */ + window->counter = IWL_RATE_MAX_WINDOW - 1; + + if (window->data & mask) { + window->data &= ~mask; + window->success_counter--; + } + } + + /* Increment frames-attempted counter */ + window->counter++; + + /* Shift bitmap by one frame (throw away oldest history), + * OR in "1", and increment "success" if this + * frame was successful. */ + window->data <<= 1;; + if (successes > 0) { + window->success_counter++; + window->data |= 0x1; + successes--; + } + + retries--; + } + + /* Calculate current success ratio, avoid divide-by-0! */ + if (window->counter > 0) + window->success_ratio = 128 * (100 * window->success_counter) + / window->counter; + else + window->success_ratio = IWL_INVALID_VALUE; + + fail_count = window->counter - window->success_counter; + + /* Calculate average throughput, if we have enough history. */ + if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) || + (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH)) + window->average_tpt = (window->success_ratio * tpt + 64) / 128; + else + window->average_tpt = IWL_INVALID_VALUE; + + /* Tag this window as having been updated */ + window->stamp = jiffies; + + return 0; +} + +/* + * Fill uCode API rate_n_flags field, based on "search" or "active" table. + */ +/* FIXME:RS:remove this function and put the flags statically in the table */ +static u32 rate_n_flags_from_tbl(struct iwl_scale_tbl_info *tbl, + int index, u8 use_green) +{ + u32 rate_n_flags = 0; + + if (is_legacy(tbl->lq_type)) { + rate_n_flags = iwl_rates[index].plcp; + if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) + rate_n_flags |= RATE_MCS_CCK_MSK; + + } else if (is_Ht(tbl->lq_type)) { + if (index > IWL_LAST_OFDM_RATE) { + IWL_ERROR("invalid HT rate index %d\n", index); + index = IWL_LAST_OFDM_RATE; + } + rate_n_flags = RATE_MCS_HT_MSK; + + if (is_siso(tbl->lq_type)) + rate_n_flags |= iwl_rates[index].plcp_siso; + else if (is_mimo2(tbl->lq_type)) + rate_n_flags |= iwl_rates[index].plcp_mimo2; + else + rate_n_flags |= iwl_rates[index].plcp_mimo3; + } else { + IWL_ERROR("Invalid tbl->lq_type %d\n", tbl->lq_type); + } + + rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & + RATE_MCS_ANT_ABC_MSK); + + if (is_Ht(tbl->lq_type)) { + if (tbl->is_fat) { + if (tbl->is_dup) + rate_n_flags |= RATE_MCS_DUP_MSK; + else + rate_n_flags |= RATE_MCS_FAT_MSK; + } + if (tbl->is_SGI) + rate_n_flags |= RATE_MCS_SGI_MSK; + + if (use_green) { + rate_n_flags |= RATE_MCS_GF_MSK; + if (is_siso(tbl->lq_type) && tbl->is_SGI) { + rate_n_flags &= ~RATE_MCS_SGI_MSK; + IWL_ERROR("GF was set with SGI:SISO\n"); + } + } + } + return rate_n_flags; +} + +/* + * Interpret uCode API's rate_n_flags format, + * fill "search" or "active" tx mode table. + */ +static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, + enum ieee80211_band band, + struct iwl_scale_tbl_info *tbl, + int *rate_idx) +{ + u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); + u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags); + u8 mcs; + + *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); + + if (*rate_idx == IWL_RATE_INVALID) { + *rate_idx = -1; + return -EINVAL; + } + tbl->is_SGI = 0; /* default legacy setup */ + tbl->is_fat = 0; + tbl->is_dup = 0; + tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); + tbl->lq_type = LQ_NONE; + + /* legacy rate format */ + if (!(rate_n_flags & RATE_MCS_HT_MSK)) { + if (num_of_ant == 1) { + if (band == IEEE80211_BAND_5GHZ) + tbl->lq_type = LQ_A; + else + tbl->lq_type = LQ_G; + } + /* HT rate format */ + } else { + if (rate_n_flags & RATE_MCS_SGI_MSK) + tbl->is_SGI = 1; + + if ((rate_n_flags & RATE_MCS_FAT_MSK) || + (rate_n_flags & RATE_MCS_DUP_MSK)) + tbl->is_fat = 1; + + if (rate_n_flags & RATE_MCS_DUP_MSK) + tbl->is_dup = 1; + + mcs = rs_extract_rate(rate_n_flags); + + /* SISO */ + if (mcs <= IWL_RATE_SISO_60M_PLCP) { + if (num_of_ant == 1) + tbl->lq_type = LQ_SISO; /*else NONE*/ + /* MIMO2 */ + } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) { + if (num_of_ant == 2) + tbl->lq_type = LQ_MIMO2; + /* MIMO3 */ + } else { + if (num_of_ant == 3) + tbl->lq_type = LQ_MIMO3; + } + } + return 0; +} + +/* switch to another antenna/antennas and return 1 */ +/* if no other valid antenna found, return 0 */ +static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, + struct iwl_scale_tbl_info *tbl) +{ + u8 new_ant_type; + + if (!tbl->ant_type || tbl->ant_type > ANT_ABC) + return 0; + + if (!rs_is_valid_ant(valid_ant, tbl->ant_type)) + return 0; + + new_ant_type = ant_toggle_lookup[tbl->ant_type]; + + while ((new_ant_type != tbl->ant_type) && + !rs_is_valid_ant(valid_ant, new_ant_type)) + new_ant_type = ant_toggle_lookup[new_ant_type]; + + if (new_ant_type == tbl->ant_type) + return 0; + + tbl->ant_type = new_ant_type; + *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; + *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; + return 1; +} + +/* FIXME:RS: in 4965 we don't use greenfield at all */ +/* FIXME:RS: don't use greenfield for now in TX */ +#if 0 +static inline u8 rs_use_green(struct iwl_priv *priv, struct ieee80211_conf *conf) +{ + return ((conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) && + priv->current_ht_config.is_green_field && + !priv->current_ht_config.non_GF_STA_present); +} +#endif +static inline u8 rs_use_green(struct iwl_priv *priv, struct ieee80211_conf *conf) +{ + return 0; +} + +/** + * rs_get_supported_rates - get the available rates + * + * if management frame or broadcast frame only return + * basic available rates. + * + */ +static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, + struct ieee80211_hdr *hdr, + enum iwl_table_type rate_type) +{ + if (hdr && is_multicast_ether_addr(hdr->addr1) && + lq_sta->active_rate_basic) + return lq_sta->active_rate_basic; + + if (is_legacy(rate_type)) { + return lq_sta->active_legacy_rate; + } else { + if (is_siso(rate_type)) + return lq_sta->active_siso_rate; + else if (is_mimo2(rate_type)) + return lq_sta->active_mimo2_rate; + else + return lq_sta->active_mimo3_rate; + } +} + +static u16 rs_get_adjacent_rate(struct iwl_priv *priv, u8 index, u16 rate_mask, + int rate_type) +{ + u8 high = IWL_RATE_INVALID; + u8 low = IWL_RATE_INVALID; + + /* 802.11A or ht walks to the next literal adjacent rate in + * the rate table */ + if (is_a_band(rate_type) || !is_legacy(rate_type)) { + int i; + u32 mask; + + /* Find the previous rate that is in the rate mask */ + i = index - 1; + for (mask = (1 << i); i >= 0; i--, mask >>= 1) { + if (rate_mask & mask) { + low = i; + break; + } + } + + /* Find the next rate that is in the rate mask */ + i = index + 1; + for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { + if (rate_mask & mask) { + high = i; + break; + } + } + + return (high << 8) | low; + } + + low = index; + while (low != IWL_RATE_INVALID) { + low = iwl_rates[low].prev_rs; + if (low == IWL_RATE_INVALID) + break; + if (rate_mask & (1 << low)) + break; + IWL_DEBUG_RATE("Skipping masked lower rate: %d\n", low); + } + + high = index; + while (high != IWL_RATE_INVALID) { + high = iwl_rates[high].next_rs; + if (high == IWL_RATE_INVALID) + break; + if (rate_mask & (1 << high)) + break; + IWL_DEBUG_RATE("Skipping masked higher rate: %d\n", high); + } + + return (high << 8) | low; +} + +static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + u8 scale_index, u8 ht_possible) +{ + s32 low; + u16 rate_mask; + u16 high_low; + u8 switch_to_legacy = 0; + u8 is_green = lq_sta->is_green; + + /* check if we need to switch from HT to legacy rates. + * assumption is that mandatory rates (1Mbps or 6Mbps) + * are always supported (spec demand) */ + if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) { + switch_to_legacy = 1; + scale_index = rs_ht_to_legacy[scale_index]; + if (lq_sta->band == IEEE80211_BAND_5GHZ) + tbl->lq_type = LQ_A; + else + tbl->lq_type = LQ_G; + + if (num_of_ant(tbl->ant_type) > 1) + tbl->ant_type = ANT_A;/*FIXME:RS*/ + + tbl->is_fat = 0; + tbl->is_SGI = 0; + } + + rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); + + /* Mask with station rate restriction */ + if (is_legacy(tbl->lq_type)) { + /* supp_rates has no CCK bits in A mode */ + if (lq_sta->band == IEEE80211_BAND_5GHZ) + rate_mask = (u16)(rate_mask & + (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); + else + rate_mask = (u16)(rate_mask & lq_sta->supp_rates); + } + + /* If we switched from HT to legacy, check current rate */ + if (switch_to_legacy && (rate_mask & (1 << scale_index))) { + low = scale_index; + goto out; + } + + high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask, + tbl->lq_type); + low = high_low & 0xff; + + if (low == IWL_RATE_INVALID) + low = scale_index; + +out: + return rate_n_flags_from_tbl(tbl, low, is_green); +} + +/* + * mac80211 sends us Tx status + */ +static void rs_tx_status(void *priv_rate, struct net_device *dev, + struct sk_buff *skb) +{ + int status; + u8 retries; + int rs_index, index = 0; + struct iwl_lq_sta *lq_sta; + struct iwl_link_quality_cmd *table; + struct sta_info *sta; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct iwl_priv *priv = (struct iwl_priv *)priv_rate; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw *hw = local_to_hw(local); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_rate_scale_data *window = NULL; + struct iwl_rate_scale_data *search_win = NULL; + u32 tx_rate; + struct iwl_scale_tbl_info tbl_type; + struct iwl_scale_tbl_info *curr_tbl, *search_tbl; + u8 active_index = 0; + __le16 fc = hdr->frame_control; + s32 tpt = 0; + + IWL_DEBUG_RATE_LIMIT("get frame ack response, update rate scale window\n"); + + if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) + return; + + /* This packet was aggregated but doesn't carry rate scale info */ + if ((info->flags & IEEE80211_TX_CTL_AMPDU) && + !(info->flags & IEEE80211_TX_STAT_AMPDU)) + return; + + retries = info->status.retry_count; + + if (retries > 15) + retries = 15; + + rcu_read_lock(); + + sta = sta_info_get(local, hdr->addr1); + + if (!sta || !sta->rate_ctrl_priv) + goto out; + + + lq_sta = (struct iwl_lq_sta *)sta->rate_ctrl_priv; + + if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && + !lq_sta->ibss_sta_added) + goto out; + + table = &lq_sta->lq; + active_index = lq_sta->active_tbl; + + curr_tbl = &(lq_sta->lq_info[active_index]); + search_tbl = &(lq_sta->lq_info[(1 - active_index)]); + window = (struct iwl_rate_scale_data *)&(curr_tbl->win[0]); + search_win = (struct iwl_rate_scale_data *)&(search_tbl->win[0]); + + /* + * Ignore this Tx frame response if its initial rate doesn't match + * that of latest Link Quality command. There may be stragglers + * from a previous Link Quality command, but we're no longer interested + * in those; they're either from the "active" mode while we're trying + * to check "search" mode, or a prior "search" mode after we've moved + * to a new "search" mode (which might become the new "active" mode). + */ + tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); + rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); + if (priv->band == IEEE80211_BAND_5GHZ) + rs_index -= IWL_FIRST_OFDM_RATE; + + if ((info->tx_rate_idx < 0) || + (tbl_type.is_SGI ^ + !!(info->flags & IEEE80211_TX_CTL_SHORT_GI)) || + (tbl_type.is_fat ^ + !!(info->flags & IEEE80211_TX_CTL_40_MHZ_WIDTH)) || + (tbl_type.is_dup ^ + !!(info->flags & IEEE80211_TX_CTL_DUP_DATA)) || + (tbl_type.ant_type ^ info->antenna_sel_tx) || + (!!(tx_rate & RATE_MCS_HT_MSK) ^ + !!(info->flags & IEEE80211_TX_CTL_OFDM_HT)) || + (!!(tx_rate & RATE_MCS_GF_MSK) ^ + !!(info->flags & IEEE80211_TX_CTL_GREEN_FIELD)) || + (hw->wiphy->bands[priv->band]->bitrates[rs_index].bitrate != + hw->wiphy->bands[info->band]->bitrates[info->tx_rate_idx].bitrate)) { + IWL_DEBUG_RATE("initial rate does not match 0x%x\n", tx_rate); + goto out; + } + + /* Update frame history window with "failure" for each Tx retry. */ + while (retries) { + /* Look up the rate and other info used for each tx attempt. + * Each tx attempt steps one entry deeper in the rate table. */ + tx_rate = le32_to_cpu(table->rs_table[index].rate_n_flags); + rs_get_tbl_info_from_mcs(tx_rate, priv->band, + &tbl_type, &rs_index); + + /* If type matches "search" table, + * add failure to "search" history */ + if ((tbl_type.lq_type == search_tbl->lq_type) && + (tbl_type.ant_type == search_tbl->ant_type) && + (tbl_type.is_SGI == search_tbl->is_SGI)) { + if (search_tbl->expected_tpt) + tpt = search_tbl->expected_tpt[rs_index]; + else + tpt = 0; + rs_collect_tx_data(search_win, rs_index, tpt, 1, 0); + + /* Else if type matches "current/active" table, + * add failure to "current/active" history */ + } else if ((tbl_type.lq_type == curr_tbl->lq_type) && + (tbl_type.ant_type == curr_tbl->ant_type) && + (tbl_type.is_SGI == curr_tbl->is_SGI)) { + if (curr_tbl->expected_tpt) + tpt = curr_tbl->expected_tpt[rs_index]; + else + tpt = 0; + rs_collect_tx_data(window, rs_index, tpt, 1, 0); + } + + /* If not searching for a new mode, increment failed counter + * ... this helps determine when to start searching again */ + if (lq_sta->stay_in_tbl) + lq_sta->total_failed++; + --retries; + index++; + + } + + /* + * Find (by rate) the history window to update with final Tx attempt; + * if Tx was successful first try, use original rate, + * else look up the rate that was, finally, successful. + */ + tx_rate = le32_to_cpu(table->rs_table[index].rate_n_flags); + rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, &rs_index); + + /* Update frame history window with "success" if Tx got ACKed ... */ + status = !!(info->flags & IEEE80211_TX_STAT_ACK); + + /* If type matches "search" table, + * add final tx status to "search" history */ + if ((tbl_type.lq_type == search_tbl->lq_type) && + (tbl_type.ant_type == search_tbl->ant_type) && + (tbl_type.is_SGI == search_tbl->is_SGI)) { + if (search_tbl->expected_tpt) + tpt = search_tbl->expected_tpt[rs_index]; + else + tpt = 0; + if (info->flags & IEEE80211_TX_CTL_AMPDU) + rs_collect_tx_data(search_win, rs_index, tpt, + info->status.ampdu_ack_len, + info->status.ampdu_ack_map); + else + rs_collect_tx_data(search_win, rs_index, tpt, + 1, status); + /* Else if type matches "current/active" table, + * add final tx status to "current/active" history */ + } else if ((tbl_type.lq_type == curr_tbl->lq_type) && + (tbl_type.ant_type == curr_tbl->ant_type) && + (tbl_type.is_SGI == curr_tbl->is_SGI)) { + if (curr_tbl->expected_tpt) + tpt = curr_tbl->expected_tpt[rs_index]; + else + tpt = 0; + if (info->flags & IEEE80211_TX_CTL_AMPDU) + rs_collect_tx_data(window, rs_index, tpt, + info->status.ampdu_ack_len, + info->status.ampdu_ack_map); + else + rs_collect_tx_data(window, rs_index, tpt, + 1, status); + } + + /* If not searching for new mode, increment success/failed counter + * ... these help determine when to start searching again */ + if (lq_sta->stay_in_tbl) { + if (info->flags & IEEE80211_TX_CTL_AMPDU) { + lq_sta->total_success += info->status.ampdu_ack_map; + lq_sta->total_failed += + (info->status.ampdu_ack_len - info->status.ampdu_ack_map); + } else { + if (status) + lq_sta->total_success++; + else + lq_sta->total_failed++; + } + } + + /* See if there's a better rate or modulation mode to try. */ + rs_rate_scale_perform(priv, dev, hdr, sta); +out: + rcu_read_unlock(); + return; +} + +/* + * Begin a period of staying with a selected modulation mode. + * Set "stay_in_tbl" flag to prevent any mode switches. + * Set frame tx success limits according to legacy vs. high-throughput, + * and reset overall (spanning all rates) tx success history statistics. + * These control how long we stay using same modulation mode before + * searching for a new mode. + */ +static void rs_set_stay_in_table(struct iwl_priv *priv, u8 is_legacy, + struct iwl_lq_sta *lq_sta) +{ + IWL_DEBUG_RATE("we are staying in the same table\n"); + lq_sta->stay_in_tbl = 1; /* only place this gets set */ + if (is_legacy) { + lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT; + } else { + lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT; + } + lq_sta->table_count = 0; + lq_sta->total_failed = 0; + lq_sta->total_success = 0; +} + +/* + * Find correct throughput table for given mode of modulation + */ +static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl) +{ + if (is_legacy(tbl->lq_type)) { + if (!is_a_band(tbl->lq_type)) + tbl->expected_tpt = expected_tpt_G; + else + tbl->expected_tpt = expected_tpt_A; + } else if (is_siso(tbl->lq_type)) { + if (tbl->is_fat && !lq_sta->is_dup) + if (tbl->is_SGI) + tbl->expected_tpt = expected_tpt_siso40MHzSGI; + else + tbl->expected_tpt = expected_tpt_siso40MHz; + else if (tbl->is_SGI) + tbl->expected_tpt = expected_tpt_siso20MHzSGI; + else + tbl->expected_tpt = expected_tpt_siso20MHz; + + } else if (is_mimo(tbl->lq_type)) { /* FIXME:need to separate mimo2/3 */ + if (tbl->is_fat && !lq_sta->is_dup) + if (tbl->is_SGI) + tbl->expected_tpt = expected_tpt_mimo40MHzSGI; + else + tbl->expected_tpt = expected_tpt_mimo40MHz; + else if (tbl->is_SGI) + tbl->expected_tpt = expected_tpt_mimo20MHzSGI; + else + tbl->expected_tpt = expected_tpt_mimo20MHz; + } else + tbl->expected_tpt = expected_tpt_G; +} + +/* + * Find starting rate for new "search" high-throughput mode of modulation. + * Goal is to find lowest expected rate (under perfect conditions) that is + * above the current measured throughput of "active" mode, to give new mode + * a fair chance to prove itself without too many challenges. + * + * This gets called when transitioning to more aggressive modulation + * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive + * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need + * to decrease to match "active" throughput. When moving from MIMO to SISO, + * bit rate will typically need to increase, but not if performance was bad. + */ +static s32 rs_get_best_rate(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, /* "search" */ + u16 rate_mask, s8 index) +{ + /* "active" values */ + struct iwl_scale_tbl_info *active_tbl = + &(lq_sta->lq_info[lq_sta->active_tbl]); + s32 active_sr = active_tbl->win[index].success_ratio; + s32 active_tpt = active_tbl->expected_tpt[index]; + + /* expected "search" throughput */ + s32 *tpt_tbl = tbl->expected_tpt; + + s32 new_rate, high, low, start_hi; + u16 high_low; + s8 rate = index; + + new_rate = high = low = start_hi = IWL_RATE_INVALID; + + for (; ;) { + high_low = rs_get_adjacent_rate(priv, rate, rate_mask, + tbl->lq_type); + + low = high_low & 0xff; + high = (high_low >> 8) & 0xff; + + /* + * Lower the "search" bit rate, to give new "search" mode + * approximately the same throughput as "active" if: + * + * 1) "Active" mode has been working modestly well (but not + * great), and expected "search" throughput (under perfect + * conditions) at candidate rate is above the actual + * measured "active" throughput (but less than expected + * "active" throughput under perfect conditions). + * OR + * 2) "Active" mode has been working perfectly or very well + * and expected "search" throughput (under perfect + * conditions) at candidate rate is above expected + * "active" throughput (under perfect conditions). + */ + if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) && + ((active_sr > IWL_RATE_DECREASE_TH) && + (active_sr <= IWL_RATE_HIGH_TH) && + (tpt_tbl[rate] <= active_tpt))) || + ((active_sr >= IWL_RATE_SCALE_SWITCH) && + (tpt_tbl[rate] > active_tpt))) { + + /* (2nd or later pass) + * If we've already tried to raise the rate, and are + * now trying to lower it, use the higher rate. */ + if (start_hi != IWL_RATE_INVALID) { + new_rate = start_hi; + break; + } + + new_rate = rate; + + /* Loop again with lower rate */ + if (low != IWL_RATE_INVALID) + rate = low; + + /* Lower rate not available, use the original */ + else + break; + + /* Else try to raise the "search" rate to match "active" */ + } else { + /* (2nd or later pass) + * If we've already tried to lower the rate, and are + * now trying to raise it, use the lower rate. */ + if (new_rate != IWL_RATE_INVALID) + break; + + /* Loop again with higher rate */ + else if (high != IWL_RATE_INVALID) { + start_hi = high; + rate = high; + + /* Higher rate not available, use the original */ + } else { + break; + } + } + } + + return new_rate; +} + +/* + * Set up search table for MIMO + */ +static int rs_switch_to_mimo2(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct sta_info *sta, + struct iwl_scale_tbl_info *tbl, int index) +{ + u16 rate_mask; + s32 rate; + s8 is_green = lq_sta->is_green; + + if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) || + !sta->ht_info.ht_supported) + return -1; + + if (priv->current_ht_config.tx_mimo_ps_mode == IWL_MIMO_PS_STATIC) + return -1; + + /* Need both Tx chains/antennas to support MIMO */ + if (priv->hw_params.tx_chains_num < 2) + return -1; + + IWL_DEBUG_RATE("LQ: try to switch to MIMO2\n"); + + tbl->lq_type = LQ_MIMO2; + tbl->is_dup = lq_sta->is_dup; + tbl->action = 0; + rate_mask = lq_sta->active_mimo2_rate; + + if (priv->current_ht_config.supported_chan_width + == IWL_CHANNEL_WIDTH_40MHZ) + tbl->is_fat = 1; + else + tbl->is_fat = 0; + + /* FIXME: - don't toggle SGI here + if (tbl->is_fat) { + if (priv->current_ht_config.sgf & HT_SHORT_GI_40MHZ_ONLY) + tbl->is_SGI = 1; + else + tbl->is_SGI = 0; + } else if (priv->current_ht_config.sgf & HT_SHORT_GI_20MHZ_ONLY) + tbl->is_SGI = 1; + else + tbl->is_SGI = 0; + */ + + rs_set_expected_tpt_table(lq_sta, tbl); + + rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); + + IWL_DEBUG_RATE("LQ: MIMO2 best rate %d mask %X\n", rate, rate_mask); + + if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { + IWL_DEBUG_RATE("Can't switch with index %d rate mask %x\n", + rate, rate_mask); + return -1; + } + tbl->current_rate = rate_n_flags_from_tbl(tbl, rate, is_green); + + IWL_DEBUG_RATE("LQ: Switch to new mcs %X index is green %X\n", + tbl->current_rate, is_green); + return 0; +} + +/* + * Set up search table for SISO + */ +static int rs_switch_to_siso(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct sta_info *sta, + struct iwl_scale_tbl_info *tbl, int index) +{ + u16 rate_mask; + u8 is_green = lq_sta->is_green; + s32 rate; + + if (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) || + !sta->ht_info.ht_supported) + return -1; + + IWL_DEBUG_RATE("LQ: try to switch to SISO\n"); + + tbl->is_dup = lq_sta->is_dup; + tbl->lq_type = LQ_SISO; + tbl->action = 0; + rate_mask = lq_sta->active_siso_rate; + + if (priv->current_ht_config.supported_chan_width + == IWL_CHANNEL_WIDTH_40MHZ) + tbl->is_fat = 1; + else + tbl->is_fat = 0; + + /* FIXME: - don't toggle SGI here + if (tbl->is_fat) { + if (priv->current_ht_config.sgf & HT_SHORT_GI_40MHZ_ONLY) + tbl->is_SGI = 1; + else + tbl->is_SGI = 0; + } else if (priv->current_ht_config.sgf & HT_SHORT_GI_20MHZ_ONLY) + tbl->is_SGI = 1; + else + tbl->is_SGI = 0; + */ + + if (is_green) + tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/ + + rs_set_expected_tpt_table(lq_sta, tbl); + rate = rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); + + IWL_DEBUG_RATE("LQ: get best rate %d mask %X\n", rate, rate_mask); + if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { + IWL_DEBUG_RATE("can not switch with index %d rate mask %x\n", + rate, rate_mask); + return -1; + } + tbl->current_rate = rate_n_flags_from_tbl(tbl, rate, is_green); + IWL_DEBUG_RATE("LQ: Switch to new mcs %X index is green %X\n", + tbl->current_rate, is_green); + return 0; +} + +/* + * Try to switch to new modulation mode from legacy + */ +static int rs_move_legacy_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct sta_info *sta, + int index) +{ + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct iwl_rate_scale_data *window = &(tbl->win[index]); + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action = tbl->action; + u8 valid_tx_ant = priv->hw_params.valid_tx_ant; + int ret = 0; + + for (; ;) { + switch (tbl->action) { + case IWL_LEGACY_SWITCH_ANTENNA: + IWL_DEBUG_RATE("LQ: Legacy toggle Antenna\n"); + + lq_sta->action_counter++; + + /* Don't change antenna if success has been great */ + if (window->success_ratio >= IWL_RS_GOOD_RATIO) + break; + + /* Set up search table to try other antenna */ + memcpy(search_tbl, tbl, sz); + + if (rs_toggle_antenna(valid_tx_ant, + &search_tbl->current_rate, search_tbl)) { + lq_sta->search_better_tbl = 1; + goto out; + } + break; + case IWL_LEGACY_SWITCH_SISO: + IWL_DEBUG_RATE("LQ: Legacy switch to SISO\n"); + + /* Set up search table to try SISO */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + ret = rs_switch_to_siso(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) { + lq_sta->search_better_tbl = 1; + lq_sta->action_counter = 0; + goto out; + } + + break; + case IWL_LEGACY_SWITCH_MIMO2: + IWL_DEBUG_RATE("LQ: Legacy switch to MIMO2\n"); + + /* Set up search table to try MIMO */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + search_tbl->ant_type = ANT_AB;/*FIXME:RS*/ + /*FIXME:RS:need to check ant validity*/ + ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) { + lq_sta->search_better_tbl = 1; + lq_sta->action_counter = 0; + goto out; + } + break; + } + tbl->action++; + if (tbl->action > IWL_LEGACY_SWITCH_MIMO2) + tbl->action = IWL_LEGACY_SWITCH_ANTENNA; + + if (tbl->action == start_action) + break; + + } + return 0; + + out: + tbl->action++; + if (tbl->action > IWL_LEGACY_SWITCH_MIMO2) + tbl->action = IWL_LEGACY_SWITCH_ANTENNA; + return 0; + +} + +/* + * Try to switch to new modulation mode from SISO + */ +static int rs_move_siso_to_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct sta_info *sta, int index) +{ + u8 is_green = lq_sta->is_green; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct iwl_rate_scale_data *window = &(tbl->win[index]); + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action = tbl->action; + u8 valid_tx_ant = priv->hw_params.valid_tx_ant; + int ret; + + for (;;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IWL_SISO_SWITCH_ANTENNA: + IWL_DEBUG_RATE("LQ: SISO toggle Antenna\n"); + if (window->success_ratio >= IWL_RS_GOOD_RATIO) + break; + + memcpy(search_tbl, tbl, sz); + if (rs_toggle_antenna(valid_tx_ant, + &search_tbl->current_rate, search_tbl)) { + lq_sta->search_better_tbl = 1; + goto out; + } + break; + case IWL_SISO_SWITCH_MIMO2: + IWL_DEBUG_RATE("LQ: SISO switch to MIMO2\n"); + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + search_tbl->ant_type = ANT_AB; /*FIXME:RS*/ + ret = rs_switch_to_mimo2(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) { + lq_sta->search_better_tbl = 1; + goto out; + } + break; + case IWL_SISO_SWITCH_GI: + if (!tbl->is_fat && + !(priv->current_ht_config.sgf & + HT_SHORT_GI_20MHZ)) + break; + if (tbl->is_fat && + !(priv->current_ht_config.sgf & + HT_SHORT_GI_40MHZ)) + break; + + IWL_DEBUG_RATE("LQ: SISO toggle SGI/NGI\n"); + + memcpy(search_tbl, tbl, sz); + if (is_green) { + if (!tbl->is_SGI) + break; + else + IWL_ERROR("SGI was set in GF+SISO\n"); + } + search_tbl->is_SGI = !tbl->is_SGI; + rs_set_expected_tpt_table(lq_sta, search_tbl); + if (tbl->is_SGI) { + s32 tpt = lq_sta->last_tpt / 100; + if (tpt >= search_tbl->expected_tpt[index]) + break; + } + search_tbl->current_rate = rate_n_flags_from_tbl( + search_tbl, index, is_green); + lq_sta->search_better_tbl = 1; + goto out; + } + tbl->action++; + if (tbl->action > IWL_SISO_SWITCH_GI) + tbl->action = IWL_SISO_SWITCH_ANTENNA; + + if (tbl->action == start_action) + break; + } + return 0; + + out: + tbl->action++; + if (tbl->action > IWL_SISO_SWITCH_GI) + tbl->action = IWL_SISO_SWITCH_ANTENNA; + return 0; +} + +/* + * Try to switch to new modulation mode from MIMO + */ +static int rs_move_mimo_to_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct sta_info *sta, int index) +{ + s8 is_green = lq_sta->is_green; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action = tbl->action; + /*u8 valid_tx_ant = priv->hw_params.valid_tx_ant;*/ + int ret; + + for (;;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IWL_MIMO_SWITCH_ANTENNA_A: + case IWL_MIMO_SWITCH_ANTENNA_B: + IWL_DEBUG_RATE("LQ: MIMO2 switch to SISO\n"); + + /* Set up new search table for SISO */ + memcpy(search_tbl, tbl, sz); + + /*FIXME:RS:need to check ant validity + C*/ + if (tbl->action == IWL_MIMO_SWITCH_ANTENNA_A) + search_tbl->ant_type = ANT_A; + else + search_tbl->ant_type = ANT_B; + + ret = rs_switch_to_siso(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) { + lq_sta->search_better_tbl = 1; + goto out; + } + break; + + case IWL_MIMO_SWITCH_GI: + if (!tbl->is_fat && + !(priv->current_ht_config.sgf & + HT_SHORT_GI_20MHZ)) + break; + if (tbl->is_fat && + !(priv->current_ht_config.sgf & + HT_SHORT_GI_40MHZ)) + break; + + IWL_DEBUG_RATE("LQ: MIMO toggle SGI/NGI\n"); + + /* Set up new search table for MIMO */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = !tbl->is_SGI; + rs_set_expected_tpt_table(lq_sta, search_tbl); + /* + * If active table already uses the fastest possible + * modulation (dual stream with short guard interval), + * and it's working well, there's no need to look + * for a better type of modulation! + */ + if (tbl->is_SGI) { + s32 tpt = lq_sta->last_tpt / 100; + if (tpt >= search_tbl->expected_tpt[index]) + break; + } + search_tbl->current_rate = rate_n_flags_from_tbl( + search_tbl, index, is_green); + lq_sta->search_better_tbl = 1; + goto out; + + } + tbl->action++; + if (tbl->action > IWL_MIMO_SWITCH_GI) + tbl->action = IWL_MIMO_SWITCH_ANTENNA_A; + + if (tbl->action == start_action) + break; + } + + return 0; + out: + tbl->action++; + if (tbl->action > IWL_MIMO_SWITCH_GI) + tbl->action = IWL_MIMO_SWITCH_ANTENNA_A; + return 0; + +} + +/* + * Check whether we should continue using same modulation mode, or + * begin search for a new mode, based on: + * 1) # tx successes or failures while using this mode + * 2) # times calling this function + * 3) elapsed time in this mode (not used, for now) + */ +static void rs_stay_in_table(struct iwl_lq_sta *lq_sta) +{ + struct iwl_scale_tbl_info *tbl; + int i; + int active_tbl; + int flush_interval_passed = 0; + struct iwl_priv *priv; + + priv = lq_sta->drv; + active_tbl = lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + + /* If we've been disallowing search, see if we should now allow it */ + if (lq_sta->stay_in_tbl) { + + /* Elapsed time using current modulation mode */ + if (lq_sta->flush_timer) + flush_interval_passed = + time_after(jiffies, + (unsigned long)(lq_sta->flush_timer + + IWL_RATE_SCALE_FLUSH_INTVL)); + + /* + * Check if we should allow search for new modulation mode. + * If many frames have failed or succeeded, or we've used + * this same modulation for a long time, allow search, and + * reset history stats that keep track of whether we should + * allow a new search. Also (below) reset all bitmaps and + * stats in active history. + */ + if ((lq_sta->total_failed > lq_sta->max_failure_limit) || + (lq_sta->total_success > lq_sta->max_success_limit) || + ((!lq_sta->search_better_tbl) && (lq_sta->flush_timer) + && (flush_interval_passed))) { + IWL_DEBUG_RATE("LQ: stay is expired %d %d %d\n:", + lq_sta->total_failed, + lq_sta->total_success, + flush_interval_passed); + + /* Allow search for new mode */ + lq_sta->stay_in_tbl = 0; /* only place reset */ + lq_sta->total_failed = 0; + lq_sta->total_success = 0; + lq_sta->flush_timer = 0; + + /* + * Else if we've used this modulation mode enough repetitions + * (regardless of elapsed time or success/failure), reset + * history bitmaps and rate-specific stats for all rates in + * active table. + */ + } else { + lq_sta->table_count++; + if (lq_sta->table_count >= + lq_sta->table_count_limit) { + lq_sta->table_count = 0; + + IWL_DEBUG_RATE("LQ: stay in table clear win\n"); + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window( + &(tbl->win[i])); + } + } + + /* If transitioning to allow "search", reset all history + * bitmaps and stats in active table (this will become the new + * "search" table). */ + if (!lq_sta->stay_in_tbl) { + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&(tbl->win[i])); + } + } +} + +/* + * Do rate scaling and search for new modulation mode. + */ +static void rs_rate_scale_perform(struct iwl_priv *priv, + struct net_device *dev, + struct ieee80211_hdr *hdr, + struct sta_info *sta) +{ + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_hw *hw = local_to_hw(local); + struct ieee80211_conf *conf = &hw->conf; + int low = IWL_RATE_INVALID; + int high = IWL_RATE_INVALID; + int index; + int i; + struct iwl_rate_scale_data *window = NULL; + int current_tpt = IWL_INVALID_VALUE; + int low_tpt = IWL_INVALID_VALUE; + int high_tpt = IWL_INVALID_VALUE; + u32 fail_count; + s8 scale_action = 0; + __le16 fc; + u16 rate_mask; + u8 update_lq = 0; + struct iwl_lq_sta *lq_sta; + struct iwl_scale_tbl_info *tbl, *tbl1; + u16 rate_scale_index_msk = 0; + u32 rate; + u8 is_green = 0; + u8 active_tbl = 0; + u8 done_search = 0; + u16 high_low; + s32 sr; + u8 tid = MAX_TID_COUNT; + + IWL_DEBUG_RATE("rate scale calculate new rate for skb\n"); + + fc = hdr->frame_control; + if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) { + /* Send management frames and broadcast/multicast data using + * lowest rate. */ + /* TODO: this could probably be improved.. */ + return; + } + + if (!sta || !sta->rate_ctrl_priv) + return; + + lq_sta = (struct iwl_lq_sta *)sta->rate_ctrl_priv; + + tid = rs_tl_add_packet(lq_sta, hdr); + + /* + * Select rate-scale / modulation-mode table to work with in + * the rest of this function: "search" if searching for better + * modulation mode, or "active" if doing rate scaling within a mode. + */ + if (!lq_sta->search_better_tbl) + active_tbl = lq_sta->active_tbl; + else + active_tbl = 1 - lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + is_green = lq_sta->is_green; + + /* current tx rate */ + index = sta->last_txrate_idx; + + IWL_DEBUG_RATE("Rate scale index %d for type %d\n", index, + tbl->lq_type); + + /* rates available for this association, and for modulation mode */ + rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); + + IWL_DEBUG_RATE("mask 0x%04X \n", rate_mask); + + /* mask with station rate restriction */ + if (is_legacy(tbl->lq_type)) { + if (lq_sta->band == IEEE80211_BAND_5GHZ) + /* supp_rates has no CCK bits in A mode */ + rate_scale_index_msk = (u16) (rate_mask & + (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); + else + rate_scale_index_msk = (u16) (rate_mask & + lq_sta->supp_rates); + + } else + rate_scale_index_msk = rate_mask; + + if (!rate_scale_index_msk) + rate_scale_index_msk = rate_mask; + + if (!((1 << index) & rate_scale_index_msk)) { + IWL_ERROR("Current Rate is not valid\n"); + return; + } + + /* Get expected throughput table and history window for current rate */ + if (!tbl->expected_tpt) { + IWL_ERROR("tbl->expected_tpt is NULL\n"); + return; + } + + window = &(tbl->win[index]); + + /* + * If there is not enough history to calculate actual average + * throughput, keep analyzing results of more tx frames, without + * changing rate or mode (bypass most of the rest of this function). + * Set up new rate table in uCode only if old rate is not supported + * in current association (use new rate found above). + */ + fail_count = window->counter - window->success_counter; + if ((fail_count < IWL_RATE_MIN_FAILURE_TH) && + (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) { + IWL_DEBUG_RATE("LQ: still below TH. succ=%d total=%d " + "for index %d\n", + window->success_counter, window->counter, index); + + /* Can't calculate this yet; not enough history */ + window->average_tpt = IWL_INVALID_VALUE; + + /* Should we stay with this modulation mode, + * or search for a new one? */ + rs_stay_in_table(lq_sta); + + goto out; + + /* Else we have enough samples; calculate estimate of + * actual average throughput */ + } else { + /*FIXME:RS remove this else if we don't get this error*/ + if (window->average_tpt != ((window->success_ratio * + tbl->expected_tpt[index] + 64) / 128)) { + IWL_ERROR("expected_tpt should have been calculated" + " by now\n"); + window->average_tpt = ((window->success_ratio * + tbl->expected_tpt[index] + 64) / 128); + } + } + + /* If we are searching for better modulation mode, check success. */ + if (lq_sta->search_better_tbl) { + + /* If good success, continue using the "search" mode; + * no need to send new link quality command, since we're + * continuing to use the setup that we've been trying. */ + if (window->average_tpt > lq_sta->last_tpt) { + + IWL_DEBUG_RATE("LQ: SWITCHING TO CURRENT TABLE " + "suc=%d cur-tpt=%d old-tpt=%d\n", + window->success_ratio, + window->average_tpt, + lq_sta->last_tpt); + + if (!is_legacy(tbl->lq_type)) + lq_sta->enable_counter = 1; + + /* Swap tables; "search" becomes "active" */ + lq_sta->active_tbl = active_tbl; + current_tpt = window->average_tpt; + + /* Else poor success; go back to mode in "active" table */ + } else { + + IWL_DEBUG_RATE("LQ: GOING BACK TO THE OLD TABLE " + "suc=%d cur-tpt=%d old-tpt=%d\n", + window->success_ratio, + window->average_tpt, + lq_sta->last_tpt); + + /* Nullify "search" table */ + tbl->lq_type = LQ_NONE; + + /* Revert to "active" table */ + active_tbl = lq_sta->active_tbl; + tbl = &(lq_sta->lq_info[active_tbl]); + + /* Revert to "active" rate and throughput info */ + index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + current_tpt = lq_sta->last_tpt; + + /* Need to set up a new rate table in uCode */ + update_lq = 1; + } + + /* Either way, we've made a decision; modulation mode + * search is done, allow rate adjustment next time. */ + lq_sta->search_better_tbl = 0; + done_search = 1; /* Don't switch modes below! */ + goto lq_update; + } + + /* (Else) not in search of better modulation mode, try for better + * starting rate, while staying in this mode. */ + high_low = rs_get_adjacent_rate(priv, index, rate_scale_index_msk, + tbl->lq_type); + low = high_low & 0xff; + high = (high_low >> 8) & 0xff; + + sr = window->success_ratio; + + /* Collect measured throughputs for current and adjacent rates */ + current_tpt = window->average_tpt; + if (low != IWL_RATE_INVALID) + low_tpt = tbl->win[low].average_tpt; + if (high != IWL_RATE_INVALID) + high_tpt = tbl->win[high].average_tpt; + + scale_action = 0; + + /* Too many failures, decrease rate */ + if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) { + IWL_DEBUG_RATE("decrease rate because of low success_ratio\n"); + scale_action = -1; + + /* No throughput measured yet for adjacent rates; try increase. */ + } else if ((low_tpt == IWL_INVALID_VALUE) && + (high_tpt == IWL_INVALID_VALUE)) { + + if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) + scale_action = 1; + else if (low != IWL_RATE_INVALID) + scale_action = -1; + } + + /* Both adjacent throughputs are measured, but neither one has better + * throughput; we're using the best rate, don't change it! */ + else if ((low_tpt != IWL_INVALID_VALUE) && + (high_tpt != IWL_INVALID_VALUE) && + (low_tpt < current_tpt) && + (high_tpt < current_tpt)) + scale_action = 0; + + /* At least one adjacent rate's throughput is measured, + * and may have better performance. */ + else { + /* Higher adjacent rate's throughput is measured */ + if (high_tpt != IWL_INVALID_VALUE) { + /* Higher rate has better throughput */ + if (high_tpt > current_tpt && + sr >= IWL_RATE_INCREASE_TH) { + scale_action = 1; + } else { + IWL_DEBUG_RATE + ("decrease rate because of high tpt\n"); + scale_action = -1; + } + + /* Lower adjacent rate's throughput is measured */ + } else if (low_tpt != IWL_INVALID_VALUE) { + /* Lower rate has better throughput */ + if (low_tpt > current_tpt) { + IWL_DEBUG_RATE + ("decrease rate because of low tpt\n"); + scale_action = -1; + } else if (sr >= IWL_RATE_INCREASE_TH) { + scale_action = 1; + } + } + } + + /* Sanity check; asked for decrease, but success rate or throughput + * has been good at old rate. Don't change it. */ + if ((scale_action == -1) && (low != IWL_RATE_INVALID) && + ((sr > IWL_RATE_HIGH_TH) || + (current_tpt > (100 * tbl->expected_tpt[low])))) + scale_action = 0; + + switch (scale_action) { + case -1: + /* Decrease starting rate, update uCode's rate table */ + if (low != IWL_RATE_INVALID) { + update_lq = 1; + index = low; + } + break; + case 1: + /* Increase starting rate, update uCode's rate table */ + if (high != IWL_RATE_INVALID) { + update_lq = 1; + index = high; + } + + break; + case 0: + /* No change */ + default: + break; + } + + IWL_DEBUG_RATE("choose rate scale index %d action %d low %d " + "high %d type %d\n", + index, scale_action, low, high, tbl->lq_type); + +lq_update: + /* Replace uCode's rate table for the destination station. */ + if (update_lq) { + rate = rate_n_flags_from_tbl(tbl, index, is_green); + rs_fill_link_cmd(priv, lq_sta, rate); + iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); + } + + /* Should we stay with this modulation mode, or search for a new one? */ + rs_stay_in_table(lq_sta); + + /* + * Search for new modulation mode if we're: + * 1) Not changing rates right now + * 2) Not just finishing up a search + * 3) Allowing a new search + */ + if (!update_lq && !done_search && !lq_sta->stay_in_tbl && window->counter) { + /* Save current throughput to compare with "search" throughput*/ + lq_sta->last_tpt = current_tpt; + + /* Select a new "search" modulation mode to try. + * If one is found, set up the new "search" table. */ + if (is_legacy(tbl->lq_type)) + rs_move_legacy_other(priv, lq_sta, conf, sta, index); + else if (is_siso(tbl->lq_type)) + rs_move_siso_to_other(priv, lq_sta, conf, sta, index); + else + rs_move_mimo_to_other(priv, lq_sta, conf, sta, index); + + /* If new "search" mode was selected, set up in uCode table */ + if (lq_sta->search_better_tbl) { + /* Access the "search" table, clear its history. */ + tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&(tbl->win[i])); + + /* Use new "search" start rate */ + index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + + IWL_DEBUG_RATE("Switch current mcs: %X index: %d\n", + tbl->current_rate, index); + rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); + iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); + } + + /* If the "active" (non-search) mode was legacy, + * and we've tried switching antennas, + * but we haven't been able to try HT modes (not available), + * stay with best antenna legacy modulation for a while + * before next round of mode comparisons. */ + tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); + if (is_legacy(tbl1->lq_type) && + (!(conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE)) && + (lq_sta->action_counter >= 1)) { + lq_sta->action_counter = 0; + IWL_DEBUG_RATE("LQ: STAY in legacy table\n"); + rs_set_stay_in_table(priv, 1, lq_sta); + } + + /* If we're in an HT mode, and all 3 mode switch actions + * have been tried and compared, stay in this best modulation + * mode for a while before next round of mode comparisons. */ + if (lq_sta->enable_counter && + (lq_sta->action_counter >= IWL_ACTION_LIMIT)) { + if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && + (lq_sta->tx_agg_tid_en & (1 << tid)) && + (tid != MAX_TID_COUNT)) { + IWL_DEBUG_RATE("try to aggregate tid %d\n", tid); + rs_tl_turn_on_agg(priv, tid, lq_sta, sta); + } + lq_sta->action_counter = 0; + rs_set_stay_in_table(priv, 0, lq_sta); + } + + /* + * Else, don't search for a new modulation mode. + * Put new timestamp in stay-in-modulation-mode flush timer if: + * 1) Not changing rates right now + * 2) Not just finishing up a search + * 3) flush timer is empty + */ + } else { + if ((!update_lq) && (!done_search) && (!lq_sta->flush_timer)) + lq_sta->flush_timer = jiffies; + } + +out: + tbl->current_rate = rate_n_flags_from_tbl(tbl, index, is_green); + i = index; + sta->last_txrate_idx = i; + + /* sta->txrate_idx is an index to A mode rates which start + * at IWL_FIRST_OFDM_RATE + */ + if (lq_sta->band == IEEE80211_BAND_5GHZ) + sta->txrate_idx = i - IWL_FIRST_OFDM_RATE; + else + sta->txrate_idx = i; + + return; +} + + +static void rs_initialize_lq(struct iwl_priv *priv, + struct ieee80211_conf *conf, + struct sta_info *sta) +{ + struct iwl_lq_sta *lq_sta; + struct iwl_scale_tbl_info *tbl; + int rate_idx; + int i; + u32 rate; + u8 use_green = rs_use_green(priv, conf); + u8 active_tbl = 0; + u8 valid_tx_ant; + + if (!sta || !sta->rate_ctrl_priv) + goto out; + + lq_sta = (struct iwl_lq_sta *)sta->rate_ctrl_priv; + i = sta->last_txrate_idx; + + if ((lq_sta->lq.sta_id == 0xff) && + (priv->iw_mode == IEEE80211_IF_TYPE_IBSS)) + goto out; + + valid_tx_ant = priv->hw_params.valid_tx_ant; + + if (!lq_sta->search_better_tbl) + active_tbl = lq_sta->active_tbl; + else + active_tbl = 1 - lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + + if ((i < 0) || (i >= IWL_RATE_COUNT)) + i = 0; + + /* FIXME:RS: This is also wrong in 4965 */ + rate = iwl_rates[i].plcp; + rate |= RATE_MCS_ANT_B_MSK; + rate &= ~RATE_MCS_ANT_A_MSK; + + if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE) + rate |= RATE_MCS_CCK_MSK; + + tbl->ant_type = ANT_B; + rs_get_tbl_info_from_mcs(rate, priv->band, tbl, &rate_idx); + if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) + rs_toggle_antenna(valid_tx_ant, &rate, tbl); + + rate = rate_n_flags_from_tbl(tbl, rate_idx, use_green); + tbl->current_rate = rate; + rs_set_expected_tpt_table(lq_sta, tbl); + rs_fill_link_cmd(NULL, lq_sta, rate); + iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); + out: + return; +} + +static void rs_get_rate(void *priv_rate, struct net_device *dev, + struct ieee80211_supported_band *sband, + struct sk_buff *skb, + struct rate_selection *sel) +{ + + int i; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_conf *conf = &local->hw.conf; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct sta_info *sta; + __le16 fc; + struct iwl_priv *priv = (struct iwl_priv *)priv_rate; + struct iwl_lq_sta *lq_sta; + + IWL_DEBUG_RATE_LIMIT("rate scale calculate new rate for skb\n"); + + rcu_read_lock(); + + sta = sta_info_get(local, hdr->addr1); + + /* Send management frames and broadcast/multicast data using lowest + * rate. */ + fc = hdr->frame_control; + if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) || + !sta || !sta->rate_ctrl_priv) { + sel->rate_idx = rate_lowest_index(local, sband, sta); + goto out; + } + + lq_sta = (struct iwl_lq_sta *)sta->rate_ctrl_priv; + i = sta->last_txrate_idx; + + if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && + !lq_sta->ibss_sta_added) { + u8 sta_id = iwl_find_station(priv, hdr->addr1); + DECLARE_MAC_BUF(mac); + + if (sta_id == IWL_INVALID_STATION) { + IWL_DEBUG_RATE("LQ: ADD station %s\n", + print_mac(mac, hdr->addr1)); + sta_id = iwl_add_station_flags(priv, hdr->addr1, + 0, CMD_ASYNC, NULL); + } + if ((sta_id != IWL_INVALID_STATION)) { + lq_sta->lq.sta_id = sta_id; + lq_sta->lq.rs_table[0].rate_n_flags = 0; + lq_sta->ibss_sta_added = 1; + rs_initialize_lq(priv, conf, sta); + } + } + + if ((i < 0) || (i > IWL_RATE_COUNT)) { + sel->rate_idx = rate_lowest_index(local, sband, sta); + goto out; + } + + if (sband->band == IEEE80211_BAND_5GHZ) + i -= IWL_FIRST_OFDM_RATE; + sel->rate_idx = i; +out: + rcu_read_unlock(); +} + +static void *rs_alloc_sta(void *priv_rate, gfp_t gfp) +{ + struct iwl_lq_sta *lq_sta; + struct iwl_priv *priv; + int i, j; + + priv = (struct iwl_priv *)priv_rate; + IWL_DEBUG_RATE("create station rate scale window\n"); + + lq_sta = kzalloc(sizeof(struct iwl_lq_sta), gfp); + + if (lq_sta == NULL) + return NULL; + lq_sta->lq.sta_id = 0xff; + + + for (j = 0; j < LQ_SIZE; j++) + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&(lq_sta->lq_info[j].win[i])); + + return lq_sta; +} + +static void rs_rate_init(void *priv_rate, void *priv_sta, + struct ieee80211_local *local, + struct sta_info *sta) +{ + int i, j; + struct ieee80211_conf *conf = &local->hw.conf; + struct ieee80211_supported_band *sband; + struct iwl_priv *priv = (struct iwl_priv *)priv_rate; + struct iwl_lq_sta *lq_sta = priv_sta; + + sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + + lq_sta->flush_timer = 0; + lq_sta->supp_rates = sta->supp_rates[sband->band]; + sta->txrate_idx = 3; + for (j = 0; j < LQ_SIZE; j++) + for (i = 0; i < IWL_RATE_COUNT; i++) + rs_rate_scale_clear_window(&(lq_sta->lq_info[j].win[i])); + + IWL_DEBUG_RATE("LQ: *** rate scale global init ***\n"); + /* TODO: what is a good starting rate for STA? About middle? Maybe not + * the lowest or the highest rate.. Could consider using RSSI from + * previous packets? Need to have IEEE 802.1X auth succeed immediately + * after assoc.. */ + + lq_sta->ibss_sta_added = 0; + if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { + u8 sta_id = iwl_find_station(priv, sta->addr); + DECLARE_MAC_BUF(mac); + + /* for IBSS the call are from tasklet */ + IWL_DEBUG_RATE("LQ: ADD station %s\n", + print_mac(mac, sta->addr)); + + if (sta_id == IWL_INVALID_STATION) { + IWL_DEBUG_RATE("LQ: ADD station %s\n", + print_mac(mac, sta->addr)); + sta_id = iwl_add_station_flags(priv, sta->addr, + 0, CMD_ASYNC, NULL); + } + if ((sta_id != IWL_INVALID_STATION)) { + lq_sta->lq.sta_id = sta_id; + lq_sta->lq.rs_table[0].rate_n_flags = 0; + } + /* FIXME: this is w/a remove it later */ + priv->assoc_station_added = 1; + } + + /* Find highest tx rate supported by hardware and destination station */ + for (i = 0; i < sband->n_bitrates; i++) + if (sta->supp_rates[sband->band] & BIT(i)) + sta->txrate_idx = i; + + sta->last_txrate_idx = sta->txrate_idx; + /* WTF is with this bogus comment? A doesn't have cck rates */ + /* For MODE_IEEE80211A, cck rates are at end of rate table */ + if (local->hw.conf.channel->band == IEEE80211_BAND_5GHZ) + sta->last_txrate_idx += IWL_FIRST_OFDM_RATE; + + lq_sta->is_dup = 0; + lq_sta->is_green = rs_use_green(priv, conf); + lq_sta->active_legacy_rate = priv->active_rate & ~(0x1000); + lq_sta->active_rate_basic = priv->active_rate_basic; + lq_sta->band = priv->band; + /* + * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), + * supp_rates[] does not; shift to convert format, force 9 MBits off. + */ + lq_sta->active_siso_rate = conf->ht_conf.supp_mcs_set[0] << 1; + lq_sta->active_siso_rate |= conf->ht_conf.supp_mcs_set[0] & 0x1; + lq_sta->active_siso_rate &= ~((u16)0x2); + lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; + + /* Same here */ + lq_sta->active_mimo2_rate = conf->ht_conf.supp_mcs_set[1] << 1; + lq_sta->active_mimo2_rate |= conf->ht_conf.supp_mcs_set[1] & 0x1; + lq_sta->active_mimo2_rate &= ~((u16)0x2); + lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; + + lq_sta->active_mimo3_rate = conf->ht_conf.supp_mcs_set[2] << 1; + lq_sta->active_mimo3_rate |= conf->ht_conf.supp_mcs_set[2] & 0x1; + lq_sta->active_mimo3_rate &= ~((u16)0x2); + lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE; + + IWL_DEBUG_RATE("SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n", + lq_sta->active_siso_rate, + lq_sta->active_mimo2_rate, + lq_sta->active_mimo3_rate); + + /* These values will be overriden later */ + lq_sta->lq.general_params.single_stream_ant_msk = ANT_A; + lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB; + + /* as default allow aggregation for all tids */ + lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; + lq_sta->drv = priv; + + rs_initialize_lq(priv, conf, sta); +} + +static void rs_fill_link_cmd(const struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, u32 new_rate) +{ + struct iwl_scale_tbl_info tbl_type; + int index = 0; + int rate_idx; + int repeat_rate = 0; + u8 ant_toggle_cnt = 0; + u8 use_ht_possible = 1; + u8 valid_tx_ant = 0; + struct iwl_link_quality_cmd *lq_cmd = &lq_sta->lq; + + /* Override starting rate (index 0) if needed for debug purposes */ + rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + + /* Interpret new_rate (rate_n_flags) */ + memset(&tbl_type, 0, sizeof(tbl_type)); + rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, + &tbl_type, &rate_idx); + + /* How many times should we repeat the initial rate? */ + if (is_legacy(tbl_type.lq_type)) { + ant_toggle_cnt = 1; + repeat_rate = IWL_NUMBER_TRY; + } else { + repeat_rate = IWL_HT_NUMBER_TRY; + } + + lq_cmd->general_params.mimo_delimiter = + is_mimo(tbl_type.lq_type) ? 1 : 0; + + /* Fill 1st table entry (index 0) */ + lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); + + if (num_of_ant(tbl_type.ant_type) == 1) { + lq_cmd->general_params.single_stream_ant_msk = + tbl_type.ant_type; + } else if (num_of_ant(tbl_type.ant_type) == 2) { + lq_cmd->general_params.dual_stream_ant_msk = + tbl_type.ant_type; + } /* otherwise we don't modify the existing value */ + + index++; + repeat_rate--; + + if (priv) + valid_tx_ant = priv->hw_params.valid_tx_ant; + + /* Fill rest of rate table */ + while (index < LINK_QUAL_MAX_RETRY_NUM) { + /* Repeat initial/next rate. + * For legacy IWL_NUMBER_TRY == 1, this loop will not execute. + * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */ + while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) { + if (is_legacy(tbl_type.lq_type)) { + if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) + ant_toggle_cnt++; + else if (priv && + rs_toggle_antenna(valid_tx_ant, + &new_rate, &tbl_type)) + ant_toggle_cnt = 1; +} + + /* Override next rate if needed for debug purposes */ + rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + + /* Fill next table entry */ + lq_cmd->rs_table[index].rate_n_flags = + cpu_to_le32(new_rate); + repeat_rate--; + index++; + } + + rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, + &rate_idx); + + /* Indicate to uCode which entries might be MIMO. + * If initial rate was MIMO, this will finally end up + * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ + if (is_mimo(tbl_type.lq_type)) + lq_cmd->general_params.mimo_delimiter = index; + + /* Get next rate */ + new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx, + use_ht_possible); + + /* How many times should we repeat the next rate? */ + if (is_legacy(tbl_type.lq_type)) { + if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) + ant_toggle_cnt++; + else if (priv && + rs_toggle_antenna(valid_tx_ant, + &new_rate, &tbl_type)) + ant_toggle_cnt = 1; + + repeat_rate = IWL_NUMBER_TRY; + } else { + repeat_rate = IWL_HT_NUMBER_TRY; + } + + /* Don't allow HT rates after next pass. + * rs_get_lower_rate() will change type to LQ_A or LQ_G. */ + use_ht_possible = 0; + + /* Override next rate if needed for debug purposes */ + rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + + /* Fill next table entry */ + lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); + + index++; + repeat_rate--; + } + + lq_cmd->agg_params.agg_frame_cnt_limit = 64; + lq_cmd->agg_params.agg_dis_start_th = 3; + lq_cmd->agg_params.agg_time_limit = cpu_to_le16(4000); +} + +static void *rs_alloc(struct ieee80211_local *local) +{ + return local->hw.priv; +} +/* rate scale requires free function to be implemented */ +static void rs_free(void *priv_rate) +{ + return; +} + +static void rs_clear(void *priv_rate) +{ + struct iwl_priv *priv = (struct iwl_priv *) priv_rate; + + IWL_DEBUG_RATE("enter\n"); + + /* TODO - add rate scale state reset */ + + IWL_DEBUG_RATE("leave\n"); +} + +static void rs_free_sta(void *priv_rate, void *priv_sta) +{ + struct iwl_lq_sta *lq_sta = priv_sta; + struct iwl_priv *priv; + + priv = (struct iwl_priv *)priv_rate; + IWL_DEBUG_RATE("enter\n"); + kfree(lq_sta); + IWL_DEBUG_RATE("leave\n"); +} + + +#ifdef CONFIG_MAC80211_DEBUGFS +static int open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} +static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, + u32 *rate_n_flags, int index) +{ + struct iwl_priv *priv; + + priv = lq_sta->drv; + if (lq_sta->dbg_fixed_rate) { + if (index < 12) { + *rate_n_flags = lq_sta->dbg_fixed_rate; + } else { + if (lq_sta->band == IEEE80211_BAND_5GHZ) + *rate_n_flags = 0x800D; + else + *rate_n_flags = 0x820A; + } + IWL_DEBUG_RATE("Fixed rate ON\n"); + } else { + IWL_DEBUG_RATE("Fixed rate OFF\n"); + } +} + +static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct iwl_lq_sta *lq_sta = file->private_data; + struct iwl_priv *priv; + char buf[64]; + int buf_size; + u32 parsed_rate; + + priv = lq_sta->drv; + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%x", &parsed_rate) == 1) + lq_sta->dbg_fixed_rate = parsed_rate; + else + lq_sta->dbg_fixed_rate = 0; + + lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ + lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + + IWL_DEBUG_RATE("sta_id %d rate 0x%X\n", + lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); + + if (lq_sta->dbg_fixed_rate) { + rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); + iwl_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC); + } + + return count; +} + +static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char buff[1024]; + int desc = 0; + int i = 0; + + struct iwl_lq_sta *lq_sta = file->private_data; + + desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id); + desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n", + lq_sta->total_failed, lq_sta->total_success, + lq_sta->active_legacy_rate); + desc += sprintf(buff+desc, "fixed rate 0x%X\n", + lq_sta->dbg_fixed_rate); + desc += sprintf(buff+desc, "general:" + "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", + lq_sta->lq.general_params.flags, + lq_sta->lq.general_params.mimo_delimiter, + lq_sta->lq.general_params.single_stream_ant_msk, + lq_sta->lq.general_params.dual_stream_ant_msk); + + desc += sprintf(buff+desc, "agg:" + "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", + le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit), + lq_sta->lq.agg_params.agg_dis_start_th, + lq_sta->lq.agg_params.agg_frame_cnt_limit); + + desc += sprintf(buff+desc, + "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", + lq_sta->lq.general_params.start_rate_index[0], + lq_sta->lq.general_params.start_rate_index[1], + lq_sta->lq.general_params.start_rate_index[2], + lq_sta->lq.general_params.start_rate_index[3]); + + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) + desc += sprintf(buff+desc, " rate[%d] 0x%X\n", + i, le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags)); + + return simple_read_from_buffer(user_buf, count, ppos, buff, desc); +} + +static const struct file_operations rs_sta_dbgfs_scale_table_ops = { + .write = rs_sta_dbgfs_scale_table_write, + .read = rs_sta_dbgfs_scale_table_read, + .open = open_file_generic, +}; +static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char buff[1024]; + int desc = 0; + int i, j; + + struct iwl_lq_sta *lq_sta = file->private_data; + for (i = 0; i < LQ_SIZE; i++) { + desc += sprintf(buff+desc, "%s type=%d SGI=%d FAT=%d DUP=%d\n" + "rate=0x%X\n", + lq_sta->active_tbl == i?"*":"x", + lq_sta->lq_info[i].lq_type, + lq_sta->lq_info[i].is_SGI, + lq_sta->lq_info[i].is_fat, + lq_sta->lq_info[i].is_dup, + lq_sta->lq_info[i].current_rate); + for (j = 0; j < IWL_RATE_COUNT; j++) { + desc += sprintf(buff+desc, + "counter=%d success=%d %%=%d\n", + lq_sta->lq_info[i].win[j].counter, + lq_sta->lq_info[i].win[j].success_counter, + lq_sta->lq_info[i].win[j].success_ratio); + } + } + return simple_read_from_buffer(user_buf, count, ppos, buff, desc); +} + +static const struct file_operations rs_sta_dbgfs_stats_table_ops = { + .read = rs_sta_dbgfs_stats_table_read, + .open = open_file_generic, +}; + +static void rs_add_debugfs(void *priv, void *priv_sta, + struct dentry *dir) +{ + struct iwl_lq_sta *lq_sta = priv_sta; + lq_sta->rs_sta_dbgfs_scale_table_file = + debugfs_create_file("rate_scale_table", 0600, dir, + lq_sta, &rs_sta_dbgfs_scale_table_ops); + lq_sta->rs_sta_dbgfs_stats_table_file = + debugfs_create_file("rate_stats_table", 0600, dir, + lq_sta, &rs_sta_dbgfs_stats_table_ops); + lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = + debugfs_create_u8("tx_agg_tid_enable", 0600, dir, + &lq_sta->tx_agg_tid_en); + +} + +static void rs_remove_debugfs(void *priv, void *priv_sta) +{ + struct iwl_lq_sta *lq_sta = priv_sta; + debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); +} +#endif + +static struct rate_control_ops rs_ops = { + .module = NULL, + .name = RS_NAME, + .tx_status = rs_tx_status, + .get_rate = rs_get_rate, + .rate_init = rs_rate_init, + .clear = rs_clear, + .alloc = rs_alloc, + .free = rs_free, + .alloc_sta = rs_alloc_sta, + .free_sta = rs_free_sta, +#ifdef CONFIG_MAC80211_DEBUGFS + .add_sta_debugfs = rs_add_debugfs, + .remove_sta_debugfs = rs_remove_debugfs, +#endif +}; + +int iwl4965_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct iwl_priv *priv = hw->priv; + struct iwl_lq_sta *lq_sta; + struct sta_info *sta; + int cnt = 0, i; + u32 samples = 0, success = 0, good = 0; + unsigned long now = jiffies; + u32 max_time = 0; + u8 lq_type, antenna; + + rcu_read_lock(); + + sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr); + if (!sta || !sta->rate_ctrl_priv) { + if (sta) + IWL_DEBUG_RATE("leave - no private rate data!\n"); + else + IWL_DEBUG_RATE("leave - no station!\n"); + rcu_read_unlock(); + return sprintf(buf, "station %d not found\n", sta_id); + } + + lq_sta = (void *)sta->rate_ctrl_priv; + + lq_type = lq_sta->lq_info[lq_sta->active_tbl].lq_type; + antenna = lq_sta->lq_info[lq_sta->active_tbl].ant_type; + + if (is_legacy(lq_type)) + i = IWL_RATE_54M_INDEX; + else + i = IWL_RATE_60M_INDEX; + while (1) { + u64 mask; + int j; + int active = lq_sta->active_tbl; + + cnt += + sprintf(&buf[cnt], " %2dMbs: ", iwl_rates[i].ieee / 2); + + mask = (1ULL << (IWL_RATE_MAX_WINDOW - 1)); + for (j = 0; j < IWL_RATE_MAX_WINDOW; j++, mask >>= 1) + buf[cnt++] = + (lq_sta->lq_info[active].win[i].data & mask) + ? '1' : '0'; + + samples += lq_sta->lq_info[active].win[i].counter; + good += lq_sta->lq_info[active].win[i].success_counter; + success += lq_sta->lq_info[active].win[i].success_counter * + iwl_rates[i].ieee; + + if (lq_sta->lq_info[active].win[i].stamp) { + int delta = + jiffies_to_msecs(now - + lq_sta->lq_info[active].win[i].stamp); + + if (delta > max_time) + max_time = delta; + + cnt += sprintf(&buf[cnt], "%5dms\n", delta); + } else + buf[cnt++] = '\n'; + + j = iwl4965_get_prev_ieee_rate(i); + if (j == i) + break; + i = j; + } + + /* + * Display the average rate of all samples taken. + * NOTE: We multiply # of samples by 2 since the IEEE measurement + * added from iwl_rates is actually 2X the rate. + */ + if (samples) + cnt += sprintf(&buf[cnt], + "\nAverage rate is %3d.%02dMbs over last %4dms\n" + "%3d%% success (%d good packets over %d tries)\n", + success / (2 * samples), (success * 5 / samples) % 10, + max_time, good * 100 / samples, good, samples); + else + cnt += sprintf(&buf[cnt], "\nAverage rate: 0Mbs\n"); + + cnt += sprintf(&buf[cnt], "\nrate scale type %d antenna %d " + "active_search %d rate index %d\n", lq_type, antenna, + lq_sta->search_better_tbl, sta->last_txrate_idx); + + rcu_read_unlock(); + return cnt; +} + +int iwlagn_rate_control_register(void) +{ + return ieee80211_rate_control_register(&rs_ops); +} + +void iwlagn_rate_control_unregister(void) +{ + ieee80211_rate_control_unregister(&rs_ops); +} + diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h new file mode 100644 index 000000000000..3b06c9da77e3 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h @@ -0,0 +1,318 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2008 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * James P. Ketrenos + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_agn_rs_h__ +#define __iwl_agn_rs_h__ + +#include "iwl-dev.h" + +struct iwl_rate_info { + u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ + u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ + u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ + u8 plcp_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */ + u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ + u8 prev_ieee; /* previous rate in IEEE speeds */ + u8 next_ieee; /* next rate in IEEE speeds */ + u8 prev_rs; /* previous rate used in rs algo */ + u8 next_rs; /* next rate used in rs algo */ + u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ + u8 next_rs_tgg; /* next rate used in TGG rs algo */ +}; + +/* + * These serve as indexes into + * struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; + */ +enum { + IWL_RATE_1M_INDEX = 0, + IWL_RATE_2M_INDEX, + IWL_RATE_5M_INDEX, + IWL_RATE_11M_INDEX, + IWL_RATE_6M_INDEX, + IWL_RATE_9M_INDEX, + IWL_RATE_12M_INDEX, + IWL_RATE_18M_INDEX, + IWL_RATE_24M_INDEX, + IWL_RATE_36M_INDEX, + IWL_RATE_48M_INDEX, + IWL_RATE_54M_INDEX, + IWL_RATE_60M_INDEX, + IWL_RATE_COUNT, /*FIXME:RS:change to IWL_RATE_INDEX_COUNT,*/ + IWL_RATE_INVM_INDEX = IWL_RATE_COUNT, + IWL_RATE_INVALID = IWL_RATE_COUNT, +}; + +enum { + IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, + IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX, + IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX, + IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, +}; + +/* #define vs. enum to keep from defaulting to 'large integer' */ +#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX) +#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX) +#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX) +#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX) +#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX) +#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX) +#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX) +#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX) +#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX) +#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX) +#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX) +#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX) +#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX) + +/* uCode API values for legacy bit rates, both OFDM and CCK */ +enum { + IWL_RATE_6M_PLCP = 13, + IWL_RATE_9M_PLCP = 15, + IWL_RATE_12M_PLCP = 5, + IWL_RATE_18M_PLCP = 7, + IWL_RATE_24M_PLCP = 9, + IWL_RATE_36M_PLCP = 11, + IWL_RATE_48M_PLCP = 1, + IWL_RATE_54M_PLCP = 3, + IWL_RATE_60M_PLCP = 3,/*FIXME:RS:should be removed*/ + IWL_RATE_1M_PLCP = 10, + IWL_RATE_2M_PLCP = 20, + IWL_RATE_5M_PLCP = 55, + IWL_RATE_11M_PLCP = 110, + /*FIXME:RS:change to IWL_RATE_LEGACY_??M_PLCP */ + /*FIXME:RS:add IWL_RATE_LEGACY_INVM_PLCP = 0,*/ +}; + +/* uCode API values for OFDM high-throughput (HT) bit rates */ +enum { + IWL_RATE_SISO_6M_PLCP = 0, + IWL_RATE_SISO_12M_PLCP = 1, + IWL_RATE_SISO_18M_PLCP = 2, + IWL_RATE_SISO_24M_PLCP = 3, + IWL_RATE_SISO_36M_PLCP = 4, + IWL_RATE_SISO_48M_PLCP = 5, + IWL_RATE_SISO_54M_PLCP = 6, + IWL_RATE_SISO_60M_PLCP = 7, + IWL_RATE_MIMO2_6M_PLCP = 0x8, + IWL_RATE_MIMO2_12M_PLCP = 0x9, + IWL_RATE_MIMO2_18M_PLCP = 0xa, + IWL_RATE_MIMO2_24M_PLCP = 0xb, + IWL_RATE_MIMO2_36M_PLCP = 0xc, + IWL_RATE_MIMO2_48M_PLCP = 0xd, + IWL_RATE_MIMO2_54M_PLCP = 0xe, + IWL_RATE_MIMO2_60M_PLCP = 0xf, + IWL_RATE_MIMO3_6M_PLCP = 0x10, + IWL_RATE_MIMO3_12M_PLCP = 0x11, + IWL_RATE_MIMO3_18M_PLCP = 0x12, + IWL_RATE_MIMO3_24M_PLCP = 0x13, + IWL_RATE_MIMO3_36M_PLCP = 0x14, + IWL_RATE_MIMO3_48M_PLCP = 0x15, + IWL_RATE_MIMO3_54M_PLCP = 0x16, + IWL_RATE_MIMO3_60M_PLCP = 0x17, + IWL_RATE_SISO_INVM_PLCP, + IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, + IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, +}; + +/* MAC header values for bit rates */ +enum { + IWL_RATE_6M_IEEE = 12, + IWL_RATE_9M_IEEE = 18, + IWL_RATE_12M_IEEE = 24, + IWL_RATE_18M_IEEE = 36, + IWL_RATE_24M_IEEE = 48, + IWL_RATE_36M_IEEE = 72, + IWL_RATE_48M_IEEE = 96, + IWL_RATE_54M_IEEE = 108, + IWL_RATE_60M_IEEE = 120, + IWL_RATE_1M_IEEE = 2, + IWL_RATE_2M_IEEE = 4, + IWL_RATE_5M_IEEE = 11, + IWL_RATE_11M_IEEE = 22, +}; + +#define IWL_CCK_BASIC_RATES_MASK \ + (IWL_RATE_1M_MASK | \ + IWL_RATE_2M_MASK) + +#define IWL_CCK_RATES_MASK \ + (IWL_BASIC_RATES_MASK | \ + IWL_RATE_5M_MASK | \ + IWL_RATE_11M_MASK) + +#define IWL_OFDM_BASIC_RATES_MASK \ + (IWL_RATE_6M_MASK | \ + IWL_RATE_12M_MASK | \ + IWL_RATE_24M_MASK) + +#define IWL_OFDM_RATES_MASK \ + (IWL_OFDM_BASIC_RATES_MASK | \ + IWL_RATE_9M_MASK | \ + IWL_RATE_18M_MASK | \ + IWL_RATE_36M_MASK | \ + IWL_RATE_48M_MASK | \ + IWL_RATE_54M_MASK) + +#define IWL_BASIC_RATES_MASK \ + (IWL_OFDM_BASIC_RATES_MASK | \ + IWL_CCK_BASIC_RATES_MASK) + +#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) + +#define IWL_INVALID_VALUE -1 + +#define IWL_MIN_RSSI_VAL -100 +#define IWL_MAX_RSSI_VAL 0 + +/* These values specify how many Tx frame attempts before + * searching for a new modulation mode */ +#define IWL_LEGACY_FAILURE_LIMIT 160 +#define IWL_LEGACY_SUCCESS_LIMIT 480 +#define IWL_LEGACY_TABLE_COUNT 160 + +#define IWL_NONE_LEGACY_FAILURE_LIMIT 400 +#define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500 +#define IWL_NONE_LEGACY_TABLE_COUNT 1500 + +/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ +#define IWL_RS_GOOD_RATIO 12800 /* 100% */ +#define IWL_RATE_SCALE_SWITCH 10880 /* 85% */ +#define IWL_RATE_HIGH_TH 10880 /* 85% */ +#define IWL_RATE_INCREASE_TH 8960 /* 70% */ +#define IWL_RATE_DECREASE_TH 1920 /* 15% */ + +/* possible actions when in legacy mode */ +#define IWL_LEGACY_SWITCH_ANTENNA 0 +#define IWL_LEGACY_SWITCH_SISO 1 +#define IWL_LEGACY_SWITCH_MIMO2 2 + +/* possible actions when in siso mode */ +#define IWL_SISO_SWITCH_ANTENNA 0 +#define IWL_SISO_SWITCH_MIMO2 1 +#define IWL_SISO_SWITCH_GI 2 + +/* possible actions when in mimo mode */ +#define IWL_MIMO_SWITCH_ANTENNA_A 0 +#define IWL_MIMO_SWITCH_ANTENNA_B 1 +#define IWL_MIMO_SWITCH_GI 2 + +/*FIXME:RS:separate MIMO2/3 transitions*/ + +/*FIXME:RS:add posible acctions for MIMO3*/ + +#define IWL_ACTION_LIMIT 3 /* # possible actions */ + +#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ + +/* load per tid defines for A-MPDU activation */ +#define IWL_AGG_TPT_THREHOLD 0 +#define IWL_AGG_LOAD_THRESHOLD 10 +#define IWL_AGG_ALL_TID 0xff +#define TID_QUEUE_CELL_SPACING 50 /*mS */ +#define TID_QUEUE_MAX_SIZE 20 +#define TID_ROUND_VALUE 5 /* mS */ +#define TID_MAX_LOAD_COUNT 8 + +#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) +#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) + +extern const struct iwl_rate_info iwl_rates[IWL_RATE_COUNT]; + +enum iwl_table_type { + LQ_NONE, + LQ_G, /* legacy types */ + LQ_A, + LQ_SISO, /* high-throughput types */ + LQ_MIMO2, + LQ_MIMO3, + LQ_MAX, +}; + +#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A)) +#define is_siso(tbl) ((tbl) == LQ_SISO) +#define is_mimo2(tbl) ((tbl) == LQ_MIMO2) +#define is_mimo3(tbl) ((tbl) == LQ_MIMO3) +#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl)) +#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) +#define is_a_band(tbl) ((tbl) == LQ_A) +#define is_g_and(tbl) ((tbl) == LQ_G) + +#define ANT_NONE 0x0 +#define ANT_A BIT(0) +#define ANT_B BIT(1) +#define ANT_AB (ANT_A | ANT_B) +#define ANT_C BIT(2) +#define ANT_AC (ANT_A | ANT_C) +#define ANT_BC (ANT_B | ANT_C) +#define ANT_ABC (ANT_AB | ANT_C) + +static inline u8 num_of_ant(u8 mask) +{ + return !!((mask) & ANT_A) + + !!((mask) & ANT_B) + + !!((mask) & ANT_C); +} + +static inline u8 iwl4965_get_prev_ieee_rate(u8 rate_index) +{ + u8 rate = iwl_rates[rate_index].prev_ieee; + + if (rate == IWL_RATE_INVALID) + rate = rate_index; + return rate; +} + +/** + * iwl4965_fill_rs_info - Fill an output text buffer with the rate representation + * + * NOTE: This is provided as a quick mechanism for a user to visualize + * the performance of the rate control algorithm and is not meant to be + * parsed software. + */ +extern int iwl4965_fill_rs_info(struct ieee80211_hw *, char *buf, u8 sta_id); + +/** + * iwl4965_rate_control_register - Register the rate control algorithm callbacks + * + * Since the rate control algorithm is hardware specific, there is no need + * or reason to place it as a stand alone module. The driver can call + * iwl4965_rate_control_register in order to register the rate control callbacks + * with the mac80211 subsystem. This should be performed prior to calling + * ieee80211_register_hw + * + */ +extern int iwlagn_rate_control_register(void); + +/** + * iwl4965_rate_control_unregister - Unregister the rate control callbacks + * + * This should be called after calling ieee80211_unregister_hw, but before + * the driver is unloaded. + */ +extern void iwlagn_rate_control_unregister(void); + +#endif /* __iwl_agn__rs__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index e3427c205ccf..60d443e77c38 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -815,7 +815,7 @@ int iwl_setup_mac(struct iwl_priv *priv) { int ret; struct ieee80211_hw *hw = priv->hw; - hw->rate_control_algorithm = "iwl-4965-rs"; + hw->rate_control_algorithm = "iwl-agn-rs"; /* Tell mac80211 our characteristics */ hw->flags = IEEE80211_HW_SIGNAL_DBM | diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index ff16cca2b8c7..7ac56b1350b7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -45,6 +45,7 @@ #include "iwl-debug.h" #include "iwl-led.h" #include "iwl-power.h" +#include "iwl-agn-rs.h" /* configuration for the iwl4965 */ extern struct iwl_cfg iwl4965_agn_cfg; @@ -191,7 +192,6 @@ struct iwl4965_clip_group { const s8 clip_powers[IWL_MAX_RATES]; }; -#include "iwl-4965-rs.h" #define IWL_TX_FIFO_AC0 0 #define IWL_TX_FIFO_AC1 1 diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index 6d1467d0bd9d..10af82170f6b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -839,7 +839,7 @@ EXPORT_SYMBOL(iwl_send_lq_cmd); * for automatic fallback during transmission. * * NOTE: This sets up a default set of values. These will be replaced later - * if the driver's iwl-4965-rs rate scaling algorithm is used, instead of + * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of * rc80211_simple. * * NOTE: Run REPLY_ADD_STA command to set up station table entry, before diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index ac02342d2281..dcd11e9bd86c 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -4503,7 +4503,7 @@ static int __init iwl4965_init(void) printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n"); printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n"); - ret = iwl4965_rate_control_register(); + ret = iwlagn_rate_control_register(); if (ret) { IWL_ERROR("Unable to register rate control algorithm: %d\n", ret); return ret; @@ -4518,14 +4518,14 @@ static int __init iwl4965_init(void) return ret; error_register: - iwl4965_rate_control_unregister(); + iwlagn_rate_control_unregister(); return ret; } static void __exit iwl4965_exit(void) { pci_unregister_driver(&iwl_driver); - iwl4965_rate_control_unregister(); + iwlagn_rate_control_unregister(); } module_exit(iwl4965_exit); -- cgit v1.2.1 From 3ce84b9f2f495f59c4a4e68d814c348eaa497f65 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 18 Jul 2008 13:53:06 +0800 Subject: iwlwifi: kill iwl4965_fill_rs_info iwl4965_fill_rs_info was used in sysfs. This info is already present in iwl-agn-rs debugfs. Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 92 ----------------------------- drivers/net/wireless/iwlwifi/iwl-agn-rs.h | 9 --- drivers/net/wireless/iwlwifi/iwl4965-base.c | 10 ---- 3 files changed, 111 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index eee22c6dec73..0e8100e70620 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -2600,98 +2600,6 @@ static struct rate_control_ops rs_ops = { #endif }; -int iwl4965_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id) -{ - struct ieee80211_local *local = hw_to_local(hw); - struct iwl_priv *priv = hw->priv; - struct iwl_lq_sta *lq_sta; - struct sta_info *sta; - int cnt = 0, i; - u32 samples = 0, success = 0, good = 0; - unsigned long now = jiffies; - u32 max_time = 0; - u8 lq_type, antenna; - - rcu_read_lock(); - - sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr); - if (!sta || !sta->rate_ctrl_priv) { - if (sta) - IWL_DEBUG_RATE("leave - no private rate data!\n"); - else - IWL_DEBUG_RATE("leave - no station!\n"); - rcu_read_unlock(); - return sprintf(buf, "station %d not found\n", sta_id); - } - - lq_sta = (void *)sta->rate_ctrl_priv; - - lq_type = lq_sta->lq_info[lq_sta->active_tbl].lq_type; - antenna = lq_sta->lq_info[lq_sta->active_tbl].ant_type; - - if (is_legacy(lq_type)) - i = IWL_RATE_54M_INDEX; - else - i = IWL_RATE_60M_INDEX; - while (1) { - u64 mask; - int j; - int active = lq_sta->active_tbl; - - cnt += - sprintf(&buf[cnt], " %2dMbs: ", iwl_rates[i].ieee / 2); - - mask = (1ULL << (IWL_RATE_MAX_WINDOW - 1)); - for (j = 0; j < IWL_RATE_MAX_WINDOW; j++, mask >>= 1) - buf[cnt++] = - (lq_sta->lq_info[active].win[i].data & mask) - ? '1' : '0'; - - samples += lq_sta->lq_info[active].win[i].counter; - good += lq_sta->lq_info[active].win[i].success_counter; - success += lq_sta->lq_info[active].win[i].success_counter * - iwl_rates[i].ieee; - - if (lq_sta->lq_info[active].win[i].stamp) { - int delta = - jiffies_to_msecs(now - - lq_sta->lq_info[active].win[i].stamp); - - if (delta > max_time) - max_time = delta; - - cnt += sprintf(&buf[cnt], "%5dms\n", delta); - } else - buf[cnt++] = '\n'; - - j = iwl4965_get_prev_ieee_rate(i); - if (j == i) - break; - i = j; - } - - /* - * Display the average rate of all samples taken. - * NOTE: We multiply # of samples by 2 since the IEEE measurement - * added from iwl_rates is actually 2X the rate. - */ - if (samples) - cnt += sprintf(&buf[cnt], - "\nAverage rate is %3d.%02dMbs over last %4dms\n" - "%3d%% success (%d good packets over %d tries)\n", - success / (2 * samples), (success * 5 / samples) % 10, - max_time, good * 100 / samples, good, samples); - else - cnt += sprintf(&buf[cnt], "\nAverage rate: 0Mbs\n"); - - cnt += sprintf(&buf[cnt], "\nrate scale type %d antenna %d " - "active_search %d rate index %d\n", lq_type, antenna, - lq_sta->search_better_tbl, sta->last_txrate_idx); - - rcu_read_unlock(); - return cnt; -} - int iwlagn_rate_control_register(void) { return ieee80211_rate_control_register(&rs_ops); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h index 3b06c9da77e3..84d4d1e33755 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h @@ -286,15 +286,6 @@ static inline u8 iwl4965_get_prev_ieee_rate(u8 rate_index) return rate; } -/** - * iwl4965_fill_rs_info - Fill an output text buffer with the rate representation - * - * NOTE: This is provided as a quick mechanism for a user to visualize - * the performance of the rate control algorithm and is not meant to be - * parsed software. - */ -extern int iwl4965_fill_rs_info(struct ieee80211_hw *, char *buf, u8 sta_id); - /** * iwl4965_rate_control_register - Register the rate control algorithm callbacks * diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index dcd11e9bd86c..9db6aac02185 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -3687,15 +3687,6 @@ static ssize_t show_temperature(struct device *d, static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL); -static ssize_t show_rs_window(struct device *d, - struct device_attribute *attr, - char *buf) -{ - struct iwl_priv *priv = d->driver_data; - return iwl4965_fill_rs_info(priv->hw, buf, IWL_AP_ID); -} -static DEVICE_ATTR(rs_window, S_IRUGO, show_rs_window, NULL); - static ssize_t show_tx_power(struct device *d, struct device_attribute *attr, char *buf) { @@ -4118,7 +4109,6 @@ static struct attribute *iwl4965_sysfs_entries[] = { #endif &dev_attr_power_level.attr, &dev_attr_retry_rate.attr, - &dev_attr_rs_window.attr, &dev_attr_statistics.attr, &dev_attr_status.attr, &dev_attr_temperature.attr, -- cgit v1.2.1 From c785d1d5018b93878a9280b0c04df96682cc6eff Mon Sep 17 00:00:00 2001 From: Esti Kummer Date: Fri, 18 Jul 2008 13:53:07 +0800 Subject: iwlwifi: set led register in disassociation This patch sets the led register in disassociation flow according to rf-kill state : off - in case of rf_kill, on - otherwise. Signed-off-by: Esti Kummer Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-led.c | 40 ++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c index 61250e6a7d1b..0a01f091c516 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-led.c @@ -161,11 +161,31 @@ int iwl4965_led_off(struct iwl_priv *priv, int led_id) /* Set led register off */ static int iwl4965_led_off_reg(struct iwl_priv *priv, int led_id) { - IWL_DEBUG_LED("radio off\n"); + IWL_DEBUG_LED("LED Reg off\n"); iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_OFF); return 0; } +/* + * Set led register in case of disassociation according to rfkill state + */ +static int iwl_led_associate(struct iwl_priv *priv, int led_id) +{ + IWL_DEBUG_LED("Associated\n"); + priv->allow_blinking = 1; + return iwl4965_led_on_reg(priv, led_id); +} +static int iwl_led_disassociate(struct iwl_priv *priv, int led_id) +{ + priv->allow_blinking = 0; + if (iwl_is_rfkill(priv)) + iwl4965_led_off_reg(priv, led_id); + else + iwl4965_led_on_reg(priv, led_id); + + return 0; +} + /* * brightness call back function for Tx/Rx LED */ @@ -199,16 +219,10 @@ static void iwl_led_brightness_set(struct led_classdev *led_cdev, led_type_str[led->type], brightness); switch (brightness) { case LED_FULL: - if (led->type == IWL_LED_TRG_ASSOC) - priv->allow_blinking = 1; - if (led->led_on) led->led_on(priv, IWL_LED_LINK); break; case LED_OFF: - if (led->type == IWL_LED_TRG_ASSOC) - priv->allow_blinking = 0; - if (led->led_off) led->led_off(priv, IWL_LED_LINK); break; @@ -284,12 +298,6 @@ static int iwl_get_blink_rate(struct iwl_priv *priv) return i; } -static inline int is_rf_kill(struct iwl_priv *priv) -{ - return test_bit(STATUS_RF_KILL_HW, &priv->status) || - test_bit(STATUS_RF_KILL_SW, &priv->status); -} - /* * this function called from handler. Since setting Led command can * happen very frequent we postpone led command to be called from @@ -303,7 +311,7 @@ void iwl_leds_background(struct iwl_priv *priv) priv->last_blink_time = 0; return; } - if (is_rf_kill(priv)) { + if (iwl_is_rfkill(priv)) { priv->last_blink_time = 0; return; } @@ -366,8 +374,8 @@ int iwl_leds_register(struct iwl_priv *priv) IWL_LED_TRG_ASSOC, 0, name, trigger); /* for assoc always turn led on */ - priv->led[IWL_LED_TRG_ASSOC].led_on = iwl4965_led_on_reg; - priv->led[IWL_LED_TRG_ASSOC].led_off = iwl4965_led_on_reg; + priv->led[IWL_LED_TRG_ASSOC].led_on = iwl_led_associate; + priv->led[IWL_LED_TRG_ASSOC].led_off = iwl_led_disassociate; priv->led[IWL_LED_TRG_ASSOC].led_pattern = NULL; if (ret) -- cgit v1.2.1 From 4aa41f12aa4f08a10b0b07ed334faa3638ba8e9c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 18 Jul 2008 13:53:09 +0800 Subject: iwlwifi: bug fix in AGG flow - cast const to ULL This patch fixes a bug in AGG flow: u64 bitmap = 0; bitmap |= 1 << 32 results to be 0xffffffff80000000. Signed-off-by: Emmanuel Grumbach Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-4965.c | 6 +++--- drivers/net/wireless/iwlwifi/iwl-5000.c | 6 +++--- drivers/net/wireless/iwlwifi/iwl-tx.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index c0ba28fa6724..f356f4f0944b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2130,9 +2130,9 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv, bitmap = bitmap << sh; sh = 0; } - bitmap |= (1 << sh); - IWL_DEBUG_TX_REPLY("start=%d bitmap=0x%x\n", - start, (u32)(bitmap & 0xFFFFFFFF)); + bitmap |= 1ULL << sh; + IWL_DEBUG_TX_REPLY("start=%d bitmap=0x%llx\n", + start, (unsigned long long)bitmap); } agg->bitmap = bitmap; diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index f91c54b5ff53..076d3560302b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1228,9 +1228,9 @@ static int iwl5000_tx_status_reply_tx(struct iwl_priv *priv, bitmap = bitmap << sh; sh = 0; } - bitmap |= (1 << sh); - IWL_DEBUG_TX_REPLY("start=%d bitmap=0x%x\n", - start, (u32)(bitmap & 0xFFFFFFFF)); + bitmap |= 1ULL << sh; + IWL_DEBUG_TX_REPLY("start=%d bitmap=0x%llx\n", + start, (unsigned long long)bitmap); } agg->bitmap = bitmap; diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 0182e4da8e35..39f19ebee973 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -1391,7 +1391,7 @@ static int iwl_tx_status_reply_compressed_ba(struct iwl_priv *priv, /* For each frame attempted in aggregation, * update driver's record of tx frame's status. */ for (i = 0; i < agg->frame_count ; i++) { - ack = bitmap & (1 << i); + ack = bitmap & (1ULL << i); successes += !!ack; IWL_DEBUG_TX_REPLY("%s ON i=%d idx=%d raw=%d\n", ack? "ACK":"NACK", i, (agg->start_idx + i) & 0xff, -- cgit v1.2.1 From e170402e5459c12ed8f5bfaa11e6550eba09e57a Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Sat, 19 Jul 2008 04:04:18 +0300 Subject: iwlwifi: RS small compile warnings without CONFIG_IWLWIFI_DEBUG iwl-agn-rs.c: In function 'rs_clear': iwl-agn-rs.c:2405: warning: unused variable 'priv Signed-off-by: Denis V. Lunev Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index 0e8100e70620..b498b58d81fa 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -2393,6 +2393,7 @@ static void rs_free(void *priv_rate) static void rs_clear(void *priv_rate) { +#ifdef CONFIG_IWLWIFI_DEBUG struct iwl_priv *priv = (struct iwl_priv *) priv_rate; IWL_DEBUG_RATE("enter\n"); @@ -2400,6 +2401,7 @@ static void rs_clear(void *priv_rate) /* TODO - add rate scale state reset */ IWL_DEBUG_RATE("leave\n"); +#endif /* CONFIG_IWLWIFI_DEBUG */ } static void rs_free_sta(void *priv_rate, void *priv_sta) -- cgit v1.2.1 From b5d7be5e665f29274cfe6645b661acb38cb1d19b Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sat, 19 Jul 2008 04:41:24 +0300 Subject: iwlwifi: use dtim_period from association, and set listen_interval This patch uses dtim_period from association, and sets the listen_interval. Signed-off-by: Tomas Winkler Signed-off-by: Emmanuel Grumbach Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-commands.h | 3 +-- drivers/net/wireless/iwlwifi/iwl-core.c | 1 + drivers/net/wireless/iwlwifi/iwl4965-base.c | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index cd6d668f4e00..5e57f3ae2ea6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -666,8 +666,7 @@ struct iwl4965_rxon_assoc_cmd { __le16 reserved; } __attribute__ ((packed)); - - +#define IWL_CONN_MAX_LISTEN_INTERVAL 10 /* * REPLY_RXON_TIMING = 0x14 (command, has simple generic response) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 60d443e77c38..96beacf703a6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -827,6 +827,7 @@ int iwl_setup_mac(struct iwl_priv *priv) hw->ampdu_queues = priv->cfg->mod_params->num_of_ampdu_queues; hw->conf.beacon_int = 100; + hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; if (priv->bands[IEEE80211_BAND_2GHZ].n_channels) priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c index 9db6aac02185..f71b3f3f81ba 100644 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ b/drivers/net/wireless/iwlwifi/iwl4965-base.c @@ -639,7 +639,6 @@ static void iwl_activate_qos(struct iwl_priv *priv, u8 force) } #define MAX_UCODE_BEACON_INTERVAL 4096 -#define INTEL_CONN_LISTEN_INTERVAL __constant_cpu_to_le16(0xA) static __le16 iwl4965_adjust_beacon_interval(u16 beacon_val) { @@ -669,7 +668,7 @@ static void iwl4965_setup_rxon_timing(struct iwl_priv *priv) priv->rxon_timing.timestamp.dw[0] = cpu_to_le32(priv->timestamp & 0xFFFFFFFF); - priv->rxon_timing.listen_interval = INTEL_CONN_LISTEN_INTERVAL; + priv->rxon_timing.listen_interval = cpu_to_le16(conf->listen_interval); tsf = priv->timestamp; @@ -2835,6 +2834,7 @@ static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *co spin_lock_irqsave(&priv->lock, flags); + /* if we are switching from ht to 2.4 clear flags * from any ht related info since 2.4 does not * support ht */ @@ -3164,6 +3164,7 @@ static void iwl4965_bss_info_changed(struct ieee80211_hw *hw, if (bss_conf->assoc) { priv->assoc_id = bss_conf->aid; priv->beacon_int = bss_conf->beacon_int; + priv->power_data.dtim_period = bss_conf->dtim_period; priv->timestamp = bss_conf->timestamp; priv->assoc_capability = bss_conf->assoc_capability; priv->next_scan_jiffies = jiffies + -- cgit v1.2.1 From 25bc2deda9e8a430ed49f507a1120fb2c86abf33 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 21 Jul 2008 02:40:13 +0300 Subject: iwlwifi: rename iwl4965-base.c to iwl-agn.c This patch renames iwl4965-base.c to iwl-agn.c Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/Makefile | 6 +- drivers/net/wireless/iwlwifi/iwl-agn.c | 4523 +++++++++++++++++++++++++++ drivers/net/wireless/iwlwifi/iwl4965-base.c | 4523 --------------------------- 3 files changed, 4525 insertions(+), 4527 deletions(-) create mode 100644 drivers/net/wireless/iwlwifi/iwl-agn.c delete mode 100644 drivers/net/wireless/iwlwifi/iwl4965-base.c (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index f50d2f8fd5c9..6bf3998736b5 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -11,10 +11,8 @@ iwl3945-objs := iwl3945-base.o iwl-3945.o iwl-3945-rs.o iwl3945-$(CONFIG_IWL3945_LEDS) += iwl-3945-led.o obj-$(CONFIG_IWL4965) += iwl4965.o -iwl4965-objs := iwl4965-base.o iwl-4965.o iwl-agn-rs.o +iwl4965-objs := iwl-agn.o iwl-4965.o iwl-agn-rs.o -ifeq ($(CONFIG_IWL5000),y) - iwl4965-objs += iwl-5000.o -endif +iwl4965-$(CONFIG_IWL5000) += iwl-5000.o diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c new file mode 100644 index 000000000000..f71b3f3f81ba --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -0,0 +1,4523 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2008 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * James P. Ketrenos + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "iwl-eeprom.h" +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-io.h" +#include "iwl-helpers.h" +#include "iwl-sta.h" +#include "iwl-calib.h" + + +/****************************************************************************** + * + * module boiler plate + * + ******************************************************************************/ + +/* + * module name, copyright, version, etc. + * NOTE: DRV_NAME is defined in iwlwifi.h for use by iwl-debug.h and printk + */ + +#define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link AGN driver for Linux" + +#ifdef CONFIG_IWLWIFI_DEBUG +#define VD "d" +#else +#define VD +#endif + +#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT +#define VS "s" +#else +#define VS +#endif + +#define DRV_VERSION IWLWIFI_VERSION VD VS + + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR(DRV_COPYRIGHT); +MODULE_LICENSE("GPL"); + +/*************** STATION TABLE MANAGEMENT **** + * mac80211 should be examined to determine if sta_info is duplicating + * the functionality provided here + */ + +/**************************************************************/ + + + +static void iwl4965_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt) +{ + struct iwl_rxon_cmd *rxon = &priv->staging_rxon; + + if (hw_decrypt) + rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; + else + rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; + +} + +/** + * iwl4965_check_rxon_cmd - validate RXON structure is valid + * + * NOTE: This is really only useful during development and can eventually + * be #ifdef'd out once the driver is stable and folks aren't actively + * making changes + */ +static int iwl4965_check_rxon_cmd(struct iwl_rxon_cmd *rxon) +{ + int error = 0; + int counter = 1; + + if (rxon->flags & RXON_FLG_BAND_24G_MSK) { + error |= le32_to_cpu(rxon->flags & + (RXON_FLG_TGJ_NARROW_BAND_MSK | + RXON_FLG_RADAR_DETECT_MSK)); + if (error) + IWL_WARNING("check 24G fields %d | %d\n", + counter++, error); + } else { + error |= (rxon->flags & RXON_FLG_SHORT_SLOT_MSK) ? + 0 : le32_to_cpu(RXON_FLG_SHORT_SLOT_MSK); + if (error) + IWL_WARNING("check 52 fields %d | %d\n", + counter++, error); + error |= le32_to_cpu(rxon->flags & RXON_FLG_CCK_MSK); + if (error) + IWL_WARNING("check 52 CCK %d | %d\n", + counter++, error); + } + error |= (rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1; + if (error) + IWL_WARNING("check mac addr %d | %d\n", counter++, error); + + /* make sure basic rates 6Mbps and 1Mbps are supported */ + error |= (((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0) && + ((rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0)); + if (error) + IWL_WARNING("check basic rate %d | %d\n", counter++, error); + + error |= (le16_to_cpu(rxon->assoc_id) > 2007); + if (error) + IWL_WARNING("check assoc id %d | %d\n", counter++, error); + + error |= ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) + == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)); + if (error) + IWL_WARNING("check CCK and short slot %d | %d\n", + counter++, error); + + error |= ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) + == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)); + if (error) + IWL_WARNING("check CCK & auto detect %d | %d\n", + counter++, error); + + error |= ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK | + RXON_FLG_TGG_PROTECT_MSK)) == RXON_FLG_TGG_PROTECT_MSK); + if (error) + IWL_WARNING("check TGG and auto detect %d | %d\n", + counter++, error); + + if (error) + IWL_WARNING("Tuning to channel %d\n", + le16_to_cpu(rxon->channel)); + + if (error) { + IWL_ERROR("Not a valid iwl4965_rxon_assoc_cmd field values\n"); + return -1; + } + return 0; +} + +/** + * iwl4965_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed + * @priv: staging_rxon is compared to active_rxon + * + * If the RXON structure is changing enough to require a new tune, + * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that + * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. + */ +static int iwl4965_full_rxon_required(struct iwl_priv *priv) +{ + + /* These items are only settable from the full RXON command */ + if (!(iwl_is_associated(priv)) || + compare_ether_addr(priv->staging_rxon.bssid_addr, + priv->active_rxon.bssid_addr) || + compare_ether_addr(priv->staging_rxon.node_addr, + priv->active_rxon.node_addr) || + compare_ether_addr(priv->staging_rxon.wlap_bssid_addr, + priv->active_rxon.wlap_bssid_addr) || + (priv->staging_rxon.dev_type != priv->active_rxon.dev_type) || + (priv->staging_rxon.channel != priv->active_rxon.channel) || + (priv->staging_rxon.air_propagation != + priv->active_rxon.air_propagation) || + (priv->staging_rxon.ofdm_ht_single_stream_basic_rates != + priv->active_rxon.ofdm_ht_single_stream_basic_rates) || + (priv->staging_rxon.ofdm_ht_dual_stream_basic_rates != + priv->active_rxon.ofdm_ht_dual_stream_basic_rates) || + (priv->staging_rxon.rx_chain != priv->active_rxon.rx_chain) || + (priv->staging_rxon.assoc_id != priv->active_rxon.assoc_id)) + return 1; + + /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can + * be updated with the RXON_ASSOC command -- however only some + * flag transitions are allowed using RXON_ASSOC */ + + /* Check if we are not switching bands */ + if ((priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) != + (priv->active_rxon.flags & RXON_FLG_BAND_24G_MSK)) + return 1; + + /* Check if we are switching association toggle */ + if ((priv->staging_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) != + (priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK)) + return 1; + + return 0; +} + +/** + * iwl4965_commit_rxon - commit staging_rxon to hardware + * + * The RXON command in staging_rxon is committed to the hardware and + * the active_rxon structure is updated with the new data. This + * function correctly transitions out of the RXON_ASSOC_MSK state if + * a HW tune is required based on the RXON structure changes. + */ +static int iwl4965_commit_rxon(struct iwl_priv *priv) +{ + /* cast away the const for active_rxon in this function */ + struct iwl_rxon_cmd *active_rxon = (void *)&priv->active_rxon; + DECLARE_MAC_BUF(mac); + int ret; + bool new_assoc = + !!(priv->staging_rxon.filter_flags & RXON_FILTER_ASSOC_MSK); + + if (!iwl_is_alive(priv)) + return -EBUSY; + + /* always get timestamp with Rx frame */ + priv->staging_rxon.flags |= RXON_FLG_TSF2HOST_MSK; + /* allow CTS-to-self if possible. this is relevant only for + * 5000, but will not damage 4965 */ + priv->staging_rxon.flags |= RXON_FLG_SELF_CTS_EN; + + ret = iwl4965_check_rxon_cmd(&priv->staging_rxon); + if (ret) { + IWL_ERROR("Invalid RXON configuration. Not committing.\n"); + return -EINVAL; + } + + /* If we don't need to send a full RXON, we can use + * iwl4965_rxon_assoc_cmd which is used to reconfigure filter + * and other flags for the current radio configuration. */ + if (!iwl4965_full_rxon_required(priv)) { + ret = iwl_send_rxon_assoc(priv); + if (ret) { + IWL_ERROR("Error setting RXON_ASSOC (%d)\n", ret); + return ret; + } + + memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon)); + return 0; + } + + /* station table will be cleared */ + priv->assoc_station_added = 0; + + /* If we are currently associated and the new config requires + * an RXON_ASSOC and the new config wants the associated mask enabled, + * we must clear the associated from the active configuration + * before we apply the new config */ + if (iwl_is_associated(priv) && new_assoc) { + IWL_DEBUG_INFO("Toggling associated bit on current RXON\n"); + active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + + ret = iwl_send_cmd_pdu(priv, REPLY_RXON, + sizeof(struct iwl_rxon_cmd), + &priv->active_rxon); + + /* If the mask clearing failed then we set + * active_rxon back to what it was previously */ + if (ret) { + active_rxon->filter_flags |= RXON_FILTER_ASSOC_MSK; + IWL_ERROR("Error clearing ASSOC_MSK (%d)\n", ret); + return ret; + } + } + + IWL_DEBUG_INFO("Sending RXON\n" + "* with%s RXON_FILTER_ASSOC_MSK\n" + "* channel = %d\n" + "* bssid = %s\n", + (new_assoc ? "" : "out"), + le16_to_cpu(priv->staging_rxon.channel), + print_mac(mac, priv->staging_rxon.bssid_addr)); + + iwl4965_set_rxon_hwcrypto(priv, !priv->hw_params.sw_crypto); + + /* Apply the new configuration + * RXON unassoc clears the station table in uCode, send it before + * we add the bcast station. If assoc bit is set, we will send RXON + * after having added the bcast and bssid station. + */ + if (!new_assoc) { + ret = iwl_send_cmd_pdu(priv, REPLY_RXON, + sizeof(struct iwl_rxon_cmd), &priv->staging_rxon); + if (ret) { + IWL_ERROR("Error setting new RXON (%d)\n", ret); + return ret; + } + memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon)); + } + + iwl_clear_stations_table(priv); + + if (!priv->error_recovering) + priv->start_calib = 0; + + /* Add the broadcast address so we can send broadcast frames */ + if (iwl_rxon_add_station(priv, iwl_bcast_addr, 0) == + IWL_INVALID_STATION) { + IWL_ERROR("Error adding BROADCAST address for transmit.\n"); + return -EIO; + } + + /* If we have set the ASSOC_MSK and we are in BSS mode then + * add the IWL_AP_ID to the station rate table */ + if (new_assoc) { + if (priv->iw_mode == IEEE80211_IF_TYPE_STA) { + ret = iwl_rxon_add_station(priv, + priv->active_rxon.bssid_addr, 1); + if (ret == IWL_INVALID_STATION) { + IWL_ERROR("Error adding AP address for TX.\n"); + return -EIO; + } + priv->assoc_station_added = 1; + if (priv->default_wep_key && + iwl_send_static_wepkey_cmd(priv, 0)) + IWL_ERROR("Could not send WEP static key.\n"); + } + + /* Apply the new configuration + * RXON assoc doesn't clear the station table in uCode, + */ + ret = iwl_send_cmd_pdu(priv, REPLY_RXON, + sizeof(struct iwl_rxon_cmd), &priv->staging_rxon); + if (ret) { + IWL_ERROR("Error setting new RXON (%d)\n", ret); + return ret; + } + memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon)); + } + + iwl_init_sensitivity(priv); + + /* If we issue a new RXON command which required a tune then we must + * send a new TXPOWER command or we won't be able to Tx any frames */ + ret = iwl_set_tx_power(priv, priv->tx_power_user_lmt, true); + if (ret) { + IWL_ERROR("Error sending TX power (%d)\n", ret); + return ret; + } + + return 0; +} + +void iwl4965_update_chain_flags(struct iwl_priv *priv) +{ + + iwl_set_rxon_chain(priv); + iwl4965_commit_rxon(priv); +} + +static int iwl4965_send_bt_config(struct iwl_priv *priv) +{ + struct iwl4965_bt_cmd bt_cmd = { + .flags = 3, + .lead_time = 0xAA, + .max_kill = 1, + .kill_ack_mask = 0, + .kill_cts_mask = 0, + }; + + return iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG, + sizeof(struct iwl4965_bt_cmd), &bt_cmd); +} + +static void iwl_clear_free_frames(struct iwl_priv *priv) +{ + struct list_head *element; + + IWL_DEBUG_INFO("%d frames on pre-allocated heap on clear.\n", + priv->frames_count); + + while (!list_empty(&priv->free_frames)) { + element = priv->free_frames.next; + list_del(element); + kfree(list_entry(element, struct iwl_frame, list)); + priv->frames_count--; + } + + if (priv->frames_count) { + IWL_WARNING("%d frames still in use. Did we lose one?\n", + priv->frames_count); + priv->frames_count = 0; + } +} + +static struct iwl_frame *iwl_get_free_frame(struct iwl_priv *priv) +{ + struct iwl_frame *frame; + struct list_head *element; + if (list_empty(&priv->free_frames)) { + frame = kzalloc(sizeof(*frame), GFP_KERNEL); + if (!frame) { + IWL_ERROR("Could not allocate frame!\n"); + return NULL; + } + + priv->frames_count++; + return frame; + } + + element = priv->free_frames.next; + list_del(element); + return list_entry(element, struct iwl_frame, list); +} + +static void iwl_free_frame(struct iwl_priv *priv, struct iwl_frame *frame) +{ + memset(frame, 0, sizeof(*frame)); + list_add(&frame->list, &priv->free_frames); +} + +static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv, + struct ieee80211_hdr *hdr, + const u8 *dest, int left) +{ + if (!iwl_is_associated(priv) || !priv->ibss_beacon || + ((priv->iw_mode != IEEE80211_IF_TYPE_IBSS) && + (priv->iw_mode != IEEE80211_IF_TYPE_AP))) + return 0; + + if (priv->ibss_beacon->len > left) + return 0; + + memcpy(hdr, priv->ibss_beacon->data, priv->ibss_beacon->len); + + return priv->ibss_beacon->len; +} + +static u8 iwl4965_rate_get_lowest_plcp(struct iwl_priv *priv) +{ + int i; + int rate_mask; + + /* Set rate mask*/ + if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) + rate_mask = priv->active_rate_basic & 0xF; + else + rate_mask = priv->active_rate_basic & 0xFF0; + + /* Find lowest valid rate */ + for (i = IWL_RATE_1M_INDEX; i != IWL_RATE_INVALID; + i = iwl_rates[i].next_ieee) { + if (rate_mask & (1 << i)) + return iwl_rates[i].plcp; + } + + /* No valid rate was found. Assign the lowest one */ + if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) + return IWL_RATE_1M_PLCP; + else + return IWL_RATE_6M_PLCP; +} + +unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv, + struct iwl_frame *frame, u8 rate) +{ + struct iwl_tx_beacon_cmd *tx_beacon_cmd; + unsigned int frame_size; + + tx_beacon_cmd = &frame->u.beacon; + memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); + + tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id; + tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + + frame_size = iwl_fill_beacon_frame(priv, tx_beacon_cmd->frame, + iwl_bcast_addr, + sizeof(frame->u) - sizeof(*tx_beacon_cmd)); + + BUG_ON(frame_size > MAX_MPDU_SIZE); + tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size); + + if ((rate == IWL_RATE_1M_PLCP) || (rate >= IWL_RATE_2M_PLCP)) + tx_beacon_cmd->tx.rate_n_flags = + iwl_hw_set_rate_n_flags(rate, RATE_MCS_CCK_MSK); + else + tx_beacon_cmd->tx.rate_n_flags = + iwl_hw_set_rate_n_flags(rate, 0); + + tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | + TX_CMD_FLG_TSF_MSK | + TX_CMD_FLG_STA_RATE_MSK; + + return sizeof(*tx_beacon_cmd) + frame_size; +} +static int iwl4965_send_beacon_cmd(struct iwl_priv *priv) +{ + struct iwl_frame *frame; + unsigned int frame_size; + int rc; + u8 rate; + + frame = iwl_get_free_frame(priv); + + if (!frame) { + IWL_ERROR("Could not obtain free frame buffer for beacon " + "command.\n"); + return -ENOMEM; + } + + rate = iwl4965_rate_get_lowest_plcp(priv); + + frame_size = iwl4965_hw_get_beacon_cmd(priv, frame, rate); + + rc = iwl_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size, + &frame->u.cmd[0]); + + iwl_free_frame(priv, frame); + + return rc; +} + +/****************************************************************************** + * + * Misc. internal state and helper functions + * + ******************************************************************************/ + +static void iwl4965_ht_conf(struct iwl_priv *priv, + struct ieee80211_bss_conf *bss_conf) +{ + struct ieee80211_ht_info *ht_conf = bss_conf->ht_conf; + struct ieee80211_ht_bss_info *ht_bss_conf = bss_conf->ht_bss_conf; + struct iwl_ht_info *iwl_conf = &priv->current_ht_config; + + IWL_DEBUG_MAC80211("enter: \n"); + + iwl_conf->is_ht = bss_conf->assoc_ht; + + if (!iwl_conf->is_ht) + return; + + priv->ps_mode = (u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2); + + if (ht_conf->cap & IEEE80211_HT_CAP_SGI_20) + iwl_conf->sgf |= HT_SHORT_GI_20MHZ; + if (ht_conf->cap & IEEE80211_HT_CAP_SGI_40) + iwl_conf->sgf |= HT_SHORT_GI_40MHZ; + + iwl_conf->is_green_field = !!(ht_conf->cap & IEEE80211_HT_CAP_GRN_FLD); + iwl_conf->max_amsdu_size = + !!(ht_conf->cap & IEEE80211_HT_CAP_MAX_AMSDU); + + iwl_conf->supported_chan_width = + !!(ht_conf->cap & IEEE80211_HT_CAP_SUP_WIDTH); + iwl_conf->extension_chan_offset = + ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_SEC_OFFSET; + /* If no above or below channel supplied disable FAT channel */ + if (iwl_conf->extension_chan_offset != IEEE80211_HT_IE_CHA_SEC_ABOVE && + iwl_conf->extension_chan_offset != IEEE80211_HT_IE_CHA_SEC_BELOW) { + iwl_conf->extension_chan_offset = IEEE80211_HT_IE_CHA_SEC_NONE; + iwl_conf->supported_chan_width = 0; + } + + iwl_conf->tx_mimo_ps_mode = + (u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2); + memcpy(iwl_conf->supp_mcs_set, ht_conf->supp_mcs_set, 16); + + iwl_conf->control_channel = ht_bss_conf->primary_channel; + iwl_conf->tx_chan_width = + !!(ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_WIDTH); + iwl_conf->ht_protection = + ht_bss_conf->bss_op_mode & IEEE80211_HT_IE_HT_PROTECTION; + iwl_conf->non_GF_STA_present = + !!(ht_bss_conf->bss_op_mode & IEEE80211_HT_IE_NON_GF_STA_PRSNT); + + IWL_DEBUG_MAC80211("control channel %d\n", iwl_conf->control_channel); + IWL_DEBUG_MAC80211("leave\n"); +} + +/* + * QoS support +*/ +static void iwl_activate_qos(struct iwl_priv *priv, u8 force) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!priv->qos_data.qos_enable) + return; + + priv->qos_data.def_qos_parm.qos_flags = 0; + + if (priv->qos_data.qos_cap.q_AP.queue_request && + !priv->qos_data.qos_cap.q_AP.txop_request) + priv->qos_data.def_qos_parm.qos_flags |= + QOS_PARAM_FLG_TXOP_TYPE_MSK; + if (priv->qos_data.qos_active) + priv->qos_data.def_qos_parm.qos_flags |= + QOS_PARAM_FLG_UPDATE_EDCA_MSK; + + if (priv->current_ht_config.is_ht) + priv->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; + + if (force || iwl_is_associated(priv)) { + IWL_DEBUG_QOS("send QoS cmd with Qos active=%d FLAGS=0x%X\n", + priv->qos_data.qos_active, + priv->qos_data.def_qos_parm.qos_flags); + + iwl_send_cmd_pdu_async(priv, REPLY_QOS_PARAM, + sizeof(struct iwl_qosparam_cmd), + &priv->qos_data.def_qos_parm, NULL); + } +} + +#define MAX_UCODE_BEACON_INTERVAL 4096 + +static __le16 iwl4965_adjust_beacon_interval(u16 beacon_val) +{ + u16 new_val = 0; + u16 beacon_factor = 0; + + beacon_factor = + (beacon_val + MAX_UCODE_BEACON_INTERVAL) + / MAX_UCODE_BEACON_INTERVAL; + new_val = beacon_val / beacon_factor; + + return cpu_to_le16(new_val); +} + +static void iwl4965_setup_rxon_timing(struct iwl_priv *priv) +{ + u64 interval_tm_unit; + u64 tsf, result; + unsigned long flags; + struct ieee80211_conf *conf = NULL; + u16 beacon_int = 0; + + conf = ieee80211_get_hw_conf(priv->hw); + + spin_lock_irqsave(&priv->lock, flags); + priv->rxon_timing.timestamp.dw[1] = cpu_to_le32(priv->timestamp >> 32); + priv->rxon_timing.timestamp.dw[0] = + cpu_to_le32(priv->timestamp & 0xFFFFFFFF); + + priv->rxon_timing.listen_interval = cpu_to_le16(conf->listen_interval); + + tsf = priv->timestamp; + + beacon_int = priv->beacon_int; + spin_unlock_irqrestore(&priv->lock, flags); + + if (priv->iw_mode == IEEE80211_IF_TYPE_STA) { + if (beacon_int == 0) { + priv->rxon_timing.beacon_interval = cpu_to_le16(100); + priv->rxon_timing.beacon_init_val = cpu_to_le32(102400); + } else { + priv->rxon_timing.beacon_interval = + cpu_to_le16(beacon_int); + priv->rxon_timing.beacon_interval = + iwl4965_adjust_beacon_interval( + le16_to_cpu(priv->rxon_timing.beacon_interval)); + } + + priv->rxon_timing.atim_window = 0; + } else { + priv->rxon_timing.beacon_interval = + iwl4965_adjust_beacon_interval(conf->beacon_int); + /* TODO: we need to get atim_window from upper stack + * for now we set to 0 */ + priv->rxon_timing.atim_window = 0; + } + + interval_tm_unit = + (le16_to_cpu(priv->rxon_timing.beacon_interval) * 1024); + result = do_div(tsf, interval_tm_unit); + priv->rxon_timing.beacon_init_val = + cpu_to_le32((u32) ((u64) interval_tm_unit - result)); + + IWL_DEBUG_ASSOC + ("beacon interval %d beacon timer %d beacon tim %d\n", + le16_to_cpu(priv->rxon_timing.beacon_interval), + le32_to_cpu(priv->rxon_timing.beacon_init_val), + le16_to_cpu(priv->rxon_timing.atim_window)); +} + +static void iwl_set_flags_for_band(struct iwl_priv *priv, + enum ieee80211_band band) +{ + if (band == IEEE80211_BAND_5GHZ) { + priv->staging_rxon.flags &= + ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK + | RXON_FLG_CCK_MSK); + priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK; + } else { + /* Copied from iwl4965_post_associate() */ + if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) + priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK; + else + priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + + if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) + priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + + priv->staging_rxon.flags |= RXON_FLG_BAND_24G_MSK; + priv->staging_rxon.flags |= RXON_FLG_AUTO_DETECT_MSK; + priv->staging_rxon.flags &= ~RXON_FLG_CCK_MSK; + } +} + +/* + * initialize rxon structure with default values from eeprom + */ +static void iwl4965_connection_init_rx_config(struct iwl_priv *priv) +{ + const struct iwl_channel_info *ch_info; + + memset(&priv->staging_rxon, 0, sizeof(priv->staging_rxon)); + + switch (priv->iw_mode) { + case IEEE80211_IF_TYPE_AP: + priv->staging_rxon.dev_type = RXON_DEV_TYPE_AP; + break; + + case IEEE80211_IF_TYPE_STA: + priv->staging_rxon.dev_type = RXON_DEV_TYPE_ESS; + priv->staging_rxon.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; + break; + + case IEEE80211_IF_TYPE_IBSS: + priv->staging_rxon.dev_type = RXON_DEV_TYPE_IBSS; + priv->staging_rxon.flags = RXON_FLG_SHORT_PREAMBLE_MSK; + priv->staging_rxon.filter_flags = RXON_FILTER_BCON_AWARE_MSK | + RXON_FILTER_ACCEPT_GRP_MSK; + break; + + case IEEE80211_IF_TYPE_MNTR: + priv->staging_rxon.dev_type = RXON_DEV_TYPE_SNIFFER; + priv->staging_rxon.filter_flags = RXON_FILTER_PROMISC_MSK | + RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_ACCEPT_GRP_MSK; + break; + default: + IWL_ERROR("Unsupported interface type %d\n", priv->iw_mode); + break; + } + +#if 0 + /* TODO: Figure out when short_preamble would be set and cache from + * that */ + if (!hw_to_local(priv->hw)->short_preamble) + priv->staging_rxon.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + else + priv->staging_rxon.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; +#endif + + ch_info = iwl_get_channel_info(priv, priv->band, + le16_to_cpu(priv->active_rxon.channel)); + + if (!ch_info) + ch_info = &priv->channel_info[0]; + + /* + * in some case A channels are all non IBSS + * in this case force B/G channel + */ + if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && + !(is_channel_ibss(ch_info))) + ch_info = &priv->channel_info[0]; + + priv->staging_rxon.channel = cpu_to_le16(ch_info->channel); + priv->band = ch_info->band; + + iwl_set_flags_for_band(priv, priv->band); + + priv->staging_rxon.ofdm_basic_rates = + (IWL_OFDM_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF; + priv->staging_rxon.cck_basic_rates = + (IWL_CCK_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF; + + priv->staging_rxon.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK | + RXON_FLG_CHANNEL_MODE_PURE_40_MSK); + memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); + memcpy(priv->staging_rxon.wlap_bssid_addr, priv->mac_addr, ETH_ALEN); + priv->staging_rxon.ofdm_ht_single_stream_basic_rates = 0xff; + priv->staging_rxon.ofdm_ht_dual_stream_basic_rates = 0xff; + iwl_set_rxon_chain(priv); +} + +static int iwl4965_set_mode(struct iwl_priv *priv, int mode) +{ + priv->iw_mode = mode; + + iwl4965_connection_init_rx_config(priv); + memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); + + iwl_clear_stations_table(priv); + + /* dont commit rxon if rf-kill is on*/ + if (!iwl_is_ready_rf(priv)) + return -EAGAIN; + + cancel_delayed_work(&priv->scan_check); + if (iwl_scan_cancel_timeout(priv, 100)) { + IWL_WARNING("Aborted scan still in progress after 100ms\n"); + IWL_DEBUG_MAC80211("leaving - scan abort failed.\n"); + return -EAGAIN; + } + + iwl4965_commit_rxon(priv); + + return 0; +} + +static void iwl4965_set_rate(struct iwl_priv *priv) +{ + const struct ieee80211_supported_band *hw = NULL; + struct ieee80211_rate *rate; + int i; + + hw = iwl_get_hw_mode(priv, priv->band); + if (!hw) { + IWL_ERROR("Failed to set rate: unable to get hw mode\n"); + return; + } + + priv->active_rate = 0; + priv->active_rate_basic = 0; + + for (i = 0; i < hw->n_bitrates; i++) { + rate = &(hw->bitrates[i]); + if (rate->hw_value < IWL_RATE_COUNT) + priv->active_rate |= (1 << rate->hw_value); + } + + IWL_DEBUG_RATE("Set active_rate = %0x, active_rate_basic = %0x\n", + priv->active_rate, priv->active_rate_basic); + + /* + * If a basic rate is configured, then use it (adding IWL_RATE_1M_MASK) + * otherwise set it to the default of all CCK rates and 6, 12, 24 for + * OFDM + */ + if (priv->active_rate_basic & IWL_CCK_BASIC_RATES_MASK) + priv->staging_rxon.cck_basic_rates = + ((priv->active_rate_basic & + IWL_CCK_RATES_MASK) >> IWL_FIRST_CCK_RATE) & 0xF; + else + priv->staging_rxon.cck_basic_rates = + (IWL_CCK_BASIC_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF; + + if (priv->active_rate_basic & IWL_OFDM_BASIC_RATES_MASK) + priv->staging_rxon.ofdm_basic_rates = + ((priv->active_rate_basic & + (IWL_OFDM_BASIC_RATES_MASK | IWL_RATE_6M_MASK)) >> + IWL_FIRST_OFDM_RATE) & 0xFF; + else + priv->staging_rxon.ofdm_basic_rates = + (IWL_OFDM_BASIC_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF; +} + +#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT + +#include "iwl-spectrum.h" + +#define BEACON_TIME_MASK_LOW 0x00FFFFFF +#define BEACON_TIME_MASK_HIGH 0xFF000000 +#define TIME_UNIT 1024 + +/* + * extended beacon time format + * time in usec will be changed into a 32-bit value in 8:24 format + * the high 1 byte is the beacon counts + * the lower 3 bytes is the time in usec within one beacon interval + */ + +static u32 iwl4965_usecs_to_beacons(u32 usec, u32 beacon_interval) +{ + u32 quot; + u32 rem; + u32 interval = beacon_interval * 1024; + + if (!interval || !usec) + return 0; + + quot = (usec / interval) & (BEACON_TIME_MASK_HIGH >> 24); + rem = (usec % interval) & BEACON_TIME_MASK_LOW; + + return (quot << 24) + rem; +} + +/* base is usually what we get from ucode with each received frame, + * the same as HW timer counter counting down + */ + +static __le32 iwl4965_add_beacon_time(u32 base, u32 addon, u32 beacon_interval) +{ + u32 base_low = base & BEACON_TIME_MASK_LOW; + u32 addon_low = addon & BEACON_TIME_MASK_LOW; + u32 interval = beacon_interval * TIME_UNIT; + u32 res = (base & BEACON_TIME_MASK_HIGH) + + (addon & BEACON_TIME_MASK_HIGH); + + if (base_low > addon_low) + res += base_low - addon_low; + else if (base_low < addon_low) { + res += interval + base_low - addon_low; + res += (1 << 24); + } else + res += (1 << 24); + + return cpu_to_le32(res); +} + +static int iwl4965_get_measurement(struct iwl_priv *priv, + struct ieee80211_measurement_params *params, + u8 type) +{ + struct iwl4965_spectrum_cmd spectrum; + struct iwl_rx_packet *res; + struct iwl_host_cmd cmd = { + .id = REPLY_SPECTRUM_MEASUREMENT_CMD, + .data = (void *)&spectrum, + .meta.flags = CMD_WANT_SKB, + }; + u32 add_time = le64_to_cpu(params->start_time); + int rc; + int spectrum_resp_status; + int duration = le16_to_cpu(params->duration); + + if (iwl_is_associated(priv)) + add_time = + iwl4965_usecs_to_beacons( + le64_to_cpu(params->start_time) - priv->last_tsf, + le16_to_cpu(priv->rxon_timing.beacon_interval)); + + memset(&spectrum, 0, sizeof(spectrum)); + + spectrum.channel_count = cpu_to_le16(1); + spectrum.flags = + RXON_FLG_TSF2HOST_MSK | RXON_FLG_ANT_A_MSK | RXON_FLG_DIS_DIV_MSK; + spectrum.filter_flags = MEASUREMENT_FILTER_FLAG; + cmd.len = sizeof(spectrum); + spectrum.len = cpu_to_le16(cmd.len - sizeof(spectrum.len)); + + if (iwl_is_associated(priv)) + spectrum.start_time = + iwl4965_add_beacon_time(priv->last_beacon_time, + add_time, + le16_to_cpu(priv->rxon_timing.beacon_interval)); + else + spectrum.start_time = 0; + + spectrum.channels[0].duration = cpu_to_le32(duration * TIME_UNIT); + spectrum.channels[0].channel = params->channel; + spectrum.channels[0].type = type; + if (priv->active_rxon.flags & RXON_FLG_BAND_24G_MSK) + spectrum.flags |= RXON_FLG_BAND_24G_MSK | + RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK; + + rc = iwl_send_cmd_sync(priv, &cmd); + if (rc) + return rc; + + res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; + if (res->hdr.flags & IWL_CMD_FAILED_MSK) { + IWL_ERROR("Bad return from REPLY_RX_ON_ASSOC command\n"); + rc = -EIO; + } + + spectrum_resp_status = le16_to_cpu(res->u.spectrum.status); + switch (spectrum_resp_status) { + case 0: /* Command will be handled */ + if (res->u.spectrum.id != 0xff) { + IWL_DEBUG_INFO + ("Replaced existing measurement: %d\n", + res->u.spectrum.id); + priv->measurement_status &= ~MEASUREMENT_READY; + } + priv->measurement_status |= MEASUREMENT_ACTIVE; + rc = 0; + break; + + case 1: /* Command will not be handled */ + rc = -EAGAIN; + break; + } + + dev_kfree_skb_any(cmd.meta.u.skb); + + return rc; +} +#endif + +/****************************************************************************** + * + * Generic RX handler implementations + * + ******************************************************************************/ +static void iwl_rx_reply_alive(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_alive_resp *palive; + struct delayed_work *pwork; + + palive = &pkt->u.alive_frame; + + IWL_DEBUG_INFO("Alive ucode status 0x%08X revision " + "0x%01X 0x%01X\n", + palive->is_valid, palive->ver_type, + palive->ver_subtype); + + if (palive->ver_subtype == INITIALIZE_SUBTYPE) { + IWL_DEBUG_INFO("Initialization Alive received.\n"); + memcpy(&priv->card_alive_init, + &pkt->u.alive_frame, + sizeof(struct iwl_init_alive_resp)); + pwork = &priv->init_alive_start; + } else { + IWL_DEBUG_INFO("Runtime Alive received.\n"); + memcpy(&priv->card_alive, &pkt->u.alive_frame, + sizeof(struct iwl_alive_resp)); + pwork = &priv->alive_start; + } + + /* We delay the ALIVE response by 5ms to + * give the HW RF Kill time to activate... */ + if (palive->is_valid == UCODE_VALID_OK) + queue_delayed_work(priv->workqueue, pwork, + msecs_to_jiffies(5)); + else + IWL_WARNING("uCode did not respond OK.\n"); +} + +static void iwl4965_rx_reply_error(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + + IWL_ERROR("Error Reply type 0x%08X cmd %s (0x%02X) " + "seq 0x%04X ser 0x%08X\n", + le32_to_cpu(pkt->u.err_resp.error_type), + get_cmd_string(pkt->u.err_resp.cmd_id), + pkt->u.err_resp.cmd_id, + le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num), + le32_to_cpu(pkt->u.err_resp.error_info)); +} + +#define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x + +static void iwl4965_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl_rxon_cmd *rxon = (void *)&priv->active_rxon; + struct iwl4965_csa_notification *csa = &(pkt->u.csa_notif); + IWL_DEBUG_11H("CSA notif: channel %d, status %d\n", + le16_to_cpu(csa->channel), le32_to_cpu(csa->status)); + rxon->channel = csa->channel; + priv->staging_rxon.channel = csa->channel; +} + +static void iwl4965_rx_spectrum_measure_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT + struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl4965_spectrum_notification *report = &(pkt->u.spectrum_notif); + + if (!report->state) { + IWL_DEBUG(IWL_DL_11H, + "Spectrum Measure Notification: Start\n"); + return; + } + + memcpy(&priv->measure_report, report, sizeof(*report)); + priv->measurement_status |= MEASUREMENT_READY; +#endif +} + +static void iwl4965_rx_pm_sleep_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWLWIFI_DEBUG + struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl4965_sleep_notification *sleep = &(pkt->u.sleep_notif); + IWL_DEBUG_RX("sleep mode: %d, src: %d\n", + sleep->pm_sleep_mode, sleep->pm_wakeup_src); +#endif +} + +static void iwl4965_rx_pm_debug_statistics_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + IWL_DEBUG_RADIO("Dumping %d bytes of unhandled " + "notification for %s:\n", + le32_to_cpu(pkt->len), get_cmd_string(pkt->hdr.cmd)); + iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->u.raw, le32_to_cpu(pkt->len)); +} + +static void iwl4965_bg_beacon_update(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, beacon_update); + struct sk_buff *beacon; + + /* Pull updated AP beacon from mac80211. will fail if not in AP mode */ + beacon = ieee80211_beacon_get(priv->hw, priv->vif); + + if (!beacon) { + IWL_ERROR("update beacon failed\n"); + return; + } + + mutex_lock(&priv->mutex); + /* new beacon skb is allocated every time; dispose previous.*/ + if (priv->ibss_beacon) + dev_kfree_skb(priv->ibss_beacon); + + priv->ibss_beacon = beacon; + mutex_unlock(&priv->mutex); + + iwl4965_send_beacon_cmd(priv); +} + +/** + * iwl4965_bg_statistics_periodic - Timer callback to queue statistics + * + * This callback is provided in order to send a statistics request. + * + * This timer function is continually reset to execute within + * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION + * was received. We need to ensure we receive the statistics in order + * to update the temperature used for calibrating the TXPOWER. + */ +static void iwl4965_bg_statistics_periodic(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + iwl_send_statistics_request(priv, CMD_ASYNC); +} + +static void iwl4965_rx_beacon_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWLWIFI_DEBUG + struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + struct iwl4965_beacon_notif *beacon = &(pkt->u.beacon_status); + u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); + + IWL_DEBUG_RX("beacon status %x retries %d iss %d " + "tsf %d %d rate %d\n", + le32_to_cpu(beacon->beacon_notify_hdr.u.status) & TX_STATUS_MSK, + beacon->beacon_notify_hdr.failure_frame, + le32_to_cpu(beacon->ibss_mgr_status), + le32_to_cpu(beacon->high_tsf), + le32_to_cpu(beacon->low_tsf), rate); +#endif + + if ((priv->iw_mode == IEEE80211_IF_TYPE_AP) && + (!test_bit(STATUS_EXIT_PENDING, &priv->status))) + queue_work(priv->workqueue, &priv->beacon_update); +} + +/* Handle notification from uCode that card's power state is changing + * due to software, hardware, or critical temperature RFKILL */ +static void iwl4965_rx_card_state_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; + u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); + unsigned long status = priv->status; + + IWL_DEBUG_RF_KILL("Card state received: HW:%s SW:%s\n", + (flags & HW_CARD_DISABLED) ? "Kill" : "On", + (flags & SW_CARD_DISABLED) ? "Kill" : "On"); + + if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | + RF_CARD_DISABLED)) { + + iwl_write32(priv, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + if (!iwl_grab_nic_access(priv)) { + iwl_write_direct32( + priv, HBUS_TARG_MBX_C, + HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); + + iwl_release_nic_access(priv); + } + + if (!(flags & RXON_CARD_DISABLED)) { + iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + if (!iwl_grab_nic_access(priv)) { + iwl_write_direct32( + priv, HBUS_TARG_MBX_C, + HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); + + iwl_release_nic_access(priv); + } + } + + if (flags & RF_CARD_DISABLED) { + iwl_write32(priv, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + iwl_read32(priv, CSR_UCODE_DRV_GP1); + if (!iwl_grab_nic_access(priv)) + iwl_release_nic_access(priv); + } + } + + if (flags & HW_CARD_DISABLED) + set_bit(STATUS_RF_KILL_HW, &priv->status); + else + clear_bit(STATUS_RF_KILL_HW, &priv->status); + + + if (flags & SW_CARD_DISABLED) + set_bit(STATUS_RF_KILL_SW, &priv->status); + else + clear_bit(STATUS_RF_KILL_SW, &priv->status); + + if (!(flags & RXON_CARD_DISABLED)) + iwl_scan_cancel(priv); + + if ((test_bit(STATUS_RF_KILL_HW, &status) != + test_bit(STATUS_RF_KILL_HW, &priv->status)) || + (test_bit(STATUS_RF_KILL_SW, &status) != + test_bit(STATUS_RF_KILL_SW, &priv->status))) + queue_work(priv->workqueue, &priv->rf_kill); + else + wake_up_interruptible(&priv->wait_command_queue); +} + +int iwl4965_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + ret = iwl_grab_nic_access(priv); + if (ret) + goto err; + + if (src == IWL_PWR_SRC_VAUX) { + u32 val; + ret = pci_read_config_dword(priv->pci_dev, PCI_POWER_SOURCE, + &val); + + if (val & PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT) + iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VAUX, + ~APMG_PS_CTRL_MSK_PWR_SRC); + } else { + iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, + ~APMG_PS_CTRL_MSK_PWR_SRC); + } + + iwl_release_nic_access(priv); +err: + spin_unlock_irqrestore(&priv->lock, flags); + return ret; +} + +/** + * iwl4965_setup_rx_handlers - Initialize Rx handler callbacks + * + * Setup the RX handlers for each of the reply types sent from the uCode + * to the host. + * + * This function chains into the hardware specific files for them to setup + * any hardware specific handlers as well. + */ +static void iwl_setup_rx_handlers(struct iwl_priv *priv) +{ + priv->rx_handlers[REPLY_ALIVE] = iwl_rx_reply_alive; + priv->rx_handlers[REPLY_ERROR] = iwl4965_rx_reply_error; + priv->rx_handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl4965_rx_csa; + priv->rx_handlers[SPECTRUM_MEASURE_NOTIFICATION] = + iwl4965_rx_spectrum_measure_notif; + priv->rx_handlers[PM_SLEEP_NOTIFICATION] = iwl4965_rx_pm_sleep_notif; + priv->rx_handlers[PM_DEBUG_STATISTIC_NOTIFIC] = + iwl4965_rx_pm_debug_statistics_notif; + priv->rx_handlers[BEACON_NOTIFICATION] = iwl4965_rx_beacon_notif; + + /* + * The same handler is used for both the REPLY to a discrete + * statistics request from the host as well as for the periodic + * statistics notifications (after received beacons) from the uCode. + */ + priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_rx_statistics; + priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl_rx_statistics; + + iwl_setup_rx_scan_handlers(priv); + + /* status change handler */ + priv->rx_handlers[CARD_STATE_NOTIFICATION] = iwl4965_rx_card_state_notif; + + priv->rx_handlers[MISSED_BEACONS_NOTIFICATION] = + iwl_rx_missed_beacon_notif; + /* Rx handlers */ + priv->rx_handlers[REPLY_RX_PHY_CMD] = iwl_rx_reply_rx_phy; + priv->rx_handlers[REPLY_RX_MPDU_CMD] = iwl_rx_reply_rx; + /* block ack */ + priv->rx_handlers[REPLY_COMPRESSED_BA] = iwl_rx_reply_compressed_ba; + /* Set up hardware specific Rx handlers */ + priv->cfg->ops->lib->rx_handler_setup(priv); +} + +/* + * this should be called while priv->lock is locked +*/ +static void __iwl_rx_replenish(struct iwl_priv *priv) +{ + iwl_rx_allocate(priv); + iwl_rx_queue_restock(priv); +} + + +/** + * iwl_rx_handle - Main entry function for receiving responses from uCode + * + * Uses the priv->rx_handlers callback function array to invoke + * the appropriate handlers, including command responses, + * frame-received notifications, and other notifications. + */ +void iwl_rx_handle(struct iwl_priv *priv) +{ + struct iwl_rx_mem_buffer *rxb; + struct iwl_rx_packet *pkt; + struct iwl_rx_queue *rxq = &priv->rxq; + u32 r, i; + int reclaim; + unsigned long flags; + u8 fill_rx = 0; + u32 count = 8; + + /* uCode's read index (stored in shared DRAM) indicates the last Rx + * buffer that the driver may process (last buffer filled by ucode). */ + r = priv->cfg->ops->lib->shared_mem_rx_idx(priv); + i = rxq->read; + + /* Rx interrupt, but nothing sent from uCode */ + if (i == r) + IWL_DEBUG(IWL_DL_RX, "r = %d, i = %d\n", r, i); + + if (iwl_rx_queue_space(rxq) > (RX_QUEUE_SIZE / 2)) + fill_rx = 1; + + while (i != r) { + rxb = rxq->queue[i]; + + /* If an RXB doesn't have a Rx queue slot associated with it, + * then a bug has been introduced in the queue refilling + * routines -- catch it here */ + BUG_ON(rxb == NULL); + + rxq->queue[i] = NULL; + + pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr, + priv->hw_params.rx_buf_size, + PCI_DMA_FROMDEVICE); + pkt = (struct iwl_rx_packet *)rxb->skb->data; + + /* Reclaim a command buffer only if this packet is a response + * to a (driver-originated) command. + * If the packet (e.g. Rx frame) originated from uCode, + * there is no command buffer to reclaim. + * Ucode should set SEQ_RX_FRAME bit if ucode-originated, + * but apparently a few don't get set; catch them here. */ + reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) && + (pkt->hdr.cmd != REPLY_RX_PHY_CMD) && + (pkt->hdr.cmd != REPLY_RX) && + (pkt->hdr.cmd != REPLY_COMPRESSED_BA) && + (pkt->hdr.cmd != STATISTICS_NOTIFICATION) && + (pkt->hdr.cmd != REPLY_TX); + + /* Based on type of command response or notification, + * handle those that need handling via function in + * rx_handlers table. See iwl4965_setup_rx_handlers() */ + if (priv->rx_handlers[pkt->hdr.cmd]) { + IWL_DEBUG(IWL_DL_RX, "r = %d, i = %d, %s, 0x%02x\n", r, + i, get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); + priv->rx_handlers[pkt->hdr.cmd] (priv, rxb); + } else { + /* No handling needed */ + IWL_DEBUG(IWL_DL_RX, + "r %d i %d No handler needed for %s, 0x%02x\n", + r, i, get_cmd_string(pkt->hdr.cmd), + pkt->hdr.cmd); + } + + if (reclaim) { + /* Invoke any callbacks, transfer the skb to caller, and + * fire off the (possibly) blocking iwl_send_cmd() + * as we reclaim the driver command queue */ + if (rxb && rxb->skb) + iwl_tx_cmd_complete(priv, rxb); + else + IWL_WARNING("Claim null rxb?\n"); + } + + /* For now we just don't re-use anything. We can tweak this + * later to try and re-use notification packets and SKBs that + * fail to Rx correctly */ + if (rxb->skb != NULL) { + priv->alloc_rxb_skb--; + dev_kfree_skb_any(rxb->skb); + rxb->skb = NULL; + } + + pci_unmap_single(priv->pci_dev, rxb->dma_addr, + priv->hw_params.rx_buf_size, + PCI_DMA_FROMDEVICE); + spin_lock_irqsave(&rxq->lock, flags); + list_add_tail(&rxb->list, &priv->rxq.rx_used); + spin_unlock_irqrestore(&rxq->lock, flags); + i = (i + 1) & RX_QUEUE_MASK; + /* If there are a lot of unused frames, + * restock the Rx queue so ucode wont assert. */ + if (fill_rx) { + count++; + if (count >= 8) { + priv->rxq.read = i; + __iwl_rx_replenish(priv); + count = 0; + } + } + } + + /* Backtrack one entry */ + priv->rxq.read = i; + iwl_rx_queue_restock(priv); +} + +#ifdef CONFIG_IWLWIFI_DEBUG +static void iwl4965_print_rx_config_cmd(struct iwl_priv *priv) +{ + struct iwl_rxon_cmd *rxon = &priv->staging_rxon; + DECLARE_MAC_BUF(mac); + + IWL_DEBUG_RADIO("RX CONFIG:\n"); + iwl_print_hex_dump(priv, IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); + IWL_DEBUG_RADIO("u16 channel: 0x%x\n", le16_to_cpu(rxon->channel)); + IWL_DEBUG_RADIO("u32 flags: 0x%08X\n", le32_to_cpu(rxon->flags)); + IWL_DEBUG_RADIO("u32 filter_flags: 0x%08x\n", + le32_to_cpu(rxon->filter_flags)); + IWL_DEBUG_RADIO("u8 dev_type: 0x%x\n", rxon->dev_type); + IWL_DEBUG_RADIO("u8 ofdm_basic_rates: 0x%02x\n", + rxon->ofdm_basic_rates); + IWL_DEBUG_RADIO("u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates); + IWL_DEBUG_RADIO("u8[6] node_addr: %s\n", + print_mac(mac, rxon->node_addr)); + IWL_DEBUG_RADIO("u8[6] bssid_addr: %s\n", + print_mac(mac, rxon->bssid_addr)); + IWL_DEBUG_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); +} +#endif + +static void iwl4965_enable_interrupts(struct iwl_priv *priv) +{ + IWL_DEBUG_ISR("Enabling interrupts\n"); + set_bit(STATUS_INT_ENABLED, &priv->status); + iwl_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK); +} + +/* call this function to flush any scheduled tasklet */ +static inline void iwl_synchronize_irq(struct iwl_priv *priv) +{ + /* wait to make sure we flush pedding tasklet*/ + synchronize_irq(priv->pci_dev->irq); + tasklet_kill(&priv->irq_tasklet); +} + +static inline void iwl4965_disable_interrupts(struct iwl_priv *priv) +{ + clear_bit(STATUS_INT_ENABLED, &priv->status); + + /* disable interrupts from uCode/NIC to host */ + iwl_write32(priv, CSR_INT_MASK, 0x00000000); + + /* acknowledge/clear/reset any interrupts still pending + * from uCode or flow handler (Rx/Tx DMA) */ + iwl_write32(priv, CSR_INT, 0xffffffff); + iwl_write32(priv, CSR_FH_INT_STATUS, 0xffffffff); + IWL_DEBUG_ISR("Disabled interrupts\n"); +} + + +/** + * iwl4965_irq_handle_error - called for HW or SW error interrupt from card + */ +static void iwl4965_irq_handle_error(struct iwl_priv *priv) +{ + /* Set the FW error flag -- cleared on iwl4965_down */ + set_bit(STATUS_FW_ERROR, &priv->status); + + /* Cancel currently queued command. */ + clear_bit(STATUS_HCMD_ACTIVE, &priv->status); + +#ifdef CONFIG_IWLWIFI_DEBUG + if (priv->debug_level & IWL_DL_FW_ERRORS) { + iwl_dump_nic_error_log(priv); + iwl_dump_nic_event_log(priv); + iwl4965_print_rx_config_cmd(priv); + } +#endif + + wake_up_interruptible(&priv->wait_command_queue); + + /* Keep the restart process from trying to send host + * commands by clearing the INIT status bit */ + clear_bit(STATUS_READY, &priv->status); + + if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) { + IWL_DEBUG(IWL_DL_FW_ERRORS, + "Restarting adapter due to uCode error.\n"); + + if (iwl_is_associated(priv)) { + memcpy(&priv->recovery_rxon, &priv->active_rxon, + sizeof(priv->recovery_rxon)); + priv->error_recovering = 1; + } + if (priv->cfg->mod_params->restart_fw) + queue_work(priv->workqueue, &priv->restart); + } +} + +static void iwl4965_error_recovery(struct iwl_priv *priv) +{ + unsigned long flags; + + memcpy(&priv->staging_rxon, &priv->recovery_rxon, + sizeof(priv->staging_rxon)); + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl4965_commit_rxon(priv); + + iwl_rxon_add_station(priv, priv->bssid, 1); + + spin_lock_irqsave(&priv->lock, flags); + priv->assoc_id = le16_to_cpu(priv->staging_rxon.assoc_id); + priv->error_recovering = 0; + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void iwl4965_irq_tasklet(struct iwl_priv *priv) +{ + u32 inta, handled = 0; + u32 inta_fh; + unsigned long flags; +#ifdef CONFIG_IWLWIFI_DEBUG + u32 inta_mask; +#endif + + spin_lock_irqsave(&priv->lock, flags); + + /* Ack/clear/reset pending uCode interrupts. + * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, + * and will clear only when CSR_FH_INT_STATUS gets cleared. */ + inta = iwl_read32(priv, CSR_INT); + iwl_write32(priv, CSR_INT, inta); + + /* Ack/clear/reset pending flow-handler (DMA) interrupts. + * Any new interrupts that happen after this, either while we're + * in this tasklet, or later, will show up in next ISR/tasklet. */ + inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); + iwl_write32(priv, CSR_FH_INT_STATUS, inta_fh); + +#ifdef CONFIG_IWLWIFI_DEBUG + if (priv->debug_level & IWL_DL_ISR) { + /* just for debug */ + inta_mask = iwl_read32(priv, CSR_INT_MASK); + IWL_DEBUG_ISR("inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", + inta, inta_mask, inta_fh); + } +#endif + + /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not + * atomic, make sure that inta covers all the interrupts that + * we've discovered, even if FH interrupt came in just after + * reading CSR_INT. */ + if (inta_fh & CSR49_FH_INT_RX_MASK) + inta |= CSR_INT_BIT_FH_RX; + if (inta_fh & CSR49_FH_INT_TX_MASK) + inta |= CSR_INT_BIT_FH_TX; + + /* Now service all interrupt bits discovered above. */ + if (inta & CSR_INT_BIT_HW_ERR) { + IWL_ERROR("Microcode HW error detected. Restarting.\n"); + + /* Tell the device to stop sending interrupts */ + iwl4965_disable_interrupts(priv); + + iwl4965_irq_handle_error(priv); + + handled |= CSR_INT_BIT_HW_ERR; + + spin_unlock_irqrestore(&priv->lock, flags); + + return; + } + +#ifdef CONFIG_IWLWIFI_DEBUG + if (priv->debug_level & (IWL_DL_ISR)) { + /* NIC fires this, but we don't use it, redundant with WAKEUP */ + if (inta & CSR_INT_BIT_SCD) + IWL_DEBUG_ISR("Scheduler finished to transmit " + "the frame/frames.\n"); + + /* Alive notification via Rx interrupt will do the real work */ + if (inta & CSR_INT_BIT_ALIVE) + IWL_DEBUG_ISR("Alive interrupt\n"); + } +#endif + /* Safely ignore these bits for debug checks below */ + inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); + + /* HW RF KILL switch toggled */ + if (inta & CSR_INT_BIT_RF_KILL) { + int hw_rf_kill = 0; + if (!(iwl_read32(priv, CSR_GP_CNTRL) & + CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) + hw_rf_kill = 1; + + IWL_DEBUG(IWL_DL_RF_KILL, "RF_KILL bit toggled to %s.\n", + hw_rf_kill ? "disable radio":"enable radio"); + + /* driver only loads ucode once setting the interface up. + * the driver as well won't allow loading if RFKILL is set + * therefore no need to restart the driver from this handler + */ + if (!hw_rf_kill && !test_bit(STATUS_ALIVE, &priv->status)) + clear_bit(STATUS_RF_KILL_HW, &priv->status); + + handled |= CSR_INT_BIT_RF_KILL; + } + + /* Chip got too hot and stopped itself */ + if (inta & CSR_INT_BIT_CT_KILL) { + IWL_ERROR("Microcode CT kill error detected.\n"); + handled |= CSR_INT_BIT_CT_KILL; + } + + /* Error detected by uCode */ + if (inta & CSR_INT_BIT_SW_ERR) { + IWL_ERROR("Microcode SW error detected. Restarting 0x%X.\n", + inta); + iwl4965_irq_handle_error(priv); + handled |= CSR_INT_BIT_SW_ERR; + } + + /* uCode wakes up after power-down sleep */ + if (inta & CSR_INT_BIT_WAKEUP) { + IWL_DEBUG_ISR("Wakeup interrupt\n"); + iwl_rx_queue_update_write_ptr(priv, &priv->rxq); + iwl_txq_update_write_ptr(priv, &priv->txq[0]); + iwl_txq_update_write_ptr(priv, &priv->txq[1]); + iwl_txq_update_write_ptr(priv, &priv->txq[2]); + iwl_txq_update_write_ptr(priv, &priv->txq[3]); + iwl_txq_update_write_ptr(priv, &priv->txq[4]); + iwl_txq_update_write_ptr(priv, &priv->txq[5]); + + handled |= CSR_INT_BIT_WAKEUP; + } + + /* All uCode command responses, including Tx command responses, + * Rx "responses" (frame-received notification), and other + * notifications from uCode come through here*/ + if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { + iwl_rx_handle(priv); + handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); + } + + if (inta & CSR_INT_BIT_FH_TX) { + IWL_DEBUG_ISR("Tx interrupt\n"); + handled |= CSR_INT_BIT_FH_TX; + /* FH finished to write, send event */ + priv->ucode_write_complete = 1; + wake_up_interruptible(&priv->wait_command_queue); + } + + if (inta & ~handled) + IWL_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled); + + if (inta & ~CSR_INI_SET_MASK) { + IWL_WARNING("Disabled INTA bits 0x%08x were pending\n", + inta & ~CSR_INI_SET_MASK); + IWL_WARNING(" with FH_INT = 0x%08x\n", inta_fh); + } + + /* Re-enable all interrupts */ + /* only Re-enable if diabled by irq */ + if (test_bit(STATUS_INT_ENABLED, &priv->status)) + iwl4965_enable_interrupts(priv); + +#ifdef CONFIG_IWLWIFI_DEBUG + if (priv->debug_level & (IWL_DL_ISR)) { + inta = iwl_read32(priv, CSR_INT); + inta_mask = iwl_read32(priv, CSR_INT_MASK); + inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); + IWL_DEBUG_ISR("End inta 0x%08x, enabled 0x%08x, fh 0x%08x, " + "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags); + } +#endif + spin_unlock_irqrestore(&priv->lock, flags); +} + +static irqreturn_t iwl4965_isr(int irq, void *data) +{ + struct iwl_priv *priv = data; + u32 inta, inta_mask; + u32 inta_fh; + if (!priv) + return IRQ_NONE; + + spin_lock(&priv->lock); + + /* Disable (but don't clear!) interrupts here to avoid + * back-to-back ISRs and sporadic interrupts from our NIC. + * If we have something to service, the tasklet will re-enable ints. + * If we *don't* have something, we'll re-enable before leaving here. */ + inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */ + iwl_write32(priv, CSR_INT_MASK, 0x00000000); + + /* Discover which interrupts are active/pending */ + inta = iwl_read32(priv, CSR_INT); + inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); + + /* Ignore interrupt if there's nothing in NIC to service. + * This may be due to IRQ shared with another device, + * or due to sporadic interrupts thrown from our NIC. */ + if (!inta && !inta_fh) { + IWL_DEBUG_ISR("Ignore interrupt, inta == 0, inta_fh == 0\n"); + goto none; + } + + if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { + /* Hardware disappeared. It might have already raised + * an interrupt */ + IWL_WARNING("HARDWARE GONE?? INTA == 0x%080x\n", inta); + goto unplugged; + } + + IWL_DEBUG_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", + inta, inta_mask, inta_fh); + + inta &= ~CSR_INT_BIT_SCD; + + /* iwl4965_irq_tasklet() will service interrupts and re-enable them */ + if (likely(inta || inta_fh)) + tasklet_schedule(&priv->irq_tasklet); + + unplugged: + spin_unlock(&priv->lock); + return IRQ_HANDLED; + + none: + /* re-enable interrupts here since we don't have anything to service. */ + /* only Re-enable if diabled by irq */ + if (test_bit(STATUS_INT_ENABLED, &priv->status)) + iwl4965_enable_interrupts(priv); + spin_unlock(&priv->lock); + return IRQ_NONE; +} + +/****************************************************************************** + * + * uCode download functions + * + ******************************************************************************/ + +static void iwl4965_dealloc_ucode_pci(struct iwl_priv *priv) +{ + iwl_free_fw_desc(priv->pci_dev, &priv->ucode_code); + iwl_free_fw_desc(priv->pci_dev, &priv->ucode_data); + iwl_free_fw_desc(priv->pci_dev, &priv->ucode_data_backup); + iwl_free_fw_desc(priv->pci_dev, &priv->ucode_init); + iwl_free_fw_desc(priv->pci_dev, &priv->ucode_init_data); + iwl_free_fw_desc(priv->pci_dev, &priv->ucode_boot); +} + +static void iwl4965_nic_start(struct iwl_priv *priv) +{ + /* Remove all resets to allow NIC to operate */ + iwl_write32(priv, CSR_RESET, 0); +} + + +/** + * iwl4965_read_ucode - Read uCode images from disk file. + * + * Copy into buffers for card to fetch via bus-mastering + */ +static int iwl4965_read_ucode(struct iwl_priv *priv) +{ + struct iwl_ucode *ucode; + int ret; + const struct firmware *ucode_raw; + const char *name = priv->cfg->fw_name; + u8 *src; + size_t len; + u32 ver, inst_size, data_size, init_size, init_data_size, boot_size; + + /* Ask kernel firmware_class module to get the boot firmware off disk. + * request_firmware() is synchronous, file is in memory on return. */ + ret = request_firmware(&ucode_raw, name, &priv->pci_dev->dev); + if (ret < 0) { + IWL_ERROR("%s firmware file req failed: Reason %d\n", + name, ret); + goto error; + } + + IWL_DEBUG_INFO("Got firmware '%s' file (%zd bytes) from disk\n", + name, ucode_raw->size); + + /* Make sure that we got at least our header! */ + if (ucode_raw->size < sizeof(*ucode)) { + IWL_ERROR("File size way too small!\n"); + ret = -EINVAL; + goto err_release; + } + + /* Data from ucode file: header followed by uCode images */ + ucode = (void *)ucode_raw->data; + + ver = le32_to_cpu(ucode->ver); + inst_size = le32_to_cpu(ucode->inst_size); + data_size = le32_to_cpu(ucode->data_size); + init_size = le32_to_cpu(ucode->init_size); + init_data_size = le32_to_cpu(ucode->init_data_size); + boot_size = le32_to_cpu(ucode->boot_size); + + IWL_DEBUG_INFO("f/w package hdr ucode version = 0x%x\n", ver); + IWL_DEBUG_INFO("f/w package hdr runtime inst size = %u\n", + inst_size); + IWL_DEBUG_INFO("f/w package hdr runtime data size = %u\n", + data_size); + IWL_DEBUG_INFO("f/w package hdr init inst size = %u\n", + init_size); + IWL_DEBUG_INFO("f/w package hdr init data size = %u\n", + init_data_size); + IWL_DEBUG_INFO("f/w package hdr boot inst size = %u\n", + boot_size); + + /* Verify size of file vs. image size info in file's header */ + if (ucode_raw->size < sizeof(*ucode) + + inst_size + data_size + init_size + + init_data_size + boot_size) { + + IWL_DEBUG_INFO("uCode file size %d too small\n", + (int)ucode_raw->size); + ret = -EINVAL; + goto err_release; + } + + /* Verify that uCode images will fit in card's SRAM */ + if (inst_size > priv->hw_params.max_inst_size) { + IWL_DEBUG_INFO("uCode instr len %d too large to fit in\n", + inst_size); + ret = -EINVAL; + goto err_release; + } + + if (data_size > priv->hw_params.max_data_size) { + IWL_DEBUG_INFO("uCode data len %d too large to fit in\n", + data_size); + ret = -EINVAL; + goto err_release; + } + if (init_size > priv->hw_params.max_inst_size) { + IWL_DEBUG_INFO + ("uCode init instr len %d too large to fit in\n", + init_size); + ret = -EINVAL; + goto err_release; + } + if (init_data_size > priv->hw_params.max_data_size) { + IWL_DEBUG_INFO + ("uCode init data len %d too large to fit in\n", + init_data_size); + ret = -EINVAL; + goto err_release; + } + if (boot_size > priv->hw_params.max_bsm_size) { + IWL_DEBUG_INFO + ("uCode boot instr len %d too large to fit in\n", + boot_size); + ret = -EINVAL; + goto err_release; + } + + /* Allocate ucode buffers for card's bus-master loading ... */ + + /* Runtime instructions and 2 copies of data: + * 1) unmodified from disk + * 2) backup cache for save/restore during power-downs */ + priv->ucode_code.len = inst_size; + iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_code); + + priv->ucode_data.len = data_size; + iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_data); + + priv->ucode_data_backup.len = data_size; + iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_data_backup); + + /* Initialization instructions and data */ + if (init_size && init_data_size) { + priv->ucode_init.len = init_size; + iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_init); + + priv->ucode_init_data.len = init_data_size; + iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_init_data); + + if (!priv->ucode_init.v_addr || !priv->ucode_init_data.v_addr) + goto err_pci_alloc; + } + + /* Bootstrap (instructions only, no data) */ + if (boot_size) { + priv->ucode_boot.len = boot_size; + iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_boot); + + if (!priv->ucode_boot.v_addr) + goto err_pci_alloc; + } + + /* Copy images into buffers for card's bus-master reads ... */ + + /* Runtime instructions (first block of data in file) */ + src = &ucode->data[0]; + len = priv->ucode_code.len; + IWL_DEBUG_INFO("Copying (but not loading) uCode instr len %Zd\n", len); + memcpy(priv->ucode_code.v_addr, src, len); + IWL_DEBUG_INFO("uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n", + priv->ucode_code.v_addr, (u32)priv->ucode_code.p_addr); + + /* Runtime data (2nd block) + * NOTE: Copy into backup buffer will be done in iwl4965_up() */ + src = &ucode->data[inst_size]; + len = priv->ucode_data.len; + IWL_DEBUG_INFO("Copying (but not loading) uCode data len %Zd\n", len); + memcpy(priv->ucode_data.v_addr, src, len); + memcpy(priv->ucode_data_backup.v_addr, src, len); + + /* Initialization instructions (3rd block) */ + if (init_size) { + src = &ucode->data[inst_size + data_size]; + len = priv->ucode_init.len; + IWL_DEBUG_INFO("Copying (but not loading) init instr len %Zd\n", + len); + memcpy(priv->ucode_init.v_addr, src, len); + } + + /* Initialization data (4th block) */ + if (init_data_size) { + src = &ucode->data[inst_size + data_size + init_size]; + len = priv->ucode_init_data.len; + IWL_DEBUG_INFO("Copying (but not loading) init data len %Zd\n", + len); + memcpy(priv->ucode_init_data.v_addr, src, len); + } + + /* Bootstrap instructions (5th block) */ + src = &ucode->data[inst_size + data_size + init_size + init_data_size]; + len = priv->ucode_boot.len; + IWL_DEBUG_INFO("Copying (but not loading) boot instr len %Zd\n", len); + memcpy(priv->ucode_boot.v_addr, src, len); + + /* We have our copies now, allow OS release its copies */ + release_firmware(ucode_raw); + return 0; + + err_pci_alloc: + IWL_ERROR("failed to allocate pci memory\n"); + ret = -ENOMEM; + iwl4965_dealloc_ucode_pci(priv); + + err_release: + release_firmware(ucode_raw); + + error: + return ret; +} + +/** + * iwl_alive_start - called after REPLY_ALIVE notification received + * from protocol/runtime uCode (initialization uCode's + * Alive gets handled by iwl_init_alive_start()). + */ +static void iwl_alive_start(struct iwl_priv *priv) +{ + int ret = 0; + + IWL_DEBUG_INFO("Runtime Alive received.\n"); + + if (priv->card_alive.is_valid != UCODE_VALID_OK) { + /* We had an error bringing up the hardware, so take it + * all the way back down so we can try again */ + IWL_DEBUG_INFO("Alive failed.\n"); + goto restart; + } + + /* Initialize uCode has loaded Runtime uCode ... verify inst image. + * This is a paranoid check, because we would not have gotten the + * "runtime" alive if code weren't properly loaded. */ + if (iwl_verify_ucode(priv)) { + /* Runtime instruction load was bad; + * take it all the way back down so we can try again */ + IWL_DEBUG_INFO("Bad runtime uCode load.\n"); + goto restart; + } + + iwl_clear_stations_table(priv); + ret = priv->cfg->ops->lib->alive_notify(priv); + if (ret) { + IWL_WARNING("Could not complete ALIVE transition [ntf]: %d\n", + ret); + goto restart; + } + + /* After the ALIVE response, we can send host commands to 4965 uCode */ + set_bit(STATUS_ALIVE, &priv->status); + + if (iwl_is_rfkill(priv)) + return; + + ieee80211_wake_queues(priv->hw); + + priv->active_rate = priv->rates_mask; + priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK; + + if (iwl_is_associated(priv)) { + struct iwl_rxon_cmd *active_rxon = + (struct iwl_rxon_cmd *)&priv->active_rxon; + + memcpy(&priv->staging_rxon, &priv->active_rxon, + sizeof(priv->staging_rxon)); + active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + } else { + /* Initialize our rx_config data */ + iwl4965_connection_init_rx_config(priv); + memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); + } + + /* Configure Bluetooth device coexistence support */ + iwl4965_send_bt_config(priv); + + iwl_reset_run_time_calib(priv); + + /* Configure the adapter for unassociated operation */ + iwl4965_commit_rxon(priv); + + /* At this point, the NIC is initialized and operational */ + iwl_rf_kill_ct_config(priv); + + iwl_leds_register(priv); + + IWL_DEBUG_INFO("ALIVE processing complete.\n"); + set_bit(STATUS_READY, &priv->status); + wake_up_interruptible(&priv->wait_command_queue); + + if (priv->error_recovering) + iwl4965_error_recovery(priv); + + iwl_power_update_mode(priv, 1); + ieee80211_notify_mac(priv->hw, IEEE80211_NOTIFY_RE_ASSOC); + + if (test_and_clear_bit(STATUS_MODE_PENDING, &priv->status)) + iwl4965_set_mode(priv, priv->iw_mode); + + return; + + restart: + queue_work(priv->workqueue, &priv->restart); +} + +static void iwl_cancel_deferred_work(struct iwl_priv *priv); + +static void __iwl4965_down(struct iwl_priv *priv) +{ + unsigned long flags; + int exit_pending = test_bit(STATUS_EXIT_PENDING, &priv->status); + + IWL_DEBUG_INFO(DRV_NAME " is going down\n"); + + if (!exit_pending) + set_bit(STATUS_EXIT_PENDING, &priv->status); + + iwl_leds_unregister(priv); + + iwl_clear_stations_table(priv); + + /* Unblock any waiting calls */ + wake_up_interruptible_all(&priv->wait_command_queue); + + /* Wipe out the EXIT_PENDING status bit if we are not actually + * exiting the module */ + if (!exit_pending) + clear_bit(STATUS_EXIT_PENDING, &priv->status); + + /* stop and reset the on-board processor */ + iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); + + /* tell the device to stop sending interrupts */ + spin_lock_irqsave(&priv->lock, flags); + iwl4965_disable_interrupts(priv); + spin_unlock_irqrestore(&priv->lock, flags); + iwl_synchronize_irq(priv); + + if (priv->mac80211_registered) + ieee80211_stop_queues(priv->hw); + + /* If we have not previously called iwl4965_init() then + * clear all bits but the RF Kill and SUSPEND bits and return */ + if (!iwl_is_init(priv)) { + priv->status = test_bit(STATUS_RF_KILL_HW, &priv->status) << + STATUS_RF_KILL_HW | + test_bit(STATUS_RF_KILL_SW, &priv->status) << + STATUS_RF_KILL_SW | + test_bit(STATUS_GEO_CONFIGURED, &priv->status) << + STATUS_GEO_CONFIGURED | + test_bit(STATUS_IN_SUSPEND, &priv->status) << + STATUS_IN_SUSPEND | + test_bit(STATUS_EXIT_PENDING, &priv->status) << + STATUS_EXIT_PENDING; + goto exit; + } + + /* ...otherwise clear out all the status bits but the RF Kill and + * SUSPEND bits and continue taking the NIC down. */ + priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) << + STATUS_RF_KILL_HW | + test_bit(STATUS_RF_KILL_SW, &priv->status) << + STATUS_RF_KILL_SW | + test_bit(STATUS_GEO_CONFIGURED, &priv->status) << + STATUS_GEO_CONFIGURED | + test_bit(STATUS_IN_SUSPEND, &priv->status) << + STATUS_IN_SUSPEND | + test_bit(STATUS_FW_ERROR, &priv->status) << + STATUS_FW_ERROR | + test_bit(STATUS_EXIT_PENDING, &priv->status) << + STATUS_EXIT_PENDING; + + spin_lock_irqsave(&priv->lock, flags); + iwl_clear_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + spin_unlock_irqrestore(&priv->lock, flags); + + iwl_txq_ctx_stop(priv); + iwl_rxq_stop(priv); + + spin_lock_irqsave(&priv->lock, flags); + if (!iwl_grab_nic_access(priv)) { + iwl_write_prph(priv, APMG_CLK_DIS_REG, + APMG_CLK_VAL_DMA_CLK_RQT); + iwl_release_nic_access(priv); + } + spin_unlock_irqrestore(&priv->lock, flags); + + udelay(5); + + /* FIXME: apm_ops.suspend(priv) */ + priv->cfg->ops->lib->apm_ops.reset(priv); + priv->cfg->ops->lib->free_shared_mem(priv); + + exit: + memset(&priv->card_alive, 0, sizeof(struct iwl_alive_resp)); + + if (priv->ibss_beacon) + dev_kfree_skb(priv->ibss_beacon); + priv->ibss_beacon = NULL; + + /* clear out any free frames */ + iwl_clear_free_frames(priv); +} + +static void iwl4965_down(struct iwl_priv *priv) +{ + mutex_lock(&priv->mutex); + __iwl4965_down(priv); + mutex_unlock(&priv->mutex); + + iwl_cancel_deferred_work(priv); +} + +#define MAX_HW_RESTARTS 5 + +static int __iwl4965_up(struct iwl_priv *priv) +{ + int i; + int ret; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { + IWL_WARNING("Exit pending; will not bring the NIC up\n"); + return -EIO; + } + + if (!priv->ucode_data_backup.v_addr || !priv->ucode_data.v_addr) { + IWL_ERROR("ucode not available for device bringup\n"); + return -EIO; + } + + /* If platform's RF_KILL switch is NOT set to KILL */ + if (iwl_read32(priv, CSR_GP_CNTRL) & + CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) + clear_bit(STATUS_RF_KILL_HW, &priv->status); + else + set_bit(STATUS_RF_KILL_HW, &priv->status); + + if (!test_bit(STATUS_IN_SUSPEND, &priv->status) && + iwl_is_rfkill(priv)) { + IWL_WARNING("Radio disabled by %s RF Kill switch\n", + test_bit(STATUS_RF_KILL_HW, &priv->status) ? "HW" : "SW"); + return -ENODEV; + } + + iwl_write32(priv, CSR_INT, 0xFFFFFFFF); + + ret = priv->cfg->ops->lib->alloc_shared_mem(priv); + if (ret) { + IWL_ERROR("Unable to allocate shared memory\n"); + return ret; + } + + ret = iwl_hw_nic_init(priv); + if (ret) { + IWL_ERROR("Unable to init nic\n"); + return ret; + } + + /* make sure rfkill handshake bits are cleared */ + iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + /* clear (again), then enable host interrupts */ + iwl_write32(priv, CSR_INT, 0xFFFFFFFF); + iwl4965_enable_interrupts(priv); + + /* really make sure rfkill handshake bits are cleared */ + iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + + /* Copy original ucode data image from disk into backup cache. + * This will be used to initialize the on-board processor's + * data SRAM for a clean start when the runtime program first loads. */ + memcpy(priv->ucode_data_backup.v_addr, priv->ucode_data.v_addr, + priv->ucode_data.len); + + /* We return success when we resume from suspend and rf_kill is on. */ + if (test_bit(STATUS_RF_KILL_HW, &priv->status) || + test_bit(STATUS_RF_KILL_SW, &priv->status)) + return 0; + + for (i = 0; i < MAX_HW_RESTARTS; i++) { + + iwl_clear_stations_table(priv); + + /* load bootstrap state machine, + * load bootstrap program into processor's memory, + * prepare to load the "initialize" uCode */ + ret = priv->cfg->ops->lib->load_ucode(priv); + + if (ret) { + IWL_ERROR("Unable to set up bootstrap uCode: %d\n", ret); + continue; + } + + /* Clear out the uCode error bit if it is set */ + clear_bit(STATUS_FW_ERROR, &priv->status); + + /* start card; "initialize" will load runtime ucode */ + iwl4965_nic_start(priv); + + IWL_DEBUG_INFO(DRV_NAME " is coming up\n"); + + return 0; + } + + set_bit(STATUS_EXIT_PENDING, &priv->status); + __iwl4965_down(priv); + clear_bit(STATUS_EXIT_PENDING, &priv->status); + + /* tried to restart and config the device for as long as our + * patience could withstand */ + IWL_ERROR("Unable to initialize device after %d attempts.\n", i); + return -EIO; +} + + +/***************************************************************************** + * + * Workqueue callbacks + * + *****************************************************************************/ + +static void iwl_bg_init_alive_start(struct work_struct *data) +{ + struct iwl_priv *priv = + container_of(data, struct iwl_priv, init_alive_start.work); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + priv->cfg->ops->lib->init_alive_start(priv); + mutex_unlock(&priv->mutex); +} + +static void iwl_bg_alive_start(struct work_struct *data) +{ + struct iwl_priv *priv = + container_of(data, struct iwl_priv, alive_start.work); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + iwl_alive_start(priv); + mutex_unlock(&priv->mutex); +} + +static void iwl4965_bg_rf_kill(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, rf_kill); + + wake_up_interruptible(&priv->wait_command_queue); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + + if (!iwl_is_rfkill(priv)) { + IWL_DEBUG(IWL_DL_RF_KILL, + "HW and/or SW RF Kill no longer active, restarting " + "device\n"); + if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) + queue_work(priv->workqueue, &priv->restart); + } else { + /* make sure mac80211 stop sending Tx frame */ + if (priv->mac80211_registered) + ieee80211_stop_queues(priv->hw); + + if (!test_bit(STATUS_RF_KILL_HW, &priv->status)) + IWL_DEBUG_RF_KILL("Can not turn radio back on - " + "disabled by SW switch\n"); + else + IWL_WARNING("Radio Frequency Kill Switch is On:\n" + "Kill switch must be turned off for " + "wireless networking to work.\n"); + } + mutex_unlock(&priv->mutex); + iwl_rfkill_set_hw_state(priv); +} + +static void iwl4965_bg_set_monitor(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, + struct iwl_priv, set_monitor); + int ret; + + IWL_DEBUG(IWL_DL_STATE, "setting monitor mode\n"); + + mutex_lock(&priv->mutex); + + ret = iwl4965_set_mode(priv, IEEE80211_IF_TYPE_MNTR); + + if (ret) { + if (ret == -EAGAIN) + IWL_DEBUG(IWL_DL_STATE, "leave - not ready\n"); + else + IWL_ERROR("iwl4965_set_mode() failed ret = %d\n", ret); + } + + mutex_unlock(&priv->mutex); +} + +static void iwl_bg_run_time_calib_work(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, + run_time_calib_work); + + mutex_lock(&priv->mutex); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status) || + test_bit(STATUS_SCANNING, &priv->status)) { + mutex_unlock(&priv->mutex); + return; + } + + if (priv->start_calib) { + iwl_chain_noise_calibration(priv, &priv->statistics); + + iwl_sensitivity_calibration(priv, &priv->statistics); + } + + mutex_unlock(&priv->mutex); + return; +} + +static void iwl4965_bg_up(struct work_struct *data) +{ + struct iwl_priv *priv = container_of(data, struct iwl_priv, up); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + __iwl4965_up(priv); + mutex_unlock(&priv->mutex); + iwl_rfkill_set_hw_state(priv); +} + +static void iwl4965_bg_restart(struct work_struct *data) +{ + struct iwl_priv *priv = container_of(data, struct iwl_priv, restart); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + iwl4965_down(priv); + queue_work(priv->workqueue, &priv->up); +} + +static void iwl4965_bg_rx_replenish(struct work_struct *data) +{ + struct iwl_priv *priv = + container_of(data, struct iwl_priv, rx_replenish); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + iwl_rx_replenish(priv); + mutex_unlock(&priv->mutex); +} + +#define IWL_DELAY_NEXT_SCAN (HZ*2) + +static void iwl4965_post_associate(struct iwl_priv *priv) +{ + struct ieee80211_conf *conf = NULL; + int ret = 0; + DECLARE_MAC_BUF(mac); + unsigned long flags; + + if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { + IWL_ERROR("%s Should not be called in AP mode\n", __FUNCTION__); + return; + } + + IWL_DEBUG_ASSOC("Associated as %d to: %s\n", + priv->assoc_id, + print_mac(mac, priv->active_rxon.bssid_addr)); + + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + + if (!priv->vif || !priv->is_open) + return; + + iwl_scan_cancel_timeout(priv, 200); + + conf = ieee80211_get_hw_conf(priv->hw); + + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl4965_commit_rxon(priv); + + memset(&priv->rxon_timing, 0, sizeof(struct iwl4965_rxon_time_cmd)); + iwl4965_setup_rxon_timing(priv); + ret = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING, + sizeof(priv->rxon_timing), &priv->rxon_timing); + if (ret) + IWL_WARNING("REPLY_RXON_TIMING failed - " + "Attempting to continue.\n"); + + priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK; + + if (priv->current_ht_config.is_ht) + iwl_set_rxon_ht(priv, &priv->current_ht_config); + + iwl_set_rxon_chain(priv); + priv->staging_rxon.assoc_id = cpu_to_le16(priv->assoc_id); + + IWL_DEBUG_ASSOC("assoc id %d beacon interval %d\n", + priv->assoc_id, priv->beacon_int); + + if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_PREAMBLE) + priv->staging_rxon.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + else + priv->staging_rxon.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + + if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) { + if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) + priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK; + else + priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + + if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) + priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + + } + + iwl4965_commit_rxon(priv); + + switch (priv->iw_mode) { + case IEEE80211_IF_TYPE_STA: + break; + + case IEEE80211_IF_TYPE_IBSS: + + /* assume default assoc id */ + priv->assoc_id = 1; + + iwl_rxon_add_station(priv, priv->bssid, 0); + iwl4965_send_beacon_cmd(priv); + + break; + + default: + IWL_ERROR("%s Should not be called in %d mode\n", + __FUNCTION__, priv->iw_mode); + break; + } + + /* Enable Rx differential gain and sensitivity calibrations */ + iwl_chain_noise_reset(priv); + priv->start_calib = 1; + + if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) + priv->assoc_station_added = 1; + + spin_lock_irqsave(&priv->lock, flags); + iwl_activate_qos(priv, 0); + spin_unlock_irqrestore(&priv->lock, flags); + + iwl_power_update_mode(priv, 0); + /* we have just associated, don't start scan too early */ + priv->next_scan_jiffies = jiffies + IWL_DELAY_NEXT_SCAN; +} + +static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf); + +static void iwl_bg_scan_completed(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, scan_completed); + + IWL_DEBUG_SCAN("SCAN complete scan\n"); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (test_bit(STATUS_CONF_PENDING, &priv->status)) + iwl4965_mac_config(priv->hw, ieee80211_get_hw_conf(priv->hw)); + + ieee80211_scan_completed(priv->hw); + + /* Since setting the TXPOWER may have been deferred while + * performing the scan, fire one off */ + mutex_lock(&priv->mutex); + iwl_set_tx_power(priv, priv->tx_power_user_lmt, true); + mutex_unlock(&priv->mutex); +} + +/***************************************************************************** + * + * mac80211 entry point functions + * + *****************************************************************************/ + +#define UCODE_READY_TIMEOUT (4 * HZ) + +static int iwl4965_mac_start(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = hw->priv; + int ret; + + IWL_DEBUG_MAC80211("enter\n"); + + if (pci_enable_device(priv->pci_dev)) { + IWL_ERROR("Fail to pci_enable_device\n"); + return -ENODEV; + } + pci_restore_state(priv->pci_dev); + pci_enable_msi(priv->pci_dev); + + ret = request_irq(priv->pci_dev->irq, iwl4965_isr, IRQF_SHARED, + DRV_NAME, priv); + if (ret) { + IWL_ERROR("Error allocating IRQ %d\n", priv->pci_dev->irq); + goto out_disable_msi; + } + + /* we should be verifying the device is ready to be opened */ + mutex_lock(&priv->mutex); + + memset(&priv->staging_rxon, 0, sizeof(struct iwl_rxon_cmd)); + /* fetch ucode file from disk, alloc and copy to bus-master buffers ... + * ucode filename and max sizes are card-specific. */ + + if (!priv->ucode_code.len) { + ret = iwl4965_read_ucode(priv); + if (ret) { + IWL_ERROR("Could not read microcode: %d\n", ret); + mutex_unlock(&priv->mutex); + goto out_release_irq; + } + } + + ret = __iwl4965_up(priv); + + mutex_unlock(&priv->mutex); + + iwl_rfkill_set_hw_state(priv); + + if (ret) + goto out_release_irq; + + IWL_DEBUG_INFO("Start UP work done.\n"); + + if (test_bit(STATUS_IN_SUSPEND, &priv->status)) + return 0; + + /* Wait for START_ALIVE from Run Time ucode. Otherwise callbacks from + * mac80211 will not be run successfully. */ + ret = wait_event_interruptible_timeout(priv->wait_command_queue, + test_bit(STATUS_READY, &priv->status), + UCODE_READY_TIMEOUT); + if (!ret) { + if (!test_bit(STATUS_READY, &priv->status)) { + IWL_ERROR("START_ALIVE timeout after %dms.\n", + jiffies_to_msecs(UCODE_READY_TIMEOUT)); + ret = -ETIMEDOUT; + goto out_release_irq; + } + } + + priv->is_open = 1; + IWL_DEBUG_MAC80211("leave\n"); + return 0; + +out_release_irq: + free_irq(priv->pci_dev->irq, priv); +out_disable_msi: + pci_disable_msi(priv->pci_dev); + pci_disable_device(priv->pci_dev); + priv->is_open = 0; + IWL_DEBUG_MAC80211("leave - failed\n"); + return ret; +} + +static void iwl4965_mac_stop(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211("enter\n"); + + if (!priv->is_open) { + IWL_DEBUG_MAC80211("leave - skip\n"); + return; + } + + priv->is_open = 0; + + if (iwl_is_ready_rf(priv)) { + /* stop mac, cancel any scan request and clear + * RXON_FILTER_ASSOC_MSK BIT + */ + mutex_lock(&priv->mutex); + iwl_scan_cancel_timeout(priv, 100); + mutex_unlock(&priv->mutex); + } + + iwl4965_down(priv); + + flush_workqueue(priv->workqueue); + free_irq(priv->pci_dev->irq, priv); + pci_disable_msi(priv->pci_dev); + pci_save_state(priv->pci_dev); + pci_disable_device(priv->pci_dev); + + IWL_DEBUG_MAC80211("leave\n"); +} + +static int iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct iwl_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211("enter\n"); + + if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) { + IWL_DEBUG_MAC80211("leave - monitor\n"); + dev_kfree_skb_any(skb); + return 0; + } + + IWL_DEBUG_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, + ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); + + if (iwl_tx_skb(priv, skb)) + dev_kfree_skb_any(skb); + + IWL_DEBUG_MAC80211("leave\n"); + return 0; +} + +static int iwl4965_mac_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct iwl_priv *priv = hw->priv; + unsigned long flags; + DECLARE_MAC_BUF(mac); + + IWL_DEBUG_MAC80211("enter: type %d\n", conf->type); + + if (priv->vif) { + IWL_DEBUG_MAC80211("leave - vif != NULL\n"); + return -EOPNOTSUPP; + } + + spin_lock_irqsave(&priv->lock, flags); + priv->vif = conf->vif; + + spin_unlock_irqrestore(&priv->lock, flags); + + mutex_lock(&priv->mutex); + + if (conf->mac_addr) { + IWL_DEBUG_MAC80211("Set %s\n", print_mac(mac, conf->mac_addr)); + memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN); + } + + if (iwl4965_set_mode(priv, conf->type) == -EAGAIN) + /* we are not ready, will run again when ready */ + set_bit(STATUS_MODE_PENDING, &priv->status); + + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211("leave\n"); + return 0; +} + +/** + * iwl4965_mac_config - mac80211 config callback + * + * We ignore conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME since it seems to + * be set inappropriately and the driver currently sets the hardware up to + * use it whenever needed. + */ +static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) +{ + struct iwl_priv *priv = hw->priv; + const struct iwl_channel_info *ch_info; + unsigned long flags; + int ret = 0; + u16 channel; + + mutex_lock(&priv->mutex); + IWL_DEBUG_MAC80211("enter to channel %d\n", conf->channel->hw_value); + + priv->add_radiotap = !!(conf->flags & IEEE80211_CONF_RADIOTAP); + + if (conf->radio_enabled && iwl_radio_kill_sw_enable_radio(priv)) { + IWL_DEBUG_MAC80211("leave - RF-KILL - waiting for uCode\n"); + goto out; + } + + if (!conf->radio_enabled) + iwl_radio_kill_sw_disable_radio(priv); + + if (!iwl_is_ready(priv)) { + IWL_DEBUG_MAC80211("leave - not ready\n"); + ret = -EIO; + goto out; + } + + if (unlikely(!priv->cfg->mod_params->disable_hw_scan && + test_bit(STATUS_SCANNING, &priv->status))) { + IWL_DEBUG_MAC80211("leave - scanning\n"); + set_bit(STATUS_CONF_PENDING, &priv->status); + mutex_unlock(&priv->mutex); + return 0; + } + + channel = ieee80211_frequency_to_channel(conf->channel->center_freq); + ch_info = iwl_get_channel_info(priv, conf->channel->band, channel); + if (!is_channel_valid(ch_info)) { + IWL_DEBUG_MAC80211("leave - invalid channel\n"); + ret = -EINVAL; + goto out; + } + + if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS && + !is_channel_ibss(ch_info)) { + IWL_ERROR("channel %d in band %d not IBSS channel\n", + conf->channel->hw_value, conf->channel->band); + ret = -EINVAL; + goto out; + } + + spin_lock_irqsave(&priv->lock, flags); + + + /* if we are switching from ht to 2.4 clear flags + * from any ht related info since 2.4 does not + * support ht */ + if ((le16_to_cpu(priv->staging_rxon.channel) != channel) +#ifdef IEEE80211_CONF_CHANNEL_SWITCH + && !(conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) +#endif + ) + priv->staging_rxon.flags = 0; + + iwl_set_rxon_channel(priv, conf->channel->band, channel); + + iwl_set_flags_for_band(priv, conf->channel->band); + + /* The list of supported rates and rate mask can be different + * for each band; since the band may have changed, reset + * the rate mask to what mac80211 lists */ + iwl4965_set_rate(priv); + + spin_unlock_irqrestore(&priv->lock, flags); + +#ifdef IEEE80211_CONF_CHANNEL_SWITCH + if (conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) { + iwl4965_hw_channel_switch(priv, conf->channel); + goto out; + } +#endif + + if (!conf->radio_enabled) { + IWL_DEBUG_MAC80211("leave - radio disabled\n"); + goto out; + } + + if (iwl_is_rfkill(priv)) { + IWL_DEBUG_MAC80211("leave - RF kill\n"); + ret = -EIO; + goto out; + } + + IWL_DEBUG_MAC80211("TX Power old=%d new=%d\n", + priv->tx_power_user_lmt, conf->power_level); + + iwl_set_tx_power(priv, conf->power_level, false); + + iwl4965_set_rate(priv); + + if (memcmp(&priv->active_rxon, + &priv->staging_rxon, sizeof(priv->staging_rxon))) + iwl4965_commit_rxon(priv); + else + IWL_DEBUG_INFO("No re-sending same RXON configuration.\n"); + + IWL_DEBUG_MAC80211("leave\n"); + +out: + clear_bit(STATUS_CONF_PENDING, &priv->status); + mutex_unlock(&priv->mutex); + return ret; +} + +static void iwl4965_config_ap(struct iwl_priv *priv) +{ + int ret = 0; + unsigned long flags; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + /* The following should be done only at AP bring up */ + if (!(iwl_is_associated(priv))) { + + /* RXON - unassoc (to set timing command) */ + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl4965_commit_rxon(priv); + + /* RXON Timing */ + memset(&priv->rxon_timing, 0, sizeof(struct iwl4965_rxon_time_cmd)); + iwl4965_setup_rxon_timing(priv); + ret = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING, + sizeof(priv->rxon_timing), &priv->rxon_timing); + if (ret) + IWL_WARNING("REPLY_RXON_TIMING failed - " + "Attempting to continue.\n"); + + iwl_set_rxon_chain(priv); + + /* FIXME: what should be the assoc_id for AP? */ + priv->staging_rxon.assoc_id = cpu_to_le16(priv->assoc_id); + if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_PREAMBLE) + priv->staging_rxon.flags |= + RXON_FLG_SHORT_PREAMBLE_MSK; + else + priv->staging_rxon.flags &= + ~RXON_FLG_SHORT_PREAMBLE_MSK; + + if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) { + if (priv->assoc_capability & + WLAN_CAPABILITY_SHORT_SLOT_TIME) + priv->staging_rxon.flags |= + RXON_FLG_SHORT_SLOT_MSK; + else + priv->staging_rxon.flags &= + ~RXON_FLG_SHORT_SLOT_MSK; + + if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) + priv->staging_rxon.flags &= + ~RXON_FLG_SHORT_SLOT_MSK; + } + /* restore RXON assoc */ + priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK; + iwl4965_commit_rxon(priv); + spin_lock_irqsave(&priv->lock, flags); + iwl_activate_qos(priv, 1); + spin_unlock_irqrestore(&priv->lock, flags); + iwl_rxon_add_station(priv, iwl_bcast_addr, 0); + } + iwl4965_send_beacon_cmd(priv); + + /* FIXME - we need to add code here to detect a totally new + * configuration, reset the AP, unassoc, rxon timing, assoc, + * clear sta table, add BCAST sta... */ +} + +/* temporary */ +static int iwl4965_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb); + +static int iwl4965_mac_config_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_if_conf *conf) +{ + struct iwl_priv *priv = hw->priv; + DECLARE_MAC_BUF(mac); + unsigned long flags; + int rc; + + if (conf == NULL) + return -EIO; + + if (priv->vif != vif) { + IWL_DEBUG_MAC80211("leave - priv->vif != vif\n"); + return 0; + } + + if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS && + conf->changed & IEEE80211_IFCC_BEACON) { + struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); + if (!beacon) + return -ENOMEM; + rc = iwl4965_mac_beacon_update(hw, beacon); + if (rc) + return rc; + } + + if ((priv->iw_mode == IEEE80211_IF_TYPE_AP) && + (!conf->ssid_len)) { + IWL_DEBUG_MAC80211 + ("Leaving in AP mode because HostAPD is not ready.\n"); + return 0; + } + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + mutex_lock(&priv->mutex); + + if (conf->bssid) + IWL_DEBUG_MAC80211("bssid: %s\n", + print_mac(mac, conf->bssid)); + +/* + * very dubious code was here; the probe filtering flag is never set: + * + if (unlikely(test_bit(STATUS_SCANNING, &priv->status)) && + !(priv->hw->flags & IEEE80211_HW_NO_PROBE_FILTERING)) { + */ + + if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { + if (!conf->bssid) { + conf->bssid = priv->mac_addr; + memcpy(priv->bssid, priv->mac_addr, ETH_ALEN); + IWL_DEBUG_MAC80211("bssid was set to: %s\n", + print_mac(mac, conf->bssid)); + } + if (priv->ibss_beacon) + dev_kfree_skb(priv->ibss_beacon); + + priv->ibss_beacon = ieee80211_beacon_get(hw, vif); + } + + if (iwl_is_rfkill(priv)) + goto done; + + if (conf->bssid && !is_zero_ether_addr(conf->bssid) && + !is_multicast_ether_addr(conf->bssid)) { + /* If there is currently a HW scan going on in the background + * then we need to cancel it else the RXON below will fail. */ + if (iwl_scan_cancel_timeout(priv, 100)) { + IWL_WARNING("Aborted scan still in progress " + "after 100ms\n"); + IWL_DEBUG_MAC80211("leaving - scan abort failed.\n"); + mutex_unlock(&priv->mutex); + return -EAGAIN; + } + memcpy(priv->staging_rxon.bssid_addr, conf->bssid, ETH_ALEN); + + /* TODO: Audit driver for usage of these members and see + * if mac80211 deprecates them (priv->bssid looks like it + * shouldn't be there, but I haven't scanned the IBSS code + * to verify) - jpk */ + memcpy(priv->bssid, conf->bssid, ETH_ALEN); + + if (priv->iw_mode == IEEE80211_IF_TYPE_AP) + iwl4965_config_ap(priv); + else { + rc = iwl4965_commit_rxon(priv); + if ((priv->iw_mode == IEEE80211_IF_TYPE_STA) && rc) + iwl_rxon_add_station( + priv, priv->active_rxon.bssid_addr, 1); + } + + } else { + iwl_scan_cancel_timeout(priv, 100); + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl4965_commit_rxon(priv); + } + + done: + spin_lock_irqsave(&priv->lock, flags); + if (!conf->ssid_len) + memset(priv->essid, 0, IW_ESSID_MAX_SIZE); + else + memcpy(priv->essid, conf->ssid, conf->ssid_len); + + priv->essid_len = conf->ssid_len; + spin_unlock_irqrestore(&priv->lock, flags); + + IWL_DEBUG_MAC80211("leave\n"); + mutex_unlock(&priv->mutex); + + return 0; +} + +static void iwl4965_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + int mc_count, struct dev_addr_list *mc_list) +{ + struct iwl_priv *priv = hw->priv; + + if (changed_flags & (*total_flags) & FIF_OTHER_BSS) { + IWL_DEBUG_MAC80211("Enter: type %d (0x%x, 0x%x)\n", + IEEE80211_IF_TYPE_MNTR, + changed_flags, *total_flags); + /* queue work 'cuz mac80211 is holding a lock which + * prevents us from issuing (synchronous) f/w cmds */ + queue_work(priv->workqueue, &priv->set_monitor); + } + *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | + FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; +} + +static void iwl4965_mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct iwl_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211("enter\n"); + + mutex_lock(&priv->mutex); + + if (iwl_is_ready_rf(priv)) { + iwl_scan_cancel_timeout(priv, 100); + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl4965_commit_rxon(priv); + } + if (priv->vif == conf->vif) { + priv->vif = NULL; + memset(priv->bssid, 0, ETH_ALEN); + memset(priv->essid, 0, IW_ESSID_MAX_SIZE); + priv->essid_len = 0; + } + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211("leave\n"); + +} + +#define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6) +static void iwl4965_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct iwl_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211("changes = 0x%X\n", changes); + + if (changes & BSS_CHANGED_ERP_PREAMBLE) { + IWL_DEBUG_MAC80211("ERP_PREAMBLE %d\n", + bss_conf->use_short_preamble); + if (bss_conf->use_short_preamble) + priv->staging_rxon.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + else + priv->staging_rxon.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + } + + if (changes & BSS_CHANGED_ERP_CTS_PROT) { + IWL_DEBUG_MAC80211("ERP_CTS %d\n", bss_conf->use_cts_prot); + if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ)) + priv->staging_rxon.flags |= RXON_FLG_TGG_PROTECT_MSK; + else + priv->staging_rxon.flags &= ~RXON_FLG_TGG_PROTECT_MSK; + } + + if (changes & BSS_CHANGED_HT) { + IWL_DEBUG_MAC80211("HT %d\n", bss_conf->assoc_ht); + iwl4965_ht_conf(priv, bss_conf); + iwl_set_rxon_chain(priv); + } + + if (changes & BSS_CHANGED_ASSOC) { + IWL_DEBUG_MAC80211("ASSOC %d\n", bss_conf->assoc); + /* This should never happen as this function should + * never be called from interrupt context. */ + if (WARN_ON_ONCE(in_interrupt())) + return; + if (bss_conf->assoc) { + priv->assoc_id = bss_conf->aid; + priv->beacon_int = bss_conf->beacon_int; + priv->power_data.dtim_period = bss_conf->dtim_period; + priv->timestamp = bss_conf->timestamp; + priv->assoc_capability = bss_conf->assoc_capability; + priv->next_scan_jiffies = jiffies + + IWL_DELAY_NEXT_SCAN_AFTER_ASSOC; + mutex_lock(&priv->mutex); + iwl4965_post_associate(priv); + mutex_unlock(&priv->mutex); + } else { + priv->assoc_id = 0; + IWL_DEBUG_MAC80211("DISASSOC %d\n", bss_conf->assoc); + } + } else if (changes && iwl_is_associated(priv) && priv->assoc_id) { + IWL_DEBUG_MAC80211("Associated Changes %d\n", changes); + iwl_send_rxon_assoc(priv); + } + +} + +static int iwl4965_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len) +{ + int rc = 0; + unsigned long flags; + struct iwl_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211("enter\n"); + + mutex_lock(&priv->mutex); + spin_lock_irqsave(&priv->lock, flags); + + if (!iwl_is_ready_rf(priv)) { + rc = -EIO; + IWL_DEBUG_MAC80211("leave - not ready or exit pending\n"); + goto out_unlock; + } + + if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { /* APs don't scan */ + rc = -EIO; + IWL_ERROR("ERROR: APs don't scan\n"); + goto out_unlock; + } + + /* we don't schedule scan within next_scan_jiffies period */ + if (priv->next_scan_jiffies && + time_after(priv->next_scan_jiffies, jiffies)) { + rc = -EAGAIN; + goto out_unlock; + } + /* if we just finished scan ask for delay */ + if (priv->last_scan_jiffies && time_after(priv->last_scan_jiffies + + IWL_DELAY_NEXT_SCAN, jiffies)) { + rc = -EAGAIN; + goto out_unlock; + } + if (len) { + IWL_DEBUG_SCAN("direct scan for %s [%d]\n ", + iwl_escape_essid(ssid, len), (int)len); + + priv->one_direct_scan = 1; + priv->direct_ssid_len = (u8) + min((u8) len, (u8) IW_ESSID_MAX_SIZE); + memcpy(priv->direct_ssid, ssid, priv->direct_ssid_len); + } else + priv->one_direct_scan = 0; + + rc = iwl_scan_initiate(priv); + + IWL_DEBUG_MAC80211("leave\n"); + +out_unlock: + spin_unlock_irqrestore(&priv->lock, flags); + mutex_unlock(&priv->mutex); + + return rc; +} + +static void iwl4965_mac_update_tkip_key(struct ieee80211_hw *hw, + struct ieee80211_key_conf *keyconf, const u8 *addr, + u32 iv32, u16 *phase1key) +{ + struct iwl_priv *priv = hw->priv; + u8 sta_id = IWL_INVALID_STATION; + unsigned long flags; + __le16 key_flags = 0; + int i; + DECLARE_MAC_BUF(mac); + + IWL_DEBUG_MAC80211("enter\n"); + + sta_id = iwl_find_station(priv, addr); + if (sta_id == IWL_INVALID_STATION) { + IWL_DEBUG_MAC80211("leave - %s not in station map.\n", + print_mac(mac, addr)); + return; + } + + iwl_scan_cancel_timeout(priv, 100); + + key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK); + key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); + key_flags &= ~STA_KEY_FLG_INVALID; + + if (sta_id == priv->hw_params.bcast_sta_id) + key_flags |= STA_KEY_MULTICAST_MSK; + + spin_lock_irqsave(&priv->sta_lock, flags); + + priv->stations[sta_id].sta.key.key_flags = key_flags; + priv->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32; + + for (i = 0; i < 5; i++) + priv->stations[sta_id].sta.key.tkip_rx_ttak[i] = + cpu_to_le16(phase1key[i]); + + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + + iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); + + spin_unlock_irqrestore(&priv->sta_lock, flags); + + IWL_DEBUG_MAC80211("leave\n"); +} + +static int iwl4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + const u8 *local_addr, const u8 *addr, + struct ieee80211_key_conf *key) +{ + struct iwl_priv *priv = hw->priv; + DECLARE_MAC_BUF(mac); + int ret = 0; + u8 sta_id = IWL_INVALID_STATION; + u8 is_default_wep_key = 0; + + IWL_DEBUG_MAC80211("enter\n"); + + if (priv->hw_params.sw_crypto) { + IWL_DEBUG_MAC80211("leave - hwcrypto disabled\n"); + return -EOPNOTSUPP; + } + + if (is_zero_ether_addr(addr)) + /* only support pairwise keys */ + return -EOPNOTSUPP; + + sta_id = iwl_find_station(priv, addr); + if (sta_id == IWL_INVALID_STATION) { + IWL_DEBUG_MAC80211("leave - %s not in station map.\n", + print_mac(mac, addr)); + return -EINVAL; + + } + + mutex_lock(&priv->mutex); + iwl_scan_cancel_timeout(priv, 100); + mutex_unlock(&priv->mutex); + + /* If we are getting WEP group key and we didn't receive any key mapping + * so far, we are in legacy wep mode (group key only), otherwise we are + * in 1X mode. + * In legacy wep mode, we use another host command to the uCode */ + if (key->alg == ALG_WEP && sta_id == priv->hw_params.bcast_sta_id && + priv->iw_mode != IEEE80211_IF_TYPE_AP) { + if (cmd == SET_KEY) + is_default_wep_key = !priv->key_mapping_key; + else + is_default_wep_key = + (key->hw_key_idx == HW_KEY_DEFAULT); + } + + switch (cmd) { + case SET_KEY: + if (is_default_wep_key) + ret = iwl_set_default_wep_key(priv, key); + else + ret = iwl_set_dynamic_key(priv, key, sta_id); + + IWL_DEBUG_MAC80211("enable hwcrypto key\n"); + break; + case DISABLE_KEY: + if (is_default_wep_key) + ret = iwl_remove_default_wep_key(priv, key); + else + ret = iwl_remove_dynamic_key(priv, key, sta_id); + + IWL_DEBUG_MAC80211("disable hwcrypto key\n"); + break; + default: + ret = -EINVAL; + } + + IWL_DEBUG_MAC80211("leave\n"); + + return ret; +} + +static int iwl4965_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct iwl_priv *priv = hw->priv; + unsigned long flags; + int q; + + IWL_DEBUG_MAC80211("enter\n"); + + if (!iwl_is_ready_rf(priv)) { + IWL_DEBUG_MAC80211("leave - RF not ready\n"); + return -EIO; + } + + if (queue >= AC_NUM) { + IWL_DEBUG_MAC80211("leave - queue >= AC_NUM %d\n", queue); + return 0; + } + + if (!priv->qos_data.qos_enable) { + priv->qos_data.qos_active = 0; + IWL_DEBUG_MAC80211("leave - qos not enabled\n"); + return 0; + } + q = AC_NUM - 1 - queue; + + spin_lock_irqsave(&priv->lock, flags); + + priv->qos_data.def_qos_parm.ac[q].cw_min = cpu_to_le16(params->cw_min); + priv->qos_data.def_qos_parm.ac[q].cw_max = cpu_to_le16(params->cw_max); + priv->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; + priv->qos_data.def_qos_parm.ac[q].edca_txop = + cpu_to_le16((params->txop * 32)); + + priv->qos_data.def_qos_parm.ac[q].reserved1 = 0; + priv->qos_data.qos_active = 1; + + if (priv->iw_mode == IEEE80211_IF_TYPE_AP) + iwl_activate_qos(priv, 1); + else if (priv->assoc_id && iwl_is_associated(priv)) + iwl_activate_qos(priv, 0); + + spin_unlock_irqrestore(&priv->lock, flags); + + IWL_DEBUG_MAC80211("leave\n"); + return 0; +} + +static int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw, + enum ieee80211_ampdu_mlme_action action, + const u8 *addr, u16 tid, u16 *ssn) +{ + struct iwl_priv *priv = hw->priv; + DECLARE_MAC_BUF(mac); + + IWL_DEBUG_HT("A-MPDU action on addr %s tid %d\n", + print_mac(mac, addr), tid); + + if (!(priv->cfg->sku & IWL_SKU_N)) + return -EACCES; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + IWL_DEBUG_HT("start Rx\n"); + return iwl_rx_agg_start(priv, addr, tid, *ssn); + case IEEE80211_AMPDU_RX_STOP: + IWL_DEBUG_HT("stop Rx\n"); + return iwl_rx_agg_stop(priv, addr, tid); + case IEEE80211_AMPDU_TX_START: + IWL_DEBUG_HT("start Tx\n"); + return iwl_tx_agg_start(priv, addr, tid, ssn); + case IEEE80211_AMPDU_TX_STOP: + IWL_DEBUG_HT("stop Tx\n"); + return iwl_tx_agg_stop(priv, addr, tid); + default: + IWL_DEBUG_HT("unknown\n"); + return -EINVAL; + break; + } + return 0; +} +static int iwl4965_mac_get_tx_stats(struct ieee80211_hw *hw, + struct ieee80211_tx_queue_stats *stats) +{ + struct iwl_priv *priv = hw->priv; + int i, avail; + struct iwl_tx_queue *txq; + struct iwl_queue *q; + unsigned long flags; + + IWL_DEBUG_MAC80211("enter\n"); + + if (!iwl_is_ready_rf(priv)) { + IWL_DEBUG_MAC80211("leave - RF not ready\n"); + return -EIO; + } + + spin_lock_irqsave(&priv->lock, flags); + + for (i = 0; i < AC_NUM; i++) { + txq = &priv->txq[i]; + q = &txq->q; + avail = iwl_queue_space(q); + + stats[i].len = q->n_window - avail; + stats[i].limit = q->n_window - q->high_mark; + stats[i].count = q->n_window; + + } + spin_unlock_irqrestore(&priv->lock, flags); + + IWL_DEBUG_MAC80211("leave\n"); + + return 0; +} + +static int iwl4965_mac_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct iwl_priv *priv = hw->priv; + + priv = hw->priv; + IWL_DEBUG_MAC80211("enter\n"); + IWL_DEBUG_MAC80211("leave\n"); + + return 0; +} + +static void iwl4965_mac_reset_tsf(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = hw->priv; + unsigned long flags; + + mutex_lock(&priv->mutex); + IWL_DEBUG_MAC80211("enter\n"); + + spin_lock_irqsave(&priv->lock, flags); + memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_info)); + spin_unlock_irqrestore(&priv->lock, flags); + + iwl_reset_qos(priv); + + spin_lock_irqsave(&priv->lock, flags); + priv->assoc_id = 0; + priv->assoc_capability = 0; + priv->assoc_station_added = 0; + + /* new association get rid of ibss beacon skb */ + if (priv->ibss_beacon) + dev_kfree_skb(priv->ibss_beacon); + + priv->ibss_beacon = NULL; + + priv->beacon_int = priv->hw->conf.beacon_int; + priv->timestamp = 0; + if ((priv->iw_mode == IEEE80211_IF_TYPE_STA)) + priv->beacon_int = 0; + + spin_unlock_irqrestore(&priv->lock, flags); + + if (!iwl_is_ready_rf(priv)) { + IWL_DEBUG_MAC80211("leave - not ready\n"); + mutex_unlock(&priv->mutex); + return; + } + + /* we are restarting association process + * clear RXON_FILTER_ASSOC_MSK bit + */ + if (priv->iw_mode != IEEE80211_IF_TYPE_AP) { + iwl_scan_cancel_timeout(priv, 100); + priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl4965_commit_rxon(priv); + } + + iwl_power_update_mode(priv, 0); + + /* Per mac80211.h: This is only used in IBSS mode... */ + if (priv->iw_mode != IEEE80211_IF_TYPE_IBSS) { + + IWL_DEBUG_MAC80211("leave - not in IBSS\n"); + mutex_unlock(&priv->mutex); + return; + } + + iwl4965_set_rate(priv); + + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211("leave\n"); +} + +static int iwl4965_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct iwl_priv *priv = hw->priv; + unsigned long flags; + __le64 timestamp; + + mutex_lock(&priv->mutex); + IWL_DEBUG_MAC80211("enter\n"); + + if (!iwl_is_ready_rf(priv)) { + IWL_DEBUG_MAC80211("leave - RF not ready\n"); + mutex_unlock(&priv->mutex); + return -EIO; + } + + if (priv->iw_mode != IEEE80211_IF_TYPE_IBSS) { + IWL_DEBUG_MAC80211("leave - not IBSS\n"); + mutex_unlock(&priv->mutex); + return -EIO; + } + + spin_lock_irqsave(&priv->lock, flags); + + if (priv->ibss_beacon) + dev_kfree_skb(priv->ibss_beacon); + + priv->ibss_beacon = skb; + + priv->assoc_id = 0; + timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; + priv->timestamp = le64_to_cpu(timestamp) + (priv->beacon_int * 1000); + + IWL_DEBUG_MAC80211("leave\n"); + spin_unlock_irqrestore(&priv->lock, flags); + + iwl_reset_qos(priv); + + iwl4965_post_associate(priv); + + mutex_unlock(&priv->mutex); + + return 0; +} + +/***************************************************************************** + * + * sysfs attributes + * + *****************************************************************************/ + +#ifdef CONFIG_IWLWIFI_DEBUG + +/* + * The following adds a new attribute to the sysfs representation + * of this device driver (i.e. a new file in /sys/bus/pci/drivers/iwl/) + * used for controlling the debug level. + * + * See the level definitions in iwl for details. + */ + +static ssize_t show_debug_level(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = d->driver_data; + + return sprintf(buf, "0x%08X\n", priv->debug_level); +} +static ssize_t store_debug_level(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl_priv *priv = d->driver_data; + char *p = (char *)buf; + u32 val; + + val = simple_strtoul(p, &p, 0); + if (p == buf) + printk(KERN_INFO DRV_NAME + ": %s is not in hex or decimal form.\n", buf); + else + priv->debug_level = val; + + return strnlen(buf, count); +} + +static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, + show_debug_level, store_debug_level); + + +#endif /* CONFIG_IWLWIFI_DEBUG */ + + +static ssize_t show_version(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = d->driver_data; + struct iwl_alive_resp *palive = &priv->card_alive; + ssize_t pos = 0; + u16 eeprom_ver; + + if (palive->is_valid) + pos += sprintf(buf + pos, + "fw version: 0x%01X.0x%01X.0x%01X.0x%01X\n" + "fw type: 0x%01X 0x%01X\n", + palive->ucode_major, palive->ucode_minor, + palive->sw_rev[0], palive->sw_rev[1], + palive->ver_type, palive->ver_subtype); + else + pos += sprintf(buf + pos, "fw not loaded\n"); + + if (priv->eeprom) { + eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); + pos += sprintf(buf + pos, "EEPROM version: 0x%x\n", + eeprom_ver); + } else { + pos += sprintf(buf + pos, "EEPROM not initialzed\n"); + } + + return pos; +} + +static DEVICE_ATTR(version, S_IWUSR | S_IRUGO, show_version, NULL); + +static ssize_t show_temperature(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + return sprintf(buf, "%d\n", priv->temperature); +} + +static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL); + +static ssize_t show_tx_power(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; + return sprintf(buf, "%d\n", priv->tx_power_user_lmt); +} + +static ssize_t store_tx_power(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; + char *p = (char *)buf; + u32 val; + + val = simple_strtoul(p, &p, 10); + if (p == buf) + printk(KERN_INFO DRV_NAME + ": %s is not in decimal form.\n", buf); + else + iwl_set_tx_power(priv, val, false); + + return count; +} + +static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, show_tx_power, store_tx_power); + +static ssize_t show_flags(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; + + return sprintf(buf, "0x%04X\n", priv->active_rxon.flags); +} + +static ssize_t store_flags(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; + u32 flags = simple_strtoul(buf, NULL, 0); + + mutex_lock(&priv->mutex); + if (le32_to_cpu(priv->staging_rxon.flags) != flags) { + /* Cancel any currently running scans... */ + if (iwl_scan_cancel_timeout(priv, 100)) + IWL_WARNING("Could not cancel scan.\n"); + else { + IWL_DEBUG_INFO("Committing rxon.flags = 0x%04X\n", + flags); + priv->staging_rxon.flags = cpu_to_le32(flags); + iwl4965_commit_rxon(priv); + } + } + mutex_unlock(&priv->mutex); + + return count; +} + +static DEVICE_ATTR(flags, S_IWUSR | S_IRUGO, show_flags, store_flags); + +static ssize_t show_filter_flags(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; + + return sprintf(buf, "0x%04X\n", + le32_to_cpu(priv->active_rxon.filter_flags)); +} + +static ssize_t store_filter_flags(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; + u32 filter_flags = simple_strtoul(buf, NULL, 0); + + mutex_lock(&priv->mutex); + if (le32_to_cpu(priv->staging_rxon.filter_flags) != filter_flags) { + /* Cancel any currently running scans... */ + if (iwl_scan_cancel_timeout(priv, 100)) + IWL_WARNING("Could not cancel scan.\n"); + else { + IWL_DEBUG_INFO("Committing rxon.filter_flags = " + "0x%04X\n", filter_flags); + priv->staging_rxon.filter_flags = + cpu_to_le32(filter_flags); + iwl4965_commit_rxon(priv); + } + } + mutex_unlock(&priv->mutex); + + return count; +} + +static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, show_filter_flags, + store_filter_flags); + +#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT + +static ssize_t show_measurement(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + struct iwl4965_spectrum_notification measure_report; + u32 size = sizeof(measure_report), len = 0, ofs = 0; + u8 *data = (u8 *) & measure_report; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + if (!(priv->measurement_status & MEASUREMENT_READY)) { + spin_unlock_irqrestore(&priv->lock, flags); + return 0; + } + memcpy(&measure_report, &priv->measure_report, size); + priv->measurement_status = 0; + spin_unlock_irqrestore(&priv->lock, flags); + + while (size && (PAGE_SIZE - len)) { + hex_dump_to_buffer(data + ofs, size, 16, 1, buf + len, + PAGE_SIZE - len, 1); + len = strlen(buf); + if (PAGE_SIZE - len) + buf[len++] = '\n'; + + ofs += 16; + size -= min(size, 16U); + } + + return len; +} + +static ssize_t store_measurement(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + struct ieee80211_measurement_params params = { + .channel = le16_to_cpu(priv->active_rxon.channel), + .start_time = cpu_to_le64(priv->last_tsf), + .duration = cpu_to_le16(1), + }; + u8 type = IWL_MEASURE_BASIC; + u8 buffer[32]; + u8 channel; + + if (count) { + char *p = buffer; + strncpy(buffer, buf, min(sizeof(buffer), count)); + channel = simple_strtoul(p, NULL, 0); + if (channel) + params.channel = channel; + + p = buffer; + while (*p && *p != ' ') + p++; + if (*p) + type = simple_strtoul(p + 1, NULL, 0); + } + + IWL_DEBUG_INFO("Invoking measurement of type %d on " + "channel %d (for '%s')\n", type, params.channel, buf); + iwl4965_get_measurement(priv, ¶ms, type); + + return count; +} + +static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR, + show_measurement, store_measurement); +#endif /* CONFIG_IWL4965_SPECTRUM_MEASUREMENT */ + +static ssize_t store_retry_rate(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + + priv->retry_rate = simple_strtoul(buf, NULL, 0); + if (priv->retry_rate <= 0) + priv->retry_rate = 1; + + return count; +} + +static ssize_t show_retry_rate(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + return sprintf(buf, "%d", priv->retry_rate); +} + +static DEVICE_ATTR(retry_rate, S_IWUSR | S_IRUSR, show_retry_rate, + store_retry_rate); + +static ssize_t store_power_level(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + int ret; + int mode; + + mode = simple_strtoul(buf, NULL, 0); + mutex_lock(&priv->mutex); + + if (!iwl_is_ready(priv)) { + ret = -EAGAIN; + goto out; + } + + ret = iwl_power_set_user_mode(priv, mode); + if (ret) { + IWL_DEBUG_MAC80211("failed setting power mode.\n"); + goto out; + } + ret = count; + + out: + mutex_unlock(&priv->mutex); + return ret; +} + +static ssize_t show_power_level(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + int mode = priv->power_data.user_power_setting; + int system = priv->power_data.system_power_setting; + int level = priv->power_data.power_mode; + char *p = buf; + + switch (system) { + case IWL_POWER_SYS_AUTO: + p += sprintf(p, "SYSTEM:auto"); + break; + case IWL_POWER_SYS_AC: + p += sprintf(p, "SYSTEM:ac"); + break; + case IWL_POWER_SYS_BATTERY: + p += sprintf(p, "SYSTEM:battery"); + break; + } + + p += sprintf(p, "\tMODE:%s", (mode < IWL_POWER_AUTO)?"fixed":"auto"); + p += sprintf(p, "\tINDEX:%d", level); + p += sprintf(p, "\n"); + return (p - buf + 1); +} + +static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, show_power_level, + store_power_level); + +static ssize_t show_channels(struct device *d, + struct device_attribute *attr, char *buf) +{ + + struct iwl_priv *priv = dev_get_drvdata(d); + struct ieee80211_channel *channels = NULL; + const struct ieee80211_supported_band *supp_band = NULL; + int len = 0, i; + int count = 0; + + if (!test_bit(STATUS_GEO_CONFIGURED, &priv->status)) + return -EAGAIN; + + supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ); + channels = supp_band->channels; + count = supp_band->n_channels; + + len += sprintf(&buf[len], + "Displaying %d channels in 2.4GHz band " + "(802.11bg):\n", count); + + for (i = 0; i < count; i++) + len += sprintf(&buf[len], "%d: %ddBm: BSS%s%s, %s.\n", + ieee80211_frequency_to_channel( + channels[i].center_freq), + channels[i].max_power, + channels[i].flags & IEEE80211_CHAN_RADAR ? + " (IEEE 802.11h required)" : "", + (!(channels[i].flags & IEEE80211_CHAN_NO_IBSS) + || (channels[i].flags & + IEEE80211_CHAN_RADAR)) ? "" : + ", IBSS", + channels[i].flags & + IEEE80211_CHAN_PASSIVE_SCAN ? + "passive only" : "active/passive"); + + supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ); + channels = supp_band->channels; + count = supp_band->n_channels; + + len += sprintf(&buf[len], "Displaying %d channels in 5.2GHz band " + "(802.11a):\n", count); + + for (i = 0; i < count; i++) + len += sprintf(&buf[len], "%d: %ddBm: BSS%s%s, %s.\n", + ieee80211_frequency_to_channel( + channels[i].center_freq), + channels[i].max_power, + channels[i].flags & IEEE80211_CHAN_RADAR ? + " (IEEE 802.11h required)" : "", + ((channels[i].flags & IEEE80211_CHAN_NO_IBSS) + || (channels[i].flags & + IEEE80211_CHAN_RADAR)) ? "" : + ", IBSS", + channels[i].flags & + IEEE80211_CHAN_PASSIVE_SCAN ? + "passive only" : "active/passive"); + + return len; +} + +static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL); + +static ssize_t show_statistics(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + u32 size = sizeof(struct iwl_notif_statistics); + u32 len = 0, ofs = 0; + u8 *data = (u8 *) & priv->statistics; + int rc = 0; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + mutex_lock(&priv->mutex); + rc = iwl_send_statistics_request(priv, 0); + mutex_unlock(&priv->mutex); + + if (rc) { + len = sprintf(buf, + "Error sending statistics request: 0x%08X\n", rc); + return len; + } + + while (size && (PAGE_SIZE - len)) { + hex_dump_to_buffer(data + ofs, size, 16, 1, buf + len, + PAGE_SIZE - len, 1); + len = strlen(buf); + if (PAGE_SIZE - len) + buf[len++] = '\n'; + + ofs += 16; + size -= min(size, 16U); + } + + return len; +} + +static DEVICE_ATTR(statistics, S_IRUGO, show_statistics, NULL); + +static ssize_t show_status(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; + if (!iwl_is_alive(priv)) + return -EAGAIN; + return sprintf(buf, "0x%08x\n", (int)priv->status); +} + +static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); + +/***************************************************************************** + * + * driver setup and teardown + * + *****************************************************************************/ + +static void iwl_setup_deferred_work(struct iwl_priv *priv) +{ + priv->workqueue = create_workqueue(DRV_NAME); + + init_waitqueue_head(&priv->wait_command_queue); + + INIT_WORK(&priv->up, iwl4965_bg_up); + INIT_WORK(&priv->restart, iwl4965_bg_restart); + INIT_WORK(&priv->rx_replenish, iwl4965_bg_rx_replenish); + INIT_WORK(&priv->rf_kill, iwl4965_bg_rf_kill); + INIT_WORK(&priv->beacon_update, iwl4965_bg_beacon_update); + INIT_WORK(&priv->set_monitor, iwl4965_bg_set_monitor); + INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work); + INIT_DELAYED_WORK(&priv->init_alive_start, iwl_bg_init_alive_start); + INIT_DELAYED_WORK(&priv->alive_start, iwl_bg_alive_start); + + /* FIXME : remove when resolved PENDING */ + INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed); + iwl_setup_scan_deferred_work(priv); + + if (priv->cfg->ops->lib->setup_deferred_work) + priv->cfg->ops->lib->setup_deferred_work(priv); + + init_timer(&priv->statistics_periodic); + priv->statistics_periodic.data = (unsigned long)priv; + priv->statistics_periodic.function = iwl4965_bg_statistics_periodic; + + tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) + iwl4965_irq_tasklet, (unsigned long)priv); +} + +static void iwl_cancel_deferred_work(struct iwl_priv *priv) +{ + if (priv->cfg->ops->lib->cancel_deferred_work) + priv->cfg->ops->lib->cancel_deferred_work(priv); + + cancel_delayed_work_sync(&priv->init_alive_start); + cancel_delayed_work(&priv->scan_check); + cancel_delayed_work(&priv->alive_start); + cancel_work_sync(&priv->beacon_update); + del_timer_sync(&priv->statistics_periodic); +} + +static struct attribute *iwl4965_sysfs_entries[] = { + &dev_attr_channels.attr, + &dev_attr_flags.attr, + &dev_attr_filter_flags.attr, +#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT + &dev_attr_measurement.attr, +#endif + &dev_attr_power_level.attr, + &dev_attr_retry_rate.attr, + &dev_attr_statistics.attr, + &dev_attr_status.attr, + &dev_attr_temperature.attr, + &dev_attr_tx_power.attr, +#ifdef CONFIG_IWLWIFI_DEBUG + &dev_attr_debug_level.attr, +#endif + &dev_attr_version.attr, + + NULL +}; + +static struct attribute_group iwl4965_attribute_group = { + .name = NULL, /* put in device directory */ + .attrs = iwl4965_sysfs_entries, +}; + +static struct ieee80211_ops iwl4965_hw_ops = { + .tx = iwl4965_mac_tx, + .start = iwl4965_mac_start, + .stop = iwl4965_mac_stop, + .add_interface = iwl4965_mac_add_interface, + .remove_interface = iwl4965_mac_remove_interface, + .config = iwl4965_mac_config, + .config_interface = iwl4965_mac_config_interface, + .configure_filter = iwl4965_configure_filter, + .set_key = iwl4965_mac_set_key, + .update_tkip_key = iwl4965_mac_update_tkip_key, + .get_stats = iwl4965_mac_get_stats, + .get_tx_stats = iwl4965_mac_get_tx_stats, + .conf_tx = iwl4965_mac_conf_tx, + .reset_tsf = iwl4965_mac_reset_tsf, + .bss_info_changed = iwl4965_bss_info_changed, + .ampdu_action = iwl4965_mac_ampdu_action, + .hw_scan = iwl4965_mac_hw_scan +}; + +static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = 0; + struct iwl_priv *priv; + struct ieee80211_hw *hw; + struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data); + unsigned long flags; + DECLARE_MAC_BUF(mac); + + /************************ + * 1. Allocating HW data + ************************/ + + /* Disabling hardware scan means that mac80211 will perform scans + * "the hard way", rather than using device's scan. */ + if (cfg->mod_params->disable_hw_scan) { + if (cfg->mod_params->debug & IWL_DL_INFO) + dev_printk(KERN_DEBUG, &(pdev->dev), + "Disabling hw_scan\n"); + iwl4965_hw_ops.hw_scan = NULL; + } + + hw = iwl_alloc_all(cfg, &iwl4965_hw_ops); + if (!hw) { + err = -ENOMEM; + goto out; + } + priv = hw->priv; + /* At this point both hw and priv are allocated. */ + + SET_IEEE80211_DEV(hw, &pdev->dev); + + IWL_DEBUG_INFO("*** LOAD DRIVER ***\n"); + priv->cfg = cfg; + priv->pci_dev = pdev; + +#ifdef CONFIG_IWLWIFI_DEBUG + priv->debug_level = priv->cfg->mod_params->debug; + atomic_set(&priv->restrict_refcnt, 0); +#endif + + /************************** + * 2. Initializing PCI bus + **************************/ + if (pci_enable_device(pdev)) { + err = -ENODEV; + goto out_ieee80211_free_hw; + } + + pci_set_master(pdev); + + err = pci_set_dma_mask(pdev, DMA_64BIT_MASK); + if (!err) + err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); + if (err) { + err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (!err) + err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + /* both attempts failed: */ + if (err) { + printk(KERN_WARNING "%s: No suitable DMA available.\n", + DRV_NAME); + goto out_pci_disable_device; + } + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) + goto out_pci_disable_device; + + pci_set_drvdata(pdev, priv); + + /* We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state */ + pci_write_config_byte(pdev, 0x41, 0x00); + + /*********************** + * 3. Read REV register + ***********************/ + priv->hw_base = pci_iomap(pdev, 0, 0); + if (!priv->hw_base) { + err = -ENODEV; + goto out_pci_release_regions; + } + + IWL_DEBUG_INFO("pci_resource_len = 0x%08llx\n", + (unsigned long long) pci_resource_len(pdev, 0)); + IWL_DEBUG_INFO("pci_resource_base = %p\n", priv->hw_base); + + iwl_hw_detect(priv); + printk(KERN_INFO DRV_NAME + ": Detected Intel Wireless WiFi Link %s REV=0x%X\n", + priv->cfg->name, priv->hw_rev); + + /* amp init */ + err = priv->cfg->ops->lib->apm_ops.init(priv); + if (err < 0) { + IWL_DEBUG_INFO("Failed to init APMG\n"); + goto out_iounmap; + } + /***************** + * 4. Read EEPROM + *****************/ + /* Read the EEPROM */ + err = iwl_eeprom_init(priv); + if (err) { + IWL_ERROR("Unable to init EEPROM\n"); + goto out_iounmap; + } + err = iwl_eeprom_check_version(priv); + if (err) + goto out_iounmap; + + /* extract MAC Address */ + iwl_eeprom_get_mac(priv, priv->mac_addr); + IWL_DEBUG_INFO("MAC address: %s\n", print_mac(mac, priv->mac_addr)); + SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr); + + /************************ + * 5. Setup HW constants + ************************/ + if (iwl_set_hw_params(priv)) { + IWL_ERROR("failed to set hw parameters\n"); + goto out_free_eeprom; + } + + /******************* + * 6. Setup priv + *******************/ + + err = iwl_init_drv(priv); + if (err) + goto out_free_eeprom; + /* At this point both hw and priv are initialized. */ + + /********************************** + * 7. Initialize module parameters + **********************************/ + + /* Disable radio (SW RF KILL) via parameter when loading driver */ + if (priv->cfg->mod_params->disable) { + set_bit(STATUS_RF_KILL_SW, &priv->status); + IWL_DEBUG_INFO("Radio disabled.\n"); + } + + /******************** + * 8. Setup services + ********************/ + spin_lock_irqsave(&priv->lock, flags); + iwl4965_disable_interrupts(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + err = sysfs_create_group(&pdev->dev.kobj, &iwl4965_attribute_group); + if (err) { + IWL_ERROR("failed to create sysfs device attributes\n"); + goto out_uninit_drv; + } + + + iwl_setup_deferred_work(priv); + iwl_setup_rx_handlers(priv); + + /******************** + * 9. Conclude + ********************/ + pci_save_state(pdev); + pci_disable_device(pdev); + + /********************************** + * 10. Setup and register mac80211 + **********************************/ + + err = iwl_setup_mac(priv); + if (err) + goto out_remove_sysfs; + + err = iwl_dbgfs_register(priv, DRV_NAME); + if (err) + IWL_ERROR("failed to create debugfs files\n"); + + err = iwl_rfkill_init(priv); + if (err) + IWL_ERROR("Unable to initialize RFKILL system. " + "Ignoring error: %d\n", err); + iwl_power_initialize(priv); + return 0; + + out_remove_sysfs: + sysfs_remove_group(&pdev->dev.kobj, &iwl4965_attribute_group); + out_uninit_drv: + iwl_uninit_drv(priv); + out_free_eeprom: + iwl_eeprom_free(priv); + out_iounmap: + pci_iounmap(pdev, priv->hw_base); + out_pci_release_regions: + pci_release_regions(pdev); + pci_set_drvdata(pdev, NULL); + out_pci_disable_device: + pci_disable_device(pdev); + out_ieee80211_free_hw: + ieee80211_free_hw(priv->hw); + out: + return err; +} + +static void __devexit iwl4965_pci_remove(struct pci_dev *pdev) +{ + struct iwl_priv *priv = pci_get_drvdata(pdev); + unsigned long flags; + + if (!priv) + return; + + IWL_DEBUG_INFO("*** UNLOAD DRIVER ***\n"); + + iwl_dbgfs_unregister(priv); + sysfs_remove_group(&pdev->dev.kobj, &iwl4965_attribute_group); + + if (priv->mac80211_registered) { + ieee80211_unregister_hw(priv->hw); + priv->mac80211_registered = 0; + } + + set_bit(STATUS_EXIT_PENDING, &priv->status); + + iwl4965_down(priv); + + /* make sure we flush any pending irq or + * tasklet for the driver + */ + spin_lock_irqsave(&priv->lock, flags); + iwl4965_disable_interrupts(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + iwl_synchronize_irq(priv); + + iwl_rfkill_unregister(priv); + iwl4965_dealloc_ucode_pci(priv); + + if (priv->rxq.bd) + iwl_rx_queue_free(priv, &priv->rxq); + iwl_hw_txq_ctx_free(priv); + + iwl_clear_stations_table(priv); + iwl_eeprom_free(priv); + + + /*netif_stop_queue(dev); */ + flush_workqueue(priv->workqueue); + + /* ieee80211_unregister_hw calls iwl4965_mac_stop, which flushes + * priv->workqueue... so we can't take down the workqueue + * until now... */ + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + + pci_iounmap(pdev, priv->hw_base); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + + iwl_uninit_drv(priv); + + if (priv->ibss_beacon) + dev_kfree_skb(priv->ibss_beacon); + + ieee80211_free_hw(priv->hw); +} + +#ifdef CONFIG_PM + +static int iwl4965_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct iwl_priv *priv = pci_get_drvdata(pdev); + + if (priv->is_open) { + set_bit(STATUS_IN_SUSPEND, &priv->status); + iwl4965_mac_stop(priv->hw); + priv->is_open = 1; + } + + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int iwl4965_pci_resume(struct pci_dev *pdev) +{ + struct iwl_priv *priv = pci_get_drvdata(pdev); + + pci_set_power_state(pdev, PCI_D0); + + if (priv->is_open) + iwl4965_mac_start(priv->hw); + + clear_bit(STATUS_IN_SUSPEND, &priv->status); + return 0; +} + +#endif /* CONFIG_PM */ + +/***************************************************************************** + * + * driver and module entry point + * + *****************************************************************************/ + +/* Hardware specific file defines the PCI IDs table for that hardware module */ +static struct pci_device_id iwl_hw_card_ids[] = { + {IWL_PCI_DEVICE(0x4229, PCI_ANY_ID, iwl4965_agn_cfg)}, + {IWL_PCI_DEVICE(0x4230, PCI_ANY_ID, iwl4965_agn_cfg)}, +#ifdef CONFIG_IWL5000 + {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bg_cfg)}, + {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bg_cfg)}, + {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, + {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, + {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, + {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, + {IWL_PCI_DEVICE(0x4232, PCI_ANY_ID, iwl5100_agn_cfg)}, + {IWL_PCI_DEVICE(0x4235, PCI_ANY_ID, iwl5300_agn_cfg)}, + {IWL_PCI_DEVICE(0x4236, PCI_ANY_ID, iwl5300_agn_cfg)}, + {IWL_PCI_DEVICE(0x4237, PCI_ANY_ID, iwl5100_agn_cfg)}, + {IWL_PCI_DEVICE(0x423A, PCI_ANY_ID, iwl5350_agn_cfg)}, +#endif /* CONFIG_IWL5000 */ + {0} +}; +MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); + +static struct pci_driver iwl_driver = { + .name = DRV_NAME, + .id_table = iwl_hw_card_ids, + .probe = iwl4965_pci_probe, + .remove = __devexit_p(iwl4965_pci_remove), +#ifdef CONFIG_PM + .suspend = iwl4965_pci_suspend, + .resume = iwl4965_pci_resume, +#endif +}; + +static int __init iwl4965_init(void) +{ + + int ret; + printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n"); + printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n"); + + ret = iwlagn_rate_control_register(); + if (ret) { + IWL_ERROR("Unable to register rate control algorithm: %d\n", ret); + return ret; + } + + ret = pci_register_driver(&iwl_driver); + if (ret) { + IWL_ERROR("Unable to initialize PCI module\n"); + goto error_register; + } + + return ret; + +error_register: + iwlagn_rate_control_unregister(); + return ret; +} + +static void __exit iwl4965_exit(void) +{ + pci_unregister_driver(&iwl_driver); + iwlagn_rate_control_unregister(); +} + +module_exit(iwl4965_exit); +module_init(iwl4965_init); diff --git a/drivers/net/wireless/iwlwifi/iwl4965-base.c b/drivers/net/wireless/iwlwifi/iwl4965-base.c deleted file mode 100644 index f71b3f3f81ba..000000000000 --- a/drivers/net/wireless/iwlwifi/iwl4965-base.c +++ /dev/null @@ -1,4523 +0,0 @@ -/****************************************************************************** - * - * Copyright(c) 2003 - 2008 Intel Corporation. All rights reserved. - * - * Portions of this file are derived from the ipw3945 project, as well - * as portions of the ieee80211 subsystem header files. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA - * - * The full GNU General Public License is included in this distribution in the - * file called LICENSE. - * - * Contact Information: - * James P. Ketrenos - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - *****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "iwl-eeprom.h" -#include "iwl-dev.h" -#include "iwl-core.h" -#include "iwl-io.h" -#include "iwl-helpers.h" -#include "iwl-sta.h" -#include "iwl-calib.h" - - -/****************************************************************************** - * - * module boiler plate - * - ******************************************************************************/ - -/* - * module name, copyright, version, etc. - * NOTE: DRV_NAME is defined in iwlwifi.h for use by iwl-debug.h and printk - */ - -#define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link AGN driver for Linux" - -#ifdef CONFIG_IWLWIFI_DEBUG -#define VD "d" -#else -#define VD -#endif - -#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT -#define VS "s" -#else -#define VS -#endif - -#define DRV_VERSION IWLWIFI_VERSION VD VS - - -MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); -MODULE_AUTHOR(DRV_COPYRIGHT); -MODULE_LICENSE("GPL"); - -/*************** STATION TABLE MANAGEMENT **** - * mac80211 should be examined to determine if sta_info is duplicating - * the functionality provided here - */ - -/**************************************************************/ - - - -static void iwl4965_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt) -{ - struct iwl_rxon_cmd *rxon = &priv->staging_rxon; - - if (hw_decrypt) - rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; - else - rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; - -} - -/** - * iwl4965_check_rxon_cmd - validate RXON structure is valid - * - * NOTE: This is really only useful during development and can eventually - * be #ifdef'd out once the driver is stable and folks aren't actively - * making changes - */ -static int iwl4965_check_rxon_cmd(struct iwl_rxon_cmd *rxon) -{ - int error = 0; - int counter = 1; - - if (rxon->flags & RXON_FLG_BAND_24G_MSK) { - error |= le32_to_cpu(rxon->flags & - (RXON_FLG_TGJ_NARROW_BAND_MSK | - RXON_FLG_RADAR_DETECT_MSK)); - if (error) - IWL_WARNING("check 24G fields %d | %d\n", - counter++, error); - } else { - error |= (rxon->flags & RXON_FLG_SHORT_SLOT_MSK) ? - 0 : le32_to_cpu(RXON_FLG_SHORT_SLOT_MSK); - if (error) - IWL_WARNING("check 52 fields %d | %d\n", - counter++, error); - error |= le32_to_cpu(rxon->flags & RXON_FLG_CCK_MSK); - if (error) - IWL_WARNING("check 52 CCK %d | %d\n", - counter++, error); - } - error |= (rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1; - if (error) - IWL_WARNING("check mac addr %d | %d\n", counter++, error); - - /* make sure basic rates 6Mbps and 1Mbps are supported */ - error |= (((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0) && - ((rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0)); - if (error) - IWL_WARNING("check basic rate %d | %d\n", counter++, error); - - error |= (le16_to_cpu(rxon->assoc_id) > 2007); - if (error) - IWL_WARNING("check assoc id %d | %d\n", counter++, error); - - error |= ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) - == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)); - if (error) - IWL_WARNING("check CCK and short slot %d | %d\n", - counter++, error); - - error |= ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) - == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)); - if (error) - IWL_WARNING("check CCK & auto detect %d | %d\n", - counter++, error); - - error |= ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK | - RXON_FLG_TGG_PROTECT_MSK)) == RXON_FLG_TGG_PROTECT_MSK); - if (error) - IWL_WARNING("check TGG and auto detect %d | %d\n", - counter++, error); - - if (error) - IWL_WARNING("Tuning to channel %d\n", - le16_to_cpu(rxon->channel)); - - if (error) { - IWL_ERROR("Not a valid iwl4965_rxon_assoc_cmd field values\n"); - return -1; - } - return 0; -} - -/** - * iwl4965_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed - * @priv: staging_rxon is compared to active_rxon - * - * If the RXON structure is changing enough to require a new tune, - * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that - * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. - */ -static int iwl4965_full_rxon_required(struct iwl_priv *priv) -{ - - /* These items are only settable from the full RXON command */ - if (!(iwl_is_associated(priv)) || - compare_ether_addr(priv->staging_rxon.bssid_addr, - priv->active_rxon.bssid_addr) || - compare_ether_addr(priv->staging_rxon.node_addr, - priv->active_rxon.node_addr) || - compare_ether_addr(priv->staging_rxon.wlap_bssid_addr, - priv->active_rxon.wlap_bssid_addr) || - (priv->staging_rxon.dev_type != priv->active_rxon.dev_type) || - (priv->staging_rxon.channel != priv->active_rxon.channel) || - (priv->staging_rxon.air_propagation != - priv->active_rxon.air_propagation) || - (priv->staging_rxon.ofdm_ht_single_stream_basic_rates != - priv->active_rxon.ofdm_ht_single_stream_basic_rates) || - (priv->staging_rxon.ofdm_ht_dual_stream_basic_rates != - priv->active_rxon.ofdm_ht_dual_stream_basic_rates) || - (priv->staging_rxon.rx_chain != priv->active_rxon.rx_chain) || - (priv->staging_rxon.assoc_id != priv->active_rxon.assoc_id)) - return 1; - - /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can - * be updated with the RXON_ASSOC command -- however only some - * flag transitions are allowed using RXON_ASSOC */ - - /* Check if we are not switching bands */ - if ((priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) != - (priv->active_rxon.flags & RXON_FLG_BAND_24G_MSK)) - return 1; - - /* Check if we are switching association toggle */ - if ((priv->staging_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) != - (priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK)) - return 1; - - return 0; -} - -/** - * iwl4965_commit_rxon - commit staging_rxon to hardware - * - * The RXON command in staging_rxon is committed to the hardware and - * the active_rxon structure is updated with the new data. This - * function correctly transitions out of the RXON_ASSOC_MSK state if - * a HW tune is required based on the RXON structure changes. - */ -static int iwl4965_commit_rxon(struct iwl_priv *priv) -{ - /* cast away the const for active_rxon in this function */ - struct iwl_rxon_cmd *active_rxon = (void *)&priv->active_rxon; - DECLARE_MAC_BUF(mac); - int ret; - bool new_assoc = - !!(priv->staging_rxon.filter_flags & RXON_FILTER_ASSOC_MSK); - - if (!iwl_is_alive(priv)) - return -EBUSY; - - /* always get timestamp with Rx frame */ - priv->staging_rxon.flags |= RXON_FLG_TSF2HOST_MSK; - /* allow CTS-to-self if possible. this is relevant only for - * 5000, but will not damage 4965 */ - priv->staging_rxon.flags |= RXON_FLG_SELF_CTS_EN; - - ret = iwl4965_check_rxon_cmd(&priv->staging_rxon); - if (ret) { - IWL_ERROR("Invalid RXON configuration. Not committing.\n"); - return -EINVAL; - } - - /* If we don't need to send a full RXON, we can use - * iwl4965_rxon_assoc_cmd which is used to reconfigure filter - * and other flags for the current radio configuration. */ - if (!iwl4965_full_rxon_required(priv)) { - ret = iwl_send_rxon_assoc(priv); - if (ret) { - IWL_ERROR("Error setting RXON_ASSOC (%d)\n", ret); - return ret; - } - - memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon)); - return 0; - } - - /* station table will be cleared */ - priv->assoc_station_added = 0; - - /* If we are currently associated and the new config requires - * an RXON_ASSOC and the new config wants the associated mask enabled, - * we must clear the associated from the active configuration - * before we apply the new config */ - if (iwl_is_associated(priv) && new_assoc) { - IWL_DEBUG_INFO("Toggling associated bit on current RXON\n"); - active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - - ret = iwl_send_cmd_pdu(priv, REPLY_RXON, - sizeof(struct iwl_rxon_cmd), - &priv->active_rxon); - - /* If the mask clearing failed then we set - * active_rxon back to what it was previously */ - if (ret) { - active_rxon->filter_flags |= RXON_FILTER_ASSOC_MSK; - IWL_ERROR("Error clearing ASSOC_MSK (%d)\n", ret); - return ret; - } - } - - IWL_DEBUG_INFO("Sending RXON\n" - "* with%s RXON_FILTER_ASSOC_MSK\n" - "* channel = %d\n" - "* bssid = %s\n", - (new_assoc ? "" : "out"), - le16_to_cpu(priv->staging_rxon.channel), - print_mac(mac, priv->staging_rxon.bssid_addr)); - - iwl4965_set_rxon_hwcrypto(priv, !priv->hw_params.sw_crypto); - - /* Apply the new configuration - * RXON unassoc clears the station table in uCode, send it before - * we add the bcast station. If assoc bit is set, we will send RXON - * after having added the bcast and bssid station. - */ - if (!new_assoc) { - ret = iwl_send_cmd_pdu(priv, REPLY_RXON, - sizeof(struct iwl_rxon_cmd), &priv->staging_rxon); - if (ret) { - IWL_ERROR("Error setting new RXON (%d)\n", ret); - return ret; - } - memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon)); - } - - iwl_clear_stations_table(priv); - - if (!priv->error_recovering) - priv->start_calib = 0; - - /* Add the broadcast address so we can send broadcast frames */ - if (iwl_rxon_add_station(priv, iwl_bcast_addr, 0) == - IWL_INVALID_STATION) { - IWL_ERROR("Error adding BROADCAST address for transmit.\n"); - return -EIO; - } - - /* If we have set the ASSOC_MSK and we are in BSS mode then - * add the IWL_AP_ID to the station rate table */ - if (new_assoc) { - if (priv->iw_mode == IEEE80211_IF_TYPE_STA) { - ret = iwl_rxon_add_station(priv, - priv->active_rxon.bssid_addr, 1); - if (ret == IWL_INVALID_STATION) { - IWL_ERROR("Error adding AP address for TX.\n"); - return -EIO; - } - priv->assoc_station_added = 1; - if (priv->default_wep_key && - iwl_send_static_wepkey_cmd(priv, 0)) - IWL_ERROR("Could not send WEP static key.\n"); - } - - /* Apply the new configuration - * RXON assoc doesn't clear the station table in uCode, - */ - ret = iwl_send_cmd_pdu(priv, REPLY_RXON, - sizeof(struct iwl_rxon_cmd), &priv->staging_rxon); - if (ret) { - IWL_ERROR("Error setting new RXON (%d)\n", ret); - return ret; - } - memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon)); - } - - iwl_init_sensitivity(priv); - - /* If we issue a new RXON command which required a tune then we must - * send a new TXPOWER command or we won't be able to Tx any frames */ - ret = iwl_set_tx_power(priv, priv->tx_power_user_lmt, true); - if (ret) { - IWL_ERROR("Error sending TX power (%d)\n", ret); - return ret; - } - - return 0; -} - -void iwl4965_update_chain_flags(struct iwl_priv *priv) -{ - - iwl_set_rxon_chain(priv); - iwl4965_commit_rxon(priv); -} - -static int iwl4965_send_bt_config(struct iwl_priv *priv) -{ - struct iwl4965_bt_cmd bt_cmd = { - .flags = 3, - .lead_time = 0xAA, - .max_kill = 1, - .kill_ack_mask = 0, - .kill_cts_mask = 0, - }; - - return iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG, - sizeof(struct iwl4965_bt_cmd), &bt_cmd); -} - -static void iwl_clear_free_frames(struct iwl_priv *priv) -{ - struct list_head *element; - - IWL_DEBUG_INFO("%d frames on pre-allocated heap on clear.\n", - priv->frames_count); - - while (!list_empty(&priv->free_frames)) { - element = priv->free_frames.next; - list_del(element); - kfree(list_entry(element, struct iwl_frame, list)); - priv->frames_count--; - } - - if (priv->frames_count) { - IWL_WARNING("%d frames still in use. Did we lose one?\n", - priv->frames_count); - priv->frames_count = 0; - } -} - -static struct iwl_frame *iwl_get_free_frame(struct iwl_priv *priv) -{ - struct iwl_frame *frame; - struct list_head *element; - if (list_empty(&priv->free_frames)) { - frame = kzalloc(sizeof(*frame), GFP_KERNEL); - if (!frame) { - IWL_ERROR("Could not allocate frame!\n"); - return NULL; - } - - priv->frames_count++; - return frame; - } - - element = priv->free_frames.next; - list_del(element); - return list_entry(element, struct iwl_frame, list); -} - -static void iwl_free_frame(struct iwl_priv *priv, struct iwl_frame *frame) -{ - memset(frame, 0, sizeof(*frame)); - list_add(&frame->list, &priv->free_frames); -} - -static unsigned int iwl_fill_beacon_frame(struct iwl_priv *priv, - struct ieee80211_hdr *hdr, - const u8 *dest, int left) -{ - if (!iwl_is_associated(priv) || !priv->ibss_beacon || - ((priv->iw_mode != IEEE80211_IF_TYPE_IBSS) && - (priv->iw_mode != IEEE80211_IF_TYPE_AP))) - return 0; - - if (priv->ibss_beacon->len > left) - return 0; - - memcpy(hdr, priv->ibss_beacon->data, priv->ibss_beacon->len); - - return priv->ibss_beacon->len; -} - -static u8 iwl4965_rate_get_lowest_plcp(struct iwl_priv *priv) -{ - int i; - int rate_mask; - - /* Set rate mask*/ - if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) - rate_mask = priv->active_rate_basic & 0xF; - else - rate_mask = priv->active_rate_basic & 0xFF0; - - /* Find lowest valid rate */ - for (i = IWL_RATE_1M_INDEX; i != IWL_RATE_INVALID; - i = iwl_rates[i].next_ieee) { - if (rate_mask & (1 << i)) - return iwl_rates[i].plcp; - } - - /* No valid rate was found. Assign the lowest one */ - if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) - return IWL_RATE_1M_PLCP; - else - return IWL_RATE_6M_PLCP; -} - -unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv, - struct iwl_frame *frame, u8 rate) -{ - struct iwl_tx_beacon_cmd *tx_beacon_cmd; - unsigned int frame_size; - - tx_beacon_cmd = &frame->u.beacon; - memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); - - tx_beacon_cmd->tx.sta_id = priv->hw_params.bcast_sta_id; - tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - - frame_size = iwl_fill_beacon_frame(priv, tx_beacon_cmd->frame, - iwl_bcast_addr, - sizeof(frame->u) - sizeof(*tx_beacon_cmd)); - - BUG_ON(frame_size > MAX_MPDU_SIZE); - tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size); - - if ((rate == IWL_RATE_1M_PLCP) || (rate >= IWL_RATE_2M_PLCP)) - tx_beacon_cmd->tx.rate_n_flags = - iwl_hw_set_rate_n_flags(rate, RATE_MCS_CCK_MSK); - else - tx_beacon_cmd->tx.rate_n_flags = - iwl_hw_set_rate_n_flags(rate, 0); - - tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | - TX_CMD_FLG_TSF_MSK | - TX_CMD_FLG_STA_RATE_MSK; - - return sizeof(*tx_beacon_cmd) + frame_size; -} -static int iwl4965_send_beacon_cmd(struct iwl_priv *priv) -{ - struct iwl_frame *frame; - unsigned int frame_size; - int rc; - u8 rate; - - frame = iwl_get_free_frame(priv); - - if (!frame) { - IWL_ERROR("Could not obtain free frame buffer for beacon " - "command.\n"); - return -ENOMEM; - } - - rate = iwl4965_rate_get_lowest_plcp(priv); - - frame_size = iwl4965_hw_get_beacon_cmd(priv, frame, rate); - - rc = iwl_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size, - &frame->u.cmd[0]); - - iwl_free_frame(priv, frame); - - return rc; -} - -/****************************************************************************** - * - * Misc. internal state and helper functions - * - ******************************************************************************/ - -static void iwl4965_ht_conf(struct iwl_priv *priv, - struct ieee80211_bss_conf *bss_conf) -{ - struct ieee80211_ht_info *ht_conf = bss_conf->ht_conf; - struct ieee80211_ht_bss_info *ht_bss_conf = bss_conf->ht_bss_conf; - struct iwl_ht_info *iwl_conf = &priv->current_ht_config; - - IWL_DEBUG_MAC80211("enter: \n"); - - iwl_conf->is_ht = bss_conf->assoc_ht; - - if (!iwl_conf->is_ht) - return; - - priv->ps_mode = (u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2); - - if (ht_conf->cap & IEEE80211_HT_CAP_SGI_20) - iwl_conf->sgf |= HT_SHORT_GI_20MHZ; - if (ht_conf->cap & IEEE80211_HT_CAP_SGI_40) - iwl_conf->sgf |= HT_SHORT_GI_40MHZ; - - iwl_conf->is_green_field = !!(ht_conf->cap & IEEE80211_HT_CAP_GRN_FLD); - iwl_conf->max_amsdu_size = - !!(ht_conf->cap & IEEE80211_HT_CAP_MAX_AMSDU); - - iwl_conf->supported_chan_width = - !!(ht_conf->cap & IEEE80211_HT_CAP_SUP_WIDTH); - iwl_conf->extension_chan_offset = - ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_SEC_OFFSET; - /* If no above or below channel supplied disable FAT channel */ - if (iwl_conf->extension_chan_offset != IEEE80211_HT_IE_CHA_SEC_ABOVE && - iwl_conf->extension_chan_offset != IEEE80211_HT_IE_CHA_SEC_BELOW) { - iwl_conf->extension_chan_offset = IEEE80211_HT_IE_CHA_SEC_NONE; - iwl_conf->supported_chan_width = 0; - } - - iwl_conf->tx_mimo_ps_mode = - (u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2); - memcpy(iwl_conf->supp_mcs_set, ht_conf->supp_mcs_set, 16); - - iwl_conf->control_channel = ht_bss_conf->primary_channel; - iwl_conf->tx_chan_width = - !!(ht_bss_conf->bss_cap & IEEE80211_HT_IE_CHA_WIDTH); - iwl_conf->ht_protection = - ht_bss_conf->bss_op_mode & IEEE80211_HT_IE_HT_PROTECTION; - iwl_conf->non_GF_STA_present = - !!(ht_bss_conf->bss_op_mode & IEEE80211_HT_IE_NON_GF_STA_PRSNT); - - IWL_DEBUG_MAC80211("control channel %d\n", iwl_conf->control_channel); - IWL_DEBUG_MAC80211("leave\n"); -} - -/* - * QoS support -*/ -static void iwl_activate_qos(struct iwl_priv *priv, u8 force) -{ - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (!priv->qos_data.qos_enable) - return; - - priv->qos_data.def_qos_parm.qos_flags = 0; - - if (priv->qos_data.qos_cap.q_AP.queue_request && - !priv->qos_data.qos_cap.q_AP.txop_request) - priv->qos_data.def_qos_parm.qos_flags |= - QOS_PARAM_FLG_TXOP_TYPE_MSK; - if (priv->qos_data.qos_active) - priv->qos_data.def_qos_parm.qos_flags |= - QOS_PARAM_FLG_UPDATE_EDCA_MSK; - - if (priv->current_ht_config.is_ht) - priv->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; - - if (force || iwl_is_associated(priv)) { - IWL_DEBUG_QOS("send QoS cmd with Qos active=%d FLAGS=0x%X\n", - priv->qos_data.qos_active, - priv->qos_data.def_qos_parm.qos_flags); - - iwl_send_cmd_pdu_async(priv, REPLY_QOS_PARAM, - sizeof(struct iwl_qosparam_cmd), - &priv->qos_data.def_qos_parm, NULL); - } -} - -#define MAX_UCODE_BEACON_INTERVAL 4096 - -static __le16 iwl4965_adjust_beacon_interval(u16 beacon_val) -{ - u16 new_val = 0; - u16 beacon_factor = 0; - - beacon_factor = - (beacon_val + MAX_UCODE_BEACON_INTERVAL) - / MAX_UCODE_BEACON_INTERVAL; - new_val = beacon_val / beacon_factor; - - return cpu_to_le16(new_val); -} - -static void iwl4965_setup_rxon_timing(struct iwl_priv *priv) -{ - u64 interval_tm_unit; - u64 tsf, result; - unsigned long flags; - struct ieee80211_conf *conf = NULL; - u16 beacon_int = 0; - - conf = ieee80211_get_hw_conf(priv->hw); - - spin_lock_irqsave(&priv->lock, flags); - priv->rxon_timing.timestamp.dw[1] = cpu_to_le32(priv->timestamp >> 32); - priv->rxon_timing.timestamp.dw[0] = - cpu_to_le32(priv->timestamp & 0xFFFFFFFF); - - priv->rxon_timing.listen_interval = cpu_to_le16(conf->listen_interval); - - tsf = priv->timestamp; - - beacon_int = priv->beacon_int; - spin_unlock_irqrestore(&priv->lock, flags); - - if (priv->iw_mode == IEEE80211_IF_TYPE_STA) { - if (beacon_int == 0) { - priv->rxon_timing.beacon_interval = cpu_to_le16(100); - priv->rxon_timing.beacon_init_val = cpu_to_le32(102400); - } else { - priv->rxon_timing.beacon_interval = - cpu_to_le16(beacon_int); - priv->rxon_timing.beacon_interval = - iwl4965_adjust_beacon_interval( - le16_to_cpu(priv->rxon_timing.beacon_interval)); - } - - priv->rxon_timing.atim_window = 0; - } else { - priv->rxon_timing.beacon_interval = - iwl4965_adjust_beacon_interval(conf->beacon_int); - /* TODO: we need to get atim_window from upper stack - * for now we set to 0 */ - priv->rxon_timing.atim_window = 0; - } - - interval_tm_unit = - (le16_to_cpu(priv->rxon_timing.beacon_interval) * 1024); - result = do_div(tsf, interval_tm_unit); - priv->rxon_timing.beacon_init_val = - cpu_to_le32((u32) ((u64) interval_tm_unit - result)); - - IWL_DEBUG_ASSOC - ("beacon interval %d beacon timer %d beacon tim %d\n", - le16_to_cpu(priv->rxon_timing.beacon_interval), - le32_to_cpu(priv->rxon_timing.beacon_init_val), - le16_to_cpu(priv->rxon_timing.atim_window)); -} - -static void iwl_set_flags_for_band(struct iwl_priv *priv, - enum ieee80211_band band) -{ - if (band == IEEE80211_BAND_5GHZ) { - priv->staging_rxon.flags &= - ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK - | RXON_FLG_CCK_MSK); - priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK; - } else { - /* Copied from iwl4965_post_associate() */ - if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) - priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK; - else - priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - - if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) - priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - - priv->staging_rxon.flags |= RXON_FLG_BAND_24G_MSK; - priv->staging_rxon.flags |= RXON_FLG_AUTO_DETECT_MSK; - priv->staging_rxon.flags &= ~RXON_FLG_CCK_MSK; - } -} - -/* - * initialize rxon structure with default values from eeprom - */ -static void iwl4965_connection_init_rx_config(struct iwl_priv *priv) -{ - const struct iwl_channel_info *ch_info; - - memset(&priv->staging_rxon, 0, sizeof(priv->staging_rxon)); - - switch (priv->iw_mode) { - case IEEE80211_IF_TYPE_AP: - priv->staging_rxon.dev_type = RXON_DEV_TYPE_AP; - break; - - case IEEE80211_IF_TYPE_STA: - priv->staging_rxon.dev_type = RXON_DEV_TYPE_ESS; - priv->staging_rxon.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; - break; - - case IEEE80211_IF_TYPE_IBSS: - priv->staging_rxon.dev_type = RXON_DEV_TYPE_IBSS; - priv->staging_rxon.flags = RXON_FLG_SHORT_PREAMBLE_MSK; - priv->staging_rxon.filter_flags = RXON_FILTER_BCON_AWARE_MSK | - RXON_FILTER_ACCEPT_GRP_MSK; - break; - - case IEEE80211_IF_TYPE_MNTR: - priv->staging_rxon.dev_type = RXON_DEV_TYPE_SNIFFER; - priv->staging_rxon.filter_flags = RXON_FILTER_PROMISC_MSK | - RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_ACCEPT_GRP_MSK; - break; - default: - IWL_ERROR("Unsupported interface type %d\n", priv->iw_mode); - break; - } - -#if 0 - /* TODO: Figure out when short_preamble would be set and cache from - * that */ - if (!hw_to_local(priv->hw)->short_preamble) - priv->staging_rxon.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - else - priv->staging_rxon.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; -#endif - - ch_info = iwl_get_channel_info(priv, priv->band, - le16_to_cpu(priv->active_rxon.channel)); - - if (!ch_info) - ch_info = &priv->channel_info[0]; - - /* - * in some case A channels are all non IBSS - * in this case force B/G channel - */ - if ((priv->iw_mode == IEEE80211_IF_TYPE_IBSS) && - !(is_channel_ibss(ch_info))) - ch_info = &priv->channel_info[0]; - - priv->staging_rxon.channel = cpu_to_le16(ch_info->channel); - priv->band = ch_info->band; - - iwl_set_flags_for_band(priv, priv->band); - - priv->staging_rxon.ofdm_basic_rates = - (IWL_OFDM_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF; - priv->staging_rxon.cck_basic_rates = - (IWL_CCK_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF; - - priv->staging_rxon.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED_MSK | - RXON_FLG_CHANNEL_MODE_PURE_40_MSK); - memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); - memcpy(priv->staging_rxon.wlap_bssid_addr, priv->mac_addr, ETH_ALEN); - priv->staging_rxon.ofdm_ht_single_stream_basic_rates = 0xff; - priv->staging_rxon.ofdm_ht_dual_stream_basic_rates = 0xff; - iwl_set_rxon_chain(priv); -} - -static int iwl4965_set_mode(struct iwl_priv *priv, int mode) -{ - priv->iw_mode = mode; - - iwl4965_connection_init_rx_config(priv); - memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); - - iwl_clear_stations_table(priv); - - /* dont commit rxon if rf-kill is on*/ - if (!iwl_is_ready_rf(priv)) - return -EAGAIN; - - cancel_delayed_work(&priv->scan_check); - if (iwl_scan_cancel_timeout(priv, 100)) { - IWL_WARNING("Aborted scan still in progress after 100ms\n"); - IWL_DEBUG_MAC80211("leaving - scan abort failed.\n"); - return -EAGAIN; - } - - iwl4965_commit_rxon(priv); - - return 0; -} - -static void iwl4965_set_rate(struct iwl_priv *priv) -{ - const struct ieee80211_supported_band *hw = NULL; - struct ieee80211_rate *rate; - int i; - - hw = iwl_get_hw_mode(priv, priv->band); - if (!hw) { - IWL_ERROR("Failed to set rate: unable to get hw mode\n"); - return; - } - - priv->active_rate = 0; - priv->active_rate_basic = 0; - - for (i = 0; i < hw->n_bitrates; i++) { - rate = &(hw->bitrates[i]); - if (rate->hw_value < IWL_RATE_COUNT) - priv->active_rate |= (1 << rate->hw_value); - } - - IWL_DEBUG_RATE("Set active_rate = %0x, active_rate_basic = %0x\n", - priv->active_rate, priv->active_rate_basic); - - /* - * If a basic rate is configured, then use it (adding IWL_RATE_1M_MASK) - * otherwise set it to the default of all CCK rates and 6, 12, 24 for - * OFDM - */ - if (priv->active_rate_basic & IWL_CCK_BASIC_RATES_MASK) - priv->staging_rxon.cck_basic_rates = - ((priv->active_rate_basic & - IWL_CCK_RATES_MASK) >> IWL_FIRST_CCK_RATE) & 0xF; - else - priv->staging_rxon.cck_basic_rates = - (IWL_CCK_BASIC_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF; - - if (priv->active_rate_basic & IWL_OFDM_BASIC_RATES_MASK) - priv->staging_rxon.ofdm_basic_rates = - ((priv->active_rate_basic & - (IWL_OFDM_BASIC_RATES_MASK | IWL_RATE_6M_MASK)) >> - IWL_FIRST_OFDM_RATE) & 0xFF; - else - priv->staging_rxon.ofdm_basic_rates = - (IWL_OFDM_BASIC_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF; -} - -#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT - -#include "iwl-spectrum.h" - -#define BEACON_TIME_MASK_LOW 0x00FFFFFF -#define BEACON_TIME_MASK_HIGH 0xFF000000 -#define TIME_UNIT 1024 - -/* - * extended beacon time format - * time in usec will be changed into a 32-bit value in 8:24 format - * the high 1 byte is the beacon counts - * the lower 3 bytes is the time in usec within one beacon interval - */ - -static u32 iwl4965_usecs_to_beacons(u32 usec, u32 beacon_interval) -{ - u32 quot; - u32 rem; - u32 interval = beacon_interval * 1024; - - if (!interval || !usec) - return 0; - - quot = (usec / interval) & (BEACON_TIME_MASK_HIGH >> 24); - rem = (usec % interval) & BEACON_TIME_MASK_LOW; - - return (quot << 24) + rem; -} - -/* base is usually what we get from ucode with each received frame, - * the same as HW timer counter counting down - */ - -static __le32 iwl4965_add_beacon_time(u32 base, u32 addon, u32 beacon_interval) -{ - u32 base_low = base & BEACON_TIME_MASK_LOW; - u32 addon_low = addon & BEACON_TIME_MASK_LOW; - u32 interval = beacon_interval * TIME_UNIT; - u32 res = (base & BEACON_TIME_MASK_HIGH) + - (addon & BEACON_TIME_MASK_HIGH); - - if (base_low > addon_low) - res += base_low - addon_low; - else if (base_low < addon_low) { - res += interval + base_low - addon_low; - res += (1 << 24); - } else - res += (1 << 24); - - return cpu_to_le32(res); -} - -static int iwl4965_get_measurement(struct iwl_priv *priv, - struct ieee80211_measurement_params *params, - u8 type) -{ - struct iwl4965_spectrum_cmd spectrum; - struct iwl_rx_packet *res; - struct iwl_host_cmd cmd = { - .id = REPLY_SPECTRUM_MEASUREMENT_CMD, - .data = (void *)&spectrum, - .meta.flags = CMD_WANT_SKB, - }; - u32 add_time = le64_to_cpu(params->start_time); - int rc; - int spectrum_resp_status; - int duration = le16_to_cpu(params->duration); - - if (iwl_is_associated(priv)) - add_time = - iwl4965_usecs_to_beacons( - le64_to_cpu(params->start_time) - priv->last_tsf, - le16_to_cpu(priv->rxon_timing.beacon_interval)); - - memset(&spectrum, 0, sizeof(spectrum)); - - spectrum.channel_count = cpu_to_le16(1); - spectrum.flags = - RXON_FLG_TSF2HOST_MSK | RXON_FLG_ANT_A_MSK | RXON_FLG_DIS_DIV_MSK; - spectrum.filter_flags = MEASUREMENT_FILTER_FLAG; - cmd.len = sizeof(spectrum); - spectrum.len = cpu_to_le16(cmd.len - sizeof(spectrum.len)); - - if (iwl_is_associated(priv)) - spectrum.start_time = - iwl4965_add_beacon_time(priv->last_beacon_time, - add_time, - le16_to_cpu(priv->rxon_timing.beacon_interval)); - else - spectrum.start_time = 0; - - spectrum.channels[0].duration = cpu_to_le32(duration * TIME_UNIT); - spectrum.channels[0].channel = params->channel; - spectrum.channels[0].type = type; - if (priv->active_rxon.flags & RXON_FLG_BAND_24G_MSK) - spectrum.flags |= RXON_FLG_BAND_24G_MSK | - RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK; - - rc = iwl_send_cmd_sync(priv, &cmd); - if (rc) - return rc; - - res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; - if (res->hdr.flags & IWL_CMD_FAILED_MSK) { - IWL_ERROR("Bad return from REPLY_RX_ON_ASSOC command\n"); - rc = -EIO; - } - - spectrum_resp_status = le16_to_cpu(res->u.spectrum.status); - switch (spectrum_resp_status) { - case 0: /* Command will be handled */ - if (res->u.spectrum.id != 0xff) { - IWL_DEBUG_INFO - ("Replaced existing measurement: %d\n", - res->u.spectrum.id); - priv->measurement_status &= ~MEASUREMENT_READY; - } - priv->measurement_status |= MEASUREMENT_ACTIVE; - rc = 0; - break; - - case 1: /* Command will not be handled */ - rc = -EAGAIN; - break; - } - - dev_kfree_skb_any(cmd.meta.u.skb); - - return rc; -} -#endif - -/****************************************************************************** - * - * Generic RX handler implementations - * - ******************************************************************************/ -static void iwl_rx_reply_alive(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) -{ - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; - struct iwl_alive_resp *palive; - struct delayed_work *pwork; - - palive = &pkt->u.alive_frame; - - IWL_DEBUG_INFO("Alive ucode status 0x%08X revision " - "0x%01X 0x%01X\n", - palive->is_valid, palive->ver_type, - palive->ver_subtype); - - if (palive->ver_subtype == INITIALIZE_SUBTYPE) { - IWL_DEBUG_INFO("Initialization Alive received.\n"); - memcpy(&priv->card_alive_init, - &pkt->u.alive_frame, - sizeof(struct iwl_init_alive_resp)); - pwork = &priv->init_alive_start; - } else { - IWL_DEBUG_INFO("Runtime Alive received.\n"); - memcpy(&priv->card_alive, &pkt->u.alive_frame, - sizeof(struct iwl_alive_resp)); - pwork = &priv->alive_start; - } - - /* We delay the ALIVE response by 5ms to - * give the HW RF Kill time to activate... */ - if (palive->is_valid == UCODE_VALID_OK) - queue_delayed_work(priv->workqueue, pwork, - msecs_to_jiffies(5)); - else - IWL_WARNING("uCode did not respond OK.\n"); -} - -static void iwl4965_rx_reply_error(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) -{ - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; - - IWL_ERROR("Error Reply type 0x%08X cmd %s (0x%02X) " - "seq 0x%04X ser 0x%08X\n", - le32_to_cpu(pkt->u.err_resp.error_type), - get_cmd_string(pkt->u.err_resp.cmd_id), - pkt->u.err_resp.cmd_id, - le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num), - le32_to_cpu(pkt->u.err_resp.error_info)); -} - -#define TX_STATUS_ENTRY(x) case TX_STATUS_FAIL_ ## x: return #x - -static void iwl4965_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) -{ - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; - struct iwl_rxon_cmd *rxon = (void *)&priv->active_rxon; - struct iwl4965_csa_notification *csa = &(pkt->u.csa_notif); - IWL_DEBUG_11H("CSA notif: channel %d, status %d\n", - le16_to_cpu(csa->channel), le32_to_cpu(csa->status)); - rxon->channel = csa->channel; - priv->staging_rxon.channel = csa->channel; -} - -static void iwl4965_rx_spectrum_measure_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) -{ -#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; - struct iwl4965_spectrum_notification *report = &(pkt->u.spectrum_notif); - - if (!report->state) { - IWL_DEBUG(IWL_DL_11H, - "Spectrum Measure Notification: Start\n"); - return; - } - - memcpy(&priv->measure_report, report, sizeof(*report)); - priv->measurement_status |= MEASUREMENT_READY; -#endif -} - -static void iwl4965_rx_pm_sleep_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) -{ -#ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; - struct iwl4965_sleep_notification *sleep = &(pkt->u.sleep_notif); - IWL_DEBUG_RX("sleep mode: %d, src: %d\n", - sleep->pm_sleep_mode, sleep->pm_wakeup_src); -#endif -} - -static void iwl4965_rx_pm_debug_statistics_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) -{ - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; - IWL_DEBUG_RADIO("Dumping %d bytes of unhandled " - "notification for %s:\n", - le32_to_cpu(pkt->len), get_cmd_string(pkt->hdr.cmd)); - iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->u.raw, le32_to_cpu(pkt->len)); -} - -static void iwl4965_bg_beacon_update(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, beacon_update); - struct sk_buff *beacon; - - /* Pull updated AP beacon from mac80211. will fail if not in AP mode */ - beacon = ieee80211_beacon_get(priv->hw, priv->vif); - - if (!beacon) { - IWL_ERROR("update beacon failed\n"); - return; - } - - mutex_lock(&priv->mutex); - /* new beacon skb is allocated every time; dispose previous.*/ - if (priv->ibss_beacon) - dev_kfree_skb(priv->ibss_beacon); - - priv->ibss_beacon = beacon; - mutex_unlock(&priv->mutex); - - iwl4965_send_beacon_cmd(priv); -} - -/** - * iwl4965_bg_statistics_periodic - Timer callback to queue statistics - * - * This callback is provided in order to send a statistics request. - * - * This timer function is continually reset to execute within - * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION - * was received. We need to ensure we receive the statistics in order - * to update the temperature used for calibrating the TXPOWER. - */ -static void iwl4965_bg_statistics_periodic(unsigned long data) -{ - struct iwl_priv *priv = (struct iwl_priv *)data; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - iwl_send_statistics_request(priv, CMD_ASYNC); -} - -static void iwl4965_rx_beacon_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) -{ -#ifdef CONFIG_IWLWIFI_DEBUG - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; - struct iwl4965_beacon_notif *beacon = &(pkt->u.beacon_status); - u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); - - IWL_DEBUG_RX("beacon status %x retries %d iss %d " - "tsf %d %d rate %d\n", - le32_to_cpu(beacon->beacon_notify_hdr.u.status) & TX_STATUS_MSK, - beacon->beacon_notify_hdr.failure_frame, - le32_to_cpu(beacon->ibss_mgr_status), - le32_to_cpu(beacon->high_tsf), - le32_to_cpu(beacon->low_tsf), rate); -#endif - - if ((priv->iw_mode == IEEE80211_IF_TYPE_AP) && - (!test_bit(STATUS_EXIT_PENDING, &priv->status))) - queue_work(priv->workqueue, &priv->beacon_update); -} - -/* Handle notification from uCode that card's power state is changing - * due to software, hardware, or critical temperature RFKILL */ -static void iwl4965_rx_card_state_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) -{ - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; - u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); - unsigned long status = priv->status; - - IWL_DEBUG_RF_KILL("Card state received: HW:%s SW:%s\n", - (flags & HW_CARD_DISABLED) ? "Kill" : "On", - (flags & SW_CARD_DISABLED) ? "Kill" : "On"); - - if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | - RF_CARD_DISABLED)) { - - iwl_write32(priv, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - - if (!iwl_grab_nic_access(priv)) { - iwl_write_direct32( - priv, HBUS_TARG_MBX_C, - HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); - - iwl_release_nic_access(priv); - } - - if (!(flags & RXON_CARD_DISABLED)) { - iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - if (!iwl_grab_nic_access(priv)) { - iwl_write_direct32( - priv, HBUS_TARG_MBX_C, - HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); - - iwl_release_nic_access(priv); - } - } - - if (flags & RF_CARD_DISABLED) { - iwl_write32(priv, CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); - iwl_read32(priv, CSR_UCODE_DRV_GP1); - if (!iwl_grab_nic_access(priv)) - iwl_release_nic_access(priv); - } - } - - if (flags & HW_CARD_DISABLED) - set_bit(STATUS_RF_KILL_HW, &priv->status); - else - clear_bit(STATUS_RF_KILL_HW, &priv->status); - - - if (flags & SW_CARD_DISABLED) - set_bit(STATUS_RF_KILL_SW, &priv->status); - else - clear_bit(STATUS_RF_KILL_SW, &priv->status); - - if (!(flags & RXON_CARD_DISABLED)) - iwl_scan_cancel(priv); - - if ((test_bit(STATUS_RF_KILL_HW, &status) != - test_bit(STATUS_RF_KILL_HW, &priv->status)) || - (test_bit(STATUS_RF_KILL_SW, &status) != - test_bit(STATUS_RF_KILL_SW, &priv->status))) - queue_work(priv->workqueue, &priv->rf_kill); - else - wake_up_interruptible(&priv->wait_command_queue); -} - -int iwl4965_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src) -{ - int ret; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - ret = iwl_grab_nic_access(priv); - if (ret) - goto err; - - if (src == IWL_PWR_SRC_VAUX) { - u32 val; - ret = pci_read_config_dword(priv->pci_dev, PCI_POWER_SOURCE, - &val); - - if (val & PCI_CFG_PMC_PME_FROM_D3COLD_SUPPORT) - iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VAUX, - ~APMG_PS_CTRL_MSK_PWR_SRC); - } else { - iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, - ~APMG_PS_CTRL_MSK_PWR_SRC); - } - - iwl_release_nic_access(priv); -err: - spin_unlock_irqrestore(&priv->lock, flags); - return ret; -} - -/** - * iwl4965_setup_rx_handlers - Initialize Rx handler callbacks - * - * Setup the RX handlers for each of the reply types sent from the uCode - * to the host. - * - * This function chains into the hardware specific files for them to setup - * any hardware specific handlers as well. - */ -static void iwl_setup_rx_handlers(struct iwl_priv *priv) -{ - priv->rx_handlers[REPLY_ALIVE] = iwl_rx_reply_alive; - priv->rx_handlers[REPLY_ERROR] = iwl4965_rx_reply_error; - priv->rx_handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl4965_rx_csa; - priv->rx_handlers[SPECTRUM_MEASURE_NOTIFICATION] = - iwl4965_rx_spectrum_measure_notif; - priv->rx_handlers[PM_SLEEP_NOTIFICATION] = iwl4965_rx_pm_sleep_notif; - priv->rx_handlers[PM_DEBUG_STATISTIC_NOTIFIC] = - iwl4965_rx_pm_debug_statistics_notif; - priv->rx_handlers[BEACON_NOTIFICATION] = iwl4965_rx_beacon_notif; - - /* - * The same handler is used for both the REPLY to a discrete - * statistics request from the host as well as for the periodic - * statistics notifications (after received beacons) from the uCode. - */ - priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl_rx_statistics; - priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl_rx_statistics; - - iwl_setup_rx_scan_handlers(priv); - - /* status change handler */ - priv->rx_handlers[CARD_STATE_NOTIFICATION] = iwl4965_rx_card_state_notif; - - priv->rx_handlers[MISSED_BEACONS_NOTIFICATION] = - iwl_rx_missed_beacon_notif; - /* Rx handlers */ - priv->rx_handlers[REPLY_RX_PHY_CMD] = iwl_rx_reply_rx_phy; - priv->rx_handlers[REPLY_RX_MPDU_CMD] = iwl_rx_reply_rx; - /* block ack */ - priv->rx_handlers[REPLY_COMPRESSED_BA] = iwl_rx_reply_compressed_ba; - /* Set up hardware specific Rx handlers */ - priv->cfg->ops->lib->rx_handler_setup(priv); -} - -/* - * this should be called while priv->lock is locked -*/ -static void __iwl_rx_replenish(struct iwl_priv *priv) -{ - iwl_rx_allocate(priv); - iwl_rx_queue_restock(priv); -} - - -/** - * iwl_rx_handle - Main entry function for receiving responses from uCode - * - * Uses the priv->rx_handlers callback function array to invoke - * the appropriate handlers, including command responses, - * frame-received notifications, and other notifications. - */ -void iwl_rx_handle(struct iwl_priv *priv) -{ - struct iwl_rx_mem_buffer *rxb; - struct iwl_rx_packet *pkt; - struct iwl_rx_queue *rxq = &priv->rxq; - u32 r, i; - int reclaim; - unsigned long flags; - u8 fill_rx = 0; - u32 count = 8; - - /* uCode's read index (stored in shared DRAM) indicates the last Rx - * buffer that the driver may process (last buffer filled by ucode). */ - r = priv->cfg->ops->lib->shared_mem_rx_idx(priv); - i = rxq->read; - - /* Rx interrupt, but nothing sent from uCode */ - if (i == r) - IWL_DEBUG(IWL_DL_RX, "r = %d, i = %d\n", r, i); - - if (iwl_rx_queue_space(rxq) > (RX_QUEUE_SIZE / 2)) - fill_rx = 1; - - while (i != r) { - rxb = rxq->queue[i]; - - /* If an RXB doesn't have a Rx queue slot associated with it, - * then a bug has been introduced in the queue refilling - * routines -- catch it here */ - BUG_ON(rxb == NULL); - - rxq->queue[i] = NULL; - - pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr, - priv->hw_params.rx_buf_size, - PCI_DMA_FROMDEVICE); - pkt = (struct iwl_rx_packet *)rxb->skb->data; - - /* Reclaim a command buffer only if this packet is a response - * to a (driver-originated) command. - * If the packet (e.g. Rx frame) originated from uCode, - * there is no command buffer to reclaim. - * Ucode should set SEQ_RX_FRAME bit if ucode-originated, - * but apparently a few don't get set; catch them here. */ - reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) && - (pkt->hdr.cmd != REPLY_RX_PHY_CMD) && - (pkt->hdr.cmd != REPLY_RX) && - (pkt->hdr.cmd != REPLY_COMPRESSED_BA) && - (pkt->hdr.cmd != STATISTICS_NOTIFICATION) && - (pkt->hdr.cmd != REPLY_TX); - - /* Based on type of command response or notification, - * handle those that need handling via function in - * rx_handlers table. See iwl4965_setup_rx_handlers() */ - if (priv->rx_handlers[pkt->hdr.cmd]) { - IWL_DEBUG(IWL_DL_RX, "r = %d, i = %d, %s, 0x%02x\n", r, - i, get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); - priv->rx_handlers[pkt->hdr.cmd] (priv, rxb); - } else { - /* No handling needed */ - IWL_DEBUG(IWL_DL_RX, - "r %d i %d No handler needed for %s, 0x%02x\n", - r, i, get_cmd_string(pkt->hdr.cmd), - pkt->hdr.cmd); - } - - if (reclaim) { - /* Invoke any callbacks, transfer the skb to caller, and - * fire off the (possibly) blocking iwl_send_cmd() - * as we reclaim the driver command queue */ - if (rxb && rxb->skb) - iwl_tx_cmd_complete(priv, rxb); - else - IWL_WARNING("Claim null rxb?\n"); - } - - /* For now we just don't re-use anything. We can tweak this - * later to try and re-use notification packets and SKBs that - * fail to Rx correctly */ - if (rxb->skb != NULL) { - priv->alloc_rxb_skb--; - dev_kfree_skb_any(rxb->skb); - rxb->skb = NULL; - } - - pci_unmap_single(priv->pci_dev, rxb->dma_addr, - priv->hw_params.rx_buf_size, - PCI_DMA_FROMDEVICE); - spin_lock_irqsave(&rxq->lock, flags); - list_add_tail(&rxb->list, &priv->rxq.rx_used); - spin_unlock_irqrestore(&rxq->lock, flags); - i = (i + 1) & RX_QUEUE_MASK; - /* If there are a lot of unused frames, - * restock the Rx queue so ucode wont assert. */ - if (fill_rx) { - count++; - if (count >= 8) { - priv->rxq.read = i; - __iwl_rx_replenish(priv); - count = 0; - } - } - } - - /* Backtrack one entry */ - priv->rxq.read = i; - iwl_rx_queue_restock(priv); -} - -#ifdef CONFIG_IWLWIFI_DEBUG -static void iwl4965_print_rx_config_cmd(struct iwl_priv *priv) -{ - struct iwl_rxon_cmd *rxon = &priv->staging_rxon; - DECLARE_MAC_BUF(mac); - - IWL_DEBUG_RADIO("RX CONFIG:\n"); - iwl_print_hex_dump(priv, IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); - IWL_DEBUG_RADIO("u16 channel: 0x%x\n", le16_to_cpu(rxon->channel)); - IWL_DEBUG_RADIO("u32 flags: 0x%08X\n", le32_to_cpu(rxon->flags)); - IWL_DEBUG_RADIO("u32 filter_flags: 0x%08x\n", - le32_to_cpu(rxon->filter_flags)); - IWL_DEBUG_RADIO("u8 dev_type: 0x%x\n", rxon->dev_type); - IWL_DEBUG_RADIO("u8 ofdm_basic_rates: 0x%02x\n", - rxon->ofdm_basic_rates); - IWL_DEBUG_RADIO("u8 cck_basic_rates: 0x%02x\n", rxon->cck_basic_rates); - IWL_DEBUG_RADIO("u8[6] node_addr: %s\n", - print_mac(mac, rxon->node_addr)); - IWL_DEBUG_RADIO("u8[6] bssid_addr: %s\n", - print_mac(mac, rxon->bssid_addr)); - IWL_DEBUG_RADIO("u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); -} -#endif - -static void iwl4965_enable_interrupts(struct iwl_priv *priv) -{ - IWL_DEBUG_ISR("Enabling interrupts\n"); - set_bit(STATUS_INT_ENABLED, &priv->status); - iwl_write32(priv, CSR_INT_MASK, CSR_INI_SET_MASK); -} - -/* call this function to flush any scheduled tasklet */ -static inline void iwl_synchronize_irq(struct iwl_priv *priv) -{ - /* wait to make sure we flush pedding tasklet*/ - synchronize_irq(priv->pci_dev->irq); - tasklet_kill(&priv->irq_tasklet); -} - -static inline void iwl4965_disable_interrupts(struct iwl_priv *priv) -{ - clear_bit(STATUS_INT_ENABLED, &priv->status); - - /* disable interrupts from uCode/NIC to host */ - iwl_write32(priv, CSR_INT_MASK, 0x00000000); - - /* acknowledge/clear/reset any interrupts still pending - * from uCode or flow handler (Rx/Tx DMA) */ - iwl_write32(priv, CSR_INT, 0xffffffff); - iwl_write32(priv, CSR_FH_INT_STATUS, 0xffffffff); - IWL_DEBUG_ISR("Disabled interrupts\n"); -} - - -/** - * iwl4965_irq_handle_error - called for HW or SW error interrupt from card - */ -static void iwl4965_irq_handle_error(struct iwl_priv *priv) -{ - /* Set the FW error flag -- cleared on iwl4965_down */ - set_bit(STATUS_FW_ERROR, &priv->status); - - /* Cancel currently queued command. */ - clear_bit(STATUS_HCMD_ACTIVE, &priv->status); - -#ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & IWL_DL_FW_ERRORS) { - iwl_dump_nic_error_log(priv); - iwl_dump_nic_event_log(priv); - iwl4965_print_rx_config_cmd(priv); - } -#endif - - wake_up_interruptible(&priv->wait_command_queue); - - /* Keep the restart process from trying to send host - * commands by clearing the INIT status bit */ - clear_bit(STATUS_READY, &priv->status); - - if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) { - IWL_DEBUG(IWL_DL_FW_ERRORS, - "Restarting adapter due to uCode error.\n"); - - if (iwl_is_associated(priv)) { - memcpy(&priv->recovery_rxon, &priv->active_rxon, - sizeof(priv->recovery_rxon)); - priv->error_recovering = 1; - } - if (priv->cfg->mod_params->restart_fw) - queue_work(priv->workqueue, &priv->restart); - } -} - -static void iwl4965_error_recovery(struct iwl_priv *priv) -{ - unsigned long flags; - - memcpy(&priv->staging_rxon, &priv->recovery_rxon, - sizeof(priv->staging_rxon)); - priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - iwl4965_commit_rxon(priv); - - iwl_rxon_add_station(priv, priv->bssid, 1); - - spin_lock_irqsave(&priv->lock, flags); - priv->assoc_id = le16_to_cpu(priv->staging_rxon.assoc_id); - priv->error_recovering = 0; - spin_unlock_irqrestore(&priv->lock, flags); -} - -static void iwl4965_irq_tasklet(struct iwl_priv *priv) -{ - u32 inta, handled = 0; - u32 inta_fh; - unsigned long flags; -#ifdef CONFIG_IWLWIFI_DEBUG - u32 inta_mask; -#endif - - spin_lock_irqsave(&priv->lock, flags); - - /* Ack/clear/reset pending uCode interrupts. - * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, - * and will clear only when CSR_FH_INT_STATUS gets cleared. */ - inta = iwl_read32(priv, CSR_INT); - iwl_write32(priv, CSR_INT, inta); - - /* Ack/clear/reset pending flow-handler (DMA) interrupts. - * Any new interrupts that happen after this, either while we're - * in this tasklet, or later, will show up in next ISR/tasklet. */ - inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); - iwl_write32(priv, CSR_FH_INT_STATUS, inta_fh); - -#ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & IWL_DL_ISR) { - /* just for debug */ - inta_mask = iwl_read32(priv, CSR_INT_MASK); - IWL_DEBUG_ISR("inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", - inta, inta_mask, inta_fh); - } -#endif - - /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not - * atomic, make sure that inta covers all the interrupts that - * we've discovered, even if FH interrupt came in just after - * reading CSR_INT. */ - if (inta_fh & CSR49_FH_INT_RX_MASK) - inta |= CSR_INT_BIT_FH_RX; - if (inta_fh & CSR49_FH_INT_TX_MASK) - inta |= CSR_INT_BIT_FH_TX; - - /* Now service all interrupt bits discovered above. */ - if (inta & CSR_INT_BIT_HW_ERR) { - IWL_ERROR("Microcode HW error detected. Restarting.\n"); - - /* Tell the device to stop sending interrupts */ - iwl4965_disable_interrupts(priv); - - iwl4965_irq_handle_error(priv); - - handled |= CSR_INT_BIT_HW_ERR; - - spin_unlock_irqrestore(&priv->lock, flags); - - return; - } - -#ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { - /* NIC fires this, but we don't use it, redundant with WAKEUP */ - if (inta & CSR_INT_BIT_SCD) - IWL_DEBUG_ISR("Scheduler finished to transmit " - "the frame/frames.\n"); - - /* Alive notification via Rx interrupt will do the real work */ - if (inta & CSR_INT_BIT_ALIVE) - IWL_DEBUG_ISR("Alive interrupt\n"); - } -#endif - /* Safely ignore these bits for debug checks below */ - inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); - - /* HW RF KILL switch toggled */ - if (inta & CSR_INT_BIT_RF_KILL) { - int hw_rf_kill = 0; - if (!(iwl_read32(priv, CSR_GP_CNTRL) & - CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) - hw_rf_kill = 1; - - IWL_DEBUG(IWL_DL_RF_KILL, "RF_KILL bit toggled to %s.\n", - hw_rf_kill ? "disable radio":"enable radio"); - - /* driver only loads ucode once setting the interface up. - * the driver as well won't allow loading if RFKILL is set - * therefore no need to restart the driver from this handler - */ - if (!hw_rf_kill && !test_bit(STATUS_ALIVE, &priv->status)) - clear_bit(STATUS_RF_KILL_HW, &priv->status); - - handled |= CSR_INT_BIT_RF_KILL; - } - - /* Chip got too hot and stopped itself */ - if (inta & CSR_INT_BIT_CT_KILL) { - IWL_ERROR("Microcode CT kill error detected.\n"); - handled |= CSR_INT_BIT_CT_KILL; - } - - /* Error detected by uCode */ - if (inta & CSR_INT_BIT_SW_ERR) { - IWL_ERROR("Microcode SW error detected. Restarting 0x%X.\n", - inta); - iwl4965_irq_handle_error(priv); - handled |= CSR_INT_BIT_SW_ERR; - } - - /* uCode wakes up after power-down sleep */ - if (inta & CSR_INT_BIT_WAKEUP) { - IWL_DEBUG_ISR("Wakeup interrupt\n"); - iwl_rx_queue_update_write_ptr(priv, &priv->rxq); - iwl_txq_update_write_ptr(priv, &priv->txq[0]); - iwl_txq_update_write_ptr(priv, &priv->txq[1]); - iwl_txq_update_write_ptr(priv, &priv->txq[2]); - iwl_txq_update_write_ptr(priv, &priv->txq[3]); - iwl_txq_update_write_ptr(priv, &priv->txq[4]); - iwl_txq_update_write_ptr(priv, &priv->txq[5]); - - handled |= CSR_INT_BIT_WAKEUP; - } - - /* All uCode command responses, including Tx command responses, - * Rx "responses" (frame-received notification), and other - * notifications from uCode come through here*/ - if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { - iwl_rx_handle(priv); - handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); - } - - if (inta & CSR_INT_BIT_FH_TX) { - IWL_DEBUG_ISR("Tx interrupt\n"); - handled |= CSR_INT_BIT_FH_TX; - /* FH finished to write, send event */ - priv->ucode_write_complete = 1; - wake_up_interruptible(&priv->wait_command_queue); - } - - if (inta & ~handled) - IWL_ERROR("Unhandled INTA bits 0x%08x\n", inta & ~handled); - - if (inta & ~CSR_INI_SET_MASK) { - IWL_WARNING("Disabled INTA bits 0x%08x were pending\n", - inta & ~CSR_INI_SET_MASK); - IWL_WARNING(" with FH_INT = 0x%08x\n", inta_fh); - } - - /* Re-enable all interrupts */ - /* only Re-enable if diabled by irq */ - if (test_bit(STATUS_INT_ENABLED, &priv->status)) - iwl4965_enable_interrupts(priv); - -#ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { - inta = iwl_read32(priv, CSR_INT); - inta_mask = iwl_read32(priv, CSR_INT_MASK); - inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); - IWL_DEBUG_ISR("End inta 0x%08x, enabled 0x%08x, fh 0x%08x, " - "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags); - } -#endif - spin_unlock_irqrestore(&priv->lock, flags); -} - -static irqreturn_t iwl4965_isr(int irq, void *data) -{ - struct iwl_priv *priv = data; - u32 inta, inta_mask; - u32 inta_fh; - if (!priv) - return IRQ_NONE; - - spin_lock(&priv->lock); - - /* Disable (but don't clear!) interrupts here to avoid - * back-to-back ISRs and sporadic interrupts from our NIC. - * If we have something to service, the tasklet will re-enable ints. - * If we *don't* have something, we'll re-enable before leaving here. */ - inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */ - iwl_write32(priv, CSR_INT_MASK, 0x00000000); - - /* Discover which interrupts are active/pending */ - inta = iwl_read32(priv, CSR_INT); - inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); - - /* Ignore interrupt if there's nothing in NIC to service. - * This may be due to IRQ shared with another device, - * or due to sporadic interrupts thrown from our NIC. */ - if (!inta && !inta_fh) { - IWL_DEBUG_ISR("Ignore interrupt, inta == 0, inta_fh == 0\n"); - goto none; - } - - if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { - /* Hardware disappeared. It might have already raised - * an interrupt */ - IWL_WARNING("HARDWARE GONE?? INTA == 0x%080x\n", inta); - goto unplugged; - } - - IWL_DEBUG_ISR("ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", - inta, inta_mask, inta_fh); - - inta &= ~CSR_INT_BIT_SCD; - - /* iwl4965_irq_tasklet() will service interrupts and re-enable them */ - if (likely(inta || inta_fh)) - tasklet_schedule(&priv->irq_tasklet); - - unplugged: - spin_unlock(&priv->lock); - return IRQ_HANDLED; - - none: - /* re-enable interrupts here since we don't have anything to service. */ - /* only Re-enable if diabled by irq */ - if (test_bit(STATUS_INT_ENABLED, &priv->status)) - iwl4965_enable_interrupts(priv); - spin_unlock(&priv->lock); - return IRQ_NONE; -} - -/****************************************************************************** - * - * uCode download functions - * - ******************************************************************************/ - -static void iwl4965_dealloc_ucode_pci(struct iwl_priv *priv) -{ - iwl_free_fw_desc(priv->pci_dev, &priv->ucode_code); - iwl_free_fw_desc(priv->pci_dev, &priv->ucode_data); - iwl_free_fw_desc(priv->pci_dev, &priv->ucode_data_backup); - iwl_free_fw_desc(priv->pci_dev, &priv->ucode_init); - iwl_free_fw_desc(priv->pci_dev, &priv->ucode_init_data); - iwl_free_fw_desc(priv->pci_dev, &priv->ucode_boot); -} - -static void iwl4965_nic_start(struct iwl_priv *priv) -{ - /* Remove all resets to allow NIC to operate */ - iwl_write32(priv, CSR_RESET, 0); -} - - -/** - * iwl4965_read_ucode - Read uCode images from disk file. - * - * Copy into buffers for card to fetch via bus-mastering - */ -static int iwl4965_read_ucode(struct iwl_priv *priv) -{ - struct iwl_ucode *ucode; - int ret; - const struct firmware *ucode_raw; - const char *name = priv->cfg->fw_name; - u8 *src; - size_t len; - u32 ver, inst_size, data_size, init_size, init_data_size, boot_size; - - /* Ask kernel firmware_class module to get the boot firmware off disk. - * request_firmware() is synchronous, file is in memory on return. */ - ret = request_firmware(&ucode_raw, name, &priv->pci_dev->dev); - if (ret < 0) { - IWL_ERROR("%s firmware file req failed: Reason %d\n", - name, ret); - goto error; - } - - IWL_DEBUG_INFO("Got firmware '%s' file (%zd bytes) from disk\n", - name, ucode_raw->size); - - /* Make sure that we got at least our header! */ - if (ucode_raw->size < sizeof(*ucode)) { - IWL_ERROR("File size way too small!\n"); - ret = -EINVAL; - goto err_release; - } - - /* Data from ucode file: header followed by uCode images */ - ucode = (void *)ucode_raw->data; - - ver = le32_to_cpu(ucode->ver); - inst_size = le32_to_cpu(ucode->inst_size); - data_size = le32_to_cpu(ucode->data_size); - init_size = le32_to_cpu(ucode->init_size); - init_data_size = le32_to_cpu(ucode->init_data_size); - boot_size = le32_to_cpu(ucode->boot_size); - - IWL_DEBUG_INFO("f/w package hdr ucode version = 0x%x\n", ver); - IWL_DEBUG_INFO("f/w package hdr runtime inst size = %u\n", - inst_size); - IWL_DEBUG_INFO("f/w package hdr runtime data size = %u\n", - data_size); - IWL_DEBUG_INFO("f/w package hdr init inst size = %u\n", - init_size); - IWL_DEBUG_INFO("f/w package hdr init data size = %u\n", - init_data_size); - IWL_DEBUG_INFO("f/w package hdr boot inst size = %u\n", - boot_size); - - /* Verify size of file vs. image size info in file's header */ - if (ucode_raw->size < sizeof(*ucode) + - inst_size + data_size + init_size + - init_data_size + boot_size) { - - IWL_DEBUG_INFO("uCode file size %d too small\n", - (int)ucode_raw->size); - ret = -EINVAL; - goto err_release; - } - - /* Verify that uCode images will fit in card's SRAM */ - if (inst_size > priv->hw_params.max_inst_size) { - IWL_DEBUG_INFO("uCode instr len %d too large to fit in\n", - inst_size); - ret = -EINVAL; - goto err_release; - } - - if (data_size > priv->hw_params.max_data_size) { - IWL_DEBUG_INFO("uCode data len %d too large to fit in\n", - data_size); - ret = -EINVAL; - goto err_release; - } - if (init_size > priv->hw_params.max_inst_size) { - IWL_DEBUG_INFO - ("uCode init instr len %d too large to fit in\n", - init_size); - ret = -EINVAL; - goto err_release; - } - if (init_data_size > priv->hw_params.max_data_size) { - IWL_DEBUG_INFO - ("uCode init data len %d too large to fit in\n", - init_data_size); - ret = -EINVAL; - goto err_release; - } - if (boot_size > priv->hw_params.max_bsm_size) { - IWL_DEBUG_INFO - ("uCode boot instr len %d too large to fit in\n", - boot_size); - ret = -EINVAL; - goto err_release; - } - - /* Allocate ucode buffers for card's bus-master loading ... */ - - /* Runtime instructions and 2 copies of data: - * 1) unmodified from disk - * 2) backup cache for save/restore during power-downs */ - priv->ucode_code.len = inst_size; - iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_code); - - priv->ucode_data.len = data_size; - iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_data); - - priv->ucode_data_backup.len = data_size; - iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_data_backup); - - /* Initialization instructions and data */ - if (init_size && init_data_size) { - priv->ucode_init.len = init_size; - iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_init); - - priv->ucode_init_data.len = init_data_size; - iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_init_data); - - if (!priv->ucode_init.v_addr || !priv->ucode_init_data.v_addr) - goto err_pci_alloc; - } - - /* Bootstrap (instructions only, no data) */ - if (boot_size) { - priv->ucode_boot.len = boot_size; - iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_boot); - - if (!priv->ucode_boot.v_addr) - goto err_pci_alloc; - } - - /* Copy images into buffers for card's bus-master reads ... */ - - /* Runtime instructions (first block of data in file) */ - src = &ucode->data[0]; - len = priv->ucode_code.len; - IWL_DEBUG_INFO("Copying (but not loading) uCode instr len %Zd\n", len); - memcpy(priv->ucode_code.v_addr, src, len); - IWL_DEBUG_INFO("uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n", - priv->ucode_code.v_addr, (u32)priv->ucode_code.p_addr); - - /* Runtime data (2nd block) - * NOTE: Copy into backup buffer will be done in iwl4965_up() */ - src = &ucode->data[inst_size]; - len = priv->ucode_data.len; - IWL_DEBUG_INFO("Copying (but not loading) uCode data len %Zd\n", len); - memcpy(priv->ucode_data.v_addr, src, len); - memcpy(priv->ucode_data_backup.v_addr, src, len); - - /* Initialization instructions (3rd block) */ - if (init_size) { - src = &ucode->data[inst_size + data_size]; - len = priv->ucode_init.len; - IWL_DEBUG_INFO("Copying (but not loading) init instr len %Zd\n", - len); - memcpy(priv->ucode_init.v_addr, src, len); - } - - /* Initialization data (4th block) */ - if (init_data_size) { - src = &ucode->data[inst_size + data_size + init_size]; - len = priv->ucode_init_data.len; - IWL_DEBUG_INFO("Copying (but not loading) init data len %Zd\n", - len); - memcpy(priv->ucode_init_data.v_addr, src, len); - } - - /* Bootstrap instructions (5th block) */ - src = &ucode->data[inst_size + data_size + init_size + init_data_size]; - len = priv->ucode_boot.len; - IWL_DEBUG_INFO("Copying (but not loading) boot instr len %Zd\n", len); - memcpy(priv->ucode_boot.v_addr, src, len); - - /* We have our copies now, allow OS release its copies */ - release_firmware(ucode_raw); - return 0; - - err_pci_alloc: - IWL_ERROR("failed to allocate pci memory\n"); - ret = -ENOMEM; - iwl4965_dealloc_ucode_pci(priv); - - err_release: - release_firmware(ucode_raw); - - error: - return ret; -} - -/** - * iwl_alive_start - called after REPLY_ALIVE notification received - * from protocol/runtime uCode (initialization uCode's - * Alive gets handled by iwl_init_alive_start()). - */ -static void iwl_alive_start(struct iwl_priv *priv) -{ - int ret = 0; - - IWL_DEBUG_INFO("Runtime Alive received.\n"); - - if (priv->card_alive.is_valid != UCODE_VALID_OK) { - /* We had an error bringing up the hardware, so take it - * all the way back down so we can try again */ - IWL_DEBUG_INFO("Alive failed.\n"); - goto restart; - } - - /* Initialize uCode has loaded Runtime uCode ... verify inst image. - * This is a paranoid check, because we would not have gotten the - * "runtime" alive if code weren't properly loaded. */ - if (iwl_verify_ucode(priv)) { - /* Runtime instruction load was bad; - * take it all the way back down so we can try again */ - IWL_DEBUG_INFO("Bad runtime uCode load.\n"); - goto restart; - } - - iwl_clear_stations_table(priv); - ret = priv->cfg->ops->lib->alive_notify(priv); - if (ret) { - IWL_WARNING("Could not complete ALIVE transition [ntf]: %d\n", - ret); - goto restart; - } - - /* After the ALIVE response, we can send host commands to 4965 uCode */ - set_bit(STATUS_ALIVE, &priv->status); - - if (iwl_is_rfkill(priv)) - return; - - ieee80211_wake_queues(priv->hw); - - priv->active_rate = priv->rates_mask; - priv->active_rate_basic = priv->rates_mask & IWL_BASIC_RATES_MASK; - - if (iwl_is_associated(priv)) { - struct iwl_rxon_cmd *active_rxon = - (struct iwl_rxon_cmd *)&priv->active_rxon; - - memcpy(&priv->staging_rxon, &priv->active_rxon, - sizeof(priv->staging_rxon)); - active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - } else { - /* Initialize our rx_config data */ - iwl4965_connection_init_rx_config(priv); - memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); - } - - /* Configure Bluetooth device coexistence support */ - iwl4965_send_bt_config(priv); - - iwl_reset_run_time_calib(priv); - - /* Configure the adapter for unassociated operation */ - iwl4965_commit_rxon(priv); - - /* At this point, the NIC is initialized and operational */ - iwl_rf_kill_ct_config(priv); - - iwl_leds_register(priv); - - IWL_DEBUG_INFO("ALIVE processing complete.\n"); - set_bit(STATUS_READY, &priv->status); - wake_up_interruptible(&priv->wait_command_queue); - - if (priv->error_recovering) - iwl4965_error_recovery(priv); - - iwl_power_update_mode(priv, 1); - ieee80211_notify_mac(priv->hw, IEEE80211_NOTIFY_RE_ASSOC); - - if (test_and_clear_bit(STATUS_MODE_PENDING, &priv->status)) - iwl4965_set_mode(priv, priv->iw_mode); - - return; - - restart: - queue_work(priv->workqueue, &priv->restart); -} - -static void iwl_cancel_deferred_work(struct iwl_priv *priv); - -static void __iwl4965_down(struct iwl_priv *priv) -{ - unsigned long flags; - int exit_pending = test_bit(STATUS_EXIT_PENDING, &priv->status); - - IWL_DEBUG_INFO(DRV_NAME " is going down\n"); - - if (!exit_pending) - set_bit(STATUS_EXIT_PENDING, &priv->status); - - iwl_leds_unregister(priv); - - iwl_clear_stations_table(priv); - - /* Unblock any waiting calls */ - wake_up_interruptible_all(&priv->wait_command_queue); - - /* Wipe out the EXIT_PENDING status bit if we are not actually - * exiting the module */ - if (!exit_pending) - clear_bit(STATUS_EXIT_PENDING, &priv->status); - - /* stop and reset the on-board processor */ - iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); - - /* tell the device to stop sending interrupts */ - spin_lock_irqsave(&priv->lock, flags); - iwl4965_disable_interrupts(priv); - spin_unlock_irqrestore(&priv->lock, flags); - iwl_synchronize_irq(priv); - - if (priv->mac80211_registered) - ieee80211_stop_queues(priv->hw); - - /* If we have not previously called iwl4965_init() then - * clear all bits but the RF Kill and SUSPEND bits and return */ - if (!iwl_is_init(priv)) { - priv->status = test_bit(STATUS_RF_KILL_HW, &priv->status) << - STATUS_RF_KILL_HW | - test_bit(STATUS_RF_KILL_SW, &priv->status) << - STATUS_RF_KILL_SW | - test_bit(STATUS_GEO_CONFIGURED, &priv->status) << - STATUS_GEO_CONFIGURED | - test_bit(STATUS_IN_SUSPEND, &priv->status) << - STATUS_IN_SUSPEND | - test_bit(STATUS_EXIT_PENDING, &priv->status) << - STATUS_EXIT_PENDING; - goto exit; - } - - /* ...otherwise clear out all the status bits but the RF Kill and - * SUSPEND bits and continue taking the NIC down. */ - priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) << - STATUS_RF_KILL_HW | - test_bit(STATUS_RF_KILL_SW, &priv->status) << - STATUS_RF_KILL_SW | - test_bit(STATUS_GEO_CONFIGURED, &priv->status) << - STATUS_GEO_CONFIGURED | - test_bit(STATUS_IN_SUSPEND, &priv->status) << - STATUS_IN_SUSPEND | - test_bit(STATUS_FW_ERROR, &priv->status) << - STATUS_FW_ERROR | - test_bit(STATUS_EXIT_PENDING, &priv->status) << - STATUS_EXIT_PENDING; - - spin_lock_irqsave(&priv->lock, flags); - iwl_clear_bit(priv, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - spin_unlock_irqrestore(&priv->lock, flags); - - iwl_txq_ctx_stop(priv); - iwl_rxq_stop(priv); - - spin_lock_irqsave(&priv->lock, flags); - if (!iwl_grab_nic_access(priv)) { - iwl_write_prph(priv, APMG_CLK_DIS_REG, - APMG_CLK_VAL_DMA_CLK_RQT); - iwl_release_nic_access(priv); - } - spin_unlock_irqrestore(&priv->lock, flags); - - udelay(5); - - /* FIXME: apm_ops.suspend(priv) */ - priv->cfg->ops->lib->apm_ops.reset(priv); - priv->cfg->ops->lib->free_shared_mem(priv); - - exit: - memset(&priv->card_alive, 0, sizeof(struct iwl_alive_resp)); - - if (priv->ibss_beacon) - dev_kfree_skb(priv->ibss_beacon); - priv->ibss_beacon = NULL; - - /* clear out any free frames */ - iwl_clear_free_frames(priv); -} - -static void iwl4965_down(struct iwl_priv *priv) -{ - mutex_lock(&priv->mutex); - __iwl4965_down(priv); - mutex_unlock(&priv->mutex); - - iwl_cancel_deferred_work(priv); -} - -#define MAX_HW_RESTARTS 5 - -static int __iwl4965_up(struct iwl_priv *priv) -{ - int i; - int ret; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { - IWL_WARNING("Exit pending; will not bring the NIC up\n"); - return -EIO; - } - - if (!priv->ucode_data_backup.v_addr || !priv->ucode_data.v_addr) { - IWL_ERROR("ucode not available for device bringup\n"); - return -EIO; - } - - /* If platform's RF_KILL switch is NOT set to KILL */ - if (iwl_read32(priv, CSR_GP_CNTRL) & - CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) - clear_bit(STATUS_RF_KILL_HW, &priv->status); - else - set_bit(STATUS_RF_KILL_HW, &priv->status); - - if (!test_bit(STATUS_IN_SUSPEND, &priv->status) && - iwl_is_rfkill(priv)) { - IWL_WARNING("Radio disabled by %s RF Kill switch\n", - test_bit(STATUS_RF_KILL_HW, &priv->status) ? "HW" : "SW"); - return -ENODEV; - } - - iwl_write32(priv, CSR_INT, 0xFFFFFFFF); - - ret = priv->cfg->ops->lib->alloc_shared_mem(priv); - if (ret) { - IWL_ERROR("Unable to allocate shared memory\n"); - return ret; - } - - ret = iwl_hw_nic_init(priv); - if (ret) { - IWL_ERROR("Unable to init nic\n"); - return ret; - } - - /* make sure rfkill handshake bits are cleared */ - iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - - /* clear (again), then enable host interrupts */ - iwl_write32(priv, CSR_INT, 0xFFFFFFFF); - iwl4965_enable_interrupts(priv); - - /* really make sure rfkill handshake bits are cleared */ - iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - - /* Copy original ucode data image from disk into backup cache. - * This will be used to initialize the on-board processor's - * data SRAM for a clean start when the runtime program first loads. */ - memcpy(priv->ucode_data_backup.v_addr, priv->ucode_data.v_addr, - priv->ucode_data.len); - - /* We return success when we resume from suspend and rf_kill is on. */ - if (test_bit(STATUS_RF_KILL_HW, &priv->status) || - test_bit(STATUS_RF_KILL_SW, &priv->status)) - return 0; - - for (i = 0; i < MAX_HW_RESTARTS; i++) { - - iwl_clear_stations_table(priv); - - /* load bootstrap state machine, - * load bootstrap program into processor's memory, - * prepare to load the "initialize" uCode */ - ret = priv->cfg->ops->lib->load_ucode(priv); - - if (ret) { - IWL_ERROR("Unable to set up bootstrap uCode: %d\n", ret); - continue; - } - - /* Clear out the uCode error bit if it is set */ - clear_bit(STATUS_FW_ERROR, &priv->status); - - /* start card; "initialize" will load runtime ucode */ - iwl4965_nic_start(priv); - - IWL_DEBUG_INFO(DRV_NAME " is coming up\n"); - - return 0; - } - - set_bit(STATUS_EXIT_PENDING, &priv->status); - __iwl4965_down(priv); - clear_bit(STATUS_EXIT_PENDING, &priv->status); - - /* tried to restart and config the device for as long as our - * patience could withstand */ - IWL_ERROR("Unable to initialize device after %d attempts.\n", i); - return -EIO; -} - - -/***************************************************************************** - * - * Workqueue callbacks - * - *****************************************************************************/ - -static void iwl_bg_init_alive_start(struct work_struct *data) -{ - struct iwl_priv *priv = - container_of(data, struct iwl_priv, init_alive_start.work); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - mutex_lock(&priv->mutex); - priv->cfg->ops->lib->init_alive_start(priv); - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_alive_start(struct work_struct *data) -{ - struct iwl_priv *priv = - container_of(data, struct iwl_priv, alive_start.work); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - mutex_lock(&priv->mutex); - iwl_alive_start(priv); - mutex_unlock(&priv->mutex); -} - -static void iwl4965_bg_rf_kill(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, rf_kill); - - wake_up_interruptible(&priv->wait_command_queue); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - mutex_lock(&priv->mutex); - - if (!iwl_is_rfkill(priv)) { - IWL_DEBUG(IWL_DL_RF_KILL, - "HW and/or SW RF Kill no longer active, restarting " - "device\n"); - if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) - queue_work(priv->workqueue, &priv->restart); - } else { - /* make sure mac80211 stop sending Tx frame */ - if (priv->mac80211_registered) - ieee80211_stop_queues(priv->hw); - - if (!test_bit(STATUS_RF_KILL_HW, &priv->status)) - IWL_DEBUG_RF_KILL("Can not turn radio back on - " - "disabled by SW switch\n"); - else - IWL_WARNING("Radio Frequency Kill Switch is On:\n" - "Kill switch must be turned off for " - "wireless networking to work.\n"); - } - mutex_unlock(&priv->mutex); - iwl_rfkill_set_hw_state(priv); -} - -static void iwl4965_bg_set_monitor(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, - struct iwl_priv, set_monitor); - int ret; - - IWL_DEBUG(IWL_DL_STATE, "setting monitor mode\n"); - - mutex_lock(&priv->mutex); - - ret = iwl4965_set_mode(priv, IEEE80211_IF_TYPE_MNTR); - - if (ret) { - if (ret == -EAGAIN) - IWL_DEBUG(IWL_DL_STATE, "leave - not ready\n"); - else - IWL_ERROR("iwl4965_set_mode() failed ret = %d\n", ret); - } - - mutex_unlock(&priv->mutex); -} - -static void iwl_bg_run_time_calib_work(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, - run_time_calib_work); - - mutex_lock(&priv->mutex); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status) || - test_bit(STATUS_SCANNING, &priv->status)) { - mutex_unlock(&priv->mutex); - return; - } - - if (priv->start_calib) { - iwl_chain_noise_calibration(priv, &priv->statistics); - - iwl_sensitivity_calibration(priv, &priv->statistics); - } - - mutex_unlock(&priv->mutex); - return; -} - -static void iwl4965_bg_up(struct work_struct *data) -{ - struct iwl_priv *priv = container_of(data, struct iwl_priv, up); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - mutex_lock(&priv->mutex); - __iwl4965_up(priv); - mutex_unlock(&priv->mutex); - iwl_rfkill_set_hw_state(priv); -} - -static void iwl4965_bg_restart(struct work_struct *data) -{ - struct iwl_priv *priv = container_of(data, struct iwl_priv, restart); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - iwl4965_down(priv); - queue_work(priv->workqueue, &priv->up); -} - -static void iwl4965_bg_rx_replenish(struct work_struct *data) -{ - struct iwl_priv *priv = - container_of(data, struct iwl_priv, rx_replenish); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - mutex_lock(&priv->mutex); - iwl_rx_replenish(priv); - mutex_unlock(&priv->mutex); -} - -#define IWL_DELAY_NEXT_SCAN (HZ*2) - -static void iwl4965_post_associate(struct iwl_priv *priv) -{ - struct ieee80211_conf *conf = NULL; - int ret = 0; - DECLARE_MAC_BUF(mac); - unsigned long flags; - - if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { - IWL_ERROR("%s Should not be called in AP mode\n", __FUNCTION__); - return; - } - - IWL_DEBUG_ASSOC("Associated as %d to: %s\n", - priv->assoc_id, - print_mac(mac, priv->active_rxon.bssid_addr)); - - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - - if (!priv->vif || !priv->is_open) - return; - - iwl_scan_cancel_timeout(priv, 200); - - conf = ieee80211_get_hw_conf(priv->hw); - - priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - iwl4965_commit_rxon(priv); - - memset(&priv->rxon_timing, 0, sizeof(struct iwl4965_rxon_time_cmd)); - iwl4965_setup_rxon_timing(priv); - ret = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING, - sizeof(priv->rxon_timing), &priv->rxon_timing); - if (ret) - IWL_WARNING("REPLY_RXON_TIMING failed - " - "Attempting to continue.\n"); - - priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK; - - if (priv->current_ht_config.is_ht) - iwl_set_rxon_ht(priv, &priv->current_ht_config); - - iwl_set_rxon_chain(priv); - priv->staging_rxon.assoc_id = cpu_to_le16(priv->assoc_id); - - IWL_DEBUG_ASSOC("assoc id %d beacon interval %d\n", - priv->assoc_id, priv->beacon_int); - - if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_PREAMBLE) - priv->staging_rxon.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; - else - priv->staging_rxon.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - - if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) { - if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) - priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK; - else - priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - - if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) - priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - - } - - iwl4965_commit_rxon(priv); - - switch (priv->iw_mode) { - case IEEE80211_IF_TYPE_STA: - break; - - case IEEE80211_IF_TYPE_IBSS: - - /* assume default assoc id */ - priv->assoc_id = 1; - - iwl_rxon_add_station(priv, priv->bssid, 0); - iwl4965_send_beacon_cmd(priv); - - break; - - default: - IWL_ERROR("%s Should not be called in %d mode\n", - __FUNCTION__, priv->iw_mode); - break; - } - - /* Enable Rx differential gain and sensitivity calibrations */ - iwl_chain_noise_reset(priv); - priv->start_calib = 1; - - if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) - priv->assoc_station_added = 1; - - spin_lock_irqsave(&priv->lock, flags); - iwl_activate_qos(priv, 0); - spin_unlock_irqrestore(&priv->lock, flags); - - iwl_power_update_mode(priv, 0); - /* we have just associated, don't start scan too early */ - priv->next_scan_jiffies = jiffies + IWL_DELAY_NEXT_SCAN; -} - -static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf); - -static void iwl_bg_scan_completed(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, scan_completed); - - IWL_DEBUG_SCAN("SCAN complete scan\n"); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (test_bit(STATUS_CONF_PENDING, &priv->status)) - iwl4965_mac_config(priv->hw, ieee80211_get_hw_conf(priv->hw)); - - ieee80211_scan_completed(priv->hw); - - /* Since setting the TXPOWER may have been deferred while - * performing the scan, fire one off */ - mutex_lock(&priv->mutex); - iwl_set_tx_power(priv, priv->tx_power_user_lmt, true); - mutex_unlock(&priv->mutex); -} - -/***************************************************************************** - * - * mac80211 entry point functions - * - *****************************************************************************/ - -#define UCODE_READY_TIMEOUT (4 * HZ) - -static int iwl4965_mac_start(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = hw->priv; - int ret; - - IWL_DEBUG_MAC80211("enter\n"); - - if (pci_enable_device(priv->pci_dev)) { - IWL_ERROR("Fail to pci_enable_device\n"); - return -ENODEV; - } - pci_restore_state(priv->pci_dev); - pci_enable_msi(priv->pci_dev); - - ret = request_irq(priv->pci_dev->irq, iwl4965_isr, IRQF_SHARED, - DRV_NAME, priv); - if (ret) { - IWL_ERROR("Error allocating IRQ %d\n", priv->pci_dev->irq); - goto out_disable_msi; - } - - /* we should be verifying the device is ready to be opened */ - mutex_lock(&priv->mutex); - - memset(&priv->staging_rxon, 0, sizeof(struct iwl_rxon_cmd)); - /* fetch ucode file from disk, alloc and copy to bus-master buffers ... - * ucode filename and max sizes are card-specific. */ - - if (!priv->ucode_code.len) { - ret = iwl4965_read_ucode(priv); - if (ret) { - IWL_ERROR("Could not read microcode: %d\n", ret); - mutex_unlock(&priv->mutex); - goto out_release_irq; - } - } - - ret = __iwl4965_up(priv); - - mutex_unlock(&priv->mutex); - - iwl_rfkill_set_hw_state(priv); - - if (ret) - goto out_release_irq; - - IWL_DEBUG_INFO("Start UP work done.\n"); - - if (test_bit(STATUS_IN_SUSPEND, &priv->status)) - return 0; - - /* Wait for START_ALIVE from Run Time ucode. Otherwise callbacks from - * mac80211 will not be run successfully. */ - ret = wait_event_interruptible_timeout(priv->wait_command_queue, - test_bit(STATUS_READY, &priv->status), - UCODE_READY_TIMEOUT); - if (!ret) { - if (!test_bit(STATUS_READY, &priv->status)) { - IWL_ERROR("START_ALIVE timeout after %dms.\n", - jiffies_to_msecs(UCODE_READY_TIMEOUT)); - ret = -ETIMEDOUT; - goto out_release_irq; - } - } - - priv->is_open = 1; - IWL_DEBUG_MAC80211("leave\n"); - return 0; - -out_release_irq: - free_irq(priv->pci_dev->irq, priv); -out_disable_msi: - pci_disable_msi(priv->pci_dev); - pci_disable_device(priv->pci_dev); - priv->is_open = 0; - IWL_DEBUG_MAC80211("leave - failed\n"); - return ret; -} - -static void iwl4965_mac_stop(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = hw->priv; - - IWL_DEBUG_MAC80211("enter\n"); - - if (!priv->is_open) { - IWL_DEBUG_MAC80211("leave - skip\n"); - return; - } - - priv->is_open = 0; - - if (iwl_is_ready_rf(priv)) { - /* stop mac, cancel any scan request and clear - * RXON_FILTER_ASSOC_MSK BIT - */ - mutex_lock(&priv->mutex); - iwl_scan_cancel_timeout(priv, 100); - mutex_unlock(&priv->mutex); - } - - iwl4965_down(priv); - - flush_workqueue(priv->workqueue); - free_irq(priv->pci_dev->irq, priv); - pci_disable_msi(priv->pci_dev); - pci_save_state(priv->pci_dev); - pci_disable_device(priv->pci_dev); - - IWL_DEBUG_MAC80211("leave\n"); -} - -static int iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) -{ - struct iwl_priv *priv = hw->priv; - - IWL_DEBUG_MAC80211("enter\n"); - - if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) { - IWL_DEBUG_MAC80211("leave - monitor\n"); - dev_kfree_skb_any(skb); - return 0; - } - - IWL_DEBUG_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, - ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); - - if (iwl_tx_skb(priv, skb)) - dev_kfree_skb_any(skb); - - IWL_DEBUG_MAC80211("leave\n"); - return 0; -} - -static int iwl4965_mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) -{ - struct iwl_priv *priv = hw->priv; - unsigned long flags; - DECLARE_MAC_BUF(mac); - - IWL_DEBUG_MAC80211("enter: type %d\n", conf->type); - - if (priv->vif) { - IWL_DEBUG_MAC80211("leave - vif != NULL\n"); - return -EOPNOTSUPP; - } - - spin_lock_irqsave(&priv->lock, flags); - priv->vif = conf->vif; - - spin_unlock_irqrestore(&priv->lock, flags); - - mutex_lock(&priv->mutex); - - if (conf->mac_addr) { - IWL_DEBUG_MAC80211("Set %s\n", print_mac(mac, conf->mac_addr)); - memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN); - } - - if (iwl4965_set_mode(priv, conf->type) == -EAGAIN) - /* we are not ready, will run again when ready */ - set_bit(STATUS_MODE_PENDING, &priv->status); - - mutex_unlock(&priv->mutex); - - IWL_DEBUG_MAC80211("leave\n"); - return 0; -} - -/** - * iwl4965_mac_config - mac80211 config callback - * - * We ignore conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME since it seems to - * be set inappropriately and the driver currently sets the hardware up to - * use it whenever needed. - */ -static int iwl4965_mac_config(struct ieee80211_hw *hw, struct ieee80211_conf *conf) -{ - struct iwl_priv *priv = hw->priv; - const struct iwl_channel_info *ch_info; - unsigned long flags; - int ret = 0; - u16 channel; - - mutex_lock(&priv->mutex); - IWL_DEBUG_MAC80211("enter to channel %d\n", conf->channel->hw_value); - - priv->add_radiotap = !!(conf->flags & IEEE80211_CONF_RADIOTAP); - - if (conf->radio_enabled && iwl_radio_kill_sw_enable_radio(priv)) { - IWL_DEBUG_MAC80211("leave - RF-KILL - waiting for uCode\n"); - goto out; - } - - if (!conf->radio_enabled) - iwl_radio_kill_sw_disable_radio(priv); - - if (!iwl_is_ready(priv)) { - IWL_DEBUG_MAC80211("leave - not ready\n"); - ret = -EIO; - goto out; - } - - if (unlikely(!priv->cfg->mod_params->disable_hw_scan && - test_bit(STATUS_SCANNING, &priv->status))) { - IWL_DEBUG_MAC80211("leave - scanning\n"); - set_bit(STATUS_CONF_PENDING, &priv->status); - mutex_unlock(&priv->mutex); - return 0; - } - - channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - ch_info = iwl_get_channel_info(priv, conf->channel->band, channel); - if (!is_channel_valid(ch_info)) { - IWL_DEBUG_MAC80211("leave - invalid channel\n"); - ret = -EINVAL; - goto out; - } - - if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS && - !is_channel_ibss(ch_info)) { - IWL_ERROR("channel %d in band %d not IBSS channel\n", - conf->channel->hw_value, conf->channel->band); - ret = -EINVAL; - goto out; - } - - spin_lock_irqsave(&priv->lock, flags); - - - /* if we are switching from ht to 2.4 clear flags - * from any ht related info since 2.4 does not - * support ht */ - if ((le16_to_cpu(priv->staging_rxon.channel) != channel) -#ifdef IEEE80211_CONF_CHANNEL_SWITCH - && !(conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) -#endif - ) - priv->staging_rxon.flags = 0; - - iwl_set_rxon_channel(priv, conf->channel->band, channel); - - iwl_set_flags_for_band(priv, conf->channel->band); - - /* The list of supported rates and rate mask can be different - * for each band; since the band may have changed, reset - * the rate mask to what mac80211 lists */ - iwl4965_set_rate(priv); - - spin_unlock_irqrestore(&priv->lock, flags); - -#ifdef IEEE80211_CONF_CHANNEL_SWITCH - if (conf->flags & IEEE80211_CONF_CHANNEL_SWITCH) { - iwl4965_hw_channel_switch(priv, conf->channel); - goto out; - } -#endif - - if (!conf->radio_enabled) { - IWL_DEBUG_MAC80211("leave - radio disabled\n"); - goto out; - } - - if (iwl_is_rfkill(priv)) { - IWL_DEBUG_MAC80211("leave - RF kill\n"); - ret = -EIO; - goto out; - } - - IWL_DEBUG_MAC80211("TX Power old=%d new=%d\n", - priv->tx_power_user_lmt, conf->power_level); - - iwl_set_tx_power(priv, conf->power_level, false); - - iwl4965_set_rate(priv); - - if (memcmp(&priv->active_rxon, - &priv->staging_rxon, sizeof(priv->staging_rxon))) - iwl4965_commit_rxon(priv); - else - IWL_DEBUG_INFO("No re-sending same RXON configuration.\n"); - - IWL_DEBUG_MAC80211("leave\n"); - -out: - clear_bit(STATUS_CONF_PENDING, &priv->status); - mutex_unlock(&priv->mutex); - return ret; -} - -static void iwl4965_config_ap(struct iwl_priv *priv) -{ - int ret = 0; - unsigned long flags; - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - /* The following should be done only at AP bring up */ - if (!(iwl_is_associated(priv))) { - - /* RXON - unassoc (to set timing command) */ - priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - iwl4965_commit_rxon(priv); - - /* RXON Timing */ - memset(&priv->rxon_timing, 0, sizeof(struct iwl4965_rxon_time_cmd)); - iwl4965_setup_rxon_timing(priv); - ret = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING, - sizeof(priv->rxon_timing), &priv->rxon_timing); - if (ret) - IWL_WARNING("REPLY_RXON_TIMING failed - " - "Attempting to continue.\n"); - - iwl_set_rxon_chain(priv); - - /* FIXME: what should be the assoc_id for AP? */ - priv->staging_rxon.assoc_id = cpu_to_le16(priv->assoc_id); - if (priv->assoc_capability & WLAN_CAPABILITY_SHORT_PREAMBLE) - priv->staging_rxon.flags |= - RXON_FLG_SHORT_PREAMBLE_MSK; - else - priv->staging_rxon.flags &= - ~RXON_FLG_SHORT_PREAMBLE_MSK; - - if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) { - if (priv->assoc_capability & - WLAN_CAPABILITY_SHORT_SLOT_TIME) - priv->staging_rxon.flags |= - RXON_FLG_SHORT_SLOT_MSK; - else - priv->staging_rxon.flags &= - ~RXON_FLG_SHORT_SLOT_MSK; - - if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS) - priv->staging_rxon.flags &= - ~RXON_FLG_SHORT_SLOT_MSK; - } - /* restore RXON assoc */ - priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK; - iwl4965_commit_rxon(priv); - spin_lock_irqsave(&priv->lock, flags); - iwl_activate_qos(priv, 1); - spin_unlock_irqrestore(&priv->lock, flags); - iwl_rxon_add_station(priv, iwl_bcast_addr, 0); - } - iwl4965_send_beacon_cmd(priv); - - /* FIXME - we need to add code here to detect a totally new - * configuration, reset the AP, unassoc, rxon timing, assoc, - * clear sta table, add BCAST sta... */ -} - -/* temporary */ -static int iwl4965_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb); - -static int iwl4965_mac_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct iwl_priv *priv = hw->priv; - DECLARE_MAC_BUF(mac); - unsigned long flags; - int rc; - - if (conf == NULL) - return -EIO; - - if (priv->vif != vif) { - IWL_DEBUG_MAC80211("leave - priv->vif != vif\n"); - return 0; - } - - if (priv->iw_mode == IEEE80211_IF_TYPE_IBSS && - conf->changed & IEEE80211_IFCC_BEACON) { - struct sk_buff *beacon = ieee80211_beacon_get(hw, vif); - if (!beacon) - return -ENOMEM; - rc = iwl4965_mac_beacon_update(hw, beacon); - if (rc) - return rc; - } - - if ((priv->iw_mode == IEEE80211_IF_TYPE_AP) && - (!conf->ssid_len)) { - IWL_DEBUG_MAC80211 - ("Leaving in AP mode because HostAPD is not ready.\n"); - return 0; - } - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - mutex_lock(&priv->mutex); - - if (conf->bssid) - IWL_DEBUG_MAC80211("bssid: %s\n", - print_mac(mac, conf->bssid)); - -/* - * very dubious code was here; the probe filtering flag is never set: - * - if (unlikely(test_bit(STATUS_SCANNING, &priv->status)) && - !(priv->hw->flags & IEEE80211_HW_NO_PROBE_FILTERING)) { - */ - - if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { - if (!conf->bssid) { - conf->bssid = priv->mac_addr; - memcpy(priv->bssid, priv->mac_addr, ETH_ALEN); - IWL_DEBUG_MAC80211("bssid was set to: %s\n", - print_mac(mac, conf->bssid)); - } - if (priv->ibss_beacon) - dev_kfree_skb(priv->ibss_beacon); - - priv->ibss_beacon = ieee80211_beacon_get(hw, vif); - } - - if (iwl_is_rfkill(priv)) - goto done; - - if (conf->bssid && !is_zero_ether_addr(conf->bssid) && - !is_multicast_ether_addr(conf->bssid)) { - /* If there is currently a HW scan going on in the background - * then we need to cancel it else the RXON below will fail. */ - if (iwl_scan_cancel_timeout(priv, 100)) { - IWL_WARNING("Aborted scan still in progress " - "after 100ms\n"); - IWL_DEBUG_MAC80211("leaving - scan abort failed.\n"); - mutex_unlock(&priv->mutex); - return -EAGAIN; - } - memcpy(priv->staging_rxon.bssid_addr, conf->bssid, ETH_ALEN); - - /* TODO: Audit driver for usage of these members and see - * if mac80211 deprecates them (priv->bssid looks like it - * shouldn't be there, but I haven't scanned the IBSS code - * to verify) - jpk */ - memcpy(priv->bssid, conf->bssid, ETH_ALEN); - - if (priv->iw_mode == IEEE80211_IF_TYPE_AP) - iwl4965_config_ap(priv); - else { - rc = iwl4965_commit_rxon(priv); - if ((priv->iw_mode == IEEE80211_IF_TYPE_STA) && rc) - iwl_rxon_add_station( - priv, priv->active_rxon.bssid_addr, 1); - } - - } else { - iwl_scan_cancel_timeout(priv, 100); - priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - iwl4965_commit_rxon(priv); - } - - done: - spin_lock_irqsave(&priv->lock, flags); - if (!conf->ssid_len) - memset(priv->essid, 0, IW_ESSID_MAX_SIZE); - else - memcpy(priv->essid, conf->ssid, conf->ssid_len); - - priv->essid_len = conf->ssid_len; - spin_unlock_irqrestore(&priv->lock, flags); - - IWL_DEBUG_MAC80211("leave\n"); - mutex_unlock(&priv->mutex); - - return 0; -} - -static void iwl4965_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list) -{ - struct iwl_priv *priv = hw->priv; - - if (changed_flags & (*total_flags) & FIF_OTHER_BSS) { - IWL_DEBUG_MAC80211("Enter: type %d (0x%x, 0x%x)\n", - IEEE80211_IF_TYPE_MNTR, - changed_flags, *total_flags); - /* queue work 'cuz mac80211 is holding a lock which - * prevents us from issuing (synchronous) f/w cmds */ - queue_work(priv->workqueue, &priv->set_monitor); - } - *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | - FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; -} - -static void iwl4965_mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_if_init_conf *conf) -{ - struct iwl_priv *priv = hw->priv; - - IWL_DEBUG_MAC80211("enter\n"); - - mutex_lock(&priv->mutex); - - if (iwl_is_ready_rf(priv)) { - iwl_scan_cancel_timeout(priv, 100); - priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - iwl4965_commit_rxon(priv); - } - if (priv->vif == conf->vif) { - priv->vif = NULL; - memset(priv->bssid, 0, ETH_ALEN); - memset(priv->essid, 0, IW_ESSID_MAX_SIZE); - priv->essid_len = 0; - } - mutex_unlock(&priv->mutex); - - IWL_DEBUG_MAC80211("leave\n"); - -} - -#define IWL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6) -static void iwl4965_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct iwl_priv *priv = hw->priv; - - IWL_DEBUG_MAC80211("changes = 0x%X\n", changes); - - if (changes & BSS_CHANGED_ERP_PREAMBLE) { - IWL_DEBUG_MAC80211("ERP_PREAMBLE %d\n", - bss_conf->use_short_preamble); - if (bss_conf->use_short_preamble) - priv->staging_rxon.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; - else - priv->staging_rxon.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - } - - if (changes & BSS_CHANGED_ERP_CTS_PROT) { - IWL_DEBUG_MAC80211("ERP_CTS %d\n", bss_conf->use_cts_prot); - if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ)) - priv->staging_rxon.flags |= RXON_FLG_TGG_PROTECT_MSK; - else - priv->staging_rxon.flags &= ~RXON_FLG_TGG_PROTECT_MSK; - } - - if (changes & BSS_CHANGED_HT) { - IWL_DEBUG_MAC80211("HT %d\n", bss_conf->assoc_ht); - iwl4965_ht_conf(priv, bss_conf); - iwl_set_rxon_chain(priv); - } - - if (changes & BSS_CHANGED_ASSOC) { - IWL_DEBUG_MAC80211("ASSOC %d\n", bss_conf->assoc); - /* This should never happen as this function should - * never be called from interrupt context. */ - if (WARN_ON_ONCE(in_interrupt())) - return; - if (bss_conf->assoc) { - priv->assoc_id = bss_conf->aid; - priv->beacon_int = bss_conf->beacon_int; - priv->power_data.dtim_period = bss_conf->dtim_period; - priv->timestamp = bss_conf->timestamp; - priv->assoc_capability = bss_conf->assoc_capability; - priv->next_scan_jiffies = jiffies + - IWL_DELAY_NEXT_SCAN_AFTER_ASSOC; - mutex_lock(&priv->mutex); - iwl4965_post_associate(priv); - mutex_unlock(&priv->mutex); - } else { - priv->assoc_id = 0; - IWL_DEBUG_MAC80211("DISASSOC %d\n", bss_conf->assoc); - } - } else if (changes && iwl_is_associated(priv) && priv->assoc_id) { - IWL_DEBUG_MAC80211("Associated Changes %d\n", changes); - iwl_send_rxon_assoc(priv); - } - -} - -static int iwl4965_mac_hw_scan(struct ieee80211_hw *hw, u8 *ssid, size_t len) -{ - int rc = 0; - unsigned long flags; - struct iwl_priv *priv = hw->priv; - - IWL_DEBUG_MAC80211("enter\n"); - - mutex_lock(&priv->mutex); - spin_lock_irqsave(&priv->lock, flags); - - if (!iwl_is_ready_rf(priv)) { - rc = -EIO; - IWL_DEBUG_MAC80211("leave - not ready or exit pending\n"); - goto out_unlock; - } - - if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { /* APs don't scan */ - rc = -EIO; - IWL_ERROR("ERROR: APs don't scan\n"); - goto out_unlock; - } - - /* we don't schedule scan within next_scan_jiffies period */ - if (priv->next_scan_jiffies && - time_after(priv->next_scan_jiffies, jiffies)) { - rc = -EAGAIN; - goto out_unlock; - } - /* if we just finished scan ask for delay */ - if (priv->last_scan_jiffies && time_after(priv->last_scan_jiffies + - IWL_DELAY_NEXT_SCAN, jiffies)) { - rc = -EAGAIN; - goto out_unlock; - } - if (len) { - IWL_DEBUG_SCAN("direct scan for %s [%d]\n ", - iwl_escape_essid(ssid, len), (int)len); - - priv->one_direct_scan = 1; - priv->direct_ssid_len = (u8) - min((u8) len, (u8) IW_ESSID_MAX_SIZE); - memcpy(priv->direct_ssid, ssid, priv->direct_ssid_len); - } else - priv->one_direct_scan = 0; - - rc = iwl_scan_initiate(priv); - - IWL_DEBUG_MAC80211("leave\n"); - -out_unlock: - spin_unlock_irqrestore(&priv->lock, flags); - mutex_unlock(&priv->mutex); - - return rc; -} - -static void iwl4965_mac_update_tkip_key(struct ieee80211_hw *hw, - struct ieee80211_key_conf *keyconf, const u8 *addr, - u32 iv32, u16 *phase1key) -{ - struct iwl_priv *priv = hw->priv; - u8 sta_id = IWL_INVALID_STATION; - unsigned long flags; - __le16 key_flags = 0; - int i; - DECLARE_MAC_BUF(mac); - - IWL_DEBUG_MAC80211("enter\n"); - - sta_id = iwl_find_station(priv, addr); - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_MAC80211("leave - %s not in station map.\n", - print_mac(mac, addr)); - return; - } - - iwl_scan_cancel_timeout(priv, 100); - - key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK); - key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); - key_flags &= ~STA_KEY_FLG_INVALID; - - if (sta_id == priv->hw_params.bcast_sta_id) - key_flags |= STA_KEY_MULTICAST_MSK; - - spin_lock_irqsave(&priv->sta_lock, flags); - - priv->stations[sta_id].sta.key.key_flags = key_flags; - priv->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32; - - for (i = 0; i < 5; i++) - priv->stations[sta_id].sta.key.tkip_rx_ttak[i] = - cpu_to_le16(phase1key[i]); - - priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; - priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - - iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); - - spin_unlock_irqrestore(&priv->sta_lock, flags); - - IWL_DEBUG_MAC80211("leave\n"); -} - -static int iwl4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - const u8 *local_addr, const u8 *addr, - struct ieee80211_key_conf *key) -{ - struct iwl_priv *priv = hw->priv; - DECLARE_MAC_BUF(mac); - int ret = 0; - u8 sta_id = IWL_INVALID_STATION; - u8 is_default_wep_key = 0; - - IWL_DEBUG_MAC80211("enter\n"); - - if (priv->hw_params.sw_crypto) { - IWL_DEBUG_MAC80211("leave - hwcrypto disabled\n"); - return -EOPNOTSUPP; - } - - if (is_zero_ether_addr(addr)) - /* only support pairwise keys */ - return -EOPNOTSUPP; - - sta_id = iwl_find_station(priv, addr); - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_MAC80211("leave - %s not in station map.\n", - print_mac(mac, addr)); - return -EINVAL; - - } - - mutex_lock(&priv->mutex); - iwl_scan_cancel_timeout(priv, 100); - mutex_unlock(&priv->mutex); - - /* If we are getting WEP group key and we didn't receive any key mapping - * so far, we are in legacy wep mode (group key only), otherwise we are - * in 1X mode. - * In legacy wep mode, we use another host command to the uCode */ - if (key->alg == ALG_WEP && sta_id == priv->hw_params.bcast_sta_id && - priv->iw_mode != IEEE80211_IF_TYPE_AP) { - if (cmd == SET_KEY) - is_default_wep_key = !priv->key_mapping_key; - else - is_default_wep_key = - (key->hw_key_idx == HW_KEY_DEFAULT); - } - - switch (cmd) { - case SET_KEY: - if (is_default_wep_key) - ret = iwl_set_default_wep_key(priv, key); - else - ret = iwl_set_dynamic_key(priv, key, sta_id); - - IWL_DEBUG_MAC80211("enable hwcrypto key\n"); - break; - case DISABLE_KEY: - if (is_default_wep_key) - ret = iwl_remove_default_wep_key(priv, key); - else - ret = iwl_remove_dynamic_key(priv, key, sta_id); - - IWL_DEBUG_MAC80211("disable hwcrypto key\n"); - break; - default: - ret = -EINVAL; - } - - IWL_DEBUG_MAC80211("leave\n"); - - return ret; -} - -static int iwl4965_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - struct iwl_priv *priv = hw->priv; - unsigned long flags; - int q; - - IWL_DEBUG_MAC80211("enter\n"); - - if (!iwl_is_ready_rf(priv)) { - IWL_DEBUG_MAC80211("leave - RF not ready\n"); - return -EIO; - } - - if (queue >= AC_NUM) { - IWL_DEBUG_MAC80211("leave - queue >= AC_NUM %d\n", queue); - return 0; - } - - if (!priv->qos_data.qos_enable) { - priv->qos_data.qos_active = 0; - IWL_DEBUG_MAC80211("leave - qos not enabled\n"); - return 0; - } - q = AC_NUM - 1 - queue; - - spin_lock_irqsave(&priv->lock, flags); - - priv->qos_data.def_qos_parm.ac[q].cw_min = cpu_to_le16(params->cw_min); - priv->qos_data.def_qos_parm.ac[q].cw_max = cpu_to_le16(params->cw_max); - priv->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; - priv->qos_data.def_qos_parm.ac[q].edca_txop = - cpu_to_le16((params->txop * 32)); - - priv->qos_data.def_qos_parm.ac[q].reserved1 = 0; - priv->qos_data.qos_active = 1; - - if (priv->iw_mode == IEEE80211_IF_TYPE_AP) - iwl_activate_qos(priv, 1); - else if (priv->assoc_id && iwl_is_associated(priv)) - iwl_activate_qos(priv, 0); - - spin_unlock_irqrestore(&priv->lock, flags); - - IWL_DEBUG_MAC80211("leave\n"); - return 0; -} - -static int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw, - enum ieee80211_ampdu_mlme_action action, - const u8 *addr, u16 tid, u16 *ssn) -{ - struct iwl_priv *priv = hw->priv; - DECLARE_MAC_BUF(mac); - - IWL_DEBUG_HT("A-MPDU action on addr %s tid %d\n", - print_mac(mac, addr), tid); - - if (!(priv->cfg->sku & IWL_SKU_N)) - return -EACCES; - - switch (action) { - case IEEE80211_AMPDU_RX_START: - IWL_DEBUG_HT("start Rx\n"); - return iwl_rx_agg_start(priv, addr, tid, *ssn); - case IEEE80211_AMPDU_RX_STOP: - IWL_DEBUG_HT("stop Rx\n"); - return iwl_rx_agg_stop(priv, addr, tid); - case IEEE80211_AMPDU_TX_START: - IWL_DEBUG_HT("start Tx\n"); - return iwl_tx_agg_start(priv, addr, tid, ssn); - case IEEE80211_AMPDU_TX_STOP: - IWL_DEBUG_HT("stop Tx\n"); - return iwl_tx_agg_stop(priv, addr, tid); - default: - IWL_DEBUG_HT("unknown\n"); - return -EINVAL; - break; - } - return 0; -} -static int iwl4965_mac_get_tx_stats(struct ieee80211_hw *hw, - struct ieee80211_tx_queue_stats *stats) -{ - struct iwl_priv *priv = hw->priv; - int i, avail; - struct iwl_tx_queue *txq; - struct iwl_queue *q; - unsigned long flags; - - IWL_DEBUG_MAC80211("enter\n"); - - if (!iwl_is_ready_rf(priv)) { - IWL_DEBUG_MAC80211("leave - RF not ready\n"); - return -EIO; - } - - spin_lock_irqsave(&priv->lock, flags); - - for (i = 0; i < AC_NUM; i++) { - txq = &priv->txq[i]; - q = &txq->q; - avail = iwl_queue_space(q); - - stats[i].len = q->n_window - avail; - stats[i].limit = q->n_window - q->high_mark; - stats[i].count = q->n_window; - - } - spin_unlock_irqrestore(&priv->lock, flags); - - IWL_DEBUG_MAC80211("leave\n"); - - return 0; -} - -static int iwl4965_mac_get_stats(struct ieee80211_hw *hw, - struct ieee80211_low_level_stats *stats) -{ - struct iwl_priv *priv = hw->priv; - - priv = hw->priv; - IWL_DEBUG_MAC80211("enter\n"); - IWL_DEBUG_MAC80211("leave\n"); - - return 0; -} - -static void iwl4965_mac_reset_tsf(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = hw->priv; - unsigned long flags; - - mutex_lock(&priv->mutex); - IWL_DEBUG_MAC80211("enter\n"); - - spin_lock_irqsave(&priv->lock, flags); - memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_info)); - spin_unlock_irqrestore(&priv->lock, flags); - - iwl_reset_qos(priv); - - spin_lock_irqsave(&priv->lock, flags); - priv->assoc_id = 0; - priv->assoc_capability = 0; - priv->assoc_station_added = 0; - - /* new association get rid of ibss beacon skb */ - if (priv->ibss_beacon) - dev_kfree_skb(priv->ibss_beacon); - - priv->ibss_beacon = NULL; - - priv->beacon_int = priv->hw->conf.beacon_int; - priv->timestamp = 0; - if ((priv->iw_mode == IEEE80211_IF_TYPE_STA)) - priv->beacon_int = 0; - - spin_unlock_irqrestore(&priv->lock, flags); - - if (!iwl_is_ready_rf(priv)) { - IWL_DEBUG_MAC80211("leave - not ready\n"); - mutex_unlock(&priv->mutex); - return; - } - - /* we are restarting association process - * clear RXON_FILTER_ASSOC_MSK bit - */ - if (priv->iw_mode != IEEE80211_IF_TYPE_AP) { - iwl_scan_cancel_timeout(priv, 100); - priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - iwl4965_commit_rxon(priv); - } - - iwl_power_update_mode(priv, 0); - - /* Per mac80211.h: This is only used in IBSS mode... */ - if (priv->iw_mode != IEEE80211_IF_TYPE_IBSS) { - - IWL_DEBUG_MAC80211("leave - not in IBSS\n"); - mutex_unlock(&priv->mutex); - return; - } - - iwl4965_set_rate(priv); - - mutex_unlock(&priv->mutex); - - IWL_DEBUG_MAC80211("leave\n"); -} - -static int iwl4965_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb) -{ - struct iwl_priv *priv = hw->priv; - unsigned long flags; - __le64 timestamp; - - mutex_lock(&priv->mutex); - IWL_DEBUG_MAC80211("enter\n"); - - if (!iwl_is_ready_rf(priv)) { - IWL_DEBUG_MAC80211("leave - RF not ready\n"); - mutex_unlock(&priv->mutex); - return -EIO; - } - - if (priv->iw_mode != IEEE80211_IF_TYPE_IBSS) { - IWL_DEBUG_MAC80211("leave - not IBSS\n"); - mutex_unlock(&priv->mutex); - return -EIO; - } - - spin_lock_irqsave(&priv->lock, flags); - - if (priv->ibss_beacon) - dev_kfree_skb(priv->ibss_beacon); - - priv->ibss_beacon = skb; - - priv->assoc_id = 0; - timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; - priv->timestamp = le64_to_cpu(timestamp) + (priv->beacon_int * 1000); - - IWL_DEBUG_MAC80211("leave\n"); - spin_unlock_irqrestore(&priv->lock, flags); - - iwl_reset_qos(priv); - - iwl4965_post_associate(priv); - - mutex_unlock(&priv->mutex); - - return 0; -} - -/***************************************************************************** - * - * sysfs attributes - * - *****************************************************************************/ - -#ifdef CONFIG_IWLWIFI_DEBUG - -/* - * The following adds a new attribute to the sysfs representation - * of this device driver (i.e. a new file in /sys/bus/pci/drivers/iwl/) - * used for controlling the debug level. - * - * See the level definitions in iwl for details. - */ - -static ssize_t show_debug_level(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = d->driver_data; - - return sprintf(buf, "0x%08X\n", priv->debug_level); -} -static ssize_t store_debug_level(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct iwl_priv *priv = d->driver_data; - char *p = (char *)buf; - u32 val; - - val = simple_strtoul(p, &p, 0); - if (p == buf) - printk(KERN_INFO DRV_NAME - ": %s is not in hex or decimal form.\n", buf); - else - priv->debug_level = val; - - return strnlen(buf, count); -} - -static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, - show_debug_level, store_debug_level); - - -#endif /* CONFIG_IWLWIFI_DEBUG */ - - -static ssize_t show_version(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = d->driver_data; - struct iwl_alive_resp *palive = &priv->card_alive; - ssize_t pos = 0; - u16 eeprom_ver; - - if (palive->is_valid) - pos += sprintf(buf + pos, - "fw version: 0x%01X.0x%01X.0x%01X.0x%01X\n" - "fw type: 0x%01X 0x%01X\n", - palive->ucode_major, palive->ucode_minor, - palive->sw_rev[0], palive->sw_rev[1], - palive->ver_type, palive->ver_subtype); - else - pos += sprintf(buf + pos, "fw not loaded\n"); - - if (priv->eeprom) { - eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); - pos += sprintf(buf + pos, "EEPROM version: 0x%x\n", - eeprom_ver); - } else { - pos += sprintf(buf + pos, "EEPROM not initialzed\n"); - } - - return pos; -} - -static DEVICE_ATTR(version, S_IWUSR | S_IRUGO, show_version, NULL); - -static ssize_t show_temperature(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - return sprintf(buf, "%d\n", priv->temperature); -} - -static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL); - -static ssize_t show_tx_power(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; - return sprintf(buf, "%d\n", priv->tx_power_user_lmt); -} - -static ssize_t store_tx_power(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; - char *p = (char *)buf; - u32 val; - - val = simple_strtoul(p, &p, 10); - if (p == buf) - printk(KERN_INFO DRV_NAME - ": %s is not in decimal form.\n", buf); - else - iwl_set_tx_power(priv, val, false); - - return count; -} - -static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, show_tx_power, store_tx_power); - -static ssize_t show_flags(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; - - return sprintf(buf, "0x%04X\n", priv->active_rxon.flags); -} - -static ssize_t store_flags(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; - u32 flags = simple_strtoul(buf, NULL, 0); - - mutex_lock(&priv->mutex); - if (le32_to_cpu(priv->staging_rxon.flags) != flags) { - /* Cancel any currently running scans... */ - if (iwl_scan_cancel_timeout(priv, 100)) - IWL_WARNING("Could not cancel scan.\n"); - else { - IWL_DEBUG_INFO("Committing rxon.flags = 0x%04X\n", - flags); - priv->staging_rxon.flags = cpu_to_le32(flags); - iwl4965_commit_rxon(priv); - } - } - mutex_unlock(&priv->mutex); - - return count; -} - -static DEVICE_ATTR(flags, S_IWUSR | S_IRUGO, show_flags, store_flags); - -static ssize_t show_filter_flags(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; - - return sprintf(buf, "0x%04X\n", - le32_to_cpu(priv->active_rxon.filter_flags)); -} - -static ssize_t store_filter_flags(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; - u32 filter_flags = simple_strtoul(buf, NULL, 0); - - mutex_lock(&priv->mutex); - if (le32_to_cpu(priv->staging_rxon.filter_flags) != filter_flags) { - /* Cancel any currently running scans... */ - if (iwl_scan_cancel_timeout(priv, 100)) - IWL_WARNING("Could not cancel scan.\n"); - else { - IWL_DEBUG_INFO("Committing rxon.filter_flags = " - "0x%04X\n", filter_flags); - priv->staging_rxon.filter_flags = - cpu_to_le32(filter_flags); - iwl4965_commit_rxon(priv); - } - } - mutex_unlock(&priv->mutex); - - return count; -} - -static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, show_filter_flags, - store_filter_flags); - -#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT - -static ssize_t show_measurement(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - struct iwl4965_spectrum_notification measure_report; - u32 size = sizeof(measure_report), len = 0, ofs = 0; - u8 *data = (u8 *) & measure_report; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (!(priv->measurement_status & MEASUREMENT_READY)) { - spin_unlock_irqrestore(&priv->lock, flags); - return 0; - } - memcpy(&measure_report, &priv->measure_report, size); - priv->measurement_status = 0; - spin_unlock_irqrestore(&priv->lock, flags); - - while (size && (PAGE_SIZE - len)) { - hex_dump_to_buffer(data + ofs, size, 16, 1, buf + len, - PAGE_SIZE - len, 1); - len = strlen(buf); - if (PAGE_SIZE - len) - buf[len++] = '\n'; - - ofs += 16; - size -= min(size, 16U); - } - - return len; -} - -static ssize_t store_measurement(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - struct ieee80211_measurement_params params = { - .channel = le16_to_cpu(priv->active_rxon.channel), - .start_time = cpu_to_le64(priv->last_tsf), - .duration = cpu_to_le16(1), - }; - u8 type = IWL_MEASURE_BASIC; - u8 buffer[32]; - u8 channel; - - if (count) { - char *p = buffer; - strncpy(buffer, buf, min(sizeof(buffer), count)); - channel = simple_strtoul(p, NULL, 0); - if (channel) - params.channel = channel; - - p = buffer; - while (*p && *p != ' ') - p++; - if (*p) - type = simple_strtoul(p + 1, NULL, 0); - } - - IWL_DEBUG_INFO("Invoking measurement of type %d on " - "channel %d (for '%s')\n", type, params.channel, buf); - iwl4965_get_measurement(priv, ¶ms, type); - - return count; -} - -static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR, - show_measurement, store_measurement); -#endif /* CONFIG_IWL4965_SPECTRUM_MEASUREMENT */ - -static ssize_t store_retry_rate(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - - priv->retry_rate = simple_strtoul(buf, NULL, 0); - if (priv->retry_rate <= 0) - priv->retry_rate = 1; - - return count; -} - -static ssize_t show_retry_rate(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - return sprintf(buf, "%d", priv->retry_rate); -} - -static DEVICE_ATTR(retry_rate, S_IWUSR | S_IRUSR, show_retry_rate, - store_retry_rate); - -static ssize_t store_power_level(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - int ret; - int mode; - - mode = simple_strtoul(buf, NULL, 0); - mutex_lock(&priv->mutex); - - if (!iwl_is_ready(priv)) { - ret = -EAGAIN; - goto out; - } - - ret = iwl_power_set_user_mode(priv, mode); - if (ret) { - IWL_DEBUG_MAC80211("failed setting power mode.\n"); - goto out; - } - ret = count; - - out: - mutex_unlock(&priv->mutex); - return ret; -} - -static ssize_t show_power_level(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - int mode = priv->power_data.user_power_setting; - int system = priv->power_data.system_power_setting; - int level = priv->power_data.power_mode; - char *p = buf; - - switch (system) { - case IWL_POWER_SYS_AUTO: - p += sprintf(p, "SYSTEM:auto"); - break; - case IWL_POWER_SYS_AC: - p += sprintf(p, "SYSTEM:ac"); - break; - case IWL_POWER_SYS_BATTERY: - p += sprintf(p, "SYSTEM:battery"); - break; - } - - p += sprintf(p, "\tMODE:%s", (mode < IWL_POWER_AUTO)?"fixed":"auto"); - p += sprintf(p, "\tINDEX:%d", level); - p += sprintf(p, "\n"); - return (p - buf + 1); -} - -static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, show_power_level, - store_power_level); - -static ssize_t show_channels(struct device *d, - struct device_attribute *attr, char *buf) -{ - - struct iwl_priv *priv = dev_get_drvdata(d); - struct ieee80211_channel *channels = NULL; - const struct ieee80211_supported_band *supp_band = NULL; - int len = 0, i; - int count = 0; - - if (!test_bit(STATUS_GEO_CONFIGURED, &priv->status)) - return -EAGAIN; - - supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ); - channels = supp_band->channels; - count = supp_band->n_channels; - - len += sprintf(&buf[len], - "Displaying %d channels in 2.4GHz band " - "(802.11bg):\n", count); - - for (i = 0; i < count; i++) - len += sprintf(&buf[len], "%d: %ddBm: BSS%s%s, %s.\n", - ieee80211_frequency_to_channel( - channels[i].center_freq), - channels[i].max_power, - channels[i].flags & IEEE80211_CHAN_RADAR ? - " (IEEE 802.11h required)" : "", - (!(channels[i].flags & IEEE80211_CHAN_NO_IBSS) - || (channels[i].flags & - IEEE80211_CHAN_RADAR)) ? "" : - ", IBSS", - channels[i].flags & - IEEE80211_CHAN_PASSIVE_SCAN ? - "passive only" : "active/passive"); - - supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ); - channels = supp_band->channels; - count = supp_band->n_channels; - - len += sprintf(&buf[len], "Displaying %d channels in 5.2GHz band " - "(802.11a):\n", count); - - for (i = 0; i < count; i++) - len += sprintf(&buf[len], "%d: %ddBm: BSS%s%s, %s.\n", - ieee80211_frequency_to_channel( - channels[i].center_freq), - channels[i].max_power, - channels[i].flags & IEEE80211_CHAN_RADAR ? - " (IEEE 802.11h required)" : "", - ((channels[i].flags & IEEE80211_CHAN_NO_IBSS) - || (channels[i].flags & - IEEE80211_CHAN_RADAR)) ? "" : - ", IBSS", - channels[i].flags & - IEEE80211_CHAN_PASSIVE_SCAN ? - "passive only" : "active/passive"); - - return len; -} - -static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL); - -static ssize_t show_statistics(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - u32 size = sizeof(struct iwl_notif_statistics); - u32 len = 0, ofs = 0; - u8 *data = (u8 *) & priv->statistics; - int rc = 0; - - if (!iwl_is_alive(priv)) - return -EAGAIN; - - mutex_lock(&priv->mutex); - rc = iwl_send_statistics_request(priv, 0); - mutex_unlock(&priv->mutex); - - if (rc) { - len = sprintf(buf, - "Error sending statistics request: 0x%08X\n", rc); - return len; - } - - while (size && (PAGE_SIZE - len)) { - hex_dump_to_buffer(data + ofs, size, 16, 1, buf + len, - PAGE_SIZE - len, 1); - len = strlen(buf); - if (PAGE_SIZE - len) - buf[len++] = '\n'; - - ofs += 16; - size -= min(size, 16U); - } - - return len; -} - -static DEVICE_ATTR(statistics, S_IRUGO, show_statistics, NULL); - -static ssize_t show_status(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = (struct iwl_priv *)d->driver_data; - if (!iwl_is_alive(priv)) - return -EAGAIN; - return sprintf(buf, "0x%08x\n", (int)priv->status); -} - -static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); - -/***************************************************************************** - * - * driver setup and teardown - * - *****************************************************************************/ - -static void iwl_setup_deferred_work(struct iwl_priv *priv) -{ - priv->workqueue = create_workqueue(DRV_NAME); - - init_waitqueue_head(&priv->wait_command_queue); - - INIT_WORK(&priv->up, iwl4965_bg_up); - INIT_WORK(&priv->restart, iwl4965_bg_restart); - INIT_WORK(&priv->rx_replenish, iwl4965_bg_rx_replenish); - INIT_WORK(&priv->rf_kill, iwl4965_bg_rf_kill); - INIT_WORK(&priv->beacon_update, iwl4965_bg_beacon_update); - INIT_WORK(&priv->set_monitor, iwl4965_bg_set_monitor); - INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work); - INIT_DELAYED_WORK(&priv->init_alive_start, iwl_bg_init_alive_start); - INIT_DELAYED_WORK(&priv->alive_start, iwl_bg_alive_start); - - /* FIXME : remove when resolved PENDING */ - INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed); - iwl_setup_scan_deferred_work(priv); - - if (priv->cfg->ops->lib->setup_deferred_work) - priv->cfg->ops->lib->setup_deferred_work(priv); - - init_timer(&priv->statistics_periodic); - priv->statistics_periodic.data = (unsigned long)priv; - priv->statistics_periodic.function = iwl4965_bg_statistics_periodic; - - tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) - iwl4965_irq_tasklet, (unsigned long)priv); -} - -static void iwl_cancel_deferred_work(struct iwl_priv *priv) -{ - if (priv->cfg->ops->lib->cancel_deferred_work) - priv->cfg->ops->lib->cancel_deferred_work(priv); - - cancel_delayed_work_sync(&priv->init_alive_start); - cancel_delayed_work(&priv->scan_check); - cancel_delayed_work(&priv->alive_start); - cancel_work_sync(&priv->beacon_update); - del_timer_sync(&priv->statistics_periodic); -} - -static struct attribute *iwl4965_sysfs_entries[] = { - &dev_attr_channels.attr, - &dev_attr_flags.attr, - &dev_attr_filter_flags.attr, -#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT - &dev_attr_measurement.attr, -#endif - &dev_attr_power_level.attr, - &dev_attr_retry_rate.attr, - &dev_attr_statistics.attr, - &dev_attr_status.attr, - &dev_attr_temperature.attr, - &dev_attr_tx_power.attr, -#ifdef CONFIG_IWLWIFI_DEBUG - &dev_attr_debug_level.attr, -#endif - &dev_attr_version.attr, - - NULL -}; - -static struct attribute_group iwl4965_attribute_group = { - .name = NULL, /* put in device directory */ - .attrs = iwl4965_sysfs_entries, -}; - -static struct ieee80211_ops iwl4965_hw_ops = { - .tx = iwl4965_mac_tx, - .start = iwl4965_mac_start, - .stop = iwl4965_mac_stop, - .add_interface = iwl4965_mac_add_interface, - .remove_interface = iwl4965_mac_remove_interface, - .config = iwl4965_mac_config, - .config_interface = iwl4965_mac_config_interface, - .configure_filter = iwl4965_configure_filter, - .set_key = iwl4965_mac_set_key, - .update_tkip_key = iwl4965_mac_update_tkip_key, - .get_stats = iwl4965_mac_get_stats, - .get_tx_stats = iwl4965_mac_get_tx_stats, - .conf_tx = iwl4965_mac_conf_tx, - .reset_tsf = iwl4965_mac_reset_tsf, - .bss_info_changed = iwl4965_bss_info_changed, - .ampdu_action = iwl4965_mac_ampdu_action, - .hw_scan = iwl4965_mac_hw_scan -}; - -static int iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - int err = 0; - struct iwl_priv *priv; - struct ieee80211_hw *hw; - struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data); - unsigned long flags; - DECLARE_MAC_BUF(mac); - - /************************ - * 1. Allocating HW data - ************************/ - - /* Disabling hardware scan means that mac80211 will perform scans - * "the hard way", rather than using device's scan. */ - if (cfg->mod_params->disable_hw_scan) { - if (cfg->mod_params->debug & IWL_DL_INFO) - dev_printk(KERN_DEBUG, &(pdev->dev), - "Disabling hw_scan\n"); - iwl4965_hw_ops.hw_scan = NULL; - } - - hw = iwl_alloc_all(cfg, &iwl4965_hw_ops); - if (!hw) { - err = -ENOMEM; - goto out; - } - priv = hw->priv; - /* At this point both hw and priv are allocated. */ - - SET_IEEE80211_DEV(hw, &pdev->dev); - - IWL_DEBUG_INFO("*** LOAD DRIVER ***\n"); - priv->cfg = cfg; - priv->pci_dev = pdev; - -#ifdef CONFIG_IWLWIFI_DEBUG - priv->debug_level = priv->cfg->mod_params->debug; - atomic_set(&priv->restrict_refcnt, 0); -#endif - - /************************** - * 2. Initializing PCI bus - **************************/ - if (pci_enable_device(pdev)) { - err = -ENODEV; - goto out_ieee80211_free_hw; - } - - pci_set_master(pdev); - - err = pci_set_dma_mask(pdev, DMA_64BIT_MASK); - if (!err) - err = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); - if (err) { - err = pci_set_dma_mask(pdev, DMA_32BIT_MASK); - if (!err) - err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); - /* both attempts failed: */ - if (err) { - printk(KERN_WARNING "%s: No suitable DMA available.\n", - DRV_NAME); - goto out_pci_disable_device; - } - } - - err = pci_request_regions(pdev, DRV_NAME); - if (err) - goto out_pci_disable_device; - - pci_set_drvdata(pdev, priv); - - /* We disable the RETRY_TIMEOUT register (0x41) to keep - * PCI Tx retries from interfering with C3 CPU state */ - pci_write_config_byte(pdev, 0x41, 0x00); - - /*********************** - * 3. Read REV register - ***********************/ - priv->hw_base = pci_iomap(pdev, 0, 0); - if (!priv->hw_base) { - err = -ENODEV; - goto out_pci_release_regions; - } - - IWL_DEBUG_INFO("pci_resource_len = 0x%08llx\n", - (unsigned long long) pci_resource_len(pdev, 0)); - IWL_DEBUG_INFO("pci_resource_base = %p\n", priv->hw_base); - - iwl_hw_detect(priv); - printk(KERN_INFO DRV_NAME - ": Detected Intel Wireless WiFi Link %s REV=0x%X\n", - priv->cfg->name, priv->hw_rev); - - /* amp init */ - err = priv->cfg->ops->lib->apm_ops.init(priv); - if (err < 0) { - IWL_DEBUG_INFO("Failed to init APMG\n"); - goto out_iounmap; - } - /***************** - * 4. Read EEPROM - *****************/ - /* Read the EEPROM */ - err = iwl_eeprom_init(priv); - if (err) { - IWL_ERROR("Unable to init EEPROM\n"); - goto out_iounmap; - } - err = iwl_eeprom_check_version(priv); - if (err) - goto out_iounmap; - - /* extract MAC Address */ - iwl_eeprom_get_mac(priv, priv->mac_addr); - IWL_DEBUG_INFO("MAC address: %s\n", print_mac(mac, priv->mac_addr)); - SET_IEEE80211_PERM_ADDR(priv->hw, priv->mac_addr); - - /************************ - * 5. Setup HW constants - ************************/ - if (iwl_set_hw_params(priv)) { - IWL_ERROR("failed to set hw parameters\n"); - goto out_free_eeprom; - } - - /******************* - * 6. Setup priv - *******************/ - - err = iwl_init_drv(priv); - if (err) - goto out_free_eeprom; - /* At this point both hw and priv are initialized. */ - - /********************************** - * 7. Initialize module parameters - **********************************/ - - /* Disable radio (SW RF KILL) via parameter when loading driver */ - if (priv->cfg->mod_params->disable) { - set_bit(STATUS_RF_KILL_SW, &priv->status); - IWL_DEBUG_INFO("Radio disabled.\n"); - } - - /******************** - * 8. Setup services - ********************/ - spin_lock_irqsave(&priv->lock, flags); - iwl4965_disable_interrupts(priv); - spin_unlock_irqrestore(&priv->lock, flags); - - err = sysfs_create_group(&pdev->dev.kobj, &iwl4965_attribute_group); - if (err) { - IWL_ERROR("failed to create sysfs device attributes\n"); - goto out_uninit_drv; - } - - - iwl_setup_deferred_work(priv); - iwl_setup_rx_handlers(priv); - - /******************** - * 9. Conclude - ********************/ - pci_save_state(pdev); - pci_disable_device(pdev); - - /********************************** - * 10. Setup and register mac80211 - **********************************/ - - err = iwl_setup_mac(priv); - if (err) - goto out_remove_sysfs; - - err = iwl_dbgfs_register(priv, DRV_NAME); - if (err) - IWL_ERROR("failed to create debugfs files\n"); - - err = iwl_rfkill_init(priv); - if (err) - IWL_ERROR("Unable to initialize RFKILL system. " - "Ignoring error: %d\n", err); - iwl_power_initialize(priv); - return 0; - - out_remove_sysfs: - sysfs_remove_group(&pdev->dev.kobj, &iwl4965_attribute_group); - out_uninit_drv: - iwl_uninit_drv(priv); - out_free_eeprom: - iwl_eeprom_free(priv); - out_iounmap: - pci_iounmap(pdev, priv->hw_base); - out_pci_release_regions: - pci_release_regions(pdev); - pci_set_drvdata(pdev, NULL); - out_pci_disable_device: - pci_disable_device(pdev); - out_ieee80211_free_hw: - ieee80211_free_hw(priv->hw); - out: - return err; -} - -static void __devexit iwl4965_pci_remove(struct pci_dev *pdev) -{ - struct iwl_priv *priv = pci_get_drvdata(pdev); - unsigned long flags; - - if (!priv) - return; - - IWL_DEBUG_INFO("*** UNLOAD DRIVER ***\n"); - - iwl_dbgfs_unregister(priv); - sysfs_remove_group(&pdev->dev.kobj, &iwl4965_attribute_group); - - if (priv->mac80211_registered) { - ieee80211_unregister_hw(priv->hw); - priv->mac80211_registered = 0; - } - - set_bit(STATUS_EXIT_PENDING, &priv->status); - - iwl4965_down(priv); - - /* make sure we flush any pending irq or - * tasklet for the driver - */ - spin_lock_irqsave(&priv->lock, flags); - iwl4965_disable_interrupts(priv); - spin_unlock_irqrestore(&priv->lock, flags); - - iwl_synchronize_irq(priv); - - iwl_rfkill_unregister(priv); - iwl4965_dealloc_ucode_pci(priv); - - if (priv->rxq.bd) - iwl_rx_queue_free(priv, &priv->rxq); - iwl_hw_txq_ctx_free(priv); - - iwl_clear_stations_table(priv); - iwl_eeprom_free(priv); - - - /*netif_stop_queue(dev); */ - flush_workqueue(priv->workqueue); - - /* ieee80211_unregister_hw calls iwl4965_mac_stop, which flushes - * priv->workqueue... so we can't take down the workqueue - * until now... */ - destroy_workqueue(priv->workqueue); - priv->workqueue = NULL; - - pci_iounmap(pdev, priv->hw_base); - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - - iwl_uninit_drv(priv); - - if (priv->ibss_beacon) - dev_kfree_skb(priv->ibss_beacon); - - ieee80211_free_hw(priv->hw); -} - -#ifdef CONFIG_PM - -static int iwl4965_pci_suspend(struct pci_dev *pdev, pm_message_t state) -{ - struct iwl_priv *priv = pci_get_drvdata(pdev); - - if (priv->is_open) { - set_bit(STATUS_IN_SUSPEND, &priv->status); - iwl4965_mac_stop(priv->hw); - priv->is_open = 1; - } - - pci_set_power_state(pdev, PCI_D3hot); - - return 0; -} - -static int iwl4965_pci_resume(struct pci_dev *pdev) -{ - struct iwl_priv *priv = pci_get_drvdata(pdev); - - pci_set_power_state(pdev, PCI_D0); - - if (priv->is_open) - iwl4965_mac_start(priv->hw); - - clear_bit(STATUS_IN_SUSPEND, &priv->status); - return 0; -} - -#endif /* CONFIG_PM */ - -/***************************************************************************** - * - * driver and module entry point - * - *****************************************************************************/ - -/* Hardware specific file defines the PCI IDs table for that hardware module */ -static struct pci_device_id iwl_hw_card_ids[] = { - {IWL_PCI_DEVICE(0x4229, PCI_ANY_ID, iwl4965_agn_cfg)}, - {IWL_PCI_DEVICE(0x4230, PCI_ANY_ID, iwl4965_agn_cfg)}, -#ifdef CONFIG_IWL5000 - {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bg_cfg)}, - {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bg_cfg)}, - {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, - {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, - {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, - {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, - {IWL_PCI_DEVICE(0x4232, PCI_ANY_ID, iwl5100_agn_cfg)}, - {IWL_PCI_DEVICE(0x4235, PCI_ANY_ID, iwl5300_agn_cfg)}, - {IWL_PCI_DEVICE(0x4236, PCI_ANY_ID, iwl5300_agn_cfg)}, - {IWL_PCI_DEVICE(0x4237, PCI_ANY_ID, iwl5100_agn_cfg)}, - {IWL_PCI_DEVICE(0x423A, PCI_ANY_ID, iwl5350_agn_cfg)}, -#endif /* CONFIG_IWL5000 */ - {0} -}; -MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); - -static struct pci_driver iwl_driver = { - .name = DRV_NAME, - .id_table = iwl_hw_card_ids, - .probe = iwl4965_pci_probe, - .remove = __devexit_p(iwl4965_pci_remove), -#ifdef CONFIG_PM - .suspend = iwl4965_pci_suspend, - .resume = iwl4965_pci_resume, -#endif -}; - -static int __init iwl4965_init(void) -{ - - int ret; - printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION ", " DRV_VERSION "\n"); - printk(KERN_INFO DRV_NAME ": " DRV_COPYRIGHT "\n"); - - ret = iwlagn_rate_control_register(); - if (ret) { - IWL_ERROR("Unable to register rate control algorithm: %d\n", ret); - return ret; - } - - ret = pci_register_driver(&iwl_driver); - if (ret) { - IWL_ERROR("Unable to initialize PCI module\n"); - goto error_register; - } - - return ret; - -error_register: - iwlagn_rate_control_unregister(); - return ret; -} - -static void __exit iwl4965_exit(void) -{ - pci_unregister_driver(&iwl_driver); - iwlagn_rate_control_unregister(); -} - -module_exit(iwl4965_exit); -module_init(iwl4965_init); -- cgit v1.2.1 From 3ac7f14694dd38273d9d96f1c873233d71190c15 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 21 Jul 2008 02:40:14 +0300 Subject: iwlwifi: fix checkpatch.pl errors This patch fixes errors reported by checkpatch in iwlwifi drivers Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945.c | 7 ++-- drivers/net/wireless/iwlwifi/iwl-4965.c | 4 +-- drivers/net/wireless/iwlwifi/iwl-5000.c | 2 +- drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 16 ++++----- drivers/net/wireless/iwlwifi/iwl-agn.c | 10 +++--- drivers/net/wireless/iwlwifi/iwl-core.c | 4 +-- drivers/net/wireless/iwlwifi/iwl-debug.h | 4 +-- drivers/net/wireless/iwlwifi/iwl-debugfs.c | 11 +++--- drivers/net/wireless/iwlwifi/iwl-eeprom.c | 3 +- drivers/net/wireless/iwlwifi/iwl-sta.c | 2 +- drivers/net/wireless/iwlwifi/iwl3945-base.c | 52 +++++++++++++---------------- 11 files changed, 55 insertions(+), 60 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 56a9361a847f..b3931f6135a4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -795,8 +795,7 @@ static void iwl3945_rx_reply_rx(struct iwl3945_priv *priv, struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)header; __le32 *pos; - pos = - (__le32 *) & mgmt->u.beacon. + pos = (__le32 *)&mgmt->u.beacon. timestamp; priv->timestamp0 = le32_to_cpu(pos[0]); priv->timestamp1 = le32_to_cpu(pos[1]); @@ -1509,7 +1508,7 @@ static int iwl3945_hw_reg_adjust_power_by_temp(int new_reading, int old_reading) */ static inline int iwl3945_hw_reg_temp_out_of_range(int temperature) { - return (((temperature < -260) || (temperature > 25)) ? 1 : 0); + return ((temperature < -260) || (temperature > 25)) ? 1 : 0; } int iwl3945_hw_get_temperature(struct iwl3945_priv *priv) @@ -2630,7 +2629,7 @@ unsigned int iwl3945_hw_get_beacon_cmd(struct iwl3945_priv *priv, tx_beacon_cmd->tx.supp_rates[1] = (IWL_CCK_BASIC_RATES_MASK & 0xF); - return (sizeof(struct iwl3945_tx_beacon_cmd) + frame_size); + return sizeof(struct iwl3945_tx_beacon_cmd) + frame_size; } void iwl3945_hw_rx_handler_setup(struct iwl3945_priv *priv) diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index f356f4f0944b..718f9d9e4947 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -1515,11 +1515,11 @@ static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel, c, atten_value, power_index, tx_power.s.radio_tx_gain[c], tx_power.s.dsp_predis_atten[c]); - }/* for each chain */ + } /* for each chain */ tx_power_tbl->power_tbl[i].dw = cpu_to_le32(tx_power.dw); - }/* for each rate */ + } /* for each rate */ return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 076d3560302b..f7bbd12193f9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1131,7 +1131,7 @@ static void iwl5000_txq_set_sched(struct iwl_priv *priv, u32 mask) static inline u32 iwl5000_get_scd_ssn(struct iwl5000_tx_resp *tx_resp) { - return le32_to_cpup((__le32*)&tx_resp->status + + return le32_to_cpup((__le32 *)&tx_resp->status + tx_resp->frame_count) & MAX_SN; } diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index b498b58d81fa..754fef5b592f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -245,7 +245,7 @@ static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) { - return ((ant_type & valid_antenna) == ant_type); + return (ant_type & valid_antenna) == ant_type; } /* @@ -384,9 +384,9 @@ static void rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid, static inline int get_num_of_ant_from_rate(u32 rate_n_flags) { - return (!!(rate_n_flags & RATE_MCS_ANT_A_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + - !!(rate_n_flags & RATE_MCS_ANT_C_MSK)); + return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_C_MSK); } /** @@ -620,9 +620,9 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, #if 0 static inline u8 rs_use_green(struct iwl_priv *priv, struct ieee80211_conf *conf) { - return ((conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) && + return (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) && priv->current_ht_config.is_green_field && - !priv->current_ht_config.non_GF_STA_present); + !priv->current_ht_config.non_GF_STA_present; } #endif static inline u8 rs_use_green(struct iwl_priv *priv, struct ieee80211_conf *conf) @@ -2157,7 +2157,7 @@ static void *rs_alloc_sta(void *priv_rate, gfp_t gfp) for (j = 0; j < LQ_SIZE; j++) for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&(lq_sta->lq_info[j].win[i])); + rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); return lq_sta; } @@ -2179,7 +2179,7 @@ static void rs_rate_init(void *priv_rate, void *priv_sta, sta->txrate_idx = 3; for (j = 0; j < LQ_SIZE; j++) for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&(lq_sta->lq_info[j].win[i])); + rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); IWL_DEBUG_RATE("LQ: *** rate scale global init ***\n"); /* TODO: what is a good starting rate for STA? About middle? Maybe not diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index f71b3f3f81ba..4ff0636dbddc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2476,7 +2476,7 @@ static void iwl4965_post_associate(struct iwl_priv *priv) unsigned long flags; if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { - IWL_ERROR("%s Should not be called in AP mode\n", __FUNCTION__); + IWL_ERROR("%s Should not be called in AP mode\n", __func__); return; } @@ -2552,7 +2552,7 @@ static void iwl4965_post_associate(struct iwl_priv *priv) default: IWL_ERROR("%s Should not be called in %d mode\n", - __FUNCTION__, priv->iw_mode); + __func__, priv->iw_mode); break; } @@ -3794,7 +3794,7 @@ static ssize_t show_measurement(struct device *d, struct iwl_priv *priv = dev_get_drvdata(d); struct iwl4965_spectrum_notification measure_report; u32 size = sizeof(measure_report), len = 0, ofs = 0; - u8 *data = (u8 *) & measure_report; + u8 *data = (u8 *)&measure_report; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); @@ -3934,7 +3934,7 @@ static ssize_t show_power_level(struct device *d, p += sprintf(p, "\tMODE:%s", (mode < IWL_POWER_AUTO)?"fixed":"auto"); p += sprintf(p, "\tINDEX:%d", level); p += sprintf(p, "\n"); - return (p - buf + 1); + return p - buf + 1; } static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, show_power_level, @@ -4009,7 +4009,7 @@ static ssize_t show_statistics(struct device *d, struct iwl_priv *priv = dev_get_drvdata(d); u32 size = sizeof(struct iwl_notif_statistics); u32 len = 0, ofs = 0; - u8 *data = (u8 *) & priv->statistics; + u8 *data = (u8 *)&priv->statistics; int rc = 0; if (!iwl_is_alive(priv)) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 96beacf703a6..9bd61809129f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -383,8 +383,8 @@ void iwl_reset_qos(struct iwl_priv *priv) } EXPORT_SYMBOL(iwl_reset_qos); -#define MAX_BIT_RATE_40_MHZ 0x96; /* 150 Mbps */ -#define MAX_BIT_RATE_20_MHZ 0x48; /* 72 Mbps */ +#define MAX_BIT_RATE_40_MHZ 0x96 /* 150 Mbps */ +#define MAX_BIT_RATE_20_MHZ 0x48 /* 72 Mbps */ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv, struct ieee80211_ht_info *ht_info, enum ieee80211_band band) diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index d6d729e86bdb..b4ffd33ef98c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -33,12 +33,12 @@ #define IWL_DEBUG(level, fmt, args...) \ do { if (priv->debug_level & (level)) \ dev_printk(KERN_ERR, &(priv->hw->wiphy->dev), "%c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0) + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) #define IWL_DEBUG_LIMIT(level, fmt, args...) \ do { if ((priv->debug_level & (level)) && net_ratelimit()) \ dev_printk(KERN_ERR, &(priv->hw->wiphy->dev), "%c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0) + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) #ifdef CONFIG_IWLWIFI_DEBUGFS struct iwl_debugfs { diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c index ed948dc59b3d..20db0eb636a8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c @@ -231,7 +231,7 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, DECLARE_MAC_BUF(mac); buf = kmalloc(bufsz, GFP_KERNEL); - if(!buf) + if (!buf) return -ENOMEM; pos += scnprintf(buf + pos, bufsz - pos, "num of stations: %d\n\n", @@ -364,16 +364,19 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) { struct iwl_debugfs *dbgfs; struct dentry *phyd = priv->hw->wiphy->debugfsdir; + int ret = 0; dbgfs = kzalloc(sizeof(struct iwl_debugfs), GFP_KERNEL); if (!dbgfs) { + ret = -ENOMEM; goto err; } priv->dbgfs = dbgfs; dbgfs->name = name; dbgfs->dir_drv = debugfs_create_dir(name, phyd); - if (!dbgfs->dir_drv || IS_ERR(dbgfs->dir_drv)){ + if (!dbgfs->dir_drv || IS_ERR(dbgfs->dir_drv)) { + ret = -ENOENT; goto err; } @@ -394,7 +397,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) err: IWL_ERROR("Can't open the debugfs directory\n"); iwl_dbgfs_unregister(priv); - return -ENOENT; + return ret; } EXPORT_SYMBOL(iwl_dbgfs_register); @@ -404,7 +407,7 @@ EXPORT_SYMBOL(iwl_dbgfs_register); */ void iwl_dbgfs_unregister(struct iwl_priv *priv) { - if (!(priv->dbgfs)) + if (!priv->dbgfs) return; DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_eeprom); diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index 4a08a1b50979..bce53830b301 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -273,8 +273,7 @@ EXPORT_SYMBOL(iwl_eeprom_init); void iwl_eeprom_free(struct iwl_priv *priv) { - if(priv->eeprom) - kfree(priv->eeprom); + kfree(priv->eeprom); priv->eeprom = NULL; } EXPORT_SYMBOL(iwl_eeprom_free); diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index 10af82170f6b..60a6e0106036 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -823,7 +823,7 @@ int iwl_send_lq_cmd(struct iwl_priv *priv, if (lq->sta_id == 0xFF) lq->sta_id = IWL_AP_ID; - iwl_dump_lq_cmd(priv,lq); + iwl_dump_lq_cmd(priv, lq); if (iwl_is_associated(priv) && priv->assoc_station_added) return iwl_send_cmd(priv, &cmd); diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 7c82ecfa30a4..eb41b02b506d 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -275,10 +275,8 @@ static int iwl3945_tx_queue_alloc(struct iwl3945_priv *priv, return 0; error: - if (txq->txb) { - kfree(txq->txb); - txq->txb = NULL; - } + kfree(txq->txb); + txq->txb = NULL; return -ENOMEM; } @@ -365,10 +363,8 @@ void iwl3945_tx_queue_free(struct iwl3945_priv *priv, struct iwl3945_tx_queue *t txq->q.n_bd, txq->bd, txq->q.dma_addr); /* De-alloc array of per-TFD driver data */ - if (txq->txb) { - kfree(txq->txb); - txq->txb = NULL; - } + kfree(txq->txb); + txq->txb = NULL; /* 0-fill queue descriptor structure */ memset(txq, 0, sizeof(*txq)); @@ -2703,9 +2699,8 @@ static int iwl3945_tx_skb(struct iwl3945_priv *priv, struct sk_buff *skb) if (!ieee80211_has_morefrags(hdr->frame_control)) { txq->need_update = 1; - if (qc) { + if (qc) priv->stations[sta_id].tid[tid].seq_number = seq_number; - } } else { wait_write_ptr = 1; txq->need_update = 0; @@ -3813,7 +3808,7 @@ int iwl3945_calc_db_from_ratio(int sig_ratio) /* 100:1 or higher, divide by 10 and use table, * add 20 dB to make up for divide by 10 */ if (sig_ratio >= 100) - return (20 + (int)ratio2dB[sig_ratio/10]); + return 20 + (int)ratio2dB[sig_ratio/10]; /* We shouldn't see this */ if (sig_ratio < 1) @@ -5088,7 +5083,7 @@ static void iwl3945_dealloc_ucode_pci(struct iwl3945_priv *priv) * iwl3945_verify_inst_full - verify runtime uCode image in card vs. host, * looking at all data. */ -static int iwl3945_verify_inst_full(struct iwl3945_priv *priv, __le32 * image, u32 len) +static int iwl3945_verify_inst_full(struct iwl3945_priv *priv, __le32 *image, u32 len) { u32 val; u32 save_len = len; @@ -5237,7 +5232,7 @@ static int iwl3945_verify_bsm(struct iwl3945_priv *priv) val = iwl3945_read_prph(priv, BSM_WR_DWCOUNT_REG); for (reg = BSM_SRAM_LOWER_BOUND; reg < BSM_SRAM_LOWER_BOUND + len; - reg += sizeof(u32), image ++) { + reg += sizeof(u32), image++) { val = iwl3945_read_prph(priv, reg); if (val != le32_to_cpu(*image)) { IWL_ERROR("BSM uCode verification failed at " @@ -6336,7 +6331,7 @@ static void iwl3945_bg_post_associate(struct work_struct *data) DECLARE_MAC_BUF(mac); if (priv->iw_mode == IEEE80211_IF_TYPE_AP) { - IWL_ERROR("%s Should not be called in AP mode\n", __FUNCTION__); + IWL_ERROR("%s Should not be called in AP mode\n", __func__); return; } @@ -6417,7 +6412,7 @@ static void iwl3945_bg_post_associate(struct work_struct *data) default: IWL_ERROR("%s Should not be called in %d mode\n", - __FUNCTION__, priv->iw_mode); + __func__, priv->iw_mode); break; } @@ -7456,7 +7451,7 @@ static ssize_t show_measurement(struct device *d, struct iwl3945_priv *priv = dev_get_drvdata(d); struct iwl3945_spectrum_notification measure_report; u32 size = sizeof(measure_report), len = 0, ofs = 0; - u8 *data = (u8 *) & measure_report; + u8 *data = (u8 *)&measure_report; unsigned long flags; spin_lock_irqsave(&priv->lock, flags); @@ -7627,7 +7622,7 @@ static ssize_t show_power_level(struct device *d, else p += sprintf(p, " \n"); - return (p - buf + 1); + return p - buf + 1; } @@ -7649,7 +7644,7 @@ static ssize_t show_statistics(struct device *d, struct iwl3945_priv *priv = dev_get_drvdata(d); u32 size = sizeof(struct iwl3945_notif_statistics); u32 len = 0, ofs = 0; - u8 *data = (u8 *) & priv->statistics; + u8 *data = (u8 *)&priv->statistics; int rc = 0; if (!iwl3945_is_alive(priv)) @@ -8003,16 +7998,16 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e /* nic init */ iwl3945_set_bit(priv, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); - - iwl3945_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); - err = iwl3945_poll_bit(priv, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, - CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); - if (err < 0) { - IWL_DEBUG_INFO("Failed to init the card\n"); + CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); + + iwl3945_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + err = iwl3945_poll_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); + if (err < 0) { + IWL_DEBUG_INFO("Failed to init the card\n"); goto out_remove_sysfs; - } + } /* Read the EEPROM */ err = iwl3945_eeprom_init(priv); if (err) { @@ -8114,9 +8109,8 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev) iwl3945_unset_hw_setting(priv); iwl3945_clear_stations_table(priv); - if (priv->mac80211_registered) { + if (priv->mac80211_registered) ieee80211_unregister_hw(priv->hw); - } /*netif_stop_queue(dev); */ flush_workqueue(priv->workqueue); -- cgit v1.2.1 From 4fc22b21b3fcb3580c32b70605ef114178f8e611 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 21 Jul 2008 18:54:42 +0300 Subject: iwlwifi: rename 4965 to AGN This patch renames driver name from 4965 to AGN The driver supports both 4965AGN and 5000AGN family The driver's original module name iwl4965.ko remains as an alias Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/Kconfig | 98 ++++++++++++++++++---------------- drivers/net/wireless/iwlwifi/Makefile | 11 ++-- drivers/net/wireless/iwlwifi/iwl-agn.c | 15 +++--- drivers/net/wireless/iwlwifi/iwl-dev.h | 7 +-- 4 files changed, 68 insertions(+), 63 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 82b66a3d3a5d..b0ac0ce3fb9f 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -14,18 +14,49 @@ config IWLWIFI_LEDS default n config IWLWIFI_RFKILL - boolean "IWLWIFI RF kill support" + boolean "Iwlwifi RF kill support" depends on IWLCORE -config IWL4965 - tristate "Intel Wireless WiFi 4965AGN" +config IWLWIFI_DEBUG + bool "Enable full debugging output in iwlagn driver" + depends on IWLCORE + ---help--- + This option will enable debug tracing output for the iwlwifi drivers + + This will result in the kernel module being ~100k larger. You can + control which debug output is sent to the kernel log by setting the + value in + + /sys/class/net/wlan0/device/debug_level + + This entry will only exist if this option is enabled. + + To set a value, simply echo an 8-byte hex value to the same file: + + % echo 0x43fff > /sys/class/net/wlan0/device/debug_level + + You can find the list of debug mask values in: + drivers/net/wireless/iwlwifi/iwl-debug.h + + If this is your first time using this driver, you should say Y here + as the debug information can assist others in helping you resolve + any problems you may encounter. + +config IWLWIFI_DEBUGFS + bool "Iwlwifi debugfs support" + depends on IWLCORE && IWLWIFI_DEBUG && MAC80211_DEBUGFS + ---help--- + Enable creation of debugfs files for the iwlwifi drivers. + +config IWLAGN + tristate "Intel Wireless WiFi Next Gen AGN" depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL select FW_LOADER select IWLCORE ---help--- Select to build the driver supporting the: - Intel Wireless WiFi Link 4965AGN + Intel Wireless WiFi Link Next-Gen AGN This driver uses the kernel's mac80211 subsystem. @@ -42,60 +73,33 @@ config IWL4965 If you want to compile the driver as a module ( = code which can be inserted in and removed from the running kernel whenever you want), say M here and read . The - module will be called iwl4965.ko. - -config IWL4965_LEDS - bool "Enable LEDS features in iwl4965 driver" - depends on IWL4965 - select IWLWIFI_LEDS - ---help--- - This option enables LEDS for the iwlwifi drivers + module will be called iwlagn.ko. - -config IWL4965_SPECTRUM_MEASUREMENT - bool "Enable Spectrum Measurement in iwl4965 driver" - depends on IWL4965 +config IWLAGN_SPECTRUM_MEASUREMENT + bool "Enable Spectrum Measurement in iwlagn driver" + depends on IWLAGN ---help--- - This option will enable spectrum measurement for the iwl4965 driver. + This option will enable spectrum measurement for the iwlagn driver. -config IWLWIFI_DEBUG - bool "Enable full debugging output in iwl4965 driver" - depends on IWL4965 +config IWLAGN_LEDS + bool "Enable LEDS features in iwlagn driver" + depends on IWLAGN + select IWLWIFI_LEDS ---help--- - This option will enable debug tracing output for the iwl4965 - driver. - - This will result in the kernel module being ~100k larger. You can - control which debug output is sent to the kernel log by setting the - value in - - /sys/class/net/wlan0/device/debug_level - - This entry will only exist if this option is enabled. - - To set a value, simply echo an 8-byte hex value to the same file: - - % echo 0x43fff > /sys/class/net/wlan0/device/debug_level + This option enables LEDS for the iwlagn drivers - You can find the list of debug mask values in: - drivers/net/wireless/iwlwifi/iwl-4965-debug.h - If this is your first time using this driver, you should say Y here - as the debug information can assist others in helping you resolve - any problems you may encounter. +config IWL4965 + bool "Intel Wireless WiFi 4965AGN" + depends on IWLAGN + ---help--- + This option enables support for Intel Wireless WiFi Link 4965AGN config IWL5000 bool "Intel Wireless WiFi 5000AGN" - depends on IWL4965 + depends on IWLAGN ---help--- This option enables support for Intel Wireless WiFi Link 5000AGN Family - Dependency on 4965 is temporary - -config IWLWIFI_DEBUGFS - bool "Iwlwifi debugfs support" - depends on IWLCORE && IWLWIFI_DEBUG && MAC80211_DEBUGFS - ---help--- - Enable creation of debugfs files for the iwlwifi drivers. config IWL3945 tristate "Intel PRO/Wireless 3945ABG/BG Network Connection" diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 6bf3998736b5..47aa28f6a513 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -6,13 +6,14 @@ iwlcore-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o iwlcore-$(CONFIG_IWLWIFI_LEDS) += iwl-led.o iwlcore-$(CONFIG_IWLWIFI_RFKILL) += iwl-rfkill.o +obj-$(CONFIG_IWLAGN) += iwlagn.o +iwlagn-objs := iwl-agn.o iwl-agn-rs.o + +iwlagn-$(CONFIG_IWL4965) += iwl-4965.o +iwlagn-$(CONFIG_IWL5000) += iwl-5000.o + obj-$(CONFIG_IWL3945) += iwl3945.o iwl3945-objs := iwl3945-base.o iwl-3945.o iwl-3945-rs.o iwl3945-$(CONFIG_IWL3945_LEDS) += iwl-3945-led.o -obj-$(CONFIG_IWL4965) += iwl4965.o -iwl4965-objs := iwl-agn.o iwl-4965.o iwl-agn-rs.o - -iwl4965-$(CONFIG_IWL5000) += iwl-5000.o - diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 4ff0636dbddc..6503b3ac8d66 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -73,7 +73,7 @@ #define VD #endif -#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT +#ifdef CONFIG_IWLAGN_SPECTRUM_MEASUREMENT #define VS "s" #else #define VS @@ -86,6 +86,7 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT); MODULE_LICENSE("GPL"); +MODULE_ALIAS("iwl4965"); /*************** STATION TABLE MANAGEMENT **** * mac80211 should be examined to determine if sta_info is duplicating @@ -883,7 +884,7 @@ static void iwl4965_set_rate(struct iwl_priv *priv) (IWL_OFDM_BASIC_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF; } -#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT +#ifdef CONFIG_IWLAGN_SPECTRUM_MEASUREMENT #include "iwl-spectrum.h" @@ -1087,7 +1088,7 @@ static void iwl4965_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) static void iwl4965_rx_spectrum_measure_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { -#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT +#ifdef CONFIG_IWLAGN_SPECTRUM_MEASUREMENT struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; struct iwl4965_spectrum_notification *report = &(pkt->u.spectrum_notif); @@ -3786,7 +3787,7 @@ static ssize_t store_filter_flags(struct device *d, static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, show_filter_flags, store_filter_flags); -#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT +#ifdef CONFIG_IWLAGN_SPECTRUM_MEASUREMENT static ssize_t show_measurement(struct device *d, struct device_attribute *attr, char *buf) @@ -3857,7 +3858,7 @@ static ssize_t store_measurement(struct device *d, static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR, show_measurement, store_measurement); -#endif /* CONFIG_IWL4965_SPECTRUM_MEASUREMENT */ +#endif /* CONFIG_IWLAGN_SPECTRUM_MEASUREMENT */ static ssize_t store_retry_rate(struct device *d, struct device_attribute *attr, @@ -4105,7 +4106,7 @@ static struct attribute *iwl4965_sysfs_entries[] = { &dev_attr_channels.attr, &dev_attr_flags.attr, &dev_attr_filter_flags.attr, -#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT +#ifdef CONFIG_IWLAGN_SPECTRUM_MEASUREMENT &dev_attr_measurement.attr, #endif &dev_attr_power_level.attr, @@ -4457,8 +4458,10 @@ static int iwl4965_pci_resume(struct pci_dev *pdev) /* Hardware specific file defines the PCI IDs table for that hardware module */ static struct pci_device_id iwl_hw_card_ids[] = { +#ifdef CONFIG_IWL4965 {IWL_PCI_DEVICE(0x4229, PCI_ANY_ID, iwl4965_agn_cfg)}, {IWL_PCI_DEVICE(0x4230, PCI_ANY_ID, iwl4965_agn_cfg)}, +#endif /* CONFIG_IWL4965 */ #ifdef CONFIG_IWL5000 {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bg_cfg)}, {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bg_cfg)}, diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 7ac56b1350b7..848786ab7916 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -36,7 +36,7 @@ #include #include -#define DRV_NAME "iwl4965" +#define DRV_NAME "iwlagn" #include "iwl-rfkill.h" #include "iwl-eeprom.h" #include "iwl-4965-hw.h" @@ -808,14 +808,11 @@ struct iwl_chain_noise_data { #define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ -#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT - enum { MEASUREMENT_READY = (1 << 0), MEASUREMENT_ACTIVE = (1 << 1), }; -#endif #define IWL_MAX_NUM_QUEUES 20 /* FIXME: do dynamic allocation */ @@ -840,7 +837,7 @@ struct iwl_priv { struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; -#ifdef CONFIG_IWL4965_SPECTRUM_MEASUREMENT +#ifdef CONFIG_IWLAGN_SPECTRUM_MEASUREMENT /* spectrum measurement report caching */ struct iwl4965_spectrum_notification measure_report; u8 measurement_status; -- cgit v1.2.1 From 5cbbb376d65ed181ed290cea505ba37a0425ee25 Mon Sep 17 00:00:00 2001 From: Sven Wegener Date: Fri, 1 Aug 2008 21:57:16 +0200 Subject: iwlwifi: Don't use buffer allocated on the stack for led names Having the buffer on the stack and even re-using it for all led devices is bad. Not being able to resolve the name member of the led device structure to a meaningful value leads to confusion during ad-hoc debugging and potential breakage in the future, if we ever decide to access the name member outside of the registration function. Move the buffer to our private per led device structures so that it is accessible after registration. A quick grep didn't yield any occurence of using the led device name parameter outside of the led device registration function, so currently we should already be safe for normal operation. Signed-off-by: Sven Wegener Cc: Richard Purdie Acked-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-3945-led.c | 33 +++++++++++++++-------------- drivers/net/wireless/iwlwifi/iwl-3945-led.h | 1 + drivers/net/wireless/iwlwifi/iwl-led.c | 29 ++++++++++++++----------- drivers/net/wireless/iwlwifi/iwl-led.h | 1 + 4 files changed, 36 insertions(+), 28 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.c b/drivers/net/wireless/iwlwifi/iwl-3945-led.c index 6be1fe13fa57..d3336966b6b5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.c @@ -206,12 +206,12 @@ static void iwl3945_led_brightness_set(struct led_classdev *led_cdev, static int iwl3945_led_register_led(struct iwl3945_priv *priv, struct iwl3945_led *led, enum led_type type, u8 set_led, - const char *name, char *trigger) + char *trigger) { struct device *device = wiphy_dev(priv->hw->wiphy); int ret; - led->led_dev.name = name; + led->led_dev.name = led->name; led->led_dev.brightness_set = iwl3945_led_brightness_set; led->led_dev.default_trigger = trigger; @@ -308,7 +308,6 @@ void iwl3945_led_background(struct iwl3945_priv *priv) int iwl3945_led_register(struct iwl3945_priv *priv) { char *trigger; - char name[32]; int ret; priv->last_blink_rate = 0; @@ -318,7 +317,8 @@ int iwl3945_led_register(struct iwl3945_priv *priv) priv->allow_blinking = 0; trigger = ieee80211_get_radio_led_name(priv->hw); - snprintf(name, sizeof(name), "iwl-%s:radio", + snprintf(priv->led[IWL_LED_TRG_RADIO].name, + sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s:radio", wiphy_name(priv->hw->wiphy)); priv->led[IWL_LED_TRG_RADIO].led_on = iwl3945_led_on; @@ -327,19 +327,20 @@ int iwl3945_led_register(struct iwl3945_priv *priv) ret = iwl3945_led_register_led(priv, &priv->led[IWL_LED_TRG_RADIO], - IWL_LED_TRG_RADIO, 1, - name, trigger); + IWL_LED_TRG_RADIO, 1, trigger); + if (ret) goto exit_fail; trigger = ieee80211_get_assoc_led_name(priv->hw); - snprintf(name, sizeof(name), "iwl-%s:assoc", + snprintf(priv->led[IWL_LED_TRG_ASSOC].name, + sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s:assoc", wiphy_name(priv->hw->wiphy)); ret = iwl3945_led_register_led(priv, &priv->led[IWL_LED_TRG_ASSOC], - IWL_LED_TRG_ASSOC, 0, - name, trigger); + IWL_LED_TRG_ASSOC, 0, trigger); + /* for assoc always turn led on */ priv->led[IWL_LED_TRG_ASSOC].led_on = iwl3945_led_on; priv->led[IWL_LED_TRG_ASSOC].led_off = iwl3945_led_on; @@ -349,14 +350,13 @@ int iwl3945_led_register(struct iwl3945_priv *priv) goto exit_fail; trigger = ieee80211_get_rx_led_name(priv->hw); - snprintf(name, sizeof(name), "iwl-%s:RX", + snprintf(priv->led[IWL_LED_TRG_RX].name, + sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s:RX", wiphy_name(priv->hw->wiphy)); - ret = iwl3945_led_register_led(priv, &priv->led[IWL_LED_TRG_RX], - IWL_LED_TRG_RX, 0, - name, trigger); + IWL_LED_TRG_RX, 0, trigger); priv->led[IWL_LED_TRG_RX].led_on = iwl3945_led_associated; priv->led[IWL_LED_TRG_RX].led_off = iwl3945_led_associated; @@ -366,13 +366,14 @@ int iwl3945_led_register(struct iwl3945_priv *priv) goto exit_fail; trigger = ieee80211_get_tx_led_name(priv->hw); - snprintf(name, sizeof(name), "iwl-%s:TX", + snprintf(priv->led[IWL_LED_TRG_TX].name, + sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s:TX", wiphy_name(priv->hw->wiphy)); ret = iwl3945_led_register_led(priv, &priv->led[IWL_LED_TRG_TX], - IWL_LED_TRG_TX, 0, - name, trigger); + IWL_LED_TRG_TX, 0, trigger); + priv->led[IWL_LED_TRG_TX].led_on = iwl3945_led_associated; priv->led[IWL_LED_TRG_TX].led_off = iwl3945_led_associated; priv->led[IWL_LED_TRG_TX].led_pattern = iwl3945_led_pattern; diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.h b/drivers/net/wireless/iwlwifi/iwl-3945-led.h index 47b7e0bac802..2fbd126c1347 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-led.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.h @@ -50,6 +50,7 @@ enum led_type { struct iwl3945_led { struct iwl3945_priv *priv; struct led_classdev led_dev; + char name[32]; int (*led_on) (struct iwl3945_priv *priv, int led_id); int (*led_off) (struct iwl3945_priv *priv, int led_id); diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c index 0a01f091c516..cb11c4a4d691 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-led.c @@ -242,12 +242,12 @@ static void iwl_led_brightness_set(struct led_classdev *led_cdev, */ static int iwl_leds_register_led(struct iwl_priv *priv, struct iwl_led *led, enum led_type type, u8 set_led, - const char *name, char *trigger) + char *trigger) { struct device *device = wiphy_dev(priv->hw->wiphy); int ret; - led->led_dev.name = name; + led->led_dev.name = led->name; led->led_dev.brightness_set = iwl_led_brightness_set; led->led_dev.default_trigger = trigger; @@ -345,7 +345,6 @@ EXPORT_SYMBOL(iwl_leds_background); int iwl_leds_register(struct iwl_priv *priv) { char *trigger; - char name[32]; int ret; priv->last_blink_rate = 0; @@ -354,7 +353,8 @@ int iwl_leds_register(struct iwl_priv *priv) priv->allow_blinking = 0; trigger = ieee80211_get_radio_led_name(priv->hw); - snprintf(name, sizeof(name), "iwl-%s:radio", + snprintf(priv->led[IWL_LED_TRG_RADIO].name, + sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s:radio", wiphy_name(priv->hw->wiphy)); priv->led[IWL_LED_TRG_RADIO].led_on = iwl4965_led_on_reg; @@ -362,16 +362,17 @@ int iwl_leds_register(struct iwl_priv *priv) priv->led[IWL_LED_TRG_RADIO].led_pattern = NULL; ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_RADIO], - IWL_LED_TRG_RADIO, 1, name, trigger); + IWL_LED_TRG_RADIO, 1, trigger); if (ret) goto exit_fail; trigger = ieee80211_get_assoc_led_name(priv->hw); - snprintf(name, sizeof(name), "iwl-%s:assoc", + snprintf(priv->led[IWL_LED_TRG_ASSOC].name, + sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s:assoc", wiphy_name(priv->hw->wiphy)); ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_ASSOC], - IWL_LED_TRG_ASSOC, 0, name, trigger); + IWL_LED_TRG_ASSOC, 0, trigger); /* for assoc always turn led on */ priv->led[IWL_LED_TRG_ASSOC].led_on = iwl_led_associate; @@ -382,11 +383,12 @@ int iwl_leds_register(struct iwl_priv *priv) goto exit_fail; trigger = ieee80211_get_rx_led_name(priv->hw); - snprintf(name, sizeof(name), "iwl-%s:RX", wiphy_name(priv->hw->wiphy)); - + snprintf(priv->led[IWL_LED_TRG_RX].name, + sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s:RX", + wiphy_name(priv->hw->wiphy)); ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_RX], - IWL_LED_TRG_RX, 0, name, trigger); + IWL_LED_TRG_RX, 0, trigger); priv->led[IWL_LED_TRG_RX].led_on = iwl_led_associated; priv->led[IWL_LED_TRG_RX].led_off = iwl_led_associated; @@ -396,9 +398,12 @@ int iwl_leds_register(struct iwl_priv *priv) goto exit_fail; trigger = ieee80211_get_tx_led_name(priv->hw); - snprintf(name, sizeof(name), "iwl-%s:TX", wiphy_name(priv->hw->wiphy)); + snprintf(priv->led[IWL_LED_TRG_TX].name, + sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s:TX", + wiphy_name(priv->hw->wiphy)); + ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_TX], - IWL_LED_TRG_TX, 0, name, trigger); + IWL_LED_TRG_TX, 0, trigger); priv->led[IWL_LED_TRG_TX].led_on = iwl_led_associated; priv->led[IWL_LED_TRG_TX].led_off = iwl_led_associated; diff --git a/drivers/net/wireless/iwlwifi/iwl-led.h b/drivers/net/wireless/iwlwifi/iwl-led.h index 1980ae5a7e82..588c9ad20e83 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.h +++ b/drivers/net/wireless/iwlwifi/iwl-led.h @@ -52,6 +52,7 @@ enum led_type { struct iwl_led { struct iwl_priv *priv; struct led_classdev led_dev; + char name[32]; int (*led_on) (struct iwl_priv *priv, int led_id); int (*led_off) (struct iwl_priv *priv, int led_id); -- cgit v1.2.1 From d06193f311102b2c990ec5f66b470ea49ecc73a4 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Sun, 3 Aug 2008 23:36:01 +0200 Subject: rt2x00: Disable link tuning in rt2500usb In the legacy rt2570 driver the link tuner was never really called. And now the reason has finally become apparent: It breaks TX capabilities As soon as the device has been associated all following TX frames will be queued in the hardware and never transmitted to the air. Disabling sections of the link tuner did not have the expected result, but completely disabling the link tuner did have the right result (Both of my rt2570 devices came back to life). This should fix Fedora bug: 411481 v2: Fix typos Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2500usb.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index c6f6eb6e17a1..cd5af656932d 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -633,6 +633,16 @@ static void rt2500usb_reset_tuner(struct rt2x00_dev *rt2x00dev) rt2x00dev->link.vgc_level = value; } +/* + * NOTE: This function is directly ported from legacy driver, but + * despite it being declared it was never called. Although link tuning + * sounds like a good idea, and usually works well for the other drivers, + * it does _not_ work with rt2500usb. Enabling this function will result + * in TX capabilities only until association kicks in. Immediately + * after the successful association all TX frames will be kept in the + * hardware queue and never transmitted. + */ +#if 0 static void rt2500usb_link_tuner(struct rt2x00_dev *rt2x00dev) { int rssi = rt2x00_get_link_rssi(&rt2x00dev->link); @@ -752,6 +762,9 @@ dynamic_cca_tune: rt2x00dev->link.vgc_level = r17; } } +#else +#define rt2500usb_link_tuner NULL +#endif /* * Initialization functions. @@ -1737,6 +1750,7 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev) __set_bit(DRIVER_REQUIRE_ATIM_QUEUE, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_BEACON_GUARD, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags); + __set_bit(CONFIG_DISABLE_LINK_TUNING, &rt2x00dev->flags); /* * Set the rssi offset. -- cgit v1.2.1 From 6041e2a08c50e3fcaf1e56422bfafda62c597cea Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 3 Aug 2008 17:58:36 -0500 Subject: p54: Fix potential concurrent access to private data Experience with the rtl8187 driver has shown that mac80211 can make calls to the config callback routine in rapid succession. This patch creates a mutex that protects the private data in several of the routines called by mac80211. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54.h | 1 + drivers/net/wireless/p54/p54common.c | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h index c6f27b9022f9..cac9a515b82d 100644 --- a/drivers/net/wireless/p54/p54.h +++ b/drivers/net/wireless/p54/p54.h @@ -52,6 +52,7 @@ struct p54_common { int (*open)(struct ieee80211_hw *dev); void (*stop)(struct ieee80211_hw *dev); int mode; + struct mutex conf_mutex; u8 mac_addr[ETH_ALEN]; u8 bssid[ETH_ALEN]; struct pda_iq_autocal_entry *iq_autocal; diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index ffaf7a6b6810..4da89ea9b561 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -886,9 +886,12 @@ static void p54_remove_interface(struct ieee80211_hw *dev, static int p54_config(struct ieee80211_hw *dev, struct ieee80211_conf *conf) { int ret; + struct p54_common *priv = dev->priv; + mutex_lock(&priv->conf_mutex); ret = p54_set_freq(dev, cpu_to_le16(conf->channel->center_freq)); p54_set_vdcf(dev); + mutex_unlock(&priv->conf_mutex); return ret; } @@ -898,10 +901,12 @@ static int p54_config_interface(struct ieee80211_hw *dev, { struct p54_common *priv = dev->priv; + mutex_lock(&priv->conf_mutex); p54_set_filter(dev, 0, priv->mac_addr, conf->bssid, 0, 1, 0, 0xF642); p54_set_filter(dev, 0, priv->mac_addr, conf->bssid, 2, 0, 0, 0); p54_set_leds(dev, 1, !is_multicast_ether_addr(conf->bssid), 0); memcpy(priv->bssid, conf->bssid, ETH_ALEN); + mutex_unlock(&priv->conf_mutex); return 0; } @@ -1009,6 +1014,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) } p54_init_vdcf(dev); + mutex_init(&priv->conf_mutex); return dev; } -- cgit v1.2.1 From 4c43e0d0ecd5196ed5c67f64ed2f1860770eed34 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 4 Aug 2008 16:00:39 +0800 Subject: iwlwifi: HW bug fixes This patch adds few HW bug fixes. Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-5000.c | 15 +++++++++++++++ drivers/net/wireless/iwlwifi/iwl-csr.h | 10 +++++++++- drivers/net/wireless/iwlwifi/iwl-prph.h | 12 +++++++----- 3 files changed, 31 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index f7bbd12193f9..1d793c093f1a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -93,6 +93,13 @@ static int iwl5000_apm_init(struct iwl_priv *priv) iwl_set_bit(priv, CSR_GIO_CHICKEN_BITS, CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); + /* Set FH wait treshold to maximum (HW error during stress W/A) */ + iwl_set_bit(priv, CSR_DBG_HPET_MEM_REG, CSR_DBG_HPET_MEM_REG_VAL); + + /* enable HAP INTA to move device L1a -> L0s */ + iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); + iwl_set_bit(priv, CSR_ANA_PLL_CFG, CSR50_ANA_PLL_CFG_VAL); /* set "initialization complete" bit to move adapter @@ -230,6 +237,14 @@ static void iwl5000_nic_config(struct iwl_priv *priv) CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); + /* W/A : NIC is stuck in a reset state after Early PCIe power off + * (PCIe power is lost before PERST# is asserted), + * causing ME FW to lose ownership and not being able to obtain it back. + */ + iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, + ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); + spin_unlock_irqrestore(&priv->lock, flags); } diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index 545ed692d889..52629fbd835a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -104,6 +104,7 @@ * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step */ #define CSR_HW_REV_WA_REG (CSR_BASE+0x22C) +#define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240) /* Bits for CSR_HW_IF_CONFIG_REG */ #define CSR49_HW_IF_CONFIG_REG_BIT_4965_R (0x00000010) @@ -118,7 +119,12 @@ #define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A (0x00000000) #define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B (0x00001000) -#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) +#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000) +#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) +#define CSR_HW_IF_CONFIG_REG_BIT_PCI_OWN_SEM (0x00400000) +#define CSR_HW_IF_CONFIG_REG_BIT_ME_OWN (0x02000000) +#define CSR_HW_IF_CONFIG_REG_BIT_WAKE_ME (0x08000000) + /* interrupt flags in INTA, set by uCode or hardware (e.g. dma), * acknowledged (reset) by host writing "1" to flagged bits. */ @@ -236,6 +242,8 @@ #define CSR39_ANA_PLL_CFG_VAL (0x01000000) #define CSR50_ANA_PLL_CFG_VAL (0x00880300) +/* HPET MEM debug */ +#define CSR_DBG_HPET_MEM_REG_VAL (0xFFFF0000) /*=== HBUS (Host-side Bus) ===*/ #define HBUS_BASE (0x400) /* diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 70d9c7568b98..ee5afd48d3af 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -84,14 +84,16 @@ #define APMG_CLK_VAL_DMA_CLK_RQT (0x00000200) #define APMG_CLK_VAL_BSM_CLK_RQT (0x00000800) -#define APMG_PS_CTRL_VAL_RESET_REQ (0x04000000) -#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) +#define APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS (0x00400000) +#define APMG_PS_CTRL_VAL_RESET_REQ (0x04000000) +#define APMG_PS_CTRL_MSK_PWR_SRC (0x03000000) +#define APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000) +#define APMG_PS_CTRL_VAL_PWR_SRC_MAX (0x01000000) /* 3945 only */ +#define APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x02000000) -#define APMG_PS_CTRL_MSK_PWR_SRC (0x03000000) -#define APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000) -#define APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x01000000) +#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) /** * BSM (Bootstrap State Machine) -- cgit v1.2.1 From da99c4b6c25964b90c79f19beccda208df1a865a Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Mon, 4 Aug 2008 16:00:40 +0800 Subject: iwlwifi: memory allocation optimization This patch optimizes memory allocation. The cmd member of iwl_tx_queue was allocated previously as a continuous block of memory. This patch allocates separate memory chunks for each command and maps/unmaps these chunks in the run time. Signed-off-by: Gregory Greenman Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-5000.c | 6 +-- drivers/net/wireless/iwlwifi/iwl-dev.h | 3 +- drivers/net/wireless/iwlwifi/iwl-hcmd.c | 2 +- drivers/net/wireless/iwlwifi/iwl-tx.c | 76 +++++++++++++++++++++++---------- 4 files changed, 59 insertions(+), 28 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 1d793c093f1a..56dbc8144a34 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -939,8 +939,8 @@ static void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv, len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE; if (txq_id != IWL_CMD_QUEUE_NUM) { - sta = txq->cmd[txq->q.write_ptr].cmd.tx.sta_id; - sec_ctl = txq->cmd[txq->q.write_ptr].cmd.tx.sec_ctl; + sta = txq->cmd[txq->q.write_ptr]->cmd.tx.sta_id; + sec_ctl = txq->cmd[txq->q.write_ptr]->cmd.tx.sec_ctl; switch (sec_ctl & TX_CMD_SEC_MSK) { case TX_CMD_SEC_CCM: @@ -979,7 +979,7 @@ static void iwl5000_txq_inval_byte_cnt_tbl(struct iwl_priv *priv, u8 sta = 0; if (txq_id != IWL_CMD_QUEUE_NUM) - sta = txq->cmd[txq->q.read_ptr].cmd.tx.sta_id; + sta = txq->cmd[txq->q.read_ptr]->cmd.tx.sta_id; shared_data->queues_byte_cnt_tbls[txq_id].tfd_offset[txq->q.read_ptr]. val = cpu_to_le16(1 | (sta << 12)); diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 848786ab7916..c19db438306c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -135,8 +135,7 @@ struct iwl_tx_info { struct iwl_tx_queue { struct iwl_queue q; struct iwl_tfd_frame *bd; - struct iwl_cmd *cmd; - dma_addr_t dma_addr_cmd; + struct iwl_cmd *cmd[TFD_TX_CMD_SLOTS]; struct iwl_tx_info *txb; int need_update; int sched_retry; diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c index 8fa991b7202a..6512834bb916 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c +++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c @@ -228,7 +228,7 @@ cancel: * TX cmd queue. Otherwise in case the cmd comes * in later, it will possibly set an invalid * address (cmd->meta.source). */ - qcmd = &priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_idx]; + qcmd = priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_idx]; qcmd->meta.flags &= ~CMD_WANT_SKB; } fail: diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 39f19ebee973..aa98c76d8195 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -208,11 +208,12 @@ EXPORT_SYMBOL(iwl_txq_update_write_ptr); * Free all buffers. * 0-fill, but do not free "txq" descriptor structure. */ -static void iwl_tx_queue_free(struct iwl_priv *priv, struct iwl_tx_queue *txq) +static void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id) { + struct iwl_tx_queue *txq = &priv->txq[txq_id]; struct iwl_queue *q = &txq->q; struct pci_dev *dev = priv->pci_dev; - int len; + int i, slots_num, len; if (q->n_bd == 0) return; @@ -227,7 +228,12 @@ static void iwl_tx_queue_free(struct iwl_priv *priv, struct iwl_tx_queue *txq) len += IWL_MAX_SCAN_SIZE; /* De-alloc array of command/tx buffers */ - pci_free_consistent(dev, len, txq->cmd, txq->dma_addr_cmd); + slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ? + TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + for (i = 0; i < slots_num; i++) + kfree(txq->cmd[i]); + if (txq_id == IWL_CMD_QUEUE_NUM) + kfree(txq->cmd[slots_num]); /* De-alloc circular buffer of TFDs */ if (txq->q.n_bd) @@ -400,8 +406,7 @@ static int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, int slots_num, u32 txq_id) { - struct pci_dev *dev = priv->pci_dev; - int len; + int i, len; int rc = 0; /* @@ -412,17 +417,25 @@ static int iwl_tx_queue_init(struct iwl_priv *priv, * For normal Tx queues (all other queues), no super-size command * space is needed. */ - len = sizeof(struct iwl_cmd) * slots_num; - if (txq_id == IWL_CMD_QUEUE_NUM) - len += IWL_MAX_SCAN_SIZE; - txq->cmd = pci_alloc_consistent(dev, len, &txq->dma_addr_cmd); - if (!txq->cmd) - return -ENOMEM; + len = sizeof(struct iwl_cmd); + for (i = 0; i <= slots_num; i++) { + if (i == slots_num) { + if (txq_id == IWL_CMD_QUEUE_NUM) + len += IWL_MAX_SCAN_SIZE; + else + continue; + } + + txq->cmd[i] = kmalloc(len, GFP_KERNEL | GFP_DMA); + if (!txq->cmd[i]) + return -ENOMEM; + } /* Alloc driver data array and TFD circular buffer */ rc = iwl_tx_queue_alloc(priv, txq, txq_id); if (rc) { - pci_free_consistent(dev, len, txq->cmd, txq->dma_addr_cmd); + for (i = 0; i < slots_num; i++) + kfree(txq->cmd[i]); return -ENOMEM; } @@ -451,7 +464,7 @@ void iwl_hw_txq_ctx_free(struct iwl_priv *priv) /* Tx queues */ for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) - iwl_tx_queue_free(priv, &priv->txq[txq_id]); + iwl_tx_queue_free(priv, txq_id); /* Keep-warm buffer */ iwl_kw_free(priv); @@ -859,7 +872,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) txq->txb[q->write_ptr].skb[0] = skb; /* Set up first empty entry in queue's array of Tx/cmd buffers */ - out_cmd = &txq->cmd[idx]; + out_cmd = txq->cmd[idx]; tx_cmd = &out_cmd->cmd.tx; memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); memset(tx_cmd, 0, sizeof(struct iwl_tx_cmd)); @@ -899,8 +912,9 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* Physical address of this Tx command's header (not MAC header!), * within command buffer array. */ - txcmd_phys = txq->dma_addr_cmd + sizeof(struct iwl_cmd) * idx + - offsetof(struct iwl_cmd, hdr); + txcmd_phys = pci_map_single(priv->pci_dev, out_cmd, + sizeof(struct iwl_cmd), PCI_DMA_TODEVICE); + txcmd_phys += offsetof(struct iwl_cmd, hdr); /* Add buffer containing Tx command and MAC(!) header to TFD's * first entry */ @@ -1004,7 +1018,7 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) u32 idx; u16 fix_size; dma_addr_t phys_addr; - int ret; + int len, ret; unsigned long flags; cmd->len = priv->cfg->ops->utils->get_hcmd_size(cmd->id, cmd->len); @@ -1034,7 +1048,7 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) control_flags = (u32 *) tfd; idx = get_cmd_index(q, q->write_ptr, cmd->meta.flags & CMD_SIZE_HUGE); - out_cmd = &txq->cmd[idx]; + out_cmd = txq->cmd[idx]; out_cmd->hdr.cmd = cmd->id; memcpy(&out_cmd->meta, &cmd->meta, sizeof(cmd->meta)); @@ -1048,9 +1062,11 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) INDEX_TO_SEQ(q->write_ptr)); if (out_cmd->meta.flags & CMD_SIZE_HUGE) out_cmd->hdr.sequence |= cpu_to_le16(SEQ_HUGE_FRAME); - - phys_addr = txq->dma_addr_cmd + sizeof(txq->cmd[0]) * idx + - offsetof(struct iwl_cmd, hdr); + len = (idx == TFD_CMD_SLOTS) ? + IWL_MAX_SCAN_SIZE : sizeof(struct iwl_cmd); + phys_addr = pci_map_single(priv->pci_dev, out_cmd, len, + PCI_DMA_TODEVICE); + phys_addr += offsetof(struct iwl_cmd, hdr); iwl_hw_txq_attach_buf_to_tfd(priv, tfd, phys_addr, fix_size); IWL_DEBUG_HC("Sending command %s (#%x), seq: 0x%04X, " @@ -1115,6 +1131,9 @@ static void iwl_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) { struct iwl_tx_queue *txq = &priv->txq[txq_id]; struct iwl_queue *q = &txq->q; + struct iwl_tfd_frame *bd = &txq->bd[index]; + dma_addr_t dma_addr; + int is_odd, buf_len; int nfreed = 0; if ((index >= q->n_bd) || (iwl_queue_used(q, index) == 0)) { @@ -1132,6 +1151,19 @@ static void iwl_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) q->write_ptr, q->read_ptr); queue_work(priv->workqueue, &priv->restart); } + is_odd = (index/2) & 0x1; + if (is_odd) { + dma_addr = IWL_GET_BITS(bd->pa[index], tb2_addr_lo16) | + (IWL_GET_BITS(bd->pa[index], + tb2_addr_hi20) << 16); + buf_len = IWL_GET_BITS(bd->pa[index], tb2_len); + } else { + dma_addr = le32_to_cpu(bd->pa[index].tb1_addr); + buf_len = IWL_GET_BITS(bd->pa[index], tb1_len); + } + + pci_unmap_single(priv->pci_dev, dma_addr, buf_len, + PCI_DMA_TODEVICE); nfreed++; } } @@ -1163,7 +1195,7 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) BUG_ON(txq_id != IWL_CMD_QUEUE_NUM); cmd_index = get_cmd_index(&priv->txq[IWL_CMD_QUEUE_NUM].q, index, huge); - cmd = &priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_index]; + cmd = priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_index]; /* Input error checking is done when commands are added to queue. */ if (cmd->meta.flags & CMD_WANT_SKB) { -- cgit v1.2.1 From caab8f1a5d0da583b6ffe41afea2774c676444ca Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 4 Aug 2008 16:00:42 +0800 Subject: iwlwifi: implement iwl5000_calc_rssi This patch implements rssi calculation for 5000 HW. Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-4965.c | 35 +++++++++++++++++ drivers/net/wireless/iwlwifi/iwl-5000.c | 39 +++++++++++++++++++ drivers/net/wireless/iwlwifi/iwl-commands.h | 35 +++++++++++++---- drivers/net/wireless/iwlwifi/iwl-core.h | 2 + drivers/net/wireless/iwlwifi/iwl-rx.c | 59 ++++++++--------------------- 5 files changed, 119 insertions(+), 51 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 718f9d9e4947..22bb26985c2e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2258,6 +2258,40 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, IWL_ERROR("TODO: Implement Tx ABORT REQUIRED!!!\n"); } +static int iwl4965_calc_rssi(struct iwl_priv *priv, + struct iwl_rx_phy_res *rx_resp) +{ + /* data from PHY/DSP regarding signal strength, etc., + * contents are always there, not configurable by host. */ + struct iwl4965_rx_non_cfg_phy *ncphy = + (struct iwl4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy_buf; + u32 agc = (le16_to_cpu(ncphy->agc_info) & IWL49_AGC_DB_MASK) + >> IWL49_AGC_DB_POS; + + u32 valid_antennae = + (le16_to_cpu(rx_resp->phy_flags) & IWL49_RX_PHY_FLAGS_ANTENNAE_MASK) + >> IWL49_RX_PHY_FLAGS_ANTENNAE_OFFSET; + u8 max_rssi = 0; + u32 i; + + /* Find max rssi among 3 possible receivers. + * These values are measured by the digital signal processor (DSP). + * They should stay fairly constant even as the signal strength varies, + * if the radio's automatic gain control (AGC) is working right. + * AGC value (see below) will provide the "interesting" info. */ + for (i = 0; i < 3; i++) + if (valid_antennae & (1 << i)) + max_rssi = max(ncphy->rssi_info[i << 1], max_rssi); + + IWL_DEBUG_STATS("Rssi In A %d B %d C %d Max %d AGC dB %d\n", + ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4], + max_rssi, agc); + + /* dBm = max_rssi dB - agc dB - constant. + * Higher AGC (higher radio gain) means lower signal. */ + return max_rssi - agc - IWL_RSSI_OFFSET; +} + /* Set up 4965-specific Rx frame reply handlers */ static void iwl4965_rx_handler_setup(struct iwl_priv *priv) @@ -2289,6 +2323,7 @@ static struct iwl_hcmd_utils_ops iwl4965_hcmd_utils = { .chain_noise_reset = iwl4965_chain_noise_reset, .gain_computation = iwl4965_gain_computation, .rts_tx_cmd_flag = iwl4965_rts_tx_cmd_flag, + .calc_rssi = iwl4965_calc_rssi, }; static struct iwl_lib_ops iwl4965_lib = { diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 56dbc8144a34..c5b104fd149c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1459,6 +1459,44 @@ static void iwl5000_temperature(struct iwl_priv *priv) priv->temperature = le32_to_cpu(priv->statistics.general.temperature); } +/* Calc max signal level (dBm) among 3 possible receivers */ +static int iwl5000_calc_rssi(struct iwl_priv *priv, + struct iwl_rx_phy_res *rx_resp) +{ + /* data from PHY/DSP regarding signal strength, etc., + * contents are always there, not configurable by host + */ + struct iwl5000_non_cfg_phy *ncphy = + (struct iwl5000_non_cfg_phy *)rx_resp->non_cfg_phy_buf; + u32 val, rssi_a, rssi_b, rssi_c, max_rssi; + u8 agc; + + val = le32_to_cpu(ncphy->non_cfg_phy[IWL50_RX_RES_AGC_IDX]); + agc = (val & IWL50_OFDM_AGC_MSK) >> IWL50_OFDM_AGC_BIT_POS; + + /* Find max rssi among 3 possible receivers. + * These values are measured by the digital signal processor (DSP). + * They should stay fairly constant even as the signal strength varies, + * if the radio's automatic gain control (AGC) is working right. + * AGC value (see below) will provide the "interesting" info. + */ + val = le32_to_cpu(ncphy->non_cfg_phy[IWL50_RX_RES_RSSI_AB_IDX]); + rssi_a = (val & IWL50_OFDM_RSSI_A_MSK) >> IWL50_OFDM_RSSI_A_BIT_POS; + rssi_b = (val & IWL50_OFDM_RSSI_B_MSK) >> IWL50_OFDM_RSSI_B_BIT_POS; + val = le32_to_cpu(ncphy->non_cfg_phy[IWL50_RX_RES_RSSI_C_IDX]); + rssi_c = (val & IWL50_OFDM_RSSI_C_MSK) >> IWL50_OFDM_RSSI_C_BIT_POS; + + max_rssi = max_t(u32, rssi_a, rssi_b); + max_rssi = max_t(u32, max_rssi, rssi_c); + + IWL_DEBUG_STATS("Rssi In A %d B %d C %d Max %d AGC dB %d\n", + rssi_a, rssi_b, rssi_c, max_rssi, agc); + + /* dBm = max_rssi dB - agc dB - constant. + * Higher AGC (higher radio gain) means lower signal. */ + return max_rssi - agc - IWL_RSSI_OFFSET; +} + static struct iwl_hcmd_ops iwl5000_hcmd = { .rxon_assoc = iwl5000_send_rxon_assoc, }; @@ -1469,6 +1507,7 @@ static struct iwl_hcmd_utils_ops iwl5000_hcmd_utils = { .gain_computation = iwl5000_gain_computation, .chain_noise_reset = iwl5000_chain_noise_reset, .rts_tx_cmd_flag = iwl5000_rts_tx_cmd_flag, + .calc_rssi = iwl5000_calc_rssi, }; static struct iwl_lib_ops iwl5000_lib = { diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 5e57f3ae2ea6..28b5b09996ed 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -1075,10 +1075,12 @@ struct iwl4965_rx_frame { } __attribute__ ((packed)); /* Fixed (non-configurable) rx data from phy */ -#define RX_PHY_FLAGS_ANTENNAE_OFFSET (4) -#define RX_PHY_FLAGS_ANTENNAE_MASK (0x70) -#define IWL_AGC_DB_MASK (0x3f80) /* MASK(7,13) */ -#define IWL_AGC_DB_POS (7) + +#define IWL49_RX_RES_PHY_CNT 14 +#define IWL49_RX_PHY_FLAGS_ANTENNAE_OFFSET (4) +#define IWL49_RX_PHY_FLAGS_ANTENNAE_MASK (0x70) +#define IWL49_AGC_DB_MASK (0x3f80) /* MASK(7,13) */ +#define IWL49_AGC_DB_POS (7) struct iwl4965_rx_non_cfg_phy { __le16 ant_selection; /* ant A bit 4, ant B bit 5, ant C bit 6 */ __le16 agc_info; /* agc code 0:6, agc dB 7:13, reserved 14:15 */ @@ -1086,12 +1088,30 @@ struct iwl4965_rx_non_cfg_phy { u8 pad[0]; } __attribute__ ((packed)); + +#define IWL50_RX_RES_PHY_CNT 8 +#define IWL50_RX_RES_AGC_IDX 1 +#define IWL50_RX_RES_RSSI_AB_IDX 2 +#define IWL50_RX_RES_RSSI_C_IDX 3 +#define IWL50_OFDM_AGC_MSK 0xfe00 +#define IWL50_OFDM_AGC_BIT_POS 9 +#define IWL50_OFDM_RSSI_A_MSK 0x00ff +#define IWL50_OFDM_RSSI_A_BIT_POS 0 +#define IWL50_OFDM_RSSI_B_MSK 0xff0000 +#define IWL50_OFDM_RSSI_B_BIT_POS 16 +#define IWL50_OFDM_RSSI_C_MSK 0x00ff +#define IWL50_OFDM_RSSI_C_BIT_POS 0 + +struct iwl5000_non_cfg_phy { + __le32 non_cfg_phy[IWL50_RX_RES_PHY_CNT]; /* upto 8 phy entries */ +} __attribute__ ((packed)); + + /* * REPLY_RX = 0xc3 (response only, not a command) * Used only for legacy (non 11n) frames. */ -#define RX_RES_PHY_CNT 14 -struct iwl4965_rx_phy_res { +struct iwl_rx_phy_res { u8 non_cfg_phy_cnt; /* non configurable DSP phy data byte count */ u8 cfg_phy_cnt; /* configurable DSP phy data byte count */ u8 stat_id; /* configurable DSP phy data set ID */ @@ -1100,8 +1120,7 @@ struct iwl4965_rx_phy_res { __le32 beacon_time_stamp; /* beacon at on-air rise */ __le16 phy_flags; /* general phy flags: band, modulation, ... */ __le16 channel; /* channel number */ - __le16 non_cfg_phy[RX_RES_PHY_CNT]; /* upto 14 phy entries */ - __le32 reserved2; + u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */ __le32 rate_n_flags; /* RATE_MCS_* */ __le16 byte_count; /* frame's byte-count */ __le16 reserved3; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index eaefa42f37c5..64f139e97444 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -95,6 +95,8 @@ struct iwl_hcmd_utils_ops { void (*chain_noise_reset)(struct iwl_priv *priv); void (*rts_tx_cmd_flag)(struct ieee80211_tx_info *info, __le32 *tx_flags); + int (*calc_rssi)(struct iwl_priv *priv, + struct iwl_rx_phy_res *rx_resp); }; struct iwl_lib_ops { diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index e2d9afba38a5..f3f6ea49fdd2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -791,7 +791,7 @@ static inline void iwl_dbg_report_frame(struct iwl_priv *priv, static void iwl_add_radiotap(struct iwl_priv *priv, struct sk_buff *skb, - struct iwl4965_rx_phy_res *rx_start, + struct iwl_rx_phy_res *rx_start, struct ieee80211_rx_status *stats, u32 ampdu_status) { @@ -1010,8 +1010,8 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv, struct ieee80211_rx_status *stats) { struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; - struct iwl4965_rx_phy_res *rx_start = (include_phy) ? - (struct iwl4965_rx_phy_res *)&(pkt->u.raw[0]) : NULL; + struct iwl_rx_phy_res *rx_start = (include_phy) ? + (struct iwl_rx_phy_res *)&(pkt->u.raw[0]) : NULL; struct ieee80211_hdr *hdr; u16 len; __le32 *rx_end; @@ -1020,7 +1020,7 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv, u32 ampdu_status_legacy; if (!include_phy && priv->last_phy_res[0]) - rx_start = (struct iwl4965_rx_phy_res *)&priv->last_phy_res[1]; + rx_start = (struct iwl_rx_phy_res *)&priv->last_phy_res[1]; if (!rx_start) { IWL_ERROR("MPDU frame without a PHY data\n"); @@ -1032,8 +1032,8 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv, len = le16_to_cpu(rx_start->byte_count); - rx_end = (__le32 *) ((u8 *) &pkt->u.raw[0] + - sizeof(struct iwl4965_rx_phy_res) + + rx_end = (__le32 *)((u8 *) &pkt->u.raw[0] + + sizeof(struct iwl_rx_phy_res) + rx_start->cfg_phy_cnt + len); } else { @@ -1084,40 +1084,13 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv, } /* Calc max signal level (dBm) among 3 possible receivers */ -static int iwl_calc_rssi(struct iwl_priv *priv, - struct iwl4965_rx_phy_res *rx_resp) +static inline int iwl_calc_rssi(struct iwl_priv *priv, + struct iwl_rx_phy_res *rx_resp) { - /* data from PHY/DSP regarding signal strength, etc., - * contents are always there, not configurable by host. */ - struct iwl4965_rx_non_cfg_phy *ncphy = - (struct iwl4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy; - u32 agc = (le16_to_cpu(ncphy->agc_info) & IWL_AGC_DB_MASK) - >> IWL_AGC_DB_POS; - - u32 valid_antennae = - (le16_to_cpu(rx_resp->phy_flags) & RX_PHY_FLAGS_ANTENNAE_MASK) - >> RX_PHY_FLAGS_ANTENNAE_OFFSET; - u8 max_rssi = 0; - u32 i; - - /* Find max rssi among 3 possible receivers. - * These values are measured by the digital signal processor (DSP). - * They should stay fairly constant even as the signal strength varies, - * if the radio's automatic gain control (AGC) is working right. - * AGC value (see below) will provide the "interesting" info. */ - for (i = 0; i < 3; i++) - if (valid_antennae & (1 << i)) - max_rssi = max(ncphy->rssi_info[i << 1], max_rssi); - - IWL_DEBUG_STATS("Rssi In A %d B %d C %d Max %d AGC dB %d\n", - ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4], - max_rssi, agc); - - /* dBm = max_rssi dB - agc dB - constant. - * Higher AGC (higher radio gain) means lower signal. */ - return max_rssi - agc - IWL_RSSI_OFFSET; + return priv->cfg->ops->utils->calc_rssi(priv, rx_resp); } + static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) { unsigned long flags; @@ -1180,9 +1153,9 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, * this rx packet for legacy frames, * or phy data cached from REPLY_RX_PHY_CMD for HT frames. */ int include_phy = (pkt->hdr.cmd == REPLY_RX); - struct iwl4965_rx_phy_res *rx_start = (include_phy) ? - (struct iwl4965_rx_phy_res *)&(pkt->u.raw[0]) : - (struct iwl4965_rx_phy_res *)&priv->last_phy_res[1]; + struct iwl_rx_phy_res *rx_start = (include_phy) ? + (struct iwl_rx_phy_res *)&(pkt->u.raw[0]) : + (struct iwl_rx_phy_res *)&priv->last_phy_res[1]; __le32 *rx_end; unsigned int len = 0; u16 fc; @@ -1210,7 +1183,7 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, if (!include_phy) { if (priv->last_phy_res[0]) - rx_start = (struct iwl4965_rx_phy_res *) + rx_start = (struct iwl_rx_phy_res *) &priv->last_phy_res[1]; else rx_start = NULL; @@ -1227,7 +1200,7 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, len = le16_to_cpu(rx_start->byte_count); rx_end = (__le32 *)(pkt->u.raw + rx_start->cfg_phy_cnt + - sizeof(struct iwl4965_rx_phy_res) + len); + sizeof(struct iwl_rx_phy_res) + len); } else { struct iwl4965_rx_mpdu_res_start *amsdu = (struct iwl4965_rx_mpdu_res_start *)pkt->u.raw; @@ -1316,6 +1289,6 @@ void iwl_rx_reply_rx_phy(struct iwl_priv *priv, struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; priv->last_phy_res[0] = 1; memcpy(&priv->last_phy_res[1], &(pkt->u.raw[0]), - sizeof(struct iwl4965_rx_phy_res)); + sizeof(struct iwl_rx_phy_res)); } EXPORT_SYMBOL(iwl_rx_reply_rx_phy); -- cgit v1.2.1 From c1842d6150c4efe1d01e7a8cf86c63aec6223486 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 4 Aug 2008 16:00:43 +0800 Subject: iwlwifi: fix unhandled interrupt when HW rfkill is on This patch fixes unhandled interrupt when HW rfkill is on during devices start up. The behavior changes, now open is successful even when rfkill is on. This is to align with the situation when rfkill is set on after opening. Signed-off-by: Tomas Winkler Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 6503b3ac8d66..b8407d5704a1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2232,17 +2232,16 @@ static int __iwl4965_up(struct iwl_priv *priv) } /* If platform's RF_KILL switch is NOT set to KILL */ - if (iwl_read32(priv, CSR_GP_CNTRL) & - CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) + if (iwl_read32(priv, CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) clear_bit(STATUS_RF_KILL_HW, &priv->status); else set_bit(STATUS_RF_KILL_HW, &priv->status); - if (!test_bit(STATUS_IN_SUSPEND, &priv->status) && - iwl_is_rfkill(priv)) { + if (iwl_is_rfkill(priv)) { + iwl4965_enable_interrupts(priv); IWL_WARNING("Radio disabled by %s RF Kill switch\n", test_bit(STATUS_RF_KILL_HW, &priv->status) ? "HW" : "SW"); - return -ENODEV; + return 0; } iwl_write32(priv, CSR_INT, 0xFFFFFFFF); @@ -2278,11 +2277,6 @@ static int __iwl4965_up(struct iwl_priv *priv) memcpy(priv->ucode_data_backup.v_addr, priv->ucode_data.v_addr, priv->ucode_data.len); - /* We return success when we resume from suspend and rf_kill is on. */ - if (test_bit(STATUS_RF_KILL_HW, &priv->status) || - test_bit(STATUS_RF_KILL_SW, &priv->status)) - return 0; - for (i = 0; i < MAX_HW_RESTARTS; i++) { iwl_clear_stations_table(priv); @@ -2651,6 +2645,9 @@ static int iwl4965_mac_start(struct ieee80211_hw *hw) if (ret) goto out_release_irq; + if (iwl_is_rfkill(priv)) + goto out; + IWL_DEBUG_INFO("Start UP work done.\n"); if (test_bit(STATUS_IN_SUSPEND, &priv->status)) @@ -2670,6 +2667,7 @@ static int iwl4965_mac_start(struct ieee80211_hw *hw) } } +out: priv->is_open = 1; IWL_DEBUG_MAC80211("leave\n"); return 0; -- cgit v1.2.1 From 14652562364dad636ddce2cd11e71702ca21bfbd Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 4 Aug 2008 16:00:46 +0800 Subject: iwlwifi: decrement rx skb counter in scan abort handler This patch decrements rx skb counter in scan abort handler. Signed-off-by: Emmanuel Grumbach Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-scan.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index 5a00ac23e2d0..9bb6adb28b73 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -202,6 +202,7 @@ static int iwl_send_scan_abort(struct iwl_priv *priv) clear_bit(STATUS_SCAN_HW, &priv->status); } + priv->alloc_rxb_skb--; dev_kfree_skb_any(cmd.meta.u.skb); return ret; -- cgit v1.2.1 From 2d3db679511be102741cb2d5f8c2b8a1ededdee7 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 4 Aug 2008 16:00:47 +0800 Subject: iwlwifi: grap nic access before accessing periphery registers We need to grap nic access before accessing periphery registers. Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-5000.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index c5b104fd149c..f3d139b663e6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -241,9 +241,11 @@ static void iwl5000_nic_config(struct iwl_priv *priv) * (PCIe power is lost before PERST# is asserted), * causing ME FW to lose ownership and not being able to obtain it back. */ - iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + iwl_grab_nic_access(priv); + iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); + iwl_release_nic_access(priv); spin_unlock_irqrestore(&priv->lock, flags); } -- cgit v1.2.1 From 3e2236c108792c3afbbfbe3f373ee7fdd68eda8e Mon Sep 17 00:00:00 2001 From: Zhu Yi Date: Mon, 4 Aug 2008 16:00:48 +0800 Subject: iwl3945: fix merge mistake for packet injection We should allow packets transmission in monitor mode for 3945. The patch fixes a merge error with 2.6.26 kernel. Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl3945-base.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index eb41b02b506d..444847ab1b5a 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -6589,12 +6589,6 @@ static int iwl3945_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) IWL_DEBUG_MAC80211("enter\n"); - if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) { - IWL_DEBUG_MAC80211("leave - monitor\n"); - dev_kfree_skb_any(skb); - return 0; - } - IWL_DEBUG_TX("dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); -- cgit v1.2.1 From c16d118537cadb21d186e35aebad90a13cd78846 Mon Sep 17 00:00:00 2001 From: Wang Chen Date: Tue, 22 Jul 2008 13:13:12 +0800 Subject: [netdrvr] Drivers should not set IFF_* flag themselves Some hardware set promisc when they are requested to set IFF_ALLMULTI flag. It's ok, but if drivers set IFF_PROMISC flag when they set promisc, it will broken upper layer handle for promisc and allmulti. In addition, drivers can use their own hardware programming to make it. So do not allow drivers to set IFF_* flags. This is a general driver fix, so I didn't split it to pieces and send to specific driver maintainers. Signed-off-by: Wang Chen Signed-off-by: Jeff Garzik --- drivers/net/wireless/orinoco.c | 7 ------- drivers/net/wireless/wavelan.c | 3 --- drivers/net/wireless/wavelan_cs.c | 6 ------ 3 files changed, 16 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index b047306bf386..1ebcafe7ca5f 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -1998,13 +1998,6 @@ __orinoco_set_multicast_list(struct net_device *dev) else priv->mc_count = mc_count; } - - /* Since we can set the promiscuous flag when it wasn't asked - for, make sure the net_device knows about it. */ - if (priv->promiscuous) - dev->flags |= IFF_PROMISC; - else - dev->flags &= ~IFF_PROMISC; } /* This must be called from user context, without locks held - use diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c index 49ae97003952..136220b5ca81 100644 --- a/drivers/net/wireless/wavelan.c +++ b/drivers/net/wireless/wavelan.c @@ -1409,9 +1409,6 @@ static void wavelan_set_multicast_list(struct net_device * dev) lp->mc_count = 0; wv_82586_reconfig(dev); - - /* Tell the kernel that we are doing a really bad job. */ - dev->flags |= IFF_PROMISC; } } else /* Are there multicast addresses to send? */ diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c index b584c0ecc62d..00a3559e5aa4 100644 --- a/drivers/net/wireless/wavelan_cs.c +++ b/drivers/net/wireless/wavelan_cs.c @@ -1412,9 +1412,6 @@ wavelan_set_multicast_list(struct net_device * dev) lp->mc_count = 0; wv_82593_reconfig(dev); - - /* Tell the kernel that we are doing a really bad job... */ - dev->flags |= IFF_PROMISC; } } else @@ -1433,9 +1430,6 @@ wavelan_set_multicast_list(struct net_device * dev) lp->mc_count = 0; wv_82593_reconfig(dev); - - /* Tell the kernel that we are doing a really bad job... */ - dev->flags |= IFF_ALLMULTI; } } else -- cgit v1.2.1 From f367422794315b7632f2a78c93e15b3c757d4be8 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Mon, 4 Aug 2008 16:00:44 +0800 Subject: iwlwifi: cleanup iwl_tx_skb This patch cleans up iwl_tx_skb function. Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 4 ++-- drivers/net/wireless/iwlwifi/iwl-debug.h | 3 ++- drivers/net/wireless/iwlwifi/iwl-tx.c | 40 ++++++++++++++------------------ 3 files changed, 22 insertions(+), 25 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index b8407d5704a1..ed09e48b1b61 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2719,7 +2719,7 @@ static int iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct iwl_priv *priv = hw->priv; - IWL_DEBUG_MAC80211("enter\n"); + IWL_DEBUG_MACDUMP("enter\n"); if (priv->iw_mode == IEEE80211_IF_TYPE_MNTR) { IWL_DEBUG_MAC80211("leave - monitor\n"); @@ -2733,7 +2733,7 @@ static int iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (iwl_tx_skb(priv, skb)) dev_kfree_skb_any(skb); - IWL_DEBUG_MAC80211("leave\n"); + IWL_DEBUG_MACDUMP("leave\n"); return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index b4ffd33ef98c..d2daa174df22 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -114,7 +114,7 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv) #define IWL_DL_MAC80211 (1 << 1) #define IWL_DL_HOST_COMMAND (1 << 2) #define IWL_DL_STATE (1 << 3) - +#define IWL_DL_MACDUMP (1 << 4) #define IWL_DL_RADIO (1 << 7) #define IWL_DL_POWER (1 << 8) #define IWL_DL_TEMP (1 << 9) @@ -154,6 +154,7 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv) #define IWL_DEBUG_INFO(f, a...) IWL_DEBUG(IWL_DL_INFO, f, ## a) #define IWL_DEBUG_MAC80211(f, a...) IWL_DEBUG(IWL_DL_MAC80211, f, ## a) +#define IWL_DEBUG_MACDUMP(f, a...) IWL_DEBUG(IWL_DL_MACDUMP, f, ## a) #define IWL_DEBUG_TEMP(f, a...) IWL_DEBUG(IWL_DL_TEMP, f, ## a) #define IWL_DEBUG_SCAN(f, a...) IWL_DEBUG(IWL_DL_SCAN, f, ## a) #define IWL_DEBUG_RX(f, a...) IWL_DEBUG(IWL_DL_RX, f, ## a) diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index aa98c76d8195..4108c7c8f00f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -764,20 +764,19 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_tfd_frame *tfd; - u32 *control_flags; - int txq_id = skb_get_queue_mapping(skb); - struct iwl_tx_queue *txq = NULL; - struct iwl_queue *q = NULL; + struct iwl_tx_queue *txq; + struct iwl_queue *q; + struct iwl_cmd *out_cmd; + struct iwl_tx_cmd *tx_cmd; + int swq_id, txq_id; dma_addr_t phys_addr; dma_addr_t txcmd_phys; dma_addr_t scratch_phys; - struct iwl_cmd *out_cmd = NULL; - struct iwl_tx_cmd *tx_cmd; u16 len, idx, len_org; u16 seq_number = 0; - u8 id, hdr_len, unicast; - u8 sta_id; __le16 fc; + u8 hdr_len, unicast; + u8 sta_id; u8 wait_write_ptr = 0; u8 tid = 0; u8 *qc = NULL; @@ -802,7 +801,6 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) } unicast = !is_multicast_ether_addr(hdr->addr1); - id = 0; fc = hdr->frame_control; @@ -840,14 +838,16 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX("station Id %d\n", sta_id); + swq_id = skb_get_queue_mapping(skb); + txq_id = swq_id; if (ieee80211_is_data_qos(fc)) { qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; - seq_number = priv->stations[sta_id].tid[tid].seq_number & - IEEE80211_SCTL_SEQ; - hdr->seq_ctrl = cpu_to_le16(seq_number) | - (hdr->seq_ctrl & - __constant_cpu_to_le16(IEEE80211_SCTL_FRAG)); + seq_number = priv->stations[sta_id].tid[tid].seq_number; + seq_number &= IEEE80211_SCTL_SEQ; + hdr->seq_ctrl = hdr->seq_ctrl & + __constant_cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(seq_number); seq_number += 0x10; /* aggregation is on for this */ if (info->flags & IEEE80211_TX_CTL_AMPDU) @@ -864,7 +864,6 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* Set up first empty TFD within this queue's circular TFD buffer */ tfd = &txq->bd[q->write_ptr]; memset(tfd, 0, sizeof(*tfd)); - control_flags = (u32 *) tfd; idx = get_cmd_index(q, q->write_ptr, 0); /* Set up driver data for this TFD */ @@ -983,8 +982,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) iwl_txq_update_write_ptr(priv, txq); spin_unlock_irqrestore(&priv->lock, flags); } else { - ieee80211_stop_queue(priv->hw, - skb_get_queue_mapping(skb)); + ieee80211_stop_queue(priv->hw, swq_id); } } @@ -1013,13 +1011,12 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) struct iwl_tx_queue *txq = &priv->txq[IWL_CMD_QUEUE_NUM]; struct iwl_queue *q = &txq->q; struct iwl_tfd_frame *tfd; - u32 *control_flags; struct iwl_cmd *out_cmd; - u32 idx; - u16 fix_size; dma_addr_t phys_addr; - int len, ret; unsigned long flags; + int len, ret; + u32 idx; + u16 fix_size; cmd->len = priv->cfg->ops->utils->get_hcmd_size(cmd->id, cmd->len); fix_size = (u16)(cmd->len + sizeof(out_cmd->hdr)); @@ -1045,7 +1042,6 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) tfd = &txq->bd[q->write_ptr]; memset(tfd, 0, sizeof(*tfd)); - control_flags = (u32 *) tfd; idx = get_cmd_index(q, q->write_ptr, cmd->meta.flags & CMD_SIZE_HUGE); out_cmd = txq->cmd[idx]; -- cgit v1.2.1 From eda0c003d1ff14c99d8476b482377ccfaf967b6c Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Tue, 5 Aug 2008 11:23:16 -0500 Subject: p54: Fix for TX sequence number problem Following "mac80211: fix TX sequence numbers", if a packet has the IEEE80211_TX_CTL_ASSIGN_SEQ assigned, a sequence number must be supplied, either by hardware or software. AFAIK, no such hardware exists for the p54, thus it must be done in software. With this patch, a connection qith p54usb is stable, whereas the interface went off-line in 2-3 hours without this change. Note that this code will have to be reworked for proper sequence numbers on beacons. In addition, the sequence number has been placed in the hardware state, not the vif state. Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54.h | 1 + drivers/net/wireless/p54/p54common.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h index cac9a515b82d..4801a363507b 100644 --- a/drivers/net/wireless/p54/p54.h +++ b/drivers/net/wireless/p54/p54.h @@ -52,6 +52,7 @@ struct p54_common { int (*open)(struct ieee80211_hw *dev); void (*stop)(struct ieee80211_hw *dev); int mode; + u16 seqno; struct mutex conf_mutex; u8 mac_addr[ETH_ALEN]; u8 bssid[ETH_ALEN]; diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 4da89ea9b561..2d2d33612aef 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -553,6 +553,7 @@ static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb) struct ieee80211_tx_queue_stats *current_queue; struct p54_common *priv = dev->priv; struct p54_control_hdr *hdr; + struct ieee80211_hdr *ieee80211hdr = (struct ieee80211_hdr *)skb->data; struct p54_tx_control_allocdata *txhdr; size_t padding, len; u8 rate; @@ -605,6 +606,19 @@ static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb) if (padding) txhdr->align[0] = padding; + /* FIXME: The sequence that follows is needed for this driver to + * work with mac80211 since "mac80211: fix TX sequence numbers". + * As with the temporary code in rt2x00, changes will be needed + * to get proper sequence numbers on beacons. In addition, this + * patch places the sequence number in the hardware state, which + * limits us to a single virtual state. + */ + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) + priv->seqno += 0x10; + ieee80211hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + ieee80211hdr->seq_ctrl |= cpu_to_le16(priv->seqno); + } /* modifies skb->cb and with it info, so must be last! */ p54_assign_address(dev, skb, hdr, skb->len); -- cgit v1.2.1 From 0fc980392e5a06cff34ca7acaac5790bb59528d1 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Wed, 6 Aug 2008 16:18:31 +0200 Subject: rt2x00: Block all unsupported modes It was possible for unsupported operating modes to be accepted by the add_interface callback function. This patch will block those modes until proper support has been implemented for them. Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00mac.c | 54 ++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 17 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index bd422fd6a894..d06507388635 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -203,23 +203,43 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw, !test_bit(DEVICE_STARTED, &rt2x00dev->flags)) return -ENODEV; - /* - * We don't support mixed combinations of sta and ap virtual - * interfaces. We can only add this interface when the rival - * interface count is 0. - */ - if ((conf->type == IEEE80211_IF_TYPE_AP && rt2x00dev->intf_sta_count) || - (conf->type != IEEE80211_IF_TYPE_AP && rt2x00dev->intf_ap_count)) - return -ENOBUFS; - - /* - * Check if we exceeded the maximum amount of supported interfaces. - */ - if ((conf->type == IEEE80211_IF_TYPE_AP && - rt2x00dev->intf_ap_count >= rt2x00dev->ops->max_ap_intf) || - (conf->type != IEEE80211_IF_TYPE_AP && - rt2x00dev->intf_sta_count >= rt2x00dev->ops->max_sta_intf)) - return -ENOBUFS; + switch (conf->type) { + case IEEE80211_IF_TYPE_AP: + /* + * We don't support mixed combinations of + * sta and ap interfaces. + */ + if (rt2x00dev->intf_sta_count) + return -ENOBUFS; + + /* + * Check if we exceeded the maximum amount + * of supported interfaces. + */ + if (rt2x00dev->intf_ap_count >= rt2x00dev->ops->max_ap_intf) + return -ENOBUFS; + + break; + case IEEE80211_IF_TYPE_STA: + case IEEE80211_IF_TYPE_IBSS: + /* + * We don't support mixed combinations of + * sta and ap interfaces. + */ + if (rt2x00dev->intf_ap_count) + return -ENOBUFS; + + /* + * Check if we exceeded the maximum amount + * of supported interfaces. + */ + if (rt2x00dev->intf_sta_count >= rt2x00dev->ops->max_sta_intf) + return -ENOBUFS; + + break; + default: + return -EINVAL; + } /* * Loop through all beacon queues to find a free -- cgit v1.2.1 From 5423b2ed25993e13009ce39f55d742c3c6d03edb Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Thu, 7 Aug 2008 10:22:28 +0200 Subject: p54: swap short slot time dcf values these "magic" values must to be the other way round... Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 2d2d33612aef..83cd85e1f847 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -817,8 +817,8 @@ static void p54_set_vdcf(struct ieee80211_hw *dev) if (dev->conf.flags & IEEE80211_CONF_SHORT_SLOT_TIME) { vdcf->slottime = 9; - vdcf->magic1 = 0x00; - vdcf->magic2 = 0x10; + vdcf->magic1 = 0x10; + vdcf->magic2 = 0x00; } else { vdcf->slottime = 20; vdcf->magic1 = 0x0a; -- cgit v1.2.1 From b1a5215004130689aeee9e522585c879d3b71472 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 4 Aug 2008 00:16:40 -0700 Subject: ath5k: remove Atheros 11n devices from supported list Remove Atheros 11n devices from being claimed by ath5k as its now handled by ath9k. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index ebf19bc11f5b..2028866f5995 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -95,8 +95,6 @@ static struct pci_device_id ath5k_pci_id_table[] __devinitdata = { { PCI_VDEVICE(ATHEROS, 0x001a), .driver_data = AR5K_AR5212 }, /* 2413 Griffin-lite */ { PCI_VDEVICE(ATHEROS, 0x001b), .driver_data = AR5K_AR5212 }, /* 5413 Eagle */ { PCI_VDEVICE(ATHEROS, 0x001c), .driver_data = AR5K_AR5212 }, /* 5424 Condor (PCI-E)*/ - { PCI_VDEVICE(ATHEROS, 0x0023), .driver_data = AR5K_AR5212 }, /* 5416 */ - { PCI_VDEVICE(ATHEROS, 0x0024), .driver_data = AR5K_AR5212 }, /* 5418 */ { 0 } }; MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table); -- cgit v1.2.1 From f078f209704849c86bd43c0beccfc1f410ed1c66 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 4 Aug 2008 00:16:41 -0700 Subject: ath9k: Add new Atheros IEEE 802.11n driver This adds the new mac80211 11n ath9k Atheros driver. Only STA support is currently enabled and tested. Signed-off-by: Senthil Balasubramanian Signed-off-by: Felix Fietkau Signed-off-by: Jack Howarth Signed-off-by: Jouni Malinen Signed-off-by: Sujith Manoharan Signed-off-by: Luis R. Rodriguez Signed-off-by: Pavel Roskin Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John W. Linville --- drivers/net/wireless/Kconfig | 1 + drivers/net/wireless/Makefile | 1 + drivers/net/wireless/ath9k/Kconfig | 8 + drivers/net/wireless/ath9k/Makefile | 11 + drivers/net/wireless/ath9k/ath9k.h | 1066 ++++ drivers/net/wireless/ath9k/beacon.c | 977 ++++ drivers/net/wireless/ath9k/core.c | 1961 +++++++ drivers/net/wireless/ath9k/core.h | 1097 ++++ drivers/net/wireless/ath9k/hw.c | 8563 ++++++++++++++++++++++++++++++ drivers/net/wireless/ath9k/hw.h | 969 ++++ drivers/net/wireless/ath9k/initvals.h | 3146 +++++++++++ drivers/net/wireless/ath9k/main.c | 1533 ++++++ drivers/net/wireless/ath9k/phy.c | 436 ++ drivers/net/wireless/ath9k/phy.h | 543 ++ drivers/net/wireless/ath9k/rc.c | 2126 ++++++++ drivers/net/wireless/ath9k/rc.h | 316 ++ drivers/net/wireless/ath9k/recv.c | 1318 +++++ drivers/net/wireless/ath9k/reg.h | 1385 +++++ drivers/net/wireless/ath9k/regd.c | 1031 ++++ drivers/net/wireless/ath9k/regd.h | 412 ++ drivers/net/wireless/ath9k/regd_common.h | 1915 +++++++ drivers/net/wireless/ath9k/xmit.c | 2870 ++++++++++ 22 files changed, 31685 insertions(+) create mode 100644 drivers/net/wireless/ath9k/Kconfig create mode 100644 drivers/net/wireless/ath9k/Makefile create mode 100644 drivers/net/wireless/ath9k/ath9k.h create mode 100644 drivers/net/wireless/ath9k/beacon.c create mode 100644 drivers/net/wireless/ath9k/core.c create mode 100644 drivers/net/wireless/ath9k/core.h create mode 100644 drivers/net/wireless/ath9k/hw.c create mode 100644 drivers/net/wireless/ath9k/hw.h create mode 100644 drivers/net/wireless/ath9k/initvals.h create mode 100644 drivers/net/wireless/ath9k/main.c create mode 100644 drivers/net/wireless/ath9k/phy.c create mode 100644 drivers/net/wireless/ath9k/phy.h create mode 100644 drivers/net/wireless/ath9k/rc.c create mode 100644 drivers/net/wireless/ath9k/rc.h create mode 100644 drivers/net/wireless/ath9k/recv.c create mode 100644 drivers/net/wireless/ath9k/reg.h create mode 100644 drivers/net/wireless/ath9k/regd.c create mode 100644 drivers/net/wireless/ath9k/regd.h create mode 100644 drivers/net/wireless/ath9k/regd_common.h create mode 100644 drivers/net/wireless/ath9k/xmit.c (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 4c7ff61a1a9c..9931b5ab59cd 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -695,6 +695,7 @@ config MAC80211_HWSIM source "drivers/net/wireless/p54/Kconfig" source "drivers/net/wireless/ath5k/Kconfig" +source "drivers/net/wireless/ath9k/Kconfig" source "drivers/net/wireless/iwlwifi/Kconfig" source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/b43/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 54a4f6f1db67..59aa89ec6e81 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -62,5 +62,6 @@ obj-$(CONFIG_RT2X00) += rt2x00/ obj-$(CONFIG_P54_COMMON) += p54/ obj-$(CONFIG_ATH5K) += ath5k/ +obj-$(CONFIG_ATH9K) += ath9k/ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o diff --git a/drivers/net/wireless/ath9k/Kconfig b/drivers/net/wireless/ath9k/Kconfig new file mode 100644 index 000000000000..9e19dcceb3a2 --- /dev/null +++ b/drivers/net/wireless/ath9k/Kconfig @@ -0,0 +1,8 @@ +config ATH9K + tristate "Atheros 802.11n wireless cards support" + depends on PCI && MAC80211 && WLAN_80211 + ---help--- + This module adds support for wireless adapters based on + Atheros IEEE 802.11n AR5008 and AR9001 family of chipsets. + + If you choose to build a module, it'll be called ath9k. diff --git a/drivers/net/wireless/ath9k/Makefile b/drivers/net/wireless/ath9k/Makefile new file mode 100644 index 000000000000..a6411517e5f8 --- /dev/null +++ b/drivers/net/wireless/ath9k/Makefile @@ -0,0 +1,11 @@ +ath9k-y += hw.o \ + phy.o \ + regd.o \ + beacon.o \ + main.o \ + recv.o \ + xmit.o \ + rc.o \ + core.o + +obj-$(CONFIG_ATH9K) += ath9k.o diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h new file mode 100644 index 000000000000..dc1da64d2d7c --- /dev/null +++ b/drivers/net/wireless/ath9k/ath9k.h @@ -0,0 +1,1066 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef ATH9K_H +#define ATH9K_H + +#include + +#define ATHEROS_VENDOR_ID 0x168c + +#define AR5416_DEVID_PCI 0x0023 +#define AR5416_DEVID_PCIE 0x0024 +#define AR9160_DEVID_PCI 0x0027 +#define AR9280_DEVID_PCI 0x0029 +#define AR9280_DEVID_PCIE 0x002a + +#define AR5416_AR9100_DEVID 0x000b + +#define AR_SUBVENDOR_ID_NOG 0x0e11 +#define AR_SUBVENDOR_ID_NEW_A 0x7065 + +#define ATH9K_TXERR_XRETRY 0x01 +#define ATH9K_TXERR_FILT 0x02 +#define ATH9K_TXERR_FIFO 0x04 +#define ATH9K_TXERR_XTXOP 0x08 +#define ATH9K_TXERR_TIMER_EXPIRED 0x10 + +#define ATH9K_TX_BA 0x01 +#define ATH9K_TX_PWRMGMT 0x02 +#define ATH9K_TX_DESC_CFG_ERR 0x04 +#define ATH9K_TX_DATA_UNDERRUN 0x08 +#define ATH9K_TX_DELIM_UNDERRUN 0x10 +#define ATH9K_TX_SW_ABORTED 0x40 +#define ATH9K_TX_SW_FILTERED 0x80 + +#define NBBY 8 + +struct ath_tx_status { + u32 ts_tstamp; + u16 ts_seqnum; + u8 ts_status; + u8 ts_ratecode; + u8 ts_rateindex; + int8_t ts_rssi; + u8 ts_shortretry; + u8 ts_longretry; + u8 ts_virtcol; + u8 ts_antenna; + u8 ts_flags; + int8_t ts_rssi_ctl0; + int8_t ts_rssi_ctl1; + int8_t ts_rssi_ctl2; + int8_t ts_rssi_ext0; + int8_t ts_rssi_ext1; + int8_t ts_rssi_ext2; + u8 pad[3]; + u32 ba_low; + u32 ba_high; + u32 evm0; + u32 evm1; + u32 evm2; +}; + +struct ath_rx_status { + u32 rs_tstamp; + u16 rs_datalen; + u8 rs_status; + u8 rs_phyerr; + int8_t rs_rssi; + u8 rs_keyix; + u8 rs_rate; + u8 rs_antenna; + u8 rs_more; + int8_t rs_rssi_ctl0; + int8_t rs_rssi_ctl1; + int8_t rs_rssi_ctl2; + int8_t rs_rssi_ext0; + int8_t rs_rssi_ext1; + int8_t rs_rssi_ext2; + u8 rs_isaggr; + u8 rs_moreaggr; + u8 rs_num_delims; + u8 rs_flags; + u32 evm0; + u32 evm1; + u32 evm2; +}; + +#define ATH9K_RXERR_CRC 0x01 +#define ATH9K_RXERR_PHY 0x02 +#define ATH9K_RXERR_FIFO 0x04 +#define ATH9K_RXERR_DECRYPT 0x08 +#define ATH9K_RXERR_MIC 0x10 + +#define ATH9K_RX_MORE 0x01 +#define ATH9K_RX_MORE_AGGR 0x02 +#define ATH9K_RX_GI 0x04 +#define ATH9K_RX_2040 0x08 +#define ATH9K_RX_DELIM_CRC_PRE 0x10 +#define ATH9K_RX_DELIM_CRC_POST 0x20 +#define ATH9K_RX_DECRYPT_BUSY 0x40 + +#define ATH9K_RXKEYIX_INVALID ((u8)-1) +#define ATH9K_TXKEYIX_INVALID ((u32)-1) + +struct ath_desc { + u32 ds_link; + u32 ds_data; + u32 ds_ctl0; + u32 ds_ctl1; + u32 ds_hw[20]; + union { + struct ath_tx_status tx; + struct ath_rx_status rx; + void *stats; + } ds_us; + void *ds_vdata; +} __packed; + +#define ds_txstat ds_us.tx +#define ds_rxstat ds_us.rx +#define ds_stat ds_us.stats + +#define ATH9K_TXDESC_CLRDMASK 0x0001 +#define ATH9K_TXDESC_NOACK 0x0002 +#define ATH9K_TXDESC_RTSENA 0x0004 +#define ATH9K_TXDESC_CTSENA 0x0008 +#define ATH9K_TXDESC_INTREQ 0x0010 +#define ATH9K_TXDESC_VEOL 0x0020 +#define ATH9K_TXDESC_EXT_ONLY 0x0040 +#define ATH9K_TXDESC_EXT_AND_CTL 0x0080 +#define ATH9K_TXDESC_VMF 0x0100 +#define ATH9K_TXDESC_FRAG_IS_ON 0x0200 + +#define ATH9K_RXDESC_INTREQ 0x0020 + +enum hal_capability_type { + HAL_CAP_CIPHER = 0, + HAL_CAP_TKIP_MIC, + HAL_CAP_TKIP_SPLIT, + HAL_CAP_PHYCOUNTERS, + HAL_CAP_DIVERSITY, + HAL_CAP_PSPOLL, + HAL_CAP_TXPOW, + HAL_CAP_PHYDIAG, + HAL_CAP_MCAST_KEYSRCH, + HAL_CAP_TSF_ADJUST, + HAL_CAP_WME_TKIPMIC, + HAL_CAP_RFSILENT, + HAL_CAP_ANT_CFG_2GHZ, + HAL_CAP_ANT_CFG_5GHZ +}; + +struct hal_capabilities { + u32 halChanSpreadSupport:1, + halChapTuningSupport:1, + halMicAesCcmSupport:1, + halMicCkipSupport:1, + halMicTkipSupport:1, + halCipherAesCcmSupport:1, + halCipherCkipSupport:1, + halCipherTkipSupport:1, + halVEOLSupport:1, + halBssIdMaskSupport:1, + halMcastKeySrchSupport:1, + halTsfAddSupport:1, + halChanHalfRate:1, + halChanQuarterRate:1, + halHTSupport:1, + halGTTSupport:1, + halFastCCSupport:1, + halRfSilentSupport:1, + halWowSupport:1, + halCSTSupport:1, + halEnhancedPmSupport:1, + halAutoSleepSupport:1, + hal4kbSplitTransSupport:1, + halWowMatchPatternExact:1; + u32 halWirelessModes; + u16 halTotalQueues; + u16 halKeyCacheSize; + u16 halLow5GhzChan, halHigh5GhzChan; + u16 halLow2GhzChan, halHigh2GhzChan; + u16 halNumMRRetries; + u16 halRtsAggrLimit; + u8 halTxChainMask; + u8 halRxChainMask; + u16 halTxTrigLevelMax; + u16 halRegCap; + u8 halNumGpioPins; + u8 halNumAntCfg2GHz; + u8 halNumAntCfg5GHz; +}; + +struct hal_ops_config { + int ath_hal_dma_beacon_response_time; + int ath_hal_sw_beacon_response_time; + int ath_hal_additional_swba_backoff; + int ath_hal_6mb_ack; + int ath_hal_cwmIgnoreExtCCA; + u8 ath_hal_pciePowerSaveEnable; + u8 ath_hal_pcieL1SKPEnable; + u8 ath_hal_pcieClockReq; + u32 ath_hal_pcieWaen; + int ath_hal_pciePowerReset; + u8 ath_hal_pcieRestore; + u8 ath_hal_analogShiftReg; + u8 ath_hal_htEnable; + u32 ath_hal_ofdmTrigLow; + u32 ath_hal_ofdmTrigHigh; + u32 ath_hal_cckTrigHigh; + u32 ath_hal_cckTrigLow; + u32 ath_hal_enableANI; + u8 ath_hal_noiseImmunityLvl; + u32 ath_hal_ofdmWeakSigDet; + u32 ath_hal_cckWeakSigThr; + u8 ath_hal_spurImmunityLvl; + u8 ath_hal_firStepLvl; + int8_t ath_hal_rssiThrHigh; + int8_t ath_hal_rssiThrLow; + u16 ath_hal_diversityControl; + u16 ath_hal_antennaSwitchSwap; + int ath_hal_serializeRegMode; + int ath_hal_intrMitigation; +#define SPUR_DISABLE 0 +#define SPUR_ENABLE_IOCTL 1 +#define SPUR_ENABLE_EEPROM 2 +#define AR_EEPROM_MODAL_SPURS 5 +#define AR_SPUR_5413_1 1640 +#define AR_SPUR_5413_2 1200 +#define AR_NO_SPUR 0x8000 +#define AR_BASE_FREQ_2GHZ 2300 +#define AR_BASE_FREQ_5GHZ 4900 +#define AR_SPUR_FEEQ_BOUND_HT40 19 +#define AR_SPUR_FEEQ_BOUND_HT20 10 + int ath_hal_spurMode; + u16 ath_hal_spurChans[AR_EEPROM_MODAL_SPURS][2]; +}; + +enum ath9k_tx_queue { + ATH9K_TX_QUEUE_INACTIVE = 0, + ATH9K_TX_QUEUE_DATA, + ATH9K_TX_QUEUE_BEACON, + ATH9K_TX_QUEUE_CAB, + ATH9K_TX_QUEUE_UAPSD, + ATH9K_TX_QUEUE_PSPOLL +}; + +#define ATH9K_NUM_TX_QUEUES 10 + +enum ath9k_tx_queue_subtype { + ATH9K_WME_AC_BK = 0, + ATH9K_WME_AC_BE, + ATH9K_WME_AC_VI, + ATH9K_WME_AC_VO, + ATH9K_WME_UPSD +}; + +enum ath9k_tx_queue_flags { + TXQ_FLAG_TXOKINT_ENABLE = 0x0001, + TXQ_FLAG_TXERRINT_ENABLE = 0x0001, + TXQ_FLAG_TXDESCINT_ENABLE = 0x0002, + TXQ_FLAG_TXEOLINT_ENABLE = 0x0004, + TXQ_FLAG_TXURNINT_ENABLE = 0x0008, + TXQ_FLAG_BACKOFF_DISABLE = 0x0010, + TXQ_FLAG_COMPRESSION_ENABLE = 0x0020, + TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE = 0x0040, + TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE = 0x0080, +}; + +struct ath9k_txq_info { + u32 tqi_ver; + enum ath9k_tx_queue_subtype tqi_subtype; + enum ath9k_tx_queue_flags tqi_qflags; + u32 tqi_priority; + u32 tqi_aifs; + u32 tqi_cwmin; + u32 tqi_cwmax; + u16 tqi_shretry; + u16 tqi_lgretry; + u32 tqi_cbrPeriod; + u32 tqi_cbrOverflowLimit; + u32 tqi_burstTime; + u32 tqi_readyTime; + u32 tqi_compBuf; +}; + +#define ATH9K_TXQ_USEDEFAULT ((u32) -1) + +#define ATH9K_DECOMP_MASK_SIZE 128 +#define ATH9K_READY_TIME_LO_BOUND 50 +#define ATH9K_READY_TIME_HI_BOUND 96 + +enum ath9k_pkt_type { + ATH9K_PKT_TYPE_NORMAL = 0, + ATH9K_PKT_TYPE_ATIM, + ATH9K_PKT_TYPE_PSPOLL, + ATH9K_PKT_TYPE_BEACON, + ATH9K_PKT_TYPE_PROBE_RESP, + ATH9K_PKT_TYPE_CHIRP, + ATH9K_PKT_TYPE_GRP_POLL, +}; + +struct ath9k_tx_queue_info { + u32 tqi_ver; + enum ath9k_tx_queue tqi_type; + enum ath9k_tx_queue_subtype tqi_subtype; + enum ath9k_tx_queue_flags tqi_qflags; + u32 tqi_priority; + u32 tqi_aifs; + u32 tqi_cwmin; + u32 tqi_cwmax; + u16 tqi_shretry; + u16 tqi_lgretry; + u32 tqi_cbrPeriod; + u32 tqi_cbrOverflowLimit; + u32 tqi_burstTime; + u32 tqi_readyTime; + u32 tqi_physCompBuf; + u32 tqi_intFlags; +}; + +enum ath9k_rx_filter { + ATH9K_RX_FILTER_UCAST = 0x00000001, + ATH9K_RX_FILTER_MCAST = 0x00000002, + ATH9K_RX_FILTER_BCAST = 0x00000004, + ATH9K_RX_FILTER_CONTROL = 0x00000008, + ATH9K_RX_FILTER_BEACON = 0x00000010, + ATH9K_RX_FILTER_PROM = 0x00000020, + ATH9K_RX_FILTER_PROBEREQ = 0x00000080, + ATH9K_RX_FILTER_PSPOLL = 0x00004000, + ATH9K_RX_FILTER_PHYERR = 0x00000100, + ATH9K_RX_FILTER_PHYRADAR = 0x00002000, +}; + +enum ath9k_int { + ATH9K_INT_RX = 0x00000001, + ATH9K_INT_RXDESC = 0x00000002, + ATH9K_INT_RXNOFRM = 0x00000008, + ATH9K_INT_RXEOL = 0x00000010, + ATH9K_INT_RXORN = 0x00000020, + ATH9K_INT_TX = 0x00000040, + ATH9K_INT_TXDESC = 0x00000080, + ATH9K_INT_TIM_TIMER = 0x00000100, + ATH9K_INT_TXURN = 0x00000800, + ATH9K_INT_MIB = 0x00001000, + ATH9K_INT_RXPHY = 0x00004000, + ATH9K_INT_RXKCM = 0x00008000, + ATH9K_INT_SWBA = 0x00010000, + ATH9K_INT_BMISS = 0x00040000, + ATH9K_INT_BNR = 0x00100000, + ATH9K_INT_TIM = 0x00200000, + ATH9K_INT_DTIM = 0x00400000, + ATH9K_INT_DTIMSYNC = 0x00800000, + ATH9K_INT_GPIO = 0x01000000, + ATH9K_INT_CABEND = 0x02000000, + ATH9K_INT_CST = 0x10000000, + ATH9K_INT_GTT = 0x20000000, + ATH9K_INT_FATAL = 0x40000000, + ATH9K_INT_GLOBAL = 0x80000000, + ATH9K_INT_BMISC = ATH9K_INT_TIM | + ATH9K_INT_DTIM | + ATH9K_INT_DTIMSYNC | + ATH9K_INT_CABEND, + ATH9K_INT_COMMON = ATH9K_INT_RXNOFRM | + ATH9K_INT_RXDESC | + ATH9K_INT_RXEOL | + ATH9K_INT_RXORN | + ATH9K_INT_TXURN | + ATH9K_INT_TXDESC | + ATH9K_INT_MIB | + ATH9K_INT_RXPHY | + ATH9K_INT_RXKCM | + ATH9K_INT_SWBA | + ATH9K_INT_BMISS | + ATH9K_INT_GPIO, + ATH9K_INT_NOCARD = 0xffffffff +}; + +struct ath9k_rate_table { + int rateCount; + u8 rateCodeToIndex[256]; + struct { + u8 valid; + u8 phy; + u32 rateKbps; + u8 rateCode; + u8 shortPreamble; + u8 dot11Rate; + u8 controlRate; + u16 lpAckDuration; + u16 spAckDuration; + } info[32]; +}; + +#define ATH9K_RATESERIES_RTS_CTS 0x0001 +#define ATH9K_RATESERIES_2040 0x0002 +#define ATH9K_RATESERIES_HALFGI 0x0004 + +struct ath9k_11n_rate_series { + u32 Tries; + u32 Rate; + u32 PktDuration; + u32 ChSel; + u32 RateFlags; +}; + +#define CHANNEL_CW_INT 0x00002 +#define CHANNEL_CCK 0x00020 +#define CHANNEL_OFDM 0x00040 +#define CHANNEL_2GHZ 0x00080 +#define CHANNEL_5GHZ 0x00100 +#define CHANNEL_PASSIVE 0x00200 +#define CHANNEL_DYN 0x00400 +#define CHANNEL_HALF 0x04000 +#define CHANNEL_QUARTER 0x08000 +#define CHANNEL_HT20 0x10000 +#define CHANNEL_HT40PLUS 0x20000 +#define CHANNEL_HT40MINUS 0x40000 + +#define CHANNEL_INTERFERENCE 0x01 +#define CHANNEL_DFS 0x02 +#define CHANNEL_4MS_LIMIT 0x04 +#define CHANNEL_DFS_CLEAR 0x08 +#define CHANNEL_DISALLOW_ADHOC 0x10 +#define CHANNEL_PER_11D_ADHOC 0x20 + +#define CHANNEL_A (CHANNEL_5GHZ|CHANNEL_OFDM) +#define CHANNEL_B (CHANNEL_2GHZ|CHANNEL_CCK) +#define CHANNEL_G (CHANNEL_2GHZ|CHANNEL_OFDM) +#define CHANNEL_G_HT20 (CHANNEL_2GHZ|CHANNEL_HT20) +#define CHANNEL_A_HT20 (CHANNEL_5GHZ|CHANNEL_HT20) +#define CHANNEL_G_HT40PLUS (CHANNEL_2GHZ|CHANNEL_HT40PLUS) +#define CHANNEL_G_HT40MINUS (CHANNEL_2GHZ|CHANNEL_HT40MINUS) +#define CHANNEL_A_HT40PLUS (CHANNEL_5GHZ|CHANNEL_HT40PLUS) +#define CHANNEL_A_HT40MINUS (CHANNEL_5GHZ|CHANNEL_HT40MINUS) +#define CHANNEL_ALL \ + (CHANNEL_OFDM| \ + CHANNEL_CCK| \ + CHANNEL_2GHZ | \ + CHANNEL_5GHZ | \ + CHANNEL_HT20 | \ + CHANNEL_HT40PLUS | \ + CHANNEL_HT40MINUS) + +struct ath9k_channel { + u16 channel; + u32 channelFlags; + u8 privFlags; + int8_t maxRegTxPower; + int8_t maxTxPower; + int8_t minTxPower; + u32 chanmode; + int32_t CalValid; + bool oneTimeCalsDone; + int8_t iCoff; + int8_t qCoff; + int16_t rawNoiseFloor; + int8_t antennaMax; + u32 regDmnFlags; + u32 conformanceTestLimit[3]; /* 0:11a, 1: 11b, 2:11g */ +#ifdef ATH_NF_PER_CHAN + struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; +#endif +}; + +#define IS_CHAN_A(_c) ((((_c)->channelFlags & CHANNEL_A) == CHANNEL_A) || \ + (((_c)->channelFlags & CHANNEL_A_HT20) == CHANNEL_A_HT20) || \ + (((_c)->channelFlags & CHANNEL_A_HT40PLUS) == CHANNEL_A_HT40PLUS) || \ + (((_c)->channelFlags & CHANNEL_A_HT40MINUS) == CHANNEL_A_HT40MINUS)) +#define IS_CHAN_B(_c) (((_c)->channelFlags & CHANNEL_B) == CHANNEL_B) +#define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \ + (((_c)->channelFlags & CHANNEL_G_HT20) == CHANNEL_G_HT20) || \ + (((_c)->channelFlags & CHANNEL_G_HT40PLUS) == CHANNEL_G_HT40PLUS) || \ + (((_c)->channelFlags & CHANNEL_G_HT40MINUS) == CHANNEL_G_HT40MINUS)) +#define IS_CHAN_CCK(_c) (((_c)->channelFlags & CHANNEL_CCK) != 0) +#define IS_CHAN_OFDM(_c) (((_c)->channelFlags & CHANNEL_OFDM) != 0) +#define IS_CHAN_5GHZ(_c) (((_c)->channelFlags & CHANNEL_5GHZ) != 0) +#define IS_CHAN_2GHZ(_c) (((_c)->channelFlags & CHANNEL_2GHZ) != 0) +#define IS_CHAN_PASSIVE(_c) (((_c)->channelFlags & CHANNEL_PASSIVE) != 0) +#define IS_CHAN_HALF_RATE(_c) (((_c)->channelFlags & CHANNEL_HALF) != 0) +#define IS_CHAN_QUARTER_RATE(_c) (((_c)->channelFlags & CHANNEL_QUARTER) != 0) + +/* These macros check chanmode and not channelFlags */ +#define IS_CHAN_HT20(_c) (((_c)->chanmode == CHANNEL_A_HT20) || \ + ((_c)->chanmode == CHANNEL_G_HT20)) +#define IS_CHAN_HT40(_c) (((_c)->chanmode == CHANNEL_A_HT40PLUS) || \ + ((_c)->chanmode == CHANNEL_A_HT40MINUS) || \ + ((_c)->chanmode == CHANNEL_G_HT40PLUS) || \ + ((_c)->chanmode == CHANNEL_G_HT40MINUS)) +#define IS_CHAN_HT(_c) (IS_CHAN_HT20((_c)) || IS_CHAN_HT40((_c))) + +#define IS_CHAN_IN_PUBLIC_SAFETY_BAND(_c) ((_c) > 4940 && (_c) < 4990) +#define IS_CHAN_A_5MHZ_SPACED(_c) \ + ((((_c)->channelFlags & CHANNEL_5GHZ) != 0) && \ + (((_c)->channel % 20) != 0) && \ + (((_c)->channel % 10) != 0)) + +struct ath9k_keyval { + u8 kv_type; + u8 kv_pad; + u16 kv_len; + u8 kv_val[16]; + u8 kv_mic[8]; + u8 kv_txmic[8]; +}; + +enum ath9k_key_type { + ATH9K_KEY_TYPE_CLEAR, + ATH9K_KEY_TYPE_WEP, + ATH9K_KEY_TYPE_AES, + ATH9K_KEY_TYPE_TKIP, +}; + +enum ath9k_cipher { + ATH9K_CIPHER_WEP = 0, + ATH9K_CIPHER_AES_OCB = 1, + ATH9K_CIPHER_AES_CCM = 2, + ATH9K_CIPHER_CKIP = 3, + ATH9K_CIPHER_TKIP = 4, + ATH9K_CIPHER_CLR = 5, + ATH9K_CIPHER_MIC = 127 +}; + +#define AR_EEPROM_EEPCAP_COMPRESS_DIS 0x0001 +#define AR_EEPROM_EEPCAP_AES_DIS 0x0002 +#define AR_EEPROM_EEPCAP_FASTFRAME_DIS 0x0004 +#define AR_EEPROM_EEPCAP_BURST_DIS 0x0008 +#define AR_EEPROM_EEPCAP_MAXQCU 0x01F0 +#define AR_EEPROM_EEPCAP_MAXQCU_S 4 +#define AR_EEPROM_EEPCAP_HEAVY_CLIP_EN 0x0200 +#define AR_EEPROM_EEPCAP_KC_ENTRIES 0xF000 +#define AR_EEPROM_EEPCAP_KC_ENTRIES_S 12 + +#define AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND 0x0040 +#define AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN 0x0080 +#define AR_EEPROM_EEREGCAP_EN_KK_U2 0x0100 +#define AR_EEPROM_EEREGCAP_EN_KK_MIDBAND 0x0200 +#define AR_EEPROM_EEREGCAP_EN_KK_U1_ODD 0x0400 +#define AR_EEPROM_EEREGCAP_EN_KK_NEW_11A 0x0800 + +#define AR_EEPROM_EEREGCAP_EN_KK_U1_ODD_PRE4_0 0x4000 +#define AR_EEPROM_EEREGCAP_EN_KK_NEW_11A_PRE4_0 0x8000 + +#define SD_NO_CTL 0xE0 +#define NO_CTL 0xff +#define CTL_MODE_M 7 +#define CTL_11A 0 +#define CTL_11B 1 +#define CTL_11G 2 +#define CTL_2GHT20 5 +#define CTL_5GHT20 6 +#define CTL_2GHT40 7 +#define CTL_5GHT40 8 + +#define AR_EEPROM_MAC(i) (0x1d+(i)) +#define EEP_SCALE 100 +#define EEP_DELTA 10 + +#define AR_EEPROM_RFSILENT_GPIO_SEL 0x001c +#define AR_EEPROM_RFSILENT_GPIO_SEL_S 2 +#define AR_EEPROM_RFSILENT_POLARITY 0x0002 +#define AR_EEPROM_RFSILENT_POLARITY_S 1 + +#define CTRY_DEBUG 0x1ff +#define CTRY_DEFAULT 0 + +enum reg_ext_bitmap { + REG_EXT_JAPAN_MIDBAND = 1, + REG_EXT_FCC_DFS_HT40 = 2, + REG_EXT_JAPAN_NONDFS_HT40 = 3, + REG_EXT_JAPAN_DFS_HT40 = 4 +}; + +struct ath9k_country_entry { + u16 countryCode; + u16 regDmnEnum; + u16 regDmn5G; + u16 regDmn2G; + u8 isMultidomain; + u8 iso[3]; +}; + +#define REG_WRITE(_ah, _reg, _val) iowrite32(_val, _ah->ah_sh + _reg) +#define REG_READ(_ah, _reg) ioread32(_ah->ah_sh + _reg) + +#define SM(_v, _f) (((_v) << _f##_S) & _f) +#define MS(_v, _f) (((_v) & _f) >> _f##_S) +#define REG_RMW(_a, _r, _set, _clr) \ + REG_WRITE(_a, _r, (REG_READ(_a, _r) & ~(_clr)) | (_set)) +#define REG_RMW_FIELD(_a, _r, _f, _v) \ + REG_WRITE(_a, _r, \ + (REG_READ(_a, _r) & ~_f) | (((_v) << _f##_S) & _f)) +#define REG_SET_BIT(_a, _r, _f) \ + REG_WRITE(_a, _r, REG_READ(_a, _r) | _f) +#define REG_CLR_BIT(_a, _r, _f) \ + REG_WRITE(_a, _r, REG_READ(_a, _r) & ~_f) + +#define ATH9K_COMP_BUF_MAX_SIZE 9216 +#define ATH9K_COMP_BUF_ALIGN_SIZE 512 + +#define ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001 + +#define INIT_AIFS 2 +#define INIT_CWMIN 15 +#define INIT_CWMIN_11B 31 +#define INIT_CWMAX 1023 +#define INIT_SH_RETRY 10 +#define INIT_LG_RETRY 10 +#define INIT_SSH_RETRY 32 +#define INIT_SLG_RETRY 32 + +#define WLAN_CTRL_FRAME_SIZE (2+2+6+4) + +#define ATH_AMPDU_LIMIT_MAX (64 * 1024 - 1) +#define ATH_AMPDU_LIMIT_DEFAULT ATH_AMPDU_LIMIT_MAX + +#define IEEE80211_WEP_IVLEN 3 +#define IEEE80211_WEP_KIDLEN 1 +#define IEEE80211_WEP_CRCLEN 4 +#define IEEE80211_MAX_MPDU_LEN (3840 + FCS_LEN + \ + (IEEE80211_WEP_IVLEN + \ + IEEE80211_WEP_KIDLEN + \ + IEEE80211_WEP_CRCLEN)) +#define IEEE80211_MAX_LEN (2300 + FCS_LEN + \ + (IEEE80211_WEP_IVLEN + \ + IEEE80211_WEP_KIDLEN + \ + IEEE80211_WEP_CRCLEN)) + +#define MAX_REG_ADD_COUNT 129 +#define MAX_RATE_POWER 63 + +enum ath9k_power_mode { + ATH9K_PM_AWAKE = 0, + ATH9K_PM_FULL_SLEEP, + ATH9K_PM_NETWORK_SLEEP, + ATH9K_PM_UNDEFINED +}; + +#define HAL_ANTENNA_MIN_MODE 0 +#define HAL_ANTENNA_FIXED_A 1 +#define HAL_ANTENNA_FIXED_B 2 +#define HAL_ANTENNA_MAX_MODE 3 + +struct ath9k_mib_stats { + u32 ackrcv_bad; + u32 rts_bad; + u32 rts_good; + u32 fcs_bad; + u32 beacons; +}; + +enum ath9k_ant_setting { + ATH9K_ANT_VARIABLE = 0, + ATH9K_ANT_FIXED_A, + ATH9K_ANT_FIXED_B +}; + +enum ath9k_opmode { + ATH9K_M_STA = 1, + ATH9K_M_IBSS = 0, + ATH9K_M_HOSTAP = 6, + ATH9K_M_MONITOR = 8 +}; + +#define ATH9K_SLOT_TIME_6 6 +#define ATH9K_SLOT_TIME_9 9 +#define ATH9K_SLOT_TIME_20 20 + +enum ath9k_ht_macmode { + ATH9K_HT_MACMODE_20 = 0, + ATH9K_HT_MACMODE_2040 = 1, +}; + +enum ath9k_ht_extprotspacing { + ATH9K_HT_EXTPROTSPACING_20 = 0, + ATH9K_HT_EXTPROTSPACING_25 = 1, +}; + +struct ath9k_ht_cwm { + enum ath9k_ht_macmode ht_macmode; + enum ath9k_ht_extprotspacing ht_extprotspacing; +}; + +enum hal_freq_band { + HAL_FREQ_BAND_5GHZ = 0, + HAL_FREQ_BAND_2GHZ = 1, +}; + +enum ath9k_ani_cmd { + ATH9K_ANI_PRESENT = 0x1, + ATH9K_ANI_NOISE_IMMUNITY_LEVEL = 0x2, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION = 0x4, + ATH9K_ANI_CCK_WEAK_SIGNAL_THR = 0x8, + ATH9K_ANI_FIRSTEP_LEVEL = 0x10, + ATH9K_ANI_SPUR_IMMUNITY_LEVEL = 0x20, + ATH9K_ANI_MODE = 0x40, + ATH9K_ANI_PHYERR_RESET = 0x80, + ATH9K_ANI_ALL = 0xff +}; + +enum phytype { + PHY_DS, + PHY_FH, + PHY_OFDM, + PHY_HT, + PHY_MAX +}; +#define PHY_CCK PHY_DS + +enum start_adhoc_option { + START_ADHOC_NO_11A, + START_ADHOC_PER_11D, + START_ADHOC_IN_11A, + START_ADHOC_IN_11B, +}; + +enum ath9k_tp_scale { + ATH9K_TP_SCALE_MAX = 0, + ATH9K_TP_SCALE_50, + ATH9K_TP_SCALE_25, + ATH9K_TP_SCALE_12, + ATH9K_TP_SCALE_MIN +}; + +enum ser_reg_mode { + SER_REG_MODE_OFF = 0, + SER_REG_MODE_ON = 1, + SER_REG_MODE_AUTO = 2, +}; + +#define AR_PHY_CCA_MAX_GOOD_VALUE -85 +#define AR_PHY_CCA_MAX_HIGH_VALUE -62 +#define AR_PHY_CCA_MIN_BAD_VALUE -121 +#define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT 3 +#define AR_PHY_CCA_FILTERWINDOW_LENGTH 5 + +#define ATH9K_NF_CAL_HIST_MAX 5 +#define NUM_NF_READINGS 6 + +struct ath9k_nfcal_hist { + int16_t nfCalBuffer[ATH9K_NF_CAL_HIST_MAX]; + u8 currIndex; + int16_t privNF; + u8 invalidNFcount; +}; + +struct ath9k_beacon_state { + u32 bs_nexttbtt; + u32 bs_nextdtim; + u32 bs_intval; +#define ATH9K_BEACON_PERIOD 0x0000ffff +#define ATH9K_BEACON_ENA 0x00800000 +#define ATH9K_BEACON_RESET_TSF 0x01000000 + u32 bs_dtimperiod; + u16 bs_cfpperiod; + u16 bs_cfpmaxduration; + u32 bs_cfpnext; + u16 bs_timoffset; + u16 bs_bmissthreshold; + u32 bs_sleepduration; +}; + +struct ath9k_node_stats { + u32 ns_avgbrssi; + u32 ns_avgrssi; + u32 ns_avgtxrssi; + u32 ns_avgtxrate; +}; + +#define ATH9K_RSSI_EP_MULTIPLIER (1<<7) + +enum ath9k_gpio_output_mux_type { + ATH9K_GPIO_OUTPUT_MUX_AS_OUTPUT, + ATH9K_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED, + ATH9K_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED, + ATH9K_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED, + ATH9K_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED, + ATH9K_GPIO_OUTPUT_MUX_NUM_ENTRIES +}; + +enum { + ATH9K_RESET_POWER_ON, + ATH9K_RESET_WARM, + ATH9K_RESET_COLD, +}; + +#define AH_USE_EEPROM 0x1 + +struct ath_hal { + u32 ah_magic; + u16 ah_devid; + u16 ah_subvendorid; + struct ath_softc *ah_sc; + void __iomem *ah_sh; + u16 ah_countryCode; + u32 ah_macVersion; + u16 ah_macRev; + u16 ah_phyRev; + u16 ah_analog5GhzRev; + u16 ah_analog2GhzRev; + u8 ah_decompMask[ATH9K_DECOMP_MASK_SIZE]; + u32 ah_flags; + enum ath9k_opmode ah_opmode; + struct hal_ops_config ah_config; + struct hal_capabilities ah_caps; + int16_t ah_powerLimit; + u16 ah_maxPowerLevel; + u32 ah_tpScale; + u16 ah_currentRD; + u16 ah_currentRDExt; + u16 ah_currentRDInUse; + u16 ah_currentRD5G; + u16 ah_currentRD2G; + char ah_iso[4]; + enum start_adhoc_option ah_adHocMode; + bool ah_commonMode; + struct ath9k_channel ah_channels[150]; + u32 ah_nchan; + struct ath9k_channel *ah_curchan; + u16 ah_rfsilent; + bool ah_rfkillEnabled; + bool ah_isPciExpress; + u16 ah_txTrigLevel; +#ifndef ATH_NF_PER_CHAN + struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; +#endif +}; + +enum wireless_mode { + WIRELESS_MODE_11a = 0, + WIRELESS_MODE_11b = 2, + WIRELESS_MODE_11g = 3, + WIRELESS_MODE_11NA_HT20 = 6, + WIRELESS_MODE_11NG_HT20 = 7, + WIRELESS_MODE_11NA_HT40PLUS = 8, + WIRELESS_MODE_11NA_HT40MINUS = 9, + WIRELESS_MODE_11NG_HT40PLUS = 10, + WIRELESS_MODE_11NG_HT40MINUS = 11, + WIRELESS_MODE_MAX +}; + +enum { + ATH9K_MODE_SEL_11A = 0x00001, + ATH9K_MODE_SEL_11B = 0x00002, + ATH9K_MODE_SEL_11G = 0x00004, + ATH9K_MODE_SEL_11NG_HT20 = 0x00008, + ATH9K_MODE_SEL_11NA_HT20 = 0x00010, + ATH9K_MODE_SEL_11NG_HT40PLUS = 0x00020, + ATH9K_MODE_SEL_11NG_HT40MINUS = 0x00040, + ATH9K_MODE_SEL_11NA_HT40PLUS = 0x00080, + ATH9K_MODE_SEL_11NA_HT40MINUS = 0x00100, + ATH9K_MODE_SEL_2GHZ = (ATH9K_MODE_SEL_11B | + ATH9K_MODE_SEL_11G | + ATH9K_MODE_SEL_11NG_HT20), + ATH9K_MODE_SEL_5GHZ = (ATH9K_MODE_SEL_11A | + ATH9K_MODE_SEL_11NA_HT20), + ATH9K_MODE_SEL_ALL = 0xffffffff +}; + +struct chan_centers { + u16 synth_center; + u16 ctl_center; + u16 ext_center; +}; + +int ath_hal_getcapability(struct ath_hal *ah, + enum hal_capability_type type, + u32 capability, + u32 *result); +const struct ath9k_rate_table *ath9k_hw_getratetable(struct ath_hal *ah, + u32 mode); +void ath9k_hw_detach(struct ath_hal *ah); +struct ath_hal *ath9k_hw_attach(u16 devid, + struct ath_softc *sc, + void __iomem *mem, + int *error); +bool ath9k_regd_init_channels(struct ath_hal *ah, + u32 maxchans, u32 *nchans, + u8 *regclassids, + u32 maxregids, u32 *nregids, + u16 cc, u32 modeSelect, + bool enableOutdoor, + bool enableExtendedChannels); +u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags); +enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah, + enum ath9k_int ints); +bool ath9k_hw_reset(struct ath_hal *ah, enum ath9k_opmode opmode, + struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode, + u8 txchainmask, u8 rxchainmask, + enum ath9k_ht_extprotspacing extprotspacing, + bool bChannelChange, + int *status); +bool ath9k_hw_phy_disable(struct ath_hal *ah); +void ath9k_hw_reset_calvalid(struct ath_hal *ah, struct ath9k_channel *chan, + bool *isCalDone); +void ath9k_hw_ani_monitor(struct ath_hal *ah, + const struct ath9k_node_stats *stats, + struct ath9k_channel *chan); +bool ath9k_hw_calibrate(struct ath_hal *ah, + struct ath9k_channel *chan, + u8 rxchainmask, + bool longcal, + bool *isCalDone); +int16_t ath9k_hw_getchan_noise(struct ath_hal *ah, + struct ath9k_channel *chan); +void ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid, + u16 assocId); +void ath9k_hw_setrxfilter(struct ath_hal *ah, u32 bits); +void ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid, + u16 assocId); +bool ath9k_hw_stoptxdma(struct ath_hal *ah, u32 q); +void ath9k_hw_reset_tsf(struct ath_hal *ah); +bool ath9k_hw_keyisvalid(struct ath_hal *ah, u16 entry); +bool ath9k_hw_keysetmac(struct ath_hal *ah, u16 entry, + const u8 *mac); +bool ath9k_hw_set_keycache_entry(struct ath_hal *ah, + u16 entry, + const struct ath9k_keyval *k, + const u8 *mac, + int xorKey); +bool ath9k_hw_set_tsfadjust(struct ath_hal *ah, + u32 setting); +void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore); +bool ath9k_hw_intrpend(struct ath_hal *ah); +bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked); +bool ath9k_hw_updatetxtriglevel(struct ath_hal *ah, + bool bIncTrigLevel); +void ath9k_hw_procmibevent(struct ath_hal *ah, + const struct ath9k_node_stats *stats); +bool ath9k_hw_setrxabort(struct ath_hal *ah, bool set); +void ath9k_hw_set11nmac2040(struct ath_hal *ah, enum ath9k_ht_macmode mode); +bool ath9k_hw_phycounters(struct ath_hal *ah); +bool ath9k_hw_keyreset(struct ath_hal *ah, u16 entry); +bool ath9k_hw_getcapability(struct ath_hal *ah, + enum hal_capability_type type, + u32 capability, + u32 *result); +bool ath9k_hw_setcapability(struct ath_hal *ah, + enum hal_capability_type type, + u32 capability, + u32 setting, + int *status); +u32 ath9k_hw_getdefantenna(struct ath_hal *ah); +void ath9k_hw_getmac(struct ath_hal *ah, u8 *mac); +void ath9k_hw_getbssidmask(struct ath_hal *ah, u8 *mask); +bool ath9k_hw_setbssidmask(struct ath_hal *ah, + const u8 *mask); +bool ath9k_hw_setpower(struct ath_hal *ah, + enum ath9k_power_mode mode); +enum ath9k_int ath9k_hw_intrget(struct ath_hal *ah); +u64 ath9k_hw_gettsf64(struct ath_hal *ah); +u32 ath9k_hw_getdefantenna(struct ath_hal *ah); +bool ath9k_hw_setslottime(struct ath_hal *ah, u32 us); +bool ath9k_hw_setantennaswitch(struct ath_hal *ah, + enum ath9k_ant_setting settings, + struct ath9k_channel *chan, + u8 *tx_chainmask, + u8 *rx_chainmask, + u8 *antenna_cfgd); +void ath9k_hw_setantenna(struct ath_hal *ah, u32 antenna); +int ath9k_hw_select_antconfig(struct ath_hal *ah, + u32 cfg); +bool ath9k_hw_puttxbuf(struct ath_hal *ah, u32 q, + u32 txdp); +bool ath9k_hw_txstart(struct ath_hal *ah, u32 q); +u16 ath9k_hw_computetxtime(struct ath_hal *ah, + const struct ath9k_rate_table *rates, + u32 frameLen, u16 rateix, + bool shortPreamble); +void ath9k_hw_set11n_ratescenario(struct ath_hal *ah, struct ath_desc *ds, + struct ath_desc *lastds, + u32 durUpdateEn, u32 rtsctsRate, + u32 rtsctsDuration, + struct ath9k_11n_rate_series series[], + u32 nseries, u32 flags); +void ath9k_hw_set11n_burstduration(struct ath_hal *ah, + struct ath_desc *ds, + u32 burstDuration); +void ath9k_hw_cleartxdesc(struct ath_hal *ah, struct ath_desc *ds); +u32 ath9k_hw_reverse_bits(u32 val, u32 n); +bool ath9k_hw_resettxqueue(struct ath_hal *ah, u32 q); +u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan); +u32 ath9k_regd_get_antenna_allowed(struct ath_hal *ah, + struct ath9k_channel *chan); +u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags); +bool ath9k_hw_gettxqueueprops(struct ath_hal *ah, int q, + struct ath9k_txq_info *qInfo); +bool ath9k_hw_settxqueueprops(struct ath_hal *ah, int q, + const struct ath9k_txq_info *qInfo); +struct ath9k_channel *ath9k_regd_check_channel(struct ath_hal *ah, + const struct ath9k_channel *c); +void ath9k_hw_set11n_txdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 pktLen, enum ath9k_pkt_type type, + u32 txPower, u32 keyIx, + enum ath9k_key_type keyType, u32 flags); +bool ath9k_hw_filltxdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 segLen, bool firstSeg, + bool lastSeg, + const struct ath_desc *ds0); +u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hal *ah, + u32 *rxc_pcnt, + u32 *rxf_pcnt, + u32 *txf_pcnt); +void ath9k_hw_dmaRegDump(struct ath_hal *ah); +void ath9k_hw_beaconinit(struct ath_hal *ah, + u32 next_beacon, u32 beacon_period); +void ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah, + const struct ath9k_beacon_state *bs); +bool ath9k_hw_setuprxdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 size, u32 flags); +void ath9k_hw_putrxbuf(struct ath_hal *ah, u32 rxdp); +void ath9k_hw_rxena(struct ath_hal *ah); +void ath9k_hw_setopmode(struct ath_hal *ah); +bool ath9k_hw_setmac(struct ath_hal *ah, const u8 *mac); +void ath9k_hw_setmcastfilter(struct ath_hal *ah, u32 filter0, + u32 filter1); +u32 ath9k_hw_getrxfilter(struct ath_hal *ah); +void ath9k_hw_startpcureceive(struct ath_hal *ah); +void ath9k_hw_stoppcurecv(struct ath_hal *ah); +bool ath9k_hw_stopdmarecv(struct ath_hal *ah); +int ath9k_hw_rxprocdesc(struct ath_hal *ah, + struct ath_desc *ds, u32 pa, + struct ath_desc *nds, u64 tsf); +u32 ath9k_hw_gettxbuf(struct ath_hal *ah, u32 q); +int ath9k_hw_txprocdesc(struct ath_hal *ah, + struct ath_desc *ds); +void ath9k_hw_set11n_aggr_middle(struct ath_hal *ah, struct ath_desc *ds, + u32 numDelims); +void ath9k_hw_set11n_aggr_first(struct ath_hal *ah, struct ath_desc *ds, + u32 aggrLen); +void ath9k_hw_set11n_aggr_last(struct ath_hal *ah, struct ath_desc *ds); +bool ath9k_hw_releasetxqueue(struct ath_hal *ah, u32 q); +void ath9k_hw_gettxintrtxqs(struct ath_hal *ah, u32 *txqs); +void ath9k_hw_clr11n_aggr(struct ath_hal *ah, struct ath_desc *ds); +void ath9k_hw_set11n_virtualmorefrag(struct ath_hal *ah, + struct ath_desc *ds, u32 vmf); +bool ath9k_hw_set_txpowerlimit(struct ath_hal *ah, u32 limit); +bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah); +int ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type, + const struct ath9k_txq_info *qInfo); +u32 ath9k_hw_numtxpending(struct ath_hal *ah, u32 q); +const char *ath9k_hw_probe(u16 vendorid, u16 devid); +bool ath9k_hw_disable(struct ath_hal *ah); +void ath9k_hw_rfdetach(struct ath_hal *ah); +void ath9k_hw_get_channel_centers(struct ath_hal *ah, + struct ath9k_channel *chan, + struct chan_centers *centers); +bool ath9k_get_channel_edges(struct ath_hal *ah, + u16 flags, u16 *low, + u16 *high); +#endif diff --git a/drivers/net/wireless/ath9k/beacon.c b/drivers/net/wireless/ath9k/beacon.c new file mode 100644 index 000000000000..00993f828c5c --- /dev/null +++ b/drivers/net/wireless/ath9k/beacon.c @@ -0,0 +1,977 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + /* Implementation of beacon processing. */ + +#include +#include "core.h" + +/* + * Configure parameters for the beacon queue + * + * This function will modify certain transmit queue properties depending on + * the operating mode of the station (AP or AdHoc). Parameters are AIFS + * settings and channel width min/max +*/ + +static int ath_beaconq_config(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath9k_txq_info qi; + + ath9k_hw_gettxqueueprops(ah, sc->sc_bhalq, &qi); + if (sc->sc_opmode == ATH9K_M_HOSTAP) { + /* Always burst out beacon and CAB traffic. */ + qi.tqi_aifs = 1; + qi.tqi_cwmin = 0; + qi.tqi_cwmax = 0; + } else { + /* Adhoc mode; important thing is to use 2x cwmin. */ + qi.tqi_aifs = sc->sc_beacon_qi.tqi_aifs; + qi.tqi_cwmin = 2*sc->sc_beacon_qi.tqi_cwmin; + qi.tqi_cwmax = sc->sc_beacon_qi.tqi_cwmax; + } + + if (!ath9k_hw_settxqueueprops(ah, sc->sc_bhalq, &qi)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to update h/w beacon queue parameters\n", + __func__); + return 0; + } else { + ath9k_hw_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */ + return 1; + } +} + +/* + * Setup the beacon frame for transmit. + * + * Associates the beacon frame buffer with a transmit descriptor. Will set + * up all required antenna switch parameters, rate codes, and channel flags. + * Beacons are always sent out at the lowest rate, and are not retried. +*/ + +static void ath_beacon_setup(struct ath_softc *sc, + struct ath_vap *avp, struct ath_buf *bf) +{ + struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu; + struct ath_hal *ah = sc->sc_ah; + struct ath_desc *ds; + int flags, antenna; + const struct ath9k_rate_table *rt; + u8 rix, rate; + int ctsrate = 0; + int ctsduration = 0; + struct ath9k_11n_rate_series series[4]; + + DPRINTF(sc, ATH_DBG_BEACON, "%s: m %p len %u\n", + __func__, skb, skb->len); + + /* setup descriptors */ + ds = bf->bf_desc; + + flags = ATH9K_TXDESC_NOACK; + + if (sc->sc_opmode == ATH9K_M_IBSS && ah->ah_caps.halVEOLSupport) { + ds->ds_link = bf->bf_daddr; /* self-linked */ + flags |= ATH9K_TXDESC_VEOL; + /* Let hardware handle antenna switching. */ + antenna = 0; + } else { + ds->ds_link = 0; + /* + * Switch antenna every beacon. + * Should only switch every beacon period, not for every + * SWBA's + * XXX assumes two antenna + */ + antenna = ((sc->ast_be_xmit / sc->sc_nbcnvaps) & 1 ? 2 : 1); + } + + ds->ds_data = bf->bf_buf_addr; + + /* + * Calculate rate code. + * XXX everything at min xmit rate + */ + rix = sc->sc_minrateix; + rt = sc->sc_currates; + rate = rt->info[rix].rateCode; + if (sc->sc_flags & ATH_PREAMBLE_SHORT) + rate |= rt->info[rix].shortPreamble; + + ath9k_hw_set11n_txdesc(ah, ds + , skb->len + FCS_LEN /* frame length */ + , ATH9K_PKT_TYPE_BEACON /* Atheros packet type */ + , avp->av_btxctl.txpower /* txpower XXX */ + , ATH9K_TXKEYIX_INVALID /* no encryption */ + , ATH9K_KEY_TYPE_CLEAR /* no encryption */ + , flags /* no ack, veol for beacons */ + ); + + /* NB: beacon's BufLen must be a multiple of 4 bytes */ + ath9k_hw_filltxdesc(ah, ds + , roundup(skb->len, 4) /* buffer length */ + , true /* first segment */ + , true /* last segment */ + , ds /* first descriptor */ + ); + + memzero(series, sizeof(struct ath9k_11n_rate_series) * 4); + series[0].Tries = 1; + series[0].Rate = rate; + series[0].ChSel = sc->sc_tx_chainmask; + series[0].RateFlags = (ctsrate) ? ATH9K_RATESERIES_RTS_CTS : 0; + ath9k_hw_set11n_ratescenario(ah, ds, ds, 0, + ctsrate, ctsduration, series, 4, 0); +} + +/* Move everything from the vap's mcast queue to the hardware cab queue. + * Caller must hold mcasq lock and cabq lock + * XXX MORE_DATA bit? + */ +static void empty_mcastq_into_cabq(struct ath_hal *ah, + struct ath_txq *mcastq, struct ath_txq *cabq) +{ + struct ath_buf *bfmcast; + + BUG_ON(list_empty(&mcastq->axq_q)); + + bfmcast = list_first_entry(&mcastq->axq_q, struct ath_buf, list); + + /* link the descriptors */ + if (!cabq->axq_link) + ath9k_hw_puttxbuf(ah, cabq->axq_qnum, bfmcast->bf_daddr); + else + *cabq->axq_link = bfmcast->bf_daddr; + + /* append the private vap mcast list to the cabq */ + + cabq->axq_depth += mcastq->axq_depth; + cabq->axq_totalqueued += mcastq->axq_totalqueued; + cabq->axq_linkbuf = mcastq->axq_linkbuf; + cabq->axq_link = mcastq->axq_link; + list_splice_tail_init(&mcastq->axq_q, &cabq->axq_q); + mcastq->axq_depth = 0; + mcastq->axq_totalqueued = 0; + mcastq->axq_linkbuf = NULL; + mcastq->axq_link = NULL; +} + +/* This is only run at DTIM. We move everything from the vap's mcast queue + * to the hardware cab queue. Caller must hold the mcastq lock. */ +static void trigger_mcastq(struct ath_hal *ah, + struct ath_txq *mcastq, struct ath_txq *cabq) +{ + spin_lock_bh(&cabq->axq_lock); + + if (!list_empty(&mcastq->axq_q)) + empty_mcastq_into_cabq(ah, mcastq, cabq); + + /* cabq is gated by beacon so it is safe to start here */ + if (!list_empty(&cabq->axq_q)) + ath9k_hw_txstart(ah, cabq->axq_qnum); + + spin_unlock_bh(&cabq->axq_lock); +} + +/* + * Generate beacon frame and queue cab data for a vap. + * + * Updates the contents of the beacon frame. It is assumed that the buffer for + * the beacon frame has been allocated in the ATH object, and simply needs to + * be filled for this cycle. Also, any CAB (crap after beacon?) traffic will + * be added to the beacon frame at this point. +*/ +static struct ath_buf *ath_beacon_generate(struct ath_softc *sc, int if_id) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf; + struct ath_vap *avp; + struct sk_buff *skb; + int cabq_depth; + int mcastq_depth; + int is_beacon_dtim = 0; + unsigned int curlen; + struct ath_txq *cabq; + struct ath_txq *mcastq; + avp = sc->sc_vaps[if_id]; + + mcastq = &avp->av_mcastq; + cabq = sc->sc_cabq; + + ASSERT(avp); + + if (avp->av_bcbuf == NULL) { + DPRINTF(sc, ATH_DBG_BEACON, "%s: avp=%p av_bcbuf=%p\n", + __func__, avp, avp->av_bcbuf); + return NULL; + } + bf = avp->av_bcbuf; + skb = (struct sk_buff *) bf->bf_mpdu; + + /* + * Update dynamic beacon contents. If this returns + * non-zero then we need to remap the memory because + * the beacon frame changed size (probably because + * of the TIM bitmap). + */ + curlen = skb->len; + + /* XXX: spin_lock_bh should not be used here, but sparse bitches + * otherwise. We should fix sparse :) */ + spin_lock_bh(&mcastq->axq_lock); + mcastq_depth = avp->av_mcastq.axq_depth; + + if (ath_update_beacon(sc, if_id, &avp->av_boff, skb, mcastq_depth) == + 1) { + ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + bf->bf_buf_addr = ath_skb_map_single(sc, skb, PCI_DMA_TODEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + } else { + pci_dma_sync_single_for_cpu(sc->pdev, + bf->bf_buf_addr, + skb_tailroom(skb), + PCI_DMA_TODEVICE); + } + + /* + * if the CABQ traffic from previous DTIM is pending and the current + * beacon is also a DTIM. + * 1) if there is only one vap let the cab traffic continue. + * 2) if there are more than one vap and we are using staggered + * beacons, then drain the cabq by dropping all the frames in + * the cabq so that the current vaps cab traffic can be scheduled. + */ + spin_lock_bh(&cabq->axq_lock); + cabq_depth = cabq->axq_depth; + spin_unlock_bh(&cabq->axq_lock); + + is_beacon_dtim = avp->av_boff.bo_tim[4] & 1; + + if (mcastq_depth && is_beacon_dtim && cabq_depth) { + /* + * Unlock the cabq lock as ath_tx_draintxq acquires + * the lock again which is a common function and that + * acquires txq lock inside. + */ + if (sc->sc_nvaps > 1) { + ath_tx_draintxq(sc, cabq, false); + DPRINTF(sc, ATH_DBG_BEACON, + "%s: flush previous cabq traffic\n", __func__); + } + } + + /* Construct tx descriptor. */ + ath_beacon_setup(sc, avp, bf); + + /* + * Enable the CAB queue before the beacon queue to + * insure cab frames are triggered by this beacon. + */ + if (is_beacon_dtim) + trigger_mcastq(ah, mcastq, cabq); + + spin_unlock_bh(&mcastq->axq_lock); + return bf; +} + +/* + * Startup beacon transmission for adhoc mode when they are sent entirely + * by the hardware using the self-linked descriptor + veol trick. +*/ + +static void ath_beacon_start_adhoc(struct ath_softc *sc, int if_id) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf; + struct ath_vap *avp; + struct sk_buff *skb; + + avp = sc->sc_vaps[if_id]; + ASSERT(avp); + + if (avp->av_bcbuf == NULL) { + DPRINTF(sc, ATH_DBG_BEACON, "%s: avp=%p av_bcbuf=%p\n", + __func__, avp, avp != NULL ? avp->av_bcbuf : NULL); + return; + } + bf = avp->av_bcbuf; + skb = (struct sk_buff *) bf->bf_mpdu; + + /* Construct tx descriptor. */ + ath_beacon_setup(sc, avp, bf); + + /* NB: caller is known to have already stopped tx dma */ + ath9k_hw_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr); + ath9k_hw_txstart(ah, sc->sc_bhalq); + DPRINTF(sc, ATH_DBG_BEACON, "%s: TXDP%u = %llx (%p)\n", __func__, + sc->sc_bhalq, ito64(bf->bf_daddr), bf->bf_desc); +} + +/* + * Setup a h/w transmit queue for beacons. + * + * This function allocates an information structure (struct ath9k_txq_info) + * on the stack, sets some specific parameters (zero out channel width + * min/max, and enable aifs). The info structure does not need to be + * persistant. +*/ + +int ath_beaconq_setup(struct ath_hal *ah) +{ + struct ath9k_txq_info qi; + + memzero(&qi, sizeof(qi)); + qi.tqi_aifs = 1; + qi.tqi_cwmin = 0; + qi.tqi_cwmax = 0; + /* NB: don't enable any interrupts */ + return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi); +} + + +/* + * Allocate and setup an initial beacon frame. + * + * Allocate a beacon state variable for a specific VAP instance created on + * the ATH interface. This routine also calculates the beacon "slot" for + * staggared beacons in the mBSSID case. +*/ + +int ath_beacon_alloc(struct ath_softc *sc, int if_id) +{ + struct ath_vap *avp; + struct ieee80211_hdr *wh; + struct ath_buf *bf; + struct sk_buff *skb; + + avp = sc->sc_vaps[if_id]; + ASSERT(avp); + + /* Allocate a beacon descriptor if we haven't done so. */ + if (!avp->av_bcbuf) { + /* + * Allocate beacon state for hostap/ibss. We know + * a buffer is available. + */ + + avp->av_bcbuf = list_first_entry(&sc->sc_bbuf, + struct ath_buf, list); + list_del(&avp->av_bcbuf->list); + + if (sc->sc_opmode == ATH9K_M_HOSTAP || + !sc->sc_ah->ah_caps.halVEOLSupport) { + int slot; + /* + * Assign the vap to a beacon xmit slot. As + * above, this cannot fail to find one. + */ + avp->av_bslot = 0; + for (slot = 0; slot < ATH_BCBUF; slot++) + if (sc->sc_bslot[slot] == ATH_IF_ID_ANY) { + /* + * XXX hack, space out slots to better + * deal with misses + */ + if (slot+1 < ATH_BCBUF && + sc->sc_bslot[slot+1] == + ATH_IF_ID_ANY) { + avp->av_bslot = slot+1; + break; + } + avp->av_bslot = slot; + /* NB: keep looking for a double slot */ + } + BUG_ON(sc->sc_bslot[avp->av_bslot] != ATH_IF_ID_ANY); + sc->sc_bslot[avp->av_bslot] = if_id; + sc->sc_nbcnvaps++; + } + } + + /* release the previous beacon frame , if it already exists. */ + bf = avp->av_bcbuf; + if (bf->bf_mpdu != NULL) { + skb = (struct sk_buff *)bf->bf_mpdu; + ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + dev_kfree_skb_any(skb); + bf->bf_mpdu = NULL; + } + + /* + * NB: the beacon data buffer must be 32-bit aligned; + * we assume the wbuf routines will return us something + * with this alignment (perhaps should assert). + * FIXME: Fill avp->av_boff.bo_tim,avp->av_btxctl.txpower and + * avp->av_btxctl.shortPreamble + */ + skb = ieee80211_beacon_get(sc->hw, avp->av_if_data); + if (skb == NULL) { + DPRINTF(sc, ATH_DBG_BEACON, "%s: cannot get skb\n", + __func__); + return -ENOMEM; + } + + /* + * Calculate a TSF adjustment factor required for + * staggered beacons. Note that we assume the format + * of the beacon frame leaves the tstamp field immediately + * following the header. + */ + if (avp->av_bslot > 0) { + u64 tsfadjust; + __le64 val; + int intval; + + /* FIXME: Use default value for now: Sujith */ + + intval = ATH_DEFAULT_BINTVAL; + + /* + * The beacon interval is in TU's; the TSF in usecs. + * We figure out how many TU's to add to align the + * timestamp then convert to TSF units and handle + * byte swapping before writing it in the frame. + * The hardware will then add this each time a beacon + * frame is sent. Note that we align vap's 1..N + * and leave vap 0 untouched. This means vap 0 + * has a timestamp in one beacon interval while the + * others get a timestamp aligned to the next interval. + */ + tsfadjust = (intval * (ATH_BCBUF - avp->av_bslot)) / ATH_BCBUF; + val = cpu_to_le64(tsfadjust << 10); /* TU->TSF */ + + DPRINTF(sc, ATH_DBG_BEACON, + "%s: %s beacons, bslot %d intval %u tsfadjust %llu\n", + __func__, "stagger", + avp->av_bslot, intval, (unsigned long long)tsfadjust); + + wh = (struct ieee80211_hdr *)skb->data; + memcpy(&wh[1], &val, sizeof(val)); + } + + bf->bf_buf_addr = ath_skb_map_single(sc, skb, PCI_DMA_TODEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + bf->bf_mpdu = skb; + + return 0; +} + +/* + * Reclaim beacon resources and return buffer to the pool. + * + * Checks the VAP to put the beacon frame buffer back to the ATH object + * queue, and de-allocates any wbuf frames that were sent as CAB traffic. +*/ + +void ath_beacon_return(struct ath_softc *sc, struct ath_vap *avp) +{ + if (avp->av_bcbuf != NULL) { + struct ath_buf *bf; + + if (avp->av_bslot != -1) { + sc->sc_bslot[avp->av_bslot] = ATH_IF_ID_ANY; + sc->sc_nbcnvaps--; + } + + bf = avp->av_bcbuf; + if (bf->bf_mpdu != NULL) { + struct sk_buff *skb = (struct sk_buff *)bf->bf_mpdu; + ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + dev_kfree_skb_any(skb); + bf->bf_mpdu = NULL; + } + list_add_tail(&bf->list, &sc->sc_bbuf); + + avp->av_bcbuf = NULL; + } +} + +/* + * Reclaim beacon resources and return buffer to the pool. + * + * This function will free any wbuf frames that are still attached to the + * beacon buffers in the ATH object. Note that this does not de-allocate + * any wbuf objects that are in the transmit queue and have not yet returned + * to the ATH object. +*/ + +void ath_beacon_free(struct ath_softc *sc) +{ + struct ath_buf *bf; + + list_for_each_entry(bf, &sc->sc_bbuf, list) { + if (bf->bf_mpdu != NULL) { + struct sk_buff *skb = (struct sk_buff *) bf->bf_mpdu; + ath_skb_unmap_single(sc, skb, PCI_DMA_TODEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + dev_kfree_skb_any(skb); + bf->bf_mpdu = NULL; + } + } +} + +/* + * Tasklet for Sending Beacons + * + * Transmit one or more beacon frames at SWBA. Dynamic updates to the frame + * contents are done as needed and the slot time is also adjusted based on + * current state. + * + * This tasklet is not scheduled, it's called in ISR context. +*/ + +void ath9k_beacon_tasklet(unsigned long data) +{ +#define TSF_TO_TU(_h,_l) \ + ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) + + struct ath_softc *sc = (struct ath_softc *)data; + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf = NULL; + int slot, if_id; + u32 bfaddr; + u32 rx_clear = 0, rx_frame = 0, tx_frame = 0; + u32 show_cycles = 0; + u32 bc = 0; /* beacon count */ + u64 tsf; + u32 tsftu; + u16 intval; + + if (sc->sc_noreset) { + show_cycles = ath9k_hw_GetMibCycleCountsPct(ah, + &rx_clear, + &rx_frame, + &tx_frame); + } + + /* + * Check if the previous beacon has gone out. If + * not don't try to post another, skip this period + * and wait for the next. Missed beacons indicate + * a problem and should not occur. If we miss too + * many consecutive beacons reset the device. + */ + if (ath9k_hw_numtxpending(ah, sc->sc_bhalq) != 0) { + sc->sc_bmisscount++; + /* XXX: doth needs the chanchange IE countdown decremented. + * We should consider adding a mac80211 call to indicate + * a beacon miss so appropriate action could be taken + * (in that layer). + */ + if (sc->sc_bmisscount < BSTUCK_THRESH) { + if (sc->sc_noreset) { + DPRINTF(sc, ATH_DBG_BEACON, + "%s: missed %u consecutive beacons\n", + __func__, sc->sc_bmisscount); + if (show_cycles) { + /* + * Display cycle counter stats + * from HW to aide in debug of + * stickiness. + */ + DPRINTF(sc, + ATH_DBG_BEACON, + "%s: busy times: rx_clear=%d, " + "rx_frame=%d, tx_frame=%d\n", + __func__, rx_clear, rx_frame, + tx_frame); + } else { + DPRINTF(sc, + ATH_DBG_BEACON, + "%s: unable to obtain " + "busy times\n", __func__); + } + } else { + DPRINTF(sc, ATH_DBG_BEACON, + "%s: missed %u consecutive beacons\n", + __func__, sc->sc_bmisscount); + } + } else if (sc->sc_bmisscount >= BSTUCK_THRESH) { + if (sc->sc_noreset) { + if (sc->sc_bmisscount == BSTUCK_THRESH) { + DPRINTF(sc, + ATH_DBG_BEACON, + "%s: beacon is officially " + "stuck\n", __func__); + ath9k_hw_dmaRegDump(ah); + } + } else { + DPRINTF(sc, ATH_DBG_BEACON, + "%s: beacon is officially stuck\n", + __func__); + ath_bstuck_process(sc); + } + } + + return; + } + if (sc->sc_bmisscount != 0) { + if (sc->sc_noreset) { + DPRINTF(sc, + ATH_DBG_BEACON, + "%s: resume beacon xmit after %u misses\n", + __func__, sc->sc_bmisscount); + } else { + DPRINTF(sc, ATH_DBG_BEACON, + "%s: resume beacon xmit after %u misses\n", + __func__, sc->sc_bmisscount); + } + sc->sc_bmisscount = 0; + } + + /* + * Generate beacon frames. we are sending frames + * staggered so calculate the slot for this frame based + * on the tsf to safeguard against missing an swba. + */ + + /* FIXME: Use default value for now - Sujith */ + intval = ATH_DEFAULT_BINTVAL; + + tsf = ath9k_hw_gettsf64(ah); + tsftu = TSF_TO_TU(tsf>>32, tsf); + slot = ((tsftu % intval) * ATH_BCBUF) / intval; + if_id = sc->sc_bslot[(slot + 1) % ATH_BCBUF]; + DPRINTF(sc, ATH_DBG_BEACON, + "%s: slot %d [tsf %llu tsftu %u intval %u] if_id %d\n", + __func__, slot, (unsigned long long) tsf, tsftu, + intval, if_id); + bfaddr = 0; + if (if_id != ATH_IF_ID_ANY) { + bf = ath_beacon_generate(sc, if_id); + if (bf != NULL) { + bfaddr = bf->bf_daddr; + bc = 1; + } + } + /* + * Handle slot time change when a non-ERP station joins/leaves + * an 11g network. The 802.11 layer notifies us via callback, + * we mark updateslot, then wait one beacon before effecting + * the change. This gives associated stations at least one + * beacon interval to note the state change. + * + * NB: The slot time change state machine is clocked according + * to whether we are bursting or staggering beacons. We + * recognize the request to update and record the current + * slot then don't transition until that slot is reached + * again. If we miss a beacon for that slot then we'll be + * slow to transition but we'll be sure at least one beacon + * interval has passed. When bursting slot is always left + * set to ATH_BCBUF so this check is a noop. + */ + /* XXX locking */ + if (sc->sc_updateslot == UPDATE) { + sc->sc_updateslot = COMMIT; /* commit next beacon */ + sc->sc_slotupdate = slot; + } else if (sc->sc_updateslot == COMMIT && sc->sc_slotupdate == slot) + ath_setslottime(sc); /* commit change to hardware */ + + if (bfaddr != 0) { + /* + * Stop any current dma and put the new frame(s) on the queue. + * This should never fail since we check above that no frames + * are still pending on the queue. + */ + if (!ath9k_hw_stoptxdma(ah, sc->sc_bhalq)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: beacon queue %u did not stop?\n", + __func__, sc->sc_bhalq); + /* NB: the HAL still stops DMA, so proceed */ + } + + /* NB: cabq traffic should already be queued and primed */ + ath9k_hw_puttxbuf(ah, sc->sc_bhalq, bfaddr); + ath9k_hw_txstart(ah, sc->sc_bhalq); + + sc->ast_be_xmit += bc; /* XXX per-vap? */ + } +#undef TSF_TO_TU +} + +/* + * Tasklet for Beacon Stuck processing + * + * Processing for Beacon Stuck. + * Basically calls the ath_internal_reset function to reset the chip. +*/ + +void ath_bstuck_process(struct ath_softc *sc) +{ + DPRINTF(sc, ATH_DBG_BEACON, + "%s: stuck beacon; resetting (bmiss count %u)\n", + __func__, sc->sc_bmisscount); + ath_internal_reset(sc); +} + +/* + * Configure the beacon and sleep timers. + * + * When operating as an AP this resets the TSF and sets + * up the hardware to notify us when we need to issue beacons. + * + * When operating in station mode this sets up the beacon + * timers according to the timestamp of the last received + * beacon and the current TSF, configures PCF and DTIM + * handling, programs the sleep registers so the hardware + * will wakeup in time to receive beacons, and configures + * the beacon miss handling so we'll receive a BMISS + * interrupt when we stop seeing beacons from the AP + * we've associated with. + */ + +void ath_beacon_config(struct ath_softc *sc, int if_id) +{ +#define TSF_TO_TU(_h,_l) \ + ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) + struct ath_hal *ah = sc->sc_ah; + u32 nexttbtt, intval; + struct ath_beacon_config conf; + enum ath9k_opmode av_opmode; + + if (if_id != ATH_IF_ID_ANY) + av_opmode = sc->sc_vaps[if_id]->av_opmode; + else + av_opmode = sc->sc_opmode; + + memzero(&conf, sizeof(struct ath_beacon_config)); + + /* FIXME: Use default values for now - Sujith */ + /* Query beacon configuration first */ + /* + * Protocol stack doesn't support dynamic beacon configuration, + * use default configurations. + */ + conf.beacon_interval = ATH_DEFAULT_BINTVAL; + conf.listen_interval = 1; + conf.dtim_period = conf.beacon_interval; + conf.dtim_count = 1; + conf.bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf.beacon_interval; + + /* extract tstamp from last beacon and convert to TU */ + nexttbtt = TSF_TO_TU(get_unaligned_le32(conf.u.last_tstamp + 4), + get_unaligned_le32(conf.u.last_tstamp)); + /* XXX conditionalize multi-bss support? */ + if (sc->sc_opmode == ATH9K_M_HOSTAP) { + /* + * For multi-bss ap support beacons are either staggered + * evenly over N slots or burst together. For the former + * arrange for the SWBA to be delivered for each slot. + * Slots that are not occupied will generate nothing. + */ + /* NB: the beacon interval is kept internally in TU's */ + intval = conf.beacon_interval & ATH9K_BEACON_PERIOD; + intval /= ATH_BCBUF; /* for staggered beacons */ + } else { + intval = conf.beacon_interval & ATH9K_BEACON_PERIOD; + } + + if (nexttbtt == 0) /* e.g. for ap mode */ + nexttbtt = intval; + else if (intval) /* NB: can be 0 for monitor mode */ + nexttbtt = roundup(nexttbtt, intval); + DPRINTF(sc, ATH_DBG_BEACON, "%s: nexttbtt %u intval %u (%u)\n", + __func__, nexttbtt, intval, conf.beacon_interval); + /* Check for ATH9K_M_HOSTAP and sc_nostabeacons for WDS client */ + if (sc->sc_opmode == ATH9K_M_STA) { + struct ath9k_beacon_state bs; + u64 tsf; + u32 tsftu; + int dtimperiod, dtimcount, sleepduration; + int cfpperiod, cfpcount; + + /* + * Setup dtim and cfp parameters according to + * last beacon we received (which may be none). + */ + dtimperiod = conf.dtim_period; + if (dtimperiod <= 0) /* NB: 0 if not known */ + dtimperiod = 1; + dtimcount = conf.dtim_count; + if (dtimcount >= dtimperiod) /* NB: sanity check */ + dtimcount = 0; /* XXX? */ + cfpperiod = 1; /* NB: no PCF support yet */ + cfpcount = 0; + + sleepduration = conf.listen_interval * intval; + if (sleepduration <= 0) + sleepduration = intval; + +#define FUDGE 2 + /* + * Pull nexttbtt forward to reflect the current + * TSF and calculate dtim+cfp state for the result. + */ + tsf = ath9k_hw_gettsf64(ah); + tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; + do { + nexttbtt += intval; + if (--dtimcount < 0) { + dtimcount = dtimperiod - 1; + if (--cfpcount < 0) + cfpcount = cfpperiod - 1; + } + } while (nexttbtt < tsftu); +#undef FUDGE + memzero(&bs, sizeof(bs)); + bs.bs_intval = intval; + bs.bs_nexttbtt = nexttbtt; + bs.bs_dtimperiod = dtimperiod*intval; + bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; + bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; + bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; + bs.bs_cfpmaxduration = 0; + /* + * Calculate the number of consecutive beacons to miss + * before taking a BMISS interrupt. The configuration + * is specified in TU so we only need calculate based + * on the beacon interval. Note that we clamp the + * result to at most 15 beacons. + */ + if (sleepduration > intval) { + bs.bs_bmissthreshold = + conf.listen_interval * + ATH_DEFAULT_BMISS_LIMIT / 2; + } else { + bs.bs_bmissthreshold = + DIV_ROUND_UP(conf.bmiss_timeout, intval); + if (bs.bs_bmissthreshold > 15) + bs.bs_bmissthreshold = 15; + else if (bs.bs_bmissthreshold <= 0) + bs.bs_bmissthreshold = 1; + } + + /* + * Calculate sleep duration. The configuration is + * given in ms. We insure a multiple of the beacon + * period is used. Also, if the sleep duration is + * greater than the DTIM period then it makes senses + * to make it a multiple of that. + * + * XXX fixed at 100ms + */ + + bs.bs_sleepduration = + roundup(IEEE80211_MS_TO_TU(100), sleepduration); + if (bs.bs_sleepduration > bs.bs_dtimperiod) + bs.bs_sleepduration = bs.bs_dtimperiod; + + DPRINTF(sc, ATH_DBG_BEACON, + "%s: tsf %llu " + "tsf:tu %u " + "intval %u " + "nexttbtt %u " + "dtim %u " + "nextdtim %u " + "bmiss %u " + "sleep %u " + "cfp:period %u " + "maxdur %u " + "next %u " + "timoffset %u\n" + , __func__ + , (unsigned long long)tsf, tsftu + , bs.bs_intval + , bs.bs_nexttbtt + , bs.bs_dtimperiod + , bs.bs_nextdtim + , bs.bs_bmissthreshold + , bs.bs_sleepduration + , bs.bs_cfpperiod + , bs.bs_cfpmaxduration + , bs.bs_cfpnext + , bs.bs_timoffset + ); + + ath9k_hw_set_interrupts(ah, 0); + ath9k_hw_set_sta_beacon_timers(ah, &bs); + sc->sc_imask |= ATH9K_INT_BMISS; + ath9k_hw_set_interrupts(ah, sc->sc_imask); + } else { + u64 tsf; + u32 tsftu; + ath9k_hw_set_interrupts(ah, 0); + if (nexttbtt == intval) + intval |= ATH9K_BEACON_RESET_TSF; + if (sc->sc_opmode == ATH9K_M_IBSS) { + /* + * Pull nexttbtt forward to reflect the current + * TSF . + */ +#define FUDGE 2 + if (!(intval & ATH9K_BEACON_RESET_TSF)) { + tsf = ath9k_hw_gettsf64(ah); + tsftu = TSF_TO_TU((u32)(tsf>>32), + (u32)tsf) + FUDGE; + do { + nexttbtt += intval; + } while (nexttbtt < tsftu); + } +#undef FUDGE + DPRINTF(sc, ATH_DBG_BEACON, + "%s: IBSS nexttbtt %u intval %u (%u)\n", + __func__, nexttbtt, + intval & ~ATH9K_BEACON_RESET_TSF, + conf.beacon_interval); + + /* + * In IBSS mode enable the beacon timers but only + * enable SWBA interrupts if we need to manually + * prepare beacon frames. Otherwise we use a + * self-linked tx descriptor and let the hardware + * deal with things. + */ + intval |= ATH9K_BEACON_ENA; + if (!ah->ah_caps.halVEOLSupport) + sc->sc_imask |= ATH9K_INT_SWBA; + ath_beaconq_config(sc); + } else if (sc->sc_opmode == ATH9K_M_HOSTAP) { + /* + * In AP mode we enable the beacon timers and + * SWBA interrupts to prepare beacon frames. + */ + intval |= ATH9K_BEACON_ENA; + sc->sc_imask |= ATH9K_INT_SWBA; /* beacon prepare */ + ath_beaconq_config(sc); + } + ath9k_hw_beaconinit(ah, nexttbtt, intval); + sc->sc_bmisscount = 0; + ath9k_hw_set_interrupts(ah, sc->sc_imask); + /* + * When using a self-linked beacon descriptor in + * ibss mode load it once here. + */ + if (sc->sc_opmode == ATH9K_M_IBSS && ah->ah_caps.halVEOLSupport) + ath_beacon_start_adhoc(sc, 0); + } +#undef TSF_TO_TU +} + +/* Function to collect beacon rssi data and resync beacon if necessary */ + +void ath_beacon_sync(struct ath_softc *sc, int if_id) +{ + /* + * Resync beacon timers using the tsf of the + * beacon frame we just received. + */ + ath_beacon_config(sc, if_id); + sc->sc_beacons = 1; +} diff --git a/drivers/net/wireless/ath9k/core.c b/drivers/net/wireless/ath9k/core.c new file mode 100644 index 000000000000..e8b3f1fabb31 --- /dev/null +++ b/drivers/net/wireless/ath9k/core.c @@ -0,0 +1,1961 @@ +/* + * Copyright (c) 2008, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + /* Implementation of the main "ATH" layer. */ + +#include "core.h" +#include "regd.h" + +static int ath_outdoor; /* enable outdoor use */ + +static const u8 ath_bcast_mac[ETH_ALEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +static u32 ath_chainmask_sel_up_rssi_thres = + ATH_CHAINMASK_SEL_UP_RSSI_THRES; +static u32 ath_chainmask_sel_down_rssi_thres = + ATH_CHAINMASK_SEL_DOWN_RSSI_THRES; +static u32 ath_chainmask_sel_period = + ATH_CHAINMASK_SEL_TIMEOUT; + +/* return bus cachesize in 4B word units */ + +static void bus_read_cachesize(struct ath_softc *sc, int *csz) +{ + u8 u8tmp; + + pci_read_config_byte(sc->pdev, PCI_CACHE_LINE_SIZE, (u8 *)&u8tmp); + *csz = (int)u8tmp; + + /* + * This check was put in to avoid "unplesant" consequences if + * the bootrom has not fully initialized all PCI devices. + * Sometimes the cache line size register is not set + */ + + if (*csz == 0) + *csz = DEFAULT_CACHELINE >> 2; /* Use the default size */ +} + +/* + * Set current operating mode + * + * This function initializes and fills the rate table in the ATH object based + * on the operating mode. The blink rates are also set up here, although + * they have been superceeded by the ath_led module. +*/ + +static void ath_setcurmode(struct ath_softc *sc, enum wireless_mode mode) +{ + const struct ath9k_rate_table *rt; + int i; + + memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap)); + rt = sc->sc_rates[mode]; + BUG_ON(!rt); + + for (i = 0; i < rt->rateCount; i++) + sc->sc_rixmap[rt->info[i].rateCode] = (u8) i; + + memzero(sc->sc_hwmap, sizeof(sc->sc_hwmap)); + for (i = 0; i < 256; i++) { + u8 ix = rt->rateCodeToIndex[i]; + + if (ix == 0xff) + continue; + + sc->sc_hwmap[i].ieeerate = + rt->info[ix].dot11Rate & IEEE80211_RATE_VAL; + sc->sc_hwmap[i].rateKbps = rt->info[ix].rateKbps; + + if (rt->info[ix].shortPreamble || + rt->info[ix].phy == PHY_OFDM) { + /* XXX: Handle this */ + } + + /* NB: this uses the last entry if the rate isn't found */ + /* XXX beware of overlow */ + } + sc->sc_currates = rt; + sc->sc_curmode = mode; + /* + * All protection frames are transmited at 2Mb/s for + * 11g, otherwise at 1Mb/s. + * XXX select protection rate index from rate table. + */ + sc->sc_protrix = (mode == WIRELESS_MODE_11g ? 1 : 0); + /* rate index used to send mgt frames */ + sc->sc_minrateix = 0; +} + +/* + * Select Rate Table + * + * Based on the wireless mode passed in, the rate table in the ATH object + * is set to the mode specific rate table. This also calls the callback + * function to set the rate in the protocol layer object. +*/ + +static int ath_rate_setup(struct ath_softc *sc, enum wireless_mode mode) +{ + struct ath_hal *ah = sc->sc_ah; + const struct ath9k_rate_table *rt; + + switch (mode) { + case WIRELESS_MODE_11a: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11A); + break; + case WIRELESS_MODE_11b: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11B); + break; + case WIRELESS_MODE_11g: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11G); + break; + case WIRELESS_MODE_11NA_HT20: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NA_HT20); + break; + case WIRELESS_MODE_11NG_HT20: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NG_HT20); + break; + case WIRELESS_MODE_11NA_HT40PLUS: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NA_HT40PLUS); + break; + case WIRELESS_MODE_11NA_HT40MINUS: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, + ATH9K_MODE_SEL_11NA_HT40MINUS); + break; + case WIRELESS_MODE_11NG_HT40PLUS: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NG_HT40PLUS); + break; + case WIRELESS_MODE_11NG_HT40MINUS: + sc->sc_rates[mode] = + ath9k_hw_getratetable(ah, + ATH9K_MODE_SEL_11NG_HT40MINUS); + break; + default: + DPRINTF(sc, ATH_DBG_FATAL, "%s: invalid mode %u\n", + __func__, mode); + return 0; + } + rt = sc->sc_rates[mode]; + if (rt == NULL) + return 0; + + /* setup rate set in 802.11 protocol layer */ + ath_setup_rate(sc, mode, NORMAL_RATE, rt); + + return 1; +} + +/* + * Set up channel list + */ +static int ath_setup_channels(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + int nchan, i, a = 0, b = 0; + u8 regclassids[ATH_REGCLASSIDS_MAX]; + u32 nregclass = 0; + struct ieee80211_supported_band *band_2ghz; + struct ieee80211_supported_band *band_5ghz; + struct ieee80211_channel *chan_2ghz; + struct ieee80211_channel *chan_5ghz; + struct ath9k_channel *c; + + /* Fill in ah->ah_channels */ + if (!ath9k_regd_init_channels(ah, + ATH_CHAN_MAX, + (u32 *)&nchan, + regclassids, + ATH_REGCLASSIDS_MAX, + &nregclass, + CTRY_DEFAULT, + ATH9K_MODE_SEL_ALL, + false, + 1)) { + u32 rd = ah->ah_currentRD; + + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to collect channel list; " + "regdomain likely %u country code %u\n", + __func__, rd, CTRY_DEFAULT); + return -EINVAL; + } + + band_2ghz = &sc->sbands[IEEE80211_BAND_2GHZ]; + band_5ghz = &sc->sbands[IEEE80211_BAND_5GHZ]; + chan_2ghz = sc->channels[IEEE80211_BAND_2GHZ]; + chan_5ghz = sc->channels[IEEE80211_BAND_5GHZ]; + + for (i = 0; i < nchan; i++) { + c = &ah->ah_channels[i]; + if (IS_CHAN_2GHZ(c)) { + chan_2ghz[a].band = IEEE80211_BAND_2GHZ; + chan_2ghz[a].center_freq = c->channel; + chan_2ghz[a].max_power = c->maxTxPower; + + if (c->privFlags & CHANNEL_DISALLOW_ADHOC) + chan_2ghz[a].flags |= + IEEE80211_CHAN_NO_IBSS; + if (c->channelFlags & CHANNEL_PASSIVE) + chan_2ghz[a].flags |= + IEEE80211_CHAN_PASSIVE_SCAN; + + band_2ghz->n_channels = ++a; + + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: 2MHz channel: %d, " + "channelFlags: 0x%x\n", + __func__, + c->channel, + c->channelFlags); + } else if (IS_CHAN_5GHZ(c)) { + chan_5ghz[b].band = IEEE80211_BAND_5GHZ; + chan_5ghz[b].center_freq = c->channel; + chan_5ghz[b].max_power = c->maxTxPower; + + if (c->privFlags & CHANNEL_DISALLOW_ADHOC) + chan_5ghz[b].flags |= + IEEE80211_CHAN_NO_IBSS; + if (c->channelFlags & CHANNEL_PASSIVE) + chan_5ghz[b].flags |= + IEEE80211_CHAN_PASSIVE_SCAN; + + band_5ghz->n_channels = ++b; + + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: 5MHz channel: %d, " + "channelFlags: 0x%x\n", + __func__, + c->channel, + c->channelFlags); + } + } + + return 0; +} + +/* + * Determine mode from channel flags + * + * This routine will provide the enumerated WIRELESSS_MODE value based + * on the settings of the channel flags. If ho valid set of flags + * exist, the lowest mode (11b) is selected. +*/ + +static enum wireless_mode ath_chan2mode(struct ath9k_channel *chan) +{ + if (chan->chanmode == CHANNEL_A) + return WIRELESS_MODE_11a; + else if (chan->chanmode == CHANNEL_G) + return WIRELESS_MODE_11g; + else if (chan->chanmode == CHANNEL_B) + return WIRELESS_MODE_11b; + else if (chan->chanmode == CHANNEL_A_HT20) + return WIRELESS_MODE_11NA_HT20; + else if (chan->chanmode == CHANNEL_G_HT20) + return WIRELESS_MODE_11NG_HT20; + else if (chan->chanmode == CHANNEL_A_HT40PLUS) + return WIRELESS_MODE_11NA_HT40PLUS; + else if (chan->chanmode == CHANNEL_A_HT40MINUS) + return WIRELESS_MODE_11NA_HT40MINUS; + else if (chan->chanmode == CHANNEL_G_HT40PLUS) + return WIRELESS_MODE_11NG_HT40PLUS; + else if (chan->chanmode == CHANNEL_G_HT40MINUS) + return WIRELESS_MODE_11NG_HT40MINUS; + + /* NB: should not get here */ + return WIRELESS_MODE_11b; +} + +/* + * Change Channels + * + * Performs the actions to change the channel in the hardware, and set up + * the current operating mode for the new channel. +*/ + +static void ath_chan_change(struct ath_softc *sc, struct ath9k_channel *chan) +{ + enum wireless_mode mode; + + mode = ath_chan2mode(chan); + + ath_rate_setup(sc, mode); + ath_setcurmode(sc, mode); +} + +/* + * Stop the device, grabbing the top-level lock to protect + * against concurrent entry through ath_init (which can happen + * if another thread does a system call and the thread doing the + * stop is preempted). + */ + +static int ath_stop(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: invalid %u\n", + __func__, sc->sc_invalid); + + /* + * Shutdown the hardware and driver: + * stop output from above + * reset 802.11 state machine + * (sends station deassoc/deauth frames) + * turn off timers + * disable interrupts + * clear transmit machinery + * clear receive machinery + * turn off the radio + * reclaim beacon resources + * + * Note that some of this work is not possible if the + * hardware is gone (invalid). + */ + + if (!sc->sc_invalid) + ath9k_hw_set_interrupts(ah, 0); + ath_draintxq(sc, false); + if (!sc->sc_invalid) { + ath_stoprecv(sc); + ath9k_hw_phy_disable(ah); + } else + sc->sc_rxlink = NULL; + + return 0; +} + +/* + * Start Scan + * + * This function is called when starting a channel scan. It will perform + * power save wakeup processing, set the filter for the scan, and get the + * chip ready to send broadcast packets out during the scan. +*/ + +void ath_scan_start(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + u32 rfilt; + u32 now = (u32) jiffies_to_msecs(get_timestamp()); + + sc->sc_scanning = 1; + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(ah, rfilt); + ath9k_hw_write_associd(ah, ath_bcast_mac, 0); + + /* Restore previous power management state. */ + + DPRINTF(sc, ATH_DBG_CONFIG, "%d.%03d | %s: RX filter 0x%x aid 0\n", + now / 1000, now % 1000, __func__, rfilt); +} + +/* + * Scan End + * + * This routine is called by the upper layer when the scan is completed. This + * will set the filters back to normal operating mode, set the BSSID to the + * correct value, and restore the power save state. +*/ + +void ath_scan_end(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + u32 rfilt; + u32 now = (u32) jiffies_to_msecs(get_timestamp()); + + sc->sc_scanning = 0; + /* Request for a full reset due to rx packet filter changes */ + sc->sc_full_reset = 1; + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(ah, rfilt); + ath9k_hw_write_associd(ah, sc->sc_curbssid, sc->sc_curaid); + + DPRINTF(sc, ATH_DBG_CONFIG, "%d.%03d | %s: RX filter 0x%x aid 0x%x\n", + now / 1000, now % 1000, __func__, rfilt, sc->sc_curaid); +} + +/* + * Set the current channel + * + * Set/change channels. If the channel is really being changed, it's done + * by reseting the chip. To accomplish this we must first cleanup any pending + * DMA, then restart stuff after a la ath_init. +*/ +int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan) +{ + struct ath_hal *ah = sc->sc_ah; + bool fastcc = true, stopped; + enum ath9k_ht_macmode ht_macmode; + + if (sc->sc_invalid) /* if the device is invalid or removed */ + return -EIO; + + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: %u (%u MHz) -> %u (%u MHz), cflags:%x\n", + __func__, + ath9k_hw_mhz2ieee(ah, sc->sc_curchan.channel, + sc->sc_curchan.channelFlags), + sc->sc_curchan.channel, + ath9k_hw_mhz2ieee(ah, hchan->channel, hchan->channelFlags), + hchan->channel, hchan->channelFlags); + + ht_macmode = ath_cwm_macmode(sc); + + if (hchan->channel != sc->sc_curchan.channel || + hchan->channelFlags != sc->sc_curchan.channelFlags || + sc->sc_update_chainmask || sc->sc_full_reset) { + int status; + /* + * This is only performed if the channel settings have + * actually changed. + * + * To switch channels clear any pending DMA operations; + * wait long enough for the RX fifo to drain, reset the + * hardware at the new frequency, and then re-enable + * the relevant bits of the h/w. + */ + ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */ + ath_draintxq(sc, false); /* clear pending tx frames */ + stopped = ath_stoprecv(sc); /* turn off frame recv */ + + /* XXX: do not flush receive queue here. We don't want + * to flush data frames already in queue because of + * changing channel. */ + + if (!stopped || sc->sc_full_reset) + fastcc = false; + + spin_lock_bh(&sc->sc_resetlock); + if (!ath9k_hw_reset(ah, sc->sc_opmode, hchan, + ht_macmode, sc->sc_tx_chainmask, + sc->sc_rx_chainmask, + sc->sc_ht_extprotspacing, + fastcc, &status)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to reset channel %u (%uMhz) " + "flags 0x%x hal status %u\n", __func__, + ath9k_hw_mhz2ieee(ah, hchan->channel, + hchan->channelFlags), + hchan->channel, hchan->channelFlags, status); + spin_unlock_bh(&sc->sc_resetlock); + return -EIO; + } + spin_unlock_bh(&sc->sc_resetlock); + + sc->sc_curchan = *hchan; + sc->sc_update_chainmask = 0; + sc->sc_full_reset = 0; + + /* Re-enable rx framework */ + if (ath_startrecv(sc) != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to restart recv logic\n", __func__); + return -EIO; + } + /* + * Change channels and update the h/w rate map + * if we're switching; e.g. 11a to 11b/g. + */ + ath_chan_change(sc, hchan); + ath_update_txpow(sc); /* update tx power state */ + /* + * Re-enable interrupts. + */ + ath9k_hw_set_interrupts(ah, sc->sc_imask); + } + return 0; +} + +/**********************/ +/* Chainmask Handling */ +/**********************/ + +static void ath_chainmask_sel_timertimeout(unsigned long data) +{ + struct ath_chainmask_sel *cm = (struct ath_chainmask_sel *)data; + cm->switch_allowed = 1; +} + +/* Start chainmask select timer */ +static void ath_chainmask_sel_timerstart(struct ath_chainmask_sel *cm) +{ + cm->switch_allowed = 0; + mod_timer(&cm->timer, ath_chainmask_sel_period); +} + +/* Stop chainmask select timer */ +static void ath_chainmask_sel_timerstop(struct ath_chainmask_sel *cm) +{ + cm->switch_allowed = 0; + del_timer_sync(&cm->timer); +} + +static void ath_chainmask_sel_init(struct ath_softc *sc, struct ath_node *an) +{ + struct ath_chainmask_sel *cm = &an->an_chainmask_sel; + + memzero(cm, sizeof(struct ath_chainmask_sel)); + + cm->cur_tx_mask = sc->sc_tx_chainmask; + cm->cur_rx_mask = sc->sc_rx_chainmask; + cm->tx_avgrssi = ATH_RSSI_DUMMY_MARKER; + setup_timer(&cm->timer, + ath_chainmask_sel_timertimeout, (unsigned long) cm); +} + +int ath_chainmask_sel_logic(struct ath_softc *sc, struct ath_node *an) +{ + struct ath_chainmask_sel *cm = &an->an_chainmask_sel; + + /* + * Disable auto-swtiching in one of the following if conditions. + * sc_chainmask_auto_sel is used for internal global auto-switching + * enabled/disabled setting + */ + if (sc->sc_ah->ah_caps.halTxChainMask != ATH_CHAINMASK_SEL_3X3) { + cm->cur_tx_mask = sc->sc_tx_chainmask; + return cm->cur_tx_mask; + } + + if (cm->tx_avgrssi == ATH_RSSI_DUMMY_MARKER) + return cm->cur_tx_mask; + + if (cm->switch_allowed) { + /* Switch down from tx 3 to tx 2. */ + if (cm->cur_tx_mask == ATH_CHAINMASK_SEL_3X3 && + ATH_RSSI_OUT(cm->tx_avgrssi) >= + ath_chainmask_sel_down_rssi_thres) { + cm->cur_tx_mask = sc->sc_tx_chainmask; + + /* Don't let another switch happen until + * this timer expires */ + ath_chainmask_sel_timerstart(cm); + } + /* Switch up from tx 2 to 3. */ + else if (cm->cur_tx_mask == sc->sc_tx_chainmask && + ATH_RSSI_OUT(cm->tx_avgrssi) <= + ath_chainmask_sel_up_rssi_thres) { + cm->cur_tx_mask = ATH_CHAINMASK_SEL_3X3; + + /* Don't let another switch happen + * until this timer expires */ + ath_chainmask_sel_timerstart(cm); + } + } + + return cm->cur_tx_mask; +} + +/* + * Update tx/rx chainmask. For legacy association, + * hard code chainmask to 1x1, for 11n association, use + * the chainmask configuration. + */ + +void ath_update_chainmask(struct ath_softc *sc, int is_ht) +{ + sc->sc_update_chainmask = 1; + if (is_ht) { + sc->sc_tx_chainmask = sc->sc_ah->ah_caps.halTxChainMask; + sc->sc_rx_chainmask = sc->sc_ah->ah_caps.halRxChainMask; + } else { + sc->sc_tx_chainmask = 1; + sc->sc_rx_chainmask = 1; + } + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: tx chmask: %d, rx chmask: %d\n", + __func__, sc->sc_tx_chainmask, sc->sc_rx_chainmask); +} + +/******************/ +/* VAP management */ +/******************/ + +/* + * VAP in Listen mode + * + * This routine brings the VAP out of the down state into a "listen" state + * where it waits for association requests. This is used in AP and AdHoc + * modes. +*/ + +int ath_vap_listen(struct ath_softc *sc, int if_id) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_vap *avp; + u32 rfilt = 0; + DECLARE_MAC_BUF(mac); + + avp = sc->sc_vaps[if_id]; + if (avp == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, "%s: invalid interface id %u\n", + __func__, if_id); + return -EINVAL; + } + +#ifdef CONFIG_SLOW_ANT_DIV + ath_slow_ant_div_stop(&sc->sc_antdiv); +#endif + + /* update ratectrl about the new state */ + ath_rate_newstate(sc, avp); + + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(ah, rfilt); + + if (sc->sc_opmode == ATH9K_M_STA || sc->sc_opmode == ATH9K_M_IBSS) { + memcpy(sc->sc_curbssid, ath_bcast_mac, ETH_ALEN); + ath9k_hw_write_associd(ah, sc->sc_curbssid, sc->sc_curaid); + } else + sc->sc_curaid = 0; + + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: RX filter 0x%x bssid %s aid 0x%x\n", + __func__, rfilt, print_mac(mac, + sc->sc_curbssid), sc->sc_curaid); + + /* + * XXXX + * Disable BMISS interrupt when we're not associated + */ + ath9k_hw_set_interrupts(ah, + sc->sc_imask & ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS)); + sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); + /* need to reconfigure the beacons when it moves to RUN */ + sc->sc_beacons = 0; + + return 0; +} + +int ath_vap_attach(struct ath_softc *sc, + int if_id, + struct ieee80211_vif *if_data, + enum ath9k_opmode opmode) +{ + struct ath_vap *avp; + + if (if_id >= ATH_BCBUF || sc->sc_vaps[if_id] != NULL) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Invalid interface id = %u\n", __func__, if_id); + return -EINVAL; + } + + switch (opmode) { + case ATH9K_M_STA: + case ATH9K_M_IBSS: + case ATH9K_M_MONITOR: + break; + case ATH9K_M_HOSTAP: + /* XXX not right, beacon buffer is allocated on RUN trans */ + if (list_empty(&sc->sc_bbuf)) + return -ENOMEM; + break; + default: + return -EINVAL; + } + + /* create ath_vap */ + avp = kmalloc(sizeof(struct ath_vap), GFP_KERNEL); + if (avp == NULL) + return -ENOMEM; + + memzero(avp, sizeof(struct ath_vap)); + avp->av_if_data = if_data; + /* Set the VAP opmode */ + avp->av_opmode = opmode; + avp->av_bslot = -1; + INIT_LIST_HEAD(&avp->av_mcastq.axq_q); + INIT_LIST_HEAD(&avp->av_mcastq.axq_acq); + spin_lock_init(&avp->av_mcastq.axq_lock); + + ath9k_hw_set_tsfadjust(sc->sc_ah, 1); + + sc->sc_vaps[if_id] = avp; + sc->sc_nvaps++; + /* Set the device opmode */ + sc->sc_opmode = opmode; + + /* default VAP configuration */ + avp->av_config.av_fixed_rateset = IEEE80211_FIXED_RATE_NONE; + avp->av_config.av_fixed_retryset = 0x03030303; + + return 0; +} + +int ath_vap_detach(struct ath_softc *sc, int if_id) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_vap *avp; + + avp = sc->sc_vaps[if_id]; + if (avp == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, "%s: invalid interface id %u\n", + __func__, if_id); + return -EINVAL; + } + + /* + * Quiesce the hardware while we remove the vap. In + * particular we need to reclaim all references to the + * vap state by any frames pending on the tx queues. + * + * XXX can we do this w/o affecting other vap's? + */ + ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */ + ath_draintxq(sc, false); /* stop xmit side */ + ath_stoprecv(sc); /* stop recv side */ + ath_flushrecv(sc); /* flush recv queue */ + + /* Reclaim any pending mcast bufs on the vap. */ + ath_tx_draintxq(sc, &avp->av_mcastq, false); + + kfree(avp); + sc->sc_vaps[if_id] = NULL; + sc->sc_nvaps--; + + return 0; +} + +int ath_vap_config(struct ath_softc *sc, + int if_id, struct ath_vap_config *if_config) +{ + struct ath_vap *avp; + + if (if_id >= ATH_BCBUF) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Invalid interface id = %u\n", __func__, if_id); + return -EINVAL; + } + + avp = sc->sc_vaps[if_id]; + ASSERT(avp != NULL); + + if (avp) + memcpy(&avp->av_config, if_config, sizeof(avp->av_config)); + + return 0; +} + +/********/ +/* Core */ +/********/ + +int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan) +{ + struct ath_hal *ah = sc->sc_ah; + int status; + int error = 0; + enum ath9k_ht_macmode ht_macmode = ath_cwm_macmode(sc); + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: mode %d\n", __func__, sc->sc_opmode); + + /* + * Stop anything previously setup. This is safe + * whether this is the first time through or not. + */ + ath_stop(sc); + + /* Initialize chanmask selection */ + sc->sc_tx_chainmask = ah->ah_caps.halTxChainMask; + sc->sc_rx_chainmask = ah->ah_caps.halRxChainMask; + + /* Reset SERDES registers */ + ath9k_hw_configpcipowersave(ah, 0); + + /* + * The basic interface to setting the hardware in a good + * state is ``reset''. On return the hardware is known to + * be powered up and with interrupts disabled. This must + * be followed by initialization of the appropriate bits + * and then setup of the interrupt mask. + */ + sc->sc_curchan = *initial_chan; + + spin_lock_bh(&sc->sc_resetlock); + if (!ath9k_hw_reset(ah, sc->sc_opmode, &sc->sc_curchan, ht_macmode, + sc->sc_tx_chainmask, sc->sc_rx_chainmask, + sc->sc_ht_extprotspacing, false, &status)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to reset hardware; hal status %u " + "(freq %u flags 0x%x)\n", __func__, status, + sc->sc_curchan.channel, sc->sc_curchan.channelFlags); + error = -EIO; + spin_unlock_bh(&sc->sc_resetlock); + goto done; + } + spin_unlock_bh(&sc->sc_resetlock); + /* + * This is needed only to setup initial state + * but it's best done after a reset. + */ + ath_update_txpow(sc); + + /* + * Setup the hardware after reset: + * The receive engine is set going. + * Frame transmit is handled entirely + * in the frame output path; there's nothing to do + * here except setup the interrupt mask. + */ + if (ath_startrecv(sc) != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to start recv logic\n", __func__); + error = -EIO; + goto done; + } + /* Setup our intr mask. */ + sc->sc_imask = ATH9K_INT_RX | ATH9K_INT_TX + | ATH9K_INT_RXEOL | ATH9K_INT_RXORN + | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL; + + if (ah->ah_caps.halGTTSupport) + sc->sc_imask |= ATH9K_INT_GTT; + + if (ah->ah_caps.halHTSupport) + sc->sc_imask |= ATH9K_INT_CST; + + /* + * Enable MIB interrupts when there are hardware phy counters. + * Note we only do this (at the moment) for station mode. + */ + if (ath9k_hw_phycounters(ah) && + ((sc->sc_opmode == ATH9K_M_STA) || (sc->sc_opmode == ATH9K_M_IBSS))) + sc->sc_imask |= ATH9K_INT_MIB; + /* + * Some hardware processes the TIM IE and fires an + * interrupt when the TIM bit is set. For hardware + * that does, if not overridden by configuration, + * enable the TIM interrupt when operating as station. + */ + if (ah->ah_caps.halEnhancedPmSupport && sc->sc_opmode == ATH9K_M_STA && + !sc->sc_config.swBeaconProcess) + sc->sc_imask |= ATH9K_INT_TIM; + /* + * Don't enable interrupts here as we've not yet built our + * vap and node data structures, which will be needed as soon + * as we start receiving. + */ + ath_chan_change(sc, initial_chan); + + /* XXX: we must make sure h/w is ready and clear invalid flag + * before turning on interrupt. */ + sc->sc_invalid = 0; +done: + return error; +} + +/* + * Reset the hardware w/o losing operational state. This is + * basically a more efficient way of doing ath_stop, ath_init, + * followed by state transitions to the current 802.11 + * operational state. Used to recover from errors rx overrun + * and to reset the hardware when rf gain settings must be reset. + */ + +static int ath_reset_start(struct ath_softc *sc, u32 flag) +{ + struct ath_hal *ah = sc->sc_ah; + + ath9k_hw_set_interrupts(ah, 0); /* disable interrupts */ + ath_draintxq(sc, flag & RESET_RETRY_TXQ); /* stop xmit side */ + ath_stoprecv(sc); /* stop recv side */ + ath_flushrecv(sc); /* flush recv queue */ + + return 0; +} + +static int ath_reset_end(struct ath_softc *sc, u32 flag) +{ + struct ath_hal *ah = sc->sc_ah; + + if (ath_startrecv(sc) != 0) /* restart recv */ + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to start recv logic\n", __func__); + + /* + * We may be doing a reset in response to a request + * that changes the channel so update any state that + * might change as a result. + */ + ath_chan_change(sc, &sc->sc_curchan); + + ath_update_txpow(sc); /* update tx power state */ + + if (sc->sc_beacons) + ath_beacon_config(sc, ATH_IF_ID_ANY); /* restart beacons */ + ath9k_hw_set_interrupts(ah, sc->sc_imask); + + /* Restart the txq */ + if (flag & RESET_RETRY_TXQ) { + int i; + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) { + spin_lock_bh(&sc->sc_txq[i].axq_lock); + ath_txq_schedule(sc, &sc->sc_txq[i]); + spin_unlock_bh(&sc->sc_txq[i].axq_lock); + } + } + } + return 0; +} + +int ath_reset(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + int status; + int error = 0; + enum ath9k_ht_macmode ht_macmode = ath_cwm_macmode(sc); + + /* NB: indicate channel change so we do a full reset */ + spin_lock_bh(&sc->sc_resetlock); + if (!ath9k_hw_reset(ah, sc->sc_opmode, &sc->sc_curchan, + ht_macmode, + sc->sc_tx_chainmask, sc->sc_rx_chainmask, + sc->sc_ht_extprotspacing, false, &status)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to reset hardware; hal status %u\n", + __func__, status); + error = -EIO; + } + spin_unlock_bh(&sc->sc_resetlock); + + return error; +} + +int ath_suspend(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + + /* No I/O if device has been surprise removed */ + if (sc->sc_invalid) + return -EIO; + + /* Shut off the interrupt before setting sc->sc_invalid to '1' */ + ath9k_hw_set_interrupts(ah, 0); + + /* XXX: we must make sure h/w will not generate any interrupt + * before setting the invalid flag. */ + sc->sc_invalid = 1; + + /* disable HAL and put h/w to sleep */ + ath9k_hw_disable(sc->sc_ah); + + ath9k_hw_configpcipowersave(sc->sc_ah, 1); + + return 0; +} + +/* Interrupt handler. Most of the actual processing is deferred. + * It's the caller's responsibility to ensure the chip is awake. */ + +irqreturn_t ath_isr(int irq, void *dev) +{ + struct ath_softc *sc = dev; + struct ath_hal *ah = sc->sc_ah; + enum ath9k_int status; + bool sched = false; + + do { + if (sc->sc_invalid) { + /* + * The hardware is not ready/present, don't + * touch anything. Note this can happen early + * on if the IRQ is shared. + */ + return IRQ_NONE; + } + if (!ath9k_hw_intrpend(ah)) { /* shared irq, not for us */ + return IRQ_NONE; + } + + /* + * Figure out the reason(s) for the interrupt. Note + * that the hal returns a pseudo-ISR that may include + * bits we haven't explicitly enabled so we mask the + * value to insure we only process bits we requested. + */ + ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */ + + status &= sc->sc_imask; /* discard unasked-for bits */ + + /* + * If there are no status bits set, then this interrupt was not + * for me (should have been caught above). + */ + + if (!status) + return IRQ_NONE; + + sc->sc_intrstatus = status; + + if (status & ATH9K_INT_FATAL) { + /* need a chip reset */ + sched = true; + } else if (status & ATH9K_INT_RXORN) { + /* need a chip reset */ + sched = true; + } else { + if (status & ATH9K_INT_SWBA) { + /* schedule a tasklet for beacon handling */ + tasklet_schedule(&sc->bcon_tasklet); + } + if (status & ATH9K_INT_RXEOL) { + /* + * NB: the hardware should re-read the link when + * RXE bit is written, but it doesn't work + * at least on older hardware revs. + */ + sched = true; + } + + if (status & ATH9K_INT_TXURN) + /* bump tx trigger level */ + ath9k_hw_updatetxtriglevel(ah, true); + /* XXX: optimize this */ + if (status & ATH9K_INT_RX) + sched = true; + if (status & ATH9K_INT_TX) + sched = true; + if (status & ATH9K_INT_BMISS) + sched = true; + /* carrier sense timeout */ + if (status & ATH9K_INT_CST) + sched = true; + if (status & ATH9K_INT_MIB) { + /* + * Disable interrupts until we service the MIB + * interrupt; otherwise it will continue to + * fire. + */ + ath9k_hw_set_interrupts(ah, 0); + /* + * Let the hal handle the event. We assume + * it will clear whatever condition caused + * the interrupt. + */ + ath9k_hw_procmibevent(ah, &sc->sc_halstats); + ath9k_hw_set_interrupts(ah, sc->sc_imask); + } + if (status & ATH9K_INT_TIM_TIMER) { + if (!ah->ah_caps.halAutoSleepSupport) { + /* Clear RxAbort bit so that we can + * receive frames */ + ath9k_hw_setrxabort(ah, 0); + sched = true; + } + } + } + } while (0); + + if (sched) { + /* turn off every interrupt except SWBA */ + ath9k_hw_set_interrupts(ah, (sc->sc_imask & ATH9K_INT_SWBA)); + tasklet_schedule(&sc->intr_tq); + } + + return IRQ_HANDLED; +} + +/* Deferred interrupt processing */ + +static void ath9k_tasklet(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + u32 status = sc->sc_intrstatus; + + if (status & ATH9K_INT_FATAL) { + /* need a chip reset */ + ath_internal_reset(sc); + return; + } else { + + if (status & + (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN)) { + /* XXX: fill me in */ + /* + if (status & ATH9K_INT_RXORN) { + } + if (status & ATH9K_INT_RXEOL) { + } + */ + spin_lock_bh(&sc->sc_rxflushlock); + ath_rx_tasklet(sc, 0); + spin_unlock_bh(&sc->sc_rxflushlock); + } + /* XXX: optimize this */ + if (status & ATH9K_INT_TX) + ath_tx_tasklet(sc); + /* XXX: fill me in */ + /* + if (status & ATH9K_INT_BMISS) { + } + if (status & (ATH9K_INT_TIM | ATH9K_INT_DTIMSYNC)) { + if (status & ATH9K_INT_TIM) { + } + if (status & ATH9K_INT_DTIMSYNC) { + } + } + */ + } + + /* re-enable hardware interrupt */ + ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask); +} + +int ath_init(u16 devid, struct ath_softc *sc) +{ + struct ath_hal *ah = NULL; + int status; + int error = 0, i; + int csz = 0; + u32 rd; + + /* XXX: hardware will not be ready until ath_open() being called */ + sc->sc_invalid = 1; + + sc->sc_debug = DBG_DEFAULT; + DPRINTF(sc, ATH_DBG_CONFIG, "%s: devid 0x%x\n", __func__, devid); + + /* Initialize tasklet */ + tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); + tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, + (unsigned long)sc); + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + bus_read_cachesize(sc, &csz); + /* XXX assert csz is non-zero */ + sc->sc_cachelsz = csz << 2; /* convert to bytes */ + + spin_lock_init(&sc->sc_resetlock); + + ah = ath9k_hw_attach(devid, sc, sc->mem, &status); + if (ah == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to attach hardware; HAL status %u\n", + __func__, status); + error = -ENXIO; + goto bad; + } + sc->sc_ah = ah; + + /* Get the chipset-specific aggr limit. */ + sc->sc_rtsaggrlimit = ah->ah_caps.halRtsAggrLimit; + + /* Get the hardware key cache size. */ + sc->sc_keymax = ah->ah_caps.halKeyCacheSize; + if (sc->sc_keymax > ATH_KEYMAX) { + DPRINTF(sc, ATH_DBG_KEYCACHE, + "%s: Warning, using only %u entries in %u key cache\n", + __func__, ATH_KEYMAX, sc->sc_keymax); + sc->sc_keymax = ATH_KEYMAX; + } + + /* + * Reset the key cache since some parts do not + * reset the contents on initial power up. + */ + for (i = 0; i < sc->sc_keymax; i++) + ath9k_hw_keyreset(ah, (u16) i); + /* + * Mark key cache slots associated with global keys + * as in use. If we knew TKIP was not to be used we + * could leave the +32, +64, and +32+64 slots free. + * XXX only for splitmic. + */ + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + set_bit(i, sc->sc_keymap); + set_bit(i + 32, sc->sc_keymap); + set_bit(i + 64, sc->sc_keymap); + set_bit(i + 32 + 64, sc->sc_keymap); + } + /* + * Collect the channel list using the default country + * code and including outdoor channels. The 802.11 layer + * is resposible for filtering this list based on settings + * like the phy mode. + */ + rd = ah->ah_currentRD; + + error = ath_setup_channels(sc); + if (error) + goto bad; + + /* default to STA mode */ + sc->sc_opmode = ATH9K_M_MONITOR; + + /* Setup rate tables for all potential media types. */ + /* 11g encompasses b,g */ + + ath_rate_setup(sc, WIRELESS_MODE_11a); + ath_rate_setup(sc, WIRELESS_MODE_11g); + + /* NB: setup here so ath_rate_update is happy */ + ath_setcurmode(sc, WIRELESS_MODE_11a); + + /* + * Allocate hardware transmit queues: one queue for + * beacon frames and one data queue for each QoS + * priority. Note that the hal handles reseting + * these queues at the needed time. + */ + sc->sc_bhalq = ath_beaconq_setup(ah); + if (sc->sc_bhalq == -1) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to setup a beacon xmit queue\n", __func__); + error = -EIO; + goto bad2; + } + sc->sc_cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); + if (sc->sc_cabq == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to setup CAB xmit queue\n", __func__); + error = -EIO; + goto bad2; + } + + sc->sc_config.cabqReadytime = ATH_CABQ_READY_TIME; + ath_cabq_update(sc); + + for (i = 0; i < ARRAY_SIZE(sc->sc_haltype2q); i++) + sc->sc_haltype2q[i] = -1; + + /* Setup data queues */ + /* NB: ensure BK queue is the lowest priority h/w queue */ + if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to setup xmit queue for BK traffic\n", + __func__); + error = -EIO; + goto bad2; + } + + if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to setup xmit queue for BE traffic\n", + __func__); + error = -EIO; + goto bad2; + } + if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to setup xmit queue for VI traffic\n", + __func__); + error = -EIO; + goto bad2; + } + if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to setup xmit queue for VO traffic\n", + __func__); + error = -EIO; + goto bad2; + } + + sc->sc_rc = ath_rate_attach(ah); + if (sc->sc_rc == NULL) { + error = EIO; + goto bad2; + } + + if (ath9k_hw_getcapability(ah, HAL_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL)) { + /* + * Whether we should enable h/w TKIP MIC. + * XXX: if we don't support WME TKIP MIC, then we wouldn't + * report WMM capable, so it's always safe to turn on + * TKIP MIC in this case. + */ + ath9k_hw_setcapability(sc->sc_ah, HAL_CAP_TKIP_MIC, 0, 1, NULL); + } + + /* + * Check whether the separate key cache entries + * are required to handle both tx+rx MIC keys. + * With split mic keys the number of stations is limited + * to 27 otherwise 59. + */ + if (ath9k_hw_getcapability(ah, HAL_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL) + && ath9k_hw_getcapability(ah, HAL_CAP_CIPHER, + ATH9K_CIPHER_MIC, NULL) + && ath9k_hw_getcapability(ah, HAL_CAP_TKIP_SPLIT, + 0, NULL)) + sc->sc_splitmic = 1; + + /* turn on mcast key search if possible */ + if (!ath9k_hw_getcapability(ah, HAL_CAP_MCAST_KEYSRCH, 0, NULL)) + (void)ath9k_hw_setcapability(ah, HAL_CAP_MCAST_KEYSRCH, 1, + 1, NULL); + + sc->sc_config.txpowlimit = ATH_TXPOWER_MAX; + sc->sc_config.txpowlimit_override = 0; + + /* 11n Capabilities */ + if (ah->ah_caps.halHTSupport) { + sc->sc_txaggr = 1; + sc->sc_rxaggr = 1; + } + + sc->sc_tx_chainmask = ah->ah_caps.halTxChainMask; + sc->sc_rx_chainmask = ah->ah_caps.halRxChainMask; + + /* Configuration for rx chain detection */ + sc->sc_rxchaindetect_ref = 0; + sc->sc_rxchaindetect_thresh5GHz = 35; + sc->sc_rxchaindetect_thresh2GHz = 35; + sc->sc_rxchaindetect_delta5GHz = 30; + sc->sc_rxchaindetect_delta2GHz = 30; + + ath9k_hw_setcapability(ah, HAL_CAP_DIVERSITY, 1, true, NULL); + sc->sc_defant = ath9k_hw_getdefantenna(ah); + + ath9k_hw_getmac(ah, sc->sc_myaddr); + if (ah->ah_caps.halBssIdMaskSupport) { + ath9k_hw_getbssidmask(ah, sc->sc_bssidmask); + ATH_SET_VAP_BSSID_MASK(sc->sc_bssidmask); + ath9k_hw_setbssidmask(ah, sc->sc_bssidmask); + } + sc->sc_slottime = ATH9K_SLOT_TIME_9; /* default to short slot time */ + + /* initialize beacon slots */ + for (i = 0; i < ARRAY_SIZE(sc->sc_bslot); i++) + sc->sc_bslot[i] = ATH_IF_ID_ANY; + + /* save MISC configurations */ + sc->sc_config.swBeaconProcess = 1; + +#ifdef CONFIG_SLOW_ANT_DIV + /* range is 40 - 255, we use something in the middle */ + ath_slow_ant_div_init(&sc->sc_antdiv, sc, 0x127); +#endif + + return 0; +bad2: + /* cleanup tx queues */ + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->sc_txq[i]); +bad: + if (ah) + ath9k_hw_detach(ah); + return error; +} + +void ath_deinit(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + int i; + + DPRINTF(sc, ATH_DBG_CONFIG, "%s\n", __func__); + + ath_stop(sc); + if (!sc->sc_invalid) + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); + ath_rate_detach(sc->sc_rc); + /* cleanup tx queues */ + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->sc_txq[i]); + ath9k_hw_detach(ah); +} + +/*******************/ +/* Node Management */ +/*******************/ + +struct ath_node *ath_node_attach(struct ath_softc *sc, u8 *addr, int if_id) +{ + struct ath_vap *avp; + struct ath_node *an; + DECLARE_MAC_BUF(mac); + + avp = sc->sc_vaps[if_id]; + ASSERT(avp != NULL); + + /* mac80211 sta_notify callback is from an IRQ context, so no sleep */ + an = kmalloc(sizeof(struct ath_node), GFP_ATOMIC); + if (an == NULL) + return NULL; + memzero(an, sizeof(*an)); + + an->an_sc = sc; + memcpy(an->an_addr, addr, ETH_ALEN); + atomic_set(&an->an_refcnt, 1); + + /* set up per-node tx/rx state */ + ath_tx_node_init(sc, an); + ath_rx_node_init(sc, an); + + ath_chainmask_sel_init(sc, an); + ath_chainmask_sel_timerstart(&an->an_chainmask_sel); + list_add(&an->list, &sc->node_list); + + return an; +} + +void ath_node_detach(struct ath_softc *sc, struct ath_node *an, bool bh_flag) +{ + unsigned long flags; + + DECLARE_MAC_BUF(mac); + + ath_chainmask_sel_timerstop(&an->an_chainmask_sel); + an->an_flags |= ATH_NODE_CLEAN; + ath_tx_node_cleanup(sc, an, bh_flag); + ath_rx_node_cleanup(sc, an); + + ath_tx_node_free(sc, an); + ath_rx_node_free(sc, an); + + spin_lock_irqsave(&sc->node_lock, flags); + + list_del(&an->list); + + spin_unlock_irqrestore(&sc->node_lock, flags); + + kfree(an); +} + +/* Finds a node and increases the refcnt if found */ + +struct ath_node *ath_node_get(struct ath_softc *sc, u8 *addr) +{ + struct ath_node *an = NULL, *an_found = NULL; + + if (list_empty(&sc->node_list)) /* FIXME */ + goto out; + list_for_each_entry(an, &sc->node_list, list) { + if (!compare_ether_addr(an->an_addr, addr)) { + atomic_inc(&an->an_refcnt); + an_found = an; + break; + } + } +out: + return an_found; +} + +/* Decrements the refcnt and if it drops to zero, detach the node */ + +void ath_node_put(struct ath_softc *sc, struct ath_node *an, bool bh_flag) +{ + if (atomic_dec_and_test(&an->an_refcnt)) + ath_node_detach(sc, an, bh_flag); +} + +/* Finds a node, doesn't increment refcnt. Caller must hold sc->node_lock */ +struct ath_node *ath_node_find(struct ath_softc *sc, u8 *addr) +{ + struct ath_node *an = NULL, *an_found = NULL; + + if (list_empty(&sc->node_list)) + return NULL; + + list_for_each_entry(an, &sc->node_list, list) + if (!compare_ether_addr(an->an_addr, addr)) { + an_found = an; + break; + } + + return an_found; +} + +/* + * Set up New Node + * + * Setup driver-specific state for a newly associated node. This routine + * really only applies if compression or XR are enabled, there is no code + * covering any other cases. +*/ + +void ath_newassoc(struct ath_softc *sc, + struct ath_node *an, int isnew, int isuapsd) +{ + int tidno; + + /* if station reassociates, tear down the aggregation state. */ + if (!isnew) { + for (tidno = 0; tidno < WME_NUM_TID; tidno++) { + if (sc->sc_txaggr) + ath_tx_aggr_teardown(sc, an, tidno); + if (sc->sc_rxaggr) + ath_rx_aggr_teardown(sc, an, tidno); + } + } + an->an_flags = 0; +} + +/**************/ +/* Encryption */ +/**************/ + +void ath_key_reset(struct ath_softc *sc, u16 keyix, int freeslot) +{ + ath9k_hw_keyreset(sc->sc_ah, keyix); + if (freeslot) + clear_bit(keyix, sc->sc_keymap); +} + +int ath_keyset(struct ath_softc *sc, + u16 keyix, + struct ath9k_keyval *hk, + const u8 mac[ETH_ALEN]) +{ + bool status; + + status = ath9k_hw_set_keycache_entry(sc->sc_ah, + keyix, hk, mac, false); + + return status != false; +} + +/***********************/ +/* TX Power/Regulatory */ +/***********************/ + +/* + * Set Transmit power in HAL + * + * This routine makes the actual HAL calls to set the new transmit power + * limit. +*/ + +void ath_update_txpow(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + u32 txpow; + + if (sc->sc_curtxpow != sc->sc_config.txpowlimit) { + ath9k_hw_set_txpowerlimit(ah, sc->sc_config.txpowlimit); + /* read back in case value is clamped */ + ath9k_hw_getcapability(ah, HAL_CAP_TXPOW, 1, &txpow); + sc->sc_curtxpow = txpow; + } +} + +/* Return the current country and domain information */ +void ath_get_currentCountry(struct ath_softc *sc, + struct ath9k_country_entry *ctry) +{ + ath9k_regd_get_current_country(sc->sc_ah, ctry); + + /* If HAL not specific yet, since it is band dependent, + * use the one we passed in. */ + if (ctry->countryCode == CTRY_DEFAULT) { + ctry->iso[0] = 0; + ctry->iso[1] = 0; + } else if (ctry->iso[0] && ctry->iso[1]) { + if (!ctry->iso[2]) { + if (ath_outdoor) + ctry->iso[2] = 'O'; + else + ctry->iso[2] = 'I'; + } + } +} + +/**************************/ +/* Slow Antenna Diversity */ +/**************************/ + +void ath_slow_ant_div_init(struct ath_antdiv *antdiv, + struct ath_softc *sc, + int32_t rssitrig) +{ + int trig; + + /* antdivf_rssitrig can range from 40 - 0xff */ + trig = (rssitrig > 0xff) ? 0xff : rssitrig; + trig = (rssitrig < 40) ? 40 : rssitrig; + + antdiv->antdiv_sc = sc; + antdiv->antdivf_rssitrig = trig; +} + +void ath_slow_ant_div_start(struct ath_antdiv *antdiv, + u8 num_antcfg, + const u8 *bssid) +{ + antdiv->antdiv_num_antcfg = + num_antcfg < ATH_ANT_DIV_MAX_CFG ? + num_antcfg : ATH_ANT_DIV_MAX_CFG; + antdiv->antdiv_state = ATH_ANT_DIV_IDLE; + antdiv->antdiv_curcfg = 0; + antdiv->antdiv_bestcfg = 0; + antdiv->antdiv_laststatetsf = 0; + + memcpy(antdiv->antdiv_bssid, bssid, sizeof(antdiv->antdiv_bssid)); + + antdiv->antdiv_start = 1; +} + +void ath_slow_ant_div_stop(struct ath_antdiv *antdiv) +{ + antdiv->antdiv_start = 0; +} + +static int32_t ath_find_max_val(int32_t *val, + u8 num_val, u8 *max_index) +{ + u32 MaxVal = *val++; + u32 cur_index = 0; + + *max_index = 0; + while (++cur_index < num_val) { + if (*val > MaxVal) { + MaxVal = *val; + *max_index = cur_index; + } + + val++; + } + + return MaxVal; +} + +void ath_slow_ant_div(struct ath_antdiv *antdiv, + struct ieee80211_hdr *hdr, + struct ath_rx_status *rx_stats) +{ + struct ath_softc *sc = antdiv->antdiv_sc; + struct ath_hal *ah = sc->sc_ah; + u64 curtsf = 0; + u8 bestcfg, curcfg = antdiv->antdiv_curcfg; + __le16 fc = hdr->frame_control; + + if (antdiv->antdiv_start && ieee80211_is_beacon(fc) + && !compare_ether_addr(hdr->addr3, antdiv->antdiv_bssid)) { + antdiv->antdiv_lastbrssi[curcfg] = rx_stats->rs_rssi; + antdiv->antdiv_lastbtsf[curcfg] = ath9k_hw_gettsf64(sc->sc_ah); + curtsf = antdiv->antdiv_lastbtsf[curcfg]; + } else { + return; + } + + switch (antdiv->antdiv_state) { + case ATH_ANT_DIV_IDLE: + if ((antdiv->antdiv_lastbrssi[curcfg] < + antdiv->antdivf_rssitrig) + && ((curtsf - antdiv->antdiv_laststatetsf) > + ATH_ANT_DIV_MIN_IDLE_US)) { + + curcfg++; + if (curcfg == antdiv->antdiv_num_antcfg) + curcfg = 0; + + if (!ath9k_hw_select_antconfig(ah, curcfg)) { + antdiv->antdiv_bestcfg = antdiv->antdiv_curcfg; + antdiv->antdiv_curcfg = curcfg; + antdiv->antdiv_laststatetsf = curtsf; + antdiv->antdiv_state = ATH_ANT_DIV_SCAN; + } + } + break; + + case ATH_ANT_DIV_SCAN: + if ((curtsf - antdiv->antdiv_laststatetsf) < + ATH_ANT_DIV_MIN_SCAN_US) + break; + + curcfg++; + if (curcfg == antdiv->antdiv_num_antcfg) + curcfg = 0; + + if (curcfg == antdiv->antdiv_bestcfg) { + ath_find_max_val(antdiv->antdiv_lastbrssi, + antdiv->antdiv_num_antcfg, &bestcfg); + if (!ath9k_hw_select_antconfig(ah, bestcfg)) { + antdiv->antdiv_bestcfg = bestcfg; + antdiv->antdiv_curcfg = bestcfg; + antdiv->antdiv_laststatetsf = curtsf; + antdiv->antdiv_state = ATH_ANT_DIV_IDLE; + } + } else { + if (!ath9k_hw_select_antconfig(ah, curcfg)) { + antdiv->antdiv_curcfg = curcfg; + antdiv->antdiv_laststatetsf = curtsf; + antdiv->antdiv_state = ATH_ANT_DIV_SCAN; + } + } + + break; + } +} + +/***********************/ +/* Descriptor Handling */ +/***********************/ + +/* + * Set up DMA descriptors + * + * This function will allocate both the DMA descriptor structure, and the + * buffers it contains. These are used to contain the descriptors used + * by the system. +*/ + +int ath_descdma_setup(struct ath_softc *sc, + struct ath_descdma *dd, + struct list_head *head, + const char *name, + int nbuf, + int ndesc) +{ +#define DS2PHYS(_dd, _ds) \ + ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) +#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0) +#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096) + + struct ath_desc *ds; + struct ath_buf *bf; + int i, bsize, error; + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: %s DMA: %u buffers %u desc/buf\n", + __func__, name, nbuf, ndesc); + + /* ath_desc must be a multiple of DWORDs */ + if ((sizeof(struct ath_desc) % 4) != 0) { + DPRINTF(sc, ATH_DBG_FATAL, "%s: ath_desc not DWORD aligned\n", + __func__); + ASSERT((sizeof(struct ath_desc) % 4) == 0); + error = -ENOMEM; + goto fail; + } + + dd->dd_name = name; + dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc; + + /* + * Need additional DMA memory because we can't use + * descriptors that cross the 4K page boundary. Assume + * one skipped descriptor per 4K page. + */ + if (!(sc->sc_ah->ah_caps.hal4kbSplitTransSupport)) { + u32 ndesc_skipped = + ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len); + u32 dma_len; + + while (ndesc_skipped) { + dma_len = ndesc_skipped * sizeof(struct ath_desc); + dd->dd_desc_len += dma_len; + + ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len); + }; + } + + /* allocate descriptors */ + dd->dd_desc = pci_alloc_consistent(sc->pdev, + dd->dd_desc_len, + &dd->dd_desc_paddr); + if (dd->dd_desc == NULL) { + error = -ENOMEM; + goto fail; + } + ds = dd->dd_desc; + DPRINTF(sc, ATH_DBG_CONFIG, "%s: %s DMA map: %p (%u) -> %llx (%u)\n", + __func__, dd->dd_name, ds, (u32) dd->dd_desc_len, + ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); + + /* allocate buffers */ + bsize = sizeof(struct ath_buf) * nbuf; + bf = kmalloc(bsize, GFP_KERNEL); + if (bf == NULL) { + error = -ENOMEM; + goto fail2; + } + memzero(bf, bsize); + dd->dd_bufptr = bf; + + INIT_LIST_HEAD(head); + for (i = 0; i < nbuf; i++, bf++, ds += ndesc) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + + if (!(sc->sc_ah->ah_caps.hal4kbSplitTransSupport)) { + /* + * Skip descriptor addresses which can cause 4KB + * boundary crossing (addr + length) with a 32 dword + * descriptor fetch. + */ + while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { + ASSERT((caddr_t) bf->bf_desc < + ((caddr_t) dd->dd_desc + + dd->dd_desc_len)); + + ds += ndesc; + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + } + } + list_add_tail(&bf->list, head); + } + return 0; +fail2: + pci_free_consistent(sc->pdev, + dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr); +fail: + memzero(dd, sizeof(*dd)); + return error; +#undef ATH_DESC_4KB_BOUND_CHECK +#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED +#undef DS2PHYS +} + +/* + * Cleanup DMA descriptors + * + * This function will free the DMA block that was allocated for the descriptor + * pool. Since this was allocated as one "chunk", it is freed in the same + * manner. +*/ + +void ath_descdma_cleanup(struct ath_softc *sc, + struct ath_descdma *dd, + struct list_head *head) +{ + /* Free memory associated with descriptors */ + pci_free_consistent(sc->pdev, + dd->dd_desc_len, dd->dd_desc, dd->dd_desc_paddr); + + INIT_LIST_HEAD(head); + kfree(dd->dd_bufptr); + memzero(dd, sizeof(*dd)); +} + +/*************/ +/* Utilities */ +/*************/ + +void ath_internal_reset(struct ath_softc *sc) +{ + ath_reset_start(sc, 0); + ath_reset(sc); + ath_reset_end(sc, 0); +} + +int ath_get_hal_qnum(u16 queue, struct ath_softc *sc) +{ + int qnum; + + switch (queue) { + case 0: + qnum = sc->sc_haltype2q[ATH9K_WME_AC_VO]; + break; + case 1: + qnum = sc->sc_haltype2q[ATH9K_WME_AC_VI]; + break; + case 2: + qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE]; + break; + case 3: + qnum = sc->sc_haltype2q[ATH9K_WME_AC_BK]; + break; + default: + qnum = sc->sc_haltype2q[ATH9K_WME_AC_BE]; + break; + } + + return qnum; +} + +int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc) +{ + int qnum; + + switch (queue) { + case ATH9K_WME_AC_VO: + qnum = 0; + break; + case ATH9K_WME_AC_VI: + qnum = 1; + break; + case ATH9K_WME_AC_BE: + qnum = 2; + break; + case ATH9K_WME_AC_BK: + qnum = 3; + break; + default: + qnum = -1; + break; + } + + return qnum; +} + + +/* + * Expand time stamp to TSF + * + * Extend 15-bit time stamp from rx descriptor to + * a full 64-bit TSF using the current h/w TSF. +*/ + +u64 ath_extend_tsf(struct ath_softc *sc, u32 rstamp) +{ + u64 tsf; + + tsf = ath9k_hw_gettsf64(sc->sc_ah); + if ((tsf & 0x7fff) < rstamp) + tsf -= 0x8000; + return (tsf & ~0x7fff) | rstamp; +} + +/* + * Set Default Antenna + * + * Call into the HAL to set the default antenna to use. Not really valid for + * MIMO technology. +*/ + +void ath_setdefantenna(void *context, u32 antenna) +{ + struct ath_softc *sc = (struct ath_softc *)context; + struct ath_hal *ah = sc->sc_ah; + + /* XXX block beacon interrupts */ + ath9k_hw_setantenna(ah, antenna); + sc->sc_defant = antenna; + sc->sc_rxotherant = 0; +} + +/* + * Set Slot Time + * + * This will wake up the chip if required, and set the slot time for the + * frame (maximum transmit time). Slot time is assumed to be already set + * in the ATH object member sc_slottime +*/ + +void ath_setslottime(struct ath_softc *sc) +{ + ath9k_hw_setslottime(sc->sc_ah, sc->sc_slottime); + sc->sc_updateslot = OK; +} diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h new file mode 100644 index 000000000000..cf76b36d1757 --- /dev/null +++ b/drivers/net/wireless/ath9k/core.h @@ -0,0 +1,1097 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef CORE_H +#define CORE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ath9k.h" +#include "rc.h" + +struct ath_node; + +/******************/ +/* Utility macros */ +/******************/ + +/* Macro to expand scalars to 64-bit objects */ + +#define ito64(x) (sizeof(x) == 8) ? \ + (((unsigned long long int)(x)) & (0xff)) : \ + (sizeof(x) == 16) ? \ + (((unsigned long long int)(x)) & 0xffff) : \ + ((sizeof(x) == 32) ? \ + (((unsigned long long int)(x)) & 0xffffffff) : \ + (unsigned long long int)(x)) + +/* increment with wrap-around */ +#define INCR(_l, _sz) do { \ + (_l)++; \ + (_l) &= ((_sz) - 1); \ + } while (0) + +/* decrement with wrap-around */ +#define DECR(_l, _sz) do { \ + (_l)--; \ + (_l) &= ((_sz) - 1); \ + } while (0) + +#define A_MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define ASSERT(exp) do { \ + if (unlikely(!(exp))) { \ + BUG(); \ + } \ + } while (0) + +/* XXX: remove */ +#define memzero(_buf, _len) memset(_buf, 0, _len) + +#define get_dma_mem_context(var, field) (&((var)->field)) +#define copy_dma_mem_context(dst, src) (*dst = *src) + +#define ATH9K_BH_STATUS_INTACT 0 +#define ATH9K_BH_STATUS_CHANGE 1 + +#define ATH_TXQ_SETUP(sc, i) ((sc)->sc_txqsetup & (1<sc_debug & (_m)) \ + printk(_fmt , ##__VA_ARGS__); \ + } while (0) + +/***************************/ +/* Load-time Configuration */ +/***************************/ + +/* Per-instance load-time (note: NOT run-time) configurations + * for Atheros Device */ +struct ath_config { + u32 ath_aggr_prot; + u16 txpowlimit; + u16 txpowlimit_override; + u8 cabqReadytime; /* Cabq Readytime % */ + u8 swBeaconProcess; /* Process received beacons + in SW (vs HW) */ +}; + +/***********************/ +/* Chainmask Selection */ +/***********************/ + +#define ATH_CHAINMASK_SEL_TIMEOUT 6000 +/* Default - Number of last RSSI values that is used for + * chainmask selection */ +#define ATH_CHAINMASK_SEL_RSSI_CNT 10 +/* Means use 3x3 chainmask instead of configured chainmask */ +#define ATH_CHAINMASK_SEL_3X3 7 +/* Default - Rssi threshold below which we have to switch to 3x3 */ +#define ATH_CHAINMASK_SEL_UP_RSSI_THRES 20 +/* Default - Rssi threshold above which we have to switch to + * user configured values */ +#define ATH_CHAINMASK_SEL_DOWN_RSSI_THRES 35 +/* Struct to store the chainmask select related info */ +struct ath_chainmask_sel { + struct timer_list timer; + int cur_tx_mask; /* user configured or 3x3 */ + int cur_rx_mask; /* user configured or 3x3 */ + int tx_avgrssi; + u8 switch_allowed:1, /* timer will set this */ + cm_sel_enabled:1; +}; + +int ath_chainmask_sel_logic(struct ath_softc *sc, struct ath_node *an); +void ath_update_chainmask(struct ath_softc *sc, int is_ht); + +/*************************/ +/* Descriptor Management */ +/*************************/ + +/* Number of descriptors per buffer. The only case where we see skbuff +chains is due to FF aggregation in the driver. */ +#define ATH_TXDESC 1 +/* if there's more fragment for this MSDU */ +#define ATH_BF_MORE_MPDU 1 +#define ATH_TXBUF_RESET(_bf) do { \ + (_bf)->bf_status = 0; \ + (_bf)->bf_lastbf = NULL; \ + (_bf)->bf_lastfrm = NULL; \ + (_bf)->bf_next = NULL; \ + memzero(&((_bf)->bf_state), \ + sizeof(struct ath_buf_state)); \ + } while (0) + +struct ath_buf_state { + int bfs_nframes; /* # frames in aggregate */ + u16 bfs_al; /* length of aggregate */ + u16 bfs_frmlen; /* length of frame */ + int bfs_seqno; /* sequence number */ + int bfs_tidno; /* tid of this frame */ + int bfs_retries; /* current retries */ + struct ath_rc_series bfs_rcs[4]; /* rate series */ + u8 bfs_isdata:1; /* is a data frame/aggregate */ + u8 bfs_isaggr:1; /* is an aggregate */ + u8 bfs_isampdu:1; /* is an a-mpdu, aggregate or not */ + u8 bfs_ht:1; /* is an HT frame */ + u8 bfs_isretried:1; /* is retried */ + u8 bfs_isxretried:1; /* is excessive retried */ + u8 bfs_shpreamble:1; /* is short preamble */ + u8 bfs_isbar:1; /* is a BAR */ + u8 bfs_ispspoll:1; /* is a PS-Poll */ + u8 bfs_aggrburst:1; /* is a aggr burst */ + u8 bfs_calcairtime:1; /* requests airtime be calculated + when set for tx frame */ + int bfs_rifsburst_elem; /* RIFS burst/bar */ + int bfs_nrifsubframes; /* # of elements in burst */ + /* key type use to encrypt this frame */ + enum ath9k_key_type bfs_keytype; +}; + +#define bf_nframes bf_state.bfs_nframes +#define bf_al bf_state.bfs_al +#define bf_frmlen bf_state.bfs_frmlen +#define bf_retries bf_state.bfs_retries +#define bf_seqno bf_state.bfs_seqno +#define bf_tidno bf_state.bfs_tidno +#define bf_rcs bf_state.bfs_rcs +#define bf_isdata bf_state.bfs_isdata +#define bf_isaggr bf_state.bfs_isaggr +#define bf_isampdu bf_state.bfs_isampdu +#define bf_ht bf_state.bfs_ht +#define bf_isretried bf_state.bfs_isretried +#define bf_isxretried bf_state.bfs_isxretried +#define bf_shpreamble bf_state.bfs_shpreamble +#define bf_rifsburst_elem bf_state.bfs_rifsburst_elem +#define bf_nrifsubframes bf_state.bfs_nrifsubframes +#define bf_keytype bf_state.bfs_keytype +#define bf_isbar bf_state.bfs_isbar +#define bf_ispspoll bf_state.bfs_ispspoll +#define bf_aggrburst bf_state.bfs_aggrburst +#define bf_calcairtime bf_state.bfs_calcairtime + +/* + * Abstraction of a contiguous buffer to transmit/receive. There is only + * a single hw descriptor encapsulated here. + */ + +struct ath_buf { + struct list_head list; + struct list_head *last; + struct ath_buf *bf_lastbf; /* last buf of this unit (a frame or + an aggregate) */ + struct ath_buf *bf_lastfrm; /* last buf of this frame */ + struct ath_buf *bf_next; /* next subframe in the aggregate */ + struct ath_buf *bf_rifslast; /* last buf for RIFS burst */ + void *bf_mpdu; /* enclosing frame structure */ + void *bf_node; /* pointer to the node */ + struct ath_desc *bf_desc; /* virtual addr of desc */ + dma_addr_t bf_daddr; /* physical addr of desc */ + dma_addr_t bf_buf_addr; /* physical addr of data buffer */ + u32 bf_status; + u16 bf_flags; /* tx descriptor flags */ + struct ath_buf_state bf_state; /* buffer state */ + dma_addr_t bf_dmacontext; +}; + +/* + * reset the rx buffer. + * any new fields added to the athbuf and require + * reset need to be added to this macro. + * currently bf_status is the only one requires that + * requires reset. + */ +#define ATH_RXBUF_RESET(_bf) ((_bf)->bf_status = 0) + +/* hw processing complete, desc processed by hal */ +#define ATH_BUFSTATUS_DONE 0x00000001 +/* hw processing complete, desc hold for hw */ +#define ATH_BUFSTATUS_STALE 0x00000002 +/* Rx-only: OS is done with this packet and it's ok to queued it to hw */ +#define ATH_BUFSTATUS_FREE 0x00000004 + +/* DMA state for tx/rx descriptors */ + +struct ath_descdma { + const char *dd_name; + struct ath_desc *dd_desc; /* descriptors */ + dma_addr_t dd_desc_paddr; /* physical addr of dd_desc */ + u32 dd_desc_len; /* size of dd_desc */ + struct ath_buf *dd_bufptr; /* associated buffers */ + dma_addr_t dd_dmacontext; +}; + +/* Abstraction of a received RX MPDU/MMPDU, or a RX fragment */ + +struct ath_rx_context { + struct ath_buf *ctx_rxbuf; /* associated ath_buf for rx */ +}; +#define ATH_RX_CONTEXT(skb) ((struct ath_rx_context *)skb->cb) + +int ath_descdma_setup(struct ath_softc *sc, + struct ath_descdma *dd, + struct list_head *head, + const char *name, + int nbuf, + int ndesc); +int ath_desc_alloc(struct ath_softc *sc); +void ath_desc_free(struct ath_softc *sc); +void ath_descdma_cleanup(struct ath_softc *sc, + struct ath_descdma *dd, + struct list_head *head); + +/******/ +/* RX */ +/******/ + +#define ATH_MAX_ANTENNA 3 +#define ATH_RXBUF 512 +#define ATH_RX_TIMEOUT 40 /* 40 milliseconds */ +#define WME_NUM_TID 16 +#define IEEE80211_BAR_CTL_TID_M 0xF000 /* tid mask */ +#define IEEE80211_BAR_CTL_TID_S 2 /* tid shift */ + +enum ATH_RX_TYPE { + ATH_RX_NON_CONSUMED = 0, + ATH_RX_CONSUMED +}; + +/* per frame rx status block */ +struct ath_recv_status { + u64 tsf; /* mac tsf */ + int8_t rssi; /* RSSI (noise floor ajusted) */ + int8_t rssictl[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */ + int8_t rssiextn[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */ + int8_t abs_rssi; /* absolute RSSI */ + u8 rateieee; /* data rate received (IEEE rate code) */ + u8 ratecode; /* phy rate code */ + int rateKbps; /* data rate received (Kbps) */ + int antenna; /* rx antenna */ + int flags; /* status of associated skb */ +#define ATH_RX_FCS_ERROR 0x01 +#define ATH_RX_MIC_ERROR 0x02 +#define ATH_RX_DECRYPT_ERROR 0x04 +#define ATH_RX_RSSI_VALID 0x08 +/* if any of ctl,extn chainrssis are valid */ +#define ATH_RX_CHAIN_RSSI_VALID 0x10 +/* if extn chain rssis are valid */ +#define ATH_RX_RSSI_EXTN_VALID 0x20 +/* set if 40Mhz, clear if 20Mhz */ +#define ATH_RX_40MHZ 0x40 +/* set if short GI, clear if full GI */ +#define ATH_RX_SHORT_GI 0x80 +}; + +struct ath_rxbuf { + struct sk_buff *rx_wbuf; /* buffer */ + unsigned long rx_time; /* system time when received */ + struct ath_recv_status rx_status; /* cached rx status */ +}; + +/* Per-TID aggregate receiver state for a node */ +struct ath_arx_tid { + struct ath_node *an; /* parent ath node */ + struct ath_rxbuf *rxbuf; /* re-ordering buffer */ + struct timer_list timer; + spinlock_t tidlock; /* lock to protect this TID structure */ + int baw_head; /* seq_next at head */ + int baw_tail; /* tail of block-ack window */ + int seq_reset; /* need to reset start sequence */ + int addba_exchangecomplete; + u16 seq_next; /* next expected sequence */ + u16 baw_size; /* block-ack window size */ +}; + +/* Per-node receiver aggregate state */ +struct ath_arx { + struct ath_arx_tid tid[WME_NUM_TID]; +}; + +int ath_startrecv(struct ath_softc *sc); +bool ath_stoprecv(struct ath_softc *sc); +void ath_flushrecv(struct ath_softc *sc); +u32 ath_calcrxfilter(struct ath_softc *sc); +void ath_rx_node_init(struct ath_softc *sc, struct ath_node *an); +void ath_rx_node_free(struct ath_softc *sc, struct ath_node *an); +void ath_rx_node_cleanup(struct ath_softc *sc, struct ath_node *an); +void ath_handle_rx_intr(struct ath_softc *sc); +int ath_rx_init(struct ath_softc *sc, int nbufs); +void ath_rx_cleanup(struct ath_softc *sc); +int ath_rx_tasklet(struct ath_softc *sc, int flush); +int ath_rx_input(struct ath_softc *sc, + struct ath_node *node, + int is_ampdu, + struct sk_buff *skb, + struct ath_recv_status *rx_status, + enum ATH_RX_TYPE *status); +int ath__rx_indicate(struct ath_softc *sc, + struct sk_buff *skb, + struct ath_recv_status *status, + u16 keyix); +int ath_rx_subframe(struct ath_node *an, struct sk_buff *skb, + struct ath_recv_status *status); + +/******/ +/* TX */ +/******/ + +#define ATH_FRAG_PER_MSDU 1 +#define ATH_TXBUF (512/ATH_FRAG_PER_MSDU) +/* max number of transmit attempts (tries) */ +#define ATH_TXMAXTRY 13 +/* max number of 11n transmit attempts (tries) */ +#define ATH_11N_TXMAXTRY 10 +/* max number of tries for management and control frames */ +#define ATH_MGT_TXMAXTRY 4 +#define WME_BA_BMP_SIZE 64 +#define WME_MAX_BA WME_BA_BMP_SIZE +#define ATH_TID_MAX_BUFS (2 * WME_MAX_BA) +#define TID_TO_WME_AC(_tid) \ + ((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \ + (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK : \ + (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \ + WME_AC_VO) + + +/* Wireless Multimedia Extension Defines */ +#define WME_AC_BE 0 /* best effort */ +#define WME_AC_BK 1 /* background */ +#define WME_AC_VI 2 /* video */ +#define WME_AC_VO 3 /* voice */ +#define WME_NUM_AC 4 + +enum ATH_SM_PWRSAV{ + ATH_SM_ENABLE, + ATH_SM_PWRSAV_STATIC, + ATH_SM_PWRSAV_DYNAMIC, +}; + +/* + * Data transmit queue state. One of these exists for each + * hardware transmit queue. Packets sent to us from above + * are assigned to queues based on their priority. Not all + * devices support a complete set of hardware transmit queues. + * For those devices the array sc_ac2q will map multiple + * priorities to fewer hardware queues (typically all to one + * hardware queue). + */ +struct ath_txq { + u32 axq_qnum; /* hardware q number */ + u32 *axq_link; /* link ptr in last TX desc */ + struct list_head axq_q; /* transmit queue */ + spinlock_t axq_lock; /* lock on q and link */ + unsigned long axq_lockflags; /* intr state when must cli */ + u32 axq_depth; /* queue depth */ + u8 axq_aggr_depth; /* aggregates queued */ + u32 axq_totalqueued;/* total ever queued */ + u32 axq_intrcnt; /* count to determine + if descriptor should generate + int on this txq. */ + bool stopped; /* Is mac80211 queue + stopped ? */ + /* State for patching up CTS when bursting */ + struct ath_buf *axq_linkbuf; /* virtual addr of last buffer*/ + struct ath_desc *axq_lastdsWithCTS; /* first desc of the + last descriptor that contains CTS */ + struct ath_desc *axq_gatingds; /* final desc of the gating desc + * that determines whether lastdsWithCTS has + * been DMA'ed or not */ + struct list_head axq_acq; +}; + +/* per TID aggregate tx state for a destination */ +struct ath_atx_tid { + struct list_head list; /* round-robin tid entry */ + struct list_head buf_q; /* pending buffers */ + struct ath_node *an; /* parent node structure */ + struct ath_atx_ac *ac; /* parent access category */ + struct ath_buf *tx_buf[ATH_TID_MAX_BUFS];/* active tx frames */ + u16 seq_start; /* starting seq of BA window */ + u16 seq_next; /* next seq to be used */ + u16 baw_size; /* BA window size */ + int tidno; /* TID number */ + int baw_head; /* first un-acked tx buffer */ + int baw_tail; /* next unused tx buffer slot */ + int sched; /* TID is scheduled */ + int paused; /* TID is paused */ + int cleanup_inprogress; /* aggr of this TID is + being teared down */ + u32 addba_exchangecomplete:1; /* ADDBA state */ + int32_t addba_exchangeinprogress; + int addba_exchangeattempts; +}; + +/* per access-category aggregate tx state for a destination */ +struct ath_atx_ac { + int sched; /* dest-ac is scheduled */ + int qnum; /* H/W queue number associated + with this AC */ + struct list_head list; /* round-robin txq entry */ + struct list_head tid_q; /* queue of TIDs with buffers */ +}; + +/* per dest tx state */ +struct ath_atx { + struct ath_atx_tid tid[WME_NUM_TID]; + struct ath_atx_ac ac[WME_NUM_AC]; +}; + +/* per-frame tx control block */ +struct ath_tx_control { + struct ath_node *an; /* destination to sent to */ + int if_id; /* only valid for cab traffic */ + int qnum; /* h/w queue number */ + u32 ht:1; /* if it can be transmitted using HT */ + u32 ps:1; /* if one or more stations are in PS mode */ + u32 use_minrate:1; /* if this frame should transmitted using + minimum rate */ + enum ath9k_pkt_type atype; /* Atheros packet type */ + enum ath9k_key_type keytype; /* key type */ + u32 flags; /* HAL flags */ + u16 seqno; /* sequence number */ + u16 tidno; /* tid number */ + u16 txpower; /* transmit power */ + u16 frmlen; /* frame length */ + u32 keyix; /* key index */ + int min_rate; /* minimum rate */ + int mcast_rate; /* multicast rate */ + u16 nextfraglen; /* next fragment length */ + /* below is set only by ath_dev */ + struct ath_softc *dev; /* device handle */ + dma_addr_t dmacontext; +}; + +/* per frame tx status block */ +struct ath_xmit_status { + int retries; /* number of retries to successufully + transmit this frame */ + int flags; /* status of transmit */ +#define ATH_TX_ERROR 0x01 +#define ATH_TX_XRETRY 0x02 +#define ATH_TX_BAR 0x04 +}; + +struct ath_tx_stat { + int rssi; /* RSSI (noise floor ajusted) */ + int rssictl[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */ + int rssiextn[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */ + int rateieee; /* data rate xmitted (IEEE rate code) */ + int rateKbps; /* data rate xmitted (Kbps) */ + int ratecode; /* phy rate code */ + int flags; /* validity flags */ +/* if any of ctl,extn chain rssis are valid */ +#define ATH_TX_CHAIN_RSSI_VALID 0x01 +/* if extn chain rssis are valid */ +#define ATH_TX_RSSI_EXTN_VALID 0x02 + u32 airtime; /* time on air per final tx rate */ +}; + +struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype); +void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq); +int ath_tx_setup(struct ath_softc *sc, int haltype); +void ath_draintxq(struct ath_softc *sc, bool retry_tx); +void ath_tx_draintxq(struct ath_softc *sc, + struct ath_txq *txq, bool retry_tx); +void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an); +void ath_tx_node_cleanup(struct ath_softc *sc, + struct ath_node *an, bool bh_flag); +void ath_tx_node_free(struct ath_softc *sc, struct ath_node *an); +void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq); +int ath_tx_init(struct ath_softc *sc, int nbufs); +int ath_tx_cleanup(struct ath_softc *sc); +int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype); +int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_txq_info *q); +int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb); +void ath_tx_tasklet(struct ath_softc *sc); +u32 ath_txq_depth(struct ath_softc *sc, int qnum); +u32 ath_txq_aggr_depth(struct ath_softc *sc, int qnum); +void ath_notify_txq_status(struct ath_softc *sc, u16 queue_depth); +void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, + struct ath_xmit_status *tx_status, struct ath_node *an); + +/**********************/ +/* Node / Aggregation */ +/**********************/ + +/* indicates the node is clened up */ +#define ATH_NODE_CLEAN 0x1 +/* indicates the node is 80211 power save */ +#define ATH_NODE_PWRSAVE 0x2 + +#define ADDBA_TIMEOUT 200 /* 200 milliseconds */ +#define ADDBA_EXCHANGE_ATTEMPTS 10 +#define ATH_AGGR_DELIM_SZ 4 /* delimiter size */ +#define ATH_AGGR_MINPLEN 256 /* in bytes, minimum packet length */ +/* number of delimiters for encryption padding */ +#define ATH_AGGR_ENCRYPTDELIM 10 +/* minimum h/w qdepth to be sustained to maximize aggregation */ +#define ATH_AGGR_MIN_QDEPTH 2 +#define ATH_AMPDU_SUBFRAME_DEFAULT 32 +#define IEEE80211_SEQ_SEQ_SHIFT 4 +#define IEEE80211_SEQ_MAX 4096 +#define IEEE80211_MIN_AMPDU_BUF 0x8 + +/* return whether a bit at index _n in bitmap _bm is set + * _sz is the size of the bitmap */ +#define ATH_BA_ISSET(_bm, _n) (((_n) < (WME_BA_BMP_SIZE)) && \ + ((_bm)[(_n) >> 5] & (1 << ((_n) & 31)))) + +/* return block-ack bitmap index given sequence and starting sequence */ +#define ATH_BA_INDEX(_st, _seq) (((_seq) - (_st)) & (IEEE80211_SEQ_MAX - 1)) + +/* returns delimiter padding required given the packet length */ +#define ATH_AGGR_GET_NDELIM(_len) \ + (((((_len) + ATH_AGGR_DELIM_SZ) < ATH_AGGR_MINPLEN) ? \ + (ATH_AGGR_MINPLEN - (_len) - ATH_AGGR_DELIM_SZ) : 0) >> 2) + +#define BAW_WITHIN(_start, _bawsz, _seqno) \ + ((((_seqno) - (_start)) & 4095) < (_bawsz)) + +#define ATH_DS_BA_SEQ(_ds) ((_ds)->ds_us.tx.ts_seqnum) +#define ATH_DS_BA_BITMAP(_ds) (&(_ds)->ds_us.tx.ba_low) +#define ATH_DS_TX_BA(_ds) ((_ds)->ds_us.tx.ts_flags & ATH9K_TX_BA) +#define ATH_AN_2_TID(_an, _tidno) (&(_an)->an_aggr.tx.tid[(_tidno)]) + +enum ATH_AGGR_STATUS { + ATH_AGGR_DONE, + ATH_AGGR_BAW_CLOSED, + ATH_AGGR_LIMITED, + ATH_AGGR_SHORTPKT, + ATH_AGGR_8K_LIMITED, +}; + +enum ATH_AGGR_CHECK { + AGGR_NOT_REQUIRED, + AGGR_REQUIRED, + AGGR_CLEANUP_PROGRESS, + AGGR_EXCHANGE_PROGRESS, + AGGR_EXCHANGE_DONE +}; + +struct aggr_rifs_param { + int param_max_frames; + int param_max_len; + int param_rl; + int param_al; + struct ath_rc_series *param_rcs; +}; + +/* Per-node aggregation state */ +struct ath_node_aggr { + struct ath_atx tx; /* node transmit state */ + struct ath_arx rx; /* node receive state */ +}; + +/* driver-specific node state */ +struct ath_node { + struct list_head list; + struct ath_softc *an_sc; /* back pointer */ + atomic_t an_refcnt; + struct ath_chainmask_sel an_chainmask_sel; + struct ath_node_aggr an_aggr; /* A-MPDU aggregation state */ + u8 an_smmode; /* SM Power save mode */ + u8 an_flags; + u8 an_addr[ETH_ALEN]; +}; + +void ath_tx_resume_tid(struct ath_softc *sc, + struct ath_atx_tid *tid); +enum ATH_AGGR_CHECK ath_tx_aggr_check(struct ath_softc *sc, + struct ath_node *an, u8 tidno); +void ath_tx_aggr_teardown(struct ath_softc *sc, + struct ath_node *an, u8 tidno); +void ath_rx_aggr_teardown(struct ath_softc *sc, + struct ath_node *an, u8 tidno); +int ath_rx_aggr_start(struct ath_softc *sc, + const u8 *addr, + u16 tid, + u16 *ssn); +int ath_rx_aggr_stop(struct ath_softc *sc, + const u8 *addr, + u16 tid); +int ath_tx_aggr_start(struct ath_softc *sc, + const u8 *addr, + u16 tid, + u16 *ssn); +int ath_tx_aggr_stop(struct ath_softc *sc, + const u8 *addr, + u16 tid); +void ath_newassoc(struct ath_softc *sc, + struct ath_node *node, int isnew, int isuapsd); +struct ath_node *ath_node_attach(struct ath_softc *sc, + u8 addr[ETH_ALEN], int if_id); +void ath_node_detach(struct ath_softc *sc, struct ath_node *an, bool bh_flag); +struct ath_node *ath_node_get(struct ath_softc *sc, u8 addr[ETH_ALEN]); +void ath_node_put(struct ath_softc *sc, struct ath_node *an, bool bh_flag); +struct ath_node *ath_node_find(struct ath_softc *sc, u8 *addr); + +/*******************/ +/* Beacon Handling */ +/*******************/ + +/* + * Regardless of the number of beacons we stagger, (i.e. regardless of the + * number of BSSIDs) if a given beacon does not go out even after waiting this + * number of beacon intervals, the game's up. + */ +#define BSTUCK_THRESH (9 * ATH_BCBUF) +#define ATH_BCBUF 4 /* number of beacon buffers */ +#define ATH_DEFAULT_BINTVAL 100 /* default beacon interval in TU */ +#define ATH_DEFAULT_BMISS_LIMIT 10 +#define ATH_BEACON_AIFS_DEFAULT 0 /* Default aifs for ap beacon q */ +#define ATH_BEACON_CWMIN_DEFAULT 0 /* Default cwmin for ap beacon q */ +#define ATH_BEACON_CWMAX_DEFAULT 0 /* Default cwmax for ap beacon q */ +#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) + +/* beacon configuration */ +struct ath_beacon_config { + u16 beacon_interval; + u16 listen_interval; + u16 dtim_period; + u16 bmiss_timeout; + u8 dtim_count; + u8 tim_offset; + union { + u64 last_tsf; + u8 last_tstamp[8]; + } u; /* last received beacon/probe response timestamp of this BSS. */ +}; + +/* offsets in a beacon frame for + * quick acess of beacon content by low-level driver */ +struct ath_beacon_offset { + u8 *bo_tim; /* start of atim/dtim */ +}; + +void ath9k_beacon_tasklet(unsigned long data); +void ath_beacon_config(struct ath_softc *sc, int if_id); +int ath_beaconq_setup(struct ath_hal *ah); +int ath_beacon_alloc(struct ath_softc *sc, int if_id); +void ath_bstuck_process(struct ath_softc *sc); +void ath_beacon_tasklet(struct ath_softc *sc, int *needmark); +void ath_beacon_free(struct ath_softc *sc); +void ath_beacon_return(struct ath_softc *sc, struct ath_vap *avp); +void ath_beacon_sync(struct ath_softc *sc, int if_id); +void ath_update_beacon_info(struct ath_softc *sc, int avgbrssi); +void ath_get_beaconconfig(struct ath_softc *sc, + int if_id, + struct ath_beacon_config *conf); +int ath_update_beacon(struct ath_softc *sc, + int if_id, + struct ath_beacon_offset *bo, + struct sk_buff *skb, + int mcast); +/********/ +/* VAPs */ +/********/ + +#define ATH_IF_HW_OFF 0x0001 /* hardware state needs to turn off */ +#define ATH_IF_HW_ON 0x0002 /* hardware state needs to turn on */ +/* STA only: the associated AP is HT capable */ +#define ATH_IF_HT 0x0004 +/* AP/IBSS only: current BSS has privacy on */ +#define ATH_IF_PRIVACY 0x0008 +#define ATH_IF_BEACON_ENABLE 0x0010 /* AP/IBSS only: enable beacon */ +#define ATH_IF_BEACON_SYNC 0x0020 /* IBSS only: need to sync beacon */ + +/* + * Define the scheme that we select MAC address for multiple + * BSS on the same radio. The very first VAP will just use the MAC + * address from the EEPROM. For the next 3 VAPs, we set the + * U/L bit (bit 1) in MAC address, and use the next two bits as the + * index of the VAP. + */ + +#define ATH_SET_VAP_BSSID_MASK(bssid_mask) \ + ((bssid_mask)[0] &= ~(((ATH_BCBUF-1)<<2)|0x02)) + +/* VAP configuration (from protocol layer) */ +struct ath_vap_config { + u32 av_fixed_rateset; + u32 av_fixed_retryset; +}; + +/* driver-specific vap state */ +struct ath_vap { + struct ieee80211_vif *av_if_data; /* interface(vap) + instance from 802.11 protocal layer */ + enum ath9k_opmode av_opmode; /* VAP operational mode */ + struct ath_buf *av_bcbuf; /* beacon buffer */ + struct ath_beacon_offset av_boff; /* dynamic update state */ + struct ath_tx_control av_btxctl; /* tx control information + for beacon */ + int av_bslot; /* beacon slot index */ + struct ath_txq av_mcastq; /* multicast + transmit queue */ + struct ath_vap_config av_config; /* vap configuration + parameters from 802.11 protocol layer*/ + struct ath_rate_node *rc_node; +}; + +int ath_vap_attach(struct ath_softc *sc, + int if_id, + struct ieee80211_vif *if_data, + enum ath9k_opmode opmode); +int ath_vap_detach(struct ath_softc *sc, int if_id); +int ath_vap_config(struct ath_softc *sc, + int if_id, struct ath_vap_config *if_config); +int ath_vap_listen(struct ath_softc *sc, int if_id); + +/*********************/ +/* Antenna diversity */ +/*********************/ + +#define ATH_ANT_DIV_MAX_CFG 2 +#define ATH_ANT_DIV_MIN_IDLE_US 1000000 /* us */ +#define ATH_ANT_DIV_MIN_SCAN_US 50000 /* us */ + +enum ATH_ANT_DIV_STATE{ + ATH_ANT_DIV_IDLE, + ATH_ANT_DIV_SCAN, /* evaluating antenna */ +}; + +struct ath_antdiv { + struct ath_softc *antdiv_sc; + u8 antdiv_start; + enum ATH_ANT_DIV_STATE antdiv_state; + u8 antdiv_num_antcfg; + u8 antdiv_curcfg; + u8 antdiv_bestcfg; + int32_t antdivf_rssitrig; + int32_t antdiv_lastbrssi[ATH_ANT_DIV_MAX_CFG]; + u64 antdiv_lastbtsf[ATH_ANT_DIV_MAX_CFG]; + u64 antdiv_laststatetsf; + u8 antdiv_bssid[ETH_ALEN]; +}; + +void ath_slow_ant_div_init(struct ath_antdiv *antdiv, + struct ath_softc *sc, int32_t rssitrig); +void ath_slow_ant_div_start(struct ath_antdiv *antdiv, + u8 num_antcfg, + const u8 *bssid); +void ath_slow_ant_div_stop(struct ath_antdiv *antdiv); +void ath_slow_ant_div(struct ath_antdiv *antdiv, + struct ieee80211_hdr *wh, + struct ath_rx_status *rx_stats); +void ath_setdefantenna(void *sc, u32 antenna); + +/********************/ +/* Main driver core */ +/********************/ + +/* + * Default cache line size, in bytes. + * Used when PCI device not fully initialized by bootrom/BIOS +*/ +#define DEFAULT_CACHELINE 32 +#define ATH_DEFAULT_NOISE_FLOOR -95 +#define ATH_REGCLASSIDS_MAX 10 +#define ATH_CABQ_READY_TIME 80 /* % of beacon interval */ +#define ATH_PREAMBLE_SHORT (1<<0) +#define ATH_PROTECT_ENABLE (1<<1) +#define ATH_MAX_SW_RETRIES 10 +/* Num farmes difference in tx to flip default recv */ +#define ATH_ANTENNA_DIFF 2 +#define ATH_CHAN_MAX 255 +#define IEEE80211_WEP_NKID 4 /* number of key ids */ +#define IEEE80211_RATE_VAL 0x7f +/* + * The key cache is used for h/w cipher state and also for + * tracking station state such as the current tx antenna. + * We also setup a mapping table between key cache slot indices + * and station state to short-circuit node lookups on rx. + * Different parts have different size key caches. We handle + * up to ATH_KEYMAX entries (could dynamically allocate state). + */ +#define ATH_KEYMAX 128 /* max key cache size we handle */ + +#define RESET_RETRY_TXQ 0x00000001 +#define ATH_IF_ID_ANY 0xff + +#define ATH_TXPOWER_MAX 100 /* .5 dBm units */ + +#define RSSI_LPF_THRESHOLD -20 +#define ATH_RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */ +#define ATH_RATE_DUMMY_MARKER 0 +#define ATH_RSSI_LPF_LEN 10 +#define ATH_RSSI_DUMMY_MARKER 0x127 + +#define ATH_EP_MUL(x, mul) ((x) * (mul)) +#define ATH_EP_RND(x, mul) \ + ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) +#define ATH_RSSI_OUT(x) \ + (((x) != ATH_RSSI_DUMMY_MARKER) ? \ + (ATH_EP_RND((x), ATH_RSSI_EP_MULTIPLIER)) : ATH_RSSI_DUMMY_MARKER) +#define ATH_RSSI_IN(x) \ + (ATH_EP_MUL((x), ATH_RSSI_EP_MULTIPLIER)) +#define ATH_LPF_RSSI(x, y, len) \ + ((x != ATH_RSSI_DUMMY_MARKER) ? \ + (((x) * ((len) - 1) + (y)) / (len)) : (y)) +#define ATH_RSSI_LPF(x, y) do { \ + if ((y) >= RSSI_LPF_THRESHOLD) \ + x = ATH_LPF_RSSI((x), \ + ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN); \ + } while (0) + + +enum PROT_MODE { + PROT_M_NONE = 0, + PROT_M_RTSCTS, + PROT_M_CTSONLY +}; + +enum RATE_TYPE { + NORMAL_RATE = 0, + HALF_RATE, + QUARTER_RATE +}; + +struct ath_ht_info { + enum ath9k_ht_macmode tx_chan_width; + u16 maxampdu; + u8 mpdudensity; + u8 ext_chan_offset; +}; + +struct ath_softc { + struct ieee80211_hw *hw; /* mac80211 instance */ + struct pci_dev *pdev; /* Bus handle */ + void __iomem *mem; /* address of the device */ + struct tasklet_struct intr_tq; /* General tasklet */ + struct tasklet_struct bcon_tasklet; /* Beacon tasklet */ + struct ath_config sc_config; /* per-instance load-time + parameters */ + int sc_debug; /* Debug masks */ + struct ath_hal *sc_ah; /* HAL Instance */ + struct ath_rate_softc *sc_rc; /* tx rate control support */ + u32 sc_intrstatus; /* HAL_STATUS */ + enum ath9k_opmode sc_opmode; /* current operating mode */ + + /* Properties, Config */ + u8 sc_invalid; /* being detached */ + u8 sc_beacons; /* beacons running */ + u8 sc_scanning; /* scanning active */ + u8 sc_txaggr; /* enable 11n tx aggregation */ + u8 sc_rxaggr; /* enable 11n rx aggregation */ + u8 sc_update_chainmask; /* change chain mask */ + u8 sc_full_reset; /* force full reset */ + enum wireless_mode sc_curmode; /* current phy mode */ + u16 sc_curtxpow; /* current tx power limit */ + u16 sc_curaid; /* current association id */ + u8 sc_curbssid[ETH_ALEN]; + u8 sc_myaddr[ETH_ALEN]; + enum PROT_MODE sc_protmode; /* protection mode */ + u8 sc_mcastantenna;/* Multicast antenna number */ + u8 sc_txantenna; /* data tx antenna + (fixed or auto) */ + u8 sc_nbcnvaps; /* # of vaps sending beacons */ + u16 sc_nvaps; /* # of active virtual ap's */ + struct ath_vap *sc_vaps[ATH_BCBUF]; /* interface id + to avp map */ + enum ath9k_int sc_imask; /* interrupt mask copy */ + u8 sc_bssidmask[ETH_ALEN]; + u8 sc_defant; /* current default antenna */ + u8 sc_rxotherant; /* rx's on non-default antenna*/ + u16 sc_cachelsz; /* cache line size */ + int sc_slotupdate; /* slot to next advance fsm */ + int sc_slottime; /* slot time */ + u8 sc_noreset; + int sc_bslot[ATH_BCBUF];/* beacon xmit slots */ + struct ath9k_node_stats sc_halstats; /* station-mode rssi stats */ + struct list_head node_list; + struct ath_ht_info sc_ht_info; + int16_t sc_noise_floor; /* signal noise floor in dBm */ + enum ath9k_ht_extprotspacing sc_ht_extprotspacing; + u8 sc_tx_chainmask; + u8 sc_rx_chainmask; + u8 sc_rxchaindetect_ref; + u8 sc_rxchaindetect_thresh5GHz; + u8 sc_rxchaindetect_thresh2GHz; + u8 sc_rxchaindetect_delta5GHz; + u8 sc_rxchaindetect_delta2GHz; + u32 sc_rtsaggrlimit; /* Chipset specific + aggr limit */ + u32 sc_flags; +#ifdef CONFIG_SLOW_ANT_DIV + /* Slow antenna diversity */ + struct ath_antdiv sc_antdiv; +#endif + enum { + OK, /* no change needed */ + UPDATE, /* update pending */ + COMMIT /* beacon sent, commit change */ + } sc_updateslot; /* slot time update fsm */ + + /* Crypto */ + u32 sc_keymax; /* size of key cache */ + DECLARE_BITMAP(sc_keymap, ATH_KEYMAX); /* key use bit map */ + u8 sc_splitmic; /* split TKIP MIC keys */ + int sc_keytype; /* type of the key being used */ + + /* RX */ + struct list_head sc_rxbuf; /* receive buffer */ + struct ath_descdma sc_rxdma; /* RX descriptors */ + int sc_rxbufsize; /* rx size based on mtu */ + u32 *sc_rxlink; /* link ptr in last RX desc */ + u32 sc_rxflush; /* rx flush in progress */ + u64 sc_lastrx; /* tsf of last rx'd frame */ + + /* TX */ + struct list_head sc_txbuf; /* transmit buffer */ + struct ath_txq sc_txq[ATH9K_NUM_TX_QUEUES]; + struct ath_descdma sc_txdma; /* TX descriptors */ + u32 sc_txqsetup; /* h/w queues setup */ + u32 sc_txintrperiod;/* tx interrupt batching */ + int sc_haltype2q[ATH9K_WME_AC_VO+1]; /* HAL WME + AC -> h/w qnum */ + u32 sc_ant_tx[8]; /* recent tx frames/antenna */ + + /* Beacon */ + struct ath9k_txq_info sc_beacon_qi; /* adhoc only: beacon + queue parameters */ + struct ath_descdma sc_bdma; /* beacon descriptors */ + struct ath_txq *sc_cabq; /* tx q for cab frames */ + struct list_head sc_bbuf; /* beacon buffers */ + u32 sc_bhalq; /* HAL q for outgoing beacons */ + u32 sc_bmisscount; /* missed beacon transmits */ + u32 ast_be_xmit; /* beacons transmitted */ + + /* Rate */ + struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX]; + const struct ath9k_rate_table *sc_rates[WIRELESS_MODE_MAX]; + const struct ath9k_rate_table *sc_currates; /* current rate table */ + u8 sc_rixmap[256]; /* IEEE to h/w + rate table ix */ + u8 sc_minrateix; /* min h/w rate index */ + u8 sc_protrix; /* protection rate index */ + struct { + u32 rateKbps; /* transfer rate in kbs */ + u8 ieeerate; /* IEEE rate */ + } sc_hwmap[256]; /* h/w rate ix mappings */ + + /* Channel, Band */ + struct ieee80211_channel channels[IEEE80211_NUM_BANDS][ATH_CHAN_MAX]; + struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; + struct ath9k_channel sc_curchan; /* current h/w channel */ + + /* Locks */ + spinlock_t sc_rxflushlock; /* lock of RX flush */ + spinlock_t sc_rxbuflock; /* rxbuf lock */ + spinlock_t sc_txbuflock; /* txbuf lock */ + spinlock_t sc_resetlock; + spinlock_t node_lock; +}; + +int ath_init(u16 devid, struct ath_softc *sc); +void ath_deinit(struct ath_softc *sc); +int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan); +int ath_suspend(struct ath_softc *sc); +irqreturn_t ath_isr(int irq, void *dev); +int ath_reset(struct ath_softc *sc); +void ath_scan_start(struct ath_softc *sc); +void ath_scan_end(struct ath_softc *sc); +int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan); +void ath_setup_rate(struct ath_softc *sc, + enum wireless_mode wMode, + enum RATE_TYPE type, + const struct ath9k_rate_table *rt); + +/*********************/ +/* Utility Functions */ +/*********************/ + +void ath_key_reset(struct ath_softc *sc, u16 keyix, int freeslot); +int ath_keyset(struct ath_softc *sc, + u16 keyix, + struct ath9k_keyval *hk, + const u8 mac[ETH_ALEN]); +int ath_get_hal_qnum(u16 queue, struct ath_softc *sc); +int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc); +void ath_setslottime(struct ath_softc *sc); +void ath_update_txpow(struct ath_softc *sc); +int ath_cabq_update(struct ath_softc *); +void ath_get_currentCountry(struct ath_softc *sc, + struct ath9k_country_entry *ctry); +u64 ath_extend_tsf(struct ath_softc *sc, u32 rstamp); +void ath_internal_reset(struct ath_softc *sc); +u32 ath_chan2flags(struct ieee80211_channel *chan, struct ath_softc *sc); +dma_addr_t ath_skb_map_single(struct ath_softc *sc, + struct sk_buff *skb, + int direction, + dma_addr_t *pa); +void ath_skb_unmap_single(struct ath_softc *sc, + struct sk_buff *skb, + int direction, + dma_addr_t *pa); +void ath_mcast_merge(struct ath_softc *sc, u32 mfilt[2]); +enum ath9k_ht_macmode ath_cwm_macmode(struct ath_softc *sc); + +#endif /* CORE_H */ diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c new file mode 100644 index 000000000000..1f6f3934d379 --- /dev/null +++ b/drivers/net/wireless/ath9k/hw.c @@ -0,0 +1,8563 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "core.h" +#include "hw.h" +#include "reg.h" +#include "phy.h" +#include "initvals.h" + +static void ath9k_hw_iqcal_collect(struct ath_hal *ah); +static void ath9k_hw_iqcalibrate(struct ath_hal *ah, u8 numChains); +static void ath9k_hw_adc_gaincal_collect(struct ath_hal *ah); +static void ath9k_hw_adc_gaincal_calibrate(struct ath_hal *ah, + u8 numChains); +static void ath9k_hw_adc_dccal_collect(struct ath_hal *ah); +static void ath9k_hw_adc_dccal_calibrate(struct ath_hal *ah, + u8 numChains); + +static const u8 CLOCK_RATE[] = { 40, 80, 22, 44, 88, 40 }; +static const int16_t NOISE_FLOOR[] = { -96, -93, -98, -96, -93, -96 }; + +static const struct hal_percal_data iq_cal_multi_sample = { + IQ_MISMATCH_CAL, + MAX_CAL_SAMPLES, + PER_MIN_LOG_COUNT, + ath9k_hw_iqcal_collect, + ath9k_hw_iqcalibrate +}; +static const struct hal_percal_data iq_cal_single_sample = { + IQ_MISMATCH_CAL, + MIN_CAL_SAMPLES, + PER_MAX_LOG_COUNT, + ath9k_hw_iqcal_collect, + ath9k_hw_iqcalibrate +}; +static const struct hal_percal_data adc_gain_cal_multi_sample = { + ADC_GAIN_CAL, + MAX_CAL_SAMPLES, + PER_MIN_LOG_COUNT, + ath9k_hw_adc_gaincal_collect, + ath9k_hw_adc_gaincal_calibrate +}; +static const struct hal_percal_data adc_gain_cal_single_sample = { + ADC_GAIN_CAL, + MIN_CAL_SAMPLES, + PER_MAX_LOG_COUNT, + ath9k_hw_adc_gaincal_collect, + ath9k_hw_adc_gaincal_calibrate +}; +static const struct hal_percal_data adc_dc_cal_multi_sample = { + ADC_DC_CAL, + MAX_CAL_SAMPLES, + PER_MIN_LOG_COUNT, + ath9k_hw_adc_dccal_collect, + ath9k_hw_adc_dccal_calibrate +}; +static const struct hal_percal_data adc_dc_cal_single_sample = { + ADC_DC_CAL, + MIN_CAL_SAMPLES, + PER_MAX_LOG_COUNT, + ath9k_hw_adc_dccal_collect, + ath9k_hw_adc_dccal_calibrate +}; +static const struct hal_percal_data adc_init_dc_cal = { + ADC_DC_INIT_CAL, + MIN_CAL_SAMPLES, + INIT_LOG_COUNT, + ath9k_hw_adc_dccal_collect, + ath9k_hw_adc_dccal_calibrate +}; + +static const struct ath_hal ar5416hal = { + AR5416_MAGIC, + 0, + 0, + NULL, + NULL, + CTRY_DEFAULT, + 0, + 0, + 0, + 0, + 0, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, +}; + +static struct ath9k_rate_table ar5416_11a_table = { + 8, + {0}, + { + {true, PHY_OFDM, 6000, 0x0b, 0x00, (0x80 | 12), 0}, + {true, PHY_OFDM, 9000, 0x0f, 0x00, 18, 0}, + {true, PHY_OFDM, 12000, 0x0a, 0x00, (0x80 | 24), 2}, + {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 2}, + {true, PHY_OFDM, 24000, 0x09, 0x00, (0x80 | 48), 4}, + {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 4}, + {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 4}, + {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 4} + }, +}; + +static struct ath9k_rate_table ar5416_11b_table = { + 4, + {0}, + { + {true, PHY_CCK, 1000, 0x1b, 0x00, (0x80 | 2), 0}, + {true, PHY_CCK, 2000, 0x1a, 0x04, (0x80 | 4), 1}, + {true, PHY_CCK, 5500, 0x19, 0x04, (0x80 | 11), 1}, + {true, PHY_CCK, 11000, 0x18, 0x04, (0x80 | 22), 1} + }, +}; + +static struct ath9k_rate_table ar5416_11g_table = { + 12, + {0}, + { + {true, PHY_CCK, 1000, 0x1b, 0x00, (0x80 | 2), 0}, + {true, PHY_CCK, 2000, 0x1a, 0x04, (0x80 | 4), 1}, + {true, PHY_CCK, 5500, 0x19, 0x04, (0x80 | 11), 2}, + {true, PHY_CCK, 11000, 0x18, 0x04, (0x80 | 22), 3}, + + {false, PHY_OFDM, 6000, 0x0b, 0x00, 12, 4}, + {false, PHY_OFDM, 9000, 0x0f, 0x00, 18, 4}, + {true, PHY_OFDM, 12000, 0x0a, 0x00, 24, 6}, + {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 6}, + {true, PHY_OFDM, 24000, 0x09, 0x00, 48, 8}, + {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 8}, + {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 8}, + {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 8} + }, +}; + +static struct ath9k_rate_table ar5416_11ng_table = { + 28, + {0}, + { + {true, PHY_CCK, 1000, 0x1b, 0x00, (0x80 | 2), 0}, + {true, PHY_CCK, 2000, 0x1a, 0x04, (0x80 | 4), 1}, + {true, PHY_CCK, 5500, 0x19, 0x04, (0x80 | 11), 2}, + {true, PHY_CCK, 11000, 0x18, 0x04, (0x80 | 22), 3}, + + {false, PHY_OFDM, 6000, 0x0b, 0x00, 12, 4}, + {false, PHY_OFDM, 9000, 0x0f, 0x00, 18, 4}, + {true, PHY_OFDM, 12000, 0x0a, 0x00, 24, 6}, + {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 6}, + {true, PHY_OFDM, 24000, 0x09, 0x00, 48, 8}, + {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 8}, + {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 8}, + {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 8}, + {true, PHY_HT, 6500, 0x80, 0x00, 0, 4}, + {true, PHY_HT, 13000, 0x81, 0x00, 1, 6}, + {true, PHY_HT, 19500, 0x82, 0x00, 2, 6}, + {true, PHY_HT, 26000, 0x83, 0x00, 3, 8}, + {true, PHY_HT, 39000, 0x84, 0x00, 4, 8}, + {true, PHY_HT, 52000, 0x85, 0x00, 5, 8}, + {true, PHY_HT, 58500, 0x86, 0x00, 6, 8}, + {true, PHY_HT, 65000, 0x87, 0x00, 7, 8}, + {true, PHY_HT, 13000, 0x88, 0x00, 8, 4}, + {true, PHY_HT, 26000, 0x89, 0x00, 9, 6}, + {true, PHY_HT, 39000, 0x8a, 0x00, 10, 6}, + {true, PHY_HT, 52000, 0x8b, 0x00, 11, 8}, + {true, PHY_HT, 78000, 0x8c, 0x00, 12, 8}, + {true, PHY_HT, 104000, 0x8d, 0x00, 13, 8}, + {true, PHY_HT, 117000, 0x8e, 0x00, 14, 8}, + {true, PHY_HT, 130000, 0x8f, 0x00, 15, 8}, + }, +}; + +static struct ath9k_rate_table ar5416_11na_table = { + 24, + {0}, + { + {true, PHY_OFDM, 6000, 0x0b, 0x00, (0x80 | 12), 0}, + {true, PHY_OFDM, 9000, 0x0f, 0x00, 18, 0}, + {true, PHY_OFDM, 12000, 0x0a, 0x00, (0x80 | 24), 2}, + {true, PHY_OFDM, 18000, 0x0e, 0x00, 36, 2}, + {true, PHY_OFDM, 24000, 0x09, 0x00, (0x80 | 48), 4}, + {true, PHY_OFDM, 36000, 0x0d, 0x00, 72, 4}, + {true, PHY_OFDM, 48000, 0x08, 0x00, 96, 4}, + {true, PHY_OFDM, 54000, 0x0c, 0x00, 108, 4}, + {true, PHY_HT, 6500, 0x80, 0x00, 0, 0}, + {true, PHY_HT, 13000, 0x81, 0x00, 1, 2}, + {true, PHY_HT, 19500, 0x82, 0x00, 2, 2}, + {true, PHY_HT, 26000, 0x83, 0x00, 3, 4}, + {true, PHY_HT, 39000, 0x84, 0x00, 4, 4}, + {true, PHY_HT, 52000, 0x85, 0x00, 5, 4}, + {true, PHY_HT, 58500, 0x86, 0x00, 6, 4}, + {true, PHY_HT, 65000, 0x87, 0x00, 7, 4}, + {true, PHY_HT, 13000, 0x88, 0x00, 8, 0}, + {true, PHY_HT, 26000, 0x89, 0x00, 9, 2}, + {true, PHY_HT, 39000, 0x8a, 0x00, 10, 2}, + {true, PHY_HT, 52000, 0x8b, 0x00, 11, 4}, + {true, PHY_HT, 78000, 0x8c, 0x00, 12, 4}, + {true, PHY_HT, 104000, 0x8d, 0x00, 13, 4}, + {true, PHY_HT, 117000, 0x8e, 0x00, 14, 4}, + {true, PHY_HT, 130000, 0x8f, 0x00, 15, 4}, + }, +}; + +static enum wireless_mode ath9k_hw_chan2wmode(struct ath_hal *ah, + const struct ath9k_channel *chan) +{ + if (IS_CHAN_CCK(chan)) + return WIRELESS_MODE_11b; + if (IS_CHAN_G(chan)) + return WIRELESS_MODE_11g; + return WIRELESS_MODE_11a; +} + +static bool ath9k_hw_wait(struct ath_hal *ah, + u32 reg, + u32 mask, + u32 val) +{ + int i; + + for (i = 0; i < (AH_TIMEOUT / AH_TIME_QUANTUM); i++) { + if ((REG_READ(ah, reg) & mask) == val) + return true; + + udelay(AH_TIME_QUANTUM); + } + DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO, + "%s: timeout on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n", + __func__, reg, REG_READ(ah, reg), mask, val); + return false; +} + +static bool ath9k_hw_eeprom_read(struct ath_hal *ah, u32 off, + u16 *data) +{ + (void) REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S)); + + if (!ath9k_hw_wait(ah, + AR_EEPROM_STATUS_DATA, + AR_EEPROM_STATUS_DATA_BUSY | + AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0)) { + return false; + } + + *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA), + AR_EEPROM_STATUS_DATA_VAL); + + return true; +} + +static int ath9k_hw_flash_map(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + ahp->ah_cal_mem = ioremap(AR5416_EEPROM_START_ADDR, AR5416_EEPROM_MAX); + + if (!ahp->ah_cal_mem) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: cannot remap eeprom region \n", __func__); + return -EIO; + } + + return 0; +} + +static bool ath9k_hw_flash_read(struct ath_hal *ah, u32 off, + u16 *data) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + *data = ioread16(ahp->ah_cal_mem + off); + return true; +} + +static void ath9k_hw_read_revisions(struct ath_hal *ah) +{ + u32 val; + + val = REG_READ(ah, AR_SREV) & AR_SREV_ID; + + if (val == 0xFF) { + val = REG_READ(ah, AR_SREV); + + ah->ah_macVersion = + (val & AR_SREV_VERSION2) >> AR_SREV_TYPE2_S; + + ah->ah_macRev = MS(val, AR_SREV_REVISION2); + ah->ah_isPciExpress = + (val & AR_SREV_TYPE2_HOST_MODE) ? 0 : 1; + + } else { + if (!AR_SREV_9100(ah)) + ah->ah_macVersion = MS(val, AR_SREV_VERSION); + + ah->ah_macRev = val & AR_SREV_REVISION; + + if (ah->ah_macVersion == AR_SREV_VERSION_5416_PCIE) + ah->ah_isPciExpress = true; + } +} + +u32 ath9k_hw_reverse_bits(u32 val, u32 n) +{ + u32 retval; + int i; + + for (i = 0, retval = 0; i < n; i++) { + retval = (retval << 1) | (val & 1); + val >>= 1; + } + return retval; +} + +static void ath9k_hw_set_defaults(struct ath_hal *ah) +{ + int i; + + ah->ah_config.ath_hal_dma_beacon_response_time = 2; + ah->ah_config.ath_hal_sw_beacon_response_time = 10; + ah->ah_config.ath_hal_additional_swba_backoff = 0; + ah->ah_config.ath_hal_6mb_ack = 0x0; + ah->ah_config.ath_hal_cwmIgnoreExtCCA = 0; + ah->ah_config.ath_hal_pciePowerSaveEnable = 0; + ah->ah_config.ath_hal_pcieL1SKPEnable = 0; + ah->ah_config.ath_hal_pcieClockReq = 0; + ah->ah_config.ath_hal_pciePowerReset = 0x100; + ah->ah_config.ath_hal_pcieRestore = 0; + ah->ah_config.ath_hal_pcieWaen = 0; + ah->ah_config.ath_hal_analogShiftReg = 1; + ah->ah_config.ath_hal_htEnable = 1; + ah->ah_config.ath_hal_ofdmTrigLow = 200; + ah->ah_config.ath_hal_ofdmTrigHigh = 500; + ah->ah_config.ath_hal_cckTrigHigh = 200; + ah->ah_config.ath_hal_cckTrigLow = 100; + ah->ah_config.ath_hal_enableANI = 0; + ah->ah_config.ath_hal_noiseImmunityLvl = 4; + ah->ah_config.ath_hal_ofdmWeakSigDet = 1; + ah->ah_config.ath_hal_cckWeakSigThr = 0; + ah->ah_config.ath_hal_spurImmunityLvl = 2; + ah->ah_config.ath_hal_firStepLvl = 0; + ah->ah_config.ath_hal_rssiThrHigh = 40; + ah->ah_config.ath_hal_rssiThrLow = 7; + ah->ah_config.ath_hal_diversityControl = 0; + ah->ah_config.ath_hal_antennaSwitchSwap = 0; + + for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { + ah->ah_config.ath_hal_spurChans[i][0] = AR_NO_SPUR; + ah->ah_config.ath_hal_spurChans[i][1] = AR_NO_SPUR; + } + + ah->ah_config.ath_hal_intrMitigation = 0; +} + +static inline void ath9k_hw_override_ini(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + if (!AR_SREV_5416_V20_OR_LATER(ah) + || AR_SREV_9280_10_OR_LATER(ah)) + return; + + REG_WRITE(ah, 0x9800 + (651 << 2), 0x11); +} + +static inline void ath9k_hw_init_bb(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + u32 synthDelay; + + synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; + if (IS_CHAN_CCK(chan)) + synthDelay = (4 * synthDelay) / 22; + else + synthDelay /= 10; + + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + + udelay(synthDelay + BASE_ACTIVATE_DELAY); +} + +static inline void ath9k_hw_init_interrupt_masks(struct ath_hal *ah, + enum ath9k_opmode opmode) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + ahp->ah_maskReg = AR_IMR_TXERR | + AR_IMR_TXURN | + AR_IMR_RXERR | + AR_IMR_RXORN | + AR_IMR_BCNMISC; + + if (ahp->ah_intrMitigation) + ahp->ah_maskReg |= AR_IMR_RXINTM | AR_IMR_RXMINTR; + else + ahp->ah_maskReg |= AR_IMR_RXOK; + + ahp->ah_maskReg |= AR_IMR_TXOK; + + if (opmode == ATH9K_M_HOSTAP) + ahp->ah_maskReg |= AR_IMR_MIB; + + REG_WRITE(ah, AR_IMR, ahp->ah_maskReg); + REG_WRITE(ah, AR_IMR_S2, REG_READ(ah, AR_IMR_S2) | AR_IMR_S2_GTT); + + if (!AR_SREV_9100(ah)) { + REG_WRITE(ah, AR_INTR_SYNC_CAUSE, 0xFFFFFFFF); + REG_WRITE(ah, AR_INTR_SYNC_ENABLE, AR_INTR_SYNC_DEFAULT); + REG_WRITE(ah, AR_INTR_SYNC_MASK, 0); + } +} + +static inline void ath9k_hw_init_qos(struct ath_hal *ah) +{ + REG_WRITE(ah, AR_MIC_QOS_CONTROL, 0x100aa); + REG_WRITE(ah, AR_MIC_QOS_SELECT, 0x3210); + + REG_WRITE(ah, AR_QOS_NO_ACK, + SM(2, AR_QOS_NO_ACK_TWO_BIT) | + SM(5, AR_QOS_NO_ACK_BIT_OFF) | + SM(0, AR_QOS_NO_ACK_BYTE_OFF)); + + REG_WRITE(ah, AR_TXOP_X, AR_TXOP_X_VAL); + REG_WRITE(ah, AR_TXOP_0_3, 0xFFFFFFFF); + REG_WRITE(ah, AR_TXOP_4_7, 0xFFFFFFFF); + REG_WRITE(ah, AR_TXOP_8_11, 0xFFFFFFFF); + REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); +} + +static void ath9k_hw_analog_shift_rmw(struct ath_hal *ah, + u32 reg, + u32 mask, + u32 shift, + u32 val) +{ + u32 regVal; + + regVal = REG_READ(ah, reg) & ~mask; + regVal |= (val << shift) & mask; + + REG_WRITE(ah, reg, regVal); + + if (ah->ah_config.ath_hal_analogShiftReg) + udelay(100); + + return; +} + +static u8 ath9k_hw_get_num_ant_config(struct ath_hal_5416 *ahp, + enum hal_freq_band freq_band) +{ + struct ar5416_eeprom *eep = &ahp->ah_eeprom; + struct modal_eep_header *pModal = + &(eep->modalHeader[HAL_FREQ_BAND_2GHZ == freq_band]); + struct base_eep_header *pBase = &eep->baseEepHeader; + u8 num_ant_config; + + num_ant_config = 1; + + if (pBase->version >= 0x0E0D) + if (pModal->useAnt1) + num_ant_config += 1; + + return num_ant_config; +} + +static int +ath9k_hw_get_eeprom_antenna_cfg(struct ath_hal_5416 *ahp, + struct ath9k_channel *chan, + u8 index, + u16 *config) +{ + struct ar5416_eeprom *eep = &ahp->ah_eeprom; + struct modal_eep_header *pModal = + &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); + struct base_eep_header *pBase = &eep->baseEepHeader; + + switch (index) { + case 0: + *config = pModal->antCtrlCommon & 0xFFFF; + return 0; + case 1: + if (pBase->version >= 0x0E0D) { + if (pModal->useAnt1) { + *config = + ((pModal->antCtrlCommon & 0xFFFF0000) >> 16); + return 0; + } + } + break; + default: + break; + } + + return -EINVAL; +} + +static inline bool ath9k_hw_nvram_read(struct ath_hal *ah, + u32 off, + u16 *data) +{ + if (ath9k_hw_use_flash(ah)) + return ath9k_hw_flash_read(ah, off, data); + else + return ath9k_hw_eeprom_read(ah, off, data); +} + +static inline bool ath9k_hw_fill_eeprom(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416_eeprom *eep = &ahp->ah_eeprom; + u16 *eep_data; + int addr, ar5416_eep_start_loc = 0; + + if (!ath9k_hw_use_flash(ah)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: Reading from EEPROM, not flash\n", __func__); + ar5416_eep_start_loc = 256; + } + if (AR_SREV_9100(ah)) + ar5416_eep_start_loc = 256; + + eep_data = (u16 *) eep; + for (addr = 0; + addr < sizeof(struct ar5416_eeprom) / sizeof(u16); + addr++) { + if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc, + eep_data)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: Unable to read eeprom region \n", + __func__); + return false; + } + eep_data++; + } + return true; +} + +/* XXX: Clean me up, make me more legible */ +static bool +ath9k_hw_eeprom_set_board_values(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + struct modal_eep_header *pModal; + int i, regChainOffset; + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416_eeprom *eep = &ahp->ah_eeprom; + u8 txRxAttenLocal; + u16 ant_config; + + pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); + + txRxAttenLocal = IS_CHAN_2GHZ(chan) ? 23 : 44; + + ath9k_hw_get_eeprom_antenna_cfg(ahp, chan, 1, &ant_config); + REG_WRITE(ah, AR_PHY_SWITCH_COM, ant_config); + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + if (AR_SREV_9280(ah)) { + if (i >= 2) + break; + } + + if (AR_SREV_5416_V20_OR_LATER(ah) && + (ahp->ah_rxchainmask == 5 || ahp->ah_txchainmask == 5) + && (i != 0)) + regChainOffset = (i == 1) ? 0x2000 : 0x1000; + else + regChainOffset = i * 0x1000; + + REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset, + pModal->antCtrlChain[i]); + + REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0) + regChainOffset, + (REG_READ(ah, + AR_PHY_TIMING_CTRL4(0) + + regChainOffset) & + ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | + AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | + SM(pModal->iqCalICh[i], + AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | + SM(pModal->iqCalQCh[i], + AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); + + if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) { + if ((eep->baseEepHeader.version & + AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_3) { + txRxAttenLocal = pModal->txRxAttenCh[i]; + if (AR_SREV_9280_10_OR_LATER(ah)) { + REG_RMW_FIELD(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, + pModal-> + bswMargin[i]); + REG_RMW_FIELD(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN1_DB, + pModal-> + bswAtten[i]); + REG_RMW_FIELD(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, + pModal-> + xatten2Margin[i]); + REG_RMW_FIELD(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + AR_PHY_GAIN_2GHZ_XATTEN2_DB, + pModal-> + xatten2Db[i]); + } else { + REG_WRITE(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + (REG_READ(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset) & + ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) + | SM(pModal-> + bswMargin[i], + AR_PHY_GAIN_2GHZ_BSW_MARGIN)); + REG_WRITE(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + (REG_READ(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset) & + ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) + | SM(pModal->bswAtten[i], + AR_PHY_GAIN_2GHZ_BSW_ATTEN)); + } + } + if (AR_SREV_9280_10_OR_LATER(ah)) { + REG_RMW_FIELD(ah, + AR_PHY_RXGAIN + + regChainOffset, + AR9280_PHY_RXGAIN_TXRX_ATTEN, + txRxAttenLocal); + REG_RMW_FIELD(ah, + AR_PHY_RXGAIN + + regChainOffset, + AR9280_PHY_RXGAIN_TXRX_MARGIN, + pModal->rxTxMarginCh[i]); + } else { + REG_WRITE(ah, + AR_PHY_RXGAIN + regChainOffset, + (REG_READ(ah, + AR_PHY_RXGAIN + + regChainOffset) & + ~AR_PHY_RXGAIN_TXRX_ATTEN) | + SM(txRxAttenLocal, + AR_PHY_RXGAIN_TXRX_ATTEN)); + REG_WRITE(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset, + (REG_READ(ah, + AR_PHY_GAIN_2GHZ + + regChainOffset) & + ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) | + SM(pModal->rxTxMarginCh[i], + AR_PHY_GAIN_2GHZ_RXTX_MARGIN)); + } + } + } + + if (AR_SREV_9280_10_OR_LATER(ah)) { + if (IS_CHAN_2GHZ(chan)) { + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH0, + AR_AN_RF2G1_CH0_OB, + AR_AN_RF2G1_CH0_OB_S, + pModal->ob); + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH0, + AR_AN_RF2G1_CH0_DB, + AR_AN_RF2G1_CH0_DB_S, + pModal->db); + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH1, + AR_AN_RF2G1_CH1_OB, + AR_AN_RF2G1_CH1_OB_S, + pModal->ob_ch1); + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF2G1_CH1, + AR_AN_RF2G1_CH1_DB, + AR_AN_RF2G1_CH1_DB_S, + pModal->db_ch1); + } else { + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH0, + AR_AN_RF5G1_CH0_OB5, + AR_AN_RF5G1_CH0_OB5_S, + pModal->ob); + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH0, + AR_AN_RF5G1_CH0_DB5, + AR_AN_RF5G1_CH0_DB5_S, + pModal->db); + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH1, + AR_AN_RF5G1_CH1_OB5, + AR_AN_RF5G1_CH1_OB5_S, + pModal->ob_ch1); + ath9k_hw_analog_shift_rmw(ah, AR_AN_RF5G1_CH1, + AR_AN_RF5G1_CH1_DB5, + AR_AN_RF5G1_CH1_DB5_S, + pModal->db_ch1); + } + ath9k_hw_analog_shift_rmw(ah, AR_AN_TOP2, + AR_AN_TOP2_XPABIAS_LVL, + AR_AN_TOP2_XPABIAS_LVL_S, + pModal->xpaBiasLvl); + ath9k_hw_analog_shift_rmw(ah, AR_AN_TOP2, + AR_AN_TOP2_LOCALBIAS, + AR_AN_TOP2_LOCALBIAS_S, + pModal->local_bias); + DPRINTF(ah->ah_sc, ATH_DBG_ANY, "ForceXPAon: %d\n", + pModal->force_xpaon); + REG_RMW_FIELD(ah, AR_PHY_XPA_CFG, AR_PHY_FORCE_XPA_CFG, + pModal->force_xpaon); + } + + REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, + pModal->switchSettling); + REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, + pModal->adcDesiredSize); + + if (!AR_SREV_9280_10_OR_LATER(ah)) + REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, + AR_PHY_DESIRED_SZ_PGA, + pModal->pgaDesiredSize); + + REG_WRITE(ah, AR_PHY_RF_CTL4, + SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) + | SM(pModal->txEndToXpaOff, + AR_PHY_RF_CTL4_TX_END_XPAB_OFF) + | SM(pModal->txFrameToXpaOn, + AR_PHY_RF_CTL4_FRAME_XPAA_ON) + | SM(pModal->txFrameToXpaOn, + AR_PHY_RF_CTL4_FRAME_XPAB_ON)); + + REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, + pModal->txEndToRxOn); + if (AR_SREV_9280_10_OR_LATER(ah)) { + REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62, + pModal->thresh62); + REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, + AR_PHY_EXT_CCA0_THRESH62, + pModal->thresh62); + } else { + REG_RMW_FIELD(ah, AR_PHY_CCA, AR_PHY_CCA_THRESH62, + pModal->thresh62); + REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, + AR_PHY_EXT_CCA_THRESH62, + pModal->thresh62); + } + + if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_2) { + REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, + AR_PHY_TX_END_DATA_START, + pModal->txFrameToDataStart); + REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_END_PA_ON, + pModal->txFrameToPaOn); + } + + if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_3) { + if (IS_CHAN_HT40(chan)) + REG_RMW_FIELD(ah, AR_PHY_SETTLING, + AR_PHY_SETTLING_SWITCH, + pModal->swSettleHt40); + } + + return true; +} + +static inline int ath9k_hw_check_eeprom(struct ath_hal *ah) +{ + u32 sum = 0, el; + u16 *eepdata; + int i; + struct ath_hal_5416 *ahp = AH5416(ah); + bool need_swap = false; + struct ar5416_eeprom *eep = + (struct ar5416_eeprom *) &ahp->ah_eeprom; + + if (!ath9k_hw_use_flash(ah)) { + u16 magic, magic2; + int addr; + + if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, + &magic)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: Reading Magic # failed\n", __func__); + return false; + } + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "%s: Read Magic = 0x%04X\n", + __func__, magic); + + if (magic != AR5416_EEPROM_MAGIC) { + magic2 = swab16(magic); + + if (magic2 == AR5416_EEPROM_MAGIC) { + need_swap = true; + eepdata = (u16 *) (&ahp->ah_eeprom); + + for (addr = 0; + addr < + sizeof(struct ar5416_eeprom) / + sizeof(u16); addr++) { + u16 temp; + + temp = swab16(*eepdata); + *eepdata = temp; + eepdata++; + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "0x%04X ", *eepdata); + if (((addr + 1) % 6) == 0) + DPRINTF(ah->ah_sc, + ATH_DBG_EEPROM, + "\n"); + } + } else { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Invalid EEPROM Magic. " + "endianness missmatch.\n"); + return -EINVAL; + } + } + } + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n", + need_swap ? "True" : "False"); + + if (need_swap) + el = swab16(ahp->ah_eeprom.baseEepHeader.length); + else + el = ahp->ah_eeprom.baseEepHeader.length; + + if (el > sizeof(struct ar5416_eeprom)) + el = sizeof(struct ar5416_eeprom) / sizeof(u16); + else + el = el / sizeof(u16); + + eepdata = (u16 *) (&ahp->ah_eeprom); + + for (i = 0; i < el; i++) + sum ^= *eepdata++; + + if (need_swap) { + u32 integer, j; + u16 word; + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "EEPROM Endianness is not native.. Changing \n"); + + word = swab16(eep->baseEepHeader.length); + eep->baseEepHeader.length = word; + + word = swab16(eep->baseEepHeader.checksum); + eep->baseEepHeader.checksum = word; + + word = swab16(eep->baseEepHeader.version); + eep->baseEepHeader.version = word; + + word = swab16(eep->baseEepHeader.regDmn[0]); + eep->baseEepHeader.regDmn[0] = word; + + word = swab16(eep->baseEepHeader.regDmn[1]); + eep->baseEepHeader.regDmn[1] = word; + + word = swab16(eep->baseEepHeader.rfSilent); + eep->baseEepHeader.rfSilent = word; + + word = swab16(eep->baseEepHeader.blueToothOptions); + eep->baseEepHeader.blueToothOptions = word; + + word = swab16(eep->baseEepHeader.deviceCap); + eep->baseEepHeader.deviceCap = word; + + for (j = 0; j < ARRAY_SIZE(eep->modalHeader); j++) { + struct modal_eep_header *pModal = + &eep->modalHeader[j]; + integer = swab32(pModal->antCtrlCommon); + pModal->antCtrlCommon = integer; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + integer = swab32(pModal->antCtrlChain[i]); + pModal->antCtrlChain[i] = integer; + } + + for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) { + word = swab16(pModal->spurChans[i].spurChan); + pModal->spurChans[i].spurChan = word; + } + } + } + + if (sum != 0xffff || ar5416_get_eep_ver(ahp) != AR5416_EEP_VER || + ar5416_get_eep_rev(ahp) < AR5416_EEP_NO_BACK_VER) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Bad EEPROM checksum 0x%x or revision 0x%04x\n", + sum, ar5416_get_eep_ver(ahp)); + return -EINVAL; + } + + return 0; +} + +static bool ath9k_hw_chip_test(struct ath_hal *ah) +{ + u32 regAddr[2] = { AR_STA_ID0, AR_PHY_BASE + (8 << 2) }; + u32 regHold[2]; + u32 patternData[4] = { 0x55555555, + 0xaaaaaaaa, + 0x66666666, + 0x99999999 }; + int i, j; + + for (i = 0; i < 2; i++) { + u32 addr = regAddr[i]; + u32 wrData, rdData; + + regHold[i] = REG_READ(ah, addr); + for (j = 0; j < 0x100; j++) { + wrData = (j << 16) | j; + REG_WRITE(ah, addr, wrData); + rdData = REG_READ(ah, addr); + if (rdData != wrData) { + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "%s: address test failed " + "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", + __func__, addr, wrData, rdData); + return false; + } + } + for (j = 0; j < 4; j++) { + wrData = patternData[j]; + REG_WRITE(ah, addr, wrData); + rdData = REG_READ(ah, addr); + if (wrData != rdData) { + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "%s: address test failed " + "addr: 0x%08x - wr:0x%08x != rd:0x%08x\n", + __func__, addr, wrData, rdData); + return false; + } + } + REG_WRITE(ah, regAddr[i], regHold[i]); + } + udelay(100); + return true; +} + +u32 ath9k_hw_getrxfilter(struct ath_hal *ah) +{ + u32 bits = REG_READ(ah, AR_RX_FILTER); + u32 phybits = REG_READ(ah, AR_PHY_ERR); + + if (phybits & AR_PHY_ERR_RADAR) + bits |= ATH9K_RX_FILTER_PHYRADAR; + if (phybits & (AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING)) + bits |= ATH9K_RX_FILTER_PHYERR; + return bits; +} + +void ath9k_hw_setrxfilter(struct ath_hal *ah, u32 bits) +{ + u32 phybits; + + REG_WRITE(ah, AR_RX_FILTER, (bits & 0xffff) | AR_RX_COMPR_BAR); + phybits = 0; + if (bits & ATH9K_RX_FILTER_PHYRADAR) + phybits |= AR_PHY_ERR_RADAR; + if (bits & ATH9K_RX_FILTER_PHYERR) + phybits |= AR_PHY_ERR_OFDM_TIMING | AR_PHY_ERR_CCK_TIMING; + REG_WRITE(ah, AR_PHY_ERR, phybits); + + if (phybits) + REG_WRITE(ah, AR_RXCFG, + REG_READ(ah, AR_RXCFG) | AR_RXCFG_ZLFDMA); + else + REG_WRITE(ah, AR_RXCFG, + REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_ZLFDMA); +} + +bool ath9k_hw_setcapability(struct ath_hal *ah, + enum hal_capability_type type, + u32 capability, + u32 setting, + int *status) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 v; + + switch (type) { + case HAL_CAP_TKIP_MIC: + if (setting) + ahp->ah_staId1Defaults |= + AR_STA_ID1_CRPT_MIC_ENABLE; + else + ahp->ah_staId1Defaults &= + ~AR_STA_ID1_CRPT_MIC_ENABLE; + return true; + case HAL_CAP_DIVERSITY: + v = REG_READ(ah, AR_PHY_CCK_DETECT); + if (setting) + v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; + else + v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; + REG_WRITE(ah, AR_PHY_CCK_DETECT, v); + return true; + case HAL_CAP_MCAST_KEYSRCH: + if (setting) + ahp->ah_staId1Defaults |= AR_STA_ID1_MCAST_KSRCH; + else + ahp->ah_staId1Defaults &= ~AR_STA_ID1_MCAST_KSRCH; + return true; + case HAL_CAP_TSF_ADJUST: + if (setting) + ahp->ah_miscMode |= AR_PCU_TX_ADD_TSF; + else + ahp->ah_miscMode &= ~AR_PCU_TX_ADD_TSF; + return true; + default: + return false; + } +} + +void ath9k_hw_dmaRegDump(struct ath_hal *ah) +{ + u32 val[ATH9K_NUM_DMA_DEBUG_REGS]; + int qcuOffset = 0, dcuOffset = 0; + u32 *qcuBase = &val[0], *dcuBase = &val[4]; + int i; + + REG_WRITE(ah, AR_MACMISC, + ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | + (AR_MACMISC_MISC_OBS_BUS_1 << + AR_MACMISC_MISC_OBS_BUS_MSB_S))); + + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "Raw DMA Debug values:\n"); + for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) { + if (i % 4 == 0) + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "\n"); + + val[i] = REG_READ(ah, AR_DMADBG_0 + (i * sizeof(u32))); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "%d: %08x ", i, val[i]); + } + + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "\n\n"); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); + + for (i = 0; i < ATH9K_NUM_QUEUES; + i++, qcuOffset += 4, dcuOffset += 5) { + if (i == 8) { + qcuOffset = 0; + qcuBase++; + } + + if (i == 6) { + dcuOffset = 0; + dcuBase++; + } + + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "%2d %2x %1x %2x %2x\n", + i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset, + (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + + 3), + val[2] & (0x7 << (i * 3)) >> (i * 3), + (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset); + } + + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "\n"); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "qcu_stitch state: %2x qcu_fetch state: %2x\n", + (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "qcu_complete state: %2x dcu_complete state: %2x\n", + (val[3] & 0x1c000000) >> 26, (val[6] & 0x3)); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "dcu_arb state: %2x dcu_fp state: %2x\n", + (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n", + (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "txfifo_valid_0: %1d txfifo_valid_1: %1d\n", + (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n", + (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17); + + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, "pcu observe 0x%x \n", + REG_READ(ah, AR_OBS_BUS_1)); + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "AR_CR 0x%x \n", REG_READ(ah, AR_CR)); +} + +u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hal *ah, + u32 *rxc_pcnt, + u32 *rxf_pcnt, + u32 *txf_pcnt) +{ + static u32 cycles, rx_clear, rx_frame, tx_frame; + u32 good = 1; + + u32 rc = REG_READ(ah, AR_RCCNT); + u32 rf = REG_READ(ah, AR_RFCNT); + u32 tf = REG_READ(ah, AR_TFCNT); + u32 cc = REG_READ(ah, AR_CCCNT); + + if (cycles == 0 || cycles > cc) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: cycle counter wrap. ExtBusy = 0\n", + __func__); + good = 0; + } else { + u32 cc_d = cc - cycles; + u32 rc_d = rc - rx_clear; + u32 rf_d = rf - rx_frame; + u32 tf_d = tf - tx_frame; + + if (cc_d != 0) { + *rxc_pcnt = rc_d * 100 / cc_d; + *rxf_pcnt = rf_d * 100 / cc_d; + *txf_pcnt = tf_d * 100 / cc_d; + } else { + good = 0; + } + } + + cycles = cc; + rx_frame = rf; + rx_clear = rc; + tx_frame = tf; + + return good; +} + +void ath9k_hw_set11nmac2040(struct ath_hal *ah, enum ath9k_ht_macmode mode) +{ + u32 macmode; + + if (mode == ATH9K_HT_MACMODE_2040 && + !ah->ah_config.ath_hal_cwmIgnoreExtCCA) + macmode = AR_2040_JOINED_RX_CLEAR; + else + macmode = 0; + + REG_WRITE(ah, AR_2040_MODE, macmode); +} + +static void ath9k_hw_mark_phy_inactive(struct ath_hal *ah) +{ + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); +} + + +static struct ath_hal_5416 *ath9k_hw_newstate(u16 devid, + struct ath_softc *sc, + void __iomem *mem, + int *status) +{ + static const u8 defbssidmask[ETH_ALEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + struct ath_hal_5416 *ahp; + struct ath_hal *ah; + + ahp = kzalloc(sizeof(struct ath_hal_5416), GFP_KERNEL); + if (ahp == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: cannot allocate memory for state block\n", + __func__); + *status = -ENOMEM; + return NULL; + } + + ah = &ahp->ah; + + memcpy(&ahp->ah, &ar5416hal, sizeof(struct ath_hal)); + + ah->ah_sc = sc; + ah->ah_sh = mem; + + ah->ah_devid = devid; + ah->ah_subvendorid = 0; + + ah->ah_flags = 0; + if ((devid == AR5416_AR9100_DEVID)) + ah->ah_macVersion = AR_SREV_VERSION_9100; + if (!AR_SREV_9100(ah)) + ah->ah_flags = AH_USE_EEPROM; + + ah->ah_powerLimit = MAX_RATE_POWER; + ah->ah_tpScale = ATH9K_TP_SCALE_MAX; + + ahp->ah_atimWindow = 0; + ahp->ah_diversityControl = ah->ah_config.ath_hal_diversityControl; + ahp->ah_antennaSwitchSwap = + ah->ah_config.ath_hal_antennaSwitchSwap; + + ahp->ah_staId1Defaults = AR_STA_ID1_CRPT_MIC_ENABLE; + ahp->ah_beaconInterval = 100; + ahp->ah_enable32kHzClock = DONT_USE_32KHZ; + ahp->ah_slottime = (u32) -1; + ahp->ah_acktimeout = (u32) -1; + ahp->ah_ctstimeout = (u32) -1; + ahp->ah_globaltxtimeout = (u32) -1; + memcpy(&ahp->ah_bssidmask, defbssidmask, ETH_ALEN); + + ahp->ah_gBeaconRate = 0; + + return ahp; +} + +static int ath9k_hw_eeprom_attach(struct ath_hal *ah) +{ + int status; + + if (ath9k_hw_use_flash(ah)) + ath9k_hw_flash_map(ah); + + if (!ath9k_hw_fill_eeprom(ah)) + return -EIO; + + status = ath9k_hw_check_eeprom(ah); + + return status; +} + +u32 ath9k_hw_get_eeprom(struct ath_hal_5416 *ahp, + enum eeprom_param param) +{ + struct ar5416_eeprom *eep = &ahp->ah_eeprom; + struct modal_eep_header *pModal = eep->modalHeader; + struct base_eep_header *pBase = &eep->baseEepHeader; + + switch (param) { + case EEP_NFTHRESH_5: + return -pModal[0].noiseFloorThreshCh[0]; + case EEP_NFTHRESH_2: + return -pModal[1].noiseFloorThreshCh[0]; + case AR_EEPROM_MAC(0): + return pBase->macAddr[0] << 8 | pBase->macAddr[1]; + case AR_EEPROM_MAC(1): + return pBase->macAddr[2] << 8 | pBase->macAddr[3]; + case AR_EEPROM_MAC(2): + return pBase->macAddr[4] << 8 | pBase->macAddr[5]; + case EEP_REG_0: + return pBase->regDmn[0]; + case EEP_REG_1: + return pBase->regDmn[1]; + case EEP_OP_CAP: + return pBase->deviceCap; + case EEP_OP_MODE: + return pBase->opCapFlags; + case EEP_RF_SILENT: + return pBase->rfSilent; + case EEP_OB_5: + return pModal[0].ob; + case EEP_DB_5: + return pModal[0].db; + case EEP_OB_2: + return pModal[1].ob; + case EEP_DB_2: + return pModal[1].db; + case EEP_MINOR_REV: + return pBase->version & AR5416_EEP_VER_MINOR_MASK; + case EEP_TX_MASK: + return pBase->txMask; + case EEP_RX_MASK: + return pBase->rxMask; + default: + return 0; + } +} + +static inline int ath9k_hw_get_radiorev(struct ath_hal *ah) +{ + u32 val; + int i; + + REG_WRITE(ah, AR_PHY(0x36), 0x00007058); + for (i = 0; i < 8; i++) + REG_WRITE(ah, AR_PHY(0x20), 0x00010000); + val = (REG_READ(ah, AR_PHY(256)) >> 24) & 0xff; + val = ((val & 0xf0) >> 4) | ((val & 0x0f) << 4); + return ath9k_hw_reverse_bits(val, 8); +} + +static inline int ath9k_hw_init_macaddr(struct ath_hal *ah) +{ + u32 sum; + int i; + u16 eeval; + struct ath_hal_5416 *ahp = AH5416(ah); + DECLARE_MAC_BUF(mac); + + sum = 0; + for (i = 0; i < 3; i++) { + eeval = ath9k_hw_get_eeprom(ahp, AR_EEPROM_MAC(i)); + sum += eeval; + ahp->ah_macaddr[2 * i] = eeval >> 8; + ahp->ah_macaddr[2 * i + 1] = eeval & 0xff; + } + if (sum == 0 || sum == 0xffff * 3) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: mac address read failed: %s\n", __func__, + print_mac(mac, ahp->ah_macaddr)); + return -EADDRNOTAVAIL; + } + + return 0; +} + +static inline int16_t ath9k_hw_interpolate(u16 target, + u16 srcLeft, + u16 srcRight, + int16_t targetLeft, + int16_t targetRight) +{ + int16_t rv; + + if (srcRight == srcLeft) { + rv = targetLeft; + } else { + rv = (int16_t) (((target - srcLeft) * targetRight + + (srcRight - target) * targetLeft) / + (srcRight - srcLeft)); + } + return rv; +} + +static inline u16 ath9k_hw_fbin2freq(u8 fbin, + bool is2GHz) +{ + + if (fbin == AR5416_BCHAN_UNUSED) + return fbin; + + return (u16) ((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin)); +} + +static u16 ath9k_hw_eeprom_get_spur_chan(struct ath_hal *ah, + u16 i, + bool is2GHz) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416_eeprom *eep = + (struct ar5416_eeprom *) &ahp->ah_eeprom; + u16 spur_val = AR_NO_SPUR; + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "Getting spur idx %d is2Ghz. %d val %x\n", + i, is2GHz, ah->ah_config.ath_hal_spurChans[i][is2GHz]); + + switch (ah->ah_config.ath_hal_spurMode) { + case SPUR_DISABLE: + break; + case SPUR_ENABLE_IOCTL: + spur_val = ah->ah_config.ath_hal_spurChans[i][is2GHz]; + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "Getting spur val from new loc. %d\n", spur_val); + break; + case SPUR_ENABLE_EEPROM: + spur_val = eep->modalHeader[is2GHz].spurChans[i].spurChan; + break; + + } + return spur_val; +} + +static inline int ath9k_hw_rfattach(struct ath_hal *ah) +{ + bool rfStatus = false; + int ecode = 0; + + rfStatus = ath9k_hw_init_rf(ah, &ecode); + if (!rfStatus) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: RF setup failed, status %u\n", __func__, + ecode); + return ecode; + } + + return 0; +} + +static int ath9k_hw_rf_claim(struct ath_hal *ah) +{ + u32 val; + + REG_WRITE(ah, AR_PHY(0), 0x00000007); + + val = ath9k_hw_get_radiorev(ah); + switch (val & AR_RADIO_SREV_MAJOR) { + case 0: + val = AR_RAD5133_SREV_MAJOR; + break; + case AR_RAD5133_SREV_MAJOR: + case AR_RAD5122_SREV_MAJOR: + case AR_RAD2133_SREV_MAJOR: + case AR_RAD2122_SREV_MAJOR: + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: 5G Radio Chip Rev 0x%02X is not " + "supported by this driver\n", + __func__, ah->ah_analog5GhzRev); + return -EOPNOTSUPP; + } + + ah->ah_analog5GhzRev = val; + + return 0; +} + +static inline void ath9k_hw_init_pll(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + u32 pll; + + if (AR_SREV_9100(ah)) { + if (chan && IS_CHAN_5GHZ(chan)) + pll = 0x1450; + else + pll = 0x1458; + } else { + if (AR_SREV_9280_10_OR_LATER(ah)) { + pll = SM(0x5, AR_RTC_9160_PLL_REFDIV); + + if (chan && IS_CHAN_HALF_RATE(chan)) + pll |= SM(0x1, AR_RTC_9160_PLL_CLKSEL); + else if (chan && IS_CHAN_QUARTER_RATE(chan)) + pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL); + + if (chan && IS_CHAN_5GHZ(chan)) { + pll |= SM(0x28, AR_RTC_9160_PLL_DIV); + + + if (AR_SREV_9280_20(ah)) { + if (((chan->channel % 20) == 0) + || ((chan->channel % 10) == 0)) + pll = 0x2850; + else + pll = 0x142c; + } + } else { + pll |= SM(0x2c, AR_RTC_9160_PLL_DIV); + } + + } else if (AR_SREV_9160_10_OR_LATER(ah)) { + + pll = SM(0x5, AR_RTC_9160_PLL_REFDIV); + + if (chan && IS_CHAN_HALF_RATE(chan)) + pll |= SM(0x1, AR_RTC_9160_PLL_CLKSEL); + else if (chan && IS_CHAN_QUARTER_RATE(chan)) + pll |= SM(0x2, AR_RTC_9160_PLL_CLKSEL); + + if (chan && IS_CHAN_5GHZ(chan)) + pll |= SM(0x50, AR_RTC_9160_PLL_DIV); + else + pll |= SM(0x58, AR_RTC_9160_PLL_DIV); + } else { + pll = AR_RTC_PLL_REFDIV_5 | AR_RTC_PLL_DIV2; + + if (chan && IS_CHAN_HALF_RATE(chan)) + pll |= SM(0x1, AR_RTC_PLL_CLKSEL); + else if (chan && IS_CHAN_QUARTER_RATE(chan)) + pll |= SM(0x2, AR_RTC_PLL_CLKSEL); + + if (chan && IS_CHAN_5GHZ(chan)) + pll |= SM(0xa, AR_RTC_PLL_DIV); + else + pll |= SM(0xb, AR_RTC_PLL_DIV); + } + } + REG_WRITE(ah, (u16) (AR_RTC_PLL_CONTROL), pll); + + udelay(RTC_PLL_SETTLE_DELAY); + + REG_WRITE(ah, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_DERIVED_CLK); +} + +static void ath9k_hw_set_regs(struct ath_hal *ah, struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode) +{ + u32 phymode; + struct ath_hal_5416 *ahp = AH5416(ah); + + phymode = AR_PHY_FC_HT_EN | AR_PHY_FC_SHORT_GI_40 + | AR_PHY_FC_SINGLE_HT_LTF1 | AR_PHY_FC_WALSH; + + if (IS_CHAN_HT40(chan)) { + phymode |= AR_PHY_FC_DYN2040_EN; + + if ((chan->chanmode == CHANNEL_A_HT40PLUS) || + (chan->chanmode == CHANNEL_G_HT40PLUS)) + phymode |= AR_PHY_FC_DYN2040_PRI_CH; + + if (ahp->ah_extprotspacing == ATH9K_HT_EXTPROTSPACING_25) + phymode |= AR_PHY_FC_DYN2040_EXT_CH; + } + REG_WRITE(ah, AR_PHY_TURBO, phymode); + + ath9k_hw_set11nmac2040(ah, macmode); + + REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S); + REG_WRITE(ah, AR_CST, 0xF << AR_CST_TIMEOUT_LIMIT_S); +} + +static void ath9k_hw_set_operating_mode(struct ath_hal *ah, int opmode) +{ + u32 val; + + val = REG_READ(ah, AR_STA_ID1); + val &= ~(AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC); + switch (opmode) { + case ATH9K_M_HOSTAP: + REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_STA_AP + | AR_STA_ID1_KSRCH_MODE); + REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); + break; + case ATH9K_M_IBSS: + REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC + | AR_STA_ID1_KSRCH_MODE); + REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); + break; + case ATH9K_M_STA: + case ATH9K_M_MONITOR: + REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_KSRCH_MODE); + break; + } +} + +static inline void +ath9k_hw_set_rfmode(struct ath_hal *ah, struct ath9k_channel *chan) +{ + u32 rfMode = 0; + + if (chan == NULL) + return; + + rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan)) + ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; + + if (!AR_SREV_9280_10_OR_LATER(ah)) + rfMode |= (IS_CHAN_5GHZ(chan)) ? AR_PHY_MODE_RF5GHZ : + AR_PHY_MODE_RF2GHZ; + + if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) + rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE); + + REG_WRITE(ah, AR_PHY_MODE, rfMode); +} + +static bool ath9k_hw_set_reset(struct ath_hal *ah, int type) +{ + u32 rst_flags; + u32 tmpReg; + + REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | + AR_RTC_FORCE_WAKE_ON_INT); + + if (AR_SREV_9100(ah)) { + rst_flags = AR_RTC_RC_MAC_WARM | AR_RTC_RC_MAC_COLD | + AR_RTC_RC_COLD_RESET | AR_RTC_RC_WARM_RESET; + } else { + tmpReg = REG_READ(ah, AR_INTR_SYNC_CAUSE); + if (tmpReg & + (AR_INTR_SYNC_LOCAL_TIMEOUT | + AR_INTR_SYNC_RADM_CPL_TIMEOUT)) { + REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0); + REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF); + } else { + REG_WRITE(ah, AR_RC, AR_RC_AHB); + } + + rst_flags = AR_RTC_RC_MAC_WARM; + if (type == ATH9K_RESET_COLD) + rst_flags |= AR_RTC_RC_MAC_COLD; + } + + REG_WRITE(ah, (u16) (AR_RTC_RC), rst_flags); + udelay(50); + + REG_WRITE(ah, (u16) (AR_RTC_RC), 0); + if (!ath9k_hw_wait(ah, (u16) (AR_RTC_RC), AR_RTC_RC_M, 0)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: RTC stuck in MAC reset\n", + __func__); + return false; + } + + if (!AR_SREV_9100(ah)) + REG_WRITE(ah, AR_RC, 0); + + ath9k_hw_init_pll(ah, NULL); + + if (AR_SREV_9100(ah)) + udelay(50); + + return true; +} + +static inline bool ath9k_hw_set_reset_power_on(struct ath_hal *ah) +{ + REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | + AR_RTC_FORCE_WAKE_ON_INT); + + REG_WRITE(ah, (u16) (AR_RTC_RESET), 0); + REG_WRITE(ah, (u16) (AR_RTC_RESET), 1); + + if (!ath9k_hw_wait(ah, + AR_RTC_STATUS, + AR_RTC_STATUS_M, + AR_RTC_STATUS_ON)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: RTC not waking up\n", + __func__); + return false; + } + + ath9k_hw_read_revisions(ah); + + return ath9k_hw_set_reset(ah, ATH9K_RESET_WARM); +} + +static bool ath9k_hw_set_reset_reg(struct ath_hal *ah, + u32 type) +{ + REG_WRITE(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); + + switch (type) { + case ATH9K_RESET_POWER_ON: + return ath9k_hw_set_reset_power_on(ah); + break; + case ATH9K_RESET_WARM: + case ATH9K_RESET_COLD: + return ath9k_hw_set_reset(ah, type); + break; + default: + return false; + } +} + +static inline +struct ath9k_channel *ath9k_hw_check_chan(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + if (!(IS_CHAN_2GHZ(chan) ^ IS_CHAN_5GHZ(chan))) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: invalid channel %u/0x%x; not marked as " + "2GHz or 5GHz\n", __func__, chan->channel, + chan->channelFlags); + return NULL; + } + + if (!IS_CHAN_OFDM(chan) && + !IS_CHAN_CCK(chan) && + !IS_CHAN_HT20(chan) && + !IS_CHAN_HT40(chan)) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: invalid channel %u/0x%x; not marked as " + "OFDM or CCK or HT20 or HT40PLUS or HT40MINUS\n", + __func__, chan->channel, chan->channelFlags); + return NULL; + } + + return ath9k_regd_check_channel(ah, chan); +} + +static inline bool +ath9k_hw_get_lower_upper_index(u8 target, + u8 *pList, + u16 listSize, + u16 *indexL, + u16 *indexR) +{ + u16 i; + + if (target <= pList[0]) { + *indexL = *indexR = 0; + return true; + } + if (target >= pList[listSize - 1]) { + *indexL = *indexR = (u16) (listSize - 1); + return true; + } + + for (i = 0; i < listSize - 1; i++) { + if (pList[i] == target) { + *indexL = *indexR = i; + return true; + } + if (target < pList[i + 1]) { + *indexL = i; + *indexR = (u16) (i + 1); + return false; + } + } + return false; +} + +static int16_t ath9k_hw_get_nf_hist_mid(int16_t *nfCalBuffer) +{ + int16_t nfval; + int16_t sort[ATH9K_NF_CAL_HIST_MAX]; + int i, j; + + for (i = 0; i < ATH9K_NF_CAL_HIST_MAX; i++) + sort[i] = nfCalBuffer[i]; + + for (i = 0; i < ATH9K_NF_CAL_HIST_MAX - 1; i++) { + for (j = 1; j < ATH9K_NF_CAL_HIST_MAX - i; j++) { + if (sort[j] > sort[j - 1]) { + nfval = sort[j]; + sort[j] = sort[j - 1]; + sort[j - 1] = nfval; + } + } + } + nfval = sort[(ATH9K_NF_CAL_HIST_MAX - 1) >> 1]; + + return nfval; +} + +static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h, + int16_t *nfarray) +{ + int i; + + for (i = 0; i < NUM_NF_READINGS; i++) { + h[i].nfCalBuffer[h[i].currIndex] = nfarray[i]; + + if (++h[i].currIndex >= ATH9K_NF_CAL_HIST_MAX) + h[i].currIndex = 0; + + if (h[i].invalidNFcount > 0) { + if (nfarray[i] < AR_PHY_CCA_MIN_BAD_VALUE + || nfarray[i] > AR_PHY_CCA_MAX_HIGH_VALUE) { + h[i].invalidNFcount = ATH9K_NF_CAL_HIST_MAX; + } else { + h[i].invalidNFcount--; + h[i].privNF = nfarray[i]; + } + } else { + h[i].privNF = + ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); + } + } + return; +} + +static void ar5416GetNoiseFloor(struct ath_hal *ah, + int16_t nfarray[NUM_NF_READINGS]) +{ + int16_t nf; + + if (AR_SREV_9280_10_OR_LATER(ah)) + nf = MS(REG_READ(ah, AR_PHY_CCA), AR9280_PHY_MINCCA_PWR); + else + nf = MS(REG_READ(ah, AR_PHY_CCA), AR_PHY_MINCCA_PWR); + + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "NF calibrated [ctl] [chain 0] is %d\n", nf); + nfarray[0] = nf; + + if (AR_SREV_9280_10_OR_LATER(ah)) + nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), + AR9280_PHY_CH1_MINCCA_PWR); + else + nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), + AR_PHY_CH1_MINCCA_PWR); + + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL, + "NF calibrated [ctl] [chain 1] is %d\n", nf); + nfarray[1] = nf; + + if (!AR_SREV_9280(ah)) { + nf = MS(REG_READ(ah, AR_PHY_CH2_CCA), + AR_PHY_CH2_MINCCA_PWR); + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL, + "NF calibrated [ctl] [chain 2] is %d\n", nf); + nfarray[2] = nf; + } + + if (AR_SREV_9280_10_OR_LATER(ah)) + nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), + AR9280_PHY_EXT_MINCCA_PWR); + else + nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), + AR_PHY_EXT_MINCCA_PWR); + + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL, + "NF calibrated [ext] [chain 0] is %d\n", nf); + nfarray[3] = nf; + + if (AR_SREV_9280_10_OR_LATER(ah)) + nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), + AR9280_PHY_CH1_EXT_MINCCA_PWR); + else + nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), + AR_PHY_CH1_EXT_MINCCA_PWR); + + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "NF calibrated [ext] [chain 1] is %d\n", nf); + nfarray[4] = nf; + + if (!AR_SREV_9280(ah)) { + nf = MS(REG_READ(ah, AR_PHY_CH2_EXT_CCA), + AR_PHY_CH2_EXT_MINCCA_PWR); + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL, + "NF calibrated [ext] [chain 2] is %d\n", nf); + nfarray[5] = nf; + } +} + +static bool +getNoiseFloorThresh(struct ath_hal *ah, + const struct ath9k_channel *chan, + int16_t *nft) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + switch (chan->chanmode) { + case CHANNEL_A: + case CHANNEL_A_HT20: + case CHANNEL_A_HT40PLUS: + case CHANNEL_A_HT40MINUS: + *nft = (int16_t) ath9k_hw_get_eeprom(ahp, EEP_NFTHRESH_5); + break; + case CHANNEL_B: + case CHANNEL_G: + case CHANNEL_G_HT20: + case CHANNEL_G_HT40PLUS: + case CHANNEL_G_HT40MINUS: + *nft = (int16_t) ath9k_hw_get_eeprom(ahp, EEP_NFTHRESH_2); + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: invalid channel flags 0x%x\n", __func__, + chan->channelFlags); + return false; + } + return true; +} + +static void ath9k_hw_start_nfcal(struct ath_hal *ah) +{ + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_ENABLE_NF); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_NO_UPDATE_NF); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); +} + +static void +ath9k_hw_loadnf(struct ath_hal *ah, struct ath9k_channel *chan) +{ + struct ath9k_nfcal_hist *h; + int i, j; + int32_t val; + const u32 ar5416_cca_regs[6] = { + AR_PHY_CCA, + AR_PHY_CH1_CCA, + AR_PHY_CH2_CCA, + AR_PHY_EXT_CCA, + AR_PHY_CH1_EXT_CCA, + AR_PHY_CH2_EXT_CCA + }; + u8 chainmask; + + if (AR_SREV_9280(ah)) + chainmask = 0x1B; + else + chainmask = 0x3F; + +#ifdef ATH_NF_PER_CHAN + h = chan->nfCalHist; +#else + h = ah->nfCalHist; +#endif + + for (i = 0; i < NUM_NF_READINGS; i++) { + if (chainmask & (1 << i)) { + val = REG_READ(ah, ar5416_cca_regs[i]); + val &= 0xFFFFFE00; + val |= (((u32) (h[i].privNF) << 1) & 0x1ff); + REG_WRITE(ah, ar5416_cca_regs[i], val); + } + } + + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_ENABLE_NF); + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_NO_UPDATE_NF); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); + + for (j = 0; j < 1000; j++) { + if ((REG_READ(ah, AR_PHY_AGC_CONTROL) & + AR_PHY_AGC_CONTROL_NF) == 0) + break; + udelay(10); + } + + for (i = 0; i < NUM_NF_READINGS; i++) { + if (chainmask & (1 << i)) { + val = REG_READ(ah, ar5416_cca_regs[i]); + val &= 0xFFFFFE00; + val |= (((u32) (-50) << 1) & 0x1ff); + REG_WRITE(ah, ar5416_cca_regs[i], val); + } + } +} + +static int16_t ath9k_hw_getnf(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + int16_t nf, nfThresh; + int16_t nfarray[NUM_NF_READINGS] = { 0 }; + struct ath9k_nfcal_hist *h; + u8 chainmask; + + if (AR_SREV_9280(ah)) + chainmask = 0x1B; + else + chainmask = 0x3F; + + chan->channelFlags &= (~CHANNEL_CW_INT); + if (REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: NF did not complete in calibration window\n", + __func__); + nf = 0; + chan->rawNoiseFloor = nf; + return chan->rawNoiseFloor; + } else { + ar5416GetNoiseFloor(ah, nfarray); + nf = nfarray[0]; + if (getNoiseFloorThresh(ah, chan, &nfThresh) + && nf > nfThresh) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: noise floor failed detected; " + "detected %d, threshold %d\n", __func__, + nf, nfThresh); + chan->channelFlags |= CHANNEL_CW_INT; + } + } + +#ifdef ATH_NF_PER_CHAN + h = chan->nfCalHist; +#else + h = ah->nfCalHist; +#endif + + ath9k_hw_update_nfcal_hist_buffer(h, nfarray); + chan->rawNoiseFloor = h[0].privNF; + + return chan->rawNoiseFloor; +} + +static void ath9k_hw_update_mibstats(struct ath_hal *ah, + struct ath9k_mib_stats *stats) +{ + stats->ackrcv_bad += REG_READ(ah, AR_ACK_FAIL); + stats->rts_bad += REG_READ(ah, AR_RTS_FAIL); + stats->fcs_bad += REG_READ(ah, AR_FCS_FAIL); + stats->rts_good += REG_READ(ah, AR_RTS_OK); + stats->beacons += REG_READ(ah, AR_BEACON_CNT); +} + +static void ath9k_enable_mib_counters(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Enable mib counters\n"); + + ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats); + + REG_WRITE(ah, AR_FILT_OFDM, 0); + REG_WRITE(ah, AR_FILT_CCK, 0); + REG_WRITE(ah, AR_MIBC, + ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS) + & 0x0f); + REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); + REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); +} + +static void ath9k_hw_disable_mib_counters(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Disabling MIB counters\n"); + + REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC | AR_MIBC_CMC); + + ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats); + + REG_WRITE(ah, AR_FILT_OFDM, 0); + REG_WRITE(ah, AR_FILT_CCK, 0); +} + +static int ath9k_hw_get_ani_channel_idx(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + for (i = 0; i < ARRAY_SIZE(ahp->ah_ani); i++) { + if (ahp->ah_ani[i].c.channel == chan->channel) + return i; + if (ahp->ah_ani[i].c.channel == 0) { + ahp->ah_ani[i].c.channel = chan->channel; + ahp->ah_ani[i].c.channelFlags = chan->channelFlags; + return i; + } + } + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "No more channel states left. Using channel 0\n"); + return 0; +} + +static void ath9k_hw_ani_attach(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + ahp->ah_hasHwPhyCounters = 1; + + memset(ahp->ah_ani, 0, sizeof(ahp->ah_ani)); + for (i = 0; i < ARRAY_SIZE(ahp->ah_ani); i++) { + ahp->ah_ani[i].ofdmTrigHigh = ATH9K_ANI_OFDM_TRIG_HIGH; + ahp->ah_ani[i].ofdmTrigLow = ATH9K_ANI_OFDM_TRIG_LOW; + ahp->ah_ani[i].cckTrigHigh = ATH9K_ANI_CCK_TRIG_HIGH; + ahp->ah_ani[i].cckTrigLow = ATH9K_ANI_CCK_TRIG_LOW; + ahp->ah_ani[i].rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH; + ahp->ah_ani[i].rssiThrLow = ATH9K_ANI_RSSI_THR_LOW; + ahp->ah_ani[i].ofdmWeakSigDetectOff = + !ATH9K_ANI_USE_OFDM_WEAK_SIG; + ahp->ah_ani[i].cckWeakSigThreshold = + ATH9K_ANI_CCK_WEAK_SIG_THR; + ahp->ah_ani[i].spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; + ahp->ah_ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL; + if (ahp->ah_hasHwPhyCounters) { + ahp->ah_ani[i].ofdmPhyErrBase = + AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH; + ahp->ah_ani[i].cckPhyErrBase = + AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH; + } + } + if (ahp->ah_hasHwPhyCounters) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "Setting OfdmErrBase = 0x%08x\n", + ahp->ah_ani[0].ofdmPhyErrBase); + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n", + ahp->ah_ani[0].cckPhyErrBase); + + REG_WRITE(ah, AR_PHY_ERR_1, ahp->ah_ani[0].ofdmPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_2, ahp->ah_ani[0].cckPhyErrBase); + ath9k_enable_mib_counters(ah); + } + ahp->ah_aniPeriod = ATH9K_ANI_PERIOD; + if (ah->ah_config.ath_hal_enableANI) + ahp->ah_procPhyErr |= HAL_PROCESS_ANI; +} + +static inline void ath9k_hw_ani_setup(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + const int totalSizeDesired[] = { -55, -55, -55, -55, -62 }; + const int coarseHigh[] = { -14, -14, -14, -14, -12 }; + const int coarseLow[] = { -64, -64, -64, -64, -70 }; + const int firpwr[] = { -78, -78, -78, -78, -80 }; + + for (i = 0; i < 5; i++) { + ahp->ah_totalSizeDesired[i] = totalSizeDesired[i]; + ahp->ah_coarseHigh[i] = coarseHigh[i]; + ahp->ah_coarseLow[i] = coarseLow[i]; + ahp->ah_firpwr[i] = firpwr[i]; + } +} + +static void ath9k_hw_ani_detach(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Detaching Ani\n"); + if (ahp->ah_hasHwPhyCounters) { + ath9k_hw_disable_mib_counters(ah); + REG_WRITE(ah, AR_PHY_ERR_1, 0); + REG_WRITE(ah, AR_PHY_ERR_2, 0); + } +} + + +static bool ath9k_hw_ani_control(struct ath_hal *ah, + enum ath9k_ani_cmd cmd, int param) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416AniState *aniState = ahp->ah_curani; + + switch (cmd & ahp->ah_ani_function) { + case ATH9K_ANI_NOISE_IMMUNITY_LEVEL:{ + u32 level = param; + + if (level >= ARRAY_SIZE(ahp->ah_totalSizeDesired)) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: level out of range (%u > %u)\n", + __func__, level, + (unsigned) ARRAY_SIZE(ahp-> + ah_totalSizeDesired)); + return false; + } + + REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, + AR_PHY_DESIRED_SZ_TOT_DES, + ahp->ah_totalSizeDesired[level]); + REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, + AR_PHY_AGC_CTL1_COARSE_LOW, + ahp->ah_coarseLow[level]); + REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1, + AR_PHY_AGC_CTL1_COARSE_HIGH, + ahp->ah_coarseHigh[level]); + REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, + AR_PHY_FIND_SIG_FIRPWR, + ahp->ah_firpwr[level]); + + if (level > aniState->noiseImmunityLevel) + ahp->ah_stats.ast_ani_niup++; + else if (level < aniState->noiseImmunityLevel) + ahp->ah_stats.ast_ani_nidown++; + aniState->noiseImmunityLevel = level; + break; + } + case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{ + const int m1ThreshLow[] = { 127, 50 }; + const int m2ThreshLow[] = { 127, 40 }; + const int m1Thresh[] = { 127, 0x4d }; + const int m2Thresh[] = { 127, 0x40 }; + const int m2CountThr[] = { 31, 16 }; + const int m2CountThrLow[] = { 63, 48 }; + u32 on = param ? 1 : 0; + + REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M1_THRESH_LOW, + m1ThreshLow[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M2_THRESH_LOW, + m2ThreshLow[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M1_THRESH, + m1Thresh[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M2_THRESH, + m2Thresh[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR, + AR_PHY_SFCORR_M2COUNT_THR, + m2CountThr[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, + m2CountThrLow[on]); + + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M1_THRESH_LOW, + m1ThreshLow[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M2_THRESH_LOW, + m2ThreshLow[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M1_THRESH, + m1Thresh[on]); + REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, + AR_PHY_SFCORR_EXT_M2_THRESH, + m2Thresh[on]); + + if (on) + REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); + else + REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, + AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); + + if (!on != aniState->ofdmWeakSigDetectOff) { + if (on) + ahp->ah_stats.ast_ani_ofdmon++; + else + ahp->ah_stats.ast_ani_ofdmoff++; + aniState->ofdmWeakSigDetectOff = !on; + } + break; + } + case ATH9K_ANI_CCK_WEAK_SIGNAL_THR:{ + const int weakSigThrCck[] = { 8, 6 }; + u32 high = param ? 1 : 0; + + REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT, + AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, + weakSigThrCck[high]); + if (high != aniState->cckWeakSigThreshold) { + if (high) + ahp->ah_stats.ast_ani_cckhigh++; + else + ahp->ah_stats.ast_ani_ccklow++; + aniState->cckWeakSigThreshold = high; + } + break; + } + case ATH9K_ANI_FIRSTEP_LEVEL:{ + const int firstep[] = { 0, 4, 8 }; + u32 level = param; + + if (level >= ARRAY_SIZE(firstep)) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: level out of range (%u > %u)\n", + __func__, level, + (unsigned) ARRAY_SIZE(firstep)); + return false; + } + REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, + AR_PHY_FIND_SIG_FIRSTEP, + firstep[level]); + if (level > aniState->firstepLevel) + ahp->ah_stats.ast_ani_stepup++; + else if (level < aniState->firstepLevel) + ahp->ah_stats.ast_ani_stepdown++; + aniState->firstepLevel = level; + break; + } + case ATH9K_ANI_SPUR_IMMUNITY_LEVEL:{ + const int cycpwrThr1[] = + { 2, 4, 6, 8, 10, 12, 14, 16 }; + u32 level = param; + + if (level >= ARRAY_SIZE(cycpwrThr1)) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: level out of range (%u > %u)\n", + __func__, level, + (unsigned) + ARRAY_SIZE(cycpwrThr1)); + return false; + } + REG_RMW_FIELD(ah, AR_PHY_TIMING5, + AR_PHY_TIMING5_CYCPWR_THR1, + cycpwrThr1[level]); + if (level > aniState->spurImmunityLevel) + ahp->ah_stats.ast_ani_spurup++; + else if (level < aniState->spurImmunityLevel) + ahp->ah_stats.ast_ani_spurdown++; + aniState->spurImmunityLevel = level; + break; + } + case ATH9K_ANI_PRESENT: + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: invalid cmd %u\n", __func__, cmd); + return false; + } + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "%s: ANI parameters:\n", __func__); + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "noiseImmunityLevel=%d, spurImmunityLevel=%d, " + "ofdmWeakSigDetectOff=%d\n", + aniState->noiseImmunityLevel, aniState->spurImmunityLevel, + !aniState->ofdmWeakSigDetectOff); + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "cckWeakSigThreshold=%d, " + "firstepLevel=%d, listenTime=%d\n", + aniState->cckWeakSigThreshold, aniState->firstepLevel, + aniState->listenTime); + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "cycleCount=%d, ofdmPhyErrCount=%d, cckPhyErrCount=%d\n\n", + aniState->cycleCount, aniState->ofdmPhyErrCount, + aniState->cckPhyErrCount); + return true; +} + +static void ath9k_ani_restart(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416AniState *aniState; + + if (!DO_ANI(ah)) + return; + + aniState = ahp->ah_curani; + + aniState->listenTime = 0; + if (ahp->ah_hasHwPhyCounters) { + if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) { + aniState->ofdmPhyErrBase = 0; + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "OFDM Trigger is too high for hw counters\n"); + } else { + aniState->ofdmPhyErrBase = + AR_PHY_COUNTMAX - aniState->ofdmTrigHigh; + } + if (aniState->cckTrigHigh > AR_PHY_COUNTMAX) { + aniState->cckPhyErrBase = 0; + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "CCK Trigger is too high for hw counters\n"); + } else { + aniState->cckPhyErrBase = + AR_PHY_COUNTMAX - aniState->cckTrigHigh; + } + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: Writing ofdmbase=%u cckbase=%u\n", + __func__, aniState->ofdmPhyErrBase, + aniState->cckPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_1, aniState->ofdmPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_2, aniState->cckPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); + REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); + + ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats); + } + aniState->ofdmPhyErrCount = 0; + aniState->cckPhyErrCount = 0; +} + +static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *chan = ah->ah_curchan; + struct ar5416AniState *aniState; + enum wireless_mode mode; + int32_t rssi; + + if (!DO_ANI(ah)) + return; + + aniState = ahp->ah_curani; + + if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) { + if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, + aniState->noiseImmunityLevel + 1)) { + return; + } + } + + if (aniState->spurImmunityLevel < HAL_SPUR_IMMUNE_MAX) { + if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, + aniState->spurImmunityLevel + 1)) { + return; + } + } + + if (ah->ah_opmode == ATH9K_M_HOSTAP) { + if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1); + } + return; + } + rssi = BEACON_RSSI(ahp); + if (rssi > aniState->rssiThrHigh) { + if (!aniState->ofdmWeakSigDetectOff) { + if (ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + false)) { + ath9k_hw_ani_control(ah, + ATH9K_ANI_SPUR_IMMUNITY_LEVEL, + 0); + return; + } + } + if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1); + return; + } + } else if (rssi > aniState->rssiThrLow) { + if (aniState->ofdmWeakSigDetectOff) + ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + true); + if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1); + return; + } else { + mode = ath9k_hw_chan2wmode(ah, chan); + if (mode == WIRELESS_MODE_11g || mode == WIRELESS_MODE_11b) { + if (!aniState->ofdmWeakSigDetectOff) + ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + false); + if (aniState->firstepLevel > 0) + ath9k_hw_ani_control(ah, + ATH9K_ANI_FIRSTEP_LEVEL, + 0); + return; + } + } +} + +static void ath9k_hw_ani_cck_err_trigger(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *chan = ah->ah_curchan; + struct ar5416AniState *aniState; + enum wireless_mode mode; + int32_t rssi; + + if (!DO_ANI(ah)) + return; + + aniState = ahp->ah_curani; + if (aniState->noiseImmunityLevel < HAL_NOISE_IMMUNE_MAX) { + if (ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, + aniState->noiseImmunityLevel + 1)) { + return; + } + } + if (ah->ah_opmode == ATH9K_M_HOSTAP) { + if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) { + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1); + } + return; + } + rssi = BEACON_RSSI(ahp); + if (rssi > aniState->rssiThrLow) { + if (aniState->firstepLevel < HAL_FIRST_STEP_MAX) + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel + 1); + } else { + mode = ath9k_hw_chan2wmode(ah, chan); + if (mode == WIRELESS_MODE_11g || mode == WIRELESS_MODE_11b) { + if (aniState->firstepLevel > 0) + ath9k_hw_ani_control(ah, + ATH9K_ANI_FIRSTEP_LEVEL, + 0); + } + } +} + +static void ath9k_ani_reset(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416AniState *aniState; + struct ath9k_channel *chan = ah->ah_curchan; + int index; + + if (!DO_ANI(ah)) + return; + + index = ath9k_hw_get_ani_channel_idx(ah, chan); + aniState = &ahp->ah_ani[index]; + ahp->ah_curani = aniState; + + if (DO_ANI(ah) && ah->ah_opmode != ATH9K_M_STA + && ah->ah_opmode != ATH9K_M_IBSS) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: Reset ANI state opmode %u\n", __func__, + ah->ah_opmode); + ahp->ah_stats.ast_ani_reset++; + ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, 0); + ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, 0); + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, 0); + ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + !ATH9K_ANI_USE_OFDM_WEAK_SIG); + ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR, + ATH9K_ANI_CCK_WEAK_SIG_THR); + ath9k_hw_setrxfilter(ah, + ath9k_hw_getrxfilter(ah) | + ATH9K_RX_FILTER_PHYERR); + if (ah->ah_opmode == ATH9K_M_HOSTAP) { + ahp->ah_curani->ofdmTrigHigh = + ah->ah_config.ath_hal_ofdmTrigHigh; + ahp->ah_curani->ofdmTrigLow = + ah->ah_config.ath_hal_ofdmTrigLow; + ahp->ah_curani->cckTrigHigh = + ah->ah_config.ath_hal_cckTrigHigh; + ahp->ah_curani->cckTrigLow = + ah->ah_config.ath_hal_cckTrigLow; + } + ath9k_ani_restart(ah); + return; + } + + if (aniState->noiseImmunityLevel != 0) + ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, + aniState->noiseImmunityLevel); + if (aniState->spurImmunityLevel != 0) + ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, + aniState->spurImmunityLevel); + if (aniState->ofdmWeakSigDetectOff) + ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + !aniState->ofdmWeakSigDetectOff); + if (aniState->cckWeakSigThreshold) + ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR, + aniState->cckWeakSigThreshold); + if (aniState->firstepLevel != 0) + ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel); + if (ahp->ah_hasHwPhyCounters) { + ath9k_hw_setrxfilter(ah, + ath9k_hw_getrxfilter(ah) & + ~ATH9K_RX_FILTER_PHYERR); + ath9k_ani_restart(ah); + REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING); + REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING); + + } else { + ath9k_ani_restart(ah); + ath9k_hw_setrxfilter(ah, + ath9k_hw_getrxfilter(ah) | + ATH9K_RX_FILTER_PHYERR); + } +} + +void ath9k_hw_procmibevent(struct ath_hal *ah, + const struct ath9k_node_stats *stats) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 phyCnt1, phyCnt2; + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Processing Mib Intr\n"); + + REG_WRITE(ah, AR_FILT_OFDM, 0); + REG_WRITE(ah, AR_FILT_CCK, 0); + if (!(REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING)) + REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR); + + ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats); + ahp->ah_stats.ast_nodestats = *stats; + + if (!DO_ANI(ah)) + return; + + phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); + phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); + if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) || + ((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) { + struct ar5416AniState *aniState = ahp->ah_curani; + u32 ofdmPhyErrCnt, cckPhyErrCnt; + + ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase; + ahp->ah_stats.ast_ani_ofdmerrs += + ofdmPhyErrCnt - aniState->ofdmPhyErrCount; + aniState->ofdmPhyErrCount = ofdmPhyErrCnt; + + cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase; + ahp->ah_stats.ast_ani_cckerrs += + cckPhyErrCnt - aniState->cckPhyErrCount; + aniState->cckPhyErrCount = cckPhyErrCnt; + + if (aniState->ofdmPhyErrCount > aniState->ofdmTrigHigh) + ath9k_hw_ani_ofdm_err_trigger(ah); + if (aniState->cckPhyErrCount > aniState->cckTrigHigh) + ath9k_hw_ani_cck_err_trigger(ah); + + ath9k_ani_restart(ah); + } +} + +static void ath9k_hw_ani_lower_immunity(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416AniState *aniState; + int32_t rssi; + + aniState = ahp->ah_curani; + + if (ah->ah_opmode == ATH9K_M_HOSTAP) { + if (aniState->firstepLevel > 0) { + if (ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel - 1)) { + return; + } + } + } else { + rssi = BEACON_RSSI(ahp); + if (rssi > aniState->rssiThrHigh) { + /* XXX: Handle me */ + } else if (rssi > aniState->rssiThrLow) { + if (aniState->ofdmWeakSigDetectOff) { + if (ath9k_hw_ani_control(ah, + ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + true) == + true) { + return; + } + } + if (aniState->firstepLevel > 0) { + if (ath9k_hw_ani_control + (ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel - 1) == + true) { + return; + } + } + } else { + if (aniState->firstepLevel > 0) { + if (ath9k_hw_ani_control + (ah, ATH9K_ANI_FIRSTEP_LEVEL, + aniState->firstepLevel - 1) == + true) { + return; + } + } + } + } + + if (aniState->spurImmunityLevel > 0) { + if (ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, + aniState->spurImmunityLevel - 1)) { + return; + } + } + + if (aniState->noiseImmunityLevel > 0) { + ath9k_hw_ani_control(ah, ATH9K_ANI_NOISE_IMMUNITY_LEVEL, + aniState->noiseImmunityLevel - 1); + return; + } +} + +static int32_t ath9k_hw_ani_get_listen_time(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416AniState *aniState; + u32 txFrameCount, rxFrameCount, cycleCount; + int32_t listenTime; + + txFrameCount = REG_READ(ah, AR_TFCNT); + rxFrameCount = REG_READ(ah, AR_RFCNT); + cycleCount = REG_READ(ah, AR_CCCNT); + + aniState = ahp->ah_curani; + if (aniState->cycleCount == 0 || aniState->cycleCount > cycleCount) { + + listenTime = 0; + ahp->ah_stats.ast_ani_lzero++; + } else { + int32_t ccdelta = cycleCount - aniState->cycleCount; + int32_t rfdelta = rxFrameCount - aniState->rxFrameCount; + int32_t tfdelta = txFrameCount - aniState->txFrameCount; + listenTime = (ccdelta - rfdelta - tfdelta) / 44000; + } + aniState->cycleCount = cycleCount; + aniState->txFrameCount = txFrameCount; + aniState->rxFrameCount = rxFrameCount; + + return listenTime; +} + +void ath9k_hw_ani_monitor(struct ath_hal *ah, + const struct ath9k_node_stats *stats, + struct ath9k_channel *chan) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416AniState *aniState; + int32_t listenTime; + + aniState = ahp->ah_curani; + ahp->ah_stats.ast_nodestats = *stats; + + listenTime = ath9k_hw_ani_get_listen_time(ah); + if (listenTime < 0) { + ahp->ah_stats.ast_ani_lneg++; + ath9k_ani_restart(ah); + return; + } + + aniState->listenTime += listenTime; + + if (ahp->ah_hasHwPhyCounters) { + u32 phyCnt1, phyCnt2; + u32 ofdmPhyErrCnt, cckPhyErrCnt; + + ath9k_hw_update_mibstats(ah, &ahp->ah_mibStats); + + phyCnt1 = REG_READ(ah, AR_PHY_ERR_1); + phyCnt2 = REG_READ(ah, AR_PHY_ERR_2); + + if (phyCnt1 < aniState->ofdmPhyErrBase || + phyCnt2 < aniState->cckPhyErrBase) { + if (phyCnt1 < aniState->ofdmPhyErrBase) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: phyCnt1 0x%x, resetting " + "counter value to 0x%x\n", + __func__, phyCnt1, + aniState->ofdmPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_1, + aniState->ofdmPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_MASK_1, + AR_PHY_ERR_OFDM_TIMING); + } + if (phyCnt2 < aniState->cckPhyErrBase) { + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "%s: phyCnt2 0x%x, resetting " + "counter value to 0x%x\n", + __func__, phyCnt2, + aniState->cckPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_2, + aniState->cckPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_MASK_2, + AR_PHY_ERR_CCK_TIMING); + } + return; + } + + ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase; + ahp->ah_stats.ast_ani_ofdmerrs += + ofdmPhyErrCnt - aniState->ofdmPhyErrCount; + aniState->ofdmPhyErrCount = ofdmPhyErrCnt; + + cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase; + ahp->ah_stats.ast_ani_cckerrs += + cckPhyErrCnt - aniState->cckPhyErrCount; + aniState->cckPhyErrCount = cckPhyErrCnt; + } + + if (!DO_ANI(ah)) + return; + + if (aniState->listenTime > 5 * ahp->ah_aniPeriod) { + if (aniState->ofdmPhyErrCount <= aniState->listenTime * + aniState->ofdmTrigLow / 1000 && + aniState->cckPhyErrCount <= aniState->listenTime * + aniState->cckTrigLow / 1000) + ath9k_hw_ani_lower_immunity(ah); + ath9k_ani_restart(ah); + } else if (aniState->listenTime > ahp->ah_aniPeriod) { + if (aniState->ofdmPhyErrCount > aniState->listenTime * + aniState->ofdmTrigHigh / 1000) { + ath9k_hw_ani_ofdm_err_trigger(ah); + ath9k_ani_restart(ah); + } else if (aniState->cckPhyErrCount > + aniState->listenTime * aniState->cckTrigHigh / + 1000) { + ath9k_hw_ani_cck_err_trigger(ah); + ath9k_ani_restart(ah); + } + } +} + +#ifndef ATH_NF_PER_CHAN +static void ath9k_init_nfcal_hist_buffer(struct ath_hal *ah) +{ + int i, j; + + for (i = 0; i < NUM_NF_READINGS; i++) { + ah->nfCalHist[i].currIndex = 0; + ah->nfCalHist[i].privNF = AR_PHY_CCA_MAX_GOOD_VALUE; + ah->nfCalHist[i].invalidNFcount = + AR_PHY_CCA_FILTERWINDOW_LENGTH; + for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) { + ah->nfCalHist[i].nfCalBuffer[j] = + AR_PHY_CCA_MAX_GOOD_VALUE; + } + } + return; +} +#endif + +static void ath9k_hw_gpio_cfg_output_mux(struct ath_hal *ah, + u32 gpio, u32 type) +{ + int addr; + u32 gpio_shift, tmp; + + if (gpio > 11) + addr = AR_GPIO_OUTPUT_MUX3; + else if (gpio > 5) + addr = AR_GPIO_OUTPUT_MUX2; + else + addr = AR_GPIO_OUTPUT_MUX1; + + gpio_shift = (gpio % 6) * 5; + + if (AR_SREV_9280_20_OR_LATER(ah) + || (addr != AR_GPIO_OUTPUT_MUX1)) { + REG_RMW(ah, addr, (type << gpio_shift), + (0x1f << gpio_shift)); + } else { + tmp = REG_READ(ah, addr); + tmp = ((tmp & 0x1F0) << 1) | (tmp & ~0x1F0); + tmp &= ~(0x1f << gpio_shift); + tmp |= (type << gpio_shift); + REG_WRITE(ah, addr, tmp); + } +} + +static bool ath9k_hw_cfg_output(struct ath_hal *ah, u32 gpio, + enum ath9k_gpio_output_mux_type + halSignalType) +{ + u32 ah_signal_type; + u32 gpio_shift; + + static u32 MuxSignalConversionTable[] = { + + AR_GPIO_OUTPUT_MUX_AS_OUTPUT, + + AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED, + + AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED, + + AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED, + + AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED, + }; + + if ((halSignalType >= 0) + && (halSignalType < ARRAY_SIZE(MuxSignalConversionTable))) + ah_signal_type = MuxSignalConversionTable[halSignalType]; + else + return false; + + ath9k_hw_gpio_cfg_output_mux(ah, gpio, ah_signal_type); + + gpio_shift = 2 * gpio; + + REG_RMW(ah, + AR_GPIO_OE_OUT, + (AR_GPIO_OE_OUT_DRV_ALL << gpio_shift), + (AR_GPIO_OE_OUT_DRV << gpio_shift)); + + return true; +} + +static bool ath9k_hw_set_gpio(struct ath_hal *ah, u32 gpio, + u32 val) +{ + REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio), + AR_GPIO_BIT(gpio)); + return true; +} + +static u32 ath9k_hw_gpio_get(struct ath_hal *ah, u32 gpio) +{ + if (gpio >= ah->ah_caps.halNumGpioPins) + return 0xffffffff; + + if (AR_SREV_9280_10_OR_LATER(ah)) { + return (MS + (REG_READ(ah, AR_GPIO_IN_OUT), + AR928X_GPIO_IN_VAL) & AR_GPIO_BIT(gpio)) != 0; + } else { + return (MS(REG_READ(ah, AR_GPIO_IN_OUT), AR_GPIO_IN_VAL) & + AR_GPIO_BIT(gpio)) != 0; + } +} + +static inline int ath9k_hw_post_attach(struct ath_hal *ah) +{ + int ecode; + + if (!ath9k_hw_chip_test(ah)) { + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "%s: hardware self-test failed\n", __func__); + return -ENODEV; + } + + ecode = ath9k_hw_rf_claim(ah); + if (ecode != 0) + return ecode; + + ecode = ath9k_hw_eeprom_attach(ah); + if (ecode != 0) + return ecode; + ecode = ath9k_hw_rfattach(ah); + if (ecode != 0) + return ecode; + + if (!AR_SREV_9100(ah)) { + ath9k_hw_ani_setup(ah); + ath9k_hw_ani_attach(ah); + } + return 0; +} + +static u32 ath9k_hw_ini_fixup(struct ath_hal *ah, + struct ar5416_eeprom *pEepData, + u32 reg, u32 value) +{ + struct base_eep_header *pBase = &(pEepData->baseEepHeader); + + switch (ah->ah_devid) { + case AR9280_DEVID_PCI: + if (reg == 0x7894) { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "ini VAL: %x EEPROM: %x\n", value, + (pBase->version & 0xff)); + + if ((pBase->version & 0xff) > 0x0a) { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "PWDCLKIND: %d\n", + pBase->pwdclkind); + value &= ~AR_AN_TOP2_PWDCLKIND; + value |= AR_AN_TOP2_PWDCLKIND & (pBase-> + pwdclkind << AR_AN_TOP2_PWDCLKIND_S); + } else { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "PWDCLKIND Earlier Rev\n"); + } + + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "final ini VAL: %x\n", value); + } + break; + } + return value; +} + +static bool ath9k_hw_fill_cap_info(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct hal_capabilities *pCap = &ah->ah_caps; + u16 capField = 0, eeval; + + eeval = ath9k_hw_get_eeprom(ahp, EEP_REG_0); + + ah->ah_currentRD = eeval; + + eeval = ath9k_hw_get_eeprom(ahp, EEP_REG_1); + ah->ah_currentRDExt = eeval; + + capField = ath9k_hw_get_eeprom(ahp, EEP_OP_CAP); + + if (ah->ah_opmode != ATH9K_M_HOSTAP && + ah->ah_subvendorid == AR_SUBVENDOR_ID_NEW_A) { + if (ah->ah_currentRD == 0x64 || ah->ah_currentRD == 0x65) + ah->ah_currentRD += 5; + else if (ah->ah_currentRD == 0x41) + ah->ah_currentRD = 0x43; + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: regdomain mapped to 0x%x\n", __func__, + ah->ah_currentRD); + } + + pCap->halWirelessModes = 0; + eeval = ath9k_hw_get_eeprom(ahp, EEP_OP_MODE); + + if (eeval & AR5416_OPFLAGS_11A) { + pCap->halWirelessModes |= ATH9K_MODE_SEL_11A | + ((!ah->ah_config.ath_hal_htEnable + || (eeval & AR5416_OPFLAGS_N_5G_HT20)) ? 0 + : (ATH9K_MODE_SEL_11NA_HT20 | + ((eeval & AR5416_OPFLAGS_N_5G_HT40) ? 0 + : (ATH9K_MODE_SEL_11NA_HT40PLUS | + ATH9K_MODE_SEL_11NA_HT40MINUS)))); + } + if (eeval & AR5416_OPFLAGS_11G) { + pCap->halWirelessModes |= + ATH9K_MODE_SEL_11B | ATH9K_MODE_SEL_11G | + ((!ah->ah_config.ath_hal_htEnable + || (eeval & AR5416_OPFLAGS_N_2G_HT20)) ? 0 + : (ATH9K_MODE_SEL_11NG_HT20 | + ((eeval & AR5416_OPFLAGS_N_2G_HT40) ? 0 + : (ATH9K_MODE_SEL_11NG_HT40PLUS | + ATH9K_MODE_SEL_11NG_HT40MINUS)))); + + } + pCap->halTxChainMask = ath9k_hw_get_eeprom(ahp, EEP_TX_MASK); + if ((ah->ah_isPciExpress) + || (eeval & AR5416_OPFLAGS_11A)) { + pCap->halRxChainMask = + ath9k_hw_get_eeprom(ahp, EEP_RX_MASK); + } else { + pCap->halRxChainMask = + (ath9k_hw_gpio_get(ah, 0)) ? 0x5 : 0x7; + } + + if (!(AR_SREV_9280(ah) && (ah->ah_macRev == 0))) + ahp->ah_miscMode |= AR_PCU_MIC_NEW_LOC_ENA; + + pCap->halLow2GhzChan = 2312; + pCap->halHigh2GhzChan = 2732; + + pCap->halLow5GhzChan = 4920; + pCap->halHigh5GhzChan = 6100; + + pCap->halCipherCkipSupport = false; + pCap->halCipherTkipSupport = true; + pCap->halCipherAesCcmSupport = true; + + pCap->halMicCkipSupport = false; + pCap->halMicTkipSupport = true; + pCap->halMicAesCcmSupport = true; + + pCap->halChanSpreadSupport = true; + + pCap->halHTSupport = + ah->ah_config.ath_hal_htEnable ? true : false; + pCap->halGTTSupport = true; + pCap->halVEOLSupport = true; + pCap->halBssIdMaskSupport = true; + pCap->halMcastKeySrchSupport = false; + + if (capField & AR_EEPROM_EEPCAP_MAXQCU) + pCap->halTotalQueues = + MS(capField, AR_EEPROM_EEPCAP_MAXQCU); + else + pCap->halTotalQueues = ATH9K_NUM_TX_QUEUES; + + if (capField & AR_EEPROM_EEPCAP_KC_ENTRIES) + pCap->halKeyCacheSize = + 1 << MS(capField, AR_EEPROM_EEPCAP_KC_ENTRIES); + else + pCap->halKeyCacheSize = AR_KEYTABLE_SIZE; + + pCap->halFastCCSupport = true; + pCap->halNumMRRetries = 4; + pCap->halTxTrigLevelMax = MAX_TX_FIFO_THRESHOLD; + + if (AR_SREV_9280_10_OR_LATER(ah)) + pCap->halNumGpioPins = AR928X_NUM_GPIO; + else + pCap->halNumGpioPins = AR_NUM_GPIO; + + if (AR_SREV_9280_10_OR_LATER(ah)) { + pCap->halWowSupport = true; + pCap->halWowMatchPatternExact = true; + } else { + pCap->halWowSupport = false; + pCap->halWowMatchPatternExact = false; + } + + if (AR_SREV_9160_10_OR_LATER(ah) || AR_SREV_9100(ah)) { + pCap->halCSTSupport = true; + pCap->halRtsAggrLimit = ATH_AMPDU_LIMIT_MAX; + } else { + pCap->halRtsAggrLimit = (8 * 1024); + } + + pCap->halEnhancedPmSupport = true; + + ah->ah_rfsilent = ath9k_hw_get_eeprom(ahp, EEP_RF_SILENT); + if (ah->ah_rfsilent & EEP_RFSILENT_ENABLED) { + ahp->ah_gpioSelect = + MS(ah->ah_rfsilent, EEP_RFSILENT_GPIO_SEL); + ahp->ah_polarity = + MS(ah->ah_rfsilent, EEP_RFSILENT_POLARITY); + + ath9k_hw_setcapability(ah, HAL_CAP_RFSILENT, 1, true, + NULL); + pCap->halRfSilentSupport = true; + } + + if ((ah->ah_macVersion == AR_SREV_VERSION_5416_PCI) || + (ah->ah_macVersion == AR_SREV_VERSION_5416_PCIE) || + (ah->ah_macVersion == AR_SREV_VERSION_9160) || + (ah->ah_macVersion == AR_SREV_VERSION_9100) || + (ah->ah_macVersion == AR_SREV_VERSION_9280)) + pCap->halAutoSleepSupport = false; + else + pCap->halAutoSleepSupport = true; + + if (AR_SREV_9280(ah)) + pCap->hal4kbSplitTransSupport = false; + else + pCap->hal4kbSplitTransSupport = true; + + if (ah->ah_currentRDExt & (1 << REG_EXT_JAPAN_MIDBAND)) { + pCap->halRegCap = + AR_EEPROM_EEREGCAP_EN_KK_NEW_11A | + AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN | + AR_EEPROM_EEREGCAP_EN_KK_U2 | + AR_EEPROM_EEREGCAP_EN_KK_MIDBAND; + } else { + pCap->halRegCap = + AR_EEPROM_EEREGCAP_EN_KK_NEW_11A | + AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN; + } + + pCap->halRegCap |= AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND; + + pCap->halNumAntCfg5GHz = + ath9k_hw_get_num_ant_config(ahp, HAL_FREQ_BAND_5GHZ); + pCap->halNumAntCfg2GHz = + ath9k_hw_get_num_ant_config(ahp, HAL_FREQ_BAND_2GHZ); + + return true; +} + +static void ar5416DisablePciePhy(struct ath_hal *ah) +{ + if (!AR_SREV_9100(ah)) + return; + + REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); + REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + REG_WRITE(ah, AR_PCIE_SERDES, 0x28000029); + REG_WRITE(ah, AR_PCIE_SERDES, 0x57160824); + REG_WRITE(ah, AR_PCIE_SERDES, 0x25980579); + REG_WRITE(ah, AR_PCIE_SERDES, 0x00000000); + REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); + REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); + REG_WRITE(ah, AR_PCIE_SERDES, 0x000e1007); + + REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); +} + +static void ath9k_set_power_sleep(struct ath_hal *ah, int setChip) +{ + REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); + if (setChip) { + REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN); + if (!AR_SREV_9100(ah)) + REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF); + + REG_CLR_BIT(ah, (u16) (AR_RTC_RESET), + AR_RTC_RESET_EN); + } +} + +static void ath9k_set_power_network_sleep(struct ath_hal *ah, int setChip) +{ + REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); + if (setChip) { + struct hal_capabilities *pCap = &ah->ah_caps; + + if (!pCap->halAutoSleepSupport) { + REG_WRITE(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_ON_INT); + } else { + REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN); + } + } +} + +static bool ath9k_hw_set_power_awake(struct ath_hal *ah, + int setChip) +{ + u32 val; + int i; + + if (setChip) { + if ((REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) == + AR_RTC_STATUS_SHUTDOWN) { + if (ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON) + != true) { + return false; + } + } + if (AR_SREV_9100(ah)) + REG_SET_BIT(ah, AR_RTC_RESET, + AR_RTC_RESET_EN); + + REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN); + udelay(50); + + for (i = POWER_UP_TIME / 50; i > 0; i--) { + val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M; + if (val == AR_RTC_STATUS_ON) + break; + udelay(50); + REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, + AR_RTC_FORCE_WAKE_EN); + } + if (i == 0) { + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + "%s: Failed to wakeup in %uus\n", + __func__, POWER_UP_TIME / 20); + return false; + } + } + + REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); + return true; +} + +bool ath9k_hw_setpower(struct ath_hal *ah, + enum ath9k_power_mode mode) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + static const char *modes[] = { + "AWAKE", + "FULL-SLEEP", + "NETWORK SLEEP", + "UNDEFINED" + }; + int status = true, setChip = true; + + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, "%s: %s -> %s (%s)\n", __func__, + modes[ahp->ah_powerMode], modes[mode], + setChip ? "set chip " : ""); + + switch (mode) { + case ATH9K_PM_AWAKE: + status = ath9k_hw_set_power_awake(ah, setChip); + break; + case ATH9K_PM_FULL_SLEEP: + ath9k_set_power_sleep(ah, setChip); + ahp->ah_chipFullSleep = true; + break; + case ATH9K_PM_NETWORK_SLEEP: + ath9k_set_power_network_sleep(ah, setChip); + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + "%s: unknown power mode %u\n", __func__, mode); + return false; + } + ahp->ah_powerMode = mode; + return status; +} + +static struct ath_hal *ath9k_hw_do_attach(u16 devid, + struct ath_softc *sc, + void __iomem *mem, + int *status) +{ + struct ath_hal_5416 *ahp; + struct ath_hal *ah; + int ecode; +#ifndef CONFIG_SLOW_ANT_DIV + u32 i; + u32 j; +#endif + + ahp = ath9k_hw_newstate(devid, sc, mem, status); + if (ahp == NULL) + return NULL; + + ah = &ahp->ah; + + ath9k_hw_set_defaults(ah); + + if (ah->ah_config.ath_hal_intrMitigation != 0) + ahp->ah_intrMitigation = true; + + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: couldn't reset chip\n", + __func__); + ecode = -EIO; + goto bad; + } + + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: couldn't wakeup chip\n", + __func__); + ecode = -EIO; + goto bad; + } + + if (ah->ah_config.ath_hal_serializeRegMode == SER_REG_MODE_AUTO) { + if (ah->ah_macVersion == AR_SREV_VERSION_5416_PCI) { + ah->ah_config.ath_hal_serializeRegMode = + SER_REG_MODE_ON; + } else { + ah->ah_config.ath_hal_serializeRegMode = + SER_REG_MODE_OFF; + } + } + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: ath_hal_serializeRegMode is %d\n", + __func__, ah->ah_config.ath_hal_serializeRegMode); + + if ((ah->ah_macVersion != AR_SREV_VERSION_5416_PCI) && + (ah->ah_macVersion != AR_SREV_VERSION_5416_PCIE) && + (ah->ah_macVersion != AR_SREV_VERSION_9160) && + (!AR_SREV_9100(ah)) && (!AR_SREV_9280(ah))) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: Mac Chip Rev 0x%02x.%x is not supported by " + "this driver\n", __func__, + ah->ah_macVersion, ah->ah_macRev); + ecode = -EOPNOTSUPP; + goto bad; + } + + if (AR_SREV_9100(ah)) { + ahp->ah_iqCalData.calData = &iq_cal_multi_sample; + ahp->ah_suppCals = IQ_MISMATCH_CAL; + ah->ah_isPciExpress = false; + } + ah->ah_phyRev = REG_READ(ah, AR_PHY_CHIP_ID); + + if (AR_SREV_9160_10_OR_LATER(ah)) { + if (AR_SREV_9280_10_OR_LATER(ah)) { + ahp->ah_iqCalData.calData = &iq_cal_single_sample; + ahp->ah_adcGainCalData.calData = + &adc_gain_cal_single_sample; + ahp->ah_adcDcCalData.calData = + &adc_dc_cal_single_sample; + ahp->ah_adcDcCalInitData.calData = + &adc_init_dc_cal; + } else { + ahp->ah_iqCalData.calData = &iq_cal_multi_sample; + ahp->ah_adcGainCalData.calData = + &adc_gain_cal_multi_sample; + ahp->ah_adcDcCalData.calData = + &adc_dc_cal_multi_sample; + ahp->ah_adcDcCalInitData.calData = + &adc_init_dc_cal; + } + ahp->ah_suppCals = + ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL; + } + + if (AR_SREV_9160(ah)) { + ah->ah_config.ath_hal_enableANI = 1; + ahp->ah_ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL | + ATH9K_ANI_FIRSTEP_LEVEL); + } else { + ahp->ah_ani_function = ATH9K_ANI_ALL; + if (AR_SREV_9280_10_OR_LATER(ah)) { + ahp->ah_ani_function &= + ~ATH9K_ANI_NOISE_IMMUNITY_LEVEL; + } + } + + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: This Mac Chip Rev 0x%02x.%x is \n", __func__, + ah->ah_macVersion, ah->ah_macRev); + + if (AR_SREV_9280_20_OR_LATER(ah)) { + INIT_INI_ARRAY(&ahp->ah_iniModes, ar9280Modes_9280_2, + ARRAY_SIZE(ar9280Modes_9280_2), 6); + INIT_INI_ARRAY(&ahp->ah_iniCommon, ar9280Common_9280_2, + ARRAY_SIZE(ar9280Common_9280_2), 2); + + if (ah->ah_config.ath_hal_pcieClockReq) { + INIT_INI_ARRAY(&ahp->ah_iniPcieSerdes, + ar9280PciePhy_clkreq_off_L1_9280, + ARRAY_SIZE + (ar9280PciePhy_clkreq_off_L1_9280), + 2); + } else { + INIT_INI_ARRAY(&ahp->ah_iniPcieSerdes, + ar9280PciePhy_clkreq_always_on_L1_9280, + ARRAY_SIZE + (ar9280PciePhy_clkreq_always_on_L1_9280), + 2); + } + INIT_INI_ARRAY(&ahp->ah_iniModesAdditional, + ar9280Modes_fast_clock_9280_2, + ARRAY_SIZE(ar9280Modes_fast_clock_9280_2), + 3); + } else if (AR_SREV_9280_10_OR_LATER(ah)) { + INIT_INI_ARRAY(&ahp->ah_iniModes, ar9280Modes_9280, + ARRAY_SIZE(ar9280Modes_9280), 6); + INIT_INI_ARRAY(&ahp->ah_iniCommon, ar9280Common_9280, + ARRAY_SIZE(ar9280Common_9280), 2); + } else if (AR_SREV_9160_10_OR_LATER(ah)) { + INIT_INI_ARRAY(&ahp->ah_iniModes, ar5416Modes_9160, + ARRAY_SIZE(ar5416Modes_9160), 6); + INIT_INI_ARRAY(&ahp->ah_iniCommon, ar5416Common_9160, + ARRAY_SIZE(ar5416Common_9160), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank0, ar5416Bank0_9160, + ARRAY_SIZE(ar5416Bank0_9160), 2); + INIT_INI_ARRAY(&ahp->ah_iniBB_RfGain, ar5416BB_RfGain_9160, + ARRAY_SIZE(ar5416BB_RfGain_9160), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank1, ar5416Bank1_9160, + ARRAY_SIZE(ar5416Bank1_9160), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank2, ar5416Bank2_9160, + ARRAY_SIZE(ar5416Bank2_9160), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank3, ar5416Bank3_9160, + ARRAY_SIZE(ar5416Bank3_9160), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank6, ar5416Bank6_9160, + ARRAY_SIZE(ar5416Bank6_9160), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank6TPC, ar5416Bank6TPC_9160, + ARRAY_SIZE(ar5416Bank6TPC_9160), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank7, ar5416Bank7_9160, + ARRAY_SIZE(ar5416Bank7_9160), 2); + if (AR_SREV_9160_11(ah)) { + INIT_INI_ARRAY(&ahp->ah_iniAddac, + ar5416Addac_91601_1, + ARRAY_SIZE(ar5416Addac_91601_1), 2); + } else { + INIT_INI_ARRAY(&ahp->ah_iniAddac, ar5416Addac_9160, + ARRAY_SIZE(ar5416Addac_9160), 2); + } + } else if (AR_SREV_9100_OR_LATER(ah)) { + INIT_INI_ARRAY(&ahp->ah_iniModes, ar5416Modes_9100, + ARRAY_SIZE(ar5416Modes_9100), 6); + INIT_INI_ARRAY(&ahp->ah_iniCommon, ar5416Common_9100, + ARRAY_SIZE(ar5416Common_9100), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank0, ar5416Bank0_9100, + ARRAY_SIZE(ar5416Bank0_9100), 2); + INIT_INI_ARRAY(&ahp->ah_iniBB_RfGain, ar5416BB_RfGain_9100, + ARRAY_SIZE(ar5416BB_RfGain_9100), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank1, ar5416Bank1_9100, + ARRAY_SIZE(ar5416Bank1_9100), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank2, ar5416Bank2_9100, + ARRAY_SIZE(ar5416Bank2_9100), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank3, ar5416Bank3_9100, + ARRAY_SIZE(ar5416Bank3_9100), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank6, ar5416Bank6_9100, + ARRAY_SIZE(ar5416Bank6_9100), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank6TPC, ar5416Bank6TPC_9100, + ARRAY_SIZE(ar5416Bank6TPC_9100), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank7, ar5416Bank7_9100, + ARRAY_SIZE(ar5416Bank7_9100), 2); + INIT_INI_ARRAY(&ahp->ah_iniAddac, ar5416Addac_9100, + ARRAY_SIZE(ar5416Addac_9100), 2); + } else { + INIT_INI_ARRAY(&ahp->ah_iniModes, ar5416Modes, + ARRAY_SIZE(ar5416Modes), 6); + INIT_INI_ARRAY(&ahp->ah_iniCommon, ar5416Common, + ARRAY_SIZE(ar5416Common), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank0, ar5416Bank0, + ARRAY_SIZE(ar5416Bank0), 2); + INIT_INI_ARRAY(&ahp->ah_iniBB_RfGain, ar5416BB_RfGain, + ARRAY_SIZE(ar5416BB_RfGain), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank1, ar5416Bank1, + ARRAY_SIZE(ar5416Bank1), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank2, ar5416Bank2, + ARRAY_SIZE(ar5416Bank2), 2); + INIT_INI_ARRAY(&ahp->ah_iniBank3, ar5416Bank3, + ARRAY_SIZE(ar5416Bank3), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank6, ar5416Bank6, + ARRAY_SIZE(ar5416Bank6), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank6TPC, ar5416Bank6TPC, + ARRAY_SIZE(ar5416Bank6TPC), 3); + INIT_INI_ARRAY(&ahp->ah_iniBank7, ar5416Bank7, + ARRAY_SIZE(ar5416Bank7), 2); + INIT_INI_ARRAY(&ahp->ah_iniAddac, ar5416Addac, + ARRAY_SIZE(ar5416Addac), 2); + } + + if (ah->ah_isPciExpress) + ath9k_hw_configpcipowersave(ah, 0); + else + ar5416DisablePciePhy(ah); + + ecode = ath9k_hw_post_attach(ah); + if (ecode != 0) + goto bad; + +#ifndef CONFIG_SLOW_ANT_DIV + if (ah->ah_devid == AR9280_DEVID_PCI) { + for (i = 0; i < ahp->ah_iniModes.ia_rows; i++) { + u32 reg = INI_RA(&ahp->ah_iniModes, i, 0); + + for (j = 1; j < ahp->ah_iniModes.ia_columns; j++) { + u32 val = INI_RA(&ahp->ah_iniModes, i, j); + + INI_RA(&ahp->ah_iniModes, i, j) = + ath9k_hw_ini_fixup(ah, &ahp->ah_eeprom, + reg, val); + } + } + } +#endif + + if (!ath9k_hw_fill_cap_info(ah)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s:failed ath9k_hw_fill_cap_info\n", __func__); + ecode = -EINVAL; + goto bad; + } + + ecode = ath9k_hw_init_macaddr(ah); + if (ecode != 0) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: failed initializing mac address\n", + __func__); + goto bad; + } + + if (AR_SREV_9285(ah)) + ah->ah_txTrigLevel = (AR_FTRIG_256B >> AR_FTRIG_S); + else + ah->ah_txTrigLevel = (AR_FTRIG_512B >> AR_FTRIG_S); + +#ifndef ATH_NF_PER_CHAN + + ath9k_init_nfcal_hist_buffer(ah); +#endif + + return ah; + +bad: + if (ahp) + ath9k_hw_detach((struct ath_hal *) ahp); + if (status) + *status = ecode; + return NULL; +} + +void ath9k_hw_detach(struct ath_hal *ah) +{ + if (!AR_SREV_9100(ah)) + ath9k_hw_ani_detach(ah); + ath9k_hw_rfdetach(ah); + + ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); + kfree(ah); +} + +bool ath9k_get_channel_edges(struct ath_hal *ah, + u16 flags, u16 *low, + u16 *high) +{ + struct hal_capabilities *pCap = &ah->ah_caps; + + if (flags & CHANNEL_5GHZ) { + *low = pCap->halLow5GhzChan; + *high = pCap->halHigh5GhzChan; + return true; + } + if ((flags & CHANNEL_2GHZ)) { + *low = pCap->halLow2GhzChan; + *high = pCap->halHigh2GhzChan; + + return true; + } + return false; +} + +static inline bool ath9k_hw_fill_vpd_table(u8 pwrMin, + u8 pwrMax, + u8 *pPwrList, + u8 *pVpdList, + u16 + numIntercepts, + u8 *pRetVpdList) +{ + u16 i, k; + u8 currPwr = pwrMin; + u16 idxL = 0, idxR = 0; + + for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) { + ath9k_hw_get_lower_upper_index(currPwr, pPwrList, + numIntercepts, &(idxL), + &(idxR)); + if (idxR < 1) + idxR = 1; + if (idxL == numIntercepts - 1) + idxL = (u16) (numIntercepts - 2); + if (pPwrList[idxL] == pPwrList[idxR]) + k = pVpdList[idxL]; + else + k = (u16) (((currPwr - + pPwrList[idxL]) * + pVpdList[idxR] + + (pPwrList[idxR] - + currPwr) * pVpdList[idxL]) / + (pPwrList[idxR] - + pPwrList[idxL])); + pRetVpdList[i] = (u8) k; + currPwr += 2; + } + + return true; +} + +static inline void +ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hal *ah, + struct ath9k_channel *chan, + struct cal_data_per_freq *pRawDataSet, + u8 *bChans, + u16 availPiers, + u16 tPdGainOverlap, + int16_t *pMinCalPower, + u16 *pPdGainBoundaries, + u8 *pPDADCValues, + u16 numXpdGains) +{ + int i, j, k; + int16_t ss; + u16 idxL = 0, idxR = 0, numPiers; + static u8 vpdTableL[AR5416_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + static u8 vpdTableR[AR5416_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + static u8 vpdTableI[AR5416_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + + u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR; + u8 minPwrT4[AR5416_NUM_PD_GAINS]; + u8 maxPwrT4[AR5416_NUM_PD_GAINS]; + int16_t vpdStep; + int16_t tmpVal; + u16 sizeCurrVpdTable, maxIndex, tgtIndex; + bool match; + int16_t minDelta = 0; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + for (numPiers = 0; numPiers < availPiers; numPiers++) { + if (bChans[numPiers] == AR5416_BCHAN_UNUSED) + break; + } + + match = ath9k_hw_get_lower_upper_index((u8) + FREQ2FBIN(centers. + synth_center, + IS_CHAN_2GHZ + (chan)), bChans, + numPiers, &idxL, &idxR); + + if (match) { + for (i = 0; i < numXpdGains; i++) { + minPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][0]; + maxPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][4]; + ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], + pRawDataSet[idxL]. + pwrPdg[i], + pRawDataSet[idxL]. + vpdPdg[i], + AR5416_PD_GAIN_ICEPTS, + vpdTableI[i]); + } + } else { + for (i = 0; i < numXpdGains; i++) { + pVpdL = pRawDataSet[idxL].vpdPdg[i]; + pPwrL = pRawDataSet[idxL].pwrPdg[i]; + pVpdR = pRawDataSet[idxR].vpdPdg[i]; + pPwrR = pRawDataSet[idxR].pwrPdg[i]; + + minPwrT4[i] = max(pPwrL[0], pPwrR[0]); + + maxPwrT4[i] = + min(pPwrL[AR5416_PD_GAIN_ICEPTS - 1], + pPwrR[AR5416_PD_GAIN_ICEPTS - 1]); + + + ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], + pPwrL, pVpdL, + AR5416_PD_GAIN_ICEPTS, + vpdTableL[i]); + ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], + pPwrR, pVpdR, + AR5416_PD_GAIN_ICEPTS, + vpdTableR[i]); + + for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) { + vpdTableI[i][j] = + (u8) (ath9k_hw_interpolate + ((u16) + FREQ2FBIN(centers. + synth_center, + IS_CHAN_2GHZ + (chan)), + bChans[idxL], + bChans[idxR], vpdTableL[i] + [j], vpdTableR[i] + [j])); + } + } + } + + *pMinCalPower = (int16_t) (minPwrT4[0] / 2); + + k = 0; + for (i = 0; i < numXpdGains; i++) { + if (i == (numXpdGains - 1)) + pPdGainBoundaries[i] = + (u16) (maxPwrT4[i] / 2); + else + pPdGainBoundaries[i] = + (u16) ((maxPwrT4[i] + + minPwrT4[i + 1]) / 4); + + pPdGainBoundaries[i] = + min((u16) AR5416_MAX_RATE_POWER, + pPdGainBoundaries[i]); + + if ((i == 0) && !AR_SREV_5416_V20_OR_LATER(ah)) { + minDelta = pPdGainBoundaries[0] - 23; + pPdGainBoundaries[0] = 23; + } else { + minDelta = 0; + } + + if (i == 0) { + if (AR_SREV_9280_10_OR_LATER(ah)) + ss = (int16_t) (0 - (minPwrT4[i] / 2)); + else + ss = 0; + } else { + ss = (int16_t) ((pPdGainBoundaries[i - 1] - + (minPwrT4[i] / 2)) - + tPdGainOverlap + 1 + minDelta); + } + vpdStep = (int16_t) (vpdTableI[i][1] - vpdTableI[i][0]); + vpdStep = (int16_t) ((vpdStep < 1) ? 1 : vpdStep); + + while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { + tmpVal = (int16_t) (vpdTableI[i][0] + ss * vpdStep); + pPDADCValues[k++] = + (u8) ((tmpVal < 0) ? 0 : tmpVal); + ss++; + } + + sizeCurrVpdTable = + (u8) ((maxPwrT4[i] - minPwrT4[i]) / 2 + 1); + tgtIndex = (u8) (pPdGainBoundaries[i] + tPdGainOverlap - + (minPwrT4[i] / 2)); + maxIndex = (tgtIndex < + sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable; + + while ((ss < maxIndex) + && (k < (AR5416_NUM_PDADC_VALUES - 1))) { + pPDADCValues[k++] = vpdTableI[i][ss++]; + } + + vpdStep = (int16_t) (vpdTableI[i][sizeCurrVpdTable - 1] - + vpdTableI[i][sizeCurrVpdTable - 2]); + vpdStep = (int16_t) ((vpdStep < 1) ? 1 : vpdStep); + + if (tgtIndex > maxIndex) { + while ((ss <= tgtIndex) + && (k < (AR5416_NUM_PDADC_VALUES - 1))) { + tmpVal = (int16_t) ((vpdTableI[i] + [sizeCurrVpdTable - + 1] + (ss - maxIndex + + 1) * vpdStep)); + pPDADCValues[k++] = (u8) ((tmpVal > + 255) ? 255 : tmpVal); + ss++; + } + } + } + + while (i < AR5416_PD_GAINS_IN_MASK) { + pPdGainBoundaries[i] = pPdGainBoundaries[i - 1]; + i++; + } + + while (k < AR5416_NUM_PDADC_VALUES) { + pPDADCValues[k] = pPDADCValues[k - 1]; + k++; + } + return; +} + +static inline bool +ath9k_hw_set_power_cal_table(struct ath_hal *ah, + struct ar5416_eeprom *pEepData, + struct ath9k_channel *chan, + int16_t *pTxPowerIndexOffset) +{ + struct cal_data_per_freq *pRawDataset; + u8 *pCalBChans = NULL; + u16 pdGainOverlap_t2; + static u8 pdadcValues[AR5416_NUM_PDADC_VALUES]; + u16 gainBoundaries[AR5416_PD_GAINS_IN_MASK]; + u16 numPiers, i, j; + int16_t tMinCalPower; + u16 numXpdGain, xpdMask; + u16 xpdGainValues[AR5416_NUM_PD_GAINS] = { 0, 0, 0, 0 }; + u32 reg32, regOffset, regChainOffset; + int16_t modalIdx; + struct ath_hal_5416 *ahp = AH5416(ah); + + modalIdx = IS_CHAN_2GHZ(chan) ? 1 : 0; + xpdMask = pEepData->modalHeader[modalIdx].xpdGain; + + if ((pEepData->baseEepHeader. + version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_2) { + pdGainOverlap_t2 = + pEepData->modalHeader[modalIdx].pdGainOverlap; + } else { + pdGainOverlap_t2 = + (u16) (MS + (REG_READ(ah, AR_PHY_TPCRG5), + AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); + } + + if (IS_CHAN_2GHZ(chan)) { + pCalBChans = pEepData->calFreqPier2G; + numPiers = AR5416_NUM_2G_CAL_PIERS; + } else { + pCalBChans = pEepData->calFreqPier5G; + numPiers = AR5416_NUM_5G_CAL_PIERS; + } + + numXpdGain = 0; + + for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) { + if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) { + if (numXpdGain >= AR5416_NUM_PD_GAINS) + break; + xpdGainValues[numXpdGain] = + (u16) (AR5416_PD_GAINS_IN_MASK - i); + numXpdGain++; + } + } + + REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN, + (numXpdGain - 1) & 0x3); + REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_1, + xpdGainValues[0]); + REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_2, + xpdGainValues[1]); + REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, + xpdGainValues[2]); + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + if (AR_SREV_5416_V20_OR_LATER(ah) && + (ahp->ah_rxchainmask == 5 || ahp->ah_txchainmask == 5) + && (i != 0)) { + regChainOffset = (i == 1) ? 0x2000 : 0x1000; + } else + regChainOffset = i * 0x1000; + if (pEepData->baseEepHeader.txMask & (1 << i)) { + if (IS_CHAN_2GHZ(chan)) + pRawDataset = pEepData->calPierData2G[i]; + else + pRawDataset = pEepData->calPierData5G[i]; + + ath9k_hw_get_gain_boundaries_pdadcs(ah, chan, + pRawDataset, + pCalBChans, + numPiers, + pdGainOverlap_t2, + &tMinCalPower, + gainBoundaries, + pdadcValues, + numXpdGain); + + if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) { + + REG_WRITE(ah, + AR_PHY_TPCRG5 + regChainOffset, + SM(pdGainOverlap_t2, + AR_PHY_TPCRG5_PD_GAIN_OVERLAP) + | SM(gainBoundaries[0], + AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) + | SM(gainBoundaries[1], + AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) + | SM(gainBoundaries[2], + AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) + | SM(gainBoundaries[3], + AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); + } + + regOffset = + AR_PHY_BASE + (672 << 2) + regChainOffset; + for (j = 0; j < 32; j++) { + reg32 = + ((pdadcValues[4 * j + 0] & 0xFF) << 0) + | ((pdadcValues[4 * j + 1] & 0xFF) << + 8) | ((pdadcValues[4 * j + 2] & + 0xFF) << 16) | + ((pdadcValues[4 * j + 3] & 0xFF) << + 24); + REG_WRITE(ah, regOffset, reg32); + + DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO, + "PDADC (%d,%4x): %4.4x %8.8x\n", + i, regChainOffset, regOffset, + reg32); + DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO, + "PDADC: Chain %d | PDADC %3d Value %3d | " + "PDADC %3d Value %3d | PDADC %3d Value %3d | " + "PDADC %3d Value %3d |\n", + i, 4 * j, pdadcValues[4 * j], + 4 * j + 1, pdadcValues[4 * j + 1], + 4 * j + 2, pdadcValues[4 * j + 2], + 4 * j + 3, + pdadcValues[4 * j + 3]); + + regOffset += 4; + } + } + } + *pTxPowerIndexOffset = 0; + + return true; +} + +void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u8 i; + + if (ah->ah_isPciExpress != true) + return; + + if (ah->ah_config.ath_hal_pciePowerSaveEnable == 2) + return; + + if (restore) + return; + + if (AR_SREV_9280_20_OR_LATER(ah)) { + for (i = 0; i < ahp->ah_iniPcieSerdes.ia_rows; i++) { + REG_WRITE(ah, INI_RA(&ahp->ah_iniPcieSerdes, i, 0), + INI_RA(&ahp->ah_iniPcieSerdes, i, 1)); + } + udelay(1000); + } else if (AR_SREV_9280(ah) + && (ah->ah_macRev == AR_SREV_REVISION_9280_10)) { + REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fd00); + REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + + REG_WRITE(ah, AR_PCIE_SERDES, 0xa8000019); + REG_WRITE(ah, AR_PCIE_SERDES, 0x13160820); + REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980560); + + if (ah->ah_config.ath_hal_pcieClockReq) + REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffc); + else + REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffd); + + REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); + REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); + REG_WRITE(ah, AR_PCIE_SERDES, 0x00043007); + + REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); + + udelay(1000); + } else { + REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); + REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039); + REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824); + REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579); + REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff); + REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); + REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); + REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007); + REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); + } + + REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); + + if (ah->ah_config.ath_hal_pcieWaen) { + REG_WRITE(ah, AR_WA, ah->ah_config.ath_hal_pcieWaen); + } else { + if (AR_SREV_9280(ah)) + REG_WRITE(ah, AR_WA, 0x0040073f); + else + REG_WRITE(ah, AR_WA, 0x0000073f); + } +} + +static inline void +ath9k_hw_get_legacy_target_powers(struct ath_hal *ah, + struct ath9k_channel *chan, + struct cal_target_power_leg *powInfo, + u16 numChannels, + struct cal_target_power_leg *pNewPower, + u16 numRates, + bool isExtTarget) +{ + u16 clo, chi; + int i; + int matchIndex = -1, lowIndex = -1; + u16 freq; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + freq = (isExtTarget) ? centers.ext_center : centers.ctl_center; + + if (freq <= ath9k_hw_fbin2freq(powInfo[0].bChannel, + IS_CHAN_2GHZ(chan))) { + matchIndex = 0; + } else { + for (i = 0; (i < numChannels) + && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { + if (freq == + ath9k_hw_fbin2freq(powInfo[i].bChannel, + IS_CHAN_2GHZ(chan))) { + matchIndex = i; + break; + } else if ((freq < + ath9k_hw_fbin2freq(powInfo[i].bChannel, + IS_CHAN_2GHZ(chan))) + && (freq > + ath9k_hw_fbin2freq(powInfo[i - 1]. + bChannel, + IS_CHAN_2GHZ + (chan)))) { + lowIndex = i - 1; + break; + } + } + if ((matchIndex == -1) && (lowIndex == -1)) + matchIndex = i - 1; + } + + if (matchIndex != -1) { + *pNewPower = powInfo[matchIndex]; + } else { + clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel, + IS_CHAN_2GHZ(chan)); + chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel, + IS_CHAN_2GHZ(chan)); + + for (i = 0; i < numRates; i++) { + pNewPower->tPow2x[i] = + (u8) ath9k_hw_interpolate(freq, clo, chi, + powInfo + [lowIndex]. + tPow2x[i], + powInfo + [lowIndex + + 1].tPow2x[i]); + } + } +} + +static inline void +ath9k_hw_get_target_powers(struct ath_hal *ah, + struct ath9k_channel *chan, + struct cal_target_power_ht *powInfo, + u16 numChannels, + struct cal_target_power_ht *pNewPower, + u16 numRates, + bool isHt40Target) +{ + u16 clo, chi; + int i; + int matchIndex = -1, lowIndex = -1; + u16 freq; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + freq = isHt40Target ? centers.synth_center : centers.ctl_center; + + if (freq <= + ath9k_hw_fbin2freq(powInfo[0].bChannel, IS_CHAN_2GHZ(chan))) { + matchIndex = 0; + } else { + for (i = 0; (i < numChannels) + && (powInfo[i].bChannel != AR5416_BCHAN_UNUSED); i++) { + if (freq == + ath9k_hw_fbin2freq(powInfo[i].bChannel, + IS_CHAN_2GHZ(chan))) { + matchIndex = i; + break; + } else + if ((freq < + ath9k_hw_fbin2freq(powInfo[i].bChannel, + IS_CHAN_2GHZ(chan))) + && (freq > + ath9k_hw_fbin2freq(powInfo[i - 1]. + bChannel, + IS_CHAN_2GHZ + (chan)))) { + lowIndex = i - 1; + break; + } + } + if ((matchIndex == -1) && (lowIndex == -1)) + matchIndex = i - 1; + } + + if (matchIndex != -1) { + *pNewPower = powInfo[matchIndex]; + } else { + clo = ath9k_hw_fbin2freq(powInfo[lowIndex].bChannel, + IS_CHAN_2GHZ(chan)); + chi = ath9k_hw_fbin2freq(powInfo[lowIndex + 1].bChannel, + IS_CHAN_2GHZ(chan)); + + for (i = 0; i < numRates; i++) { + pNewPower->tPow2x[i] = + (u8) ath9k_hw_interpolate(freq, clo, chi, + powInfo + [lowIndex]. + tPow2x[i], + powInfo + [lowIndex + + 1].tPow2x[i]); + } + } +} + +static inline u16 +ath9k_hw_get_max_edge_power(u16 freq, + struct cal_ctl_edges *pRdEdgesPower, + bool is2GHz) +{ + u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + int i; + + for (i = 0; (i < AR5416_NUM_BAND_EDGES) + && (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED); i++) { + if (freq == ath9k_hw_fbin2freq(pRdEdgesPower[i].bChannel, + is2GHz)) { + twiceMaxEdgePower = pRdEdgesPower[i].tPower; + break; + } else if ((i > 0) + && (freq < + ath9k_hw_fbin2freq(pRdEdgesPower[i]. + bChannel, is2GHz))) { + if (ath9k_hw_fbin2freq + (pRdEdgesPower[i - 1].bChannel, is2GHz) < freq + && pRdEdgesPower[i - 1].flag) { + twiceMaxEdgePower = + pRdEdgesPower[i - 1].tPower; + } + break; + } + } + return twiceMaxEdgePower; +} + +static inline bool +ath9k_hw_set_power_per_rate_table(struct ath_hal *ah, + struct ar5416_eeprom *pEepData, + struct ath9k_channel *chan, + int16_t *ratesArray, + u16 cfgCtl, + u8 AntennaReduction, + u8 twiceMaxRegulatoryPower, + u8 powerLimit) +{ + u8 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + static const u16 tpScaleReductionTable[5] = + { 0, 3, 6, 9, AR5416_MAX_RATE_POWER }; + + int i; + int8_t twiceLargestAntenna; + struct cal_ctl_data *rep; + struct cal_target_power_leg targetPowerOfdm, targetPowerCck = { + 0, { 0, 0, 0, 0} + }; + struct cal_target_power_leg targetPowerOfdmExt = { + 0, { 0, 0, 0, 0} }, targetPowerCckExt = { + 0, { 0, 0, 0, 0 } + }; + struct cal_target_power_ht targetPowerHt20, targetPowerHt40 = { + 0, {0, 0, 0, 0} + }; + u8 scaledPower = 0, minCtlPower, maxRegAllowedPower; + u16 ctlModesFor11a[] = + { CTL_11A, CTL_5GHT20, CTL_11A_EXT, CTL_5GHT40 }; + u16 ctlModesFor11g[] = + { CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT, + CTL_2GHT40 + }; + u16 numCtlModes, *pCtlMode, ctlMode, freq; + struct chan_centers centers; + int tx_chainmask; + u8 twiceMinEdgePower; + struct ath_hal_5416 *ahp = AH5416(ah); + + tx_chainmask = ahp->ah_txchainmask; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + twiceLargestAntenna = max( + pEepData->modalHeader + [IS_CHAN_2GHZ(chan)].antennaGainCh[0], + pEepData->modalHeader + [IS_CHAN_2GHZ(chan)].antennaGainCh[1]); + + twiceLargestAntenna = max((u8) twiceLargestAntenna, + pEepData->modalHeader + [IS_CHAN_2GHZ(chan)].antennaGainCh[2]); + + twiceLargestAntenna = + (int8_t) min(AntennaReduction - twiceLargestAntenna, 0); + + maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna; + + if (ah->ah_tpScale != ATH9K_TP_SCALE_MAX) { + maxRegAllowedPower -= + (tpScaleReductionTable[(ah->ah_tpScale)] * 2); + } + + scaledPower = min(powerLimit, maxRegAllowedPower); + + switch (ar5416_get_ntxchains(tx_chainmask)) { + case 1: + break; + case 2: + scaledPower -= + pEepData->modalHeader[IS_CHAN_2GHZ(chan)]. + pwrDecreaseFor2Chain; + break; + case 3: + scaledPower -= + pEepData->modalHeader[IS_CHAN_2GHZ(chan)]. + pwrDecreaseFor3Chain; + break; + } + + scaledPower = max(0, (int32_t) scaledPower); + + if (IS_CHAN_2GHZ(chan)) { + numCtlModes = + ARRAY_SIZE(ctlModesFor11g) - + SUB_NUM_CTL_MODES_AT_2G_40; + pCtlMode = ctlModesFor11g; + + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData-> + calTargetPowerCck, + AR5416_NUM_2G_CCK_TARGET_POWERS, + &targetPowerCck, 4, + false); + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData-> + calTargetPower2G, + AR5416_NUM_2G_20_TARGET_POWERS, + &targetPowerOfdm, 4, + false); + ath9k_hw_get_target_powers(ah, chan, + pEepData->calTargetPower2GHT20, + AR5416_NUM_2G_20_TARGET_POWERS, + &targetPowerHt20, 8, false); + + if (IS_CHAN_HT40(chan)) { + numCtlModes = ARRAY_SIZE(ctlModesFor11g); + ath9k_hw_get_target_powers(ah, chan, + pEepData-> + calTargetPower2GHT40, + AR5416_NUM_2G_40_TARGET_POWERS, + &targetPowerHt40, 8, + true); + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData-> + calTargetPowerCck, + AR5416_NUM_2G_CCK_TARGET_POWERS, + &targetPowerCckExt, + 4, true); + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData-> + calTargetPower2G, + AR5416_NUM_2G_20_TARGET_POWERS, + &targetPowerOfdmExt, + 4, true); + } + } else { + + numCtlModes = + ARRAY_SIZE(ctlModesFor11a) - + SUB_NUM_CTL_MODES_AT_5G_40; + pCtlMode = ctlModesFor11a; + + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData-> + calTargetPower5G, + AR5416_NUM_5G_20_TARGET_POWERS, + &targetPowerOfdm, 4, + false); + ath9k_hw_get_target_powers(ah, chan, + pEepData->calTargetPower5GHT20, + AR5416_NUM_5G_20_TARGET_POWERS, + &targetPowerHt20, 8, false); + + if (IS_CHAN_HT40(chan)) { + numCtlModes = ARRAY_SIZE(ctlModesFor11a); + ath9k_hw_get_target_powers(ah, chan, + pEepData-> + calTargetPower5GHT40, + AR5416_NUM_5G_40_TARGET_POWERS, + &targetPowerHt40, 8, + true); + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData-> + calTargetPower5G, + AR5416_NUM_5G_20_TARGET_POWERS, + &targetPowerOfdmExt, + 4, true); + } + } + + for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) { + bool isHt40CtlMode = + (pCtlMode[ctlMode] == CTL_5GHT40) + || (pCtlMode[ctlMode] == CTL_2GHT40); + if (isHt40CtlMode) + freq = centers.synth_center; + else if (pCtlMode[ctlMode] & EXT_ADDITIVE) + freq = centers.ext_center; + else + freq = centers.ctl_center; + + if (ar5416_get_eep_ver(ahp) == 14 + && ar5416_get_eep_rev(ahp) <= 2) + twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + "LOOP-Mode ctlMode %d < %d, isHt40CtlMode %d, " + "EXT_ADDITIVE %d\n", + ctlMode, numCtlModes, isHt40CtlMode, + (pCtlMode[ctlMode] & EXT_ADDITIVE)); + + for (i = 0; (i < AR5416_NUM_CTLS) && pEepData->ctlIndex[i]; + i++) { + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + " LOOP-Ctlidx %d: cfgCtl 0x%2.2x " + "pCtlMode 0x%2.2x ctlIndex 0x%2.2x " + "chan %d\n", + i, cfgCtl, pCtlMode[ctlMode], + pEepData->ctlIndex[i], chan->channel); + + if ((((cfgCtl & ~CTL_MODE_M) | + (pCtlMode[ctlMode] & CTL_MODE_M)) == + pEepData->ctlIndex[i]) + || + (((cfgCtl & ~CTL_MODE_M) | + (pCtlMode[ctlMode] & CTL_MODE_M)) == + ((pEepData-> + ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL))) { + rep = &(pEepData->ctlData[i]); + + twiceMinEdgePower = + ath9k_hw_get_max_edge_power(freq, + rep-> + ctlEdges + [ar5416_get_ntxchains + (tx_chainmask) + - 1], + IS_CHAN_2GHZ + (chan)); + + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + " MATCH-EE_IDX %d: ch %d is2 %d " + "2xMinEdge %d chainmask %d chains %d\n", + i, freq, IS_CHAN_2GHZ(chan), + twiceMinEdgePower, tx_chainmask, + ar5416_get_ntxchains + (tx_chainmask)); + if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { + twiceMaxEdgePower = + min(twiceMaxEdgePower, + twiceMinEdgePower); + } else { + twiceMaxEdgePower = + twiceMinEdgePower; + break; + } + } + } + + minCtlPower = min(twiceMaxEdgePower, scaledPower); + + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + " SEL-Min ctlMode %d pCtlMode %d " + "2xMaxEdge %d sP %d minCtlPwr %d\n", + ctlMode, pCtlMode[ctlMode], twiceMaxEdgePower, + scaledPower, minCtlPower); + + switch (pCtlMode[ctlMode]) { + case CTL_11B: + for (i = 0; i < ARRAY_SIZE(targetPowerCck.tPow2x); + i++) { + targetPowerCck.tPow2x[i] = + min(targetPowerCck.tPow2x[i], + minCtlPower); + } + break; + case CTL_11A: + case CTL_11G: + for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x); + i++) { + targetPowerOfdm.tPow2x[i] = + min(targetPowerOfdm.tPow2x[i], + minCtlPower); + } + break; + case CTL_5GHT20: + case CTL_2GHT20: + for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); + i++) { + targetPowerHt20.tPow2x[i] = + min(targetPowerHt20.tPow2x[i], + minCtlPower); + } + break; + case CTL_11B_EXT: + targetPowerCckExt.tPow2x[0] = + min(targetPowerCckExt.tPow2x[0], minCtlPower); + break; + case CTL_11A_EXT: + case CTL_11G_EXT: + targetPowerOfdmExt.tPow2x[0] = + min(targetPowerOfdmExt.tPow2x[0], minCtlPower); + break; + case CTL_5GHT40: + case CTL_2GHT40: + for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); + i++) { + targetPowerHt40.tPow2x[i] = + min(targetPowerHt40.tPow2x[i], + minCtlPower); + } + break; + default: + break; + } + } + + ratesArray[rate6mb] = ratesArray[rate9mb] = ratesArray[rate12mb] = + ratesArray[rate18mb] = ratesArray[rate24mb] = + targetPowerOfdm.tPow2x[0]; + ratesArray[rate36mb] = targetPowerOfdm.tPow2x[1]; + ratesArray[rate48mb] = targetPowerOfdm.tPow2x[2]; + ratesArray[rate54mb] = targetPowerOfdm.tPow2x[3]; + ratesArray[rateXr] = targetPowerOfdm.tPow2x[0]; + + for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++) + ratesArray[rateHt20_0 + i] = targetPowerHt20.tPow2x[i]; + + if (IS_CHAN_2GHZ(chan)) { + ratesArray[rate1l] = targetPowerCck.tPow2x[0]; + ratesArray[rate2s] = ratesArray[rate2l] = + targetPowerCck.tPow2x[1]; + ratesArray[rate5_5s] = ratesArray[rate5_5l] = + targetPowerCck.tPow2x[2]; + ; + ratesArray[rate11s] = ratesArray[rate11l] = + targetPowerCck.tPow2x[3]; + ; + } + if (IS_CHAN_HT40(chan)) { + for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) { + ratesArray[rateHt40_0 + i] = + targetPowerHt40.tPow2x[i]; + } + ratesArray[rateDupOfdm] = targetPowerHt40.tPow2x[0]; + ratesArray[rateDupCck] = targetPowerHt40.tPow2x[0]; + ratesArray[rateExtOfdm] = targetPowerOfdmExt.tPow2x[0]; + if (IS_CHAN_2GHZ(chan)) { + ratesArray[rateExtCck] = + targetPowerCckExt.tPow2x[0]; + } + } + return true; +} + +static int +ath9k_hw_set_txpower(struct ath_hal *ah, + struct ar5416_eeprom *pEepData, + struct ath9k_channel *chan, + u16 cfgCtl, + u8 twiceAntennaReduction, + u8 twiceMaxRegulatoryPower, + u8 powerLimit) +{ + struct modal_eep_header *pModal = + &(pEepData->modalHeader[IS_CHAN_2GHZ(chan)]); + int16_t ratesArray[Ar5416RateSize]; + int16_t txPowerIndexOffset = 0; + u8 ht40PowerIncForPdadc = 2; + int i; + + memset(ratesArray, 0, sizeof(ratesArray)); + + if ((pEepData->baseEepHeader. + version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_2) { + ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; + } + + if (!ath9k_hw_set_power_per_rate_table(ah, pEepData, chan, + &ratesArray[0], cfgCtl, + twiceAntennaReduction, + twiceMaxRegulatoryPower, + powerLimit)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "ath9k_hw_set_txpower: unable to set " + "tx power per rate table\n"); + return -EIO; + } + + if (!ath9k_hw_set_power_cal_table + (ah, pEepData, chan, &txPowerIndexOffset)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "ath9k_hw_set_txpower: unable to set power table\n"); + return -EIO; + } + + for (i = 0; i < ARRAY_SIZE(ratesArray); i++) { + ratesArray[i] = + (int16_t) (txPowerIndexOffset + ratesArray[i]); + if (ratesArray[i] > AR5416_MAX_RATE_POWER) + ratesArray[i] = AR5416_MAX_RATE_POWER; + } + + if (AR_SREV_9280_10_OR_LATER(ah)) { + for (i = 0; i < Ar5416RateSize; i++) + ratesArray[i] -= AR5416_PWR_TABLE_OFFSET * 2; + } + + REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, + ATH9K_POW_SM(ratesArray[rate18mb], 24) + | ATH9K_POW_SM(ratesArray[rate12mb], 16) + | ATH9K_POW_SM(ratesArray[rate9mb], 8) + | ATH9K_POW_SM(ratesArray[rate6mb], 0) + ); + REG_WRITE(ah, AR_PHY_POWER_TX_RATE2, + ATH9K_POW_SM(ratesArray[rate54mb], 24) + | ATH9K_POW_SM(ratesArray[rate48mb], 16) + | ATH9K_POW_SM(ratesArray[rate36mb], 8) + | ATH9K_POW_SM(ratesArray[rate24mb], 0) + ); + + if (IS_CHAN_2GHZ(chan)) { + REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, + ATH9K_POW_SM(ratesArray[rate2s], 24) + | ATH9K_POW_SM(ratesArray[rate2l], 16) + | ATH9K_POW_SM(ratesArray[rateXr], 8) + | ATH9K_POW_SM(ratesArray[rate1l], 0) + ); + REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, + ATH9K_POW_SM(ratesArray[rate11s], 24) + | ATH9K_POW_SM(ratesArray[rate11l], 16) + | ATH9K_POW_SM(ratesArray[rate5_5s], 8) + | ATH9K_POW_SM(ratesArray[rate5_5l], 0) + ); + } + + REG_WRITE(ah, AR_PHY_POWER_TX_RATE5, + ATH9K_POW_SM(ratesArray[rateHt20_3], 24) + | ATH9K_POW_SM(ratesArray[rateHt20_2], 16) + | ATH9K_POW_SM(ratesArray[rateHt20_1], 8) + | ATH9K_POW_SM(ratesArray[rateHt20_0], 0) + ); + REG_WRITE(ah, AR_PHY_POWER_TX_RATE6, + ATH9K_POW_SM(ratesArray[rateHt20_7], 24) + | ATH9K_POW_SM(ratesArray[rateHt20_6], 16) + | ATH9K_POW_SM(ratesArray[rateHt20_5], 8) + | ATH9K_POW_SM(ratesArray[rateHt20_4], 0) + ); + + if (IS_CHAN_HT40(chan)) { + REG_WRITE(ah, AR_PHY_POWER_TX_RATE7, + ATH9K_POW_SM(ratesArray[rateHt40_3] + + ht40PowerIncForPdadc, 24) + | ATH9K_POW_SM(ratesArray[rateHt40_2] + + ht40PowerIncForPdadc, 16) + | ATH9K_POW_SM(ratesArray[rateHt40_1] + + ht40PowerIncForPdadc, 8) + | ATH9K_POW_SM(ratesArray[rateHt40_0] + + ht40PowerIncForPdadc, 0) + ); + REG_WRITE(ah, AR_PHY_POWER_TX_RATE8, + ATH9K_POW_SM(ratesArray[rateHt40_7] + + ht40PowerIncForPdadc, 24) + | ATH9K_POW_SM(ratesArray[rateHt40_6] + + ht40PowerIncForPdadc, 16) + | ATH9K_POW_SM(ratesArray[rateHt40_5] + + ht40PowerIncForPdadc, 8) + | ATH9K_POW_SM(ratesArray[rateHt40_4] + + ht40PowerIncForPdadc, 0) + ); + + REG_WRITE(ah, AR_PHY_POWER_TX_RATE9, + ATH9K_POW_SM(ratesArray[rateExtOfdm], 24) + | ATH9K_POW_SM(ratesArray[rateExtCck], 16) + | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8) + | ATH9K_POW_SM(ratesArray[rateDupCck], 0) + ); + } + + REG_WRITE(ah, AR_PHY_POWER_TX_SUB, + ATH9K_POW_SM(pModal->pwrDecreaseFor3Chain, 6) + | ATH9K_POW_SM(pModal->pwrDecreaseFor2Chain, 0) + ); + + i = rate6mb; + if (IS_CHAN_HT40(chan)) + i = rateHt40_0; + else if (IS_CHAN_HT20(chan)) + i = rateHt20_0; + + if (AR_SREV_9280_10_OR_LATER(ah)) + ah->ah_maxPowerLevel = + ratesArray[i] + AR5416_PWR_TABLE_OFFSET * 2; + else + ah->ah_maxPowerLevel = ratesArray[i]; + + return 0; +} + +static inline void ath9k_hw_get_delta_slope_vals(struct ath_hal *ah, + u32 coef_scaled, + u32 *coef_mantissa, + u32 *coef_exponent) +{ + u32 coef_exp, coef_man; + + for (coef_exp = 31; coef_exp > 0; coef_exp--) + if ((coef_scaled >> coef_exp) & 0x1) + break; + + coef_exp = 14 - (coef_exp - COEF_SCALE_S); + + coef_man = coef_scaled + (1 << (COEF_SCALE_S - coef_exp - 1)); + + *coef_mantissa = coef_man >> (COEF_SCALE_S - coef_exp); + *coef_exponent = coef_exp - 16; +} + +static void +ath9k_hw_set_delta_slope(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + u32 coef_scaled, ds_coef_exp, ds_coef_man; + u32 clockMhzScaled = 0x64000000; + struct chan_centers centers; + + if (IS_CHAN_HALF_RATE(chan)) + clockMhzScaled = clockMhzScaled >> 1; + else if (IS_CHAN_QUARTER_RATE(chan)) + clockMhzScaled = clockMhzScaled >> 2; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + coef_scaled = clockMhzScaled / centers.synth_center; + + ath9k_hw_get_delta_slope_vals(ah, coef_scaled, &ds_coef_man, + &ds_coef_exp); + + REG_RMW_FIELD(ah, AR_PHY_TIMING3, + AR_PHY_TIMING3_DSC_MAN, ds_coef_man); + REG_RMW_FIELD(ah, AR_PHY_TIMING3, + AR_PHY_TIMING3_DSC_EXP, ds_coef_exp); + + coef_scaled = (9 * coef_scaled) / 10; + + ath9k_hw_get_delta_slope_vals(ah, coef_scaled, &ds_coef_man, + &ds_coef_exp); + + REG_RMW_FIELD(ah, AR_PHY_HALFGI, + AR_PHY_HALFGI_DSC_MAN, ds_coef_man); + REG_RMW_FIELD(ah, AR_PHY_HALFGI, + AR_PHY_HALFGI_DSC_EXP, ds_coef_exp); +} + +static void ath9k_hw_9280_spur_mitigate(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + int bb_spur = AR_NO_SPUR; + int freq; + int bin, cur_bin; + int bb_spur_off, spur_subchannel_sd; + int spur_freq_sd; + int spur_delta_phase; + int denominator; + int upper, lower, cur_vit_mask; + int tmp, newVal; + int i; + int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8, + AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60 + }; + int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10, + AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60 + }; + int inc[4] = { 0, 100, 0, 0 }; + struct chan_centers centers; + + int8_t mask_m[123]; + int8_t mask_p[123]; + int8_t mask_amt; + int tmp_mask; + int cur_bb_spur; + bool is2GHz = IS_CHAN_2GHZ(chan); + + memset(&mask_m, 0, sizeof(int8_t) * 123); + memset(&mask_p, 0, sizeof(int8_t) * 123); + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + freq = centers.synth_center; + + ah->ah_config.ath_hal_spurMode = SPUR_ENABLE_EEPROM; + for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { + cur_bb_spur = ath9k_hw_eeprom_get_spur_chan(ah, i, is2GHz); + + if (is2GHz) + cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_2GHZ; + else + cur_bb_spur = (cur_bb_spur / 10) + AR_BASE_FREQ_5GHZ; + + if (AR_NO_SPUR == cur_bb_spur) + break; + cur_bb_spur = cur_bb_spur - freq; + + if (IS_CHAN_HT40(chan)) { + if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT40) && + (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT40)) { + bb_spur = cur_bb_spur; + break; + } + } else if ((cur_bb_spur > -AR_SPUR_FEEQ_BOUND_HT20) && + (cur_bb_spur < AR_SPUR_FEEQ_BOUND_HT20)) { + bb_spur = cur_bb_spur; + break; + } + } + + if (AR_NO_SPUR == bb_spur) { + REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK, + AR_PHY_FORCE_CLKEN_CCK_MRC_MUX); + return; + } else { + REG_CLR_BIT(ah, AR_PHY_FORCE_CLKEN_CCK, + AR_PHY_FORCE_CLKEN_CCK_MRC_MUX); + } + + bin = bb_spur * 320; + + tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0)); + + newVal = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI | + AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | + AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | + AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); + REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), newVal); + + newVal = (AR_PHY_SPUR_REG_MASK_RATE_CNTL | + AR_PHY_SPUR_REG_ENABLE_MASK_PPM | + AR_PHY_SPUR_REG_MASK_RATE_SELECT | + AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI | + SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH)); + REG_WRITE(ah, AR_PHY_SPUR_REG, newVal); + + if (IS_CHAN_HT40(chan)) { + if (bb_spur < 0) { + spur_subchannel_sd = 1; + bb_spur_off = bb_spur + 10; + } else { + spur_subchannel_sd = 0; + bb_spur_off = bb_spur - 10; + } + } else { + spur_subchannel_sd = 0; + bb_spur_off = bb_spur; + } + + if (IS_CHAN_HT40(chan)) + spur_delta_phase = + ((bb_spur * 262144) / + 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; + else + spur_delta_phase = + ((bb_spur * 524288) / + 10) & AR_PHY_TIMING11_SPUR_DELTA_PHASE; + + denominator = IS_CHAN_2GHZ(chan) ? 44 : 40; + spur_freq_sd = ((bb_spur_off * 2048) / denominator) & 0x3ff; + + newVal = (AR_PHY_TIMING11_USE_SPUR_IN_AGC | + SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) | + SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); + REG_WRITE(ah, AR_PHY_TIMING11, newVal); + + newVal = spur_subchannel_sd << AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S; + REG_WRITE(ah, AR_PHY_SFCORR_EXT, newVal); + + cur_bin = -6000; + upper = bin + 100; + lower = bin - 100; + + for (i = 0; i < 4; i++) { + int pilot_mask = 0; + int chan_mask = 0; + int bp = 0; + for (bp = 0; bp < 30; bp++) { + if ((cur_bin > lower) && (cur_bin < upper)) { + pilot_mask = pilot_mask | 0x1 << bp; + chan_mask = chan_mask | 0x1 << bp; + } + cur_bin += 100; + } + cur_bin += inc[i]; + REG_WRITE(ah, pilot_mask_reg[i], pilot_mask); + REG_WRITE(ah, chan_mask_reg[i], chan_mask); + } + + cur_vit_mask = 6100; + upper = bin + 120; + lower = bin - 120; + + for (i = 0; i < 123; i++) { + if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { + if ((abs(cur_vit_mask - bin)) < 75) + mask_amt = 1; + else + mask_amt = 0; + if (cur_vit_mask < 0) + mask_m[abs(cur_vit_mask / 100)] = mask_amt; + else + mask_p[cur_vit_mask / 100] = mask_amt; + } + cur_vit_mask -= 100; + } + + tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28) + | (mask_m[48] << 26) | (mask_m[49] << 24) + | (mask_m[50] << 22) | (mask_m[51] << 20) + | (mask_m[52] << 18) | (mask_m[53] << 16) + | (mask_m[54] << 14) | (mask_m[55] << 12) + | (mask_m[56] << 10) | (mask_m[57] << 8) + | (mask_m[58] << 6) | (mask_m[59] << 4) + | (mask_m[60] << 2) | (mask_m[61] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask); + REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask); + + tmp_mask = (mask_m[31] << 28) + | (mask_m[32] << 26) | (mask_m[33] << 24) + | (mask_m[34] << 22) | (mask_m[35] << 20) + | (mask_m[36] << 18) | (mask_m[37] << 16) + | (mask_m[48] << 14) | (mask_m[39] << 12) + | (mask_m[40] << 10) | (mask_m[41] << 8) + | (mask_m[42] << 6) | (mask_m[43] << 4) + | (mask_m[44] << 2) | (mask_m[45] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask); + + tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28) + | (mask_m[18] << 26) | (mask_m[18] << 24) + | (mask_m[20] << 22) | (mask_m[20] << 20) + | (mask_m[22] << 18) | (mask_m[22] << 16) + | (mask_m[24] << 14) | (mask_m[24] << 12) + | (mask_m[25] << 10) | (mask_m[26] << 8) + | (mask_m[27] << 6) | (mask_m[28] << 4) + | (mask_m[29] << 2) | (mask_m[30] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask); + + tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28) + | (mask_m[2] << 26) | (mask_m[3] << 24) + | (mask_m[4] << 22) | (mask_m[5] << 20) + | (mask_m[6] << 18) | (mask_m[7] << 16) + | (mask_m[8] << 14) | (mask_m[9] << 12) + | (mask_m[10] << 10) | (mask_m[11] << 8) + | (mask_m[12] << 6) | (mask_m[13] << 4) + | (mask_m[14] << 2) | (mask_m[15] << 0); + REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask); + + tmp_mask = (mask_p[15] << 28) + | (mask_p[14] << 26) | (mask_p[13] << 24) + | (mask_p[12] << 22) | (mask_p[11] << 20) + | (mask_p[10] << 18) | (mask_p[9] << 16) + | (mask_p[8] << 14) | (mask_p[7] << 12) + | (mask_p[6] << 10) | (mask_p[5] << 8) + | (mask_p[4] << 6) | (mask_p[3] << 4) + | (mask_p[2] << 2) | (mask_p[1] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask); + + tmp_mask = (mask_p[30] << 28) + | (mask_p[29] << 26) | (mask_p[28] << 24) + | (mask_p[27] << 22) | (mask_p[26] << 20) + | (mask_p[25] << 18) | (mask_p[24] << 16) + | (mask_p[23] << 14) | (mask_p[22] << 12) + | (mask_p[21] << 10) | (mask_p[20] << 8) + | (mask_p[19] << 6) | (mask_p[18] << 4) + | (mask_p[17] << 2) | (mask_p[16] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask); + + tmp_mask = (mask_p[45] << 28) + | (mask_p[44] << 26) | (mask_p[43] << 24) + | (mask_p[42] << 22) | (mask_p[41] << 20) + | (mask_p[40] << 18) | (mask_p[39] << 16) + | (mask_p[38] << 14) | (mask_p[37] << 12) + | (mask_p[36] << 10) | (mask_p[35] << 8) + | (mask_p[34] << 6) | (mask_p[33] << 4) + | (mask_p[32] << 2) | (mask_p[31] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask); + + tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28) + | (mask_p[59] << 26) | (mask_p[58] << 24) + | (mask_p[57] << 22) | (mask_p[56] << 20) + | (mask_p[55] << 18) | (mask_p[54] << 16) + | (mask_p[53] << 14) | (mask_p[52] << 12) + | (mask_p[51] << 10) | (mask_p[50] << 8) + | (mask_p[49] << 6) | (mask_p[48] << 4) + | (mask_p[47] << 2) | (mask_p[46] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); +} + +static void ath9k_hw_spur_mitigate(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + int bb_spur = AR_NO_SPUR; + int bin, cur_bin; + int spur_freq_sd; + int spur_delta_phase; + int denominator; + int upper, lower, cur_vit_mask; + int tmp, new; + int i; + int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8, + AR_PHY_PILOT_MASK_01_30, AR_PHY_PILOT_MASK_31_60 + }; + int chan_mask_reg[4] = { AR_PHY_TIMING9, AR_PHY_TIMING10, + AR_PHY_CHANNEL_MASK_01_30, AR_PHY_CHANNEL_MASK_31_60 + }; + int inc[4] = { 0, 100, 0, 0 }; + + int8_t mask_m[123]; + int8_t mask_p[123]; + int8_t mask_amt; + int tmp_mask; + int cur_bb_spur; + bool is2GHz = IS_CHAN_2GHZ(chan); + + memset(&mask_m, 0, sizeof(int8_t) * 123); + memset(&mask_p, 0, sizeof(int8_t) * 123); + + for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { + cur_bb_spur = ath9k_hw_eeprom_get_spur_chan(ah, i, is2GHz); + if (AR_NO_SPUR == cur_bb_spur) + break; + cur_bb_spur = cur_bb_spur - (chan->channel * 10); + if ((cur_bb_spur > -95) && (cur_bb_spur < 95)) { + bb_spur = cur_bb_spur; + break; + } + } + + if (AR_NO_SPUR == bb_spur) + return; + + bin = bb_spur * 32; + + tmp = REG_READ(ah, AR_PHY_TIMING_CTRL4(0)); + new = tmp | (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI | + AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER | + AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK | + AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK); + + REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), new); + + new = (AR_PHY_SPUR_REG_MASK_RATE_CNTL | + AR_PHY_SPUR_REG_ENABLE_MASK_PPM | + AR_PHY_SPUR_REG_MASK_RATE_SELECT | + AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI | + SM(SPUR_RSSI_THRESH, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH)); + REG_WRITE(ah, AR_PHY_SPUR_REG, new); + + spur_delta_phase = ((bb_spur * 524288) / 100) & + AR_PHY_TIMING11_SPUR_DELTA_PHASE; + + denominator = IS_CHAN_2GHZ(chan) ? 440 : 400; + spur_freq_sd = ((bb_spur * 2048) / denominator) & 0x3ff; + + new = (AR_PHY_TIMING11_USE_SPUR_IN_AGC | + SM(spur_freq_sd, AR_PHY_TIMING11_SPUR_FREQ_SD) | + SM(spur_delta_phase, AR_PHY_TIMING11_SPUR_DELTA_PHASE)); + REG_WRITE(ah, AR_PHY_TIMING11, new); + + cur_bin = -6000; + upper = bin + 100; + lower = bin - 100; + + for (i = 0; i < 4; i++) { + int pilot_mask = 0; + int chan_mask = 0; + int bp = 0; + for (bp = 0; bp < 30; bp++) { + if ((cur_bin > lower) && (cur_bin < upper)) { + pilot_mask = pilot_mask | 0x1 << bp; + chan_mask = chan_mask | 0x1 << bp; + } + cur_bin += 100; + } + cur_bin += inc[i]; + REG_WRITE(ah, pilot_mask_reg[i], pilot_mask); + REG_WRITE(ah, chan_mask_reg[i], chan_mask); + } + + cur_vit_mask = 6100; + upper = bin + 120; + lower = bin - 120; + + for (i = 0; i < 123; i++) { + if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { + if ((abs(cur_vit_mask - bin)) < 75) + mask_amt = 1; + else + mask_amt = 0; + if (cur_vit_mask < 0) + mask_m[abs(cur_vit_mask / 100)] = mask_amt; + else + mask_p[cur_vit_mask / 100] = mask_amt; + } + cur_vit_mask -= 100; + } + + tmp_mask = (mask_m[46] << 30) | (mask_m[47] << 28) + | (mask_m[48] << 26) | (mask_m[49] << 24) + | (mask_m[50] << 22) | (mask_m[51] << 20) + | (mask_m[52] << 18) | (mask_m[53] << 16) + | (mask_m[54] << 14) | (mask_m[55] << 12) + | (mask_m[56] << 10) | (mask_m[57] << 8) + | (mask_m[58] << 6) | (mask_m[59] << 4) + | (mask_m[60] << 2) | (mask_m[61] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_1, tmp_mask); + REG_WRITE(ah, AR_PHY_VIT_MASK2_M_46_61, tmp_mask); + + tmp_mask = (mask_m[31] << 28) + | (mask_m[32] << 26) | (mask_m[33] << 24) + | (mask_m[34] << 22) | (mask_m[35] << 20) + | (mask_m[36] << 18) | (mask_m[37] << 16) + | (mask_m[48] << 14) | (mask_m[39] << 12) + | (mask_m[40] << 10) | (mask_m[41] << 8) + | (mask_m[42] << 6) | (mask_m[43] << 4) + | (mask_m[44] << 2) | (mask_m[45] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_2, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_31_45, tmp_mask); + + tmp_mask = (mask_m[16] << 30) | (mask_m[16] << 28) + | (mask_m[18] << 26) | (mask_m[18] << 24) + | (mask_m[20] << 22) | (mask_m[20] << 20) + | (mask_m[22] << 18) | (mask_m[22] << 16) + | (mask_m[24] << 14) | (mask_m[24] << 12) + | (mask_m[25] << 10) | (mask_m[26] << 8) + | (mask_m[27] << 6) | (mask_m[28] << 4) + | (mask_m[29] << 2) | (mask_m[30] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK_3, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_16_30, tmp_mask); + + tmp_mask = (mask_m[0] << 30) | (mask_m[1] << 28) + | (mask_m[2] << 26) | (mask_m[3] << 24) + | (mask_m[4] << 22) | (mask_m[5] << 20) + | (mask_m[6] << 18) | (mask_m[7] << 16) + | (mask_m[8] << 14) | (mask_m[9] << 12) + | (mask_m[10] << 10) | (mask_m[11] << 8) + | (mask_m[12] << 6) | (mask_m[13] << 4) + | (mask_m[14] << 2) | (mask_m[15] << 0); + REG_WRITE(ah, AR_PHY_MASK_CTL, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_M_00_15, tmp_mask); + + tmp_mask = (mask_p[15] << 28) + | (mask_p[14] << 26) | (mask_p[13] << 24) + | (mask_p[12] << 22) | (mask_p[11] << 20) + | (mask_p[10] << 18) | (mask_p[9] << 16) + | (mask_p[8] << 14) | (mask_p[7] << 12) + | (mask_p[6] << 10) | (mask_p[5] << 8) + | (mask_p[4] << 6) | (mask_p[3] << 4) + | (mask_p[2] << 2) | (mask_p[1] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_1, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_15_01, tmp_mask); + + tmp_mask = (mask_p[30] << 28) + | (mask_p[29] << 26) | (mask_p[28] << 24) + | (mask_p[27] << 22) | (mask_p[26] << 20) + | (mask_p[25] << 18) | (mask_p[24] << 16) + | (mask_p[23] << 14) | (mask_p[22] << 12) + | (mask_p[21] << 10) | (mask_p[20] << 8) + | (mask_p[19] << 6) | (mask_p[18] << 4) + | (mask_p[17] << 2) | (mask_p[16] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_2, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_30_16, tmp_mask); + + tmp_mask = (mask_p[45] << 28) + | (mask_p[44] << 26) | (mask_p[43] << 24) + | (mask_p[42] << 22) | (mask_p[41] << 20) + | (mask_p[40] << 18) | (mask_p[39] << 16) + | (mask_p[38] << 14) | (mask_p[37] << 12) + | (mask_p[36] << 10) | (mask_p[35] << 8) + | (mask_p[34] << 6) | (mask_p[33] << 4) + | (mask_p[32] << 2) | (mask_p[31] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_3, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_45_31, tmp_mask); + + tmp_mask = (mask_p[61] << 30) | (mask_p[60] << 28) + | (mask_p[59] << 26) | (mask_p[58] << 24) + | (mask_p[57] << 22) | (mask_p[56] << 20) + | (mask_p[55] << 18) | (mask_p[54] << 16) + | (mask_p[53] << 14) | (mask_p[52] << 12) + | (mask_p[51] << 10) | (mask_p[50] << 8) + | (mask_p[49] << 6) | (mask_p[48] << 4) + | (mask_p[47] << 2) | (mask_p[46] << 0); + REG_WRITE(ah, AR_PHY_BIN_MASK2_4, tmp_mask); + REG_WRITE(ah, AR_PHY_MASK2_P_61_45, tmp_mask); +} + +static inline void ath9k_hw_init_chain_masks(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int rx_chainmask, tx_chainmask; + + rx_chainmask = ahp->ah_rxchainmask; + tx_chainmask = ahp->ah_txchainmask; + + switch (rx_chainmask) { + case 0x5: + REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, + AR_PHY_SWAP_ALT_CHAIN); + case 0x3: + if (((ah)->ah_macVersion <= AR_SREV_VERSION_9160)) { + REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7); + REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7); + break; + } + case 0x1: + case 0x2: + if (!AR_SREV_9280(ah)) + break; + case 0x7: + REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); + REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); + break; + default: + break; + } + + REG_WRITE(ah, AR_SELFGEN_MASK, tx_chainmask); + if (tx_chainmask == 0x5) { + REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, + AR_PHY_SWAP_ALT_CHAIN); + } + if (AR_SREV_9100(ah)) + REG_WRITE(ah, AR_PHY_ANALOG_SWAP, + REG_READ(ah, AR_PHY_ANALOG_SWAP) | 0x00000001); +} + +static void ath9k_hw_set_addac(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + struct modal_eep_header *pModal; + struct ath_hal_5416 *ahp = AH5416(ah); + struct ar5416_eeprom *eep = &ahp->ah_eeprom; + u8 biaslevel; + + if (ah->ah_macVersion != AR_SREV_VERSION_9160) + return; + + if (ar5416_get_eep_rev(ahp) < AR5416_EEP_MINOR_VER_7) + return; + + pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); + + if (pModal->xpaBiasLvl != 0xff) { + biaslevel = pModal->xpaBiasLvl; + } else { + + u16 resetFreqBin, freqBin, freqCount = 0; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + resetFreqBin = + FREQ2FBIN(centers.synth_center, IS_CHAN_2GHZ(chan)); + freqBin = pModal->xpaBiasLvlFreq[0] & 0xff; + biaslevel = (u8) (pModal->xpaBiasLvlFreq[0] >> 14); + + freqCount++; + + while (freqCount < 3) { + if (pModal->xpaBiasLvlFreq[freqCount] == 0x0) + break; + + freqBin = pModal->xpaBiasLvlFreq[freqCount] & 0xff; + if (resetFreqBin >= freqBin) { + biaslevel = + (u8) (pModal-> + xpaBiasLvlFreq[freqCount] + >> 14); + } else { + break; + } + freqCount++; + } + } + + if (IS_CHAN_2GHZ(chan)) { + INI_RA(&ahp->ah_iniAddac, 7, 1) = + (INI_RA(&ahp->ah_iniAddac, 7, 1) & (~0x18)) | biaslevel + << 3; + } else { + INI_RA(&ahp->ah_iniAddac, 6, 1) = + (INI_RA(&ahp->ah_iniAddac, 6, 1) & (~0xc0)) | biaslevel + << 6; + } +} + +static u32 ath9k_hw_mac_usec(struct ath_hal *ah, u32 clks) +{ + if (ah->ah_curchan != NULL) + return clks / + CLOCK_RATE[ath9k_hw_chan2wmode(ah, ah->ah_curchan)]; + else + return clks / CLOCK_RATE[WIRELESS_MODE_11b]; +} + +static u32 ath9k_hw_mac_to_usec(struct ath_hal *ah, u32 clks) +{ + struct ath9k_channel *chan = ah->ah_curchan; + + if (chan && IS_CHAN_HT40(chan)) + return ath9k_hw_mac_usec(ah, clks) / 2; + else + return ath9k_hw_mac_usec(ah, clks); +} + +static u32 ath9k_hw_mac_clks(struct ath_hal *ah, u32 usecs) +{ + if (ah->ah_curchan != NULL) + return usecs * CLOCK_RATE[ath9k_hw_chan2wmode(ah, + ah->ah_curchan)]; + else + return usecs * CLOCK_RATE[WIRELESS_MODE_11b]; +} + +static u32 ath9k_hw_mac_to_clks(struct ath_hal *ah, u32 usecs) +{ + struct ath9k_channel *chan = ah->ah_curchan; + + if (chan && IS_CHAN_HT40(chan)) + return ath9k_hw_mac_clks(ah, usecs) * 2; + else + return ath9k_hw_mac_clks(ah, usecs); +} + +static bool ath9k_hw_set_ack_timeout(struct ath_hal *ah, u32 us) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: bad ack timeout %u\n", + __func__, us); + ahp->ah_acktimeout = (u32) -1; + return false; + } else { + REG_RMW_FIELD(ah, AR_TIME_OUT, + AR_TIME_OUT_ACK, ath9k_hw_mac_to_clks(ah, us)); + ahp->ah_acktimeout = us; + return true; + } +} + +static bool ath9k_hw_set_cts_timeout(struct ath_hal *ah, u32 us) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_CTS))) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: bad cts timeout %u\n", + __func__, us); + ahp->ah_ctstimeout = (u32) -1; + return false; + } else { + REG_RMW_FIELD(ah, AR_TIME_OUT, + AR_TIME_OUT_CTS, ath9k_hw_mac_to_clks(ah, us)); + ahp->ah_ctstimeout = us; + return true; + } +} +static bool ath9k_hw_set_global_txtimeout(struct ath_hal *ah, + u32 tu) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (tu > 0xFFFF) { + DPRINTF(ah->ah_sc, ATH_DBG_XMIT, + "%s: bad global tx timeout %u\n", __func__, tu); + ahp->ah_globaltxtimeout = (u32) -1; + return false; + } else { + REG_RMW_FIELD(ah, AR_GTXTO, AR_GTXTO_TIMEOUT_LIMIT, tu); + ahp->ah_globaltxtimeout = tu; + return true; + } +} + +bool ath9k_hw_setslottime(struct ath_hal *ah, u32 us) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (us < ATH9K_SLOT_TIME_9 || us > ath9k_hw_mac_to_usec(ah, 0xffff)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: bad slot time %u\n", + __func__, us); + ahp->ah_slottime = (u32) -1; + return false; + } else { + REG_WRITE(ah, AR_D_GBL_IFS_SLOT, ath9k_hw_mac_to_clks(ah, us)); + ahp->ah_slottime = us; + return true; + } +} + +static inline void ath9k_hw_init_user_settings(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "--AP %s ahp->ah_miscMode 0x%x\n", + __func__, ahp->ah_miscMode); + if (ahp->ah_miscMode != 0) + REG_WRITE(ah, AR_PCU_MISC, + REG_READ(ah, AR_PCU_MISC) | ahp->ah_miscMode); + if (ahp->ah_slottime != (u32) -1) + ath9k_hw_setslottime(ah, ahp->ah_slottime); + if (ahp->ah_acktimeout != (u32) -1) + ath9k_hw_set_ack_timeout(ah, ahp->ah_acktimeout); + if (ahp->ah_ctstimeout != (u32) -1) + ath9k_hw_set_cts_timeout(ah, ahp->ah_ctstimeout); + if (ahp->ah_globaltxtimeout != (u32) -1) + ath9k_hw_set_global_txtimeout(ah, ahp->ah_globaltxtimeout); +} + +static inline int +ath9k_hw_process_ini(struct ath_hal *ah, + struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode) +{ + int i, regWrites = 0; + struct ath_hal_5416 *ahp = AH5416(ah); + u32 modesIndex, freqIndex; + int status; + + switch (chan->chanmode) { + case CHANNEL_A: + case CHANNEL_A_HT20: + modesIndex = 1; + freqIndex = 1; + break; + case CHANNEL_A_HT40PLUS: + case CHANNEL_A_HT40MINUS: + modesIndex = 2; + freqIndex = 1; + break; + case CHANNEL_G: + case CHANNEL_G_HT20: + case CHANNEL_B: + modesIndex = 4; + freqIndex = 2; + break; + case CHANNEL_G_HT40PLUS: + case CHANNEL_G_HT40MINUS: + modesIndex = 3; + freqIndex = 2; + break; + + default: + return -EINVAL; + } + + REG_WRITE(ah, AR_PHY(0), 0x00000007); + + REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO); + + ath9k_hw_set_addac(ah, chan); + + if (AR_SREV_5416_V22_OR_LATER(ah)) { + REG_WRITE_ARRAY(&ahp->ah_iniAddac, 1, regWrites); + } else { + struct ar5416IniArray temp; + u32 addacSize = + sizeof(u32) * ahp->ah_iniAddac.ia_rows * + ahp->ah_iniAddac.ia_columns; + + memcpy(ahp->ah_addac5416_21, + ahp->ah_iniAddac.ia_array, addacSize); + + (ahp->ah_addac5416_21)[31 * + ahp->ah_iniAddac.ia_columns + 1] = 0; + + temp.ia_array = ahp->ah_addac5416_21; + temp.ia_columns = ahp->ah_iniAddac.ia_columns; + temp.ia_rows = ahp->ah_iniAddac.ia_rows; + REG_WRITE_ARRAY(&temp, 1, regWrites); + } + REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC); + + for (i = 0; i < ahp->ah_iniModes.ia_rows; i++) { + u32 reg = INI_RA(&ahp->ah_iniModes, i, 0); + u32 val = INI_RA(&ahp->ah_iniModes, i, modesIndex); + +#ifdef CONFIG_SLOW_ANT_DIV + if (ah->ah_devid == AR9280_DEVID_PCI) + val = ath9k_hw_ini_fixup(ah, &ahp->ah_eeprom, reg, + val); +#endif + + REG_WRITE(ah, reg, val); + + if (reg >= 0x7800 && reg < 0x78a0 + && ah->ah_config.ath_hal_analogShiftReg) { + udelay(100); + } + + DO_DELAY(regWrites); + } + + for (i = 0; i < ahp->ah_iniCommon.ia_rows; i++) { + u32 reg = INI_RA(&ahp->ah_iniCommon, i, 0); + u32 val = INI_RA(&ahp->ah_iniCommon, i, 1); + + REG_WRITE(ah, reg, val); + + if (reg >= 0x7800 && reg < 0x78a0 + && ah->ah_config.ath_hal_analogShiftReg) { + udelay(100); + } + + DO_DELAY(regWrites); + } + + ath9k_hw_write_regs(ah, modesIndex, freqIndex, regWrites); + + if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) { + REG_WRITE_ARRAY(&ahp->ah_iniModesAdditional, modesIndex, + regWrites); + } + + ath9k_hw_override_ini(ah, chan); + ath9k_hw_set_regs(ah, chan, macmode); + ath9k_hw_init_chain_masks(ah); + + status = ath9k_hw_set_txpower(ah, &ahp->ah_eeprom, chan, + ath9k_regd_get_ctl(ah, chan), + ath9k_regd_get_antenna_allowed(ah, + chan), + chan->maxRegTxPower * 2, + min((u32) MAX_RATE_POWER, + (u32) ah->ah_powerLimit)); + if (status != 0) { + DPRINTF(ah->ah_sc, ATH_DBG_POWER_MGMT, + "%s: error init'ing transmit power\n", __func__); + return -EIO; + } + + if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) { + DPRINTF(ah->ah_sc, ATH_DBG_REG_IO, + "%s: ar5416SetRfRegs failed\n", __func__); + return -EIO; + } + + return 0; +} + +static inline void ath9k_hw_setup_calibration(struct ath_hal *ah, + struct hal_cal_list *currCal) +{ + REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(0), + AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX, + currCal->calData->calCountMax); + + switch (currCal->calData->calType) { + case IQ_MISMATCH_CAL: + REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: starting IQ Mismatch Calibration\n", + __func__); + break; + case ADC_GAIN_CAL: + REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: starting ADC Gain Calibration\n", __func__); + break; + case ADC_DC_CAL: + REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: starting ADC DC Calibration\n", __func__); + break; + case ADC_DC_INIT_CAL: + REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: starting Init ADC DC Calibration\n", + __func__); + break; + } + + REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0), + AR_PHY_TIMING_CTRL4_DO_CAL); +} + +static inline void ath9k_hw_reset_calibration(struct ath_hal *ah, + struct hal_cal_list *currCal) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + ath9k_hw_setup_calibration(ah, currCal); + + currCal->calState = CAL_RUNNING; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + ahp->ah_Meas0.sign[i] = 0; + ahp->ah_Meas1.sign[i] = 0; + ahp->ah_Meas2.sign[i] = 0; + ahp->ah_Meas3.sign[i] = 0; + } + + ahp->ah_CalSamples = 0; +} + +static inline void +ath9k_hw_per_calibration(struct ath_hal *ah, + struct ath9k_channel *ichan, + u8 rxchainmask, + struct hal_cal_list *currCal, + bool *isCalDone) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + *isCalDone = false; + + if (currCal->calState == CAL_RUNNING) { + if (!(REG_READ(ah, + AR_PHY_TIMING_CTRL4(0)) & + AR_PHY_TIMING_CTRL4_DO_CAL)) { + + currCal->calData->calCollect(ah); + + ahp->ah_CalSamples++; + + if (ahp->ah_CalSamples >= + currCal->calData->calNumSamples) { + int i, numChains = 0; + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + if (rxchainmask & (1 << i)) + numChains++; + } + + currCal->calData->calPostProc(ah, + numChains); + + ichan->CalValid |= + currCal->calData->calType; + currCal->calState = CAL_DONE; + *isCalDone = true; + } else { + ath9k_hw_setup_calibration(ah, currCal); + } + } + } else if (!(ichan->CalValid & currCal->calData->calType)) { + ath9k_hw_reset_calibration(ah, currCal); + } +} + +static inline bool ath9k_hw_run_init_cals(struct ath_hal *ah, + int init_cal_count) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel ichan; + bool isCalDone; + struct hal_cal_list *currCal = ahp->ah_cal_list_curr; + const struct hal_percal_data *calData = currCal->calData; + int i; + + if (currCal == NULL) + return false; + + ichan.CalValid = 0; + + for (i = 0; i < init_cal_count; i++) { + ath9k_hw_reset_calibration(ah, currCal); + + if (!ath9k_hw_wait(ah, AR_PHY_TIMING_CTRL4(0), + AR_PHY_TIMING_CTRL4_DO_CAL, 0)) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: Cal %d failed to complete in 100ms.\n", + __func__, calData->calType); + + ahp->ah_cal_list = ahp->ah_cal_list_last = + ahp->ah_cal_list_curr = NULL; + return false; + } + + ath9k_hw_per_calibration(ah, &ichan, ahp->ah_rxchainmask, + currCal, &isCalDone); + if (!isCalDone) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: Not able to run Init Cal %d.\n", + __func__, calData->calType); + } + if (currCal->calNext) { + currCal = currCal->calNext; + calData = currCal->calData; + } + } + + ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = NULL; + return true; +} + +static inline bool +ath9k_hw_channel_change(struct ath_hal *ah, + struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode) +{ + u32 synthDelay, qnum; + struct ath_hal_5416 *ahp = AH5416(ah); + + for (qnum = 0; qnum < AR_NUM_QCU; qnum++) { + if (ath9k_hw_numtxpending(ah, qnum)) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "%s: Transmit frames pending on queue %d\n", + __func__, qnum); + return false; + } + } + + REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_EN); + if (!ath9k_hw_wait(ah, AR_PHY_RFBUS_GRANT, AR_PHY_RFBUS_GRANT_EN, + AR_PHY_RFBUS_GRANT_EN)) { + DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO, + "%s: Could not kill baseband RX\n", __func__); + return false; + } + + ath9k_hw_set_regs(ah, chan, macmode); + + if (AR_SREV_9280_10_OR_LATER(ah)) { + if (!(ath9k_hw_ar9280_set_channel(ah, chan))) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: failed to set channel\n", __func__); + return false; + } + } else { + if (!(ath9k_hw_set_channel(ah, chan))) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: failed to set channel\n", __func__); + return false; + } + } + + if (ath9k_hw_set_txpower(ah, &ahp->ah_eeprom, chan, + ath9k_regd_get_ctl(ah, chan), + ath9k_regd_get_antenna_allowed(ah, chan), + chan->maxRegTxPower * 2, + min((u32) MAX_RATE_POWER, + (u32) ah->ah_powerLimit)) != 0) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: error init'ing transmit power\n", __func__); + return false; + } + + synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; + if (IS_CHAN_CCK(chan)) + synthDelay = (4 * synthDelay) / 22; + else + synthDelay /= 10; + + udelay(synthDelay + BASE_ACTIVATE_DELAY); + + REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); + + if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) + ath9k_hw_set_delta_slope(ah, chan); + + if (AR_SREV_9280_10_OR_LATER(ah)) + ath9k_hw_9280_spur_mitigate(ah, chan); + else + ath9k_hw_spur_mitigate(ah, chan); + + if (!chan->oneTimeCalsDone) + chan->oneTimeCalsDone = true; + + return true; +} + +static bool ath9k_hw_chip_reset(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM)) + return false; + + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) + return false; + + ahp->ah_chipFullSleep = false; + + ath9k_hw_init_pll(ah, chan); + + ath9k_hw_set_rfmode(ah, chan); + + return true; +} + +static inline void ath9k_hw_set_dma(struct ath_hal *ah) +{ + u32 regval; + + regval = REG_READ(ah, AR_AHB_MODE); + REG_WRITE(ah, AR_AHB_MODE, regval | AR_AHB_PREFETCH_RD_EN); + + regval = REG_READ(ah, AR_TXCFG) & ~AR_TXCFG_DMASZ_MASK; + REG_WRITE(ah, AR_TXCFG, regval | AR_TXCFG_DMASZ_128B); + + REG_RMW_FIELD(ah, AR_TXCFG, AR_FTRIG, ah->ah_txTrigLevel); + + regval = REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_DMASZ_MASK; + REG_WRITE(ah, AR_RXCFG, regval | AR_RXCFG_DMASZ_128B); + + REG_WRITE(ah, AR_RXFIFO_CFG, 0x200); + + if (AR_SREV_9285(ah)) { + REG_WRITE(ah, AR_PCU_TXBUF_CTRL, + AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE); + } else { + REG_WRITE(ah, AR_PCU_TXBUF_CTRL, + AR_PCU_TXBUF_CTRL_USABLE_SIZE); + } +} + +bool ath9k_hw_stopdmarecv(struct ath_hal *ah) +{ + REG_WRITE(ah, AR_CR, AR_CR_RXD); + if (!ath9k_hw_wait(ah, AR_CR, AR_CR_RXE, 0)) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "%s: dma failed to stop in 10ms\n" + "AR_CR=0x%08x\nAR_DIAG_SW=0x%08x\n", + __func__, + REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW)); + return false; + } else { + return true; + } +} + +void ath9k_hw_startpcureceive(struct ath_hal *ah) +{ + REG_CLR_BIT(ah, AR_DIAG_SW, + (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + + ath9k_enable_mib_counters(ah); + + ath9k_ani_reset(ah); +} + +void ath9k_hw_stoppcurecv(struct ath_hal *ah) +{ + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + + ath9k_hw_disable_mib_counters(ah); +} + +static bool ath9k_hw_iscal_supported(struct ath_hal *ah, + struct ath9k_channel *chan, + enum hal_cal_types calType) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + bool retval = false; + + switch (calType & ahp->ah_suppCals) { + case IQ_MISMATCH_CAL: + if (!IS_CHAN_B(chan)) + retval = true; + break; + case ADC_GAIN_CAL: + case ADC_DC_CAL: + if (!IS_CHAN_B(chan) + && !(IS_CHAN_2GHZ(chan) && IS_CHAN_HT20(chan))) + retval = true; + break; + } + + return retval; +} + +static inline bool ath9k_hw_init_cal(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *ichan = + ath9k_regd_check_channel(ah, chan); + + REG_WRITE(ah, AR_PHY_AGC_CONTROL, + REG_READ(ah, AR_PHY_AGC_CONTROL) | + AR_PHY_AGC_CONTROL_CAL); + + if (!ath9k_hw_wait + (ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: offset calibration failed to complete in 1ms; " + "noisy environment?\n", __func__); + return false; + } + + REG_WRITE(ah, AR_PHY_AGC_CONTROL, + REG_READ(ah, AR_PHY_AGC_CONTROL) | + AR_PHY_AGC_CONTROL_NF); + + ahp->ah_cal_list = ahp->ah_cal_list_last = ahp->ah_cal_list_curr = + NULL; + + if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) { + if (ath9k_hw_iscal_supported(ah, chan, ADC_GAIN_CAL)) { + INIT_CAL(&ahp->ah_adcGainCalData); + INSERT_CAL(ahp, &ahp->ah_adcGainCalData); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: enabling ADC Gain Calibration.\n", + __func__); + } + if (ath9k_hw_iscal_supported(ah, chan, ADC_DC_CAL)) { + INIT_CAL(&ahp->ah_adcDcCalData); + INSERT_CAL(ahp, &ahp->ah_adcDcCalData); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: enabling ADC DC Calibration.\n", + __func__); + } + if (ath9k_hw_iscal_supported(ah, chan, IQ_MISMATCH_CAL)) { + INIT_CAL(&ahp->ah_iqCalData); + INSERT_CAL(ahp, &ahp->ah_iqCalData); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: enabling IQ Calibration.\n", + __func__); + } + + ahp->ah_cal_list_curr = ahp->ah_cal_list; + + if (ahp->ah_cal_list_curr) + ath9k_hw_reset_calibration(ah, + ahp->ah_cal_list_curr); + } + + ichan->CalValid = 0; + + return true; +} + + +bool ath9k_hw_reset(struct ath_hal *ah, enum ath9k_opmode opmode, + struct ath9k_channel *chan, + enum ath9k_ht_macmode macmode, + u8 txchainmask, u8 rxchainmask, + enum ath9k_ht_extprotspacing extprotspacing, + bool bChannelChange, + int *status) +{ +#define FAIL(_code) do { ecode = _code; goto bad; } while (0) + u32 saveLedState; + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *curchan = ah->ah_curchan; + u32 saveDefAntenna; + u32 macStaId1; + int ecode; + int i, rx_chainmask; + + ahp->ah_extprotspacing = extprotspacing; + ahp->ah_txchainmask = txchainmask; + ahp->ah_rxchainmask = rxchainmask; + + if (AR_SREV_9280(ah)) { + ahp->ah_txchainmask &= 0x3; + ahp->ah_rxchainmask &= 0x3; + } + + if (ath9k_hw_check_chan(ah, chan) == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: invalid channel %u/0x%x; no mapping\n", + __func__, chan->channel, chan->channelFlags); + FAIL(-EINVAL); + } + + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) + return false; + + if (curchan) + ath9k_hw_getnf(ah, curchan); + + if (bChannelChange && + (ahp->ah_chipFullSleep != true) && + (ah->ah_curchan != NULL) && + (chan->channel != ah->ah_curchan->channel) && + ((chan->channelFlags & CHANNEL_ALL) == + (ah->ah_curchan->channelFlags & CHANNEL_ALL)) && + (!AR_SREV_9280(ah) || (!IS_CHAN_A_5MHZ_SPACED(chan) && + !IS_CHAN_A_5MHZ_SPACED(ah-> + ah_curchan)))) { + + if (ath9k_hw_channel_change(ah, chan, macmode)) { + ath9k_hw_loadnf(ah, ah->ah_curchan); + ath9k_hw_start_nfcal(ah); + return true; + } + } + + saveDefAntenna = REG_READ(ah, AR_DEF_ANTENNA); + if (saveDefAntenna == 0) + saveDefAntenna = 1; + + macStaId1 = REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B; + + saveLedState = REG_READ(ah, AR_CFG_LED) & + (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL | + AR_CFG_LED_BLINK_THRESH_SEL | AR_CFG_LED_BLINK_SLOW); + + ath9k_hw_mark_phy_inactive(ah); + + if (!ath9k_hw_chip_reset(ah, chan)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s: chip reset failed\n", + __func__); + FAIL(-EIO); + } + + if (AR_SREV_9280(ah)) { + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, + AR_GPIO_JTAG_DISABLE); + + if (ah->ah_caps.halWirelessModes & ATH9K_MODE_SEL_11A) { + if (IS_CHAN_5GHZ(chan)) + ath9k_hw_set_gpio(ah, 9, 0); + else + ath9k_hw_set_gpio(ah, 9, 1); + } + ath9k_hw_cfg_output(ah, 9, ATH9K_GPIO_OUTPUT_MUX_AS_OUTPUT); + } + + ecode = ath9k_hw_process_ini(ah, chan, macmode); + if (ecode != 0) + goto bad; + + if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) + ath9k_hw_set_delta_slope(ah, chan); + + if (AR_SREV_9280_10_OR_LATER(ah)) + ath9k_hw_9280_spur_mitigate(ah, chan); + else + ath9k_hw_spur_mitigate(ah, chan); + + if (!ath9k_hw_eeprom_set_board_values(ah, chan)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "%s: error setting board options\n", __func__); + FAIL(-EIO); + } + + ath9k_hw_decrease_chain_power(ah, chan); + + REG_WRITE(ah, AR_STA_ID0, get_unaligned_le32(ahp->ah_macaddr)); + REG_WRITE(ah, AR_STA_ID1, get_unaligned_le16(ahp->ah_macaddr + 4) + | macStaId1 + | AR_STA_ID1_RTS_USE_DEF + | (ah->ah_config. + ath_hal_6mb_ack ? AR_STA_ID1_ACKCTS_6MB : 0) + | ahp->ah_staId1Defaults); + ath9k_hw_set_operating_mode(ah, opmode); + + REG_WRITE(ah, AR_BSSMSKL, get_unaligned_le32(ahp->ah_bssidmask)); + REG_WRITE(ah, AR_BSSMSKU, get_unaligned_le16(ahp->ah_bssidmask + 4)); + + REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna); + + REG_WRITE(ah, AR_BSS_ID0, get_unaligned_le32(ahp->ah_bssid)); + REG_WRITE(ah, AR_BSS_ID1, get_unaligned_le16(ahp->ah_bssid + 4) | + ((ahp->ah_assocId & 0x3fff) << AR_BSS_ID1_AID_S)); + + REG_WRITE(ah, AR_ISR, ~0); + + REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR); + + if (AR_SREV_9280_10_OR_LATER(ah)) { + if (!(ath9k_hw_ar9280_set_channel(ah, chan))) + FAIL(-EIO); + } else { + if (!(ath9k_hw_set_channel(ah, chan))) + FAIL(-EIO); + } + + for (i = 0; i < AR_NUM_DCU; i++) + REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); + + ahp->ah_intrTxqs = 0; + for (i = 0; i < ah->ah_caps.halTotalQueues; i++) + ath9k_hw_resettxqueue(ah, i); + + ath9k_hw_init_interrupt_masks(ah, opmode); + ath9k_hw_init_qos(ah); + + ath9k_hw_init_user_settings(ah); + + ah->ah_opmode = opmode; + + REG_WRITE(ah, AR_STA_ID1, + REG_READ(ah, AR_STA_ID1) | AR_STA_ID1_PRESERVE_SEQNUM); + + ath9k_hw_set_dma(ah); + + REG_WRITE(ah, AR_OBS, 8); + + if (ahp->ah_intrMitigation) { + + REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 500); + REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 2000); + } + + ath9k_hw_init_bb(ah, chan); + + if (!ath9k_hw_init_cal(ah, chan)) + FAIL(-ENODEV); + + rx_chainmask = ahp->ah_rxchainmask; + if ((rx_chainmask == 0x5) || (rx_chainmask == 0x3)) { + REG_WRITE(ah, AR_PHY_RX_CHAINMASK, rx_chainmask); + REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, rx_chainmask); + } + + REG_WRITE(ah, AR_CFG_LED, saveLedState | AR_CFG_SCLK_32KHZ); + + if (AR_SREV_9100(ah)) { + u32 mask; + mask = REG_READ(ah, AR_CFG); + if (mask & (AR_CFG_SWRB | AR_CFG_SWTB | AR_CFG_SWRG)) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s CFG Byte Swap Set 0x%x\n", __func__, + mask); + } else { + mask = + INIT_CONFIG_STATUS | AR_CFG_SWRB | AR_CFG_SWTB; + REG_WRITE(ah, AR_CFG, mask); + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s Setting CFG 0x%x\n", __func__, + REG_READ(ah, AR_CFG)); + } + } else { +#ifdef __BIG_ENDIAN + REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD); +#endif + } + + return true; +bad: + if (status) + *status = ecode; + return false; +#undef FAIL +} + +bool ath9k_hw_phy_disable(struct ath_hal *ah) +{ + return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_WARM); +} + +bool ath9k_hw_disable(struct ath_hal *ah) +{ + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) + return false; + + return ath9k_hw_set_reset_reg(ah, ATH9K_RESET_COLD); +} + +bool +ath9k_hw_calibrate(struct ath_hal *ah, struct ath9k_channel *chan, + u8 rxchainmask, bool longcal, + bool *isCalDone) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct hal_cal_list *currCal = ahp->ah_cal_list_curr; + struct ath9k_channel *ichan = + ath9k_regd_check_channel(ah, chan); + + *isCalDone = true; + + if (ichan == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: invalid channel %u/0x%x; no mapping\n", + __func__, chan->channel, chan->channelFlags); + return false; + } + + if (currCal && + (currCal->calState == CAL_RUNNING || + currCal->calState == CAL_WAITING)) { + ath9k_hw_per_calibration(ah, ichan, rxchainmask, currCal, + isCalDone); + if (*isCalDone) { + ahp->ah_cal_list_curr = currCal = currCal->calNext; + + if (currCal->calState == CAL_WAITING) { + *isCalDone = false; + ath9k_hw_reset_calibration(ah, currCal); + } + } + } + + if (longcal) { + ath9k_hw_getnf(ah, ichan); + ath9k_hw_loadnf(ah, ah->ah_curchan); + ath9k_hw_start_nfcal(ah); + + if ((ichan->channelFlags & CHANNEL_CW_INT) != 0) { + + chan->channelFlags |= CHANNEL_CW_INT; + ichan->channelFlags &= ~CHANNEL_CW_INT; + } + } + + return true; +} + +static void ath9k_hw_iqcal_collect(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + ahp->ah_totalPowerMeasI[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); + ahp->ah_totalPowerMeasQ[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); + ahp->ah_totalIqCorrMeas[i] += + (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n", + ahp->ah_CalSamples, i, ahp->ah_totalPowerMeasI[i], + ahp->ah_totalPowerMeasQ[i], + ahp->ah_totalIqCorrMeas[i]); + } +} + +static void ath9k_hw_adc_gaincal_collect(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + ahp->ah_totalAdcIOddPhase[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); + ahp->ah_totalAdcIEvenPhase[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); + ahp->ah_totalAdcQOddPhase[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); + ahp->ah_totalAdcQEvenPhase[i] += + REG_READ(ah, AR_PHY_CAL_MEAS_3(i)); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%d: Chn %d oddi=0x%08x; eveni=0x%08x; " + "oddq=0x%08x; evenq=0x%08x;\n", + ahp->ah_CalSamples, i, + ahp->ah_totalAdcIOddPhase[i], + ahp->ah_totalAdcIEvenPhase[i], + ahp->ah_totalAdcQOddPhase[i], + ahp->ah_totalAdcQEvenPhase[i]); + } +} + +static void ath9k_hw_adc_dccal_collect(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int i; + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + ahp->ah_totalAdcDcOffsetIOddPhase[i] += + (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); + ahp->ah_totalAdcDcOffsetIEvenPhase[i] += + (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); + ahp->ah_totalAdcDcOffsetQOddPhase[i] += + (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); + ahp->ah_totalAdcDcOffsetQEvenPhase[i] += + (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_3(i)); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%d: Chn %d oddi=0x%08x; eveni=0x%08x; " + "oddq=0x%08x; evenq=0x%08x;\n", + ahp->ah_CalSamples, i, + ahp->ah_totalAdcDcOffsetIOddPhase[i], + ahp->ah_totalAdcDcOffsetIEvenPhase[i], + ahp->ah_totalAdcDcOffsetQOddPhase[i], + ahp->ah_totalAdcDcOffsetQEvenPhase[i]); + } +} + +static void ath9k_hw_iqcalibrate(struct ath_hal *ah, u8 numChains) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 powerMeasQ, powerMeasI, iqCorrMeas; + u32 qCoffDenom, iCoffDenom; + int32_t qCoff, iCoff; + int iqCorrNeg, i; + + for (i = 0; i < numChains; i++) { + powerMeasI = ahp->ah_totalPowerMeasI[i]; + powerMeasQ = ahp->ah_totalPowerMeasQ[i]; + iqCorrMeas = ahp->ah_totalIqCorrMeas[i]; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Starting IQ Cal and Correction for Chain %d\n", + i); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Orignal: Chn %diq_corr_meas = 0x%08x\n", + i, ahp->ah_totalIqCorrMeas[i]); + + iqCorrNeg = 0; + + + if (iqCorrMeas > 0x80000000) { + iqCorrMeas = (0xffffffff - iqCorrMeas) + 1; + iqCorrNeg = 1; + } + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_i = 0x%08x\n", i, powerMeasI); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_q = 0x%08x\n", i, powerMeasQ); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "iqCorrNeg is 0x%08x\n", + iqCorrNeg); + + iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128; + qCoffDenom = powerMeasQ / 64; + + if (powerMeasQ != 0) { + + iCoff = iqCorrMeas / iCoffDenom; + qCoff = powerMeasI / qCoffDenom - 64; + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d iCoff = 0x%08x\n", i, iCoff); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d qCoff = 0x%08x\n", i, qCoff); + + + iCoff = iCoff & 0x3f; + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "New: Chn %d iCoff = 0x%08x\n", i, iCoff); + if (iqCorrNeg == 0x0) + iCoff = 0x40 - iCoff; + + if (qCoff > 15) + qCoff = 15; + else if (qCoff <= -16) + qCoff = 16; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d : iCoff = 0x%x qCoff = 0x%x\n", + i, iCoff, qCoff); + + REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i), + AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, + iCoff); + REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4(i), + AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, + qCoff); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "IQ Cal and Correction done for Chain %d\n", + i); + } + } + + REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4(0), + AR_PHY_TIMING_CTRL4_IQCORR_ENABLE); +} + +static void +ath9k_hw_adc_gaincal_calibrate(struct ath_hal *ah, u8 numChains) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 iOddMeasOffset, iEvenMeasOffset, qOddMeasOffset, + qEvenMeasOffset; + u32 qGainMismatch, iGainMismatch, val, i; + + for (i = 0; i < numChains; i++) { + iOddMeasOffset = ahp->ah_totalAdcIOddPhase[i]; + iEvenMeasOffset = ahp->ah_totalAdcIEvenPhase[i]; + qOddMeasOffset = ahp->ah_totalAdcQOddPhase[i]; + qEvenMeasOffset = ahp->ah_totalAdcQEvenPhase[i]; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Starting ADC Gain Cal for Chain %d\n", i); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_i = 0x%08x\n", i, + iOddMeasOffset); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_i = 0x%08x\n", i, + iEvenMeasOffset); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_q = 0x%08x\n", i, + qOddMeasOffset); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_q = 0x%08x\n", i, + qEvenMeasOffset); + + if (iOddMeasOffset != 0 && qEvenMeasOffset != 0) { + iGainMismatch = + ((iEvenMeasOffset * 32) / + iOddMeasOffset) & 0x3f; + qGainMismatch = + ((qOddMeasOffset * 32) / + qEvenMeasOffset) & 0x3f; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d gain_mismatch_i = 0x%08x\n", i, + iGainMismatch); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d gain_mismatch_q = 0x%08x\n", i, + qGainMismatch); + + val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i)); + val &= 0xfffff000; + val |= (qGainMismatch) | (iGainMismatch << 6); + REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "ADC Gain Cal done for Chain %d\n", i); + } + } + + REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0), + REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) | + AR_PHY_NEW_ADC_GAIN_CORR_ENABLE); +} + +static void +ath9k_hw_adc_dccal_calibrate(struct ath_hal *ah, u8 numChains) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 iOddMeasOffset, iEvenMeasOffset, val, i; + int32_t qOddMeasOffset, qEvenMeasOffset, qDcMismatch, iDcMismatch; + const struct hal_percal_data *calData = + ahp->ah_cal_list_curr->calData; + u32 numSamples = + (1 << (calData->calCountMax + 5)) * calData->calNumSamples; + + for (i = 0; i < numChains; i++) { + iOddMeasOffset = ahp->ah_totalAdcDcOffsetIOddPhase[i]; + iEvenMeasOffset = ahp->ah_totalAdcDcOffsetIEvenPhase[i]; + qOddMeasOffset = ahp->ah_totalAdcDcOffsetQOddPhase[i]; + qEvenMeasOffset = ahp->ah_totalAdcDcOffsetQEvenPhase[i]; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Starting ADC DC Offset Cal for Chain %d\n", i); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_i = %d\n", i, + iOddMeasOffset); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_i = %d\n", i, + iEvenMeasOffset); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_odd_q = %d\n", i, + qOddMeasOffset); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d pwr_meas_even_q = %d\n", i, + qEvenMeasOffset); + + iDcMismatch = (((iEvenMeasOffset - iOddMeasOffset) * 2) / + numSamples) & 0x1ff; + qDcMismatch = (((qOddMeasOffset - qEvenMeasOffset) * 2) / + numSamples) & 0x1ff; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d dc_offset_mismatch_i = 0x%08x\n", i, + iDcMismatch); + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "Chn %d dc_offset_mismatch_q = 0x%08x\n", i, + qDcMismatch); + + val = REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i)); + val &= 0xc0000fff; + val |= (qDcMismatch << 12) | (iDcMismatch << 21); + REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), val); + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "ADC DC Offset Cal done for Chain %d\n", i); + } + + REG_WRITE(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0), + REG_READ(ah, AR_PHY_NEW_ADC_DC_GAIN_CORR(0)) | + AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE); +} + +bool ath9k_hw_set_txpowerlimit(struct ath_hal *ah, u32 limit) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *chan = ah->ah_curchan; + + ah->ah_powerLimit = min(limit, (u32) MAX_RATE_POWER); + + if (ath9k_hw_set_txpower(ah, &ahp->ah_eeprom, chan, + ath9k_regd_get_ctl(ah, chan), + ath9k_regd_get_antenna_allowed(ah, + chan), + chan->maxRegTxPower * 2, + min((u32) MAX_RATE_POWER, + (u32) ah->ah_powerLimit)) != 0) + return false; + + return true; +} + +void +ath9k_hw_get_channel_centers(struct ath_hal *ah, + struct ath9k_channel *chan, + struct chan_centers *centers) +{ + int8_t extoff; + struct ath_hal_5416 *ahp = AH5416(ah); + + if (!IS_CHAN_HT40(chan)) { + centers->ctl_center = centers->ext_center = + centers->synth_center = chan->channel; + return; + } + + if ((chan->chanmode == CHANNEL_A_HT40PLUS) || + (chan->chanmode == CHANNEL_G_HT40PLUS)) { + centers->synth_center = + chan->channel + HT40_CHANNEL_CENTER_SHIFT; + extoff = 1; + } else { + centers->synth_center = + chan->channel - HT40_CHANNEL_CENTER_SHIFT; + extoff = -1; + } + + centers->ctl_center = centers->synth_center - (extoff * + HT40_CHANNEL_CENTER_SHIFT); + centers->ext_center = centers->synth_center + (extoff * + ((ahp-> + ah_extprotspacing + == + ATH9K_HT_EXTPROTSPACING_20) + ? + HT40_CHANNEL_CENTER_SHIFT + : 15)); + +} + +void +ath9k_hw_reset_calvalid(struct ath_hal *ah, struct ath9k_channel *chan, + bool *isCalDone) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *ichan = + ath9k_regd_check_channel(ah, chan); + struct hal_cal_list *currCal = ahp->ah_cal_list_curr; + + *isCalDone = true; + + if (!AR_SREV_9100(ah) && !AR_SREV_9160_10_OR_LATER(ah)) + return; + + if (currCal == NULL) + return; + + if (ichan == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: invalid channel %u/0x%x; no mapping\n", + __func__, chan->channel, chan->channelFlags); + return; + } + + + if (currCal->calState != CAL_DONE) { + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: Calibration state incorrect, %d\n", + __func__, currCal->calState); + return; + } + + + if (!ath9k_hw_iscal_supported(ah, chan, currCal->calData->calType)) + return; + + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, + "%s: Resetting Cal %d state for channel %u/0x%x\n", + __func__, currCal->calData->calType, chan->channel, + chan->channelFlags); + + ichan->CalValid &= ~currCal->calData->calType; + currCal->calState = CAL_WAITING; + + *isCalDone = false; +} + +void ath9k_hw_getmac(struct ath_hal *ah, u8 *mac) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + memcpy(mac, ahp->ah_macaddr, ETH_ALEN); +} + +bool ath9k_hw_setmac(struct ath_hal *ah, const u8 *mac) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + memcpy(ahp->ah_macaddr, mac, ETH_ALEN); + return true; +} + +void ath9k_hw_getbssidmask(struct ath_hal *ah, u8 *mask) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + memcpy(mask, ahp->ah_bssidmask, ETH_ALEN); +} + +bool +ath9k_hw_setbssidmask(struct ath_hal *ah, const u8 *mask) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + memcpy(ahp->ah_bssidmask, mask, ETH_ALEN); + + REG_WRITE(ah, AR_BSSMSKL, get_unaligned_le32(ahp->ah_bssidmask)); + REG_WRITE(ah, AR_BSSMSKU, get_unaligned_le16(ahp->ah_bssidmask + 4)); + + return true; +} + +#ifdef CONFIG_ATH9K_RFKILL +static void ath9k_enable_rfkill(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, + AR_GPIO_INPUT_EN_VAL_RFSILENT_BB); + + REG_CLR_BIT(ah, AR_GPIO_INPUT_MUX2, + AR_GPIO_INPUT_MUX2_RFSILENT); + + ath9k_hw_cfg_gpio_input(ah, ahp->ah_gpioSelect); + REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB); + + if (ahp->ah_gpioBit == ath9k_hw_gpio_get(ah, ahp->ah_gpioSelect)) { + + ath9k_hw_set_gpio_intr(ah, ahp->ah_gpioSelect, + !ahp->ah_gpioBit); + } else { + ath9k_hw_set_gpio_intr(ah, ahp->ah_gpioSelect, + ahp->ah_gpioBit); + } +} +#endif + +void +ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid, + u16 assocId) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + memcpy(ahp->ah_bssid, bssid, ETH_ALEN); + ahp->ah_assocId = assocId; + + REG_WRITE(ah, AR_BSS_ID0, get_unaligned_le32(ahp->ah_bssid)); + REG_WRITE(ah, AR_BSS_ID1, get_unaligned_le16(ahp->ah_bssid + 4) | + ((assocId & 0x3fff) << AR_BSS_ID1_AID_S)); +} + +u64 ath9k_hw_gettsf64(struct ath_hal *ah) +{ + u64 tsf; + + tsf = REG_READ(ah, AR_TSF_U32); + tsf = (tsf << 32) | REG_READ(ah, AR_TSF_L32); + return tsf; +} + +void ath9k_hw_reset_tsf(struct ath_hal *ah) +{ + int count; + + count = 0; + while (REG_READ(ah, AR_SLP32_MODE) & AR_SLP32_TSF_WRITE_STATUS) { + count++; + if (count > 10) { + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "%s: AR_SLP32_TSF_WRITE_STATUS limit exceeded\n", + __func__); + break; + } + udelay(10); + } + REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE); +} + +u32 ath9k_hw_getdefantenna(struct ath_hal *ah) +{ + return REG_READ(ah, AR_DEF_ANTENNA) & 0x7; +} + +void ath9k_hw_setantenna(struct ath_hal *ah, u32 antenna) +{ + REG_WRITE(ah, AR_DEF_ANTENNA, (antenna & 0x7)); +} + +bool +ath9k_hw_setantennaswitch(struct ath_hal *ah, + enum ath9k_ant_setting settings, + struct ath9k_channel *chan, + u8 *tx_chainmask, + u8 *rx_chainmask, + u8 *antenna_cfgd) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + static u8 tx_chainmask_cfg, rx_chainmask_cfg; + + if (AR_SREV_9280(ah)) { + if (!tx_chainmask_cfg) { + + tx_chainmask_cfg = *tx_chainmask; + rx_chainmask_cfg = *rx_chainmask; + } + + switch (settings) { + case ATH9K_ANT_FIXED_A: + *tx_chainmask = ATH9K_ANTENNA0_CHAINMASK; + *rx_chainmask = ATH9K_ANTENNA0_CHAINMASK; + *antenna_cfgd = true; + break; + case ATH9K_ANT_FIXED_B: + if (ah->ah_caps.halTxChainMask > + ATH9K_ANTENNA1_CHAINMASK) { + *tx_chainmask = ATH9K_ANTENNA1_CHAINMASK; + } + *rx_chainmask = ATH9K_ANTENNA1_CHAINMASK; + *antenna_cfgd = true; + break; + case ATH9K_ANT_VARIABLE: + *tx_chainmask = tx_chainmask_cfg; + *rx_chainmask = rx_chainmask_cfg; + *antenna_cfgd = true; + break; + default: + break; + } + } else { + ahp->ah_diversityControl = settings; + } + + return true; +} + +void ath9k_hw_setopmode(struct ath_hal *ah) +{ + ath9k_hw_set_operating_mode(ah, ah->ah_opmode); +} + +bool +ath9k_hw_getcapability(struct ath_hal *ah, enum hal_capability_type type, + u32 capability, u32 *result) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + const struct hal_capabilities *pCap = &ah->ah_caps; + + switch (type) { + case HAL_CAP_CIPHER: + switch (capability) { + case ATH9K_CIPHER_AES_CCM: + case ATH9K_CIPHER_AES_OCB: + case ATH9K_CIPHER_TKIP: + case ATH9K_CIPHER_WEP: + case ATH9K_CIPHER_MIC: + case ATH9K_CIPHER_CLR: + return true; + default: + return false; + } + case HAL_CAP_TKIP_MIC: + switch (capability) { + case 0: + return true; + case 1: + return (ahp->ah_staId1Defaults & + AR_STA_ID1_CRPT_MIC_ENABLE) ? true : + false; + } + case HAL_CAP_TKIP_SPLIT: + return (ahp->ah_miscMode & AR_PCU_MIC_NEW_LOC_ENA) ? + false : true; + case HAL_CAP_WME_TKIPMIC: + return 0; + case HAL_CAP_PHYCOUNTERS: + return ahp->ah_hasHwPhyCounters ? 0 : -ENXIO; + case HAL_CAP_DIVERSITY: + return (REG_READ(ah, AR_PHY_CCK_DETECT) & + AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV) ? + true : false; + case HAL_CAP_PHYDIAG: + return true; + case HAL_CAP_MCAST_KEYSRCH: + switch (capability) { + case 0: + return true; + case 1: + if (REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_ADHOC) { + return false; + } else { + return (ahp->ah_staId1Defaults & + AR_STA_ID1_MCAST_KSRCH) ? true : + false; + } + } + return false; + case HAL_CAP_TSF_ADJUST: + return (ahp->ah_miscMode & AR_PCU_TX_ADD_TSF) ? + true : false; + case HAL_CAP_RFSILENT: + if (capability == 3) + return false; + case HAL_CAP_ANT_CFG_2GHZ: + *result = pCap->halNumAntCfg2GHz; + return true; + case HAL_CAP_ANT_CFG_5GHZ: + *result = pCap->halNumAntCfg5GHz; + return true; + case HAL_CAP_TXPOW: + switch (capability) { + case 0: + return 0; + case 1: + *result = ah->ah_powerLimit; + return 0; + case 2: + *result = ah->ah_maxPowerLevel; + return 0; + case 3: + *result = ah->ah_tpScale; + return 0; + } + return false; + default: + return false; + } +} + +int +ath9k_hw_select_antconfig(struct ath_hal *ah, u32 cfg) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_channel *chan = ah->ah_curchan; + const struct hal_capabilities *pCap = &ah->ah_caps; + u16 ant_config; + u32 halNumAntConfig; + + halNumAntConfig = + IS_CHAN_2GHZ(chan) ? pCap->halNumAntCfg2GHz : pCap-> + halNumAntCfg5GHz; + + if (cfg < halNumAntConfig) { + if (!ath9k_hw_get_eeprom_antenna_cfg(ahp, chan, + cfg, &ant_config)) { + REG_WRITE(ah, AR_PHY_SWITCH_COM, ant_config); + return 0; + } + } + + return -EINVAL; +} + +bool ath9k_hw_intrpend(struct ath_hal *ah) +{ + u32 host_isr; + + if (AR_SREV_9100(ah)) + return true; + + host_isr = REG_READ(ah, AR_INTR_ASYNC_CAUSE); + if ((host_isr & AR_INTR_MAC_IRQ) && (host_isr != AR_INTR_SPURIOUS)) + return true; + + host_isr = REG_READ(ah, AR_INTR_SYNC_CAUSE); + if ((host_isr & AR_INTR_SYNC_DEFAULT) + && (host_isr != AR_INTR_SPURIOUS)) + return true; + + return false; +} + +bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked) +{ + u32 isr = 0; + u32 mask2 = 0; + struct hal_capabilities *pCap = &ah->ah_caps; + u32 sync_cause = 0; + bool fatal_int = false; + + if (!AR_SREV_9100(ah)) { + if (REG_READ(ah, AR_INTR_ASYNC_CAUSE) & AR_INTR_MAC_IRQ) { + if ((REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M) + == AR_RTC_STATUS_ON) { + isr = REG_READ(ah, AR_ISR); + } + } + + sync_cause = + REG_READ(ah, + AR_INTR_SYNC_CAUSE) & AR_INTR_SYNC_DEFAULT; + + *masked = 0; + + if (!isr && !sync_cause) + return false; + } else { + *masked = 0; + isr = REG_READ(ah, AR_ISR); + } + + if (isr) { + struct ath_hal_5416 *ahp = AH5416(ah); + + if (isr & AR_ISR_BCNMISC) { + u32 isr2; + isr2 = REG_READ(ah, AR_ISR_S2); + if (isr2 & AR_ISR_S2_TIM) + mask2 |= ATH9K_INT_TIM; + if (isr2 & AR_ISR_S2_DTIM) + mask2 |= ATH9K_INT_DTIM; + if (isr2 & AR_ISR_S2_DTIMSYNC) + mask2 |= ATH9K_INT_DTIMSYNC; + if (isr2 & (AR_ISR_S2_CABEND)) + mask2 |= ATH9K_INT_CABEND; + if (isr2 & AR_ISR_S2_GTT) + mask2 |= ATH9K_INT_GTT; + if (isr2 & AR_ISR_S2_CST) + mask2 |= ATH9K_INT_CST; + } + + isr = REG_READ(ah, AR_ISR_RAC); + if (isr == 0xffffffff) { + *masked = 0; + return false; + } + + *masked = isr & ATH9K_INT_COMMON; + + if (ahp->ah_intrMitigation) { + + if (isr & (AR_ISR_RXMINTR | AR_ISR_RXINTM)) + *masked |= ATH9K_INT_RX; + } + + if (isr & (AR_ISR_RXOK | AR_ISR_RXERR)) + *masked |= ATH9K_INT_RX; + if (isr & + (AR_ISR_TXOK | AR_ISR_TXDESC | AR_ISR_TXERR | + AR_ISR_TXEOL)) { + u32 s0_s, s1_s; + + *masked |= ATH9K_INT_TX; + + s0_s = REG_READ(ah, AR_ISR_S0_S); + ahp->ah_intrTxqs |= MS(s0_s, AR_ISR_S0_QCU_TXOK); + ahp->ah_intrTxqs |= MS(s0_s, AR_ISR_S0_QCU_TXDESC); + + s1_s = REG_READ(ah, AR_ISR_S1_S); + ahp->ah_intrTxqs |= MS(s1_s, AR_ISR_S1_QCU_TXERR); + ahp->ah_intrTxqs |= MS(s1_s, AR_ISR_S1_QCU_TXEOL); + } + + if (isr & AR_ISR_RXORN) { + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, + "%s: receive FIFO overrun interrupt\n", + __func__); + } + + if (!AR_SREV_9100(ah)) { + if (!pCap->halAutoSleepSupport) { + u32 isr5 = REG_READ(ah, AR_ISR_S5_S); + if (isr5 & AR_ISR_S5_TIM_TIMER) + *masked |= ATH9K_INT_TIM_TIMER; + } + } + + *masked |= mask2; + } + if (AR_SREV_9100(ah)) + return true; + if (sync_cause) { + fatal_int = + (sync_cause & + (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) + ? true : false; + + if (fatal_int) { + if (sync_cause & AR_INTR_SYNC_HOST1_FATAL) { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "%s: received PCI FATAL interrupt\n", + __func__); + } + if (sync_cause & AR_INTR_SYNC_HOST1_PERR) { + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "%s: received PCI PERR interrupt\n", + __func__); + } + } + if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) { + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, + "%s: AR_INTR_SYNC_RADM_CPL_TIMEOUT\n", + __func__); + REG_WRITE(ah, AR_RC, AR_RC_HOSTIF); + REG_WRITE(ah, AR_RC, 0); + *masked |= ATH9K_INT_FATAL; + } + if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) { + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, + "%s: AR_INTR_SYNC_LOCAL_TIMEOUT\n", + __func__); + } + + REG_WRITE(ah, AR_INTR_SYNC_CAUSE_CLR, sync_cause); + (void) REG_READ(ah, AR_INTR_SYNC_CAUSE_CLR); + } + return true; +} + +enum ath9k_int ath9k_hw_intrget(struct ath_hal *ah) +{ + return AH5416(ah)->ah_maskReg; +} + +enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah, enum ath9k_int ints) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 omask = ahp->ah_maskReg; + u32 mask, mask2; + struct hal_capabilities *pCap = &ah->ah_caps; + + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "%s: 0x%x => 0x%x\n", __func__, + omask, ints); + + if (omask & ATH9K_INT_GLOBAL) { + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "%s: disable IER\n", + __func__); + REG_WRITE(ah, AR_IER, AR_IER_DISABLE); + (void) REG_READ(ah, AR_IER); + if (!AR_SREV_9100(ah)) { + REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, 0); + (void) REG_READ(ah, AR_INTR_ASYNC_ENABLE); + + REG_WRITE(ah, AR_INTR_SYNC_ENABLE, 0); + (void) REG_READ(ah, AR_INTR_SYNC_ENABLE); + } + } + + mask = ints & ATH9K_INT_COMMON; + mask2 = 0; + + if (ints & ATH9K_INT_TX) { + if (ahp->ah_txOkInterruptMask) + mask |= AR_IMR_TXOK; + if (ahp->ah_txDescInterruptMask) + mask |= AR_IMR_TXDESC; + if (ahp->ah_txErrInterruptMask) + mask |= AR_IMR_TXERR; + if (ahp->ah_txEolInterruptMask) + mask |= AR_IMR_TXEOL; + } + if (ints & ATH9K_INT_RX) { + mask |= AR_IMR_RXERR; + if (ahp->ah_intrMitigation) + mask |= AR_IMR_RXMINTR | AR_IMR_RXINTM; + else + mask |= AR_IMR_RXOK | AR_IMR_RXDESC; + if (!pCap->halAutoSleepSupport) + mask |= AR_IMR_GENTMR; + } + + if (ints & (ATH9K_INT_BMISC)) { + mask |= AR_IMR_BCNMISC; + if (ints & ATH9K_INT_TIM) + mask2 |= AR_IMR_S2_TIM; + if (ints & ATH9K_INT_DTIM) + mask2 |= AR_IMR_S2_DTIM; + if (ints & ATH9K_INT_DTIMSYNC) + mask2 |= AR_IMR_S2_DTIMSYNC; + if (ints & ATH9K_INT_CABEND) + mask2 |= (AR_IMR_S2_CABEND); + } + + if (ints & (ATH9K_INT_GTT | ATH9K_INT_CST)) { + mask |= AR_IMR_BCNMISC; + if (ints & ATH9K_INT_GTT) + mask2 |= AR_IMR_S2_GTT; + if (ints & ATH9K_INT_CST) + mask2 |= AR_IMR_S2_CST; + } + + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "%s: new IMR 0x%x\n", __func__, + mask); + REG_WRITE(ah, AR_IMR, mask); + mask = REG_READ(ah, AR_IMR_S2) & ~(AR_IMR_S2_TIM | + AR_IMR_S2_DTIM | + AR_IMR_S2_DTIMSYNC | + AR_IMR_S2_CABEND | + AR_IMR_S2_CABTO | + AR_IMR_S2_TSFOOR | + AR_IMR_S2_GTT | AR_IMR_S2_CST); + REG_WRITE(ah, AR_IMR_S2, mask | mask2); + ahp->ah_maskReg = ints; + + if (!pCap->halAutoSleepSupport) { + if (ints & ATH9K_INT_TIM_TIMER) + REG_SET_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER); + else + REG_CLR_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER); + } + + if (ints & ATH9K_INT_GLOBAL) { + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "%s: enable IER\n", + __func__); + REG_WRITE(ah, AR_IER, AR_IER_ENABLE); + if (!AR_SREV_9100(ah)) { + REG_WRITE(ah, AR_INTR_ASYNC_ENABLE, + AR_INTR_MAC_IRQ); + REG_WRITE(ah, AR_INTR_ASYNC_MASK, AR_INTR_MAC_IRQ); + + + REG_WRITE(ah, AR_INTR_SYNC_ENABLE, + AR_INTR_SYNC_DEFAULT); + REG_WRITE(ah, AR_INTR_SYNC_MASK, + AR_INTR_SYNC_DEFAULT); + } + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "AR_IMR 0x%x IER 0x%x\n", + REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER)); + } + + return omask; +} + +void +ath9k_hw_beaconinit(struct ath_hal *ah, + u32 next_beacon, u32 beacon_period) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + int flags = 0; + + ahp->ah_beaconInterval = beacon_period; + + switch (ah->ah_opmode) { + case ATH9K_M_STA: + case ATH9K_M_MONITOR: + REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(next_beacon)); + REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, 0xffff); + REG_WRITE(ah, AR_NEXT_SWBA, 0x7ffff); + flags |= AR_TBTT_TIMER_EN; + break; + case ATH9K_M_IBSS: + REG_SET_BIT(ah, AR_TXCFG, + AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY); + REG_WRITE(ah, AR_NEXT_NDP_TIMER, + TU_TO_USEC(next_beacon + + (ahp->ah_atimWindow ? ahp-> + ah_atimWindow : 1))); + flags |= AR_NDP_TIMER_EN; + case ATH9K_M_HOSTAP: + REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(next_beacon)); + REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, + TU_TO_USEC(next_beacon - + ah->ah_config. + ath_hal_dma_beacon_response_time)); + REG_WRITE(ah, AR_NEXT_SWBA, + TU_TO_USEC(next_beacon - + ah->ah_config. + ath_hal_sw_beacon_response_time)); + flags |= + AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN; + break; + } + + REG_WRITE(ah, AR_BEACON_PERIOD, TU_TO_USEC(beacon_period)); + REG_WRITE(ah, AR_DMA_BEACON_PERIOD, TU_TO_USEC(beacon_period)); + REG_WRITE(ah, AR_SWBA_PERIOD, TU_TO_USEC(beacon_period)); + REG_WRITE(ah, AR_NDP_PERIOD, TU_TO_USEC(beacon_period)); + + beacon_period &= ~ATH9K_BEACON_ENA; + if (beacon_period & ATH9K_BEACON_RESET_TSF) { + beacon_period &= ~ATH9K_BEACON_RESET_TSF; + ath9k_hw_reset_tsf(ah); + } + + REG_SET_BIT(ah, AR_TIMER_MODE, flags); +} + +void +ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah, + const struct ath9k_beacon_state *bs) +{ + u32 nextTbtt, beaconintval, dtimperiod, beacontimeout; + struct hal_capabilities *pCap = &ah->ah_caps; + + REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt)); + + REG_WRITE(ah, AR_BEACON_PERIOD, + TU_TO_USEC(bs->bs_intval & ATH9K_BEACON_PERIOD)); + REG_WRITE(ah, AR_DMA_BEACON_PERIOD, + TU_TO_USEC(bs->bs_intval & ATH9K_BEACON_PERIOD)); + + REG_RMW_FIELD(ah, AR_RSSI_THR, + AR_RSSI_THR_BM_THR, bs->bs_bmissthreshold); + + beaconintval = bs->bs_intval & ATH9K_BEACON_PERIOD; + + if (bs->bs_sleepduration > beaconintval) + beaconintval = bs->bs_sleepduration; + + dtimperiod = bs->bs_dtimperiod; + if (bs->bs_sleepduration > dtimperiod) + dtimperiod = bs->bs_sleepduration; + + if (beaconintval == dtimperiod) + nextTbtt = bs->bs_nextdtim; + else + nextTbtt = bs->bs_nexttbtt; + + DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "%s: next DTIM %d\n", __func__, + bs->bs_nextdtim); + DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "%s: next beacon %d\n", __func__, + nextTbtt); + DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "%s: beacon period %d\n", __func__, + beaconintval); + DPRINTF(ah->ah_sc, ATH_DBG_BEACON, "%s: DTIM period %d\n", __func__, + dtimperiod); + + REG_WRITE(ah, AR_NEXT_DTIM, + TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP)); + REG_WRITE(ah, AR_NEXT_TIM, TU_TO_USEC(nextTbtt - SLEEP_SLOP)); + + REG_WRITE(ah, AR_SLEEP1, + SM((CAB_TIMEOUT_VAL << 3), AR_SLEEP1_CAB_TIMEOUT) + | AR_SLEEP1_ASSUME_DTIM); + + if (pCap->halAutoSleepSupport) + beacontimeout = (BEACON_TIMEOUT_VAL << 3); + else + beacontimeout = MIN_BEACON_TIMEOUT_VAL; + + REG_WRITE(ah, AR_SLEEP2, + SM(beacontimeout, AR_SLEEP2_BEACON_TIMEOUT)); + + REG_WRITE(ah, AR_TIM_PERIOD, TU_TO_USEC(beaconintval)); + REG_WRITE(ah, AR_DTIM_PERIOD, TU_TO_USEC(dtimperiod)); + + REG_SET_BIT(ah, AR_TIMER_MODE, + AR_TBTT_TIMER_EN | AR_TIM_TIMER_EN | + AR_DTIM_TIMER_EN); + +} + +bool ath9k_hw_keyisvalid(struct ath_hal *ah, u16 entry) +{ + if (entry < ah->ah_caps.halKeyCacheSize) { + u32 val = REG_READ(ah, AR_KEYTABLE_MAC1(entry)); + if (val & AR_KEYTABLE_VALID) + return true; + } + return false; +} + +bool ath9k_hw_keyreset(struct ath_hal *ah, u16 entry) +{ + u32 keyType; + + if (entry >= ah->ah_caps.halKeyCacheSize) { + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: entry %u out of range\n", __func__, entry); + return false; + } + keyType = REG_READ(ah, AR_KEYTABLE_TYPE(entry)); + + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), AR_KEYTABLE_TYPE_CLR); + REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), 0); + REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), 0); + + if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) { + u16 micentry = entry + 64; + + REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); + + } + + if (ah->ah_curchan == NULL) + return true; + + return true; +} + +bool +ath9k_hw_keysetmac(struct ath_hal *ah, u16 entry, + const u8 *mac) +{ + u32 macHi, macLo; + + if (entry >= ah->ah_caps.halKeyCacheSize) { + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: entry %u out of range\n", __func__, entry); + return false; + } + + if (mac != NULL) { + macHi = (mac[5] << 8) | mac[4]; + macLo = (mac[3] << 24) | (mac[2] << 16) + | (mac[1] << 8) | mac[0]; + macLo >>= 1; + macLo |= (macHi & 1) << 31; + macHi >>= 1; + } else { + macLo = macHi = 0; + } + REG_WRITE(ah, AR_KEYTABLE_MAC0(entry), macLo); + REG_WRITE(ah, AR_KEYTABLE_MAC1(entry), macHi | AR_KEYTABLE_VALID); + + return true; +} + +bool +ath9k_hw_set_keycache_entry(struct ath_hal *ah, u16 entry, + const struct ath9k_keyval *k, + const u8 *mac, int xorKey) +{ + const struct hal_capabilities *pCap = &ah->ah_caps; + u32 key0, key1, key2, key3, key4; + u32 keyType; + u32 xorMask = xorKey ? + (ATH9K_KEY_XOR << 24 | ATH9K_KEY_XOR << 16 | ATH9K_KEY_XOR << 8 + | ATH9K_KEY_XOR) : 0; + struct ath_hal_5416 *ahp = AH5416(ah); + + if (entry >= pCap->halKeyCacheSize) { + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: entry %u out of range\n", __func__, entry); + return false; + } + switch (k->kv_type) { + case ATH9K_CIPHER_AES_OCB: + keyType = AR_KEYTABLE_TYPE_AES; + break; + case ATH9K_CIPHER_AES_CCM: + if (!pCap->halCipherAesCcmSupport) { + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: AES-CCM not supported by " + "mac rev 0x%x\n", __func__, + ah->ah_macRev); + return false; + } + keyType = AR_KEYTABLE_TYPE_CCM; + break; + case ATH9K_CIPHER_TKIP: + keyType = AR_KEYTABLE_TYPE_TKIP; + if (ATH9K_IS_MIC_ENABLED(ah) + && entry + 64 >= pCap->halKeyCacheSize) { + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: entry %u inappropriate for TKIP\n", + __func__, entry); + return false; + } + break; + case ATH9K_CIPHER_WEP: + if (k->kv_len < 40 / NBBY) { + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: WEP key length %u too small\n", + __func__, k->kv_len); + return false; + } + if (k->kv_len <= 40 / NBBY) + keyType = AR_KEYTABLE_TYPE_40; + else if (k->kv_len <= 104 / NBBY) + keyType = AR_KEYTABLE_TYPE_104; + else + keyType = AR_KEYTABLE_TYPE_128; + break; + case ATH9K_CIPHER_CLR: + keyType = AR_KEYTABLE_TYPE_CLR; + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, + "%s: cipher %u not supported\n", __func__, + k->kv_type); + return false; + } + + key0 = get_unaligned_le32(k->kv_val + 0) ^ xorMask; + key1 = (get_unaligned_le16(k->kv_val + 4) ^ xorMask) & 0xffff; + key2 = get_unaligned_le32(k->kv_val + 6) ^ xorMask; + key3 = (get_unaligned_le16(k->kv_val + 10) ^ xorMask) & 0xffff; + key4 = get_unaligned_le32(k->kv_val + 12) ^ xorMask; + if (k->kv_len <= 104 / NBBY) + key4 &= 0xff; + + if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) { + u16 micentry = entry + 64; + + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), ~key0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), ~key1); + REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); + REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); + REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); + (void) ath9k_hw_keysetmac(ah, entry, mac); + + if (ahp->ah_miscMode & AR_PCU_MIC_NEW_LOC_ENA) { + u32 mic0, mic1, mic2, mic3, mic4; + + mic0 = get_unaligned_le32(k->kv_mic + 0); + mic2 = get_unaligned_le32(k->kv_mic + 4); + mic1 = get_unaligned_le16(k->kv_txmic + 2) & 0xffff; + mic3 = get_unaligned_le16(k->kv_txmic + 0) & 0xffff; + mic4 = get_unaligned_le32(k->kv_txmic + 4); + REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), mic1); + REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), mic3); + REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), mic4); + REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), + AR_KEYTABLE_TYPE_CLR); + + } else { + u32 mic0, mic2; + + mic0 = get_unaligned_le32(k->kv_mic + 0); + mic2 = get_unaligned_le32(k->kv_mic + 4); + REG_WRITE(ah, AR_KEYTABLE_KEY0(micentry), mic0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY2(micentry), mic2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY4(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_TYPE(micentry), + AR_KEYTABLE_TYPE_CLR); + } + REG_WRITE(ah, AR_KEYTABLE_MAC0(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_MAC1(micentry), 0); + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); + } else { + REG_WRITE(ah, AR_KEYTABLE_KEY0(entry), key0); + REG_WRITE(ah, AR_KEYTABLE_KEY1(entry), key1); + REG_WRITE(ah, AR_KEYTABLE_KEY2(entry), key2); + REG_WRITE(ah, AR_KEYTABLE_KEY3(entry), key3); + REG_WRITE(ah, AR_KEYTABLE_KEY4(entry), key4); + REG_WRITE(ah, AR_KEYTABLE_TYPE(entry), keyType); + + (void) ath9k_hw_keysetmac(ah, entry, mac); + } + + if (ah->ah_curchan == NULL) + return true; + + return true; +} + +bool +ath9k_hw_updatetxtriglevel(struct ath_hal *ah, bool bIncTrigLevel) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u32 txcfg, curLevel, newLevel; + enum ath9k_int omask; + + if (ah->ah_txTrigLevel >= MAX_TX_FIFO_THRESHOLD) + return false; + + omask = ath9k_hw_set_interrupts(ah, + ahp->ah_maskReg & ~ATH9K_INT_GLOBAL); + + txcfg = REG_READ(ah, AR_TXCFG); + curLevel = MS(txcfg, AR_FTRIG); + newLevel = curLevel; + if (bIncTrigLevel) { + if (curLevel < MAX_TX_FIFO_THRESHOLD) + newLevel++; + } else if (curLevel > MIN_TX_FIFO_THRESHOLD) + newLevel--; + if (newLevel != curLevel) + REG_WRITE(ah, AR_TXCFG, + (txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG)); + + ath9k_hw_set_interrupts(ah, omask); + + ah->ah_txTrigLevel = newLevel; + + return newLevel != curLevel; +} + +static bool ath9k_hw_set_txq_props(struct ath_hal *ah, + struct ath9k_tx_queue_info *qi, + const struct ath9k_txq_info *qInfo) +{ + u32 cw; + + if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue\n", + __func__); + return false; + } + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: queue %p\n", __func__, qi); + + qi->tqi_ver = qInfo->tqi_ver; + qi->tqi_subtype = qInfo->tqi_subtype; + qi->tqi_qflags = qInfo->tqi_qflags; + qi->tqi_priority = qInfo->tqi_priority; + if (qInfo->tqi_aifs != ATH9K_TXQ_USEDEFAULT) + qi->tqi_aifs = min(qInfo->tqi_aifs, 255U); + else + qi->tqi_aifs = INIT_AIFS; + if (qInfo->tqi_cwmin != ATH9K_TXQ_USEDEFAULT) { + cw = min(qInfo->tqi_cwmin, 1024U); + qi->tqi_cwmin = 1; + while (qi->tqi_cwmin < cw) + qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1; + } else + qi->tqi_cwmin = qInfo->tqi_cwmin; + if (qInfo->tqi_cwmax != ATH9K_TXQ_USEDEFAULT) { + cw = min(qInfo->tqi_cwmax, 1024U); + qi->tqi_cwmax = 1; + while (qi->tqi_cwmax < cw) + qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1; + } else + qi->tqi_cwmax = INIT_CWMAX; + + if (qInfo->tqi_shretry != 0) + qi->tqi_shretry = min((u32) qInfo->tqi_shretry, 15U); + else + qi->tqi_shretry = INIT_SH_RETRY; + if (qInfo->tqi_lgretry != 0) + qi->tqi_lgretry = min((u32) qInfo->tqi_lgretry, 15U); + else + qi->tqi_lgretry = INIT_LG_RETRY; + qi->tqi_cbrPeriod = qInfo->tqi_cbrPeriod; + qi->tqi_cbrOverflowLimit = qInfo->tqi_cbrOverflowLimit; + qi->tqi_burstTime = qInfo->tqi_burstTime; + qi->tqi_readyTime = qInfo->tqi_readyTime; + + switch (qInfo->tqi_subtype) { + case ATH9K_WME_UPSD: + if (qi->tqi_type == ATH9K_TX_QUEUE_DATA) + qi->tqi_intFlags = ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS; + break; + default: + break; + } + return true; +} + +bool ath9k_hw_settxqueueprops(struct ath_hal *ah, int q, + const struct ath9k_txq_info *qInfo) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct hal_capabilities *pCap = &ah->ah_caps; + + if (q >= pCap->halTotalQueues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", + __func__, q); + return false; + } + return ath9k_hw_set_txq_props(ah, &ahp->ah_txq[q], qInfo); +} + +static bool ath9k_hw_get_txq_props(struct ath_hal *ah, + struct ath9k_txq_info *qInfo, + const struct ath9k_tx_queue_info *qi) +{ + if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue\n", + __func__); + return false; + } + + qInfo->tqi_qflags = qi->tqi_qflags; + qInfo->tqi_ver = qi->tqi_ver; + qInfo->tqi_subtype = qi->tqi_subtype; + qInfo->tqi_qflags = qi->tqi_qflags; + qInfo->tqi_priority = qi->tqi_priority; + qInfo->tqi_aifs = qi->tqi_aifs; + qInfo->tqi_cwmin = qi->tqi_cwmin; + qInfo->tqi_cwmax = qi->tqi_cwmax; + qInfo->tqi_shretry = qi->tqi_shretry; + qInfo->tqi_lgretry = qi->tqi_lgretry; + qInfo->tqi_cbrPeriod = qi->tqi_cbrPeriod; + qInfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit; + qInfo->tqi_burstTime = qi->tqi_burstTime; + qInfo->tqi_readyTime = qi->tqi_readyTime; + + return true; +} + +bool +ath9k_hw_gettxqueueprops(struct ath_hal *ah, int q, + struct ath9k_txq_info *qInfo) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct hal_capabilities *pCap = &ah->ah_caps; + + if (q >= pCap->halTotalQueues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", + __func__, q); + return false; + } + return ath9k_hw_get_txq_props(ah, qInfo, &ahp->ah_txq[q]); +} + +int +ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type, + const struct ath9k_txq_info *qInfo) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_tx_queue_info *qi; + struct hal_capabilities *pCap = &ah->ah_caps; + int q; + + switch (type) { + case ATH9K_TX_QUEUE_BEACON: + q = pCap->halTotalQueues - 1; + break; + case ATH9K_TX_QUEUE_CAB: + q = pCap->halTotalQueues - 2; + break; + case ATH9K_TX_QUEUE_PSPOLL: + q = 1; + break; + case ATH9K_TX_QUEUE_UAPSD: + q = pCap->halTotalQueues - 3; + break; + case ATH9K_TX_QUEUE_DATA: + for (q = 0; q < pCap->halTotalQueues; q++) + if (ahp->ah_txq[q].tqi_type == + ATH9K_TX_QUEUE_INACTIVE) + break; + if (q == pCap->halTotalQueues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "%s: no available tx queue\n", __func__); + return -1; + } + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: bad tx queue type %u\n", + __func__, type); + return -1; + } + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: queue %u\n", __func__, q); + + qi = &ahp->ah_txq[q]; + if (qi->tqi_type != ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "%s: tx queue %u already active\n", __func__, q); + return -1; + } + memset(qi, 0, sizeof(struct ath9k_tx_queue_info)); + qi->tqi_type = type; + if (qInfo == NULL) { + qi->tqi_qflags = + TXQ_FLAG_TXOKINT_ENABLE + | TXQ_FLAG_TXERRINT_ENABLE + | TXQ_FLAG_TXDESCINT_ENABLE | TXQ_FLAG_TXURNINT_ENABLE; + qi->tqi_aifs = INIT_AIFS; + qi->tqi_cwmin = ATH9K_TXQ_USEDEFAULT; + qi->tqi_cwmax = INIT_CWMAX; + qi->tqi_shretry = INIT_SH_RETRY; + qi->tqi_lgretry = INIT_LG_RETRY; + qi->tqi_physCompBuf = 0; + } else { + qi->tqi_physCompBuf = qInfo->tqi_compBuf; + (void) ath9k_hw_settxqueueprops(ah, q, qInfo); + } + + return q; +} + +static void +ath9k_hw_set_txq_interrupts(struct ath_hal *ah, + struct ath9k_tx_queue_info *qi) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, + "%s: tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n", + __func__, ahp->ah_txOkInterruptMask, + ahp->ah_txErrInterruptMask, ahp->ah_txDescInterruptMask, + ahp->ah_txEolInterruptMask, ahp->ah_txUrnInterruptMask); + + REG_WRITE(ah, AR_IMR_S0, + SM(ahp->ah_txOkInterruptMask, AR_IMR_S0_QCU_TXOK) + | SM(ahp->ah_txDescInterruptMask, AR_IMR_S0_QCU_TXDESC)); + REG_WRITE(ah, AR_IMR_S1, + SM(ahp->ah_txErrInterruptMask, AR_IMR_S1_QCU_TXERR) + | SM(ahp->ah_txEolInterruptMask, AR_IMR_S1_QCU_TXEOL)); + REG_RMW_FIELD(ah, AR_IMR_S2, + AR_IMR_S2_QCU_TXURN, ahp->ah_txUrnInterruptMask); +} + +bool ath9k_hw_releasetxqueue(struct ath_hal *ah, u32 q) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_tx_queue_info *qi; + + if (q >= pCap->halTotalQueues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", + __func__, q); + return false; + } + qi = &ahp->ah_txq[q]; + if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue %u\n", + __func__, q); + return false; + } + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: release queue %u\n", + __func__, q); + + qi->tqi_type = ATH9K_TX_QUEUE_INACTIVE; + ahp->ah_txOkInterruptMask &= ~(1 << q); + ahp->ah_txErrInterruptMask &= ~(1 << q); + ahp->ah_txDescInterruptMask &= ~(1 << q); + ahp->ah_txEolInterruptMask &= ~(1 << q); + ahp->ah_txUrnInterruptMask &= ~(1 << q); + ath9k_hw_set_txq_interrupts(ah, qi); + + return true; +} + +bool ath9k_hw_resettxqueue(struct ath_hal *ah, u32 q) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_channel *chan = ah->ah_curchan; + struct ath9k_tx_queue_info *qi; + u32 cwMin, chanCwMin, value; + + if (q >= pCap->halTotalQueues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", + __func__, q); + return false; + } + qi = &ahp->ah_txq[q]; + if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue %u\n", + __func__, q); + return true; + } + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: reset queue %u\n", __func__, q); + + if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) { + if (chan && IS_CHAN_B(chan)) + chanCwMin = INIT_CWMIN_11B; + else + chanCwMin = INIT_CWMIN; + + for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1); + } else + cwMin = qi->tqi_cwmin; + + REG_WRITE(ah, AR_DLCL_IFS(q), SM(cwMin, AR_D_LCL_IFS_CWMIN) + | SM(qi->tqi_cwmax, AR_D_LCL_IFS_CWMAX) + | SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS)); + + REG_WRITE(ah, AR_DRETRY_LIMIT(q), + SM(INIT_SSH_RETRY, AR_D_RETRY_LIMIT_STA_SH) + | SM(INIT_SLG_RETRY, AR_D_RETRY_LIMIT_STA_LG) + | SM(qi->tqi_shretry, AR_D_RETRY_LIMIT_FR_SH) + ); + + REG_WRITE(ah, AR_QMISC(q), AR_Q_MISC_DCU_EARLY_TERM_REQ); + REG_WRITE(ah, AR_DMISC(q), + AR_D_MISC_CW_BKOFF_EN | AR_D_MISC_FRAG_WAIT_EN | 0x2); + + if (qi->tqi_cbrPeriod) { + REG_WRITE(ah, AR_QCBRCFG(q), + SM(qi->tqi_cbrPeriod, AR_Q_CBRCFG_INTERVAL) + | SM(qi->tqi_cbrOverflowLimit, + AR_Q_CBRCFG_OVF_THRESH)); + REG_WRITE(ah, AR_QMISC(q), + REG_READ(ah, + AR_QMISC(q)) | AR_Q_MISC_FSP_CBR | (qi-> + tqi_cbrOverflowLimit + ? + AR_Q_MISC_CBR_EXP_CNTR_LIMIT_EN + : + 0)); + } + if (qi->tqi_readyTime && (qi->tqi_type != ATH9K_TX_QUEUE_CAB)) { + REG_WRITE(ah, AR_QRDYTIMECFG(q), + SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_DURATION) | + AR_Q_RDYTIMECFG_EN); + } + + REG_WRITE(ah, AR_DCHNTIME(q), + SM(qi->tqi_burstTime, AR_D_CHNTIME_DUR) | + (qi->tqi_burstTime ? AR_D_CHNTIME_EN : 0)); + + if (qi->tqi_burstTime + && (qi->tqi_qflags & TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)) { + REG_WRITE(ah, AR_QMISC(q), + REG_READ(ah, + AR_QMISC(q)) | + AR_Q_MISC_RDYTIME_EXP_POLICY); + + } + + if (qi->tqi_qflags & TXQ_FLAG_BACKOFF_DISABLE) { + REG_WRITE(ah, AR_DMISC(q), + REG_READ(ah, AR_DMISC(q)) | + AR_D_MISC_POST_FR_BKOFF_DIS); + } + if (qi->tqi_qflags & TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) { + REG_WRITE(ah, AR_DMISC(q), + REG_READ(ah, AR_DMISC(q)) | + AR_D_MISC_FRAG_BKOFF_EN); + } + switch (qi->tqi_type) { + case ATH9K_TX_QUEUE_BEACON: + REG_WRITE(ah, AR_QMISC(q), REG_READ(ah, AR_QMISC(q)) + | AR_Q_MISC_FSP_DBA_GATED + | AR_Q_MISC_BEACON_USE + | AR_Q_MISC_CBR_INCR_DIS1); + + REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q)) + | (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL << + AR_D_MISC_ARB_LOCKOUT_CNTRL_S) + | AR_D_MISC_BEACON_USE + | AR_D_MISC_POST_FR_BKOFF_DIS); + break; + case ATH9K_TX_QUEUE_CAB: + REG_WRITE(ah, AR_QMISC(q), REG_READ(ah, AR_QMISC(q)) + | AR_Q_MISC_FSP_DBA_GATED + | AR_Q_MISC_CBR_INCR_DIS1 + | AR_Q_MISC_CBR_INCR_DIS0); + value = (qi->tqi_readyTime + - (ah->ah_config.ath_hal_sw_beacon_response_time - + ah->ah_config.ath_hal_dma_beacon_response_time) + - + ah->ah_config.ath_hal_additional_swba_backoff) * + 1024; + REG_WRITE(ah, AR_QRDYTIMECFG(q), + value | AR_Q_RDYTIMECFG_EN); + REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q)) + | (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL << + AR_D_MISC_ARB_LOCKOUT_CNTRL_S)); + break; + case ATH9K_TX_QUEUE_PSPOLL: + REG_WRITE(ah, AR_QMISC(q), + REG_READ(ah, + AR_QMISC(q)) | AR_Q_MISC_CBR_INCR_DIS1); + break; + case ATH9K_TX_QUEUE_UAPSD: + REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q)) + | AR_D_MISC_POST_FR_BKOFF_DIS); + break; + default: + break; + } + + if (qi->tqi_intFlags & ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS) { + REG_WRITE(ah, AR_DMISC(q), + REG_READ(ah, AR_DMISC(q)) | + SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL, + AR_D_MISC_ARB_LOCKOUT_CNTRL) | + AR_D_MISC_POST_FR_BKOFF_DIS); + } + + if (qi->tqi_qflags & TXQ_FLAG_TXOKINT_ENABLE) + ahp->ah_txOkInterruptMask |= 1 << q; + else + ahp->ah_txOkInterruptMask &= ~(1 << q); + if (qi->tqi_qflags & TXQ_FLAG_TXERRINT_ENABLE) + ahp->ah_txErrInterruptMask |= 1 << q; + else + ahp->ah_txErrInterruptMask &= ~(1 << q); + if (qi->tqi_qflags & TXQ_FLAG_TXDESCINT_ENABLE) + ahp->ah_txDescInterruptMask |= 1 << q; + else + ahp->ah_txDescInterruptMask &= ~(1 << q); + if (qi->tqi_qflags & TXQ_FLAG_TXEOLINT_ENABLE) + ahp->ah_txEolInterruptMask |= 1 << q; + else + ahp->ah_txEolInterruptMask &= ~(1 << q); + if (qi->tqi_qflags & TXQ_FLAG_TXURNINT_ENABLE) + ahp->ah_txUrnInterruptMask |= 1 << q; + else + ahp->ah_txUrnInterruptMask &= ~(1 << q); + ath9k_hw_set_txq_interrupts(ah, qi); + + return true; +} + +void ath9k_hw_gettxintrtxqs(struct ath_hal *ah, u32 *txqs) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + *txqs &= ahp->ah_intrTxqs; + ahp->ah_intrTxqs &= ~(*txqs); +} + +bool +ath9k_hw_filltxdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 segLen, bool firstSeg, + bool lastSeg, const struct ath_desc *ds0) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + if (firstSeg) { + ads->ds_ctl1 |= segLen | (lastSeg ? 0 : AR_TxMore); + } else if (lastSeg) { + ads->ds_ctl0 = 0; + ads->ds_ctl1 = segLen; + ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2; + ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3; + } else { + ads->ds_ctl0 = 0; + ads->ds_ctl1 = segLen | AR_TxMore; + ads->ds_ctl2 = 0; + ads->ds_ctl3 = 0; + } + ads->ds_txstatus0 = ads->ds_txstatus1 = 0; + ads->ds_txstatus2 = ads->ds_txstatus3 = 0; + ads->ds_txstatus4 = ads->ds_txstatus5 = 0; + ads->ds_txstatus6 = ads->ds_txstatus7 = 0; + ads->ds_txstatus8 = ads->ds_txstatus9 = 0; + return true; +} + +void ath9k_hw_cleartxdesc(struct ath_hal *ah, struct ath_desc *ds) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + ads->ds_txstatus0 = ads->ds_txstatus1 = 0; + ads->ds_txstatus2 = ads->ds_txstatus3 = 0; + ads->ds_txstatus4 = ads->ds_txstatus5 = 0; + ads->ds_txstatus6 = ads->ds_txstatus7 = 0; + ads->ds_txstatus8 = ads->ds_txstatus9 = 0; +} + +int +ath9k_hw_txprocdesc(struct ath_hal *ah, struct ath_desc *ds) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + if ((ads->ds_txstatus9 & AR_TxDone) == 0) + return -EINPROGRESS; + + ds->ds_txstat.ts_seqnum = MS(ads->ds_txstatus9, AR_SeqNum); + ds->ds_txstat.ts_tstamp = ads->AR_SendTimestamp; + ds->ds_txstat.ts_status = 0; + ds->ds_txstat.ts_flags = 0; + + if (ads->ds_txstatus1 & AR_ExcessiveRetries) + ds->ds_txstat.ts_status |= ATH9K_TXERR_XRETRY; + if (ads->ds_txstatus1 & AR_Filtered) + ds->ds_txstat.ts_status |= ATH9K_TXERR_FILT; + if (ads->ds_txstatus1 & AR_FIFOUnderrun) + ds->ds_txstat.ts_status |= ATH9K_TXERR_FIFO; + if (ads->ds_txstatus9 & AR_TxOpExceeded) + ds->ds_txstat.ts_status |= ATH9K_TXERR_XTXOP; + if (ads->ds_txstatus1 & AR_TxTimerExpired) + ds->ds_txstat.ts_status |= ATH9K_TXERR_TIMER_EXPIRED; + + if (ads->ds_txstatus1 & AR_DescCfgErr) + ds->ds_txstat.ts_flags |= ATH9K_TX_DESC_CFG_ERR; + if (ads->ds_txstatus1 & AR_TxDataUnderrun) { + ds->ds_txstat.ts_flags |= ATH9K_TX_DATA_UNDERRUN; + ath9k_hw_updatetxtriglevel(ah, true); + } + if (ads->ds_txstatus1 & AR_TxDelimUnderrun) { + ds->ds_txstat.ts_flags |= ATH9K_TX_DELIM_UNDERRUN; + ath9k_hw_updatetxtriglevel(ah, true); + } + if (ads->ds_txstatus0 & AR_TxBaStatus) { + ds->ds_txstat.ts_flags |= ATH9K_TX_BA; + ds->ds_txstat.ba_low = ads->AR_BaBitmapLow; + ds->ds_txstat.ba_high = ads->AR_BaBitmapHigh; + } + + ds->ds_txstat.ts_rateindex = MS(ads->ds_txstatus9, AR_FinalTxIdx); + switch (ds->ds_txstat.ts_rateindex) { + case 0: + ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate0); + break; + case 1: + ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate1); + break; + case 2: + ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate2); + break; + case 3: + ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate3); + break; + } + + ds->ds_txstat.ts_rssi = MS(ads->ds_txstatus5, AR_TxRSSICombined); + ds->ds_txstat.ts_rssi_ctl0 = MS(ads->ds_txstatus0, AR_TxRSSIAnt00); + ds->ds_txstat.ts_rssi_ctl1 = MS(ads->ds_txstatus0, AR_TxRSSIAnt01); + ds->ds_txstat.ts_rssi_ctl2 = MS(ads->ds_txstatus0, AR_TxRSSIAnt02); + ds->ds_txstat.ts_rssi_ext0 = MS(ads->ds_txstatus5, AR_TxRSSIAnt10); + ds->ds_txstat.ts_rssi_ext1 = MS(ads->ds_txstatus5, AR_TxRSSIAnt11); + ds->ds_txstat.ts_rssi_ext2 = MS(ads->ds_txstatus5, AR_TxRSSIAnt12); + ds->ds_txstat.evm0 = ads->AR_TxEVM0; + ds->ds_txstat.evm1 = ads->AR_TxEVM1; + ds->ds_txstat.evm2 = ads->AR_TxEVM2; + ds->ds_txstat.ts_shortretry = MS(ads->ds_txstatus1, AR_RTSFailCnt); + ds->ds_txstat.ts_longretry = MS(ads->ds_txstatus1, AR_DataFailCnt); + ds->ds_txstat.ts_virtcol = MS(ads->ds_txstatus1, AR_VirtRetryCnt); + ds->ds_txstat.ts_antenna = 1; + + return 0; +} + +void +ath9k_hw_set11n_txdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 pktLen, enum ath9k_pkt_type type, u32 txPower, + u32 keyIx, enum ath9k_key_type keyType, u32 flags) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + struct ath_hal_5416 *ahp = AH5416(ah); + + txPower += ahp->ah_txPowerIndexOffset; + if (txPower > 63) + txPower = 63; + + ads->ds_ctl0 = (pktLen & AR_FrameLen) + | (flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0) + | SM(txPower, AR_XmitPower) + | (flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0) + | (flags & ATH9K_TXDESC_CLRDMASK ? AR_ClrDestMask : 0) + | (flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0) + | (keyIx != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0); + + ads->ds_ctl1 = + (keyIx != ATH9K_TXKEYIX_INVALID ? SM(keyIx, AR_DestIdx) : 0) + | SM(type, AR_FrameType) + | (flags & ATH9K_TXDESC_NOACK ? AR_NoAck : 0) + | (flags & ATH9K_TXDESC_EXT_ONLY ? AR_ExtOnly : 0) + | (flags & ATH9K_TXDESC_EXT_AND_CTL ? AR_ExtAndCtl : 0); + + ads->ds_ctl6 = SM(keyType, AR_EncrType); + + if (AR_SREV_9285(ah)) { + + ads->ds_ctl8 = 0; + ads->ds_ctl9 = 0; + ads->ds_ctl10 = 0; + ads->ds_ctl11 = 0; + } +} + +void +ath9k_hw_set11n_ratescenario(struct ath_hal *ah, struct ath_desc *ds, + struct ath_desc *lastds, + u32 durUpdateEn, u32 rtsctsRate, + u32 rtsctsDuration, + struct ath9k_11n_rate_series series[], + u32 nseries, u32 flags) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + struct ar5416_desc *last_ads = AR5416DESC(lastds); + u32 ds_ctl0; + + (void) nseries; + (void) rtsctsDuration; + + if (flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA)) { + ds_ctl0 = ads->ds_ctl0; + + if (flags & ATH9K_TXDESC_RTSENA) { + ds_ctl0 &= ~AR_CTSEnable; + ds_ctl0 |= AR_RTSEnable; + } else { + ds_ctl0 &= ~AR_RTSEnable; + ds_ctl0 |= AR_CTSEnable; + } + + ads->ds_ctl0 = ds_ctl0; + } else { + ads->ds_ctl0 = + (ads->ds_ctl0 & ~(AR_RTSEnable | AR_CTSEnable)); + } + + ads->ds_ctl2 = set11nTries(series, 0) + | set11nTries(series, 1) + | set11nTries(series, 2) + | set11nTries(series, 3) + | (durUpdateEn ? AR_DurUpdateEna : 0) + | SM(0, AR_BurstDur); + + ads->ds_ctl3 = set11nRate(series, 0) + | set11nRate(series, 1) + | set11nRate(series, 2) + | set11nRate(series, 3); + + ads->ds_ctl4 = set11nPktDurRTSCTS(series, 0) + | set11nPktDurRTSCTS(series, 1); + + ads->ds_ctl5 = set11nPktDurRTSCTS(series, 2) + | set11nPktDurRTSCTS(series, 3); + + ads->ds_ctl7 = set11nRateFlags(series, 0) + | set11nRateFlags(series, 1) + | set11nRateFlags(series, 2) + | set11nRateFlags(series, 3) + | SM(rtsctsRate, AR_RTSCTSRate); + last_ads->ds_ctl2 = ads->ds_ctl2; + last_ads->ds_ctl3 = ads->ds_ctl3; +} + +void +ath9k_hw_set11n_aggr_first(struct ath_hal *ah, struct ath_desc *ds, + u32 aggrLen) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr); + + ads->ds_ctl6 &= ~AR_AggrLen; + ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen); +} + +void +ath9k_hw_set11n_aggr_middle(struct ath_hal *ah, struct ath_desc *ds, + u32 numDelims) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + unsigned int ctl6; + + ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr); + + ctl6 = ads->ds_ctl6; + ctl6 &= ~AR_PadDelim; + ctl6 |= SM(numDelims, AR_PadDelim); + ads->ds_ctl6 = ctl6; +} + +void ath9k_hw_set11n_aggr_last(struct ath_hal *ah, struct ath_desc *ds) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + ads->ds_ctl1 |= AR_IsAggr; + ads->ds_ctl1 &= ~AR_MoreAggr; + ads->ds_ctl6 &= ~AR_PadDelim; +} + +void ath9k_hw_clr11n_aggr(struct ath_hal *ah, struct ath_desc *ds) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + ads->ds_ctl1 &= (~AR_IsAggr & ~AR_MoreAggr); +} + +void +ath9k_hw_set11n_burstduration(struct ath_hal *ah, struct ath_desc *ds, + u32 burstDuration) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + ads->ds_ctl2 &= ~AR_BurstDur; + ads->ds_ctl2 |= SM(burstDuration, AR_BurstDur); +} + +void +ath9k_hw_set11n_virtualmorefrag(struct ath_hal *ah, struct ath_desc *ds, + u32 vmf) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + + if (vmf) + ads->ds_ctl0 |= AR_VirtMoreFrag; + else + ads->ds_ctl0 &= ~AR_VirtMoreFrag; +} + +void ath9k_hw_putrxbuf(struct ath_hal *ah, u32 rxdp) +{ + REG_WRITE(ah, AR_RXDP, rxdp); +} + +void ath9k_hw_rxena(struct ath_hal *ah) +{ + REG_WRITE(ah, AR_CR, AR_CR_RXE); +} + +bool ath9k_hw_setrxabort(struct ath_hal *ah, bool set) +{ + if (set) { + + REG_SET_BIT(ah, AR_DIAG_SW, + (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + + if (!ath9k_hw_wait + (ah, AR_OBS_BUS_1, AR_OBS_BUS_1_RX_STATE, 0)) { + u32 reg; + + REG_CLR_BIT(ah, AR_DIAG_SW, + (AR_DIAG_RX_DIS | + AR_DIAG_RX_ABORT)); + + reg = REG_READ(ah, AR_OBS_BUS_1); + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "%s: rx failed to go idle in 10 ms RXSM=0x%x\n", + __func__, reg); + + return false; + } + } else { + REG_CLR_BIT(ah, AR_DIAG_SW, + (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); + } + + return true; +} + +void +ath9k_hw_setmcastfilter(struct ath_hal *ah, u32 filter0, + u32 filter1) +{ + REG_WRITE(ah, AR_MCAST_FIL0, filter0); + REG_WRITE(ah, AR_MCAST_FIL1, filter1); +} + +bool +ath9k_hw_setuprxdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 size, u32 flags) +{ + struct ar5416_desc *ads = AR5416DESC(ds); + struct hal_capabilities *pCap = &ah->ah_caps; + + ads->ds_ctl1 = size & AR_BufLen; + if (flags & ATH9K_RXDESC_INTREQ) + ads->ds_ctl1 |= AR_RxIntrReq; + + ads->ds_rxstatus8 &= ~AR_RxDone; + if (!pCap->halAutoSleepSupport) + memset(&(ads->u), 0, sizeof(ads->u)); + return true; +} + +int +ath9k_hw_rxprocdesc(struct ath_hal *ah, struct ath_desc *ds, + u32 pa, struct ath_desc *nds, u64 tsf) +{ + struct ar5416_desc ads; + struct ar5416_desc *adsp = AR5416DESC(ds); + + if ((adsp->ds_rxstatus8 & AR_RxDone) == 0) + return -EINPROGRESS; + + ads.u.rx = adsp->u.rx; + + ds->ds_rxstat.rs_status = 0; + ds->ds_rxstat.rs_flags = 0; + + ds->ds_rxstat.rs_datalen = ads.ds_rxstatus1 & AR_DataLen; + ds->ds_rxstat.rs_tstamp = ads.AR_RcvTimestamp; + + ds->ds_rxstat.rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined); + ds->ds_rxstat.rs_rssi_ctl0 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt00); + ds->ds_rxstat.rs_rssi_ctl1 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt01); + ds->ds_rxstat.rs_rssi_ctl2 = MS(ads.ds_rxstatus0, AR_RxRSSIAnt02); + ds->ds_rxstat.rs_rssi_ext0 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt10); + ds->ds_rxstat.rs_rssi_ext1 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt11); + ds->ds_rxstat.rs_rssi_ext2 = MS(ads.ds_rxstatus4, AR_RxRSSIAnt12); + if (ads.ds_rxstatus8 & AR_RxKeyIdxValid) + ds->ds_rxstat.rs_keyix = MS(ads.ds_rxstatus8, AR_KeyIdx); + else + ds->ds_rxstat.rs_keyix = ATH9K_RXKEYIX_INVALID; + + ds->ds_rxstat.rs_rate = RXSTATUS_RATE(ah, (&ads)); + ds->ds_rxstat.rs_more = (ads.ds_rxstatus1 & AR_RxMore) ? 1 : 0; + + ds->ds_rxstat.rs_isaggr = (ads.ds_rxstatus8 & AR_RxAggr) ? 1 : 0; + ds->ds_rxstat.rs_moreaggr = + (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0; + ds->ds_rxstat.rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna); + ds->ds_rxstat.rs_flags = + (ads.ds_rxstatus3 & AR_GI) ? ATH9K_RX_GI : 0; + ds->ds_rxstat.rs_flags |= + (ads.ds_rxstatus3 & AR_2040) ? ATH9K_RX_2040 : 0; + + if (ads.ds_rxstatus8 & AR_PreDelimCRCErr) + ds->ds_rxstat.rs_flags |= ATH9K_RX_DELIM_CRC_PRE; + if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) + ds->ds_rxstat.rs_flags |= ATH9K_RX_DELIM_CRC_POST; + if (ads.ds_rxstatus8 & AR_DecryptBusyErr) + ds->ds_rxstat.rs_flags |= ATH9K_RX_DECRYPT_BUSY; + + if ((ads.ds_rxstatus8 & AR_RxFrameOK) == 0) { + + if (ads.ds_rxstatus8 & AR_CRCErr) + ds->ds_rxstat.rs_status |= ATH9K_RXERR_CRC; + else if (ads.ds_rxstatus8 & AR_PHYErr) { + u32 phyerr; + + ds->ds_rxstat.rs_status |= ATH9K_RXERR_PHY; + phyerr = MS(ads.ds_rxstatus8, AR_PHYErrCode); + ds->ds_rxstat.rs_phyerr = phyerr; + } else if (ads.ds_rxstatus8 & AR_DecryptCRCErr) + ds->ds_rxstat.rs_status |= ATH9K_RXERR_DECRYPT; + else if (ads.ds_rxstatus8 & AR_MichaelErr) + ds->ds_rxstat.rs_status |= ATH9K_RXERR_MIC; + } + + return 0; +} + +static void ath9k_hw_setup_rate_table(struct ath_hal *ah, + struct ath9k_rate_table *rt) +{ + int i; + + if (rt->rateCodeToIndex[0] != 0) + return; + for (i = 0; i < 256; i++) + rt->rateCodeToIndex[i] = (u8) -1; + for (i = 0; i < rt->rateCount; i++) { + u8 code = rt->info[i].rateCode; + u8 cix = rt->info[i].controlRate; + + rt->rateCodeToIndex[code] = i; + rt->rateCodeToIndex[code | rt->info[i].shortPreamble] = i; + + rt->info[i].lpAckDuration = + ath9k_hw_computetxtime(ah, rt, + WLAN_CTRL_FRAME_SIZE, + cix, + false); + rt->info[i].spAckDuration = + ath9k_hw_computetxtime(ah, rt, + WLAN_CTRL_FRAME_SIZE, + cix, + true); + } +} + +const struct ath9k_rate_table *ath9k_hw_getratetable(struct ath_hal *ah, + u32 mode) +{ + struct ath9k_rate_table *rt; + switch (mode) { + case ATH9K_MODE_SEL_11A: + rt = &ar5416_11a_table; + break; + case ATH9K_MODE_SEL_11B: + rt = &ar5416_11b_table; + break; + case ATH9K_MODE_SEL_11G: + rt = &ar5416_11g_table; + break; + case ATH9K_MODE_SEL_11NG_HT20: + case ATH9K_MODE_SEL_11NG_HT40PLUS: + case ATH9K_MODE_SEL_11NG_HT40MINUS: + rt = &ar5416_11ng_table; + break; + case ATH9K_MODE_SEL_11NA_HT20: + case ATH9K_MODE_SEL_11NA_HT40PLUS: + case ATH9K_MODE_SEL_11NA_HT40MINUS: + rt = &ar5416_11na_table; + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, "%s: invalid mode 0x%x\n", + __func__, mode); + return NULL; + } + ath9k_hw_setup_rate_table(ah, rt); + return rt; +} + +static const char *ath9k_hw_devname(u16 devid) +{ + switch (devid) { + case AR5416_DEVID_PCI: + case AR5416_DEVID_PCIE: + return "Atheros 5416"; + case AR9160_DEVID_PCI: + return "Atheros 9160"; + case AR9280_DEVID_PCI: + case AR9280_DEVID_PCIE: + return "Atheros 9280"; + } + return NULL; +} + +const char *ath9k_hw_probe(u16 vendorid, u16 devid) +{ + return vendorid == ATHEROS_VENDOR_ID ? + ath9k_hw_devname(devid) : NULL; +} + +struct ath_hal *ath9k_hw_attach(u16 devid, + struct ath_softc *sc, + void __iomem *mem, + int *error) +{ + struct ath_hal *ah = NULL; + + switch (devid) { + case AR5416_DEVID_PCI: + case AR5416_DEVID_PCIE: + case AR9160_DEVID_PCI: + case AR9280_DEVID_PCI: + case AR9280_DEVID_PCIE: + ah = ath9k_hw_do_attach(devid, sc, mem, error); + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_ANY, + "devid=0x%x not supported.\n", devid); + ah = NULL; + *error = -ENXIO; + break; + } + if (ah != NULL) { + ah->ah_devid = ah->ah_devid; + ah->ah_subvendorid = ah->ah_subvendorid; + ah->ah_macVersion = ah->ah_macVersion; + ah->ah_macRev = ah->ah_macRev; + ah->ah_phyRev = ah->ah_phyRev; + ah->ah_analog5GhzRev = ah->ah_analog5GhzRev; + ah->ah_analog2GhzRev = ah->ah_analog2GhzRev; + } + return ah; +} + +u16 +ath9k_hw_computetxtime(struct ath_hal *ah, + const struct ath9k_rate_table *rates, + u32 frameLen, u16 rateix, + bool shortPreamble) +{ + u32 bitsPerSymbol, numBits, numSymbols, phyTime, txTime; + u32 kbps; + + kbps = rates->info[rateix].rateKbps; + + if (kbps == 0) + return 0; + switch (rates->info[rateix].phy) { + + case PHY_CCK: + phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS; + if (shortPreamble && rates->info[rateix].shortPreamble) + phyTime >>= 1; + numBits = frameLen << 3; + txTime = CCK_SIFS_TIME + phyTime + + ((numBits * 1000) / kbps); + break; + case PHY_OFDM: + if (ah->ah_curchan && IS_CHAN_QUARTER_RATE(ah->ah_curchan)) { + bitsPerSymbol = + (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000; + + numBits = OFDM_PLCP_BITS + (frameLen << 3); + numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol); + txTime = OFDM_SIFS_TIME_QUARTER + + OFDM_PREAMBLE_TIME_QUARTER + + (numSymbols * OFDM_SYMBOL_TIME_QUARTER); + } else if (ah->ah_curchan && + IS_CHAN_HALF_RATE(ah->ah_curchan)) { + bitsPerSymbol = + (kbps * OFDM_SYMBOL_TIME_HALF) / 1000; + + numBits = OFDM_PLCP_BITS + (frameLen << 3); + numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol); + txTime = OFDM_SIFS_TIME_HALF + + OFDM_PREAMBLE_TIME_HALF + + (numSymbols * OFDM_SYMBOL_TIME_HALF); + } else { + bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000; + + numBits = OFDM_PLCP_BITS + (frameLen << 3); + numSymbols = DIV_ROUND_UP(numBits, bitsPerSymbol); + txTime = OFDM_SIFS_TIME + OFDM_PREAMBLE_TIME + + (numSymbols * OFDM_SYMBOL_TIME); + } + break; + + default: + DPRINTF(ah->ah_sc, ATH_DBG_PHY_IO, + "%s: unknown phy %u (rate ix %u)\n", __func__, + rates->info[rateix].phy, rateix); + txTime = 0; + break; + } + return txTime; +} + +u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags) +{ + if (flags & CHANNEL_2GHZ) { + if (freq == 2484) + return 14; + if (freq < 2484) + return (freq - 2407) / 5; + else + return 15 + ((freq - 2512) / 20); + } else if (flags & CHANNEL_5GHZ) { + if (ath9k_regd_is_public_safety_sku(ah) && + IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) { + return ((freq * 10) + + (((freq % 5) == 2) ? 5 : 0) - 49400) / 5; + } else if ((flags & CHANNEL_A) && (freq <= 5000)) { + return (freq - 4000) / 5; + } else { + return (freq - 5000) / 5; + } + } else { + if (freq == 2484) + return 14; + if (freq < 2484) + return (freq - 2407) / 5; + if (freq < 5000) { + if (ath9k_regd_is_public_safety_sku(ah) + && IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) { + return ((freq * 10) + + (((freq % 5) == + 2) ? 5 : 0) - 49400) / 5; + } else if (freq > 4900) { + return (freq - 4000) / 5; + } else { + return 15 + ((freq - 2512) / 20); + } + } + return (freq - 5000) / 5; + } +} + +int16_t +ath9k_hw_getchan_noise(struct ath_hal *ah, struct ath9k_channel *chan) +{ + struct ath9k_channel *ichan; + + ichan = ath9k_regd_check_channel(ah, chan); + if (ichan == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_NF_CAL, + "%s: invalid channel %u/0x%x; no mapping\n", + __func__, chan->channel, chan->channelFlags); + return 0; + } + if (ichan->rawNoiseFloor == 0) { + enum wireless_mode mode = ath9k_hw_chan2wmode(ah, chan); + return NOISE_FLOOR[mode]; + } else + return ichan->rawNoiseFloor; +} + +bool ath9k_hw_set_tsfadjust(struct ath_hal *ah, u32 setting) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (setting) + ahp->ah_miscMode |= AR_PCU_TX_ADD_TSF; + else + ahp->ah_miscMode &= ~AR_PCU_TX_ADD_TSF; + return true; +} + +bool ath9k_hw_phycounters(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + return ahp->ah_hasHwPhyCounters ? true : false; +} + +u32 ath9k_hw_gettxbuf(struct ath_hal *ah, u32 q) +{ + return REG_READ(ah, AR_QTXDP(q)); +} + +bool ath9k_hw_puttxbuf(struct ath_hal *ah, u32 q, + u32 txdp) +{ + REG_WRITE(ah, AR_QTXDP(q), txdp); + + return true; +} + +bool ath9k_hw_txstart(struct ath_hal *ah, u32 q) +{ + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: queue %u\n", __func__, q); + + REG_WRITE(ah, AR_Q_TXE, 1 << q); + + return true; +} + +u32 ath9k_hw_numtxpending(struct ath_hal *ah, u32 q) +{ + u32 npend; + + npend = REG_READ(ah, AR_QSTS(q)) & AR_Q_STS_PEND_FR_CNT; + if (npend == 0) { + + if (REG_READ(ah, AR_Q_TXE) & (1 << q)) + npend = 1; + } + return npend; +} + +bool ath9k_hw_stoptxdma(struct ath_hal *ah, u32 q) +{ + u32 wait; + + REG_WRITE(ah, AR_Q_TXD, 1 << q); + + for (wait = 1000; wait != 0; wait--) { + if (ath9k_hw_numtxpending(ah, q) == 0) + break; + udelay(100); + } + + if (ath9k_hw_numtxpending(ah, q)) { + u32 tsfLow, j; + + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "%s: Num of pending TX Frames %d on Q %d\n", + __func__, ath9k_hw_numtxpending(ah, q), q); + + for (j = 0; j < 2; j++) { + tsfLow = REG_READ(ah, AR_TSF_L32); + REG_WRITE(ah, AR_QUIET2, + SM(10, AR_QUIET2_QUIET_DUR)); + REG_WRITE(ah, AR_QUIET_PERIOD, 100); + REG_WRITE(ah, AR_NEXT_QUIET_TIMER, tsfLow >> 10); + REG_SET_BIT(ah, AR_TIMER_MODE, + AR_QUIET_TIMER_EN); + + if ((REG_READ(ah, AR_TSF_L32) >> 10) == + (tsfLow >> 10)) { + break; + } + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, + "%s: TSF have moved while trying to set " + "quiet time TSF: 0x%08x\n", + __func__, tsfLow); + } + + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); + + udelay(200); + REG_CLR_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN); + + wait = 1000; + + while (ath9k_hw_numtxpending(ah, q)) { + if ((--wait) == 0) { + DPRINTF(ah->ah_sc, ATH_DBG_XMIT, + "%s: Failed to stop Tx DMA in 100 " + "msec after killing last frame\n", + __func__); + break; + } + udelay(100); + } + + REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); + } + + REG_WRITE(ah, AR_Q_TXD, 0); + return wait != 0; +} diff --git a/drivers/net/wireless/ath9k/hw.h b/drivers/net/wireless/ath9k/hw.h new file mode 100644 index 000000000000..ae680f21ba7e --- /dev/null +++ b/drivers/net/wireless/ath9k/hw.h @@ -0,0 +1,969 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef HW_H +#define HW_H + +#include +#include + +struct ar5416_desc { + u32 ds_link; + u32 ds_data; + u32 ds_ctl0; + u32 ds_ctl1; + union { + struct { + u32 ctl2; + u32 ctl3; + u32 ctl4; + u32 ctl5; + u32 ctl6; + u32 ctl7; + u32 ctl8; + u32 ctl9; + u32 ctl10; + u32 ctl11; + u32 status0; + u32 status1; + u32 status2; + u32 status3; + u32 status4; + u32 status5; + u32 status6; + u32 status7; + u32 status8; + u32 status9; + } tx; + struct { + u32 status0; + u32 status1; + u32 status2; + u32 status3; + u32 status4; + u32 status5; + u32 status6; + u32 status7; + u32 status8; + } rx; + } u; +} __packed; + +#define AR5416DESC(_ds) ((struct ar5416_desc *)(_ds)) +#define AR5416DESC_CONST(_ds) ((const struct ar5416_desc *)(_ds)) + +#define ds_ctl2 u.tx.ctl2 +#define ds_ctl3 u.tx.ctl3 +#define ds_ctl4 u.tx.ctl4 +#define ds_ctl5 u.tx.ctl5 +#define ds_ctl6 u.tx.ctl6 +#define ds_ctl7 u.tx.ctl7 +#define ds_ctl8 u.tx.ctl8 +#define ds_ctl9 u.tx.ctl9 +#define ds_ctl10 u.tx.ctl10 +#define ds_ctl11 u.tx.ctl11 + +#define ds_txstatus0 u.tx.status0 +#define ds_txstatus1 u.tx.status1 +#define ds_txstatus2 u.tx.status2 +#define ds_txstatus3 u.tx.status3 +#define ds_txstatus4 u.tx.status4 +#define ds_txstatus5 u.tx.status5 +#define ds_txstatus6 u.tx.status6 +#define ds_txstatus7 u.tx.status7 +#define ds_txstatus8 u.tx.status8 +#define ds_txstatus9 u.tx.status9 + +#define ds_rxstatus0 u.rx.status0 +#define ds_rxstatus1 u.rx.status1 +#define ds_rxstatus2 u.rx.status2 +#define ds_rxstatus3 u.rx.status3 +#define ds_rxstatus4 u.rx.status4 +#define ds_rxstatus5 u.rx.status5 +#define ds_rxstatus6 u.rx.status6 +#define ds_rxstatus7 u.rx.status7 +#define ds_rxstatus8 u.rx.status8 + +#define AR_FrameLen 0x00000fff +#define AR_VirtMoreFrag 0x00001000 +#define AR_TxCtlRsvd00 0x0000e000 +#define AR_XmitPower 0x003f0000 +#define AR_XmitPower_S 16 +#define AR_RTSEnable 0x00400000 +#define AR_VEOL 0x00800000 +#define AR_ClrDestMask 0x01000000 +#define AR_TxCtlRsvd01 0x1e000000 +#define AR_TxIntrReq 0x20000000 +#define AR_DestIdxValid 0x40000000 +#define AR_CTSEnable 0x80000000 + +#define AR_BufLen 0x00000fff +#define AR_TxMore 0x00001000 +#define AR_DestIdx 0x000fe000 +#define AR_DestIdx_S 13 +#define AR_FrameType 0x00f00000 +#define AR_FrameType_S 20 +#define AR_NoAck 0x01000000 +#define AR_InsertTS 0x02000000 +#define AR_CorruptFCS 0x04000000 +#define AR_ExtOnly 0x08000000 +#define AR_ExtAndCtl 0x10000000 +#define AR_MoreAggr 0x20000000 +#define AR_IsAggr 0x40000000 + +#define AR_BurstDur 0x00007fff +#define AR_BurstDur_S 0 +#define AR_DurUpdateEna 0x00008000 +#define AR_XmitDataTries0 0x000f0000 +#define AR_XmitDataTries0_S 16 +#define AR_XmitDataTries1 0x00f00000 +#define AR_XmitDataTries1_S 20 +#define AR_XmitDataTries2 0x0f000000 +#define AR_XmitDataTries2_S 24 +#define AR_XmitDataTries3 0xf0000000 +#define AR_XmitDataTries3_S 28 + +#define AR_XmitRate0 0x000000ff +#define AR_XmitRate0_S 0 +#define AR_XmitRate1 0x0000ff00 +#define AR_XmitRate1_S 8 +#define AR_XmitRate2 0x00ff0000 +#define AR_XmitRate2_S 16 +#define AR_XmitRate3 0xff000000 +#define AR_XmitRate3_S 24 + +#define AR_PacketDur0 0x00007fff +#define AR_PacketDur0_S 0 +#define AR_RTSCTSQual0 0x00008000 +#define AR_PacketDur1 0x7fff0000 +#define AR_PacketDur1_S 16 +#define AR_RTSCTSQual1 0x80000000 + +#define AR_PacketDur2 0x00007fff +#define AR_PacketDur2_S 0 +#define AR_RTSCTSQual2 0x00008000 +#define AR_PacketDur3 0x7fff0000 +#define AR_PacketDur3_S 16 +#define AR_RTSCTSQual3 0x80000000 + +#define AR_AggrLen 0x0000ffff +#define AR_AggrLen_S 0 +#define AR_TxCtlRsvd60 0x00030000 +#define AR_PadDelim 0x03fc0000 +#define AR_PadDelim_S 18 +#define AR_EncrType 0x0c000000 +#define AR_EncrType_S 26 +#define AR_TxCtlRsvd61 0xf0000000 + +#define AR_2040_0 0x00000001 +#define AR_GI0 0x00000002 +#define AR_ChainSel0 0x0000001c +#define AR_ChainSel0_S 2 +#define AR_2040_1 0x00000020 +#define AR_GI1 0x00000040 +#define AR_ChainSel1 0x00000380 +#define AR_ChainSel1_S 7 +#define AR_2040_2 0x00000400 +#define AR_GI2 0x00000800 +#define AR_ChainSel2 0x00007000 +#define AR_ChainSel2_S 12 +#define AR_2040_3 0x00008000 +#define AR_GI3 0x00010000 +#define AR_ChainSel3 0x000e0000 +#define AR_ChainSel3_S 17 +#define AR_RTSCTSRate 0x0ff00000 +#define AR_RTSCTSRate_S 20 +#define AR_TxCtlRsvd70 0xf0000000 + +#define AR_TxRSSIAnt00 0x000000ff +#define AR_TxRSSIAnt00_S 0 +#define AR_TxRSSIAnt01 0x0000ff00 +#define AR_TxRSSIAnt01_S 8 +#define AR_TxRSSIAnt02 0x00ff0000 +#define AR_TxRSSIAnt02_S 16 +#define AR_TxStatusRsvd00 0x3f000000 +#define AR_TxBaStatus 0x40000000 +#define AR_TxStatusRsvd01 0x80000000 + +#define AR_FrmXmitOK 0x00000001 +#define AR_ExcessiveRetries 0x00000002 +#define AR_FIFOUnderrun 0x00000004 +#define AR_Filtered 0x00000008 +#define AR_RTSFailCnt 0x000000f0 +#define AR_RTSFailCnt_S 4 +#define AR_DataFailCnt 0x00000f00 +#define AR_DataFailCnt_S 8 +#define AR_VirtRetryCnt 0x0000f000 +#define AR_VirtRetryCnt_S 12 +#define AR_TxDelimUnderrun 0x00010000 +#define AR_TxDataUnderrun 0x00020000 +#define AR_DescCfgErr 0x00040000 +#define AR_TxTimerExpired 0x00080000 +#define AR_TxStatusRsvd10 0xfff00000 + +#define AR_SendTimestamp ds_txstatus2 +#define AR_BaBitmapLow ds_txstatus3 +#define AR_BaBitmapHigh ds_txstatus4 + +#define AR_TxRSSIAnt10 0x000000ff +#define AR_TxRSSIAnt10_S 0 +#define AR_TxRSSIAnt11 0x0000ff00 +#define AR_TxRSSIAnt11_S 8 +#define AR_TxRSSIAnt12 0x00ff0000 +#define AR_TxRSSIAnt12_S 16 +#define AR_TxRSSICombined 0xff000000 +#define AR_TxRSSICombined_S 24 + +#define AR_TxEVM0 ds_txstatus5 +#define AR_TxEVM1 ds_txstatus6 +#define AR_TxEVM2 ds_txstatus7 + +#define AR_TxDone 0x00000001 +#define AR_SeqNum 0x00001ffe +#define AR_SeqNum_S 1 +#define AR_TxStatusRsvd80 0x0001e000 +#define AR_TxOpExceeded 0x00020000 +#define AR_TxStatusRsvd81 0x001c0000 +#define AR_FinalTxIdx 0x00600000 +#define AR_FinalTxIdx_S 21 +#define AR_TxStatusRsvd82 0x01800000 +#define AR_PowerMgmt 0x02000000 +#define AR_TxStatusRsvd83 0xfc000000 + +#define AR_RxCTLRsvd00 0xffffffff + +#define AR_BufLen 0x00000fff +#define AR_RxCtlRsvd00 0x00001000 +#define AR_RxIntrReq 0x00002000 +#define AR_RxCtlRsvd01 0xffffc000 + +#define AR_RxRSSIAnt00 0x000000ff +#define AR_RxRSSIAnt00_S 0 +#define AR_RxRSSIAnt01 0x0000ff00 +#define AR_RxRSSIAnt01_S 8 +#define AR_RxRSSIAnt02 0x00ff0000 +#define AR_RxRSSIAnt02_S 16 +#define AR_RxRate 0xff000000 +#define AR_RxRate_S 24 +#define AR_RxStatusRsvd00 0xff000000 + +#define AR_DataLen 0x00000fff +#define AR_RxMore 0x00001000 +#define AR_NumDelim 0x003fc000 +#define AR_NumDelim_S 14 +#define AR_RxStatusRsvd10 0xff800000 + +#define AR_RcvTimestamp ds_rxstatus2 + +#define AR_GI 0x00000001 +#define AR_2040 0x00000002 +#define AR_Parallel40 0x00000004 +#define AR_Parallel40_S 2 +#define AR_RxStatusRsvd30 0x000000f8 +#define AR_RxAntenna 0xffffff00 +#define AR_RxAntenna_S 8 + +#define AR_RxRSSIAnt10 0x000000ff +#define AR_RxRSSIAnt10_S 0 +#define AR_RxRSSIAnt11 0x0000ff00 +#define AR_RxRSSIAnt11_S 8 +#define AR_RxRSSIAnt12 0x00ff0000 +#define AR_RxRSSIAnt12_S 16 +#define AR_RxRSSICombined 0xff000000 +#define AR_RxRSSICombined_S 24 + +#define AR_RxEVM0 ds_rxstatus4 +#define AR_RxEVM1 ds_rxstatus5 +#define AR_RxEVM2 ds_rxstatus6 + +#define AR_RxDone 0x00000001 +#define AR_RxFrameOK 0x00000002 +#define AR_CRCErr 0x00000004 +#define AR_DecryptCRCErr 0x00000008 +#define AR_PHYErr 0x00000010 +#define AR_MichaelErr 0x00000020 +#define AR_PreDelimCRCErr 0x00000040 +#define AR_RxStatusRsvd70 0x00000080 +#define AR_RxKeyIdxValid 0x00000100 +#define AR_KeyIdx 0x0000fe00 +#define AR_KeyIdx_S 9 +#define AR_PHYErrCode 0x0000ff00 +#define AR_PHYErrCode_S 8 +#define AR_RxMoreAggr 0x00010000 +#define AR_RxAggr 0x00020000 +#define AR_PostDelimCRCErr 0x00040000 +#define AR_RxStatusRsvd71 0x3ff80000 +#define AR_DecryptBusyErr 0x40000000 +#define AR_KeyMiss 0x80000000 + +#define AR5416_MAGIC 0x19641014 + +#define RXSTATUS_RATE(ah, ads) (AR_SREV_5416_V20_OR_LATER(ah) ? \ + MS(ads->ds_rxstatus0, AR_RxRate) : \ + (ads->ds_rxstatus3 >> 2) & 0xFF) +#define RXSTATUS_DUPLICATE(ah, ads) (AR_SREV_5416_V20_OR_LATER(ah) ? \ + MS(ads->ds_rxstatus3, AR_Parallel40) : \ + (ads->ds_rxstatus3 >> 10) & 0x1) + +#define set11nTries(_series, _index) \ + (SM((_series)[_index].Tries, AR_XmitDataTries##_index)) + +#define set11nRate(_series, _index) \ + (SM((_series)[_index].Rate, AR_XmitRate##_index)) + +#define set11nPktDurRTSCTS(_series, _index) \ + (SM((_series)[_index].PktDuration, AR_PacketDur##_index) | \ + ((_series)[_index].RateFlags & ATH9K_RATESERIES_RTS_CTS ? \ + AR_RTSCTSQual##_index : 0)) + +#define set11nRateFlags(_series, _index) \ + (((_series)[_index].RateFlags & ATH9K_RATESERIES_2040 ? \ + AR_2040_##_index : 0) \ + |((_series)[_index].RateFlags & ATH9K_RATESERIES_HALFGI ? \ + AR_GI##_index : 0) \ + |SM((_series)[_index].ChSel, AR_ChainSel##_index)) + +#define AR_SREV_9100(ah) ((ah->ah_macVersion) == AR_SREV_VERSION_9100) + +#define INIT_CONFIG_STATUS 0x00000000 +#define INIT_RSSI_THR 0x00000700 +#define INIT_BCON_CNTRL_REG 0x00000000 + +#define MIN_TX_FIFO_THRESHOLD 0x1 +#define MAX_TX_FIFO_THRESHOLD ((4096 / 64) - 1) +#define INIT_TX_FIFO_THRESHOLD MIN_TX_FIFO_THRESHOLD + +#define NUM_CORNER_FIX_BITS_2133 7 +#define CCK_OFDM_GAIN_DELTA 15 + +struct ar5416AniState { + struct ath9k_channel c; + u8 noiseImmunityLevel; + u8 spurImmunityLevel; + u8 firstepLevel; + u8 ofdmWeakSigDetectOff; + u8 cckWeakSigThreshold; + u32 listenTime; + u32 ofdmTrigHigh; + u32 ofdmTrigLow; + int32_t cckTrigHigh; + int32_t cckTrigLow; + int32_t rssiThrLow; + int32_t rssiThrHigh; + u32 noiseFloor; + u32 txFrameCount; + u32 rxFrameCount; + u32 cycleCount; + u32 ofdmPhyErrCount; + u32 cckPhyErrCount; + u32 ofdmPhyErrBase; + u32 cckPhyErrBase; + int16_t pktRssi[2]; + int16_t ofdmErrRssi[2]; + int16_t cckErrRssi[2]; +}; + +#define HAL_PROCESS_ANI 0x00000001 +#define HAL_RADAR_EN 0x80000000 +#define HAL_AR_EN 0x40000000 + +#define DO_ANI(ah) \ + ((AH5416(ah)->ah_procPhyErr & HAL_PROCESS_ANI)) + +struct ar5416Stats { + u32 ast_ani_niup; + u32 ast_ani_nidown; + u32 ast_ani_spurup; + u32 ast_ani_spurdown; + u32 ast_ani_ofdmon; + u32 ast_ani_ofdmoff; + u32 ast_ani_cckhigh; + u32 ast_ani_ccklow; + u32 ast_ani_stepup; + u32 ast_ani_stepdown; + u32 ast_ani_ofdmerrs; + u32 ast_ani_cckerrs; + u32 ast_ani_reset; + u32 ast_ani_lzero; + u32 ast_ani_lneg; + struct ath9k_mib_stats ast_mibstats; + struct ath9k_node_stats ast_nodestats; +}; + +#define AR5416_OPFLAGS_11A 0x01 +#define AR5416_OPFLAGS_11G 0x02 +#define AR5416_OPFLAGS_N_5G_HT40 0x04 +#define AR5416_OPFLAGS_N_2G_HT40 0x08 +#define AR5416_OPFLAGS_N_5G_HT20 0x10 +#define AR5416_OPFLAGS_N_2G_HT20 0x20 + +#define EEP_RFSILENT_ENABLED 0x0001 +#define EEP_RFSILENT_ENABLED_S 0 +#define EEP_RFSILENT_POLARITY 0x0002 +#define EEP_RFSILENT_POLARITY_S 1 +#define EEP_RFSILENT_GPIO_SEL 0x001c +#define EEP_RFSILENT_GPIO_SEL_S 2 + +#define AR5416_EEP_NO_BACK_VER 0x1 +#define AR5416_EEP_VER 0xE +#define AR5416_EEP_VER_MINOR_MASK 0x0FFF +#define AR5416_EEP_MINOR_VER_2 0x2 +#define AR5416_EEP_MINOR_VER_3 0x3 +#define AR5416_EEP_MINOR_VER_7 0x7 +#define AR5416_EEP_MINOR_VER_9 0x9 + +#define AR5416_EEP_START_LOC 256 +#define AR5416_NUM_5G_CAL_PIERS 8 +#define AR5416_NUM_2G_CAL_PIERS 4 +#define AR5416_NUM_5G_20_TARGET_POWERS 8 +#define AR5416_NUM_5G_40_TARGET_POWERS 8 +#define AR5416_NUM_2G_CCK_TARGET_POWERS 3 +#define AR5416_NUM_2G_20_TARGET_POWERS 4 +#define AR5416_NUM_2G_40_TARGET_POWERS 4 +#define AR5416_NUM_CTLS 24 +#define AR5416_NUM_BAND_EDGES 8 +#define AR5416_NUM_PD_GAINS 4 +#define AR5416_PD_GAINS_IN_MASK 4 +#define AR5416_PD_GAIN_ICEPTS 5 +#define AR5416_EEPROM_MODAL_SPURS 5 +#define AR5416_MAX_RATE_POWER 63 +#define AR5416_NUM_PDADC_VALUES 128 +#define AR5416_NUM_RATES 16 +#define AR5416_BCHAN_UNUSED 0xFF +#define AR5416_MAX_PWR_RANGE_IN_HALF_DB 64 +#define AR5416_EEPMISC_BIG_ENDIAN 0x01 +#define AR5416_MAX_CHAINS 3 +#define AR5416_ANT_16S 25 + +#define AR5416_NUM_ANT_CHAIN_FIELDS 7 +#define AR5416_NUM_ANT_COMMON_FIELDS 4 +#define AR5416_SIZE_ANT_CHAIN_FIELD 3 +#define AR5416_SIZE_ANT_COMMON_FIELD 4 +#define AR5416_ANT_CHAIN_MASK 0x7 +#define AR5416_ANT_COMMON_MASK 0xf +#define AR5416_CHAIN_0_IDX 0 +#define AR5416_CHAIN_1_IDX 1 +#define AR5416_CHAIN_2_IDX 2 + +#define AR5416_PWR_TABLE_OFFSET -5 +#define AR5416_LEGACY_CHAINMASK 1 + +enum eeprom_param { + EEP_NFTHRESH_5, + EEP_NFTHRESH_2, + EEP_MAC_MSW, + EEP_MAC_MID, + EEP_MAC_LSW, + EEP_REG_0, + EEP_REG_1, + EEP_OP_CAP, + EEP_OP_MODE, + EEP_RF_SILENT, + EEP_OB_5, + EEP_DB_5, + EEP_OB_2, + EEP_DB_2, + EEP_MINOR_REV, + EEP_TX_MASK, + EEP_RX_MASK, +}; + +enum ar5416_rates { + rate6mb, rate9mb, rate12mb, rate18mb, + rate24mb, rate36mb, rate48mb, rate54mb, + rate1l, rate2l, rate2s, rate5_5l, + rate5_5s, rate11l, rate11s, rateXr, + rateHt20_0, rateHt20_1, rateHt20_2, rateHt20_3, + rateHt20_4, rateHt20_5, rateHt20_6, rateHt20_7, + rateHt40_0, rateHt40_1, rateHt40_2, rateHt40_3, + rateHt40_4, rateHt40_5, rateHt40_6, rateHt40_7, + rateDupCck, rateDupOfdm, rateExtCck, rateExtOfdm, + Ar5416RateSize +}; + +struct base_eep_header { + u16 length; + u16 checksum; + u16 version; + u8 opCapFlags; + u8 eepMisc; + u16 regDmn[2]; + u8 macAddr[6]; + u8 rxMask; + u8 txMask; + u16 rfSilent; + u16 blueToothOptions; + u16 deviceCap; + u32 binBuildNumber; + u8 deviceType; + u8 pwdclkind; + u8 futureBase[32]; +} __packed; + +struct spur_chan { + u16 spurChan; + u8 spurRangeLow; + u8 spurRangeHigh; +} __packed; + +struct modal_eep_header { + u32 antCtrlChain[AR5416_MAX_CHAINS]; + u32 antCtrlCommon; + u8 antennaGainCh[AR5416_MAX_CHAINS]; + u8 switchSettling; + u8 txRxAttenCh[AR5416_MAX_CHAINS]; + u8 rxTxMarginCh[AR5416_MAX_CHAINS]; + u8 adcDesiredSize; + u8 pgaDesiredSize; + u8 xlnaGainCh[AR5416_MAX_CHAINS]; + u8 txEndToXpaOff; + u8 txEndToRxOn; + u8 txFrameToXpaOn; + u8 thresh62; + u8 noiseFloorThreshCh[AR5416_MAX_CHAINS]; + u8 xpdGain; + u8 xpd; + u8 iqCalICh[AR5416_MAX_CHAINS]; + u8 iqCalQCh[AR5416_MAX_CHAINS]; + u8 pdGainOverlap; + u8 ob; + u8 db; + u8 xpaBiasLvl; + u8 pwrDecreaseFor2Chain; + u8 pwrDecreaseFor3Chain; + u8 txFrameToDataStart; + u8 txFrameToPaOn; + u8 ht40PowerIncForPdadc; + u8 bswAtten[AR5416_MAX_CHAINS]; + u8 bswMargin[AR5416_MAX_CHAINS]; + u8 swSettleHt40; + u8 xatten2Db[AR5416_MAX_CHAINS]; + u8 xatten2Margin[AR5416_MAX_CHAINS]; + u8 ob_ch1; + u8 db_ch1; + u8 useAnt1:1, + force_xpaon:1, + local_bias:1, + femBandSelectUsed:1, xlnabufin:1, xlnaisel:2, xlnabufmode:1; + u8 futureModalar9280; + u16 xpaBiasLvlFreq[3]; + u8 futureModal[6]; + + struct spur_chan spurChans[AR5416_EEPROM_MODAL_SPURS]; +} __packed; + +struct cal_data_per_freq { + u8 pwrPdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; + u8 vpdPdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; +} __packed; + +struct cal_target_power_leg { + u8 bChannel; + u8 tPow2x[4]; +} __packed; + +struct cal_target_power_ht { + u8 bChannel; + u8 tPow2x[8]; +} __packed; + +#ifdef __BIG_ENDIAN_BITFIELD +struct cal_ctl_edges { + u8 bChannel; + u8 flag:2, tPower:6; +} __packed; +#else +struct cal_ctl_edges { + u8 bChannel; + u8 tPower:6, flag:2; +} __packed; +#endif + +struct cal_ctl_data { + struct cal_ctl_edges + ctlEdges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES]; +} __packed; + +struct ar5416_eeprom { + struct base_eep_header baseEepHeader; + u8 custData[64]; + struct modal_eep_header modalHeader[2]; + u8 calFreqPier5G[AR5416_NUM_5G_CAL_PIERS]; + u8 calFreqPier2G[AR5416_NUM_2G_CAL_PIERS]; + struct cal_data_per_freq + calPierData5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS]; + struct cal_data_per_freq + calPierData2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS]; + struct cal_target_power_leg + calTargetPower5G[AR5416_NUM_5G_20_TARGET_POWERS]; + struct cal_target_power_ht + calTargetPower5GHT20[AR5416_NUM_5G_20_TARGET_POWERS]; + struct cal_target_power_ht + calTargetPower5GHT40[AR5416_NUM_5G_40_TARGET_POWERS]; + struct cal_target_power_leg + calTargetPowerCck[AR5416_NUM_2G_CCK_TARGET_POWERS]; + struct cal_target_power_leg + calTargetPower2G[AR5416_NUM_2G_20_TARGET_POWERS]; + struct cal_target_power_ht + calTargetPower2GHT20[AR5416_NUM_2G_20_TARGET_POWERS]; + struct cal_target_power_ht + calTargetPower2GHT40[AR5416_NUM_2G_40_TARGET_POWERS]; + u8 ctlIndex[AR5416_NUM_CTLS]; + struct cal_ctl_data ctlData[AR5416_NUM_CTLS]; + u8 padding; +} __packed; + +struct ar5416IniArray { + u32 *ia_array; + u32 ia_rows; + u32 ia_columns; +}; + +#define INIT_INI_ARRAY(iniarray, array, rows, columns) do { \ + (iniarray)->ia_array = (u32 *)(array); \ + (iniarray)->ia_rows = (rows); \ + (iniarray)->ia_columns = (columns); \ + } while (0) + +#define INI_RA(iniarray, row, column) \ + (((iniarray)->ia_array)[(row) * ((iniarray)->ia_columns) + (column)]) + +#define INIT_CAL(_perCal) do { \ + (_perCal)->calState = CAL_WAITING; \ + (_perCal)->calNext = NULL; \ + } while (0) + +#define INSERT_CAL(_ahp, _perCal) \ + do { \ + if ((_ahp)->ah_cal_list_last == NULL) { \ + (_ahp)->ah_cal_list = \ + (_ahp)->ah_cal_list_last = (_perCal); \ + ((_ahp)->ah_cal_list_last)->calNext = (_perCal); \ + } else { \ + ((_ahp)->ah_cal_list_last)->calNext = (_perCal); \ + (_ahp)->ah_cal_list_last = (_perCal); \ + (_perCal)->calNext = (_ahp)->ah_cal_list; \ + } \ + } while (0) + +enum hal_cal_types { + ADC_DC_INIT_CAL = 0x1, + ADC_GAIN_CAL = 0x2, + ADC_DC_CAL = 0x4, + IQ_MISMATCH_CAL = 0x8 +}; + +enum hal_cal_state { + CAL_INACTIVE, + CAL_WAITING, + CAL_RUNNING, + CAL_DONE +}; + +#define MIN_CAL_SAMPLES 1 +#define MAX_CAL_SAMPLES 64 +#define INIT_LOG_COUNT 5 +#define PER_MIN_LOG_COUNT 2 +#define PER_MAX_LOG_COUNT 10 + +struct hal_percal_data { + enum hal_cal_types calType; + u32 calNumSamples; + u32 calCountMax; + void (*calCollect) (struct ath_hal *); + void (*calPostProc) (struct ath_hal *, u8); +}; + +struct hal_cal_list { + const struct hal_percal_data *calData; + enum hal_cal_state calState; + struct hal_cal_list *calNext; +}; + +struct ath_hal_5416 { + struct ath_hal ah; + struct ar5416_eeprom ah_eeprom; + u8 ah_macaddr[ETH_ALEN]; + u8 ah_bssid[ETH_ALEN]; + u8 ah_bssidmask[ETH_ALEN]; + u16 ah_assocId; + int16_t ah_curchanRadIndex; + u32 ah_maskReg; + struct ar5416Stats ah_stats; + u32 ah_txDescMask; + u32 ah_txOkInterruptMask; + u32 ah_txErrInterruptMask; + u32 ah_txDescInterruptMask; + u32 ah_txEolInterruptMask; + u32 ah_txUrnInterruptMask; + struct ath9k_tx_queue_info ah_txq[ATH9K_NUM_TX_QUEUES]; + enum ath9k_power_mode ah_powerMode; + bool ah_chipFullSleep; + u32 ah_atimWindow; + enum ath9k_ant_setting ah_diversityControl; + u16 ah_antennaSwitchSwap; + enum hal_cal_types ah_suppCals; + struct hal_cal_list ah_iqCalData; + struct hal_cal_list ah_adcGainCalData; + struct hal_cal_list ah_adcDcCalInitData; + struct hal_cal_list ah_adcDcCalData; + struct hal_cal_list *ah_cal_list; + struct hal_cal_list *ah_cal_list_last; + struct hal_cal_list *ah_cal_list_curr; +#define ah_totalPowerMeasI ah_Meas0.unsign +#define ah_totalPowerMeasQ ah_Meas1.unsign +#define ah_totalIqCorrMeas ah_Meas2.sign +#define ah_totalAdcIOddPhase ah_Meas0.unsign +#define ah_totalAdcIEvenPhase ah_Meas1.unsign +#define ah_totalAdcQOddPhase ah_Meas2.unsign +#define ah_totalAdcQEvenPhase ah_Meas3.unsign +#define ah_totalAdcDcOffsetIOddPhase ah_Meas0.sign +#define ah_totalAdcDcOffsetIEvenPhase ah_Meas1.sign +#define ah_totalAdcDcOffsetQOddPhase ah_Meas2.sign +#define ah_totalAdcDcOffsetQEvenPhase ah_Meas3.sign + union { + u32 unsign[AR5416_MAX_CHAINS]; + int32_t sign[AR5416_MAX_CHAINS]; + } ah_Meas0; + union { + u32 unsign[AR5416_MAX_CHAINS]; + int32_t sign[AR5416_MAX_CHAINS]; + } ah_Meas1; + union { + u32 unsign[AR5416_MAX_CHAINS]; + int32_t sign[AR5416_MAX_CHAINS]; + } ah_Meas2; + union { + u32 unsign[AR5416_MAX_CHAINS]; + int32_t sign[AR5416_MAX_CHAINS]; + } ah_Meas3; + u16 ah_CalSamples; + u32 ah_tx6PowerInHalfDbm; + u32 ah_staId1Defaults; + u32 ah_miscMode; + bool ah_tpcEnabled; + u32 ah_beaconInterval; + enum { + AUTO_32KHZ, + USE_32KHZ, + DONT_USE_32KHZ, + } ah_enable32kHzClock; + u32 *ah_analogBank0Data; + u32 *ah_analogBank1Data; + u32 *ah_analogBank2Data; + u32 *ah_analogBank3Data; + u32 *ah_analogBank6Data; + u32 *ah_analogBank6TPCData; + u32 *ah_analogBank7Data; + u32 *ah_addac5416_21; + u32 *ah_bank6Temp; + u32 ah_ofdmTxPower; + int16_t ah_txPowerIndexOffset; + u32 ah_slottime; + u32 ah_acktimeout; + u32 ah_ctstimeout; + u32 ah_globaltxtimeout; + u8 ah_gBeaconRate; + u32 ah_gpioSelect; + u32 ah_polarity; + u32 ah_gpioBit; + bool ah_eepEnabled; + u32 ah_procPhyErr; + bool ah_hasHwPhyCounters; + u32 ah_aniPeriod; + struct ar5416AniState *ah_curani; + struct ar5416AniState ah_ani[255]; + int ah_totalSizeDesired[5]; + int ah_coarseHigh[5]; + int ah_coarseLow[5]; + int ah_firpwr[5]; + u16 ah_ratesArray[16]; + u32 ah_intrTxqs; + bool ah_intrMitigation; + u32 ah_cycleCount; + u32 ah_ctlBusy; + u32 ah_extBusy; + enum ath9k_ht_extprotspacing ah_extprotspacing; + u8 ah_txchainmask; + u8 ah_rxchainmask; + int ah_hwp; + void __iomem *ah_cal_mem; + enum ath9k_ani_cmd ah_ani_function; + struct ar5416IniArray ah_iniModes; + struct ar5416IniArray ah_iniCommon; + struct ar5416IniArray ah_iniBank0; + struct ar5416IniArray ah_iniBB_RfGain; + struct ar5416IniArray ah_iniBank1; + struct ar5416IniArray ah_iniBank2; + struct ar5416IniArray ah_iniBank3; + struct ar5416IniArray ah_iniBank6; + struct ar5416IniArray ah_iniBank6TPC; + struct ar5416IniArray ah_iniBank7; + struct ar5416IniArray ah_iniAddac; + struct ar5416IniArray ah_iniPcieSerdes; + struct ar5416IniArray ah_iniModesAdditional; +}; +#define AH5416(_ah) ((struct ath_hal_5416 *)(_ah)) + +#define FREQ2FBIN(x, y) ((y) ? ((x) - 2300) : (((x) - 4800) / 5)) + +#define IS_5416_EMU(ah) \ + ((ah->ah_devid == AR5416_DEVID_EMU) || \ + (ah->ah_devid == AR5416_DEVID_EMU_PCIE)) + +#define ar5416RfDetach(ah) do { \ + if (AH5416(ah)->ah_rfHal.rfDetach != NULL) \ + AH5416(ah)->ah_rfHal.rfDetach(ah); \ + } while (0) + +#define ath9k_hw_use_flash(_ah) \ + (!(_ah->ah_flags & AH_USE_EEPROM)) + + +#define DO_DELAY(x) do { \ + if ((++(x) % 64) == 0) \ + udelay(1); \ + } while (0) + +#define REG_WRITE_ARRAY(iniarray, column, regWr) do { \ + int r; \ + for (r = 0; r < ((iniarray)->ia_rows); r++) { \ + REG_WRITE(ah, INI_RA((iniarray), (r), 0), \ + INI_RA((iniarray), r, (column))); \ + DO_DELAY(regWr); \ + } \ + } while (0) + +#define BASE_ACTIVATE_DELAY 100 +#define RTC_PLL_SETTLE_DELAY 1000 +#define COEF_SCALE_S 24 +#define HT40_CHANNEL_CENTER_SHIFT 10 + +#define ar5416CheckOpMode(_opmode) \ + ((_opmode == ATH9K_M_STA) || (_opmode == ATH9K_M_IBSS) || \ + (_opmode == ATH9K_M_HOSTAP) || (_opmode == ATH9K_M_MONITOR)) + +#define AR5416_EEPROM_MAGIC_OFFSET 0x0 + +#define AR5416_EEPROM_S 2 +#define AR5416_EEPROM_OFFSET 0x2000 +#define AR5416_EEPROM_START_ADDR \ + (AR_SREV_9100(ah)) ? 0x1fff1000 : 0x503f1200 +#define AR5416_EEPROM_MAX 0xae0 +#define ar5416_get_eep_ver(_ahp) \ + (((_ahp)->ah_eeprom.baseEepHeader.version >> 12) & 0xF) +#define ar5416_get_eep_rev(_ahp) \ + (((_ahp)->ah_eeprom.baseEepHeader.version) & 0xFFF) +#define ar5416_get_ntxchains(_txchainmask) \ + (((_txchainmask >> 2) & 1) + \ + ((_txchainmask >> 1) & 1) + (_txchainmask & 1)) + +#define IS_EEP_MINOR_V3(_ahp) \ + (ath9k_hw_get_eeprom((_ahp), EEP_MINOR_REV) >= AR5416_EEP_MINOR_VER_3) + +#define FIXED_CCA_THRESHOLD 15 + +#ifdef __BIG_ENDIAN +#define AR5416_EEPROM_MAGIC 0x5aa5 +#else +#define AR5416_EEPROM_MAGIC 0xa55a +#endif + +#define ATH9K_POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) + +#define ATH9K_ANTENNA0_CHAINMASK 0x1 +#define ATH9K_ANTENNA1_CHAINMASK 0x2 + +#define ATH9K_NUM_DMA_DEBUG_REGS 8 +#define ATH9K_NUM_QUEUES 10 + +#define HAL_NOISE_IMMUNE_MAX 4 +#define HAL_SPUR_IMMUNE_MAX 7 +#define HAL_FIRST_STEP_MAX 2 + +#define ATH9K_ANI_OFDM_TRIG_HIGH 500 +#define ATH9K_ANI_OFDM_TRIG_LOW 200 +#define ATH9K_ANI_CCK_TRIG_HIGH 200 +#define ATH9K_ANI_CCK_TRIG_LOW 100 +#define ATH9K_ANI_NOISE_IMMUNE_LVL 4 +#define ATH9K_ANI_USE_OFDM_WEAK_SIG true +#define ATH9K_ANI_CCK_WEAK_SIG_THR false +#define ATH9K_ANI_SPUR_IMMUNE_LVL 7 +#define ATH9K_ANI_FIRSTEP_LVL 0 +#define ATH9K_ANI_RSSI_THR_HIGH 40 +#define ATH9K_ANI_RSSI_THR_LOW 7 +#define ATH9K_ANI_PERIOD 100 + +#define AR_GPIOD_MASK 0x00001FFF +#define AR_GPIO_BIT(_gpio) (1 << (_gpio)) + +#define MAX_ANALOG_START 319 + +#define HAL_EP_RND(x, mul) \ + ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) +#define BEACON_RSSI(ahp) \ + HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \ + ATH9K_RSSI_EP_MULTIPLIER) + +#define ah_mibStats ah_stats.ast_mibstats + +#define AH_TIMEOUT 100000 +#define AH_TIME_QUANTUM 10 + +#define IS(_c, _f) (((_c)->channelFlags & _f) || 0) + +#define AR_KEYTABLE_SIZE 128 +#define POWER_UP_TIME 200000 + +#define EXT_ADDITIVE (0x8000) +#define CTL_11A_EXT (CTL_11A | EXT_ADDITIVE) +#define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE) +#define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE) + +#define SUB_NUM_CTL_MODES_AT_5G_40 2 +#define SUB_NUM_CTL_MODES_AT_2G_40 3 +#define SPUR_RSSI_THRESH 40 + +#define TU_TO_USEC(_tu) ((_tu) << 10) + +#define CAB_TIMEOUT_VAL 10 +#define BEACON_TIMEOUT_VAL 10 +#define MIN_BEACON_TIMEOUT_VAL 1 +#define SLEEP_SLOP 3 + +#define CCK_SIFS_TIME 10 +#define CCK_PREAMBLE_BITS 144 +#define CCK_PLCP_BITS 48 + +#define OFDM_SIFS_TIME 16 +#define OFDM_PREAMBLE_TIME 20 +#define OFDM_PLCP_BITS 22 +#define OFDM_SYMBOL_TIME 4 + +#define OFDM_SIFS_TIME_HALF 32 +#define OFDM_PREAMBLE_TIME_HALF 40 +#define OFDM_PLCP_BITS_HALF 22 +#define OFDM_SYMBOL_TIME_HALF 8 + +#define OFDM_SIFS_TIME_QUARTER 64 +#define OFDM_PREAMBLE_TIME_QUARTER 80 +#define OFDM_PLCP_BITS_QUARTER 22 +#define OFDM_SYMBOL_TIME_QUARTER 16 + +u32 ath9k_hw_get_eeprom(struct ath_hal_5416 *ahp, + enum eeprom_param param); + +#endif diff --git a/drivers/net/wireless/ath9k/initvals.h b/drivers/net/wireless/ath9k/initvals.h new file mode 100644 index 000000000000..3dd3815940a4 --- /dev/null +++ b/drivers/net/wireless/ath9k/initvals.h @@ -0,0 +1,3146 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +static const u32 ar5416Modes_9100[][6] = { + { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000, 0x00014008 }, + { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 }, + { 0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab, 0x098813cf }, + { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 }, + { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, + { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, + { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 }, + { 0x00009844, 0x1372161e, 0x1372161e, 0x137216a0, 0x137216a0, 0x137216a0 }, + { 0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x00009850, 0x6de8b4e0, 0x6de8b4e0, 0x6de8b0de, 0x6de8b0de, 0x6de8b0de }, + { 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e }, + { 0x00009860, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18, 0x00049d18 }, + { 0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190, 0x409a4190 }, + { 0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 }, + { 0x00009914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x000001b8, 0x00000370, 0x00000268, 0x00000134, 0x00000134 }, + { 0x00009924, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b, 0xd0058a0b }, + { 0x00009944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020 }, + { 0x00009960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80 }, + { 0x0000a960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80 }, + { 0x0000b960, 0x00000900, 0x00000900, 0x00012d80, 0x00012d80, 0x00012d80 }, + { 0x00009964, 0x00000000, 0x00000000, 0x00001120, 0x00001120, 0x00001120 }, + { 0x0000c9bc, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00, 0x001a0a00 }, + { 0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, 0x038919be }, + { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, + { 0x000099c8, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c }, + { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 }, + { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 }, + { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880, 0x00000880 }, + { 0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, 0xd03e4788 }, + { 0x0000a20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000b20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000c20c, 0x002ec1e0, 0x002ec1e0, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, 0x0a1a7caa }, + { 0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, 0x18010000 }, + { 0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, 0x2e032402 }, + { 0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, 0x4a0a3c06 }, + { 0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, 0x621a540b }, + { 0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, 0x764f6c1b }, + { 0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, 0x845b7a5a }, + { 0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, 0x950f8ccf }, + { 0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, 0xa5cf9b4f }, + { 0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, 0xbddfaf1f }, + { 0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, 0xd1ffc93f }, + { 0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, +}; + +static const u32 ar5416Common_9100[][2] = { + { 0x0000000c, 0x00000000 }, + { 0x00000030, 0x00020015 }, + { 0x00000034, 0x00000005 }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000008 }, + { 0x00000048, 0x00000008 }, + { 0x0000004c, 0x00000010 }, + { 0x00000050, 0x00000000 }, + { 0x00000054, 0x0000001f }, + { 0x00000800, 0x00000000 }, + { 0x00000804, 0x00000000 }, + { 0x00000808, 0x00000000 }, + { 0x0000080c, 0x00000000 }, + { 0x00000810, 0x00000000 }, + { 0x00000814, 0x00000000 }, + { 0x00000818, 0x00000000 }, + { 0x0000081c, 0x00000000 }, + { 0x00000820, 0x00000000 }, + { 0x00000824, 0x00000000 }, + { 0x00001040, 0x002ffc0f }, + { 0x00001044, 0x002ffc0f }, + { 0x00001048, 0x002ffc0f }, + { 0x0000104c, 0x002ffc0f }, + { 0x00001050, 0x002ffc0f }, + { 0x00001054, 0x002ffc0f }, + { 0x00001058, 0x002ffc0f }, + { 0x0000105c, 0x002ffc0f }, + { 0x00001060, 0x002ffc0f }, + { 0x00001064, 0x002ffc0f }, + { 0x00001230, 0x00000000 }, + { 0x00001270, 0x00000000 }, + { 0x00001038, 0x00000000 }, + { 0x00001078, 0x00000000 }, + { 0x000010b8, 0x00000000 }, + { 0x000010f8, 0x00000000 }, + { 0x00001138, 0x00000000 }, + { 0x00001178, 0x00000000 }, + { 0x000011b8, 0x00000000 }, + { 0x000011f8, 0x00000000 }, + { 0x00001238, 0x00000000 }, + { 0x00001278, 0x00000000 }, + { 0x000012b8, 0x00000000 }, + { 0x000012f8, 0x00000000 }, + { 0x00001338, 0x00000000 }, + { 0x00001378, 0x00000000 }, + { 0x000013b8, 0x00000000 }, + { 0x000013f8, 0x00000000 }, + { 0x00001438, 0x00000000 }, + { 0x00001478, 0x00000000 }, + { 0x000014b8, 0x00000000 }, + { 0x000014f8, 0x00000000 }, + { 0x00001538, 0x00000000 }, + { 0x00001578, 0x00000000 }, + { 0x000015b8, 0x00000000 }, + { 0x000015f8, 0x00000000 }, + { 0x00001638, 0x00000000 }, + { 0x00001678, 0x00000000 }, + { 0x000016b8, 0x00000000 }, + { 0x000016f8, 0x00000000 }, + { 0x00001738, 0x00000000 }, + { 0x00001778, 0x00000000 }, + { 0x000017b8, 0x00000000 }, + { 0x000017f8, 0x00000000 }, + { 0x0000103c, 0x00000000 }, + { 0x0000107c, 0x00000000 }, + { 0x000010bc, 0x00000000 }, + { 0x000010fc, 0x00000000 }, + { 0x0000113c, 0x00000000 }, + { 0x0000117c, 0x00000000 }, + { 0x000011bc, 0x00000000 }, + { 0x000011fc, 0x00000000 }, + { 0x0000123c, 0x00000000 }, + { 0x0000127c, 0x00000000 }, + { 0x000012bc, 0x00000000 }, + { 0x000012fc, 0x00000000 }, + { 0x0000133c, 0x00000000 }, + { 0x0000137c, 0x00000000 }, + { 0x000013bc, 0x00000000 }, + { 0x000013fc, 0x00000000 }, + { 0x0000143c, 0x00000000 }, + { 0x0000147c, 0x00000000 }, + { 0x00004030, 0x00000002 }, + { 0x0000403c, 0x00000002 }, + { 0x00007010, 0x00000000 }, + { 0x00007038, 0x000004c2 }, + { 0x00008004, 0x00000000 }, + { 0x00008008, 0x00000000 }, + { 0x0000800c, 0x00000000 }, + { 0x00008018, 0x00000700 }, + { 0x00008020, 0x00000000 }, + { 0x00008038, 0x00000000 }, + { 0x0000803c, 0x00000000 }, + { 0x00008048, 0x40000000 }, + { 0x00008054, 0x00000000 }, + { 0x00008058, 0x00000000 }, + { 0x0000805c, 0x000fc78f }, + { 0x00008060, 0x0000000f }, + { 0x00008064, 0x00000000 }, + { 0x000080c0, 0x2a82301a }, + { 0x000080c4, 0x05dc01e0 }, + { 0x000080c8, 0x1f402710 }, + { 0x000080cc, 0x01f40000 }, + { 0x000080d0, 0x00001e00 }, + { 0x000080d4, 0x00000000 }, + { 0x000080d8, 0x00400000 }, + { 0x000080e0, 0xffffffff }, + { 0x000080e4, 0x0000ffff }, + { 0x000080e8, 0x003f3f3f }, + { 0x000080ec, 0x00000000 }, + { 0x000080f0, 0x00000000 }, + { 0x000080f4, 0x00000000 }, + { 0x000080f8, 0x00000000 }, + { 0x000080fc, 0x00020000 }, + { 0x00008100, 0x00020000 }, + { 0x00008104, 0x00000001 }, + { 0x00008108, 0x00000052 }, + { 0x0000810c, 0x00000000 }, + { 0x00008110, 0x00000168 }, + { 0x00008118, 0x000100aa }, + { 0x0000811c, 0x00003210 }, + { 0x00008120, 0x08f04800 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0x00000000 }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x32143320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c4, 0x00000000 }, + { 0x000081d0, 0x00003210 }, + { 0x000081ec, 0x00000000 }, + { 0x000081f0, 0x00000000 }, + { 0x000081f4, 0x00000000 }, + { 0x000081f8, 0x00000000 }, + { 0x000081fc, 0x00000000 }, + { 0x00008200, 0x00000000 }, + { 0x00008204, 0x00000000 }, + { 0x00008208, 0x00000000 }, + { 0x0000820c, 0x00000000 }, + { 0x00008210, 0x00000000 }, + { 0x00008214, 0x00000000 }, + { 0x00008218, 0x00000000 }, + { 0x0000821c, 0x00000000 }, + { 0x00008220, 0x00000000 }, + { 0x00008224, 0x00000000 }, + { 0x00008228, 0x00000000 }, + { 0x0000822c, 0x00000000 }, + { 0x00008230, 0x00000000 }, + { 0x00008234, 0x00000000 }, + { 0x00008238, 0x00000000 }, + { 0x0000823c, 0x00000000 }, + { 0x00008240, 0x00100000 }, + { 0x00008244, 0x0010f400 }, + { 0x00008248, 0x00000100 }, + { 0x0000824c, 0x0001e800 }, + { 0x00008250, 0x00000000 }, + { 0x00008254, 0x00000000 }, + { 0x00008258, 0x00000000 }, + { 0x0000825c, 0x400000ff }, + { 0x00008260, 0x00080922 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x00000000 }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x00008300, 0x00000000 }, + { 0x00008304, 0x00000000 }, + { 0x00008308, 0x00000000 }, + { 0x0000830c, 0x00000000 }, + { 0x00008310, 0x00000000 }, + { 0x00008314, 0x00000000 }, + { 0x00008318, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000007 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00000000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x000107ff }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xad848e19 }, + { 0x00009810, 0x7d14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x00009840, 0x206a002e }, + { 0x0000984c, 0x1284233c }, + { 0x00009854, 0x00000859 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x05100000 }, + { 0x0000a920, 0x05100000 }, + { 0x0000b920, 0x05100000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009948, 0x9280b212 }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0x5d50e188 }, + { 0x00009958, 0x00081fff }, + { 0x0000c95c, 0x004b6a8e }, + { 0x0000c968, 0x000003ce }, + { 0x00009970, 0x190fb515 }, + { 0x00009974, 0x00000000 }, + { 0x00009978, 0x00000001 }, + { 0x0000997c, 0x00000000 }, + { 0x00009980, 0x00000000 }, + { 0x00009984, 0x00000000 }, + { 0x00009988, 0x00000000 }, + { 0x0000998c, 0x00000000 }, + { 0x00009990, 0x00000000 }, + { 0x00009994, 0x00000000 }, + { 0x00009998, 0x00000000 }, + { 0x0000999c, 0x00000000 }, + { 0x000099a0, 0x00000000 }, + { 0x000099a4, 0x00000001 }, + { 0x000099a8, 0x001fff00 }, + { 0x000099ac, 0x00000000 }, + { 0x000099b0, 0x03051000 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000200 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x000000aa }, + { 0x000099fc, 0x00001042 }, + { 0x00009b00, 0x00000000 }, + { 0x00009b04, 0x00000001 }, + { 0x00009b08, 0x00000002 }, + { 0x00009b0c, 0x00000003 }, + { 0x00009b10, 0x00000004 }, + { 0x00009b14, 0x00000005 }, + { 0x00009b18, 0x00000008 }, + { 0x00009b1c, 0x00000009 }, + { 0x00009b20, 0x0000000a }, + { 0x00009b24, 0x0000000b }, + { 0x00009b28, 0x0000000c }, + { 0x00009b2c, 0x0000000d }, + { 0x00009b30, 0x00000010 }, + { 0x00009b34, 0x00000011 }, + { 0x00009b38, 0x00000012 }, + { 0x00009b3c, 0x00000013 }, + { 0x00009b40, 0x00000014 }, + { 0x00009b44, 0x00000015 }, + { 0x00009b48, 0x00000018 }, + { 0x00009b4c, 0x00000019 }, + { 0x00009b50, 0x0000001a }, + { 0x00009b54, 0x0000001b }, + { 0x00009b58, 0x0000001c }, + { 0x00009b5c, 0x0000001d }, + { 0x00009b60, 0x00000020 }, + { 0x00009b64, 0x00000021 }, + { 0x00009b68, 0x00000022 }, + { 0x00009b6c, 0x00000023 }, + { 0x00009b70, 0x00000024 }, + { 0x00009b74, 0x00000025 }, + { 0x00009b78, 0x00000028 }, + { 0x00009b7c, 0x00000029 }, + { 0x00009b80, 0x0000002a }, + { 0x00009b84, 0x0000002b }, + { 0x00009b88, 0x0000002c }, + { 0x00009b8c, 0x0000002d }, + { 0x00009b90, 0x00000030 }, + { 0x00009b94, 0x00000031 }, + { 0x00009b98, 0x00000032 }, + { 0x00009b9c, 0x00000033 }, + { 0x00009ba0, 0x00000034 }, + { 0x00009ba4, 0x00000035 }, + { 0x00009ba8, 0x00000035 }, + { 0x00009bac, 0x00000035 }, + { 0x00009bb0, 0x00000035 }, + { 0x00009bb4, 0x00000035 }, + { 0x00009bb8, 0x00000035 }, + { 0x00009bbc, 0x00000035 }, + { 0x00009bc0, 0x00000035 }, + { 0x00009bc4, 0x00000035 }, + { 0x00009bc8, 0x00000035 }, + { 0x00009bcc, 0x00000035 }, + { 0x00009bd0, 0x00000035 }, + { 0x00009bd4, 0x00000035 }, + { 0x00009bd8, 0x00000035 }, + { 0x00009bdc, 0x00000035 }, + { 0x00009be0, 0x00000035 }, + { 0x00009be4, 0x00000035 }, + { 0x00009be8, 0x00000035 }, + { 0x00009bec, 0x00000035 }, + { 0x00009bf0, 0x00000035 }, + { 0x00009bf4, 0x00000035 }, + { 0x00009bf8, 0x00000010 }, + { 0x00009bfc, 0x0000001a }, + { 0x0000a210, 0x40806333 }, + { 0x0000a214, 0x00106c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x018830c6 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x00000bb5 }, + { 0x0000a22c, 0x00000011 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a23c, 0x13c889af }, + { 0x0000a240, 0x38490a20 }, + { 0x0000a244, 0x00007bb6 }, + { 0x0000a248, 0x0fff3ffc }, + { 0x0000a24c, 0x00000001 }, + { 0x0000a250, 0x0000a000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0cc75380 }, + { 0x0000a25c, 0x0f0f0f01 }, + { 0x0000a260, 0xdfa91f01 }, + { 0x0000a268, 0x00000000 }, + { 0x0000a26c, 0x0ebae9c6 }, + { 0x0000b26c, 0x0ebae9c6 }, + { 0x0000c26c, 0x0ebae9c6 }, + { 0x0000d270, 0x00820820 }, + { 0x0000a278, 0x1ce739ce }, + { 0x0000a27c, 0x051701ce }, + { 0x0000a338, 0x00000000 }, + { 0x0000a33c, 0x00000000 }, + { 0x0000a340, 0x00000000 }, + { 0x0000a344, 0x00000000 }, + { 0x0000a348, 0x3fffffff }, + { 0x0000a34c, 0x3fffffff }, + { 0x0000a350, 0x3fffffff }, + { 0x0000a354, 0x0003ffff }, + { 0x0000a358, 0x79a8aa1f }, + { 0x0000d35c, 0x07ffffef }, + { 0x0000d360, 0x0fffffe7 }, + { 0x0000d364, 0x17ffffe5 }, + { 0x0000d368, 0x1fffffe4 }, + { 0x0000d36c, 0x37ffffe3 }, + { 0x0000d370, 0x3fffffe3 }, + { 0x0000d374, 0x57ffffe3 }, + { 0x0000d378, 0x5fffffe2 }, + { 0x0000d37c, 0x7fffffe2 }, + { 0x0000d380, 0x7f3c7bba }, + { 0x0000d384, 0xf3307ff0 }, + { 0x0000a388, 0x08000000 }, + { 0x0000a38c, 0x20202020 }, + { 0x0000a390, 0x20202020 }, + { 0x0000a394, 0x1ce739ce }, + { 0x0000a398, 0x000001ce }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3a0, 0x00000000 }, + { 0x0000a3a4, 0x00000000 }, + { 0x0000a3a8, 0x00000000 }, + { 0x0000a3ac, 0x00000000 }, + { 0x0000a3b0, 0x00000000 }, + { 0x0000a3b4, 0x00000000 }, + { 0x0000a3b8, 0x00000000 }, + { 0x0000a3bc, 0x00000000 }, + { 0x0000a3c0, 0x00000000 }, + { 0x0000a3c4, 0x00000000 }, + { 0x0000a3c8, 0x00000246 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3dc, 0x1ce739ce }, + { 0x0000a3e0, 0x000001ce }, +}; + +static const u32 ar5416Bank0_9100[][2] = { + { 0x000098b0, 0x1e5795e5 }, + { 0x000098e0, 0x02008020 }, +}; + +static const u32 ar5416BB_RfGain_9100[][3] = { + { 0x00009a00, 0x00000000, 0x00000000 }, + { 0x00009a04, 0x00000040, 0x00000040 }, + { 0x00009a08, 0x00000080, 0x00000080 }, + { 0x00009a0c, 0x000001a1, 0x00000141 }, + { 0x00009a10, 0x000001e1, 0x00000181 }, + { 0x00009a14, 0x00000021, 0x000001c1 }, + { 0x00009a18, 0x00000061, 0x00000001 }, + { 0x00009a1c, 0x00000168, 0x00000041 }, + { 0x00009a20, 0x000001a8, 0x000001a8 }, + { 0x00009a24, 0x000001e8, 0x000001e8 }, + { 0x00009a28, 0x00000028, 0x00000028 }, + { 0x00009a2c, 0x00000068, 0x00000068 }, + { 0x00009a30, 0x00000189, 0x000000a8 }, + { 0x00009a34, 0x000001c9, 0x00000169 }, + { 0x00009a38, 0x00000009, 0x000001a9 }, + { 0x00009a3c, 0x00000049, 0x000001e9 }, + { 0x00009a40, 0x00000089, 0x00000029 }, + { 0x00009a44, 0x00000170, 0x00000069 }, + { 0x00009a48, 0x000001b0, 0x00000190 }, + { 0x00009a4c, 0x000001f0, 0x000001d0 }, + { 0x00009a50, 0x00000030, 0x00000010 }, + { 0x00009a54, 0x00000070, 0x00000050 }, + { 0x00009a58, 0x00000191, 0x00000090 }, + { 0x00009a5c, 0x000001d1, 0x00000151 }, + { 0x00009a60, 0x00000011, 0x00000191 }, + { 0x00009a64, 0x00000051, 0x000001d1 }, + { 0x00009a68, 0x00000091, 0x00000011 }, + { 0x00009a6c, 0x000001b8, 0x00000051 }, + { 0x00009a70, 0x000001f8, 0x00000198 }, + { 0x00009a74, 0x00000038, 0x000001d8 }, + { 0x00009a78, 0x00000078, 0x00000018 }, + { 0x00009a7c, 0x00000199, 0x00000058 }, + { 0x00009a80, 0x000001d9, 0x00000098 }, + { 0x00009a84, 0x00000019, 0x00000159 }, + { 0x00009a88, 0x00000059, 0x00000199 }, + { 0x00009a8c, 0x00000099, 0x000001d9 }, + { 0x00009a90, 0x000000d9, 0x00000019 }, + { 0x00009a94, 0x000000f9, 0x00000059 }, + { 0x00009a98, 0x000000f9, 0x00000099 }, + { 0x00009a9c, 0x000000f9, 0x000000d9 }, + { 0x00009aa0, 0x000000f9, 0x000000f9 }, + { 0x00009aa4, 0x000000f9, 0x000000f9 }, + { 0x00009aa8, 0x000000f9, 0x000000f9 }, + { 0x00009aac, 0x000000f9, 0x000000f9 }, + { 0x00009ab0, 0x000000f9, 0x000000f9 }, + { 0x00009ab4, 0x000000f9, 0x000000f9 }, + { 0x00009ab8, 0x000000f9, 0x000000f9 }, + { 0x00009abc, 0x000000f9, 0x000000f9 }, + { 0x00009ac0, 0x000000f9, 0x000000f9 }, + { 0x00009ac4, 0x000000f9, 0x000000f9 }, + { 0x00009ac8, 0x000000f9, 0x000000f9 }, + { 0x00009acc, 0x000000f9, 0x000000f9 }, + { 0x00009ad0, 0x000000f9, 0x000000f9 }, + { 0x00009ad4, 0x000000f9, 0x000000f9 }, + { 0x00009ad8, 0x000000f9, 0x000000f9 }, + { 0x00009adc, 0x000000f9, 0x000000f9 }, + { 0x00009ae0, 0x000000f9, 0x000000f9 }, + { 0x00009ae4, 0x000000f9, 0x000000f9 }, + { 0x00009ae8, 0x000000f9, 0x000000f9 }, + { 0x00009aec, 0x000000f9, 0x000000f9 }, + { 0x00009af0, 0x000000f9, 0x000000f9 }, + { 0x00009af4, 0x000000f9, 0x000000f9 }, + { 0x00009af8, 0x000000f9, 0x000000f9 }, + { 0x00009afc, 0x000000f9, 0x000000f9 }, +}; + +static const u32 ar5416Bank1_9100[][2] = { + { 0x000098b0, 0x02108421 }, + { 0x000098ec, 0x00000008 }, +}; + +static const u32 ar5416Bank2_9100[][2] = { + { 0x000098b0, 0x0e73ff17 }, + { 0x000098e0, 0x00000420 }, +}; + +static const u32 ar5416Bank3_9100[][3] = { + { 0x000098f0, 0x01400018, 0x01c00018 }, +}; + +static const u32 ar5416Bank6_9100[][3] = { + + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00e00000, 0x00e00000 }, + { 0x0000989c, 0x005e0000, 0x005e0000 }, + { 0x0000989c, 0x00120000, 0x00120000 }, + { 0x0000989c, 0x00620000, 0x00620000 }, + { 0x0000989c, 0x00020000, 0x00020000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x40ff0000, 0x40ff0000 }, + { 0x0000989c, 0x005f0000, 0x005f0000 }, + { 0x0000989c, 0x00870000, 0x00870000 }, + { 0x0000989c, 0x00f90000, 0x00f90000 }, + { 0x0000989c, 0x007b0000, 0x007b0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00f50000, 0x00f50000 }, + { 0x0000989c, 0x00dc0000, 0x00dc0000 }, + { 0x0000989c, 0x00110000, 0x00110000 }, + { 0x0000989c, 0x006100a8, 0x006100a8 }, + { 0x0000989c, 0x004210a2, 0x004210a2 }, + { 0x0000989c, 0x0014008f, 0x0014008f }, + { 0x0000989c, 0x00c40003, 0x00c40003 }, + { 0x0000989c, 0x003000f2, 0x003000f2 }, + { 0x0000989c, 0x00440016, 0x00440016 }, + { 0x0000989c, 0x00410040, 0x00410040 }, + { 0x0000989c, 0x0001805e, 0x0001805e }, + { 0x0000989c, 0x0000c0ab, 0x0000c0ab }, + { 0x0000989c, 0x000000f1, 0x000000f1 }, + { 0x0000989c, 0x00002081, 0x00002081 }, + { 0x0000989c, 0x000000d4, 0x000000d4 }, + { 0x000098d0, 0x0000000f, 0x0010000f }, +}; + +static const u32 ar5416Bank6TPC_9100[][3] = { + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00e00000, 0x00e00000 }, + { 0x0000989c, 0x005e0000, 0x005e0000 }, + { 0x0000989c, 0x00120000, 0x00120000 }, + { 0x0000989c, 0x00620000, 0x00620000 }, + { 0x0000989c, 0x00020000, 0x00020000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x40ff0000, 0x40ff0000 }, + { 0x0000989c, 0x005f0000, 0x005f0000 }, + { 0x0000989c, 0x00870000, 0x00870000 }, + { 0x0000989c, 0x00f90000, 0x00f90000 }, + { 0x0000989c, 0x007b0000, 0x007b0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00f50000, 0x00f50000 }, + { 0x0000989c, 0x00dc0000, 0x00dc0000 }, + { 0x0000989c, 0x00110000, 0x00110000 }, + { 0x0000989c, 0x006100a8, 0x006100a8 }, + { 0x0000989c, 0x00423022, 0x00423022 }, + { 0x0000989c, 0x201400df, 0x201400df }, + { 0x0000989c, 0x00c40002, 0x00c40002 }, + { 0x0000989c, 0x003000f2, 0x003000f2 }, + { 0x0000989c, 0x00440016, 0x00440016 }, + { 0x0000989c, 0x00410040, 0x00410040 }, + { 0x0000989c, 0x0001805e, 0x0001805e }, + { 0x0000989c, 0x0000c0ab, 0x0000c0ab }, + { 0x0000989c, 0x000000e1, 0x000000e1 }, + { 0x0000989c, 0x00007081, 0x00007081 }, + { 0x0000989c, 0x000000d4, 0x000000d4 }, + { 0x000098d0, 0x0000000f, 0x0010000f }, +}; + +static const u32 ar5416Bank7_9100[][2] = { + { 0x0000989c, 0x00000500 }, + { 0x0000989c, 0x00000800 }, + { 0x000098cc, 0x0000000e }, +}; + +static const u32 ar5416Addac_9100[][2] = { + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000003 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x0000000c }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000030 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000060 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000058 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x000098c4, 0x00000000 }, +}; + +static const u32 ar5416Modes[][6] = { + { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000, 0x00014008 }, + { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 }, + { 0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab, 0x098813cf }, + { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 }, + { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, + { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, + { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 }, + { 0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0, 0x037216a0 }, + { 0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x00009850, 0x6d48b4e2, 0x6d48b4e2, 0x6d48b0e2, 0x6d48b0e2, 0x6d48b0e2 }, + { 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec86d2e, 0x7ec84d2e, 0x7ec82d2e }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e }, + { 0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20, 0x00048d18 }, + { 0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0 }, + { 0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 }, + { 0x00009914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a11, 0xd00a8a0d, 0xd00a8a0d }, + { 0x00009940, 0x00754604, 0x00754604, 0xfff81204, 0xfff81204, 0xfff81204 }, + { 0x00009944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020 }, + { 0x00009954, 0x5f3ca3de, 0x5f3ca3de, 0xe250a51e, 0xe250a51e, 0xe250a51e }, + { 0x00009958, 0x2108ecff, 0x2108ecff, 0x3388ffff, 0x3388ffff, 0x3388ffff }, +#ifdef TB243 + { 0x00009960, 0x00000900, 0x00000900, 0x00009b40, 0x00009b40, 0x00012d80 }, + { 0x0000a960, 0x00000900, 0x00000900, 0x00009b40, 0x00009b40, 0x00012d80 }, + { 0x0000b960, 0x00000900, 0x00000900, 0x00009b40, 0x00009b40, 0x00012d80 }, + { 0x00009964, 0x00000000, 0x00000000, 0x00002210, 0x00002210, 0x00001120 }, +#else + { 0x00009960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0 }, + { 0x0000a960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0 }, + { 0x0000b960, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0, 0x0001bfc0 }, + { 0x00009964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, 0x00001120 }, +#endif + { 0x0000c9bc, 0x001a0600, 0x001a0600, 0x001a1000, 0x001a0c00, 0x001a0c00 }, + { 0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, 0x038919be }, + { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, + { 0x000099c8, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329 }, + { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 }, + { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 }, + { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880, 0x00000880 }, + { 0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, 0xd03e4788 }, + { 0x0000a20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000b20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000c20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, 0x0a1a7caa }, + { 0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, 0x18010000 }, + { 0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, 0x2e032402 }, + { 0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, 0x4a0a3c06 }, + { 0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, 0x621a540b }, + { 0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, 0x764f6c1b }, + { 0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, 0x845b7a5a }, + { 0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, 0x950f8ccf }, + { 0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, 0xa5cf9b4f }, + { 0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, 0xbddfaf1f }, + { 0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, 0xd1ffc93f }, + { 0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, +}; + +static const u32 ar5416Common[][2] = { + { 0x0000000c, 0x00000000 }, + { 0x00000030, 0x00020015 }, + { 0x00000034, 0x00000005 }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000008 }, + { 0x00000048, 0x00000008 }, + { 0x0000004c, 0x00000010 }, + { 0x00000050, 0x00000000 }, + { 0x00000054, 0x0000001f }, + { 0x00000800, 0x00000000 }, + { 0x00000804, 0x00000000 }, + { 0x00000808, 0x00000000 }, + { 0x0000080c, 0x00000000 }, + { 0x00000810, 0x00000000 }, + { 0x00000814, 0x00000000 }, + { 0x00000818, 0x00000000 }, + { 0x0000081c, 0x00000000 }, + { 0x00000820, 0x00000000 }, + { 0x00000824, 0x00000000 }, + { 0x00001040, 0x002ffc0f }, + { 0x00001044, 0x002ffc0f }, + { 0x00001048, 0x002ffc0f }, + { 0x0000104c, 0x002ffc0f }, + { 0x00001050, 0x002ffc0f }, + { 0x00001054, 0x002ffc0f }, + { 0x00001058, 0x002ffc0f }, + { 0x0000105c, 0x002ffc0f }, + { 0x00001060, 0x002ffc0f }, + { 0x00001064, 0x002ffc0f }, + { 0x00001230, 0x00000000 }, + { 0x00001270, 0x00000000 }, + { 0x00001038, 0x00000000 }, + { 0x00001078, 0x00000000 }, + { 0x000010b8, 0x00000000 }, + { 0x000010f8, 0x00000000 }, + { 0x00001138, 0x00000000 }, + { 0x00001178, 0x00000000 }, + { 0x000011b8, 0x00000000 }, + { 0x000011f8, 0x00000000 }, + { 0x00001238, 0x00000000 }, + { 0x00001278, 0x00000000 }, + { 0x000012b8, 0x00000000 }, + { 0x000012f8, 0x00000000 }, + { 0x00001338, 0x00000000 }, + { 0x00001378, 0x00000000 }, + { 0x000013b8, 0x00000000 }, + { 0x000013f8, 0x00000000 }, + { 0x00001438, 0x00000000 }, + { 0x00001478, 0x00000000 }, + { 0x000014b8, 0x00000000 }, + { 0x000014f8, 0x00000000 }, + { 0x00001538, 0x00000000 }, + { 0x00001578, 0x00000000 }, + { 0x000015b8, 0x00000000 }, + { 0x000015f8, 0x00000000 }, + { 0x00001638, 0x00000000 }, + { 0x00001678, 0x00000000 }, + { 0x000016b8, 0x00000000 }, + { 0x000016f8, 0x00000000 }, + { 0x00001738, 0x00000000 }, + { 0x00001778, 0x00000000 }, + { 0x000017b8, 0x00000000 }, + { 0x000017f8, 0x00000000 }, + { 0x0000103c, 0x00000000 }, + { 0x0000107c, 0x00000000 }, + { 0x000010bc, 0x00000000 }, + { 0x000010fc, 0x00000000 }, + { 0x0000113c, 0x00000000 }, + { 0x0000117c, 0x00000000 }, + { 0x000011bc, 0x00000000 }, + { 0x000011fc, 0x00000000 }, + { 0x0000123c, 0x00000000 }, + { 0x0000127c, 0x00000000 }, + { 0x000012bc, 0x00000000 }, + { 0x000012fc, 0x00000000 }, + { 0x0000133c, 0x00000000 }, + { 0x0000137c, 0x00000000 }, + { 0x000013bc, 0x00000000 }, + { 0x000013fc, 0x00000000 }, + { 0x0000143c, 0x00000000 }, + { 0x0000147c, 0x00000000 }, + { 0x00020010, 0x00000003 }, + { 0x00020038, 0x000004c2 }, + { 0x00008004, 0x00000000 }, + { 0x00008008, 0x00000000 }, + { 0x0000800c, 0x00000000 }, + { 0x00008018, 0x00000700 }, + { 0x00008020, 0x00000000 }, + { 0x00008038, 0x00000000 }, + { 0x0000803c, 0x00000000 }, + { 0x00008048, 0x40000000 }, + { 0x00008054, 0x00004000 }, + { 0x00008058, 0x00000000 }, + { 0x0000805c, 0x000fc78f }, + { 0x00008060, 0x0000000f }, + { 0x00008064, 0x00000000 }, + { 0x000080c0, 0x2a82301a }, + { 0x000080c4, 0x05dc01e0 }, + { 0x000080c8, 0x1f402710 }, + { 0x000080cc, 0x01f40000 }, + { 0x000080d0, 0x00001e00 }, + { 0x000080d4, 0x00000000 }, + { 0x000080d8, 0x00400000 }, + { 0x000080e0, 0xffffffff }, + { 0x000080e4, 0x0000ffff }, + { 0x000080e8, 0x003f3f3f }, + { 0x000080ec, 0x00000000 }, + { 0x000080f0, 0x00000000 }, + { 0x000080f4, 0x00000000 }, + { 0x000080f8, 0x00000000 }, + { 0x000080fc, 0x00020000 }, + { 0x00008100, 0x00020000 }, + { 0x00008104, 0x00000001 }, + { 0x00008108, 0x00000052 }, + { 0x0000810c, 0x00000000 }, + { 0x00008110, 0x00000168 }, + { 0x00008118, 0x000100aa }, + { 0x0000811c, 0x00003210 }, + { 0x00008120, 0x08f04800 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0x00000000 }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x32143320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c4, 0x00000000 }, + { 0x000081d0, 0x00003210 }, + { 0x000081ec, 0x00000000 }, + { 0x000081f0, 0x00000000 }, + { 0x000081f4, 0x00000000 }, + { 0x000081f8, 0x00000000 }, + { 0x000081fc, 0x00000000 }, + { 0x00008200, 0x00000000 }, + { 0x00008204, 0x00000000 }, + { 0x00008208, 0x00000000 }, + { 0x0000820c, 0x00000000 }, + { 0x00008210, 0x00000000 }, + { 0x00008214, 0x00000000 }, + { 0x00008218, 0x00000000 }, + { 0x0000821c, 0x00000000 }, + { 0x00008220, 0x00000000 }, + { 0x00008224, 0x00000000 }, + { 0x00008228, 0x00000000 }, + { 0x0000822c, 0x00000000 }, + { 0x00008230, 0x00000000 }, + { 0x00008234, 0x00000000 }, + { 0x00008238, 0x00000000 }, + { 0x0000823c, 0x00000000 }, + { 0x00008240, 0x00100000 }, + { 0x00008244, 0x0010f400 }, + { 0x00008248, 0x00000100 }, + { 0x0000824c, 0x0001e800 }, + { 0x00008250, 0x00000000 }, + { 0x00008254, 0x00000000 }, + { 0x00008258, 0x00000000 }, + { 0x0000825c, 0x400000ff }, + { 0x00008260, 0x00080922 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x00000000 }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x00008300, 0x00000000 }, + { 0x00008304, 0x00000000 }, + { 0x00008308, 0x00000000 }, + { 0x0000830c, 0x00000000 }, + { 0x00008310, 0x00000000 }, + { 0x00008314, 0x00000000 }, + { 0x00008318, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000007 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00000000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x000107ff }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xad848e19 }, + { 0x00009810, 0x7d14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x00009840, 0x206a01ae }, + { 0x0000984c, 0x1284233c }, + { 0x00009854, 0x00000859 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x05100000 }, + { 0x0000a920, 0x05100000 }, + { 0x0000b920, 0x05100000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009948, 0x9280b212 }, + { 0x0000994c, 0x00020028 }, + { 0x0000c95c, 0x004b6a8e }, + { 0x0000c968, 0x000003ce }, + { 0x00009970, 0x190fb514 }, + { 0x00009974, 0x00000000 }, + { 0x00009978, 0x00000001 }, + { 0x0000997c, 0x00000000 }, + { 0x00009980, 0x00000000 }, + { 0x00009984, 0x00000000 }, + { 0x00009988, 0x00000000 }, + { 0x0000998c, 0x00000000 }, + { 0x00009990, 0x00000000 }, + { 0x00009994, 0x00000000 }, + { 0x00009998, 0x00000000 }, + { 0x0000999c, 0x00000000 }, + { 0x000099a0, 0x00000000 }, + { 0x000099a4, 0x00000001 }, + { 0x000099a8, 0x201fff00 }, + { 0x000099ac, 0x006f0000 }, + { 0x000099b0, 0x03051000 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000200 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099fc, 0x00001042 }, + { 0x00009b00, 0x00000000 }, + { 0x00009b04, 0x00000001 }, + { 0x00009b08, 0x00000002 }, + { 0x00009b0c, 0x00000003 }, + { 0x00009b10, 0x00000004 }, + { 0x00009b14, 0x00000005 }, + { 0x00009b18, 0x00000008 }, + { 0x00009b1c, 0x00000009 }, + { 0x00009b20, 0x0000000a }, + { 0x00009b24, 0x0000000b }, + { 0x00009b28, 0x0000000c }, + { 0x00009b2c, 0x0000000d }, + { 0x00009b30, 0x00000010 }, + { 0x00009b34, 0x00000011 }, + { 0x00009b38, 0x00000012 }, + { 0x00009b3c, 0x00000013 }, + { 0x00009b40, 0x00000014 }, + { 0x00009b44, 0x00000015 }, + { 0x00009b48, 0x00000018 }, + { 0x00009b4c, 0x00000019 }, + { 0x00009b50, 0x0000001a }, + { 0x00009b54, 0x0000001b }, + { 0x00009b58, 0x0000001c }, + { 0x00009b5c, 0x0000001d }, + { 0x00009b60, 0x00000020 }, + { 0x00009b64, 0x00000021 }, + { 0x00009b68, 0x00000022 }, + { 0x00009b6c, 0x00000023 }, + { 0x00009b70, 0x00000024 }, + { 0x00009b74, 0x00000025 }, + { 0x00009b78, 0x00000028 }, + { 0x00009b7c, 0x00000029 }, + { 0x00009b80, 0x0000002a }, + { 0x00009b84, 0x0000002b }, + { 0x00009b88, 0x0000002c }, + { 0x00009b8c, 0x0000002d }, + { 0x00009b90, 0x00000030 }, + { 0x00009b94, 0x00000031 }, + { 0x00009b98, 0x00000032 }, + { 0x00009b9c, 0x00000033 }, + { 0x00009ba0, 0x00000034 }, + { 0x00009ba4, 0x00000035 }, + { 0x00009ba8, 0x00000035 }, + { 0x00009bac, 0x00000035 }, + { 0x00009bb0, 0x00000035 }, + { 0x00009bb4, 0x00000035 }, + { 0x00009bb8, 0x00000035 }, + { 0x00009bbc, 0x00000035 }, + { 0x00009bc0, 0x00000035 }, + { 0x00009bc4, 0x00000035 }, + { 0x00009bc8, 0x00000035 }, + { 0x00009bcc, 0x00000035 }, + { 0x00009bd0, 0x00000035 }, + { 0x00009bd4, 0x00000035 }, + { 0x00009bd8, 0x00000035 }, + { 0x00009bdc, 0x00000035 }, + { 0x00009be0, 0x00000035 }, + { 0x00009be4, 0x00000035 }, + { 0x00009be8, 0x00000035 }, + { 0x00009bec, 0x00000035 }, + { 0x00009bf0, 0x00000035 }, + { 0x00009bf4, 0x00000035 }, + { 0x00009bf8, 0x00000010 }, + { 0x00009bfc, 0x0000001a }, + { 0x0000a210, 0x40806333 }, + { 0x0000a214, 0x00106c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x018830c6 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x001a0bb5 }, + { 0x0000a22c, 0x00000000 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a23c, 0x13c889ae }, + { 0x0000a240, 0x38490a20 }, + { 0x0000a244, 0x00007bb6 }, + { 0x0000a248, 0x0fff3ffc }, + { 0x0000a24c, 0x00000001 }, + { 0x0000a250, 0x0000a000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0cc75380 }, + { 0x0000a25c, 0x0f0f0f01 }, + { 0x0000a260, 0xdfa91f01 }, + { 0x0000a268, 0x00000001 }, + { 0x0000a26c, 0x0ebae9c6 }, + { 0x0000b26c, 0x0ebae9c6 }, + { 0x0000c26c, 0x0ebae9c6 }, + { 0x0000d270, 0x00820820 }, + { 0x0000a278, 0x1ce739ce }, + { 0x0000a27c, 0x050701ce }, + { 0x0000a338, 0x00000000 }, + { 0x0000a33c, 0x00000000 }, + { 0x0000a340, 0x00000000 }, + { 0x0000a344, 0x00000000 }, + { 0x0000a348, 0x3fffffff }, + { 0x0000a34c, 0x3fffffff }, + { 0x0000a350, 0x3fffffff }, + { 0x0000a354, 0x0003ffff }, + { 0x0000a358, 0x79a8aa33 }, + { 0x0000d35c, 0x07ffffef }, + { 0x0000d360, 0x0fffffe7 }, + { 0x0000d364, 0x17ffffe5 }, + { 0x0000d368, 0x1fffffe4 }, + { 0x0000d36c, 0x37ffffe3 }, + { 0x0000d370, 0x3fffffe3 }, + { 0x0000d374, 0x57ffffe3 }, + { 0x0000d378, 0x5fffffe2 }, + { 0x0000d37c, 0x7fffffe2 }, + { 0x0000d380, 0x7f3c7bba }, + { 0x0000d384, 0xf3307ff0 }, + { 0x0000a388, 0x0c000000 }, + { 0x0000a38c, 0x20202020 }, + { 0x0000a390, 0x20202020 }, + { 0x0000a394, 0x1ce739ce }, + { 0x0000a398, 0x000001ce }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3a0, 0x00000000 }, + { 0x0000a3a4, 0x00000000 }, + { 0x0000a3a8, 0x00000000 }, + { 0x0000a3ac, 0x00000000 }, + { 0x0000a3b0, 0x00000000 }, + { 0x0000a3b4, 0x00000000 }, + { 0x0000a3b8, 0x00000000 }, + { 0x0000a3bc, 0x00000000 }, + { 0x0000a3c0, 0x00000000 }, + { 0x0000a3c4, 0x00000000 }, + { 0x0000a3c8, 0x00000246 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3dc, 0x1ce739ce }, + { 0x0000a3e0, 0x000001ce }, +}; + +static const u32 ar5416Bank0[][2] = { + { 0x000098b0, 0x1e5795e5 }, + { 0x000098e0, 0x02008020 }, +}; + +static const u32 ar5416BB_RfGain[][3] = { + { 0x00009a00, 0x00000000, 0x00000000 }, + { 0x00009a04, 0x00000040, 0x00000040 }, + { 0x00009a08, 0x00000080, 0x00000080 }, + { 0x00009a0c, 0x000001a1, 0x00000141 }, + { 0x00009a10, 0x000001e1, 0x00000181 }, + { 0x00009a14, 0x00000021, 0x000001c1 }, + { 0x00009a18, 0x00000061, 0x00000001 }, + { 0x00009a1c, 0x00000168, 0x00000041 }, + { 0x00009a20, 0x000001a8, 0x000001a8 }, + { 0x00009a24, 0x000001e8, 0x000001e8 }, + { 0x00009a28, 0x00000028, 0x00000028 }, + { 0x00009a2c, 0x00000068, 0x00000068 }, + { 0x00009a30, 0x00000189, 0x000000a8 }, + { 0x00009a34, 0x000001c9, 0x00000169 }, + { 0x00009a38, 0x00000009, 0x000001a9 }, + { 0x00009a3c, 0x00000049, 0x000001e9 }, + { 0x00009a40, 0x00000089, 0x00000029 }, + { 0x00009a44, 0x00000170, 0x00000069 }, + { 0x00009a48, 0x000001b0, 0x00000190 }, + { 0x00009a4c, 0x000001f0, 0x000001d0 }, + { 0x00009a50, 0x00000030, 0x00000010 }, + { 0x00009a54, 0x00000070, 0x00000050 }, + { 0x00009a58, 0x00000191, 0x00000090 }, + { 0x00009a5c, 0x000001d1, 0x00000151 }, + { 0x00009a60, 0x00000011, 0x00000191 }, + { 0x00009a64, 0x00000051, 0x000001d1 }, + { 0x00009a68, 0x00000091, 0x00000011 }, + { 0x00009a6c, 0x000001b8, 0x00000051 }, + { 0x00009a70, 0x000001f8, 0x00000198 }, + { 0x00009a74, 0x00000038, 0x000001d8 }, + { 0x00009a78, 0x00000078, 0x00000018 }, + { 0x00009a7c, 0x00000199, 0x00000058 }, + { 0x00009a80, 0x000001d9, 0x00000098 }, + { 0x00009a84, 0x00000019, 0x00000159 }, + { 0x00009a88, 0x00000059, 0x00000199 }, + { 0x00009a8c, 0x00000099, 0x000001d9 }, + { 0x00009a90, 0x000000d9, 0x00000019 }, + { 0x00009a94, 0x000000f9, 0x00000059 }, + { 0x00009a98, 0x000000f9, 0x00000099 }, + { 0x00009a9c, 0x000000f9, 0x000000d9 }, + { 0x00009aa0, 0x000000f9, 0x000000f9 }, + { 0x00009aa4, 0x000000f9, 0x000000f9 }, + { 0x00009aa8, 0x000000f9, 0x000000f9 }, + { 0x00009aac, 0x000000f9, 0x000000f9 }, + { 0x00009ab0, 0x000000f9, 0x000000f9 }, + { 0x00009ab4, 0x000000f9, 0x000000f9 }, + { 0x00009ab8, 0x000000f9, 0x000000f9 }, + { 0x00009abc, 0x000000f9, 0x000000f9 }, + { 0x00009ac0, 0x000000f9, 0x000000f9 }, + { 0x00009ac4, 0x000000f9, 0x000000f9 }, + { 0x00009ac8, 0x000000f9, 0x000000f9 }, + { 0x00009acc, 0x000000f9, 0x000000f9 }, + { 0x00009ad0, 0x000000f9, 0x000000f9 }, + { 0x00009ad4, 0x000000f9, 0x000000f9 }, + { 0x00009ad8, 0x000000f9, 0x000000f9 }, + { 0x00009adc, 0x000000f9, 0x000000f9 }, + { 0x00009ae0, 0x000000f9, 0x000000f9 }, + { 0x00009ae4, 0x000000f9, 0x000000f9 }, + { 0x00009ae8, 0x000000f9, 0x000000f9 }, + { 0x00009aec, 0x000000f9, 0x000000f9 }, + { 0x00009af0, 0x000000f9, 0x000000f9 }, + { 0x00009af4, 0x000000f9, 0x000000f9 }, + { 0x00009af8, 0x000000f9, 0x000000f9 }, + { 0x00009afc, 0x000000f9, 0x000000f9 }, +}; + +static const u32 ar5416Bank1[][2] = { + { 0x000098b0, 0x02108421}, + { 0x000098ec, 0x00000008}, +}; + +static const u32 ar5416Bank2[][2] = { + { 0x000098b0, 0x0e73ff17}, + { 0x000098e0, 0x00000420}, +}; + +static const u32 ar5416Bank3[][3] = { + { 0x000098f0, 0x01400018, 0x01c00018 }, +}; + +static const u32 ar5416Bank6[][3] = { + + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00e00000, 0x00e00000 }, + { 0x0000989c, 0x005e0000, 0x005e0000 }, + { 0x0000989c, 0x00120000, 0x00120000 }, + { 0x0000989c, 0x00620000, 0x00620000 }, + { 0x0000989c, 0x00020000, 0x00020000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x005f0000, 0x005f0000 }, + { 0x0000989c, 0x00870000, 0x00870000 }, + { 0x0000989c, 0x00f90000, 0x00f90000 }, + { 0x0000989c, 0x007b0000, 0x007b0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00f50000, 0x00f50000 }, + { 0x0000989c, 0x00dc0000, 0x00dc0000 }, + { 0x0000989c, 0x00110000, 0x00110000 }, + { 0x0000989c, 0x006100a8, 0x006100a8 }, + { 0x0000989c, 0x004210a2, 0x004210a2 }, + { 0x0000989c, 0x0014000f, 0x0014000f }, + { 0x0000989c, 0x00c40002, 0x00c40002 }, + { 0x0000989c, 0x003000f2, 0x003000f2 }, + { 0x0000989c, 0x00440016, 0x00440016 }, + { 0x0000989c, 0x00410040, 0x00410040 }, + { 0x0000989c, 0x000180d6, 0x000180d6 }, + { 0x0000989c, 0x0000c0aa, 0x0000c0aa }, + { 0x0000989c, 0x000000b1, 0x000000b1 }, + { 0x0000989c, 0x00002000, 0x00002000 }, + { 0x0000989c, 0x000000d4, 0x000000d4 }, + { 0x000098d0, 0x0000000f, 0x0010000f }, +}; + + +static const u32 ar5416Bank6TPC[][3] = { + + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00e00000, 0x00e00000 }, + { 0x0000989c, 0x005e0000, 0x005e0000 }, + { 0x0000989c, 0x00120000, 0x00120000 }, + { 0x0000989c, 0x00620000, 0x00620000 }, + { 0x0000989c, 0x00020000, 0x00020000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x40ff0000, 0x40ff0000 }, + { 0x0000989c, 0x005f0000, 0x005f0000 }, + { 0x0000989c, 0x00870000, 0x00870000 }, + { 0x0000989c, 0x00f90000, 0x00f90000 }, + { 0x0000989c, 0x007b0000, 0x007b0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00f50000, 0x00f50000 }, + { 0x0000989c, 0x00dc0000, 0x00dc0000 }, + { 0x0000989c, 0x00110000, 0x00110000 }, + { 0x0000989c, 0x006100a8, 0x006100a8 }, + { 0x0000989c, 0x00423022, 0x00423022 }, + { 0x0000989c, 0x2014008f, 0x2014008f }, + { 0x0000989c, 0x00c40002, 0x00c40002 }, + { 0x0000989c, 0x003000f2, 0x003000f2 }, + { 0x0000989c, 0x00440016, 0x00440016 }, + { 0x0000989c, 0x00410040, 0x00410040 }, + { 0x0000989c, 0x0001805e, 0x0001805e }, + { 0x0000989c, 0x0000c0ab, 0x0000c0ab }, + { 0x0000989c, 0x000000e1, 0x000000e1 }, + { 0x0000989c, 0x00007080, 0x00007080 }, + { 0x0000989c, 0x000000d4, 0x000000d4 }, + { 0x000098d0, 0x0000000f, 0x0010000f }, +}; + +static const u32 ar5416Bank7[][2] = { + { 0x0000989c, 0x00000500 }, + { 0x0000989c, 0x00000800 }, + { 0x000098cc, 0x0000000e }, +}; + +static const u32 ar5416Addac[][2] = { + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000010 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x000000c0 }, + {0x0000989c, 0x00000015 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x000098cc, 0x00000000 }, +}; + + +static const u32 ar5416Modes_9160[][6] = { + { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000, 0x00014008 }, + { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 }, + { 0x0000801c, 0x128d93a7, 0x128d93cf, 0x12e013d7, 0x12e013ab, 0x098813cf }, + { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 }, + { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, + { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, + { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 }, + { 0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0, 0x037216a0 }, + { 0x00009848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x0000a848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x0000b848, 0x001a6a65, 0x001a6a65, 0x00197a68, 0x00197a68, 0x00197a68 }, + { 0x00009850, 0x6d48b4e2, 0x6d48b4e2, 0x6d48b0e2, 0x6d48b0e2, 0x6d48b0e2 }, + { 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e }, + { 0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20, 0x00048d18 }, + { 0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0, 0x409a40d0 }, + { 0x0000986c, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081, 0x050cb081 }, + { 0x00009914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d }, + { 0x00009944, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020, 0xdfb81020 }, + { 0x00009960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40 }, + { 0x0000a960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40 }, + { 0x0000b960, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40, 0x00009b40 }, + { 0x00009964, 0x00001120, 0x00001120, 0x00001120, 0x00001120, 0x00001120 }, + { 0x0000c9bc, 0x001a0600, 0x001a0600, 0x001a0c00, 0x001a0c00, 0x001a0c00 }, + { 0x000099c0, 0x038919be, 0x038919be, 0x038919be, 0x038919be, 0x038919be }, + { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, + { 0x000099c8, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329 }, + { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 }, + { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 }, + { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a204, 0x00000880, 0x00000880, 0x00000880, 0x00000880, 0x00000880 }, + { 0x0000a208, 0xd6be4788, 0xd6be4788, 0xd03e4788, 0xd03e4788, 0xd03e4788 }, + { 0x0000a20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000b20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000c20c, 0x002fc160, 0x002fc160, 0x002ac120, 0x002ac120, 0x002ac120 }, + { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a274, 0x0a1a9caa, 0x0a1a9caa, 0x0a1a7caa, 0x0a1a7caa, 0x0a1a7caa }, + { 0x0000a300, 0x18010000, 0x18010000, 0x18010000, 0x18010000, 0x18010000 }, + { 0x0000a304, 0x30032602, 0x30032602, 0x2e032402, 0x2e032402, 0x2e032402 }, + { 0x0000a308, 0x48073e06, 0x48073e06, 0x4a0a3c06, 0x4a0a3c06, 0x4a0a3c06 }, + { 0x0000a30c, 0x560b4c0a, 0x560b4c0a, 0x621a540b, 0x621a540b, 0x621a540b }, + { 0x0000a310, 0x641a600f, 0x641a600f, 0x764f6c1b, 0x764f6c1b, 0x764f6c1b }, + { 0x0000a314, 0x7a4f6e1b, 0x7a4f6e1b, 0x845b7a5a, 0x845b7a5a, 0x845b7a5a }, + { 0x0000a318, 0x8c5b7e5a, 0x8c5b7e5a, 0x950f8ccf, 0x950f8ccf, 0x950f8ccf }, + { 0x0000a31c, 0x9d0f96cf, 0x9d0f96cf, 0xa5cf9b4f, 0xa5cf9b4f, 0xa5cf9b4f }, + { 0x0000a320, 0xb51fa69f, 0xb51fa69f, 0xbddfaf1f, 0xbddfaf1f, 0xbddfaf1f }, + { 0x0000a324, 0xcb3fbd07, 0xcb3fbcbf, 0xd1ffc93f, 0xd1ffc93f, 0xd1ffc93f }, + { 0x0000a328, 0x0000d7bf, 0x0000d7bf, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a334, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, +}; + +static const u32 ar5416Common_9160[][2] = { + { 0x0000000c, 0x00000000 }, + { 0x00000030, 0x00020015 }, + { 0x00000034, 0x00000005 }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000008 }, + { 0x00000048, 0x00000008 }, + { 0x0000004c, 0x00000010 }, + { 0x00000050, 0x00000000 }, + { 0x00000054, 0x0000001f }, + { 0x00000800, 0x00000000 }, + { 0x00000804, 0x00000000 }, + { 0x00000808, 0x00000000 }, + { 0x0000080c, 0x00000000 }, + { 0x00000810, 0x00000000 }, + { 0x00000814, 0x00000000 }, + { 0x00000818, 0x00000000 }, + { 0x0000081c, 0x00000000 }, + { 0x00000820, 0x00000000 }, + { 0x00000824, 0x00000000 }, + { 0x00001040, 0x002ffc0f }, + { 0x00001044, 0x002ffc0f }, + { 0x00001048, 0x002ffc0f }, + { 0x0000104c, 0x002ffc0f }, + { 0x00001050, 0x002ffc0f }, + { 0x00001054, 0x002ffc0f }, + { 0x00001058, 0x002ffc0f }, + { 0x0000105c, 0x002ffc0f }, + { 0x00001060, 0x002ffc0f }, + { 0x00001064, 0x002ffc0f }, + { 0x00001230, 0x00000000 }, + { 0x00001270, 0x00000000 }, + { 0x00001038, 0x00000000 }, + { 0x00001078, 0x00000000 }, + { 0x000010b8, 0x00000000 }, + { 0x000010f8, 0x00000000 }, + { 0x00001138, 0x00000000 }, + { 0x00001178, 0x00000000 }, + { 0x000011b8, 0x00000000 }, + { 0x000011f8, 0x00000000 }, + { 0x00001238, 0x00000000 }, + { 0x00001278, 0x00000000 }, + { 0x000012b8, 0x00000000 }, + { 0x000012f8, 0x00000000 }, + { 0x00001338, 0x00000000 }, + { 0x00001378, 0x00000000 }, + { 0x000013b8, 0x00000000 }, + { 0x000013f8, 0x00000000 }, + { 0x00001438, 0x00000000 }, + { 0x00001478, 0x00000000 }, + { 0x000014b8, 0x00000000 }, + { 0x000014f8, 0x00000000 }, + { 0x00001538, 0x00000000 }, + { 0x00001578, 0x00000000 }, + { 0x000015b8, 0x00000000 }, + { 0x000015f8, 0x00000000 }, + { 0x00001638, 0x00000000 }, + { 0x00001678, 0x00000000 }, + { 0x000016b8, 0x00000000 }, + { 0x000016f8, 0x00000000 }, + { 0x00001738, 0x00000000 }, + { 0x00001778, 0x00000000 }, + { 0x000017b8, 0x00000000 }, + { 0x000017f8, 0x00000000 }, + { 0x0000103c, 0x00000000 }, + { 0x0000107c, 0x00000000 }, + { 0x000010bc, 0x00000000 }, + { 0x000010fc, 0x00000000 }, + { 0x0000113c, 0x00000000 }, + { 0x0000117c, 0x00000000 }, + { 0x000011bc, 0x00000000 }, + { 0x000011fc, 0x00000000 }, + { 0x0000123c, 0x00000000 }, + { 0x0000127c, 0x00000000 }, + { 0x000012bc, 0x00000000 }, + { 0x000012fc, 0x00000000 }, + { 0x0000133c, 0x00000000 }, + { 0x0000137c, 0x00000000 }, + { 0x000013bc, 0x00000000 }, + { 0x000013fc, 0x00000000 }, + { 0x0000143c, 0x00000000 }, + { 0x0000147c, 0x00000000 }, + { 0x00004030, 0x00000002 }, + { 0x0000403c, 0x00000002 }, + { 0x00007010, 0x00000020 }, + { 0x00007038, 0x000004c2 }, + { 0x00008004, 0x00000000 }, + { 0x00008008, 0x00000000 }, + { 0x0000800c, 0x00000000 }, + { 0x00008018, 0x00000700 }, + { 0x00008020, 0x00000000 }, + { 0x00008038, 0x00000000 }, + { 0x0000803c, 0x00000000 }, + { 0x00008048, 0x40000000 }, + { 0x00008054, 0x00000000 }, + { 0x00008058, 0x00000000 }, + { 0x0000805c, 0x000fc78f }, + { 0x00008060, 0x0000000f }, + { 0x00008064, 0x00000000 }, + { 0x000080c0, 0x2a82301a }, + { 0x000080c4, 0x05dc01e0 }, + { 0x000080c8, 0x1f402710 }, + { 0x000080cc, 0x01f40000 }, + { 0x000080d0, 0x00001e00 }, + { 0x000080d4, 0x00000000 }, + { 0x000080d8, 0x00400000 }, + { 0x000080e0, 0xffffffff }, + { 0x000080e4, 0x0000ffff }, + { 0x000080e8, 0x003f3f3f }, + { 0x000080ec, 0x00000000 }, + { 0x000080f0, 0x00000000 }, + { 0x000080f4, 0x00000000 }, + { 0x000080f8, 0x00000000 }, + { 0x000080fc, 0x00020000 }, + { 0x00008100, 0x00020000 }, + { 0x00008104, 0x00000001 }, + { 0x00008108, 0x00000052 }, + { 0x0000810c, 0x00000000 }, + { 0x00008110, 0x00000168 }, + { 0x00008118, 0x000100aa }, + { 0x0000811c, 0x00003210 }, + { 0x00008120, 0x08f04800 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0x00000000 }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x32143320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c4, 0x00000000 }, + { 0x000081d0, 0x00003210 }, + { 0x000081ec, 0x00000000 }, + { 0x000081f0, 0x00000000 }, + { 0x000081f4, 0x00000000 }, + { 0x000081f8, 0x00000000 }, + { 0x000081fc, 0x00000000 }, + { 0x00008200, 0x00000000 }, + { 0x00008204, 0x00000000 }, + { 0x00008208, 0x00000000 }, + { 0x0000820c, 0x00000000 }, + { 0x00008210, 0x00000000 }, + { 0x00008214, 0x00000000 }, + { 0x00008218, 0x00000000 }, + { 0x0000821c, 0x00000000 }, + { 0x00008220, 0x00000000 }, + { 0x00008224, 0x00000000 }, + { 0x00008228, 0x00000000 }, + { 0x0000822c, 0x00000000 }, + { 0x00008230, 0x00000000 }, + { 0x00008234, 0x00000000 }, + { 0x00008238, 0x00000000 }, + { 0x0000823c, 0x00000000 }, + { 0x00008240, 0x00100000 }, + { 0x00008244, 0x0010f400 }, + { 0x00008248, 0x00000100 }, + { 0x0000824c, 0x0001e800 }, + { 0x00008250, 0x00000000 }, + { 0x00008254, 0x00000000 }, + { 0x00008258, 0x00000000 }, + { 0x0000825c, 0x400000ff }, + { 0x00008260, 0x00080922 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x00000000 }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x00008300, 0x00000000 }, + { 0x00008304, 0x00000000 }, + { 0x00008308, 0x00000000 }, + { 0x0000830c, 0x00000000 }, + { 0x00008310, 0x00000000 }, + { 0x00008314, 0x00000000 }, + { 0x00008318, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000007 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00000000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x000107ff }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xad848e19 }, + { 0x00009810, 0x7d14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x00009840, 0x206a01ae }, + { 0x0000984c, 0x1284233c }, + { 0x00009854, 0x00000859 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x05100000 }, + { 0x0000a920, 0x05100000 }, + { 0x0000b920, 0x05100000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009948, 0x9280b212 }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0x5f3ca3de }, + { 0x00009958, 0x2108ecff }, + { 0x00009940, 0x00750604 }, + { 0x0000c95c, 0x004b6a8e }, + { 0x0000c968, 0x000003ce }, + { 0x00009970, 0x190fb515 }, + { 0x00009974, 0x00000000 }, + { 0x00009978, 0x00000001 }, + { 0x0000997c, 0x00000000 }, + { 0x00009980, 0x00000000 }, + { 0x00009984, 0x00000000 }, + { 0x00009988, 0x00000000 }, + { 0x0000998c, 0x00000000 }, + { 0x00009990, 0x00000000 }, + { 0x00009994, 0x00000000 }, + { 0x00009998, 0x00000000 }, + { 0x0000999c, 0x00000000 }, + { 0x000099a0, 0x00000000 }, + { 0x000099a4, 0x00000001 }, + { 0x000099a8, 0x201fff00 }, + { 0x000099ac, 0x006f0000 }, + { 0x000099b0, 0x03051000 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000200 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099fc, 0x00001042 }, + { 0x00009b00, 0x00000000 }, + { 0x00009b04, 0x00000001 }, + { 0x00009b08, 0x00000002 }, + { 0x00009b0c, 0x00000003 }, + { 0x00009b10, 0x00000004 }, + { 0x00009b14, 0x00000005 }, + { 0x00009b18, 0x00000008 }, + { 0x00009b1c, 0x00000009 }, + { 0x00009b20, 0x0000000a }, + { 0x00009b24, 0x0000000b }, + { 0x00009b28, 0x0000000c }, + { 0x00009b2c, 0x0000000d }, + { 0x00009b30, 0x00000010 }, + { 0x00009b34, 0x00000011 }, + { 0x00009b38, 0x00000012 }, + { 0x00009b3c, 0x00000013 }, + { 0x00009b40, 0x00000014 }, + { 0x00009b44, 0x00000015 }, + { 0x00009b48, 0x00000018 }, + { 0x00009b4c, 0x00000019 }, + { 0x00009b50, 0x0000001a }, + { 0x00009b54, 0x0000001b }, + { 0x00009b58, 0x0000001c }, + { 0x00009b5c, 0x0000001d }, + { 0x00009b60, 0x00000020 }, + { 0x00009b64, 0x00000021 }, + { 0x00009b68, 0x00000022 }, + { 0x00009b6c, 0x00000023 }, + { 0x00009b70, 0x00000024 }, + { 0x00009b74, 0x00000025 }, + { 0x00009b78, 0x00000028 }, + { 0x00009b7c, 0x00000029 }, + { 0x00009b80, 0x0000002a }, + { 0x00009b84, 0x0000002b }, + { 0x00009b88, 0x0000002c }, + { 0x00009b8c, 0x0000002d }, + { 0x00009b90, 0x00000030 }, + { 0x00009b94, 0x00000031 }, + { 0x00009b98, 0x00000032 }, + { 0x00009b9c, 0x00000033 }, + { 0x00009ba0, 0x00000034 }, + { 0x00009ba4, 0x00000035 }, + { 0x00009ba8, 0x00000035 }, + { 0x00009bac, 0x00000035 }, + { 0x00009bb0, 0x00000035 }, + { 0x00009bb4, 0x00000035 }, + { 0x00009bb8, 0x00000035 }, + { 0x00009bbc, 0x00000035 }, + { 0x00009bc0, 0x00000035 }, + { 0x00009bc4, 0x00000035 }, + { 0x00009bc8, 0x00000035 }, + { 0x00009bcc, 0x00000035 }, + { 0x00009bd0, 0x00000035 }, + { 0x00009bd4, 0x00000035 }, + { 0x00009bd8, 0x00000035 }, + { 0x00009bdc, 0x00000035 }, + { 0x00009be0, 0x00000035 }, + { 0x00009be4, 0x00000035 }, + { 0x00009be8, 0x00000035 }, + { 0x00009bec, 0x00000035 }, + { 0x00009bf0, 0x00000035 }, + { 0x00009bf4, 0x00000035 }, + { 0x00009bf8, 0x00000010 }, + { 0x00009bfc, 0x0000001a }, + { 0x0000a210, 0x40806333 }, + { 0x0000a214, 0x00106c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x018830c6 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x001a0bb5 }, + { 0x0000a22c, 0x00000000 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a23c, 0x13c889af }, + { 0x0000a240, 0x38490a20 }, + { 0x0000a244, 0x00007bb6 }, + { 0x0000a248, 0x0fff3ffc }, + { 0x0000a24c, 0x00000001 }, + { 0x0000a250, 0x0000a000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0cc75380 }, + { 0x0000a25c, 0x0f0f0f01 }, + { 0x0000a260, 0xdfa91f01 }, + { 0x0000a268, 0x00000001 }, + { 0x0000a26c, 0x0ebae9c6 }, + { 0x0000b26c, 0x0ebae9c6 }, + { 0x0000c26c, 0x0ebae9c6 }, + { 0x0000d270, 0x00820820 }, + { 0x0000a278, 0x1ce739ce }, + { 0x0000a27c, 0x050701ce }, + { 0x0000a338, 0x00000000 }, + { 0x0000a33c, 0x00000000 }, + { 0x0000a340, 0x00000000 }, + { 0x0000a344, 0x00000000 }, + { 0x0000a348, 0x3fffffff }, + { 0x0000a34c, 0x3fffffff }, + { 0x0000a350, 0x3fffffff }, + { 0x0000a354, 0x0003ffff }, + { 0x0000a358, 0x79a8aa33 }, + { 0x0000d35c, 0x07ffffef }, + { 0x0000d360, 0x0fffffe7 }, + { 0x0000d364, 0x17ffffe5 }, + { 0x0000d368, 0x1fffffe4 }, + { 0x0000d36c, 0x37ffffe3 }, + { 0x0000d370, 0x3fffffe3 }, + { 0x0000d374, 0x57ffffe3 }, + { 0x0000d378, 0x5fffffe2 }, + { 0x0000d37c, 0x7fffffe2 }, + { 0x0000d380, 0x7f3c7bba }, + { 0x0000d384, 0xf3307ff0 }, + { 0x0000a388, 0x0c000000 }, + { 0x0000a38c, 0x20202020 }, + { 0x0000a390, 0x20202020 }, + { 0x0000a394, 0x1ce739ce }, + { 0x0000a398, 0x000001ce }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3a0, 0x00000000 }, + { 0x0000a3a4, 0x00000000 }, + { 0x0000a3a8, 0x00000000 }, + { 0x0000a3ac, 0x00000000 }, + { 0x0000a3b0, 0x00000000 }, + { 0x0000a3b4, 0x00000000 }, + { 0x0000a3b8, 0x00000000 }, + { 0x0000a3bc, 0x00000000 }, + { 0x0000a3c0, 0x00000000 }, + { 0x0000a3c4, 0x00000000 }, + { 0x0000a3c8, 0x00000246 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3dc, 0x1ce739ce }, + { 0x0000a3e0, 0x000001ce }, +}; + +static const u32 ar5416Bank0_9160[][2] = { + { 0x000098b0, 0x1e5795e5 }, + { 0x000098e0, 0x02008020 }, +}; + +static const u32 ar5416BB_RfGain_9160[][3] = { + { 0x00009a00, 0x00000000, 0x00000000 }, + { 0x00009a04, 0x00000040, 0x00000040 }, + { 0x00009a08, 0x00000080, 0x00000080 }, + { 0x00009a0c, 0x000001a1, 0x00000141 }, + { 0x00009a10, 0x000001e1, 0x00000181 }, + { 0x00009a14, 0x00000021, 0x000001c1 }, + { 0x00009a18, 0x00000061, 0x00000001 }, + { 0x00009a1c, 0x00000168, 0x00000041 }, + { 0x00009a20, 0x000001a8, 0x000001a8 }, + { 0x00009a24, 0x000001e8, 0x000001e8 }, + { 0x00009a28, 0x00000028, 0x00000028 }, + { 0x00009a2c, 0x00000068, 0x00000068 }, + { 0x00009a30, 0x00000189, 0x000000a8 }, + { 0x00009a34, 0x000001c9, 0x00000169 }, + { 0x00009a38, 0x00000009, 0x000001a9 }, + { 0x00009a3c, 0x00000049, 0x000001e9 }, + { 0x00009a40, 0x00000089, 0x00000029 }, + { 0x00009a44, 0x00000170, 0x00000069 }, + { 0x00009a48, 0x000001b0, 0x00000190 }, + { 0x00009a4c, 0x000001f0, 0x000001d0 }, + { 0x00009a50, 0x00000030, 0x00000010 }, + { 0x00009a54, 0x00000070, 0x00000050 }, + { 0x00009a58, 0x00000191, 0x00000090 }, + { 0x00009a5c, 0x000001d1, 0x00000151 }, + { 0x00009a60, 0x00000011, 0x00000191 }, + { 0x00009a64, 0x00000051, 0x000001d1 }, + { 0x00009a68, 0x00000091, 0x00000011 }, + { 0x00009a6c, 0x000001b8, 0x00000051 }, + { 0x00009a70, 0x000001f8, 0x00000198 }, + { 0x00009a74, 0x00000038, 0x000001d8 }, + { 0x00009a78, 0x00000078, 0x00000018 }, + { 0x00009a7c, 0x00000199, 0x00000058 }, + { 0x00009a80, 0x000001d9, 0x00000098 }, + { 0x00009a84, 0x00000019, 0x00000159 }, + { 0x00009a88, 0x00000059, 0x00000199 }, + { 0x00009a8c, 0x00000099, 0x000001d9 }, + { 0x00009a90, 0x000000d9, 0x00000019 }, + { 0x00009a94, 0x000000f9, 0x00000059 }, + { 0x00009a98, 0x000000f9, 0x00000099 }, + { 0x00009a9c, 0x000000f9, 0x000000d9 }, + { 0x00009aa0, 0x000000f9, 0x000000f9 }, + { 0x00009aa4, 0x000000f9, 0x000000f9 }, + { 0x00009aa8, 0x000000f9, 0x000000f9 }, + { 0x00009aac, 0x000000f9, 0x000000f9 }, + { 0x00009ab0, 0x000000f9, 0x000000f9 }, + { 0x00009ab4, 0x000000f9, 0x000000f9 }, + { 0x00009ab8, 0x000000f9, 0x000000f9 }, + { 0x00009abc, 0x000000f9, 0x000000f9 }, + { 0x00009ac0, 0x000000f9, 0x000000f9 }, + { 0x00009ac4, 0x000000f9, 0x000000f9 }, + { 0x00009ac8, 0x000000f9, 0x000000f9 }, + { 0x00009acc, 0x000000f9, 0x000000f9 }, + { 0x00009ad0, 0x000000f9, 0x000000f9 }, + { 0x00009ad4, 0x000000f9, 0x000000f9 }, + { 0x00009ad8, 0x000000f9, 0x000000f9 }, + { 0x00009adc, 0x000000f9, 0x000000f9 }, + { 0x00009ae0, 0x000000f9, 0x000000f9 }, + { 0x00009ae4, 0x000000f9, 0x000000f9 }, + { 0x00009ae8, 0x000000f9, 0x000000f9 }, + { 0x00009aec, 0x000000f9, 0x000000f9 }, + { 0x00009af0, 0x000000f9, 0x000000f9 }, + { 0x00009af4, 0x000000f9, 0x000000f9 }, + { 0x00009af8, 0x000000f9, 0x000000f9 }, + { 0x00009afc, 0x000000f9, 0x000000f9 }, +}; + +static const u32 ar5416Bank1_9160[][2] = { + { 0x000098b0, 0x02108421 }, + { 0x000098ec, 0x00000008 }, +}; + +static const u32 ar5416Bank2_9160[][2] = { + { 0x000098b0, 0x0e73ff17 }, + { 0x000098e0, 0x00000420 }, +}; + +static const u32 ar5416Bank3_9160[][3] = { + { 0x000098f0, 0x01400018, 0x01c00018 }, +}; + +static const u32 ar5416Bank6_9160[][3] = { + + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00e00000, 0x00e00000 }, + { 0x0000989c, 0x005e0000, 0x005e0000 }, + { 0x0000989c, 0x00120000, 0x00120000 }, + { 0x0000989c, 0x00620000, 0x00620000 }, + { 0x0000989c, 0x00020000, 0x00020000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x40ff0000, 0x40ff0000 }, + { 0x0000989c, 0x005f0000, 0x005f0000 }, + { 0x0000989c, 0x00870000, 0x00870000 }, + { 0x0000989c, 0x00f90000, 0x00f90000 }, + { 0x0000989c, 0x007b0000, 0x007b0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00f50000, 0x00f50000 }, + { 0x0000989c, 0x00dc0000, 0x00dc0000 }, + { 0x0000989c, 0x00110000, 0x00110000 }, + { 0x0000989c, 0x006100a8, 0x006100a8 }, + { 0x0000989c, 0x004210a2, 0x004210a2 }, + { 0x0000989c, 0x0014008f, 0x0014008f }, + { 0x0000989c, 0x00c40003, 0x00c40003 }, + { 0x0000989c, 0x003000f2, 0x003000f2 }, + { 0x0000989c, 0x00440016, 0x00440016 }, + { 0x0000989c, 0x00410040, 0x00410040 }, + { 0x0000989c, 0x0001805e, 0x0001805e }, + { 0x0000989c, 0x0000c0ab, 0x0000c0ab }, + { 0x0000989c, 0x000000f1, 0x000000f1 }, + { 0x0000989c, 0x00002081, 0x00002081 }, + { 0x0000989c, 0x000000d4, 0x000000d4 }, + { 0x000098d0, 0x0000000f, 0x0010000f }, +}; + +static const u32 ar5416Bank6TPC_9160[][3] = { + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00000000, 0x00000000 }, + { 0x0000989c, 0x00e00000, 0x00e00000 }, + { 0x0000989c, 0x005e0000, 0x005e0000 }, + { 0x0000989c, 0x00120000, 0x00120000 }, + { 0x0000989c, 0x00620000, 0x00620000 }, + { 0x0000989c, 0x00020000, 0x00020000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x40ff0000, 0x40ff0000 }, + { 0x0000989c, 0x005f0000, 0x005f0000 }, + { 0x0000989c, 0x00870000, 0x00870000 }, + { 0x0000989c, 0x00f90000, 0x00f90000 }, + { 0x0000989c, 0x007b0000, 0x007b0000 }, + { 0x0000989c, 0x00ff0000, 0x00ff0000 }, + { 0x0000989c, 0x00f50000, 0x00f50000 }, + { 0x0000989c, 0x00dc0000, 0x00dc0000 }, + { 0x0000989c, 0x00110000, 0x00110000 }, + { 0x0000989c, 0x006100a8, 0x006100a8 }, + { 0x0000989c, 0x00423022, 0x00423022 }, + { 0x0000989c, 0x2014008f, 0x2014008f }, + { 0x0000989c, 0x00c40002, 0x00c40002 }, + { 0x0000989c, 0x003000f2, 0x003000f2 }, + { 0x0000989c, 0x00440016, 0x00440016 }, + { 0x0000989c, 0x00410040, 0x00410040 }, + { 0x0000989c, 0x0001805e, 0x0001805e }, + { 0x0000989c, 0x0000c0ab, 0x0000c0ab }, + { 0x0000989c, 0x000000e1, 0x000000e1 }, + { 0x0000989c, 0x00007080, 0x00007080 }, + { 0x0000989c, 0x000000d4, 0x000000d4 }, + { 0x000098d0, 0x0000000f, 0x0010000f }, +}; + +static const u32 ar5416Bank7_9160[][2] = { + { 0x0000989c, 0x00000500 }, + { 0x0000989c, 0x00000800 }, + { 0x000098cc, 0x0000000e }, +}; + + +static u32 ar5416Addac_9160[][2] = { + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x000000c0 }, + {0x0000989c, 0x00000018 }, + {0x0000989c, 0x00000004 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x000000c0 }, + {0x0000989c, 0x00000019 }, + {0x0000989c, 0x00000004 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000004 }, + {0x0000989c, 0x00000003 }, + {0x0000989c, 0x00000008 }, + {0x0000989c, 0x00000000 }, + {0x000098cc, 0x00000000 }, +}; + + +static u32 ar5416Addac_91601_1[][2] = { + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x000000c0 }, + {0x0000989c, 0x00000018 }, + {0x0000989c, 0x00000004 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x000000c0 }, + {0x0000989c, 0x00000019 }, + {0x0000989c, 0x00000004 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x0000989c, 0x00000000 }, + {0x000098cc, 0x00000000 }, +}; + + + +static const u32 ar9280Modes_9280[][6] = { + { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x0000a000, 0x00014000, 0x00016000, 0x0000b000, 0x00014008 }, + { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801080, 0x08400840, 0x06e006e0 }, + { 0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b, 0x0988004f }, + { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 }, + { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, + { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, + { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 }, + { 0x00009844, 0x1372161e, 0x1372161e, 0x137216a0, 0x137216a0, 0x137216a0 }, + { 0x00009848, 0x00028566, 0x00028566, 0x00028563, 0x00028563, 0x00028563 }, + { 0x0000a848, 0x00028566, 0x00028566, 0x00028563, 0x00028563, 0x00028563 }, + { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 }, + { 0x00009858, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e, 0x7ec82d2e }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e }, + { 0x00009860, 0x00049d18, 0x00049d18, 0x00049d20, 0x00049d20, 0x00049d18 }, + { 0x0000c864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x5ac64190, 0x5ac64190, 0x5ac64190, 0x5ac64190, 0x5ac64190 }, + { 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 }, + { 0x00009914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d }, + { 0x00009944, 0xdfbc1010, 0xdfbc1010, 0xdfbc1010, 0xdfbc1010, 0xdfbc1010 }, + { 0x00009960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 }, + { 0x0000a960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 }, + { 0x00009964, 0x00000210, 0x00000210, 0x00000210, 0x00000210, 0x00000210 }, + { 0x0000c9b8, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a }, + { 0x0000c9bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, + { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, + { 0x000099c8, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c, 0x60f6532c }, + { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 }, + { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 }, + { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00009a00, 0x00008184, 0x00008184, 0x00000214, 0x00000214, 0x00000214 }, + { 0x00009a04, 0x00008188, 0x00008188, 0x00000218, 0x00000218, 0x00000218 }, + { 0x00009a08, 0x0000818c, 0x0000818c, 0x00000224, 0x00000224, 0x00000224 }, + { 0x00009a0c, 0x00008190, 0x00008190, 0x00000228, 0x00000228, 0x00000228 }, + { 0x00009a10, 0x00008194, 0x00008194, 0x0000022c, 0x0000022c, 0x0000022c }, + { 0x00009a14, 0x00008200, 0x00008200, 0x00000230, 0x00000230, 0x00000230 }, + { 0x00009a18, 0x00008204, 0x00008204, 0x000002a4, 0x000002a4, 0x000002a4 }, + { 0x00009a1c, 0x00008208, 0x00008208, 0x000002a8, 0x000002a8, 0x000002a8 }, + { 0x00009a20, 0x0000820c, 0x0000820c, 0x000002ac, 0x000002ac, 0x000002ac }, + { 0x00009a24, 0x00008210, 0x00008210, 0x000002b0, 0x000002b0, 0x000002b0 }, + { 0x00009a28, 0x00008214, 0x00008214, 0x000002b4, 0x000002b4, 0x000002b4 }, + { 0x00009a2c, 0x00008280, 0x00008280, 0x000002b8, 0x000002b8, 0x000002b8 }, + { 0x00009a30, 0x00008284, 0x00008284, 0x00000390, 0x00000390, 0x00000390 }, + { 0x00009a34, 0x00008288, 0x00008288, 0x00000394, 0x00000394, 0x00000394 }, + { 0x00009a38, 0x0000828c, 0x0000828c, 0x00000398, 0x00000398, 0x00000398 }, + { 0x00009a3c, 0x00008290, 0x00008290, 0x00000334, 0x00000334, 0x00000334 }, + { 0x00009a40, 0x00008300, 0x00008300, 0x00000338, 0x00000338, 0x00000338 }, + { 0x00009a44, 0x00008304, 0x00008304, 0x000003ac, 0x000003ac, 0x000003ac }, + { 0x00009a48, 0x00008308, 0x00008308, 0x000003b0, 0x000003b0, 0x000003b0 }, + { 0x00009a4c, 0x0000830c, 0x0000830c, 0x000003b4, 0x000003b4, 0x000003b4 }, + { 0x00009a50, 0x00008310, 0x00008310, 0x000003b8, 0x000003b8, 0x000003b8 }, + { 0x00009a54, 0x00008314, 0x00008314, 0x000003a5, 0x000003a5, 0x000003a5 }, + { 0x00009a58, 0x00008380, 0x00008380, 0x000003a9, 0x000003a9, 0x000003a9 }, + { 0x00009a5c, 0x00008384, 0x00008384, 0x000003ad, 0x000003ad, 0x000003ad }, + { 0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194, 0x00008194 }, + { 0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0, 0x000081a0 }, + { 0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c, 0x0000820c }, + { 0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8, 0x000081a8 }, + { 0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284, 0x00008284 }, + { 0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288, 0x00008288 }, + { 0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224, 0x00008224 }, + { 0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290, 0x00008290 }, + { 0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300, 0x00008300 }, + { 0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304, 0x00008304 }, + { 0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308, 0x00008308 }, + { 0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c, 0x0000830c }, + { 0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380, 0x00008380 }, + { 0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384, 0x00008384 }, + { 0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700, 0x00008700 }, + { 0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704, 0x00008704 }, + { 0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708, 0x00008708 }, + { 0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c, 0x0000870c }, + { 0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780, 0x00008780 }, + { 0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784, 0x00008784 }, + { 0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00, 0x00008b00 }, + { 0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04, 0x00008b04 }, + { 0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08, 0x00008b08 }, + { 0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c, 0x00008b0c }, + { 0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b80, 0x00008b80, 0x00008b80 }, + { 0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b84, 0x00008b84, 0x00008b84 }, + { 0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b88, 0x00008b88, 0x00008b88 }, + { 0x00009acc, 0x0000b380, 0x0000b380, 0x00008b8c, 0x00008b8c, 0x00008b8c }, + { 0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b90, 0x00008b90, 0x00008b90 }, + { 0x00009ad4, 0x0000b388, 0x0000b388, 0x00008f80, 0x00008f80, 0x00008f80 }, + { 0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008f84, 0x00008f84, 0x00008f84 }, + { 0x00009adc, 0x0000b390, 0x0000b390, 0x00008f88, 0x00008f88, 0x00008f88 }, + { 0x00009ae0, 0x0000b394, 0x0000b394, 0x00008f8c, 0x00008f8c, 0x00008f8c }, + { 0x00009ae4, 0x0000b398, 0x0000b398, 0x00008f90, 0x00008f90, 0x00008f90 }, + { 0x00009ae8, 0x0000b780, 0x0000b780, 0x0000930c, 0x0000930c, 0x0000930c }, + { 0x00009aec, 0x0000b784, 0x0000b784, 0x00009310, 0x00009310, 0x00009310 }, + { 0x00009af0, 0x0000b788, 0x0000b788, 0x00009384, 0x00009384, 0x00009384 }, + { 0x00009af4, 0x0000b78c, 0x0000b78c, 0x00009388, 0x00009388, 0x00009388 }, + { 0x00009af8, 0x0000b790, 0x0000b790, 0x00009324, 0x00009324, 0x00009324 }, + { 0x00009afc, 0x0000b794, 0x0000b794, 0x00009704, 0x00009704, 0x00009704 }, + { 0x00009b00, 0x0000b798, 0x0000b798, 0x000096a4, 0x000096a4, 0x000096a4 }, + { 0x00009b04, 0x0000d784, 0x0000d784, 0x000096a8, 0x000096a8, 0x000096a8 }, + { 0x00009b08, 0x0000d788, 0x0000d788, 0x00009710, 0x00009710, 0x00009710 }, + { 0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00009714, 0x00009714, 0x00009714 }, + { 0x00009b10, 0x0000d790, 0x0000d790, 0x00009720, 0x00009720, 0x00009720 }, + { 0x00009b14, 0x0000f780, 0x0000f780, 0x00009724, 0x00009724, 0x00009724 }, + { 0x00009b18, 0x0000f784, 0x0000f784, 0x00009728, 0x00009728, 0x00009728 }, + { 0x00009b1c, 0x0000f788, 0x0000f788, 0x0000972c, 0x0000972c, 0x0000972c }, + { 0x00009b20, 0x0000f78c, 0x0000f78c, 0x000097a0, 0x000097a0, 0x000097a0 }, + { 0x00009b24, 0x0000f790, 0x0000f790, 0x000097a4, 0x000097a4, 0x000097a4 }, + { 0x00009b28, 0x0000f794, 0x0000f794, 0x000097a8, 0x000097a8, 0x000097a8 }, + { 0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x000097b0, 0x000097b0, 0x000097b0 }, + { 0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x000097b4, 0x000097b4, 0x000097b4 }, + { 0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x000097b8, 0x000097b8, 0x000097b8 }, + { 0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x000097a5, 0x000097a5, 0x000097a5 }, + { 0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x000097a9, 0x000097a9, 0x000097a9 }, + { 0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x000097ad, 0x000097ad, 0x000097ad }, + { 0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x000097b1, 0x000097b1, 0x000097b1 }, + { 0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x000097b5, 0x000097b5, 0x000097b5 }, + { 0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x000097b9, 0x000097b9, 0x000097b9 }, + { 0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x000097c5, 0x000097c5, 0x000097c5 }, + { 0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x000097c9, 0x000097c9, 0x000097c9 }, + { 0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x000097d1, 0x000097d1, 0x000097d1 }, + { 0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x000097d5, 0x000097d5, 0x000097d5 }, + { 0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x000097d9, 0x000097d9, 0x000097d9 }, + { 0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x000097c6, 0x000097c6, 0x000097c6 }, + { 0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x000097ca, 0x000097ca, 0x000097ca }, + { 0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x000097ce, 0x000097ce, 0x000097ce }, + { 0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x000097d2, 0x000097d2, 0x000097d2 }, + { 0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x000097d6, 0x000097d6, 0x000097d6 }, + { 0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x000097c3, 0x000097c3, 0x000097c3 }, + { 0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x000097c7, 0x000097c7, 0x000097c7 }, + { 0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x000097cb, 0x000097cb, 0x000097cb }, + { 0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x000097cf, 0x000097cf, 0x000097cf }, + { 0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x000097d7, 0x000097d7, 0x000097d7 }, + { 0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009b98, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009b9c, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009ba0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009ba4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009ba8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bac, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bb0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bb4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bb8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bbc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bc0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bc4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bc8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bcc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bd0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bd4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bd8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bdc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009be0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009be4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009be8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bec, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bf0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bf4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bf8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bfc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x0000a204, 0x00000444, 0x00000444, 0x00000444, 0x00000444, 0x00000444 }, + { 0x0000a208, 0x803e4788, 0x803e4788, 0x803e4788, 0x803e4788, 0x803e4788 }, + { 0x0000a20c, 0x000c6019, 0x000c6019, 0x000c6019, 0x000c6019, 0x000c6019 }, + { 0x0000b20c, 0x000c6019, 0x000c6019, 0x000c6019, 0x000c6019, 0x000c6019 }, + { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 }, + { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a304, 0x00003002, 0x00003002, 0x00003002, 0x00003002, 0x00003002 }, + { 0x0000a308, 0x00006004, 0x00006004, 0x00008009, 0x00008009, 0x00008009 }, + { 0x0000a30c, 0x0000a006, 0x0000a006, 0x0000b00b, 0x0000b00b, 0x0000b00b }, + { 0x0000a310, 0x0000e012, 0x0000e012, 0x0000e012, 0x0000e012, 0x0000e012 }, + { 0x0000a314, 0x00011014, 0x00011014, 0x00012048, 0x00012048, 0x00012048 }, + { 0x0000a318, 0x0001504a, 0x0001504a, 0x0001604a, 0x0001604a, 0x0001604a }, + { 0x0000a31c, 0x0001904c, 0x0001904c, 0x0001a211, 0x0001a211, 0x0001a211 }, + { 0x0000a320, 0x0001c04e, 0x0001c04e, 0x0001e213, 0x0001e213, 0x0001e213 }, + { 0x0000a324, 0x00020092, 0x00020092, 0x0002121b, 0x0002121b, 0x0002121b }, + { 0x0000a328, 0x0002410a, 0x0002410a, 0x00024412, 0x00024412, 0x00024412 }, + { 0x0000a32c, 0x0002710c, 0x0002710c, 0x00028414, 0x00028414, 0x00028414 }, + { 0x0000a330, 0x0002b18b, 0x0002b18b, 0x0002b44a, 0x0002b44a, 0x0002b44a }, + { 0x0000a334, 0x0002e1cc, 0x0002e1cc, 0x00030649, 0x00030649, 0x00030649 }, + { 0x0000a338, 0x000321ec, 0x000321ec, 0x0003364b, 0x0003364b, 0x0003364b }, + { 0x0000a33c, 0x000321ec, 0x000321ec, 0x00038a49, 0x00038a49, 0x00038a49 }, + { 0x0000a340, 0x000321ec, 0x000321ec, 0x0003be48, 0x0003be48, 0x0003be48 }, + { 0x0000a344, 0x000321ec, 0x000321ec, 0x0003ee4a, 0x0003ee4a, 0x0003ee4a }, + { 0x0000a348, 0x000321ec, 0x000321ec, 0x00042e88, 0x00042e88, 0x00042e88 }, + { 0x0000a34c, 0x000321ec, 0x000321ec, 0x00046e8a, 0x00046e8a, 0x00046e8a }, + { 0x0000a350, 0x000321ec, 0x000321ec, 0x00049ec9, 0x00049ec9, 0x00049ec9 }, + { 0x0000a354, 0x000321ec, 0x000321ec, 0x0004bf42, 0x0004bf42, 0x0004bf42 }, + { 0x0000784c, 0x0e4f048c, 0x0e4f048c, 0x0e4d048c, 0x0e4d048c, 0x0e4d048c }, + { 0x00007854, 0x12031828, 0x12031828, 0x12035828, 0x12035828, 0x12035828 }, + { 0x00007870, 0x807ec400, 0x807ec400, 0x807ec000, 0x807ec000, 0x807ec000 }, + { 0x0000788c, 0x00010000, 0x00010000, 0x00110000, 0x00110000, 0x00110000 }, +}; + +static const u32 ar9280Common_9280[][2] = { + { 0x0000000c, 0x00000000 }, + { 0x00000030, 0x00020015 }, + { 0x00000034, 0x00000005 }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000008 }, + { 0x00000048, 0x00000008 }, + { 0x0000004c, 0x00000010 }, + { 0x00000050, 0x00000000 }, + { 0x00000054, 0x0000001f }, + { 0x00000800, 0x00000000 }, + { 0x00000804, 0x00000000 }, + { 0x00000808, 0x00000000 }, + { 0x0000080c, 0x00000000 }, + { 0x00000810, 0x00000000 }, + { 0x00000814, 0x00000000 }, + { 0x00000818, 0x00000000 }, + { 0x0000081c, 0x00000000 }, + { 0x00000820, 0x00000000 }, + { 0x00000824, 0x00000000 }, + { 0x00001040, 0x002ffc0f }, + { 0x00001044, 0x002ffc0f }, + { 0x00001048, 0x002ffc0f }, + { 0x0000104c, 0x002ffc0f }, + { 0x00001050, 0x002ffc0f }, + { 0x00001054, 0x002ffc0f }, + { 0x00001058, 0x002ffc0f }, + { 0x0000105c, 0x002ffc0f }, + { 0x00001060, 0x002ffc0f }, + { 0x00001064, 0x002ffc0f }, + { 0x00001230, 0x00000000 }, + { 0x00001270, 0x00000000 }, + { 0x00001038, 0x00000000 }, + { 0x00001078, 0x00000000 }, + { 0x000010b8, 0x00000000 }, + { 0x000010f8, 0x00000000 }, + { 0x00001138, 0x00000000 }, + { 0x00001178, 0x00000000 }, + { 0x000011b8, 0x00000000 }, + { 0x000011f8, 0x00000000 }, + { 0x00001238, 0x00000000 }, + { 0x00001278, 0x00000000 }, + { 0x000012b8, 0x00000000 }, + { 0x000012f8, 0x00000000 }, + { 0x00001338, 0x00000000 }, + { 0x00001378, 0x00000000 }, + { 0x000013b8, 0x00000000 }, + { 0x000013f8, 0x00000000 }, + { 0x00001438, 0x00000000 }, + { 0x00001478, 0x00000000 }, + { 0x000014b8, 0x00000000 }, + { 0x000014f8, 0x00000000 }, + { 0x00001538, 0x00000000 }, + { 0x00001578, 0x00000000 }, + { 0x000015b8, 0x00000000 }, + { 0x000015f8, 0x00000000 }, + { 0x00001638, 0x00000000 }, + { 0x00001678, 0x00000000 }, + { 0x000016b8, 0x00000000 }, + { 0x000016f8, 0x00000000 }, + { 0x00001738, 0x00000000 }, + { 0x00001778, 0x00000000 }, + { 0x000017b8, 0x00000000 }, + { 0x000017f8, 0x00000000 }, + { 0x0000103c, 0x00000000 }, + { 0x0000107c, 0x00000000 }, + { 0x000010bc, 0x00000000 }, + { 0x000010fc, 0x00000000 }, + { 0x0000113c, 0x00000000 }, + { 0x0000117c, 0x00000000 }, + { 0x000011bc, 0x00000000 }, + { 0x000011fc, 0x00000000 }, + { 0x0000123c, 0x00000000 }, + { 0x0000127c, 0x00000000 }, + { 0x000012bc, 0x00000000 }, + { 0x000012fc, 0x00000000 }, + { 0x0000133c, 0x00000000 }, + { 0x0000137c, 0x00000000 }, + { 0x000013bc, 0x00000000 }, + { 0x000013fc, 0x00000000 }, + { 0x0000143c, 0x00000000 }, + { 0x0000147c, 0x00000000 }, + { 0x00004030, 0x00000002 }, + { 0x0000403c, 0x00000002 }, + { 0x00004024, 0x0000001f }, + { 0x00007010, 0x00000033 }, + { 0x00007038, 0x000004c2 }, + { 0x00008004, 0x00000000 }, + { 0x00008008, 0x00000000 }, + { 0x0000800c, 0x00000000 }, + { 0x00008018, 0x00000700 }, + { 0x00008020, 0x00000000 }, + { 0x00008038, 0x00000000 }, + { 0x0000803c, 0x00000000 }, + { 0x00008048, 0x40000000 }, + { 0x00008054, 0x00000000 }, + { 0x00008058, 0x00000000 }, + { 0x0000805c, 0x000fc78f }, + { 0x00008060, 0x0000000f }, + { 0x00008064, 0x00000000 }, + { 0x00008070, 0x00000000 }, + { 0x000080c0, 0x2a82301a }, + { 0x000080c4, 0x05dc01e0 }, + { 0x000080c8, 0x1f402710 }, + { 0x000080cc, 0x01f40000 }, + { 0x000080d0, 0x00001e00 }, + { 0x000080d4, 0x00000000 }, + { 0x000080d8, 0x00400000 }, + { 0x000080e0, 0xffffffff }, + { 0x000080e4, 0x0000ffff }, + { 0x000080e8, 0x003f3f3f }, + { 0x000080ec, 0x00000000 }, + { 0x000080f0, 0x00000000 }, + { 0x000080f4, 0x00000000 }, + { 0x000080f8, 0x00000000 }, + { 0x000080fc, 0x00020000 }, + { 0x00008100, 0x00020000 }, + { 0x00008104, 0x00000001 }, + { 0x00008108, 0x00000052 }, + { 0x0000810c, 0x00000000 }, + { 0x00008110, 0x00000168 }, + { 0x00008118, 0x000100aa }, + { 0x0000811c, 0x00003210 }, + { 0x00008120, 0x08f04800 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0x00000000 }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x32143320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c4, 0x00000000 }, + { 0x000081d0, 0x00003210 }, + { 0x000081ec, 0x00000000 }, + { 0x000081f0, 0x00000000 }, + { 0x000081f4, 0x00000000 }, + { 0x000081f8, 0x00000000 }, + { 0x000081fc, 0x00000000 }, + { 0x00008200, 0x00000000 }, + { 0x00008204, 0x00000000 }, + { 0x00008208, 0x00000000 }, + { 0x0000820c, 0x00000000 }, + { 0x00008210, 0x00000000 }, + { 0x00008214, 0x00000000 }, + { 0x00008218, 0x00000000 }, + { 0x0000821c, 0x00000000 }, + { 0x00008220, 0x00000000 }, + { 0x00008224, 0x00000000 }, + { 0x00008228, 0x00000000 }, + { 0x0000822c, 0x00000000 }, + { 0x00008230, 0x00000000 }, + { 0x00008234, 0x00000000 }, + { 0x00008238, 0x00000000 }, + { 0x0000823c, 0x00000000 }, + { 0x00008240, 0x00100000 }, + { 0x00008244, 0x0010f400 }, + { 0x00008248, 0x00000100 }, + { 0x0000824c, 0x0001e800 }, + { 0x00008250, 0x00000000 }, + { 0x00008254, 0x00000000 }, + { 0x00008258, 0x00000000 }, + { 0x0000825c, 0x400000ff }, + { 0x00008260, 0x00080922 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x00000000 }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x00008300, 0x00000000 }, + { 0x00008304, 0x00000000 }, + { 0x00008308, 0x00000000 }, + { 0x0000830c, 0x00000000 }, + { 0x00008310, 0x00000000 }, + { 0x00008314, 0x00000000 }, + { 0x00008318, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000007 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00000000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x000107ff }, + { 0x00008344, 0x00000000 }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xaf268e30 }, + { 0x00009810, 0xfd14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x00009840, 0x206a01ae }, + { 0x0000984c, 0x0040233c }, + { 0x0000a84c, 0x0040233c }, + { 0x00009854, 0x00000044 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x04900000 }, + { 0x0000a920, 0x04900000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009948, 0x9280c00a }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0xe250a51e }, + { 0x00009958, 0x3388ffff }, + { 0x00009940, 0x00781204 }, + { 0x0000c95c, 0x004b6a8e }, + { 0x0000c968, 0x000003ce }, + { 0x00009970, 0x190fb514 }, + { 0x00009974, 0x00000000 }, + { 0x00009978, 0x00000001 }, + { 0x0000997c, 0x00000000 }, + { 0x00009980, 0x00000000 }, + { 0x00009984, 0x00000000 }, + { 0x00009988, 0x00000000 }, + { 0x0000998c, 0x00000000 }, + { 0x00009990, 0x00000000 }, + { 0x00009994, 0x00000000 }, + { 0x00009998, 0x00000000 }, + { 0x0000999c, 0x00000000 }, + { 0x000099a0, 0x00000000 }, + { 0x000099a4, 0x00000001 }, + { 0x000099a8, 0x201fff00 }, + { 0x000099ac, 0x006f00c4 }, + { 0x000099b0, 0x03051000 }, + { 0x000099b4, 0x00000820 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000000 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099fc, 0x00001042 }, + { 0x0000a210, 0x4080a333 }, + { 0x0000a214, 0x40206c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x01834061 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x000003b5 }, + { 0x0000a22c, 0x23277200 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a23c, 0x13c889af }, + { 0x0000a240, 0x38490a20 }, + { 0x0000a244, 0x00007bb6 }, + { 0x0000a248, 0x0fff3ffc }, + { 0x0000a24c, 0x00000001 }, + { 0x0000a250, 0x001da000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0cdbd380 }, + { 0x0000a25c, 0x0f0f0f01 }, + { 0x0000a260, 0xdfa91f01 }, + { 0x0000a268, 0x00000000 }, + { 0x0000a26c, 0x0ebae9c6 }, + { 0x0000b26c, 0x0ebae9c6 }, + { 0x0000d270, 0x00820820 }, + { 0x0000a278, 0x1ce739ce }, + { 0x0000a27c, 0x050701ce }, + { 0x0000a358, 0x7999aa0f }, + { 0x0000d35c, 0x07ffffef }, + { 0x0000d360, 0x0fffffe7 }, + { 0x0000d364, 0x17ffffe5 }, + { 0x0000d368, 0x1fffffe4 }, + { 0x0000d36c, 0x37ffffe3 }, + { 0x0000d370, 0x3fffffe3 }, + { 0x0000d374, 0x57ffffe3 }, + { 0x0000d378, 0x5fffffe2 }, + { 0x0000d37c, 0x7fffffe2 }, + { 0x0000d380, 0x7f3c7bba }, + { 0x0000d384, 0xf3307ff0 }, + { 0x0000a388, 0x0c000000 }, + { 0x0000a38c, 0x20202020 }, + { 0x0000a390, 0x20202020 }, + { 0x0000a394, 0x1ce739ce }, + { 0x0000a398, 0x000001ce }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3a0, 0x00000000 }, + { 0x0000a3a4, 0x00000000 }, + { 0x0000a3a8, 0x00000000 }, + { 0x0000a3ac, 0x00000000 }, + { 0x0000a3b0, 0x00000000 }, + { 0x0000a3b4, 0x00000000 }, + { 0x0000a3b8, 0x00000000 }, + { 0x0000a3bc, 0x00000000 }, + { 0x0000a3c0, 0x00000000 }, + { 0x0000a3c4, 0x00000000 }, + { 0x0000a3c8, 0x00000246 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3dc, 0x1ce739ce }, + { 0x0000a3e0, 0x000001ce }, + { 0x0000a3e4, 0x00000000 }, + { 0x0000a3e8, 0x18c43433 }, + { 0x0000a3ec, 0x00f38081 }, + { 0x00007800, 0x00040000 }, + { 0x00007804, 0xdb005012 }, + { 0x00007808, 0x04924914 }, + { 0x0000780c, 0x21084210 }, + { 0x00007810, 0x6d801300 }, + { 0x00007814, 0x0019beff }, + { 0x00007818, 0x07e40000 }, + { 0x0000781c, 0x00492000 }, + { 0x00007820, 0x92492480 }, + { 0x00007824, 0x00040000 }, + { 0x00007828, 0xdb005012 }, + { 0x0000782c, 0x04924914 }, + { 0x00007830, 0x21084210 }, + { 0x00007834, 0x6d801300 }, + { 0x00007838, 0x0019beff }, + { 0x0000783c, 0x07e40000 }, + { 0x00007840, 0x00492000 }, + { 0x00007844, 0x92492480 }, + { 0x00007848, 0x00120000 }, + { 0x00007850, 0x54214514 }, + { 0x00007858, 0x92592692 }, + { 0x00007860, 0x52802000 }, + { 0x00007864, 0x0a8e370e }, + { 0x00007868, 0xc0102850 }, + { 0x0000786c, 0x812d4000 }, + { 0x00007874, 0x001b6db0 }, + { 0x00007878, 0x00376b63 }, + { 0x0000787c, 0x06db6db6 }, + { 0x00007880, 0x006d8000 }, + { 0x00007884, 0xffeffffe }, + { 0x00007888, 0xffeffffe }, + { 0x00007890, 0x00060aeb }, + { 0x00007894, 0x5a108000 }, + { 0x00007898, 0x2a850160 }, +}; + + + + +static const u32 ar9280Modes_9280_2[][6] = { + { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008 }, + { 0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00, 0x06e006e0 }, + { 0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b, 0x0988004f }, + { 0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880 }, + { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 }, + { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, + { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, + { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 }, + { 0x00009840, 0x206a012e, 0x206a012e, 0x206a022e, 0x206a022e, 0x206a022e }, + { 0x00009844, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0, 0x037216a0 }, + { 0x00009848, 0x00001066, 0x00001066, 0x00001063, 0x00001063, 0x00001063 }, + { 0x0000a848, 0x00001066, 0x00001066, 0x00001063, 0x00001063, 0x00001063 }, + { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 }, + { 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec88d2e, 0x7ec88d2e, 0x7ec88d2e }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e, 0x3139605e }, + { 0x00009860, 0x00048d18, 0x00048d18, 0x00048d20, 0x00048d20, 0x00048d18 }, + { 0x0000c864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 }, + { 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 }, + { 0x00009914, 0x000007d0, 0x000007d0, 0x00000898, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8a07, 0xd00a8a07, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d }, + { 0x00009944, 0xdfbc1010, 0xdfbc1010, 0xdfbc1010, 0xdfbc1010, 0xdfbc1010 }, + { 0x00009960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 }, + { 0x0000a960, 0x00000010, 0x00000010, 0x00000010, 0x00000010, 0x00000010 }, + { 0x00009964, 0x00000210, 0x00000210, 0x00000210, 0x00000210, 0x00000210 }, + { 0x0000c9b8, 0x0000000f, 0x0000000f, 0x0000001c, 0x0000001c, 0x0000001c }, + { 0x0000c9bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, + { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, + { 0x000099c8, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329, 0x60f65329 }, + { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 }, + { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 }, + { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00009a00, 0x00008184, 0x00008184, 0x00000290, 0x00000290, 0x00000290 }, + { 0x00009a04, 0x00008188, 0x00008188, 0x00000300, 0x00000300, 0x00000300 }, + { 0x00009a08, 0x0000818c, 0x0000818c, 0x00000304, 0x00000304, 0x00000304 }, + { 0x00009a0c, 0x00008190, 0x00008190, 0x00000308, 0x00000308, 0x00000308 }, + { 0x00009a10, 0x00008194, 0x00008194, 0x0000030c, 0x0000030c, 0x0000030c }, + { 0x00009a14, 0x00008200, 0x00008200, 0x00008000, 0x00008000, 0x00008000 }, + { 0x00009a18, 0x00008204, 0x00008204, 0x00008004, 0x00008004, 0x00008004 }, + { 0x00009a1c, 0x00008208, 0x00008208, 0x00008008, 0x00008008, 0x00008008 }, + { 0x00009a20, 0x0000820c, 0x0000820c, 0x0000800c, 0x0000800c, 0x0000800c }, + { 0x00009a24, 0x00008210, 0x00008210, 0x00008080, 0x00008080, 0x00008080 }, + { 0x00009a28, 0x00008214, 0x00008214, 0x00008084, 0x00008084, 0x00008084 }, + { 0x00009a2c, 0x00008280, 0x00008280, 0x00008088, 0x00008088, 0x00008088 }, + { 0x00009a30, 0x00008284, 0x00008284, 0x0000808c, 0x0000808c, 0x0000808c }, + { 0x00009a34, 0x00008288, 0x00008288, 0x00008100, 0x00008100, 0x00008100 }, + { 0x00009a38, 0x0000828c, 0x0000828c, 0x00008104, 0x00008104, 0x00008104 }, + { 0x00009a3c, 0x00008290, 0x00008290, 0x00008108, 0x00008108, 0x00008108 }, + { 0x00009a40, 0x00008300, 0x00008300, 0x0000810c, 0x0000810c, 0x0000810c }, + { 0x00009a44, 0x00008304, 0x00008304, 0x00008110, 0x00008110, 0x00008110 }, + { 0x00009a48, 0x00008308, 0x00008308, 0x00008114, 0x00008114, 0x00008114 }, + { 0x00009a4c, 0x0000830c, 0x0000830c, 0x00008180, 0x00008180, 0x00008180 }, + { 0x00009a50, 0x00008310, 0x00008310, 0x00008184, 0x00008184, 0x00008184 }, + { 0x00009a54, 0x00008314, 0x00008314, 0x00008188, 0x00008188, 0x00008188 }, + { 0x00009a58, 0x00008380, 0x00008380, 0x0000818c, 0x0000818c, 0x0000818c }, + { 0x00009a5c, 0x00008384, 0x00008384, 0x00008190, 0x00008190, 0x00008190 }, + { 0x00009a60, 0x00008388, 0x00008388, 0x00008194, 0x00008194, 0x00008194 }, + { 0x00009a64, 0x0000838c, 0x0000838c, 0x000081a0, 0x000081a0, 0x000081a0 }, + { 0x00009a68, 0x00008390, 0x00008390, 0x0000820c, 0x0000820c, 0x0000820c }, + { 0x00009a6c, 0x00008394, 0x00008394, 0x000081a8, 0x000081a8, 0x000081a8 }, + { 0x00009a70, 0x0000a380, 0x0000a380, 0x00008284, 0x00008284, 0x00008284 }, + { 0x00009a74, 0x0000a384, 0x0000a384, 0x00008288, 0x00008288, 0x00008288 }, + { 0x00009a78, 0x0000a388, 0x0000a388, 0x00008224, 0x00008224, 0x00008224 }, + { 0x00009a7c, 0x0000a38c, 0x0000a38c, 0x00008290, 0x00008290, 0x00008290 }, + { 0x00009a80, 0x0000a390, 0x0000a390, 0x00008300, 0x00008300, 0x00008300 }, + { 0x00009a84, 0x0000a394, 0x0000a394, 0x00008304, 0x00008304, 0x00008304 }, + { 0x00009a88, 0x0000a780, 0x0000a780, 0x00008308, 0x00008308, 0x00008308 }, + { 0x00009a8c, 0x0000a784, 0x0000a784, 0x0000830c, 0x0000830c, 0x0000830c }, + { 0x00009a90, 0x0000a788, 0x0000a788, 0x00008380, 0x00008380, 0x00008380 }, + { 0x00009a94, 0x0000a78c, 0x0000a78c, 0x00008384, 0x00008384, 0x00008384 }, + { 0x00009a98, 0x0000a790, 0x0000a790, 0x00008700, 0x00008700, 0x00008700 }, + { 0x00009a9c, 0x0000a794, 0x0000a794, 0x00008704, 0x00008704, 0x00008704 }, + { 0x00009aa0, 0x0000ab84, 0x0000ab84, 0x00008708, 0x00008708, 0x00008708 }, + { 0x00009aa4, 0x0000ab88, 0x0000ab88, 0x0000870c, 0x0000870c, 0x0000870c }, + { 0x00009aa8, 0x0000ab8c, 0x0000ab8c, 0x00008780, 0x00008780, 0x00008780 }, + { 0x00009aac, 0x0000ab90, 0x0000ab90, 0x00008784, 0x00008784, 0x00008784 }, + { 0x00009ab0, 0x0000ab94, 0x0000ab94, 0x00008b00, 0x00008b00, 0x00008b00 }, + { 0x00009ab4, 0x0000af80, 0x0000af80, 0x00008b04, 0x00008b04, 0x00008b04 }, + { 0x00009ab8, 0x0000af84, 0x0000af84, 0x00008b08, 0x00008b08, 0x00008b08 }, + { 0x00009abc, 0x0000af88, 0x0000af88, 0x00008b0c, 0x00008b0c, 0x00008b0c }, + { 0x00009ac0, 0x0000af8c, 0x0000af8c, 0x00008b80, 0x00008b80, 0x00008b80 }, + { 0x00009ac4, 0x0000af90, 0x0000af90, 0x00008b84, 0x00008b84, 0x00008b84 }, + { 0x00009ac8, 0x0000af94, 0x0000af94, 0x00008b88, 0x00008b88, 0x00008b88 }, + { 0x00009acc, 0x0000b380, 0x0000b380, 0x00008b8c, 0x00008b8c, 0x00008b8c }, + { 0x00009ad0, 0x0000b384, 0x0000b384, 0x00008b90, 0x00008b90, 0x00008b90 }, + { 0x00009ad4, 0x0000b388, 0x0000b388, 0x00008f80, 0x00008f80, 0x00008f80 }, + { 0x00009ad8, 0x0000b38c, 0x0000b38c, 0x00008f84, 0x00008f84, 0x00008f84 }, + { 0x00009adc, 0x0000b390, 0x0000b390, 0x00008f88, 0x00008f88, 0x00008f88 }, + { 0x00009ae0, 0x0000b394, 0x0000b394, 0x00008f8c, 0x00008f8c, 0x00008f8c }, + { 0x00009ae4, 0x0000b398, 0x0000b398, 0x00008f90, 0x00008f90, 0x00008f90 }, + { 0x00009ae8, 0x0000b780, 0x0000b780, 0x0000930c, 0x0000930c, 0x0000930c }, + { 0x00009aec, 0x0000b784, 0x0000b784, 0x00009310, 0x00009310, 0x00009310 }, + { 0x00009af0, 0x0000b788, 0x0000b788, 0x00009384, 0x00009384, 0x00009384 }, + { 0x00009af4, 0x0000b78c, 0x0000b78c, 0x00009388, 0x00009388, 0x00009388 }, + { 0x00009af8, 0x0000b790, 0x0000b790, 0x00009324, 0x00009324, 0x00009324 }, + { 0x00009afc, 0x0000b794, 0x0000b794, 0x00009704, 0x00009704, 0x00009704 }, + { 0x00009b00, 0x0000b798, 0x0000b798, 0x000096a4, 0x000096a4, 0x000096a4 }, + { 0x00009b04, 0x0000d784, 0x0000d784, 0x000096a8, 0x000096a8, 0x000096a8 }, + { 0x00009b08, 0x0000d788, 0x0000d788, 0x00009710, 0x00009710, 0x00009710 }, + { 0x00009b0c, 0x0000d78c, 0x0000d78c, 0x00009714, 0x00009714, 0x00009714 }, + { 0x00009b10, 0x0000d790, 0x0000d790, 0x00009720, 0x00009720, 0x00009720 }, + { 0x00009b14, 0x0000f780, 0x0000f780, 0x00009724, 0x00009724, 0x00009724 }, + { 0x00009b18, 0x0000f784, 0x0000f784, 0x00009728, 0x00009728, 0x00009728 }, + { 0x00009b1c, 0x0000f788, 0x0000f788, 0x0000972c, 0x0000972c, 0x0000972c }, + { 0x00009b20, 0x0000f78c, 0x0000f78c, 0x000097a0, 0x000097a0, 0x000097a0 }, + { 0x00009b24, 0x0000f790, 0x0000f790, 0x000097a4, 0x000097a4, 0x000097a4 }, + { 0x00009b28, 0x0000f794, 0x0000f794, 0x000097a8, 0x000097a8, 0x000097a8 }, + { 0x00009b2c, 0x0000f7a4, 0x0000f7a4, 0x000097b0, 0x000097b0, 0x000097b0 }, + { 0x00009b30, 0x0000f7a8, 0x0000f7a8, 0x000097b4, 0x000097b4, 0x000097b4 }, + { 0x00009b34, 0x0000f7ac, 0x0000f7ac, 0x000097b8, 0x000097b8, 0x000097b8 }, + { 0x00009b38, 0x0000f7b0, 0x0000f7b0, 0x000097a5, 0x000097a5, 0x000097a5 }, + { 0x00009b3c, 0x0000f7b4, 0x0000f7b4, 0x000097a9, 0x000097a9, 0x000097a9 }, + { 0x00009b40, 0x0000f7a1, 0x0000f7a1, 0x000097ad, 0x000097ad, 0x000097ad }, + { 0x00009b44, 0x0000f7a5, 0x0000f7a5, 0x000097b1, 0x000097b1, 0x000097b1 }, + { 0x00009b48, 0x0000f7a9, 0x0000f7a9, 0x000097b5, 0x000097b5, 0x000097b5 }, + { 0x00009b4c, 0x0000f7ad, 0x0000f7ad, 0x000097b9, 0x000097b9, 0x000097b9 }, + { 0x00009b50, 0x0000f7b1, 0x0000f7b1, 0x000097c5, 0x000097c5, 0x000097c5 }, + { 0x00009b54, 0x0000f7b5, 0x0000f7b5, 0x000097c9, 0x000097c9, 0x000097c9 }, + { 0x00009b58, 0x0000f7c5, 0x0000f7c5, 0x000097d1, 0x000097d1, 0x000097d1 }, + { 0x00009b5c, 0x0000f7c9, 0x0000f7c9, 0x000097d5, 0x000097d5, 0x000097d5 }, + { 0x00009b60, 0x0000f7cd, 0x0000f7cd, 0x000097d9, 0x000097d9, 0x000097d9 }, + { 0x00009b64, 0x0000f7d1, 0x0000f7d1, 0x000097c6, 0x000097c6, 0x000097c6 }, + { 0x00009b68, 0x0000f7d5, 0x0000f7d5, 0x000097ca, 0x000097ca, 0x000097ca }, + { 0x00009b6c, 0x0000f7c2, 0x0000f7c2, 0x000097ce, 0x000097ce, 0x000097ce }, + { 0x00009b70, 0x0000f7c6, 0x0000f7c6, 0x000097d2, 0x000097d2, 0x000097d2 }, + { 0x00009b74, 0x0000f7ca, 0x0000f7ca, 0x000097d6, 0x000097d6, 0x000097d6 }, + { 0x00009b78, 0x0000f7ce, 0x0000f7ce, 0x000097c3, 0x000097c3, 0x000097c3 }, + { 0x00009b7c, 0x0000f7d2, 0x0000f7d2, 0x000097c7, 0x000097c7, 0x000097c7 }, + { 0x00009b80, 0x0000f7d6, 0x0000f7d6, 0x000097cb, 0x000097cb, 0x000097cb }, + { 0x00009b84, 0x0000f7c3, 0x0000f7c3, 0x000097cf, 0x000097cf, 0x000097cf }, + { 0x00009b88, 0x0000f7c7, 0x0000f7c7, 0x000097d7, 0x000097d7, 0x000097d7 }, + { 0x00009b8c, 0x0000f7cb, 0x0000f7cb, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009b90, 0x0000f7d3, 0x0000f7d3, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009b94, 0x0000f7d7, 0x0000f7d7, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009b98, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009b9c, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009ba0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009ba4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009ba8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bac, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bb0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bb4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bb8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bbc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bc0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bc4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bc8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bcc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bd0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bd4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bd8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bdc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009be0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009be4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009be8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bec, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bf0, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bf4, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bf8, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x00009bfc, 0x0000f7db, 0x0000f7db, 0x000097db, 0x000097db, 0x000097db }, + { 0x0000a204, 0x00000444, 0x00000444, 0x00000444, 0x00000444, 0x00000444 }, + { 0x0000a208, 0x803e4788, 0x803e4788, 0x803e4788, 0x803e4788, 0x803e4788 }, + { 0x0000a20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019, 0x0001f019 }, + { 0x0000b20c, 0x00000014, 0x00000014, 0x0001f019, 0x0001f019, 0x0001f019 }, + { 0x0000a21c, 0x1463800a, 0x1463800a, 0x1463800a, 0x1463800a, 0x1463800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a250, 0x001ff000, 0x001ff000, 0x001da000, 0x001da000, 0x001da000 }, + { 0x0000a274, 0x0a19c652, 0x0a19c652, 0x0a1aa652, 0x0a1aa652, 0x0a1aa652 }, + { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a304, 0x00003002, 0x00003002, 0x00003002, 0x00003002, 0x00003002 }, + { 0x0000a308, 0x00006004, 0x00006004, 0x00008009, 0x00008009, 0x00008009 }, + { 0x0000a30c, 0x0000a006, 0x0000a006, 0x0000b00b, 0x0000b00b, 0x0000b00b }, + { 0x0000a310, 0x0000e012, 0x0000e012, 0x0000e012, 0x0000e012, 0x0000e012 }, + { 0x0000a314, 0x00011014, 0x00011014, 0x00012048, 0x00012048, 0x00012048 }, + { 0x0000a318, 0x0001504a, 0x0001504a, 0x0001604a, 0x0001604a, 0x0001604a }, + { 0x0000a31c, 0x0001904c, 0x0001904c, 0x0001a211, 0x0001a211, 0x0001a211 }, + { 0x0000a320, 0x0001c04e, 0x0001c04e, 0x0001e213, 0x0001e213, 0x0001e213 }, + { 0x0000a324, 0x00020092, 0x00020092, 0x0002121b, 0x0002121b, 0x0002121b }, + { 0x0000a328, 0x0002410a, 0x0002410a, 0x00024412, 0x00024412, 0x00024412 }, + { 0x0000a32c, 0x0002710c, 0x0002710c, 0x00028414, 0x00028414, 0x00028414 }, + { 0x0000a330, 0x0002b18b, 0x0002b18b, 0x0002b44a, 0x0002b44a, 0x0002b44a }, + { 0x0000a334, 0x0002e1cc, 0x0002e1cc, 0x00030649, 0x00030649, 0x00030649 }, + { 0x0000a338, 0x000321ec, 0x000321ec, 0x0003364b, 0x0003364b, 0x0003364b }, + { 0x0000a33c, 0x000321ec, 0x000321ec, 0x00038a49, 0x00038a49, 0x00038a49 }, + { 0x0000a340, 0x000321ec, 0x000321ec, 0x0003be48, 0x0003be48, 0x0003be48 }, + { 0x0000a344, 0x000321ec, 0x000321ec, 0x0003ee4a, 0x0003ee4a, 0x0003ee4a }, + { 0x0000a348, 0x000321ec, 0x000321ec, 0x00042e88, 0x00042e88, 0x00042e88 }, + { 0x0000a34c, 0x000321ec, 0x000321ec, 0x00046e8a, 0x00046e8a, 0x00046e8a }, + { 0x0000a350, 0x000321ec, 0x000321ec, 0x00049ec9, 0x00049ec9, 0x00049ec9 }, + { 0x0000a354, 0x000321ec, 0x000321ec, 0x0004bf42, 0x0004bf42, 0x0004bf42 }, + { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e }, + { 0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00007894, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000, 0x5a508000 }, +}; + +static const u32 ar9280Common_9280_2[][2] = { + { 0x0000000c, 0x00000000 }, + { 0x00000030, 0x00020015 }, + { 0x00000034, 0x00000005 }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000008 }, + { 0x00000048, 0x00000008 }, + { 0x0000004c, 0x00000010 }, + { 0x00000050, 0x00000000 }, + { 0x00000054, 0x0000001f }, + { 0x00000800, 0x00000000 }, + { 0x00000804, 0x00000000 }, + { 0x00000808, 0x00000000 }, + { 0x0000080c, 0x00000000 }, + { 0x00000810, 0x00000000 }, + { 0x00000814, 0x00000000 }, + { 0x00000818, 0x00000000 }, + { 0x0000081c, 0x00000000 }, + { 0x00000820, 0x00000000 }, + { 0x00000824, 0x00000000 }, + { 0x00001040, 0x002ffc0f }, + { 0x00001044, 0x002ffc0f }, + { 0x00001048, 0x002ffc0f }, + { 0x0000104c, 0x002ffc0f }, + { 0x00001050, 0x002ffc0f }, + { 0x00001054, 0x002ffc0f }, + { 0x00001058, 0x002ffc0f }, + { 0x0000105c, 0x002ffc0f }, + { 0x00001060, 0x002ffc0f }, + { 0x00001064, 0x002ffc0f }, + { 0x00001230, 0x00000000 }, + { 0x00001270, 0x00000000 }, + { 0x00001038, 0x00000000 }, + { 0x00001078, 0x00000000 }, + { 0x000010b8, 0x00000000 }, + { 0x000010f8, 0x00000000 }, + { 0x00001138, 0x00000000 }, + { 0x00001178, 0x00000000 }, + { 0x000011b8, 0x00000000 }, + { 0x000011f8, 0x00000000 }, + { 0x00001238, 0x00000000 }, + { 0x00001278, 0x00000000 }, + { 0x000012b8, 0x00000000 }, + { 0x000012f8, 0x00000000 }, + { 0x00001338, 0x00000000 }, + { 0x00001378, 0x00000000 }, + { 0x000013b8, 0x00000000 }, + { 0x000013f8, 0x00000000 }, + { 0x00001438, 0x00000000 }, + { 0x00001478, 0x00000000 }, + { 0x000014b8, 0x00000000 }, + { 0x000014f8, 0x00000000 }, + { 0x00001538, 0x00000000 }, + { 0x00001578, 0x00000000 }, + { 0x000015b8, 0x00000000 }, + { 0x000015f8, 0x00000000 }, + { 0x00001638, 0x00000000 }, + { 0x00001678, 0x00000000 }, + { 0x000016b8, 0x00000000 }, + { 0x000016f8, 0x00000000 }, + { 0x00001738, 0x00000000 }, + { 0x00001778, 0x00000000 }, + { 0x000017b8, 0x00000000 }, + { 0x000017f8, 0x00000000 }, + { 0x0000103c, 0x00000000 }, + { 0x0000107c, 0x00000000 }, + { 0x000010bc, 0x00000000 }, + { 0x000010fc, 0x00000000 }, + { 0x0000113c, 0x00000000 }, + { 0x0000117c, 0x00000000 }, + { 0x000011bc, 0x00000000 }, + { 0x000011fc, 0x00000000 }, + { 0x0000123c, 0x00000000 }, + { 0x0000127c, 0x00000000 }, + { 0x000012bc, 0x00000000 }, + { 0x000012fc, 0x00000000 }, + { 0x0000133c, 0x00000000 }, + { 0x0000137c, 0x00000000 }, + { 0x000013bc, 0x00000000 }, + { 0x000013fc, 0x00000000 }, + { 0x0000143c, 0x00000000 }, + { 0x0000147c, 0x00000000 }, + { 0x00004030, 0x00000002 }, + { 0x0000403c, 0x00000002 }, + { 0x00004024, 0x0000001f }, + { 0x00004060, 0x00000000 }, + { 0x00004064, 0x00000000 }, + { 0x00007010, 0x00000033 }, + { 0x00007034, 0x00000002 }, + { 0x00007038, 0x000004c2 }, + { 0x00008004, 0x00000000 }, + { 0x00008008, 0x00000000 }, + { 0x0000800c, 0x00000000 }, + { 0x00008018, 0x00000700 }, + { 0x00008020, 0x00000000 }, + { 0x00008038, 0x00000000 }, + { 0x0000803c, 0x00000000 }, + { 0x00008048, 0x40000000 }, + { 0x00008054, 0x00000000 }, + { 0x00008058, 0x00000000 }, + { 0x0000805c, 0x000fc78f }, + { 0x00008060, 0x0000000f }, + { 0x00008064, 0x00000000 }, + { 0x00008070, 0x00000000 }, + { 0x000080c0, 0x2a80001a }, + { 0x000080c4, 0x05dc01e0 }, + { 0x000080c8, 0x1f402710 }, + { 0x000080cc, 0x01f40000 }, + { 0x000080d0, 0x00001e00 }, + { 0x000080d4, 0x00000000 }, + { 0x000080d8, 0x00400000 }, + { 0x000080e0, 0xffffffff }, + { 0x000080e4, 0x0000ffff }, + { 0x000080e8, 0x003f3f3f }, + { 0x000080ec, 0x00000000 }, + { 0x000080f0, 0x00000000 }, + { 0x000080f4, 0x00000000 }, + { 0x000080f8, 0x00000000 }, + { 0x000080fc, 0x00020000 }, + { 0x00008100, 0x00020000 }, + { 0x00008104, 0x00000001 }, + { 0x00008108, 0x00000052 }, + { 0x0000810c, 0x00000000 }, + { 0x00008110, 0x00000168 }, + { 0x00008118, 0x000100aa }, + { 0x0000811c, 0x00003210 }, + { 0x00008120, 0x08f04800 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0x00000000 }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x32143320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c0, 0x00000000 }, + { 0x000081d0, 0x00003210 }, + { 0x000081ec, 0x00000000 }, + { 0x000081f0, 0x00000000 }, + { 0x000081f4, 0x00000000 }, + { 0x000081f8, 0x00000000 }, + { 0x000081fc, 0x00000000 }, + { 0x00008200, 0x00000000 }, + { 0x00008204, 0x00000000 }, + { 0x00008208, 0x00000000 }, + { 0x0000820c, 0x00000000 }, + { 0x00008210, 0x00000000 }, + { 0x00008214, 0x00000000 }, + { 0x00008218, 0x00000000 }, + { 0x0000821c, 0x00000000 }, + { 0x00008220, 0x00000000 }, + { 0x00008224, 0x00000000 }, + { 0x00008228, 0x00000000 }, + { 0x0000822c, 0x00000000 }, + { 0x00008230, 0x00000000 }, + { 0x00008234, 0x00000000 }, + { 0x00008238, 0x00000000 }, + { 0x0000823c, 0x00000000 }, + { 0x00008240, 0x00100000 }, + { 0x00008244, 0x0010f400 }, + { 0x00008248, 0x00000100 }, + { 0x0000824c, 0x0001e800 }, + { 0x00008250, 0x00000000 }, + { 0x00008254, 0x00000000 }, + { 0x00008258, 0x00000000 }, + { 0x0000825c, 0x400000ff }, + { 0x00008260, 0x00080922 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x00000000 }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x0000829c, 0x00000000 }, + { 0x00008300, 0x00000040 }, + { 0x00008314, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000007 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00000000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x000107ff }, + { 0x00008344, 0x00581043 }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xafa68e30 }, + { 0x00009810, 0xfd14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x0000984c, 0x0040233c }, + { 0x0000a84c, 0x0040233c }, + { 0x00009854, 0x00000044 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x00009910, 0x01002310 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x04900000 }, + { 0x0000a920, 0x04900000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009948, 0x9280c00a }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0x5f3ca3de }, + { 0x00009958, 0x2108ecff }, + { 0x00009940, 0x14750604 }, + { 0x0000c95c, 0x004b6a8e }, + { 0x0000c968, 0x000003ce }, + { 0x00009970, 0x190fb515 }, + { 0x00009974, 0x00000000 }, + { 0x00009978, 0x00000001 }, + { 0x0000997c, 0x00000000 }, + { 0x00009980, 0x00000000 }, + { 0x00009984, 0x00000000 }, + { 0x00009988, 0x00000000 }, + { 0x0000998c, 0x00000000 }, + { 0x00009990, 0x00000000 }, + { 0x00009994, 0x00000000 }, + { 0x00009998, 0x00000000 }, + { 0x0000999c, 0x00000000 }, + { 0x000099a0, 0x00000000 }, + { 0x000099a4, 0x00000001 }, + { 0x000099a8, 0x201fff00 }, + { 0x000099ac, 0x006f0000 }, + { 0x000099b0, 0x03051000 }, + { 0x000099b4, 0x00000820 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000000 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099f0, 0x00000000 }, + { 0x000099fc, 0x00001042 }, + { 0x0000a210, 0x4080a333 }, + { 0x0000a214, 0x40206c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x01834061 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x000003b5 }, + { 0x0000a22c, 0x233f71c0 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a23c, 0x13c88000 }, + { 0x0000a240, 0x38490a20 }, + { 0x0000a244, 0x00007bb6 }, + { 0x0000a248, 0x0fff3ffc }, + { 0x0000a24c, 0x00000000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0cdbd380 }, + { 0x0000a25c, 0x0f0f0f01 }, + { 0x0000a260, 0xdfa91f01 }, + { 0x0000a268, 0x00000000 }, + { 0x0000a26c, 0x0ebae9c6 }, + { 0x0000b26c, 0x0ebae9c6 }, + { 0x0000d270, 0x00820820 }, + { 0x0000a278, 0x1ce739ce }, + { 0x0000a27c, 0x050701ce }, + { 0x0000d35c, 0x07ffffef }, + { 0x0000d360, 0x0fffffe7 }, + { 0x0000d364, 0x17ffffe5 }, + { 0x0000d368, 0x1fffffe4 }, + { 0x0000d36c, 0x37ffffe3 }, + { 0x0000d370, 0x3fffffe3 }, + { 0x0000d374, 0x57ffffe3 }, + { 0x0000d378, 0x5fffffe2 }, + { 0x0000d37c, 0x7fffffe2 }, + { 0x0000d380, 0x7f3c7bba }, + { 0x0000d384, 0xf3307ff0 }, + { 0x0000a388, 0x0c000000 }, + { 0x0000a38c, 0x20202020 }, + { 0x0000a390, 0x20202020 }, + { 0x0000a394, 0x1ce739ce }, + { 0x0000a398, 0x000001ce }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3a0, 0x00000000 }, + { 0x0000a3a4, 0x00000000 }, + { 0x0000a3a8, 0x00000000 }, + { 0x0000a3ac, 0x00000000 }, + { 0x0000a3b0, 0x00000000 }, + { 0x0000a3b4, 0x00000000 }, + { 0x0000a3b8, 0x00000000 }, + { 0x0000a3bc, 0x00000000 }, + { 0x0000a3c0, 0x00000000 }, + { 0x0000a3c4, 0x00000000 }, + { 0x0000a3c8, 0x00000246 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3dc, 0x1ce739ce }, + { 0x0000a3e0, 0x000001ce }, + { 0x0000a3e4, 0x00000000 }, + { 0x0000a3e8, 0x18c43433 }, + { 0x0000a3ec, 0x00f70081 }, + { 0x00007800, 0x00040000 }, + { 0x00007804, 0xdb005012 }, + { 0x00007808, 0x04924914 }, + { 0x0000780c, 0x21084210 }, + { 0x00007810, 0x6d801300 }, + { 0x00007814, 0x0019beff }, + { 0x00007818, 0x07e41000 }, + { 0x0000781c, 0x00392000 }, + { 0x00007820, 0x92592480 }, + { 0x00007824, 0x00040000 }, + { 0x00007828, 0xdb005012 }, + { 0x0000782c, 0x04924914 }, + { 0x00007830, 0x21084210 }, + { 0x00007834, 0x6d801300 }, + { 0x00007838, 0x0019beff }, + { 0x0000783c, 0x07e40000 }, + { 0x00007840, 0x00392000 }, + { 0x00007844, 0x92592480 }, + { 0x00007848, 0x00100000 }, + { 0x0000784c, 0x773f0567 }, + { 0x00007850, 0x54214514 }, + { 0x00007854, 0x12035828 }, + { 0x00007858, 0x9259269a }, + { 0x00007860, 0x52802000 }, + { 0x00007864, 0x0a8e370e }, + { 0x00007868, 0xc0102850 }, + { 0x0000786c, 0x812d4000 }, + { 0x00007870, 0x807ec400 }, + { 0x00007874, 0x001b6db0 }, + { 0x00007878, 0x00376b63 }, + { 0x0000787c, 0x06db6db6 }, + { 0x00007880, 0x006d8000 }, + { 0x00007884, 0xffeffffe }, + { 0x00007888, 0xffeffffe }, + { 0x0000788c, 0x00010000 }, + { 0x00007890, 0x02060aeb }, + { 0x00007898, 0x2a850160 }, +}; + +static const u32 ar9280Modes_fast_clock_9280_2[][3] = { + { 0x00001030, 0x00000268, 0x000004d0 }, + { 0x00001070, 0x0000018c, 0x00000318 }, + { 0x000010b0, 0x00000fd0, 0x00001fa0 }, + { 0x00008014, 0x044c044c, 0x08980898 }, + { 0x0000801c, 0x148ec02b, 0x148ec057 }, + { 0x00008318, 0x000044c0, 0x00008980 }, + { 0x00009820, 0x02020200, 0x02020200 }, + { 0x00009824, 0x00000f0f, 0x00000f0f }, + { 0x00009828, 0x0b020001, 0x0b020001 }, + { 0x00009834, 0x00000f0f, 0x00000f0f }, + { 0x00009844, 0x03721821, 0x03721821 }, + { 0x00009914, 0x00000898, 0x00000898 }, + { 0x00009918, 0x0000000b, 0x00000016 }, + { 0x00009944, 0xdfbc1210, 0xdfbc1210 }, +}; + + + +static const u32 ar9280PciePhy_clkreq_off_L1_9280[][2] = { + {0x00004040, 0x9248fd00 }, + {0x00004040, 0x24924924 }, + {0x00004040, 0xa8000019 }, + {0x00004040, 0x13160820 }, + {0x00004040, 0xe5980560 }, + {0x00004040, 0x401dcffc }, + {0x00004040, 0x1aaabe40 }, + {0x00004040, 0xbe105554 }, + {0x00004040, 0x00043007 }, + {0x00004044, 0x00000000 }, +}; + + + +static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = { + {0x00004040, 0x9248fd00 }, + {0x00004040, 0x24924924 }, + {0x00004040, 0xa8000019 }, + {0x00004040, 0x13160820 }, + {0x00004040, 0xe5980560 }, + {0x00004040, 0x401dcffd }, + {0x00004040, 0x1aaabe40 }, + {0x00004040, 0xbe105554 }, + {0x00004040, 0x00043007 }, + {0x00004044, 0x00000000 }, +}; diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c new file mode 100644 index 000000000000..9549524630f7 --- /dev/null +++ b/drivers/net/wireless/ath9k/main.c @@ -0,0 +1,1533 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* mac80211 and PCI callbacks */ + +#include +#include "core.h" + +#define ATH_PCI_VERSION "0.1" + +#define IEEE80211_HTCAP_MAXRXAMPDU_FACTOR 13 +#define IEEE80211_ACTION_CAT_HT 7 +#define IEEE80211_ACTION_HT_TXCHWIDTH 0 + +static char *dev_info = "ath9k"; + +MODULE_AUTHOR("Atheros Communications"); +MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards."); +MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards"); +MODULE_LICENSE("Dual BSD/GPL"); + +static struct pci_device_id ath_pci_id_table[] __devinitdata = { + { PCI_VDEVICE(ATHEROS, 0x0023) }, /* PCI */ + { PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */ + { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */ + { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ + { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ + { 0 } +}; + +static int ath_get_channel(struct ath_softc *sc, + struct ieee80211_channel *chan) +{ + int i; + + for (i = 0; i < sc->sc_ah->ah_nchan; i++) { + if (sc->sc_ah->ah_channels[i].channel == chan->center_freq) + return i; + } + + return -1; +} + +static u32 ath_get_extchanmode(struct ath_softc *sc, + struct ieee80211_channel *chan) +{ + u32 chanmode = 0; + u8 ext_chan_offset = sc->sc_ht_info.ext_chan_offset; + enum ath9k_ht_macmode tx_chan_width = sc->sc_ht_info.tx_chan_width; + + switch (chan->band) { + case IEEE80211_BAND_2GHZ: + if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_NONE) && + (tx_chan_width == ATH9K_HT_MACMODE_20)) + chanmode = CHANNEL_G_HT20; + if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_ABOVE) && + (tx_chan_width == ATH9K_HT_MACMODE_2040)) + chanmode = CHANNEL_G_HT40PLUS; + if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_BELOW) && + (tx_chan_width == ATH9K_HT_MACMODE_2040)) + chanmode = CHANNEL_G_HT40MINUS; + break; + case IEEE80211_BAND_5GHZ: + if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_NONE) && + (tx_chan_width == ATH9K_HT_MACMODE_20)) + chanmode = CHANNEL_A_HT20; + if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_ABOVE) && + (tx_chan_width == ATH9K_HT_MACMODE_2040)) + chanmode = CHANNEL_A_HT40PLUS; + if ((ext_chan_offset == IEEE80211_HT_IE_CHA_SEC_BELOW) && + (tx_chan_width == ATH9K_HT_MACMODE_2040)) + chanmode = CHANNEL_A_HT40MINUS; + break; + default: + break; + } + + return chanmode; +} + + +static int ath_setkey_tkip(struct ath_softc *sc, + struct ieee80211_key_conf *key, + struct ath9k_keyval *hk, + const u8 *addr) +{ + u8 *key_rxmic = NULL; + u8 *key_txmic = NULL; + + key_txmic = key->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; + key_rxmic = key->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; + + if (addr == NULL) { + /* Group key installation */ + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + return ath_keyset(sc, key->keyidx, hk, addr); + } + if (!sc->sc_splitmic) { + /* + * data key goes at first index, + * the hal handles the MIC keys at index+64. + */ + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); + return ath_keyset(sc, key->keyidx, hk, addr); + } + /* + * TX key goes at first index, RX key at +32. + * The hal handles the MIC keys at index+64. + */ + memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); + if (!ath_keyset(sc, key->keyidx, hk, NULL)) { + /* Txmic entry failed. No need to proceed further */ + DPRINTF(sc, ATH_DBG_KEYCACHE, + "%s Setting TX MIC Key Failed\n", __func__); + return 0; + } + + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + /* XXX delete tx key on failure? */ + return ath_keyset(sc, key->keyidx+32, hk, addr); +} + +static int ath_key_config(struct ath_softc *sc, + const u8 *addr, + struct ieee80211_key_conf *key) +{ + struct ieee80211_vif *vif; + struct ath9k_keyval hk; + const u8 *mac = NULL; + int ret = 0; + enum ieee80211_if_types opmode; + + memset(&hk, 0, sizeof(hk)); + + switch (key->alg) { + case ALG_WEP: + hk.kv_type = ATH9K_CIPHER_WEP; + break; + case ALG_TKIP: + hk.kv_type = ATH9K_CIPHER_TKIP; + break; + case ALG_CCMP: + hk.kv_type = ATH9K_CIPHER_AES_CCM; + break; + default: + return -EINVAL; + } + + hk.kv_len = key->keylen; + memcpy(hk.kv_val, key->key, key->keylen); + + if (!sc->sc_vaps[0]) + return -EIO; + + vif = sc->sc_vaps[0]->av_if_data; + opmode = vif->type; + + /* + * Strategy: + * For _M_STA mc tx, we will not setup a key at all since we never + * tx mc. + * _M_STA mc rx, we will use the keyID. + * for _M_IBSS mc tx, we will use the keyID, and no macaddr. + * for _M_IBSS mc rx, we will alloc a slot and plumb the mac of the + * peer node. BUT we will plumb a cleartext key so that we can do + * perSta default key table lookup in software. + */ + if (is_broadcast_ether_addr(addr)) { + switch (opmode) { + case IEEE80211_IF_TYPE_STA: + /* default key: could be group WPA key + * or could be static WEP key */ + mac = NULL; + break; + case IEEE80211_IF_TYPE_IBSS: + break; + case IEEE80211_IF_TYPE_AP: + break; + default: + ASSERT(0); + break; + } + } else { + mac = addr; + } + + if (key->alg == ALG_TKIP) + ret = ath_setkey_tkip(sc, key, &hk, mac); + else + ret = ath_keyset(sc, key->keyidx, &hk, mac); + + if (!ret) + return -EIO; + + sc->sc_keytype = hk.kv_type; + return 0; +} + +static void ath_key_delete(struct ath_softc *sc, struct ieee80211_key_conf *key) +{ +#define ATH_MAX_NUM_KEYS 4 + int freeslot; + + freeslot = (key->keyidx >= ATH_MAX_NUM_KEYS) ? 1 : 0; + ath_key_reset(sc, key->keyidx, freeslot); +#undef ATH_MAX_NUM_KEYS +} + +static void setup_ht_cap(struct ieee80211_ht_info *ht_info) +{ +/* Until mac80211 includes these fields */ + +#define IEEE80211_HT_CAP_DSSSCCK40 0x1000 +#define IEEE80211_HT_CAP_MAXRXAMPDU_65536 0x3 /* 2 ^ 16 */ +#define IEEE80211_HT_CAP_MPDUDENSITY_8 0x6 /* 8 usec */ + + ht_info->ht_supported = 1; + ht_info->cap = (u16)IEEE80211_HT_CAP_SUP_WIDTH + |(u16)IEEE80211_HT_CAP_MIMO_PS + |(u16)IEEE80211_HT_CAP_SGI_40 + |(u16)IEEE80211_HT_CAP_DSSSCCK40; + + ht_info->ampdu_factor = IEEE80211_HT_CAP_MAXRXAMPDU_65536; + ht_info->ampdu_density = IEEE80211_HT_CAP_MPDUDENSITY_8; + /* setup supported mcs set */ + memset(ht_info->supp_mcs_set, 0, 16); + ht_info->supp_mcs_set[0] = 0xff; + ht_info->supp_mcs_set[1] = 0xff; + ht_info->supp_mcs_set[12] = IEEE80211_HT_CAP_MCS_TX_DEFINED; +} + +static int ath_rate2idx(struct ath_softc *sc, int rate) +{ + int i = 0, cur_band, n_rates; + struct ieee80211_hw *hw = sc->hw; + + cur_band = hw->conf.channel->band; + n_rates = sc->sbands[cur_band].n_bitrates; + + for (i = 0; i < n_rates; i++) { + if (sc->sbands[cur_band].bitrates[i].bitrate == rate) + break; + } + + /* + * NB:mac80211 validates rx rate index against the supported legacy rate + * index only (should be done against ht rates also), return the highest + * legacy rate index for rx rate which does not match any one of the + * supported basic and extended rates to make mac80211 happy. + * The following hack will be cleaned up once the issue with + * the rx rate index validation in mac80211 is fixed. + */ + if (i == n_rates) + return n_rates - 1; + return i; +} + +static void ath9k_rx_prepare(struct ath_softc *sc, + struct sk_buff *skb, + struct ath_recv_status *status, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_channel *curchan = hw->conf.channel; + + memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); + + rx_status->mactime = status->tsf; + rx_status->band = curchan->band; + rx_status->freq = curchan->center_freq; + rx_status->noise = ATH_DEFAULT_NOISE_FLOOR; + rx_status->signal = rx_status->noise + status->rssi; + rx_status->rate_idx = ath_rate2idx(sc, (status->rateKbps / 100)); + rx_status->antenna = status->antenna; + rx_status->qual = status->rssi * 100 / 64; + + if (status->flags & ATH_RX_MIC_ERROR) + rx_status->flag |= RX_FLAG_MMIC_ERROR; + if (status->flags & ATH_RX_FCS_ERROR) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + + rx_status->flag |= RX_FLAG_TSFT; +} + +static u8 parse_mpdudensity(u8 mpdudensity) +{ + /* + * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": + * 0 for no restriction + * 1 for 1/4 us + * 2 for 1/2 us + * 3 for 1 us + * 4 for 2 us + * 5 for 4 us + * 6 for 8 us + * 7 for 16 us + */ + switch (mpdudensity) { + case 0: + return 0; + case 1: + case 2: + case 3: + /* Our lower layer calculations limit our precision to + 1 microsecond */ + return 1; + case 4: + return 2; + case 5: + return 4; + case 6: + return 8; + case 7: + return 16; + default: + return 0; + } +} + +static int ath9k_start(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + struct ieee80211_channel *curchan = hw->conf.channel; + int error = 0, pos; + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: Starting driver with " + "initial channel: %d MHz\n", __func__, curchan->center_freq); + + /* setup initial channel */ + + pos = ath_get_channel(sc, curchan); + if (pos == -1) { + DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__); + return -EINVAL; + } + + sc->sc_ah->ah_channels[pos].chanmode = + (curchan->band == IEEE80211_BAND_2GHZ) ? CHANNEL_G : CHANNEL_A; + + /* open ath_dev */ + error = ath_open(sc, &sc->sc_ah->ah_channels[pos]); + if (error) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Unable to complete ath_open\n", __func__); + return error; + } + + ieee80211_wake_queues(hw); + return 0; +} + +static int ath9k_tx(struct ieee80211_hw *hw, + struct sk_buff *skb) +{ + struct ath_softc *sc = hw->priv; + int hdrlen, padsize; + + /* Add the padding after the header if this is not already done */ + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + if (hdrlen & 3) { + padsize = hdrlen % 4; + if (skb_headroom(skb) < padsize) + return -1; + skb_push(skb, padsize); + memmove(skb->data, skb->data + padsize, hdrlen); + } + + DPRINTF(sc, ATH_DBG_XMIT, "%s: transmitting packet, skb: %p\n", + __func__, + skb); + + if (ath_tx_start(sc, skb) != 0) { + DPRINTF(sc, ATH_DBG_XMIT, "%s: TX failed\n", __func__); + dev_kfree_skb_any(skb); + /* FIXME: Check for proper return value from ATH_DEV */ + return 0; + } + + return 0; +} + +static void ath9k_stop(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + int error; + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: Driver halt\n", __func__); + + error = ath_suspend(sc); + if (error) + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: Device is no longer present\n", __func__); + + ieee80211_stop_queues(hw); +} + +static int ath9k_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ath_softc *sc = hw->priv; + int error, ic_opmode = 0; + + /* Support only vap for now */ + + if (sc->sc_nvaps) + return -ENOBUFS; + + switch (conf->type) { + case IEEE80211_IF_TYPE_STA: + ic_opmode = ATH9K_M_STA; + break; + case IEEE80211_IF_TYPE_IBSS: + ic_opmode = ATH9K_M_IBSS; + break; + default: + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Only STA and IBSS are supported currently\n", + __func__); + return -EOPNOTSUPP; + } + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a VAP of type: %d\n", + __func__, + ic_opmode); + + error = ath_vap_attach(sc, 0, conf->vif, ic_opmode); + if (error) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Unable to attach vap, error: %d\n", + __func__, error); + return error; + } + + return 0; +} + +static void ath9k_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct ath_softc *sc = hw->priv; + struct ath_vap *avp; + int error; + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach VAP\n", __func__); + + avp = sc->sc_vaps[0]; + if (avp == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n", + __func__); + return; + } + +#ifdef CONFIG_SLOW_ANT_DIV + ath_slow_ant_div_stop(&sc->sc_antdiv); +#endif + + /* Update ratectrl */ + ath_rate_newstate(sc, avp); + + /* Reclaim beacon resources */ + if (sc->sc_opmode == ATH9K_M_HOSTAP || sc->sc_opmode == ATH9K_M_IBSS) { + ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq); + ath_beacon_return(sc, avp); + } + + /* Set interrupt mask */ + sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); + ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask & ~ATH9K_INT_GLOBAL); + sc->sc_beacons = 0; + + error = ath_vap_detach(sc, 0); + if (error) + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Unable to detach vap, error: %d\n", + __func__, error); +} + +static int ath9k_config(struct ieee80211_hw *hw, + struct ieee80211_conf *conf) +{ + struct ath_softc *sc = hw->priv; + struct ieee80211_channel *curchan = hw->conf.channel; + int pos; + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n", + __func__, + curchan->center_freq); + + pos = ath_get_channel(sc, curchan); + if (pos == -1) { + DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__); + return -EINVAL; + } + + sc->sc_ah->ah_channels[pos].chanmode = + (curchan->band == IEEE80211_BAND_2GHZ) ? CHANNEL_G : CHANNEL_A; + sc->sc_config.txpowlimit = 2 * conf->power_level; + + /* set h/w channel */ + if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0) + DPRINTF(sc, ATH_DBG_FATAL, "%s: Unable to set channel\n", + __func__); + + return 0; +} + +static int ath9k_config_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_if_conf *conf) +{ + struct ath_softc *sc = hw->priv; + struct ath_vap *avp; + u32 rfilt = 0; + int error, i; + DECLARE_MAC_BUF(mac); + + avp = sc->sc_vaps[0]; + if (avp == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n", + __func__); + return -EINVAL; + } + + if ((conf->changed & IEEE80211_IFCC_BSSID) && + !is_zero_ether_addr(conf->bssid)) { + switch (vif->type) { + case IEEE80211_IF_TYPE_STA: + case IEEE80211_IF_TYPE_IBSS: + /* Update ratectrl about the new state */ + ath_rate_newstate(sc, avp); + + /* Set rx filter */ + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(sc->sc_ah, rfilt); + + /* Set BSSID */ + memcpy(sc->sc_curbssid, conf->bssid, ETH_ALEN); + sc->sc_curaid = 0; + ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid, + sc->sc_curaid); + + /* Set aggregation protection mode parameters */ + sc->sc_config.ath_aggr_prot = 0; + + /* + * Reset our TSF so that its value is lower than the + * beacon that we are trying to catch. + * Only then hw will update its TSF register with the + * new beacon. Reset the TSF before setting the BSSID + * to avoid allowing in any frames that would update + * our TSF only to have us clear it + * immediately thereafter. + */ + ath9k_hw_reset_tsf(sc->sc_ah); + + /* Disable BMISS interrupt when we're not associated */ + ath9k_hw_set_interrupts(sc->sc_ah, + sc->sc_imask & + ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS)); + sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); + + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: RX filter 0x%x bssid %s aid 0x%x\n", + __func__, rfilt, + print_mac(mac, sc->sc_curbssid), sc->sc_curaid); + + /* need to reconfigure the beacon */ + sc->sc_beacons = 0; + + break; + default: + break; + } + } + + if ((conf->changed & IEEE80211_IFCC_BEACON) && + (vif->type == IEEE80211_IF_TYPE_IBSS)) { + /* + * Allocate and setup the beacon frame. + * + * Stop any previous beacon DMA. This may be + * necessary, for example, when an ibss merge + * causes reconfiguration; we may be called + * with beacon transmission active. + */ + ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq); + + error = ath_beacon_alloc(sc, 0); + if (error != 0) + return error; + + ath_beacon_sync(sc, 0); + } + + /* Check for WLAN_CAPABILITY_PRIVACY ? */ + if ((avp->av_opmode != IEEE80211_IF_TYPE_STA)) { + for (i = 0; i < IEEE80211_WEP_NKID; i++) + if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i)) + ath9k_hw_keysetmac(sc->sc_ah, + (u16)i, + sc->sc_curbssid); + } + + /* Only legacy IBSS for now */ + if (vif->type == IEEE80211_IF_TYPE_IBSS) + ath_update_chainmask(sc, 0); + + return 0; +} + +#define SUPPORTED_FILTERS \ + (FIF_PROMISC_IN_BSS | \ + FIF_ALLMULTI | \ + FIF_CONTROL | \ + FIF_OTHER_BSS | \ + FIF_BCN_PRBRESP_PROMISC | \ + FIF_FCSFAIL) + +/* Accept unicast, bcast and mcast frames */ + +static void ath9k_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + int mc_count, + struct dev_mc_list *mclist) +{ + struct ath_softc *sc = hw->priv; + + changed_flags &= SUPPORTED_FILTERS; + *total_flags &= SUPPORTED_FILTERS; + + if (changed_flags & FIF_BCN_PRBRESP_PROMISC) { + if (*total_flags & FIF_BCN_PRBRESP_PROMISC) + ath_scan_start(sc); + else + ath_scan_end(sc); + } +} + +static void ath9k_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + const u8 *addr) +{ + struct ath_softc *sc = hw->priv; + struct ath_node *an; + unsigned long flags; + DECLARE_MAC_BUF(mac); + + spin_lock_irqsave(&sc->node_lock, flags); + an = ath_node_find(sc, (u8 *) addr); + spin_unlock_irqrestore(&sc->node_lock, flags); + + switch (cmd) { + case STA_NOTIFY_ADD: + spin_lock_irqsave(&sc->node_lock, flags); + if (!an) { + ath_node_attach(sc, (u8 *)addr, 0); + DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a node: %s\n", + __func__, + print_mac(mac, addr)); + } else { + ath_node_get(sc, (u8 *)addr); + } + spin_unlock_irqrestore(&sc->node_lock, flags); + break; + case STA_NOTIFY_REMOVE: + if (!an) + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Removal of a non-existent node\n", + __func__); + else { + ath_node_put(sc, an, ATH9K_BH_STATUS_INTACT); + DPRINTF(sc, ATH_DBG_CONFIG, "%s: Put a node: %s\n", + __func__, + print_mac(mac, addr)); + } + break; + default: + break; + } +} + +static int ath9k_conf_tx(struct ieee80211_hw *hw, + u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct ath_softc *sc = hw->priv; + struct ath9k_txq_info qi; + int ret = 0, qnum; + + if (queue >= WME_NUM_AC) + return 0; + + qi.tqi_aifs = params->aifs; + qi.tqi_cwmin = params->cw_min; + qi.tqi_cwmax = params->cw_max; + qi.tqi_burstTime = params->txop; + qnum = ath_get_hal_qnum(queue, sc); + + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: Configure tx [queue/halq] [%d/%d], " + "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", + __func__, + queue, + qnum, + params->aifs, + params->cw_min, + params->cw_max, + params->txop); + + ret = ath_txq_update(sc, qnum, &qi); + if (ret) + DPRINTF(sc, ATH_DBG_FATAL, + "%s: TXQ Update failed\n", __func__); + + return ret; +} + +static int ath9k_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + const u8 *local_addr, + const u8 *addr, + struct ieee80211_key_conf *key) +{ + struct ath_softc *sc = hw->priv; + int ret = 0; + + DPRINTF(sc, ATH_DBG_KEYCACHE, " %s: Set HW Key\n", __func__); + + switch (cmd) { + case SET_KEY: + ret = ath_key_config(sc, addr, key); + if (!ret) { + set_bit(key->keyidx, sc->sc_keymap); + key->hw_key_idx = key->keyidx; + /* push IV and Michael MIC generation to stack */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + } + break; + case DISABLE_KEY: + ath_key_delete(sc, key); + clear_bit(key->keyidx, sc->sc_keymap); + sc->sc_keytype = ATH9K_CIPHER_CLR; + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static void ath9k_ht_conf(struct ath_softc *sc, + struct ieee80211_bss_conf *bss_conf) +{ +#define IEEE80211_HT_CAP_40MHZ_INTOLERANT BIT(14) + struct ath_ht_info *ht_info = &sc->sc_ht_info; + + if (bss_conf->assoc_ht) { + ht_info->ext_chan_offset = + bss_conf->ht_bss_conf->bss_cap & + IEEE80211_HT_IE_CHA_SEC_OFFSET; + + if (!(bss_conf->ht_conf->cap & + IEEE80211_HT_CAP_40MHZ_INTOLERANT) && + (bss_conf->ht_bss_conf->bss_cap & + IEEE80211_HT_IE_CHA_WIDTH)) + ht_info->tx_chan_width = ATH9K_HT_MACMODE_2040; + else + ht_info->tx_chan_width = ATH9K_HT_MACMODE_20; + + ath9k_hw_set11nmac2040(sc->sc_ah, ht_info->tx_chan_width); + ht_info->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR + + bss_conf->ht_conf->ampdu_factor); + ht_info->mpdudensity = + parse_mpdudensity(bss_conf->ht_conf->ampdu_density); + + } + +#undef IEEE80211_HT_CAP_40MHZ_INTOLERANT +} + +static void ath9k_bss_assoc_info(struct ath_softc *sc, + struct ieee80211_bss_conf *bss_conf) +{ + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_channel *curchan = hw->conf.channel; + struct ath_vap *avp; + int pos; + DECLARE_MAC_BUF(mac); + + if (bss_conf->assoc) { + DPRINTF(sc, ATH_DBG_CONFIG, "%s: Bss Info ASSOC %d\n", + __func__, + bss_conf->aid); + + avp = sc->sc_vaps[0]; + if (avp == NULL) { + DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n", + __func__); + return; + } + + /* New association, store aid */ + if (avp->av_opmode == ATH9K_M_STA) { + sc->sc_curaid = bss_conf->aid; + ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid, + sc->sc_curaid); + } + + /* Configure the beacon */ + ath_beacon_config(sc, 0); + sc->sc_beacons = 1; + + /* Reset rssi stats */ + sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; + sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; + sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; + sc->sc_halstats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER; + + /* Update chainmask */ + ath_update_chainmask(sc, bss_conf->assoc_ht); + + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: bssid %s aid 0x%x\n", + __func__, + print_mac(mac, sc->sc_curbssid), sc->sc_curaid); + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n", + __func__, + curchan->center_freq); + + pos = ath_get_channel(sc, curchan); + if (pos == -1) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Invalid channel\n", __func__); + return; + } + + if (hw->conf.ht_conf.ht_supported) + sc->sc_ah->ah_channels[pos].chanmode = + ath_get_extchanmode(sc, curchan); + else + sc->sc_ah->ah_channels[pos].chanmode = + (curchan->band == IEEE80211_BAND_2GHZ) ? + CHANNEL_G : CHANNEL_A; + + /* set h/w channel */ + if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0) + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Unable to set channel\n", + __func__); + + ath_rate_newstate(sc, avp); + /* Update ratectrl about the new state */ + ath_rc_node_update(hw, avp->rc_node); + } else { + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: Bss Info DISSOC\n", __func__); + sc->sc_curaid = 0; + } +} + +static void ath9k_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + struct ath_softc *sc = hw->priv; + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed PREAMBLE %d\n", + __func__, + bss_conf->use_short_preamble); + if (bss_conf->use_short_preamble) + sc->sc_flags |= ATH_PREAMBLE_SHORT; + else + sc->sc_flags &= ~ATH_PREAMBLE_SHORT; + } + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed CTS PROT %d\n", + __func__, + bss_conf->use_cts_prot); + if (bss_conf->use_cts_prot && + hw->conf.channel->band != IEEE80211_BAND_5GHZ) + sc->sc_flags |= ATH_PROTECT_ENABLE; + else + sc->sc_flags &= ~ATH_PROTECT_ENABLE; + } + + if (changed & BSS_CHANGED_HT) { + DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed HT %d\n", + __func__, + bss_conf->assoc_ht); + ath9k_ht_conf(sc, bss_conf); + } + + if (changed & BSS_CHANGED_ASSOC) { + DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed ASSOC %d\n", + __func__, + bss_conf->assoc); + ath9k_bss_assoc_info(sc, bss_conf); + } +} + +static u64 ath9k_get_tsf(struct ieee80211_hw *hw) +{ + u64 tsf; + struct ath_softc *sc = hw->priv; + struct ath_hal *ah = sc->sc_ah; + + tsf = ath9k_hw_gettsf64(ah); + + return tsf; +} + +static void ath9k_reset_tsf(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + struct ath_hal *ah = sc->sc_ah; + + ath9k_hw_reset_tsf(ah); +} + +static int ath9k_ampdu_action(struct ieee80211_hw *hw, + enum ieee80211_ampdu_mlme_action action, + const u8 *addr, + u16 tid, + u16 *ssn) +{ + struct ath_softc *sc = hw->priv; + int ret = 0; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + ret = ath_rx_aggr_start(sc, addr, tid, ssn); + if (ret < 0) + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Unable to start RX aggregation\n", + __func__); + break; + case IEEE80211_AMPDU_RX_STOP: + ret = ath_rx_aggr_stop(sc, addr, tid); + if (ret < 0) + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Unable to stop RX aggregation\n", + __func__); + break; + case IEEE80211_AMPDU_TX_START: + ret = ath_tx_aggr_start(sc, addr, tid, ssn); + if (ret < 0) + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Unable to start TX aggregation\n", + __func__); + else + ieee80211_start_tx_ba_cb_irqsafe(hw, (u8 *)addr, tid); + break; + case IEEE80211_AMPDU_TX_STOP: + ret = ath_tx_aggr_stop(sc, addr, tid); + if (ret < 0) + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Unable to stop TX aggregation\n", + __func__); + + ieee80211_stop_tx_ba_cb_irqsafe(hw, (u8 *)addr, tid); + break; + default: + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Unknown AMPDU action\n", __func__); + } + + return ret; +} + +static struct ieee80211_ops ath9k_ops = { + .tx = ath9k_tx, + .start = ath9k_start, + .stop = ath9k_stop, + .add_interface = ath9k_add_interface, + .remove_interface = ath9k_remove_interface, + .config = ath9k_config, + .config_interface = ath9k_config_interface, + .configure_filter = ath9k_configure_filter, + .get_stats = NULL, + .sta_notify = ath9k_sta_notify, + .conf_tx = ath9k_conf_tx, + .get_tx_stats = NULL, + .bss_info_changed = ath9k_bss_info_changed, + .set_tim = NULL, + .set_key = ath9k_set_key, + .hw_scan = NULL, + .get_tkip_seq = NULL, + .set_rts_threshold = NULL, + .set_frag_threshold = NULL, + .set_retry_limit = NULL, + .get_tsf = ath9k_get_tsf, + .reset_tsf = ath9k_reset_tsf, + .tx_last_beacon = NULL, + .ampdu_action = ath9k_ampdu_action +}; + +void ath_get_beaconconfig(struct ath_softc *sc, + int if_id, + struct ath_beacon_config *conf) +{ + struct ieee80211_hw *hw = sc->hw; + + /* fill in beacon config data */ + + conf->beacon_interval = hw->conf.beacon_int; + conf->listen_interval = 100; + conf->dtim_count = 1; + conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf->listen_interval; +} + +int ath_update_beacon(struct ath_softc *sc, + int if_id, + struct ath_beacon_offset *bo, + struct sk_buff *skb, + int mcast) +{ + return 0; +} + +void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, + struct ath_xmit_status *tx_status, struct ath_node *an) +{ + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + + DPRINTF(sc, ATH_DBG_XMIT, + "%s: TX complete: skb: %p\n", __func__, skb); + + if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK || + tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) { + /* free driver's private data area of tx_info */ + if (tx_info->driver_data[0] != NULL) + kfree(tx_info->driver_data[0]); + tx_info->driver_data[0] = NULL; + } + + if (tx_status->flags & ATH_TX_BAR) { + tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; + tx_status->flags &= ~ATH_TX_BAR; + } + if (tx_status->flags) + tx_info->status.excessive_retries = 1; + + tx_info->status.retry_count = tx_status->retries; + + ieee80211_tx_status(hw, skb); + if (an) + ath_node_put(sc, an, ATH9K_BH_STATUS_CHANGE); +} + +int ath__rx_indicate(struct ath_softc *sc, + struct sk_buff *skb, + struct ath_recv_status *status, + u16 keyix) +{ + struct ieee80211_hw *hw = sc->hw; + struct ath_node *an = NULL; + struct ieee80211_rx_status rx_status; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + int hdrlen = ieee80211_get_hdrlen_from_skb(skb); + int padsize; + enum ATH_RX_TYPE st; + + /* see if any padding is done by the hw and remove it */ + if (hdrlen & 3) { + padsize = hdrlen % 4; + memmove(skb->data + padsize, skb->data, hdrlen); + skb_pull(skb, padsize); + } + + /* remove FCS before passing up to protocol stack */ + skb_trim(skb, (skb->len - FCS_LEN)); + + /* Prepare rx status */ + ath9k_rx_prepare(sc, skb, status, &rx_status); + + if (!(keyix == ATH9K_RXKEYIX_INVALID) && + !(status->flags & ATH_RX_DECRYPT_ERROR)) { + rx_status.flag |= RX_FLAG_DECRYPTED; + } else if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED) + && !(status->flags & ATH_RX_DECRYPT_ERROR) + && skb->len >= hdrlen + 4) { + keyix = skb->data[hdrlen + 3] >> 6; + + if (test_bit(keyix, sc->sc_keymap)) + rx_status.flag |= RX_FLAG_DECRYPTED; + } + + spin_lock_bh(&sc->node_lock); + an = ath_node_find(sc, hdr->addr2); + spin_unlock_bh(&sc->node_lock); + + if (an) { + ath_rx_input(sc, an, + hw->conf.ht_conf.ht_supported, + skb, status, &st); + } + if (!an || (st != ATH_RX_CONSUMED)) + __ieee80211_rx(hw, skb, &rx_status); + + return 0; +} + +int ath_rx_subframe(struct ath_node *an, + struct sk_buff *skb, + struct ath_recv_status *status) +{ + struct ath_softc *sc = an->an_sc; + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_rx_status rx_status; + + /* Prepare rx status */ + ath9k_rx_prepare(sc, skb, status, &rx_status); + if (!(status->flags & ATH_RX_DECRYPT_ERROR)) + rx_status.flag |= RX_FLAG_DECRYPTED; + + __ieee80211_rx(hw, skb, &rx_status); + + return 0; +} + +enum ath9k_ht_macmode ath_cwm_macmode(struct ath_softc *sc) +{ + return sc->sc_ht_info.tx_chan_width; +} + +void ath_setup_rate(struct ath_softc *sc, + enum wireless_mode wMode, + enum RATE_TYPE type, + const struct ath9k_rate_table *rt) +{ + int i, maxrates, a = 0, b = 0; + struct ieee80211_supported_band *band_2ghz; + struct ieee80211_supported_band *band_5ghz; + struct ieee80211_rate *rates_2ghz; + struct ieee80211_rate *rates_5ghz; + + if ((wMode >= WIRELESS_MODE_MAX) || (type != NORMAL_RATE)) + return; + + band_2ghz = &sc->sbands[IEEE80211_BAND_2GHZ]; + band_5ghz = &sc->sbands[IEEE80211_BAND_5GHZ]; + rates_2ghz = sc->rates[IEEE80211_BAND_2GHZ]; + rates_5ghz = sc->rates[IEEE80211_BAND_5GHZ]; + + if (rt->rateCount > ATH_RATE_MAX) + maxrates = ATH_RATE_MAX; + else + maxrates = rt->rateCount; + + if ((band_2ghz->n_bitrates != 0) && (band_5ghz->n_bitrates != 0)) { + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: Rates already setup\n", __func__); + return; + } + + for (i = 0; i < maxrates; i++) { + switch (wMode) { + case WIRELESS_MODE_11b: + case WIRELESS_MODE_11g: + rates_2ghz[a].bitrate = rt->info[i].rateKbps / 100; + rates_2ghz[a].hw_value = rt->info[i].rateCode; + a++; + band_2ghz->n_bitrates = a; + break; + case WIRELESS_MODE_11a: + rates_5ghz[b].bitrate = rt->info[i].rateKbps / 100; + rates_5ghz[b].hw_value = rt->info[i].rateCode; + b++; + band_5ghz->n_bitrates = b; + break; + default: + break; + } + } + + if (band_2ghz->n_bitrates) { + for (i = 0; i < band_2ghz->n_bitrates; i++) { + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: 2GHz Rate: %2dMbps, ratecode: %2d\n", + __func__, + rates_2ghz[i].bitrate / 10, + rates_2ghz[i].hw_value); + } + } else if (band_5ghz->n_bitrates) { + for (i = 0; i < band_5ghz->n_bitrates; i++) { + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: 5Ghz Rate: %2dMbps, ratecode: %2d\n", + __func__, + rates_5ghz[i].bitrate / 10, + rates_5ghz[i].hw_value); + } + } +} + +static int ath_detach(struct ath_softc *sc) +{ + struct ieee80211_hw *hw = sc->hw; + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach ATH hw\n", __func__); + + /* Unregister hw */ + + ieee80211_unregister_hw(hw); + + /* unregister Rate control */ + ath_rate_control_unregister(); + + /* tx/rx cleanup */ + + ath_rx_cleanup(sc); + ath_tx_cleanup(sc); + + /* Deinit */ + + ath_deinit(sc); + + return 0; +} + +static int ath_attach(u16 devid, + struct ath_softc *sc) +{ + struct ieee80211_hw *hw = sc->hw; + int error = 0; + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach ATH hw\n", __func__); + + error = ath_init(devid, sc); + if (error != 0) + return error; + + /* Init nodes */ + + INIT_LIST_HEAD(&sc->node_list); + spin_lock_init(&sc->node_lock); + + /* get mac address from hardware and set in mac80211 */ + + SET_IEEE80211_PERM_ADDR(hw, sc->sc_myaddr); + + /* setup channels and rates */ + + sc->sbands[IEEE80211_BAND_2GHZ].channels = + sc->channels[IEEE80211_BAND_2GHZ]; + sc->sbands[IEEE80211_BAND_2GHZ].bitrates = + sc->rates[IEEE80211_BAND_2GHZ]; + sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; + + if (sc->sc_ah->ah_caps.halHTSupport) + /* Setup HT capabilities for 2.4Ghz*/ + setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_info); + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &sc->sbands[IEEE80211_BAND_2GHZ]; + + if (sc->sc_ah->ah_caps.halWirelessModes & ATH9K_MODE_SEL_11A) { + sc->sbands[IEEE80211_BAND_5GHZ].channels = + sc->channels[IEEE80211_BAND_5GHZ]; + sc->sbands[IEEE80211_BAND_5GHZ].bitrates = + sc->rates[IEEE80211_BAND_5GHZ]; + sc->sbands[IEEE80211_BAND_5GHZ].band = + IEEE80211_BAND_5GHZ; + + if (sc->sc_ah->ah_caps.halHTSupport) + /* Setup HT capabilities for 5Ghz*/ + setup_ht_cap(&sc->sbands[IEEE80211_BAND_5GHZ].ht_info); + + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &sc->sbands[IEEE80211_BAND_5GHZ]; + } + + /* FIXME: Have to figure out proper hw init values later */ + + hw->queues = 4; + hw->ampdu_queues = 1; + + /* Register rate control */ + hw->rate_control_algorithm = "ath9k_rate_control"; + error = ath_rate_control_register(); + if (error != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: Unable to register rate control " + "algorithm:%d\n", __func__, error); + ath_rate_control_unregister(); + goto bad; + } + + error = ieee80211_register_hw(hw); + if (error != 0) { + ath_rate_control_unregister(); + goto bad; + } + + /* initialize tx/rx engine */ + + error = ath_tx_init(sc, ATH_TXBUF); + if (error != 0) + goto bad1; + + error = ath_rx_init(sc, ATH_RXBUF); + if (error != 0) + goto bad1; + + return 0; +bad1: + ath_detach(sc); +bad: + return error; +} + +static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + void __iomem *mem; + struct ath_softc *sc; + struct ieee80211_hw *hw; + const char *athname; + u8 csz; + u32 val; + int ret = 0; + + if (pci_enable_device(pdev)) + return -EIO; + + /* XXX 32-bit addressing only */ + if (pci_set_dma_mask(pdev, 0xffffffff)) { + printk(KERN_ERR "ath_pci: 32-bit DMA not available\n"); + ret = -ENODEV; + goto bad; + } + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &csz); + if (csz == 0) { + /* + * Linux 2.4.18 (at least) writes the cache line size + * register as a 16-bit wide register which is wrong. + * We must have this setup properly for rx buffer + * DMA to work so force a reasonable value here if it + * comes up zero. + */ + csz = L1_CACHE_BYTES / sizeof(u32); + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz); + } + /* + * The default setting of latency timer yields poor results, + * set it to the value used by other systems. It may be worth + * tweaking this setting more. + */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xa8); + + pci_set_master(pdev); + + /* + * Disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state. + */ + pci_read_config_dword(pdev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + + ret = pci_request_region(pdev, 0, "ath9k"); + if (ret) { + dev_err(&pdev->dev, "PCI memory region reserve error\n"); + ret = -ENODEV; + goto bad; + } + + mem = pci_iomap(pdev, 0, 0); + if (!mem) { + printk(KERN_ERR "PCI memory map error\n") ; + ret = -EIO; + goto bad1; + } + + hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops); + if (hw == NULL) { + printk(KERN_ERR "ath_pci: no memory for ieee80211_hw\n"); + goto bad2; + } + + hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_NOISE_DBM; + + SET_IEEE80211_DEV(hw, &pdev->dev); + pci_set_drvdata(pdev, hw); + + sc = hw->priv; + sc->hw = hw; + sc->pdev = pdev; + sc->mem = mem; + + if (ath_attach(id->device, sc) != 0) { + ret = -ENODEV; + goto bad3; + } + + /* setup interrupt service routine */ + + if (request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath", sc)) { + printk(KERN_ERR "%s: request_irq failed\n", + wiphy_name(hw->wiphy)); + ret = -EIO; + goto bad4; + } + + athname = ath9k_hw_probe(id->vendor, id->device); + + printk(KERN_INFO "%s: %s: mem=0x%lx, irq=%d\n", + wiphy_name(hw->wiphy), + athname ? athname : "Atheros ???", + (unsigned long)mem, pdev->irq); + + return 0; +bad4: + ath_detach(sc); +bad3: + ieee80211_free_hw(hw); +bad2: + pci_iounmap(pdev, mem); +bad1: + pci_release_region(pdev, 0); +bad: + pci_disable_device(pdev); + return ret; +} + +static void ath_pci_remove(struct pci_dev *pdev) +{ + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ath_softc *sc = hw->priv; + + if (pdev->irq) + free_irq(pdev->irq, sc); + ath_detach(sc); + pci_iounmap(pdev, sc->mem); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + ieee80211_free_hw(hw); +} + +#ifdef CONFIG_PM + +static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, 3); + + return 0; +} + +static int ath_pci_resume(struct pci_dev *pdev) +{ + u32 val; + int err; + + err = pci_enable_device(pdev); + if (err) + return err; + pci_restore_state(pdev); + /* + * Suspend/Resume resets the PCI configuration space, so we have to + * re-disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state + */ + pci_read_config_dword(pdev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + + return 0; +} + +#endif /* CONFIG_PM */ + +MODULE_DEVICE_TABLE(pci, ath_pci_id_table); + +static struct pci_driver ath_pci_driver = { + .name = "ath9k", + .id_table = ath_pci_id_table, + .probe = ath_pci_probe, + .remove = ath_pci_remove, +#ifdef CONFIG_PM + .suspend = ath_pci_suspend, + .resume = ath_pci_resume, +#endif /* CONFIG_PM */ +}; + +static int __init init_ath_pci(void) +{ + printk(KERN_INFO "%s: %s\n", dev_info, ATH_PCI_VERSION); + + if (pci_register_driver(&ath_pci_driver) < 0) { + printk(KERN_ERR + "ath_pci: No devices found, driver not installed.\n"); + pci_unregister_driver(&ath_pci_driver); + return -ENODEV; + } + + return 0; +} +module_init(init_ath_pci); + +static void __exit exit_ath_pci(void) +{ + pci_unregister_driver(&ath_pci_driver); + printk(KERN_INFO "%s: driver unloaded\n", dev_info); +} +module_exit(exit_ath_pci); diff --git a/drivers/net/wireless/ath9k/phy.c b/drivers/net/wireless/ath9k/phy.c new file mode 100644 index 000000000000..eb9121fdfd38 --- /dev/null +++ b/drivers/net/wireless/ath9k/phy.c @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "core.h" +#include "hw.h" +#include "reg.h" +#include "phy.h" + +void +ath9k_hw_write_regs(struct ath_hal *ah, u32 modesIndex, u32 freqIndex, + int regWrites) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + REG_WRITE_ARRAY(&ahp->ah_iniBB_RfGain, freqIndex, regWrites); +} + +bool +ath9k_hw_set_channel(struct ath_hal *ah, struct ath9k_channel *chan) +{ + u32 channelSel = 0; + u32 bModeSynth = 0; + u32 aModeRefSel = 0; + u32 reg32 = 0; + u16 freq; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + freq = centers.synth_center; + + if (freq < 4800) { + u32 txctl; + + if (((freq - 2192) % 5) == 0) { + channelSel = ((freq - 672) * 2 - 3040) / 10; + bModeSynth = 0; + } else if (((freq - 2224) % 5) == 0) { + channelSel = ((freq - 704) * 2 - 3040) / 10; + bModeSynth = 1; + } else { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: invalid channel %u MHz\n", __func__, + freq); + return false; + } + + channelSel = (channelSel << 2) & 0xff; + channelSel = ath9k_hw_reverse_bits(channelSel, 8); + + txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL); + if (freq == 2484) { + + REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, + txctl | AR_PHY_CCK_TX_CTRL_JAPAN); + } else { + REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, + txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN); + } + + } else if ((freq % 20) == 0 && freq >= 5120) { + channelSel = + ath9k_hw_reverse_bits(((freq - 4800) / 20 << 2), 8); + aModeRefSel = ath9k_hw_reverse_bits(1, 2); + } else if ((freq % 10) == 0) { + channelSel = + ath9k_hw_reverse_bits(((freq - 4800) / 10 << 1), 8); + if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) + aModeRefSel = ath9k_hw_reverse_bits(2, 2); + else + aModeRefSel = ath9k_hw_reverse_bits(1, 2); + } else if ((freq % 5) == 0) { + channelSel = ath9k_hw_reverse_bits((freq - 4800) / 5, 8); + aModeRefSel = ath9k_hw_reverse_bits(1, 2); + } else { + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, + "%s: invalid channel %u MHz\n", __func__, freq); + return false; + } + + reg32 = + (channelSel << 8) | (aModeRefSel << 2) | (bModeSynth << 1) | + (1 << 5) | 0x1; + + REG_WRITE(ah, AR_PHY(0x37), reg32); + + ah->ah_curchan = chan; + + AH5416(ah)->ah_curchanRadIndex = -1; + + return true; +} + +bool +ath9k_hw_ar9280_set_channel(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + u16 bMode, fracMode, aModeRefSel = 0; + u32 freq, ndiv, channelSel = 0, channelFrac = 0, reg32 = 0; + struct chan_centers centers; + u32 refDivA = 24; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + freq = centers.synth_center; + + reg32 = REG_READ(ah, AR_PHY_SYNTH_CONTROL); + reg32 &= 0xc0000000; + + if (freq < 4800) { + u32 txctl; + + bMode = 1; + fracMode = 1; + aModeRefSel = 0; + channelSel = (freq * 0x10000) / 15; + + txctl = REG_READ(ah, AR_PHY_CCK_TX_CTRL); + if (freq == 2484) { + + REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, + txctl | AR_PHY_CCK_TX_CTRL_JAPAN); + } else { + REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, + txctl & ~AR_PHY_CCK_TX_CTRL_JAPAN); + } + } else { + bMode = 0; + fracMode = 0; + + if ((freq % 20) == 0) { + aModeRefSel = 3; + } else if ((freq % 10) == 0) { + aModeRefSel = 2; + } else { + aModeRefSel = 0; + + fracMode = 1; + refDivA = 1; + channelSel = (freq * 0x8000) / 15; + + REG_RMW_FIELD(ah, AR_AN_SYNTH9, + AR_AN_SYNTH9_REFDIVA, refDivA); + } + if (!fracMode) { + ndiv = (freq * (refDivA >> aModeRefSel)) / 60; + channelSel = ndiv & 0x1ff; + channelFrac = (ndiv & 0xfffffe00) * 2; + channelSel = (channelSel << 17) | channelFrac; + } + } + + reg32 = reg32 | + (bMode << 29) | + (fracMode << 28) | (aModeRefSel << 26) | (channelSel); + + REG_WRITE(ah, AR_PHY_SYNTH_CONTROL, reg32); + + ah->ah_curchan = chan; + + AH5416(ah)->ah_curchanRadIndex = -1; + + return true; +} + +static void +ath9k_phy_modify_rx_buffer(u32 *rfBuf, u32 reg32, + u32 numBits, u32 firstBit, + u32 column) +{ + u32 tmp32, mask, arrayEntry, lastBit; + int32_t bitPosition, bitsLeft; + + tmp32 = ath9k_hw_reverse_bits(reg32, numBits); + arrayEntry = (firstBit - 1) / 8; + bitPosition = (firstBit - 1) % 8; + bitsLeft = numBits; + while (bitsLeft > 0) { + lastBit = (bitPosition + bitsLeft > 8) ? + 8 : bitPosition + bitsLeft; + mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) << + (column * 8); + rfBuf[arrayEntry] &= ~mask; + rfBuf[arrayEntry] |= ((tmp32 << bitPosition) << + (column * 8)) & mask; + bitsLeft -= 8 - bitPosition; + tmp32 = tmp32 >> (8 - bitPosition); + bitPosition = 0; + arrayEntry++; + } +} + +bool +ath9k_hw_set_rf_regs(struct ath_hal *ah, struct ath9k_channel *chan, + u16 modesIndex) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + u32 eepMinorRev; + u32 ob5GHz = 0, db5GHz = 0; + u32 ob2GHz = 0, db2GHz = 0; + int regWrites = 0; + + if (AR_SREV_9280_10_OR_LATER(ah)) + return true; + + eepMinorRev = ath9k_hw_get_eeprom(ahp, EEP_MINOR_REV); + + RF_BANK_SETUP(ahp->ah_analogBank0Data, &ahp->ah_iniBank0, 1); + + RF_BANK_SETUP(ahp->ah_analogBank1Data, &ahp->ah_iniBank1, 1); + + RF_BANK_SETUP(ahp->ah_analogBank2Data, &ahp->ah_iniBank2, 1); + + RF_BANK_SETUP(ahp->ah_analogBank3Data, &ahp->ah_iniBank3, + modesIndex); + { + int i; + for (i = 0; i < ahp->ah_iniBank6TPC.ia_rows; i++) { + ahp->ah_analogBank6Data[i] = + INI_RA(&ahp->ah_iniBank6TPC, i, modesIndex); + } + } + + if (eepMinorRev >= 2) { + if (IS_CHAN_2GHZ(chan)) { + ob2GHz = ath9k_hw_get_eeprom(ahp, EEP_OB_2); + db2GHz = ath9k_hw_get_eeprom(ahp, EEP_DB_2); + ath9k_phy_modify_rx_buffer(ahp->ah_analogBank6Data, + ob2GHz, 3, 197, 0); + ath9k_phy_modify_rx_buffer(ahp->ah_analogBank6Data, + db2GHz, 3, 194, 0); + } else { + ob5GHz = ath9k_hw_get_eeprom(ahp, EEP_OB_5); + db5GHz = ath9k_hw_get_eeprom(ahp, EEP_DB_5); + ath9k_phy_modify_rx_buffer(ahp->ah_analogBank6Data, + ob5GHz, 3, 203, 0); + ath9k_phy_modify_rx_buffer(ahp->ah_analogBank6Data, + db5GHz, 3, 200, 0); + } + } + + RF_BANK_SETUP(ahp->ah_analogBank7Data, &ahp->ah_iniBank7, 1); + + REG_WRITE_RF_ARRAY(&ahp->ah_iniBank0, ahp->ah_analogBank0Data, + regWrites); + REG_WRITE_RF_ARRAY(&ahp->ah_iniBank1, ahp->ah_analogBank1Data, + regWrites); + REG_WRITE_RF_ARRAY(&ahp->ah_iniBank2, ahp->ah_analogBank2Data, + regWrites); + REG_WRITE_RF_ARRAY(&ahp->ah_iniBank3, ahp->ah_analogBank3Data, + regWrites); + REG_WRITE_RF_ARRAY(&ahp->ah_iniBank6TPC, ahp->ah_analogBank6Data, + regWrites); + REG_WRITE_RF_ARRAY(&ahp->ah_iniBank7, ahp->ah_analogBank7Data, + regWrites); + + return true; +} + +void +ath9k_hw_rfdetach(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (ahp->ah_analogBank0Data != NULL) { + kfree(ahp->ah_analogBank0Data); + ahp->ah_analogBank0Data = NULL; + } + if (ahp->ah_analogBank1Data != NULL) { + kfree(ahp->ah_analogBank1Data); + ahp->ah_analogBank1Data = NULL; + } + if (ahp->ah_analogBank2Data != NULL) { + kfree(ahp->ah_analogBank2Data); + ahp->ah_analogBank2Data = NULL; + } + if (ahp->ah_analogBank3Data != NULL) { + kfree(ahp->ah_analogBank3Data); + ahp->ah_analogBank3Data = NULL; + } + if (ahp->ah_analogBank6Data != NULL) { + kfree(ahp->ah_analogBank6Data); + ahp->ah_analogBank6Data = NULL; + } + if (ahp->ah_analogBank6TPCData != NULL) { + kfree(ahp->ah_analogBank6TPCData); + ahp->ah_analogBank6TPCData = NULL; + } + if (ahp->ah_analogBank7Data != NULL) { + kfree(ahp->ah_analogBank7Data); + ahp->ah_analogBank7Data = NULL; + } + if (ahp->ah_addac5416_21 != NULL) { + kfree(ahp->ah_addac5416_21); + ahp->ah_addac5416_21 = NULL; + } + if (ahp->ah_bank6Temp != NULL) { + kfree(ahp->ah_bank6Temp); + ahp->ah_bank6Temp = NULL; + } +} + +bool ath9k_hw_init_rf(struct ath_hal *ah, int *status) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + if (!AR_SREV_9280_10_OR_LATER(ah)) { + + ahp->ah_analogBank0Data = + kzalloc((sizeof(u32) * + ahp->ah_iniBank0.ia_rows), GFP_KERNEL); + ahp->ah_analogBank1Data = + kzalloc((sizeof(u32) * + ahp->ah_iniBank1.ia_rows), GFP_KERNEL); + ahp->ah_analogBank2Data = + kzalloc((sizeof(u32) * + ahp->ah_iniBank2.ia_rows), GFP_KERNEL); + ahp->ah_analogBank3Data = + kzalloc((sizeof(u32) * + ahp->ah_iniBank3.ia_rows), GFP_KERNEL); + ahp->ah_analogBank6Data = + kzalloc((sizeof(u32) * + ahp->ah_iniBank6.ia_rows), GFP_KERNEL); + ahp->ah_analogBank6TPCData = + kzalloc((sizeof(u32) * + ahp->ah_iniBank6TPC.ia_rows), GFP_KERNEL); + ahp->ah_analogBank7Data = + kzalloc((sizeof(u32) * + ahp->ah_iniBank7.ia_rows), GFP_KERNEL); + + if (ahp->ah_analogBank0Data == NULL + || ahp->ah_analogBank1Data == NULL + || ahp->ah_analogBank2Data == NULL + || ahp->ah_analogBank3Data == NULL + || ahp->ah_analogBank6Data == NULL + || ahp->ah_analogBank6TPCData == NULL + || ahp->ah_analogBank7Data == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "%s: cannot allocate RF banks\n", + __func__); + *status = -ENOMEM; + return false; + } + + ahp->ah_addac5416_21 = + kzalloc((sizeof(u32) * + ahp->ah_iniAddac.ia_rows * + ahp->ah_iniAddac.ia_columns), GFP_KERNEL); + if (ahp->ah_addac5416_21 == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "%s: cannot allocate ah_addac5416_21\n", + __func__); + *status = -ENOMEM; + return false; + } + + ahp->ah_bank6Temp = + kzalloc((sizeof(u32) * + ahp->ah_iniBank6.ia_rows), GFP_KERNEL); + if (ahp->ah_bank6Temp == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "%s: cannot allocate ah_bank6Temp\n", + __func__); + *status = -ENOMEM; + return false; + } + } + + return true; +} + +void +ath9k_hw_decrease_chain_power(struct ath_hal *ah, struct ath9k_channel *chan) +{ + int i, regWrites = 0; + struct ath_hal_5416 *ahp = AH5416(ah); + u32 bank6SelMask; + u32 *bank6Temp = ahp->ah_bank6Temp; + + switch (ahp->ah_diversityControl) { + case ATH9K_ANT_FIXED_A: + bank6SelMask = + (ahp-> + ah_antennaSwitchSwap & ANTSWAP_AB) ? REDUCE_CHAIN_0 : + REDUCE_CHAIN_1; + break; + case ATH9K_ANT_FIXED_B: + bank6SelMask = + (ahp-> + ah_antennaSwitchSwap & ANTSWAP_AB) ? REDUCE_CHAIN_1 : + REDUCE_CHAIN_0; + break; + case ATH9K_ANT_VARIABLE: + return; + break; + default: + return; + break; + } + + for (i = 0; i < ahp->ah_iniBank6.ia_rows; i++) + bank6Temp[i] = ahp->ah_analogBank6Data[i]; + + REG_WRITE(ah, AR_PHY_BASE + 0xD8, bank6SelMask); + + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 189, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 190, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 191, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 192, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 193, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 222, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 245, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 246, 0); + ath9k_phy_modify_rx_buffer(bank6Temp, 1, 1, 247, 0); + + REG_WRITE_RF_ARRAY(&ahp->ah_iniBank6, bank6Temp, regWrites); + + REG_WRITE(ah, AR_PHY_BASE + 0xD8, 0x00000053); +#ifdef ALTER_SWITCH + REG_WRITE(ah, PHY_SWITCH_CHAIN_0, + (REG_READ(ah, PHY_SWITCH_CHAIN_0) & ~0x38) + | ((REG_READ(ah, PHY_SWITCH_CHAIN_0) >> 3) & 0x38)); +#endif +} diff --git a/drivers/net/wireless/ath9k/phy.h b/drivers/net/wireless/ath9k/phy.h new file mode 100644 index 000000000000..0cd399a5344a --- /dev/null +++ b/drivers/net/wireless/ath9k/phy.h @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef PHY_H +#define PHY_H + +bool ath9k_hw_ar9280_set_channel(struct ath_hal *ah, + struct ath9k_channel + *chan); +bool ath9k_hw_set_channel(struct ath_hal *ah, + struct ath9k_channel *chan); +void ath9k_hw_write_regs(struct ath_hal *ah, u32 modesIndex, + u32 freqIndex, int regWrites); +bool ath9k_hw_set_rf_regs(struct ath_hal *ah, + struct ath9k_channel *chan, + u16 modesIndex); +void ath9k_hw_decrease_chain_power(struct ath_hal *ah, + struct ath9k_channel *chan); +bool ath9k_hw_init_rf(struct ath_hal *ah, + int *status); + +#define AR_PHY_BASE 0x9800 +#define AR_PHY(_n) (AR_PHY_BASE + ((_n)<<2)) + +#define AR_PHY_TEST 0x9800 +#define PHY_AGC_CLR 0x10000000 +#define RFSILENT_BB 0x00002000 + +#define AR_PHY_TURBO 0x9804 +#define AR_PHY_FC_TURBO_MODE 0x00000001 +#define AR_PHY_FC_TURBO_SHORT 0x00000002 +#define AR_PHY_FC_DYN2040_EN 0x00000004 +#define AR_PHY_FC_DYN2040_PRI_ONLY 0x00000008 +#define AR_PHY_FC_DYN2040_PRI_CH 0x00000010 +#define AR_PHY_FC_DYN2040_EXT_CH 0x00000020 +#define AR_PHY_FC_HT_EN 0x00000040 +#define AR_PHY_FC_SHORT_GI_40 0x00000080 +#define AR_PHY_FC_WALSH 0x00000100 +#define AR_PHY_FC_SINGLE_HT_LTF1 0x00000200 + +#define AR_PHY_TIMING2 0x9810 +#define AR_PHY_TIMING3 0x9814 +#define AR_PHY_TIMING3_DSC_MAN 0xFFFE0000 +#define AR_PHY_TIMING3_DSC_MAN_S 17 +#define AR_PHY_TIMING3_DSC_EXP 0x0001E000 +#define AR_PHY_TIMING3_DSC_EXP_S 13 + +#define AR_PHY_CHIP_ID 0x9818 +#define AR_PHY_CHIP_ID_REV_0 0x80 +#define AR_PHY_CHIP_ID_REV_1 0x81 +#define AR_PHY_CHIP_ID_9160_REV_0 0xb0 + +#define AR_PHY_ACTIVE 0x981C +#define AR_PHY_ACTIVE_EN 0x00000001 +#define AR_PHY_ACTIVE_DIS 0x00000000 + +#define AR_PHY_RF_CTL2 0x9824 +#define AR_PHY_TX_END_DATA_START 0x000000FF +#define AR_PHY_TX_END_DATA_START_S 0 +#define AR_PHY_TX_END_PA_ON 0x0000FF00 +#define AR_PHY_TX_END_PA_ON_S 8 + +#define AR_PHY_RF_CTL3 0x9828 +#define AR_PHY_TX_END_TO_A2_RX_ON 0x00FF0000 +#define AR_PHY_TX_END_TO_A2_RX_ON_S 16 + +#define AR_PHY_ADC_CTL 0x982C +#define AR_PHY_ADC_CTL_OFF_INBUFGAIN 0x00000003 +#define AR_PHY_ADC_CTL_OFF_INBUFGAIN_S 0 +#define AR_PHY_ADC_CTL_OFF_PWDDAC 0x00002000 +#define AR_PHY_ADC_CTL_OFF_PWDBANDGAP 0x00004000 +#define AR_PHY_ADC_CTL_OFF_PWDADC 0x00008000 +#define AR_PHY_ADC_CTL_ON_INBUFGAIN 0x00030000 +#define AR_PHY_ADC_CTL_ON_INBUFGAIN_S 16 + +#define AR_PHY_ADC_SERIAL_CTL 0x9830 +#define AR_PHY_SEL_INTERNAL_ADDAC 0x00000000 +#define AR_PHY_SEL_EXTERNAL_RADIO 0x00000001 + +#define AR_PHY_RF_CTL4 0x9834 +#define AR_PHY_RF_CTL4_TX_END_XPAB_OFF 0xFF000000 +#define AR_PHY_RF_CTL4_TX_END_XPAB_OFF_S 24 +#define AR_PHY_RF_CTL4_TX_END_XPAA_OFF 0x00FF0000 +#define AR_PHY_RF_CTL4_TX_END_XPAA_OFF_S 16 +#define AR_PHY_RF_CTL4_FRAME_XPAB_ON 0x0000FF00 +#define AR_PHY_RF_CTL4_FRAME_XPAB_ON_S 8 +#define AR_PHY_RF_CTL4_FRAME_XPAA_ON 0x000000FF +#define AR_PHY_RF_CTL4_FRAME_XPAA_ON_S 0 + +#define AR_PHY_SETTLING 0x9844 +#define AR_PHY_SETTLING_SWITCH 0x00003F80 +#define AR_PHY_SETTLING_SWITCH_S 7 + +#define AR_PHY_RXGAIN 0x9848 +#define AR_PHY_RXGAIN_TXRX_ATTEN 0x0003F000 +#define AR_PHY_RXGAIN_TXRX_ATTEN_S 12 +#define AR_PHY_RXGAIN_TXRX_RF_MAX 0x007C0000 +#define AR_PHY_RXGAIN_TXRX_RF_MAX_S 18 +#define AR9280_PHY_RXGAIN_TXRX_ATTEN 0x00003F80 +#define AR9280_PHY_RXGAIN_TXRX_ATTEN_S 7 +#define AR9280_PHY_RXGAIN_TXRX_MARGIN 0x001FC000 +#define AR9280_PHY_RXGAIN_TXRX_MARGIN_S 14 + +#define AR_PHY_DESIRED_SZ 0x9850 +#define AR_PHY_DESIRED_SZ_ADC 0x000000FF +#define AR_PHY_DESIRED_SZ_ADC_S 0 +#define AR_PHY_DESIRED_SZ_PGA 0x0000FF00 +#define AR_PHY_DESIRED_SZ_PGA_S 8 +#define AR_PHY_DESIRED_SZ_TOT_DES 0x0FF00000 +#define AR_PHY_DESIRED_SZ_TOT_DES_S 20 + +#define AR_PHY_FIND_SIG 0x9858 +#define AR_PHY_FIND_SIG_FIRSTEP 0x0003F000 +#define AR_PHY_FIND_SIG_FIRSTEP_S 12 +#define AR_PHY_FIND_SIG_FIRPWR 0x03FC0000 +#define AR_PHY_FIND_SIG_FIRPWR_S 18 + +#define AR_PHY_AGC_CTL1 0x985C +#define AR_PHY_AGC_CTL1_COARSE_LOW 0x00007F80 +#define AR_PHY_AGC_CTL1_COARSE_LOW_S 7 +#define AR_PHY_AGC_CTL1_COARSE_HIGH 0x003F8000 +#define AR_PHY_AGC_CTL1_COARSE_HIGH_S 15 + +#define AR_PHY_AGC_CONTROL 0x9860 +#define AR_PHY_AGC_CONTROL_CAL 0x00000001 +#define AR_PHY_AGC_CONTROL_NF 0x00000002 +#define AR_PHY_AGC_CONTROL_ENABLE_NF 0x00008000 +#define AR_PHY_AGC_CONTROL_FLTR_CAL 0x00010000 +#define AR_PHY_AGC_CONTROL_NO_UPDATE_NF 0x00020000 + +#define AR_PHY_CCA 0x9864 +#define AR_PHY_MINCCA_PWR 0x0FF80000 +#define AR_PHY_MINCCA_PWR_S 19 +#define AR_PHY_CCA_THRESH62 0x0007F000 +#define AR_PHY_CCA_THRESH62_S 12 +#define AR9280_PHY_MINCCA_PWR 0x1FF00000 +#define AR9280_PHY_MINCCA_PWR_S 20 +#define AR9280_PHY_CCA_THRESH62 0x000FF000 +#define AR9280_PHY_CCA_THRESH62_S 12 + +#define AR_PHY_SFCORR_LOW 0x986C +#define AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW 0x00000001 +#define AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW 0x00003F00 +#define AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW_S 8 +#define AR_PHY_SFCORR_LOW_M1_THRESH_LOW 0x001FC000 +#define AR_PHY_SFCORR_LOW_M1_THRESH_LOW_S 14 +#define AR_PHY_SFCORR_LOW_M2_THRESH_LOW 0x0FE00000 +#define AR_PHY_SFCORR_LOW_M2_THRESH_LOW_S 21 + +#define AR_PHY_SFCORR 0x9868 +#define AR_PHY_SFCORR_M2COUNT_THR 0x0000001F +#define AR_PHY_SFCORR_M2COUNT_THR_S 0 +#define AR_PHY_SFCORR_M1_THRESH 0x00FE0000 +#define AR_PHY_SFCORR_M1_THRESH_S 17 +#define AR_PHY_SFCORR_M2_THRESH 0x7F000000 +#define AR_PHY_SFCORR_M2_THRESH_S 24 + +#define AR_PHY_SLEEP_CTR_CONTROL 0x9870 +#define AR_PHY_SLEEP_CTR_LIMIT 0x9874 +#define AR_PHY_SYNTH_CONTROL 0x9874 +#define AR_PHY_SLEEP_SCAL 0x9878 + +#define AR_PHY_PLL_CTL 0x987c +#define AR_PHY_PLL_CTL_40 0xaa +#define AR_PHY_PLL_CTL_40_5413 0x04 +#define AR_PHY_PLL_CTL_44 0xab +#define AR_PHY_PLL_CTL_44_2133 0xeb +#define AR_PHY_PLL_CTL_40_2133 0xea + +#define AR_PHY_RX_DELAY 0x9914 +#define AR_PHY_SEARCH_START_DELAY 0x9918 +#define AR_PHY_RX_DELAY_DELAY 0x00003FFF + +#define AR_PHY_TIMING_CTRL4(_i) (0x9920 + ((_i) << 12)) +#define AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF 0x01F +#define AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF_S 0 +#define AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF 0x7E0 +#define AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF_S 5 +#define AR_PHY_TIMING_CTRL4_IQCORR_ENABLE 0x800 +#define AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX 0xF000 +#define AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX_S 12 +#define AR_PHY_TIMING_CTRL4_DO_CAL 0x10000 + +#define AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI 0x80000000 +#define AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER 0x40000000 +#define AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK 0x20000000 +#define AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK 0x10000000 + +#define AR_PHY_TIMING5 0x9924 +#define AR_PHY_TIMING5_CYCPWR_THR1 0x000000FE +#define AR_PHY_TIMING5_CYCPWR_THR1_S 1 + +#define AR_PHY_POWER_TX_RATE1 0x9934 +#define AR_PHY_POWER_TX_RATE2 0x9938 +#define AR_PHY_POWER_TX_RATE_MAX 0x993c +#define AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE 0x00000040 + +#define AR_PHY_FRAME_CTL 0x9944 +#define AR_PHY_FRAME_CTL_TX_CLIP 0x00000038 +#define AR_PHY_FRAME_CTL_TX_CLIP_S 3 + +#define AR_PHY_TXPWRADJ 0x994C +#define AR_PHY_TXPWRADJ_CCK_GAIN_DELTA 0x00000FC0 +#define AR_PHY_TXPWRADJ_CCK_GAIN_DELTA_S 6 +#define AR_PHY_TXPWRADJ_CCK_PCDAC_INDEX 0x00FC0000 +#define AR_PHY_TXPWRADJ_CCK_PCDAC_INDEX_S 18 + +#define AR_PHY_RADAR_EXT 0x9940 +#define AR_PHY_RADAR_EXT_ENA 0x00004000 + +#define AR_PHY_RADAR_0 0x9954 +#define AR_PHY_RADAR_0_ENA 0x00000001 +#define AR_PHY_RADAR_0_FFT_ENA 0x80000000 +#define AR_PHY_RADAR_0_INBAND 0x0000003e +#define AR_PHY_RADAR_0_INBAND_S 1 +#define AR_PHY_RADAR_0_PRSSI 0x00000FC0 +#define AR_PHY_RADAR_0_PRSSI_S 6 +#define AR_PHY_RADAR_0_HEIGHT 0x0003F000 +#define AR_PHY_RADAR_0_HEIGHT_S 12 +#define AR_PHY_RADAR_0_RRSSI 0x00FC0000 +#define AR_PHY_RADAR_0_RRSSI_S 18 +#define AR_PHY_RADAR_0_FIRPWR 0x7F000000 +#define AR_PHY_RADAR_0_FIRPWR_S 24 + +#define AR_PHY_RADAR_1 0x9958 +#define AR_PHY_RADAR_1_RELPWR_ENA 0x00800000 +#define AR_PHY_RADAR_1_USE_FIR128 0x00400000 +#define AR_PHY_RADAR_1_RELPWR_THRESH 0x003F0000 +#define AR_PHY_RADAR_1_RELPWR_THRESH_S 16 +#define AR_PHY_RADAR_1_BLOCK_CHECK 0x00008000 +#define AR_PHY_RADAR_1_MAX_RRSSI 0x00004000 +#define AR_PHY_RADAR_1_RELSTEP_CHECK 0x00002000 +#define AR_PHY_RADAR_1_RELSTEP_THRESH 0x00001F00 +#define AR_PHY_RADAR_1_RELSTEP_THRESH_S 8 +#define AR_PHY_RADAR_1_MAXLEN 0x000000FF +#define AR_PHY_RADAR_1_MAXLEN_S 0 + +#define AR_PHY_SWITCH_CHAIN_0 0x9960 +#define AR_PHY_SWITCH_COM 0x9964 + +#define AR_PHY_SIGMA_DELTA 0x996C +#define AR_PHY_SIGMA_DELTA_ADC_SEL 0x00000003 +#define AR_PHY_SIGMA_DELTA_ADC_SEL_S 0 +#define AR_PHY_SIGMA_DELTA_FILT2 0x000000F8 +#define AR_PHY_SIGMA_DELTA_FILT2_S 3 +#define AR_PHY_SIGMA_DELTA_FILT1 0x00001F00 +#define AR_PHY_SIGMA_DELTA_FILT1_S 8 +#define AR_PHY_SIGMA_DELTA_ADC_CLIP 0x01FFE000 +#define AR_PHY_SIGMA_DELTA_ADC_CLIP_S 13 + +#define AR_PHY_RESTART 0x9970 +#define AR_PHY_RESTART_DIV_GC 0x001C0000 +#define AR_PHY_RESTART_DIV_GC_S 18 + +#define AR_PHY_RFBUS_REQ 0x997C +#define AR_PHY_RFBUS_REQ_EN 0x00000001 + +#define AR_PHY_TIMING7 0x9980 +#define AR_PHY_TIMING8 0x9984 +#define AR_PHY_TIMING8_PILOT_MASK_2 0x000FFFFF +#define AR_PHY_TIMING8_PILOT_MASK_2_S 0 + +#define AR_PHY_BIN_MASK2_1 0x9988 +#define AR_PHY_BIN_MASK2_2 0x998c +#define AR_PHY_BIN_MASK2_3 0x9990 +#define AR_PHY_BIN_MASK2_4 0x9994 + +#define AR_PHY_BIN_MASK_1 0x9900 +#define AR_PHY_BIN_MASK_2 0x9904 +#define AR_PHY_BIN_MASK_3 0x9908 + +#define AR_PHY_MASK_CTL 0x990c + +#define AR_PHY_BIN_MASK2_4_MASK_4 0x00003FFF +#define AR_PHY_BIN_MASK2_4_MASK_4_S 0 + +#define AR_PHY_TIMING9 0x9998 +#define AR_PHY_TIMING10 0x999c +#define AR_PHY_TIMING10_PILOT_MASK_2 0x000FFFFF +#define AR_PHY_TIMING10_PILOT_MASK_2_S 0 + +#define AR_PHY_TIMING11 0x99a0 +#define AR_PHY_TIMING11_SPUR_DELTA_PHASE 0x000FFFFF +#define AR_PHY_TIMING11_SPUR_DELTA_PHASE_S 0 +#define AR_PHY_TIMING11_SPUR_FREQ_SD 0x3FF00000 +#define AR_PHY_TIMING11_SPUR_FREQ_SD_S 20 +#define AR_PHY_TIMING11_USE_SPUR_IN_AGC 0x40000000 +#define AR_PHY_TIMING11_USE_SPUR_IN_SELFCOR 0x80000000 + +#define AR_PHY_RX_CHAINMASK 0x99a4 +#define AR_PHY_NEW_ADC_DC_GAIN_CORR(_i) (0x99b4 + ((_i) << 12)) +#define AR_PHY_NEW_ADC_GAIN_CORR_ENABLE 0x40000000 +#define AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE 0x80000000 +#define AR_PHY_MULTICHAIN_GAIN_CTL 0x99ac + +#define AR_PHY_EXT_CCA0 0x99b8 +#define AR_PHY_EXT_CCA0_THRESH62 0x000000FF +#define AR_PHY_EXT_CCA0_THRESH62_S 0 + +#define AR_PHY_EXT_CCA 0x99bc +#define AR_PHY_EXT_CCA_CYCPWR_THR1 0x0000FE00 +#define AR_PHY_EXT_CCA_CYCPWR_THR1_S 9 +#define AR_PHY_EXT_CCA_THRESH62 0x007F0000 +#define AR_PHY_EXT_CCA_THRESH62_S 16 +#define AR_PHY_EXT_MINCCA_PWR 0xFF800000 +#define AR_PHY_EXT_MINCCA_PWR_S 23 +#define AR9280_PHY_EXT_MINCCA_PWR 0x01FF0000 +#define AR9280_PHY_EXT_MINCCA_PWR_S 16 + +#define AR_PHY_SFCORR_EXT 0x99c0 +#define AR_PHY_SFCORR_EXT_M1_THRESH 0x0000007F +#define AR_PHY_SFCORR_EXT_M1_THRESH_S 0 +#define AR_PHY_SFCORR_EXT_M2_THRESH 0x00003F80 +#define AR_PHY_SFCORR_EXT_M2_THRESH_S 7 +#define AR_PHY_SFCORR_EXT_M1_THRESH_LOW 0x001FC000 +#define AR_PHY_SFCORR_EXT_M1_THRESH_LOW_S 14 +#define AR_PHY_SFCORR_EXT_M2_THRESH_LOW 0x0FE00000 +#define AR_PHY_SFCORR_EXT_M2_THRESH_LOW_S 21 +#define AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S 28 + +#define AR_PHY_HALFGI 0x99D0 +#define AR_PHY_HALFGI_DSC_MAN 0x0007FFF0 +#define AR_PHY_HALFGI_DSC_MAN_S 4 +#define AR_PHY_HALFGI_DSC_EXP 0x0000000F +#define AR_PHY_HALFGI_DSC_EXP_S 0 + +#define AR_PHY_CHAN_INFO_MEMORY 0x99DC +#define AR_PHY_CHAN_INFO_MEMORY_CAPTURE_MASK 0x0001 + +#define AR_PHY_HEAVY_CLIP_ENABLE 0x99E0 + +#define AR_PHY_M_SLEEP 0x99f0 +#define AR_PHY_REFCLKDLY 0x99f4 +#define AR_PHY_REFCLKPD 0x99f8 + +#define AR_PHY_CALMODE 0x99f0 + +#define AR_PHY_CALMODE_IQ 0x00000000 +#define AR_PHY_CALMODE_ADC_GAIN 0x00000001 +#define AR_PHY_CALMODE_ADC_DC_PER 0x00000002 +#define AR_PHY_CALMODE_ADC_DC_INIT 0x00000003 + +#define AR_PHY_CAL_MEAS_0(_i) (0x9c10 + ((_i) << 12)) +#define AR_PHY_CAL_MEAS_1(_i) (0x9c14 + ((_i) << 12)) +#define AR_PHY_CAL_MEAS_2(_i) (0x9c18 + ((_i) << 12)) +#define AR_PHY_CAL_MEAS_3(_i) (0x9c1c + ((_i) << 12)) + +#define AR_PHY_CURRENT_RSSI 0x9c1c +#define AR9280_PHY_CURRENT_RSSI 0x9c3c + +#define AR_PHY_RFBUS_GRANT 0x9C20 +#define AR_PHY_RFBUS_GRANT_EN 0x00000001 + +#define AR_PHY_CHAN_INFO_GAIN_DIFF 0x9CF4 +#define AR_PHY_CHAN_INFO_GAIN_DIFF_UPPER_LIMIT 320 + +#define AR_PHY_CHAN_INFO_GAIN 0x9CFC + +#define AR_PHY_MODE 0xA200 +#define AR_PHY_MODE_AR2133 0x08 +#define AR_PHY_MODE_AR5111 0x00 +#define AR_PHY_MODE_AR5112 0x08 +#define AR_PHY_MODE_DYNAMIC 0x04 +#define AR_PHY_MODE_RF2GHZ 0x02 +#define AR_PHY_MODE_RF5GHZ 0x00 +#define AR_PHY_MODE_CCK 0x01 +#define AR_PHY_MODE_OFDM 0x00 +#define AR_PHY_MODE_DYN_CCK_DISABLE 0x100 + +#define AR_PHY_CCK_TX_CTRL 0xA204 +#define AR_PHY_CCK_TX_CTRL_JAPAN 0x00000010 + +#define AR_PHY_CCK_DETECT 0xA208 +#define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK 0x0000003F +#define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK_S 0 +/* [12:6] settling time for antenna switch */ +#define AR_PHY_CCK_DETECT_ANT_SWITCH_TIME 0x00001FC0 +#define AR_PHY_CCK_DETECT_ANT_SWITCH_TIME_S 6 +#define AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV 0x2000 + +#define AR_PHY_GAIN_2GHZ 0xA20C +#define AR_PHY_GAIN_2GHZ_RXTX_MARGIN 0x00FC0000 +#define AR_PHY_GAIN_2GHZ_RXTX_MARGIN_S 18 +#define AR_PHY_GAIN_2GHZ_BSW_MARGIN 0x00003C00 +#define AR_PHY_GAIN_2GHZ_BSW_MARGIN_S 10 +#define AR_PHY_GAIN_2GHZ_BSW_ATTEN 0x0000001F +#define AR_PHY_GAIN_2GHZ_BSW_ATTEN_S 0 + +#define AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN 0x003E0000 +#define AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN_S 17 +#define AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN 0x0001F000 +#define AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN_S 12 +#define AR_PHY_GAIN_2GHZ_XATTEN2_DB 0x00000FC0 +#define AR_PHY_GAIN_2GHZ_XATTEN2_DB_S 6 +#define AR_PHY_GAIN_2GHZ_XATTEN1_DB 0x0000003F +#define AR_PHY_GAIN_2GHZ_XATTEN1_DB_S 0 + +#define AR_PHY_CCK_RXCTRL4 0xA21C +#define AR_PHY_CCK_RXCTRL4_FREQ_EST_SHORT 0x01F80000 +#define AR_PHY_CCK_RXCTRL4_FREQ_EST_SHORT_S 19 + +#define AR_PHY_DAG_CTRLCCK 0xA228 +#define AR_PHY_DAG_CTRLCCK_EN_RSSI_THR 0x00000200 +#define AR_PHY_DAG_CTRLCCK_RSSI_THR 0x0001FC00 +#define AR_PHY_DAG_CTRLCCK_RSSI_THR_S 10 + +#define AR_PHY_FORCE_CLKEN_CCK 0xA22C +#define AR_PHY_FORCE_CLKEN_CCK_MRC_MUX 0x00000040 + +#define AR_PHY_POWER_TX_RATE3 0xA234 +#define AR_PHY_POWER_TX_RATE4 0xA238 + +#define AR_PHY_SCRM_SEQ_XR 0xA23C +#define AR_PHY_HEADER_DETECT_XR 0xA240 +#define AR_PHY_CHIRP_DETECTED_XR 0xA244 +#define AR_PHY_BLUETOOTH 0xA254 + +#define AR_PHY_TPCRG1 0xA258 +#define AR_PHY_TPCRG1_NUM_PD_GAIN 0x0000c000 +#define AR_PHY_TPCRG1_NUM_PD_GAIN_S 14 + +#define AR_PHY_TPCRG1_PD_GAIN_1 0x00030000 +#define AR_PHY_TPCRG1_PD_GAIN_1_S 16 +#define AR_PHY_TPCRG1_PD_GAIN_2 0x000C0000 +#define AR_PHY_TPCRG1_PD_GAIN_2_S 18 +#define AR_PHY_TPCRG1_PD_GAIN_3 0x00300000 +#define AR_PHY_TPCRG1_PD_GAIN_3_S 20 + +#define AR_PHY_VIT_MASK2_M_46_61 0xa3a0 +#define AR_PHY_MASK2_M_31_45 0xa3a4 +#define AR_PHY_MASK2_M_16_30 0xa3a8 +#define AR_PHY_MASK2_M_00_15 0xa3ac +#define AR_PHY_MASK2_P_15_01 0xa3b8 +#define AR_PHY_MASK2_P_30_16 0xa3bc +#define AR_PHY_MASK2_P_45_31 0xa3c0 +#define AR_PHY_MASK2_P_61_45 0xa3c4 +#define AR_PHY_SPUR_REG 0x994c + +#define AR_PHY_SPUR_REG_MASK_RATE_CNTL (0xFF << 18) +#define AR_PHY_SPUR_REG_MASK_RATE_CNTL_S 18 + +#define AR_PHY_SPUR_REG_ENABLE_MASK_PPM 0x20000 +#define AR_PHY_SPUR_REG_MASK_RATE_SELECT (0xFF << 9) +#define AR_PHY_SPUR_REG_MASK_RATE_SELECT_S 9 +#define AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI 0x100 +#define AR_PHY_SPUR_REG_SPUR_RSSI_THRESH 0x7F +#define AR_PHY_SPUR_REG_SPUR_RSSI_THRESH_S 0 + +#define AR_PHY_PILOT_MASK_01_30 0xa3b0 +#define AR_PHY_PILOT_MASK_31_60 0xa3b4 + +#define AR_PHY_CHANNEL_MASK_01_30 0x99d4 +#define AR_PHY_CHANNEL_MASK_31_60 0x99d8 + +#define AR_PHY_ANALOG_SWAP 0xa268 +#define AR_PHY_SWAP_ALT_CHAIN 0x00000040 + +#define AR_PHY_TPCRG5 0xA26C +#define AR_PHY_TPCRG5_PD_GAIN_OVERLAP 0x0000000F +#define AR_PHY_TPCRG5_PD_GAIN_OVERLAP_S 0 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1 0x000003F0 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1_S 4 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2 0x0000FC00 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2_S 10 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3 0x003F0000 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3_S 16 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4 0x0FC00000 +#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4_S 22 + +#define AR_PHY_POWER_TX_RATE5 0xA38C +#define AR_PHY_POWER_TX_RATE6 0xA390 + +#define AR_PHY_CAL_CHAINMASK 0xA39C + +#define AR_PHY_POWER_TX_SUB 0xA3C8 +#define AR_PHY_POWER_TX_RATE7 0xA3CC +#define AR_PHY_POWER_TX_RATE8 0xA3D0 +#define AR_PHY_POWER_TX_RATE9 0xA3D4 + +#define AR_PHY_XPA_CFG 0xA3D8 +#define AR_PHY_FORCE_XPA_CFG 0x000000001 +#define AR_PHY_FORCE_XPA_CFG_S 0 + +#define AR_PHY_CH1_CCA 0xa864 +#define AR_PHY_CH1_MINCCA_PWR 0x0FF80000 +#define AR_PHY_CH1_MINCCA_PWR_S 19 +#define AR9280_PHY_CH1_MINCCA_PWR 0x1FF00000 +#define AR9280_PHY_CH1_MINCCA_PWR_S 20 + +#define AR_PHY_CH2_CCA 0xb864 +#define AR_PHY_CH2_MINCCA_PWR 0x0FF80000 +#define AR_PHY_CH2_MINCCA_PWR_S 19 + +#define AR_PHY_CH1_EXT_CCA 0xa9bc +#define AR_PHY_CH1_EXT_MINCCA_PWR 0xFF800000 +#define AR_PHY_CH1_EXT_MINCCA_PWR_S 23 +#define AR9280_PHY_CH1_EXT_MINCCA_PWR 0x01FF0000 +#define AR9280_PHY_CH1_EXT_MINCCA_PWR_S 16 + +#define AR_PHY_CH2_EXT_CCA 0xb9bc +#define AR_PHY_CH2_EXT_MINCCA_PWR 0xFF800000 +#define AR_PHY_CH2_EXT_MINCCA_PWR_S 23 + +#define REG_WRITE_RF_ARRAY(iniarray, regData, regWr) do { \ + int r; \ + for (r = 0; r < ((iniarray)->ia_rows); r++) { \ + REG_WRITE(ah, INI_RA((iniarray), r, 0), (regData)[r]); \ + DPRINTF(ah->ah_sc, ATH_DBG_CHANNEL, \ + "RF 0x%x V 0x%x\n", \ + INI_RA((iniarray), r, 0), (regData)[r]); \ + DO_DELAY(regWr); \ + } \ + } while (0) + +#define ATH9K_KEY_XOR 0xaa + +#define ATH9K_IS_MIC_ENABLED(ah) \ + (AH5416(ah)->ah_staId1Defaults & AR_STA_ID1_CRPT_MIC_ENABLE) + +#define ANTSWAP_AB 0x0001 +#define REDUCE_CHAIN_0 0x00000050 +#define REDUCE_CHAIN_1 0x00000051 + +#define RF_BANK_SETUP(_bank, _iniarray, _col) do { \ + int i; \ + for (i = 0; i < (_iniarray)->ia_rows; i++) \ + (_bank)[i] = INI_RA((_iniarray), i, _col);; \ + } while (0) + +#endif diff --git a/drivers/net/wireless/ath9k/rc.c b/drivers/net/wireless/ath9k/rc.c new file mode 100644 index 000000000000..e0797afee52b --- /dev/null +++ b/drivers/net/wireless/ath9k/rc.c @@ -0,0 +1,2126 @@ +/* + * Copyright (c) 2004 Video54 Technologies, Inc. + * Copyright (c) 2004-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Atheros rate control algorithm + */ + +#include "core.h" +#include "../net/mac80211/rate.h" + +static u32 tx_triglevel_max; + +static struct ath_rate_table ar5416_11na_ratetable = { + 42, + { + { TRUE, TRUE, WLAN_PHY_OFDM, 6000, /* 6 Mb */ + 5400, 0x0b, 0x00, 12, + 0, 2, 1, 0, 0, 0, 0, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 9000, /* 9 Mb */ + 7800, 0x0f, 0x00, 18, + 0, 3, 1, 1, 1, 1, 1, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 12 Mb */ + 10000, 0x0a, 0x00, 24, + 2, 4, 2, 2, 2, 2, 2, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 18 Mb */ + 13900, 0x0e, 0x00, 36, + 2, 6, 2, 3, 3, 3, 3, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 24 Mb */ + 17300, 0x09, 0x00, 48, + 4, 10, 3, 4, 4, 4, 4, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 36000, /* 36 Mb */ + 23000, 0x0d, 0x00, 72, + 4, 14, 3, 5, 5, 5, 5, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 48000, /* 48 Mb */ + 27400, 0x08, 0x00, 96, + 4, 20, 3, 6, 6, 6, 6, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 54000, /* 54 Mb */ + 29300, 0x0c, 0x00, 108, + 4, 23, 3, 7, 7, 7, 7, 0 }, + { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 6500, /* 6.5 Mb */ + 6400, 0x80, 0x00, 0, + 0, 2, 3, 8, 24, 8, 24, 3216 }, + { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 13000, /* 13 Mb */ + 12700, 0x81, 0x00, 1, + 2, 4, 3, 9, 25, 9, 25, 6434 }, + { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 19500, /* 19.5 Mb */ + 18800, 0x82, 0x00, 2, + 2, 6, 3, 10, 26, 10, 26, 9650 }, + { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 26000, /* 26 Mb */ + 25000, 0x83, 0x00, 3, + 4, 10, 3, 11, 27, 11, 27, 12868 }, + { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 39000, /* 39 Mb */ + 36700, 0x84, 0x00, 4, + 4, 14, 3, 12, 28, 12, 28, 19304 }, + { FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 52000, /* 52 Mb */ + 48100, 0x85, 0x00, 5, + 4, 20, 3, 13, 29, 13, 29, 25740 }, + { FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 58500, /* 58.5 Mb */ + 53500, 0x86, 0x00, 6, + 4, 23, 3, 14, 30, 14, 30, 28956 }, + { FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 65000, /* 65 Mb */ + 59000, 0x87, 0x00, 7, + 4, 25, 3, 15, 31, 15, 32, 32180 }, + { FALSE, FALSE, WLAN_PHY_HT_20_DS, 13000, /* 13 Mb */ + 12700, 0x88, 0x00, + 8, 0, 2, 3, 16, 33, 16, 33, 6430 }, + { FALSE, FALSE, WLAN_PHY_HT_20_DS, 26000, /* 26 Mb */ + 24800, 0x89, 0x00, 9, + 2, 4, 3, 17, 34, 17, 34, 12860 }, + { FALSE, FALSE, WLAN_PHY_HT_20_DS, 39000, /* 39 Mb */ + 36600, 0x8a, 0x00, 10, + 2, 6, 3, 18, 35, 18, 35, 19300 }, + { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 52000, /* 52 Mb */ + 48100, 0x8b, 0x00, 11, + 4, 10, 3, 19, 36, 19, 36, 25736 }, + { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 78000, /* 78 Mb */ + 69500, 0x8c, 0x00, 12, + 4, 14, 3, 20, 37, 20, 37, 38600 }, + { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 104000, /* 104 Mb */ + 89500, 0x8d, 0x00, 13, + 4, 20, 3, 21, 38, 21, 38, 51472 }, + { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 117000, /* 117 Mb */ + 98900, 0x8e, 0x00, 14, + 4, 23, 3, 22, 39, 22, 39, 57890 }, + { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 130000, /* 130 Mb */ + 108300, 0x8f, 0x00, 15, + 4, 25, 3, 23, 40, 23, 41, 64320 }, + { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 13500, /* 13.5 Mb */ + 13200, 0x80, 0x00, 0, + 0, 2, 3, 8, 24, 24, 24, 6684 }, + { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 27500, /* 27.0 Mb */ + 25900, 0x81, 0x00, 1, + 2, 4, 3, 9, 25, 25, 25, 13368 }, + { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 40500, /* 40.5 Mb */ + 38600, 0x82, 0x00, 2, + 2, 6, 3, 10, 26, 26, 26, 20052 }, + { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 54000, /* 54 Mb */ + 49800, 0x83, 0x00, 3, + 4, 10, 3, 11, 27, 27, 27, 26738 }, + { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 81500, /* 81 Mb */ + 72200, 0x84, 0x00, 4, + 4, 14, 3, 12, 28, 28, 28, 40104 }, + { FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 108000, /* 108 Mb */ + 92900, 0x85, 0x00, 5, + 4, 20, 3, 13, 29, 29, 29, 53476 }, + { FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 121500, /* 121.5 Mb */ + 102700, 0x86, 0x00, 6, + 4, 23, 3, 14, 30, 30, 30, 60156 }, + { FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 135000, /* 135 Mb */ + 112000, 0x87, 0x00, 7, + 4, 25, 3, 15, 31, 32, 32, 66840 }, + { FALSE, TRUE_40, WLAN_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */ + 122000, 0x87, 0x00, 7, + 4, 25, 3, 15, 31, 32, 32, 74200 }, + { FALSE, FALSE, WLAN_PHY_HT_40_DS, 27000, /* 27 Mb */ + 25800, 0x88, 0x00, 8, + 0, 2, 3, 16, 33, 33, 33, 13360 }, + { FALSE, FALSE, WLAN_PHY_HT_40_DS, 54000, /* 54 Mb */ + 49800, 0x89, 0x00, 9, + 2, 4, 3, 17, 34, 34, 34, 26720 }, + { FALSE, FALSE, WLAN_PHY_HT_40_DS, 81000, /* 81 Mb */ + 71900, 0x8a, 0x00, 10, + 2, 6, 3, 18, 35, 35, 35, 40080 }, + { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 108000, /* 108 Mb */ + 92500, 0x8b, 0x00, 11, + 4, 10, 3, 19, 36, 36, 36, 53440 }, + { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 162000, /* 162 Mb */ + 130300, 0x8c, 0x00, 12, + 4, 14, 3, 20, 37, 37, 37, 80160 }, + { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 216000, /* 216 Mb */ + 162800, 0x8d, 0x00, 13, + 4, 20, 3, 21, 38, 38, 38, 106880 }, + { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 243000, /* 243 Mb */ + 178200, 0x8e, 0x00, 14, + 4, 23, 3, 22, 39, 39, 39, 120240 }, + { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 270000, /* 270 Mb */ + 192100, 0x8f, 0x00, 15, + 4, 25, 3, 23, 40, 41, 41, 133600 }, + { TRUE_40, FALSE, WLAN_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */ + 207000, 0x8f, 0x00, 15, + 4, 25, 3, 23, 40, 41, 41, 148400 }, + }, + 50, /* probe interval */ + 50, /* rssi reduce interval */ + WLAN_RC_HT_FLAG, /* Phy rates allowed initially */ +}; + +/* TRUE_ALL - valid for 20/40/Legacy, + * TRUE - Legacy only, + * TRUE_20 - HT 20 only, + * TRUE_40 - HT 40 only */ + +/* 4ms frame limit not used for NG mode. The values filled + * for HT are the 64K max aggregate limit */ + +static struct ath_rate_table ar5416_11ng_ratetable = { + 46, + { + { TRUE_ALL, TRUE_ALL, WLAN_PHY_CCK, 1000, /* 1 Mb */ + 900, 0x1b, 0x00, 2, + 0, 0, 1, 0, 0, 0, 0, 0 }, + { TRUE_ALL, TRUE_ALL, WLAN_PHY_CCK, 2000, /* 2 Mb */ + 1900, 0x1a, 0x04, 4, + 1, 1, 1, 1, 1, 1, 1, 0 }, + { TRUE_ALL, TRUE_ALL, WLAN_PHY_CCK, 5500, /* 5.5 Mb */ + 4900, 0x19, 0x04, 11, + 2, 2, 2, 2, 2, 2, 2, 0 }, + { TRUE_ALL, TRUE_ALL, WLAN_PHY_CCK, 11000, /* 11 Mb */ + 8100, 0x18, 0x04, 22, + 3, 3, 2, 3, 3, 3, 3, 0 }, + { FALSE, FALSE, WLAN_PHY_OFDM, 6000, /* 6 Mb */ + 5400, 0x0b, 0x00, 12, + 4, 2, 1, 4, 4, 4, 4, 0 }, + { FALSE, FALSE, WLAN_PHY_OFDM, 9000, /* 9 Mb */ + 7800, 0x0f, 0x00, 18, + 4, 3, 1, 5, 5, 5, 5, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 12 Mb */ + 10100, 0x0a, 0x00, 24, + 6, 4, 1, 6, 6, 6, 6, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 18 Mb */ + 14100, 0x0e, 0x00, 36, + 6, 6, 2, 7, 7, 7, 7, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 24 Mb */ + 17700, 0x09, 0x00, 48, + 8, 10, 3, 8, 8, 8, 8, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 36000, /* 36 Mb */ + 23700, 0x0d, 0x00, 72, + 8, 14, 3, 9, 9, 9, 9, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 48000, /* 48 Mb */ + 27400, 0x08, 0x00, 96, + 8, 20, 3, 10, 10, 10, 10, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 54000, /* 54 Mb */ + 30900, 0x0c, 0x00, 108, + 8, 23, 3, 11, 11, 11, 11, 0 }, + { FALSE, FALSE, WLAN_PHY_HT_20_SS, 6500, /* 6.5 Mb */ + 6400, 0x80, 0x00, 0, + 4, 2, 3, 12, 28, 12, 28, 3216 }, + { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 13000, /* 13 Mb */ + 12700, 0x81, 0x00, 1, + 6, 4, 3, 13, 29, 13, 29, 6434 }, + { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 19500, /* 19.5 Mb */ + 18800, 0x82, 0x00, 2, + 6, 6, 3, 14, 30, 14, 30, 9650 }, + { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 26000, /* 26 Mb */ + 25000, 0x83, 0x00, 3, + 8, 10, 3, 15, 31, 15, 31, 12868 }, + { TRUE_20, TRUE_20, WLAN_PHY_HT_20_SS, 39000, /* 39 Mb */ + 36700, 0x84, 0x00, 4, + 8, 14, 3, 16, 32, 16, 32, 19304 }, + { FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 52000, /* 52 Mb */ + 48100, 0x85, 0x00, 5, + 8, 20, 3, 17, 33, 17, 33, 25740 }, + { FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 58500, /* 58.5 Mb */ + 53500, 0x86, 0x00, 6, + 8, 23, 3, 18, 34, 18, 34, 28956 }, + { FALSE, TRUE_20, WLAN_PHY_HT_20_SS, 65000, /* 65 Mb */ + 59000, 0x87, 0x00, 7, + 8, 25, 3, 19, 35, 19, 36, 32180 }, + { FALSE, FALSE, WLAN_PHY_HT_20_DS, 13000, /* 13 Mb */ + 12700, 0x88, 0x00, 8, + 4, 2, 3, 20, 37, 20, 37, 6430 }, + { FALSE, FALSE, WLAN_PHY_HT_20_DS, 26000, /* 26 Mb */ + 24800, 0x89, 0x00, 9, + 6, 4, 3, 21, 38, 21, 38, 12860 }, + { FALSE, FALSE, WLAN_PHY_HT_20_DS, 39000, /* 39 Mb */ + 36600, 0x8a, 0x00, 10, + 6, 6, 3, 22, 39, 22, 39, 19300 }, + { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 52000, /* 52 Mb */ + 48100, 0x8b, 0x00, 11, + 8, 10, 3, 23, 40, 23, 40, 25736 }, + { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 78000, /* 78 Mb */ + 69500, 0x8c, 0x00, 12, + 8, 14, 3, 24, 41, 24, 41, 38600 }, + { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 104000, /* 104 Mb */ + 89500, 0x8d, 0x00, 13, + 8, 20, 3, 25, 42, 25, 42, 51472 }, + { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 117000, /* 117 Mb */ + 98900, 0x8e, 0x00, 14, + 8, 23, 3, 26, 43, 26, 44, 57890 }, + { TRUE_20, FALSE, WLAN_PHY_HT_20_DS, 130000, /* 130 Mb */ + 108300, 0x8f, 0x00, 15, + 8, 25, 3, 27, 44, 27, 45, 64320 }, + { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 13500, /* 13.5 Mb */ + 13200, 0x80, 0x00, 0, + 8, 2, 3, 12, 28, 28, 28, 6684 }, + { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 27500, /* 27.0 Mb */ + 25900, 0x81, 0x00, 1, + 8, 4, 3, 13, 29, 29, 29, 13368 }, + { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 40500, /* 40.5 Mb */ + 38600, 0x82, 0x00, 2, + 8, 6, 3, 14, 30, 30, 30, 20052 }, + { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 54000, /* 54 Mb */ + 49800, 0x83, 0x00, 3, + 8, 10, 3, 15, 31, 31, 31, 26738 }, + { TRUE_40, TRUE_40, WLAN_PHY_HT_40_SS, 81500, /* 81 Mb */ + 72200, 0x84, 0x00, 4, + 8, 14, 3, 16, 32, 32, 32, 40104 }, + { FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 108000, /* 108 Mb */ + 92900, 0x85, 0x00, 5, + 8, 20, 3, 17, 33, 33, 33, 53476 }, + { FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 121500, /* 121.5 Mb */ + 102700, 0x86, 0x00, 6, + 8, 23, 3, 18, 34, 34, 34, 60156 }, + { FALSE, TRUE_40, WLAN_PHY_HT_40_SS, 135000, /* 135 Mb */ + 112000, 0x87, 0x00, 7, + 8, 23, 3, 19, 35, 36, 36, 66840 }, + { FALSE, TRUE_40, WLAN_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */ + 122000, 0x87, 0x00, 7, + 8, 25, 3, 19, 35, 36, 36, 74200 }, + { FALSE, FALSE, WLAN_PHY_HT_40_DS, 27000, /* 27 Mb */ + 25800, 0x88, 0x00, 8, + 8, 2, 3, 20, 37, 37, 37, 13360 }, + { FALSE, FALSE, WLAN_PHY_HT_40_DS, 54000, /* 54 Mb */ + 49800, 0x89, 0x00, 9, + 8, 4, 3, 21, 38, 38, 38, 26720 }, + { FALSE, FALSE, WLAN_PHY_HT_40_DS, 81000, /* 81 Mb */ + 71900, 0x8a, 0x00, 10, + 8, 6, 3, 22, 39, 39, 39, 40080 }, + { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 108000, /* 108 Mb */ + 92500, 0x8b, 0x00, 11, + 8, 10, 3, 23, 40, 40, 40, 53440 }, + { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 162000, /* 162 Mb */ + 130300, 0x8c, 0x00, 12, + 8, 14, 3, 24, 41, 41, 41, 80160 }, + { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 216000, /* 216 Mb */ + 162800, 0x8d, 0x00, 13, + 8, 20, 3, 25, 42, 42, 42, 106880 }, + { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 243000, /* 243 Mb */ + 178200, 0x8e, 0x00, 14, + 8, 23, 3, 26, 43, 43, 43, 120240 }, + { TRUE_40, FALSE, WLAN_PHY_HT_40_DS, 270000, /* 270 Mb */ + 192100, 0x8f, 0x00, 15, + 8, 23, 3, 27, 44, 45, 45, 133600 }, + { TRUE_40, FALSE, WLAN_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */ + 207000, 0x8f, 0x00, 15, + 8, 25, 3, 27, 44, 45, 45, 148400 }, + }, + 50, /* probe interval */ + 50, /* rssi reduce interval */ + WLAN_RC_HT_FLAG, /* Phy rates allowed initially */ +}; + +static struct ath_rate_table ar5416_11a_ratetable = { + 8, + { + { TRUE, TRUE, WLAN_PHY_OFDM, 6000, /* 6 Mb */ + 5400, 0x0b, 0x00, (0x80|12), + 0, 2, 1, 0, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 9000, /* 9 Mb */ + 7800, 0x0f, 0x00, 18, + 0, 3, 1, 1, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 12 Mb */ + 10000, 0x0a, 0x00, (0x80|24), + 2, 4, 2, 2, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 18 Mb */ + 13900, 0x0e, 0x00, 36, + 2, 6, 2, 3, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 24 Mb */ + 17300, 0x09, 0x00, (0x80|48), + 4, 10, 3, 4, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 36000, /* 36 Mb */ + 23000, 0x0d, 0x00, 72, + 4, 14, 3, 5, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 48000, /* 48 Mb */ + 27400, 0x08, 0x00, 96, + 4, 19, 3, 6, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 54000, /* 54 Mb */ + 29300, 0x0c, 0x00, 108, + 4, 23, 3, 7, 0 }, + }, + 50, /* probe interval */ + 50, /* rssi reduce interval */ + 0, /* Phy rates allowed initially */ +}; + +static struct ath_rate_table ar5416_11a_ratetable_Half = { + 8, + { + { TRUE, TRUE, WLAN_PHY_OFDM, 3000, /* 6 Mb */ + 2700, 0x0b, 0x00, (0x80|6), + 0, 2, 1, 0, 0}, + { TRUE, TRUE, WLAN_PHY_OFDM, 4500, /* 9 Mb */ + 3900, 0x0f, 0x00, 9, + 0, 3, 1, 1, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 6000, /* 12 Mb */ + 5000, 0x0a, 0x00, (0x80|12), + 2, 4, 2, 2, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 9000, /* 18 Mb */ + 6950, 0x0e, 0x00, 18, + 2, 6, 2, 3, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 24 Mb */ + 8650, 0x09, 0x00, (0x80|24), + 4, 10, 3, 4, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 36 Mb */ + 11500, 0x0d, 0x00, 36, + 4, 14, 3, 5, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 48 Mb */ + 13700, 0x08, 0x00, 48, + 4, 19, 3, 6, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 27000, /* 54 Mb */ + 14650, 0x0c, 0x00, 54, + 4, 23, 3, 7, 0 }, + }, + 50, /* probe interval */ + 50, /* rssi reduce interval */ + 0, /* Phy rates allowed initially */ +}; + +static struct ath_rate_table ar5416_11a_ratetable_Quarter = { + 8, + { + { TRUE, TRUE, WLAN_PHY_OFDM, 1500, /* 6 Mb */ + 1350, 0x0b, 0x00, (0x80|3), + 0, 2, 1, 0, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 2250, /* 9 Mb */ + 1950, 0x0f, 0x00, 4, + 0, 3, 1, 1, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 3000, /* 12 Mb */ + 2500, 0x0a, 0x00, (0x80|6), + 2, 4, 2, 2, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 4500, /* 18 Mb */ + 3475, 0x0e, 0x00, 9, + 2, 6, 2, 3, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 6000, /* 25 Mb */ + 4325, 0x09, 0x00, (0x80|12), + 4, 10, 3, 4, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 9000, /* 36 Mb */ + 5750, 0x0d, 0x00, 18, + 4, 14, 3, 5, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 48 Mb */ + 6850, 0x08, 0x00, 24, + 4, 19, 3, 6, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 13500, /* 54 Mb */ + 7325, 0x0c, 0x00, 27, + 4, 23, 3, 7, 0 }, + }, + 50, /* probe interval */ + 50, /* rssi reduce interval */ + 0, /* Phy rates allowed initially */ +}; + +static struct ath_rate_table ar5416_11g_ratetable = { + 12, + { + { TRUE, TRUE, WLAN_PHY_CCK, 1000, /* 1 Mb */ + 900, 0x1b, 0x00, 2, + 0, 0, 1, 0, 0 }, + { TRUE, TRUE, WLAN_PHY_CCK, 2000, /* 2 Mb */ + 1900, 0x1a, 0x04, 4, + 1, 1, 1, 1, 0 }, + { TRUE, TRUE, WLAN_PHY_CCK, 5500, /* 5.5 Mb */ + 4900, 0x19, 0x04, 11, + 2, 2, 2, 2, 0 }, + { TRUE, TRUE, WLAN_PHY_CCK, 11000, /* 11 Mb */ + 8100, 0x18, 0x04, 22, + 3, 3, 2, 3, 0 }, + { FALSE, FALSE, WLAN_PHY_OFDM, 6000, /* 6 Mb */ + 5400, 0x0b, 0x00, 12, + 4, 2, 1, 4, 0 }, + { FALSE, FALSE, WLAN_PHY_OFDM, 9000, /* 9 Mb */ + 7800, 0x0f, 0x00, 18, + 4, 3, 1, 5, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 12000, /* 12 Mb */ + 10000, 0x0a, 0x00, 24, + 6, 4, 1, 6, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 18000, /* 18 Mb */ + 13900, 0x0e, 0x00, 36, + 6, 6, 2, 7, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 24000, /* 24 Mb */ + 17300, 0x09, 0x00, 48, + 8, 10, 3, 8, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 36000, /* 36 Mb */ + 23000, 0x0d, 0x00, 72, + 8, 14, 3, 9, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 48000, /* 48 Mb */ + 27400, 0x08, 0x00, 96, + 8, 19, 3, 10, 0 }, + { TRUE, TRUE, WLAN_PHY_OFDM, 54000, /* 54 Mb */ + 29300, 0x0c, 0x00, 108, + 8, 23, 3, 11, 0 }, + }, + 50, /* probe interval */ + 50, /* rssi reduce interval */ + 0, /* Phy rates allowed initially */ +}; + +static struct ath_rate_table ar5416_11b_ratetable = { + 4, + { + { TRUE, TRUE, WLAN_PHY_CCK, 1000, /* 1 Mb */ + 900, 0x1b, 0x00, (0x80|2), + 0, 0, 1, 0, 0 }, + { TRUE, TRUE, WLAN_PHY_CCK, 2000, /* 2 Mb */ + 1800, 0x1a, 0x04, (0x80|4), + 1, 1, 1, 1, 0 }, + { TRUE, TRUE, WLAN_PHY_CCK, 5500, /* 5.5 Mb */ + 4300, 0x19, 0x04, (0x80|11), + 1, 2, 2, 2, 0 }, + { TRUE, TRUE, WLAN_PHY_CCK, 11000, /* 11 Mb */ + 7100, 0x18, 0x04, (0x80|22), + 1, 4, 100, 3, 0 }, + }, + 100, /* probe interval */ + 100, /* rssi reduce interval */ + 0, /* Phy rates allowed initially */ +}; + +static void ar5416_attach_ratetables(struct ath_rate_softc *sc) +{ + /* + * Attach rate tables. + */ + sc->hw_rate_table[WIRELESS_MODE_11b] = &ar5416_11b_ratetable; + sc->hw_rate_table[WIRELESS_MODE_11a] = &ar5416_11a_ratetable; + sc->hw_rate_table[WIRELESS_MODE_11g] = &ar5416_11g_ratetable; + + sc->hw_rate_table[WIRELESS_MODE_11NA_HT20] = &ar5416_11na_ratetable; + sc->hw_rate_table[WIRELESS_MODE_11NG_HT20] = &ar5416_11ng_ratetable; + sc->hw_rate_table[WIRELESS_MODE_11NA_HT40PLUS] = + &ar5416_11na_ratetable; + sc->hw_rate_table[WIRELESS_MODE_11NA_HT40MINUS] = + &ar5416_11na_ratetable; + sc->hw_rate_table[WIRELESS_MODE_11NG_HT40PLUS] = + &ar5416_11ng_ratetable; + sc->hw_rate_table[WIRELESS_MODE_11NG_HT40MINUS] = + &ar5416_11ng_ratetable; +} + +static void ar5416_setquarter_ratetable(struct ath_rate_softc *sc) +{ + sc->hw_rate_table[WIRELESS_MODE_11a] = &ar5416_11a_ratetable_Quarter; + return; +} + +static void ar5416_sethalf_ratetable(struct ath_rate_softc *sc) +{ + sc->hw_rate_table[WIRELESS_MODE_11a] = &ar5416_11a_ratetable_Half; + return; +} + +static void ar5416_setfull_ratetable(struct ath_rate_softc *sc) +{ + sc->hw_rate_table[WIRELESS_MODE_11a] = &ar5416_11a_ratetable; + return; +} + +/* + * Return the median of three numbers + */ +static inline int8_t median(int8_t a, int8_t b, int8_t c) +{ + if (a >= b) { + if (b >= c) + return b; + else if (a > c) + return c; + else + return a; + } else { + if (a >= c) + return a; + else if (b >= c) + return c; + else + return b; + } +} + +static void ath_rc_sort_validrates(const struct ath_rate_table *rate_table, + struct ath_tx_ratectrl *rate_ctrl) +{ + u8 i, j, idx, idx_next; + + for (i = rate_ctrl->max_valid_rate - 1; i > 0; i--) { + for (j = 0; j <= i-1; j++) { + idx = rate_ctrl->valid_rate_index[j]; + idx_next = rate_ctrl->valid_rate_index[j+1]; + + if (rate_table->info[idx].ratekbps > + rate_table->info[idx_next].ratekbps) { + rate_ctrl->valid_rate_index[j] = idx_next; + rate_ctrl->valid_rate_index[j+1] = idx; + } + } + } +} + +/* Access functions for valid_txrate_mask */ + +static void ath_rc_init_valid_txmask(struct ath_tx_ratectrl *rate_ctrl) +{ + u8 i; + + for (i = 0; i < rate_ctrl->rate_table_size; i++) + rate_ctrl->valid_rate_index[i] = FALSE; +} + +static inline void ath_rc_set_valid_txmask(struct ath_tx_ratectrl *rate_ctrl, + u8 index, int valid_tx_rate) +{ + ASSERT(index <= rate_ctrl->rate_table_size); + rate_ctrl->valid_rate_index[index] = valid_tx_rate ? TRUE : FALSE; +} + +static inline int ath_rc_isvalid_txmask(struct ath_tx_ratectrl *rate_ctrl, + u8 index) +{ + ASSERT(index <= rate_ctrl->rate_table_size); + return rate_ctrl->valid_rate_index[index]; +} + +/* Iterators for valid_txrate_mask */ +static inline int +ath_rc_get_nextvalid_txrate(const struct ath_rate_table *rate_table, + struct ath_tx_ratectrl *rate_ctrl, + u8 cur_valid_txrate, + u8 *next_idx) +{ + u8 i; + + for (i = 0; i < rate_ctrl->max_valid_rate - 1; i++) { + if (rate_ctrl->valid_rate_index[i] == cur_valid_txrate) { + *next_idx = rate_ctrl->valid_rate_index[i+1]; + return TRUE; + } + } + + /* No more valid rates */ + *next_idx = 0; + return FALSE; +} + +/* Return true only for single stream */ + +static int ath_rc_valid_phyrate(u32 phy, u32 capflag, int ignore_cw) +{ + if (WLAN_RC_PHY_HT(phy) & !(capflag & WLAN_RC_HT_FLAG)) + return FALSE; + if (WLAN_RC_PHY_DS(phy) && !(capflag & WLAN_RC_DS_FLAG)) + return FALSE; + if (WLAN_RC_PHY_SGI(phy) && !(capflag & WLAN_RC_SGI_FLAG)) + return FALSE; + if (!ignore_cw && WLAN_RC_PHY_HT(phy)) + if (WLAN_RC_PHY_40(phy) && !(capflag & WLAN_RC_40_FLAG)) + return FALSE; + if (!WLAN_RC_PHY_40(phy) && (capflag & WLAN_RC_40_FLAG)) + return FALSE; + return TRUE; +} + +static inline int +ath_rc_get_nextlowervalid_txrate(const struct ath_rate_table *rate_table, + struct ath_tx_ratectrl *rate_ctrl, + u8 cur_valid_txrate, u8 *next_idx) +{ + int8_t i; + + for (i = 1; i < rate_ctrl->max_valid_rate ; i++) { + if (rate_ctrl->valid_rate_index[i] == cur_valid_txrate) { + *next_idx = rate_ctrl->valid_rate_index[i-1]; + return TRUE; + } + } + return FALSE; +} + +/* + * Initialize the Valid Rate Index from valid entries in Rate Table + */ +static u8 +ath_rc_sib_init_validrates(struct ath_rate_node *ath_rc_priv, + const struct ath_rate_table *rate_table, + u32 capflag) +{ + struct ath_tx_ratectrl *rate_ctrl; + u8 i, hi = 0; + u32 valid; + + rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv); + for (i = 0; i < rate_table->rate_cnt; i++) { + valid = (ath_rc_priv->single_stream ? + rate_table->info[i].valid_single_stream : + rate_table->info[i].valid); + if (valid == TRUE) { + u32 phy = rate_table->info[i].phy; + u8 valid_rate_count = 0; + + if (!ath_rc_valid_phyrate(phy, capflag, FALSE)) + continue; + + valid_rate_count = rate_ctrl->valid_phy_ratecnt[phy]; + + rate_ctrl->valid_phy_rateidx[phy][valid_rate_count] = i; + rate_ctrl->valid_phy_ratecnt[phy] += 1; + ath_rc_set_valid_txmask(rate_ctrl, i, TRUE); + hi = A_MAX(hi, i); + } + } + return hi; +} + +/* + * Initialize the Valid Rate Index from Rate Set + */ +static u8 +ath_rc_sib_setvalid_rates(struct ath_rate_node *ath_rc_priv, + const struct ath_rate_table *rate_table, + struct ath_rateset *rateset, + u32 capflag) +{ + /* XXX: Clean me up and make identation friendly */ + u8 i, j, hi = 0; + struct ath_tx_ratectrl *rate_ctrl = + (struct ath_tx_ratectrl *)(ath_rc_priv); + + /* Use intersection of working rates and valid rates */ + for (i = 0; i < rateset->rs_nrates; i++) { + for (j = 0; j < rate_table->rate_cnt; j++) { + u32 phy = rate_table->info[j].phy; + u32 valid = (ath_rc_priv->single_stream ? + rate_table->info[j].valid_single_stream : + rate_table->info[j].valid); + + /* We allow a rate only if its valid and the + * capflag matches one of the validity + * (TRUE/TRUE_20/TRUE_40) flags */ + + /* XXX: catch the negative of this branch + * first and then continue */ + if (((rateset->rs_rates[i] & 0x7F) == + (rate_table->info[j].dot11rate & 0x7F)) && + ((valid & WLAN_RC_CAP_MODE(capflag)) == + WLAN_RC_CAP_MODE(capflag)) && + !WLAN_RC_PHY_HT(phy)) { + + u8 valid_rate_count = 0; + + if (!ath_rc_valid_phyrate(phy, capflag, FALSE)) + continue; + + valid_rate_count = + rate_ctrl->valid_phy_ratecnt[phy]; + + rate_ctrl->valid_phy_rateidx[phy] + [valid_rate_count] = j; + rate_ctrl->valid_phy_ratecnt[phy] += 1; + ath_rc_set_valid_txmask(rate_ctrl, j, TRUE); + hi = A_MAX(hi, j); + } + } + } + return hi; +} + +static u8 +ath_rc_sib_setvalid_htrates(struct ath_rate_node *ath_rc_priv, + const struct ath_rate_table *rate_table, + u8 *mcs_set, u32 capflag) +{ + u8 i, j, hi = 0; + struct ath_tx_ratectrl *rate_ctrl = + (struct ath_tx_ratectrl *)(ath_rc_priv); + + /* Use intersection of working rates and valid rates */ + for (i = 0; i < ((struct ath_rateset *)mcs_set)->rs_nrates; i++) { + for (j = 0; j < rate_table->rate_cnt; j++) { + u32 phy = rate_table->info[j].phy; + u32 valid = (ath_rc_priv->single_stream ? + rate_table->info[j].valid_single_stream : + rate_table->info[j].valid); + + if (((((struct ath_rateset *) + mcs_set)->rs_rates[i] & 0x7F) != + (rate_table->info[j].dot11rate & 0x7F)) || + !WLAN_RC_PHY_HT(phy) || + !WLAN_RC_PHY_HT_VALID(valid, capflag)) + continue; + + if (!ath_rc_valid_phyrate(phy, capflag, FALSE)) + continue; + + rate_ctrl->valid_phy_rateidx[phy] + [rate_ctrl->valid_phy_ratecnt[phy]] = j; + rate_ctrl->valid_phy_ratecnt[phy] += 1; + ath_rc_set_valid_txmask(rate_ctrl, j, TRUE); + hi = A_MAX(hi, j); + } + } + return hi; +} + +/* + * Attach to a device instance. Setup the public definition + * of how much per-node space we need and setup the private + * phy tables that have rate control parameters. + */ +struct ath_rate_softc *ath_rate_attach(struct ath_hal *ah) +{ + struct ath_rate_softc *asc; + + /* we are only in user context so we can sleep for memory */ + asc = kzalloc(sizeof(struct ath_rate_softc), GFP_KERNEL); + if (asc == NULL) + return NULL; + + ar5416_attach_ratetables(asc); + + /* Save Maximum TX Trigger Level (used for 11n) */ + tx_triglevel_max = ah->ah_caps.halTxTrigLevelMax; + /* return alias for ath_rate_softc * */ + return asc; +} + +static struct ath_rate_node *ath_rate_node_alloc(struct ath_vap *avp, + struct ath_rate_softc *rsc, + gfp_t gfp) +{ + struct ath_rate_node *anode; + + anode = kzalloc(sizeof(struct ath_rate_node), gfp); + if (anode == NULL) + return NULL; + + anode->avp = avp; + anode->asc = rsc; + avp->rc_node = anode; + + return anode; +} + +static void ath_rate_node_free(struct ath_rate_node *anode) +{ + if (anode != NULL) + kfree(anode); +} + +void ath_rate_detach(struct ath_rate_softc *asc) +{ + if (asc != NULL) + kfree(asc); +} + +u8 ath_rate_findrateix(struct ath_softc *sc, + u8 dot11rate) +{ + const struct ath_rate_table *ratetable; + struct ath_rate_softc *rsc = sc->sc_rc; + int i; + + ratetable = rsc->hw_rate_table[sc->sc_curmode]; + + if (WARN_ON(!ratetable)) + return 0; + + for (i = 0; i < ratetable->rate_cnt; i++) { + if ((ratetable->info[i].dot11rate & 0x7f) == (dot11rate & 0x7f)) + return i; + } + + return 0; +} + +/* + * Update rate-control state on a device state change. When + * operating as a station this includes associate/reassociate + * with an AP. Otherwise this gets called, for example, when + * the we transition to run state when operating as an AP. + */ +void ath_rate_newstate(struct ath_softc *sc, struct ath_vap *avp) +{ + struct ath_rate_softc *asc = sc->sc_rc; + + /* For half and quarter rate channles use different + * rate tables + */ + if (sc->sc_curchan.channelFlags & CHANNEL_HALF) + ar5416_sethalf_ratetable(asc); + else if (sc->sc_curchan.channelFlags & CHANNEL_QUARTER) + ar5416_setquarter_ratetable(asc); + else /* full rate */ + ar5416_setfull_ratetable(asc); + + if (avp->av_config.av_fixed_rateset != IEEE80211_FIXED_RATE_NONE) { + asc->fixedrix = + sc->sc_rixmap[avp->av_config.av_fixed_rateset & 0xff]; + /* NB: check the fixed rate exists */ + if (asc->fixedrix == 0xff) + asc->fixedrix = IEEE80211_FIXED_RATE_NONE; + } else { + asc->fixedrix = IEEE80211_FIXED_RATE_NONE; + } +} + +static u8 ath_rc_ratefind_ht(struct ath_softc *sc, + struct ath_rate_node *ath_rc_priv, + const struct ath_rate_table *rate_table, + int probe_allowed, int *is_probing, + int is_retry) +{ + u32 dt, best_thruput, this_thruput, now_msec; + u8 rate, next_rate, best_rate, maxindex, minindex; + int8_t rssi_last, rssi_reduce = 0, index = 0; + struct ath_tx_ratectrl *rate_ctrl = NULL; + + rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv ? + (ath_rc_priv) : NULL); + + *is_probing = FALSE; + + rssi_last = median(rate_ctrl->rssi_last, + rate_ctrl->rssi_last_prev, + rate_ctrl->rssi_last_prev2); + + /* + * Age (reduce) last ack rssi based on how old it is. + * The bizarre numbers are so the delta is 160msec, + * meaning we divide by 16. + * 0msec <= dt <= 25msec: don't derate + * 25msec <= dt <= 185msec: derate linearly from 0 to 10dB + * 185msec <= dt: derate by 10dB + */ + + now_msec = jiffies_to_msecs(jiffies); + dt = now_msec - rate_ctrl->rssi_time; + + if (dt >= 185) + rssi_reduce = 10; + else if (dt >= 25) + rssi_reduce = (u8)((dt - 25) >> 4); + + /* Now reduce rssi_last by rssi_reduce */ + if (rssi_last < rssi_reduce) + rssi_last = 0; + else + rssi_last -= rssi_reduce; + + /* + * Now look up the rate in the rssi table and return it. + * If no rates match then we return 0 (lowest rate) + */ + + best_thruput = 0; + maxindex = rate_ctrl->max_valid_rate-1; + + minindex = 0; + best_rate = minindex; + + /* + * Try the higher rate first. It will reduce memory moving time + * if we have very good channel characteristics. + */ + for (index = maxindex; index >= minindex ; index--) { + u8 per_thres; + + rate = rate_ctrl->valid_rate_index[index]; + if (rate > rate_ctrl->rate_max_phy) + continue; + + /* + * For TCP the average collision rate is around 11%, + * so we ignore PERs less than this. This is to + * prevent the rate we are currently using (whose + * PER might be in the 10-15 range because of TCP + * collisions) looking worse than the next lower + * rate whose PER has decayed close to 0. If we + * used to next lower rate, its PER would grow to + * 10-15 and we would be worse off then staying + * at the current rate. + */ + per_thres = rate_ctrl->state[rate].per; + if (per_thres < 12) + per_thres = 12; + + this_thruput = rate_table->info[rate].user_ratekbps * + (100 - per_thres); + + if (best_thruput <= this_thruput) { + best_thruput = this_thruput; + best_rate = rate; + } + } + + rate = best_rate; + + /* if we are retrying for more than half the number + * of max retries, use the min rate for the next retry + */ + if (is_retry) + rate = rate_ctrl->valid_rate_index[minindex]; + + rate_ctrl->rssi_last_lookup = rssi_last; + + /* + * Must check the actual rate (ratekbps) to account for + * non-monoticity of 11g's rate table + */ + + if (rate >= rate_ctrl->rate_max_phy && probe_allowed) { + rate = rate_ctrl->rate_max_phy; + + /* Probe the next allowed phy state */ + /* FIXME:XXXX Check to make sure ratMax is checked properly */ + if (ath_rc_get_nextvalid_txrate(rate_table, + rate_ctrl, rate, &next_rate) && + (now_msec - rate_ctrl->probe_time > + rate_table->probe_interval) && + (rate_ctrl->hw_maxretry_pktcnt >= 1)) { + rate = next_rate; + rate_ctrl->probe_rate = rate; + rate_ctrl->probe_time = now_msec; + rate_ctrl->hw_maxretry_pktcnt = 0; + *is_probing = TRUE; + } + } + + /* + * Make sure rate is not higher than the allowed maximum. + * We should also enforce the min, but I suspect the min is + * normally 1 rather than 0 because of the rate 9 vs 6 issue + * in the old code. + */ + if (rate > (rate_ctrl->rate_table_size - 1)) + rate = rate_ctrl->rate_table_size - 1; + + ASSERT((rate_table->info[rate].valid && !ath_rc_priv->single_stream) || + (rate_table->info[rate].valid_single_stream && + ath_rc_priv->single_stream)); + + return rate; +} + +static void ath_rc_rate_set_series(const struct ath_rate_table *rate_table , + struct ath_rc_series *series, + u8 tries, + u8 rix, + int rtsctsenable) +{ + series->tries = tries; + series->flags = (rtsctsenable ? ATH_RC_RTSCTS_FLAG : 0) | + (WLAN_RC_PHY_DS(rate_table->info[rix].phy) ? + ATH_RC_DS_FLAG : 0) | + (WLAN_RC_PHY_40(rate_table->info[rix].phy) ? + ATH_RC_CW40_FLAG : 0) | + (WLAN_RC_PHY_SGI(rate_table->info[rix].phy) ? + ATH_RC_SGI_FLAG : 0); + + series->rix = rate_table->info[rix].base_index; + series->max_4ms_framelen = rate_table->info[rix].max_4ms_framelen; +} + +static u8 ath_rc_rate_getidx(struct ath_softc *sc, + struct ath_rate_node *ath_rc_priv, + const struct ath_rate_table *rate_table, + u8 rix, u16 stepdown, + u16 min_rate) +{ + u32 j; + u8 nextindex; + struct ath_tx_ratectrl *rate_ctrl = + (struct ath_tx_ratectrl *)(ath_rc_priv); + + if (min_rate) { + for (j = RATE_TABLE_SIZE; j > 0; j--) { + if (ath_rc_get_nextlowervalid_txrate(rate_table, + rate_ctrl, rix, &nextindex)) + rix = nextindex; + else + break; + } + } else { + for (j = stepdown; j > 0; j--) { + if (ath_rc_get_nextlowervalid_txrate(rate_table, + rate_ctrl, rix, &nextindex)) + rix = nextindex; + else + break; + } + } + return rix; +} + +static void ath_rc_ratefind(struct ath_softc *sc, + struct ath_rate_node *ath_rc_priv, + int num_tries, int num_rates, unsigned int rcflag, + struct ath_rc_series series[], int *is_probe, + int is_retry) +{ + u8 try_per_rate = 0, i = 0, rix, nrix; + struct ath_rate_softc *asc = (struct ath_rate_softc *)sc->sc_rc; + struct ath_rate_table *rate_table; + + rate_table = + (struct ath_rate_table *)asc->hw_rate_table[sc->sc_curmode]; + rix = ath_rc_ratefind_ht(sc, ath_rc_priv, rate_table, + (rcflag & ATH_RC_PROBE_ALLOWED) ? 1 : 0, + is_probe, is_retry); + nrix = rix; + + if ((rcflag & ATH_RC_PROBE_ALLOWED) && (*is_probe)) { + /* set one try for probe rates. For the + * probes don't enable rts */ + ath_rc_rate_set_series(rate_table, + &series[i++], 1, nrix, FALSE); + + try_per_rate = (num_tries/num_rates); + /* Get the next tried/allowed rate. No RTS for the next series + * after the probe rate + */ + nrix = ath_rc_rate_getidx(sc, + ath_rc_priv, rate_table, nrix, 1, FALSE); + ath_rc_rate_set_series(rate_table, + &series[i++], try_per_rate, nrix, 0); + } else { + try_per_rate = (num_tries/num_rates); + /* Set the choosen rate. No RTS for first series entry. */ + ath_rc_rate_set_series(rate_table, + &series[i++], try_per_rate, nrix, FALSE); + } + + /* Fill in the other rates for multirate retry */ + for ( ; i < num_rates; i++) { + u8 try_num; + u8 min_rate; + + try_num = ((i + 1) == num_rates) ? + num_tries - (try_per_rate * i) : try_per_rate ; + min_rate = (((i + 1) == num_rates) && + (rcflag & ATH_RC_MINRATE_LASTRATE)) ? 1 : 0; + + nrix = ath_rc_rate_getidx(sc, ath_rc_priv, + rate_table, nrix, 1, min_rate); + /* All other rates in the series have RTS enabled */ + ath_rc_rate_set_series(rate_table, + &series[i], try_num, nrix, TRUE); + } + + /* + * NB:Change rate series to enable aggregation when operating + * at lower MCS rates. When first rate in series is MCS2 + * in HT40 @ 2.4GHz, series should look like: + * + * {MCS2, MCS1, MCS0, MCS0}. + * + * When first rate in series is MCS3 in HT20 @ 2.4GHz, series should + * look like: + * + * {MCS3, MCS2, MCS1, MCS1} + * + * So, set fourth rate in series to be same as third one for + * above conditions. + */ + if ((sc->sc_curmode == WIRELESS_MODE_11NG_HT20) || + (sc->sc_curmode == WIRELESS_MODE_11NG_HT40PLUS) || + (sc->sc_curmode == WIRELESS_MODE_11NG_HT40MINUS)) { + u8 dot11rate = rate_table->info[rix].dot11rate; + u8 phy = rate_table->info[rix].phy; + if (i == 4 && + ((dot11rate == 2 && phy == WLAN_RC_PHY_HT_40_SS) || + (dot11rate == 3 && phy == WLAN_RC_PHY_HT_20_SS))) { + series[3].rix = series[2].rix; + series[3].flags = series[2].flags; + series[3].max_4ms_framelen = series[2].max_4ms_framelen; + } + } +} + +/* + * Return the Tx rate series. + */ +void ath_rate_findrate(struct ath_softc *sc, + struct ath_rate_node *ath_rc_priv, + int num_tries, + int num_rates, + unsigned int rcflag, + struct ath_rc_series series[], + int *is_probe, + int is_retry) +{ + struct ath_vap *avp = ath_rc_priv->avp; + + DPRINTF(sc, ATH_DBG_RATE, "%s", __func__); + if (!num_rates || !num_tries) + return; + + if (avp->av_config.av_fixed_rateset == IEEE80211_FIXED_RATE_NONE) { + ath_rc_ratefind(sc, ath_rc_priv, num_tries, num_rates, + rcflag, series, is_probe, is_retry); + } else { + /* Fixed rate */ + int idx; + u8 flags; + u32 rix; + struct ath_rate_softc *asc = ath_rc_priv->asc; + struct ath_rate_table *rate_table; + + rate_table = (struct ath_rate_table *) + asc->hw_rate_table[sc->sc_curmode]; + + for (idx = 0; idx < 4; idx++) { + unsigned int mcs; + u8 series_rix = 0; + + series[idx].tries = + IEEE80211_RATE_IDX_ENTRY( + avp->av_config.av_fixed_retryset, idx); + + mcs = IEEE80211_RATE_IDX_ENTRY( + avp->av_config.av_fixed_rateset, idx); + + if (idx == 3 && (mcs & 0xf0) == 0x70) + mcs = (mcs & ~0xf0)|0x80; + + if (!(mcs & 0x80)) + flags = 0; + else + flags = ((ath_rc_priv->ht_cap & + WLAN_RC_DS_FLAG) ? + ATH_RC_DS_FLAG : 0) | + ((ath_rc_priv->ht_cap & + WLAN_RC_40_FLAG) ? + ATH_RC_CW40_FLAG : 0) | + ((ath_rc_priv->ht_cap & + WLAN_RC_SGI_FLAG) ? + ((ath_rc_priv->ht_cap & + WLAN_RC_40_FLAG) ? + ATH_RC_SGI_FLAG : 0) : 0); + + series[idx].rix = sc->sc_rixmap[mcs]; + series_rix = series[idx].rix; + + /* XXX: Give me some cleanup love */ + if ((flags & ATH_RC_CW40_FLAG) && + (flags & ATH_RC_SGI_FLAG)) + rix = rate_table->info[series_rix].ht_index; + else if (flags & ATH_RC_SGI_FLAG) + rix = rate_table->info[series_rix].sgi_index; + else if (flags & ATH_RC_CW40_FLAG) + rix = rate_table->info[series_rix].cw40index; + else + rix = rate_table->info[series_rix].base_index; + series[idx].max_4ms_framelen = + rate_table->info[rix].max_4ms_framelen; + series[idx].flags = flags; + } + } +} + +static void ath_rc_update_ht(struct ath_softc *sc, + struct ath_rate_node *ath_rc_priv, + struct ath_tx_info_priv *info_priv, + int tx_rate, int xretries, int retries) +{ + struct ath_tx_ratectrl *rate_ctrl; + u32 now_msec = jiffies_to_msecs(jiffies); + int state_change = FALSE, rate, count; + u8 last_per; + struct ath_rate_softc *asc = (struct ath_rate_softc *)sc->sc_rc; + struct ath_rate_table *rate_table = + (struct ath_rate_table *)asc->hw_rate_table[sc->sc_curmode]; + + static u32 nretry_to_per_lookup[10] = { + 100 * 0 / 1, + 100 * 1 / 4, + 100 * 1 / 2, + 100 * 3 / 4, + 100 * 4 / 5, + 100 * 5 / 6, + 100 * 6 / 7, + 100 * 7 / 8, + 100 * 8 / 9, + 100 * 9 / 10 + }; + + if (!ath_rc_priv) + return; + + rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv); + + ASSERT(tx_rate >= 0); + if (tx_rate < 0) + return; + + /* To compensate for some imbalance between ctrl and ext. channel */ + + if (WLAN_RC_PHY_40(rate_table->info[tx_rate].phy)) + info_priv->tx.ts_rssi = + info_priv->tx.ts_rssi < 3 ? 0 : + info_priv->tx.ts_rssi - 3; + + last_per = rate_ctrl->state[tx_rate].per; + + if (xretries) { + /* Update the PER. */ + if (xretries == 1) { + rate_ctrl->state[tx_rate].per += 30; + if (rate_ctrl->state[tx_rate].per > 100) + rate_ctrl->state[tx_rate].per = 100; + } else { + /* xretries == 2 */ + count = sizeof(nretry_to_per_lookup) / + sizeof(nretry_to_per_lookup[0]); + if (retries >= count) + retries = count - 1; + /* new_PER = 7/8*old_PER + 1/8*(currentPER) */ + rate_ctrl->state[tx_rate].per = + (u8)(rate_ctrl->state[tx_rate].per - + (rate_ctrl->state[tx_rate].per >> 3) + + ((100) >> 3)); + } + + /* xretries == 1 or 2 */ + + if (rate_ctrl->probe_rate == tx_rate) + rate_ctrl->probe_rate = 0; + + } else { /* xretries == 0 */ + /* Update the PER. */ + /* Make sure it doesn't index out of array's bounds. */ + count = sizeof(nretry_to_per_lookup) / + sizeof(nretry_to_per_lookup[0]); + if (retries >= count) + retries = count - 1; + if (info_priv->n_bad_frames) { + /* new_PER = 7/8*old_PER + 1/8*(currentPER) */ + /* + * Assuming that n_frames is not 0. The current PER + * from the retries is 100 * retries / (retries+1), + * since the first retries attempts failed, and the + * next one worked. For the one that worked, + * n_bad_frames subframes out of n_frames wored, + * so the PER for that part is + * 100 * n_bad_frames / n_frames, and it contributes + * 100 * n_bad_frames / (n_frames * (retries+1)) to + * the above PER. The expression below is a + * simplified version of the sum of these two terms. + */ + if (info_priv->n_frames > 0) + rate_ctrl->state[tx_rate].per + = (u8) + (rate_ctrl->state[tx_rate].per - + (rate_ctrl->state[tx_rate].per >> 3) + + ((100*(retries*info_priv->n_frames + + info_priv->n_bad_frames) / + (info_priv->n_frames * + (retries+1))) >> 3)); + } else { + /* new_PER = 7/8*old_PER + 1/8*(currentPER) */ + + rate_ctrl->state[tx_rate].per = (u8) + (rate_ctrl->state[tx_rate].per - + (rate_ctrl->state[tx_rate].per >> 3) + + (nretry_to_per_lookup[retries] >> 3)); + } + + rate_ctrl->rssi_last_prev2 = rate_ctrl->rssi_last_prev; + rate_ctrl->rssi_last_prev = rate_ctrl->rssi_last; + rate_ctrl->rssi_last = info_priv->tx.ts_rssi; + rate_ctrl->rssi_time = now_msec; + + /* + * If we got at most one retry then increase the max rate if + * this was a probe. Otherwise, ignore the probe. + */ + + if (rate_ctrl->probe_rate && rate_ctrl->probe_rate == tx_rate) { + if (retries > 0 || 2 * info_priv->n_bad_frames > + info_priv->n_frames) { + /* + * Since we probed with just a single attempt, + * any retries means the probe failed. Also, + * if the attempt worked, but more than half + * the subframes were bad then also consider + * the probe a failure. + */ + rate_ctrl->probe_rate = 0; + } else { + u8 probe_rate = 0; + + rate_ctrl->rate_max_phy = rate_ctrl->probe_rate; + probe_rate = rate_ctrl->probe_rate; + + if (rate_ctrl->state[probe_rate].per > 30) + rate_ctrl->state[probe_rate].per = 20; + + rate_ctrl->probe_rate = 0; + + /* + * Since this probe succeeded, we allow the next + * probe twice as soon. This allows the maxRate + * to move up faster if the probes are + * succesful. + */ + rate_ctrl->probe_time = now_msec - + rate_table->probe_interval / 2; + } + } + + if (retries > 0) { + /* + * Don't update anything. We don't know if + * this was because of collisions or poor signal. + * + * Later: if rssi_ack is close to + * rate_ctrl->state[txRate].rssi_thres and we see lots + * of retries, then we could increase + * rate_ctrl->state[txRate].rssi_thres. + */ + rate_ctrl->hw_maxretry_pktcnt = 0; + } else { + /* + * It worked with no retries. First ignore bogus (small) + * rssi_ack values. + */ + if (tx_rate == rate_ctrl->rate_max_phy && + rate_ctrl->hw_maxretry_pktcnt < 255) { + rate_ctrl->hw_maxretry_pktcnt++; + } + + if (info_priv->tx.ts_rssi >= + rate_table->info[tx_rate].rssi_ack_validmin) { + /* Average the rssi */ + if (tx_rate != rate_ctrl->rssi_sum_rate) { + rate_ctrl->rssi_sum_rate = tx_rate; + rate_ctrl->rssi_sum = + rate_ctrl->rssi_sum_cnt = 0; + } + + rate_ctrl->rssi_sum += info_priv->tx.ts_rssi; + rate_ctrl->rssi_sum_cnt++; + + if (rate_ctrl->rssi_sum_cnt > 4) { + int32_t rssi_ackAvg = + (rate_ctrl->rssi_sum + 2) / 4; + int8_t rssi_thres = + rate_ctrl->state[tx_rate]. + rssi_thres; + int8_t rssi_ack_vmin = + rate_table->info[tx_rate]. + rssi_ack_validmin; + + rate_ctrl->rssi_sum = + rate_ctrl->rssi_sum_cnt = 0; + + /* Now reduce the current + * rssi threshold. */ + if ((rssi_ackAvg < rssi_thres + 2) && + (rssi_thres > rssi_ack_vmin)) { + rate_ctrl->state[tx_rate]. + rssi_thres--; + } + + state_change = TRUE; + } + } + } + } + + /* For all cases */ + + /* + * If this rate looks bad (high PER) then stop using it for + * a while (except if we are probing). + */ + if (rate_ctrl->state[tx_rate].per >= 55 && tx_rate > 0 && + rate_table->info[tx_rate].ratekbps <= + rate_table->info[rate_ctrl->rate_max_phy].ratekbps) { + ath_rc_get_nextlowervalid_txrate(rate_table, rate_ctrl, + (u8) tx_rate, &rate_ctrl->rate_max_phy); + + /* Don't probe for a little while. */ + rate_ctrl->probe_time = now_msec; + } + + if (state_change) { + /* + * Make sure the rates above this have higher rssi thresholds. + * (Note: Monotonicity is kept within the OFDM rates and + * within the CCK rates. However, no adjustment is + * made to keep the rssi thresholds monotonically + * increasing between the CCK and OFDM rates.) + */ + for (rate = tx_rate; rate < + rate_ctrl->rate_table_size - 1; rate++) { + if (rate_table->info[rate+1].phy != + rate_table->info[tx_rate].phy) + break; + + if (rate_ctrl->state[rate].rssi_thres + + rate_table->info[rate].rssi_ack_deltamin > + rate_ctrl->state[rate+1].rssi_thres) { + rate_ctrl->state[rate+1].rssi_thres = + rate_ctrl->state[rate]. + rssi_thres + + rate_table->info[rate]. + rssi_ack_deltamin; + } + } + + /* Make sure the rates below this have lower rssi thresholds. */ + for (rate = tx_rate - 1; rate >= 0; rate--) { + if (rate_table->info[rate].phy != + rate_table->info[tx_rate].phy) + break; + + if (rate_ctrl->state[rate].rssi_thres + + rate_table->info[rate].rssi_ack_deltamin > + rate_ctrl->state[rate+1].rssi_thres) { + if (rate_ctrl->state[rate+1].rssi_thres < + rate_table->info[rate]. + rssi_ack_deltamin) + rate_ctrl->state[rate].rssi_thres = 0; + else { + rate_ctrl->state[rate].rssi_thres = + rate_ctrl->state[rate+1]. + rssi_thres - + rate_table->info[rate]. + rssi_ack_deltamin; + } + + if (rate_ctrl->state[rate].rssi_thres < + rate_table->info[rate]. + rssi_ack_validmin) { + rate_ctrl->state[rate].rssi_thres = + rate_table->info[rate]. + rssi_ack_validmin; + } + } + } + } + + /* Make sure the rates below this have lower PER */ + /* Monotonicity is kept only for rates below the current rate. */ + if (rate_ctrl->state[tx_rate].per < last_per) { + for (rate = tx_rate - 1; rate >= 0; rate--) { + if (rate_table->info[rate].phy != + rate_table->info[tx_rate].phy) + break; + + if (rate_ctrl->state[rate].per > + rate_ctrl->state[rate+1].per) { + rate_ctrl->state[rate].per = + rate_ctrl->state[rate+1].per; + } + } + } + + /* Maintain monotonicity for rates above the current rate */ + for (rate = tx_rate; rate < rate_ctrl->rate_table_size - 1; rate++) { + if (rate_ctrl->state[rate+1].per < rate_ctrl->state[rate].per) + rate_ctrl->state[rate+1].per = + rate_ctrl->state[rate].per; + } + + /* Every so often, we reduce the thresholds and + * PER (different for CCK and OFDM). */ + if (now_msec - rate_ctrl->rssi_down_time >= + rate_table->rssi_reduce_interval) { + + for (rate = 0; rate < rate_ctrl->rate_table_size; rate++) { + if (rate_ctrl->state[rate].rssi_thres > + rate_table->info[rate].rssi_ack_validmin) + rate_ctrl->state[rate].rssi_thres -= 1; + } + rate_ctrl->rssi_down_time = now_msec; + } + + /* Every so often, we reduce the thresholds + * and PER (different for CCK and OFDM). */ + if (now_msec - rate_ctrl->per_down_time >= + rate_table->rssi_reduce_interval) { + for (rate = 0; rate < rate_ctrl->rate_table_size; rate++) { + rate_ctrl->state[rate].per = + 7 * rate_ctrl->state[rate].per / 8; + } + + rate_ctrl->per_down_time = now_msec; + } +} + +/* + * This routine is called in rate control callback tx_status() to give + * the status of previous frames. + */ +static void ath_rc_update(struct ath_softc *sc, + struct ath_rate_node *ath_rc_priv, + struct ath_tx_info_priv *info_priv, int final_ts_idx, + int xretries, int long_retry) +{ + struct ath_rate_softc *asc = (struct ath_rate_softc *)sc->sc_rc; + struct ath_rate_table *rate_table; + struct ath_tx_ratectrl *rate_ctrl; + struct ath_rc_series rcs[4]; + u8 flags; + u32 series = 0, rix; + + memcpy(rcs, info_priv->rcs, 4 * sizeof(rcs[0])); + rate_table = (struct ath_rate_table *) + asc->hw_rate_table[sc->sc_curmode]; + rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv); + ASSERT(rcs[0].tries != 0); + + /* + * If the first rate is not the final index, there + * are intermediate rate failures to be processed. + */ + if (final_ts_idx != 0) { + /* Process intermediate rates that failed.*/ + for (series = 0; series < final_ts_idx ; series++) { + if (rcs[series].tries != 0) { + flags = rcs[series].flags; + /* If HT40 and we have switched mode from + * 40 to 20 => don't update */ + if ((flags & ATH_RC_CW40_FLAG) && + (rate_ctrl->rc_phy_mode != + (flags & ATH_RC_CW40_FLAG))) + return; + if ((flags & ATH_RC_CW40_FLAG) && + (flags & ATH_RC_SGI_FLAG)) + rix = rate_table->info[ + rcs[series].rix].ht_index; + else if (flags & ATH_RC_SGI_FLAG) + rix = rate_table->info[ + rcs[series].rix].sgi_index; + else if (flags & ATH_RC_CW40_FLAG) + rix = rate_table->info[ + rcs[series].rix].cw40index; + else + rix = rate_table->info[ + rcs[series].rix].base_index; + ath_rc_update_ht(sc, ath_rc_priv, + info_priv, rix, + xretries ? 1 : 2, + rcs[series].tries); + } + } + } else { + /* + * Handle the special case of MIMO PS burst, where the second + * aggregate is sent out with only one rate and one try. + * Treating it as an excessive retry penalizes the rate + * inordinately. + */ + if (rcs[0].tries == 1 && xretries == 1) + xretries = 2; + } + + flags = rcs[series].flags; + /* If HT40 and we have switched mode from 40 to 20 => don't update */ + if ((flags & ATH_RC_CW40_FLAG) && + (rate_ctrl->rc_phy_mode != (flags & ATH_RC_CW40_FLAG))) + return; + + if ((flags & ATH_RC_CW40_FLAG) && (flags & ATH_RC_SGI_FLAG)) + rix = rate_table->info[rcs[series].rix].ht_index; + else if (flags & ATH_RC_SGI_FLAG) + rix = rate_table->info[rcs[series].rix].sgi_index; + else if (flags & ATH_RC_CW40_FLAG) + rix = rate_table->info[rcs[series].rix].cw40index; + else + rix = rate_table->info[rcs[series].rix].base_index; + + ath_rc_update_ht(sc, ath_rc_priv, info_priv, rix, + xretries, long_retry); +} + + +/* + * Process a tx descriptor for a completed transmit (success or failure). + */ +static void ath_rate_tx_complete(struct ath_softc *sc, + struct ath_node *an, + struct ath_rate_node *rc_priv, + struct ath_tx_info_priv *info_priv) +{ + int final_ts_idx = info_priv->tx.ts_rateindex; + int tx_status = 0, is_underrun = 0; + struct ath_vap *avp; + + avp = rc_priv->avp; + if ((avp->av_config.av_fixed_rateset != IEEE80211_FIXED_RATE_NONE) + || info_priv->tx.ts_status & ATH9K_TXERR_FILT) + return; + + if (info_priv->tx.ts_rssi > 0) { + ATH_RSSI_LPF(an->an_chainmask_sel.tx_avgrssi, + info_priv->tx.ts_rssi); + } + + /* + * If underrun error is seen assume it as an excessive retry only + * if prefetch trigger level have reached the max (0x3f for 5416) + * Adjust the long retry as if the frame was tried ATH_11N_TXMAXTRY + * times. This affects how ratectrl updates PER for the failed rate. + */ + if (info_priv->tx.ts_flags & + (ATH9K_TX_DATA_UNDERRUN | ATH9K_TX_DELIM_UNDERRUN) && + ((sc->sc_ah->ah_txTrigLevel) >= tx_triglevel_max)) { + tx_status = 1; + is_underrun = 1; + } + + if ((info_priv->tx.ts_status & ATH9K_TXERR_XRETRY) || + (info_priv->tx.ts_status & ATH9K_TXERR_FIFO)) + tx_status = 1; + + ath_rc_update(sc, rc_priv, info_priv, final_ts_idx, tx_status, + (is_underrun) ? ATH_11N_TXMAXTRY : + info_priv->tx.ts_longretry); +} + + +/* + * Update the SIB's rate control information + * + * This should be called when the supported rates change + * (e.g. SME operation, wireless mode change) + * + * It will determine which rates are valid for use. + */ +static void ath_rc_sib_update(struct ath_softc *sc, + struct ath_rate_node *ath_rc_priv, + u32 capflag, int keep_state, + struct ath_rateset *negotiated_rates, + struct ath_rateset *negotiated_htrates) +{ + struct ath_rate_table *rate_table = NULL; + struct ath_rate_softc *asc = (struct ath_rate_softc *)sc->sc_rc; + struct ath_rateset *rateset = negotiated_rates; + u8 *ht_mcs = (u8 *)negotiated_htrates; + struct ath_tx_ratectrl *rate_ctrl = (struct ath_tx_ratectrl *) + (ath_rc_priv); + u8 i, j, k, hi = 0, hthi = 0; + + rate_table = (struct ath_rate_table *) + asc->hw_rate_table[sc->sc_curmode]; + + /* Initial rate table size. Will change depending + * on the working rate set */ + rate_ctrl->rate_table_size = MAX_TX_RATE_TBL; + + /* Initialize thresholds according to the global rate table */ + for (i = 0 ; (i < rate_ctrl->rate_table_size) && (!keep_state); i++) { + rate_ctrl->state[i].rssi_thres = + rate_table->info[i].rssi_ack_validmin; + rate_ctrl->state[i].per = 0; + } + + /* Determine the valid rates */ + ath_rc_init_valid_txmask(rate_ctrl); + + for (i = 0; i < WLAN_RC_PHY_MAX; i++) { + for (j = 0; j < MAX_TX_RATE_PHY; j++) + rate_ctrl->valid_phy_rateidx[i][j] = 0; + rate_ctrl->valid_phy_ratecnt[i] = 0; + } + rate_ctrl->rc_phy_mode = (capflag & WLAN_RC_40_FLAG); + + /* Set stream capability */ + ath_rc_priv->single_stream = (capflag & WLAN_RC_DS_FLAG) ? 0 : 1; + + if (!rateset->rs_nrates) { + /* No working rate, just initialize valid rates */ + hi = ath_rc_sib_init_validrates(ath_rc_priv, rate_table, + capflag); + } else { + /* Use intersection of working rates and valid rates */ + hi = ath_rc_sib_setvalid_rates(ath_rc_priv, rate_table, + rateset, capflag); + if (capflag & WLAN_RC_HT_FLAG) { + hthi = ath_rc_sib_setvalid_htrates(ath_rc_priv, + rate_table, + ht_mcs, + capflag); + } + hi = A_MAX(hi, hthi); + } + + rate_ctrl->rate_table_size = hi + 1; + rate_ctrl->rate_max_phy = 0; + ASSERT(rate_ctrl->rate_table_size <= MAX_TX_RATE_TBL); + + for (i = 0, k = 0; i < WLAN_RC_PHY_MAX; i++) { + for (j = 0; j < rate_ctrl->valid_phy_ratecnt[i]; j++) { + rate_ctrl->valid_rate_index[k++] = + rate_ctrl->valid_phy_rateidx[i][j]; + } + + if (!ath_rc_valid_phyrate(i, rate_table->initial_ratemax, TRUE) + || !rate_ctrl->valid_phy_ratecnt[i]) + continue; + + rate_ctrl->rate_max_phy = rate_ctrl->valid_phy_rateidx[i][j-1]; + } + ASSERT(rate_ctrl->rate_table_size <= MAX_TX_RATE_TBL); + ASSERT(k <= MAX_TX_RATE_TBL); + + rate_ctrl->max_valid_rate = k; + /* + * Some third party vendors don't send the supported rate series in + * order. So sorting to make sure its in order, otherwise our RateFind + * Algo will select wrong rates + */ + ath_rc_sort_validrates(rate_table, rate_ctrl); + rate_ctrl->rate_max_phy = rate_ctrl->valid_rate_index[k-4]; +} + +/* + * Update rate-control state on station associate/reassociate. + */ +static int ath_rate_newassoc(struct ath_softc *sc, + struct ath_rate_node *ath_rc_priv, + unsigned int capflag, + struct ath_rateset *negotiated_rates, + struct ath_rateset *negotiated_htrates) +{ + + + ath_rc_priv->ht_cap = + ((capflag & ATH_RC_DS_FLAG) ? WLAN_RC_DS_FLAG : 0) | + ((capflag & ATH_RC_SGI_FLAG) ? WLAN_RC_SGI_FLAG : 0) | + ((capflag & ATH_RC_HT_FLAG) ? WLAN_RC_HT_FLAG : 0) | + ((capflag & ATH_RC_CW40_FLAG) ? WLAN_RC_40_FLAG : 0); + + ath_rc_sib_update(sc, ath_rc_priv, ath_rc_priv->ht_cap, 0, + negotiated_rates, negotiated_htrates); + + return 0; +} + +/* + * This routine is called to initialize the rate control parameters + * in the SIB. It is called initially during system initialization + * or when a station is associated with the AP. + */ +static void ath_rc_sib_init(struct ath_rate_node *ath_rc_priv) +{ + struct ath_tx_ratectrl *rate_ctrl; + + rate_ctrl = (struct ath_tx_ratectrl *)(ath_rc_priv); + rate_ctrl->rssi_down_time = jiffies_to_msecs(jiffies); +} + + +static void ath_setup_rates(struct ieee80211_local *local, struct sta_info *sta) + +{ + struct ieee80211_supported_band *sband; + struct ieee80211_hw *hw = local_to_hw(local); + struct ath_softc *sc = hw->priv; + struct ath_rate_node *rc_priv = sta->rate_ctrl_priv; + int i, j = 0; + + DPRINTF(sc, ATH_DBG_RATE, "%s", __func__); + sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + for (i = 0; i < sband->n_bitrates; i++) { + if (sta->supp_rates[local->hw.conf.channel->band] & BIT(i)) { + rc_priv->neg_rates.rs_rates[j] + = (sband->bitrates[i].bitrate * 2) / 10; + j++; + } + } + rc_priv->neg_rates.rs_nrates = j; +} + +void ath_rc_node_update(struct ieee80211_hw *hw, struct ath_rate_node *rc_priv) +{ + struct ath_softc *sc = hw->priv; + u32 capflag = 0; + + if (hw->conf.ht_conf.ht_supported) { + capflag |= ATH_RC_HT_FLAG | ATH_RC_DS_FLAG; + if (sc->sc_ht_info.tx_chan_width == ATH9K_HT_MACMODE_2040) + capflag |= ATH_RC_CW40_FLAG; + } + + ath_rate_newassoc(sc, rc_priv, capflag, + &rc_priv->neg_rates, + &rc_priv->neg_ht_rates); + +} + +/* Rate Control callbacks */ +static void ath_tx_status(void *priv, struct net_device *dev, + struct sk_buff *skb) +{ + struct ath_softc *sc = priv; + struct ath_tx_info_priv *tx_info_priv; + struct ath_node *an; + struct sta_info *sta; + struct ieee80211_local *local; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr; + __le16 fc; + + local = hw_to_local(sc->hw); + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0]; + + spin_lock_bh(&sc->node_lock); + an = ath_node_find(sc, hdr->addr1); + spin_unlock_bh(&sc->node_lock); + + sta = sta_info_get(local, hdr->addr1); + if (!an || !sta || !ieee80211_is_data(fc)) { + if (tx_info->driver_data[0] != NULL) { + kfree(tx_info->driver_data[0]); + tx_info->driver_data[0] = NULL; + } + return; + } + if (tx_info->driver_data[0] != NULL) { + ath_rate_tx_complete(sc, an, sta->rate_ctrl_priv, tx_info_priv); + kfree(tx_info->driver_data[0]); + tx_info->driver_data[0] = NULL; + } +} + +static void ath_tx_aggr_resp(struct ath_softc *sc, + struct sta_info *sta, + struct ath_node *an, + u8 tidno) +{ + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_local *local; + struct ath_atx_tid *txtid; + struct ieee80211_supported_band *sband; + u16 buffersize = 0; + int state; + DECLARE_MAC_BUF(mac); + + if (!sc->sc_txaggr) + return; + + txtid = ATH_AN_2_TID(an, tidno); + if (!txtid->paused) + return; + + local = hw_to_local(sc->hw); + sband = hw->wiphy->bands[hw->conf.channel->band]; + buffersize = IEEE80211_MIN_AMPDU_BUF << + sband->ht_info.ampdu_factor; /* FIXME */ + state = sta->ampdu_mlme.tid_state_tx[tidno]; + + if (state & HT_ADDBA_RECEIVED_MSK) { + txtid->addba_exchangecomplete = 1; + txtid->addba_exchangeinprogress = 0; + txtid->baw_size = buffersize; + + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Resuming tid, buffersize: %d\n", + __func__, + buffersize); + + ath_tx_resume_tid(sc, txtid); + } +} + +static void ath_get_rate(void *priv, struct net_device *dev, + struct ieee80211_supported_band *sband, + struct sk_buff *skb, + struct rate_selection *sel) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct sta_info *sta; + struct ath_softc *sc = (struct ath_softc *)priv; + struct ieee80211_hw *hw = sc->hw; + struct ath_tx_info_priv *tx_info_priv; + struct ath_rate_node *ath_rc_priv; + struct ath_node *an; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + int is_probe, chk, ret; + s8 lowest_idx; + __le16 fc = hdr->frame_control; + u8 *qc, tid; + DECLARE_MAC_BUF(mac); + + DPRINTF(sc, ATH_DBG_RATE, "%s\n", __func__); + + /* allocate driver private area of tx_info */ + tx_info->driver_data[0] = kzalloc(sizeof(*tx_info_priv), GFP_ATOMIC); + ASSERT(tx_info->driver_data[0] != NULL); + tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0]; + + sta = sta_info_get(local, hdr->addr1); + lowest_idx = rate_lowest_index(local, sband, sta); + tx_info_priv->min_rate = (sband->bitrates[lowest_idx].bitrate * 2) / 10; + /* lowest rate for management and multicast/broadcast frames */ + if (!ieee80211_is_data(fc) || + is_multicast_ether_addr(hdr->addr1) || !sta) { + sel->rate_idx = lowest_idx; + return; + } + + ath_rc_priv = sta->rate_ctrl_priv; + + /* Find tx rate for unicast frames */ + ath_rate_findrate(sc, ath_rc_priv, + ATH_11N_TXMAXTRY, 4, + ATH_RC_PROBE_ALLOWED, + tx_info_priv->rcs, + &is_probe, + false); + if (is_probe) + sel->probe_idx = ((struct ath_tx_ratectrl *) + sta->rate_ctrl_priv)->probe_rate; + + /* Ratecontrol sometimes returns invalid rate index */ + if (tx_info_priv->rcs[0].rix != 0xff) + ath_rc_priv->prev_data_rix = tx_info_priv->rcs[0].rix; + else + tx_info_priv->rcs[0].rix = ath_rc_priv->prev_data_rix; + + sel->rate_idx = tx_info_priv->rcs[0].rix; + + /* Check if aggregation has to be enabled for this tid */ + + if (hw->conf.ht_conf.ht_supported) { + if (ieee80211_is_data_qos(fc)) { + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + + spin_lock_bh(&sc->node_lock); + an = ath_node_find(sc, hdr->addr1); + spin_unlock_bh(&sc->node_lock); + + if (!an) { + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Node not found to " + "init/chk TX aggr\n", __func__); + return; + } + + chk = ath_tx_aggr_check(sc, an, tid); + if (chk == AGGR_REQUIRED) { + ret = ieee80211_start_tx_ba_session(hw, + hdr->addr1, tid); + if (ret) + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Unable to start tx " + "aggr for: %s\n", + __func__, + print_mac(mac, hdr->addr1)); + else + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Started tx aggr for: %s\n", + __func__, + print_mac(mac, hdr->addr1)); + } else if (chk == AGGR_EXCHANGE_PROGRESS) + ath_tx_aggr_resp(sc, sta, an, tid); + } + } +} + +static void ath_rate_init(void *priv, void *priv_sta, + struct ieee80211_local *local, + struct sta_info *sta) +{ + struct ieee80211_supported_band *sband; + struct ieee80211_hw *hw = local_to_hw(local); + struct ieee80211_conf *conf = &local->hw.conf; + struct ath_softc *sc = hw->priv; + int i, j = 0; + + DPRINTF(sc, ATH_DBG_RATE, "%s\n", __func__); + + sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; + sta->txrate_idx = rate_lowest_index(local, sband, sta); + + ath_setup_rates(local, sta); + if (conf->flags & IEEE80211_CONF_SUPPORT_HT_MODE) { + for (i = 0; i < MCS_SET_SIZE; i++) { + if (conf->ht_conf.supp_mcs_set[i/8] & (1<<(i%8))) + ((struct ath_rate_node *) + priv_sta)->neg_ht_rates.rs_rates[j++] = i; + if (j == ATH_RATE_MAX) + break; + } + ((struct ath_rate_node *)priv_sta)->neg_ht_rates.rs_nrates = j; + } + ath_rc_node_update(hw, priv_sta); +} + +static void ath_rate_clear(void *priv) +{ + return; +} + +static void *ath_rate_alloc(struct ieee80211_local *local) +{ + struct ieee80211_hw *hw = local_to_hw(local); + struct ath_softc *sc = hw->priv; + + DPRINTF(sc, ATH_DBG_RATE, "%s", __func__); + return local->hw.priv; +} + +static void ath_rate_free(void *priv) +{ + return; +} + +static void *ath_rate_alloc_sta(void *priv, gfp_t gfp) +{ + struct ath_softc *sc = priv; + struct ath_vap *avp = sc->sc_vaps[0]; + struct ath_rate_node *rate_priv; + + DPRINTF(sc, ATH_DBG_RATE, "%s", __func__); + rate_priv = ath_rate_node_alloc(avp, sc->sc_rc, gfp); + if (!rate_priv) { + DPRINTF(sc, ATH_DBG_FATAL, "%s:Unable to allocate" + "private rate control structure", __func__); + return NULL; + } + ath_rc_sib_init(rate_priv); + return rate_priv; +} + +static void ath_rate_free_sta(void *priv, void *priv_sta) +{ + struct ath_rate_node *rate_priv = priv_sta; + struct ath_softc *sc = priv; + + DPRINTF(sc, ATH_DBG_RATE, "%s", __func__); + ath_rate_node_free(rate_priv); +} + +static struct rate_control_ops ath_rate_ops = { + .module = NULL, + .name = "ath9k_rate_control", + .tx_status = ath_tx_status, + .get_rate = ath_get_rate, + .rate_init = ath_rate_init, + .clear = ath_rate_clear, + .alloc = ath_rate_alloc, + .free = ath_rate_free, + .alloc_sta = ath_rate_alloc_sta, + .free_sta = ath_rate_free_sta +}; + +int ath_rate_control_register(void) +{ + return ieee80211_rate_control_register(&ath_rate_ops); +} + +void ath_rate_control_unregister(void) +{ + ieee80211_rate_control_unregister(&ath_rate_ops); +} + diff --git a/drivers/net/wireless/ath9k/rc.h b/drivers/net/wireless/ath9k/rc.h new file mode 100644 index 000000000000..8f6c28e65191 --- /dev/null +++ b/drivers/net/wireless/ath9k/rc.h @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2004 Sam Leffler, Errno Consulting + * Copyright (c) 2004 Video54 Technologies, Inc. + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef RC_H +#define RC_H + +#include "ath9k.h" +/* + * Interface definitions for transmit rate control modules for the + * Atheros driver. + * + * A rate control module is responsible for choosing the transmit rate + * for each data frame. Management+control frames are always sent at + * a fixed rate. + * + * Only one module may be present at a time; the driver references + * rate control interfaces by symbol name. If multiple modules are + * to be supported we'll need to switch to a registration-based scheme + * as is currently done, for example, for authentication modules. + * + * An instance of the rate control module is attached to each device + * at attach time and detached when the device is destroyed. The module + * may associate data with each device and each node (station). Both + * sets of storage are opaque except for the size of the per-node storage + * which must be provided when the module is attached. + * + * The rate control module is notified for each state transition and + * station association/reassociation. Otherwise it is queried for a + * rate for each outgoing frame and provided status from each transmitted + * frame. Any ancillary processing is the responsibility of the module + * (e.g. if periodic processing is required then the module should setup + * it's own timer). + * + * In addition to the transmit rate for each frame the module must also + * indicate the number of attempts to make at the specified rate. If this + * number is != ATH_TXMAXTRY then an additional callback is made to setup + * additional transmit state. The rate control code is assumed to write + * this additional data directly to the transmit descriptor. + */ + +struct ath_softc; + +#define TRUE 1 +#define FALSE 0 + +#define ATH_RATE_MAX 30 +#define MCS_SET_SIZE 128 + +enum ieee80211_fixed_rate_mode { + IEEE80211_FIXED_RATE_NONE = 0, + IEEE80211_FIXED_RATE_MCS = 1 /* HT rates */ +}; + +/* + * Use the hal os glue code to get ms time + */ +#define IEEE80211_RATE_IDX_ENTRY(val, idx) (((val&(0xff<<(idx*8)))>>(idx*8))) + +#define SHORT_PRE 1 +#define LONG_PRE 0 + +#define WLAN_PHY_HT_20_SS WLAN_RC_PHY_HT_20_SS +#define WLAN_PHY_HT_20_DS WLAN_RC_PHY_HT_20_DS +#define WLAN_PHY_HT_20_DS_HGI WLAN_RC_PHY_HT_20_DS_HGI +#define WLAN_PHY_HT_40_SS WLAN_RC_PHY_HT_40_SS +#define WLAN_PHY_HT_40_SS_HGI WLAN_RC_PHY_HT_40_SS_HGI +#define WLAN_PHY_HT_40_DS WLAN_RC_PHY_HT_40_DS +#define WLAN_PHY_HT_40_DS_HGI WLAN_RC_PHY_HT_40_DS_HGI + +#define WLAN_PHY_OFDM PHY_OFDM +#define WLAN_PHY_CCK PHY_CCK + +#define TRUE_20 0x2 +#define TRUE_40 0x4 +#define TRUE_2040 (TRUE_20|TRUE_40) +#define TRUE_ALL (TRUE_2040|TRUE) + +enum { + WLAN_RC_PHY_HT_20_SS = 4, + WLAN_RC_PHY_HT_20_DS, + WLAN_RC_PHY_HT_40_SS, + WLAN_RC_PHY_HT_40_DS, + WLAN_RC_PHY_HT_20_SS_HGI, + WLAN_RC_PHY_HT_20_DS_HGI, + WLAN_RC_PHY_HT_40_SS_HGI, + WLAN_RC_PHY_HT_40_DS_HGI, + WLAN_RC_PHY_MAX +}; + +#define WLAN_RC_PHY_DS(_phy) ((_phy == WLAN_RC_PHY_HT_20_DS) \ + || (_phy == WLAN_RC_PHY_HT_40_DS) \ + || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \ + || (_phy == WLAN_RC_PHY_HT_40_DS_HGI)) +#define WLAN_RC_PHY_40(_phy) ((_phy == WLAN_RC_PHY_HT_40_SS) \ + || (_phy == WLAN_RC_PHY_HT_40_DS) \ + || (_phy == WLAN_RC_PHY_HT_40_SS_HGI) \ + || (_phy == WLAN_RC_PHY_HT_40_DS_HGI)) +#define WLAN_RC_PHY_SGI(_phy) ((_phy == WLAN_RC_PHY_HT_20_SS_HGI) \ + || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \ + || (_phy == WLAN_RC_PHY_HT_40_SS_HGI) \ + || (_phy == WLAN_RC_PHY_HT_40_DS_HGI)) + +#define WLAN_RC_PHY_HT(_phy) (_phy >= WLAN_RC_PHY_HT_20_SS) + +/* Returns the capflag mode */ +#define WLAN_RC_CAP_MODE(capflag) (((capflag & WLAN_RC_HT_FLAG) ? \ + (capflag & WLAN_RC_40_FLAG) ? TRUE_40 : TRUE_20 : TRUE)) + +/* Return TRUE if flag supports HT20 && client supports HT20 or + * return TRUE if flag supports HT40 && client supports HT40. + * This is used becos some rates overlap between HT20/HT40. + */ + +#define WLAN_RC_PHY_HT_VALID(flag, capflag) (((flag & TRUE_20) && !(capflag \ + & WLAN_RC_40_FLAG)) || ((flag & TRUE_40) && \ + (capflag & WLAN_RC_40_FLAG))) + +#define WLAN_RC_DS_FLAG (0x01) +#define WLAN_RC_40_FLAG (0x02) +#define WLAN_RC_SGI_FLAG (0x04) +#define WLAN_RC_HT_FLAG (0x08) + +/* Index into the rate table */ +#define INIT_RATE_MAX_20 23 +#define INIT_RATE_MAX_40 40 + +#define RATE_TABLE_SIZE 64 + +/* XXX: Convert to kdoc */ +struct ath_rate_table { + int rate_cnt; + struct { + int valid; /* Valid for use in rate control */ + int valid_single_stream;/* Valid for use in rate control + for single stream operation */ + u8 phy; /* CCK/OFDM/TURBO/XR */ + u32 ratekbps; /* Rate in Kbits per second */ + u32 user_ratekbps; /* User rate in KBits per second */ + u8 ratecode; /* rate that goes into + hw descriptors */ + u8 short_preamble; /* Mask for enabling short preamble + in rate code for CCK */ + u8 dot11rate; /* Value that goes into supported + rates info element of MLME */ + u8 ctrl_rate; /* Index of next lower basic rate, + used for duration computation */ + int8_t rssi_ack_validmin; /* Rate control related */ + int8_t rssi_ack_deltamin; /* Rate control related */ + u8 base_index; /* base rate index */ + u8 cw40index; /* 40cap rate index */ + u8 sgi_index; /* shortgi rate index */ + u8 ht_index; /* shortgi rate index */ + u32 max_4ms_framelen; /* Maximum frame length(bytes) + for 4ms tx duration */ + } info[RATE_TABLE_SIZE]; + u32 probe_interval; /* interval for ratectrl to + probe for other rates */ + u32 rssi_reduce_interval; /* interval for ratectrl + to reduce RSSI */ + u8 initial_ratemax; /* the initial ratemax value used + in ath_rc_sib_update() */ +}; + +#define ATH_RC_PROBE_ALLOWED 0x00000001 +#define ATH_RC_MINRATE_LASTRATE 0x00000002 +#define ATH_RC_SHORT_PREAMBLE 0x00000004 + +struct ath_rc_series { + u8 rix; + u8 tries; + u8 flags; + u32 max_4ms_framelen; +}; + +/* rcs_flags definition */ +#define ATH_RC_DS_FLAG 0x01 +#define ATH_RC_CW40_FLAG 0x02 /* CW 40 */ +#define ATH_RC_SGI_FLAG 0x04 /* Short Guard Interval */ +#define ATH_RC_HT_FLAG 0x08 /* HT */ +#define ATH_RC_RTSCTS_FLAG 0x10 /* RTS-CTS */ + +/* + * State structures for new rate adaptation code + */ +#define MAX_TX_RATE_TBL 64 +#define MAX_TX_RATE_PHY 48 + +struct ath_tx_ratectrl_state { + int8_t rssi_thres; /* required rssi for this rate (dB) */ + u8 per; /* recent estimate of packet error rate (%) */ +}; + +struct ath_tx_ratectrl { + struct ath_tx_ratectrl_state state[MAX_TX_RATE_TBL]; /* state */ + int8_t rssi_last; /* last ack rssi */ + int8_t rssi_last_lookup; /* last ack rssi used for lookup */ + int8_t rssi_last_prev; /* previous last ack rssi */ + int8_t rssi_last_prev2; /* 2nd previous last ack rssi */ + int32_t rssi_sum_cnt; /* count of rssi_sum for averaging */ + int32_t rssi_sum_rate; /* rate that we are averaging */ + int32_t rssi_sum; /* running sum of rssi for averaging */ + u32 valid_txrate_mask; /* mask of valid rates */ + u8 rate_table_size; /* rate table size */ + u8 rate_max; /* max rate that has recently worked */ + u8 probe_rate; /* rate we are probing at */ + u32 rssi_time; /* msec timestamp for last ack rssi */ + u32 rssi_down_time; /* msec timestamp for last down step */ + u32 probe_time; /* msec timestamp for last probe */ + u8 hw_maxretry_pktcnt; /* num packets since we got + HW max retry error */ + u8 max_valid_rate; /* maximum number of valid rate */ + u8 valid_rate_index[MAX_TX_RATE_TBL]; /* valid rate index */ + u32 per_down_time; /* msec timstamp for last + PER down step */ + + /* 11n state */ + u8 valid_phy_ratecnt[WLAN_RC_PHY_MAX]; /* valid rate count */ + u8 valid_phy_rateidx[WLAN_RC_PHY_MAX][MAX_TX_RATE_TBL]; + u8 rc_phy_mode; + u8 rate_max_phy; /* Phy index for the max rate */ + u32 rate_max_lastused; /* msec timstamp of when we + last used rateMaxPhy */ + u32 probe_interval; /* interval for ratectrl to probe + for other rates */ +}; + +struct ath_rateset { + u8 rs_nrates; + u8 rs_rates[ATH_RATE_MAX]; +}; + +/* per-device state */ +struct ath_rate_softc { + /* phy tables that contain rate control data */ + const void *hw_rate_table[WIRELESS_MODE_MAX]; + int fixedrix; /* -1 or index of fixed rate */ +}; + +/* per-node state */ +struct ath_rate_node { + struct ath_tx_ratectrl tx_ratectrl; /* rate control state proper */ + u32 prev_data_rix; /* rate idx of last data frame */ + + /* map of rate ix -> negotiated rate set ix */ + u8 rixmap[MAX_TX_RATE_TBL]; + + /* map of ht rate ix -> negotiated rate set ix */ + u8 ht_rixmap[MAX_TX_RATE_TBL]; + + u8 ht_cap; /* ht capabilities */ + u8 ant_tx; /* current transmit antenna */ + + u8 single_stream; /* When TRUE, only single + stream Tx possible */ + struct ath_rateset neg_rates; /* Negotiated rates */ + struct ath_rateset neg_ht_rates; /* Negotiated HT rates */ + struct ath_rate_softc *asc; /* back pointer to atheros softc */ + struct ath_vap *avp; /* back pointer to vap */ +}; + +/* Driver data of ieee80211_tx_info */ +struct ath_tx_info_priv { + struct ath_rc_series rcs[4]; + struct ath_tx_status tx; + int n_frames; + int n_bad_frames; + u8 min_rate; +}; + +/* + * Attach/detach a rate control module. + */ +struct ath_rate_softc *ath_rate_attach(struct ath_hal *ah); +void ath_rate_detach(struct ath_rate_softc *asc); + +/* + * Update/reset rate control state for 802.11 state transitions. + * Important mostly as the analog to ath_rate_newassoc when operating + * in station mode. + */ +void ath_rc_node_update(struct ieee80211_hw *hw, struct ath_rate_node *rc_priv); +void ath_rate_newstate(struct ath_softc *sc, struct ath_vap *avp); + +/* + * Return the tx rate series. + */ +void ath_rate_findrate(struct ath_softc *sc, struct ath_rate_node *ath_rc_priv, + int num_tries, int num_rates, + unsigned int rcflag, struct ath_rc_series[], + int *is_probe, int isretry); +/* + * Return rate index for given Dot11 Rate. + */ +u8 ath_rate_findrateix(struct ath_softc *sc, + u8 dot11_rate); + +/* Routines to register/unregister rate control algorithm */ +int ath_rate_control_register(void); +void ath_rate_control_unregister(void); + +#endif /* RC_H */ diff --git a/drivers/net/wireless/ath9k/recv.c b/drivers/net/wireless/ath9k/recv.c new file mode 100644 index 000000000000..8e14e643e994 --- /dev/null +++ b/drivers/net/wireless/ath9k/recv.c @@ -0,0 +1,1318 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Implementation of receive path. + */ + +#include "core.h" + +/* + * Setup and link descriptors. + * + * 11N: we can no longer afford to self link the last descriptor. + * MAC acknowledges BA status as long as it copies frames to host + * buffer (or rx fifo). This can incorrectly acknowledge packets + * to a sender if last desc is self-linked. + * + * NOTE: Caller should hold the rxbuf lock. + */ + +static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_desc *ds; + struct sk_buff *skb; + + ATH_RXBUF_RESET(bf); + + ds = bf->bf_desc; + ds->ds_link = 0; /* link to null */ + ds->ds_data = bf->bf_buf_addr; + + /* XXX For RADAR? + * virtual addr of the beginning of the buffer. */ + skb = bf->bf_mpdu; + ASSERT(skb != NULL); + ds->ds_vdata = skb->data; + + /* setup rx descriptors */ + ath9k_hw_setuprxdesc(ah, + ds, + skb_tailroom(skb), /* buffer size */ + 0); + + if (sc->sc_rxlink == NULL) + ath9k_hw_putrxbuf(ah, bf->bf_daddr); + else + *sc->sc_rxlink = bf->bf_daddr; + + sc->sc_rxlink = &ds->ds_link; + ath9k_hw_rxena(ah); +} + +/* Process received BAR frame */ + +static int ath_bar_rx(struct ath_softc *sc, + struct ath_node *an, + struct sk_buff *skb) +{ + struct ieee80211_bar *bar; + struct ath_arx_tid *rxtid; + struct sk_buff *tskb; + struct ath_recv_status *rx_status; + int tidno, index, cindex; + u16 seqno; + + /* look at BAR contents */ + + bar = (struct ieee80211_bar *)skb->data; + tidno = (le16_to_cpu(bar->control) & IEEE80211_BAR_CTL_TID_M) + >> IEEE80211_BAR_CTL_TID_S; + seqno = le16_to_cpu(bar->start_seq_num) >> IEEE80211_SEQ_SEQ_SHIFT; + + /* process BAR - indicate all pending RX frames till the BAR seqno */ + + rxtid = &an->an_aggr.rx.tid[tidno]; + + spin_lock_bh(&rxtid->tidlock); + + /* get relative index */ + + index = ATH_BA_INDEX(rxtid->seq_next, seqno); + + /* drop BAR if old sequence (index is too large) */ + + if ((index > rxtid->baw_size) && + (index > (IEEE80211_SEQ_MAX - (rxtid->baw_size << 2)))) + /* discard frame, ieee layer may not treat frame as a dup */ + goto unlock_and_free; + + /* complete receive processing for all pending frames upto BAR seqno */ + + cindex = (rxtid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); + while ((rxtid->baw_head != rxtid->baw_tail) && + (rxtid->baw_head != cindex)) { + tskb = rxtid->rxbuf[rxtid->baw_head].rx_wbuf; + rx_status = &rxtid->rxbuf[rxtid->baw_head].rx_status; + rxtid->rxbuf[rxtid->baw_head].rx_wbuf = NULL; + + if (tskb != NULL) + ath_rx_subframe(an, tskb, rx_status); + + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + } + + /* ... and indicate rest of the frames in-order */ + + while (rxtid->baw_head != rxtid->baw_tail && + rxtid->rxbuf[rxtid->baw_head].rx_wbuf != NULL) { + tskb = rxtid->rxbuf[rxtid->baw_head].rx_wbuf; + rx_status = &rxtid->rxbuf[rxtid->baw_head].rx_status; + rxtid->rxbuf[rxtid->baw_head].rx_wbuf = NULL; + + ath_rx_subframe(an, tskb, rx_status); + + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + } + +unlock_and_free: + spin_unlock_bh(&rxtid->tidlock); + /* free bar itself */ + dev_kfree_skb(skb); + return IEEE80211_FTYPE_CTL; +} + +/* Function to handle a subframe of aggregation when HT is enabled */ + +static int ath_ampdu_input(struct ath_softc *sc, + struct ath_node *an, + struct sk_buff *skb, + struct ath_recv_status *rx_status) +{ + struct ieee80211_hdr *hdr; + struct ath_arx_tid *rxtid; + struct ath_rxbuf *rxbuf; + u8 type, subtype; + u16 rxseq; + int tid = 0, index, cindex, rxdiff; + __le16 fc; + u8 *qc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + + /* collect stats of frames with non-zero version */ + + if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_VERS) != 0) { + dev_kfree_skb(skb); + return -1; + } + + type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE; + subtype = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_STYPE; + + if (ieee80211_is_back_req(fc)) + return ath_bar_rx(sc, an, skb); + + /* special aggregate processing only for qos unicast data frames */ + + if (!ieee80211_is_data(fc) || + !ieee80211_is_data_qos(fc) || + is_multicast_ether_addr(hdr->addr1)) + return ath_rx_subframe(an, skb, rx_status); + + /* lookup rx tid state */ + + if (ieee80211_is_data_qos(fc)) { + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + } + + if (sc->sc_opmode == ATH9K_M_STA) { + /* Drop the frame not belonging to me. */ + if (memcmp(hdr->addr1, sc->sc_myaddr, ETH_ALEN)) { + dev_kfree_skb(skb); + return -1; + } + } + + rxtid = &an->an_aggr.rx.tid[tid]; + + spin_lock(&rxtid->tidlock); + + rxdiff = (rxtid->baw_tail - rxtid->baw_head) & + (ATH_TID_MAX_BUFS - 1); + + /* + * If the ADDBA exchange has not been completed by the source, + * process via legacy path (i.e. no reordering buffer is needed) + */ + if (!rxtid->addba_exchangecomplete) { + spin_unlock(&rxtid->tidlock); + return ath_rx_subframe(an, skb, rx_status); + } + + /* extract sequence number from recvd frame */ + + rxseq = le16_to_cpu(hdr->seq_ctrl) >> IEEE80211_SEQ_SEQ_SHIFT; + + if (rxtid->seq_reset) { + rxtid->seq_reset = 0; + rxtid->seq_next = rxseq; + } + + index = ATH_BA_INDEX(rxtid->seq_next, rxseq); + + /* drop frame if old sequence (index is too large) */ + + if (index > (IEEE80211_SEQ_MAX - (rxtid->baw_size << 2))) { + /* discard frame, ieee layer may not treat frame as a dup */ + spin_unlock(&rxtid->tidlock); + dev_kfree_skb(skb); + return IEEE80211_FTYPE_DATA; + } + + /* sequence number is beyond block-ack window */ + + if (index >= rxtid->baw_size) { + + /* complete receive processing for all pending frames */ + + while (index >= rxtid->baw_size) { + + rxbuf = rxtid->rxbuf + rxtid->baw_head; + + if (rxbuf->rx_wbuf != NULL) { + ath_rx_subframe(an, rxbuf->rx_wbuf, + &rxbuf->rx_status); + rxbuf->rx_wbuf = NULL; + } + + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + + index--; + } + } + + /* add buffer to the recv ba window */ + + cindex = (rxtid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); + rxbuf = rxtid->rxbuf + cindex; + + if (rxbuf->rx_wbuf != NULL) { + spin_unlock(&rxtid->tidlock); + /* duplicate frame */ + dev_kfree_skb(skb); + return IEEE80211_FTYPE_DATA; + } + + rxbuf->rx_wbuf = skb; + rxbuf->rx_time = get_timestamp(); + rxbuf->rx_status = *rx_status; + + /* advance tail if sequence received is newer + * than any received so far */ + + if (index >= rxdiff) { + rxtid->baw_tail = cindex; + INCR(rxtid->baw_tail, ATH_TID_MAX_BUFS); + } + + /* indicate all in-order received frames */ + + while (rxtid->baw_head != rxtid->baw_tail) { + rxbuf = rxtid->rxbuf + rxtid->baw_head; + if (!rxbuf->rx_wbuf) + break; + + ath_rx_subframe(an, rxbuf->rx_wbuf, &rxbuf->rx_status); + rxbuf->rx_wbuf = NULL; + + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + } + + /* + * start a timer to flush all received frames if there are pending + * receive frames + */ + if (rxtid->baw_head != rxtid->baw_tail) + mod_timer(&rxtid->timer, ATH_RX_TIMEOUT); + else + del_timer_sync(&rxtid->timer); + + spin_unlock(&rxtid->tidlock); + return IEEE80211_FTYPE_DATA; +} + +/* Timer to flush all received sub-frames */ + +static void ath_rx_timer(unsigned long data) +{ + struct ath_arx_tid *rxtid = (struct ath_arx_tid *)data; + struct ath_node *an = rxtid->an; + struct ath_rxbuf *rxbuf; + int nosched; + + spin_lock_bh(&rxtid->tidlock); + while (rxtid->baw_head != rxtid->baw_tail) { + rxbuf = rxtid->rxbuf + rxtid->baw_head; + if (!rxbuf->rx_wbuf) { + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + continue; + } + + /* + * Stop if the next one is a very recent frame. + * + * Call get_timestamp in every iteration to protect against the + * case in which a new frame is received while we are executing + * this function. Using a timestamp obtained before entering + * the loop could lead to a very large time interval + * (a negative value typecast to unsigned), breaking the + * function's logic. + */ + if ((get_timestamp() - rxbuf->rx_time) < + (ATH_RX_TIMEOUT * HZ / 1000)) + break; + + ath_rx_subframe(an, rxbuf->rx_wbuf, + &rxbuf->rx_status); + rxbuf->rx_wbuf = NULL; + + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + } + + /* + * start a timer to flush all received frames if there are pending + * receive frames + */ + if (rxtid->baw_head != rxtid->baw_tail) + nosched = 0; + else + nosched = 1; /* no need to re-arm the timer again */ + + spin_unlock_bh(&rxtid->tidlock); +} + +/* Free all pending sub-frames in the re-ordering buffer */ + +static void ath_rx_flush_tid(struct ath_softc *sc, + struct ath_arx_tid *rxtid, int drop) +{ + struct ath_rxbuf *rxbuf; + + spin_lock_bh(&rxtid->tidlock); + while (rxtid->baw_head != rxtid->baw_tail) { + rxbuf = rxtid->rxbuf + rxtid->baw_head; + if (!rxbuf->rx_wbuf) { + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + continue; + } + + if (drop) + dev_kfree_skb(rxbuf->rx_wbuf); + else + ath_rx_subframe(rxtid->an, + rxbuf->rx_wbuf, + &rxbuf->rx_status); + + rxbuf->rx_wbuf = NULL; + + INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); + INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); + } + spin_unlock_bh(&rxtid->tidlock); +} + +static struct sk_buff *ath_rxbuf_alloc(struct ath_softc *sc, + u32 len) +{ + struct sk_buff *skb; + u32 off; + + /* + * Cache-line-align. This is important (for the + * 5210 at least) as not doing so causes bogus data + * in rx'd frames. + */ + + skb = dev_alloc_skb(len + sc->sc_cachelsz - 1); + if (skb != NULL) { + off = ((unsigned long) skb->data) % sc->sc_cachelsz; + if (off != 0) + skb_reserve(skb, sc->sc_cachelsz - off); + } else { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: skbuff alloc of size %u failed\n", + __func__, len); + return NULL; + } + + return skb; +} + +static void ath_rx_requeue(struct ath_softc *sc, struct sk_buff *skb) +{ + struct ath_buf *bf = ATH_RX_CONTEXT(skb)->ctx_rxbuf; + + ASSERT(bf != NULL); + + spin_lock_bh(&sc->sc_rxbuflock); + if (bf->bf_status & ATH_BUFSTATUS_STALE) { + /* + * This buffer is still held for hw acess. + * Mark it as free to be re-queued it later. + */ + bf->bf_status |= ATH_BUFSTATUS_FREE; + } else { + /* XXX: we probably never enter here, remove after + * verification */ + list_add_tail(&bf->list, &sc->sc_rxbuf); + ath_rx_buf_link(sc, bf); + } + spin_unlock_bh(&sc->sc_rxbuflock); +} + +/* + * The skb indicated to upper stack won't be returned to us. + * So we have to allocate a new one and queue it by ourselves. + */ +static int ath_rx_indicate(struct ath_softc *sc, + struct sk_buff *skb, + struct ath_recv_status *status, + u16 keyix) +{ + struct ath_buf *bf = ATH_RX_CONTEXT(skb)->ctx_rxbuf; + struct sk_buff *nskb; + int type; + + /* indicate frame to the stack, which will free the old skb. */ + type = ath__rx_indicate(sc, skb, status, keyix); + + /* allocate a new skb and queue it to for H/W processing */ + nskb = ath_rxbuf_alloc(sc, sc->sc_rxbufsize); + if (nskb != NULL) { + bf->bf_mpdu = nskb; + bf->bf_buf_addr = ath_skb_map_single(sc, + nskb, + PCI_DMA_FROMDEVICE, + /* XXX: Remove get_dma_mem_context() */ + get_dma_mem_context(bf, bf_dmacontext)); + ATH_RX_CONTEXT(nskb)->ctx_rxbuf = bf; + + /* queue the new wbuf to H/W */ + ath_rx_requeue(sc, nskb); + } + + return type; +} + +static void ath_opmode_init(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + u32 rfilt, mfilt[2]; + + /* configure rx filter */ + rfilt = ath_calcrxfilter(sc); + ath9k_hw_setrxfilter(ah, rfilt); + + /* configure bssid mask */ + if (ah->ah_caps.halBssIdMaskSupport) + ath9k_hw_setbssidmask(ah, sc->sc_bssidmask); + + /* configure operational mode */ + ath9k_hw_setopmode(ah); + + /* Handle any link-level address change. */ + ath9k_hw_setmac(ah, sc->sc_myaddr); + + /* calculate and install multicast filter */ + mfilt[0] = mfilt[1] = ~0; + + ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]); + DPRINTF(sc, ATH_DBG_CONFIG , + "%s: RX filter 0x%x, MC filter %08x:%08x\n", + __func__, rfilt, mfilt[0], mfilt[1]); +} + +int ath_rx_init(struct ath_softc *sc, int nbufs) +{ + struct sk_buff *skb; + struct ath_buf *bf; + int error = 0; + + do { + spin_lock_init(&sc->sc_rxflushlock); + sc->sc_rxflush = 0; + spin_lock_init(&sc->sc_rxbuflock); + + /* + * Cisco's VPN software requires that drivers be able to + * receive encapsulated frames that are larger than the MTU. + * Since we can't be sure how large a frame we'll get, setup + * to handle the larges on possible. + */ + sc->sc_rxbufsize = roundup(IEEE80211_MAX_MPDU_LEN, + min(sc->sc_cachelsz, + (u16)64)); + + DPRINTF(sc, ATH_DBG_CONFIG, "%s: cachelsz %u rxbufsize %u\n", + __func__, sc->sc_cachelsz, sc->sc_rxbufsize); + + /* Initialize rx descriptors */ + + error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf, + "rx", nbufs, 1); + if (error != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: failed to allocate rx descriptors: %d\n", + __func__, error); + break; + } + + /* Pre-allocate a wbuf for each rx buffer */ + + list_for_each_entry(bf, &sc->sc_rxbuf, list) { + skb = ath_rxbuf_alloc(sc, sc->sc_rxbufsize); + if (skb == NULL) { + error = -ENOMEM; + break; + } + + bf->bf_mpdu = skb; + bf->bf_buf_addr = + ath_skb_map_single(sc, skb, PCI_DMA_FROMDEVICE, + get_dma_mem_context(bf, bf_dmacontext)); + ATH_RX_CONTEXT(skb)->ctx_rxbuf = bf; + } + sc->sc_rxlink = NULL; + + } while (0); + + if (error) + ath_rx_cleanup(sc); + + return error; +} + +/* Reclaim all rx queue resources */ + +void ath_rx_cleanup(struct ath_softc *sc) +{ + struct sk_buff *skb; + struct ath_buf *bf; + + list_for_each_entry(bf, &sc->sc_rxbuf, list) { + skb = bf->bf_mpdu; + if (skb) + dev_kfree_skb(skb); + } + + /* cleanup rx descriptors */ + + if (sc->sc_rxdma.dd_desc_len != 0) + ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf); +} + +/* + * Calculate the receive filter according to the + * operating mode and state: + * + * o always accept unicast, broadcast, and multicast traffic + * o maintain current state of phy error reception (the hal + * may enable phy error frames for noise immunity work) + * o probe request frames are accepted only when operating in + * hostap, adhoc, or monitor modes + * o enable promiscuous mode according to the interface state + * o accept beacons: + * - when operating in adhoc mode so the 802.11 layer creates + * node table entries for peers, + * - when operating in station mode for collecting rssi data when + * the station is otherwise quiet, or + * - when operating as a repeater so we see repeater-sta beacons + * - when scanning + */ + +u32 ath_calcrxfilter(struct ath_softc *sc) +{ +#define RX_FILTER_PRESERVE (ATH9K_RX_FILTER_PHYERR | ATH9K_RX_FILTER_PHYRADAR) + u32 rfilt; + + rfilt = (ath9k_hw_getrxfilter(sc->sc_ah) & RX_FILTER_PRESERVE) + | ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST + | ATH9K_RX_FILTER_MCAST; + + /* If not a STA, enable processing of Probe Requests */ + if (sc->sc_opmode != ATH9K_M_STA) + rfilt |= ATH9K_RX_FILTER_PROBEREQ; + + /* Can't set HOSTAP into promiscous mode */ + if (sc->sc_opmode == ATH9K_M_MONITOR) { + rfilt |= ATH9K_RX_FILTER_PROM; + /* ??? To prevent from sending ACK */ + rfilt &= ~ATH9K_RX_FILTER_UCAST; + } + + if (sc->sc_opmode == ATH9K_M_STA || sc->sc_opmode == ATH9K_M_IBSS || + sc->sc_scanning) + rfilt |= ATH9K_RX_FILTER_BEACON; + + /* If in HOSTAP mode, want to enable reception of PSPOLL frames + & beacon frames */ + if (sc->sc_opmode == ATH9K_M_HOSTAP) + rfilt |= (ATH9K_RX_FILTER_BEACON | ATH9K_RX_FILTER_PSPOLL); + return rfilt; +#undef RX_FILTER_PRESERVE +} + +/* Enable the receive h/w following a reset. */ + +int ath_startrecv(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf, *tbf; + + spin_lock_bh(&sc->sc_rxbuflock); + if (list_empty(&sc->sc_rxbuf)) + goto start_recv; + + sc->sc_rxlink = NULL; + list_for_each_entry_safe(bf, tbf, &sc->sc_rxbuf, list) { + if (bf->bf_status & ATH_BUFSTATUS_STALE) { + /* restarting h/w, no need for holding descriptors */ + bf->bf_status &= ~ATH_BUFSTATUS_STALE; + /* + * Upper layer may not be done with the frame yet so + * we can't just re-queue it to hardware. Remove it + * from h/w queue. It'll be re-queued when upper layer + * returns the frame and ath_rx_requeue_mpdu is called. + */ + if (!(bf->bf_status & ATH_BUFSTATUS_FREE)) { + list_del(&bf->list); + continue; + } + } + /* chain descriptors */ + ath_rx_buf_link(sc, bf); + } + + /* We could have deleted elements so the list may be empty now */ + if (list_empty(&sc->sc_rxbuf)) + goto start_recv; + + bf = list_first_entry(&sc->sc_rxbuf, struct ath_buf, list); + ath9k_hw_putrxbuf(ah, bf->bf_daddr); + ath9k_hw_rxena(ah); /* enable recv descriptors */ + +start_recv: + spin_unlock_bh(&sc->sc_rxbuflock); + ath_opmode_init(sc); /* set filters, etc. */ + ath9k_hw_startpcureceive(ah); /* re-enable PCU/DMA engine */ + return 0; +} + +/* Disable the receive h/w in preparation for a reset. */ + +bool ath_stoprecv(struct ath_softc *sc) +{ + struct ath_hal *ah = sc->sc_ah; + u64 tsf; + bool stopped; + + ath9k_hw_stoppcurecv(ah); /* disable PCU */ + ath9k_hw_setrxfilter(ah, 0); /* clear recv filter */ + stopped = ath9k_hw_stopdmarecv(ah); /* disable DMA engine */ + mdelay(3); /* 3ms is long enough for 1 frame */ + tsf = ath9k_hw_gettsf64(ah); + sc->sc_rxlink = NULL; /* just in case */ + return stopped; +} + +/* Flush receive queue */ + +void ath_flushrecv(struct ath_softc *sc) +{ + /* + * ath_rx_tasklet may be used to handle rx interrupt and flush receive + * queue at the same time. Use a lock to serialize the access of rx + * queue. + * ath_rx_tasklet cannot hold the spinlock while indicating packets. + * Instead, do not claim the spinlock but check for a flush in + * progress (see references to sc_rxflush) + */ + spin_lock_bh(&sc->sc_rxflushlock); + sc->sc_rxflush = 1; + + ath_rx_tasklet(sc, 1); + + sc->sc_rxflush = 0; + spin_unlock_bh(&sc->sc_rxflushlock); +} + +/* Process an individual frame */ + +int ath_rx_input(struct ath_softc *sc, + struct ath_node *an, + int is_ampdu, + struct sk_buff *skb, + struct ath_recv_status *rx_status, + enum ATH_RX_TYPE *status) +{ + if (is_ampdu && sc->sc_rxaggr) { + *status = ATH_RX_CONSUMED; + return ath_ampdu_input(sc, an, skb, rx_status); + } else { + *status = ATH_RX_NON_CONSUMED; + return -1; + } +} + +/* Process receive queue, as well as LED, etc. */ + +int ath_rx_tasklet(struct ath_softc *sc, int flush) +{ +#define PA2DESC(_sc, _pa) \ + ((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \ + ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) + + struct ath_buf *bf, *bf_held = NULL; + struct ath_desc *ds; + struct ieee80211_hdr *hdr; + struct sk_buff *skb = NULL; + struct ath_recv_status rx_status; + struct ath_hal *ah = sc->sc_ah; + int type, rx_processed = 0; + u32 phyerr; + u8 chainreset = 0; + int retval; + __le16 fc; + + do { + /* If handling rx interrupt and flush is in progress => exit */ + if (sc->sc_rxflush && (flush == 0)) + break; + + spin_lock_bh(&sc->sc_rxbuflock); + if (list_empty(&sc->sc_rxbuf)) { + sc->sc_rxlink = NULL; + spin_unlock_bh(&sc->sc_rxbuflock); + break; + } + + bf = list_first_entry(&sc->sc_rxbuf, struct ath_buf, list); + + /* + * There is a race condition that BH gets scheduled after sw + * writes RxE and before hw re-load the last descriptor to get + * the newly chained one. Software must keep the last DONE + * descriptor as a holding descriptor - software does so by + * marking it with the STALE flag. + */ + if (bf->bf_status & ATH_BUFSTATUS_STALE) { + bf_held = bf; + if (list_is_last(&bf_held->list, &sc->sc_rxbuf)) { + /* + * The holding descriptor is the last + * descriptor in queue. It's safe to + * remove the last holding descriptor + * in BH context. + */ + list_del(&bf_held->list); + bf_held->bf_status &= ~ATH_BUFSTATUS_STALE; + sc->sc_rxlink = NULL; + + if (bf_held->bf_status & ATH_BUFSTATUS_FREE) { + list_add_tail(&bf_held->list, + &sc->sc_rxbuf); + ath_rx_buf_link(sc, bf_held); + } + spin_unlock_bh(&sc->sc_rxbuflock); + break; + } + bf = list_entry(bf->list.next, struct ath_buf, list); + } + + ds = bf->bf_desc; + ++rx_processed; + + /* + * Must provide the virtual address of the current + * descriptor, the physical address, and the virtual + * address of the next descriptor in the h/w chain. + * This allows the HAL to look ahead to see if the + * hardware is done with a descriptor by checking the + * done bit in the following descriptor and the address + * of the current descriptor the DMA engine is working + * on. All this is necessary because of our use of + * a self-linked list to avoid rx overruns. + */ + retval = ath9k_hw_rxprocdesc(ah, + ds, + bf->bf_daddr, + PA2DESC(sc, ds->ds_link), + 0); + if (retval == -EINPROGRESS) { + struct ath_buf *tbf; + struct ath_desc *tds; + + if (list_is_last(&bf->list, &sc->sc_rxbuf)) { + spin_unlock_bh(&sc->sc_rxbuflock); + break; + } + + tbf = list_entry(bf->list.next, struct ath_buf, list); + + /* + * On some hardware the descriptor status words could + * get corrupted, including the done bit. Because of + * this, check if the next descriptor's done bit is + * set or not. + * + * If the next descriptor's done bit is set, the current + * descriptor has been corrupted. Force s/w to discard + * this descriptor and continue... + */ + + tds = tbf->bf_desc; + retval = ath9k_hw_rxprocdesc(ah, + tds, tbf->bf_daddr, + PA2DESC(sc, tds->ds_link), 0); + if (retval == -EINPROGRESS) { + spin_unlock_bh(&sc->sc_rxbuflock); + break; + } + } + + /* XXX: we do not support frames spanning + * multiple descriptors */ + bf->bf_status |= ATH_BUFSTATUS_DONE; + + skb = bf->bf_mpdu; + if (skb == NULL) { /* XXX ??? can this happen */ + spin_unlock_bh(&sc->sc_rxbuflock); + continue; + } + /* + * Now we know it's a completed frame, we can indicate the + * frame. Remove the previous holding descriptor and leave + * this one in the queue as the new holding descriptor. + */ + if (bf_held) { + list_del(&bf_held->list); + bf_held->bf_status &= ~ATH_BUFSTATUS_STALE; + if (bf_held->bf_status & ATH_BUFSTATUS_FREE) { + list_add_tail(&bf_held->list, &sc->sc_rxbuf); + /* try to requeue this descriptor */ + ath_rx_buf_link(sc, bf_held); + } + } + + bf->bf_status |= ATH_BUFSTATUS_STALE; + bf_held = bf; + /* + * Release the lock here in case ieee80211_input() return + * the frame immediately by calling ath_rx_mpdu_requeue(). + */ + spin_unlock_bh(&sc->sc_rxbuflock); + + if (flush) { + /* + * If we're asked to flush receive queue, directly + * chain it back at the queue without processing it. + */ + goto rx_next; + } + + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + memzero(&rx_status, sizeof(struct ath_recv_status)); + + if (ds->ds_rxstat.rs_more) { + /* + * Frame spans multiple descriptors; this + * cannot happen yet as we don't support + * jumbograms. If not in monitor mode, + * discard the frame. + */ +#ifndef ERROR_FRAMES + /* + * Enable this if you want to see + * error frames in Monitor mode. + */ + if (sc->sc_opmode != ATH9K_M_MONITOR) + goto rx_next; +#endif + /* fall thru for monitor mode handling... */ + } else if (ds->ds_rxstat.rs_status != 0) { + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_CRC) + rx_status.flags |= ATH_RX_FCS_ERROR; + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_PHY) { + phyerr = ds->ds_rxstat.rs_phyerr & 0x1f; + goto rx_next; + } + + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_DECRYPT) { + /* + * Decrypt error. We only mark packet status + * here and always push up the frame up to let + * mac80211 handle the actual error case, be + * it no decryption key or real decryption + * error. This let us keep statistics there. + */ + rx_status.flags |= ATH_RX_DECRYPT_ERROR; + } else if (ds->ds_rxstat.rs_status & ATH9K_RXERR_MIC) { + /* + * Demic error. We only mark frame status here + * and always push up the frame up to let + * mac80211 handle the actual error case. This + * let us keep statistics there. Hardware may + * post a false-positive MIC error. + */ + if (ieee80211_is_ctl(fc)) + /* + * Sometimes, we get invalid + * MIC failures on valid control frames. + * Remove these mic errors. + */ + ds->ds_rxstat.rs_status &= + ~ATH9K_RXERR_MIC; + else + rx_status.flags |= ATH_RX_MIC_ERROR; + } + /* + * Reject error frames with the exception of + * decryption and MIC failures. For monitor mode, + * we also ignore the CRC error. + */ + if (sc->sc_opmode == ATH9K_M_MONITOR) { + if (ds->ds_rxstat.rs_status & + ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC | + ATH9K_RXERR_CRC)) + goto rx_next; + } else { + if (ds->ds_rxstat.rs_status & + ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) { + goto rx_next; + } + } + } + /* + * The status portion of the descriptor could get corrupted. + */ + if (sc->sc_rxbufsize < ds->ds_rxstat.rs_datalen) + goto rx_next; + /* + * Sync and unmap the frame. At this point we're + * committed to passing the sk_buff somewhere so + * clear buf_skb; this means a new sk_buff must be + * allocated when the rx descriptor is setup again + * to receive another frame. + */ + skb_put(skb, ds->ds_rxstat.rs_datalen); + skb->protocol = cpu_to_be16(ETH_P_CONTROL); + rx_status.tsf = ath_extend_tsf(sc, ds->ds_rxstat.rs_tstamp); + rx_status.rateieee = + sc->sc_hwmap[ds->ds_rxstat.rs_rate].ieeerate; + rx_status.rateKbps = + sc->sc_hwmap[ds->ds_rxstat.rs_rate].rateKbps; + rx_status.ratecode = ds->ds_rxstat.rs_rate; + + /* HT rate */ + if (rx_status.ratecode & 0x80) { + /* TODO - add table to avoid division */ + if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040) { + rx_status.flags |= ATH_RX_40MHZ; + rx_status.rateKbps = + (rx_status.rateKbps * 27) / 13; + } + if (ds->ds_rxstat.rs_flags & ATH9K_RX_GI) + rx_status.rateKbps = + (rx_status.rateKbps * 10) / 9; + else + rx_status.flags |= ATH_RX_SHORT_GI; + } + + /* sc->sc_noise_floor is only available when the station + attaches to an AP, so we use a default value + if we are not yet attached. */ + + /* XXX we should use either sc->sc_noise_floor or + * ath_hal_getChanNoise(ah, &sc->sc_curchan) + * to calculate the noise floor. + * However, the value returned by ath_hal_getChanNoise + * seems to be incorrect (-31dBm on the last test), + * so we will use a hard-coded value until we + * figure out what is going on. + */ + rx_status.abs_rssi = + ds->ds_rxstat.rs_rssi + ATH_DEFAULT_NOISE_FLOOR; + + pci_dma_sync_single_for_cpu(sc->pdev, + bf->bf_buf_addr, + skb_tailroom(skb), + PCI_DMA_FROMDEVICE); + pci_unmap_single(sc->pdev, + bf->bf_buf_addr, + sc->sc_rxbufsize, + PCI_DMA_FROMDEVICE); + + /* XXX: Ah! make me more readable, use a helper */ + if (ah->ah_caps.halHTSupport) { + if (ds->ds_rxstat.rs_moreaggr == 0) { + rx_status.rssictl[0] = + ds->ds_rxstat.rs_rssi_ctl0; + rx_status.rssictl[1] = + ds->ds_rxstat.rs_rssi_ctl1; + rx_status.rssictl[2] = + ds->ds_rxstat.rs_rssi_ctl2; + rx_status.rssi = ds->ds_rxstat.rs_rssi; + if (ds->ds_rxstat.rs_flags & ATH9K_RX_2040) { + rx_status.rssiextn[0] = + ds->ds_rxstat.rs_rssi_ext0; + rx_status.rssiextn[1] = + ds->ds_rxstat.rs_rssi_ext1; + rx_status.rssiextn[2] = + ds->ds_rxstat.rs_rssi_ext2; + rx_status.flags |= + ATH_RX_RSSI_EXTN_VALID; + } + rx_status.flags |= ATH_RX_RSSI_VALID | + ATH_RX_CHAIN_RSSI_VALID; + } + } else { + /* + * Need to insert the "combined" rssi into the + * status structure for upper layer processing + */ + rx_status.rssi = ds->ds_rxstat.rs_rssi; + rx_status.flags |= ATH_RX_RSSI_VALID; + } + + /* Pass frames up to the stack. */ + + type = ath_rx_indicate(sc, skb, + &rx_status, ds->ds_rxstat.rs_keyix); + + /* + * change the default rx antenna if rx diversity chooses the + * other antenna 3 times in a row. + */ + if (sc->sc_defant != ds->ds_rxstat.rs_antenna) { + if (++sc->sc_rxotherant >= 3) + ath_setdefantenna(sc, + ds->ds_rxstat.rs_antenna); + } else { + sc->sc_rxotherant = 0; + } + +#ifdef CONFIG_SLOW_ANT_DIV + if ((rx_status.flags & ATH_RX_RSSI_VALID) && + ieee80211_is_beacon(fc)) { + ath_slow_ant_div(&sc->sc_antdiv, hdr, &ds->ds_rxstat); + } +#endif + /* + * For frames successfully indicated, the buffer will be + * returned to us by upper layers by calling + * ath_rx_mpdu_requeue, either synchronusly or asynchronously. + * So we don't want to do it here in this loop. + */ + continue; + +rx_next: + bf->bf_status |= ATH_BUFSTATUS_FREE; + } while (TRUE); + + if (chainreset) { + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: Reset rx chain mask. " + "Do internal reset\n", __func__); + ASSERT(flush == 0); + ath_internal_reset(sc); + } + + return 0; +#undef PA2DESC +} + +/* Process ADDBA request in per-TID data structure */ + +int ath_rx_aggr_start(struct ath_softc *sc, + const u8 *addr, + u16 tid, + u16 *ssn) +{ + struct ath_arx_tid *rxtid; + struct ath_node *an; + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_supported_band *sband; + u16 buffersize = 0; + + spin_lock_bh(&sc->node_lock); + an = ath_node_find(sc, (u8 *) addr); + spin_unlock_bh(&sc->node_lock); + + if (!an) { + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Node not found to initialize RX aggregation\n", + __func__); + return -1; + } + + sband = hw->wiphy->bands[hw->conf.channel->band]; + buffersize = IEEE80211_MIN_AMPDU_BUF << + sband->ht_info.ampdu_factor; /* FIXME */ + + rxtid = &an->an_aggr.rx.tid[tid]; + + spin_lock_bh(&rxtid->tidlock); + if (sc->sc_rxaggr) { + /* Allow aggregation reception + * Adjust rx BA window size. Peer might indicate a + * zero buffer size for a _dont_care_ condition. + */ + if (buffersize) + rxtid->baw_size = min(buffersize, rxtid->baw_size); + + /* set rx sequence number */ + rxtid->seq_next = *ssn; + + /* Allocate the receive buffers for this TID */ + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Allcating rxbuffer for TID %d\n", __func__, tid); + + if (rxtid->rxbuf == NULL) { + /* + * If the rxbuff is not NULL at this point, we *probably* + * already allocated the buffer on a previous ADDBA, + * and this is a subsequent ADDBA that got through. + * Don't allocate, but use the value in the pointer, + * we zero it out when we de-allocate. + */ + rxtid->rxbuf = kmalloc(ATH_TID_MAX_BUFS * + sizeof(struct ath_rxbuf), GFP_ATOMIC); + } + if (rxtid->rxbuf == NULL) { + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Unable to allocate RX buffer, " + "refusing ADDBA\n", __func__); + } else { + /* Ensure the memory is zeroed out (all internal + * pointers are null) */ + memzero(rxtid->rxbuf, ATH_TID_MAX_BUFS * + sizeof(struct ath_rxbuf)); + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Allocated @%p\n", __func__, rxtid->rxbuf); + + /* Allow aggregation reception */ + rxtid->addba_exchangecomplete = 1; + } + } + spin_unlock_bh(&rxtid->tidlock); + + return 0; +} + +/* Process DELBA */ + +int ath_rx_aggr_stop(struct ath_softc *sc, + const u8 *addr, + u16 tid) +{ + struct ath_node *an; + + spin_lock_bh(&sc->node_lock); + an = ath_node_find(sc, (u8 *) addr); + spin_unlock_bh(&sc->node_lock); + + if (!an) { + DPRINTF(sc, ATH_DBG_AGGR, + "%s: RX aggr stop for non-existent node\n", __func__); + return -1; + } + + ath_rx_aggr_teardown(sc, an, tid); + return 0; +} + +/* Rx aggregation tear down */ + +void ath_rx_aggr_teardown(struct ath_softc *sc, + struct ath_node *an, u8 tid) +{ + struct ath_arx_tid *rxtid = &an->an_aggr.rx.tid[tid]; + + if (!rxtid->addba_exchangecomplete) + return; + + del_timer_sync(&rxtid->timer); + ath_rx_flush_tid(sc, rxtid, 0); + rxtid->addba_exchangecomplete = 0; + + /* De-allocate the receive buffer array allocated when addba started */ + + if (rxtid->rxbuf) { + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Deallocating TID %d rxbuff @%p\n", + __func__, tid, rxtid->rxbuf); + kfree(rxtid->rxbuf); + + /* Set pointer to null to avoid reuse*/ + rxtid->rxbuf = NULL; + } +} + +/* Initialize per-node receive state */ + +void ath_rx_node_init(struct ath_softc *sc, struct ath_node *an) +{ + if (sc->sc_rxaggr) { + struct ath_arx_tid *rxtid; + int tidno; + + /* Init per tid rx state */ + for (tidno = 0, rxtid = &an->an_aggr.rx.tid[tidno]; + tidno < WME_NUM_TID; + tidno++, rxtid++) { + rxtid->an = an; + rxtid->seq_reset = 1; + rxtid->seq_next = 0; + rxtid->baw_size = WME_MAX_BA; + rxtid->baw_head = rxtid->baw_tail = 0; + + /* + * Ensure the buffer pointer is null at this point + * (needs to be allocated when addba is received) + */ + + rxtid->rxbuf = NULL; + setup_timer(&rxtid->timer, ath_rx_timer, + (unsigned long)rxtid); + spin_lock_init(&rxtid->tidlock); + + /* ADDBA state */ + rxtid->addba_exchangecomplete = 0; + } + } +} + +void ath_rx_node_cleanup(struct ath_softc *sc, struct ath_node *an) +{ + if (sc->sc_rxaggr) { + struct ath_arx_tid *rxtid; + int tidno, i; + + /* Init per tid rx state */ + for (tidno = 0, rxtid = &an->an_aggr.rx.tid[tidno]; + tidno < WME_NUM_TID; + tidno++, rxtid++) { + + if (!rxtid->addba_exchangecomplete) + continue; + + /* must cancel timer first */ + del_timer_sync(&rxtid->timer); + + /* drop any pending sub-frames */ + ath_rx_flush_tid(sc, rxtid, 1); + + for (i = 0; i < ATH_TID_MAX_BUFS; i++) + ASSERT(rxtid->rxbuf[i].rx_wbuf == NULL); + + rxtid->addba_exchangecomplete = 0; + } + } + +} + +/* Cleanup per-node receive state */ + +void ath_rx_node_free(struct ath_softc *sc, struct ath_node *an) +{ + ath_rx_node_cleanup(sc, an); +} + +dma_addr_t ath_skb_map_single(struct ath_softc *sc, + struct sk_buff *skb, + int direction, + dma_addr_t *pa) +{ + /* + * NB: do NOT use skb->len, which is 0 on initialization. + * Use skb's entire data area instead. + */ + *pa = pci_map_single(sc->pdev, skb->data, + skb_end_pointer(skb) - skb->head, direction); + return *pa; +} + +void ath_skb_unmap_single(struct ath_softc *sc, + struct sk_buff *skb, + int direction, + dma_addr_t *pa) +{ + /* Unmap skb's entire data area */ + pci_unmap_single(sc->pdev, *pa, + skb_end_pointer(skb) - skb->head, direction); +} diff --git a/drivers/net/wireless/ath9k/reg.h b/drivers/net/wireless/ath9k/reg.h new file mode 100644 index 000000000000..42b0890a4685 --- /dev/null +++ b/drivers/net/wireless/ath9k/reg.h @@ -0,0 +1,1385 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef REG_H +#define REG_H + +#define AR_CR 0x0008 +#define AR_CR_RXE 0x00000004 +#define AR_CR_RXD 0x00000020 +#define AR_CR_SWI 0x00000040 + +#define AR_RXDP 0x000C + +#define AR_CFG 0x0014 +#define AR_CFG_SWTD 0x00000001 +#define AR_CFG_SWTB 0x00000002 +#define AR_CFG_SWRD 0x00000004 +#define AR_CFG_SWRB 0x00000008 +#define AR_CFG_SWRG 0x00000010 +#define AR_CFG_AP_ADHOC_INDICATION 0x00000020 +#define AR_CFG_PHOK 0x00000100 +#define AR_CFG_CLK_GATE_DIS 0x00000400 +#define AR_CFG_EEBS 0x00000200 +#define AR_CFG_PCI_MASTER_REQ_Q_THRESH 0x00060000 +#define AR_CFG_PCI_MASTER_REQ_Q_THRESH_S 17 + +#define AR_MIRT 0x0020 +#define AR_MIRT_VAL 0x0000ffff +#define AR_MIRT_VAL_S 16 + +#define AR_IER 0x0024 +#define AR_IER_ENABLE 0x00000001 +#define AR_IER_DISABLE 0x00000000 + +#define AR_TIMT 0x0028 +#define AR_TIMT_LAST 0x0000ffff +#define AR_TIMT_LAST_S 0 +#define AR_TIMT_FIRST 0xffff0000 +#define AR_TIMT_FIRST_S 16 + +#define AR_RIMT 0x002C +#define AR_RIMT_LAST 0x0000ffff +#define AR_RIMT_LAST_S 0 +#define AR_RIMT_FIRST 0xffff0000 +#define AR_RIMT_FIRST_S 16 + +#define AR_DMASIZE_4B 0x00000000 +#define AR_DMASIZE_8B 0x00000001 +#define AR_DMASIZE_16B 0x00000002 +#define AR_DMASIZE_32B 0x00000003 +#define AR_DMASIZE_64B 0x00000004 +#define AR_DMASIZE_128B 0x00000005 +#define AR_DMASIZE_256B 0x00000006 +#define AR_DMASIZE_512B 0x00000007 + +#define AR_TXCFG 0x0030 +#define AR_TXCFG_DMASZ_MASK 0x00000003 +#define AR_TXCFG_DMASZ_4B 0 +#define AR_TXCFG_DMASZ_8B 1 +#define AR_TXCFG_DMASZ_16B 2 +#define AR_TXCFG_DMASZ_32B 3 +#define AR_TXCFG_DMASZ_64B 4 +#define AR_TXCFG_DMASZ_128B 5 +#define AR_TXCFG_DMASZ_256B 6 +#define AR_TXCFG_DMASZ_512B 7 +#define AR_FTRIG 0x000003F0 +#define AR_FTRIG_S 4 +#define AR_FTRIG_IMMED 0x00000000 +#define AR_FTRIG_64B 0x00000010 +#define AR_FTRIG_128B 0x00000020 +#define AR_FTRIG_192B 0x00000030 +#define AR_FTRIG_256B 0x00000040 +#define AR_FTRIG_512B 0x00000080 +#define AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY 0x00000800 + +#define AR_RXCFG 0x0034 +#define AR_RXCFG_CHIRP 0x00000008 +#define AR_RXCFG_ZLFDMA 0x00000010 +#define AR_RXCFG_DMASZ_MASK 0x00000007 +#define AR_RXCFG_DMASZ_4B 0 +#define AR_RXCFG_DMASZ_8B 1 +#define AR_RXCFG_DMASZ_16B 2 +#define AR_RXCFG_DMASZ_32B 3 +#define AR_RXCFG_DMASZ_64B 4 +#define AR_RXCFG_DMASZ_128B 5 +#define AR_RXCFG_DMASZ_256B 6 +#define AR_RXCFG_DMASZ_512B 7 + +#define AR_MIBC 0x0040 +#define AR_MIBC_COW 0x00000001 +#define AR_MIBC_FMC 0x00000002 +#define AR_MIBC_CMC 0x00000004 +#define AR_MIBC_MCS 0x00000008 + +#define AR_TOPS 0x0044 +#define AR_TOPS_MASK 0x0000FFFF + +#define AR_RXNPTO 0x0048 +#define AR_RXNPTO_MASK 0x000003FF + +#define AR_TXNPTO 0x004C +#define AR_TXNPTO_MASK 0x000003FF +#define AR_TXNPTO_QCU_MASK 0x000FFC00 + +#define AR_RPGTO 0x0050 +#define AR_RPGTO_MASK 0x000003FF + +#define AR_RPCNT 0x0054 +#define AR_RPCNT_MASK 0x0000001F + +#define AR_MACMISC 0x0058 +#define AR_MACMISC_PCI_EXT_FORCE 0x00000010 +#define AR_MACMISC_DMA_OBS 0x000001E0 +#define AR_MACMISC_DMA_OBS_S 5 +#define AR_MACMISC_DMA_OBS_LINE_0 0 +#define AR_MACMISC_DMA_OBS_LINE_1 1 +#define AR_MACMISC_DMA_OBS_LINE_2 2 +#define AR_MACMISC_DMA_OBS_LINE_3 3 +#define AR_MACMISC_DMA_OBS_LINE_4 4 +#define AR_MACMISC_DMA_OBS_LINE_5 5 +#define AR_MACMISC_DMA_OBS_LINE_6 6 +#define AR_MACMISC_DMA_OBS_LINE_7 7 +#define AR_MACMISC_DMA_OBS_LINE_8 8 +#define AR_MACMISC_MISC_OBS 0x00000E00 +#define AR_MACMISC_MISC_OBS_S 9 +#define AR_MACMISC_MISC_OBS_BUS_LSB 0x00007000 +#define AR_MACMISC_MISC_OBS_BUS_LSB_S 12 +#define AR_MACMISC_MISC_OBS_BUS_MSB 0x00038000 +#define AR_MACMISC_MISC_OBS_BUS_MSB_S 15 +#define AR_MACMISC_MISC_OBS_BUS_1 1 + +#define AR_GTXTO 0x0064 +#define AR_GTXTO_TIMEOUT_COUNTER 0x0000FFFF +#define AR_GTXTO_TIMEOUT_LIMIT 0xFFFF0000 +#define AR_GTXTO_TIMEOUT_LIMIT_S 16 + +#define AR_GTTM 0x0068 +#define AR_GTTM_USEC 0x00000001 +#define AR_GTTM_IGNORE_IDLE 0x00000002 +#define AR_GTTM_RESET_IDLE 0x00000004 +#define AR_GTTM_CST_USEC 0x00000008 + +#define AR_CST 0x006C +#define AR_CST_TIMEOUT_COUNTER 0x0000FFFF +#define AR_CST_TIMEOUT_LIMIT 0xFFFF0000 +#define AR_CST_TIMEOUT_LIMIT_S 16 + +#define AR_SREV_VERSION_9100 0x014 + +#define AR_SREV_5416_V20_OR_LATER(_ah) \ + (AR_SREV_9100((_ah)) || AR_SREV_5416_20_OR_LATER(_ah)) +#define AR_SREV_5416_V22_OR_LATER(_ah) \ + (AR_SREV_9100((_ah)) || AR_SREV_5416_22_OR_LATER(_ah)) + +#define AR_ISR 0x0080 +#define AR_ISR_RXOK 0x00000001 +#define AR_ISR_RXDESC 0x00000002 +#define AR_ISR_RXERR 0x00000004 +#define AR_ISR_RXNOPKT 0x00000008 +#define AR_ISR_RXEOL 0x00000010 +#define AR_ISR_RXORN 0x00000020 +#define AR_ISR_TXOK 0x00000040 +#define AR_ISR_TXDESC 0x00000080 +#define AR_ISR_TXERR 0x00000100 +#define AR_ISR_TXNOPKT 0x00000200 +#define AR_ISR_TXEOL 0x00000400 +#define AR_ISR_TXURN 0x00000800 +#define AR_ISR_MIB 0x00001000 +#define AR_ISR_SWI 0x00002000 +#define AR_ISR_RXPHY 0x00004000 +#define AR_ISR_RXKCM 0x00008000 +#define AR_ISR_SWBA 0x00010000 +#define AR_ISR_BRSSI 0x00020000 +#define AR_ISR_BMISS 0x00040000 +#define AR_ISR_BNR 0x00100000 +#define AR_ISR_RXCHIRP 0x00200000 +#define AR_ISR_BCNMISC 0x00800000 +#define AR_ISR_TIM 0x00800000 +#define AR_ISR_QCBROVF 0x02000000 +#define AR_ISR_QCBRURN 0x04000000 +#define AR_ISR_QTRIG 0x08000000 +#define AR_ISR_GENTMR 0x10000000 + +#define AR_ISR_TXMINTR 0x00080000 +#define AR_ISR_RXMINTR 0x01000000 +#define AR_ISR_TXINTM 0x40000000 +#define AR_ISR_RXINTM 0x80000000 + +#define AR_ISR_S0 0x0084 +#define AR_ISR_S0_QCU_TXOK 0x000003FF +#define AR_ISR_S0_QCU_TXOK_S 0 +#define AR_ISR_S0_QCU_TXDESC 0x03FF0000 +#define AR_ISR_S0_QCU_TXDESC_S 16 + +#define AR_ISR_S1 0x0088 +#define AR_ISR_S1_QCU_TXERR 0x000003FF +#define AR_ISR_S1_QCU_TXERR_S 0 +#define AR_ISR_S1_QCU_TXEOL 0x03FF0000 +#define AR_ISR_S1_QCU_TXEOL_S 16 + +#define AR_ISR_S2 0x008c +#define AR_ISR_S2_QCU_TXURN 0x000003FF +#define AR_ISR_S2_CST 0x00400000 +#define AR_ISR_S2_GTT 0x00800000 +#define AR_ISR_S2_TIM 0x01000000 +#define AR_ISR_S2_CABEND 0x02000000 +#define AR_ISR_S2_DTIMSYNC 0x04000000 +#define AR_ISR_S2_BCNTO 0x08000000 +#define AR_ISR_S2_CABTO 0x10000000 +#define AR_ISR_S2_DTIM 0x20000000 +#define AR_ISR_S2_TSFOOR 0x40000000 +#define AR_ISR_S2_TBTT_TIME 0x80000000 + +#define AR_ISR_S3 0x0090 +#define AR_ISR_S3_QCU_QCBROVF 0x000003FF +#define AR_ISR_S3_QCU_QCBRURN 0x03FF0000 + +#define AR_ISR_S4 0x0094 +#define AR_ISR_S4_QCU_QTRIG 0x000003FF +#define AR_ISR_S4_RESV0 0xFFFFFC00 + +#define AR_ISR_S5 0x0098 +#define AR_ISR_S5_TIMER_TRIG 0x000000FF +#define AR_ISR_S5_TIMER_THRESH 0x0007FE00 +#define AR_ISR_S5_TIM_TIMER 0x00000010 +#define AR_ISR_S5_DTIM_TIMER 0x00000020 +#define AR_ISR_S5_S 0x00d8 +#define AR_IMR_S5 0x00b8 +#define AR_IMR_S5_TIM_TIMER 0x00000010 +#define AR_IMR_S5_DTIM_TIMER 0x00000020 + + +#define AR_IMR 0x00a0 +#define AR_IMR_RXOK 0x00000001 +#define AR_IMR_RXDESC 0x00000002 +#define AR_IMR_RXERR 0x00000004 +#define AR_IMR_RXNOPKT 0x00000008 +#define AR_IMR_RXEOL 0x00000010 +#define AR_IMR_RXORN 0x00000020 +#define AR_IMR_TXOK 0x00000040 +#define AR_IMR_TXDESC 0x00000080 +#define AR_IMR_TXERR 0x00000100 +#define AR_IMR_TXNOPKT 0x00000200 +#define AR_IMR_TXEOL 0x00000400 +#define AR_IMR_TXURN 0x00000800 +#define AR_IMR_MIB 0x00001000 +#define AR_IMR_SWI 0x00002000 +#define AR_IMR_RXPHY 0x00004000 +#define AR_IMR_RXKCM 0x00008000 +#define AR_IMR_SWBA 0x00010000 +#define AR_IMR_BRSSI 0x00020000 +#define AR_IMR_BMISS 0x00040000 +#define AR_IMR_BNR 0x00100000 +#define AR_IMR_RXCHIRP 0x00200000 +#define AR_IMR_BCNMISC 0x00800000 +#define AR_IMR_TIM 0x00800000 +#define AR_IMR_QCBROVF 0x02000000 +#define AR_IMR_QCBRURN 0x04000000 +#define AR_IMR_QTRIG 0x08000000 +#define AR_IMR_GENTMR 0x10000000 + +#define AR_IMR_TXMINTR 0x00080000 +#define AR_IMR_RXMINTR 0x01000000 +#define AR_IMR_TXINTM 0x40000000 +#define AR_IMR_RXINTM 0x80000000 + +#define AR_IMR_S0 0x00a4 +#define AR_IMR_S0_QCU_TXOK 0x000003FF +#define AR_IMR_S0_QCU_TXOK_S 0 +#define AR_IMR_S0_QCU_TXDESC 0x03FF0000 +#define AR_IMR_S0_QCU_TXDESC_S 16 + +#define AR_IMR_S1 0x00a8 +#define AR_IMR_S1_QCU_TXERR 0x000003FF +#define AR_IMR_S1_QCU_TXERR_S 0 +#define AR_IMR_S1_QCU_TXEOL 0x03FF0000 +#define AR_IMR_S1_QCU_TXEOL_S 16 + +#define AR_IMR_S2 0x00ac +#define AR_IMR_S2_QCU_TXURN 0x000003FF +#define AR_IMR_S2_QCU_TXURN_S 0 +#define AR_IMR_S2_CST 0x00400000 +#define AR_IMR_S2_GTT 0x00800000 +#define AR_IMR_S2_TIM 0x01000000 +#define AR_IMR_S2_CABEND 0x02000000 +#define AR_IMR_S2_DTIMSYNC 0x04000000 +#define AR_IMR_S2_BCNTO 0x08000000 +#define AR_IMR_S2_CABTO 0x10000000 +#define AR_IMR_S2_DTIM 0x20000000 +#define AR_IMR_S2_TSFOOR 0x40000000 + +#define AR_IMR_S3 0x00b0 +#define AR_IMR_S3_QCU_QCBROVF 0x000003FF +#define AR_IMR_S3_QCU_QCBRURN 0x03FF0000 +#define AR_IMR_S3_QCU_QCBRURN_S 16 + +#define AR_IMR_S4 0x00b4 +#define AR_IMR_S4_QCU_QTRIG 0x000003FF +#define AR_IMR_S4_RESV0 0xFFFFFC00 + +#define AR_IMR_S5 0x00b8 +#define AR_IMR_S5_TIMER_TRIG 0x000000FF +#define AR_IMR_S5_TIMER_THRESH 0x0000FF00 + + +#define AR_ISR_RAC 0x00c0 +#define AR_ISR_S0_S 0x00c4 +#define AR_ISR_S0_QCU_TXOK 0x000003FF +#define AR_ISR_S0_QCU_TXOK_S 0 +#define AR_ISR_S0_QCU_TXDESC 0x03FF0000 +#define AR_ISR_S0_QCU_TXDESC_S 16 + +#define AR_ISR_S1_S 0x00c8 +#define AR_ISR_S1_QCU_TXERR 0x000003FF +#define AR_ISR_S1_QCU_TXERR_S 0 +#define AR_ISR_S1_QCU_TXEOL 0x03FF0000 +#define AR_ISR_S1_QCU_TXEOL_S 16 + +#define AR_ISR_S2_S 0x00cc +#define AR_ISR_S3_S 0x00d0 +#define AR_ISR_S4_S 0x00d4 +#define AR_ISR_S5_S 0x00d8 +#define AR_DMADBG_0 0x00e0 +#define AR_DMADBG_1 0x00e4 +#define AR_DMADBG_2 0x00e8 +#define AR_DMADBG_3 0x00ec +#define AR_DMADBG_4 0x00f0 +#define AR_DMADBG_5 0x00f4 +#define AR_DMADBG_6 0x00f8 +#define AR_DMADBG_7 0x00fc + +#define AR_NUM_QCU 10 +#define AR_QCU_0 0x0001 +#define AR_QCU_1 0x0002 +#define AR_QCU_2 0x0004 +#define AR_QCU_3 0x0008 +#define AR_QCU_4 0x0010 +#define AR_QCU_5 0x0020 +#define AR_QCU_6 0x0040 +#define AR_QCU_7 0x0080 +#define AR_QCU_8 0x0100 +#define AR_QCU_9 0x0200 + +#define AR_Q0_TXDP 0x0800 +#define AR_Q1_TXDP 0x0804 +#define AR_Q2_TXDP 0x0808 +#define AR_Q3_TXDP 0x080c +#define AR_Q4_TXDP 0x0810 +#define AR_Q5_TXDP 0x0814 +#define AR_Q6_TXDP 0x0818 +#define AR_Q7_TXDP 0x081c +#define AR_Q8_TXDP 0x0820 +#define AR_Q9_TXDP 0x0824 +#define AR_QTXDP(_i) (AR_Q0_TXDP + ((_i)<<2)) + +#define AR_Q_TXE 0x0840 +#define AR_Q_TXE_M 0x000003FF + +#define AR_Q_TXD 0x0880 +#define AR_Q_TXD_M 0x000003FF + +#define AR_Q0_CBRCFG 0x08c0 +#define AR_Q1_CBRCFG 0x08c4 +#define AR_Q2_CBRCFG 0x08c8 +#define AR_Q3_CBRCFG 0x08cc +#define AR_Q4_CBRCFG 0x08d0 +#define AR_Q5_CBRCFG 0x08d4 +#define AR_Q6_CBRCFG 0x08d8 +#define AR_Q7_CBRCFG 0x08dc +#define AR_Q8_CBRCFG 0x08e0 +#define AR_Q9_CBRCFG 0x08e4 +#define AR_QCBRCFG(_i) (AR_Q0_CBRCFG + ((_i)<<2)) +#define AR_Q_CBRCFG_INTERVAL 0x00FFFFFF +#define AR_Q_CBRCFG_INTERVAL_S 0 +#define AR_Q_CBRCFG_OVF_THRESH 0xFF000000 +#define AR_Q_CBRCFG_OVF_THRESH_S 24 + +#define AR_Q0_RDYTIMECFG 0x0900 +#define AR_Q1_RDYTIMECFG 0x0904 +#define AR_Q2_RDYTIMECFG 0x0908 +#define AR_Q3_RDYTIMECFG 0x090c +#define AR_Q4_RDYTIMECFG 0x0910 +#define AR_Q5_RDYTIMECFG 0x0914 +#define AR_Q6_RDYTIMECFG 0x0918 +#define AR_Q7_RDYTIMECFG 0x091c +#define AR_Q8_RDYTIMECFG 0x0920 +#define AR_Q9_RDYTIMECFG 0x0924 +#define AR_QRDYTIMECFG(_i) (AR_Q0_RDYTIMECFG + ((_i)<<2)) +#define AR_Q_RDYTIMECFG_DURATION 0x00FFFFFF +#define AR_Q_RDYTIMECFG_DURATION_S 0 +#define AR_Q_RDYTIMECFG_EN 0x01000000 + +#define AR_Q_ONESHOTARM_SC 0x0940 +#define AR_Q_ONESHOTARM_SC_M 0x000003FF +#define AR_Q_ONESHOTARM_SC_RESV0 0xFFFFFC00 + +#define AR_Q_ONESHOTARM_CC 0x0980 +#define AR_Q_ONESHOTARM_CC_M 0x000003FF +#define AR_Q_ONESHOTARM_CC_RESV0 0xFFFFFC00 + +#define AR_Q0_MISC 0x09c0 +#define AR_Q1_MISC 0x09c4 +#define AR_Q2_MISC 0x09c8 +#define AR_Q3_MISC 0x09cc +#define AR_Q4_MISC 0x09d0 +#define AR_Q5_MISC 0x09d4 +#define AR_Q6_MISC 0x09d8 +#define AR_Q7_MISC 0x09dc +#define AR_Q8_MISC 0x09e0 +#define AR_Q9_MISC 0x09e4 +#define AR_QMISC(_i) (AR_Q0_MISC + ((_i)<<2)) +#define AR_Q_MISC_FSP 0x0000000F +#define AR_Q_MISC_FSP_ASAP 0 +#define AR_Q_MISC_FSP_CBR 1 +#define AR_Q_MISC_FSP_DBA_GATED 2 +#define AR_Q_MISC_FSP_TIM_GATED 3 +#define AR_Q_MISC_FSP_BEACON_SENT_GATED 4 +#define AR_Q_MISC_FSP_BEACON_RCVD_GATED 5 +#define AR_Q_MISC_ONE_SHOT_EN 0x00000010 +#define AR_Q_MISC_CBR_INCR_DIS1 0x00000020 +#define AR_Q_MISC_CBR_INCR_DIS0 0x00000040 +#define AR_Q_MISC_BEACON_USE 0x00000080 +#define AR_Q_MISC_CBR_EXP_CNTR_LIMIT_EN 0x00000100 +#define AR_Q_MISC_RDYTIME_EXP_POLICY 0x00000200 +#define AR_Q_MISC_RESET_CBR_EXP_CTR 0x00000400 +#define AR_Q_MISC_DCU_EARLY_TERM_REQ 0x00000800 +#define AR_Q_MISC_RESV0 0xFFFFF000 + +#define AR_Q0_STS 0x0a00 +#define AR_Q1_STS 0x0a04 +#define AR_Q2_STS 0x0a08 +#define AR_Q3_STS 0x0a0c +#define AR_Q4_STS 0x0a10 +#define AR_Q5_STS 0x0a14 +#define AR_Q6_STS 0x0a18 +#define AR_Q7_STS 0x0a1c +#define AR_Q8_STS 0x0a20 +#define AR_Q9_STS 0x0a24 +#define AR_QSTS(_i) (AR_Q0_STS + ((_i)<<2)) +#define AR_Q_STS_PEND_FR_CNT 0x00000003 +#define AR_Q_STS_RESV0 0x000000FC +#define AR_Q_STS_CBR_EXP_CNT 0x0000FF00 +#define AR_Q_STS_RESV1 0xFFFF0000 + +#define AR_Q_RDYTIMESHDN 0x0a40 +#define AR_Q_RDYTIMESHDN_M 0x000003FF + + +#define AR_NUM_DCU 10 +#define AR_DCU_0 0x0001 +#define AR_DCU_1 0x0002 +#define AR_DCU_2 0x0004 +#define AR_DCU_3 0x0008 +#define AR_DCU_4 0x0010 +#define AR_DCU_5 0x0020 +#define AR_DCU_6 0x0040 +#define AR_DCU_7 0x0080 +#define AR_DCU_8 0x0100 +#define AR_DCU_9 0x0200 + +#define AR_D0_QCUMASK 0x1000 +#define AR_D1_QCUMASK 0x1004 +#define AR_D2_QCUMASK 0x1008 +#define AR_D3_QCUMASK 0x100c +#define AR_D4_QCUMASK 0x1010 +#define AR_D5_QCUMASK 0x1014 +#define AR_D6_QCUMASK 0x1018 +#define AR_D7_QCUMASK 0x101c +#define AR_D8_QCUMASK 0x1020 +#define AR_D9_QCUMASK 0x1024 +#define AR_DQCUMASK(_i) (AR_D0_QCUMASK + ((_i)<<2)) +#define AR_D_QCUMASK 0x000003FF +#define AR_D_QCUMASK_RESV0 0xFFFFFC00 + +#define AR_D_TXBLK_CMD 0x1038 +#define AR_D_TXBLK_DATA(i) (AR_D_TXBLK_CMD+(i)) + +#define AR_D0_LCL_IFS 0x1040 +#define AR_D1_LCL_IFS 0x1044 +#define AR_D2_LCL_IFS 0x1048 +#define AR_D3_LCL_IFS 0x104c +#define AR_D4_LCL_IFS 0x1050 +#define AR_D5_LCL_IFS 0x1054 +#define AR_D6_LCL_IFS 0x1058 +#define AR_D7_LCL_IFS 0x105c +#define AR_D8_LCL_IFS 0x1060 +#define AR_D9_LCL_IFS 0x1064 +#define AR_DLCL_IFS(_i) (AR_D0_LCL_IFS + ((_i)<<2)) +#define AR_D_LCL_IFS_CWMIN 0x000003FF +#define AR_D_LCL_IFS_CWMIN_S 0 +#define AR_D_LCL_IFS_CWMAX 0x000FFC00 +#define AR_D_LCL_IFS_CWMAX_S 10 +#define AR_D_LCL_IFS_AIFS 0x0FF00000 +#define AR_D_LCL_IFS_AIFS_S 20 + +#define AR_D_LCL_IFS_RESV0 0xF0000000 + +#define AR_D0_RETRY_LIMIT 0x1080 +#define AR_D1_RETRY_LIMIT 0x1084 +#define AR_D2_RETRY_LIMIT 0x1088 +#define AR_D3_RETRY_LIMIT 0x108c +#define AR_D4_RETRY_LIMIT 0x1090 +#define AR_D5_RETRY_LIMIT 0x1094 +#define AR_D6_RETRY_LIMIT 0x1098 +#define AR_D7_RETRY_LIMIT 0x109c +#define AR_D8_RETRY_LIMIT 0x10a0 +#define AR_D9_RETRY_LIMIT 0x10a4 +#define AR_DRETRY_LIMIT(_i) (AR_D0_RETRY_LIMIT + ((_i)<<2)) +#define AR_D_RETRY_LIMIT_FR_SH 0x0000000F +#define AR_D_RETRY_LIMIT_FR_SH_S 0 +#define AR_D_RETRY_LIMIT_STA_SH 0x00003F00 +#define AR_D_RETRY_LIMIT_STA_SH_S 8 +#define AR_D_RETRY_LIMIT_STA_LG 0x000FC000 +#define AR_D_RETRY_LIMIT_STA_LG_S 14 +#define AR_D_RETRY_LIMIT_RESV0 0xFFF00000 + +#define AR_D0_CHNTIME 0x10c0 +#define AR_D1_CHNTIME 0x10c4 +#define AR_D2_CHNTIME 0x10c8 +#define AR_D3_CHNTIME 0x10cc +#define AR_D4_CHNTIME 0x10d0 +#define AR_D5_CHNTIME 0x10d4 +#define AR_D6_CHNTIME 0x10d8 +#define AR_D7_CHNTIME 0x10dc +#define AR_D8_CHNTIME 0x10e0 +#define AR_D9_CHNTIME 0x10e4 +#define AR_DCHNTIME(_i) (AR_D0_CHNTIME + ((_i)<<2)) +#define AR_D_CHNTIME_DUR 0x000FFFFF +#define AR_D_CHNTIME_DUR_S 0 +#define AR_D_CHNTIME_EN 0x00100000 +#define AR_D_CHNTIME_RESV0 0xFFE00000 + +#define AR_D0_MISC 0x1100 +#define AR_D1_MISC 0x1104 +#define AR_D2_MISC 0x1108 +#define AR_D3_MISC 0x110c +#define AR_D4_MISC 0x1110 +#define AR_D5_MISC 0x1114 +#define AR_D6_MISC 0x1118 +#define AR_D7_MISC 0x111c +#define AR_D8_MISC 0x1120 +#define AR_D9_MISC 0x1124 +#define AR_DMISC(_i) (AR_D0_MISC + ((_i)<<2)) +#define AR_D_MISC_BKOFF_THRESH 0x0000003F +#define AR_D_MISC_RETRY_CNT_RESET_EN 0x00000040 +#define AR_D_MISC_CW_RESET_EN 0x00000080 +#define AR_D_MISC_FRAG_WAIT_EN 0x00000100 +#define AR_D_MISC_FRAG_BKOFF_EN 0x00000200 +#define AR_D_MISC_CW_BKOFF_EN 0x00001000 +#define AR_D_MISC_VIR_COL_HANDLING 0x0000C000 +#define AR_D_MISC_VIR_COL_HANDLING_S 14 +#define AR_D_MISC_VIR_COL_HANDLING_DEFAULT 0 +#define AR_D_MISC_VIR_COL_HANDLING_IGNORE 1 +#define AR_D_MISC_BEACON_USE 0x00010000 +#define AR_D_MISC_ARB_LOCKOUT_CNTRL 0x00060000 +#define AR_D_MISC_ARB_LOCKOUT_CNTRL_S 17 +#define AR_D_MISC_ARB_LOCKOUT_CNTRL_NONE 0 +#define AR_D_MISC_ARB_LOCKOUT_CNTRL_INTRA_FR 1 +#define AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL 2 +#define AR_D_MISC_ARB_LOCKOUT_IGNORE 0x00080000 +#define AR_D_MISC_SEQ_NUM_INCR_DIS 0x00100000 +#define AR_D_MISC_POST_FR_BKOFF_DIS 0x00200000 +#define AR_D_MISC_VIT_COL_CW_BKOFF_EN 0x00400000 +#define AR_D_MISC_BLOWN_IFS_RETRY_EN 0x00800000 +#define AR_D_MISC_RESV0 0xFF000000 + +#define AR_D_SEQNUM 0x1140 + +#define AR_D_GBL_IFS_SIFS 0x1030 +#define AR_D_GBL_IFS_SIFS_M 0x0000FFFF +#define AR_D_GBL_IFS_SIFS_RESV0 0xFFFFFFFF + +#define AR_D_TXBLK_BASE 0x1038 +#define AR_D_TXBLK_WRITE_BITMASK 0x0000FFFF +#define AR_D_TXBLK_WRITE_BITMASK_S 0 +#define AR_D_TXBLK_WRITE_SLICE 0x000F0000 +#define AR_D_TXBLK_WRITE_SLICE_S 16 +#define AR_D_TXBLK_WRITE_DCU 0x00F00000 +#define AR_D_TXBLK_WRITE_DCU_S 20 +#define AR_D_TXBLK_WRITE_COMMAND 0x0F000000 +#define AR_D_TXBLK_WRITE_COMMAND_S 24 + +#define AR_D_GBL_IFS_SLOT 0x1070 +#define AR_D_GBL_IFS_SLOT_M 0x0000FFFF +#define AR_D_GBL_IFS_SLOT_RESV0 0xFFFF0000 + +#define AR_D_GBL_IFS_EIFS 0x10b0 +#define AR_D_GBL_IFS_EIFS_M 0x0000FFFF +#define AR_D_GBL_IFS_EIFS_RESV0 0xFFFF0000 + +#define AR_D_GBL_IFS_MISC 0x10f0 +#define AR_D_GBL_IFS_MISC_LFSR_SLICE_SEL 0x00000007 +#define AR_D_GBL_IFS_MISC_TURBO_MODE 0x00000008 +#define AR_D_GBL_IFS_MISC_USEC_DURATION 0x000FFC00 +#define AR_D_GBL_IFS_MISC_DCU_ARBITER_DLY 0x00300000 +#define AR_D_GBL_IFS_MISC_RANDOM_LFSR_SLICE_DIS 0x01000000 +#define AR_D_GBL_IFS_MISC_SLOT_XMIT_WIND_LEN 0x06000000 +#define AR_D_GBL_IFS_MISC_FORCE_XMIT_SLOT_BOUND 0x08000000 +#define AR_D_GBL_IFS_MISC_IGNORE_BACKOFF 0x10000000 + +#define AR_D_FPCTL 0x1230 +#define AR_D_FPCTL_DCU 0x0000000F +#define AR_D_FPCTL_DCU_S 0 +#define AR_D_FPCTL_PREFETCH_EN 0x00000010 +#define AR_D_FPCTL_BURST_PREFETCH 0x00007FE0 +#define AR_D_FPCTL_BURST_PREFETCH_S 5 + +#define AR_D_TXPSE 0x1270 +#define AR_D_TXPSE_CTRL 0x000003FF +#define AR_D_TXPSE_RESV0 0x0000FC00 +#define AR_D_TXPSE_STATUS 0x00010000 +#define AR_D_TXPSE_RESV1 0xFFFE0000 + +#define AR_D_TXSLOTMASK 0x12f0 +#define AR_D_TXSLOTMASK_NUM 0x0000000F + +#define AR_CFG_LED 0x1f04 +#define AR_CFG_SCLK_RATE_IND 0x00000003 +#define AR_CFG_SCLK_RATE_IND_S 0 +#define AR_CFG_SCLK_32MHZ 0x00000000 +#define AR_CFG_SCLK_4MHZ 0x00000001 +#define AR_CFG_SCLK_1MHZ 0x00000002 +#define AR_CFG_SCLK_32KHZ 0x00000003 +#define AR_CFG_LED_BLINK_SLOW 0x00000008 +#define AR_CFG_LED_BLINK_THRESH_SEL 0x00000070 +#define AR_CFG_LED_MODE_SEL 0x00000380 +#define AR_CFG_LED_MODE_SEL_S 7 +#define AR_CFG_LED_POWER 0x00000280 +#define AR_CFG_LED_POWER_S 7 +#define AR_CFG_LED_NETWORK 0x00000300 +#define AR_CFG_LED_NETWORK_S 7 +#define AR_CFG_LED_MODE_PROP 0x0 +#define AR_CFG_LED_MODE_RPROP 0x1 +#define AR_CFG_LED_MODE_SPLIT 0x2 +#define AR_CFG_LED_MODE_RAND 0x3 +#define AR_CFG_LED_MODE_POWER_OFF 0x4 +#define AR_CFG_LED_MODE_POWER_ON 0x5 +#define AR_CFG_LED_MODE_NETWORK_OFF 0x4 +#define AR_CFG_LED_MODE_NETWORK_ON 0x6 +#define AR_CFG_LED_ASSOC_CTL 0x00000c00 +#define AR_CFG_LED_ASSOC_CTL_S 10 +#define AR_CFG_LED_ASSOC_NONE 0x0 +#define AR_CFG_LED_ASSOC_ACTIVE 0x1 +#define AR_CFG_LED_ASSOC_PENDING 0x2 + +#define AR_CFG_LED_BLINK_SLOW 0x00000008 +#define AR_CFG_LED_BLINK_SLOW_S 3 + +#define AR_CFG_LED_BLINK_THRESH_SEL 0x00000070 +#define AR_CFG_LED_BLINK_THRESH_SEL_S 4 + +#define AR_MAC_SLEEP 0x1f00 +#define AR_MAC_SLEEP_MAC_AWAKE 0x00000000 +#define AR_MAC_SLEEP_MAC_ASLEEP 0x00000001 + +#define AR_RC 0x4000 +#define AR_RC_AHB 0x00000001 +#define AR_RC_APB 0x00000002 +#define AR_RC_HOSTIF 0x00000100 + +#define AR_WA 0x4004 + +#define AR_PM_STATE 0x4008 +#define AR_PM_STATE_PME_D3COLD_VAUX 0x00100000 + +#define AR_HOST_TIMEOUT 0x4018 +#define AR_HOST_TIMEOUT_APB_CNTR 0x0000FFFF +#define AR_HOST_TIMEOUT_APB_CNTR_S 0 +#define AR_HOST_TIMEOUT_LCL_CNTR 0xFFFF0000 +#define AR_HOST_TIMEOUT_LCL_CNTR_S 16 + +#define AR_EEPROM 0x401c +#define AR_EEPROM_ABSENT 0x00000100 +#define AR_EEPROM_CORRUPT 0x00000200 +#define AR_EEPROM_PROT_MASK 0x03FFFC00 +#define AR_EEPROM_PROT_MASK_S 10 + +#define EEPROM_PROTECT_RP_0_31 0x0001 +#define EEPROM_PROTECT_WP_0_31 0x0002 +#define EEPROM_PROTECT_RP_32_63 0x0004 +#define EEPROM_PROTECT_WP_32_63 0x0008 +#define EEPROM_PROTECT_RP_64_127 0x0010 +#define EEPROM_PROTECT_WP_64_127 0x0020 +#define EEPROM_PROTECT_RP_128_191 0x0040 +#define EEPROM_PROTECT_WP_128_191 0x0080 +#define EEPROM_PROTECT_RP_192_255 0x0100 +#define EEPROM_PROTECT_WP_192_255 0x0200 +#define EEPROM_PROTECT_RP_256_511 0x0400 +#define EEPROM_PROTECT_WP_256_511 0x0800 +#define EEPROM_PROTECT_RP_512_1023 0x1000 +#define EEPROM_PROTECT_WP_512_1023 0x2000 +#define EEPROM_PROTECT_RP_1024_2047 0x4000 +#define EEPROM_PROTECT_WP_1024_2047 0x8000 + +#define AR_SREV \ + ((AR_SREV_9100(ah)) ? 0x0600 : 0x4020) + +#define AR_SREV_ID \ + ((AR_SREV_9100(ah)) ? 0x00000FFF : 0x000000FF) +#define AR_SREV_VERSION 0x000000F0 +#define AR_SREV_VERSION_S 4 +#define AR_SREV_REVISION 0x00000007 + +#define AR_SREV_ID2 0xFFFFFFFF +#define AR_SREV_VERSION2 0xFFFC0000 +#define AR_SREV_VERSION2_S 18 +#define AR_SREV_TYPE2 0x0003F000 +#define AR_SREV_TYPE2_S 12 +#define AR_SREV_TYPE2_CHAIN 0x00001000 +#define AR_SREV_TYPE2_HOST_MODE 0x00002000 +#define AR_SREV_REVISION2 0x00000F00 +#define AR_SREV_REVISION2_S 8 + +#define AR_SREV_VERSION_5416_PCI 0xD +#define AR_SREV_VERSION_5416_PCIE 0xC +#define AR_SREV_REVISION_5416_10 0 +#define AR_SREV_REVISION_5416_20 1 +#define AR_SREV_REVISION_5416_22 2 +#define AR_SREV_VERSION_9160 0x40 +#define AR_SREV_REVISION_9160_10 0 +#define AR_SREV_REVISION_9160_11 1 +#define AR_SREV_VERSION_9280 0x80 +#define AR_SREV_REVISION_9280_10 0 +#define AR_SREV_REVISION_9280_20 1 +#define AR_SREV_REVISION_9280_21 2 +#define AR_SREV_VERSION_9285 0xC0 +#define AR_SREV_REVISION_9285_10 0 + +#define AR_SREV_9100_OR_LATER(_ah) \ + (((_ah)->ah_macVersion >= AR_SREV_VERSION_5416_PCIE)) +#define AR_SREV_5416_20_OR_LATER(_ah) \ + (((_ah)->ah_macVersion >= AR_SREV_VERSION_9160) || \ + ((_ah)->ah_macRev >= AR_SREV_REVISION_5416_20)) +#define AR_SREV_5416_22_OR_LATER(_ah) \ + (((_ah)->ah_macVersion >= AR_SREV_VERSION_9160) || \ + ((_ah)->ah_macRev >= AR_SREV_REVISION_5416_22)) +#define AR_SREV_9160(_ah) \ + (((_ah)->ah_macVersion == AR_SREV_VERSION_9160)) +#define AR_SREV_9160_10_OR_LATER(_ah) \ + (((_ah)->ah_macVersion >= AR_SREV_VERSION_9160)) +#define AR_SREV_9160_11(_ah) \ + (AR_SREV_9160(_ah) && ((_ah)->ah_macRev == AR_SREV_REVISION_9160_11)) +#define AR_SREV_9280(_ah) \ + (((_ah)->ah_macVersion == AR_SREV_VERSION_9280)) +#define AR_SREV_9280_10_OR_LATER(_ah) \ + (((_ah)->ah_macVersion >= AR_SREV_VERSION_9280)) +#define AR_SREV_9280_20(_ah) \ + (((_ah)->ah_macVersion == AR_SREV_VERSION_9280) && \ + ((_ah)->ah_macRev >= AR_SREV_REVISION_9280_20)) +#define AR_SREV_9280_20_OR_LATER(_ah) \ + (((_ah)->ah_macVersion > AR_SREV_VERSION_9280) || \ + (((_ah)->ah_macVersion == AR_SREV_VERSION_9280) && \ + ((_ah)->ah_macRev >= AR_SREV_REVISION_9280_20))) + +#define AR_SREV_9285(_ah) (((_ah)->ah_macVersion == AR_SREV_VERSION_9285)) +#define AR_SREV_9285_10_OR_LATER(_ah) \ + (((_ah)->ah_macVersion >= AR_SREV_VERSION_9285)) + +#define AR_RADIO_SREV_MAJOR 0xf0 +#define AR_RAD5133_SREV_MAJOR 0xc0 +#define AR_RAD2133_SREV_MAJOR 0xd0 +#define AR_RAD5122_SREV_MAJOR 0xe0 +#define AR_RAD2122_SREV_MAJOR 0xf0 + +#define AR_AHB_MODE 0x4024 +#define AR_AHB_EXACT_WR_EN 0x00000000 +#define AR_AHB_BUF_WR_EN 0x00000001 +#define AR_AHB_EXACT_RD_EN 0x00000000 +#define AR_AHB_CACHELINE_RD_EN 0x00000002 +#define AR_AHB_PREFETCH_RD_EN 0x00000004 +#define AR_AHB_PAGE_SIZE_1K 0x00000000 +#define AR_AHB_PAGE_SIZE_2K 0x00000008 +#define AR_AHB_PAGE_SIZE_4K 0x00000010 + +#define AR_INTR_RTC_IRQ 0x00000001 +#define AR_INTR_MAC_IRQ 0x00000002 +#define AR_INTR_EEP_PROT_ACCESS 0x00000004 +#define AR_INTR_MAC_AWAKE 0x00020000 +#define AR_INTR_MAC_ASLEEP 0x00040000 +#define AR_INTR_SPURIOUS 0xFFFFFFFF + + +#define AR_INTR_SYNC_CAUSE_CLR 0x4028 + +#define AR_INTR_SYNC_CAUSE 0x4028 + +#define AR_INTR_SYNC_ENABLE 0x402c +#define AR_INTR_SYNC_ENABLE_GPIO 0xFFFC0000 +#define AR_INTR_SYNC_ENABLE_GPIO_S 18 + +enum { + AR_INTR_SYNC_RTC_IRQ = 0x00000001, + AR_INTR_SYNC_MAC_IRQ = 0x00000002, + AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS = 0x00000004, + AR_INTR_SYNC_APB_TIMEOUT = 0x00000008, + AR_INTR_SYNC_PCI_MODE_CONFLICT = 0x00000010, + AR_INTR_SYNC_HOST1_FATAL = 0x00000020, + AR_INTR_SYNC_HOST1_PERR = 0x00000040, + AR_INTR_SYNC_TRCV_FIFO_PERR = 0x00000080, + AR_INTR_SYNC_RADM_CPL_EP = 0x00000100, + AR_INTR_SYNC_RADM_CPL_DLLP_ABORT = 0x00000200, + AR_INTR_SYNC_RADM_CPL_TLP_ABORT = 0x00000400, + AR_INTR_SYNC_RADM_CPL_ECRC_ERR = 0x00000800, + AR_INTR_SYNC_RADM_CPL_TIMEOUT = 0x00001000, + AR_INTR_SYNC_LOCAL_TIMEOUT = 0x00002000, + AR_INTR_SYNC_PM_ACCESS = 0x00004000, + AR_INTR_SYNC_MAC_AWAKE = 0x00008000, + AR_INTR_SYNC_MAC_ASLEEP = 0x00010000, + AR_INTR_SYNC_MAC_SLEEP_ACCESS = 0x00020000, + AR_INTR_SYNC_ALL = 0x0003FFFF, + + + AR_INTR_SYNC_DEFAULT = (AR_INTR_SYNC_HOST1_FATAL | + AR_INTR_SYNC_HOST1_PERR | + AR_INTR_SYNC_RADM_CPL_EP | + AR_INTR_SYNC_RADM_CPL_DLLP_ABORT | + AR_INTR_SYNC_RADM_CPL_TLP_ABORT | + AR_INTR_SYNC_RADM_CPL_ECRC_ERR | + AR_INTR_SYNC_RADM_CPL_TIMEOUT | + AR_INTR_SYNC_LOCAL_TIMEOUT | + AR_INTR_SYNC_MAC_SLEEP_ACCESS), + + AR_INTR_SYNC_SPURIOUS = 0xFFFFFFFF, + +}; + +#define AR_INTR_ASYNC_MASK 0x4030 +#define AR_INTR_ASYNC_MASK_GPIO 0xFFFC0000 +#define AR_INTR_ASYNC_MASK_GPIO_S 18 + +#define AR_INTR_SYNC_MASK 0x4034 +#define AR_INTR_SYNC_MASK_GPIO 0xFFFC0000 +#define AR_INTR_SYNC_MASK_GPIO_S 18 + +#define AR_INTR_ASYNC_CAUSE_CLR 0x4038 +#define AR_INTR_ASYNC_CAUSE 0x4038 + +#define AR_INTR_ASYNC_ENABLE 0x403c +#define AR_INTR_ASYNC_ENABLE_GPIO 0xFFFC0000 +#define AR_INTR_ASYNC_ENABLE_GPIO_S 18 + +#define AR_PCIE_SERDES 0x4040 +#define AR_PCIE_SERDES2 0x4044 +#define AR_PCIE_PM_CTRL 0x4014 +#define AR_PCIE_PM_CTRL_ENA 0x00080000 + +#define AR_NUM_GPIO 14 +#define AR928X_NUM_GPIO 10 + +#define AR_GPIO_IN_OUT 0x4048 +#define AR_GPIO_IN_VAL 0x0FFFC000 +#define AR_GPIO_IN_VAL_S 14 +#define AR928X_GPIO_IN_VAL 0x000FFC00 +#define AR928X_GPIO_IN_VAL_S 10 + +#define AR_GPIO_OE_OUT 0x404c +#define AR_GPIO_OE_OUT_DRV 0x3 +#define AR_GPIO_OE_OUT_DRV_NO 0x0 +#define AR_GPIO_OE_OUT_DRV_LOW 0x1 +#define AR_GPIO_OE_OUT_DRV_HI 0x2 +#define AR_GPIO_OE_OUT_DRV_ALL 0x3 + +#define AR_GPIO_INTR_POL 0x4050 +#define AR_GPIO_INTR_POL_VAL 0x00001FFF +#define AR_GPIO_INTR_POL_VAL_S 0 + +#define AR_GPIO_INPUT_EN_VAL 0x4054 +#define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF 0x00000080 +#define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF_S 7 +#define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB 0x00008000 +#define AR_GPIO_INPUT_EN_VAL_RFSILENT_BB_S 15 +#define AR_GPIO_RTC_RESET_OVERRIDE_ENABLE 0x00010000 +#define AR_GPIO_JTAG_DISABLE 0x00020000 + +#define AR_GPIO_INPUT_MUX1 0x4058 + +#define AR_GPIO_INPUT_MUX2 0x405c +#define AR_GPIO_INPUT_MUX2_CLK25 0x0000000f +#define AR_GPIO_INPUT_MUX2_CLK25_S 0 +#define AR_GPIO_INPUT_MUX2_RFSILENT 0x000000f0 +#define AR_GPIO_INPUT_MUX2_RFSILENT_S 4 +#define AR_GPIO_INPUT_MUX2_RTC_RESET 0x00000f00 +#define AR_GPIO_INPUT_MUX2_RTC_RESET_S 8 + +#define AR_GPIO_OUTPUT_MUX1 0x4060 +#define AR_GPIO_OUTPUT_MUX2 0x4064 +#define AR_GPIO_OUTPUT_MUX3 0x4068 + +#define AR_GPIO_OUTPUT_MUX_AS_OUTPUT 0 +#define AR_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1 +#define AR_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED 2 +#define AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED 5 +#define AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED 6 + +#define AR_INPUT_STATE 0x406c + +#define AR_EEPROM_STATUS_DATA 0x407c +#define AR_EEPROM_STATUS_DATA_VAL 0x0000ffff +#define AR_EEPROM_STATUS_DATA_VAL_S 0 +#define AR_EEPROM_STATUS_DATA_BUSY 0x00010000 +#define AR_EEPROM_STATUS_DATA_BUSY_ACCESS 0x00020000 +#define AR_EEPROM_STATUS_DATA_PROT_ACCESS 0x00040000 +#define AR_EEPROM_STATUS_DATA_ABSENT_ACCESS 0x00080000 + +#define AR_OBS 0x4080 + +#define AR_PCIE_MSI 0x4094 +#define AR_PCIE_MSI_ENABLE 0x00000001 + + +#define AR_RTC_9160_PLL_DIV 0x000003ff +#define AR_RTC_9160_PLL_DIV_S 0 +#define AR_RTC_9160_PLL_REFDIV 0x00003C00 +#define AR_RTC_9160_PLL_REFDIV_S 10 +#define AR_RTC_9160_PLL_CLKSEL 0x0000C000 +#define AR_RTC_9160_PLL_CLKSEL_S 14 + +#define AR_RTC_BASE 0x00020000 +#define AR_RTC_RC \ + (AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0000) : 0x7000 +#define AR_RTC_RC_M 0x00000003 +#define AR_RTC_RC_MAC_WARM 0x00000001 +#define AR_RTC_RC_MAC_COLD 0x00000002 +#define AR_RTC_RC_COLD_RESET 0x00000004 +#define AR_RTC_RC_WARM_RESET 0x00000008 + +#define AR_RTC_PLL_CONTROL \ + (AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0014) : 0x7014 + +#define AR_RTC_PLL_DIV 0x0000001f +#define AR_RTC_PLL_DIV_S 0 +#define AR_RTC_PLL_DIV2 0x00000020 +#define AR_RTC_PLL_REFDIV_5 0x000000c0 +#define AR_RTC_PLL_CLKSEL 0x00000300 +#define AR_RTC_PLL_CLKSEL_S 8 + + + +#define AR_RTC_RESET \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0040) : 0x7040) +#define AR_RTC_RESET_EN (0x00000001) + +#define AR_RTC_STATUS \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0044) : 0x7044) + +#define AR_RTC_STATUS_M \ + ((AR_SREV_9100(ah)) ? 0x0000003f : 0x0000000f) + +#define AR_RTC_PM_STATUS_M 0x0000000f + +#define AR_RTC_STATUS_SHUTDOWN 0x00000001 +#define AR_RTC_STATUS_ON 0x00000002 +#define AR_RTC_STATUS_SLEEP 0x00000004 +#define AR_RTC_STATUS_WAKEUP 0x00000008 + +#define AR_RTC_SLEEP_CLK \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0048) : 0x7048) +#define AR_RTC_FORCE_DERIVED_CLK 0x2 + +#define AR_RTC_FORCE_WAKE \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x004c) : 0x704c) +#define AR_RTC_FORCE_WAKE_EN 0x00000001 +#define AR_RTC_FORCE_WAKE_ON_INT 0x00000002 + + +#define AR_RTC_INTR_CAUSE \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0050) : 0x7050) + +#define AR_RTC_INTR_ENABLE \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0054) : 0x7054) + +#define AR_RTC_INTR_MASK \ + ((AR_SREV_9100(ah)) ? (AR_RTC_BASE + 0x0058) : 0x7058) + +#define AR_SEQ_MASK 0x8060 + +#define AR_AN_RF2G1_CH0 0x7810 +#define AR_AN_RF2G1_CH0_OB 0x03800000 +#define AR_AN_RF2G1_CH0_OB_S 23 +#define AR_AN_RF2G1_CH0_DB 0x1C000000 +#define AR_AN_RF2G1_CH0_DB_S 26 + +#define AR_AN_RF5G1_CH0 0x7818 +#define AR_AN_RF5G1_CH0_OB5 0x00070000 +#define AR_AN_RF5G1_CH0_OB5_S 16 +#define AR_AN_RF5G1_CH0_DB5 0x00380000 +#define AR_AN_RF5G1_CH0_DB5_S 19 + +#define AR_AN_RF2G1_CH1 0x7834 +#define AR_AN_RF2G1_CH1_OB 0x03800000 +#define AR_AN_RF2G1_CH1_OB_S 23 +#define AR_AN_RF2G1_CH1_DB 0x1C000000 +#define AR_AN_RF2G1_CH1_DB_S 26 + +#define AR_AN_RF5G1_CH1 0x783C +#define AR_AN_RF5G1_CH1_OB5 0x00070000 +#define AR_AN_RF5G1_CH1_OB5_S 16 +#define AR_AN_RF5G1_CH1_DB5 0x00380000 +#define AR_AN_RF5G1_CH1_DB5_S 19 + +#define AR_AN_TOP2 0x7894 +#define AR_AN_TOP2_XPABIAS_LVL 0xC0000000 +#define AR_AN_TOP2_XPABIAS_LVL_S 30 +#define AR_AN_TOP2_LOCALBIAS 0x00200000 +#define AR_AN_TOP2_LOCALBIAS_S 21 +#define AR_AN_TOP2_PWDCLKIND 0x00400000 +#define AR_AN_TOP2_PWDCLKIND_S 22 + +#define AR_AN_SYNTH9 0x7868 +#define AR_AN_SYNTH9_REFDIVA 0xf8000000 +#define AR_AN_SYNTH9_REFDIVA_S 27 + +#define AR_STA_ID0 0x8000 +#define AR_STA_ID1 0x8004 +#define AR_STA_ID1_SADH_MASK 0x0000FFFF +#define AR_STA_ID1_STA_AP 0x00010000 +#define AR_STA_ID1_ADHOC 0x00020000 +#define AR_STA_ID1_PWR_SAV 0x00040000 +#define AR_STA_ID1_KSRCHDIS 0x00080000 +#define AR_STA_ID1_PCF 0x00100000 +#define AR_STA_ID1_USE_DEFANT 0x00200000 +#define AR_STA_ID1_DEFANT_UPDATE 0x00400000 +#define AR_STA_ID1_RTS_USE_DEF 0x00800000 +#define AR_STA_ID1_ACKCTS_6MB 0x01000000 +#define AR_STA_ID1_BASE_RATE_11B 0x02000000 +#define AR_STA_ID1_SECTOR_SELF_GEN 0x04000000 +#define AR_STA_ID1_CRPT_MIC_ENABLE 0x08000000 +#define AR_STA_ID1_KSRCH_MODE 0x10000000 +#define AR_STA_ID1_PRESERVE_SEQNUM 0x20000000 +#define AR_STA_ID1_CBCIV_ENDIAN 0x40000000 +#define AR_STA_ID1_MCAST_KSRCH 0x80000000 + +#define AR_BSS_ID0 0x8008 +#define AR_BSS_ID1 0x800C +#define AR_BSS_ID1_U16 0x0000FFFF +#define AR_BSS_ID1_AID 0x07FF0000 +#define AR_BSS_ID1_AID_S 16 + +#define AR_BCN_RSSI_AVE 0x8010 +#define AR_BCN_RSSI_AVE_MASK 0x00000FFF + +#define AR_TIME_OUT 0x8014 +#define AR_TIME_OUT_ACK 0x00003FFF +#define AR_TIME_OUT_ACK_S 0 +#define AR_TIME_OUT_CTS 0x3FFF0000 +#define AR_TIME_OUT_CTS_S 16 + +#define AR_RSSI_THR 0x8018 +#define AR_RSSI_THR_MASK 0x000000FF +#define AR_RSSI_THR_BM_THR 0x0000FF00 +#define AR_RSSI_THR_BM_THR_S 8 +#define AR_RSSI_BCN_WEIGHT 0x1F000000 +#define AR_RSSI_BCN_WEIGHT_S 24 +#define AR_RSSI_BCN_RSSI_RST 0x20000000 + +#define AR_USEC 0x801c +#define AR_USEC_USEC 0x0000007F +#define AR_USEC_TX_LAT 0x007FC000 +#define AR_USEC_TX_LAT_S 14 +#define AR_USEC_RX_LAT 0x1F800000 +#define AR_USEC_RX_LAT_S 23 + +#define AR_RESET_TSF 0x8020 +#define AR_RESET_TSF_ONCE 0x01000000 + +#define AR_MAX_CFP_DUR 0x8038 +#define AR_CFP_VAL 0x0000FFFF + +#define AR_RX_FILTER 0x803C +#define AR_RX_FILTER_ALL 0x00000000 +#define AR_RX_UCAST 0x00000001 +#define AR_RX_MCAST 0x00000002 +#define AR_RX_BCAST 0x00000004 +#define AR_RX_CONTROL 0x00000008 +#define AR_RX_BEACON 0x00000010 +#define AR_RX_PROM 0x00000020 +#define AR_RX_PROBE_REQ 0x00000080 +#define AR_RX_MY_BEACON 0x00000200 +#define AR_RX_COMPR_BAR 0x00000400 +#define AR_RX_COMPR_BA 0x00000800 +#define AR_RX_UNCOM_BA_BAR 0x00001000 + +#define AR_MCAST_FIL0 0x8040 +#define AR_MCAST_FIL1 0x8044 + +#define AR_DIAG_SW 0x8048 +#define AR_DIAG_CACHE_ACK 0x00000001 +#define AR_DIAG_ACK_DIS 0x00000002 +#define AR_DIAG_CTS_DIS 0x00000004 +#define AR_DIAG_ENCRYPT_DIS 0x00000008 +#define AR_DIAG_DECRYPT_DIS 0x00000010 +#define AR_DIAG_RX_DIS 0x00000020 +#define AR_DIAG_LOOP_BACK 0x00000040 +#define AR_DIAG_CORR_FCS 0x00000080 +#define AR_DIAG_CHAN_INFO 0x00000100 +#define AR_DIAG_SCRAM_SEED 0x0001FE00 +#define AR_DIAG_SCRAM_SEED_S 8 +#define AR_DIAG_FRAME_NV0 0x00020000 +#define AR_DIAG_OBS_PT_SEL1 0x000C0000 +#define AR_DIAG_OBS_PT_SEL1_S 18 +#define AR_DIAG_FORCE_RX_CLEAR 0x00100000 +#define AR_DIAG_IGNORE_VIRT_CS 0x00200000 +#define AR_DIAG_FORCE_CH_IDLE_HIGH 0x00400000 +#define AR_DIAG_EIFS_CTRL_ENA 0x00800000 +#define AR_DIAG_DUAL_CHAIN_INFO 0x01000000 +#define AR_DIAG_RX_ABORT 0x02000000 +#define AR_DIAG_SATURATE_CYCLE_CNT 0x04000000 +#define AR_DIAG_OBS_PT_SEL2 0x08000000 +#define AR_DIAG_RX_CLEAR_CTL_LOW 0x10000000 +#define AR_DIAG_RX_CLEAR_EXT_LOW 0x20000000 + +#define AR_TSF_L32 0x804c +#define AR_TSF_U32 0x8050 + +#define AR_TST_ADDAC 0x8054 +#define AR_DEF_ANTENNA 0x8058 + +#define AR_AES_MUTE_MASK0 0x805c +#define AR_AES_MUTE_MASK0_FC 0x0000FFFF +#define AR_AES_MUTE_MASK0_QOS 0xFFFF0000 +#define AR_AES_MUTE_MASK0_QOS_S 16 + +#define AR_AES_MUTE_MASK1 0x8060 +#define AR_AES_MUTE_MASK1_SEQ 0x0000FFFF + +#define AR_GATED_CLKS 0x8064 +#define AR_GATED_CLKS_TX 0x00000002 +#define AR_GATED_CLKS_RX 0x00000004 +#define AR_GATED_CLKS_REG 0x00000008 + +#define AR_OBS_BUS_CTRL 0x8068 +#define AR_OBS_BUS_SEL_1 0x00040000 +#define AR_OBS_BUS_SEL_2 0x00080000 +#define AR_OBS_BUS_SEL_3 0x000C0000 +#define AR_OBS_BUS_SEL_4 0x08040000 +#define AR_OBS_BUS_SEL_5 0x08080000 + +#define AR_OBS_BUS_1 0x806c +#define AR_OBS_BUS_1_PCU 0x00000001 +#define AR_OBS_BUS_1_RX_END 0x00000002 +#define AR_OBS_BUS_1_RX_WEP 0x00000004 +#define AR_OBS_BUS_1_RX_BEACON 0x00000008 +#define AR_OBS_BUS_1_RX_FILTER 0x00000010 +#define AR_OBS_BUS_1_TX_HCF 0x00000020 +#define AR_OBS_BUS_1_QUIET_TIME 0x00000040 +#define AR_OBS_BUS_1_CHAN_IDLE 0x00000080 +#define AR_OBS_BUS_1_TX_HOLD 0x00000100 +#define AR_OBS_BUS_1_TX_FRAME 0x00000200 +#define AR_OBS_BUS_1_RX_FRAME 0x00000400 +#define AR_OBS_BUS_1_RX_CLEAR 0x00000800 +#define AR_OBS_BUS_1_WEP_STATE 0x0003F000 +#define AR_OBS_BUS_1_WEP_STATE_S 12 +#define AR_OBS_BUS_1_RX_STATE 0x01F00000 +#define AR_OBS_BUS_1_RX_STATE_S 20 +#define AR_OBS_BUS_1_TX_STATE 0x7E000000 +#define AR_OBS_BUS_1_TX_STATE_S 25 + +#define AR_LAST_TSTP 0x8080 +#define AR_NAV 0x8084 +#define AR_RTS_OK 0x8088 +#define AR_RTS_FAIL 0x808c +#define AR_ACK_FAIL 0x8090 +#define AR_FCS_FAIL 0x8094 +#define AR_BEACON_CNT 0x8098 + +#define AR_SLEEP1 0x80d4 +#define AR_SLEEP1_ASSUME_DTIM 0x00080000 +#define AR_SLEEP1_CAB_TIMEOUT 0xFFE00000 +#define AR_SLEEP1_CAB_TIMEOUT_S 21 + +#define AR_SLEEP2 0x80d8 +#define AR_SLEEP2_BEACON_TIMEOUT 0xFFE00000 +#define AR_SLEEP2_BEACON_TIMEOUT_S 21 + +#define AR_BSSMSKL 0x80e0 +#define AR_BSSMSKU 0x80e4 + +#define AR_TPC 0x80e8 +#define AR_TPC_ACK 0x0000003f +#define AR_TPC_ACK_S 0x00 +#define AR_TPC_CTS 0x00003f00 +#define AR_TPC_CTS_S 0x08 +#define AR_TPC_CHIRP 0x003f0000 +#define AR_TPC_CHIRP_S 0x16 + +#define AR_TFCNT 0x80ec +#define AR_RFCNT 0x80f0 +#define AR_RCCNT 0x80f4 +#define AR_CCCNT 0x80f8 + +#define AR_QUIET1 0x80fc +#define AR_QUIET1_NEXT_QUIET_S 0 +#define AR_QUIET1_NEXT_QUIET_M 0x0000ffff +#define AR_QUIET1_QUIET_ENABLE 0x00010000 +#define AR_QUIET1_QUIET_ACK_CTS_ENABLE 0x00020000 +#define AR_QUIET2 0x8100 +#define AR_QUIET2_QUIET_PERIOD_S 0 +#define AR_QUIET2_QUIET_PERIOD_M 0x0000ffff +#define AR_QUIET2_QUIET_DUR_S 16 +#define AR_QUIET2_QUIET_DUR 0xffff0000 + +#define AR_TSF_PARM 0x8104 +#define AR_TSF_INCREMENT_M 0x000000ff +#define AR_TSF_INCREMENT_S 0x00 + +#define AR_QOS_NO_ACK 0x8108 +#define AR_QOS_NO_ACK_TWO_BIT 0x0000000f +#define AR_QOS_NO_ACK_TWO_BIT_S 0 +#define AR_QOS_NO_ACK_BIT_OFF 0x00000070 +#define AR_QOS_NO_ACK_BIT_OFF_S 4 +#define AR_QOS_NO_ACK_BYTE_OFF 0x00000180 +#define AR_QOS_NO_ACK_BYTE_OFF_S 7 + +#define AR_PHY_ERR 0x810c + +#define AR_PHY_ERR_DCHIRP 0x00000008 +#define AR_PHY_ERR_RADAR 0x00000020 +#define AR_PHY_ERR_OFDM_TIMING 0x00020000 +#define AR_PHY_ERR_CCK_TIMING 0x02000000 + +#define AR_RXFIFO_CFG 0x8114 + + +#define AR_MIC_QOS_CONTROL 0x8118 +#define AR_MIC_QOS_SELECT 0x811c + +#define AR_PCU_MISC 0x8120 +#define AR_PCU_FORCE_BSSID_MATCH 0x00000001 +#define AR_PCU_MIC_NEW_LOC_ENA 0x00000004 +#define AR_PCU_TX_ADD_TSF 0x00000008 +#define AR_PCU_CCK_SIFS_MODE 0x00000010 +#define AR_PCU_RX_ANT_UPDT 0x00000800 +#define AR_PCU_TXOP_TBTT_LIMIT_ENA 0x00001000 +#define AR_PCU_MISS_BCN_IN_SLEEP 0x00004000 +#define AR_PCU_BUG_12306_FIX_ENA 0x00020000 +#define AR_PCU_FORCE_QUIET_COLL 0x00040000 +#define AR_PCU_TBTT_PROTECT 0x00200000 +#define AR_PCU_CLEAR_VMF 0x01000000 +#define AR_PCU_CLEAR_BA_VALID 0x04000000 + + +#define AR_FILT_OFDM 0x8124 +#define AR_FILT_OFDM_COUNT 0x00FFFFFF + +#define AR_FILT_CCK 0x8128 +#define AR_FILT_CCK_COUNT 0x00FFFFFF + +#define AR_PHY_ERR_1 0x812c +#define AR_PHY_ERR_1_COUNT 0x00FFFFFF +#define AR_PHY_ERR_MASK_1 0x8130 + +#define AR_PHY_ERR_2 0x8134 +#define AR_PHY_ERR_2_COUNT 0x00FFFFFF +#define AR_PHY_ERR_MASK_2 0x8138 + +#define AR_PHY_COUNTMAX (3 << 22) +#define AR_MIBCNT_INTRMASK (3 << 22) + +#define AR_TSF_THRESHOLD 0x813c +#define AR_TSF_THRESHOLD_VAL 0x0000FFFF + +#define AR_PHY_ERR_EIFS_MASK 8144 + +#define AR_PHY_ERR_3 0x8168 +#define AR_PHY_ERR_3_COUNT 0x00FFFFFF +#define AR_PHY_ERR_MASK_3 0x816c + +#define AR_TXSIFS 0x81d0 +#define AR_TXSIFS_TIME 0x000000FF +#define AR_TXSIFS_TX_LATENCY 0x00000F00 +#define AR_TXSIFS_TX_LATENCY_S 8 +#define AR_TXSIFS_ACK_SHIFT 0x00007000 +#define AR_TXSIFS_ACK_SHIFT_S 12 + +#define AR_TXOP_X 0x81ec +#define AR_TXOP_X_VAL 0x000000FF + + +#define AR_TXOP_0_3 0x81f0 +#define AR_TXOP_4_7 0x81f4 +#define AR_TXOP_8_11 0x81f8 +#define AR_TXOP_12_15 0x81fc + + +#define AR_NEXT_TBTT_TIMER 0x8200 +#define AR_NEXT_DMA_BEACON_ALERT 0x8204 +#define AR_NEXT_SWBA 0x8208 +#define AR_NEXT_CFP 0x8208 +#define AR_NEXT_HCF 0x820C +#define AR_NEXT_TIM 0x8210 +#define AR_NEXT_DTIM 0x8214 +#define AR_NEXT_QUIET_TIMER 0x8218 +#define AR_NEXT_NDP_TIMER 0x821C + +#define AR_BEACON_PERIOD 0x8220 +#define AR_DMA_BEACON_PERIOD 0x8224 +#define AR_SWBA_PERIOD 0x8228 +#define AR_HCF_PERIOD 0x822C +#define AR_TIM_PERIOD 0x8230 +#define AR_DTIM_PERIOD 0x8234 +#define AR_QUIET_PERIOD 0x8238 +#define AR_NDP_PERIOD 0x823C + +#define AR_TIMER_MODE 0x8240 +#define AR_TBTT_TIMER_EN 0x00000001 +#define AR_DBA_TIMER_EN 0x00000002 +#define AR_SWBA_TIMER_EN 0x00000004 +#define AR_HCF_TIMER_EN 0x00000008 +#define AR_TIM_TIMER_EN 0x00000010 +#define AR_DTIM_TIMER_EN 0x00000020 +#define AR_QUIET_TIMER_EN 0x00000040 +#define AR_NDP_TIMER_EN 0x00000080 +#define AR_TIMER_OVERFLOW_INDEX 0x00000700 +#define AR_TIMER_OVERFLOW_INDEX_S 8 +#define AR_TIMER_THRESH 0xFFFFF000 +#define AR_TIMER_THRESH_S 12 + +#define AR_SLP32_MODE 0x8244 +#define AR_SLP32_HALF_CLK_LATENCY 0x000FFFFF +#define AR_SLP32_ENA 0x00100000 +#define AR_SLP32_TSF_WRITE_STATUS 0x00200000 + +#define AR_SLP32_WAKE 0x8248 +#define AR_SLP32_WAKE_XTL_TIME 0x0000FFFF + +#define AR_SLP32_INC 0x824c +#define AR_SLP32_TST_INC 0x000FFFFF + +#define AR_SLP_CNT 0x8250 +#define AR_SLP_CYCLE_CNT 0x8254 + +#define AR_SLP_MIB_CTRL 0x8258 +#define AR_SLP_MIB_CLEAR 0x00000001 +#define AR_SLP_MIB_PENDING 0x00000002 + +#define AR_2040_MODE 0x8318 +#define AR_2040_JOINED_RX_CLEAR 0x00000001 + + +#define AR_EXTRCCNT 0x8328 + +#define AR_SELFGEN_MASK 0x832c + +#define AR_PCU_TXBUF_CTRL 0x8340 +#define AR_PCU_TXBUF_CTRL_SIZE_MASK 0x7FF +#define AR_PCU_TXBUF_CTRL_USABLE_SIZE 0x700 +#define AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE 0x380 + +#define AR_KEYTABLE_0 0x8800 +#define AR_KEYTABLE(_n) (AR_KEYTABLE_0 + ((_n)*32)) +#define AR_KEY_CACHE_SIZE 128 +#define AR_RSVD_KEYTABLE_ENTRIES 4 +#define AR_KEY_TYPE 0x00000007 +#define AR_KEYTABLE_TYPE_40 0x00000000 +#define AR_KEYTABLE_TYPE_104 0x00000001 +#define AR_KEYTABLE_TYPE_128 0x00000003 +#define AR_KEYTABLE_TYPE_TKIP 0x00000004 +#define AR_KEYTABLE_TYPE_AES 0x00000005 +#define AR_KEYTABLE_TYPE_CCM 0x00000006 +#define AR_KEYTABLE_TYPE_CLR 0x00000007 +#define AR_KEYTABLE_ANT 0x00000008 +#define AR_KEYTABLE_VALID 0x00008000 +#define AR_KEYTABLE_KEY0(_n) (AR_KEYTABLE(_n) + 0) +#define AR_KEYTABLE_KEY1(_n) (AR_KEYTABLE(_n) + 4) +#define AR_KEYTABLE_KEY2(_n) (AR_KEYTABLE(_n) + 8) +#define AR_KEYTABLE_KEY3(_n) (AR_KEYTABLE(_n) + 12) +#define AR_KEYTABLE_KEY4(_n) (AR_KEYTABLE(_n) + 16) +#define AR_KEYTABLE_TYPE(_n) (AR_KEYTABLE(_n) + 20) +#define AR_KEYTABLE_MAC0(_n) (AR_KEYTABLE(_n) + 24) +#define AR_KEYTABLE_MAC1(_n) (AR_KEYTABLE(_n) + 28) + +#endif diff --git a/drivers/net/wireless/ath9k/regd.c b/drivers/net/wireless/ath9k/regd.c new file mode 100644 index 000000000000..7b0176e3eb40 --- /dev/null +++ b/drivers/net/wireless/ath9k/regd.c @@ -0,0 +1,1031 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include "core.h" +#include "hw.h" +#include "regd.h" +#include "regd_common.h" + +static int ath9k_regd_chansort(const void *a, const void *b) +{ + const struct ath9k_channel *ca = a; + const struct ath9k_channel *cb = b; + + return (ca->channel == cb->channel) ? + (ca->channelFlags & CHAN_FLAGS) - + (cb->channelFlags & CHAN_FLAGS) : ca->channel - cb->channel; +} + +static void +ath9k_regd_sort(void *a, u32 n, u32 size, ath_hal_cmp_t *cmp) +{ + u8 *aa = a; + u8 *ai, *t; + + for (ai = aa + size; --n >= 1; ai += size) + for (t = ai; t > aa; t -= size) { + u8 *u = t - size; + if (cmp(u, t) <= 0) + break; + swap(u, t, size); + } +} + +static u16 ath9k_regd_get_eepromRD(struct ath_hal *ah) +{ + return ah->ah_currentRD & ~WORLDWIDE_ROAMING_FLAG; +} + +static bool ath9k_regd_is_chan_bm_zero(u64 *bitmask) +{ + int i; + + for (i = 0; i < BMLEN; i++) { + if (bitmask[i] != 0) + return false; + } + return true; +} + +static bool ath9k_regd_is_eeprom_valid(struct ath_hal *ah) +{ + u16 rd = ath9k_regd_get_eepromRD(ah); + int i; + + if (rd & COUNTRY_ERD_FLAG) { + u16 cc = rd & ~COUNTRY_ERD_FLAG; + for (i = 0; i < ARRAY_SIZE(allCountries); i++) + if (allCountries[i].countryCode == cc) + return true; + } else { + for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) + if (regDomainPairs[i].regDmnEnum == rd) + return true; + } + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: invalid regulatory domain/country code 0x%x\n", + __func__, rd); + return false; +} + +static bool ath9k_regd_is_fcc_midband_supported(struct ath_hal *ah) +{ + u32 regcap; + + regcap = ah->ah_caps.halRegCap; + + if (regcap & AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND) + return true; + else + return false; +} + +static bool ath9k_regd_is_ccode_valid(struct ath_hal *ah, + u16 cc) +{ + u16 rd; + int i; + + if (cc == CTRY_DEFAULT) + return true; + if (cc == CTRY_DEBUG) + return true; + + rd = ath9k_regd_get_eepromRD(ah); + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: EEPROM regdomain 0x%x\n", + __func__, rd); + + if (rd & COUNTRY_ERD_FLAG) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: EEPROM setting is country code %u\n", + __func__, rd & ~COUNTRY_ERD_FLAG); + return cc == (rd & ~COUNTRY_ERD_FLAG); + } + + for (i = 0; i < ARRAY_SIZE(allCountries); i++) { + if (cc == allCountries[i].countryCode) { +#ifdef AH_SUPPORT_11D + if ((rd & WORLD_SKU_MASK) == WORLD_SKU_PREFIX) + return true; +#endif + if (allCountries[i].regDmnEnum == rd || + rd == DEBUG_REG_DMN || rd == NO_ENUMRD) + return true; + } + } + return false; +} + +static u32 +ath9k_regd_get_wmodes_nreg(struct ath_hal *ah, + struct country_code_to_enum_rd *country, + struct regDomain *rd5GHz) +{ + u32 modesAvail; + + modesAvail = ah->ah_caps.halWirelessModes; + + if ((modesAvail & ATH9K_MODE_SEL_11G) && (!country->allow11g)) + modesAvail &= ~ATH9K_MODE_SEL_11G; + if ((modesAvail & ATH9K_MODE_SEL_11A) && + (ath9k_regd_is_chan_bm_zero(rd5GHz->chan11a))) + modesAvail &= ~ATH9K_MODE_SEL_11A; + + if ((modesAvail & ATH9K_MODE_SEL_11NG_HT20) + && (!country->allow11ng20)) + modesAvail &= ~ATH9K_MODE_SEL_11NG_HT20; + + if ((modesAvail & ATH9K_MODE_SEL_11NA_HT20) + && (!country->allow11na20)) + modesAvail &= ~ATH9K_MODE_SEL_11NA_HT20; + + if ((modesAvail & ATH9K_MODE_SEL_11NG_HT40PLUS) && + (!country->allow11ng40)) + modesAvail &= ~ATH9K_MODE_SEL_11NG_HT40PLUS; + + if ((modesAvail & ATH9K_MODE_SEL_11NG_HT40MINUS) && + (!country->allow11ng40)) + modesAvail &= ~ATH9K_MODE_SEL_11NG_HT40MINUS; + + if ((modesAvail & ATH9K_MODE_SEL_11NA_HT40PLUS) && + (!country->allow11na40)) + modesAvail &= ~ATH9K_MODE_SEL_11NA_HT40PLUS; + + if ((modesAvail & ATH9K_MODE_SEL_11NA_HT40MINUS) && + (!country->allow11na40)) + modesAvail &= ~ATH9K_MODE_SEL_11NA_HT40MINUS; + + return modesAvail; +} + +bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah) +{ + u16 rd; + + rd = ath9k_regd_get_eepromRD(ah); + + switch (rd) { + case FCC4_FCCA: + case (CTRY_UNITED_STATES_FCC49 | COUNTRY_ERD_FLAG): + return true; + case DEBUG_REG_DMN: + case NO_ENUMRD: + if (ah->ah_countryCode == CTRY_UNITED_STATES_FCC49) + return true; + break; + } + return false; +} + +static struct country_code_to_enum_rd* +ath9k_regd_find_country(u16 countryCode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(allCountries); i++) { + if (allCountries[i].countryCode == countryCode) + return &allCountries[i]; + } + return NULL; +} + +static u16 ath9k_regd_get_default_country(struct ath_hal *ah) +{ + u16 rd; + int i; + + rd = ath9k_regd_get_eepromRD(ah); + if (rd & COUNTRY_ERD_FLAG) { + struct country_code_to_enum_rd *country = NULL; + u16 cc = rd & ~COUNTRY_ERD_FLAG; + + country = ath9k_regd_find_country(cc); + if (country != NULL) + return cc; + } + + for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) + if (regDomainPairs[i].regDmnEnum == rd) { + if (regDomainPairs[i].singleCC != 0) + return regDomainPairs[i].singleCC; + else + i = ARRAY_SIZE(regDomainPairs); + } + return CTRY_DEFAULT; +} + +static bool ath9k_regd_is_valid_reg_domain(int regDmn, + struct regDomain *rd) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(regDomains); i++) { + if (regDomains[i].regDmnEnum == regDmn) { + if (rd != NULL) { + memcpy(rd, ®Domains[i], + sizeof(struct regDomain)); + } + return true; + } + } + return false; +} + +static bool ath9k_regd_is_valid_reg_domainPair(int regDmnPair) +{ + int i; + + if (regDmnPair == NO_ENUMRD) + return false; + for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) { + if (regDomainPairs[i].regDmnEnum == regDmnPair) + return true; + } + return false; +} + +static bool +ath9k_regd_get_wmode_regdomain(struct ath_hal *ah, int regDmn, + u16 channelFlag, struct regDomain *rd) +{ + int i, found; + u64 flags = NO_REQ; + struct reg_dmn_pair_mapping *regPair = NULL; + int regOrg; + + regOrg = regDmn; + if (regDmn == CTRY_DEFAULT) { + u16 rdnum; + rdnum = ath9k_regd_get_eepromRD(ah); + + if (!(rdnum & COUNTRY_ERD_FLAG)) { + if (ath9k_regd_is_valid_reg_domain(rdnum, NULL) || + ath9k_regd_is_valid_reg_domainPair(rdnum)) { + regDmn = rdnum; + } + } + } + + if ((regDmn & MULTI_DOMAIN_MASK) == 0) { + for (i = 0, found = 0; + (i < ARRAY_SIZE(regDomainPairs)) && (!found); i++) { + if (regDomainPairs[i].regDmnEnum == regDmn) { + regPair = ®DomainPairs[i]; + found = 1; + } + } + if (!found) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: Failed to find reg domain pair %u\n", + __func__, regDmn); + return false; + } + if (!(channelFlag & CHANNEL_2GHZ)) { + regDmn = regPair->regDmn5GHz; + flags = regPair->flags5GHz; + } + if (channelFlag & CHANNEL_2GHZ) { + regDmn = regPair->regDmn2GHz; + flags = regPair->flags2GHz; + } + } + + found = ath9k_regd_is_valid_reg_domain(regDmn, rd); + if (!found) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: Failed to find unitary reg domain %u\n", + __func__, regDmn); + return false; + } else { + rd->pscan &= regPair->pscanMask; + if (((regOrg & MULTI_DOMAIN_MASK) == 0) && + (flags != NO_REQ)) { + rd->flags = flags; + } + + rd->flags &= (channelFlag & CHANNEL_2GHZ) ? + REG_DOMAIN_2GHZ_MASK : REG_DOMAIN_5GHZ_MASK; + return true; + } +} + +static bool ath9k_regd_is_bit_set(int bit, u64 *bitmask) +{ + int byteOffset, bitnum; + u64 val; + + byteOffset = bit / 64; + bitnum = bit - byteOffset * 64; + val = ((u64) 1) << bitnum; + if (bitmask[byteOffset] & val) + return true; + else + return false; +} + +static void +ath9k_regd_add_reg_classid(u8 *regclassids, u32 maxregids, + u32 *nregids, u8 regclassid) +{ + int i; + + if (regclassid == 0) + return; + + for (i = 0; i < maxregids; i++) { + if (regclassids[i] == regclassid) + return; + if (regclassids[i] == 0) + break; + } + + if (i == maxregids) + return; + else { + regclassids[i] = regclassid; + *nregids += 1; + } + + return; +} + +static bool +ath9k_regd_get_eeprom_reg_ext_bits(struct ath_hal *ah, + enum reg_ext_bitmap bit) +{ + return (ah->ah_currentRDExt & (1 << bit)) ? true : false; +} + +#ifdef ATH_NF_PER_CHAN + +static void ath9k_regd_init_rf_buffer(struct ath9k_channel *ichans, + int nchans) +{ + int i, j, next; + + for (next = 0; next < nchans; next++) { + for (i = 0; i < NUM_NF_READINGS; i++) { + ichans[next].nfCalHist[i].currIndex = 0; + ichans[next].nfCalHist[i].privNF = + AR_PHY_CCA_MAX_GOOD_VALUE; + ichans[next].nfCalHist[i].invalidNFcount = + AR_PHY_CCA_FILTERWINDOW_LENGTH; + for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) { + ichans[next].nfCalHist[i].nfCalBuffer[j] = + AR_PHY_CCA_MAX_GOOD_VALUE; + } + } + } +} +#endif + +static int ath9k_regd_is_chan_present(struct ath_hal *ah, + u16 c) +{ + int i; + + for (i = 0; i < 150; i++) { + if (!ah->ah_channels[i].channel) + return -1; + else if (ah->ah_channels[i].channel == c) + return i; + } + + return -1; +} + +static bool +ath9k_regd_add_channel(struct ath_hal *ah, + u16 c, + u16 c_lo, + u16 c_hi, + u16 maxChan, + u8 ctl, + int pos, + struct regDomain rd5GHz, + struct RegDmnFreqBand *fband, + struct regDomain *rd, + const struct cmode *cm, + struct ath9k_channel *ichans, + bool enableExtendedChannels) +{ + struct ath9k_channel *chan; + int ret; + u32 channelFlags = 0; + u8 privFlags = 0; + + if (!(c_lo <= c && c <= c_hi)) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: c %u out of range [%u..%u]\n", + __func__, c, c_lo, c_hi); + return false; + } + if ((fband->channelBW == CHANNEL_HALF_BW) && + !ah->ah_caps.halChanHalfRate) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: Skipping %u half rate channel\n", + __func__, c); + return false; + } + + if ((fband->channelBW == CHANNEL_QUARTER_BW) && + !ah->ah_caps.halChanQuarterRate) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: Skipping %u quarter rate channel\n", + __func__, c); + return false; + } + + if (((c + fband->channelSep) / 2) > (maxChan + HALF_MAXCHANBW)) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: c %u > maxChan %u\n", + __func__, c, maxChan); + return false; + } + + if ((fband->usePassScan & IS_ECM_CHAN) && !enableExtendedChannels) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "Skipping ecm channel\n"); + return false; + } + + if ((rd->flags & NO_HOSTAP) && (ah->ah_opmode == ATH9K_M_HOSTAP)) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "Skipping HOSTAP channel\n"); + return false; + } + + if (IS_HT40_MODE(cm->mode) && + !(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_FCC_DFS_HT40)) && + (fband->useDfs) && + (rd->conformanceTestLimit != MKK)) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "Skipping HT40 channel (en_fcc_dfs_ht40 = 0)\n"); + return false; + } + + if (IS_HT40_MODE(cm->mode) && + !(ath9k_regd_get_eeprom_reg_ext_bits(ah, + REG_EXT_JAPAN_NONDFS_HT40)) && + !(fband->useDfs) && (rd->conformanceTestLimit == MKK)) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "Skipping HT40 channel (en_jap_ht40 = 0)\n"); + return false; + } + + if (IS_HT40_MODE(cm->mode) && + !(ath9k_regd_get_eeprom_reg_ext_bits(ah, REG_EXT_JAPAN_DFS_HT40)) && + (fband->useDfs) && + (rd->conformanceTestLimit == MKK)) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "Skipping HT40 channel (en_jap_dfs_ht40 = 0)\n"); + return false; + } + + /* Calculate channel flags */ + + channelFlags = cm->flags; + + switch (fband->channelBW) { + case CHANNEL_HALF_BW: + channelFlags |= CHANNEL_HALF; + break; + case CHANNEL_QUARTER_BW: + channelFlags |= CHANNEL_QUARTER; + break; + } + + if (fband->usePassScan & rd->pscan) + channelFlags |= CHANNEL_PASSIVE; + else + channelFlags &= ~CHANNEL_PASSIVE; + if (fband->useDfs & rd->dfsMask) + privFlags = CHANNEL_DFS; + else + privFlags = 0; + if (rd->flags & LIMIT_FRAME_4MS) + privFlags |= CHANNEL_4MS_LIMIT; + if (privFlags & CHANNEL_DFS) + privFlags |= CHANNEL_DISALLOW_ADHOC; + if (rd->flags & ADHOC_PER_11D) + privFlags |= CHANNEL_PER_11D_ADHOC; + + if (channelFlags & CHANNEL_PASSIVE) { + if ((c < 2412) || (c > 2462)) { + if (rd5GHz.regDmnEnum == MKK1 || + rd5GHz.regDmnEnum == MKK2) { + u32 regcap = ah->ah_caps.halRegCap; + if (!(regcap & + (AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN | + AR_EEPROM_EEREGCAP_EN_KK_U2 | + AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) && + isUNII1OddChan(c)) { + channelFlags &= ~CHANNEL_PASSIVE; + } else { + privFlags |= CHANNEL_DISALLOW_ADHOC; + } + } else { + privFlags |= CHANNEL_DISALLOW_ADHOC; + } + } + } + + if (cm->mode & (ATH9K_MODE_SEL_11A | + ATH9K_MODE_SEL_11NA_HT20 | + ATH9K_MODE_SEL_11NA_HT40PLUS | + ATH9K_MODE_SEL_11NA_HT40MINUS)) { + if (rd->flags & (ADHOC_NO_11A | DISALLOW_ADHOC_11A)) + privFlags |= CHANNEL_DISALLOW_ADHOC; + } + + /* Fill in channel details */ + + ret = ath9k_regd_is_chan_present(ah, c); + if (ret == -1) { + chan = &ah->ah_channels[pos]; + chan->channel = c; + chan->maxRegTxPower = fband->powerDfs; + chan->antennaMax = fband->antennaMax; + chan->regDmnFlags = rd->flags; + chan->maxTxPower = AR5416_MAX_RATE_POWER; + chan->minTxPower = AR5416_MAX_RATE_POWER; + chan->channelFlags = channelFlags; + chan->privFlags = privFlags; + } else { + chan = &ah->ah_channels[ret]; + chan->channelFlags |= channelFlags; + chan->privFlags |= privFlags; + } + + /* Set CTLs */ + + if ((cm->flags & CHANNEL_ALL) == CHANNEL_A) + chan->conformanceTestLimit[0] = ctl; + else if ((cm->flags & CHANNEL_ALL) == CHANNEL_B) + chan->conformanceTestLimit[1] = ctl; + else if ((cm->flags & CHANNEL_ALL) == CHANNEL_G) + chan->conformanceTestLimit[2] = ctl; + + return (ret == -1) ? true : false; +} + +static bool ath9k_regd_japan_check(struct ath_hal *ah, + int b, + struct regDomain *rd5GHz) +{ + bool skipband = false; + int i; + u32 regcap; + + for (i = 0; i < ARRAY_SIZE(j_bandcheck); i++) { + if (j_bandcheck[i].freqbandbit == b) { + regcap = ah->ah_caps.halRegCap; + if ((j_bandcheck[i].eepromflagtocheck & regcap) == 0) { + skipband = true; + } else if ((regcap & AR_EEPROM_EEREGCAP_EN_KK_U2) || + (regcap & AR_EEPROM_EEREGCAP_EN_KK_MIDBAND)) { + rd5GHz->dfsMask |= DFS_MKK4; + rd5GHz->pscan |= PSCAN_MKK3; + } + break; + } + } + + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: Skipping %d freq band\n", + __func__, j_bandcheck[i].freqbandbit); + + return skipband; +} + +bool +ath9k_regd_init_channels(struct ath_hal *ah, + u32 maxchans, + u32 *nchans, u8 *regclassids, + u32 maxregids, u32 *nregids, u16 cc, + u32 modeSelect, bool enableOutdoor, + bool enableExtendedChannels) +{ + u32 modesAvail; + u16 maxChan = 7000; + struct country_code_to_enum_rd *country = NULL; + struct regDomain rd5GHz, rd2GHz; + const struct cmode *cm; + struct ath9k_channel *ichans = &ah->ah_channels[0]; + int next = 0, b; + u8 ctl; + int regdmn; + u16 chanSep; + + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: cc %u mode 0x%x%s%s\n", + __func__, cc, modeSelect, + enableOutdoor ? " Enable outdoor" : " ", + enableExtendedChannels ? " Enable ecm" : ""); + + if (!ath9k_regd_is_ccode_valid(ah, cc)) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: invalid country code %d\n", __func__, cc); + return false; + } + + if (!ath9k_regd_is_eeprom_valid(ah)) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: invalid EEPROM contents\n", __func__); + return false; + } + + ah->ah_countryCode = ath9k_regd_get_default_country(ah); + + if (ah->ah_countryCode == CTRY_DEFAULT) { + ah->ah_countryCode = cc & COUNTRY_CODE_MASK; + if ((ah->ah_countryCode == CTRY_DEFAULT) && + (ath9k_regd_get_eepromRD(ah) == CTRY_DEFAULT)) { + ah->ah_countryCode = CTRY_UNITED_STATES; + } + } + +#ifdef AH_SUPPORT_11D + if (ah->ah_countryCode == CTRY_DEFAULT) { + regdmn = ath9k_regd_get_eepromRD(ah); + country = NULL; + } else { +#endif + country = ath9k_regd_find_country(ah->ah_countryCode); + if (country == NULL) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "Country is NULL!!!!, cc= %d\n", + ah->ah_countryCode); + return false; + } else { + regdmn = country->regDmnEnum; +#ifdef AH_SUPPORT_11D + if (((ath9k_regd_get_eepromRD(ah) & + WORLD_SKU_MASK) == WORLD_SKU_PREFIX) && + (cc == CTRY_UNITED_STATES)) { + if (!isWwrSKU_NoMidband(ah) + && ath9k_regd_is_fcc_midband_supported(ah)) + regdmn = FCC3_FCCA; + else + regdmn = FCC1_FCCA; + } +#endif + } +#ifdef AH_SUPPORT_11D + } +#endif + if (!ath9k_regd_get_wmode_regdomain(ah, + regdmn, + ~CHANNEL_2GHZ, + &rd5GHz)) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: couldn't find unitary " + "5GHz reg domain for country %u\n", + __func__, ah->ah_countryCode); + return false; + } + if (!ath9k_regd_get_wmode_regdomain(ah, + regdmn, + CHANNEL_2GHZ, + &rd2GHz)) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: couldn't find unitary 2GHz " + "reg domain for country %u\n", + __func__, ah->ah_countryCode); + return false; + } + + if (!isWwrSKU(ah) && ((rd5GHz.regDmnEnum == FCC1) || + (rd5GHz.regDmnEnum == FCC2))) { + if (ath9k_regd_is_fcc_midband_supported(ah)) { + if (!ath9k_regd_get_wmode_regdomain(ah, + FCC3_FCCA, + ~CHANNEL_2GHZ, + &rd5GHz)) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: couldn't find unitary 5GHz " + "reg domain for country %u\n", + __func__, ah->ah_countryCode); + return false; + } + } + } + + if (country == NULL) { + modesAvail = ah->ah_caps.halWirelessModes; + } else { + modesAvail = ath9k_regd_get_wmodes_nreg(ah, country, &rd5GHz); + if (!enableOutdoor) + maxChan = country->outdoorChanStart; + } + + next = 0; + + if (maxchans > ARRAY_SIZE(ah->ah_channels)) + maxchans = ARRAY_SIZE(ah->ah_channels); + + for (cm = modes; cm < &modes[ARRAY_SIZE(modes)]; cm++) { + u16 c, c_hi, c_lo; + u64 *channelBM = NULL; + struct regDomain *rd = NULL; + struct RegDmnFreqBand *fband = NULL, *freqs; + int8_t low_adj = 0, hi_adj = 0; + + if ((cm->mode & modeSelect) == 0) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: skip mode 0x%x flags 0x%x\n", + __func__, cm->mode, cm->flags); + continue; + } + if ((cm->mode & modesAvail) == 0) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: !avail mode 0x%x (0x%x) flags 0x%x\n", + __func__, modesAvail, cm->mode, + cm->flags); + continue; + } + if (!ath9k_get_channel_edges(ah, cm->flags, &c_lo, &c_hi)) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: channels 0x%x not supported " + "by hardware\n", + __func__, cm->flags); + continue; + } + + switch (cm->mode) { + case ATH9K_MODE_SEL_11A: + case ATH9K_MODE_SEL_11NA_HT20: + case ATH9K_MODE_SEL_11NA_HT40PLUS: + case ATH9K_MODE_SEL_11NA_HT40MINUS: + rd = &rd5GHz; + channelBM = rd->chan11a; + freqs = ®Dmn5GhzFreq[0]; + ctl = rd->conformanceTestLimit; + break; + case ATH9K_MODE_SEL_11B: + rd = &rd2GHz; + channelBM = rd->chan11b; + freqs = ®Dmn2GhzFreq[0]; + ctl = rd->conformanceTestLimit | CTL_11B; + break; + case ATH9K_MODE_SEL_11G: + case ATH9K_MODE_SEL_11NG_HT20: + case ATH9K_MODE_SEL_11NG_HT40PLUS: + case ATH9K_MODE_SEL_11NG_HT40MINUS: + rd = &rd2GHz; + channelBM = rd->chan11g; + freqs = ®Dmn2Ghz11gFreq[0]; + ctl = rd->conformanceTestLimit | CTL_11G; + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: Unknown HAL mode 0x%x\n", __func__, + cm->mode); + continue; + } + + if (ath9k_regd_is_chan_bm_zero(channelBM)) + continue; + + if ((cm->mode == ATH9K_MODE_SEL_11NA_HT40PLUS) || + (cm->mode == ATH9K_MODE_SEL_11NG_HT40PLUS)) { + hi_adj = -20; + } + + if ((cm->mode == ATH9K_MODE_SEL_11NA_HT40MINUS) || + (cm->mode == ATH9K_MODE_SEL_11NG_HT40MINUS)) { + low_adj = 20; + } + + /* XXX: Add a helper here instead */ + for (b = 0; b < 64 * BMLEN; b++) { + if (ath9k_regd_is_bit_set(b, channelBM)) { + fband = &freqs[b]; + if (rd5GHz.regDmnEnum == MKK1 + || rd5GHz.regDmnEnum == MKK2) { + if (ath9k_regd_japan_check(ah, + b, + &rd5GHz)) + continue; + } + + ath9k_regd_add_reg_classid(regclassids, + maxregids, + nregids, + fband-> + regClassId); + + if (IS_HT40_MODE(cm->mode) && (rd == &rd5GHz)) { + chanSep = 40; + if (fband->lowChannel == 5280) + low_adj += 20; + + if (fband->lowChannel == 5170) + continue; + } else + chanSep = fband->channelSep; + + for (c = fband->lowChannel + low_adj; + ((c <= (fband->highChannel + hi_adj)) && + (c >= (fband->lowChannel + low_adj))); + c += chanSep) { + if (next >= maxchans) { + DPRINTF(ah->ah_sc, + ATH_DBG_REGULATORY, + "%s: too many channels " + "for channel table\n", + __func__); + goto done; + } + if (ath9k_regd_add_channel(ah, + c, c_lo, c_hi, + maxChan, ctl, + next, + rd5GHz, + fband, rd, cm, + ichans, + enableExtendedChannels)) + next++; + } + if (IS_HT40_MODE(cm->mode) && + (fband->lowChannel == 5280)) { + low_adj -= 20; + } + } + } + } +done: + if (next != 0) { + int i; + + if (next > ARRAY_SIZE(ah->ah_channels)) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: too many channels %u; truncating to %u\n", + __func__, next, + (int) ARRAY_SIZE(ah->ah_channels)); + next = ARRAY_SIZE(ah->ah_channels); + } +#ifdef ATH_NF_PER_CHAN + ath9k_regd_init_rf_buffer(ichans, next); +#endif + ath9k_regd_sort(ichans, next, + sizeof(struct ath9k_channel), + ath9k_regd_chansort); + + ah->ah_nchan = next; + + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "Channel list:\n"); + for (i = 0; i < next; i++) { + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "chan: %d flags: 0x%x\n", + ah->ah_channels[i].channel, + ah->ah_channels[i].channelFlags); + } + } + *nchans = next; + + ah->ah_countryCode = ah->ah_countryCode; + + ah->ah_currentRDInUse = regdmn; + ah->ah_currentRD5G = rd5GHz.regDmnEnum; + ah->ah_currentRD2G = rd2GHz.regDmnEnum; + if (country == NULL) { + ah->ah_iso[0] = 0; + ah->ah_iso[1] = 0; + } else { + ah->ah_iso[0] = country->isoName[0]; + ah->ah_iso[1] = country->isoName[1]; + } + + return next != 0; +} + +struct ath9k_channel* +ath9k_regd_check_channel(struct ath_hal *ah, + const struct ath9k_channel *c) +{ + struct ath9k_channel *base, *cc; + + int flags = c->channelFlags & CHAN_FLAGS; + int n, lim; + + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: channel %u/0x%x (0x%x) requested\n", __func__, + c->channel, c->channelFlags, flags); + + cc = ah->ah_curchan; + if (cc != NULL && cc->channel == c->channel && + (cc->channelFlags & CHAN_FLAGS) == flags) { + if ((cc->privFlags & CHANNEL_INTERFERENCE) && + (cc->privFlags & CHANNEL_DFS)) + return NULL; + else + return cc; + } + + base = ah->ah_channels; + n = ah->ah_nchan; + + for (lim = n; lim != 0; lim >>= 1) { + int d; + cc = &base[lim >> 1]; + d = c->channel - cc->channel; + if (d == 0) { + if ((cc->channelFlags & CHAN_FLAGS) == flags) { + if ((cc->privFlags & CHANNEL_INTERFERENCE) && + (cc->privFlags & CHANNEL_DFS)) + return NULL; + else + return cc; + } + d = flags - (cc->channelFlags & CHAN_FLAGS); + } + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, + "%s: channel %u/0x%x d %d\n", __func__, + cc->channel, cc->channelFlags, d); + if (d > 0) { + base = cc + 1; + lim--; + } + } + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: no match for %u/0x%x\n", + __func__, c->channel, c->channelFlags); + return NULL; +} + +u32 +ath9k_regd_get_antenna_allowed(struct ath_hal *ah, + struct ath9k_channel *chan) +{ + struct ath9k_channel *ichan = NULL; + + ichan = ath9k_regd_check_channel(ah, chan); + if (!ichan) + return 0; + + return ichan->antennaMax; +} + +u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan) +{ + u32 ctl = NO_CTL; + struct ath9k_channel *ichan; + + if (ah->ah_countryCode == CTRY_DEFAULT && isWwrSKU(ah)) { + if (IS_CHAN_B(chan)) + ctl = SD_NO_CTL | CTL_11B; + else if (IS_CHAN_G(chan)) + ctl = SD_NO_CTL | CTL_11G; + else + ctl = SD_NO_CTL | CTL_11A; + } else { + ichan = ath9k_regd_check_channel(ah, chan); + if (ichan != NULL) { + /* FIXME */ + if (IS_CHAN_A(ichan)) + ctl = ichan->conformanceTestLimit[0]; + else if (IS_CHAN_B(ichan)) + ctl = ichan->conformanceTestLimit[1]; + else if (IS_CHAN_G(ichan)) + ctl = ichan->conformanceTestLimit[2]; + + if (IS_CHAN_G(chan) && (ctl & 0xf) == CTL_11B) + ctl = (ctl & ~0xf) | CTL_11G; + } + } + return ctl; +} + +void ath9k_regd_get_current_country(struct ath_hal *ah, + struct ath9k_country_entry *ctry) +{ + u16 rd = ath9k_regd_get_eepromRD(ah); + + ctry->isMultidomain = false; + if (rd == CTRY_DEFAULT) + ctry->isMultidomain = true; + else if (!(rd & COUNTRY_ERD_FLAG)) + ctry->isMultidomain = isWwrSKU(ah); + + ctry->countryCode = ah->ah_countryCode; + ctry->regDmnEnum = ah->ah_currentRD; + ctry->regDmn5G = ah->ah_currentRD5G; + ctry->regDmn2G = ah->ah_currentRD2G; + ctry->iso[0] = ah->ah_iso[0]; + ctry->iso[1] = ah->ah_iso[1]; + ctry->iso[2] = ah->ah_iso[2]; +} diff --git a/drivers/net/wireless/ath9k/regd.h b/drivers/net/wireless/ath9k/regd.h new file mode 100644 index 000000000000..ae77496bfde3 --- /dev/null +++ b/drivers/net/wireless/ath9k/regd.h @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef REGD_H +#define REGD_H + +#include "ath9k.h" + +#define BMLEN 2 +#define BMZERO {(u64) 0, (u64) 0} + +#define BM(_fa, _fb, _fc, _fd, _fe, _ff, _fg, _fh, _fi, _fj, _fk, _fl) \ + {((((_fa >= 0) && (_fa < 64)) ? \ + (((u64) 1) << _fa) : (u64) 0) | \ + (((_fb >= 0) && (_fb < 64)) ? \ + (((u64) 1) << _fb) : (u64) 0) | \ + (((_fc >= 0) && (_fc < 64)) ? \ + (((u64) 1) << _fc) : (u64) 0) | \ + (((_fd >= 0) && (_fd < 64)) ? \ + (((u64) 1) << _fd) : (u64) 0) | \ + (((_fe >= 0) && (_fe < 64)) ? \ + (((u64) 1) << _fe) : (u64) 0) | \ + (((_ff >= 0) && (_ff < 64)) ? \ + (((u64) 1) << _ff) : (u64) 0) | \ + (((_fg >= 0) && (_fg < 64)) ? \ + (((u64) 1) << _fg) : (u64) 0) | \ + (((_fh >= 0) && (_fh < 64)) ? \ + (((u64) 1) << _fh) : (u64) 0) | \ + (((_fi >= 0) && (_fi < 64)) ? \ + (((u64) 1) << _fi) : (u64) 0) | \ + (((_fj >= 0) && (_fj < 64)) ? \ + (((u64) 1) << _fj) : (u64) 0) | \ + (((_fk >= 0) && (_fk < 64)) ? \ + (((u64) 1) << _fk) : (u64) 0) | \ + (((_fl >= 0) && (_fl < 64)) ? \ + (((u64) 1) << _fl) : (u64) 0) | \ + ((((_fa > 63) && (_fa < 128)) ? \ + (((u64) 1) << (_fa - 64)) : (u64) 0) | \ + (((_fb > 63) && (_fb < 128)) ? \ + (((u64) 1) << (_fb - 64)) : (u64) 0) | \ + (((_fc > 63) && (_fc < 128)) ? \ + (((u64) 1) << (_fc - 64)) : (u64) 0) | \ + (((_fd > 63) && (_fd < 128)) ? \ + (((u64) 1) << (_fd - 64)) : (u64) 0) | \ + (((_fe > 63) && (_fe < 128)) ? \ + (((u64) 1) << (_fe - 64)) : (u64) 0) | \ + (((_ff > 63) && (_ff < 128)) ? \ + (((u64) 1) << (_ff - 64)) : (u64) 0) | \ + (((_fg > 63) && (_fg < 128)) ? \ + (((u64) 1) << (_fg - 64)) : (u64) 0) | \ + (((_fh > 63) && (_fh < 128)) ? \ + (((u64) 1) << (_fh - 64)) : (u64) 0) | \ + (((_fi > 63) && (_fi < 128)) ? \ + (((u64) 1) << (_fi - 64)) : (u64) 0) | \ + (((_fj > 63) && (_fj < 128)) ? \ + (((u64) 1) << (_fj - 64)) : (u64) 0) | \ + (((_fk > 63) && (_fk < 128)) ? \ + (((u64) 1) << (_fk - 64)) : (u64) 0) | \ + (((_fl > 63) && (_fl < 128)) ? \ + (((u64) 1) << (_fl - 64)) : (u64) 0)))} + +#define DEF_REGDMN FCC1_FCCA +#define DEF_DMN_5 FCC1 +#define DEF_DMN_2 FCCA +#define COUNTRY_ERD_FLAG 0x8000 +#define WORLDWIDE_ROAMING_FLAG 0x4000 +#define SUPER_DOMAIN_MASK 0x0fff +#define COUNTRY_CODE_MASK 0x3fff +#define CF_INTERFERENCE (CHANNEL_CW_INT | CHANNEL_RADAR_INT) +#define CHANNEL_14 (2484) +#define IS_11G_CH14(_ch,_cf) \ + (((_ch) == CHANNEL_14) && ((_cf) == CHANNEL_G)) + +#define NO_PSCAN 0x0ULL +#define PSCAN_FCC 0x0000000000000001ULL +#define PSCAN_FCC_T 0x0000000000000002ULL +#define PSCAN_ETSI 0x0000000000000004ULL +#define PSCAN_MKK1 0x0000000000000008ULL +#define PSCAN_MKK2 0x0000000000000010ULL +#define PSCAN_MKKA 0x0000000000000020ULL +#define PSCAN_MKKA_G 0x0000000000000040ULL +#define PSCAN_ETSIA 0x0000000000000080ULL +#define PSCAN_ETSIB 0x0000000000000100ULL +#define PSCAN_ETSIC 0x0000000000000200ULL +#define PSCAN_WWR 0x0000000000000400ULL +#define PSCAN_MKKA1 0x0000000000000800ULL +#define PSCAN_MKKA1_G 0x0000000000001000ULL +#define PSCAN_MKKA2 0x0000000000002000ULL +#define PSCAN_MKKA2_G 0x0000000000004000ULL +#define PSCAN_MKK3 0x0000000000008000ULL +#define PSCAN_DEFER 0x7FFFFFFFFFFFFFFFULL +#define IS_ECM_CHAN 0x8000000000000000ULL + +#define isWwrSKU(_ah) \ + (((ath9k_regd_get_eepromRD((_ah)) & WORLD_SKU_MASK) == \ + WORLD_SKU_PREFIX) || \ + (ath9k_regd_get_eepromRD(_ah) == WORLD)) + +#define isWwrSKU_NoMidband(_ah) \ + ((ath9k_regd_get_eepromRD((_ah)) == WOR3_WORLD) || \ + (ath9k_regd_get_eepromRD(_ah) == WOR4_WORLD) || \ + (ath9k_regd_get_eepromRD(_ah) == WOR5_ETSIC)) + +#define isUNII1OddChan(ch) \ + ((ch == 5170) || (ch == 5190) || (ch == 5210) || (ch == 5230)) + +#define IS_HT40_MODE(_mode) \ + (((_mode == ATH9K_MODE_SEL_11NA_HT40PLUS || \ + _mode == ATH9K_MODE_SEL_11NG_HT40PLUS || \ + _mode == ATH9K_MODE_SEL_11NA_HT40MINUS || \ + _mode == ATH9K_MODE_SEL_11NG_HT40MINUS) ? true : false)) + +#define CHAN_FLAGS (CHANNEL_ALL|CHANNEL_HALF|CHANNEL_QUARTER) + +#define swap(_a, _b, _size) { \ + u8 *s = _b; \ + int i = _size; \ + do { \ + u8 tmp = *_a; \ + *_a++ = *s; \ + *s++ = tmp; \ + } while (--i); \ + _a -= _size; \ +} + + +#define HALF_MAXCHANBW 10 + +#define MULTI_DOMAIN_MASK 0xFF00 + +#define WORLD_SKU_MASK 0x00F0 +#define WORLD_SKU_PREFIX 0x0060 + +#define CHANNEL_HALF_BW 10 +#define CHANNEL_QUARTER_BW 5 + +typedef int ath_hal_cmp_t(const void *, const void *); + +struct reg_dmn_pair_mapping { + u16 regDmnEnum; + u16 regDmn5GHz; + u16 regDmn2GHz; + u32 flags5GHz; + u32 flags2GHz; + u64 pscanMask; + u16 singleCC; +}; + +struct ccmap { + char isoName[3]; + u16 countryCode; +}; + +struct country_code_to_enum_rd { + u16 countryCode; + u16 regDmnEnum; + const char *isoName; + const char *name; + bool allow11g; + bool allow11aTurbo; + bool allow11gTurbo; + bool allow11ng20; + bool allow11ng40; + bool allow11na20; + bool allow11na40; + u16 outdoorChanStart; +}; + +struct RegDmnFreqBand { + u16 lowChannel; + u16 highChannel; + u8 powerDfs; + u8 antennaMax; + u8 channelBW; + u8 channelSep; + u64 useDfs; + u64 usePassScan; + u8 regClassId; +}; + +struct regDomain { + u16 regDmnEnum; + u8 conformanceTestLimit; + u64 dfsMask; + u64 pscan; + u32 flags; + u64 chan11a[BMLEN]; + u64 chan11a_turbo[BMLEN]; + u64 chan11a_dyn_turbo[BMLEN]; + u64 chan11b[BMLEN]; + u64 chan11g[BMLEN]; + u64 chan11g_turbo[BMLEN]; +}; + +struct cmode { + u32 mode; + u32 flags; +}; + +#define YES true +#define NO false + +struct japan_bandcheck { + u16 freqbandbit; + u32 eepromflagtocheck; +}; + +struct common_mode_power { + u16 lchan; + u16 hchan; + u8 pwrlvl; +}; + +enum CountryCode { + CTRY_ALBANIA = 8, + CTRY_ALGERIA = 12, + CTRY_ARGENTINA = 32, + CTRY_ARMENIA = 51, + CTRY_AUSTRALIA = 36, + CTRY_AUSTRIA = 40, + CTRY_AZERBAIJAN = 31, + CTRY_BAHRAIN = 48, + CTRY_BELARUS = 112, + CTRY_BELGIUM = 56, + CTRY_BELIZE = 84, + CTRY_BOLIVIA = 68, + CTRY_BOSNIA_HERZ = 70, + CTRY_BRAZIL = 76, + CTRY_BRUNEI_DARUSSALAM = 96, + CTRY_BULGARIA = 100, + CTRY_CANADA = 124, + CTRY_CHILE = 152, + CTRY_CHINA = 156, + CTRY_COLOMBIA = 170, + CTRY_COSTA_RICA = 188, + CTRY_CROATIA = 191, + CTRY_CYPRUS = 196, + CTRY_CZECH = 203, + CTRY_DENMARK = 208, + CTRY_DOMINICAN_REPUBLIC = 214, + CTRY_ECUADOR = 218, + CTRY_EGYPT = 818, + CTRY_EL_SALVADOR = 222, + CTRY_ESTONIA = 233, + CTRY_FAEROE_ISLANDS = 234, + CTRY_FINLAND = 246, + CTRY_FRANCE = 250, + CTRY_GEORGIA = 268, + CTRY_GERMANY = 276, + CTRY_GREECE = 300, + CTRY_GUATEMALA = 320, + CTRY_HONDURAS = 340, + CTRY_HONG_KONG = 344, + CTRY_HUNGARY = 348, + CTRY_ICELAND = 352, + CTRY_INDIA = 356, + CTRY_INDONESIA = 360, + CTRY_IRAN = 364, + CTRY_IRAQ = 368, + CTRY_IRELAND = 372, + CTRY_ISRAEL = 376, + CTRY_ITALY = 380, + CTRY_JAMAICA = 388, + CTRY_JAPAN = 392, + CTRY_JORDAN = 400, + CTRY_KAZAKHSTAN = 398, + CTRY_KENYA = 404, + CTRY_KOREA_NORTH = 408, + CTRY_KOREA_ROC = 410, + CTRY_KOREA_ROC2 = 411, + CTRY_KOREA_ROC3 = 412, + CTRY_KUWAIT = 414, + CTRY_LATVIA = 428, + CTRY_LEBANON = 422, + CTRY_LIBYA = 434, + CTRY_LIECHTENSTEIN = 438, + CTRY_LITHUANIA = 440, + CTRY_LUXEMBOURG = 442, + CTRY_MACAU = 446, + CTRY_MACEDONIA = 807, + CTRY_MALAYSIA = 458, + CTRY_MALTA = 470, + CTRY_MEXICO = 484, + CTRY_MONACO = 492, + CTRY_MOROCCO = 504, + CTRY_NEPAL = 524, + CTRY_NETHERLANDS = 528, + CTRY_NETHERLANDS_ANTILLES = 530, + CTRY_NEW_ZEALAND = 554, + CTRY_NICARAGUA = 558, + CTRY_NORWAY = 578, + CTRY_OMAN = 512, + CTRY_PAKISTAN = 586, + CTRY_PANAMA = 591, + CTRY_PAPUA_NEW_GUINEA = 598, + CTRY_PARAGUAY = 600, + CTRY_PERU = 604, + CTRY_PHILIPPINES = 608, + CTRY_POLAND = 616, + CTRY_PORTUGAL = 620, + CTRY_PUERTO_RICO = 630, + CTRY_QATAR = 634, + CTRY_ROMANIA = 642, + CTRY_RUSSIA = 643, + CTRY_SAUDI_ARABIA = 682, + CTRY_SERBIA_MONTENEGRO = 891, + CTRY_SINGAPORE = 702, + CTRY_SLOVAKIA = 703, + CTRY_SLOVENIA = 705, + CTRY_SOUTH_AFRICA = 710, + CTRY_SPAIN = 724, + CTRY_SRI_LANKA = 144, + CTRY_SWEDEN = 752, + CTRY_SWITZERLAND = 756, + CTRY_SYRIA = 760, + CTRY_TAIWAN = 158, + CTRY_THAILAND = 764, + CTRY_TRINIDAD_Y_TOBAGO = 780, + CTRY_TUNISIA = 788, + CTRY_TURKEY = 792, + CTRY_UAE = 784, + CTRY_UKRAINE = 804, + CTRY_UNITED_KINGDOM = 826, + CTRY_UNITED_STATES = 840, + CTRY_UNITED_STATES_FCC49 = 842, + CTRY_URUGUAY = 858, + CTRY_UZBEKISTAN = 860, + CTRY_VENEZUELA = 862, + CTRY_VIET_NAM = 704, + CTRY_YEMEN = 887, + CTRY_ZIMBABWE = 716, + CTRY_JAPAN1 = 393, + CTRY_JAPAN2 = 394, + CTRY_JAPAN3 = 395, + CTRY_JAPAN4 = 396, + CTRY_JAPAN5 = 397, + CTRY_JAPAN6 = 4006, + CTRY_JAPAN7 = 4007, + CTRY_JAPAN8 = 4008, + CTRY_JAPAN9 = 4009, + CTRY_JAPAN10 = 4010, + CTRY_JAPAN11 = 4011, + CTRY_JAPAN12 = 4012, + CTRY_JAPAN13 = 4013, + CTRY_JAPAN14 = 4014, + CTRY_JAPAN15 = 4015, + CTRY_JAPAN16 = 4016, + CTRY_JAPAN17 = 4017, + CTRY_JAPAN18 = 4018, + CTRY_JAPAN19 = 4019, + CTRY_JAPAN20 = 4020, + CTRY_JAPAN21 = 4021, + CTRY_JAPAN22 = 4022, + CTRY_JAPAN23 = 4023, + CTRY_JAPAN24 = 4024, + CTRY_JAPAN25 = 4025, + CTRY_JAPAN26 = 4026, + CTRY_JAPAN27 = 4027, + CTRY_JAPAN28 = 4028, + CTRY_JAPAN29 = 4029, + CTRY_JAPAN30 = 4030, + CTRY_JAPAN31 = 4031, + CTRY_JAPAN32 = 4032, + CTRY_JAPAN33 = 4033, + CTRY_JAPAN34 = 4034, + CTRY_JAPAN35 = 4035, + CTRY_JAPAN36 = 4036, + CTRY_JAPAN37 = 4037, + CTRY_JAPAN38 = 4038, + CTRY_JAPAN39 = 4039, + CTRY_JAPAN40 = 4040, + CTRY_JAPAN41 = 4041, + CTRY_JAPAN42 = 4042, + CTRY_JAPAN43 = 4043, + CTRY_JAPAN44 = 4044, + CTRY_JAPAN45 = 4045, + CTRY_JAPAN46 = 4046, + CTRY_JAPAN47 = 4047, + CTRY_JAPAN48 = 4048, + CTRY_JAPAN49 = 4049, + CTRY_JAPAN50 = 4050, + CTRY_JAPAN51 = 4051, + CTRY_JAPAN52 = 4052, + CTRY_JAPAN53 = 4053, + CTRY_JAPAN54 = 4054, + CTRY_JAPAN55 = 4055, + CTRY_JAPAN56 = 4056, + CTRY_JAPAN57 = 4057, + CTRY_JAPAN58 = 4058, + CTRY_JAPAN59 = 4059, + CTRY_AUSTRALIA2 = 5000, + CTRY_CANADA2 = 5001, + CTRY_BELGIUM2 = 5002 +}; + +void ath9k_regd_get_current_country(struct ath_hal *ah, + struct ath9k_country_entry *ctry); + +#endif diff --git a/drivers/net/wireless/ath9k/regd_common.h b/drivers/net/wireless/ath9k/regd_common.h new file mode 100644 index 000000000000..b3bfae4fc99c --- /dev/null +++ b/drivers/net/wireless/ath9k/regd_common.h @@ -0,0 +1,1915 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef REGD_COMMON_H +#define REGD_COMMON_H + +enum EnumRd { + NO_ENUMRD = 0x00, + NULL1_WORLD = 0x03, + NULL1_ETSIB = 0x07, + NULL1_ETSIC = 0x08, + FCC1_FCCA = 0x10, + FCC1_WORLD = 0x11, + FCC4_FCCA = 0x12, + FCC5_FCCA = 0x13, + FCC6_FCCA = 0x14, + + FCC2_FCCA = 0x20, + FCC2_WORLD = 0x21, + FCC2_ETSIC = 0x22, + FCC6_WORLD = 0x23, + FRANCE_RES = 0x31, + FCC3_FCCA = 0x3A, + FCC3_WORLD = 0x3B, + + ETSI1_WORLD = 0x37, + ETSI3_ETSIA = 0x32, + ETSI2_WORLD = 0x35, + ETSI3_WORLD = 0x36, + ETSI4_WORLD = 0x30, + ETSI4_ETSIC = 0x38, + ETSI5_WORLD = 0x39, + ETSI6_WORLD = 0x34, + ETSI_RESERVED = 0x33, + + MKK1_MKKA = 0x40, + MKK1_MKKB = 0x41, + APL4_WORLD = 0x42, + MKK2_MKKA = 0x43, + APL_RESERVED = 0x44, + APL2_WORLD = 0x45, + APL2_APLC = 0x46, + APL3_WORLD = 0x47, + MKK1_FCCA = 0x48, + APL2_APLD = 0x49, + MKK1_MKKA1 = 0x4A, + MKK1_MKKA2 = 0x4B, + MKK1_MKKC = 0x4C, + + APL3_FCCA = 0x50, + APL1_WORLD = 0x52, + APL1_FCCA = 0x53, + APL1_APLA = 0x54, + APL1_ETSIC = 0x55, + APL2_ETSIC = 0x56, + APL5_WORLD = 0x58, + APL6_WORLD = 0x5B, + APL7_FCCA = 0x5C, + APL8_WORLD = 0x5D, + APL9_WORLD = 0x5E, + + WOR0_WORLD = 0x60, + WOR1_WORLD = 0x61, + WOR2_WORLD = 0x62, + WOR3_WORLD = 0x63, + WOR4_WORLD = 0x64, + WOR5_ETSIC = 0x65, + + WOR01_WORLD = 0x66, + WOR02_WORLD = 0x67, + EU1_WORLD = 0x68, + + WOR9_WORLD = 0x69, + WORA_WORLD = 0x6A, + WORB_WORLD = 0x6B, + + MKK3_MKKB = 0x80, + MKK3_MKKA2 = 0x81, + MKK3_MKKC = 0x82, + + MKK4_MKKB = 0x83, + MKK4_MKKA2 = 0x84, + MKK4_MKKC = 0x85, + + MKK5_MKKB = 0x86, + MKK5_MKKA2 = 0x87, + MKK5_MKKC = 0x88, + + MKK6_MKKB = 0x89, + MKK6_MKKA2 = 0x8A, + MKK6_MKKC = 0x8B, + + MKK7_MKKB = 0x8C, + MKK7_MKKA2 = 0x8D, + MKK7_MKKC = 0x8E, + + MKK8_MKKB = 0x8F, + MKK8_MKKA2 = 0x90, + MKK8_MKKC = 0x91, + + MKK14_MKKA1 = 0x92, + MKK15_MKKA1 = 0x93, + + MKK10_FCCA = 0xD0, + MKK10_MKKA1 = 0xD1, + MKK10_MKKC = 0xD2, + MKK10_MKKA2 = 0xD3, + + MKK11_MKKA = 0xD4, + MKK11_FCCA = 0xD5, + MKK11_MKKA1 = 0xD6, + MKK11_MKKC = 0xD7, + MKK11_MKKA2 = 0xD8, + + MKK12_MKKA = 0xD9, + MKK12_FCCA = 0xDA, + MKK12_MKKA1 = 0xDB, + MKK12_MKKC = 0xDC, + MKK12_MKKA2 = 0xDD, + + MKK13_MKKB = 0xDE, + + MKK3_MKKA = 0xF0, + MKK3_MKKA1 = 0xF1, + MKK3_FCCA = 0xF2, + MKK4_MKKA = 0xF3, + MKK4_MKKA1 = 0xF4, + MKK4_FCCA = 0xF5, + MKK9_MKKA = 0xF6, + MKK10_MKKA = 0xF7, + MKK6_MKKA1 = 0xF8, + MKK6_FCCA = 0xF9, + MKK7_MKKA1 = 0xFA, + MKK7_FCCA = 0xFB, + MKK9_FCCA = 0xFC, + MKK9_MKKA1 = 0xFD, + MKK9_MKKC = 0xFE, + MKK9_MKKA2 = 0xFF, + + APL1 = 0x0150, + APL2 = 0x0250, + APL3 = 0x0350, + APL4 = 0x0450, + APL5 = 0x0550, + APL6 = 0x0650, + APL7 = 0x0750, + APL8 = 0x0850, + APL9 = 0x0950, + APL10 = 0x1050, + + ETSI1 = 0x0130, + ETSI2 = 0x0230, + ETSI3 = 0x0330, + ETSI4 = 0x0430, + ETSI5 = 0x0530, + ETSI6 = 0x0630, + ETSIA = 0x0A30, + ETSIB = 0x0B30, + ETSIC = 0x0C30, + + FCC1 = 0x0110, + FCC2 = 0x0120, + FCC3 = 0x0160, + FCC4 = 0x0165, + FCC5 = 0x0510, + FCC6 = 0x0610, + FCCA = 0x0A10, + + APLD = 0x0D50, + + MKK1 = 0x0140, + MKK2 = 0x0240, + MKK3 = 0x0340, + MKK4 = 0x0440, + MKK5 = 0x0540, + MKK6 = 0x0640, + MKK7 = 0x0740, + MKK8 = 0x0840, + MKK9 = 0x0940, + MKK10 = 0x0B40, + MKK11 = 0x1140, + MKK12 = 0x1240, + MKK13 = 0x0C40, + MKK14 = 0x1440, + MKK15 = 0x1540, + MKKA = 0x0A40, + MKKC = 0x0A50, + + NULL1 = 0x0198, + WORLD = 0x0199, + DEBUG_REG_DMN = 0x01ff, +}; + +enum { + FCC = 0x10, + MKK = 0x40, + ETSI = 0x30, +}; + +enum { + NO_REQ = 0x00000000, + DISALLOW_ADHOC_11A = 0x00000001, + DISALLOW_ADHOC_11A_TURB = 0x00000002, + NEED_NFC = 0x00000004, + + ADHOC_PER_11D = 0x00000008, + ADHOC_NO_11A = 0x00000010, + + PUBLIC_SAFETY_DOMAIN = 0x00000020, + LIMIT_FRAME_4MS = 0x00000040, + + NO_HOSTAP = 0x00000080, + + REQ_MASK = 0x000000FF, +}; + +#define REG_DOMAIN_2GHZ_MASK (REQ_MASK & \ + (!(ADHOC_NO_11A | DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB))) +#define REG_DOMAIN_5GHZ_MASK REQ_MASK + +static struct reg_dmn_pair_mapping regDomainPairs[] = { + {NO_ENUMRD, DEBUG_REG_DMN, DEBUG_REG_DMN, NO_REQ, NO_REQ, + PSCAN_DEFER, 0}, + {NULL1_WORLD, NULL1, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {NULL1_ETSIB, NULL1, ETSIB, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {NULL1_ETSIC, NULL1, ETSIC, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + + {FCC2_FCCA, FCC2, FCCA, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {FCC2_WORLD, FCC2, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {FCC2_ETSIC, FCC2, ETSIC, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {FCC3_FCCA, FCC3, FCCA, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {FCC3_WORLD, FCC3, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {FCC4_FCCA, FCC4, FCCA, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, + {FCC5_FCCA, FCC5, FCCA, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {FCC6_FCCA, FCC6, FCCA, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {FCC6_WORLD, FCC6, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + + {ETSI1_WORLD, ETSI1, WORLD, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, + {ETSI2_WORLD, ETSI2, WORLD, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, + {ETSI3_WORLD, ETSI3, WORLD, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, + {ETSI4_WORLD, ETSI4, WORLD, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, + {ETSI5_WORLD, ETSI5, WORLD, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, + {ETSI6_WORLD, ETSI6, WORLD, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, + + {ETSI3_ETSIA, ETSI3, WORLD, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, + {FRANCE_RES, ETSI3, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + + {FCC1_WORLD, FCC1, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {FCC1_FCCA, FCC1, FCCA, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {APL1_WORLD, APL1, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {APL2_WORLD, APL2, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {APL3_WORLD, APL3, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {APL4_WORLD, APL4, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {APL5_WORLD, APL5, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {APL6_WORLD, APL6, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {APL8_WORLD, APL8, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {APL9_WORLD, APL9, WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + + {APL3_FCCA, APL3, FCCA, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {APL1_ETSIC, APL1, ETSIC, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {APL2_ETSIC, APL2, ETSIC, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {APL2_APLD, APL2, APLD, NO_REQ, NO_REQ, PSCAN_DEFER,}, + + {MKK1_MKKA, MKK1, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKKA, CTRY_JAPAN}, + {MKK1_MKKB, MKK1, MKKA, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC | + LIMIT_FRAME_4MS, NEED_NFC, PSCAN_MKK1 | PSCAN_MKKA | PSCAN_MKKA_G, + CTRY_JAPAN1}, + {MKK1_FCCA, MKK1, FCCA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1, CTRY_JAPAN2}, + {MKK1_MKKA1, MKK1, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN4}, + {MKK1_MKKA2, MKK1, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN5}, + {MKK1_MKKC, MKK1, MKKC, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1, CTRY_JAPAN6}, + + {MKK2_MKKA, MKK2, MKKA, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC | + LIMIT_FRAME_4MS, NEED_NFC, PSCAN_MKK2 | PSCAN_MKKA | PSCAN_MKKA_G, + CTRY_JAPAN3}, + + {MKK3_MKKA, MKK3, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKKA, CTRY_JAPAN25}, + {MKK3_MKKB, MKK3, MKKA, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC | + LIMIT_FRAME_4MS, NEED_NFC, PSCAN_MKKA | PSCAN_MKKA_G, + CTRY_JAPAN7}, + {MKK3_MKKA1, MKK3, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN26}, + {MKK3_MKKA2, MKK3, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN8}, + {MKK3_MKKC, MKK3, MKKC, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + NO_PSCAN, CTRY_JAPAN9}, + {MKK3_FCCA, MKK3, FCCA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + NO_PSCAN, CTRY_JAPAN27}, + + {MKK4_MKKA, MKK4, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK3, CTRY_JAPAN36}, + {MKK4_MKKB, MKK4, MKKA, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC | + LIMIT_FRAME_4MS, NEED_NFC, PSCAN_MKK3 | PSCAN_MKKA | PSCAN_MKKA_G, + CTRY_JAPAN10}, + {MKK4_MKKA1, MKK4, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK3 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN28}, + {MKK4_MKKA2, MKK4, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK3 | PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN11}, + {MKK4_MKKC, MKK4, MKKC, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK3, CTRY_JAPAN12}, + {MKK4_FCCA, MKK4, FCCA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK3, CTRY_JAPAN29}, + + {MKK5_MKKB, MKK5, MKKA, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC | + LIMIT_FRAME_4MS, NEED_NFC, PSCAN_MKK3 | PSCAN_MKKA | PSCAN_MKKA_G, + CTRY_JAPAN13}, + {MKK5_MKKA2, MKK5, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK3 | PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN14}, + {MKK5_MKKC, MKK5, MKKC, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK3, CTRY_JAPAN15}, + + {MKK6_MKKB, MKK6, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKKA | PSCAN_MKKA_G, CTRY_JAPAN16}, + {MKK6_MKKA1, MKK6, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN30}, + {MKK6_MKKA2, MKK6, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN17}, + {MKK6_MKKC, MKK6, MKKC, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1, CTRY_JAPAN18}, + {MKK6_FCCA, MKK6, FCCA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + NO_PSCAN, CTRY_JAPAN31}, + + {MKK7_MKKB, MKK7, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA | PSCAN_MKKA_G, + CTRY_JAPAN19}, + {MKK7_MKKA1, MKK7, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN32}, + {MKK7_MKKA2, MKK7, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA2 | PSCAN_MKKA2_G, + CTRY_JAPAN20}, + {MKK7_MKKC, MKK7, MKKC, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKK3, CTRY_JAPAN21}, + {MKK7_FCCA, MKK7, FCCA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKK3, CTRY_JAPAN33}, + + {MKK8_MKKB, MKK8, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA | PSCAN_MKKA_G, + CTRY_JAPAN22}, + {MKK8_MKKA2, MKK8, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA2 | PSCAN_MKKA2_G, + CTRY_JAPAN23}, + {MKK8_MKKC, MKK8, MKKC, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKK3, CTRY_JAPAN24}, + + {MKK9_MKKA, MKK9, MKKA, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC | + LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK2 | PSCAN_MKK3 | PSCAN_MKKA | PSCAN_MKKA_G, + CTRY_JAPAN34}, + {MKK9_FCCA, MKK9, FCCA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + NO_PSCAN, CTRY_JAPAN37}, + {MKK9_MKKA1, MKK9, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN38}, + {MKK9_MKKA2, MKK9, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN40}, + {MKK9_MKKC, MKK9, MKKC, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + NO_PSCAN, CTRY_JAPAN39}, + + {MKK10_MKKA, MKK10, MKKA, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC | + LIMIT_FRAME_4MS, NEED_NFC, PSCAN_MKK2 | PSCAN_MKK3, CTRY_JAPAN35}, + {MKK10_FCCA, MKK10, FCCA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + NO_PSCAN, CTRY_JAPAN41}, + {MKK10_MKKA1, MKK10, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN42}, + {MKK10_MKKA2, MKK10, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN44}, + {MKK10_MKKC, MKK10, MKKC, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + NO_PSCAN, CTRY_JAPAN43}, + + {MKK11_MKKA, MKK11, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK3, CTRY_JAPAN45}, + {MKK11_FCCA, MKK11, FCCA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK3, CTRY_JAPAN46}, + {MKK11_MKKA1, MKK11, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK3 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN47}, + {MKK11_MKKA2, MKK11, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK3 | PSCAN_MKKA2 | PSCAN_MKKA2_G, CTRY_JAPAN49}, + {MKK11_MKKC, MKK11, MKKC, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK3, CTRY_JAPAN48}, + + {MKK12_MKKA, MKK12, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKK3, CTRY_JAPAN50}, + {MKK12_FCCA, MKK12, FCCA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKK3, CTRY_JAPAN51}, + {MKK12_MKKA1, MKK12, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA1 | PSCAN_MKKA1_G, + CTRY_JAPAN52}, + {MKK12_MKKA2, MKK12, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA2 | PSCAN_MKKA2_G, + CTRY_JAPAN54}, + {MKK12_MKKC, MKK12, MKKC, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKK3, CTRY_JAPAN53}, + + {MKK13_MKKB, MKK13, MKKA, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB | NEED_NFC | + LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKK3 | PSCAN_MKKA | PSCAN_MKKA_G, + CTRY_JAPAN57}, + + {MKK14_MKKA1, MKK14, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN58}, + {MKK15_MKKA1, MKK15, MKKA, + DISALLOW_ADHOC_11A_TURB | NEED_NFC | LIMIT_FRAME_4MS, NEED_NFC, + PSCAN_MKK1 | PSCAN_MKKA1 | PSCAN_MKKA1_G, CTRY_JAPAN59}, + + {WOR0_WORLD, WOR0_WORLD, WOR0_WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, + 0}, + {WOR1_WORLD, WOR1_WORLD, WOR1_WORLD, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, + {WOR2_WORLD, WOR2_WORLD, WOR2_WORLD, DISALLOW_ADHOC_11A_TURB, + NO_REQ, PSCAN_DEFER, 0}, + {WOR3_WORLD, WOR3_WORLD, WOR3_WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, + 0}, + {WOR4_WORLD, WOR4_WORLD, WOR4_WORLD, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, + {WOR5_ETSIC, WOR5_ETSIC, WOR5_ETSIC, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, + {WOR01_WORLD, WOR01_WORLD, WOR01_WORLD, NO_REQ, NO_REQ, + PSCAN_DEFER, 0}, + {WOR02_WORLD, WOR02_WORLD, WOR02_WORLD, NO_REQ, NO_REQ, + PSCAN_DEFER, 0}, + {EU1_WORLD, EU1_WORLD, EU1_WORLD, NO_REQ, NO_REQ, PSCAN_DEFER, 0}, + {WOR9_WORLD, WOR9_WORLD, WOR9_WORLD, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, + {WORA_WORLD, WORA_WORLD, WORA_WORLD, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, + {WORB_WORLD, WORB_WORLD, WORB_WORLD, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, NO_REQ, PSCAN_DEFER, + 0}, +}; + +#define NO_INTERSECT_REQ 0xFFFFFFFF +#define NO_UNION_REQ 0 + +static struct country_code_to_enum_rd allCountries[] = { + {CTRY_DEBUG, NO_ENUMRD, "DB", "DEBUG", YES, YES, YES, YES, YES, + YES, YES, 7000}, + {CTRY_DEFAULT, DEF_REGDMN, "NA", "NO_COUNTRY_SET", YES, YES, YES, + YES, YES, YES, YES, 7000}, + {CTRY_ALBANIA, NULL1_WORLD, "AL", "ALBANIA", YES, NO, YES, YES, NO, + NO, NO, 7000}, + {CTRY_ALGERIA, NULL1_WORLD, "DZ", "ALGERIA", YES, NO, YES, YES, NO, + NO, NO, 7000}, + {CTRY_ARGENTINA, APL3_WORLD, "AR", "ARGENTINA", YES, NO, NO, YES, + NO, YES, NO, 7000}, + {CTRY_ARMENIA, ETSI4_WORLD, "AM", "ARMENIA", YES, NO, YES, YES, + YES, NO, NO, 7000}, + {CTRY_AUSTRALIA, FCC2_WORLD, "AU", "AUSTRALIA", YES, YES, YES, YES, + YES, YES, YES, 7000}, + {CTRY_AUSTRALIA2, FCC6_WORLD, "AU", "AUSTRALIA2", YES, YES, YES, + YES, YES, YES, YES, 7000}, + {CTRY_AUSTRIA, ETSI1_WORLD, "AT", "AUSTRIA", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_AZERBAIJAN, ETSI4_WORLD, "AZ", "AZERBAIJAN", YES, YES, YES, + YES, YES, YES, YES, 7000}, + {CTRY_BAHRAIN, APL6_WORLD, "BH", "BAHRAIN", YES, NO, YES, YES, YES, + YES, NO, 7000}, + {CTRY_BELARUS, ETSI1_WORLD, "BY", "BELARUS", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_BELGIUM, ETSI1_WORLD, "BE", "BELGIUM", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_BELGIUM2, ETSI4_WORLD, "BL", "BELGIUM", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_BELIZE, APL1_ETSIC, "BZ", "BELIZE", YES, YES, YES, YES, YES, + YES, YES, 7000}, + {CTRY_BOLIVIA, APL1_ETSIC, "BO", "BOLVIA", YES, YES, YES, YES, YES, + YES, YES, 7000}, + {CTRY_BOSNIA_HERZ, ETSI1_WORLD, "BA", "BOSNIA_HERZGOWINA", YES, NO, + YES, YES, YES, YES, NO, 7000}, + {CTRY_BRAZIL, FCC3_WORLD, "BR", "BRAZIL", YES, NO, NO, YES, NO, + YES, NO, 7000}, + {CTRY_BRUNEI_DARUSSALAM, APL1_WORLD, "BN", "BRUNEI DARUSSALAM", + YES, YES, YES, YES, YES, YES, YES, 7000}, + {CTRY_BULGARIA, ETSI6_WORLD, "BG", "BULGARIA", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_CANADA, FCC2_FCCA, "CA", "CANADA", YES, YES, YES, YES, YES, + YES, YES, 7000}, + {CTRY_CANADA2, FCC6_FCCA, "CA", "CANADA2", YES, YES, YES, YES, YES, + YES, YES, 7000}, + {CTRY_CHILE, APL6_WORLD, "CL", "CHILE", YES, YES, YES, YES, YES, + YES, YES, 7000}, + {CTRY_CHINA, APL1_WORLD, "CN", "CHINA", YES, YES, YES, YES, YES, + YES, YES, 7000}, + {CTRY_COLOMBIA, FCC1_FCCA, "CO", "COLOMBIA", YES, NO, YES, YES, + YES, YES, NO, 7000}, + {CTRY_COSTA_RICA, FCC1_WORLD, "CR", "COSTA RICA", YES, NO, YES, + YES, YES, YES, NO, 7000}, + {CTRY_CROATIA, ETSI3_WORLD, "HR", "CROATIA", YES, NO, YES, YES, + YES, YES, NO, 7000}, + {CTRY_CYPRUS, ETSI1_WORLD, "CY", "CYPRUS", YES, YES, YES, YES, YES, + YES, YES, 7000}, + {CTRY_CZECH, ETSI3_WORLD, "CZ", "CZECH REPUBLIC", YES, NO, YES, + YES, YES, YES, YES, 7000}, + {CTRY_DENMARK, ETSI1_WORLD, "DK", "DENMARK", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_DOMINICAN_REPUBLIC, FCC1_FCCA, "DO", "DOMINICAN REPUBLIC", + YES, YES, YES, YES, YES, YES, YES, 7000}, + {CTRY_ECUADOR, FCC1_WORLD, "EC", "ECUADOR", YES, NO, NO, YES, YES, + YES, NO, 7000}, + {CTRY_EGYPT, ETSI3_WORLD, "EG", "EGYPT", YES, NO, YES, YES, YES, + YES, NO, 7000}, + {CTRY_EL_SALVADOR, FCC1_WORLD, "SV", "EL SALVADOR", YES, NO, YES, + YES, YES, YES, NO, 7000}, + {CTRY_ESTONIA, ETSI1_WORLD, "EE", "ESTONIA", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_FINLAND, ETSI1_WORLD, "FI", "FINLAND", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_FRANCE, ETSI1_WORLD, "FR", "FRANCE", YES, NO, YES, YES, YES, + YES, YES, 7000}, + {CTRY_GEORGIA, ETSI4_WORLD, "GE", "GEORGIA", YES, YES, YES, YES, + YES, YES, YES, 7000}, + {CTRY_GERMANY, ETSI1_WORLD, "DE", "GERMANY", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_GREECE, ETSI1_WORLD, "GR", "GREECE", YES, NO, YES, YES, YES, + YES, YES, 7000}, + {CTRY_GUATEMALA, FCC1_FCCA, "GT", "GUATEMALA", YES, YES, YES, YES, + YES, YES, YES, 7000}, + {CTRY_HONDURAS, NULL1_WORLD, "HN", "HONDURAS", YES, NO, YES, YES, + YES, NO, NO, 7000}, + {CTRY_HONG_KONG, FCC2_WORLD, "HK", "HONG KONG", YES, YES, YES, YES, + YES, YES, YES, 7000}, + {CTRY_HUNGARY, ETSI1_WORLD, "HU", "HUNGARY", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_ICELAND, ETSI1_WORLD, "IS", "ICELAND", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_INDIA, APL6_WORLD, "IN", "INDIA", YES, NO, YES, YES, YES, + YES, NO, 7000}, + {CTRY_INDONESIA, APL1_WORLD, "ID", "INDONESIA", YES, NO, YES, YES, + YES, YES, NO, 7000}, + {CTRY_IRAN, APL1_WORLD, "IR", "IRAN", YES, YES, YES, YES, YES, YES, + YES, 7000}, + {CTRY_IRELAND, ETSI1_WORLD, "IE", "IRELAND", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_ISRAEL, NULL1_WORLD, "IL", "ISRAEL", YES, NO, YES, YES, YES, + NO, NO, 7000}, + {CTRY_ITALY, ETSI1_WORLD, "IT", "ITALY", YES, NO, YES, YES, YES, + YES, YES, 7000}, + {CTRY_JAMAICA, ETSI1_WORLD, "JM", "JAMAICA", YES, NO, YES, YES, + YES, YES, YES, 7000}, + + {CTRY_JAPAN, MKK1_MKKA, "JP", "JAPAN", YES, NO, NO, YES, YES, YES, + YES, 7000}, + {CTRY_JAPAN1, MKK1_MKKB, "JP", "JAPAN1", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN2, MKK1_FCCA, "JP", "JAPAN2", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN3, MKK2_MKKA, "JP", "JAPAN3", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN4, MKK1_MKKA1, "JP", "JAPAN4", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN5, MKK1_MKKA2, "JP", "JAPAN5", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN6, MKK1_MKKC, "JP", "JAPAN6", YES, NO, NO, YES, YES, + YES, YES, 7000}, + + {CTRY_JAPAN7, MKK3_MKKB, "JP", "JAPAN7", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN8, MKK3_MKKA2, "JP", "JAPAN8", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN9, MKK3_MKKC, "JP", "JAPAN9", YES, NO, NO, YES, YES, + YES, YES, 7000}, + + {CTRY_JAPAN10, MKK4_MKKB, "JP", "JAPAN10", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN11, MKK4_MKKA2, "JP", "JAPAN11", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN12, MKK4_MKKC, "JP", "JAPAN12", YES, NO, NO, YES, YES, + YES, YES, 7000}, + + {CTRY_JAPAN13, MKK5_MKKB, "JP", "JAPAN13", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN14, MKK5_MKKA2, "JP", "JAPAN14", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN15, MKK5_MKKC, "JP", "JAPAN15", YES, NO, NO, YES, YES, + YES, YES, 7000}, + + {CTRY_JAPAN16, MKK6_MKKB, "JP", "JAPAN16", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN17, MKK6_MKKA2, "JP", "JAPAN17", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN18, MKK6_MKKC, "JP", "JAPAN18", YES, NO, NO, YES, YES, + YES, YES, 7000}, + + {CTRY_JAPAN19, MKK7_MKKB, "JP", "JAPAN19", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN20, MKK7_MKKA2, "JP", "JAPAN20", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN21, MKK7_MKKC, "JP", "JAPAN21", YES, NO, NO, YES, YES, + YES, YES, 7000}, + + {CTRY_JAPAN22, MKK8_MKKB, "JP", "JAPAN22", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN23, MKK8_MKKA2, "JP", "JAPAN23", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN24, MKK8_MKKC, "JP", "JAPAN24", YES, NO, NO, YES, YES, + YES, YES, 7000}, + + {CTRY_JAPAN25, MKK3_MKKA, "JP", "JAPAN25", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN26, MKK3_MKKA1, "JP", "JAPAN26", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN27, MKK3_FCCA, "JP", "JAPAN27", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN28, MKK4_MKKA1, "JP", "JAPAN28", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN29, MKK4_FCCA, "JP", "JAPAN29", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN30, MKK6_MKKA1, "JP", "JAPAN30", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN31, MKK6_FCCA, "JP", "JAPAN31", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN32, MKK7_MKKA1, "JP", "JAPAN32", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN33, MKK7_FCCA, "JP", "JAPAN33", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN34, MKK9_MKKA, "JP", "JAPAN34", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN35, MKK10_MKKA, "JP", "JAPAN35", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN36, MKK4_MKKA, "JP", "JAPAN36", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN37, MKK9_FCCA, "JP", "JAPAN37", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN38, MKK9_MKKA1, "JP", "JAPAN38", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN39, MKK9_MKKC, "JP", "JAPAN39", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN40, MKK9_MKKA2, "JP", "JAPAN40", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN41, MKK10_FCCA, "JP", "JAPAN41", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN42, MKK10_MKKA1, "JP", "JAPAN42", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN43, MKK10_MKKC, "JP", "JAPAN43", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN44, MKK10_MKKA2, "JP", "JAPAN44", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN45, MKK11_MKKA, "JP", "JAPAN45", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN46, MKK11_FCCA, "JP", "JAPAN46", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN47, MKK11_MKKA1, "JP", "JAPAN47", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN48, MKK11_MKKC, "JP", "JAPAN48", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN49, MKK11_MKKA2, "JP", "JAPAN49", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN50, MKK12_MKKA, "JP", "JAPAN50", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN51, MKK12_FCCA, "JP", "JAPAN51", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN52, MKK12_MKKA1, "JP", "JAPAN52", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN53, MKK12_MKKC, "JP", "JAPAN53", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN54, MKK12_MKKA2, "JP", "JAPAN54", YES, NO, NO, YES, YES, + YES, YES, 7000}, + + {CTRY_JAPAN57, MKK13_MKKB, "JP", "JAPAN57", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN58, MKK14_MKKA1, "JP", "JAPAN58", YES, NO, NO, YES, YES, + YES, YES, 7000}, + {CTRY_JAPAN59, MKK15_MKKA1, "JP", "JAPAN59", YES, NO, NO, YES, YES, + YES, YES, 7000}, + + {CTRY_JORDAN, ETSI2_WORLD, "JO", "JORDAN", YES, NO, YES, YES, YES, + YES, NO, 7000}, + {CTRY_KAZAKHSTAN, NULL1_WORLD, "KZ", "KAZAKHSTAN", YES, NO, YES, + YES, YES, NO, NO, 7000}, + {CTRY_KOREA_NORTH, APL9_WORLD, "KP", "NORTH KOREA", YES, NO, NO, + YES, YES, YES, YES, 7000}, + {CTRY_KOREA_ROC, APL9_WORLD, "KR", "KOREA REPUBLIC", YES, NO, NO, + YES, NO, YES, NO, 7000}, + {CTRY_KOREA_ROC2, APL2_WORLD, "K2", "KOREA REPUBLIC2", YES, NO, NO, + YES, NO, YES, NO, 7000}, + {CTRY_KOREA_ROC3, APL9_WORLD, "K3", "KOREA REPUBLIC3", YES, NO, NO, + YES, NO, YES, NO, 7000}, + {CTRY_KUWAIT, NULL1_WORLD, "KW", "KUWAIT", YES, NO, YES, YES, YES, + NO, NO, 7000}, + {CTRY_LATVIA, ETSI1_WORLD, "LV", "LATVIA", YES, NO, YES, YES, YES, + YES, YES, 7000}, + {CTRY_LEBANON, NULL1_WORLD, "LB", "LEBANON", YES, NO, YES, YES, + YES, NO, NO, 7000}, + {CTRY_LIECHTENSTEIN, ETSI1_WORLD, "LI", "LIECHTENSTEIN", YES, NO, + YES, YES, YES, YES, YES, 7000}, + {CTRY_LITHUANIA, ETSI1_WORLD, "LT", "LITHUANIA", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_LUXEMBOURG, ETSI1_WORLD, "LU", "LUXEMBOURG", YES, NO, YES, + YES, YES, YES, YES, 7000}, + {CTRY_MACAU, FCC2_WORLD, "MO", "MACAU", YES, YES, YES, YES, YES, + YES, YES, 7000}, + {CTRY_MACEDONIA, NULL1_WORLD, "MK", "MACEDONIA", YES, NO, YES, YES, + YES, NO, NO, 7000}, + {CTRY_MALAYSIA, APL8_WORLD, "MY", "MALAYSIA", YES, NO, NO, YES, NO, + YES, NO, 7000}, + {CTRY_MALTA, ETSI1_WORLD, "MT", "MALTA", YES, NO, YES, YES, YES, + YES, YES, 7000}, + {CTRY_MEXICO, FCC1_FCCA, "MX", "MEXICO", YES, YES, YES, YES, YES, + YES, YES, 7000}, + {CTRY_MONACO, ETSI4_WORLD, "MC", "MONACO", YES, YES, YES, YES, YES, + YES, YES, 7000}, + {CTRY_MOROCCO, NULL1_WORLD, "MA", "MOROCCO", YES, NO, YES, YES, + YES, NO, NO, 7000}, + {CTRY_NEPAL, APL1_WORLD, "NP", "NEPAL", YES, NO, YES, YES, YES, + YES, YES, 7000}, + {CTRY_NETHERLANDS, ETSI1_WORLD, "NL", "NETHERLANDS", YES, NO, YES, + YES, YES, YES, YES, 7000}, + {CTRY_NETHERLANDS_ANTILLES, ETSI1_WORLD, "AN", + "NETHERLANDS-ANTILLES", YES, NO, YES, YES, YES, YES, YES, 7000}, + {CTRY_NEW_ZEALAND, FCC2_ETSIC, "NZ", "NEW ZEALAND", YES, NO, YES, + YES, YES, YES, NO, 7000}, + {CTRY_NORWAY, ETSI1_WORLD, "NO", "NORWAY", YES, NO, YES, YES, YES, + YES, YES, 7000}, + {CTRY_OMAN, APL6_WORLD, "OM", "OMAN", YES, NO, YES, YES, YES, YES, + NO, 7000}, + {CTRY_PAKISTAN, NULL1_WORLD, "PK", "PAKISTAN", YES, NO, YES, YES, + YES, NO, NO, 7000}, + {CTRY_PANAMA, FCC1_FCCA, "PA", "PANAMA", YES, YES, YES, YES, YES, + YES, YES, 7000}, + {CTRY_PAPUA_NEW_GUINEA, FCC1_WORLD, "PG", "PAPUA NEW GUINEA", YES, + YES, YES, YES, YES, YES, YES, 7000}, + {CTRY_PERU, APL1_WORLD, "PE", "PERU", YES, NO, YES, YES, YES, YES, + NO, 7000}, + {CTRY_PHILIPPINES, APL1_WORLD, "PH", "PHILIPPINES", YES, YES, YES, + YES, YES, YES, YES, 7000}, + {CTRY_POLAND, ETSI1_WORLD, "PL", "POLAND", YES, NO, YES, YES, YES, + YES, YES, 7000}, + {CTRY_PORTUGAL, ETSI1_WORLD, "PT", "PORTUGAL", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_PUERTO_RICO, FCC1_FCCA, "PR", "PUERTO RICO", YES, YES, YES, + YES, YES, YES, YES, 7000}, + {CTRY_QATAR, NULL1_WORLD, "QA", "QATAR", YES, NO, YES, YES, YES, + NO, NO, 7000}, + {CTRY_ROMANIA, NULL1_WORLD, "RO", "ROMANIA", YES, NO, YES, YES, + YES, NO, NO, 7000}, + {CTRY_RUSSIA, NULL1_WORLD, "RU", "RUSSIA", YES, NO, YES, YES, YES, + NO, NO, 7000}, + {CTRY_SAUDI_ARABIA, NULL1_WORLD, "SA", "SAUDI ARABIA", YES, NO, + YES, YES, YES, NO, NO, 7000}, + {CTRY_SERBIA_MONTENEGRO, ETSI1_WORLD, "CS", "SERBIA & MONTENEGRO", + YES, NO, YES, YES, YES, YES, YES, 7000}, + {CTRY_SINGAPORE, APL6_WORLD, "SG", "SINGAPORE", YES, YES, YES, YES, + YES, YES, YES, 7000}, + {CTRY_SLOVAKIA, ETSI1_WORLD, "SK", "SLOVAK REPUBLIC", YES, NO, YES, + YES, YES, YES, YES, 7000}, + {CTRY_SLOVENIA, ETSI1_WORLD, "SI", "SLOVENIA", YES, NO, YES, YES, + YES, YES, YES, 7000}, + {CTRY_SOUTH_AFRICA, FCC3_WORLD, "ZA", "SOUTH AFRICA", YES, NO, YES, + YES, YES, YES, NO, 7000}, + {CTRY_SPAIN, ETSI1_WORLD, "ES", "SPAIN", YES, NO, YES, YES, YES, + YES, YES, 7000}, + {CTRY_SRI_LANKA, FCC3_WORLD, "LK", "SRI LANKA", YES, NO, YES, YES, + YES, YES, NO, 7000}, + {CTRY_SWEDEN, ETSI1_WORLD, "SE", "SWEDEN", YES, NO, YES, YES, YES, + YES, YES, 7000}, + {CTRY_SWITZERLAND, ETSI1_WORLD, "CH", "SWITZERLAND", YES, NO, YES, + YES, YES, YES, YES, 7000}, + {CTRY_SYRIA, NULL1_WORLD, "SY", "SYRIA", YES, NO, YES, YES, YES, + NO, NO, 7000}, + {CTRY_TAIWAN, APL3_FCCA, "TW", "TAIWAN", YES, YES, YES, YES, YES, + YES, YES, 7000}, + {CTRY_THAILAND, NULL1_WORLD, "TH", "THAILAND", YES, NO, YES, YES, + YES, NO, NO, 7000}, + {CTRY_TRINIDAD_Y_TOBAGO, ETSI4_WORLD, "TT", "TRINIDAD & TOBAGO", + YES, NO, YES, YES, YES, YES, NO, 7000}, + {CTRY_TUNISIA, ETSI3_WORLD, "TN", "TUNISIA", YES, NO, YES, YES, + YES, YES, NO, 7000}, + {CTRY_TURKEY, ETSI3_WORLD, "TR", "TURKEY", YES, NO, YES, YES, YES, + YES, NO, 7000}, + {CTRY_UKRAINE, NULL1_WORLD, "UA", "UKRAINE", YES, NO, YES, YES, + YES, NO, NO, 7000}, + {CTRY_UAE, NULL1_WORLD, "AE", "UNITED ARAB EMIRATES", YES, NO, YES, + YES, YES, NO, NO, 7000}, + {CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB", "UNITED KINGDOM", YES, NO, + YES, YES, YES, YES, YES, 7000}, + {CTRY_UNITED_STATES, FCC3_FCCA, "US", "UNITED STATES", YES, YES, + YES, YES, YES, YES, YES, 5825}, + {CTRY_UNITED_STATES_FCC49, FCC4_FCCA, "PS", + "UNITED STATES (PUBLIC SAFETY)", YES, YES, YES, YES, YES, YES, + YES, 7000}, + {CTRY_URUGUAY, APL2_WORLD, "UY", "URUGUAY", YES, NO, YES, YES, YES, + YES, NO, 7000}, + {CTRY_UZBEKISTAN, FCC3_FCCA, "UZ", "UZBEKISTAN", YES, YES, YES, + YES, YES, YES, YES, 7000}, + {CTRY_VENEZUELA, APL2_ETSIC, "VE", "VENEZUELA", YES, NO, YES, YES, + YES, YES, NO, 7000}, + {CTRY_VIET_NAM, NULL1_WORLD, "VN", "VIET NAM", YES, NO, YES, YES, + YES, NO, NO, 7000}, + {CTRY_YEMEN, NULL1_WORLD, "YE", "YEMEN", YES, NO, YES, YES, YES, + NO, NO, 7000}, + {CTRY_ZIMBABWE, NULL1_WORLD, "ZW", "ZIMBABWE", YES, NO, YES, YES, + YES, NO, NO, 7000} +}; + +enum { + NO_DFS = 0x0000000000000000ULL, + DFS_FCC3 = 0x0000000000000001ULL, + DFS_ETSI = 0x0000000000000002ULL, + DFS_MKK4 = 0x0000000000000004ULL, +}; + +enum { + F1_4915_4925, + F1_4935_4945, + F1_4920_4980, + F1_4942_4987, + F1_4945_4985, + F1_4950_4980, + F1_5035_5040, + F1_5040_5080, + F1_5055_5055, + + F1_5120_5240, + + F1_5170_5230, + F2_5170_5230, + + F1_5180_5240, + F2_5180_5240, + F3_5180_5240, + F4_5180_5240, + F5_5180_5240, + F6_5180_5240, + F7_5180_5240, + F8_5180_5240, + + F1_5180_5320, + + F1_5240_5280, + + F1_5260_5280, + + F1_5260_5320, + F2_5260_5320, + F3_5260_5320, + F4_5260_5320, + F5_5260_5320, + F6_5260_5320, + + F1_5260_5700, + + F1_5280_5320, + + F1_5500_5580, + + F1_5500_5620, + + F1_5500_5700, + F2_5500_5700, + F3_5500_5700, + F4_5500_5700, + F5_5500_5700, + + F1_5660_5700, + + F1_5745_5805, + F2_5745_5805, + F3_5745_5805, + + F1_5745_5825, + F2_5745_5825, + F3_5745_5825, + F4_5745_5825, + F5_5745_5825, + F6_5745_5825, + + W1_4920_4980, + W1_5040_5080, + W1_5170_5230, + W1_5180_5240, + W1_5260_5320, + W1_5745_5825, + W1_5500_5700, + A_DEMO_ALL_CHANNELS +}; + +static struct RegDmnFreqBand regDmn5GhzFreq[] = { + {4915, 4925, 23, 0, 10, 5, NO_DFS, PSCAN_MKK2, 16}, + {4935, 4945, 23, 0, 10, 5, NO_DFS, PSCAN_MKK2, 16}, + {4920, 4980, 23, 0, 20, 20, NO_DFS, PSCAN_MKK2, 7}, + {4942, 4987, 27, 6, 5, 5, NO_DFS, PSCAN_FCC, 0}, + {4945, 4985, 30, 6, 10, 5, NO_DFS, PSCAN_FCC, 0}, + {4950, 4980, 33, 6, 20, 5, NO_DFS, PSCAN_FCC, 0}, + {5035, 5040, 23, 0, 10, 5, NO_DFS, PSCAN_MKK2, 12}, + {5040, 5080, 23, 0, 20, 20, NO_DFS, PSCAN_MKK2, 2}, + {5055, 5055, 23, 0, 10, 5, NO_DFS, PSCAN_MKK2, 12}, + + {5120, 5240, 5, 6, 20, 20, NO_DFS, NO_PSCAN, 0}, + + {5170, 5230, 23, 0, 20, 20, NO_DFS, PSCAN_MKK1 | PSCAN_MKK2, 1}, + {5170, 5230, 20, 0, 20, 20, NO_DFS, PSCAN_MKK1 | PSCAN_MKK2, 1}, + + {5180, 5240, 15, 0, 20, 20, NO_DFS, PSCAN_FCC | PSCAN_ETSI, 0}, + {5180, 5240, 17, 6, 20, 20, NO_DFS, NO_PSCAN, 1}, + {5180, 5240, 18, 0, 20, 20, NO_DFS, PSCAN_FCC | PSCAN_ETSI, 0}, + {5180, 5240, 20, 0, 20, 20, NO_DFS, PSCAN_FCC | PSCAN_ETSI, 0}, + {5180, 5240, 23, 0, 20, 20, NO_DFS, PSCAN_FCC | PSCAN_ETSI, 0}, + {5180, 5240, 23, 6, 20, 20, NO_DFS, PSCAN_FCC, 0}, + {5180, 5240, 20, 0, 20, 20, NO_DFS, PSCAN_MKK1 | PSCAN_MKK3, 0}, + {5180, 5240, 23, 6, 20, 20, NO_DFS, NO_PSCAN, 0}, + + {5180, 5320, 20, 6, 20, 20, NO_DFS, PSCAN_ETSI, 0}, + + {5240, 5280, 23, 0, 20, 20, DFS_FCC3, PSCAN_FCC | PSCAN_ETSI, 0}, + + {5260, 5280, 23, 0, 20, 20, DFS_FCC3 | DFS_ETSI, + PSCAN_FCC | PSCAN_ETSI, 0}, + + {5260, 5320, 18, 0, 20, 20, DFS_FCC3 | DFS_ETSI, + PSCAN_FCC | PSCAN_ETSI, 0}, + + {5260, 5320, 20, 0, 20, 20, DFS_FCC3 | DFS_ETSI | DFS_MKK4, + PSCAN_FCC | PSCAN_ETSI | PSCAN_MKK3, 0}, + + + {5260, 5320, 20, 6, 20, 20, DFS_FCC3 | DFS_ETSI, + PSCAN_FCC | PSCAN_ETSI, 2}, + {5260, 5320, 23, 6, 20, 20, DFS_FCC3 | DFS_ETSI, PSCAN_FCC, 2}, + {5260, 5320, 23, 6, 20, 20, DFS_FCC3 | DFS_ETSI, PSCAN_FCC, 0}, + {5260, 5320, 30, 0, 20, 20, NO_DFS, NO_PSCAN, 0}, + + {5260, 5700, 5, 6, 20, 20, DFS_FCC3 | DFS_ETSI, NO_PSCAN, 0}, + + {5280, 5320, 17, 6, 20, 20, DFS_FCC3 | DFS_ETSI, PSCAN_FCC, 0}, + + {5500, 5580, 23, 6, 20, 20, DFS_FCC3, PSCAN_FCC, 0}, + + {5500, 5620, 30, 6, 20, 20, DFS_ETSI, PSCAN_ETSI, 0}, + + {5500, 5700, 20, 6, 20, 20, DFS_FCC3 | DFS_ETSI, PSCAN_FCC, 4}, + {5500, 5700, 27, 0, 20, 20, DFS_FCC3 | DFS_ETSI, + PSCAN_FCC | PSCAN_ETSI, 0}, + {5500, 5700, 30, 0, 20, 20, DFS_FCC3 | DFS_ETSI, + PSCAN_FCC | PSCAN_ETSI, 0}, + {5500, 5700, 23, 0, 20, 20, DFS_FCC3 | DFS_ETSI | DFS_MKK4, + PSCAN_MKK3 | PSCAN_FCC, 0}, + {5500, 5700, 30, 6, 20, 20, DFS_ETSI, PSCAN_ETSI, 0}, + + {5660, 5700, 23, 6, 20, 20, DFS_FCC3, PSCAN_FCC, 0}, + + {5745, 5805, 23, 0, 20, 20, NO_DFS, NO_PSCAN, 0}, + {5745, 5805, 30, 6, 20, 20, NO_DFS, NO_PSCAN, 0}, + {5745, 5805, 30, 6, 20, 20, NO_DFS, PSCAN_ETSI, 0}, + {5745, 5825, 5, 6, 20, 20, NO_DFS, NO_PSCAN, 0}, + {5745, 5825, 17, 0, 20, 20, NO_DFS, NO_PSCAN, 0}, + {5745, 5825, 20, 0, 20, 20, NO_DFS, NO_PSCAN, 0}, + {5745, 5825, 30, 0, 20, 20, NO_DFS, NO_PSCAN, 0}, + {5745, 5825, 30, 6, 20, 20, NO_DFS, NO_PSCAN, 3}, + {5745, 5825, 30, 6, 20, 20, NO_DFS, NO_PSCAN, 0}, + + + {4920, 4980, 30, 0, 20, 20, NO_DFS, PSCAN_WWR, 0}, + {5040, 5080, 30, 0, 20, 20, NO_DFS, PSCAN_WWR, 0}, + {5170, 5230, 30, 0, 20, 20, NO_DFS, PSCAN_WWR, 0}, + {5180, 5240, 30, 0, 20, 20, NO_DFS, PSCAN_WWR, 0}, + {5260, 5320, 30, 0, 20, 20, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, 0}, + {5745, 5825, 30, 0, 20, 20, NO_DFS, PSCAN_WWR, 0}, + {5500, 5700, 30, 0, 20, 20, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, 0}, + {4920, 6100, 30, 6, 20, 20, NO_DFS, NO_PSCAN, 0}, +}; + +enum { + T1_5130_5650, + T1_5150_5670, + + T1_5200_5200, + T2_5200_5200, + T3_5200_5200, + T4_5200_5200, + T5_5200_5200, + T6_5200_5200, + T7_5200_5200, + T8_5200_5200, + + T1_5200_5280, + T2_5200_5280, + T3_5200_5280, + T4_5200_5280, + T5_5200_5280, + T6_5200_5280, + + T1_5200_5240, + T1_5210_5210, + T2_5210_5210, + T3_5210_5210, + T4_5210_5210, + T5_5210_5210, + T6_5210_5210, + T7_5210_5210, + T8_5210_5210, + T9_5210_5210, + T10_5210_5210, + T1_5240_5240, + + T1_5210_5250, + T1_5210_5290, + T2_5210_5290, + T3_5210_5290, + + T1_5280_5280, + T2_5280_5280, + T1_5290_5290, + T2_5290_5290, + T3_5290_5290, + T1_5250_5290, + T2_5250_5290, + T3_5250_5290, + T4_5250_5290, + + T1_5540_5660, + T2_5540_5660, + T3_5540_5660, + T1_5760_5800, + T2_5760_5800, + T3_5760_5800, + T4_5760_5800, + T5_5760_5800, + T6_5760_5800, + T7_5760_5800, + + T1_5765_5805, + T2_5765_5805, + T3_5765_5805, + T4_5765_5805, + T5_5765_5805, + T6_5765_5805, + T7_5765_5805, + T8_5765_5805, + T9_5765_5805, + + WT1_5210_5250, + WT1_5290_5290, + WT1_5540_5660, + WT1_5760_5800, +}; + +enum { + F1_2312_2372, + F2_2312_2372, + + F1_2412_2472, + F2_2412_2472, + F3_2412_2472, + + F1_2412_2462, + F2_2412_2462, + + F1_2432_2442, + + F1_2457_2472, + + F1_2467_2472, + + F1_2484_2484, + F2_2484_2484, + + F1_2512_2732, + + W1_2312_2372, + W1_2412_2412, + W1_2417_2432, + W1_2437_2442, + W1_2447_2457, + W1_2462_2462, + W1_2467_2467, + W2_2467_2467, + W1_2472_2472, + W2_2472_2472, + W1_2484_2484, + W2_2484_2484, +}; + +static struct RegDmnFreqBand regDmn2GhzFreq[] = { + {2312, 2372, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2312, 2372, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + + {2412, 2472, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2412, 2472, 20, 0, 20, 5, NO_DFS, PSCAN_MKKA, 0}, + {2412, 2472, 30, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + + {2412, 2462, 27, 6, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2412, 2462, 20, 0, 20, 5, NO_DFS, PSCAN_MKKA, 0}, + + {2432, 2442, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + + {2457, 2472, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + + {2467, 2472, 20, 0, 20, 5, NO_DFS, PSCAN_MKKA2 | PSCAN_MKKA, 0}, + + {2484, 2484, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2484, 2484, 20, 0, 20, 5, NO_DFS, + PSCAN_MKKA | PSCAN_MKKA1 | PSCAN_MKKA2, 0}, + + {2512, 2732, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0}, + + {2312, 2372, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2412, 2412, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2417, 2432, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2437, 2442, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2447, 2457, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2462, 2462, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2467, 2467, 20, 0, 20, 5, NO_DFS, PSCAN_WWR | IS_ECM_CHAN, 0}, + {2467, 2467, 20, 0, 20, 5, NO_DFS, NO_PSCAN | IS_ECM_CHAN, 0}, + {2472, 2472, 20, 0, 20, 5, NO_DFS, PSCAN_WWR | IS_ECM_CHAN, 0}, + {2472, 2472, 20, 0, 20, 5, NO_DFS, NO_PSCAN | IS_ECM_CHAN, 0}, + {2484, 2484, 20, 0, 20, 5, NO_DFS, PSCAN_WWR | IS_ECM_CHAN, 0}, + {2484, 2484, 20, 0, 20, 5, NO_DFS, NO_PSCAN | IS_ECM_CHAN, 0}, +}; + +enum { + G1_2312_2372, + G2_2312_2372, + + G1_2412_2472, + G2_2412_2472, + G3_2412_2472, + + G1_2412_2462, + G2_2412_2462, + + G1_2432_2442, + + G1_2457_2472, + + G1_2512_2732, + + G1_2467_2472, + + WG1_2312_2372, + WG1_2412_2462, + WG1_2467_2472, + WG2_2467_2472, + G_DEMO_ALL_CHANNELS +}; + +static struct RegDmnFreqBand regDmn2Ghz11gFreq[] = { + {2312, 2372, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2312, 2372, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + + {2412, 2472, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2412, 2472, 20, 0, 20, 5, NO_DFS, PSCAN_MKKA_G, 0}, + {2412, 2472, 30, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + + {2412, 2462, 27, 6, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2412, 2462, 20, 0, 20, 5, NO_DFS, PSCAN_MKKA_G, 0}, + + {2432, 2442, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + + {2457, 2472, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + + {2512, 2732, 5, 6, 20, 5, NO_DFS, NO_PSCAN, 0}, + + {2467, 2472, 20, 0, 20, 5, NO_DFS, PSCAN_MKKA2 | PSCAN_MKKA, 0}, + + {2312, 2372, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2412, 2462, 20, 0, 20, 5, NO_DFS, NO_PSCAN, 0}, + {2467, 2472, 20, 0, 20, 5, NO_DFS, PSCAN_WWR | IS_ECM_CHAN, 0}, + {2467, 2472, 20, 0, 20, 5, NO_DFS, NO_PSCAN | IS_ECM_CHAN, 0}, + {2312, 2732, 27, 6, 20, 5, NO_DFS, NO_PSCAN, 0}, +}; + +enum { + T1_2312_2372, + T1_2437_2437, + T2_2437_2437, + T3_2437_2437, + T1_2512_2732 +}; + +static struct regDomain regDomains[] = { + + {DEBUG_REG_DMN, FCC, DFS_FCC3, NO_PSCAN, NO_REQ, + BM(A_DEMO_ALL_CHANNELS, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T1_5130_5650, T1_5150_5670, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T1_5200_5240, T1_5280_5280, T1_5540_5660, T1_5765_5805, -1, -1, + -1, -1, -1, -1, -1, -1), + BM(F1_2312_2372, F1_2412_2472, F1_2484_2484, F1_2512_2732, -1, -1, + -1, -1, -1, -1, -1, -1), + BM(G_DEMO_ALL_CHANNELS, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T1_2312_2372, T1_2437_2437, T1_2512_2732, -1, -1, -1, -1, -1, + -1, -1, -1, -1)}, + + {APL1, FCC, NO_DFS, NO_PSCAN, NO_REQ, + BM(F4_5745_5825, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T2_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T1_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {APL2, FCC, NO_DFS, NO_PSCAN, NO_REQ, + BM(F1_5745_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T1_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T2_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {APL3, FCC, NO_DFS, NO_PSCAN, NO_REQ, + BM(F1_5280_5320, F2_5745_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T1_5290_5290, T1_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T1_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {APL4, FCC, NO_DFS, NO_PSCAN, NO_REQ, + BM(F4_5180_5240, F3_5745_5825, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T1_5210_5210, T3_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T1_5200_5200, T3_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BMZERO, + BMZERO, + BMZERO}, + + {APL5, FCC, NO_DFS, NO_PSCAN, NO_REQ, + BM(F2_5745_5825, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T4_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T4_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {APL6, ETSI, DFS_ETSI, PSCAN_FCC_T | PSCAN_FCC, NO_REQ, + BM(F4_5180_5240, F2_5260_5320, F3_5745_5825, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T2_5210_5210, T1_5250_5290, T1_5760_5800, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T1_5200_5280, T5_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BMZERO, + BMZERO, + BMZERO}, + + {APL7, ETSI, DFS_ETSI, PSCAN_ETSI, NO_REQ, + BM(F1_5280_5320, F5_5500_5700, F3_5745_5805, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T3_5290_5290, T5_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T1_5540_5660, T6_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BMZERO, + BMZERO, + BMZERO}, + + {APL8, ETSI, NO_DFS, NO_PSCAN, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, + BM(F6_5260_5320, F4_5745_5825, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T2_5290_5290, T2_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T1_5280_5280, T1_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BMZERO, + BMZERO, + BMZERO}, + + {APL9, ETSI, DFS_ETSI, PSCAN_ETSI, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, + BM(F1_5180_5320, F1_5500_5620, F3_5745_5805, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T3_5290_5290, T5_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T1_5540_5660, T6_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BMZERO, + BMZERO, + BMZERO}, + + {APL10, ETSI, DFS_ETSI, PSCAN_ETSI, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, + BM(F1_5180_5320, F5_5500_5700, F3_5745_5805, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T3_5290_5290, T5_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T1_5540_5660, T6_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BMZERO, + BMZERO, + BMZERO}, + + {ETSI1, ETSI, DFS_ETSI, PSCAN_ETSI, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, + BM(F4_5180_5240, F2_5260_5320, F2_5500_5700, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T1_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T2_5200_5280, T2_5540_5660, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BMZERO, + BMZERO, + BMZERO}, + + {ETSI2, ETSI, DFS_ETSI, PSCAN_ETSI, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, + BM(F3_5180_5240, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T3_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T2_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {ETSI3, ETSI, DFS_ETSI, PSCAN_ETSI, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, + BM(F4_5180_5240, F2_5260_5320, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T1_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T2_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {ETSI4, ETSI, DFS_ETSI, PSCAN_ETSI, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, + BM(F3_5180_5240, F1_5260_5320, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T2_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T3_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {ETSI5, ETSI, DFS_ETSI, PSCAN_ETSI, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, + BM(F1_5180_5240, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T4_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T3_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {ETSI6, ETSI, DFS_ETSI, PSCAN_ETSI, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, + BM(F5_5180_5240, F1_5260_5280, F3_5500_5700, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T1_5210_5250, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T4_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {FCC1, FCC, NO_DFS, NO_PSCAN, NO_REQ, + BM(F2_5180_5240, F4_5260_5320, F5_5745_5825, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T6_5210_5210, T2_5250_5290, T6_5760_5800, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T1_5200_5240, T2_5280_5280, T7_5765_5805, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {FCC2, FCC, NO_DFS, NO_PSCAN, NO_REQ, + BM(F6_5180_5240, F5_5260_5320, F6_5745_5825, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T7_5210_5210, T3_5250_5290, T2_5760_5800, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T7_5200_5200, T1_5240_5240, T2_5280_5280, T1_5765_5805, -1, -1, + -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {FCC3, FCC, DFS_FCC3, PSCAN_FCC | PSCAN_FCC_T, NO_REQ, + BM(F2_5180_5240, F3_5260_5320, F1_5500_5700, F5_5745_5825, -1, -1, + -1, -1, -1, -1, -1, -1), + BM(T6_5210_5210, T2_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T4_5200_5200, T8_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BMZERO, + BMZERO, + BMZERO}, + + {FCC4, FCC, DFS_FCC3, PSCAN_FCC | PSCAN_FCC_T, NO_REQ, + BM(F1_4942_4987, F1_4945_4985, F1_4950_4980, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T8_5210_5210, T4_5250_5290, T7_5760_5800, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T1_5200_5240, T1_5280_5280, T9_5765_5805, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {FCC5, FCC, NO_DFS, NO_PSCAN, NO_REQ, + BM(F2_5180_5240, F6_5745_5825, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T6_5210_5210, T2_5760_5800, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T8_5200_5200, T7_5765_5805, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BMZERO, + BMZERO, + BMZERO}, + + {FCC6, FCC, DFS_FCC3, PSCAN_FCC, NO_REQ, + BM(F8_5180_5240, F5_5260_5320, F1_5500_5580, F1_5660_5700, + F6_5745_5825, -1, -1, -1, -1, -1, -1, -1), + BM(T7_5210_5210, T3_5250_5290, T2_5760_5800, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T7_5200_5200, T1_5240_5240, T2_5280_5280, T1_5765_5805, -1, -1, + -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {MKK1, MKK, NO_DFS, PSCAN_MKK1, DISALLOW_ADHOC_11A_TURB, + BM(F1_5170_5230, F4_5180_5240, F2_5260_5320, F4_5500_5700, -1, -1, + -1, -1, -1, -1, -1, -1), + BM(T7_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T5_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + {MKK2, MKK, NO_DFS, PSCAN_MKK2, DISALLOW_ADHOC_11A_TURB, + BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040, + F1_5055_5055, F1_5040_5080, F1_5170_5230, F4_5180_5240, + F2_5260_5320, F4_5500_5700, -1, -1), + BM(T7_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T5_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + + {MKK3, MKK, NO_DFS, PSCAN_MKK3, DISALLOW_ADHOC_11A_TURB, + BM(F4_5180_5240, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T9_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T1_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + + {MKK4, MKK, DFS_MKK4, PSCAN_MKK3, DISALLOW_ADHOC_11A_TURB, + BM(F4_5180_5240, F2_5260_5320, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T10_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T6_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + + {MKK5, MKK, DFS_MKK4, PSCAN_MKK3, DISALLOW_ADHOC_11A_TURB, + BM(F4_5180_5240, F2_5260_5320, F4_5500_5700, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T3_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T5_5200_5280, T3_5540_5660, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BMZERO, + BMZERO, + BMZERO}, + + + {MKK6, MKK, NO_DFS, PSCAN_MKK1, DISALLOW_ADHOC_11A_TURB, + BM(F2_5170_5230, F4_5180_5240, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T3_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T6_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + + {MKK7, MKK, DFS_MKK4, PSCAN_MKK1 | PSCAN_MKK3, + DISALLOW_ADHOC_11A_TURB, + BM(F1_5170_5230, F4_5180_5240, F2_5260_5320, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(T3_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T5_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + + {MKK8, MKK, DFS_MKK4, PSCAN_MKK1 | PSCAN_MKK3, + DISALLOW_ADHOC_11A_TURB, + BM(F1_5170_5230, F4_5180_5240, F2_5260_5320, F4_5500_5700, -1, -1, + -1, -1, -1, -1, -1, -1), + BM(T3_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T5_5200_5280, T3_5540_5660, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BMZERO, + BMZERO, + BMZERO}, + + + {MKK9, MKK, NO_DFS, PSCAN_MKK2 | PSCAN_MKK3, + DISALLOW_ADHOC_11A_TURB, + BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040, + F1_5055_5055, F1_5040_5080, F4_5180_5240, -1, -1, -1, -1, -1), + BM(T9_5210_5210, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T1_5200_5200, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + + {MKK10, MKK, DFS_MKK4, PSCAN_MKK2 | PSCAN_MKK3, + DISALLOW_ADHOC_11A_TURB, + BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040, + F1_5055_5055, F1_5040_5080, F4_5180_5240, F2_5260_5320, -1, -1, + -1, -1), + BM(T3_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T1_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + + {MKK11, MKK, DFS_MKK4, PSCAN_MKK3, DISALLOW_ADHOC_11A_TURB, + BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040, + F1_5055_5055, F1_5040_5080, F4_5180_5240, F2_5260_5320, + F4_5500_5700, -1, -1, -1), + BM(T3_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T1_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + + {MKK12, MKK, DFS_MKK4, PSCAN_MKK1 | PSCAN_MKK3, + DISALLOW_ADHOC_11A_TURB, + BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040, + F1_5055_5055, F1_5040_5080, F1_5170_5230, F4_5180_5240, + F2_5260_5320, F4_5500_5700, -1, -1), + BM(T3_5210_5290, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T1_5200_5280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO}, + + + {MKK13, MKK, DFS_MKK4, PSCAN_MKK1 | PSCAN_MKK3, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, + BM(F1_5170_5230, F7_5180_5240, F2_5260_5320, F4_5500_5700, -1, -1, + -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO, + BMZERO, + BMZERO}, + + + {MKK14, MKK, DFS_MKK4, PSCAN_MKK1, DISALLOW_ADHOC_11A_TURB, + BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040, + F1_5040_5080, F1_5055_5055, F1_5170_5230, F4_5180_5240, -1, -1, + -1, -1), + BMZERO, + BMZERO, + BMZERO, + BMZERO, + BMZERO}, + + + {MKK15, MKK, DFS_MKK4, PSCAN_MKK1, DISALLOW_ADHOC_11A_TURB, + BM(F1_4915_4925, F1_4935_4945, F1_4920_4980, F1_5035_5040, + F1_5040_5080, F1_5055_5055, F1_5170_5230, F4_5180_5240, + F2_5260_5320, -1, -1, -1), + BMZERO, + BMZERO, + BMZERO, + BMZERO, + BMZERO}, + + + {APLD, NO_CTL, NO_DFS, NO_PSCAN, NO_REQ, + BMZERO, + BMZERO, + BMZERO, + BM(F2_2312_2372, F2_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(G2_2312_2372, G2_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BMZERO}, + + {ETSIA, NO_CTL, NO_DFS, PSCAN_ETSIA, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, + BMZERO, + BMZERO, + BMZERO, + BM(F1_2457_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(G1_2457_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {ETSIB, ETSI, NO_DFS, PSCAN_ETSIB, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, + BMZERO, + BMZERO, + BMZERO, + BM(F1_2432_2442, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(G1_2432_2442, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {ETSIC, ETSI, NO_DFS, PSCAN_ETSIC, + DISALLOW_ADHOC_11A | DISALLOW_ADHOC_11A_TURB, + BMZERO, + BMZERO, + BMZERO, + BM(F3_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(G3_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {FCCA, FCC, NO_DFS, NO_PSCAN, NO_REQ, + BMZERO, + BMZERO, + BMZERO, + BM(F1_2412_2462, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(G1_2412_2462, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {MKKA, MKK, NO_DFS, + PSCAN_MKKA | PSCAN_MKKA_G | PSCAN_MKKA1 | PSCAN_MKKA1_G | + PSCAN_MKKA2 | PSCAN_MKKA2_G, DISALLOW_ADHOC_11A_TURB, + BMZERO, + BMZERO, + BMZERO, + BM(F2_2412_2462, F1_2467_2472, F2_2484_2484, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(G2_2412_2462, G1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1), + BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {MKKC, MKK, NO_DFS, NO_PSCAN, NO_REQ, + BMZERO, + BMZERO, + BMZERO, + BM(F2_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(G2_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {WORLD, ETSI, NO_DFS, NO_PSCAN, NO_REQ, + BMZERO, + BMZERO, + BMZERO, + BM(F2_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(G2_2412_2472, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T2_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {WOR0_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_PER_11D, + BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825, + W1_5500_5700, -1, -1, -1, -1, -1, -1, -1), + BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1, + -1, -1, -1, -1, -1), + BMZERO, + BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472, + W1_2417_2432, W1_2447_2457, W1_2467_2467, W1_2484_2484, -1, -1, + -1, -1), + BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1), + BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {WOR01_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, + ADHOC_PER_11D, + BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825, + W1_5500_5700, -1, -1, -1, -1, -1, -1, -1), + BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1, + -1, -1, -1, -1, -1), + BMZERO, + BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2417_2432, + W1_2447_2457, -1, -1, -1, -1, -1, -1, -1), + BM(WG1_2412_2462, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {WOR02_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, + ADHOC_PER_11D, + BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825, + W1_5500_5700, -1, -1, -1, -1, -1, -1, -1), + BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1, + -1, -1, -1, -1, -1), + BMZERO, + BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472, + W1_2417_2432, W1_2447_2457, W1_2467_2467, -1, -1, -1, -1, -1), + BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1), + BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {EU1_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_PER_11D, + BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825, + W1_5500_5700, -1, -1, -1, -1, -1, -1, -1), + BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1, + -1, -1, -1, -1, -1), + BMZERO, + BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W2_2472_2472, + W1_2417_2432, W1_2447_2457, W2_2467_2467, -1, -1, -1, -1, -1), + BM(WG1_2412_2462, WG2_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1), + BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {WOR1_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A, + BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825, + W1_5500_5700, -1, -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472, + W1_2417_2432, W1_2447_2457, W1_2467_2467, W1_2484_2484, -1, -1, + -1, -1), + BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1), + BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {WOR2_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A, + BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825, + W1_5500_5700, -1, -1, -1, -1, -1, -1, -1), + BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1, + -1, -1, -1, -1, -1), + BMZERO, + BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472, + W1_2417_2432, W1_2447_2457, W1_2467_2467, W1_2484_2484, -1, -1, + -1, -1), + BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1), + BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {WOR3_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_PER_11D, + BM(W1_5260_5320, W1_5180_5240, W1_5170_5230, W1_5745_5825, -1, -1, + -1, -1, -1, -1, -1, -1), + BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1, + -1, -1, -1, -1, -1), + BMZERO, + BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472, + W1_2417_2432, W1_2447_2457, W1_2467_2467, -1, -1, -1, -1, -1), + BM(WG1_2412_2462, WG2_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1), + BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {WOR4_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A, + BM(W1_5260_5320, W1_5180_5240, W1_5745_5825, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1, + -1, -1, -1, -1, -1), + BMZERO, + BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2417_2432, + W1_2447_2457, -1, -1, -1, -1, -1, -1, -1), + BM(WG1_2412_2462, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {WOR5_ETSIC, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A, + BM(W1_5260_5320, W1_5180_5240, W1_5745_5825, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BMZERO, + BMZERO, + BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472, + W1_2417_2432, W1_2447_2457, W1_2467_2467, -1, -1, -1, -1, -1), + BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1), + BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {WOR9_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A, + BM(W1_5260_5320, W1_5180_5240, W1_5745_5825, W1_5500_5700, -1, -1, + -1, -1, -1, -1, -1, -1), + BM(WT1_5210_5250, WT1_5290_5290, WT1_5760_5800, -1, -1, -1, -1, + -1, -1, -1, -1, -1), + BMZERO, + BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2417_2432, + W1_2447_2457, -1, -1, -1, -1, -1, -1, -1), + BM(WG1_2412_2462, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1), + BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {WORA_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A, + BM(W1_5260_5320, W1_5180_5240, W1_5745_5825, W1_5500_5700, -1, -1, + -1, -1, -1, -1, -1, -1), + BMZERO, + BMZERO, + BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472, + W1_2417_2432, W1_2447_2457, W1_2467_2467, -1, -1, -1, -1, -1), + BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1), + BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {WORB_WORLD, NO_CTL, DFS_FCC3 | DFS_ETSI, PSCAN_WWR, ADHOC_NO_11A, + BM(W1_5260_5320, W1_5180_5240, W1_5500_5700, -1, -1, -1, -1, -1, + -1, -1, -1, -1), + BMZERO, + BMZERO, + BM(W1_2412_2412, W1_2437_2442, W1_2462_2462, W1_2472_2472, + W1_2417_2432, W1_2447_2457, W1_2467_2467, -1, -1, -1, -1, -1), + BM(WG1_2412_2462, WG1_2467_2472, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1), + BM(T3_2437_2437, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1)}, + + {NULL1, NO_CTL, NO_DFS, NO_PSCAN, NO_REQ, + BMZERO, + BMZERO, + BMZERO, + BMZERO, + BMZERO, + BMZERO} +}; + +static const struct cmode modes[] = { + {ATH9K_MODE_SEL_11A, CHANNEL_A}, + {ATH9K_MODE_SEL_11B, CHANNEL_B}, + {ATH9K_MODE_SEL_11G, CHANNEL_G}, + {ATH9K_MODE_SEL_11NG_HT20, CHANNEL_G_HT20}, + {ATH9K_MODE_SEL_11NG_HT40PLUS, CHANNEL_G_HT40PLUS}, + {ATH9K_MODE_SEL_11NG_HT40MINUS, CHANNEL_G_HT40MINUS}, + {ATH9K_MODE_SEL_11NA_HT20, CHANNEL_A_HT20}, + {ATH9K_MODE_SEL_11NA_HT40PLUS, CHANNEL_A_HT40PLUS}, + {ATH9K_MODE_SEL_11NA_HT40MINUS, CHANNEL_A_HT40MINUS}, +}; + +static struct japan_bandcheck j_bandcheck[] = { + {F1_5170_5230, AR_EEPROM_EEREGCAP_EN_KK_U1_ODD}, + {F4_5180_5240, AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN}, + {F2_5260_5320, AR_EEPROM_EEREGCAP_EN_KK_U2}, + {F4_5500_5700, AR_EEPROM_EEREGCAP_EN_KK_MIDBAND} +}; + + +#endif diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c new file mode 100644 index 000000000000..f0297ee5d053 --- /dev/null +++ b/drivers/net/wireless/ath9k/xmit.c @@ -0,0 +1,2870 @@ +/* + * Copyright (c) 2008 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Implementation of transmit path. + */ + +#include "core.h" + +#define BITS_PER_BYTE 8 +#define OFDM_PLCP_BITS 22 +#define HT_RC_2_MCS(_rc) ((_rc) & 0x0f) +#define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1) +#define L_STF 8 +#define L_LTF 8 +#define L_SIG 4 +#define HT_SIG 8 +#define HT_STF 4 +#define HT_LTF(_ns) (4 * (_ns)) +#define SYMBOL_TIME(_ns) ((_ns) << 2) /* ns * 4 us */ +#define SYMBOL_TIME_HALFGI(_ns) (((_ns) * 18 + 4) / 5) /* ns * 3.6 us */ +#define NUM_SYMBOLS_PER_USEC(_usec) (_usec >> 2) +#define NUM_SYMBOLS_PER_USEC_HALFGI(_usec) (((_usec*5)-4)/18) + +#define OFDM_SIFS_TIME 16 + +static u32 bits_per_symbol[][2] = { + /* 20MHz 40MHz */ + { 26, 54 }, /* 0: BPSK */ + { 52, 108 }, /* 1: QPSK 1/2 */ + { 78, 162 }, /* 2: QPSK 3/4 */ + { 104, 216 }, /* 3: 16-QAM 1/2 */ + { 156, 324 }, /* 4: 16-QAM 3/4 */ + { 208, 432 }, /* 5: 64-QAM 2/3 */ + { 234, 486 }, /* 6: 64-QAM 3/4 */ + { 260, 540 }, /* 7: 64-QAM 5/6 */ + { 52, 108 }, /* 8: BPSK */ + { 104, 216 }, /* 9: QPSK 1/2 */ + { 156, 324 }, /* 10: QPSK 3/4 */ + { 208, 432 }, /* 11: 16-QAM 1/2 */ + { 312, 648 }, /* 12: 16-QAM 3/4 */ + { 416, 864 }, /* 13: 64-QAM 2/3 */ + { 468, 972 }, /* 14: 64-QAM 3/4 */ + { 520, 1080 }, /* 15: 64-QAM 5/6 */ +}; + +#define IS_HT_RATE(_rate) ((_rate) & 0x80) + +/* + * Insert a chain of ath_buf (descriptors) on a multicast txq + * but do NOT start tx DMA on this queue. + * NB: must be called with txq lock held + */ + +static void ath_tx_mcastqaddbuf(struct ath_softc *sc, + struct ath_txq *txq, + struct list_head *head) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf; + + if (list_empty(head)) + return; + + /* + * Insert the frame on the outbound list and + * pass it on to the hardware. + */ + bf = list_first_entry(head, struct ath_buf, list); + + /* + * The CAB queue is started from the SWBA handler since + * frames only go out on DTIM and to avoid possible races. + */ + ath9k_hw_set_interrupts(ah, 0); + + /* + * If there is anything in the mcastq, we want to set + * the "more data" bit in the last item in the queue to + * indicate that there is "more data". It makes sense to add + * it here since you are *always* going to have + * more data when adding to this queue, no matter where + * you call from. + */ + + if (txq->axq_depth) { + struct ath_buf *lbf; + struct ieee80211_hdr *hdr; + + /* + * Add the "more data flag" to the last frame + */ + + lbf = list_entry(txq->axq_q.prev, struct ath_buf, list); + hdr = (struct ieee80211_hdr *) + ((struct sk_buff *)(lbf->bf_mpdu))->data; + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + + /* + * Now, concat the frame onto the queue + */ + list_splice_tail_init(head, &txq->axq_q); + txq->axq_depth++; + txq->axq_totalqueued++; + txq->axq_linkbuf = list_entry(txq->axq_q.prev, struct ath_buf, list); + + DPRINTF(sc, ATH_DBG_QUEUE, + "%s: txq depth = %d\n", __func__, txq->axq_depth); + if (txq->axq_link != NULL) { + *txq->axq_link = bf->bf_daddr; + DPRINTF(sc, ATH_DBG_XMIT, + "%s: link[%u](%p)=%llx (%p)\n", + __func__, + txq->axq_qnum, txq->axq_link, + ito64(bf->bf_daddr), bf->bf_desc); + } + txq->axq_link = &(bf->bf_lastbf->bf_desc->ds_link); + ath9k_hw_set_interrupts(ah, sc->sc_imask); +} + +/* + * Insert a chain of ath_buf (descriptors) on a txq and + * assume the descriptors are already chained together by caller. + * NB: must be called with txq lock held + */ + +static void ath_tx_txqaddbuf(struct ath_softc *sc, + struct ath_txq *txq, struct list_head *head) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf; + /* + * Insert the frame on the outbound list and + * pass it on to the hardware. + */ + + if (list_empty(head)) + return; + + bf = list_first_entry(head, struct ath_buf, list); + + list_splice_tail_init(head, &txq->axq_q); + txq->axq_depth++; + txq->axq_totalqueued++; + txq->axq_linkbuf = list_entry(txq->axq_q.prev, struct ath_buf, list); + + DPRINTF(sc, ATH_DBG_QUEUE, + "%s: txq depth = %d\n", __func__, txq->axq_depth); + + if (txq->axq_link == NULL) { + ath9k_hw_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr); + DPRINTF(sc, ATH_DBG_XMIT, + "%s: TXDP[%u] = %llx (%p)\n", + __func__, txq->axq_qnum, + ito64(bf->bf_daddr), bf->bf_desc); + } else { + *txq->axq_link = bf->bf_daddr; + DPRINTF(sc, ATH_DBG_XMIT, "%s: link[%u] (%p)=%llx (%p)\n", + __func__, + txq->axq_qnum, txq->axq_link, + ito64(bf->bf_daddr), bf->bf_desc); + } + txq->axq_link = &(bf->bf_lastbf->bf_desc->ds_link); + ath9k_hw_txstart(ah, txq->axq_qnum); +} + +/* Get transmit rate index using rate in Kbps */ + +static int ath_tx_findindex(const struct ath9k_rate_table *rt, int rate) +{ + int i; + int ndx = 0; + + for (i = 0; i < rt->rateCount; i++) { + if (rt->info[i].rateKbps == rate) { + ndx = i; + break; + } + } + + return ndx; +} + +/* Check if it's okay to send out aggregates */ + +static int ath_aggr_query(struct ath_softc *sc, + struct ath_node *an, u8 tidno) +{ + struct ath_atx_tid *tid; + tid = ATH_AN_2_TID(an, tidno); + + if (tid->addba_exchangecomplete || tid->addba_exchangeinprogress) + return 1; + else + return 0; +} + +static enum ath9k_pkt_type get_hal_packet_type(struct ieee80211_hdr *hdr) +{ + enum ath9k_pkt_type htype; + __le16 fc; + + fc = hdr->frame_control; + + /* Calculate Atheros packet type from IEEE80211 packet header */ + + if (ieee80211_is_beacon(fc)) + htype = ATH9K_PKT_TYPE_BEACON; + else if (ieee80211_is_probe_resp(fc)) + htype = ATH9K_PKT_TYPE_PROBE_RESP; + else if (ieee80211_is_atim(fc)) + htype = ATH9K_PKT_TYPE_ATIM; + else if (ieee80211_is_pspoll(fc)) + htype = ATH9K_PKT_TYPE_PSPOLL; + else + htype = ATH9K_PKT_TYPE_NORMAL; + + return htype; +} + +static void fill_min_rates(struct sk_buff *skb, struct ath_tx_control *txctl) +{ + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ath_tx_info_priv *tx_info_priv; + __le16 fc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0]; + + if (ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc)) { + txctl->use_minrate = 1; + txctl->min_rate = tx_info_priv->min_rate; + } else if (ieee80211_is_data(fc)) { + if (ieee80211_is_nullfunc(fc) || + /* Port Access Entity (IEEE 802.1X) */ + (skb->protocol == cpu_to_be16(0x888E))) { + txctl->use_minrate = 1; + txctl->min_rate = tx_info_priv->min_rate; + } + if (is_multicast_ether_addr(hdr->addr1)) + txctl->mcast_rate = tx_info_priv->min_rate; + } + +} + +/* This function will setup additional txctl information, mostly rate stuff */ +/* FIXME: seqno, ps */ +static int ath_tx_prepare(struct ath_softc *sc, + struct sk_buff *skb, + struct ath_tx_control *txctl) +{ + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_hdr *hdr; + struct ath_rc_series *rcs; + struct ath_txq *txq = NULL; + const struct ath9k_rate_table *rt; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ath_tx_info_priv *tx_info_priv; + int hdrlen; + u8 rix, antenna; + __le16 fc; + u8 *qc; + + memset(txctl, 0, sizeof(struct ath_tx_control)); + + txctl->dev = sc; + hdr = (struct ieee80211_hdr *)skb->data; + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + fc = hdr->frame_control; + + rt = sc->sc_currates; + BUG_ON(!rt); + + /* Fill misc fields */ + + spin_lock_bh(&sc->node_lock); + txctl->an = ath_node_get(sc, hdr->addr1); + /* create a temp node, if the node is not there already */ + if (!txctl->an) + txctl->an = ath_node_attach(sc, hdr->addr1, 0); + spin_unlock_bh(&sc->node_lock); + + if (ieee80211_is_data_qos(fc)) { + qc = ieee80211_get_qos_ctl(hdr); + txctl->tidno = qc[0] & 0xf; + } + + txctl->if_id = 0; + txctl->nextfraglen = 0; + txctl->frmlen = skb->len + FCS_LEN - (hdrlen & 3); + txctl->txpower = MAX_RATE_POWER; /* FIXME */ + + /* Fill Key related fields */ + + txctl->keytype = ATH9K_KEY_TYPE_CLEAR; + txctl->keyix = ATH9K_TXKEYIX_INVALID; + + if (tx_info->control.hw_key) { + txctl->keyix = tx_info->control.hw_key->hw_key_idx; + txctl->frmlen += tx_info->control.icv_len; + + if (sc->sc_keytype == ATH9K_CIPHER_WEP) + txctl->keytype = ATH9K_KEY_TYPE_WEP; + else if (sc->sc_keytype == ATH9K_CIPHER_TKIP) + txctl->keytype = ATH9K_KEY_TYPE_TKIP; + else if (sc->sc_keytype == ATH9K_CIPHER_AES_CCM) + txctl->keytype = ATH9K_KEY_TYPE_AES; + } + + /* Fill packet type */ + + txctl->atype = get_hal_packet_type(hdr); + + /* Fill qnum */ + + txctl->qnum = ath_get_hal_qnum(skb_get_queue_mapping(skb), sc); + txq = &sc->sc_txq[txctl->qnum]; + spin_lock_bh(&txq->axq_lock); + + /* Try to avoid running out of descriptors */ + if (txq->axq_depth >= (ATH_TXBUF - 20)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: TX queue: %d is full, depth: %d\n", + __func__, + txctl->qnum, + txq->axq_depth); + ieee80211_stop_queue(hw, skb_get_queue_mapping(skb)); + txq->stopped = 1; + spin_unlock_bh(&txq->axq_lock); + return -1; + } + + spin_unlock_bh(&txq->axq_lock); + + /* Fill rate */ + + fill_min_rates(skb, txctl); + + /* Fill flags */ + + txctl->flags = ATH9K_TXDESC_CLRDMASK; /* needed for crypto errors */ + + if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) + tx_info->flags |= ATH9K_TXDESC_NOACK; + if (tx_info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) + tx_info->flags |= ATH9K_TXDESC_RTSENA; + + /* + * Setup for rate calculations. + */ + tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0]; + rcs = tx_info_priv->rcs; + + if (ieee80211_is_data(fc) && !txctl->use_minrate) { + + /* Enable HT only for DATA frames and not for EAPOL */ + txctl->ht = (hw->conf.ht_conf.ht_supported && + (tx_info->flags & IEEE80211_TX_CTL_AMPDU)); + + if (is_multicast_ether_addr(hdr->addr1)) { + rcs[0].rix = (u8) + ath_tx_findindex(rt, txctl->mcast_rate); + + /* + * mcast packets are not re-tried. + */ + rcs[0].tries = 1; + } + /* For HT capable stations, we save tidno for later use. + * We also override seqno set by upper layer with the one + * in tx aggregation state. + * + * First, the fragmentation stat is determined. + * If fragmentation is on, the sequence number is + * not overridden, since it has been + * incremented by the fragmentation routine. + */ + if (likely(!(txctl->flags & ATH9K_TXDESC_FRAG_IS_ON)) && + txctl->ht && sc->sc_txaggr) { + struct ath_atx_tid *tid; + + tid = ATH_AN_2_TID(txctl->an, txctl->tidno); + + hdr->seq_ctrl = cpu_to_le16(tid->seq_next << + IEEE80211_SEQ_SEQ_SHIFT); + txctl->seqno = tid->seq_next; + INCR(tid->seq_next, IEEE80211_SEQ_MAX); + } + } else { + /* for management and control frames, + * or for NULL and EAPOL frames */ + if (txctl->min_rate) + rcs[0].rix = ath_rate_findrateix(sc, txctl->min_rate); + else + rcs[0].rix = sc->sc_minrateix; + rcs[0].tries = ATH_MGT_TXMAXTRY; + } + rix = rcs[0].rix; + + /* + * Calculate duration. This logically belongs in the 802.11 + * layer but it lacks sufficient information to calculate it. + */ + if ((txctl->flags & ATH9K_TXDESC_NOACK) == 0 && !ieee80211_is_ctl(fc)) { + u16 dur; + /* + * XXX not right with fragmentation. + */ + if (sc->sc_flags & ATH_PREAMBLE_SHORT) + dur = rt->info[rix].spAckDuration; + else + dur = rt->info[rix].lpAckDuration; + + if (le16_to_cpu(hdr->frame_control) & + IEEE80211_FCTL_MOREFRAGS) { + dur += dur; /* Add additional 'SIFS + ACK' */ + + /* + ** Compute size of next fragment in order to compute + ** durations needed to update NAV. + ** The last fragment uses the ACK duration only. + ** Add time for next fragment. + */ + dur += ath9k_hw_computetxtime(sc->sc_ah, rt, + txctl->nextfraglen, + rix, sc->sc_flags & ATH_PREAMBLE_SHORT); + } + + if (ieee80211_has_morefrags(fc) || + (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)) { + /* + ** Force hardware to use computed duration for next + ** fragment by disabling multi-rate retry, which + ** updates duration based on the multi-rate + ** duration table. + */ + rcs[1].tries = rcs[2].tries = rcs[3].tries = 0; + rcs[1].rix = rcs[2].rix = rcs[3].rix = 0; + /* reset tries but keep rate index */ + rcs[0].tries = ATH_TXMAXTRY; + } + + hdr->duration_id = cpu_to_le16(dur); + } + + /* + * Determine if a tx interrupt should be generated for + * this descriptor. We take a tx interrupt to reap + * descriptors when the h/w hits an EOL condition or + * when the descriptor is specifically marked to generate + * an interrupt. We periodically mark descriptors in this + * way to insure timely replenishing of the supply needed + * for sending frames. Defering interrupts reduces system + * load and potentially allows more concurrent work to be + * done but if done to aggressively can cause senders to + * backup. + * + * NB: use >= to deal with sc_txintrperiod changing + * dynamically through sysctl. + */ + spin_lock_bh(&txq->axq_lock); + if ((++txq->axq_intrcnt >= sc->sc_txintrperiod)) { + txctl->flags |= ATH9K_TXDESC_INTREQ; + txq->axq_intrcnt = 0; + } + spin_unlock_bh(&txq->axq_lock); + + if (is_multicast_ether_addr(hdr->addr1)) { + antenna = sc->sc_mcastantenna + 1; + sc->sc_mcastantenna = (sc->sc_mcastantenna + 1) & 0x1; + } else + antenna = sc->sc_txantenna; + +#ifdef USE_LEGACY_HAL + txctl->antenna = antenna; +#endif + return 0; +} + +/* To complete a chain of buffers associated a frame */ + +static void ath_tx_complete_buf(struct ath_softc *sc, + struct ath_buf *bf, + struct list_head *bf_q, + int txok, int sendbar) +{ + struct sk_buff *skb = bf->bf_mpdu; + struct ath_xmit_status tx_status; + dma_addr_t *pa; + + /* + * Set retry information. + * NB: Don't use the information in the descriptor, because the frame + * could be software retried. + */ + tx_status.retries = bf->bf_retries; + tx_status.flags = 0; + + if (sendbar) + tx_status.flags = ATH_TX_BAR; + + if (!txok) { + tx_status.flags |= ATH_TX_ERROR; + + if (bf->bf_isxretried) + tx_status.flags |= ATH_TX_XRETRY; + } + /* Unmap this frame */ + pa = get_dma_mem_context(bf, bf_dmacontext); + pci_unmap_single(sc->pdev, + *pa, + skb->len, + PCI_DMA_TODEVICE); + /* complete this frame */ + ath_tx_complete(sc, skb, &tx_status, bf->bf_node); + + /* + * Return the list of ath_buf of this mpdu to free queue + */ + spin_lock_bh(&sc->sc_txbuflock); + list_splice_tail_init(bf_q, &sc->sc_txbuf); + spin_unlock_bh(&sc->sc_txbuflock); +} + +/* + * queue up a dest/ac pair for tx scheduling + * NB: must be called with txq lock held + */ + +static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid) +{ + struct ath_atx_ac *ac = tid->ac; + + /* + * if tid is paused, hold off + */ + if (tid->paused) + return; + + /* + * add tid to ac atmost once + */ + if (tid->sched) + return; + + tid->sched = true; + list_add_tail(&tid->list, &ac->tid_q); + + /* + * add node ac to txq atmost once + */ + if (ac->sched) + return; + + ac->sched = true; + list_add_tail(&ac->list, &txq->axq_acq); +} + +/* pause a tid */ + +static void ath_tx_pause_tid(struct ath_softc *sc, struct ath_atx_tid *tid) +{ + struct ath_txq *txq = &sc->sc_txq[tid->ac->qnum]; + + spin_lock_bh(&txq->axq_lock); + + tid->paused++; + + spin_unlock_bh(&txq->axq_lock); +} + +/* resume a tid and schedule aggregate */ + +void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid) +{ + struct ath_txq *txq = &sc->sc_txq[tid->ac->qnum]; + + ASSERT(tid->paused > 0); + spin_lock_bh(&txq->axq_lock); + + tid->paused--; + + if (tid->paused > 0) + goto unlock; + + if (list_empty(&tid->buf_q)) + goto unlock; + + /* + * Add this TID to scheduler and try to send out aggregates + */ + ath_tx_queue_tid(txq, tid); + ath_txq_schedule(sc, txq); +unlock: + spin_unlock_bh(&txq->axq_lock); +} + +/* Compute the number of bad frames */ + +static int ath_tx_num_badfrms(struct ath_softc *sc, + struct ath_buf *bf, int txok) +{ + struct ath_node *an = bf->bf_node; + int isnodegone = (an->an_flags & ATH_NODE_CLEAN); + struct ath_buf *bf_last = bf->bf_lastbf; + struct ath_desc *ds = bf_last->bf_desc; + u16 seq_st = 0; + u32 ba[WME_BA_BMP_SIZE >> 5]; + int ba_index; + int nbad = 0; + int isaggr = 0; + + if (isnodegone || ds->ds_txstat.ts_flags == ATH9K_TX_SW_ABORTED) + return 0; + + isaggr = bf->bf_isaggr; + if (isaggr) { + seq_st = ATH_DS_BA_SEQ(ds); + memcpy(ba, ATH_DS_BA_BITMAP(ds), WME_BA_BMP_SIZE >> 3); + } + + while (bf) { + ba_index = ATH_BA_INDEX(seq_st, bf->bf_seqno); + if (!txok || (isaggr && !ATH_BA_ISSET(ba, ba_index))) + nbad++; + + bf = bf->bf_next; + } + + return nbad; +} + +static void ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf) +{ + struct sk_buff *skb; + struct ieee80211_hdr *hdr; + + bf->bf_isretried = 1; + bf->bf_retries++; + + skb = bf->bf_mpdu; + hdr = (struct ieee80211_hdr *)skb->data; + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_RETRY); +} + +/* Update block ack window */ + +static void ath_tx_update_baw(struct ath_softc *sc, + struct ath_atx_tid *tid, int seqno) +{ + int index, cindex; + + index = ATH_BA_INDEX(tid->seq_start, seqno); + cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); + + tid->tx_buf[cindex] = NULL; + + while (tid->baw_head != tid->baw_tail && !tid->tx_buf[tid->baw_head]) { + INCR(tid->seq_start, IEEE80211_SEQ_MAX); + INCR(tid->baw_head, ATH_TID_MAX_BUFS); + } +} + +/* + * ath_pkt_dur - compute packet duration (NB: not NAV) + * + * rix - rate index + * pktlen - total bytes (delims + data + fcs + pads + pad delims) + * width - 0 for 20 MHz, 1 for 40 MHz + * half_gi - to use 4us v/s 3.6 us for symbol time + */ + +static u32 ath_pkt_duration(struct ath_softc *sc, + u8 rix, + struct ath_buf *bf, + int width, + int half_gi, + bool shortPreamble) +{ + const struct ath9k_rate_table *rt = sc->sc_currates; + u32 nbits, nsymbits, duration, nsymbols; + u8 rc; + int streams, pktlen; + + pktlen = bf->bf_isaggr ? bf->bf_al : bf->bf_frmlen; + rc = rt->info[rix].rateCode; + + /* + * for legacy rates, use old function to compute packet duration + */ + if (!IS_HT_RATE(rc)) + return ath9k_hw_computetxtime(sc->sc_ah, + rt, + pktlen, + rix, + shortPreamble); + /* + * find number of symbols: PLCP + data + */ + nbits = (pktlen << 3) + OFDM_PLCP_BITS; + nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width]; + nsymbols = (nbits + nsymbits - 1) / nsymbits; + + if (!half_gi) + duration = SYMBOL_TIME(nsymbols); + else + duration = SYMBOL_TIME_HALFGI(nsymbols); + + /* + * addup duration for legacy/ht training and signal fields + */ + streams = HT_RC_2_STREAMS(rc); + duration += L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams); + return duration; +} + +/* Rate module function to set rate related fields in tx descriptor */ + +static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf) +{ + struct ath_hal *ah = sc->sc_ah; + const struct ath9k_rate_table *rt; + struct ath_desc *ds = bf->bf_desc; + struct ath_desc *lastds = bf->bf_lastbf->bf_desc; + struct ath9k_11n_rate_series series[4]; + int i, flags, rtsctsena = 0, dynamic_mimops = 0; + u32 ctsduration = 0; + u8 rix = 0, cix, ctsrate = 0; + u32 aggr_limit_with_rts = sc->sc_rtsaggrlimit; + struct ath_node *an = (struct ath_node *) bf->bf_node; + + /* + * get the cix for the lowest valid rix. + */ + rt = sc->sc_currates; + for (i = 4; i--;) { + if (bf->bf_rcs[i].tries) { + rix = bf->bf_rcs[i].rix; + break; + } + } + flags = (bf->bf_flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA)); + cix = rt->info[rix].controlRate; + + /* + * If 802.11g protection is enabled, determine whether + * to use RTS/CTS or just CTS. Note that this is only + * done for OFDM/HT unicast frames. + */ + if (sc->sc_protmode != PROT_M_NONE && + (rt->info[rix].phy == PHY_OFDM || + rt->info[rix].phy == PHY_HT) && + (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) { + if (sc->sc_protmode == PROT_M_RTSCTS) + flags = ATH9K_TXDESC_RTSENA; + else if (sc->sc_protmode == PROT_M_CTSONLY) + flags = ATH9K_TXDESC_CTSENA; + + cix = rt->info[sc->sc_protrix].controlRate; + rtsctsena = 1; + } + + /* For 11n, the default behavior is to enable RTS for + * hw retried frames. We enable the global flag here and + * let rate series flags determine which rates will actually + * use RTS. + */ + if (ah->ah_caps.halHTSupport && bf->bf_isdata) { + BUG_ON(!an); + /* + * 802.11g protection not needed, use our default behavior + */ + if (!rtsctsena) + flags = ATH9K_TXDESC_RTSENA; + /* + * For dynamic MIMO PS, RTS needs to precede the first aggregate + * and the second aggregate should have any protection at all. + */ + if (an->an_smmode == ATH_SM_PWRSAV_DYNAMIC) { + if (!bf->bf_aggrburst) { + flags = ATH9K_TXDESC_RTSENA; + dynamic_mimops = 1; + } else { + flags = 0; + } + } + } + + /* + * Set protection if aggregate protection on + */ + if (sc->sc_config.ath_aggr_prot && + (!bf->bf_isaggr || (bf->bf_isaggr && bf->bf_al < 8192))) { + flags = ATH9K_TXDESC_RTSENA; + cix = rt->info[sc->sc_protrix].controlRate; + rtsctsena = 1; + } + + /* + * For AR5416 - RTS cannot be followed by a frame larger than 8K. + */ + if (bf->bf_isaggr && (bf->bf_al > aggr_limit_with_rts)) { + /* + * Ensure that in the case of SM Dynamic power save + * while we are bursting the second aggregate the + * RTS is cleared. + */ + flags &= ~(ATH9K_TXDESC_RTSENA); + } + + /* + * CTS transmit rate is derived from the transmit rate + * by looking in the h/w rate table. We must also factor + * in whether or not a short preamble is to be used. + */ + /* NB: cix is set above where RTS/CTS is enabled */ + BUG_ON(cix == 0xff); + ctsrate = rt->info[cix].rateCode | + (bf->bf_shpreamble ? rt->info[cix].shortPreamble : 0); + + /* + * Setup HAL rate series + */ + memzero(series, sizeof(struct ath9k_11n_rate_series) * 4); + + for (i = 0; i < 4; i++) { + if (!bf->bf_rcs[i].tries) + continue; + + rix = bf->bf_rcs[i].rix; + + series[i].Rate = rt->info[rix].rateCode | + (bf->bf_shpreamble ? rt->info[rix].shortPreamble : 0); + + series[i].Tries = bf->bf_rcs[i].tries; + + series[i].RateFlags = ( + (bf->bf_rcs[i].flags & ATH_RC_RTSCTS_FLAG) ? + ATH9K_RATESERIES_RTS_CTS : 0) | + ((bf->bf_rcs[i].flags & ATH_RC_CW40_FLAG) ? + ATH9K_RATESERIES_2040 : 0) | + ((bf->bf_rcs[i].flags & ATH_RC_SGI_FLAG) ? + ATH9K_RATESERIES_HALFGI : 0); + + series[i].PktDuration = ath_pkt_duration( + sc, rix, bf, + (bf->bf_rcs[i].flags & ATH_RC_CW40_FLAG) != 0, + (bf->bf_rcs[i].flags & ATH_RC_SGI_FLAG), + bf->bf_shpreamble); + + if ((an->an_smmode == ATH_SM_PWRSAV_STATIC) && + (bf->bf_rcs[i].flags & ATH_RC_DS_FLAG) == 0) { + /* + * When sending to an HT node that has enabled static + * SM/MIMO power save, send at single stream rates but + * use maximum allowed transmit chains per user, + * hardware, regulatory, or country limits for + * better range. + */ + series[i].ChSel = sc->sc_tx_chainmask; + } else { + if (bf->bf_ht) + series[i].ChSel = + ath_chainmask_sel_logic(sc, an); + else + series[i].ChSel = sc->sc_tx_chainmask; + } + + if (rtsctsena) + series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS; + + /* + * Set RTS for all rates if node is in dynamic powersave + * mode and we are using dual stream rates. + */ + if (dynamic_mimops && (bf->bf_rcs[i].flags & ATH_RC_DS_FLAG)) + series[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS; + } + + /* + * For non-HT devices, calculate RTS/CTS duration in software + * and disable multi-rate retry. + */ + if (flags && !ah->ah_caps.halHTSupport) { + /* + * Compute the transmit duration based on the frame + * size and the size of an ACK frame. We call into the + * HAL to do the computation since it depends on the + * characteristics of the actual PHY being used. + * + * NB: CTS is assumed the same size as an ACK so we can + * use the precalculated ACK durations. + */ + if (flags & ATH9K_TXDESC_RTSENA) { /* SIFS + CTS */ + ctsduration += bf->bf_shpreamble ? + rt->info[cix].spAckDuration : + rt->info[cix].lpAckDuration; + } + + ctsduration += series[0].PktDuration; + + if ((bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) { /* SIFS + ACK */ + ctsduration += bf->bf_shpreamble ? + rt->info[rix].spAckDuration : + rt->info[rix].lpAckDuration; + } + + /* + * Disable multi-rate retry when using RTS/CTS by clearing + * series 1, 2 and 3. + */ + memzero(&series[1], sizeof(struct ath9k_11n_rate_series) * 3); + } + + /* + * set dur_update_en for l-sig computation except for PS-Poll frames + */ + ath9k_hw_set11n_ratescenario(ah, ds, lastds, + !bf->bf_ispspoll, + ctsrate, + ctsduration, + series, 4, flags); + if (sc->sc_config.ath_aggr_prot && flags) + ath9k_hw_set11n_burstduration(ah, ds, 8192); +} + +/* + * Function to send a normal HT (non-AMPDU) frame + * NB: must be called with txq lock held + */ + +static int ath_tx_send_normal(struct ath_softc *sc, + struct ath_txq *txq, + struct ath_atx_tid *tid, + struct list_head *bf_head) +{ + struct ath_buf *bf; + struct sk_buff *skb; + struct ieee80211_tx_info *tx_info; + struct ath_tx_info_priv *tx_info_priv; + + BUG_ON(list_empty(bf_head)); + + bf = list_first_entry(bf_head, struct ath_buf, list); + bf->bf_isampdu = 0; /* regular HT frame */ + + skb = (struct sk_buff *)bf->bf_mpdu; + tx_info = IEEE80211_SKB_CB(skb); + tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0]; + memcpy(bf->bf_rcs, tx_info_priv->rcs, 4 * sizeof(tx_info_priv->rcs[0])); + + /* update starting sequence number for subsequent ADDBA request */ + INCR(tid->seq_start, IEEE80211_SEQ_MAX); + + /* Queue to h/w without aggregation */ + bf->bf_nframes = 1; + bf->bf_lastbf = bf->bf_lastfrm; /* one single frame */ + ath_buf_set_rate(sc, bf); + ath_tx_txqaddbuf(sc, txq, bf_head); + + return 0; +} + +/* flush tid's software queue and send frames as non-ampdu's */ + +static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) +{ + struct ath_txq *txq = &sc->sc_txq[tid->ac->qnum]; + struct ath_buf *bf; + struct list_head bf_head; + INIT_LIST_HEAD(&bf_head); + + ASSERT(tid->paused > 0); + spin_lock_bh(&txq->axq_lock); + + tid->paused--; + + if (tid->paused > 0) { + spin_unlock_bh(&txq->axq_lock); + return; + } + + while (!list_empty(&tid->buf_q)) { + bf = list_first_entry(&tid->buf_q, struct ath_buf, list); + ASSERT(!bf->bf_isretried); + list_cut_position(&bf_head, &tid->buf_q, &bf->bf_lastfrm->list); + ath_tx_send_normal(sc, txq, tid, &bf_head); + } + + spin_unlock_bh(&txq->axq_lock); +} + +/* Completion routine of an aggregate */ + +static void ath_tx_complete_aggr_rifs(struct ath_softc *sc, + struct ath_txq *txq, + struct ath_buf *bf, + struct list_head *bf_q, + int txok) +{ + struct ath_node *an = bf->bf_node; + struct ath_atx_tid *tid = ATH_AN_2_TID(an, bf->bf_tidno); + struct ath_buf *bf_last = bf->bf_lastbf; + struct ath_desc *ds = bf_last->bf_desc; + struct ath_buf *bf_next, *bf_lastq = NULL; + struct list_head bf_head, bf_pending; + u16 seq_st = 0; + u32 ba[WME_BA_BMP_SIZE >> 5]; + int isaggr, txfail, txpending, sendbar = 0, needreset = 0; + int isnodegone = (an->an_flags & ATH_NODE_CLEAN); + + isaggr = bf->bf_isaggr; + if (isaggr) { + if (txok) { + if (ATH_DS_TX_BA(ds)) { + /* + * extract starting sequence and + * block-ack bitmap + */ + seq_st = ATH_DS_BA_SEQ(ds); + memcpy(ba, + ATH_DS_BA_BITMAP(ds), + WME_BA_BMP_SIZE >> 3); + } else { + memzero(ba, WME_BA_BMP_SIZE >> 3); + + /* + * AR5416 can become deaf/mute when BA + * issue happens. Chip needs to be reset. + * But AP code may have sychronization issues + * when perform internal reset in this routine. + * Only enable reset in STA mode for now. + */ + if (sc->sc_opmode == ATH9K_M_STA) + needreset = 1; + } + } else { + memzero(ba, WME_BA_BMP_SIZE >> 3); + } + } + + INIT_LIST_HEAD(&bf_pending); + INIT_LIST_HEAD(&bf_head); + + while (bf) { + txfail = txpending = 0; + bf_next = bf->bf_next; + + if (ATH_BA_ISSET(ba, ATH_BA_INDEX(seq_st, bf->bf_seqno))) { + /* transmit completion, subframe is + * acked by block ack */ + } else if (!isaggr && txok) { + /* transmit completion */ + } else { + + if (!tid->cleanup_inprogress && !isnodegone && + ds->ds_txstat.ts_flags != ATH9K_TX_SW_ABORTED) { + if (bf->bf_retries < ATH_MAX_SW_RETRIES) { + ath_tx_set_retry(sc, bf); + txpending = 1; + } else { + bf->bf_isxretried = 1; + txfail = 1; + sendbar = 1; + } + } else { + /* + * cleanup in progress, just fail + * the un-acked sub-frames + */ + txfail = 1; + } + } + /* + * Remove ath_buf's of this sub-frame from aggregate queue. + */ + if (bf_next == NULL) { /* last subframe in the aggregate */ + ASSERT(bf->bf_lastfrm == bf_last); + + /* + * The last descriptor of the last sub frame could be + * a holding descriptor for h/w. If that's the case, + * bf->bf_lastfrm won't be in the bf_q. + * Make sure we handle bf_q properly here. + */ + + if (!list_empty(bf_q)) { + bf_lastq = list_entry(bf_q->prev, + struct ath_buf, list); + list_cut_position(&bf_head, + bf_q, &bf_lastq->list); + } else { + /* + * XXX: if the last subframe only has one + * descriptor which is also being used as + * a holding descriptor. Then the ath_buf + * is not in the bf_q at all. + */ + INIT_LIST_HEAD(&bf_head); + } + } else { + ASSERT(!list_empty(bf_q)); + list_cut_position(&bf_head, + bf_q, &bf->bf_lastfrm->list); + } + + if (!txpending) { + /* + * complete the acked-ones/xretried ones; update + * block-ack window + */ + spin_lock_bh(&txq->axq_lock); + ath_tx_update_baw(sc, tid, bf->bf_seqno); + spin_unlock_bh(&txq->axq_lock); + + /* complete this sub-frame */ + ath_tx_complete_buf(sc, bf, &bf_head, !txfail, sendbar); + } else { + /* + * retry the un-acked ones + */ + /* + * XXX: if the last descriptor is holding descriptor, + * in order to requeue the frame to software queue, we + * need to allocate a new descriptor and + * copy the content of holding descriptor to it. + */ + if (bf->bf_next == NULL && + bf_last->bf_status & ATH_BUFSTATUS_STALE) { + struct ath_buf *tbf; + + /* allocate new descriptor */ + spin_lock_bh(&sc->sc_txbuflock); + ASSERT(!list_empty((&sc->sc_txbuf))); + tbf = list_first_entry(&sc->sc_txbuf, + struct ath_buf, list); + list_del(&tbf->list); + spin_unlock_bh(&sc->sc_txbuflock); + + ATH_TXBUF_RESET(tbf); + + /* copy descriptor content */ + tbf->bf_mpdu = bf_last->bf_mpdu; + tbf->bf_node = bf_last->bf_node; + tbf->bf_buf_addr = bf_last->bf_buf_addr; + *(tbf->bf_desc) = *(bf_last->bf_desc); + + /* link it to the frame */ + if (bf_lastq) { + bf_lastq->bf_desc->ds_link = + tbf->bf_daddr; + bf->bf_lastfrm = tbf; + ath9k_hw_cleartxdesc(sc->sc_ah, + bf->bf_lastfrm->bf_desc); + } else { + tbf->bf_state = bf_last->bf_state; + tbf->bf_lastfrm = tbf; + ath9k_hw_cleartxdesc(sc->sc_ah, + tbf->bf_lastfrm->bf_desc); + + /* copy the DMA context */ + copy_dma_mem_context( + get_dma_mem_context(tbf, + bf_dmacontext), + get_dma_mem_context(bf_last, + bf_dmacontext)); + } + list_add_tail(&tbf->list, &bf_head); + } else { + /* + * Clear descriptor status words for + * software retry + */ + ath9k_hw_cleartxdesc(sc->sc_ah, + bf->bf_lastfrm->bf_desc); + } + + /* + * Put this buffer to the temporary pending + * queue to retain ordering + */ + list_splice_tail_init(&bf_head, &bf_pending); + } + + bf = bf_next; + } + + /* + * node is already gone. no more assocication + * with the node. the node might have been freed + * any node acces can result in panic.note tid + * is part of the node. + */ + if (isnodegone) + return; + + if (tid->cleanup_inprogress) { + /* check to see if we're done with cleaning the h/w queue */ + spin_lock_bh(&txq->axq_lock); + + if (tid->baw_head == tid->baw_tail) { + tid->addba_exchangecomplete = 0; + tid->addba_exchangeattempts = 0; + spin_unlock_bh(&txq->axq_lock); + + tid->cleanup_inprogress = false; + + /* send buffered frames as singles */ + ath_tx_flush_tid(sc, tid); + } else + spin_unlock_bh(&txq->axq_lock); + + return; + } + + /* + * prepend un-acked frames to the beginning of the pending frame queue + */ + if (!list_empty(&bf_pending)) { + spin_lock_bh(&txq->axq_lock); + /* Note: we _prepend_, we _do_not_ at to + * the end of the queue ! */ + list_splice(&bf_pending, &tid->buf_q); + ath_tx_queue_tid(txq, tid); + spin_unlock_bh(&txq->axq_lock); + } + + if (needreset) + ath_internal_reset(sc); + + return; +} + +/* Process completed xmit descriptors from the specified queue */ + +static int ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath_buf *bf, *lastbf, *bf_held = NULL; + struct list_head bf_head; + struct ath_desc *ds, *tmp_ds; + struct sk_buff *skb; + struct ieee80211_tx_info *tx_info; + struct ath_tx_info_priv *tx_info_priv; + int nacked, txok, nbad = 0, isrifs = 0; + int status; + + DPRINTF(sc, ATH_DBG_QUEUE, + "%s: tx queue %d (%x), link %p\n", __func__, + txq->axq_qnum, ath9k_hw_gettxbuf(sc->sc_ah, txq->axq_qnum), + txq->axq_link); + + nacked = 0; + for (;;) { + spin_lock_bh(&txq->axq_lock); + txq->axq_intrcnt = 0; /* reset periodic desc intr count */ + if (list_empty(&txq->axq_q)) { + txq->axq_link = NULL; + txq->axq_linkbuf = NULL; + spin_unlock_bh(&txq->axq_lock); + break; + } + bf = list_first_entry(&txq->axq_q, struct ath_buf, list); + + /* + * There is a race condition that a BH gets scheduled + * after sw writes TxE and before hw re-load the last + * descriptor to get the newly chained one. + * Software must keep the last DONE descriptor as a + * holding descriptor - software does so by marking + * it with the STALE flag. + */ + bf_held = NULL; + if (bf->bf_status & ATH_BUFSTATUS_STALE) { + bf_held = bf; + if (list_is_last(&bf_held->list, &txq->axq_q)) { + /* FIXME: + * The holding descriptor is the last + * descriptor in queue. It's safe to remove + * the last holding descriptor in BH context. + */ + spin_unlock_bh(&txq->axq_lock); + break; + } else { + /* Lets work with the next buffer now */ + bf = list_entry(bf_held->list.next, + struct ath_buf, list); + } + } + + lastbf = bf->bf_lastbf; + ds = lastbf->bf_desc; /* NB: last decriptor */ + + status = ath9k_hw_txprocdesc(ah, ds); + if (status == -EINPROGRESS) { + spin_unlock_bh(&txq->axq_lock); + break; + } + if (bf->bf_desc == txq->axq_lastdsWithCTS) + txq->axq_lastdsWithCTS = NULL; + if (ds == txq->axq_gatingds) + txq->axq_gatingds = NULL; + + /* + * Remove ath_buf's of the same transmit unit from txq, + * however leave the last descriptor back as the holding + * descriptor for hw. + */ + lastbf->bf_status |= ATH_BUFSTATUS_STALE; + INIT_LIST_HEAD(&bf_head); + + if (!list_is_singular(&lastbf->list)) + list_cut_position(&bf_head, + &txq->axq_q, lastbf->list.prev); + + txq->axq_depth--; + + if (bf->bf_isaggr) + txq->axq_aggr_depth--; + + txok = (ds->ds_txstat.ts_status == 0); + + spin_unlock_bh(&txq->axq_lock); + + if (bf_held) { + list_del(&bf_held->list); + spin_lock_bh(&sc->sc_txbuflock); + list_add_tail(&bf_held->list, &sc->sc_txbuf); + spin_unlock_bh(&sc->sc_txbuflock); + } + + if (!bf->bf_isampdu) { + /* + * This frame is sent out as a single frame. + * Use hardware retry status for this frame. + */ + bf->bf_retries = ds->ds_txstat.ts_longretry; + if (ds->ds_txstat.ts_status & ATH9K_TXERR_XRETRY) + bf->bf_isxretried = 1; + nbad = 0; + } else { + nbad = ath_tx_num_badfrms(sc, bf, txok); + } + skb = bf->bf_mpdu; + tx_info = IEEE80211_SKB_CB(skb); + tx_info_priv = (struct ath_tx_info_priv *) + tx_info->driver_data[0]; + if (ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) + tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 && + (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0) { + if (ds->ds_txstat.ts_status == 0) + nacked++; + + if (bf->bf_isdata) { + if (isrifs) + tmp_ds = bf->bf_rifslast->bf_desc; + else + tmp_ds = ds; + memcpy(&tx_info_priv->tx, + &tmp_ds->ds_txstat, + sizeof(tx_info_priv->tx)); + tx_info_priv->n_frames = bf->bf_nframes; + tx_info_priv->n_bad_frames = nbad; + } + } + + /* + * Complete this transmit unit + */ + if (bf->bf_isampdu) + ath_tx_complete_aggr_rifs(sc, txq, bf, &bf_head, txok); + else + ath_tx_complete_buf(sc, bf, &bf_head, txok, 0); + + /* Wake up mac80211 queue */ + + spin_lock_bh(&txq->axq_lock); + if (txq->stopped && ath_txq_depth(sc, txq->axq_qnum) <= + (ATH_TXBUF - 20)) { + int qnum; + qnum = ath_get_mac80211_qnum(txq->axq_qnum, sc); + if (qnum != -1) { + ieee80211_wake_queue(sc->hw, qnum); + txq->stopped = 0; + } + + } + + /* + * schedule any pending packets if aggregation is enabled + */ + if (sc->sc_txaggr) + ath_txq_schedule(sc, txq); + spin_unlock_bh(&txq->axq_lock); + } + return nacked; +} + +static void ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_hal *ah = sc->sc_ah; + + (void) ath9k_hw_stoptxdma(ah, txq->axq_qnum); + DPRINTF(sc, ATH_DBG_XMIT, "%s: tx queue [%u] %x, link %p\n", + __func__, txq->axq_qnum, + ath9k_hw_gettxbuf(ah, txq->axq_qnum), txq->axq_link); +} + +/* Drain only the data queues */ + +static void ath_drain_txdataq(struct ath_softc *sc, bool retry_tx) +{ + struct ath_hal *ah = sc->sc_ah; + int i; + int npend = 0; + enum ath9k_ht_macmode ht_macmode = ath_cwm_macmode(sc); + + /* XXX return value */ + if (!sc->sc_invalid) { + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) { + ath_tx_stopdma(sc, &sc->sc_txq[i]); + + /* The TxDMA may not really be stopped. + * Double check the hal tx pending count */ + npend += ath9k_hw_numtxpending(ah, + sc->sc_txq[i].axq_qnum); + } + } + } + + if (npend) { + int status; + + /* TxDMA not stopped, reset the hal */ + DPRINTF(sc, ATH_DBG_XMIT, + "%s: Unable to stop TxDMA. Reset HAL!\n", __func__); + + spin_lock_bh(&sc->sc_resetlock); + if (!ath9k_hw_reset(ah, sc->sc_opmode, + &sc->sc_curchan, ht_macmode, + sc->sc_tx_chainmask, sc->sc_rx_chainmask, + sc->sc_ht_extprotspacing, true, &status)) { + + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to reset hardware; hal status %u\n", + __func__, + status); + } + spin_unlock_bh(&sc->sc_resetlock); + } + + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_draintxq(sc, &sc->sc_txq[i], retry_tx); + } +} + +/* Add a sub-frame to block ack window */ + +static void ath_tx_addto_baw(struct ath_softc *sc, + struct ath_atx_tid *tid, + struct ath_buf *bf) +{ + int index, cindex; + + if (bf->bf_isretried) + return; + + index = ATH_BA_INDEX(tid->seq_start, bf->bf_seqno); + cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1); + + ASSERT(tid->tx_buf[cindex] == NULL); + tid->tx_buf[cindex] = bf; + + if (index >= ((tid->baw_tail - tid->baw_head) & + (ATH_TID_MAX_BUFS - 1))) { + tid->baw_tail = cindex; + INCR(tid->baw_tail, ATH_TID_MAX_BUFS); + } +} + +/* + * Function to send an A-MPDU + * NB: must be called with txq lock held + */ + +static int ath_tx_send_ampdu(struct ath_softc *sc, + struct ath_txq *txq, + struct ath_atx_tid *tid, + struct list_head *bf_head, + struct ath_tx_control *txctl) +{ + struct ath_buf *bf; + struct sk_buff *skb; + struct ieee80211_tx_info *tx_info; + struct ath_tx_info_priv *tx_info_priv; + + BUG_ON(list_empty(bf_head)); + + bf = list_first_entry(bf_head, struct ath_buf, list); + bf->bf_isampdu = 1; + bf->bf_seqno = txctl->seqno; /* save seqno and tidno in buffer */ + bf->bf_tidno = txctl->tidno; + + /* + * Do not queue to h/w when any of the following conditions is true: + * - there are pending frames in software queue + * - the TID is currently paused for ADDBA/BAR request + * - seqno is not within block-ack window + * - h/w queue depth exceeds low water mark + */ + if (!list_empty(&tid->buf_q) || tid->paused || + !BAW_WITHIN(tid->seq_start, tid->baw_size, bf->bf_seqno) || + txq->axq_depth >= ATH_AGGR_MIN_QDEPTH) { + /* + * Add this frame to software queue for scheduling later + * for aggregation. + */ + list_splice_tail_init(bf_head, &tid->buf_q); + ath_tx_queue_tid(txq, tid); + return 0; + } + + skb = (struct sk_buff *)bf->bf_mpdu; + tx_info = IEEE80211_SKB_CB(skb); + tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0]; + memcpy(bf->bf_rcs, tx_info_priv->rcs, 4 * sizeof(tx_info_priv->rcs[0])); + + /* Add sub-frame to BAW */ + ath_tx_addto_baw(sc, tid, bf); + + /* Queue to h/w without aggregation */ + bf->bf_nframes = 1; + bf->bf_lastbf = bf->bf_lastfrm; /* one single frame */ + ath_buf_set_rate(sc, bf); + ath_tx_txqaddbuf(sc, txq, bf_head); + return 0; +} + +/* + * looks up the rate + * returns aggr limit based on lowest of the rates + */ + +static u32 ath_lookup_rate(struct ath_softc *sc, + struct ath_buf *bf) +{ + const struct ath9k_rate_table *rt = sc->sc_currates; + struct sk_buff *skb; + struct ieee80211_tx_info *tx_info; + struct ath_tx_info_priv *tx_info_priv; + u32 max_4ms_framelen, frame_length; + u16 aggr_limit, legacy = 0, maxampdu; + int i; + + + skb = (struct sk_buff *)bf->bf_mpdu; + tx_info = IEEE80211_SKB_CB(skb); + tx_info_priv = (struct ath_tx_info_priv *) + tx_info->driver_data[0]; + memcpy(bf->bf_rcs, + tx_info_priv->rcs, 4 * sizeof(tx_info_priv->rcs[0])); + + /* + * Find the lowest frame length among the rate series that will have a + * 4ms transmit duration. + * TODO - TXOP limit needs to be considered. + */ + max_4ms_framelen = ATH_AMPDU_LIMIT_MAX; + + for (i = 0; i < 4; i++) { + if (bf->bf_rcs[i].tries) { + frame_length = bf->bf_rcs[i].max_4ms_framelen; + + if (rt->info[bf->bf_rcs[i].rix].phy != PHY_HT) { + legacy = 1; + break; + } + + max_4ms_framelen = min(max_4ms_framelen, frame_length); + } + } + + /* + * limit aggregate size by the minimum rate if rate selected is + * not a probe rate, if rate selected is a probe rate then + * avoid aggregation of this packet. + */ + if (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE || legacy) + return 0; + + aggr_limit = min(max_4ms_framelen, + (u32)ATH_AMPDU_LIMIT_DEFAULT); + + /* + * h/w can accept aggregates upto 16 bit lengths (65535). + * The IE, however can hold upto 65536, which shows up here + * as zero. Ignore 65536 since we are constrained by hw. + */ + maxampdu = sc->sc_ht_info.maxampdu; + if (maxampdu) + aggr_limit = min(aggr_limit, maxampdu); + + return aggr_limit; +} + +/* + * returns the number of delimiters to be added to + * meet the minimum required mpdudensity. + * caller should make sure that the rate is HT rate . + */ + +static int ath_compute_num_delims(struct ath_softc *sc, + struct ath_buf *bf, + u16 frmlen) +{ + const struct ath9k_rate_table *rt = sc->sc_currates; + u32 nsymbits, nsymbols, mpdudensity; + u16 minlen; + u8 rc, flags, rix; + int width, half_gi, ndelim, mindelim; + + /* Select standard number of delimiters based on frame length alone */ + ndelim = ATH_AGGR_GET_NDELIM(frmlen); + + /* + * If encryption enabled, hardware requires some more padding between + * subframes. + * TODO - this could be improved to be dependent on the rate. + * The hardware can keep up at lower rates, but not higher rates + */ + if (bf->bf_keytype != ATH9K_KEY_TYPE_CLEAR) + ndelim += ATH_AGGR_ENCRYPTDELIM; + + /* + * Convert desired mpdu density from microeconds to bytes based + * on highest rate in rate series (i.e. first rate) to determine + * required minimum length for subframe. Take into account + * whether high rate is 20 or 40Mhz and half or full GI. + */ + mpdudensity = sc->sc_ht_info.mpdudensity; + + /* + * If there is no mpdu density restriction, no further calculation + * is needed. + */ + if (mpdudensity == 0) + return ndelim; + + rix = bf->bf_rcs[0].rix; + flags = bf->bf_rcs[0].flags; + rc = rt->info[rix].rateCode; + width = (flags & ATH_RC_CW40_FLAG) ? 1 : 0; + half_gi = (flags & ATH_RC_SGI_FLAG) ? 1 : 0; + + if (half_gi) + nsymbols = NUM_SYMBOLS_PER_USEC_HALFGI(mpdudensity); + else + nsymbols = NUM_SYMBOLS_PER_USEC(mpdudensity); + + if (nsymbols == 0) + nsymbols = 1; + + nsymbits = bits_per_symbol[HT_RC_2_MCS(rc)][width]; + minlen = (nsymbols * nsymbits) / BITS_PER_BYTE; + + /* Is frame shorter than required minimum length? */ + if (frmlen < minlen) { + /* Get the minimum number of delimiters required. */ + mindelim = (minlen - frmlen) / ATH_AGGR_DELIM_SZ; + ndelim = max(mindelim, ndelim); + } + + return ndelim; +} + +/* + * For aggregation from software buffer queue. + * NB: must be called with txq lock held + */ + +static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, + struct ath_atx_tid *tid, + struct list_head *bf_q, + struct ath_buf **bf_last, + struct aggr_rifs_param *param, + int *prev_frames) +{ +#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4) + struct ath_buf *bf, *tbf, *bf_first, *bf_prev = NULL; + struct list_head bf_head; + int rl = 0, nframes = 0, ndelim; + u16 aggr_limit = 0, al = 0, bpad = 0, + al_delta, h_baw = tid->baw_size / 2; + enum ATH_AGGR_STATUS status = ATH_AGGR_DONE; + int prev_al = 0, is_ds_rate = 0; + INIT_LIST_HEAD(&bf_head); + + BUG_ON(list_empty(&tid->buf_q)); + + bf_first = list_first_entry(&tid->buf_q, struct ath_buf, list); + + do { + bf = list_first_entry(&tid->buf_q, struct ath_buf, list); + + /* + * do not step over block-ack window + */ + if (!BAW_WITHIN(tid->seq_start, tid->baw_size, bf->bf_seqno)) { + status = ATH_AGGR_BAW_CLOSED; + break; + } + + if (!rl) { + aggr_limit = ath_lookup_rate(sc, bf); + rl = 1; + /* + * Is rate dual stream + */ + is_ds_rate = + (bf->bf_rcs[0].flags & ATH_RC_DS_FLAG) ? 1 : 0; + } + + /* + * do not exceed aggregation limit + */ + al_delta = ATH_AGGR_DELIM_SZ + bf->bf_frmlen; + + if (nframes && (aggr_limit < + (al + bpad + al_delta + prev_al))) { + status = ATH_AGGR_LIMITED; + break; + } + + /* + * do not exceed subframe limit + */ + if ((nframes + *prev_frames) >= + min((int)h_baw, ATH_AMPDU_SUBFRAME_DEFAULT)) { + status = ATH_AGGR_LIMITED; + break; + } + + /* + * add padding for previous frame to aggregation length + */ + al += bpad + al_delta; + + /* + * Get the delimiters needed to meet the MPDU + * density for this node. + */ + ndelim = ath_compute_num_delims(sc, bf_first, bf->bf_frmlen); + + bpad = PADBYTES(al_delta) + (ndelim << 2); + + bf->bf_next = NULL; + bf->bf_lastfrm->bf_desc->ds_link = 0; + + /* + * this packet is part of an aggregate + * - remove all descriptors belonging to this frame from + * software queue + * - add it to block ack window + * - set up descriptors for aggregation + */ + list_cut_position(&bf_head, &tid->buf_q, &bf->bf_lastfrm->list); + ath_tx_addto_baw(sc, tid, bf); + + list_for_each_entry(tbf, &bf_head, list) { + ath9k_hw_set11n_aggr_middle(sc->sc_ah, + tbf->bf_desc, ndelim); + } + + /* + * link buffers of this frame to the aggregate + */ + list_splice_tail_init(&bf_head, bf_q); + nframes++; + + if (bf_prev) { + bf_prev->bf_next = bf; + bf_prev->bf_lastfrm->bf_desc->ds_link = bf->bf_daddr; + } + bf_prev = bf; + +#ifdef AGGR_NOSHORT + /* + * terminate aggregation on a small packet boundary + */ + if (bf->bf_frmlen < ATH_AGGR_MINPLEN) { + status = ATH_AGGR_SHORTPKT; + break; + } +#endif + } while (!list_empty(&tid->buf_q)); + + bf_first->bf_al = al; + bf_first->bf_nframes = nframes; + *bf_last = bf_prev; + return status; +#undef PADBYTES +} + +/* + * process pending frames possibly doing a-mpdu aggregation + * NB: must be called with txq lock held + */ + +static void ath_tx_sched_aggr(struct ath_softc *sc, + struct ath_txq *txq, struct ath_atx_tid *tid) +{ + struct ath_buf *bf, *tbf, *bf_last, *bf_lastaggr = NULL; + enum ATH_AGGR_STATUS status; + struct list_head bf_q; + struct aggr_rifs_param param = {0, 0, 0, 0, NULL}; + int prev_frames = 0; + + do { + if (list_empty(&tid->buf_q)) + return; + + INIT_LIST_HEAD(&bf_q); + + status = ath_tx_form_aggr(sc, tid, &bf_q, &bf_lastaggr, ¶m, + &prev_frames); + + /* + * no frames picked up to be aggregated; block-ack + * window is not open + */ + if (list_empty(&bf_q)) + break; + + bf = list_first_entry(&bf_q, struct ath_buf, list); + bf_last = list_entry(bf_q.prev, struct ath_buf, list); + bf->bf_lastbf = bf_last; + + /* + * if only one frame, send as non-aggregate + */ + if (bf->bf_nframes == 1) { + ASSERT(bf->bf_lastfrm == bf_last); + + bf->bf_isaggr = 0; + /* + * clear aggr bits for every descriptor + * XXX TODO: is there a way to optimize it? + */ + list_for_each_entry(tbf, &bf_q, list) { + ath9k_hw_clr11n_aggr(sc->sc_ah, tbf->bf_desc); + } + + ath_buf_set_rate(sc, bf); + ath_tx_txqaddbuf(sc, txq, &bf_q); + continue; + } + + /* + * setup first desc with rate and aggr info + */ + bf->bf_isaggr = 1; + ath_buf_set_rate(sc, bf); + ath9k_hw_set11n_aggr_first(sc->sc_ah, bf->bf_desc, bf->bf_al); + + /* + * anchor last frame of aggregate correctly + */ + ASSERT(bf_lastaggr); + ASSERT(bf_lastaggr->bf_lastfrm == bf_last); + tbf = bf_lastaggr; + ath9k_hw_set11n_aggr_last(sc->sc_ah, tbf->bf_desc); + + /* XXX: We don't enter into this loop, consider removing this */ + while (!list_empty(&bf_q) && !list_is_last(&tbf->list, &bf_q)) { + tbf = list_entry(tbf->list.next, struct ath_buf, list); + ath9k_hw_set11n_aggr_last(sc->sc_ah, tbf->bf_desc); + } + + txq->axq_aggr_depth++; + + /* + * Normal aggregate, queue to hardware + */ + ath_tx_txqaddbuf(sc, txq, &bf_q); + + } while (txq->axq_depth < ATH_AGGR_MIN_QDEPTH && + status != ATH_AGGR_BAW_CLOSED); +} + +/* Called with txq lock held */ + +static void ath_tid_drain(struct ath_softc *sc, + struct ath_txq *txq, + struct ath_atx_tid *tid, + bool bh_flag) +{ + struct ath_buf *bf; + struct list_head bf_head; + INIT_LIST_HEAD(&bf_head); + + for (;;) { + if (list_empty(&tid->buf_q)) + break; + bf = list_first_entry(&tid->buf_q, struct ath_buf, list); + + list_cut_position(&bf_head, &tid->buf_q, &bf->bf_lastfrm->list); + + /* update baw for software retried frame */ + if (bf->bf_isretried) + ath_tx_update_baw(sc, tid, bf->bf_seqno); + + /* + * do not indicate packets while holding txq spinlock. + * unlock is intentional here + */ + if (likely(bh_flag)) + spin_unlock_bh(&txq->axq_lock); + else + spin_unlock(&txq->axq_lock); + + /* complete this sub-frame */ + ath_tx_complete_buf(sc, bf, &bf_head, 0, 0); + + if (likely(bh_flag)) + spin_lock_bh(&txq->axq_lock); + else + spin_lock(&txq->axq_lock); + } + + /* + * TODO: For frame(s) that are in the retry state, we will reuse the + * sequence number(s) without setting the retry bit. The + * alternative is to give up on these and BAR the receiver's window + * forward. + */ + tid->seq_next = tid->seq_start; + tid->baw_tail = tid->baw_head; +} + +/* + * Drain all pending buffers + * NB: must be called with txq lock held + */ + +static void ath_txq_drain_pending_buffers(struct ath_softc *sc, + struct ath_txq *txq, + bool bh_flag) +{ + struct ath_atx_ac *ac, *ac_tmp; + struct ath_atx_tid *tid, *tid_tmp; + + list_for_each_entry_safe(ac, ac_tmp, &txq->axq_acq, list) { + list_del(&ac->list); + ac->sched = false; + list_for_each_entry_safe(tid, tid_tmp, &ac->tid_q, list) { + list_del(&tid->list); + tid->sched = false; + ath_tid_drain(sc, txq, tid, bh_flag); + } + } +} + +static int ath_tx_start_dma(struct ath_softc *sc, + struct sk_buff *skb, + struct scatterlist *sg, + u32 n_sg, + struct ath_tx_control *txctl) +{ + struct ath_node *an = txctl->an; + struct ath_buf *bf = NULL; + struct list_head bf_head; + struct ath_desc *ds; + struct ath_hal *ah = sc->sc_ah; + struct ath_txq *txq = &sc->sc_txq[txctl->qnum]; + struct ath_tx_info_priv *tx_info_priv; + struct ath_rc_series *rcs; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + __le16 fc = hdr->frame_control; + + /* For each sglist entry, allocate an ath_buf for DMA */ + INIT_LIST_HEAD(&bf_head); + spin_lock_bh(&sc->sc_txbuflock); + if (unlikely(list_empty(&sc->sc_txbuf))) { + spin_unlock_bh(&sc->sc_txbuflock); + return -ENOMEM; + } + + bf = list_first_entry(&sc->sc_txbuf, struct ath_buf, list); + list_del(&bf->list); + spin_unlock_bh(&sc->sc_txbuflock); + + list_add_tail(&bf->list, &bf_head); + + /* set up this buffer */ + ATH_TXBUF_RESET(bf); + bf->bf_frmlen = txctl->frmlen; + bf->bf_isdata = ieee80211_is_data(fc); + bf->bf_isbar = ieee80211_is_back_req(fc); + bf->bf_ispspoll = ieee80211_is_pspoll(fc); + bf->bf_flags = txctl->flags; + bf->bf_shpreamble = sc->sc_flags & ATH_PREAMBLE_SHORT; + bf->bf_keytype = txctl->keytype; + tx_info_priv = (struct ath_tx_info_priv *)tx_info->driver_data[0]; + rcs = tx_info_priv->rcs; + bf->bf_rcs[0] = rcs[0]; + bf->bf_rcs[1] = rcs[1]; + bf->bf_rcs[2] = rcs[2]; + bf->bf_rcs[3] = rcs[3]; + bf->bf_node = an; + bf->bf_mpdu = skb; + bf->bf_buf_addr = sg_dma_address(sg); + + /* setup descriptor */ + ds = bf->bf_desc; + ds->ds_link = 0; + ds->ds_data = bf->bf_buf_addr; + + /* + * Save the DMA context in the first ath_buf + */ + copy_dma_mem_context(get_dma_mem_context(bf, bf_dmacontext), + get_dma_mem_context(txctl, dmacontext)); + + /* + * Formulate first tx descriptor with tx controls. + */ + ath9k_hw_set11n_txdesc(ah, + ds, + bf->bf_frmlen, /* frame length */ + txctl->atype, /* Atheros packet type */ + min(txctl->txpower, (u16)60), /* txpower */ + txctl->keyix, /* key cache index */ + txctl->keytype, /* key type */ + txctl->flags); /* flags */ + ath9k_hw_filltxdesc(ah, + ds, + sg_dma_len(sg), /* segment length */ + true, /* first segment */ + (n_sg == 1) ? true : false, /* last segment */ + ds); /* first descriptor */ + + bf->bf_lastfrm = bf; + bf->bf_ht = txctl->ht; + + spin_lock_bh(&txq->axq_lock); + + if (txctl->ht && sc->sc_txaggr) { + struct ath_atx_tid *tid = ATH_AN_2_TID(an, txctl->tidno); + if (ath_aggr_query(sc, an, txctl->tidno)) { + /* + * Try aggregation if it's a unicast data frame + * and the destination is HT capable. + */ + ath_tx_send_ampdu(sc, txq, tid, &bf_head, txctl); + } else { + /* + * Send this frame as regular when ADDBA exchange + * is neither complete nor pending. + */ + ath_tx_send_normal(sc, txq, tid, &bf_head); + } + } else { + bf->bf_lastbf = bf; + bf->bf_nframes = 1; + ath_buf_set_rate(sc, bf); + + if (ieee80211_is_back_req(fc)) { + /* This is required for resuming tid + * during BAR completion */ + bf->bf_tidno = txctl->tidno; + } + + if (is_multicast_ether_addr(hdr->addr1)) { + struct ath_vap *avp = sc->sc_vaps[txctl->if_id]; + + /* + * When servicing one or more stations in power-save + * mode (or) if there is some mcast data waiting on + * mcast queue (to prevent out of order delivery of + * mcast,bcast packets) multicast frames must be + * buffered until after the beacon. We use the private + * mcast queue for that. + */ + /* XXX? more bit in 802.11 frame header */ + spin_lock_bh(&avp->av_mcastq.axq_lock); + if (txctl->ps || avp->av_mcastq.axq_depth) + ath_tx_mcastqaddbuf(sc, + &avp->av_mcastq, &bf_head); + else + ath_tx_txqaddbuf(sc, txq, &bf_head); + spin_unlock_bh(&avp->av_mcastq.axq_lock); + } else + ath_tx_txqaddbuf(sc, txq, &bf_head); + } + spin_unlock_bh(&txq->axq_lock); + return 0; +} + +static void xmit_map_sg(struct ath_softc *sc, + struct sk_buff *skb, + dma_addr_t *pa, + struct ath_tx_control *txctl) +{ + struct ath_xmit_status tx_status; + struct ath_atx_tid *tid; + struct scatterlist sg; + + *pa = pci_map_single(sc->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); + + /* setup S/G list */ + memset(&sg, 0, sizeof(struct scatterlist)); + sg_dma_address(&sg) = *pa; + sg_dma_len(&sg) = skb->len; + + if (ath_tx_start_dma(sc, skb, &sg, 1, txctl) != 0) { + /* + * We have to do drop frame here. + */ + pci_unmap_single(sc->pdev, *pa, skb->len, PCI_DMA_TODEVICE); + + tx_status.retries = 0; + tx_status.flags = ATH_TX_ERROR; + + if (txctl->ht && sc->sc_txaggr) { + /* Reclaim the seqno. */ + tid = ATH_AN_2_TID((struct ath_node *) + txctl->an, txctl->tidno); + DECR(tid->seq_next, IEEE80211_SEQ_MAX); + } + ath_tx_complete(sc, skb, &tx_status, txctl->an); + } +} + +/* Initialize TX queue and h/w */ + +int ath_tx_init(struct ath_softc *sc, int nbufs) +{ + int error = 0; + + do { + spin_lock_init(&sc->sc_txbuflock); + + /* Setup tx descriptors */ + error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf, + "tx", nbufs * ATH_FRAG_PER_MSDU, ATH_TXDESC); + if (error != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: failed to allocate tx descriptors: %d\n", + __func__, error); + break; + } + + /* XXX allocate beacon state together with vap */ + error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf, + "beacon", ATH_BCBUF, 1); + if (error != 0) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: failed to allocate " + "beacon descripotrs: %d\n", + __func__, error); + break; + } + + } while (0); + + if (error != 0) + ath_tx_cleanup(sc); + + return error; +} + +/* Reclaim all tx queue resources */ + +int ath_tx_cleanup(struct ath_softc *sc) +{ + /* cleanup beacon descriptors */ + if (sc->sc_bdma.dd_desc_len != 0) + ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf); + + /* cleanup tx descriptors */ + if (sc->sc_txdma.dd_desc_len != 0) + ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf); + + return 0; +} + +/* Setup a h/w transmit queue */ + +struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) +{ + struct ath_hal *ah = sc->sc_ah; + struct ath9k_txq_info qi; + int qnum; + + memzero(&qi, sizeof(qi)); + qi.tqi_subtype = subtype; + qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT; + qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT; + qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT; + qi.tqi_compBuf = 0; + + /* + * Enable interrupts only for EOL and DESC conditions. + * We mark tx descriptors to receive a DESC interrupt + * when a tx queue gets deep; otherwise waiting for the + * EOL to reap descriptors. Note that this is done to + * reduce interrupt load and this only defers reaping + * descriptors, never transmitting frames. Aside from + * reducing interrupts this also permits more concurrency. + * The only potential downside is if the tx queue backs + * up in which case the top half of the kernel may backup + * due to a lack of tx descriptors. + * + * The UAPSD queue is an exception, since we take a desc- + * based intr on the EOSP frames. + */ + if (qtype == ATH9K_TX_QUEUE_UAPSD) + qi.tqi_qflags = TXQ_FLAG_TXDESCINT_ENABLE; + else + qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | + TXQ_FLAG_TXDESCINT_ENABLE; + qnum = ath9k_hw_setuptxqueue(ah, qtype, &qi); + if (qnum == -1) { + /* + * NB: don't print a message, this happens + * normally on parts with too few tx queues + */ + return NULL; + } + if (qnum >= ARRAY_SIZE(sc->sc_txq)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: hal qnum %u out of range, max %u!\n", + __func__, qnum, (unsigned int)ARRAY_SIZE(sc->sc_txq)); + ath9k_hw_releasetxqueue(ah, qnum); + return NULL; + } + if (!ATH_TXQ_SETUP(sc, qnum)) { + struct ath_txq *txq = &sc->sc_txq[qnum]; + + txq->axq_qnum = qnum; + txq->axq_link = NULL; + INIT_LIST_HEAD(&txq->axq_q); + INIT_LIST_HEAD(&txq->axq_acq); + spin_lock_init(&txq->axq_lock); + txq->axq_depth = 0; + txq->axq_aggr_depth = 0; + txq->axq_totalqueued = 0; + txq->axq_intrcnt = 0; + txq->axq_linkbuf = NULL; + sc->sc_txqsetup |= 1<sc_txq[qnum]; +} + +/* Reclaim resources for a setup queue */ + +void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq) +{ + ath9k_hw_releasetxqueue(sc->sc_ah, txq->axq_qnum); + sc->sc_txqsetup &= ~(1<axq_qnum); +} + +/* + * Setup a hardware data transmit queue for the specified + * access control. The hal may not support all requested + * queues in which case it will return a reference to a + * previously setup queue. We record the mapping from ac's + * to h/w queues for use by ath_tx_start and also track + * the set of h/w queues being used to optimize work in the + * transmit interrupt handler and related routines. + */ + +int ath_tx_setup(struct ath_softc *sc, int haltype) +{ + struct ath_txq *txq; + + if (haltype >= ARRAY_SIZE(sc->sc_haltype2q)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: HAL AC %u out of range, max %zu!\n", + __func__, haltype, ARRAY_SIZE(sc->sc_haltype2q)); + return 0; + } + txq = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, haltype); + if (txq != NULL) { + sc->sc_haltype2q[haltype] = txq->axq_qnum; + return 1; + } else + return 0; +} + +int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype) +{ + int qnum; + + switch (qtype) { + case ATH9K_TX_QUEUE_DATA: + if (haltype >= ARRAY_SIZE(sc->sc_haltype2q)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: HAL AC %u out of range, max %zu!\n", + __func__, + haltype, ARRAY_SIZE(sc->sc_haltype2q)); + return -1; + } + qnum = sc->sc_haltype2q[haltype]; + break; + case ATH9K_TX_QUEUE_BEACON: + qnum = sc->sc_bhalq; + break; + case ATH9K_TX_QUEUE_CAB: + qnum = sc->sc_cabq->axq_qnum; + break; + default: + qnum = -1; + } + return qnum; +} + +/* Update parameters for a transmit queue */ + +int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_txq_info *qi0) +{ + struct ath_hal *ah = sc->sc_ah; + int error = 0; + struct ath9k_txq_info qi; + + if (qnum == sc->sc_bhalq) { + /* + * XXX: for beacon queue, we just save the parameter. + * It will be picked up by ath_beaconq_config when + * it's necessary. + */ + sc->sc_beacon_qi = *qi0; + return 0; + } + + ASSERT(sc->sc_txq[qnum].axq_qnum == qnum); + + ath9k_hw_gettxqueueprops(ah, qnum, &qi); + qi.tqi_aifs = qi0->tqi_aifs; + qi.tqi_cwmin = qi0->tqi_cwmin; + qi.tqi_cwmax = qi0->tqi_cwmax; + qi.tqi_burstTime = qi0->tqi_burstTime; + qi.tqi_readyTime = qi0->tqi_readyTime; + + if (!ath9k_hw_settxqueueprops(ah, qnum, &qi)) { + DPRINTF(sc, ATH_DBG_FATAL, + "%s: unable to update hardware queue %u!\n", + __func__, qnum); + error = -EIO; + } else { + ath9k_hw_resettxqueue(ah, qnum); /* push to h/w */ + } + + return error; +} + +int ath_cabq_update(struct ath_softc *sc) +{ + struct ath9k_txq_info qi; + int qnum = sc->sc_cabq->axq_qnum; + struct ath_beacon_config conf; + + ath9k_hw_gettxqueueprops(sc->sc_ah, qnum, &qi); + /* + * Ensure the readytime % is within the bounds. + */ + if (sc->sc_config.cabqReadytime < ATH9K_READY_TIME_LO_BOUND) + sc->sc_config.cabqReadytime = ATH9K_READY_TIME_LO_BOUND; + else if (sc->sc_config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND) + sc->sc_config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND; + + ath_get_beaconconfig(sc, ATH_IF_ID_ANY, &conf); + qi.tqi_readyTime = + (conf.beacon_interval * sc->sc_config.cabqReadytime) / 100; + ath_txq_update(sc, qnum, &qi); + + return 0; +} + +int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb) +{ + struct ath_tx_control txctl; + int error = 0; + + error = ath_tx_prepare(sc, skb, &txctl); + if (error == 0) + /* + * Start DMA mapping. + * ath_tx_start_dma() will be called either synchronously + * or asynchrounsly once DMA is complete. + */ + xmit_map_sg(sc, skb, + get_dma_mem_context(&txctl, dmacontext), + &txctl); + else + ath_node_put(sc, txctl.an, ATH9K_BH_STATUS_CHANGE); + + /* failed packets will be dropped by the caller */ + return error; +} + +/* Deferred processing of transmit interrupt */ + +void ath_tx_tasklet(struct ath_softc *sc) +{ + u64 tsf = ath9k_hw_gettsf64(sc->sc_ah); + int i, nacked = 0; + u32 qcumask = ((1 << ATH9K_NUM_TX_QUEUES) - 1); + + ath9k_hw_gettxintrtxqs(sc->sc_ah, &qcumask); + + /* + * Process each active queue. + */ + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i) && (qcumask & (1 << i))) + nacked += ath_tx_processq(sc, &sc->sc_txq[i]); + } + if (nacked) + sc->sc_lastrx = tsf; +} + +void ath_tx_draintxq(struct ath_softc *sc, + struct ath_txq *txq, bool retry_tx) +{ + struct ath_buf *bf, *lastbf; + struct list_head bf_head; + + INIT_LIST_HEAD(&bf_head); + + /* + * NB: this assumes output has been stopped and + * we do not need to block ath_tx_tasklet + */ + for (;;) { + spin_lock_bh(&txq->axq_lock); + + if (list_empty(&txq->axq_q)) { + txq->axq_link = NULL; + txq->axq_linkbuf = NULL; + spin_unlock_bh(&txq->axq_lock); + break; + } + + bf = list_first_entry(&txq->axq_q, struct ath_buf, list); + + if (bf->bf_status & ATH_BUFSTATUS_STALE) { + list_del(&bf->list); + spin_unlock_bh(&txq->axq_lock); + + spin_lock_bh(&sc->sc_txbuflock); + list_add_tail(&bf->list, &sc->sc_txbuf); + spin_unlock_bh(&sc->sc_txbuflock); + continue; + } + + lastbf = bf->bf_lastbf; + if (!retry_tx) + lastbf->bf_desc->ds_txstat.ts_flags = + ATH9K_TX_SW_ABORTED; + + /* remove ath_buf's of the same mpdu from txq */ + list_cut_position(&bf_head, &txq->axq_q, &lastbf->list); + txq->axq_depth--; + + spin_unlock_bh(&txq->axq_lock); + + if (bf->bf_isampdu) + ath_tx_complete_aggr_rifs(sc, txq, bf, &bf_head, 0); + else + ath_tx_complete_buf(sc, bf, &bf_head, 0, 0); + } + + /* flush any pending frames if aggregation is enabled */ + if (sc->sc_txaggr) { + if (!retry_tx) { + spin_lock_bh(&txq->axq_lock); + ath_txq_drain_pending_buffers(sc, txq, + ATH9K_BH_STATUS_CHANGE); + spin_unlock_bh(&txq->axq_lock); + } + } +} + +/* Drain the transmit queues and reclaim resources */ + +void ath_draintxq(struct ath_softc *sc, bool retry_tx) +{ + /* stop beacon queue. The beacon will be freed when + * we go to INIT state */ + if (!sc->sc_invalid) { + (void) ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq); + DPRINTF(sc, ATH_DBG_XMIT, "%s: beacon queue %x\n", __func__, + ath9k_hw_gettxbuf(sc->sc_ah, sc->sc_bhalq)); + } + + ath_drain_txdataq(sc, retry_tx); +} + +u32 ath_txq_depth(struct ath_softc *sc, int qnum) +{ + return sc->sc_txq[qnum].axq_depth; +} + +u32 ath_txq_aggr_depth(struct ath_softc *sc, int qnum) +{ + return sc->sc_txq[qnum].axq_aggr_depth; +} + +/* Check if an ADDBA is required. A valid node must be passed. */ +enum ATH_AGGR_CHECK ath_tx_aggr_check(struct ath_softc *sc, + struct ath_node *an, + u8 tidno) +{ + struct ath_atx_tid *txtid; + DECLARE_MAC_BUF(mac); + + if (!sc->sc_txaggr) + return AGGR_NOT_REQUIRED; + + /* ADDBA exchange must be completed before sending aggregates */ + txtid = ATH_AN_2_TID(an, tidno); + + if (txtid->addba_exchangecomplete) + return AGGR_EXCHANGE_DONE; + + if (txtid->cleanup_inprogress) + return AGGR_CLEANUP_PROGRESS; + + if (txtid->addba_exchangeinprogress) + return AGGR_EXCHANGE_PROGRESS; + + if (!txtid->addba_exchangecomplete) { + if (!txtid->addba_exchangeinprogress && + (txtid->addba_exchangeattempts < ADDBA_EXCHANGE_ATTEMPTS)) { + txtid->addba_exchangeattempts++; + return AGGR_REQUIRED; + } + } + + return AGGR_NOT_REQUIRED; +} + +/* Start TX aggregation */ + +int ath_tx_aggr_start(struct ath_softc *sc, + const u8 *addr, + u16 tid, + u16 *ssn) +{ + struct ath_atx_tid *txtid; + struct ath_node *an; + + spin_lock_bh(&sc->node_lock); + an = ath_node_find(sc, (u8 *) addr); + spin_unlock_bh(&sc->node_lock); + + if (!an) { + DPRINTF(sc, ATH_DBG_AGGR, + "%s: Node not found to initialize " + "TX aggregation\n", __func__); + return -1; + } + + if (sc->sc_txaggr) { + txtid = ATH_AN_2_TID(an, tid); + txtid->addba_exchangeinprogress = 1; + ath_tx_pause_tid(sc, txtid); + } + + return 0; +} + +/* Stop tx aggregation */ + +int ath_tx_aggr_stop(struct ath_softc *sc, + const u8 *addr, + u16 tid) +{ + struct ath_node *an; + + spin_lock_bh(&sc->node_lock); + an = ath_node_find(sc, (u8 *) addr); + spin_unlock_bh(&sc->node_lock); + + if (!an) { + DPRINTF(sc, ATH_DBG_AGGR, + "%s: TX aggr stop for non-existent node\n", __func__); + return -1; + } + + ath_tx_aggr_teardown(sc, an, tid); + return 0; +} + +/* + * Performs transmit side cleanup when TID changes from aggregated to + * unaggregated. + * - Pause the TID and mark cleanup in progress + * - Discard all retry frames from the s/w queue. + */ + +void ath_tx_aggr_teardown(struct ath_softc *sc, + struct ath_node *an, u8 tid) +{ + struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid); + struct ath_txq *txq = &sc->sc_txq[txtid->ac->qnum]; + struct ath_buf *bf; + struct list_head bf_head; + INIT_LIST_HEAD(&bf_head); + + DPRINTF(sc, ATH_DBG_AGGR, "%s: teardown TX aggregation\n", __func__); + + if (txtid->cleanup_inprogress) /* cleanup is in progress */ + return; + + if (!txtid->addba_exchangecomplete) { + txtid->addba_exchangeattempts = 0; + return; + } + + /* TID must be paused first */ + ath_tx_pause_tid(sc, txtid); + + /* drop all software retried frames and mark this TID */ + spin_lock_bh(&txq->axq_lock); + while (!list_empty(&txtid->buf_q)) { + bf = list_first_entry(&txtid->buf_q, struct ath_buf, list); + if (!bf->bf_isretried) { + /* + * NB: it's based on the assumption that + * software retried frame will always stay + * at the head of software queue. + */ + break; + } + list_cut_position(&bf_head, + &txtid->buf_q, &bf->bf_lastfrm->list); + ath_tx_update_baw(sc, txtid, bf->bf_seqno); + + /* complete this sub-frame */ + ath_tx_complete_buf(sc, bf, &bf_head, 0, 0); + } + + if (txtid->baw_head != txtid->baw_tail) { + spin_unlock_bh(&txq->axq_lock); + txtid->cleanup_inprogress = true; + } else { + txtid->addba_exchangecomplete = 0; + txtid->addba_exchangeattempts = 0; + spin_unlock_bh(&txq->axq_lock); + ath_tx_flush_tid(sc, txtid); + } +} + +/* + * Tx scheduling logic + * NB: must be called with txq lock held + */ + +void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) +{ + struct ath_atx_ac *ac; + struct ath_atx_tid *tid; + + /* nothing to schedule */ + if (list_empty(&txq->axq_acq)) + return; + /* + * get the first node/ac pair on the queue + */ + ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list); + list_del(&ac->list); + ac->sched = false; + + /* + * process a single tid per destination + */ + do { + /* nothing to schedule */ + if (list_empty(&ac->tid_q)) + return; + + tid = list_first_entry(&ac->tid_q, struct ath_atx_tid, list); + list_del(&tid->list); + tid->sched = false; + + if (tid->paused) /* check next tid to keep h/w busy */ + continue; + + if (!(tid->an->an_smmode == ATH_SM_PWRSAV_DYNAMIC) || + ((txq->axq_depth % 2) == 0)) { + ath_tx_sched_aggr(sc, txq, tid); + } + + /* + * add tid to round-robin queue if more frames + * are pending for the tid + */ + if (!list_empty(&tid->buf_q)) + ath_tx_queue_tid(txq, tid); + + /* only schedule one TID at a time */ + break; + } while (!list_empty(&ac->tid_q)); + + /* + * schedule AC if more TIDs need processing + */ + if (!list_empty(&ac->tid_q)) { + /* + * add dest ac to txq if not already added + */ + if (!ac->sched) { + ac->sched = true; + list_add_tail(&ac->list, &txq->axq_acq); + } + } +} + +/* Initialize per-node transmit state */ + +void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) +{ + if (sc->sc_txaggr) { + struct ath_atx_tid *tid; + struct ath_atx_ac *ac; + int tidno, acno; + + sc->sc_ht_info.maxampdu = ATH_AMPDU_LIMIT_DEFAULT; + + /* + * Init per tid tx state + */ + for (tidno = 0, tid = &an->an_aggr.tx.tid[tidno]; + tidno < WME_NUM_TID; + tidno++, tid++) { + tid->an = an; + tid->tidno = tidno; + tid->seq_start = tid->seq_next = 0; + tid->baw_size = WME_MAX_BA; + tid->baw_head = tid->baw_tail = 0; + tid->sched = false; + tid->paused = false; + tid->cleanup_inprogress = false; + INIT_LIST_HEAD(&tid->buf_q); + + acno = TID_TO_WME_AC(tidno); + tid->ac = &an->an_aggr.tx.ac[acno]; + + /* ADDBA state */ + tid->addba_exchangecomplete = 0; + tid->addba_exchangeinprogress = 0; + tid->addba_exchangeattempts = 0; + } + + /* + * Init per ac tx state + */ + for (acno = 0, ac = &an->an_aggr.tx.ac[acno]; + acno < WME_NUM_AC; acno++, ac++) { + ac->sched = false; + INIT_LIST_HEAD(&ac->tid_q); + + switch (acno) { + case WME_AC_BE: + ac->qnum = ath_tx_get_qnum(sc, + ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE); + break; + case WME_AC_BK: + ac->qnum = ath_tx_get_qnum(sc, + ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BK); + break; + case WME_AC_VI: + ac->qnum = ath_tx_get_qnum(sc, + ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_VI); + break; + case WME_AC_VO: + ac->qnum = ath_tx_get_qnum(sc, + ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_VO); + break; + } + } + } +} + +/* Cleanupthe pending buffers for the node. */ + +void ath_tx_node_cleanup(struct ath_softc *sc, + struct ath_node *an, bool bh_flag) +{ + int i; + struct ath_atx_ac *ac, *ac_tmp; + struct ath_atx_tid *tid, *tid_tmp; + struct ath_txq *txq; + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (ATH_TXQ_SETUP(sc, i)) { + txq = &sc->sc_txq[i]; + + if (likely(bh_flag)) + spin_lock_bh(&txq->axq_lock); + else + spin_lock(&txq->axq_lock); + + list_for_each_entry_safe(ac, + ac_tmp, &txq->axq_acq, list) { + tid = list_first_entry(&ac->tid_q, + struct ath_atx_tid, list); + if (tid && tid->an != an) + continue; + list_del(&ac->list); + ac->sched = false; + + list_for_each_entry_safe(tid, + tid_tmp, &ac->tid_q, list) { + list_del(&tid->list); + tid->sched = false; + ath_tid_drain(sc, txq, tid, bh_flag); + tid->addba_exchangecomplete = 0; + tid->addba_exchangeattempts = 0; + tid->cleanup_inprogress = false; + } + } + + if (likely(bh_flag)) + spin_unlock_bh(&txq->axq_lock); + else + spin_unlock(&txq->axq_lock); + } + } +} + +/* Cleanup per node transmit state */ + +void ath_tx_node_free(struct ath_softc *sc, struct ath_node *an) +{ + if (sc->sc_txaggr) { + struct ath_atx_tid *tid; + int tidno, i; + + /* Init per tid rx state */ + for (tidno = 0, tid = &an->an_aggr.tx.tid[tidno]; + tidno < WME_NUM_TID; + tidno++, tid++) { + + for (i = 0; i < ATH_TID_MAX_BUFS; i++) + ASSERT(tid->tx_buf[i] == NULL); + } + } +} -- cgit v1.2.1 From b08cbcd4546445740c2a04291204b56f8baf7be2 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 5 Aug 2008 22:06:51 +0300 Subject: ath9k: work around gcc ICEs This patch works around an internal compiler error (gcc bug #37014) in all gcc 4.2 compilers and the gcc 4.3 series up to at least 4.3.1 on at least powerpc and mips. Many thanks to Andrew Pinski for analyzing the gcc bug. Signed-off-by: Adrian Bunk Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/hw.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index 1f6f3934d379..63d0ead1c412 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -4801,7 +4801,11 @@ static void ath9k_hw_9280_spur_mitigate(struct ath_hal *ah, for (i = 0; i < 123; i++) { if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { - if ((abs(cur_vit_mask - bin)) < 75) + + /* workaround for gcc bug #37014 */ + volatile int tmp = abs(cur_vit_mask - bin); + + if (tmp < 75) mask_amt = 1; else mask_amt = 0; -- cgit v1.2.1 From 60b67f519213cf6d59236d065b0953962b56abca Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 7 Aug 2008 10:52:38 +0530 Subject: ath9k: Cleanup data structures related to HW capabilities Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/ath9k.h | 183 +++++++++--------- drivers/net/wireless/ath9k/beacon.c | 10 +- drivers/net/wireless/ath9k/core.c | 58 +++--- drivers/net/wireless/ath9k/hw.c | 369 ++++++++++++++++++------------------ drivers/net/wireless/ath9k/main.c | 6 +- drivers/net/wireless/ath9k/rc.c | 2 +- drivers/net/wireless/ath9k/recv.c | 4 +- drivers/net/wireless/ath9k/regd.c | 14 +- drivers/net/wireless/ath9k/xmit.c | 4 +- 9 files changed, 330 insertions(+), 320 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index dc1da64d2d7c..71027dcdcafc 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h @@ -147,94 +147,95 @@ struct ath_desc { #define ATH9K_RXDESC_INTREQ 0x0020 -enum hal_capability_type { - HAL_CAP_CIPHER = 0, - HAL_CAP_TKIP_MIC, - HAL_CAP_TKIP_SPLIT, - HAL_CAP_PHYCOUNTERS, - HAL_CAP_DIVERSITY, - HAL_CAP_PSPOLL, - HAL_CAP_TXPOW, - HAL_CAP_PHYDIAG, - HAL_CAP_MCAST_KEYSRCH, - HAL_CAP_TSF_ADJUST, - HAL_CAP_WME_TKIPMIC, - HAL_CAP_RFSILENT, - HAL_CAP_ANT_CFG_2GHZ, - HAL_CAP_ANT_CFG_5GHZ +enum ath9k_hw_caps { + ATH9K_HW_CAP_CHAN_SPREAD = BIT(0), + ATH9K_HW_CAP_MIC_AESCCM = BIT(1), + ATH9K_HW_CAP_MIC_CKIP = BIT(2), + ATH9K_HW_CAP_MIC_TKIP = BIT(3), + ATH9K_HW_CAP_CIPHER_AESCCM = BIT(4), + ATH9K_HW_CAP_CIPHER_CKIP = BIT(5), + ATH9K_HW_CAP_CIPHER_TKIP = BIT(6), + ATH9K_HW_CAP_VEOL = BIT(7), + ATH9K_HW_CAP_BSSIDMASK = BIT(8), + ATH9K_HW_CAP_MCAST_KEYSEARCH = BIT(9), + ATH9K_HW_CAP_CHAN_HALFRATE = BIT(10), + ATH9K_HW_CAP_CHAN_QUARTERRATE = BIT(11), + ATH9K_HW_CAP_HT = BIT(12), + ATH9K_HW_CAP_GTT = BIT(13), + ATH9K_HW_CAP_FASTCC = BIT(14), + ATH9K_HW_CAP_RFSILENT = BIT(15), + ATH9K_HW_CAP_WOW = BIT(16), + ATH9K_HW_CAP_CST = BIT(17), + ATH9K_HW_CAP_ENHANCEDPM = BIT(18), + ATH9K_HW_CAP_AUTOSLEEP = BIT(19), + ATH9K_HW_CAP_4KB_SPLITTRANS = BIT(20), + ATH9K_HW_CAP_WOW_MATCHPATTERN_EXACT = BIT(21), }; -struct hal_capabilities { - u32 halChanSpreadSupport:1, - halChapTuningSupport:1, - halMicAesCcmSupport:1, - halMicCkipSupport:1, - halMicTkipSupport:1, - halCipherAesCcmSupport:1, - halCipherCkipSupport:1, - halCipherTkipSupport:1, - halVEOLSupport:1, - halBssIdMaskSupport:1, - halMcastKeySrchSupport:1, - halTsfAddSupport:1, - halChanHalfRate:1, - halChanQuarterRate:1, - halHTSupport:1, - halGTTSupport:1, - halFastCCSupport:1, - halRfSilentSupport:1, - halWowSupport:1, - halCSTSupport:1, - halEnhancedPmSupport:1, - halAutoSleepSupport:1, - hal4kbSplitTransSupport:1, - halWowMatchPatternExact:1; - u32 halWirelessModes; - u16 halTotalQueues; - u16 halKeyCacheSize; - u16 halLow5GhzChan, halHigh5GhzChan; - u16 halLow2GhzChan, halHigh2GhzChan; - u16 halNumMRRetries; - u16 halRtsAggrLimit; - u8 halTxChainMask; - u8 halRxChainMask; - u16 halTxTrigLevelMax; - u16 halRegCap; - u8 halNumGpioPins; - u8 halNumAntCfg2GHz; - u8 halNumAntCfg5GHz; +enum ath9k_capability_type { + ATH9K_CAP_CIPHER = 0, + ATH9K_CAP_TKIP_MIC, + ATH9K_CAP_TKIP_SPLIT, + ATH9K_CAP_PHYCOUNTERS, + ATH9K_CAP_DIVERSITY, + ATH9K_CAP_TXPOW, + ATH9K_CAP_PHYDIAG, + ATH9K_CAP_MCAST_KEYSRCH, + ATH9K_CAP_TSF_ADJUST, + ATH9K_CAP_WME_TKIPMIC, + ATH9K_CAP_RFSILENT, + ATH9K_CAP_ANT_CFG_2GHZ, + ATH9K_CAP_ANT_CFG_5GHZ }; -struct hal_ops_config { - int ath_hal_dma_beacon_response_time; - int ath_hal_sw_beacon_response_time; - int ath_hal_additional_swba_backoff; - int ath_hal_6mb_ack; - int ath_hal_cwmIgnoreExtCCA; - u8 ath_hal_pciePowerSaveEnable; - u8 ath_hal_pcieL1SKPEnable; - u8 ath_hal_pcieClockReq; - u32 ath_hal_pcieWaen; - int ath_hal_pciePowerReset; - u8 ath_hal_pcieRestore; - u8 ath_hal_analogShiftReg; - u8 ath_hal_htEnable; - u32 ath_hal_ofdmTrigLow; - u32 ath_hal_ofdmTrigHigh; - u32 ath_hal_cckTrigHigh; - u32 ath_hal_cckTrigLow; - u32 ath_hal_enableANI; - u8 ath_hal_noiseImmunityLvl; - u32 ath_hal_ofdmWeakSigDet; - u32 ath_hal_cckWeakSigThr; - u8 ath_hal_spurImmunityLvl; - u8 ath_hal_firStepLvl; - int8_t ath_hal_rssiThrHigh; - int8_t ath_hal_rssiThrLow; - u16 ath_hal_diversityControl; - u16 ath_hal_antennaSwitchSwap; - int ath_hal_serializeRegMode; - int ath_hal_intrMitigation; +struct ath9k_hw_capabilities { + u32 hw_caps; /* ATH9K_HW_CAP_* from ath9k_hw_caps */ + u32 wireless_modes; + u16 total_queues; + u16 keycache_size; + u16 low_5ghz_chan, high_5ghz_chan; + u16 low_2ghz_chan, high_2ghz_chan; + u16 num_mr_retries; + u16 rts_aggr_limit; + u8 tx_chainmask; + u8 rx_chainmask; + u16 tx_triglevel_max; + u16 reg_cap; + u8 num_gpio_pins; + u8 num_antcfg_2ghz; + u8 num_antcfg_5ghz; +}; + +struct ath9k_ops_config { + int dma_beacon_response_time; + int sw_beacon_response_time; + int additional_swba_backoff; + int ack_6mb; + int cwm_ignore_extcca; + u8 pcie_powersave_enable; + u8 pcie_l1skp_enable; + u8 pcie_clock_req; + u32 pcie_waen; + int pcie_power_reset; + u8 pcie_restore; + u8 analog_shiftreg; + u8 ht_enable; + u32 ofdm_trig_low; + u32 ofdm_trig_high; + u32 cck_trig_high; + u32 cck_trig_low; + u32 enable_ani; + u8 noise_immunity_level; + u32 ofdm_weaksignal_det; + u32 cck_weaksignal_thr; + u8 spur_immunity_level; + u8 firstep_level; + int8_t rssi_thr_high; + int8_t rssi_thr_low; + u16 diversity_control; + u16 antenna_switch_swap; + int serialize_regmode; + int intr_mitigation; #define SPUR_DISABLE 0 #define SPUR_ENABLE_IOCTL 1 #define SPUR_ENABLE_EEPROM 2 @@ -246,8 +247,8 @@ struct hal_ops_config { #define AR_BASE_FREQ_5GHZ 4900 #define AR_SPUR_FEEQ_BOUND_HT40 19 #define AR_SPUR_FEEQ_BOUND_HT20 10 - int ath_hal_spurMode; - u16 ath_hal_spurChans[AR_EEPROM_MODAL_SPURS][2]; + int spurmode; + u16 spurchans[AR_EEPROM_MODAL_SPURS][2]; }; enum ath9k_tx_queue { @@ -815,8 +816,8 @@ struct ath_hal { u8 ah_decompMask[ATH9K_DECOMP_MASK_SIZE]; u32 ah_flags; enum ath9k_opmode ah_opmode; - struct hal_ops_config ah_config; - struct hal_capabilities ah_caps; + struct ath9k_ops_config ah_config; + struct ath9k_hw_capabilities ah_caps; int16_t ah_powerLimit; u16 ah_maxPowerLevel; u32 ah_tpScale; @@ -878,7 +879,7 @@ struct chan_centers { }; int ath_hal_getcapability(struct ath_hal *ah, - enum hal_capability_type type, + enum ath9k_capability_type type, u32 capability, u32 *result); const struct ath9k_rate_table *ath9k_hw_getratetable(struct ath_hal *ah, @@ -947,11 +948,11 @@ void ath9k_hw_set11nmac2040(struct ath_hal *ah, enum ath9k_ht_macmode mode); bool ath9k_hw_phycounters(struct ath_hal *ah); bool ath9k_hw_keyreset(struct ath_hal *ah, u16 entry); bool ath9k_hw_getcapability(struct ath_hal *ah, - enum hal_capability_type type, + enum ath9k_capability_type type, u32 capability, u32 *result); bool ath9k_hw_setcapability(struct ath_hal *ah, - enum hal_capability_type type, + enum ath9k_capability_type type, u32 capability, u32 setting, int *status); diff --git a/drivers/net/wireless/ath9k/beacon.c b/drivers/net/wireless/ath9k/beacon.c index 00993f828c5c..ba3dd253e963 100644 --- a/drivers/net/wireless/ath9k/beacon.c +++ b/drivers/net/wireless/ath9k/beacon.c @@ -85,7 +85,8 @@ static void ath_beacon_setup(struct ath_softc *sc, flags = ATH9K_TXDESC_NOACK; - if (sc->sc_opmode == ATH9K_M_IBSS && ah->ah_caps.halVEOLSupport) { + if (sc->sc_opmode == ATH9K_M_IBSS && + (ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL)) { ds->ds_link = bf->bf_daddr; /* self-linked */ flags |= ATH9K_TXDESC_VEOL; /* Let hardware handle antenna switching. */ @@ -375,7 +376,7 @@ int ath_beacon_alloc(struct ath_softc *sc, int if_id) list_del(&avp->av_bcbuf->list); if (sc->sc_opmode == ATH9K_M_HOSTAP || - !sc->sc_ah->ah_caps.halVEOLSupport) { + !(sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL)) { int slot; /* * Assign the vap to a beacon xmit slot. As @@ -939,7 +940,7 @@ void ath_beacon_config(struct ath_softc *sc, int if_id) * deal with things. */ intval |= ATH9K_BEACON_ENA; - if (!ah->ah_caps.halVEOLSupport) + if (!(ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL)) sc->sc_imask |= ATH9K_INT_SWBA; ath_beaconq_config(sc); } else if (sc->sc_opmode == ATH9K_M_HOSTAP) { @@ -958,7 +959,8 @@ void ath_beacon_config(struct ath_softc *sc, int if_id) * When using a self-linked beacon descriptor in * ibss mode load it once here. */ - if (sc->sc_opmode == ATH9K_M_IBSS && ah->ah_caps.halVEOLSupport) + if (sc->sc_opmode == ATH9K_M_IBSS && + (ah->ah_caps.hw_caps & ATH9K_HW_CAP_VEOL)) ath_beacon_start_adhoc(sc, 0); } #undef TSF_TO_TU diff --git a/drivers/net/wireless/ath9k/core.c b/drivers/net/wireless/ath9k/core.c index e8b3f1fabb31..f7bf0783f3be 100644 --- a/drivers/net/wireless/ath9k/core.c +++ b/drivers/net/wireless/ath9k/core.c @@ -536,7 +536,7 @@ int ath_chainmask_sel_logic(struct ath_softc *sc, struct ath_node *an) * sc_chainmask_auto_sel is used for internal global auto-switching * enabled/disabled setting */ - if (sc->sc_ah->ah_caps.halTxChainMask != ATH_CHAINMASK_SEL_3X3) { + if (sc->sc_ah->ah_caps.tx_chainmask != ATH_CHAINMASK_SEL_3X3) { cm->cur_tx_mask = sc->sc_tx_chainmask; return cm->cur_tx_mask; } @@ -580,8 +580,8 @@ void ath_update_chainmask(struct ath_softc *sc, int is_ht) { sc->sc_update_chainmask = 1; if (is_ht) { - sc->sc_tx_chainmask = sc->sc_ah->ah_caps.halTxChainMask; - sc->sc_rx_chainmask = sc->sc_ah->ah_caps.halRxChainMask; + sc->sc_tx_chainmask = sc->sc_ah->ah_caps.tx_chainmask; + sc->sc_rx_chainmask = sc->sc_ah->ah_caps.rx_chainmask; } else { sc->sc_tx_chainmask = 1; sc->sc_rx_chainmask = 1; @@ -780,8 +780,8 @@ int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan) ath_stop(sc); /* Initialize chanmask selection */ - sc->sc_tx_chainmask = ah->ah_caps.halTxChainMask; - sc->sc_rx_chainmask = ah->ah_caps.halRxChainMask; + sc->sc_tx_chainmask = ah->ah_caps.tx_chainmask; + sc->sc_rx_chainmask = ah->ah_caps.rx_chainmask; /* Reset SERDES registers */ ath9k_hw_configpcipowersave(ah, 0); @@ -832,10 +832,10 @@ int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan) | ATH9K_INT_RXEOL | ATH9K_INT_RXORN | ATH9K_INT_FATAL | ATH9K_INT_GLOBAL; - if (ah->ah_caps.halGTTSupport) + if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_GTT) sc->sc_imask |= ATH9K_INT_GTT; - if (ah->ah_caps.halHTSupport) + if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) sc->sc_imask |= ATH9K_INT_CST; /* @@ -851,8 +851,9 @@ int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan) * that does, if not overridden by configuration, * enable the TIM interrupt when operating as station. */ - if (ah->ah_caps.halEnhancedPmSupport && sc->sc_opmode == ATH9K_M_STA && - !sc->sc_config.swBeaconProcess) + if ((ah->ah_caps.hw_caps & ATH9K_HW_CAP_ENHANCEDPM) && + (sc->sc_opmode == ATH9K_M_STA) && + !sc->sc_config.swBeaconProcess) sc->sc_imask |= ATH9K_INT_TIM; /* * Don't enable interrupts here as we've not yet built our @@ -1061,7 +1062,8 @@ irqreturn_t ath_isr(int irq, void *dev) ath9k_hw_set_interrupts(ah, sc->sc_imask); } if (status & ATH9K_INT_TIM_TIMER) { - if (!ah->ah_caps.halAutoSleepSupport) { + if (!(ah->ah_caps.hw_caps & + ATH9K_HW_CAP_AUTOSLEEP)) { /* Clear RxAbort bit so that we can * receive frames */ ath9k_hw_setrxabort(ah, 0); @@ -1166,10 +1168,10 @@ int ath_init(u16 devid, struct ath_softc *sc) sc->sc_ah = ah; /* Get the chipset-specific aggr limit. */ - sc->sc_rtsaggrlimit = ah->ah_caps.halRtsAggrLimit; + sc->sc_rtsaggrlimit = ah->ah_caps.rts_aggr_limit; /* Get the hardware key cache size. */ - sc->sc_keymax = ah->ah_caps.halKeyCacheSize; + sc->sc_keymax = ah->ah_caps.keycache_size; if (sc->sc_keymax > ATH_KEYMAX) { DPRINTF(sc, ATH_DBG_KEYCACHE, "%s: Warning, using only %u entries in %u key cache\n", @@ -1284,7 +1286,7 @@ int ath_init(u16 devid, struct ath_softc *sc) goto bad2; } - if (ath9k_hw_getcapability(ah, HAL_CAP_CIPHER, + if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, ATH9K_CIPHER_TKIP, NULL)) { /* * Whether we should enable h/w TKIP MIC. @@ -1292,7 +1294,8 @@ int ath_init(u16 devid, struct ath_softc *sc) * report WMM capable, so it's always safe to turn on * TKIP MIC in this case. */ - ath9k_hw_setcapability(sc->sc_ah, HAL_CAP_TKIP_MIC, 0, 1, NULL); + ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC, + 0, 1, NULL); } /* @@ -1301,30 +1304,30 @@ int ath_init(u16 devid, struct ath_softc *sc) * With split mic keys the number of stations is limited * to 27 otherwise 59. */ - if (ath9k_hw_getcapability(ah, HAL_CAP_CIPHER, + if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, ATH9K_CIPHER_TKIP, NULL) - && ath9k_hw_getcapability(ah, HAL_CAP_CIPHER, + && ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, ATH9K_CIPHER_MIC, NULL) - && ath9k_hw_getcapability(ah, HAL_CAP_TKIP_SPLIT, + && ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT, 0, NULL)) sc->sc_splitmic = 1; /* turn on mcast key search if possible */ - if (!ath9k_hw_getcapability(ah, HAL_CAP_MCAST_KEYSRCH, 0, NULL)) - (void)ath9k_hw_setcapability(ah, HAL_CAP_MCAST_KEYSRCH, 1, + if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL)) + (void)ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1, 1, NULL); sc->sc_config.txpowlimit = ATH_TXPOWER_MAX; sc->sc_config.txpowlimit_override = 0; /* 11n Capabilities */ - if (ah->ah_caps.halHTSupport) { + if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) { sc->sc_txaggr = 1; sc->sc_rxaggr = 1; } - sc->sc_tx_chainmask = ah->ah_caps.halTxChainMask; - sc->sc_rx_chainmask = ah->ah_caps.halRxChainMask; + sc->sc_tx_chainmask = ah->ah_caps.tx_chainmask; + sc->sc_rx_chainmask = ah->ah_caps.rx_chainmask; /* Configuration for rx chain detection */ sc->sc_rxchaindetect_ref = 0; @@ -1333,11 +1336,11 @@ int ath_init(u16 devid, struct ath_softc *sc) sc->sc_rxchaindetect_delta5GHz = 30; sc->sc_rxchaindetect_delta2GHz = 30; - ath9k_hw_setcapability(ah, HAL_CAP_DIVERSITY, 1, true, NULL); + ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL); sc->sc_defant = ath9k_hw_getdefantenna(ah); ath9k_hw_getmac(ah, sc->sc_myaddr); - if (ah->ah_caps.halBssIdMaskSupport) { + if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) { ath9k_hw_getbssidmask(ah, sc->sc_bssidmask); ATH_SET_VAP_BSSID_MASK(sc->sc_bssidmask); ath9k_hw_setbssidmask(ah, sc->sc_bssidmask); @@ -1555,7 +1558,7 @@ void ath_update_txpow(struct ath_softc *sc) if (sc->sc_curtxpow != sc->sc_config.txpowlimit) { ath9k_hw_set_txpowerlimit(ah, sc->sc_config.txpowlimit); /* read back in case value is clamped */ - ath9k_hw_getcapability(ah, HAL_CAP_TXPOW, 1, &txpow); + ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow); sc->sc_curtxpow = txpow; } } @@ -1757,7 +1760,7 @@ int ath_descdma_setup(struct ath_softc *sc, * descriptors that cross the 4K page boundary. Assume * one skipped descriptor per 4K page. */ - if (!(sc->sc_ah->ah_caps.hal4kbSplitTransSupport)) { + if (!(sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) { u32 ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len); u32 dma_len; @@ -1798,7 +1801,8 @@ int ath_descdma_setup(struct ath_softc *sc, bf->bf_desc = ds; bf->bf_daddr = DS2PHYS(dd, ds); - if (!(sc->sc_ah->ah_caps.hal4kbSplitTransSupport)) { + if (!(sc->sc_ah->ah_caps.hw_caps & + ATH9K_HW_CAP_4KB_SPLITTRANS)) { /* * Skip descriptor addresses which can cause 4KB * boundary crossing (addr + length) with a 32 dword diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index 63d0ead1c412..0dd11ee3b314 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -335,40 +335,40 @@ static void ath9k_hw_set_defaults(struct ath_hal *ah) { int i; - ah->ah_config.ath_hal_dma_beacon_response_time = 2; - ah->ah_config.ath_hal_sw_beacon_response_time = 10; - ah->ah_config.ath_hal_additional_swba_backoff = 0; - ah->ah_config.ath_hal_6mb_ack = 0x0; - ah->ah_config.ath_hal_cwmIgnoreExtCCA = 0; - ah->ah_config.ath_hal_pciePowerSaveEnable = 0; - ah->ah_config.ath_hal_pcieL1SKPEnable = 0; - ah->ah_config.ath_hal_pcieClockReq = 0; - ah->ah_config.ath_hal_pciePowerReset = 0x100; - ah->ah_config.ath_hal_pcieRestore = 0; - ah->ah_config.ath_hal_pcieWaen = 0; - ah->ah_config.ath_hal_analogShiftReg = 1; - ah->ah_config.ath_hal_htEnable = 1; - ah->ah_config.ath_hal_ofdmTrigLow = 200; - ah->ah_config.ath_hal_ofdmTrigHigh = 500; - ah->ah_config.ath_hal_cckTrigHigh = 200; - ah->ah_config.ath_hal_cckTrigLow = 100; - ah->ah_config.ath_hal_enableANI = 0; - ah->ah_config.ath_hal_noiseImmunityLvl = 4; - ah->ah_config.ath_hal_ofdmWeakSigDet = 1; - ah->ah_config.ath_hal_cckWeakSigThr = 0; - ah->ah_config.ath_hal_spurImmunityLvl = 2; - ah->ah_config.ath_hal_firStepLvl = 0; - ah->ah_config.ath_hal_rssiThrHigh = 40; - ah->ah_config.ath_hal_rssiThrLow = 7; - ah->ah_config.ath_hal_diversityControl = 0; - ah->ah_config.ath_hal_antennaSwitchSwap = 0; + ah->ah_config.dma_beacon_response_time = 2; + ah->ah_config.sw_beacon_response_time = 10; + ah->ah_config.additional_swba_backoff = 0; + ah->ah_config.ack_6mb = 0x0; + ah->ah_config.cwm_ignore_extcca = 0; + ah->ah_config.pcie_powersave_enable = 0; + ah->ah_config.pcie_l1skp_enable = 0; + ah->ah_config.pcie_clock_req = 0; + ah->ah_config.pcie_power_reset = 0x100; + ah->ah_config.pcie_restore = 0; + ah->ah_config.pcie_waen = 0; + ah->ah_config.analog_shiftreg = 1; + ah->ah_config.ht_enable = 1; + ah->ah_config.ofdm_trig_low = 200; + ah->ah_config.ofdm_trig_high = 500; + ah->ah_config.cck_trig_high = 200; + ah->ah_config.cck_trig_low = 100; + ah->ah_config.enable_ani = 0; + ah->ah_config.noise_immunity_level = 4; + ah->ah_config.ofdm_weaksignal_det = 1; + ah->ah_config.cck_weaksignal_thr = 0; + ah->ah_config.spur_immunity_level = 2; + ah->ah_config.firstep_level = 0; + ah->ah_config.rssi_thr_high = 40; + ah->ah_config.rssi_thr_low = 7; + ah->ah_config.diversity_control = 0; + ah->ah_config.antenna_switch_swap = 0; for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { - ah->ah_config.ath_hal_spurChans[i][0] = AR_NO_SPUR; - ah->ah_config.ath_hal_spurChans[i][1] = AR_NO_SPUR; + ah->ah_config.spurchans[i][0] = AR_NO_SPUR; + ah->ah_config.spurchans[i][1] = AR_NO_SPUR; } - ah->ah_config.ath_hal_intrMitigation = 0; + ah->ah_config.intr_mitigation = 0; } static inline void ath9k_hw_override_ini(struct ath_hal *ah, @@ -458,7 +458,7 @@ static void ath9k_hw_analog_shift_rmw(struct ath_hal *ah, REG_WRITE(ah, reg, regVal); - if (ah->ah_config.ath_hal_analogShiftReg) + if (ah->ah_config.analog_shiftreg) udelay(100); return; @@ -1001,7 +1001,7 @@ void ath9k_hw_setrxfilter(struct ath_hal *ah, u32 bits) } bool ath9k_hw_setcapability(struct ath_hal *ah, - enum hal_capability_type type, + enum ath9k_capability_type type, u32 capability, u32 setting, int *status) @@ -1010,7 +1010,7 @@ bool ath9k_hw_setcapability(struct ath_hal *ah, u32 v; switch (type) { - case HAL_CAP_TKIP_MIC: + case ATH9K_CAP_TKIP_MIC: if (setting) ahp->ah_staId1Defaults |= AR_STA_ID1_CRPT_MIC_ENABLE; @@ -1018,7 +1018,7 @@ bool ath9k_hw_setcapability(struct ath_hal *ah, ahp->ah_staId1Defaults &= ~AR_STA_ID1_CRPT_MIC_ENABLE; return true; - case HAL_CAP_DIVERSITY: + case ATH9K_CAP_DIVERSITY: v = REG_READ(ah, AR_PHY_CCK_DETECT); if (setting) v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; @@ -1026,13 +1026,13 @@ bool ath9k_hw_setcapability(struct ath_hal *ah, v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; REG_WRITE(ah, AR_PHY_CCK_DETECT, v); return true; - case HAL_CAP_MCAST_KEYSRCH: + case ATH9K_CAP_MCAST_KEYSRCH: if (setting) ahp->ah_staId1Defaults |= AR_STA_ID1_MCAST_KSRCH; else ahp->ah_staId1Defaults &= ~AR_STA_ID1_MCAST_KSRCH; return true; - case HAL_CAP_TSF_ADJUST: + case ATH9K_CAP_TSF_ADJUST: if (setting) ahp->ah_miscMode |= AR_PCU_TX_ADD_TSF; else @@ -1161,7 +1161,7 @@ void ath9k_hw_set11nmac2040(struct ath_hal *ah, enum ath9k_ht_macmode mode) u32 macmode; if (mode == ATH9K_HT_MACMODE_2040 && - !ah->ah_config.ath_hal_cwmIgnoreExtCCA) + !ah->ah_config.cwm_ignore_extcca) macmode = AR_2040_JOINED_RX_CLEAR; else macmode = 0; @@ -1214,9 +1214,9 @@ static struct ath_hal_5416 *ath9k_hw_newstate(u16 devid, ah->ah_tpScale = ATH9K_TP_SCALE_MAX; ahp->ah_atimWindow = 0; - ahp->ah_diversityControl = ah->ah_config.ath_hal_diversityControl; + ahp->ah_diversityControl = ah->ah_config.diversity_control; ahp->ah_antennaSwitchSwap = - ah->ah_config.ath_hal_antennaSwitchSwap; + ah->ah_config.antenna_switch_swap; ahp->ah_staId1Defaults = AR_STA_ID1_CRPT_MIC_ENABLE; ahp->ah_beaconInterval = 100; @@ -1371,13 +1371,13 @@ static u16 ath9k_hw_eeprom_get_spur_chan(struct ath_hal *ah, DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Getting spur idx %d is2Ghz. %d val %x\n", - i, is2GHz, ah->ah_config.ath_hal_spurChans[i][is2GHz]); + i, is2GHz, ah->ah_config.spurchans[i][is2GHz]); - switch (ah->ah_config.ath_hal_spurMode) { + switch (ah->ah_config.spurmode) { case SPUR_DISABLE: break; case SPUR_ENABLE_IOCTL: - spur_val = ah->ah_config.ath_hal_spurChans[i][is2GHz]; + spur_val = ah->ah_config.spurchans[i][is2GHz]; DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Getting spur val from new loc. %d\n", spur_val); break; @@ -2094,7 +2094,7 @@ static void ath9k_hw_ani_attach(struct ath_hal *ah) ath9k_enable_mib_counters(ah); } ahp->ah_aniPeriod = ATH9K_ANI_PERIOD; - if (ah->ah_config.ath_hal_enableANI) + if (ah->ah_config.enable_ani) ahp->ah_procPhyErr |= HAL_PROCESS_ANI; } @@ -2504,13 +2504,13 @@ static void ath9k_ani_reset(struct ath_hal *ah) ATH9K_RX_FILTER_PHYERR); if (ah->ah_opmode == ATH9K_M_HOSTAP) { ahp->ah_curani->ofdmTrigHigh = - ah->ah_config.ath_hal_ofdmTrigHigh; + ah->ah_config.ofdm_trig_high; ahp->ah_curani->ofdmTrigLow = - ah->ah_config.ath_hal_ofdmTrigLow; + ah->ah_config.ofdm_trig_low; ahp->ah_curani->cckTrigHigh = - ah->ah_config.ath_hal_cckTrigHigh; + ah->ah_config.cck_trig_high; ahp->ah_curani->cckTrigLow = - ah->ah_config.ath_hal_cckTrigLow; + ah->ah_config.cck_trig_low; } ath9k_ani_restart(ah); return; @@ -2870,7 +2870,7 @@ static bool ath9k_hw_set_gpio(struct ath_hal *ah, u32 gpio, static u32 ath9k_hw_gpio_get(struct ath_hal *ah, u32 gpio) { - if (gpio >= ah->ah_caps.halNumGpioPins) + if (gpio >= ah->ah_caps.num_gpio_pins) return 0xffffffff; if (AR_SREV_9280_10_OR_LATER(ah)) { @@ -2947,7 +2947,7 @@ static u32 ath9k_hw_ini_fixup(struct ath_hal *ah, static bool ath9k_hw_fill_cap_info(struct ath_hal *ah) { struct ath_hal_5416 *ahp = AH5416(ah); - struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_hw_capabilities *pCap = &ah->ah_caps; u16 capField = 0, eeval; eeval = ath9k_hw_get_eeprom(ahp, EEP_REG_0); @@ -2970,12 +2970,12 @@ static bool ath9k_hw_fill_cap_info(struct ath_hal *ah) ah->ah_currentRD); } - pCap->halWirelessModes = 0; + pCap->wireless_modes = 0; eeval = ath9k_hw_get_eeprom(ahp, EEP_OP_MODE); if (eeval & AR5416_OPFLAGS_11A) { - pCap->halWirelessModes |= ATH9K_MODE_SEL_11A | - ((!ah->ah_config.ath_hal_htEnable + pCap->wireless_modes |= ATH9K_MODE_SEL_11A | + ((!ah->ah_config.ht_enable || (eeval & AR5416_OPFLAGS_N_5G_HT20)) ? 0 : (ATH9K_MODE_SEL_11NA_HT20 | ((eeval & AR5416_OPFLAGS_N_5G_HT40) ? 0 @@ -2983,9 +2983,9 @@ static bool ath9k_hw_fill_cap_info(struct ath_hal *ah) ATH9K_MODE_SEL_11NA_HT40MINUS)))); } if (eeval & AR5416_OPFLAGS_11G) { - pCap->halWirelessModes |= + pCap->wireless_modes |= ATH9K_MODE_SEL_11B | ATH9K_MODE_SEL_11G | - ((!ah->ah_config.ath_hal_htEnable + ((!ah->ah_config.ht_enable || (eeval & AR5416_OPFLAGS_N_2G_HT20)) ? 0 : (ATH9K_MODE_SEL_11NG_HT20 | ((eeval & AR5416_OPFLAGS_N_2G_HT40) ? 0 @@ -2993,79 +2993,82 @@ static bool ath9k_hw_fill_cap_info(struct ath_hal *ah) ATH9K_MODE_SEL_11NG_HT40MINUS)))); } - pCap->halTxChainMask = ath9k_hw_get_eeprom(ahp, EEP_TX_MASK); + pCap->tx_chainmask = ath9k_hw_get_eeprom(ahp, EEP_TX_MASK); if ((ah->ah_isPciExpress) || (eeval & AR5416_OPFLAGS_11A)) { - pCap->halRxChainMask = + pCap->rx_chainmask = ath9k_hw_get_eeprom(ahp, EEP_RX_MASK); } else { - pCap->halRxChainMask = + pCap->rx_chainmask = (ath9k_hw_gpio_get(ah, 0)) ? 0x5 : 0x7; } if (!(AR_SREV_9280(ah) && (ah->ah_macRev == 0))) ahp->ah_miscMode |= AR_PCU_MIC_NEW_LOC_ENA; - pCap->halLow2GhzChan = 2312; - pCap->halHigh2GhzChan = 2732; + pCap->low_2ghz_chan = 2312; + pCap->high_2ghz_chan = 2732; - pCap->halLow5GhzChan = 4920; - pCap->halHigh5GhzChan = 6100; + pCap->low_5ghz_chan = 4920; + pCap->high_5ghz_chan = 6100; - pCap->halCipherCkipSupport = false; - pCap->halCipherTkipSupport = true; - pCap->halCipherAesCcmSupport = true; + pCap->hw_caps &= ~ATH9K_HW_CAP_CIPHER_CKIP; + pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_TKIP; + pCap->hw_caps |= ATH9K_HW_CAP_CIPHER_AESCCM; - pCap->halMicCkipSupport = false; - pCap->halMicTkipSupport = true; - pCap->halMicAesCcmSupport = true; + pCap->hw_caps &= ~ATH9K_HW_CAP_MIC_CKIP; + pCap->hw_caps |= ATH9K_HW_CAP_MIC_TKIP; + pCap->hw_caps |= ATH9K_HW_CAP_MIC_AESCCM; - pCap->halChanSpreadSupport = true; + pCap->hw_caps |= ATH9K_HW_CAP_CHAN_SPREAD; - pCap->halHTSupport = - ah->ah_config.ath_hal_htEnable ? true : false; - pCap->halGTTSupport = true; - pCap->halVEOLSupport = true; - pCap->halBssIdMaskSupport = true; - pCap->halMcastKeySrchSupport = false; + if (ah->ah_config.ht_enable) + pCap->hw_caps |= ATH9K_HW_CAP_HT; + else + pCap->hw_caps &= ~ATH9K_HW_CAP_HT; + + pCap->hw_caps |= ATH9K_HW_CAP_GTT; + pCap->hw_caps |= ATH9K_HW_CAP_VEOL; + pCap->hw_caps |= ATH9K_HW_CAP_BSSIDMASK; + pCap->hw_caps &= ~ATH9K_HW_CAP_MCAST_KEYSEARCH; if (capField & AR_EEPROM_EEPCAP_MAXQCU) - pCap->halTotalQueues = + pCap->total_queues = MS(capField, AR_EEPROM_EEPCAP_MAXQCU); else - pCap->halTotalQueues = ATH9K_NUM_TX_QUEUES; + pCap->total_queues = ATH9K_NUM_TX_QUEUES; if (capField & AR_EEPROM_EEPCAP_KC_ENTRIES) - pCap->halKeyCacheSize = + pCap->keycache_size = 1 << MS(capField, AR_EEPROM_EEPCAP_KC_ENTRIES); else - pCap->halKeyCacheSize = AR_KEYTABLE_SIZE; + pCap->keycache_size = AR_KEYTABLE_SIZE; - pCap->halFastCCSupport = true; - pCap->halNumMRRetries = 4; - pCap->halTxTrigLevelMax = MAX_TX_FIFO_THRESHOLD; + pCap->hw_caps |= ATH9K_HW_CAP_FASTCC; + pCap->num_mr_retries = 4; + pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD; if (AR_SREV_9280_10_OR_LATER(ah)) - pCap->halNumGpioPins = AR928X_NUM_GPIO; + pCap->num_gpio_pins = AR928X_NUM_GPIO; else - pCap->halNumGpioPins = AR_NUM_GPIO; + pCap->num_gpio_pins = AR_NUM_GPIO; if (AR_SREV_9280_10_OR_LATER(ah)) { - pCap->halWowSupport = true; - pCap->halWowMatchPatternExact = true; + pCap->hw_caps |= ATH9K_HW_CAP_WOW; + pCap->hw_caps |= ATH9K_HW_CAP_WOW_MATCHPATTERN_EXACT; } else { - pCap->halWowSupport = false; - pCap->halWowMatchPatternExact = false; + pCap->hw_caps &= ~ATH9K_HW_CAP_WOW; + pCap->hw_caps &= ~ATH9K_HW_CAP_WOW_MATCHPATTERN_EXACT; } if (AR_SREV_9160_10_OR_LATER(ah) || AR_SREV_9100(ah)) { - pCap->halCSTSupport = true; - pCap->halRtsAggrLimit = ATH_AMPDU_LIMIT_MAX; + pCap->hw_caps |= ATH9K_HW_CAP_CST; + pCap->rts_aggr_limit = ATH_AMPDU_LIMIT_MAX; } else { - pCap->halRtsAggrLimit = (8 * 1024); + pCap->rts_aggr_limit = (8 * 1024); } - pCap->halEnhancedPmSupport = true; + pCap->hw_caps |= ATH9K_HW_CAP_ENHANCEDPM; ah->ah_rfsilent = ath9k_hw_get_eeprom(ahp, EEP_RF_SILENT); if (ah->ah_rfsilent & EEP_RFSILENT_ENABLED) { @@ -3074,9 +3077,9 @@ static bool ath9k_hw_fill_cap_info(struct ath_hal *ah) ahp->ah_polarity = MS(ah->ah_rfsilent, EEP_RFSILENT_POLARITY); - ath9k_hw_setcapability(ah, HAL_CAP_RFSILENT, 1, true, + ath9k_hw_setcapability(ah, ATH9K_CAP_RFSILENT, 1, true, NULL); - pCap->halRfSilentSupport = true; + pCap->hw_caps |= ATH9K_HW_CAP_RFSILENT; } if ((ah->ah_macVersion == AR_SREV_VERSION_5416_PCI) || @@ -3084,32 +3087,32 @@ static bool ath9k_hw_fill_cap_info(struct ath_hal *ah) (ah->ah_macVersion == AR_SREV_VERSION_9160) || (ah->ah_macVersion == AR_SREV_VERSION_9100) || (ah->ah_macVersion == AR_SREV_VERSION_9280)) - pCap->halAutoSleepSupport = false; + pCap->hw_caps &= ~ATH9K_HW_CAP_AUTOSLEEP; else - pCap->halAutoSleepSupport = true; + pCap->hw_caps |= ATH9K_HW_CAP_AUTOSLEEP; if (AR_SREV_9280(ah)) - pCap->hal4kbSplitTransSupport = false; + pCap->hw_caps &= ~ATH9K_HW_CAP_4KB_SPLITTRANS; else - pCap->hal4kbSplitTransSupport = true; + pCap->hw_caps |= ATH9K_HW_CAP_4KB_SPLITTRANS; if (ah->ah_currentRDExt & (1 << REG_EXT_JAPAN_MIDBAND)) { - pCap->halRegCap = + pCap->reg_cap = AR_EEPROM_EEREGCAP_EN_KK_NEW_11A | AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN | AR_EEPROM_EEREGCAP_EN_KK_U2 | AR_EEPROM_EEREGCAP_EN_KK_MIDBAND; } else { - pCap->halRegCap = + pCap->reg_cap = AR_EEPROM_EEREGCAP_EN_KK_NEW_11A | AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN; } - pCap->halRegCap |= AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND; + pCap->reg_cap |= AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND; - pCap->halNumAntCfg5GHz = + pCap->num_antcfg_5ghz = ath9k_hw_get_num_ant_config(ahp, HAL_FREQ_BAND_5GHZ); - pCap->halNumAntCfg2GHz = + pCap->num_antcfg_2ghz = ath9k_hw_get_num_ant_config(ahp, HAL_FREQ_BAND_2GHZ); return true; @@ -3151,9 +3154,9 @@ static void ath9k_set_power_network_sleep(struct ath_hal *ah, int setChip) { REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); if (setChip) { - struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_hw_capabilities *pCap = &ah->ah_caps; - if (!pCap->halAutoSleepSupport) { + if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT); } else { @@ -3262,7 +3265,7 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid, ath9k_hw_set_defaults(ah); - if (ah->ah_config.ath_hal_intrMitigation != 0) + if (ah->ah_config.intr_mitigation != 0) ahp->ah_intrMitigation = true; if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { @@ -3279,18 +3282,18 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid, goto bad; } - if (ah->ah_config.ath_hal_serializeRegMode == SER_REG_MODE_AUTO) { + if (ah->ah_config.serialize_regmode == SER_REG_MODE_AUTO) { if (ah->ah_macVersion == AR_SREV_VERSION_5416_PCI) { - ah->ah_config.ath_hal_serializeRegMode = + ah->ah_config.serialize_regmode = SER_REG_MODE_ON; } else { - ah->ah_config.ath_hal_serializeRegMode = + ah->ah_config.serialize_regmode = SER_REG_MODE_OFF; } } DPRINTF(ah->ah_sc, ATH_DBG_RESET, - "%s: ath_hal_serializeRegMode is %d\n", - __func__, ah->ah_config.ath_hal_serializeRegMode); + "%s: serialize_regmode is %d\n", + __func__, ah->ah_config.serialize_regmode); if ((ah->ah_macVersion != AR_SREV_VERSION_5416_PCI) && (ah->ah_macVersion != AR_SREV_VERSION_5416_PCIE) && @@ -3334,7 +3337,7 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid, } if (AR_SREV_9160(ah)) { - ah->ah_config.ath_hal_enableANI = 1; + ah->ah_config.enable_ani = 1; ahp->ah_ani_function = (ATH9K_ANI_SPUR_IMMUNITY_LEVEL | ATH9K_ANI_FIRSTEP_LEVEL); } else { @@ -3355,7 +3358,7 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid, INIT_INI_ARRAY(&ahp->ah_iniCommon, ar9280Common_9280_2, ARRAY_SIZE(ar9280Common_9280_2), 2); - if (ah->ah_config.ath_hal_pcieClockReq) { + if (ah->ah_config.pcie_clock_req) { INIT_INI_ARRAY(&ahp->ah_iniPcieSerdes, ar9280PciePhy_clkreq_off_L1_9280, ARRAY_SIZE @@ -3528,16 +3531,16 @@ bool ath9k_get_channel_edges(struct ath_hal *ah, u16 flags, u16 *low, u16 *high) { - struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_hw_capabilities *pCap = &ah->ah_caps; if (flags & CHANNEL_5GHZ) { - *low = pCap->halLow5GhzChan; - *high = pCap->halHigh5GhzChan; + *low = pCap->low_5ghz_chan; + *high = pCap->high_5ghz_chan; return true; } if ((flags & CHANNEL_2GHZ)) { - *low = pCap->halLow2GhzChan; - *high = pCap->halHigh2GhzChan; + *low = pCap->low_2ghz_chan; + *high = pCap->high_2ghz_chan; return true; } @@ -3908,7 +3911,7 @@ void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore) if (ah->ah_isPciExpress != true) return; - if (ah->ah_config.ath_hal_pciePowerSaveEnable == 2) + if (ah->ah_config.pcie_powersave_enable == 2) return; if (restore) @@ -3929,7 +3932,7 @@ void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore) REG_WRITE(ah, AR_PCIE_SERDES, 0x13160820); REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980560); - if (ah->ah_config.ath_hal_pcieClockReq) + if (ah->ah_config.pcie_clock_req) REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffc); else REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffd); @@ -3956,8 +3959,8 @@ void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore) REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); - if (ah->ah_config.ath_hal_pcieWaen) { - REG_WRITE(ah, AR_WA, ah->ah_config.ath_hal_pcieWaen); + if (ah->ah_config.pcie_waen) { + REG_WRITE(ah, AR_WA, ah->ah_config.pcie_waen); } else { if (AR_SREV_9280(ah)) REG_WRITE(ah, AR_WA, 0x0040073f); @@ -4690,7 +4693,7 @@ static void ath9k_hw_9280_spur_mitigate(struct ath_hal *ah, ath9k_hw_get_channel_centers(ah, chan, ¢ers); freq = centers.synth_center; - ah->ah_config.ath_hal_spurMode = SPUR_ENABLE_EEPROM; + ah->ah_config.spurmode = SPUR_ENABLE_EEPROM; for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { cur_bb_spur = ath9k_hw_eeprom_get_spur_chan(ah, i, is2GHz); @@ -5404,7 +5407,7 @@ ath9k_hw_process_ini(struct ath_hal *ah, REG_WRITE(ah, reg, val); if (reg >= 0x7800 && reg < 0x78a0 - && ah->ah_config.ath_hal_analogShiftReg) { + && ah->ah_config.analog_shiftreg) { udelay(100); } @@ -5418,7 +5421,7 @@ ath9k_hw_process_ini(struct ath_hal *ah, REG_WRITE(ah, reg, val); if (reg >= 0x7800 && reg < 0x78a0 - && ah->ah_config.ath_hal_analogShiftReg) { + && ah->ah_config.analog_shiftreg) { udelay(100); } @@ -5921,7 +5924,7 @@ bool ath9k_hw_reset(struct ath_hal *ah, enum ath9k_opmode opmode, REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); - if (ah->ah_caps.halWirelessModes & ATH9K_MODE_SEL_11A) { + if (ah->ah_caps.wireless_modes & ATH9K_MODE_SEL_11A) { if (IS_CHAN_5GHZ(chan)) ath9k_hw_set_gpio(ah, 9, 0); else @@ -5955,7 +5958,7 @@ bool ath9k_hw_reset(struct ath_hal *ah, enum ath9k_opmode opmode, | macStaId1 | AR_STA_ID1_RTS_USE_DEF | (ah->ah_config. - ath_hal_6mb_ack ? AR_STA_ID1_ACKCTS_6MB : 0) + ack_6mb ? AR_STA_ID1_ACKCTS_6MB : 0) | ahp->ah_staId1Defaults); ath9k_hw_set_operating_mode(ah, opmode); @@ -5984,7 +5987,7 @@ bool ath9k_hw_reset(struct ath_hal *ah, enum ath9k_opmode opmode, REG_WRITE(ah, AR_DQCUMASK(i), 1 << i); ahp->ah_intrTxqs = 0; - for (i = 0; i < ah->ah_caps.halTotalQueues; i++) + for (i = 0; i < ah->ah_caps.total_queues; i++) ath9k_hw_resettxqueue(ah, i); ath9k_hw_init_interrupt_masks(ah, opmode); @@ -6622,7 +6625,7 @@ ath9k_hw_setantennaswitch(struct ath_hal *ah, *antenna_cfgd = true; break; case ATH9K_ANT_FIXED_B: - if (ah->ah_caps.halTxChainMask > + if (ah->ah_caps.tx_chainmask > ATH9K_ANTENNA1_CHAINMASK) { *tx_chainmask = ATH9K_ANTENNA1_CHAINMASK; } @@ -6650,14 +6653,14 @@ void ath9k_hw_setopmode(struct ath_hal *ah) } bool -ath9k_hw_getcapability(struct ath_hal *ah, enum hal_capability_type type, +ath9k_hw_getcapability(struct ath_hal *ah, enum ath9k_capability_type type, u32 capability, u32 *result) { struct ath_hal_5416 *ahp = AH5416(ah); - const struct hal_capabilities *pCap = &ah->ah_caps; + const struct ath9k_hw_capabilities *pCap = &ah->ah_caps; switch (type) { - case HAL_CAP_CIPHER: + case ATH9K_CAP_CIPHER: switch (capability) { case ATH9K_CIPHER_AES_CCM: case ATH9K_CIPHER_AES_OCB: @@ -6669,7 +6672,7 @@ ath9k_hw_getcapability(struct ath_hal *ah, enum hal_capability_type type, default: return false; } - case HAL_CAP_TKIP_MIC: + case ATH9K_CAP_TKIP_MIC: switch (capability) { case 0: return true; @@ -6678,20 +6681,20 @@ ath9k_hw_getcapability(struct ath_hal *ah, enum hal_capability_type type, AR_STA_ID1_CRPT_MIC_ENABLE) ? true : false; } - case HAL_CAP_TKIP_SPLIT: + case ATH9K_CAP_TKIP_SPLIT: return (ahp->ah_miscMode & AR_PCU_MIC_NEW_LOC_ENA) ? false : true; - case HAL_CAP_WME_TKIPMIC: + case ATH9K_CAP_WME_TKIPMIC: return 0; - case HAL_CAP_PHYCOUNTERS: + case ATH9K_CAP_PHYCOUNTERS: return ahp->ah_hasHwPhyCounters ? 0 : -ENXIO; - case HAL_CAP_DIVERSITY: + case ATH9K_CAP_DIVERSITY: return (REG_READ(ah, AR_PHY_CCK_DETECT) & AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV) ? true : false; - case HAL_CAP_PHYDIAG: + case ATH9K_CAP_PHYDIAG: return true; - case HAL_CAP_MCAST_KEYSRCH: + case ATH9K_CAP_MCAST_KEYSRCH: switch (capability) { case 0: return true; @@ -6705,19 +6708,19 @@ ath9k_hw_getcapability(struct ath_hal *ah, enum hal_capability_type type, } } return false; - case HAL_CAP_TSF_ADJUST: + case ATH9K_CAP_TSF_ADJUST: return (ahp->ah_miscMode & AR_PCU_TX_ADD_TSF) ? true : false; - case HAL_CAP_RFSILENT: + case ATH9K_CAP_RFSILENT: if (capability == 3) return false; - case HAL_CAP_ANT_CFG_2GHZ: - *result = pCap->halNumAntCfg2GHz; + case ATH9K_CAP_ANT_CFG_2GHZ: + *result = pCap->num_antcfg_2ghz; return true; - case HAL_CAP_ANT_CFG_5GHZ: - *result = pCap->halNumAntCfg5GHz; + case ATH9K_CAP_ANT_CFG_5GHZ: + *result = pCap->num_antcfg_5ghz; return true; - case HAL_CAP_TXPOW: + case ATH9K_CAP_TXPOW: switch (capability) { case 0: return 0; @@ -6742,13 +6745,13 @@ ath9k_hw_select_antconfig(struct ath_hal *ah, u32 cfg) { struct ath_hal_5416 *ahp = AH5416(ah); struct ath9k_channel *chan = ah->ah_curchan; - const struct hal_capabilities *pCap = &ah->ah_caps; + const struct ath9k_hw_capabilities *pCap = &ah->ah_caps; u16 ant_config; u32 halNumAntConfig; halNumAntConfig = - IS_CHAN_2GHZ(chan) ? pCap->halNumAntCfg2GHz : pCap-> - halNumAntCfg5GHz; + IS_CHAN_2GHZ(chan) ? pCap->num_antcfg_2ghz : pCap-> + num_antcfg_5ghz; if (cfg < halNumAntConfig) { if (!ath9k_hw_get_eeprom_antenna_cfg(ahp, chan, @@ -6784,7 +6787,7 @@ bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked) { u32 isr = 0; u32 mask2 = 0; - struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_hw_capabilities *pCap = &ah->ah_caps; u32 sync_cause = 0; bool fatal_int = false; @@ -6868,7 +6871,7 @@ bool ath9k_hw_getisr(struct ath_hal *ah, enum ath9k_int *masked) } if (!AR_SREV_9100(ah)) { - if (!pCap->halAutoSleepSupport) { + if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { u32 isr5 = REG_READ(ah, AR_ISR_S5_S); if (isr5 & AR_ISR_S5_TIM_TIMER) *masked |= ATH9K_INT_TIM_TIMER; @@ -6927,7 +6930,7 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah, enum ath9k_int ints) struct ath_hal_5416 *ahp = AH5416(ah); u32 omask = ahp->ah_maskReg; u32 mask, mask2; - struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_hw_capabilities *pCap = &ah->ah_caps; DPRINTF(ah->ah_sc, ATH_DBG_INTERRUPT, "%s: 0x%x => 0x%x\n", __func__, omask, ints); @@ -6965,7 +6968,7 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah, enum ath9k_int ints) mask |= AR_IMR_RXMINTR | AR_IMR_RXINTM; else mask |= AR_IMR_RXOK | AR_IMR_RXDESC; - if (!pCap->halAutoSleepSupport) + if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) mask |= AR_IMR_GENTMR; } @@ -7002,7 +7005,7 @@ enum ath9k_int ath9k_hw_set_interrupts(struct ath_hal *ah, enum ath9k_int ints) REG_WRITE(ah, AR_IMR_S2, mask | mask2); ahp->ah_maskReg = ints; - if (!pCap->halAutoSleepSupport) { + if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { if (ints & ATH9K_INT_TIM_TIMER) REG_SET_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER); else @@ -7061,11 +7064,11 @@ ath9k_hw_beaconinit(struct ath_hal *ah, REG_WRITE(ah, AR_NEXT_DMA_BEACON_ALERT, TU_TO_USEC(next_beacon - ah->ah_config. - ath_hal_dma_beacon_response_time)); + dma_beacon_response_time)); REG_WRITE(ah, AR_NEXT_SWBA, TU_TO_USEC(next_beacon - ah->ah_config. - ath_hal_sw_beacon_response_time)); + sw_beacon_response_time)); flags |= AR_TBTT_TIMER_EN | AR_DBA_TIMER_EN | AR_SWBA_TIMER_EN; break; @@ -7090,7 +7093,7 @@ ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah, const struct ath9k_beacon_state *bs) { u32 nextTbtt, beaconintval, dtimperiod, beacontimeout; - struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_hw_capabilities *pCap = &ah->ah_caps; REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt)); @@ -7133,7 +7136,7 @@ ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah, SM((CAB_TIMEOUT_VAL << 3), AR_SLEEP1_CAB_TIMEOUT) | AR_SLEEP1_ASSUME_DTIM); - if (pCap->halAutoSleepSupport) + if (pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP) beacontimeout = (BEACON_TIMEOUT_VAL << 3); else beacontimeout = MIN_BEACON_TIMEOUT_VAL; @@ -7152,7 +7155,7 @@ ath9k_hw_set_sta_beacon_timers(struct ath_hal *ah, bool ath9k_hw_keyisvalid(struct ath_hal *ah, u16 entry) { - if (entry < ah->ah_caps.halKeyCacheSize) { + if (entry < ah->ah_caps.keycache_size) { u32 val = REG_READ(ah, AR_KEYTABLE_MAC1(entry)); if (val & AR_KEYTABLE_VALID) return true; @@ -7164,7 +7167,7 @@ bool ath9k_hw_keyreset(struct ath_hal *ah, u16 entry) { u32 keyType; - if (entry >= ah->ah_caps.halKeyCacheSize) { + if (entry >= ah->ah_caps.keycache_size) { DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, "%s: entry %u out of range\n", __func__, entry); return false; @@ -7202,7 +7205,7 @@ ath9k_hw_keysetmac(struct ath_hal *ah, u16 entry, { u32 macHi, macLo; - if (entry >= ah->ah_caps.halKeyCacheSize) { + if (entry >= ah->ah_caps.keycache_size) { DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, "%s: entry %u out of range\n", __func__, entry); return false; @@ -7229,7 +7232,7 @@ ath9k_hw_set_keycache_entry(struct ath_hal *ah, u16 entry, const struct ath9k_keyval *k, const u8 *mac, int xorKey) { - const struct hal_capabilities *pCap = &ah->ah_caps; + const struct ath9k_hw_capabilities *pCap = &ah->ah_caps; u32 key0, key1, key2, key3, key4; u32 keyType; u32 xorMask = xorKey ? @@ -7237,7 +7240,7 @@ ath9k_hw_set_keycache_entry(struct ath_hal *ah, u16 entry, | ATH9K_KEY_XOR) : 0; struct ath_hal_5416 *ahp = AH5416(ah); - if (entry >= pCap->halKeyCacheSize) { + if (entry >= pCap->keycache_size) { DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, "%s: entry %u out of range\n", __func__, entry); return false; @@ -7247,7 +7250,7 @@ ath9k_hw_set_keycache_entry(struct ath_hal *ah, u16 entry, keyType = AR_KEYTABLE_TYPE_AES; break; case ATH9K_CIPHER_AES_CCM: - if (!pCap->halCipherAesCcmSupport) { + if (!(pCap->hw_caps & ATH9K_HW_CAP_CIPHER_AESCCM)) { DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, "%s: AES-CCM not supported by " "mac rev 0x%x\n", __func__, @@ -7259,7 +7262,7 @@ ath9k_hw_set_keycache_entry(struct ath_hal *ah, u16 entry, case ATH9K_CIPHER_TKIP: keyType = AR_KEYTABLE_TYPE_TKIP; if (ATH9K_IS_MIC_ENABLED(ah) - && entry + 64 >= pCap->halKeyCacheSize) { + && entry + 64 >= pCap->keycache_size) { DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, "%s: entry %u inappropriate for TKIP\n", __func__, entry); @@ -7456,9 +7459,9 @@ bool ath9k_hw_settxqueueprops(struct ath_hal *ah, int q, const struct ath9k_txq_info *qInfo) { struct ath_hal_5416 *ahp = AH5416(ah); - struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_hw_capabilities *pCap = &ah->ah_caps; - if (q >= pCap->halTotalQueues) { + if (q >= pCap->total_queues) { DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", __func__, q); return false; @@ -7499,9 +7502,9 @@ ath9k_hw_gettxqueueprops(struct ath_hal *ah, int q, struct ath9k_txq_info *qInfo) { struct ath_hal_5416 *ahp = AH5416(ah); - struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_hw_capabilities *pCap = &ah->ah_caps; - if (q >= pCap->halTotalQueues) { + if (q >= pCap->total_queues) { DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", __func__, q); return false; @@ -7515,28 +7518,28 @@ ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type, { struct ath_hal_5416 *ahp = AH5416(ah); struct ath9k_tx_queue_info *qi; - struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_hw_capabilities *pCap = &ah->ah_caps; int q; switch (type) { case ATH9K_TX_QUEUE_BEACON: - q = pCap->halTotalQueues - 1; + q = pCap->total_queues - 1; break; case ATH9K_TX_QUEUE_CAB: - q = pCap->halTotalQueues - 2; + q = pCap->total_queues - 2; break; case ATH9K_TX_QUEUE_PSPOLL: q = 1; break; case ATH9K_TX_QUEUE_UAPSD: - q = pCap->halTotalQueues - 3; + q = pCap->total_queues - 3; break; case ATH9K_TX_QUEUE_DATA: - for (q = 0; q < pCap->halTotalQueues; q++) + for (q = 0; q < pCap->total_queues; q++) if (ahp->ah_txq[q].tqi_type == ATH9K_TX_QUEUE_INACTIVE) break; - if (q == pCap->halTotalQueues) { + if (q == pCap->total_queues) { DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: no available tx queue\n", __func__); return -1; @@ -7602,10 +7605,10 @@ ath9k_hw_set_txq_interrupts(struct ath_hal *ah, bool ath9k_hw_releasetxqueue(struct ath_hal *ah, u32 q) { struct ath_hal_5416 *ahp = AH5416(ah); - struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_hw_capabilities *pCap = &ah->ah_caps; struct ath9k_tx_queue_info *qi; - if (q >= pCap->halTotalQueues) { + if (q >= pCap->total_queues) { DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", __func__, q); return false; @@ -7634,12 +7637,12 @@ bool ath9k_hw_releasetxqueue(struct ath_hal *ah, u32 q) bool ath9k_hw_resettxqueue(struct ath_hal *ah, u32 q) { struct ath_hal_5416 *ahp = AH5416(ah); - struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_hw_capabilities *pCap = &ah->ah_caps; struct ath9k_channel *chan = ah->ah_curchan; struct ath9k_tx_queue_info *qi; u32 cwMin, chanCwMin, value; - if (q >= pCap->halTotalQueues) { + if (q >= pCap->total_queues) { DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", __func__, q); return false; @@ -7739,10 +7742,10 @@ bool ath9k_hw_resettxqueue(struct ath_hal *ah, u32 q) | AR_Q_MISC_CBR_INCR_DIS1 | AR_Q_MISC_CBR_INCR_DIS0); value = (qi->tqi_readyTime - - (ah->ah_config.ath_hal_sw_beacon_response_time - - ah->ah_config.ath_hal_dma_beacon_response_time) + - (ah->ah_config.sw_beacon_response_time - + ah->ah_config.dma_beacon_response_time) - - ah->ah_config.ath_hal_additional_swba_backoff) * + ah->ah_config.additional_swba_backoff) * 1024; REG_WRITE(ah, AR_QRDYTIMECFG(q), value | AR_Q_RDYTIMECFG_EN); @@ -8131,14 +8134,14 @@ ath9k_hw_setuprxdesc(struct ath_hal *ah, struct ath_desc *ds, u32 size, u32 flags) { struct ar5416_desc *ads = AR5416DESC(ds); - struct hal_capabilities *pCap = &ah->ah_caps; + struct ath9k_hw_capabilities *pCap = &ah->ah_caps; ads->ds_ctl1 = size & AR_BufLen; if (flags & ATH9K_RXDESC_INTREQ) ads->ds_ctl1 |= AR_RxIntrReq; ads->ds_rxstatus8 &= ~AR_RxDone; - if (!pCap->halAutoSleepSupport) + if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) memset(&(ads->u), 0, sizeof(ads->u)); return true; } diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 9549524630f7..01bee5ec7233 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -1268,14 +1268,14 @@ static int ath_attach(u16 devid, sc->rates[IEEE80211_BAND_2GHZ]; sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; - if (sc->sc_ah->ah_caps.halHTSupport) + if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) /* Setup HT capabilities for 2.4Ghz*/ setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_info); hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &sc->sbands[IEEE80211_BAND_2GHZ]; - if (sc->sc_ah->ah_caps.halWirelessModes & ATH9K_MODE_SEL_11A) { + if (sc->sc_ah->ah_caps.wireless_modes & ATH9K_MODE_SEL_11A) { sc->sbands[IEEE80211_BAND_5GHZ].channels = sc->channels[IEEE80211_BAND_5GHZ]; sc->sbands[IEEE80211_BAND_5GHZ].bitrates = @@ -1283,7 +1283,7 @@ static int ath_attach(u16 devid, sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; - if (sc->sc_ah->ah_caps.halHTSupport) + if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) /* Setup HT capabilities for 5Ghz*/ setup_ht_cap(&sc->sbands[IEEE80211_BAND_5GHZ].ht_info); diff --git a/drivers/net/wireless/ath9k/rc.c b/drivers/net/wireless/ath9k/rc.c index e0797afee52b..d3132009d94d 100644 --- a/drivers/net/wireless/ath9k/rc.c +++ b/drivers/net/wireless/ath9k/rc.c @@ -780,7 +780,7 @@ struct ath_rate_softc *ath_rate_attach(struct ath_hal *ah) ar5416_attach_ratetables(asc); /* Save Maximum TX Trigger Level (used for 11n) */ - tx_triglevel_max = ah->ah_caps.halTxTrigLevelMax; + tx_triglevel_max = ah->ah_caps.tx_triglevel_max; /* return alias for ath_rate_softc * */ return asc; } diff --git a/drivers/net/wireless/ath9k/recv.c b/drivers/net/wireless/ath9k/recv.c index 8e14e643e994..2fe806175c01 100644 --- a/drivers/net/wireless/ath9k/recv.c +++ b/drivers/net/wireless/ath9k/recv.c @@ -478,7 +478,7 @@ static void ath_opmode_init(struct ath_softc *sc) ath9k_hw_setrxfilter(ah, rfilt); /* configure bssid mask */ - if (ah->ah_caps.halBssIdMaskSupport) + if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) ath9k_hw_setbssidmask(ah, sc->sc_bssidmask); /* configure operational mode */ @@ -1018,7 +1018,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) PCI_DMA_FROMDEVICE); /* XXX: Ah! make me more readable, use a helper */ - if (ah->ah_caps.halHTSupport) { + if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) { if (ds->ds_rxstat.rs_moreaggr == 0) { rx_status.rssictl[0] = ds->ds_rxstat.rs_rssi_ctl0; diff --git a/drivers/net/wireless/ath9k/regd.c b/drivers/net/wireless/ath9k/regd.c index 7b0176e3eb40..05e10c485fa4 100644 --- a/drivers/net/wireless/ath9k/regd.c +++ b/drivers/net/wireless/ath9k/regd.c @@ -87,7 +87,7 @@ static bool ath9k_regd_is_fcc_midband_supported(struct ath_hal *ah) { u32 regcap; - regcap = ah->ah_caps.halRegCap; + regcap = ah->ah_caps.reg_cap; if (regcap & AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND) return true; @@ -138,7 +138,7 @@ ath9k_regd_get_wmodes_nreg(struct ath_hal *ah, { u32 modesAvail; - modesAvail = ah->ah_caps.halWirelessModes; + modesAvail = ah->ah_caps.wireless_modes; if ((modesAvail & ATH9K_MODE_SEL_11G) && (!country->allow11g)) modesAvail &= ~ATH9K_MODE_SEL_11G; @@ -436,7 +436,7 @@ ath9k_regd_add_channel(struct ath_hal *ah, return false; } if ((fband->channelBW == CHANNEL_HALF_BW) && - !ah->ah_caps.halChanHalfRate) { + !(ah->ah_caps.hw_caps & ATH9K_HW_CAP_CHAN_HALFRATE)) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: Skipping %u half rate channel\n", __func__, c); @@ -444,7 +444,7 @@ ath9k_regd_add_channel(struct ath_hal *ah, } if ((fband->channelBW == CHANNEL_QUARTER_BW) && - !ah->ah_caps.halChanQuarterRate) { + !(ah->ah_caps.hw_caps & ATH9K_HW_CAP_CHAN_QUARTERRATE)) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: Skipping %u quarter rate channel\n", __func__, c); @@ -529,7 +529,7 @@ ath9k_regd_add_channel(struct ath_hal *ah, if ((c < 2412) || (c > 2462)) { if (rd5GHz.regDmnEnum == MKK1 || rd5GHz.regDmnEnum == MKK2) { - u32 regcap = ah->ah_caps.halRegCap; + u32 regcap = ah->ah_caps.reg_cap; if (!(regcap & (AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN | AR_EEPROM_EEREGCAP_EN_KK_U2 | @@ -594,7 +594,7 @@ static bool ath9k_regd_japan_check(struct ath_hal *ah, for (i = 0; i < ARRAY_SIZE(j_bandcheck); i++) { if (j_bandcheck[i].freqbandbit == b) { - regcap = ah->ah_caps.halRegCap; + regcap = ah->ah_caps.reg_cap; if ((j_bandcheck[i].eepromflagtocheck & regcap) == 0) { skipband = true; } else if ((regcap & AR_EEPROM_EEREGCAP_EN_KK_U2) || @@ -726,7 +726,7 @@ ath9k_regd_init_channels(struct ath_hal *ah, } if (country == NULL) { - modesAvail = ah->ah_caps.halWirelessModes; + modesAvail = ah->ah_caps.wireless_modes; } else { modesAvail = ath9k_regd_get_wmodes_nreg(ah, country, &rd5GHz); if (!enableOutdoor) diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c index f0297ee5d053..92bb3c22e7c2 100644 --- a/drivers/net/wireless/ath9k/xmit.c +++ b/drivers/net/wireless/ath9k/xmit.c @@ -781,7 +781,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf) * let rate series flags determine which rates will actually * use RTS. */ - if (ah->ah_caps.halHTSupport && bf->bf_isdata) { + if ((ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) && bf->bf_isdata) { BUG_ON(!an); /* * 802.11g protection not needed, use our default behavior @@ -897,7 +897,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf) * For non-HT devices, calculate RTS/CTS duration in software * and disable multi-rate retry. */ - if (flags && !ah->ah_caps.halHTSupport) { + if (flags && !(ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)) { /* * Compute the transmit duration based on the frame * size and the size of an ACK frame. We call into the -- cgit v1.2.1 From ea9880fb059e0e95d651eab6029f58e7c81b8602 Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 7 Aug 2008 10:53:10 +0530 Subject: ath9k: Remove redundant data structure ath9k_txq_info Use ath9k_tx_queue_info which contains the same elements, and merge get/set functions of tx queue properties. Also, fix whitespace damage in struct ath_softc. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/ath9k.h | 27 +---- drivers/net/wireless/ath9k/beacon.c | 8 +- drivers/net/wireless/ath9k/core.h | 206 +++++++++++++++++------------------- drivers/net/wireless/ath9k/hw.c | 118 ++++++++++----------- drivers/net/wireless/ath9k/main.c | 2 +- drivers/net/wireless/ath9k/xmit.c | 29 ++--- 6 files changed, 178 insertions(+), 212 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index 71027dcdcafc..587c0fed9f8f 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h @@ -282,23 +282,6 @@ enum ath9k_tx_queue_flags { TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE = 0x0080, }; -struct ath9k_txq_info { - u32 tqi_ver; - enum ath9k_tx_queue_subtype tqi_subtype; - enum ath9k_tx_queue_flags tqi_qflags; - u32 tqi_priority; - u32 tqi_aifs; - u32 tqi_cwmin; - u32 tqi_cwmax; - u16 tqi_shretry; - u16 tqi_lgretry; - u32 tqi_cbrPeriod; - u32 tqi_cbrOverflowLimit; - u32 tqi_burstTime; - u32 tqi_readyTime; - u32 tqi_compBuf; -}; - #define ATH9K_TXQ_USEDEFAULT ((u32) -1) #define ATH9K_DECOMP_MASK_SIZE 128 @@ -999,10 +982,10 @@ u32 ath9k_regd_get_ctl(struct ath_hal *ah, struct ath9k_channel *chan); u32 ath9k_regd_get_antenna_allowed(struct ath_hal *ah, struct ath9k_channel *chan); u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags); -bool ath9k_hw_gettxqueueprops(struct ath_hal *ah, int q, - struct ath9k_txq_info *qInfo); -bool ath9k_hw_settxqueueprops(struct ath_hal *ah, int q, - const struct ath9k_txq_info *qInfo); +bool ath9k_hw_get_txq_props(struct ath_hal *ah, int q, + struct ath9k_tx_queue_info *qinfo); +bool ath9k_hw_set_txq_props(struct ath_hal *ah, int q, + const struct ath9k_tx_queue_info *qinfo); struct ath9k_channel *ath9k_regd_check_channel(struct ath_hal *ah, const struct ath9k_channel *c); void ath9k_hw_set11n_txdesc(struct ath_hal *ah, struct ath_desc *ds, @@ -1053,7 +1036,7 @@ void ath9k_hw_set11n_virtualmorefrag(struct ath_hal *ah, bool ath9k_hw_set_txpowerlimit(struct ath_hal *ah, u32 limit); bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah); int ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type, - const struct ath9k_txq_info *qInfo); + const struct ath9k_tx_queue_info *qinfo); u32 ath9k_hw_numtxpending(struct ath_hal *ah, u32 q); const char *ath9k_hw_probe(u16 vendorid, u16 devid); bool ath9k_hw_disable(struct ath_hal *ah); diff --git a/drivers/net/wireless/ath9k/beacon.c b/drivers/net/wireless/ath9k/beacon.c index ba3dd253e963..6e6538b31a4c 100644 --- a/drivers/net/wireless/ath9k/beacon.c +++ b/drivers/net/wireless/ath9k/beacon.c @@ -30,9 +30,9 @@ static int ath_beaconq_config(struct ath_softc *sc) { struct ath_hal *ah = sc->sc_ah; - struct ath9k_txq_info qi; + struct ath9k_tx_queue_info qi; - ath9k_hw_gettxqueueprops(ah, sc->sc_bhalq, &qi); + ath9k_hw_get_txq_props(ah, sc->sc_bhalq, &qi); if (sc->sc_opmode == ATH9K_M_HOSTAP) { /* Always burst out beacon and CAB traffic. */ qi.tqi_aifs = 1; @@ -45,7 +45,7 @@ static int ath_beaconq_config(struct ath_softc *sc) qi.tqi_cwmax = sc->sc_beacon_qi.tqi_cwmax; } - if (!ath9k_hw_settxqueueprops(ah, sc->sc_bhalq, &qi)) { + if (!ath9k_hw_set_txq_props(ah, sc->sc_bhalq, &qi)) { DPRINTF(sc, ATH_DBG_FATAL, "%s: unable to update h/w beacon queue parameters\n", __func__); @@ -335,7 +335,7 @@ static void ath_beacon_start_adhoc(struct ath_softc *sc, int if_id) int ath_beaconq_setup(struct ath_hal *ah) { - struct ath9k_txq_info qi; + struct ath9k_tx_queue_info qi; memzero(&qi, sizeof(qi)); qi.tqi_aifs = 1; diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h index cf76b36d1757..903bd4624c6e 100644 --- a/drivers/net/wireless/ath9k/core.h +++ b/drivers/net/wireless/ath9k/core.h @@ -568,7 +568,8 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq); int ath_tx_init(struct ath_softc *sc, int nbufs); int ath_tx_cleanup(struct ath_softc *sc); int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype); -int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_txq_info *q); +int ath_txq_update(struct ath_softc *sc, int qnum, + struct ath9k_tx_queue_info *q); int ath_tx_start(struct ath_softc *sc, struct sk_buff *skb); void ath_tx_tasklet(struct ath_softc *sc); u32 ath_txq_depth(struct ath_softc *sc, int qnum); @@ -922,132 +923,123 @@ struct ath_ht_info { }; struct ath_softc { - struct ieee80211_hw *hw; /* mac80211 instance */ - struct pci_dev *pdev; /* Bus handle */ - void __iomem *mem; /* address of the device */ - struct tasklet_struct intr_tq; /* General tasklet */ - struct tasklet_struct bcon_tasklet; /* Beacon tasklet */ - struct ath_config sc_config; /* per-instance load-time - parameters */ - int sc_debug; /* Debug masks */ - struct ath_hal *sc_ah; /* HAL Instance */ - struct ath_rate_softc *sc_rc; /* tx rate control support */ - u32 sc_intrstatus; /* HAL_STATUS */ - enum ath9k_opmode sc_opmode; /* current operating mode */ - - /* Properties, Config */ - u8 sc_invalid; /* being detached */ - u8 sc_beacons; /* beacons running */ - u8 sc_scanning; /* scanning active */ - u8 sc_txaggr; /* enable 11n tx aggregation */ - u8 sc_rxaggr; /* enable 11n rx aggregation */ - u8 sc_update_chainmask; /* change chain mask */ - u8 sc_full_reset; /* force full reset */ - enum wireless_mode sc_curmode; /* current phy mode */ - u16 sc_curtxpow; /* current tx power limit */ - u16 sc_curaid; /* current association id */ - u8 sc_curbssid[ETH_ALEN]; - u8 sc_myaddr[ETH_ALEN]; - enum PROT_MODE sc_protmode; /* protection mode */ - u8 sc_mcastantenna;/* Multicast antenna number */ - u8 sc_txantenna; /* data tx antenna - (fixed or auto) */ - u8 sc_nbcnvaps; /* # of vaps sending beacons */ - u16 sc_nvaps; /* # of active virtual ap's */ - struct ath_vap *sc_vaps[ATH_BCBUF]; /* interface id - to avp map */ - enum ath9k_int sc_imask; /* interrupt mask copy */ - u8 sc_bssidmask[ETH_ALEN]; - u8 sc_defant; /* current default antenna */ - u8 sc_rxotherant; /* rx's on non-default antenna*/ - u16 sc_cachelsz; /* cache line size */ - int sc_slotupdate; /* slot to next advance fsm */ - int sc_slottime; /* slot time */ - u8 sc_noreset; - int sc_bslot[ATH_BCBUF];/* beacon xmit slots */ - struct ath9k_node_stats sc_halstats; /* station-mode rssi stats */ - struct list_head node_list; - struct ath_ht_info sc_ht_info; - int16_t sc_noise_floor; /* signal noise floor in dBm */ - enum ath9k_ht_extprotspacing sc_ht_extprotspacing; - u8 sc_tx_chainmask; - u8 sc_rx_chainmask; - u8 sc_rxchaindetect_ref; - u8 sc_rxchaindetect_thresh5GHz; - u8 sc_rxchaindetect_thresh2GHz; - u8 sc_rxchaindetect_delta5GHz; - u8 sc_rxchaindetect_delta2GHz; - u32 sc_rtsaggrlimit; /* Chipset specific - aggr limit */ - u32 sc_flags; + struct ieee80211_hw *hw; + struct pci_dev *pdev; + void __iomem *mem; + struct tasklet_struct intr_tq; + struct tasklet_struct bcon_tasklet; + struct ath_config sc_config; /* load-time parameters */ + int sc_debug; + struct ath_hal *sc_ah; + struct ath_rate_softc *sc_rc; /* tx rate control support */ + u32 sc_intrstatus; + enum ath9k_opmode sc_opmode; /* current operating mode */ + + u8 sc_invalid; /* being detached */ + u8 sc_beacons; /* beacons running */ + u8 sc_scanning; /* scanning active */ + u8 sc_txaggr; /* enable 11n tx aggregation */ + u8 sc_rxaggr; /* enable 11n rx aggregation */ + u8 sc_update_chainmask; /* change chain mask */ + u8 sc_full_reset; /* force full reset */ + enum wireless_mode sc_curmode; /* current phy mode */ + u16 sc_curtxpow; + u16 sc_curaid; + u8 sc_curbssid[ETH_ALEN]; + u8 sc_myaddr[ETH_ALEN]; + enum PROT_MODE sc_protmode; + u8 sc_mcastantenna; + u8 sc_txantenna; /* data tx antenna (fixed or auto) */ + u8 sc_nbcnvaps; /* # of vaps sending beacons */ + u16 sc_nvaps; /* # of active virtual ap's */ + struct ath_vap *sc_vaps[ATH_BCBUF]; + enum ath9k_int sc_imask; + u8 sc_bssidmask[ETH_ALEN]; + u8 sc_defant; /* current default antenna */ + u8 sc_rxotherant; /* rx's on non-default antenna */ + u16 sc_cachelsz; + int sc_slotupdate; /* slot to next advance fsm */ + int sc_slottime; + u8 sc_noreset; + int sc_bslot[ATH_BCBUF]; + struct ath9k_node_stats sc_halstats; /* station-mode rssi stats */ + struct list_head node_list; + struct ath_ht_info sc_ht_info; + int16_t sc_noise_floor; /* signal noise floor in dBm */ + enum ath9k_ht_extprotspacing sc_ht_extprotspacing; + u8 sc_tx_chainmask; + u8 sc_rx_chainmask; + u8 sc_rxchaindetect_ref; + u8 sc_rxchaindetect_thresh5GHz; + u8 sc_rxchaindetect_thresh2GHz; + u8 sc_rxchaindetect_delta5GHz; + u8 sc_rxchaindetect_delta2GHz; + u32 sc_rtsaggrlimit; /* Chipset specific aggr limit */ + u32 sc_flags; #ifdef CONFIG_SLOW_ANT_DIV - /* Slow antenna diversity */ - struct ath_antdiv sc_antdiv; + struct ath_antdiv sc_antdiv; #endif enum { - OK, /* no change needed */ - UPDATE, /* update pending */ - COMMIT /* beacon sent, commit change */ - } sc_updateslot; /* slot time update fsm */ + OK, /* no change needed */ + UPDATE, /* update pending */ + COMMIT /* beacon sent, commit change */ + } sc_updateslot; /* slot time update fsm */ /* Crypto */ - u32 sc_keymax; /* size of key cache */ - DECLARE_BITMAP(sc_keymap, ATH_KEYMAX); /* key use bit map */ - u8 sc_splitmic; /* split TKIP MIC keys */ - int sc_keytype; /* type of the key being used */ + u32 sc_keymax; /* size of key cache */ + DECLARE_BITMAP(sc_keymap, ATH_KEYMAX); /* key use bit map */ + u8 sc_splitmic; /* split TKIP MIC keys */ + int sc_keytype; /* RX */ - struct list_head sc_rxbuf; /* receive buffer */ - struct ath_descdma sc_rxdma; /* RX descriptors */ - int sc_rxbufsize; /* rx size based on mtu */ - u32 *sc_rxlink; /* link ptr in last RX desc */ - u32 sc_rxflush; /* rx flush in progress */ - u64 sc_lastrx; /* tsf of last rx'd frame */ + struct list_head sc_rxbuf; + struct ath_descdma sc_rxdma; + int sc_rxbufsize; /* rx size based on mtu */ + u32 *sc_rxlink; /* link ptr in last RX desc */ + u32 sc_rxflush; /* rx flush in progress */ + u64 sc_lastrx; /* tsf of last rx'd frame */ /* TX */ - struct list_head sc_txbuf; /* transmit buffer */ - struct ath_txq sc_txq[ATH9K_NUM_TX_QUEUES]; - struct ath_descdma sc_txdma; /* TX descriptors */ - u32 sc_txqsetup; /* h/w queues setup */ - u32 sc_txintrperiod;/* tx interrupt batching */ - int sc_haltype2q[ATH9K_WME_AC_VO+1]; /* HAL WME - AC -> h/w qnum */ - u32 sc_ant_tx[8]; /* recent tx frames/antenna */ + struct list_head sc_txbuf; + struct ath_txq sc_txq[ATH9K_NUM_TX_QUEUES]; + struct ath_descdma sc_txdma; + u32 sc_txqsetup; + u32 sc_txintrperiod; /* tx interrupt batching */ + int sc_haltype2q[ATH9K_WME_AC_VO+1]; /* HAL WME AC -> h/w qnum */ + u32 sc_ant_tx[8]; /* recent tx frames/antenna */ /* Beacon */ - struct ath9k_txq_info sc_beacon_qi; /* adhoc only: beacon - queue parameters */ - struct ath_descdma sc_bdma; /* beacon descriptors */ - struct ath_txq *sc_cabq; /* tx q for cab frames */ - struct list_head sc_bbuf; /* beacon buffers */ - u32 sc_bhalq; /* HAL q for outgoing beacons */ - u32 sc_bmisscount; /* missed beacon transmits */ - u32 ast_be_xmit; /* beacons transmitted */ + struct ath9k_tx_queue_info sc_beacon_qi; + struct ath_descdma sc_bdma; + struct ath_txq *sc_cabq; + struct list_head sc_bbuf; + u32 sc_bhalq; + u32 sc_bmisscount; + u32 ast_be_xmit; /* beacons transmitted */ /* Rate */ - struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX]; - const struct ath9k_rate_table *sc_rates[WIRELESS_MODE_MAX]; - const struct ath9k_rate_table *sc_currates; /* current rate table */ - u8 sc_rixmap[256]; /* IEEE to h/w - rate table ix */ - u8 sc_minrateix; /* min h/w rate index */ - u8 sc_protrix; /* protection rate index */ + struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX]; + const struct ath9k_rate_table *sc_rates[WIRELESS_MODE_MAX]; + const struct ath9k_rate_table *sc_currates; + u8 sc_rixmap[256]; /* IEEE to h/w rate table ix */ + u8 sc_minrateix; /* min h/w rate index */ + u8 sc_protrix; /* protection rate index */ struct { - u32 rateKbps; /* transfer rate in kbs */ - u8 ieeerate; /* IEEE rate */ - } sc_hwmap[256]; /* h/w rate ix mappings */ + u32 rateKbps; /* transfer rate in kbs */ + u8 ieeerate; /* IEEE rate */ + } sc_hwmap[256]; /* h/w rate ix mappings */ /* Channel, Band */ struct ieee80211_channel channels[IEEE80211_NUM_BANDS][ATH_CHAN_MAX]; struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; - struct ath9k_channel sc_curchan; /* current h/w channel */ + struct ath9k_channel sc_curchan; /* Locks */ - spinlock_t sc_rxflushlock; /* lock of RX flush */ - spinlock_t sc_rxbuflock; /* rxbuf lock */ - spinlock_t sc_txbuflock; /* txbuf lock */ - spinlock_t sc_resetlock; - spinlock_t node_lock; + spinlock_t sc_rxflushlock; + spinlock_t sc_rxbuflock; + spinlock_t sc_txbuflock; + spinlock_t sc_resetlock; + spinlock_t node_lock; }; int ath_init(u16 devid, struct ath_softc *sc); diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index 0dd11ee3b314..bc5def9edd36 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -7394,12 +7394,21 @@ ath9k_hw_updatetxtriglevel(struct ath_hal *ah, bool bIncTrigLevel) return newLevel != curLevel; } -static bool ath9k_hw_set_txq_props(struct ath_hal *ah, - struct ath9k_tx_queue_info *qi, - const struct ath9k_txq_info *qInfo) +bool ath9k_hw_set_txq_props(struct ath_hal *ah, int q, + const struct ath9k_tx_queue_info *qinfo) { u32 cw; + struct ath_hal_5416 *ahp = AH5416(ah); + struct ath9k_hw_capabilities *pCap = &ah->ah_caps; + struct ath9k_tx_queue_info *qi; + if (q >= pCap->total_queues) { + DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", + __func__, q); + return false; + } + + qi = &ahp->ah_txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue\n", __func__); @@ -7408,43 +7417,43 @@ static bool ath9k_hw_set_txq_props(struct ath_hal *ah, DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: queue %p\n", __func__, qi); - qi->tqi_ver = qInfo->tqi_ver; - qi->tqi_subtype = qInfo->tqi_subtype; - qi->tqi_qflags = qInfo->tqi_qflags; - qi->tqi_priority = qInfo->tqi_priority; - if (qInfo->tqi_aifs != ATH9K_TXQ_USEDEFAULT) - qi->tqi_aifs = min(qInfo->tqi_aifs, 255U); + qi->tqi_ver = qinfo->tqi_ver; + qi->tqi_subtype = qinfo->tqi_subtype; + qi->tqi_qflags = qinfo->tqi_qflags; + qi->tqi_priority = qinfo->tqi_priority; + if (qinfo->tqi_aifs != ATH9K_TXQ_USEDEFAULT) + qi->tqi_aifs = min(qinfo->tqi_aifs, 255U); else qi->tqi_aifs = INIT_AIFS; - if (qInfo->tqi_cwmin != ATH9K_TXQ_USEDEFAULT) { - cw = min(qInfo->tqi_cwmin, 1024U); + if (qinfo->tqi_cwmin != ATH9K_TXQ_USEDEFAULT) { + cw = min(qinfo->tqi_cwmin, 1024U); qi->tqi_cwmin = 1; while (qi->tqi_cwmin < cw) qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1; } else - qi->tqi_cwmin = qInfo->tqi_cwmin; - if (qInfo->tqi_cwmax != ATH9K_TXQ_USEDEFAULT) { - cw = min(qInfo->tqi_cwmax, 1024U); + qi->tqi_cwmin = qinfo->tqi_cwmin; + if (qinfo->tqi_cwmax != ATH9K_TXQ_USEDEFAULT) { + cw = min(qinfo->tqi_cwmax, 1024U); qi->tqi_cwmax = 1; while (qi->tqi_cwmax < cw) qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1; } else qi->tqi_cwmax = INIT_CWMAX; - if (qInfo->tqi_shretry != 0) - qi->tqi_shretry = min((u32) qInfo->tqi_shretry, 15U); + if (qinfo->tqi_shretry != 0) + qi->tqi_shretry = min((u32) qinfo->tqi_shretry, 15U); else qi->tqi_shretry = INIT_SH_RETRY; - if (qInfo->tqi_lgretry != 0) - qi->tqi_lgretry = min((u32) qInfo->tqi_lgretry, 15U); + if (qinfo->tqi_lgretry != 0) + qi->tqi_lgretry = min((u32) qinfo->tqi_lgretry, 15U); else qi->tqi_lgretry = INIT_LG_RETRY; - qi->tqi_cbrPeriod = qInfo->tqi_cbrPeriod; - qi->tqi_cbrOverflowLimit = qInfo->tqi_cbrOverflowLimit; - qi->tqi_burstTime = qInfo->tqi_burstTime; - qi->tqi_readyTime = qInfo->tqi_readyTime; + qi->tqi_cbrPeriod = qinfo->tqi_cbrPeriod; + qi->tqi_cbrOverflowLimit = qinfo->tqi_cbrOverflowLimit; + qi->tqi_burstTime = qinfo->tqi_burstTime; + qi->tqi_readyTime = qinfo->tqi_readyTime; - switch (qInfo->tqi_subtype) { + switch (qinfo->tqi_subtype) { case ATH9K_WME_UPSD: if (qi->tqi_type == ATH9K_TX_QUEUE_DATA) qi->tqi_intFlags = ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS; @@ -7455,66 +7464,47 @@ static bool ath9k_hw_set_txq_props(struct ath_hal *ah, return true; } -bool ath9k_hw_settxqueueprops(struct ath_hal *ah, int q, - const struct ath9k_txq_info *qInfo) +bool ath9k_hw_get_txq_props(struct ath_hal *ah, int q, + struct ath9k_tx_queue_info *qinfo) { struct ath_hal_5416 *ahp = AH5416(ah); struct ath9k_hw_capabilities *pCap = &ah->ah_caps; + struct ath9k_tx_queue_info *qi; if (q >= pCap->total_queues) { DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", __func__, q); return false; } - return ath9k_hw_set_txq_props(ah, &ahp->ah_txq[q], qInfo); -} -static bool ath9k_hw_get_txq_props(struct ath_hal *ah, - struct ath9k_txq_info *qInfo, - const struct ath9k_tx_queue_info *qi) -{ + qi = &ahp->ah_txq[q]; if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: inactive queue\n", __func__); return false; } - qInfo->tqi_qflags = qi->tqi_qflags; - qInfo->tqi_ver = qi->tqi_ver; - qInfo->tqi_subtype = qi->tqi_subtype; - qInfo->tqi_qflags = qi->tqi_qflags; - qInfo->tqi_priority = qi->tqi_priority; - qInfo->tqi_aifs = qi->tqi_aifs; - qInfo->tqi_cwmin = qi->tqi_cwmin; - qInfo->tqi_cwmax = qi->tqi_cwmax; - qInfo->tqi_shretry = qi->tqi_shretry; - qInfo->tqi_lgretry = qi->tqi_lgretry; - qInfo->tqi_cbrPeriod = qi->tqi_cbrPeriod; - qInfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit; - qInfo->tqi_burstTime = qi->tqi_burstTime; - qInfo->tqi_readyTime = qi->tqi_readyTime; + qinfo->tqi_qflags = qi->tqi_qflags; + qinfo->tqi_ver = qi->tqi_ver; + qinfo->tqi_subtype = qi->tqi_subtype; + qinfo->tqi_qflags = qi->tqi_qflags; + qinfo->tqi_priority = qi->tqi_priority; + qinfo->tqi_aifs = qi->tqi_aifs; + qinfo->tqi_cwmin = qi->tqi_cwmin; + qinfo->tqi_cwmax = qi->tqi_cwmax; + qinfo->tqi_shretry = qi->tqi_shretry; + qinfo->tqi_lgretry = qi->tqi_lgretry; + qinfo->tqi_cbrPeriod = qi->tqi_cbrPeriod; + qinfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit; + qinfo->tqi_burstTime = qi->tqi_burstTime; + qinfo->tqi_readyTime = qi->tqi_readyTime; return true; } -bool -ath9k_hw_gettxqueueprops(struct ath_hal *ah, int q, - struct ath9k_txq_info *qInfo) -{ - struct ath_hal_5416 *ahp = AH5416(ah); - struct ath9k_hw_capabilities *pCap = &ah->ah_caps; - - if (q >= pCap->total_queues) { - DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "%s: invalid queue num %u\n", - __func__, q); - return false; - } - return ath9k_hw_get_txq_props(ah, qInfo, &ahp->ah_txq[q]); -} - int ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type, - const struct ath9k_txq_info *qInfo) + const struct ath9k_tx_queue_info *qinfo) { struct ath_hal_5416 *ahp = AH5416(ah); struct ath9k_tx_queue_info *qi; @@ -7561,7 +7551,7 @@ ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type, } memset(qi, 0, sizeof(struct ath9k_tx_queue_info)); qi->tqi_type = type; - if (qInfo == NULL) { + if (qinfo == NULL) { qi->tqi_qflags = TXQ_FLAG_TXOKINT_ENABLE | TXQ_FLAG_TXERRINT_ENABLE @@ -7573,8 +7563,8 @@ ath9k_hw_setuptxqueue(struct ath_hal *ah, enum ath9k_tx_queue type, qi->tqi_lgretry = INIT_LG_RETRY; qi->tqi_physCompBuf = 0; } else { - qi->tqi_physCompBuf = qInfo->tqi_compBuf; - (void) ath9k_hw_settxqueueprops(ah, q, qInfo); + qi->tqi_physCompBuf = qinfo->tqi_physCompBuf; + (void) ath9k_hw_set_txq_props(ah, q, qinfo); } return q; diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 01bee5ec7233..4cf0d26d1392 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -700,7 +700,7 @@ static int ath9k_conf_tx(struct ieee80211_hw *hw, const struct ieee80211_tx_queue_params *params) { struct ath_softc *sc = hw->priv; - struct ath9k_txq_info qi; + struct ath9k_tx_queue_info qi; int ret = 0, qnum; if (queue >= WME_NUM_AC) diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c index 92bb3c22e7c2..debd7f46d44a 100644 --- a/drivers/net/wireless/ath9k/xmit.c +++ b/drivers/net/wireless/ath9k/xmit.c @@ -2209,7 +2209,7 @@ int ath_tx_cleanup(struct ath_softc *sc) struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) { struct ath_hal *ah = sc->sc_ah; - struct ath9k_txq_info qi; + struct ath9k_tx_queue_info qi; int qnum; memzero(&qi, sizeof(qi)); @@ -2217,7 +2217,7 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT; qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT; qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT; - qi.tqi_compBuf = 0; + qi.tqi_physCompBuf = 0; /* * Enable interrupts only for EOL and DESC conditions. @@ -2337,11 +2337,12 @@ int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype) /* Update parameters for a transmit queue */ -int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_txq_info *qi0) +int ath_txq_update(struct ath_softc *sc, int qnum, + struct ath9k_tx_queue_info *qinfo) { struct ath_hal *ah = sc->sc_ah; int error = 0; - struct ath9k_txq_info qi; + struct ath9k_tx_queue_info qi; if (qnum == sc->sc_bhalq) { /* @@ -2349,20 +2350,20 @@ int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_txq_info *qi0) * It will be picked up by ath_beaconq_config when * it's necessary. */ - sc->sc_beacon_qi = *qi0; + sc->sc_beacon_qi = *qinfo; return 0; } ASSERT(sc->sc_txq[qnum].axq_qnum == qnum); - ath9k_hw_gettxqueueprops(ah, qnum, &qi); - qi.tqi_aifs = qi0->tqi_aifs; - qi.tqi_cwmin = qi0->tqi_cwmin; - qi.tqi_cwmax = qi0->tqi_cwmax; - qi.tqi_burstTime = qi0->tqi_burstTime; - qi.tqi_readyTime = qi0->tqi_readyTime; + ath9k_hw_get_txq_props(ah, qnum, &qi); + qi.tqi_aifs = qinfo->tqi_aifs; + qi.tqi_cwmin = qinfo->tqi_cwmin; + qi.tqi_cwmax = qinfo->tqi_cwmax; + qi.tqi_burstTime = qinfo->tqi_burstTime; + qi.tqi_readyTime = qinfo->tqi_readyTime; - if (!ath9k_hw_settxqueueprops(ah, qnum, &qi)) { + if (!ath9k_hw_set_txq_props(ah, qnum, &qi)) { DPRINTF(sc, ATH_DBG_FATAL, "%s: unable to update hardware queue %u!\n", __func__, qnum); @@ -2376,11 +2377,11 @@ int ath_txq_update(struct ath_softc *sc, int qnum, struct ath9k_txq_info *qi0) int ath_cabq_update(struct ath_softc *sc) { - struct ath9k_txq_info qi; + struct ath9k_tx_queue_info qi; int qnum = sc->sc_cabq->axq_qnum; struct ath_beacon_config conf; - ath9k_hw_gettxqueueprops(sc->sc_ah, qnum, &qi); + ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi); /* * Ensure the readytime % is within the bounds. */ -- cgit v1.2.1 From 06df8bea27e9d6fe4657e8ca0557ab7515695f00 Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 7 Aug 2008 10:53:39 +0530 Subject: ath9k: Use mac80211's band macros and remove enum hal_freq_band Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/ath9k.h | 5 ----- drivers/net/wireless/ath9k/hw.c | 8 ++++---- 2 files changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index 587c0fed9f8f..9976406f6d63 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h @@ -679,11 +679,6 @@ struct ath9k_ht_cwm { enum ath9k_ht_extprotspacing ht_extprotspacing; }; -enum hal_freq_band { - HAL_FREQ_BAND_5GHZ = 0, - HAL_FREQ_BAND_2GHZ = 1, -}; - enum ath9k_ani_cmd { ATH9K_ANI_PRESENT = 0x1, ATH9K_ANI_NOISE_IMMUNITY_LEVEL = 0x2, diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index bc5def9edd36..d4b7b63fa213 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -465,11 +465,11 @@ static void ath9k_hw_analog_shift_rmw(struct ath_hal *ah, } static u8 ath9k_hw_get_num_ant_config(struct ath_hal_5416 *ahp, - enum hal_freq_band freq_band) + enum ieee80211_band freq_band) { struct ar5416_eeprom *eep = &ahp->ah_eeprom; struct modal_eep_header *pModal = - &(eep->modalHeader[HAL_FREQ_BAND_2GHZ == freq_band]); + &(eep->modalHeader[IEEE80211_BAND_5GHZ == freq_band]); struct base_eep_header *pBase = &eep->baseEepHeader; u8 num_ant_config; @@ -3111,9 +3111,9 @@ static bool ath9k_hw_fill_cap_info(struct ath_hal *ah) pCap->reg_cap |= AR_EEPROM_EEREGCAP_EN_FCC_MIDBAND; pCap->num_antcfg_5ghz = - ath9k_hw_get_num_ant_config(ahp, HAL_FREQ_BAND_5GHZ); + ath9k_hw_get_num_ant_config(ahp, IEEE80211_BAND_5GHZ); pCap->num_antcfg_2ghz = - ath9k_hw_get_num_ant_config(ahp, HAL_FREQ_BAND_2GHZ); + ath9k_hw_get_num_ant_config(ahp, IEEE80211_BAND_2GHZ); return true; } -- cgit v1.2.1 From f22f558dcaaf2ca413571df0ae8219474af1a46f Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 7 Aug 2008 10:54:07 +0530 Subject: ath9k: Remove a few unused macros and fix indentation Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/core.h | 275 ++++++++++++++++++-------------------- 1 file changed, 130 insertions(+), 145 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h index 903bd4624c6e..e294c1b8ddb5 100644 --- a/drivers/net/wireless/ath9k/core.h +++ b/drivers/net/wireless/ath9k/core.h @@ -51,24 +51,24 @@ struct ath_node; /* Macro to expand scalars to 64-bit objects */ -#define ito64(x) (sizeof(x) == 8) ? \ - (((unsigned long long int)(x)) & (0xff)) : \ - (sizeof(x) == 16) ? \ - (((unsigned long long int)(x)) & 0xffff) : \ - ((sizeof(x) == 32) ? \ +#define ito64(x) (sizeof(x) == 8) ? \ + (((unsigned long long int)(x)) & (0xff)) : \ + (sizeof(x) == 16) ? \ + (((unsigned long long int)(x)) & 0xffff) : \ + ((sizeof(x) == 32) ? \ (((unsigned long long int)(x)) & 0xffffffff) : \ - (unsigned long long int)(x)) + (unsigned long long int)(x)) /* increment with wrap-around */ -#define INCR(_l, _sz) do { \ - (_l)++; \ - (_l) &= ((_sz) - 1); \ +#define INCR(_l, _sz) do { \ + (_l)++; \ + (_l) &= ((_sz) - 1); \ } while (0) /* decrement with wrap-around */ -#define DECR(_l, _sz) do { \ - (_l)--; \ - (_l) &= ((_sz) - 1); \ +#define DECR(_l, _sz) do { \ + (_l)--; \ + (_l) &= ((_sz) - 1); \ } while (0) #define A_MAX(a, b) ((a) > (b) ? (a) : (b)) @@ -136,12 +136,11 @@ enum ATH_DEBUG { /* Per-instance load-time (note: NOT run-time) configurations * for Atheros Device */ struct ath_config { - u32 ath_aggr_prot; - u16 txpowlimit; - u16 txpowlimit_override; - u8 cabqReadytime; /* Cabq Readytime % */ - u8 swBeaconProcess; /* Process received beacons - in SW (vs HW) */ + u32 ath_aggr_prot; + u16 txpowlimit; + u16 txpowlimit_override; + u8 cabqReadytime; /* Cabq Readytime % */ + u8 swBeaconProcess; /* Process received beacons in SW (vs HW) */ }; /***********************/ @@ -161,12 +160,12 @@ struct ath_config { #define ATH_CHAINMASK_SEL_DOWN_RSSI_THRES 35 /* Struct to store the chainmask select related info */ struct ath_chainmask_sel { - struct timer_list timer; - int cur_tx_mask; /* user configured or 3x3 */ - int cur_rx_mask; /* user configured or 3x3 */ - int tx_avgrssi; - u8 switch_allowed:1, /* timer will set this */ - cm_sel_enabled:1; + struct timer_list timer; + int cur_tx_mask; /* user configured or 3x3 */ + int cur_rx_mask; /* user configured or 3x3 */ + int tx_avgrssi; + u8 switch_allowed:1, /* timer will set this */ + cm_sel_enabled : 1; }; int ath_chainmask_sel_logic(struct ath_softc *sc, struct ath_node *an); @@ -192,8 +191,8 @@ chains is due to FF aggregation in the driver. */ struct ath_buf_state { int bfs_nframes; /* # frames in aggregate */ - u16 bfs_al; /* length of aggregate */ - u16 bfs_frmlen; /* length of frame */ + u16 bfs_al; /* length of aggregate */ + u16 bfs_frmlen; /* length of frame */ int bfs_seqno; /* sequence number */ int bfs_tidno; /* tid of this frame */ int bfs_retries; /* current retries */ @@ -205,7 +204,7 @@ struct ath_buf_state { u8 bfs_isretried:1; /* is retried */ u8 bfs_isxretried:1; /* is excessive retried */ u8 bfs_shpreamble:1; /* is short preamble */ - u8 bfs_isbar:1; /* is a BAR */ + u8 bfs_isbar:1; /* is a BAR */ u8 bfs_ispspoll:1; /* is a PS-Poll */ u8 bfs_aggrburst:1; /* is a aggr burst */ u8 bfs_calcairtime:1; /* requests airtime be calculated @@ -247,7 +246,7 @@ struct ath_buf { struct list_head list; struct list_head *last; struct ath_buf *bf_lastbf; /* last buf of this unit (a frame or - an aggregate) */ + an aggregate) */ struct ath_buf *bf_lastfrm; /* last buf of this frame */ struct ath_buf *bf_next; /* next subframe in the aggregate */ struct ath_buf *bf_rifslast; /* last buf for RIFS burst */ @@ -257,7 +256,7 @@ struct ath_buf { dma_addr_t bf_daddr; /* physical addr of desc */ dma_addr_t bf_buf_addr; /* physical addr of data buffer */ u32 bf_status; - u16 bf_flags; /* tx descriptor flags */ + u16 bf_flags; /* tx descriptor flags */ struct ath_buf_state bf_state; /* buffer state */ dma_addr_t bf_dmacontext; }; @@ -331,8 +330,8 @@ struct ath_recv_status { int8_t rssictl[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */ int8_t rssiextn[ATH_MAX_ANTENNA]; /* RSSI (noise floor ajusted) */ int8_t abs_rssi; /* absolute RSSI */ - u8 rateieee; /* data rate received (IEEE rate code) */ - u8 ratecode; /* phy rate code */ + u8 rateieee; /* data rate received (IEEE rate code) */ + u8 ratecode; /* phy rate code */ int rateKbps; /* data rate received (Kbps) */ int antenna; /* rx antenna */ int flags; /* status of associated skb */ @@ -351,28 +350,28 @@ struct ath_recv_status { }; struct ath_rxbuf { - struct sk_buff *rx_wbuf; /* buffer */ - unsigned long rx_time; /* system time when received */ - struct ath_recv_status rx_status; /* cached rx status */ + struct sk_buff *rx_wbuf; + unsigned long rx_time; /* system time when received */ + struct ath_recv_status rx_status; /* cached rx status */ }; /* Per-TID aggregate receiver state for a node */ struct ath_arx_tid { - struct ath_node *an; /* parent ath node */ - struct ath_rxbuf *rxbuf; /* re-ordering buffer */ - struct timer_list timer; - spinlock_t tidlock; /* lock to protect this TID structure */ - int baw_head; /* seq_next at head */ - int baw_tail; /* tail of block-ack window */ - int seq_reset; /* need to reset start sequence */ - int addba_exchangecomplete; - u16 seq_next; /* next expected sequence */ - u16 baw_size; /* block-ack window size */ + struct ath_node *an; + struct ath_rxbuf *rxbuf; /* re-ordering buffer */ + struct timer_list timer; + spinlock_t tidlock; + int baw_head; /* seq_next at head */ + int baw_tail; /* tail of block-ack window */ + int seq_reset; /* need to reset start sequence */ + int addba_exchangecomplete; + u16 seq_next; /* next expected sequence */ + u16 baw_size; /* block-ack window size */ }; /* Per-node receiver aggregate state */ struct ath_arx { - struct ath_arx_tid tid[WME_NUM_TID]; + struct ath_arx_tid tid[WME_NUM_TID]; }; int ath_startrecv(struct ath_softc *sc); @@ -444,96 +443,95 @@ enum ATH_SM_PWRSAV{ * hardware queue). */ struct ath_txq { - u32 axq_qnum; /* hardware q number */ - u32 *axq_link; /* link ptr in last TX desc */ - struct list_head axq_q; /* transmit queue */ - spinlock_t axq_lock; /* lock on q and link */ - unsigned long axq_lockflags; /* intr state when must cli */ - u32 axq_depth; /* queue depth */ - u8 axq_aggr_depth; /* aggregates queued */ - u32 axq_totalqueued;/* total ever queued */ - u32 axq_intrcnt; /* count to determine - if descriptor should generate - int on this txq. */ - bool stopped; /* Is mac80211 queue - stopped ? */ - /* State for patching up CTS when bursting */ - struct ath_buf *axq_linkbuf; /* virtual addr of last buffer*/ - struct ath_desc *axq_lastdsWithCTS; /* first desc of the - last descriptor that contains CTS */ - struct ath_desc *axq_gatingds; /* final desc of the gating desc - * that determines whether lastdsWithCTS has - * been DMA'ed or not */ - struct list_head axq_acq; + u32 axq_qnum; /* hardware q number */ + u32 *axq_link; /* link ptr in last TX desc */ + struct list_head axq_q; /* transmit queue */ + spinlock_t axq_lock; + unsigned long axq_lockflags; /* intr state when must cli */ + u32 axq_depth; /* queue depth */ + u8 axq_aggr_depth; /* aggregates queued */ + u32 axq_totalqueued; /* total ever queued */ + + /* count to determine if descriptor should generate int on this txq. */ + u32 axq_intrcnt; + + bool stopped; /* Is mac80211 queue stopped ? */ + struct ath_buf *axq_linkbuf; /* virtual addr of last buffer*/ + + /* first desc of the last descriptor that contains CTS */ + struct ath_desc *axq_lastdsWithCTS; + + /* final desc of the gating desc that determines whether + lastdsWithCTS has been DMA'ed or not */ + struct ath_desc *axq_gatingds; + + struct list_head axq_acq; }; /* per TID aggregate tx state for a destination */ struct ath_atx_tid { - struct list_head list; /* round-robin tid entry */ - struct list_head buf_q; /* pending buffers */ - struct ath_node *an; /* parent node structure */ - struct ath_atx_ac *ac; /* parent access category */ - struct ath_buf *tx_buf[ATH_TID_MAX_BUFS];/* active tx frames */ - u16 seq_start; /* starting seq of BA window */ - u16 seq_next; /* next seq to be used */ - u16 baw_size; /* BA window size */ - int tidno; /* TID number */ - int baw_head; /* first un-acked tx buffer */ - int baw_tail; /* next unused tx buffer slot */ - int sched; /* TID is scheduled */ - int paused; /* TID is paused */ - int cleanup_inprogress; /* aggr of this TID is - being teared down */ - u32 addba_exchangecomplete:1; /* ADDBA state */ - int32_t addba_exchangeinprogress; - int addba_exchangeattempts; + struct list_head list; /* round-robin tid entry */ + struct list_head buf_q; /* pending buffers */ + struct ath_node *an; + struct ath_atx_ac *ac; + struct ath_buf *tx_buf[ATH_TID_MAX_BUFS]; /* active tx frames */ + u16 seq_start; + u16 seq_next; + u16 baw_size; + int tidno; + int baw_head; /* first un-acked tx buffer */ + int baw_tail; /* next unused tx buffer slot */ + int sched; + int paused; + int cleanup_inprogress; + u32 addba_exchangecomplete:1; + int32_t addba_exchangeinprogress; + int addba_exchangeattempts; }; /* per access-category aggregate tx state for a destination */ struct ath_atx_ac { - int sched; /* dest-ac is scheduled */ - int qnum; /* H/W queue number associated - with this AC */ - struct list_head list; /* round-robin txq entry */ - struct list_head tid_q; /* queue of TIDs with buffers */ + int sched; /* dest-ac is scheduled */ + int qnum; /* H/W queue number associated + with this AC */ + struct list_head list; /* round-robin txq entry */ + struct list_head tid_q; /* queue of TIDs with buffers */ }; /* per dest tx state */ struct ath_atx { - struct ath_atx_tid tid[WME_NUM_TID]; - struct ath_atx_ac ac[WME_NUM_AC]; + struct ath_atx_tid tid[WME_NUM_TID]; + struct ath_atx_ac ac[WME_NUM_AC]; }; /* per-frame tx control block */ struct ath_tx_control { - struct ath_node *an; /* destination to sent to */ - int if_id; /* only valid for cab traffic */ - int qnum; /* h/w queue number */ - u32 ht:1; /* if it can be transmitted using HT */ - u32 ps:1; /* if one or more stations are in PS mode */ - u32 use_minrate:1; /* if this frame should transmitted using - minimum rate */ - enum ath9k_pkt_type atype; /* Atheros packet type */ - enum ath9k_key_type keytype; /* key type */ - u32 flags; /* HAL flags */ - u16 seqno; /* sequence number */ - u16 tidno; /* tid number */ - u16 txpower; /* transmit power */ - u16 frmlen; /* frame length */ - u32 keyix; /* key index */ - int min_rate; /* minimum rate */ - int mcast_rate; /* multicast rate */ - u16 nextfraglen; /* next fragment length */ - /* below is set only by ath_dev */ - struct ath_softc *dev; /* device handle */ + struct ath_node *an; + int if_id; + int qnum; + u32 ht:1; + u32 ps:1; + u32 use_minrate:1; + enum ath9k_pkt_type atype; + enum ath9k_key_type keytype; + u32 flags; + u16 seqno; + u16 tidno; + u16 txpower; + u16 frmlen; + u32 keyix; + int min_rate; + int mcast_rate; + u16 nextfraglen; + struct ath_softc *dev; dma_addr_t dmacontext; }; /* per frame tx status block */ struct ath_xmit_status { - int retries; /* number of retries to successufully - transmit this frame */ - int flags; /* status of transmit */ + int retries; /* number of retries to successufully + transmit this frame */ + int flags; /* status of transmit */ #define ATH_TX_ERROR 0x01 #define ATH_TX_XRETRY 0x02 #define ATH_TX_BAR 0x04 @@ -647,20 +645,20 @@ struct aggr_rifs_param { /* Per-node aggregation state */ struct ath_node_aggr { - struct ath_atx tx; /* node transmit state */ - struct ath_arx rx; /* node receive state */ + struct ath_atx tx; /* node transmit state */ + struct ath_arx rx; /* node receive state */ }; /* driver-specific node state */ struct ath_node { - struct list_head list; - struct ath_softc *an_sc; /* back pointer */ - atomic_t an_refcnt; + struct list_head list; + struct ath_softc *an_sc; + atomic_t an_refcnt; struct ath_chainmask_sel an_chainmask_sel; - struct ath_node_aggr an_aggr; /* A-MPDU aggregation state */ - u8 an_smmode; /* SM Power save mode */ - u8 an_flags; - u8 an_addr[ETH_ALEN]; + struct ath_node_aggr an_aggr; + u8 an_smmode; /* SM Power save mode */ + u8 an_flags; + u8 an_addr[ETH_ALEN]; }; void ath_tx_resume_tid(struct ath_softc *sc, @@ -754,15 +752,6 @@ int ath_update_beacon(struct ath_softc *sc, /* VAPs */ /********/ -#define ATH_IF_HW_OFF 0x0001 /* hardware state needs to turn off */ -#define ATH_IF_HW_ON 0x0002 /* hardware state needs to turn on */ -/* STA only: the associated AP is HT capable */ -#define ATH_IF_HT 0x0004 -/* AP/IBSS only: current BSS has privacy on */ -#define ATH_IF_PRIVACY 0x0008 -#define ATH_IF_BEACON_ENABLE 0x0010 /* AP/IBSS only: enable beacon */ -#define ATH_IF_BEACON_SYNC 0x0020 /* IBSS only: need to sync beacon */ - /* * Define the scheme that we select MAC address for multiple * BSS on the same radio. The very first VAP will just use the MAC @@ -782,19 +771,15 @@ struct ath_vap_config { /* driver-specific vap state */ struct ath_vap { - struct ieee80211_vif *av_if_data; /* interface(vap) - instance from 802.11 protocal layer */ - enum ath9k_opmode av_opmode; /* VAP operational mode */ - struct ath_buf *av_bcbuf; /* beacon buffer */ - struct ath_beacon_offset av_boff; /* dynamic update state */ - struct ath_tx_control av_btxctl; /* tx control information - for beacon */ - int av_bslot; /* beacon slot index */ - struct ath_txq av_mcastq; /* multicast - transmit queue */ - struct ath_vap_config av_config; /* vap configuration - parameters from 802.11 protocol layer*/ - struct ath_rate_node *rc_node; + struct ieee80211_vif *av_if_data; + enum ath9k_opmode av_opmode; /* VAP operational mode */ + struct ath_buf *av_bcbuf; /* beacon buffer */ + struct ath_beacon_offset av_boff; /* dynamic update state */ + struct ath_tx_control av_btxctl; /* txctl information for beacon */ + int av_bslot; /* beacon slot index */ + struct ath_txq av_mcastq; /* multicast transmit queue */ + struct ath_vap_config av_config;/* vap configuration parameters*/ + struct ath_rate_node *rc_node; }; int ath_vap_attach(struct ath_softc *sc, -- cgit v1.2.1 From 3b95978ddadbab594aad6280bfa660a49948af86 Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 7 Aug 2008 10:54:33 +0530 Subject: ath9k: More unused macros Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/ath9k.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index 9976406f6d63..82e71f9f316a 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h @@ -634,11 +634,6 @@ enum ath9k_power_mode { ATH9K_PM_UNDEFINED }; -#define HAL_ANTENNA_MIN_MODE 0 -#define HAL_ANTENNA_FIXED_A 1 -#define HAL_ANTENNA_FIXED_B 2 -#define HAL_ANTENNA_MAX_MODE 3 - struct ath9k_mib_stats { u32 ackrcv_bad; u32 rts_bad; @@ -696,7 +691,6 @@ enum phytype { PHY_FH, PHY_OFDM, PHY_HT, - PHY_MAX }; #define PHY_CCK PHY_DS -- cgit v1.2.1 From 86b89eed9aca2a4a335b9c1bf7380f9183db431f Mon Sep 17 00:00:00 2001 From: Sujith Date: Thu, 7 Aug 2008 10:54:57 +0530 Subject: ath9k: Revamp wireless mode usage Use a single enum for managing modes, store supported modes by the HW in a bitmask. Register legacy rates with mac80211 only at init. Signed-off-by: Sujith Manoharan Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/ath9k.h | 48 ++++------ drivers/net/wireless/ath9k/beacon.c | 2 +- drivers/net/wireless/ath9k/core.c | 150 +++++++++++-------------------- drivers/net/wireless/ath9k/core.h | 2 - drivers/net/wireless/ath9k/hw.c | 79 +++++++++------- drivers/net/wireless/ath9k/main.c | 79 ++-------------- drivers/net/wireless/ath9k/rc.c | 30 +++---- drivers/net/wireless/ath9k/rc.h | 2 +- drivers/net/wireless/ath9k/regd.c | 107 +++++++++++----------- drivers/net/wireless/ath9k/regd.h | 10 +-- drivers/net/wireless/ath9k/regd_common.h | 18 ++-- drivers/net/wireless/ath9k/xmit.c | 2 +- 12 files changed, 205 insertions(+), 324 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index 82e71f9f316a..d1b0fbae5a32 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h @@ -147,6 +147,19 @@ struct ath_desc { #define ATH9K_RXDESC_INTREQ 0x0020 +enum wireless_mode { + ATH9K_MODE_11A = 0, + ATH9K_MODE_11B = 2, + ATH9K_MODE_11G = 3, + ATH9K_MODE_11NA_HT20 = 6, + ATH9K_MODE_11NG_HT20 = 7, + ATH9K_MODE_11NA_HT40PLUS = 8, + ATH9K_MODE_11NA_HT40MINUS = 9, + ATH9K_MODE_11NG_HT40PLUS = 10, + ATH9K_MODE_11NG_HT40MINUS = 11, + ATH9K_MODE_MAX +}; + enum ath9k_hw_caps { ATH9K_HW_CAP_CHAN_SPREAD = BIT(0), ATH9K_HW_CAP_MIC_AESCCM = BIT(1), @@ -190,7 +203,7 @@ enum ath9k_capability_type { struct ath9k_hw_capabilities { u32 hw_caps; /* ATH9K_HW_CAP_* from ath9k_hw_caps */ - u32 wireless_modes; + DECLARE_BITMAP(wireless_modes, ATH9K_MODE_MAX); /* ATH9K_MODE_* */ u16 total_queues; u16 keycache_size; u16 low_5ghz_chan, high_5ghz_chan; @@ -813,37 +826,6 @@ struct ath_hal { #endif }; -enum wireless_mode { - WIRELESS_MODE_11a = 0, - WIRELESS_MODE_11b = 2, - WIRELESS_MODE_11g = 3, - WIRELESS_MODE_11NA_HT20 = 6, - WIRELESS_MODE_11NG_HT20 = 7, - WIRELESS_MODE_11NA_HT40PLUS = 8, - WIRELESS_MODE_11NA_HT40MINUS = 9, - WIRELESS_MODE_11NG_HT40PLUS = 10, - WIRELESS_MODE_11NG_HT40MINUS = 11, - WIRELESS_MODE_MAX -}; - -enum { - ATH9K_MODE_SEL_11A = 0x00001, - ATH9K_MODE_SEL_11B = 0x00002, - ATH9K_MODE_SEL_11G = 0x00004, - ATH9K_MODE_SEL_11NG_HT20 = 0x00008, - ATH9K_MODE_SEL_11NA_HT20 = 0x00010, - ATH9K_MODE_SEL_11NG_HT40PLUS = 0x00020, - ATH9K_MODE_SEL_11NG_HT40MINUS = 0x00040, - ATH9K_MODE_SEL_11NA_HT40PLUS = 0x00080, - ATH9K_MODE_SEL_11NA_HT40MINUS = 0x00100, - ATH9K_MODE_SEL_2GHZ = (ATH9K_MODE_SEL_11B | - ATH9K_MODE_SEL_11G | - ATH9K_MODE_SEL_11NG_HT20), - ATH9K_MODE_SEL_5GHZ = (ATH9K_MODE_SEL_11A | - ATH9K_MODE_SEL_11NA_HT20), - ATH9K_MODE_SEL_ALL = 0xffffffff -}; - struct chan_centers { u16 synth_center; u16 ctl_center; @@ -865,7 +847,7 @@ bool ath9k_regd_init_channels(struct ath_hal *ah, u32 maxchans, u32 *nchans, u8 *regclassids, u32 maxregids, u32 *nregids, - u16 cc, u32 modeSelect, + u16 cc, bool enableOutdoor, bool enableExtendedChannels); u32 ath9k_hw_mhz2ieee(struct ath_hal *ah, u32 freq, u32 flags); diff --git a/drivers/net/wireless/ath9k/beacon.c b/drivers/net/wireless/ath9k/beacon.c index 6e6538b31a4c..caf569401a34 100644 --- a/drivers/net/wireless/ath9k/beacon.c +++ b/drivers/net/wireless/ath9k/beacon.c @@ -108,7 +108,7 @@ static void ath_beacon_setup(struct ath_softc *sc, * Calculate rate code. * XXX everything at min xmit rate */ - rix = sc->sc_minrateix; + rix = 0; rt = sc->sc_currates; rate = rt->info[rix].rateCode; if (sc->sc_flags & ATH_PREAMBLE_SHORT) diff --git a/drivers/net/wireless/ath9k/core.c b/drivers/net/wireless/ath9k/core.c index f7bf0783f3be..f6c45288d0e7 100644 --- a/drivers/net/wireless/ath9k/core.c +++ b/drivers/net/wireless/ath9k/core.c @@ -64,7 +64,7 @@ static void ath_setcurmode(struct ath_softc *sc, enum wireless_mode mode) int i; memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap)); - rt = sc->sc_rates[mode]; + rt = ath9k_hw_getratetable(sc->sc_ah, mode); BUG_ON(!rt); for (i = 0; i < rt->rateCount; i++) @@ -96,76 +96,52 @@ static void ath_setcurmode(struct ath_softc *sc, enum wireless_mode mode) * 11g, otherwise at 1Mb/s. * XXX select protection rate index from rate table. */ - sc->sc_protrix = (mode == WIRELESS_MODE_11g ? 1 : 0); - /* rate index used to send mgt frames */ - sc->sc_minrateix = 0; + sc->sc_protrix = (mode == ATH9K_MODE_11G ? 1 : 0); } /* - * Select Rate Table - * - * Based on the wireless mode passed in, the rate table in the ATH object - * is set to the mode specific rate table. This also calls the callback - * function to set the rate in the protocol layer object. -*/ - -static int ath_rate_setup(struct ath_softc *sc, enum wireless_mode mode) + * Set up rate table (legacy rates) + */ +static void ath_setup_rates(struct ath_softc *sc, enum ieee80211_band band) { struct ath_hal *ah = sc->sc_ah; - const struct ath9k_rate_table *rt; - - switch (mode) { - case WIRELESS_MODE_11a: - sc->sc_rates[mode] = - ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11A); - break; - case WIRELESS_MODE_11b: - sc->sc_rates[mode] = - ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11B); + const struct ath9k_rate_table *rt = NULL; + struct ieee80211_supported_band *sband; + struct ieee80211_rate *rate; + int i, maxrates; + + switch (band) { + case IEEE80211_BAND_2GHZ: + rt = ath9k_hw_getratetable(ah, ATH9K_MODE_11G); break; - case WIRELESS_MODE_11g: - sc->sc_rates[mode] = - ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11G); - break; - case WIRELESS_MODE_11NA_HT20: - sc->sc_rates[mode] = - ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NA_HT20); - break; - case WIRELESS_MODE_11NG_HT20: - sc->sc_rates[mode] = - ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NG_HT20); - break; - case WIRELESS_MODE_11NA_HT40PLUS: - sc->sc_rates[mode] = - ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NA_HT40PLUS); - break; - case WIRELESS_MODE_11NA_HT40MINUS: - sc->sc_rates[mode] = - ath9k_hw_getratetable(ah, - ATH9K_MODE_SEL_11NA_HT40MINUS); - break; - case WIRELESS_MODE_11NG_HT40PLUS: - sc->sc_rates[mode] = - ath9k_hw_getratetable(ah, ATH9K_MODE_SEL_11NG_HT40PLUS); - break; - case WIRELESS_MODE_11NG_HT40MINUS: - sc->sc_rates[mode] = - ath9k_hw_getratetable(ah, - ATH9K_MODE_SEL_11NG_HT40MINUS); + case IEEE80211_BAND_5GHZ: + rt = ath9k_hw_getratetable(ah, ATH9K_MODE_11A); break; default: - DPRINTF(sc, ATH_DBG_FATAL, "%s: invalid mode %u\n", - __func__, mode); - return 0; + break; } - rt = sc->sc_rates[mode]; - if (rt == NULL) - return 0; - /* setup rate set in 802.11 protocol layer */ - ath_setup_rate(sc, mode, NORMAL_RATE, rt); + if (rt == NULL) + return; - return 1; + sband = &sc->sbands[band]; + rate = sc->rates[band]; + + if (rt->rateCount > ATH_RATE_MAX) + maxrates = ATH_RATE_MAX; + else + maxrates = rt->rateCount; + + for (i = 0; i < maxrates; i++) { + rate[i].bitrate = rt->info[i].rateKbps / 100; + rate[i].hw_value = rt->info[i].rateCode; + sband->n_bitrates++; + DPRINTF(sc, ATH_DBG_CONFIG, + "%s: Rate: %2dMbps, ratecode: %2d\n", + __func__, + rate[i].bitrate / 10, + rate[i].hw_value); + } } /* @@ -191,7 +167,6 @@ static int ath_setup_channels(struct ath_softc *sc) ATH_REGCLASSIDS_MAX, &nregclass, CTRY_DEFAULT, - ATH9K_MODE_SEL_ALL, false, 1)) { u32 rd = ah->ah_currentRD; @@ -267,43 +242,26 @@ static int ath_setup_channels(struct ath_softc *sc) static enum wireless_mode ath_chan2mode(struct ath9k_channel *chan) { if (chan->chanmode == CHANNEL_A) - return WIRELESS_MODE_11a; + return ATH9K_MODE_11A; else if (chan->chanmode == CHANNEL_G) - return WIRELESS_MODE_11g; + return ATH9K_MODE_11G; else if (chan->chanmode == CHANNEL_B) - return WIRELESS_MODE_11b; + return ATH9K_MODE_11B; else if (chan->chanmode == CHANNEL_A_HT20) - return WIRELESS_MODE_11NA_HT20; + return ATH9K_MODE_11NA_HT20; else if (chan->chanmode == CHANNEL_G_HT20) - return WIRELESS_MODE_11NG_HT20; + return ATH9K_MODE_11NG_HT20; else if (chan->chanmode == CHANNEL_A_HT40PLUS) - return WIRELESS_MODE_11NA_HT40PLUS; + return ATH9K_MODE_11NA_HT40PLUS; else if (chan->chanmode == CHANNEL_A_HT40MINUS) - return WIRELESS_MODE_11NA_HT40MINUS; + return ATH9K_MODE_11NA_HT40MINUS; else if (chan->chanmode == CHANNEL_G_HT40PLUS) - return WIRELESS_MODE_11NG_HT40PLUS; + return ATH9K_MODE_11NG_HT40PLUS; else if (chan->chanmode == CHANNEL_G_HT40MINUS) - return WIRELESS_MODE_11NG_HT40MINUS; + return ATH9K_MODE_11NG_HT40MINUS; /* NB: should not get here */ - return WIRELESS_MODE_11b; -} - -/* - * Change Channels - * - * Performs the actions to change the channel in the hardware, and set up - * the current operating mode for the new channel. -*/ - -static void ath_chan_change(struct ath_softc *sc, struct ath9k_channel *chan) -{ - enum wireless_mode mode; - - mode = ath_chan2mode(chan); - - ath_rate_setup(sc, mode); - ath_setcurmode(sc, mode); + return ATH9K_MODE_11B; } /* @@ -480,7 +438,8 @@ int ath_set_channel(struct ath_softc *sc, struct ath9k_channel *hchan) * Change channels and update the h/w rate map * if we're switching; e.g. 11a to 11b/g. */ - ath_chan_change(sc, hchan); + ath_setcurmode(sc, ath_chan2mode(hchan)); + ath_update_txpow(sc); /* update tx power state */ /* * Re-enable interrupts. @@ -860,7 +819,7 @@ int ath_open(struct ath_softc *sc, struct ath9k_channel *initial_chan) * vap and node data structures, which will be needed as soon * as we start receiving. */ - ath_chan_change(sc, initial_chan); + ath_setcurmode(sc, ath_chan2mode(initial_chan)); /* XXX: we must make sure h/w is ready and clear invalid flag * before turning on interrupt. */ @@ -902,7 +861,7 @@ static int ath_reset_end(struct ath_softc *sc, u32 flag) * that changes the channel so update any state that * might change as a result. */ - ath_chan_change(sc, &sc->sc_curchan); + ath_setcurmode(sc, ath_chan2mode(&sc->sc_curchan)); ath_update_txpow(sc); /* update tx power state */ @@ -1212,14 +1171,13 @@ int ath_init(u16 devid, struct ath_softc *sc) /* default to STA mode */ sc->sc_opmode = ATH9K_M_MONITOR; - /* Setup rate tables for all potential media types. */ - /* 11g encompasses b,g */ + /* Setup rate tables */ - ath_rate_setup(sc, WIRELESS_MODE_11a); - ath_rate_setup(sc, WIRELESS_MODE_11g); + ath_setup_rates(sc, IEEE80211_BAND_2GHZ); + ath_setup_rates(sc, IEEE80211_BAND_5GHZ); /* NB: setup here so ath_rate_update is happy */ - ath_setcurmode(sc, WIRELESS_MODE_11a); + ath_setcurmode(sc, ATH9K_MODE_11A); /* * Allocate hardware transmit queues: one queue for diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h index e294c1b8ddb5..673b3d81133a 100644 --- a/drivers/net/wireless/ath9k/core.h +++ b/drivers/net/wireless/ath9k/core.h @@ -1004,10 +1004,8 @@ struct ath_softc { /* Rate */ struct ieee80211_rate rates[IEEE80211_NUM_BANDS][ATH_RATE_MAX]; - const struct ath9k_rate_table *sc_rates[WIRELESS_MODE_MAX]; const struct ath9k_rate_table *sc_currates; u8 sc_rixmap[256]; /* IEEE to h/w rate table ix */ - u8 sc_minrateix; /* min h/w rate index */ u8 sc_protrix; /* protection rate index */ struct { u32 rateKbps; /* transfer rate in kbs */ diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index d4b7b63fa213..bde162f128ab 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -225,10 +225,10 @@ static enum wireless_mode ath9k_hw_chan2wmode(struct ath_hal *ah, const struct ath9k_channel *chan) { if (IS_CHAN_CCK(chan)) - return WIRELESS_MODE_11b; + return ATH9K_MODE_11A; if (IS_CHAN_G(chan)) - return WIRELESS_MODE_11g; - return WIRELESS_MODE_11a; + return ATH9K_MODE_11G; + return ATH9K_MODE_11A; } static bool ath9k_hw_wait(struct ath_hal *ah, @@ -2416,7 +2416,7 @@ static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hal *ah) return; } else { mode = ath9k_hw_chan2wmode(ah, chan); - if (mode == WIRELESS_MODE_11g || mode == WIRELESS_MODE_11b) { + if (mode == ATH9K_MODE_11G || mode == ATH9K_MODE_11B) { if (!aniState->ofdmWeakSigDetectOff) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, @@ -2462,7 +2462,7 @@ static void ath9k_hw_ani_cck_err_trigger(struct ath_hal *ah) aniState->firstepLevel + 1); } else { mode = ath9k_hw_chan2wmode(ah, chan); - if (mode == WIRELESS_MODE_11g || mode == WIRELESS_MODE_11b) { + if (mode == ATH9K_MODE_11G || mode == ATH9K_MODE_11B) { if (aniState->firstepLevel > 0) ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, @@ -2970,29 +2970,40 @@ static bool ath9k_hw_fill_cap_info(struct ath_hal *ah) ah->ah_currentRD); } - pCap->wireless_modes = 0; eeval = ath9k_hw_get_eeprom(ahp, EEP_OP_MODE); + bitmap_zero(pCap->wireless_modes, ATH9K_MODE_MAX); if (eeval & AR5416_OPFLAGS_11A) { - pCap->wireless_modes |= ATH9K_MODE_SEL_11A | - ((!ah->ah_config.ht_enable - || (eeval & AR5416_OPFLAGS_N_5G_HT20)) ? 0 - : (ATH9K_MODE_SEL_11NA_HT20 | - ((eeval & AR5416_OPFLAGS_N_5G_HT40) ? 0 - : (ATH9K_MODE_SEL_11NA_HT40PLUS | - ATH9K_MODE_SEL_11NA_HT40MINUS)))); + set_bit(ATH9K_MODE_11A, pCap->wireless_modes); + if (ah->ah_config.ht_enable) { + if (!(eeval & AR5416_OPFLAGS_N_5G_HT20)) + set_bit(ATH9K_MODE_11NA_HT20, + pCap->wireless_modes); + if (!(eeval & AR5416_OPFLAGS_N_5G_HT40)) { + set_bit(ATH9K_MODE_11NA_HT40PLUS, + pCap->wireless_modes); + set_bit(ATH9K_MODE_11NA_HT40MINUS, + pCap->wireless_modes); + } + } } - if (eeval & AR5416_OPFLAGS_11G) { - pCap->wireless_modes |= - ATH9K_MODE_SEL_11B | ATH9K_MODE_SEL_11G | - ((!ah->ah_config.ht_enable - || (eeval & AR5416_OPFLAGS_N_2G_HT20)) ? 0 - : (ATH9K_MODE_SEL_11NG_HT20 | - ((eeval & AR5416_OPFLAGS_N_2G_HT40) ? 0 - : (ATH9K_MODE_SEL_11NG_HT40PLUS | - ATH9K_MODE_SEL_11NG_HT40MINUS)))); + if (eeval & AR5416_OPFLAGS_11G) { + set_bit(ATH9K_MODE_11B, pCap->wireless_modes); + set_bit(ATH9K_MODE_11G, pCap->wireless_modes); + if (ah->ah_config.ht_enable) { + if (!(eeval & AR5416_OPFLAGS_N_2G_HT20)) + set_bit(ATH9K_MODE_11NG_HT20, + pCap->wireless_modes); + if (!(eeval & AR5416_OPFLAGS_N_2G_HT40)) { + set_bit(ATH9K_MODE_11NG_HT40PLUS, + pCap->wireless_modes); + set_bit(ATH9K_MODE_11NG_HT40MINUS, + pCap->wireless_modes); + } + } } + pCap->tx_chainmask = ath9k_hw_get_eeprom(ahp, EEP_TX_MASK); if ((ah->ah_isPciExpress) || (eeval & AR5416_OPFLAGS_11A)) { @@ -5213,7 +5224,7 @@ static u32 ath9k_hw_mac_usec(struct ath_hal *ah, u32 clks) return clks / CLOCK_RATE[ath9k_hw_chan2wmode(ah, ah->ah_curchan)]; else - return clks / CLOCK_RATE[WIRELESS_MODE_11b]; + return clks / CLOCK_RATE[ATH9K_MODE_11B]; } static u32 ath9k_hw_mac_to_usec(struct ath_hal *ah, u32 clks) @@ -5232,7 +5243,7 @@ static u32 ath9k_hw_mac_clks(struct ath_hal *ah, u32 usecs) return usecs * CLOCK_RATE[ath9k_hw_chan2wmode(ah, ah->ah_curchan)]; else - return usecs * CLOCK_RATE[WIRELESS_MODE_11b]; + return usecs * CLOCK_RATE[ATH9K_MODE_11B]; } static u32 ath9k_hw_mac_to_clks(struct ath_hal *ah, u32 usecs) @@ -5924,7 +5935,7 @@ bool ath9k_hw_reset(struct ath_hal *ah, enum ath9k_opmode opmode, REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); - if (ah->ah_caps.wireless_modes & ATH9K_MODE_SEL_11A) { + if (test_bit(ATH9K_MODE_11A, ah->ah_caps.wireless_modes)) { if (IS_CHAN_5GHZ(chan)) ath9k_hw_set_gpio(ah, 9, 0); else @@ -8238,23 +8249,23 @@ const struct ath9k_rate_table *ath9k_hw_getratetable(struct ath_hal *ah, { struct ath9k_rate_table *rt; switch (mode) { - case ATH9K_MODE_SEL_11A: + case ATH9K_MODE_11A: rt = &ar5416_11a_table; break; - case ATH9K_MODE_SEL_11B: + case ATH9K_MODE_11B: rt = &ar5416_11b_table; break; - case ATH9K_MODE_SEL_11G: + case ATH9K_MODE_11G: rt = &ar5416_11g_table; break; - case ATH9K_MODE_SEL_11NG_HT20: - case ATH9K_MODE_SEL_11NG_HT40PLUS: - case ATH9K_MODE_SEL_11NG_HT40MINUS: + case ATH9K_MODE_11NG_HT20: + case ATH9K_MODE_11NG_HT40PLUS: + case ATH9K_MODE_11NG_HT40MINUS: rt = &ar5416_11ng_table; break; - case ATH9K_MODE_SEL_11NA_HT20: - case ATH9K_MODE_SEL_11NA_HT40PLUS: - case ATH9K_MODE_SEL_11NA_HT40MINUS: + case ATH9K_MODE_11NA_HT20: + case ATH9K_MODE_11NA_HT40PLUS: + case ATH9K_MODE_11NA_HT40MINUS: rt = &ar5416_11na_table; break; default: diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 4cf0d26d1392..2888778040e4 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -507,7 +507,13 @@ static int ath9k_config(struct ieee80211_hw *hw, } sc->sc_ah->ah_channels[pos].chanmode = - (curchan->band == IEEE80211_BAND_2GHZ) ? CHANNEL_G : CHANNEL_A; + (curchan->band == IEEE80211_BAND_2GHZ) ? + CHANNEL_G : CHANNEL_A; + + if (sc->sc_curaid && hw->conf.ht_conf.ht_supported) + sc->sc_ah->ah_channels[pos].chanmode = + ath_get_extchanmode(sc, curchan); + sc->sc_config.txpowlimit = 2 * conf->power_level; /* set h/w channel */ @@ -1145,75 +1151,6 @@ enum ath9k_ht_macmode ath_cwm_macmode(struct ath_softc *sc) return sc->sc_ht_info.tx_chan_width; } -void ath_setup_rate(struct ath_softc *sc, - enum wireless_mode wMode, - enum RATE_TYPE type, - const struct ath9k_rate_table *rt) -{ - int i, maxrates, a = 0, b = 0; - struct ieee80211_supported_band *band_2ghz; - struct ieee80211_supported_band *band_5ghz; - struct ieee80211_rate *rates_2ghz; - struct ieee80211_rate *rates_5ghz; - - if ((wMode >= WIRELESS_MODE_MAX) || (type != NORMAL_RATE)) - return; - - band_2ghz = &sc->sbands[IEEE80211_BAND_2GHZ]; - band_5ghz = &sc->sbands[IEEE80211_BAND_5GHZ]; - rates_2ghz = sc->rates[IEEE80211_BAND_2GHZ]; - rates_5ghz = sc->rates[IEEE80211_BAND_5GHZ]; - - if (rt->rateCount > ATH_RATE_MAX) - maxrates = ATH_RATE_MAX; - else - maxrates = rt->rateCount; - - if ((band_2ghz->n_bitrates != 0) && (band_5ghz->n_bitrates != 0)) { - DPRINTF(sc, ATH_DBG_CONFIG, - "%s: Rates already setup\n", __func__); - return; - } - - for (i = 0; i < maxrates; i++) { - switch (wMode) { - case WIRELESS_MODE_11b: - case WIRELESS_MODE_11g: - rates_2ghz[a].bitrate = rt->info[i].rateKbps / 100; - rates_2ghz[a].hw_value = rt->info[i].rateCode; - a++; - band_2ghz->n_bitrates = a; - break; - case WIRELESS_MODE_11a: - rates_5ghz[b].bitrate = rt->info[i].rateKbps / 100; - rates_5ghz[b].hw_value = rt->info[i].rateCode; - b++; - band_5ghz->n_bitrates = b; - break; - default: - break; - } - } - - if (band_2ghz->n_bitrates) { - for (i = 0; i < band_2ghz->n_bitrates; i++) { - DPRINTF(sc, ATH_DBG_CONFIG, - "%s: 2GHz Rate: %2dMbps, ratecode: %2d\n", - __func__, - rates_2ghz[i].bitrate / 10, - rates_2ghz[i].hw_value); - } - } else if (band_5ghz->n_bitrates) { - for (i = 0; i < band_5ghz->n_bitrates; i++) { - DPRINTF(sc, ATH_DBG_CONFIG, - "%s: 5Ghz Rate: %2dMbps, ratecode: %2d\n", - __func__, - rates_5ghz[i].bitrate / 10, - rates_5ghz[i].hw_value); - } - } -} - static int ath_detach(struct ath_softc *sc) { struct ieee80211_hw *hw = sc->hw; @@ -1275,7 +1212,7 @@ static int ath_attach(u16 devid, hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &sc->sbands[IEEE80211_BAND_2GHZ]; - if (sc->sc_ah->ah_caps.wireless_modes & ATH9K_MODE_SEL_11A) { + if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes)) { sc->sbands[IEEE80211_BAND_5GHZ].channels = sc->channels[IEEE80211_BAND_5GHZ]; sc->sbands[IEEE80211_BAND_5GHZ].bitrates = diff --git a/drivers/net/wireless/ath9k/rc.c b/drivers/net/wireless/ath9k/rc.c index d3132009d94d..73c460ad355f 100644 --- a/drivers/net/wireless/ath9k/rc.c +++ b/drivers/net/wireless/ath9k/rc.c @@ -484,37 +484,37 @@ static void ar5416_attach_ratetables(struct ath_rate_softc *sc) /* * Attach rate tables. */ - sc->hw_rate_table[WIRELESS_MODE_11b] = &ar5416_11b_ratetable; - sc->hw_rate_table[WIRELESS_MODE_11a] = &ar5416_11a_ratetable; - sc->hw_rate_table[WIRELESS_MODE_11g] = &ar5416_11g_ratetable; + sc->hw_rate_table[ATH9K_MODE_11B] = &ar5416_11b_ratetable; + sc->hw_rate_table[ATH9K_MODE_11A] = &ar5416_11a_ratetable; + sc->hw_rate_table[ATH9K_MODE_11G] = &ar5416_11g_ratetable; - sc->hw_rate_table[WIRELESS_MODE_11NA_HT20] = &ar5416_11na_ratetable; - sc->hw_rate_table[WIRELESS_MODE_11NG_HT20] = &ar5416_11ng_ratetable; - sc->hw_rate_table[WIRELESS_MODE_11NA_HT40PLUS] = + sc->hw_rate_table[ATH9K_MODE_11NA_HT20] = &ar5416_11na_ratetable; + sc->hw_rate_table[ATH9K_MODE_11NG_HT20] = &ar5416_11ng_ratetable; + sc->hw_rate_table[ATH9K_MODE_11NA_HT40PLUS] = &ar5416_11na_ratetable; - sc->hw_rate_table[WIRELESS_MODE_11NA_HT40MINUS] = + sc->hw_rate_table[ATH9K_MODE_11NA_HT40MINUS] = &ar5416_11na_ratetable; - sc->hw_rate_table[WIRELESS_MODE_11NG_HT40PLUS] = + sc->hw_rate_table[ATH9K_MODE_11NG_HT40PLUS] = &ar5416_11ng_ratetable; - sc->hw_rate_table[WIRELESS_MODE_11NG_HT40MINUS] = + sc->hw_rate_table[ATH9K_MODE_11NG_HT40MINUS] = &ar5416_11ng_ratetable; } static void ar5416_setquarter_ratetable(struct ath_rate_softc *sc) { - sc->hw_rate_table[WIRELESS_MODE_11a] = &ar5416_11a_ratetable_Quarter; + sc->hw_rate_table[ATH9K_MODE_11A] = &ar5416_11a_ratetable_Quarter; return; } static void ar5416_sethalf_ratetable(struct ath_rate_softc *sc) { - sc->hw_rate_table[WIRELESS_MODE_11a] = &ar5416_11a_ratetable_Half; + sc->hw_rate_table[ATH9K_MODE_11A] = &ar5416_11a_ratetable_Half; return; } static void ar5416_setfull_ratetable(struct ath_rate_softc *sc) { - sc->hw_rate_table[WIRELESS_MODE_11a] = &ar5416_11a_ratetable; + sc->hw_rate_table[ATH9K_MODE_11A] = &ar5416_11a_ratetable; return; } @@ -1123,9 +1123,9 @@ static void ath_rc_ratefind(struct ath_softc *sc, * So, set fourth rate in series to be same as third one for * above conditions. */ - if ((sc->sc_curmode == WIRELESS_MODE_11NG_HT20) || - (sc->sc_curmode == WIRELESS_MODE_11NG_HT40PLUS) || - (sc->sc_curmode == WIRELESS_MODE_11NG_HT40MINUS)) { + if ((sc->sc_curmode == ATH9K_MODE_11NG_HT20) || + (sc->sc_curmode == ATH9K_MODE_11NG_HT40PLUS) || + (sc->sc_curmode == ATH9K_MODE_11NG_HT40MINUS)) { u8 dot11rate = rate_table->info[rix].dot11rate; u8 phy = rate_table->info[rix].phy; if (i == 4 && diff --git a/drivers/net/wireless/ath9k/rc.h b/drivers/net/wireless/ath9k/rc.h index 8f6c28e65191..71aef9c75232 100644 --- a/drivers/net/wireless/ath9k/rc.h +++ b/drivers/net/wireless/ath9k/rc.h @@ -247,7 +247,7 @@ struct ath_rateset { /* per-device state */ struct ath_rate_softc { /* phy tables that contain rate control data */ - const void *hw_rate_table[WIRELESS_MODE_MAX]; + const void *hw_rate_table[ATH9K_MODE_MAX]; int fixedrix; /* -1 or index of fixed rate */ }; diff --git a/drivers/net/wireless/ath9k/regd.c b/drivers/net/wireless/ath9k/regd.c index 05e10c485fa4..62e28887ccd3 100644 --- a/drivers/net/wireless/ath9k/regd.c +++ b/drivers/net/wireless/ath9k/regd.c @@ -131,46 +131,45 @@ static bool ath9k_regd_is_ccode_valid(struct ath_hal *ah, return false; } -static u32 +static void ath9k_regd_get_wmodes_nreg(struct ath_hal *ah, struct country_code_to_enum_rd *country, - struct regDomain *rd5GHz) + struct regDomain *rd5GHz, + unsigned long *modes_allowed) { - u32 modesAvail; + bitmap_copy(modes_allowed, ah->ah_caps.wireless_modes, ATH9K_MODE_MAX); - modesAvail = ah->ah_caps.wireless_modes; + if (test_bit(ATH9K_MODE_11G, ah->ah_caps.wireless_modes) && + (!country->allow11g)) + clear_bit(ATH9K_MODE_11G, modes_allowed); - if ((modesAvail & ATH9K_MODE_SEL_11G) && (!country->allow11g)) - modesAvail &= ~ATH9K_MODE_SEL_11G; - if ((modesAvail & ATH9K_MODE_SEL_11A) && + if (test_bit(ATH9K_MODE_11A, ah->ah_caps.wireless_modes) && (ath9k_regd_is_chan_bm_zero(rd5GHz->chan11a))) - modesAvail &= ~ATH9K_MODE_SEL_11A; + clear_bit(ATH9K_MODE_11A, modes_allowed); - if ((modesAvail & ATH9K_MODE_SEL_11NG_HT20) + if (test_bit(ATH9K_MODE_11NG_HT20, ah->ah_caps.wireless_modes) && (!country->allow11ng20)) - modesAvail &= ~ATH9K_MODE_SEL_11NG_HT20; + clear_bit(ATH9K_MODE_11NG_HT20, modes_allowed); - if ((modesAvail & ATH9K_MODE_SEL_11NA_HT20) + if (test_bit(ATH9K_MODE_11NA_HT20, ah->ah_caps.wireless_modes) && (!country->allow11na20)) - modesAvail &= ~ATH9K_MODE_SEL_11NA_HT20; + clear_bit(ATH9K_MODE_11NA_HT20, modes_allowed); - if ((modesAvail & ATH9K_MODE_SEL_11NG_HT40PLUS) && + if (test_bit(ATH9K_MODE_11NG_HT40PLUS, ah->ah_caps.wireless_modes) && (!country->allow11ng40)) - modesAvail &= ~ATH9K_MODE_SEL_11NG_HT40PLUS; + clear_bit(ATH9K_MODE_11NG_HT40PLUS, modes_allowed); - if ((modesAvail & ATH9K_MODE_SEL_11NG_HT40MINUS) && + if (test_bit(ATH9K_MODE_11NG_HT40MINUS, ah->ah_caps.wireless_modes) && (!country->allow11ng40)) - modesAvail &= ~ATH9K_MODE_SEL_11NG_HT40MINUS; + clear_bit(ATH9K_MODE_11NG_HT40MINUS, modes_allowed); - if ((modesAvail & ATH9K_MODE_SEL_11NA_HT40PLUS) && + if (test_bit(ATH9K_MODE_11NA_HT40PLUS, ah->ah_caps.wireless_modes) && (!country->allow11na40)) - modesAvail &= ~ATH9K_MODE_SEL_11NA_HT40PLUS; + clear_bit(ATH9K_MODE_11NA_HT40PLUS, modes_allowed); - if ((modesAvail & ATH9K_MODE_SEL_11NA_HT40MINUS) && + if (test_bit(ATH9K_MODE_11NA_HT40MINUS, ah->ah_caps.wireless_modes) && (!country->allow11na40)) - modesAvail &= ~ATH9K_MODE_SEL_11NA_HT40MINUS; - - return modesAvail; + clear_bit(ATH9K_MODE_11NA_HT40MINUS, modes_allowed); } bool ath9k_regd_is_public_safety_sku(struct ath_hal *ah) @@ -545,10 +544,10 @@ ath9k_regd_add_channel(struct ath_hal *ah, } } - if (cm->mode & (ATH9K_MODE_SEL_11A | - ATH9K_MODE_SEL_11NA_HT20 | - ATH9K_MODE_SEL_11NA_HT40PLUS | - ATH9K_MODE_SEL_11NA_HT40MINUS)) { + if ((cm->mode == ATH9K_MODE_11A) || + (cm->mode == ATH9K_MODE_11NA_HT20) || + (cm->mode == ATH9K_MODE_11NA_HT40PLUS) || + (cm->mode == ATH9K_MODE_11NA_HT40MINUS)) { if (rd->flags & (ADHOC_NO_11A | DISALLOW_ADHOC_11A)) privFlags |= CHANNEL_DISALLOW_ADHOC; } @@ -618,10 +617,9 @@ ath9k_regd_init_channels(struct ath_hal *ah, u32 maxchans, u32 *nchans, u8 *regclassids, u32 maxregids, u32 *nregids, u16 cc, - u32 modeSelect, bool enableOutdoor, + bool enableOutdoor, bool enableExtendedChannels) { - u32 modesAvail; u16 maxChan = 7000; struct country_code_to_enum_rd *country = NULL; struct regDomain rd5GHz, rd2GHz; @@ -631,11 +629,13 @@ ath9k_regd_init_channels(struct ath_hal *ah, u8 ctl; int regdmn; u16 chanSep; + unsigned long *modes_avail; + DECLARE_BITMAP(modes_allowed, ATH9K_MODE_MAX); - DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: cc %u mode 0x%x%s%s\n", - __func__, cc, modeSelect, - enableOutdoor ? " Enable outdoor" : " ", - enableExtendedChannels ? " Enable ecm" : ""); + DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: cc %u %s %s\n", + __func__, cc, + enableOutdoor ? "Enable outdoor" : "", + enableExtendedChannels ? "Enable ecm" : ""); if (!ath9k_regd_is_ccode_valid(ah, cc)) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, @@ -726,9 +726,11 @@ ath9k_regd_init_channels(struct ath_hal *ah, } if (country == NULL) { - modesAvail = ah->ah_caps.wireless_modes; + modes_avail = ah->ah_caps.wireless_modes; } else { - modesAvail = ath9k_regd_get_wmodes_nreg(ah, country, &rd5GHz); + ath9k_regd_get_wmodes_nreg(ah, country, &rd5GHz, modes_allowed); + modes_avail = modes_allowed; + if (!enableOutdoor) maxChan = country->outdoorChanStart; } @@ -745,19 +747,12 @@ ath9k_regd_init_channels(struct ath_hal *ah, struct RegDmnFreqBand *fband = NULL, *freqs; int8_t low_adj = 0, hi_adj = 0; - if ((cm->mode & modeSelect) == 0) { + if (!test_bit(cm->mode, modes_avail)) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, - "%s: skip mode 0x%x flags 0x%x\n", + "%s: !avail mode %d flags 0x%x\n", __func__, cm->mode, cm->flags); continue; } - if ((cm->mode & modesAvail) == 0) { - DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, - "%s: !avail mode 0x%x (0x%x) flags 0x%x\n", - __func__, modesAvail, cm->mode, - cm->flags); - continue; - } if (!ath9k_get_channel_edges(ah, cm->flags, &c_lo, &c_hi)) { DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, "%s: channels 0x%x not supported " @@ -767,25 +762,25 @@ ath9k_regd_init_channels(struct ath_hal *ah, } switch (cm->mode) { - case ATH9K_MODE_SEL_11A: - case ATH9K_MODE_SEL_11NA_HT20: - case ATH9K_MODE_SEL_11NA_HT40PLUS: - case ATH9K_MODE_SEL_11NA_HT40MINUS: + case ATH9K_MODE_11A: + case ATH9K_MODE_11NA_HT20: + case ATH9K_MODE_11NA_HT40PLUS: + case ATH9K_MODE_11NA_HT40MINUS: rd = &rd5GHz; channelBM = rd->chan11a; freqs = ®Dmn5GhzFreq[0]; ctl = rd->conformanceTestLimit; break; - case ATH9K_MODE_SEL_11B: + case ATH9K_MODE_11B: rd = &rd2GHz; channelBM = rd->chan11b; freqs = ®Dmn2GhzFreq[0]; ctl = rd->conformanceTestLimit | CTL_11B; break; - case ATH9K_MODE_SEL_11G: - case ATH9K_MODE_SEL_11NG_HT20: - case ATH9K_MODE_SEL_11NG_HT40PLUS: - case ATH9K_MODE_SEL_11NG_HT40MINUS: + case ATH9K_MODE_11G: + case ATH9K_MODE_11NG_HT20: + case ATH9K_MODE_11NG_HT40PLUS: + case ATH9K_MODE_11NG_HT40MINUS: rd = &rd2GHz; channelBM = rd->chan11g; freqs = ®Dmn2Ghz11gFreq[0]; @@ -801,13 +796,13 @@ ath9k_regd_init_channels(struct ath_hal *ah, if (ath9k_regd_is_chan_bm_zero(channelBM)) continue; - if ((cm->mode == ATH9K_MODE_SEL_11NA_HT40PLUS) || - (cm->mode == ATH9K_MODE_SEL_11NG_HT40PLUS)) { + if ((cm->mode == ATH9K_MODE_11NA_HT40PLUS) || + (cm->mode == ATH9K_MODE_11NG_HT40PLUS)) { hi_adj = -20; } - if ((cm->mode == ATH9K_MODE_SEL_11NA_HT40MINUS) || - (cm->mode == ATH9K_MODE_SEL_11NG_HT40MINUS)) { + if ((cm->mode == ATH9K_MODE_11NA_HT40MINUS) || + (cm->mode == ATH9K_MODE_11NG_HT40MINUS)) { low_adj = 20; } diff --git a/drivers/net/wireless/ath9k/regd.h b/drivers/net/wireless/ath9k/regd.h index ae77496bfde3..0ecd344fbd98 100644 --- a/drivers/net/wireless/ath9k/regd.h +++ b/drivers/net/wireless/ath9k/regd.h @@ -117,11 +117,11 @@ #define isUNII1OddChan(ch) \ ((ch == 5170) || (ch == 5190) || (ch == 5210) || (ch == 5230)) -#define IS_HT40_MODE(_mode) \ - (((_mode == ATH9K_MODE_SEL_11NA_HT40PLUS || \ - _mode == ATH9K_MODE_SEL_11NG_HT40PLUS || \ - _mode == ATH9K_MODE_SEL_11NA_HT40MINUS || \ - _mode == ATH9K_MODE_SEL_11NG_HT40MINUS) ? true : false)) +#define IS_HT40_MODE(_mode) \ + (((_mode == ATH9K_MODE_11NA_HT40PLUS || \ + _mode == ATH9K_MODE_11NG_HT40PLUS || \ + _mode == ATH9K_MODE_11NA_HT40MINUS || \ + _mode == ATH9K_MODE_11NG_HT40MINUS) ? true : false)) #define CHAN_FLAGS (CHANNEL_ALL|CHANNEL_HALF|CHANNEL_QUARTER) diff --git a/drivers/net/wireless/ath9k/regd_common.h b/drivers/net/wireless/ath9k/regd_common.h index b3bfae4fc99c..9112c030b1e8 100644 --- a/drivers/net/wireless/ath9k/regd_common.h +++ b/drivers/net/wireless/ath9k/regd_common.h @@ -1893,15 +1893,15 @@ static struct regDomain regDomains[] = { }; static const struct cmode modes[] = { - {ATH9K_MODE_SEL_11A, CHANNEL_A}, - {ATH9K_MODE_SEL_11B, CHANNEL_B}, - {ATH9K_MODE_SEL_11G, CHANNEL_G}, - {ATH9K_MODE_SEL_11NG_HT20, CHANNEL_G_HT20}, - {ATH9K_MODE_SEL_11NG_HT40PLUS, CHANNEL_G_HT40PLUS}, - {ATH9K_MODE_SEL_11NG_HT40MINUS, CHANNEL_G_HT40MINUS}, - {ATH9K_MODE_SEL_11NA_HT20, CHANNEL_A_HT20}, - {ATH9K_MODE_SEL_11NA_HT40PLUS, CHANNEL_A_HT40PLUS}, - {ATH9K_MODE_SEL_11NA_HT40MINUS, CHANNEL_A_HT40MINUS}, + {ATH9K_MODE_11A, CHANNEL_A}, + {ATH9K_MODE_11B, CHANNEL_B}, + {ATH9K_MODE_11G, CHANNEL_G}, + {ATH9K_MODE_11NG_HT20, CHANNEL_G_HT20}, + {ATH9K_MODE_11NG_HT40PLUS, CHANNEL_G_HT40PLUS}, + {ATH9K_MODE_11NG_HT40MINUS, CHANNEL_G_HT40MINUS}, + {ATH9K_MODE_11NA_HT20, CHANNEL_A_HT20}, + {ATH9K_MODE_11NA_HT40PLUS, CHANNEL_A_HT40PLUS}, + {ATH9K_MODE_11NA_HT40MINUS, CHANNEL_A_HT40MINUS}, }; static struct japan_bandcheck j_bandcheck[] = { diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c index debd7f46d44a..157f830ee6b8 100644 --- a/drivers/net/wireless/ath9k/xmit.c +++ b/drivers/net/wireless/ath9k/xmit.c @@ -408,7 +408,7 @@ static int ath_tx_prepare(struct ath_softc *sc, if (txctl->min_rate) rcs[0].rix = ath_rate_findrateix(sc, txctl->min_rate); else - rcs[0].rix = sc->sc_minrateix; + rcs[0].rix = 0; rcs[0].tries = ATH_MGT_TXMAXTRY; } rix = rcs[0].rix; -- cgit v1.2.1 From 88b9e2bef3e38c053ec8f054f2cbb9345724cdb1 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 5 Aug 2008 22:06:51 +0300 Subject: ath9k: work around gcc ICEs (again) (I missed the fact that the original post said to apply this patch twice... -- JWL) Original commit log message: This patch works around an internal compiler error (gcc bug #37014) in all gcc 4.2 compilers and the gcc 4.3 series up to at least 4.3.1 on at least powerpc and mips. Many thanks to Andrew Pinski for analyzing the gcc bug. Signed-off-by: Adrian Bunk Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/hw.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index bde162f128ab..a17eb130f574 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -5017,7 +5017,11 @@ static void ath9k_hw_spur_mitigate(struct ath_hal *ah, for (i = 0; i < 123; i++) { if ((cur_vit_mask > lower) && (cur_vit_mask < upper)) { - if ((abs(cur_vit_mask - bin)) < 75) + + /* workaround for gcc bug #37014 */ + volatile int tmp = abs(cur_vit_mask - bin); + + if (tmp < 75) mask_amt = 1; else mask_amt = 0; -- cgit v1.2.1 From aaa1553512b9105699113ea7e2ea726f3d9d4de2 Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Sat, 9 Aug 2008 19:20:47 -0500 Subject: p54: Fix regression due to "net: Delete NETDEVICES_MULTIQUEUE kconfig option" Commit b19fa1f, entitled "net: Delete NETDEVICES_MULTIQUEUE kconfig option" breaks p54pci and p54usb. Additionally, the old logic always tx'ed cts frames (if enabled) with a short preamble when [rate > 3]. (i.e. with any 802.11g rate). Of course this isn't that bad, but it's still wrong! (This patch also clarifies the meanings of some of the fields in the tx header for the hardware. -- JWL) Signed-off-by: Christian Lamparter Acked-by: Larry Finger Cc: [2.6.25.x, 2.6.26.x] Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54common.c | 31 ++++++++++++++++--------------- drivers/net/wireless/p54/p54common.h | 18 +++++++++--------- 2 files changed, 25 insertions(+), 24 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 83cd85e1f847..49afd20d2c30 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -413,12 +413,12 @@ static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb) last_addr = range->end_addr; __skb_unlink(entry, &priv->tx_queue); memset(&info->status, 0, sizeof(info->status)); - priv->tx_stats[skb_get_queue_mapping(skb)].len--; entry_hdr = (struct p54_control_hdr *) entry->data; entry_data = (struct p54_tx_control_allocdata *) entry_hdr->data; if ((entry_hdr->magic1 & cpu_to_le16(0x4000)) != 0) pad = entry_data->align[0]; + priv->tx_stats[entry_data->hw_queue - 4].len--; if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { if (!(payload->status & 0x01)) info->flags |= IEEE80211_TX_STAT_ACK; @@ -557,6 +557,7 @@ static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb) struct p54_tx_control_allocdata *txhdr; size_t padding, len; u8 rate; + u8 cts_rate = 0x20; current_queue = &priv->tx_stats[skb_get_queue_mapping(skb)]; if (unlikely(current_queue->len > current_queue->limit)) @@ -581,28 +582,28 @@ static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb) hdr->type = (info->flags & IEEE80211_TX_CTL_NO_ACK) ? 0 : cpu_to_le16(1); hdr->retry1 = hdr->retry2 = info->control.retry_limit; - memset(txhdr->wep_key, 0x0, 16); - txhdr->padding = 0; - txhdr->padding2 = 0; - /* TODO: add support for alternate retry TX rates */ rate = ieee80211_get_tx_rate(dev, info)->hw_value; - if (info->flags & IEEE80211_TX_CTL_SHORT_PREAMBLE) + if (info->flags & IEEE80211_TX_CTL_SHORT_PREAMBLE) { rate |= 0x10; - if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) + cts_rate |= 0x10; + } + if (info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) { rate |= 0x40; - else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) + cts_rate |= ieee80211_get_rts_cts_rate(dev, info)->hw_value; + } else if (info->flags & IEEE80211_TX_CTL_USE_CTS_PROTECT) { rate |= 0x20; + cts_rate |= ieee80211_get_rts_cts_rate(dev, info)->hw_value; + } memset(txhdr->rateset, rate, 8); - txhdr->wep_key_present = 0; - txhdr->wep_key_len = 0; - txhdr->frame_type = cpu_to_le32(skb_get_queue_mapping(skb) + 4); - txhdr->magic4 = 0; - txhdr->antenna = (info->antenna_sel_tx == 0) ? + txhdr->key_type = 0; + txhdr->key_len = 0; + txhdr->hw_queue = skb_get_queue_mapping(skb) + 4; + txhdr->tx_antenna = (info->antenna_sel_tx == 0) ? 2 : info->antenna_sel_tx - 1; txhdr->output_power = 0x7f; // HW Maximum - txhdr->magic5 = (info->flags & IEEE80211_TX_CTL_NO_ACK) ? - 0 : ((rate > 0x3) ? cpu_to_le32(0x33) : cpu_to_le32(0x23)); + txhdr->cts_rate = (info->flags & IEEE80211_TX_CTL_NO_ACK) ? + 0 : cts_rate; if (padding) txhdr->align[0] = padding; diff --git a/drivers/net/wireless/p54/p54common.h b/drivers/net/wireless/p54/p54common.h index 2245fcce92dc..8db6c0e8e540 100644 --- a/drivers/net/wireless/p54/p54common.h +++ b/drivers/net/wireless/p54/p54common.h @@ -183,16 +183,16 @@ struct p54_frame_sent_hdr { struct p54_tx_control_allocdata { u8 rateset[8]; - u16 padding; - u8 wep_key_present; - u8 wep_key_len; - u8 wep_key[16]; - __le32 frame_type; - u32 padding2; - __le16 magic4; - u8 antenna; + u8 unalloc0[2]; + u8 key_type; + u8 key_len; + u8 key[16]; + u8 hw_queue; + u8 unalloc1[9]; + u8 tx_antenna; u8 output_power; - __le32 magic5; + u8 cts_rate; + u8 unalloc2[3]; u8 align[0]; } __attribute__ ((packed)); -- cgit v1.2.1 From 37465c8a3ec7279e3314623d1e2d2a50c10cf79f Mon Sep 17 00:00:00 2001 From: Michael Karcher Date: Thu, 7 Aug 2008 19:34:01 +0200 Subject: ath5k: Don't fiddle with MSI on suspend/resume. Commit 256b152b005e319f985f50f2a910a75ba0def74f (ath5k: don't enable MSI, we cannot handle it yet) has removed msi support, but overlooked the suspend/resume code. This patch completes msi removal. I don't consider this patch copyrightable, and thus put it into the public domain. The result is of course a base.c file dual-licensed under 3-clause-BSD and GPL. Signed-off-by: Michael Karcher Acked-by: Nick Kossifidis Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 2028866f5995..f19f33c4e336 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -587,7 +587,6 @@ ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state) ath5k_stop_hw(sc); free_irq(pdev->irq, sc); - pci_disable_msi(pdev); pci_save_state(pdev); pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); @@ -616,12 +615,10 @@ ath5k_pci_resume(struct pci_dev *pdev) */ pci_write_config_byte(pdev, 0x41, 0); - pci_enable_msi(pdev); - err = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc); if (err) { ATH5K_ERR(sc, "request_irq failed\n"); - goto err_msi; + goto err_no_irq; } err = ath5k_init(sc); @@ -642,8 +639,7 @@ ath5k_pci_resume(struct pci_dev *pdev) return 0; err_irq: free_irq(pdev->irq, sc); -err_msi: - pci_disable_msi(pdev); +err_no_irq: pci_disable_device(pdev); return err; } -- cgit v1.2.1 From a58d452290aafae30b021c8bf185853b1aaf2092 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Sun, 10 Aug 2008 10:19:33 -0500 Subject: b43: Fix for SPROM coding error in Linksys WMP54G (BCM4306/3) The Linksys WMP54G (BCM4306/3) card in a PCI format has an SPROM coding error and needs the fix found for several other cards. Signed-off-by: Larry Finger Cc: Stable [2.6.25.x, 2.6.26.x] Signed-off-by: Michael Buesch Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 3bf3a869361f..ee462b514104 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4616,6 +4616,7 @@ static void b43_sprom_fixup(struct ssb_bus *bus) pdev = bus->host_pci; if (IS_PDEV(pdev, BROADCOM, 0x4318, ASUSTEK, 0x100F) || IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0015) || + IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0014) || IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0013)) bus->sprom.boardflags_lo &= ~B43_BFL_BTCOEXIST; } -- cgit v1.2.1 From fcd7cc1496681f7241ef8b80f32bb35d72a4e78b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?matthieu=20Barth=C3=A9lemy?= Date: Sun, 10 Aug 2008 23:34:59 -0500 Subject: rtl8187: Add USB ID for Netgear WG111V3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the USB ID for a Netgear WG111v3. Signed-off-by: matthieu Barthélemy Signed-off-by: Larry Finger Signed-off-by: John W. Linville --- drivers/net/wireless/rtl8187_dev.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c index 57376fb993ed..ca5deb6244e6 100644 --- a/drivers/net/wireless/rtl8187_dev.c +++ b/drivers/net/wireless/rtl8187_dev.c @@ -40,6 +40,7 @@ static struct usb_device_id rtl8187_table[] __devinitdata = { /* Netgear */ {USB_DEVICE(0x0846, 0x6100), .driver_info = DEVICE_RTL8187}, {USB_DEVICE(0x0846, 0x6a00), .driver_info = DEVICE_RTL8187}, + {USB_DEVICE(0x0846, 0x4260), .driver_info = DEVICE_RTL8187B}, /* HP */ {USB_DEVICE(0x03f0, 0xca02), .driver_info = DEVICE_RTL8187}, /* Sitecom */ -- cgit v1.2.1 From 6f14792610905fe006c0c3c4d9e1478fb6be7cec Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 11 Aug 2008 23:49:41 +0200 Subject: iwlwifi: fix printk newlines Add newlines at printk outputs to not break dmesg. Signed-off-by: Jiri Slaby Cc: Zhu Yi Cc: Reinette Chatre Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-4965.c | 2 +- drivers/net/wireless/iwlwifi/iwl-eeprom.c | 6 +++--- drivers/net/wireless/iwlwifi/iwl-sta.c | 4 ++-- drivers/net/wireless/iwlwifi/iwl-tx.c | 4 ++-- drivers/net/wireless/iwlwifi/iwl3945-base.c | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 22bb26985c2e..4a0cc78d55e3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -967,7 +967,7 @@ static int iwl4965_interpolate_chan(struct iwl_priv *priv, u32 channel, s = iwl4965_get_sub_band(priv, channel); if (s >= EEPROM_TX_POWER_BANDS) { - IWL_ERROR("Tx Power can not find channel %d ", channel); + IWL_ERROR("Tx Power can not find channel %d\n", channel); return -1; } diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index bce53830b301..19985bf0dccf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -146,7 +146,7 @@ int iwlcore_eeprom_verify_signature(struct iwl_priv *priv) { u32 gp = iwl_read32(priv, CSR_EEPROM_GP); if ((gp & CSR_EEPROM_GP_VALID_MSK) == CSR_EEPROM_GP_BAD_SIGNATURE) { - IWL_ERROR("EEPROM not found, EEPROM_GP=0x%08x", gp); + IWL_ERROR("EEPROM not found, EEPROM_GP=0x%08x\n", gp); return -ENOENT; } return 0; @@ -227,7 +227,7 @@ int iwl_eeprom_init(struct iwl_priv *priv) ret = priv->cfg->ops->lib->eeprom_ops.verify_signature(priv); if (ret < 0) { - IWL_ERROR("EEPROM not found, EEPROM_GP=0x%08x", gp); + IWL_ERROR("EEPROM not found, EEPROM_GP=0x%08x\n", gp); ret = -ENOENT; goto err; } @@ -254,7 +254,7 @@ int iwl_eeprom_init(struct iwl_priv *priv) } if (!(r & CSR_EEPROM_REG_READ_VALID_MSK)) { - IWL_ERROR("Time out reading EEPROM[%d]", addr); + IWL_ERROR("Time out reading EEPROM[%d]\n", addr); ret = -ETIMEDOUT; goto done; } diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index 60a6e0106036..6283a3a707f5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -207,7 +207,7 @@ static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, case WLAN_HT_CAP_MIMO_PS_DISABLED: break; default: - IWL_WARNING("Invalid MIMO PS mode %d", mimo_ps_mode); + IWL_WARNING("Invalid MIMO PS mode %d\n", mimo_ps_mode); break; } @@ -969,7 +969,7 @@ int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr) return priv->hw_params.bcast_sta_id; default: - IWL_WARNING("Unknown mode of operation: %d", priv->iw_mode); + IWL_WARNING("Unknown mode of operation: %d\n", priv->iw_mode); return priv->hw_params.bcast_sta_id; } } diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 4108c7c8f00f..d82823b5c8ab 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -493,7 +493,7 @@ int iwl_txq_ctx_reset(struct iwl_priv *priv) /* Alloc keep-warm buffer */ ret = iwl_kw_alloc(priv); if (ret) { - IWL_ERROR("Keep Warm allocation failed"); + IWL_ERROR("Keep Warm allocation failed\n"); goto error_kw; } spin_lock_irqsave(&priv->lock, flags); @@ -1463,7 +1463,7 @@ void iwl_rx_reply_compressed_ba(struct iwl_priv *priv, u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); if (scd_flow >= priv->hw_params.max_txq_num) { - IWL_ERROR("BUG_ON scd_flow is bigger than number of queues"); + IWL_ERROR("BUG_ON scd_flow is bigger than number of queues\n"); return; } diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 444847ab1b5a..348048a4b6cf 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -1558,7 +1558,7 @@ int iwl3945_eeprom_init(struct iwl3945_priv *priv) BUILD_BUG_ON(sizeof(priv->eeprom) != IWL_EEPROM_IMAGE_SIZE); if ((gp & CSR_EEPROM_GP_VALID_MSK) == CSR_EEPROM_GP_BAD_SIGNATURE) { - IWL_ERROR("EEPROM not found, EEPROM_GP=0x%08x", gp); + IWL_ERROR("EEPROM not found, EEPROM_GP=0x%08x\n", gp); return -ENOENT; } @@ -1583,7 +1583,7 @@ int iwl3945_eeprom_init(struct iwl3945_priv *priv) } if (!(r & CSR_EEPROM_REG_READ_VALID_MSK)) { - IWL_ERROR("Time out reading EEPROM[%d]", addr); + IWL_ERROR("Time out reading EEPROM[%d]\n", addr); return -ETIMEDOUT; } e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16)); @@ -2507,7 +2507,7 @@ static int iwl3945_get_sta_id(struct iwl3945_priv *priv, struct ieee80211_hdr *h return priv->hw_setting.bcast_sta_id; default: - IWL_WARNING("Unknown mode of operation: %d", priv->iw_mode); + IWL_WARNING("Unknown mode of operation: %d\n", priv->iw_mode); return priv->hw_setting.bcast_sta_id; } } -- cgit v1.2.1 From 69bbc7dc9f59fedb6067c7f9f9f9bc1da27407ad Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Wed, 13 Aug 2008 23:41:45 +0200 Subject: p54: move p54_vdcf_init to the right place. priv->tx_hdr_len is set by the driver _after_ it called p54_init_common. While this isn't much a problem for any PCI or ISL3887 cards/sticks, because they don't need any extra header and therefore tx_hdr_len is zero for them... Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54common.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c index 49afd20d2c30..29be3dc8ee09 100644 --- a/drivers/net/wireless/p54/p54common.c +++ b/drivers/net/wireless/p54/p54common.c @@ -837,10 +837,21 @@ static int p54_start(struct ieee80211_hw *dev) struct p54_common *priv = dev->priv; int err; + if (!priv->cached_vdcf) { + priv->cached_vdcf = kzalloc(sizeof(struct p54_tx_control_vdcf)+ + priv->tx_hdr_len + sizeof(struct p54_control_hdr), + GFP_KERNEL); + + if (!priv->cached_vdcf) + return -ENOMEM; + } + err = priv->open(dev); if (!err) priv->mode = IEEE80211_IF_TYPE_MNTR; + p54_init_vdcf(dev); + return err; } @@ -1020,15 +1031,6 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) dev->extra_tx_headroom = sizeof(struct p54_control_hdr) + 4 + sizeof(struct p54_tx_control_allocdata); - priv->cached_vdcf = kzalloc(sizeof(struct p54_tx_control_vdcf) + - priv->tx_hdr_len + sizeof(struct p54_control_hdr), GFP_KERNEL); - - if (!priv->cached_vdcf) { - ieee80211_free_hw(dev); - return NULL; - } - - p54_init_vdcf(dev); mutex_init(&priv->conf_mutex); return dev; -- cgit v1.2.1 From d47c3cebf5aae9d72b2cc18e5ac3b520fdbb0bca Mon Sep 17 00:00:00 2001 From: Christian Lamparter Date: Wed, 13 Aug 2008 23:41:48 +0200 Subject: p54u: reset skb's data/tail pointer on requeue (Only important for USB V1 Adaptors) If an incoming frame wasn't accepted by p54_rx function the skb will be reused for new frames... But, we must not forget to set the skb's data pointers into the same state in which it was initialized by p54u_init_urbs. Otherwise we either end up with 16 bytes less on every requeue, or if a new frame is worthy enough to be accepted, the data is in the wrong place (urb->transfer_buffer wasn't updated!) and mac80211 has a hard time to recognize it... Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54usb.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 815c095ef797..cbaca23a9453 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -109,7 +109,17 @@ static void p54u_rx_cb(struct urb *urb) urb->context = skb; skb_queue_tail(&priv->rx_queue, skb); } else { + if (!priv->hw_type) + skb_push(skb, sizeof(struct net2280_tx_hdr)); + + skb_reset_tail_pointer(skb); skb_trim(skb, 0); + if (urb->transfer_buffer != skb_tail_pointer(skb)) { + /* this should not happen */ + WARN_ON(1); + urb->transfer_buffer = skb_tail_pointer(skb); + } + skb_queue_tail(&priv->rx_queue, skb); } -- cgit v1.2.1 From 430cd47fa98178b5199105fab4fb74e04bd0635c Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Thu, 14 Aug 2008 18:57:11 -0500 Subject: b43: Fix for another Bluetooth Coexistence SPROM Programming error for BCM4306 In trying to help users on the Ubuntu Bugzilla, I discovered another BCM4306 with the Bluetooth Coexistence programming error in the SPROM. This patch is contingent on the one that added the Linksys device with subdevice code of 0x0014. Signed-off-by: Larry Finger Cc: Stable Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index ee462b514104..8e09982fdeda 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4615,6 +4615,7 @@ static void b43_sprom_fixup(struct ssb_bus *bus) if (bus->bustype == SSB_BUSTYPE_PCI) { pdev = bus->host_pci; if (IS_PDEV(pdev, BROADCOM, 0x4318, ASUSTEK, 0x100F) || + IS_PDEV(pdev, BROADCOM, 0x4320, DELL, 0x0003) || IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0015) || IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0014) || IS_PDEV(pdev, BROADCOM, 0x4320, LINKSYS, 0x0013)) -- cgit v1.2.1 From f126cba427170eac9e37311b2ab7afc0e2261906 Mon Sep 17 00:00:00 2001 From: Jochen Friedrich Date: Fri, 15 Aug 2008 14:47:46 +0200 Subject: rt2x00: Fix txdone_entry_desc_flags txdone_entry_desc_flags is used with __set_bit and test_bit which bit-shift the values, so don't bit-shift the flags in the enum. Also make sure flags are initialized before being used. Signed-off-by: Jochen Friedrich Signed-off-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00queue.h | 8 ++++---- drivers/net/wireless/rt2x00/rt2x00usb.c | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h index a4a8c57004db..ff78e52ce43c 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/rt2x00/rt2x00queue.h @@ -173,10 +173,10 @@ struct rxdone_entry_desc { * frame transmission failed due to excessive retries. */ enum txdone_entry_desc_flags { - TXDONE_UNKNOWN = 1 << 0, - TXDONE_SUCCESS = 1 << 1, - TXDONE_FAILURE = 1 << 2, - TXDONE_EXCESSIVE_RETRY = 1 << 3, + TXDONE_UNKNOWN, + TXDONE_SUCCESS, + TXDONE_FAILURE, + TXDONE_EXCESSIVE_RETRY, }; /** diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 8d76bb2e0312..2050227ea530 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -181,6 +181,7 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) * (Only indirectly by looking at the failed TX counters * in the register). */ + txdesc.flags = 0; if (!urb->status) __set_bit(TXDONE_UNKNOWN, &txdesc.flags); else -- cgit v1.2.1 From 3eb75aac8907e7ea36f0d078b2cc7393986001cf Mon Sep 17 00:00:00 2001 From: Huang Weiyi Date: Sun, 17 Aug 2008 07:50:47 +0800 Subject: removed unused #include The drivers below do not use LINUX_VERSION_CODE nor KERNEL_VERSION. drivers/net/wireless/ath5k/base.c drivers/net/wireless/b43/main.c drivers/net/wireless/ipw2100.c drivers/net/wireless/ipw2200.c drivers/net/wireless/iwlwifi/iwl-3945.c drivers/net/wireless/iwlwifi/iwl-4965.c drivers/net/wireless/iwlwifi/iwl-5000.c drivers/net/wireless/iwlwifi/iwl-agn.c drivers/net/wireless/iwlwifi/iwl-core.c drivers/net/wireless/iwlwifi/iwl-eeprom.c drivers/net/wireless/iwlwifi/iwl-hcmd.c drivers/net/wireless/iwlwifi/iwl-power.c drivers/net/wireless/iwlwifi/iwl3945-base.c This patch removes the said #include . Signed-off-by: Huang Weiyi Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 1 - drivers/net/wireless/b43/main.c | 1 - drivers/net/wireless/ipw2100.c | 1 - drivers/net/wireless/ipw2200.c | 1 - drivers/net/wireless/iwlwifi/iwl-3945.c | 1 - drivers/net/wireless/iwlwifi/iwl-4965.c | 1 - drivers/net/wireless/iwlwifi/iwl-5000.c | 1 - drivers/net/wireless/iwlwifi/iwl-agn.c | 1 - drivers/net/wireless/iwlwifi/iwl-core.c | 1 - drivers/net/wireless/iwlwifi/iwl-eeprom.c | 1 - drivers/net/wireless/iwlwifi/iwl-hcmd.c | 1 - drivers/net/wireless/iwlwifi/iwl-power.c | 1 - drivers/net/wireless/iwlwifi/iwl3945-base.c | 1 - 13 files changed, 13 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index f19f33c4e336..b20a45aa8680 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -40,7 +40,6 @@ * */ -#include #include #include #include diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 8e09982fdeda..7205a936ec74 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c index c6f886ec08a3..19a401c4a0dc 100644 --- a/drivers/net/wireless/ipw2100.c +++ b/drivers/net/wireless/ipw2100.c @@ -157,7 +157,6 @@ that only one external action is invoked at a time. #include #include #include -#include #include #include #include diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c index 36e8d2f6e7b4..dcce3542d5a7 100644 --- a/drivers/net/wireless/ipw2200.c +++ b/drivers/net/wireless/ipw2200.c @@ -31,7 +31,6 @@ ******************************************************************************/ #include "ipw2200.h" -#include #ifndef KBUILD_EXTMOD diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index b3931f6135a4..3f51f3635344 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 4a0cc78d55e3..e2581229d8b2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index f3d139b663e6..cbc01a00eaf4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -25,7 +25,6 @@ #include #include -#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index ed09e48b1b61..061ffba9c884 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -29,7 +29,6 @@ #include #include -#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 9bd61809129f..c72f72579bea 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -28,7 +28,6 @@ #include #include -#include #include struct iwl_priv; /* FIXME: remove */ diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index 19985bf0dccf..37155755efc5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -63,7 +63,6 @@ #include #include -#include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c index 6512834bb916..2eb03eea1908 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c +++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c @@ -28,7 +28,6 @@ #include #include -#include #include #include "iwl-dev.h" /* FIXME: remove */ diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c index 028e3053c0ca..a099c9e30e55 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.c +++ b/drivers/net/wireless/iwlwifi/iwl-power.c @@ -29,7 +29,6 @@ #include #include -#include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 348048a4b6cf..b775d5bab668 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -29,7 +29,6 @@ #include #include -#include #include #include #include -- cgit v1.2.1 From 7a8fc9b248e77a4eab0613acf30a6811799786b3 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Sun, 17 Aug 2008 17:36:59 +0300 Subject: removed unused #include 's This patch lets the files using linux/version.h match the files that #include it. Signed-off-by: Adrian Bunk Signed-off-by: Linus Torvalds --- drivers/net/wireless/b43legacy/main.c | 1 - drivers/net/wireless/iwlwifi/iwl-3945-led.c | 1 - drivers/net/wireless/iwlwifi/iwl-led.c | 1 - drivers/net/wireless/iwlwifi/iwl-rfkill.c | 1 - 4 files changed, 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 2541c81932f0..1cb77db5c292 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.c b/drivers/net/wireless/iwlwifi/iwl-3945-led.c index d3336966b6b5..705c65bed9fd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.c @@ -27,7 +27,6 @@ #include #include -#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c index cb11c4a4d691..4eee1b163cd2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-led.c @@ -27,7 +27,6 @@ #include #include -#include #include #include #include diff --git a/drivers/net/wireless/iwlwifi/iwl-rfkill.c b/drivers/net/wireless/iwlwifi/iwl-rfkill.c index e5e5846e9f25..5d642298f04c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rfkill.c +++ b/drivers/net/wireless/iwlwifi/iwl-rfkill.c @@ -27,7 +27,6 @@ *****************************************************************************/ #include #include -#include #include #include -- cgit v1.2.1 From 3c34a5d821b825b720189e87561ba18500cf8026 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 18 Aug 2008 15:40:02 -0400 Subject: atmel: return ENOENT on request_firmware failure Return errors from request_firmware() (like other drivers that do firmware load on device open) and make up plausible codes for other error conditions. Gives userspace tools like NetworkManager a clue that firmware may be missing when the result of setting IFF_UP is ENOENT. Signed-off-by: Dan Williams v2: fix reversed check of atmel_wakeup_firmware() in probe_atmel_card() Signed-off-by: John W. Linville --- drivers/net/wireless/atmel.c | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index bd35bb0a1480..f23bcd07dee8 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -1304,7 +1304,7 @@ EXPORT_SYMBOL(atmel_open); int atmel_open(struct net_device *dev) { struct atmel_private *priv = netdev_priv(dev); - int i, channel; + int i, channel, err; /* any scheduled timer is no longer needed and might screw things up.. */ del_timer_sync(&priv->management_timer); @@ -1328,8 +1328,9 @@ int atmel_open(struct net_device *dev) priv->site_survey_state = SITE_SURVEY_IDLE; priv->station_is_associated = 0; - if (!reset_atmel_card(dev)) - return -EAGAIN; + err = reset_atmel_card(dev); + if (err) + return err; if (priv->config_reg_domain) { priv->reg_domain = priv->config_reg_domain; @@ -3580,12 +3581,12 @@ static int atmel_wakeup_firmware(struct atmel_private *priv) if (i == 0) { printk(KERN_ALERT "%s: MAC failed to boot.\n", priv->dev->name); - return 0; + return -EIO; } if ((priv->host_info_base = atmel_read16(priv->dev, MR2)) == 0xffff) { printk(KERN_ALERT "%s: card missing.\n", priv->dev->name); - return 0; + return -ENODEV; } /* now check for completion of MAC initialization through @@ -3609,19 +3610,19 @@ static int atmel_wakeup_firmware(struct atmel_private *priv) if (i == 0) { printk(KERN_ALERT "%s: MAC failed to initialise.\n", priv->dev->name); - return 0; + return -EIO; } /* Check for MAC_INIT_OK only on the register that the MAC_INIT_OK was set */ if ((mr3 & MAC_INIT_COMPLETE) && !(atmel_read16(priv->dev, MR3) & MAC_INIT_OK)) { printk(KERN_ALERT "%s: MAC failed MR3 self-test.\n", priv->dev->name); - return 0; + return -EIO; } if ((mr1 & MAC_INIT_COMPLETE) && !(atmel_read16(priv->dev, MR1) & MAC_INIT_OK)) { printk(KERN_ALERT "%s: MAC failed MR1 self-test.\n", priv->dev->name); - return 0; + return -EIO; } atmel_copy_to_host(priv->dev, (unsigned char *)iface, @@ -3642,7 +3643,7 @@ static int atmel_wakeup_firmware(struct atmel_private *priv) iface->func_ctrl = le16_to_cpu(iface->func_ctrl); iface->mac_status = le16_to_cpu(iface->mac_status); - return 1; + return 0; } /* determine type of memory and MAC address */ @@ -3693,7 +3694,7 @@ static int probe_atmel_card(struct net_device *dev) /* Standard firmware in flash, boot it up and ask for the Mac Address */ priv->card_type = CARD_TYPE_SPI_FLASH; - if (atmel_wakeup_firmware(priv)) { + if (atmel_wakeup_firmware(priv) == 0) { atmel_get_mib(priv, Mac_Address_Mib_Type, 0, dev->dev_addr, 6); /* got address, now squash it again until the network @@ -3835,6 +3836,7 @@ static int reset_atmel_card(struct net_device *dev) struct atmel_private *priv = netdev_priv(dev); u8 configuration; int old_state = priv->station_state; + int err = 0; /* data to add to the firmware names, in priority order this implemenents firmware versioning */ @@ -3868,11 +3870,12 @@ static int reset_atmel_card(struct net_device *dev) dev->name); strcpy(priv->firmware_id, "atmel_at76c502.bin"); } - if (request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev) != 0) { + err = request_firmware(&fw_entry, priv->firmware_id, priv->sys_dev); + if (err != 0) { printk(KERN_ALERT "%s: firmware %s is missing, cannot continue.\n", dev->name, priv->firmware_id); - return 0; + return err; } } else { int fw_index = 0; @@ -3901,7 +3904,7 @@ static int reset_atmel_card(struct net_device *dev) "%s: firmware %s is missing, cannot start.\n", dev->name, priv->firmware_id); priv->firmware_id[0] = '\0'; - return 0; + return -ENOENT; } } @@ -3926,8 +3929,9 @@ static int reset_atmel_card(struct net_device *dev) release_firmware(fw_entry); } - if (!atmel_wakeup_firmware(priv)) - return 0; + err = atmel_wakeup_firmware(priv); + if (err != 0) + return err; /* Check the version and set the correct flag for wpa stuff, old and new firmware is incompatible. @@ -3968,10 +3972,9 @@ static int reset_atmel_card(struct net_device *dev) if (!priv->radio_on_broken) { if (atmel_send_command_wait(priv, CMD_EnableRadio, NULL, 0) == CMD_STATUS_REJECTED_RADIO_OFF) { - printk(KERN_INFO - "%s: cannot turn the radio on. (Hey radio, you're beautiful!)\n", + printk(KERN_INFO "%s: cannot turn the radio on.\n", dev->name); - return 0; + return -EIO; } } @@ -4006,7 +4009,7 @@ static int reset_atmel_card(struct net_device *dev) wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL); } - return 1; + return 0; } static void atmel_send_command(struct atmel_private *priv, int command, -- cgit v1.2.1 From d0c2912fe8df81a8b723fb6ec4d4cdf523cbaff7 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 18 Aug 2008 15:32:41 -0400 Subject: atmel: try open system authentication too When the AP rejects a Shared Key authentication request, try Open System auth too. Signed-off-by: Dan Williams Signed-off-by: John W. Linville --- drivers/net/wireless/atmel.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index f23bcd07dee8..bd65c485098c 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -3062,12 +3062,20 @@ static void authenticate(struct atmel_private *priv, u16 frame_len) } if (status == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) { - /* Do opensystem first, then try sharedkey */ + /* Flip back and forth between WEP auth modes until the max + * authentication tries has been exceeded. + */ if (system == WLAN_AUTH_OPEN) { priv->CurrentAuthentTransactionSeqNum = 0x001; priv->exclude_unencrypted = 1; send_authentication_request(priv, WLAN_AUTH_SHARED_KEY, NULL, 0); return; + } else if ( system == WLAN_AUTH_SHARED_KEY + && priv->wep_is_on) { + priv->CurrentAuthentTransactionSeqNum = 0x001; + priv->exclude_unencrypted = 0; + send_authentication_request(priv, WLAN_AUTH_OPEN, NULL, 0); + return; } else if (priv->connect_to_any_BSS) { int bss_index; -- cgit v1.2.1 From 004829730cb1b03abe7555e1c1faadec62cbcf6f Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 18 Aug 2008 21:45:27 +0200 Subject: Ath5k: lock beacons Beacons setup and config was racy with beacon send. Ensure that ISR and reset functions see consistent state of bbuf. Use also dev_kfree_skb_any in ath5k_txbuf_free since we call it from atomic now. Signed-off-by: Jiri Slaby Cc: Nick Kossifidis Cc: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 15 ++++++++++++--- drivers/net/wireless/ath5k/base.h | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index b20a45aa8680..7bc012fca059 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -251,7 +251,7 @@ static inline void ath5k_txbuf_free(struct ath5k_softc *sc, return; pci_unmap_single(sc->pdev, bf->skbaddr, bf->skb->len, PCI_DMA_TODEVICE); - dev_kfree_skb(bf->skb); + dev_kfree_skb_any(bf->skb); bf->skb = NULL; } @@ -466,6 +466,7 @@ ath5k_pci_probe(struct pci_dev *pdev, mutex_init(&sc->lock); spin_lock_init(&sc->rxbuflock); spin_lock_init(&sc->txbuflock); + spin_lock_init(&sc->block); /* Set private data */ pci_set_drvdata(pdev, hw); @@ -2179,8 +2180,11 @@ ath5k_beacon_config(struct ath5k_softc *sc) sc->imask |= AR5K_INT_SWBA; - if (ath5k_hw_hasveol(ah)) + if (ath5k_hw_hasveol(ah)) { + spin_lock(&sc->block); ath5k_beacon_send(sc); + spin_unlock(&sc->block); + } } /* TODO else AP */ @@ -2403,7 +2407,9 @@ ath5k_intr(int irq, void *dev_id) TSF_TO_TU(tsf), (unsigned long long) tsf); } else { + spin_lock(&sc->block); ath5k_beacon_send(sc); + spin_unlock(&sc->block); } } if (status & AR5K_INT_RXEOL) { @@ -3050,6 +3056,7 @@ static int ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ath5k_softc *sc = hw->priv; + unsigned long flags; int ret; ath5k_debug_dump_skb(sc, skb, "BC ", 1); @@ -3059,12 +3066,14 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb) goto end; } + spin_lock_irqsave(&sc->block, flags); ath5k_txbuf_free(sc, sc->bbuf); sc->bbuf->skb = skb; ret = ath5k_beacon_setup(sc, sc->bbuf); if (ret) sc->bbuf->skb = NULL; - else { + spin_unlock_irqrestore(&sc->block, flags); + if (!ret) { ath5k_beacon_config(sc); mmiowb(); } diff --git a/drivers/net/wireless/ath5k/base.h b/drivers/net/wireless/ath5k/base.h index d7e03e6b8271..7ec2f377d5c7 100644 --- a/drivers/net/wireless/ath5k/base.h +++ b/drivers/net/wireless/ath5k/base.h @@ -172,6 +172,7 @@ struct ath5k_softc { struct tasklet_struct txtq; /* tx intr tasklet */ struct ath5k_led tx_led; /* tx led */ + spinlock_t block; /* protects beacon */ struct ath5k_buf *bbuf; /* beacon buffer */ unsigned int bhalq, /* SW q for outgoing beacons */ bmisscount, /* missed beacon transmits */ -- cgit v1.2.1 From 67d2e2dfab31b4c0497ce8a84d63efc931f10bb7 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 18 Aug 2008 21:45:28 +0200 Subject: Ath5k: fix bintval setup bintval is set to the initial value at .config_interface which is too late, since it overwrites previously set value from .config. Move the initialization to the .add_interface. Signed-off-by: Jiri Slaby Cc: Nick Kossifidis Cc: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath5k/base.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 7bc012fca059..0676c6d84383 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c @@ -2751,6 +2751,11 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, ret = -EOPNOTSUPP; goto end; } + + /* Set to a reasonable value. Note that this will + * be set to mac80211's value at ath5k_config(). */ + sc->bintval = 1000; + ret = 0; end: mutex_unlock(&sc->lock); @@ -2795,9 +2800,6 @@ ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ath5k_hw *ah = sc->ah; int ret; - /* Set to a reasonable value. Note that this will - * be set to mac80211's value at ath5k_config(). */ - sc->bintval = 1000; mutex_lock(&sc->lock); if (sc->vif != vif) { ret = -EIO; -- cgit v1.2.1 From 49898852e6aa8a6de9a5bc0bab2cf305b3583bbf Mon Sep 17 00:00:00 2001 From: "John W. Linville" Date: Tue, 2 Sep 2008 15:07:18 -0400 Subject: iwlwifi: do not use GFP_DMA in iwl_tx_queue_init GFP_DMA is not necessary for the iwlwifi hardware and it can cause allocation failures and/or invoke the OOM killer on lots of systems. For reference: https://bugzilla.redhat.com/show_bug.cgi?id=459709 Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index d82823b5c8ab..ff879d46624a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -426,7 +426,7 @@ static int iwl_tx_queue_init(struct iwl_priv *priv, continue; } - txq->cmd[i] = kmalloc(len, GFP_KERNEL | GFP_DMA); + txq->cmd[i] = kmalloc(len, GFP_KERNEL); if (!txq->cmd[i]) return -ENOMEM; } -- cgit v1.2.1 From cf88c433bf64b6f7395a39f840bec88a8c58b58b Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 28 Aug 2008 17:25:04 +0800 Subject: iwlwifi: workaround interrupt handling no some platforms This patch adds workaround for an interrupt related hardware bug on some platforms. (Apparently these platforms boot-up w/ INTX_DISABLED set. -- JWL) Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 061ffba9c884..c0b73c4d6f44 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2602,6 +2602,7 @@ static int iwl4965_mac_start(struct ieee80211_hw *hw) { struct iwl_priv *priv = hw->priv; int ret; + u16 pci_cmd; IWL_DEBUG_MAC80211("enter\n"); @@ -2612,6 +2613,13 @@ static int iwl4965_mac_start(struct ieee80211_hw *hw) pci_restore_state(priv->pci_dev); pci_enable_msi(priv->pci_dev); + /* enable interrupts if needed: hw bug w/a */ + pci_read_config_word(priv->pci_dev, PCI_COMMAND, &pci_cmd); + if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { + pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(priv->pci_dev, PCI_COMMAND, pci_cmd); + } + ret = request_irq(priv->pci_dev->irq, iwl4965_isr, IRQF_SHARED, DRV_NAME, priv); if (ret) { -- cgit v1.2.1 From 1d3e6c61342292140dfe1b921991ee793ec1e0ae Mon Sep 17 00:00:00 2001 From: Mohamed Abbas Date: Thu, 28 Aug 2008 17:25:05 +0800 Subject: iwlwifi: fix apm_stop (wrong bit polarity for FLAG_INIT_DONE) The patch fixes CSR_GP_CNTRL_REG_FLAG_INIT_DONE was set instead of cleared which disabled moving device to D0U state. Signed-off-by: Mohamed Abbas Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-4965.c | 4 ++-- drivers/net/wireless/iwlwifi/iwl-5000.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index e2581229d8b2..23fed3298962 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -474,8 +474,8 @@ static void iwl4965_apm_stop(struct iwl_priv *priv) iwl_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); udelay(10); - - iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + /* clear "init complete" move adapter D0A* --> D0U state */ + iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); spin_unlock_irqrestore(&priv->lock, flags); } diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index cbc01a00eaf4..d95fb42b2bdf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -145,7 +145,8 @@ static void iwl5000_apm_stop(struct iwl_priv *priv) udelay(10); - iwl_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + /* clear "init complete" move adapter D0A* --> D0U state */ + iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); spin_unlock_irqrestore(&priv->lock, flags); } -- cgit v1.2.1 From f0b9f5cb4adcec9424142592ca7bf024fe6c91a9 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 28 Aug 2008 17:25:10 +0800 Subject: iwlwifi: fix 64bit platform firmware loading This patch fixes loading firmware from memory above 32bit. Signed-off-by: Tomas Winkler Signed-off-by: Emmanuel Grumbach Signed-off-by: Zhu Yi Acked-by: Marcel Holtmann Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-5000.c | 11 ++++------- drivers/net/wireless/iwlwifi/iwl-fh.h | 1 + 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index d95fb42b2bdf..b08036a9d894 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -578,14 +578,11 @@ static int iwl5000_load_section(struct iwl_priv *priv, FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); - /* FIME: write the MSB of the phy_addr in CTRL1 - * iwl_write_direct32(priv, - IWL_FH_TFDIB_CTRL1_REG(IWL_FH_SRVC_CHNL), - ((phy_addr & MSB_MSK) - << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_count); - */ iwl_write_direct32(priv, - FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), byte_cnt); + FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), + (iwl_get_dma_hi_address(phy_addr) + << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); + iwl_write_direct32(priv, FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL), 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h index 944642450d3d..cd11c0ca2991 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/iwlwifi/iwl-fh.h @@ -287,6 +287,7 @@ #define FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) +#define FH_MEM_TFDIB_REG1_ADDR_BITSHIFT 28 /** * Transmit DMA Channel Control/Status Registers (TCSR) -- cgit v1.2.1 From 667d41008eb8a0e1d495d6c0a6143b43fe587c98 Mon Sep 17 00:00:00 2001 From: David Kilroy Date: Sat, 23 Aug 2008 19:03:34 +0100 Subject: orinoco: Multicast to the specified addresses When multicasting the driver sets the number of group addresses using the count from the previous set multicast command. In general this means you have to set the multicast addresses twice to get the behaviour you want. If we were multicasting, and reduce the number of addresses we are multicasting to, then the driver would write uninitialised data from the stack into the group addresses to multicast to. Only write the multicast addresses we have specifically set. Signed-off-by: David Kilroy Signed-off-by: John W. Linville --- drivers/net/wireless/orinoco.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 1ebcafe7ca5f..36c004e15602 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -1970,6 +1970,9 @@ __orinoco_set_multicast_list(struct net_device *dev) priv->promiscuous = promisc; } + /* If we're not in promiscuous mode, then we need to set the + * group address if either we want to multicast, or if we were + * multicasting and want to stop */ if (! promisc && (mc_count || priv->mc_count) ) { struct dev_mc_list *p = dev->mc_list; struct hermes_multicast mclist; @@ -1989,9 +1992,10 @@ __orinoco_set_multicast_list(struct net_device *dev) printk(KERN_WARNING "%s: Multicast list is " "longer than mc_count\n", dev->name); - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES, - HERMES_BYTES_TO_RECLEN(priv->mc_count * ETH_ALEN), - &mclist); + err = hermes_write_ltv(hw, USER_BAP, + HERMES_RID_CNFGROUPADDRESSES, + HERMES_BYTES_TO_RECLEN(mc_count * ETH_ALEN), + &mclist); if (err) printk(KERN_ERR "%s: Error %d setting multicast list.\n", dev->name, err); -- cgit v1.2.1 From 9a52028e534b0567913a4144060e774891c00a37 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Thu, 28 Aug 2008 01:05:08 +0300 Subject: wireless/libertas/if_cs.c: fix memory leaks The leak in if_cs_prog_helper() is obvious. It looks a bit as if not freeing "fw" in if_cs_prog_real() was done intentionally, but I'm not seeing why it shouldn't be freed. Reported-by: Adrian Bunk Signed-off-by: Adrian Bunk Acked-by: Holger Schurig Signed-off-by: John W. Linville --- drivers/net/wireless/libertas/if_cs.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index 04d7a251e3f0..8941919001bb 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -595,7 +595,7 @@ static int if_cs_prog_helper(struct if_cs_card *card) if (ret < 0) { lbs_pr_err("can't download helper at 0x%x, ret %d\n", sent, ret); - goto done; + goto err_release; } if (count == 0) @@ -604,9 +604,8 @@ static int if_cs_prog_helper(struct if_cs_card *card) sent += count; } +err_release: release_firmware(fw); - ret = 0; - done: lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret); return ret; @@ -676,14 +675,8 @@ static int if_cs_prog_real(struct if_cs_card *card) } ret = if_cs_poll_while_fw_download(card, IF_CS_SCRATCH, 0x5a); - if (ret < 0) { + if (ret < 0) lbs_pr_err("firmware download failed\n"); - goto err_release; - } - - ret = 0; - goto done; - err_release: release_firmware(fw); -- cgit v1.2.1 From 445df54fec7c1924f44018c4db2a9613b877f10e Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Mon, 1 Sep 2008 14:47:19 +0300 Subject: rt2x00: Compiler warning unmasked by fix of BUILD_BUG_ON A "Set" to a sign-bit in an "&" operation causes a compiler warning. Make calculations unsigned. [ The warning was masked by the old definition of BUILD_BUG_ON() ] Also remove __builtin_constant_p from FIELD_CHECK since BUILD_BUG_ON no longer permits non-const values. Signed-off-by: Boaz Harrosh CC: Ingo Molnar CC: Rusty Russell Acked-by: Ivo van Doorn Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00reg.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/rt2x00/rt2x00reg.h b/drivers/net/wireless/rt2x00/rt2x00reg.h index 7e88ce5651b9..2ea7866abd5d 100644 --- a/drivers/net/wireless/rt2x00/rt2x00reg.h +++ b/drivers/net/wireless/rt2x00/rt2x00reg.h @@ -136,7 +136,7 @@ struct rt2x00_field32 { */ #define is_power_of_two(x) ( !((x) & ((x)-1)) ) #define low_bit_mask(x) ( ((x)-1) & ~(x) ) -#define is_valid_mask(x) is_power_of_two(1 + (x) + low_bit_mask(x)) +#define is_valid_mask(x) is_power_of_two(1LU + (x) + low_bit_mask(x)) /* * Macro's to find first set bit in a variable. @@ -173,8 +173,7 @@ struct rt2x00_field32 { * does not exceed the given typelimit. */ #define FIELD_CHECK(__mask, __type) \ - BUILD_BUG_ON(!__builtin_constant_p(__mask) || \ - !(__mask) || \ + BUILD_BUG_ON(!(__mask) || \ !is_valid_mask(__mask) || \ (__mask) != (__type)(__mask)) \ -- cgit v1.2.1 From 1b96175b7e5801a908718d8b5270a4f7d94fed28 Mon Sep 17 00:00:00 2001 From: Senthil Balasubramanian Date: Mon, 1 Sep 2008 19:45:21 +0530 Subject: ath9k: Incorrect key used when group and pairwise ciphers are different. Updating sc_keytype multiple times when groupwise and pairwise ciphers are different results in incorrect pairwise key type assumed for TX control and normal ping fails. This works fine for cases where both groupwise and pairwise ciphers are same. Also use mac80211 provided enums for key length calculation. Signed-off-by: Senthil Balasubramanian Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/hw.c | 8 ++++---- drivers/net/wireless/ath9k/main.c | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index a17eb130f574..6dbfed0b4149 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -7285,15 +7285,15 @@ ath9k_hw_set_keycache_entry(struct ath_hal *ah, u16 entry, } break; case ATH9K_CIPHER_WEP: - if (k->kv_len < 40 / NBBY) { + if (k->kv_len < LEN_WEP40) { DPRINTF(ah->ah_sc, ATH_DBG_KEYCACHE, "%s: WEP key length %u too small\n", __func__, k->kv_len); return false; } - if (k->kv_len <= 40 / NBBY) + if (k->kv_len <= LEN_WEP40) keyType = AR_KEYTABLE_TYPE_40; - else if (k->kv_len <= 104 / NBBY) + else if (k->kv_len <= LEN_WEP104) keyType = AR_KEYTABLE_TYPE_104; else keyType = AR_KEYTABLE_TYPE_128; @@ -7313,7 +7313,7 @@ ath9k_hw_set_keycache_entry(struct ath_hal *ah, u16 entry, key2 = get_unaligned_le32(k->kv_val + 6) ^ xorMask; key3 = (get_unaligned_le16(k->kv_val + 10) ^ xorMask) & 0xffff; key4 = get_unaligned_le32(k->kv_val + 12) ^ xorMask; - if (k->kv_len <= 104 / NBBY) + if (k->kv_len <= LEN_WEP104) key4 &= 0xff; if (keyType == AR_KEYTABLE_TYPE_TKIP && ATH9K_IS_MIC_ENABLED(ah)) { diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 2888778040e4..95b337149484 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -206,7 +206,8 @@ static int ath_key_config(struct ath_softc *sc, if (!ret) return -EIO; - sc->sc_keytype = hk.kv_type; + if (mac) + sc->sc_keytype = hk.kv_type; return 0; } @@ -756,7 +757,8 @@ static int ath9k_set_key(struct ieee80211_hw *hw, key->hw_key_idx = key->keyidx; /* push IV and Michael MIC generation to stack */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + if (key->alg == ALG_TKIP) + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; } break; case DISABLE_KEY: -- cgit v1.2.1 From 773b4e02be28220e9ead80a5fdb180031361439a Mon Sep 17 00:00:00 2001 From: Senthil Balasubramanian Date: Mon, 1 Sep 2008 19:58:20 +0530 Subject: ath9: Fix ath_rx_flush_tid() for IRQs disabled kernel warning message. This patch addresses an issue with the locking order. ath_rx_flush_tid() uses spin_lock/unlock_bh when IRQs are disabled in sta_notify by mac80211. As node clean up is still pending with ath9k and this problematic portion of the code is expected to change anyway, thinking of a proper fix may not be worthwhile. So having this interim fix helps the users to get rid of the kernel warning message. Pasted the kernel warning message for reference. kernel: ath0: No ProbeResp from current AP 00:1b:11:60:7a:3d - assume out of range kernel: ------------[ cut here ]------------ kernel: WARNING: at kernel/softirq.c:136 local_bh_enable+0x3c/0xab() kernel: Pid: 1029, comm: ath9k Not tainted 2.6.27-rc4-wt-w1fi-wl kernel: kernel: Call Trace: kernel: [] warn_on_slowpath+0x51/0x77 kernel: [] check_preempt_wakeup+0xf3/0x123 kernel: [] autoremove_wake_function+0x9/0x2e kernel: [] local_bh_enable+0x3c/0xab kernel: [] ath_rx_node_cleanup+0x38/0x6e [ath9k] kernel: [] ath_node_detach+0x3b/0xb6 [ath9k] kernel: [] ath9k_sta_notify+0x12b/0x165 [ath9k] kernel: [] queue_work+0x1d/0x49 kernel: [] add_todo+0x70/0x99 [mac80211] kernel: [] __sta_info_unlink+0x16b/0x19e [mac80211] kernel: [] sta_info_unlink+0x18/0x43 [mac80211] kernel: [] ieee80211_associated+0xaa/0x16d [mac80211] kernel: [] ieee80211_sta_work+0x4fb/0x6b4 [mac80211] kernel: [] thread_return+0x30/0xa9 kernel: [] ieee80211_sta_work+0x0/0x6b4 [mac80211] kernel: [] run_workqueue+0xb1/0x17a kernel: [] worker_thread+0xd0/0xdb kernel: [] autoremove_wake_function+0x0/0x2e kernel: [] worker_thread+0x0/0xdb kernel: [] kthread+0x47/0x75 kernel: [] schedule_tail+0x18/0x50 kernel: [] child_rip+0xa/0x11 kernel: [] kthread+0x0/0x75 kernel: [] child_rip+0x0/0x11 kernel: kernel: ---[ end trace e9bb5da661055827 ]--- Signed-off-by: Senthil Balasubramanian Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/recv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath9k/recv.c b/drivers/net/wireless/ath9k/recv.c index 2fe806175c01..20ddb7acdb94 100644 --- a/drivers/net/wireless/ath9k/recv.c +++ b/drivers/net/wireless/ath9k/recv.c @@ -360,8 +360,9 @@ static void ath_rx_flush_tid(struct ath_softc *sc, struct ath_arx_tid *rxtid, int drop) { struct ath_rxbuf *rxbuf; + unsigned long flag; - spin_lock_bh(&rxtid->tidlock); + spin_lock_irqsave(&rxtid->tidlock, flag); while (rxtid->baw_head != rxtid->baw_tail) { rxbuf = rxtid->rxbuf + rxtid->baw_head; if (!rxbuf->rx_wbuf) { @@ -382,7 +383,7 @@ static void ath_rx_flush_tid(struct ath_softc *sc, INCR(rxtid->baw_head, ATH_TID_MAX_BUFS); INCR(rxtid->seq_next, IEEE80211_SEQ_MAX); } - spin_unlock_bh(&rxtid->tidlock); + spin_unlock_irqrestore(&rxtid->tidlock, flag); } static struct sk_buff *ath_rxbuf_alloc(struct ath_softc *sc, -- cgit v1.2.1 From b94d8eea44df985ea22b1f51e402b4bbfdd6d793 Mon Sep 17 00:00:00 2001 From: Assaf Krauss Date: Wed, 3 Sep 2008 11:18:42 +0800 Subject: iwlwifi: W/A for the TSF correction in IBSS This patch is a W/A for the TSF sync issue in IBSS merging. HW is not capable to sync TSF (it's constantly little behind). This creates constant IBSS merging upon reception of each beacon, adding and removing station which in turn creates above 50% packet loss and thus dramatically degrade the throughput. The W/A simply stops the driver from declaring it has a reliable TSF value and thus eliminates IBSS merging. Signed-off-by: Assaf Krauss Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 2 +- drivers/net/wireless/iwlwifi/iwl-rx.c | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index c0b73c4d6f44..2afa1fb052ee 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -3588,7 +3588,7 @@ static int iwl4965_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *sk priv->assoc_id = 0; timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; - priv->timestamp = le64_to_cpu(timestamp) + (priv->beacon_int * 1000); + priv->timestamp = le64_to_cpu(timestamp); IWL_DEBUG_MAC80211("leave\n"); spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index f3f6ea49fdd2..e81bfc42a7cb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -1173,7 +1173,10 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, rx_status.antenna = 0; rx_status.flag = 0; - rx_status.flag |= RX_FLAG_TSFT; + + /* TSF isn't reliable. In order to allow smooth user experience, + * this W/A doesn't propagate it to the mac80211 */ + /*rx_status.flag |= RX_FLAG_TSFT;*/ if ((unlikely(rx_start->cfg_phy_cnt > 20))) { IWL_DEBUG_DROP("dsp size out of range [0,20]: %d/n", -- cgit v1.2.1 From 0ffe014a8c548f88694815ae7b25d6e86550917e Mon Sep 17 00:00:00 2001 From: Ron Rindjunsky Date: Wed, 3 Sep 2008 11:18:43 +0800 Subject: iwlwifi: fix hidden ssid discovery in passive channels This enables sending of direct probes on passive channels, as long as traffic was detected on that channel. This enables connectivity to hidden/non broadcasting SSIDs APs on passive channels. Note 5000 HW declares all 5.2 spectrum as passive. Signed-off-by: Cahill Ben Signed-off-by: Tomas Winkler Signed-off-by: Ron Rindjunsky Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index 9bb6adb28b73..6c8ac3a87d54 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -421,7 +421,7 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv, else scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; - if ((scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) && n_probes) + if (n_probes) scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes); scan_ch->active_dwell = cpu_to_le16(active_dwell); -- cgit v1.2.1 From 545597036caefd4b438731abaca902efe004b1b4 Mon Sep 17 00:00:00 2001 From: Mohamed Abbas Date: Wed, 3 Sep 2008 11:18:44 +0800 Subject: iwlwifi: remove false rxon if rx chain changes Rx chain might change during power save transitions but it doesn't require sending Full-ROXN command to the firmware. Full-RXON requires reconnection to an AP and thus affects user experience. The patch avoids the Full-RXON by removing the rx_chain modification check in iwl_full_rxon_required function. Signed-off-by: Mohamed Abbas Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 2afa1fb052ee..a5feb0cc982c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -181,14 +181,14 @@ static int iwl4965_check_rxon_cmd(struct iwl_rxon_cmd *rxon) } /** - * iwl4965_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed + * iwl_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed * @priv: staging_rxon is compared to active_rxon * * If the RXON structure is changing enough to require a new tune, * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. */ -static int iwl4965_full_rxon_required(struct iwl_priv *priv) +static int iwl_full_rxon_required(struct iwl_priv *priv) { /* These items are only settable from the full RXON command */ @@ -207,7 +207,6 @@ static int iwl4965_full_rxon_required(struct iwl_priv *priv) priv->active_rxon.ofdm_ht_single_stream_basic_rates) || (priv->staging_rxon.ofdm_ht_dual_stream_basic_rates != priv->active_rxon.ofdm_ht_dual_stream_basic_rates) || - (priv->staging_rxon.rx_chain != priv->active_rxon.rx_chain) || (priv->staging_rxon.assoc_id != priv->active_rxon.assoc_id)) return 1; @@ -263,7 +262,7 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv) /* If we don't need to send a full RXON, we can use * iwl4965_rxon_assoc_cmd which is used to reconfigure filter * and other flags for the current radio configuration. */ - if (!iwl4965_full_rxon_required(priv)) { + if (!iwl_full_rxon_required(priv)) { ret = iwl_send_rxon_assoc(priv); if (ret) { IWL_ERROR("Error setting RXON_ASSOC (%d)\n", ret); -- cgit v1.2.1 From 4834c73f055b92f719a23e29e275559fa8c29513 Mon Sep 17 00:00:00 2001 From: Ron Rindjunsky Date: Wed, 3 Sep 2008 11:18:46 +0800 Subject: iwlwifi: fix station mimo power save values This patch fixes the wrong use MIMO power save values. Our TX was configured with our MIMO power save values instead of peer's MIMO power save values, this may affect connectivity. The peer STA/AP may not sense our traffic at all as it doesn't have all RX chains opened. Signed-off-by: Ron Rindjunsky Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn-rs.c | 3 ++- drivers/net/wireless/iwlwifi/iwl-agn.c | 2 -- drivers/net/wireless/iwlwifi/iwl-dev.h | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index 754fef5b592f..90a2b6dee7c0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -1153,7 +1153,8 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv, !sta->ht_info.ht_supported) return -1; - if (priv->current_ht_config.tx_mimo_ps_mode == IWL_MIMO_PS_STATIC) + if (((sta->ht_info.cap & IEEE80211_HT_CAP_MIMO_PS) >> 2) + == IWL_MIMO_PS_STATIC) return -1; /* Need both Tx chains/antennas to support MIMO */ diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index a5feb0cc982c..34928c8ed202 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -586,8 +586,6 @@ static void iwl4965_ht_conf(struct iwl_priv *priv, iwl_conf->supported_chan_width = 0; } - iwl_conf->tx_mimo_ps_mode = - (u8)((ht_conf->cap & IEEE80211_HT_CAP_MIMO_PS) >> 2); memcpy(iwl_conf->supp_mcs_set, ht_conf->supp_mcs_set, 16); iwl_conf->control_channel = ht_bss_conf->primary_channel; diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index c19db438306c..cdfb343c7ec6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -412,7 +412,6 @@ struct iwl_ht_info { /* self configuration data */ u8 is_ht; u8 supported_chan_width; - u16 tx_mimo_ps_mode; u8 is_green_field; u8 sgf; /* HT_SHORT_GI_* short guard interval */ u8 max_amsdu_size; -- cgit v1.2.1 From 28a6b07a14bd5e63af2c0304306f35c5b963910b Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 3 Sep 2008 11:18:47 +0800 Subject: iwlwifi: fix rx_chain computation This patch fixes rx_chain computation. The code that adjusts number of rx chains to number supported by HW was missing. Miss configuration causes firmware error. Note: iwlwifi supports HW with up to 3 RX chains (2x2, 2x3, 1x2, and 3x3 MIMO). This patch also simplifies the whole RX chain computation. Signed-off-by: Tomas Winkler Signed-off-by: Mohamed Abbas Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-core.c | 77 ++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 31 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index c72f72579bea..80f2f84defa8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -592,12 +592,11 @@ static void iwlcore_free_geos(struct iwl_priv *priv) clear_bit(STATUS_GEO_CONFIGURED, &priv->status); } -static u8 is_single_rx_stream(struct iwl_priv *priv) +static bool is_single_rx_stream(struct iwl_priv *priv) { return !priv->current_ht_config.is_ht || ((priv->current_ht_config.supp_mcs_set[1] == 0) && - (priv->current_ht_config.supp_mcs_set[2] == 0)) || - priv->ps_mode == IWL_MIMO_PS_STATIC; + (priv->current_ht_config.supp_mcs_set[2] == 0)); } static u8 iwl_is_channel_extension(struct iwl_priv *priv, @@ -704,33 +703,39 @@ EXPORT_SYMBOL(iwl_set_rxon_ht); * MIMO (dual stream) requires at least 2, but works better with 3. * This does not determine *which* chains to use, just how many. */ -static int iwlcore_get_rx_chain_counter(struct iwl_priv *priv, - u8 *idle_state, u8 *rx_state) +static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) { - u8 is_single = is_single_rx_stream(priv); - u8 is_cam = test_bit(STATUS_POWER_PMI, &priv->status) ? 0 : 1; + bool is_single = is_single_rx_stream(priv); + bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); /* # of Rx chains to use when expecting MIMO. */ if (is_single || (!is_cam && (priv->ps_mode == IWL_MIMO_PS_STATIC))) - *rx_state = 2; + return 2; else - *rx_state = 3; + return 3; +} +static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) +{ + int idle_cnt; + bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); /* # Rx chains when idling and maybe trying to save power */ switch (priv->ps_mode) { case IWL_MIMO_PS_STATIC: case IWL_MIMO_PS_DYNAMIC: - *idle_state = (is_cam) ? 2 : 1; + idle_cnt = (is_cam) ? 2 : 1; break; case IWL_MIMO_PS_NONE: - *idle_state = (is_cam) ? *rx_state : 1; + idle_cnt = (is_cam) ? active_cnt : 1; break; + case IWL_MIMO_PS_INVALID: default: - *idle_state = 1; + IWL_ERROR("invalide mimo ps mode %d\n", priv->ps_mode); + WARN_ON(1); + idle_cnt = -1; break; } - - return 0; + return idle_cnt; } /** @@ -741,34 +746,44 @@ static int iwlcore_get_rx_chain_counter(struct iwl_priv *priv, */ void iwl_set_rxon_chain(struct iwl_priv *priv) { - u8 is_single = is_single_rx_stream(priv); - u8 idle_state, rx_state; - - priv->staging_rxon.rx_chain = 0; - rx_state = idle_state = 3; + bool is_single = is_single_rx_stream(priv); + bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); + u8 idle_rx_cnt, active_rx_cnt; + u16 rx_chain; /* Tell uCode which antennas are actually connected. * Before first association, we assume all antennas are connected. * Just after first association, iwl_chain_noise_calibration() * checks which antennas actually *are* connected. */ - priv->staging_rxon.rx_chain |= - cpu_to_le16(priv->hw_params.valid_rx_ant << - RXON_RX_CHAIN_VALID_POS); + rx_chain = priv->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS; /* How many receivers should we use? */ - iwlcore_get_rx_chain_counter(priv, &idle_state, &rx_state); - priv->staging_rxon.rx_chain |= - cpu_to_le16(rx_state << RXON_RX_CHAIN_MIMO_CNT_POS); - priv->staging_rxon.rx_chain |= - cpu_to_le16(idle_state << RXON_RX_CHAIN_CNT_POS); - - if (!is_single && (rx_state >= 2) && - !test_bit(STATUS_POWER_PMI, &priv->status)) + active_rx_cnt = iwl_get_active_rx_chain_count(priv); + idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt); + + /* correct rx chain count accoridng hw settings */ + if (priv->hw_params.rx_chains_num < active_rx_cnt) + active_rx_cnt = priv->hw_params.rx_chains_num; + + if (priv->hw_params.rx_chains_num < idle_rx_cnt) + idle_rx_cnt = priv->hw_params.rx_chains_num; + + rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; + rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; + + priv->staging_rxon.rx_chain = cpu_to_le16(rx_chain); + + if (!is_single && (active_rx_cnt >= 2) && is_cam) priv->staging_rxon.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; else priv->staging_rxon.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; - IWL_DEBUG_ASSOC("rx chain %X\n", priv->staging_rxon.rx_chain); + IWL_DEBUG_ASSOC("rx_chain=0x%Xi active=%d idle=%d\n", + priv->staging_rxon.rx_chain, + active_rx_cnt, idle_rx_cnt); + + WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || + active_rx_cnt < idle_rx_cnt); } EXPORT_SYMBOL(iwl_set_rxon_chain); -- cgit v1.2.1 From 73b7d742519ab4cc1b7a12c7af15187fc0ede8e7 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Wed, 3 Sep 2008 11:18:48 +0800 Subject: iwlwifi: fix Tx cmd memory allocation failure handling This patch "iwlwifi: do not use GFP_DMA in iwl_tx_queue_init" removes GFP_DMA from allocation tx command buffers. GFP_DMA allows allocation only for memory under 16M which causes allocation problems suspend/resume flows. Using kmalloc is temporal solution and some consistent/coherent allocation schema will be more correct. Since iwlwifi hardware supports 64bit address this solution should work on x86 (32 and 64bit) for now. This patch fixes memory freeing problem in the previous patch. Signed-off-by: Tomas Winkler Signed-off-by: Ian Schram Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-tx.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index ff879d46624a..78b1a7a4ca40 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -402,12 +402,11 @@ static int iwl_hw_tx_queue_init(struct iwl_priv *priv, /** * iwl_tx_queue_init - Allocate and initialize one tx/cmd queue */ -static int iwl_tx_queue_init(struct iwl_priv *priv, - struct iwl_tx_queue *txq, +static int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, int slots_num, u32 txq_id) { int i, len; - int rc = 0; + int ret; /* * Alloc buffer array for commands (Tx or other types of commands). @@ -428,17 +427,14 @@ static int iwl_tx_queue_init(struct iwl_priv *priv, txq->cmd[i] = kmalloc(len, GFP_KERNEL); if (!txq->cmd[i]) - return -ENOMEM; + goto err; } /* Alloc driver data array and TFD circular buffer */ - rc = iwl_tx_queue_alloc(priv, txq, txq_id); - if (rc) { - for (i = 0; i < slots_num; i++) - kfree(txq->cmd[i]); + ret = iwl_tx_queue_alloc(priv, txq, txq_id); + if (ret) + goto err; - return -ENOMEM; - } txq->need_update = 0; /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise @@ -452,6 +448,17 @@ static int iwl_tx_queue_init(struct iwl_priv *priv, iwl_hw_tx_queue_init(priv, txq); return 0; +err: + for (i = 0; i < slots_num; i++) { + kfree(txq->cmd[i]); + txq->cmd[i] = NULL; + } + + if (txq_id == IWL_CMD_QUEUE_NUM) { + kfree(txq->cmd[slots_num]); + txq->cmd[slots_num] = NULL; + } + return -ENOMEM; } /** * iwl_hw_txq_ctx_free - Free TXQ Context -- cgit v1.2.1 From d535311ecbba0d692b5f1278b8bcb2581c3482e0 Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Wed, 3 Sep 2008 11:18:49 +0800 Subject: iwlwifi: call apm stop on exit This patch calls apm stop on exit and suspend. Without this patch hardware consumes power even after driver is removed or suspended. Signed-off-by: Gregory Greenman Signed-off-by: Mohamed Abbas Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 34928c8ed202..4e3d6c7fd6de 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2187,7 +2187,10 @@ static void __iwl4965_down(struct iwl_priv *priv) udelay(5); /* FIXME: apm_ops.suspend(priv) */ - priv->cfg->ops->lib->apm_ops.reset(priv); + if (exit_pending || test_bit(STATUS_IN_SUSPEND, &priv->status)) + priv->cfg->ops->lib->apm_ops.stop(priv); + else + priv->cfg->ops->lib->apm_ops.reset(priv); priv->cfg->ops->lib->free_shared_mem(priv); exit: -- cgit v1.2.1 From 0b124c31838bcf0459708aa91ce859582c7d3ca1 Mon Sep 17 00:00:00 2001 From: Gregory Greenman Date: Wed, 3 Sep 2008 11:18:50 +0800 Subject: iwlwifi: fix STATUS_EXIT_PENDING is not set on pci_remove This patch sets STATUS_EXIT_PENDING on pci_remove. Otherwise iwl4965_down may fail to uninitialize the driver. Signed-off-by: Gregory Greenman Signed-off-by: Mohamed Abbas Signed-off-by: Tomas Winkler Signed-off-by: Zhu Yi Signed-off-by: John W. Linville --- drivers/net/wireless/iwlwifi/iwl-agn.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 4e3d6c7fd6de..e01f048a02dd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -4372,15 +4372,18 @@ static void __devexit iwl4965_pci_remove(struct pci_dev *pdev) iwl_dbgfs_unregister(priv); sysfs_remove_group(&pdev->dev.kobj, &iwl4965_attribute_group); + /* ieee80211_unregister_hw call wil cause iwl4965_mac_stop to + * to be called and iwl4965_down since we are removing the device + * we need to set STATUS_EXIT_PENDING bit. + */ + set_bit(STATUS_EXIT_PENDING, &priv->status); if (priv->mac80211_registered) { ieee80211_unregister_hw(priv->hw); priv->mac80211_registered = 0; + } else { + iwl4965_down(priv); } - set_bit(STATUS_EXIT_PENDING, &priv->status); - - iwl4965_down(priv); - /* make sure we flush any pending irq or * tasklet for the driver */ -- cgit v1.2.1 From 43f30ae0a6308fd5b862ee7851feca1fc18c72d0 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 11 Aug 2008 14:01:49 +0300 Subject: ath9k: Fix TX status reporting Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/main.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 95b337149484..c5107f269f24 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -1067,8 +1067,16 @@ void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; tx_status->flags &= ~ATH_TX_BAR; } - if (tx_status->flags) - tx_info->status.excessive_retries = 1; + + if (tx_status->flags & (ATH_TX_ERROR | ATH_TX_XRETRY)) { + if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) { + /* Frame was not ACKed, but an ACK was expected */ + tx_info->status.excessive_retries = 1; + } + } else { + /* Frame was ACKed */ + tx_info->flags |= IEEE80211_TX_STAT_ACK; + } tx_info->status.retry_count = tx_status->retries; -- cgit v1.2.1 From 9aab3e3ee6f256a1eaabc275e0748132966f963d Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 11 Aug 2008 14:01:51 +0300 Subject: ath9k: Fix TX control flag use for no ACK and RTS/CTS Signed-off-by: Jouni Malinen Signed-off-by: John W. Linville --- drivers/net/wireless/ath9k/xmit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless') diff --git a/drivers/net/wireless/ath9k/xmit.c b/drivers/net/wireless/ath9k/xmit.c index 157f830ee6b8..550129f717e2 100644 --- a/drivers/net/wireless/ath9k/xmit.c +++ b/drivers/net/wireless/ath9k/xmit.c @@ -357,9 +357,9 @@ static int ath_tx_prepare(struct ath_softc *sc, txctl->flags = ATH9K_TXDESC_CLRDMASK; /* needed for crypto errors */ if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) - tx_info->flags |= ATH9K_TXDESC_NOACK; + txctl->flags |= ATH9K_TXDESC_NOACK; if (tx_info->flags & IEEE80211_TX_CTL_USE_RTS_CTS) - tx_info->flags |= ATH9K_TXDESC_RTSENA; + txctl->flags |= ATH9K_TXDESC_RTSENA; /* * Setup for rate calculations. -- cgit v1.2.1