diff options
Diffstat (limited to 'drivers/net/wireless')
309 files changed, 43459 insertions, 20902 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 5bc00db21b24..dda7cc2e1f57 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -5,7 +5,7 @@ menu "Wireless LAN" depends on !S390 -config WLAN_PRE80211 +menuconfig WLAN_PRE80211 bool "Wireless LAN (pre-802.11)" depends on NETDEVICES ---help--- @@ -101,7 +101,7 @@ config PCMCIA_NETWAVE called netwave_cs. If unsure, say N. -config WLAN_80211 +menuconfig WLAN_80211 bool "Wireless LAN (IEEE 802.11)" depends on NETDEVICES ---help--- @@ -428,10 +428,12 @@ config RTL8187 Micronet SP907GK V5 Encore ENUWI-G2 Trendnet TEW-424UB - ASUS P5B Deluxe + ASUS P5B Deluxe/P5K Premium motherboards Toshiba Satellite Pro series of laptops Asus Wireless Link - Linksys WUSB54GC-EU + Linksys WUSB54GC-EU v2 + (v1 = rt73usb; v3 is rt2070-based, + use staging/rt3070 or try rt2800usb) Thanks to Realtek for their support! diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index 2b9e379994a1..b80f514877d8 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -452,7 +452,8 @@ static void adm8211_interrupt_rci(struct ieee80211_hw *dev) rx_status.freq = adm8211_channels[priv->channel - 1].center_freq; rx_status.band = IEEE80211_BAND_2GHZ; - ieee80211_rx_irqsafe(dev, skb, &rx_status); + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(dev, skb); } entry = (++priv->cur_rx) % priv->rx_ring_size; @@ -1327,16 +1328,39 @@ static void adm8211_bss_info_changed(struct ieee80211_hw *dev, } } +static u64 adm8211_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + unsigned int bit_nr, i; + u32 mc_filter[2]; + + mc_filter[1] = mc_filter[0] = 0; + + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; + + bit_nr &= 0x3F; + mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); + mclist = mclist->next; + } + + return mc_filter[0] | ((u64)(mc_filter[1]) << 32); +} + static void adm8211_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { static const u8 bcast[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; struct adm8211_priv *priv = dev->priv; - unsigned int bit_nr, new_flags; + unsigned int new_flags; u32 mc_filter[2]; - int i; + + mc_filter[0] = multicast; + mc_filter[1] = multicast >> 32; new_flags = 0; @@ -1345,23 +1369,13 @@ static void adm8211_configure_filter(struct ieee80211_hw *dev, priv->nar |= ADM8211_NAR_PR; priv->nar &= ~ADM8211_NAR_MM; mc_filter[1] = mc_filter[0] = ~0; - } else if ((*total_flags & FIF_ALLMULTI) || (mc_count > 32)) { + } else if (*total_flags & FIF_ALLMULTI || multicast == ~(0ULL)) { new_flags |= FIF_ALLMULTI; priv->nar &= ~ADM8211_NAR_PR; priv->nar |= ADM8211_NAR_MM; mc_filter[1] = mc_filter[0] = ~0; } else { priv->nar &= ~(ADM8211_NAR_MM | ADM8211_NAR_PR); - mc_filter[1] = mc_filter[0] = 0; - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - bit_nr = ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26; - - bit_nr &= 0x3F; - mc_filter[bit_nr >> 5] |= 1 << (bit_nr & 31); - mclist = mclist->next; - } } ADM8211_IDLE_RX(); @@ -1756,6 +1770,7 @@ static const struct ieee80211_ops adm8211_ops = { .remove_interface = adm8211_remove_interface, .config = adm8211_config, .bss_info_changed = adm8211_bss_info_changed, + .prepare_multicast = adm8211_prepare_multicast, .configure_filter = adm8211_configure_filter, .get_stats = adm8211_get_stats, .get_tx_stats = adm8211_get_tx_stats, @@ -1963,14 +1978,6 @@ static void __devexit adm8211_remove(struct pci_dev *pdev) #ifdef CONFIG_PM static int adm8211_suspend(struct pci_dev *pdev, pm_message_t state) { - struct ieee80211_hw *dev = pci_get_drvdata(pdev); - struct adm8211_priv *priv = dev->priv; - - if (priv->mode != NL80211_IFTYPE_UNSPECIFIED) { - ieee80211_stop_queues(dev); - adm8211_stop(dev); - } - pci_save_state(pdev); pci_set_power_state(pdev, pci_choose_state(pdev, state)); return 0; @@ -1978,17 +1985,8 @@ static int adm8211_suspend(struct pci_dev *pdev, pm_message_t state) static int adm8211_resume(struct pci_dev *pdev) { - struct ieee80211_hw *dev = pci_get_drvdata(pdev); - struct adm8211_priv *priv = dev->priv; - pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); - - if (priv->mode != NL80211_IFTYPE_UNSPECIFIED) { - adm8211_start(dev); - ieee80211_wake_queues(dev); - } - return 0; } #endif /* CONFIG_PM */ diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 8ce5e4cee168..7116a1aa20ce 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -1920,14 +1920,16 @@ static int airo_open(struct net_device *dev) { return 0; } -static int mpi_start_xmit(struct sk_buff *skb, struct net_device *dev) { +static netdev_tx_t mpi_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ int npacks, pending; unsigned long flags; struct airo_info *ai = dev->ml_priv; if (!skb) { airo_print_err(dev->name, "%s: skb == NULL!",__func__); - return 0; + return NETDEV_TX_OK; } npacks = skb_queue_len (&ai->txq); @@ -1938,7 +1940,7 @@ static int mpi_start_xmit(struct sk_buff *skb, struct net_device *dev) { return NETDEV_TX_BUSY; } skb_queue_tail (&ai->txq, skb); - return 0; + return NETDEV_TX_OK; } spin_lock_irqsave(&ai->aux_lock, flags); @@ -1951,7 +1953,7 @@ static int mpi_start_xmit(struct sk_buff *skb, struct net_device *dev) { set_bit(FLAG_PENDING_XMIT, &ai->flags); mpi_send_packet (dev); } - return 0; + return NETDEV_TX_OK; } /* @@ -2119,7 +2121,9 @@ static void airo_end_xmit(struct net_device *dev) { dev_kfree_skb(skb); } -static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) { +static netdev_tx_t airo_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ s16 len; int i, j; struct airo_info *priv = dev->ml_priv; @@ -2127,7 +2131,7 @@ static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) { if ( skb == NULL ) { airo_print_err(dev->name, "%s: skb == NULL!", __func__); - return 0; + return NETDEV_TX_OK; } /* Find a vacant FID */ @@ -2155,7 +2159,7 @@ static int airo_start_xmit(struct sk_buff *skb, struct net_device *dev) { wake_up_interruptible(&priv->thr_wait); } else airo_end_xmit(dev); - return 0; + return NETDEV_TX_OK; } static void airo_end_xmit11(struct net_device *dev) { @@ -2184,7 +2188,9 @@ static void airo_end_xmit11(struct net_device *dev) { dev_kfree_skb(skb); } -static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) { +static netdev_tx_t airo_start_xmit11(struct sk_buff *skb, + struct net_device *dev) +{ s16 len; int i, j; struct airo_info *priv = dev->ml_priv; @@ -2199,7 +2205,7 @@ static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) { if ( skb == NULL ) { airo_print_err(dev->name, "%s: skb == NULL!", __func__); - return 0; + return NETDEV_TX_OK; } /* Find a vacant FID */ @@ -2227,7 +2233,7 @@ static int airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) { wake_up_interruptible(&priv->thr_wait); } else airo_end_xmit11(dev); - return 0; + return NETDEV_TX_OK; } static void airo_read_stats(struct net_device *dev) diff --git a/drivers/net/wireless/arlan-main.c b/drivers/net/wireless/arlan-main.c index d84caf198a23..921a082487a1 100644 --- a/drivers/net/wireless/arlan-main.c +++ b/drivers/net/wireless/arlan-main.c @@ -77,7 +77,7 @@ struct arlan_conf_stru arlan_conf[MAX_ARLANS]; static int arlans_found; static int arlan_open(struct net_device *dev); -static int arlan_tx(struct sk_buff *skb, struct net_device *dev); +static netdev_tx_t arlan_tx(struct sk_buff *skb, struct net_device *dev); static irqreturn_t arlan_interrupt(int irq, void *dev_id); static int arlan_close(struct net_device *dev); static struct net_device_stats * @@ -1022,7 +1022,7 @@ static int arlan_mac_addr(struct net_device *dev, void *p) ARLAN_DEBUG_ENTRY("arlan_mac_addr"); return -EINVAL; - if (!netif_running(dev)) + if (netif_running(dev)) return -EBUSY; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); @@ -1169,7 +1169,7 @@ static void arlan_tx_timeout (struct net_device *dev) } -static int arlan_tx(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t arlan_tx(struct sk_buff *skb, struct net_device *dev) { short length; unsigned char *buf; @@ -1193,7 +1193,7 @@ static int arlan_tx(struct sk_buff *skb, struct net_device *dev) arlan_process_interrupt(dev); ARLAN_DEBUG_EXIT("arlan_tx"); - return 0; + return NETDEV_TX_OK; bad_end: arlan_process_interrupt(dev); diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 4efbdbe6d6bf..8e1a55dec351 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1568,7 +1568,8 @@ static void at76_rx_tasklet(unsigned long param) at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d", priv->rx_skb->len, priv->rx_skb->data_len); - ieee80211_rx_irqsafe(priv->hw, priv->rx_skb, &rx_status); + memcpy(IEEE80211_SKB_RXCB(priv->rx_skb), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(priv->hw, priv->rx_skb); /* Use a new skb for the next receive */ priv->rx_skb = NULL; @@ -1772,6 +1773,9 @@ static void at76_mac80211_stop(struct ieee80211_hw *hw) at76_dbg(DBG_MAC80211, "%s()", __func__); + cancel_delayed_work(&priv->dwork_hw_scan); + cancel_work_sync(&priv->work_set_promisc); + mutex_lock(&priv->mtx); if (!priv->device_unplugged) { @@ -1871,8 +1875,8 @@ static void at76_dwork_hw_scan(struct work_struct *work) /* FIXME: add maximum time for scan to complete */ if (ret != CMD_STATUS_COMPLETE) { - queue_delayed_work(priv->hw->workqueue, &priv->dwork_hw_scan, - SCAN_POLL_INTERVAL); + ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, + SCAN_POLL_INTERVAL); mutex_unlock(&priv->mtx); return; } @@ -1933,8 +1937,8 @@ static int at76_hw_scan(struct ieee80211_hw *hw, goto exit; } - queue_delayed_work(priv->hw->workqueue, &priv->dwork_hw_scan, - SCAN_POLL_INTERVAL); + ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, + SCAN_POLL_INTERVAL); exit: mutex_unlock(&priv->mtx); @@ -1946,9 +1950,8 @@ static int at76_config(struct ieee80211_hw *hw, u32 changed) { struct at76_priv *priv = hw->priv; - at76_dbg(DBG_MAC80211, "%s(): channel %d radio %d", - __func__, hw->conf.channel->hw_value, - hw->conf.radio_enabled); + at76_dbg(DBG_MAC80211, "%s(): channel %d", + __func__, hw->conf.channel->hw_value); at76_dbg_dump(DBG_MAC80211, priv->bssid, ETH_ALEN, "bssid:"); mutex_lock(&priv->mtx); @@ -1993,15 +1996,14 @@ static void at76_bss_info_changed(struct ieee80211_hw *hw, /* must be atomic */ static void at76_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, - unsigned int *total_flags, int mc_count, - struct dev_addr_list *mc_list) + unsigned int *total_flags, u64 multicast) { struct at76_priv *priv = hw->priv; int flags; at76_dbg(DBG_MAC80211, "%s(): changed_flags=0x%08x " - "total_flags=0x%08x mc_count=%d", - __func__, changed_flags, *total_flags, mc_count); + "total_flags=0x%08x", + __func__, changed_flags, *total_flags); flags = changed_flags & AT76_SUPPORTED_FILTERS; *total_flags = AT76_SUPPORTED_FILTERS; @@ -2023,7 +2025,7 @@ static void at76_configure_filter(struct ieee80211_hw *hw, } else return; - queue_work(hw->workqueue, &priv->work_set_promisc); + ieee80211_queue_work(hw, &priv->work_set_promisc); } static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ -2294,11 +2296,8 @@ static void at76_delete_device(struct at76_priv *priv) tasklet_kill(&priv->rx_tasklet); - if (priv->mac80211_registered) { - cancel_delayed_work(&priv->dwork_hw_scan); - flush_workqueue(priv->hw->workqueue); + if (priv->mac80211_registered) ieee80211_unregister_hw(priv->hw); - } if (priv->tx_urb) { usb_kill_urb(priv->tx_urb); diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index eb0337c49546..11ded150b932 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -1,9 +1,22 @@ -config ATH_COMMON +menuconfig ATH_COMMON tristate "Atheros Wireless Cards" depends on WLAN_80211 - depends on ATH5K || ATH9K || AR9170_USB + depends on CFG80211 + ---help--- + This will enable the support for the Atheros wireless drivers. + ath5k, ath9k and ar9170 drivers share some common code, this option + enables the common ath.ko module which shares common helpers. + For more information and documentation on this module you can visit: + + http://wireless.kernel.org/en/users/Drivers/ath + + For information on all Atheros wireless drivers visit: + + http://wireless.kernel.org/en/users/Drivers/Atheros + +if ATH_COMMON source "drivers/net/wireless/ath/ath5k/Kconfig" source "drivers/net/wireless/ath/ath9k/Kconfig" source "drivers/net/wireless/ath/ar9170/Kconfig" - +endif diff --git a/drivers/net/wireless/ath/ar9170/Kconfig b/drivers/net/wireless/ath/ar9170/Kconfig index b99e3263ee6d..05918f1e685a 100644 --- a/drivers/net/wireless/ath/ar9170/Kconfig +++ b/drivers/net/wireless/ath/ar9170/Kconfig @@ -1,13 +1,13 @@ config AR9170_USB tristate "Atheros AR9170 802.11n USB support" - depends on USB && MAC80211 && WLAN_80211 && EXPERIMENTAL + depends on USB && MAC80211 && WLAN_80211 select FW_LOADER - select ATH_COMMON help This is a driver for the Atheros "otus" 802.11n USB devices. These devices require additional firmware (2 files). For now, these files can be downloaded from here: + http://wireless.kernel.org/en/users/Drivers/ar9170 If you choose to build a module, it'll be called ar9170usb. diff --git a/drivers/net/wireless/ath/ar9170/ar9170.h b/drivers/net/wireless/ath/ar9170/ar9170.h index bb97981fb248..914e4718a9a8 100644 --- a/drivers/net/wireless/ath/ar9170/ar9170.h +++ b/drivers/net/wireless/ath/ar9170/ar9170.h @@ -109,13 +109,55 @@ struct ar9170_rxstream_mpdu_merge { bool has_plcp; }; +#define AR9170_NUM_MAX_BA_RETRY 5 +#define AR9170_NUM_TID 16 +#define WME_BA_BMP_SIZE 64 +#define AR9170_NUM_MAX_AGG_LEN (2 * WME_BA_BMP_SIZE) + +#define WME_AC_BE 2 +#define WME_AC_BK 3 +#define WME_AC_VI 1 +#define WME_AC_VO 0 + +#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) + +#define BAW_WITHIN(_start, _bawsz, _seqno) \ + ((((_seqno) - (_start)) & 0xfff) < (_bawsz)) + +enum ar9170_tid_state { + AR9170_TID_STATE_INVALID, + AR9170_TID_STATE_SHUTDOWN, + AR9170_TID_STATE_PROGRESS, + AR9170_TID_STATE_COMPLETE, +}; + +struct ar9170_sta_tid { + struct list_head list; + struct sk_buff_head queue; + u8 addr[ETH_ALEN]; + u16 ssn; + u16 tid; + enum ar9170_tid_state state; + bool active; + u8 retry; +}; + #define AR9170_QUEUE_TIMEOUT 64 #define AR9170_TX_TIMEOUT 8 +#define AR9170_BA_TIMEOUT 4 #define AR9170_JANITOR_DELAY 128 #define AR9170_TX_INVALID_RATE 0xffffffff +#define AR9170_NUM_TX_STATUS 128 +#define AR9170_NUM_TX_AGG_MAX 30 + struct ar9170 { struct ieee80211_hw *hw; + struct ath_common common; struct mutex mutex; enum ar9170_device_state state; unsigned long bad_hw_nagger; @@ -136,6 +178,7 @@ struct ar9170 { /* beaconing */ struct sk_buff *beacon; struct work_struct beacon_work; + bool enable_beacon; /* cryptographic engine */ u64 usedkeys; @@ -143,10 +186,8 @@ struct ar9170 { bool disable_offload; /* filter settings */ - struct work_struct filter_config_work; - u64 cur_mc_hash, want_mc_hash; - u32 cur_filter, want_filter; - unsigned long filter_changed; + u64 cur_mc_hash; + u32 cur_filter; unsigned int filter_state; bool sniffer_enabled; @@ -181,20 +222,30 @@ struct ar9170 { /* EEPROM */ struct ar9170_eeprom eeprom; - struct ath_regulatory regulatory; /* tx queues - as seen by hw - */ struct sk_buff_head tx_pending[__AR9170_NUM_TXQ]; struct sk_buff_head tx_status[__AR9170_NUM_TXQ]; struct delayed_work tx_janitor; + /* tx ampdu */ + struct sk_buff_head tx_status_ampdu; + spinlock_t tx_ampdu_list_lock; + struct list_head tx_ampdu_list; + unsigned int tx_ampdu_pending; /* rxstream mpdu merge */ struct ar9170_rxstream_mpdu_merge rx_mpdu; struct sk_buff *rx_failover; int rx_failover_missing; + + /* (cached) HW A-MPDU settings */ + u8 global_ampdu_density; + u8 global_ampdu_factor; }; struct ar9170_sta_info { + struct ar9170_sta_tid agg[AR9170_NUM_TID]; + unsigned int ampdu_max_len; }; #define AR9170_TX_FLAG_WAIT_FOR_ACK BIT(0) @@ -209,10 +260,6 @@ struct ar9170_tx_info { #define IS_STARTED(a) (((struct ar9170 *)a)->state >= AR9170_STARTED) #define IS_ACCEPTING_CMD(a) (((struct ar9170 *)a)->state >= AR9170_IDLE) -#define AR9170_FILTER_CHANGED_MODE BIT(0) -#define AR9170_FILTER_CHANGED_MULTICAST BIT(1) -#define AR9170_FILTER_CHANGED_FRAMEFILTER BIT(2) - /* exported interface */ void *ar9170_alloc(size_t priv_size); int ar9170_register(struct ar9170 *ar, struct device *pdev); @@ -226,8 +273,8 @@ int ar9170_nag_limiter(struct ar9170 *ar); int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb); int ar9170_init_mac(struct ar9170 *ar); int ar9170_set_qos(struct ar9170 *ar); -int ar9170_update_multicast(struct ar9170 *ar); -int ar9170_update_frame_filter(struct ar9170 *ar); +int ar9170_update_multicast(struct ar9170 *ar, const u64 mc_hast); +int ar9170_update_frame_filter(struct ar9170 *ar, const u32 filter); int ar9170_set_operating_mode(struct ar9170 *ar); int ar9170_set_beacon_timers(struct ar9170 *ar); int ar9170_set_dyn_sifs_ack(struct ar9170 *ar); diff --git a/drivers/net/wireless/ath/ar9170/led.c b/drivers/net/wireless/ath/ar9170/led.c index 63fda6cd2101..86c4e79f6bc8 100644 --- a/drivers/net/wireless/ath/ar9170/led.c +++ b/drivers/net/wireless/ath/ar9170/led.c @@ -90,9 +90,12 @@ static void ar9170_update_leds(struct work_struct *work) ar9170_set_leds_state(ar, led_val); mutex_unlock(&ar->mutex); - if (rerun) - queue_delayed_work(ar->hw->workqueue, &ar->led_work, - msecs_to_jiffies(blink_delay)); + if (!rerun) + return; + + ieee80211_queue_delayed_work(ar->hw, + &ar->led_work, + msecs_to_jiffies(blink_delay)); } static void ar9170_led_brightness_set(struct led_classdev *led, @@ -110,7 +113,7 @@ static void ar9170_led_brightness_set(struct led_classdev *led, } if (likely(IS_ACCEPTING_CMD(ar) && arl->toggled)) - queue_delayed_work(ar->hw->workqueue, &ar->led_work, HZ/10); + ieee80211_queue_delayed_work(ar->hw, &ar->led_work, HZ/10); } static int ar9170_register_led(struct ar9170 *ar, int i, char *name, diff --git a/drivers/net/wireless/ath/ar9170/mac.c b/drivers/net/wireless/ath/ar9170/mac.c index d9f1f46de183..614e3218a2bc 100644 --- a/drivers/net/wireless/ath/ar9170/mac.c +++ b/drivers/net/wireless/ath/ar9170/mac.c @@ -238,39 +238,31 @@ static int ar9170_set_mac_reg(struct ar9170 *ar, const u32 reg, const u8 *mac) return ar9170_regwrite_result(); } -int ar9170_update_multicast(struct ar9170 *ar) +int ar9170_update_multicast(struct ar9170 *ar, const u64 mc_hash) { int err; ar9170_regwrite_begin(ar); - ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H, - ar->want_mc_hash >> 32); - ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L, - ar->want_mc_hash); - + ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_H, mc_hash >> 32); + ar9170_regwrite(AR9170_MAC_REG_GROUP_HASH_TBL_L, mc_hash); ar9170_regwrite_finish(); err = ar9170_regwrite_result(); - if (err) return err; - ar->cur_mc_hash = ar->want_mc_hash; - + ar->cur_mc_hash = mc_hash; return 0; } -int ar9170_update_frame_filter(struct ar9170 *ar) +int ar9170_update_frame_filter(struct ar9170 *ar, const u32 filter) { int err; - err = ar9170_write_reg(ar, AR9170_MAC_REG_FRAMETYPE_FILTER, - ar->want_filter); - + err = ar9170_write_reg(ar, AR9170_MAC_REG_FRAMETYPE_FILTER, filter); if (err) return err; - ar->cur_filter = ar->want_filter; - + ar->cur_filter = filter; return 0; } @@ -391,24 +383,26 @@ int ar9170_set_beacon_timers(struct ar9170 *ar) if (ar->vif) { v |= ar->vif->bss_conf.beacon_int; - switch (ar->vif->type) { - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_ADHOC: - v |= BIT(25); - break; - case NL80211_IFTYPE_AP: - v |= BIT(24); - pretbtt = (ar->vif->bss_conf.beacon_int - 6) << 16; - break; - default: + if (ar->enable_beacon) { + switch (ar->vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + v |= BIT(25); + break; + case NL80211_IFTYPE_AP: + v |= BIT(24); + pretbtt = (ar->vif->bss_conf.beacon_int - 6) << + 16; + break; + default: break; + } } v |= ar->vif->bss_conf.dtim_period << 16; } ar9170_regwrite_begin(ar); - ar9170_regwrite(AR9170_MAC_REG_PRETBTT, pretbtt); ar9170_regwrite(AR9170_MAC_REG_BCN_PERIOD, v); ar9170_regwrite_finish(); diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index 88c3d8573869..c1f8c69db165 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -49,6 +49,10 @@ static int modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); +static int modparam_ht; +module_param_named(ht, modparam_ht, bool, S_IRUGO); +MODULE_PARM_DESC(ht, "enable MPDU aggregation."); + #define RATE(_bitrate, _hw_rate, _txpidx, _flags) { \ .bitrate = (_bitrate), \ .flags = (_flags), \ @@ -148,12 +152,15 @@ static struct ieee80211_channel ar9170_5ghz_chantable[] = { .cap = IEEE80211_HT_CAP_MAX_AMSDU | \ IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \ IEEE80211_HT_CAP_SGI_40 | \ + IEEE80211_HT_CAP_GRN_FLD | \ IEEE80211_HT_CAP_DSSSCCK40 | \ IEEE80211_HT_CAP_SM_PS, \ .ampdu_factor = 3, \ .ampdu_density = 6, \ .mcs = { \ - .rx_mask = { 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, }, \ + .rx_mask = { 0xff, 0xff, 0, 0, 0x1, 0, 0, 0, 0, 0, }, \ + .rx_highest = cpu_to_le16(300), \ + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \ }, \ } @@ -174,8 +181,31 @@ static struct ieee80211_supported_band ar9170_band_5GHz = { }; static void ar9170_tx(struct ar9170 *ar); +static bool ar9170_tx_ampdu(struct ar9170 *ar); -#ifdef AR9170_QUEUE_DEBUG +static inline u16 ar9170_get_seq_h(struct ieee80211_hdr *hdr) +{ + return le16_to_cpu(hdr->seq_ctrl) >> 4; +} + +static inline u16 ar9170_get_seq(struct sk_buff *skb) +{ + struct ar9170_tx_control *txc = (void *) skb->data; + return ar9170_get_seq_h((void *) txc->frame_data); +} + +static inline u16 ar9170_get_tid(struct sk_buff *skb) +{ + struct ar9170_tx_control *txc = (void *) skb->data; + struct ieee80211_hdr *hdr = (void *) txc->frame_data; + + return (ieee80211_get_qos_ctl(hdr))[0] & IEEE80211_QOS_CTL_TID_MASK; +} + +#define GET_NEXT_SEQ(seq) ((seq + 1) & 0x0fff) +#define GET_NEXT_SEQ_FROM_SKB(skb) (GET_NEXT_SEQ(ar9170_get_seq(skb))) + +#if (defined AR9170_QUEUE_DEBUG) || (defined AR9170_TXAGG_DEBUG) static void ar9170_print_txheader(struct ar9170 *ar, struct sk_buff *skb) { struct ar9170_tx_control *txc = (void *) skb->data; @@ -183,10 +213,10 @@ static void ar9170_print_txheader(struct ar9170 *ar, struct sk_buff *skb) struct ar9170_tx_info *arinfo = (void *) txinfo->rate_driver_data; struct ieee80211_hdr *hdr = (void *) txc->frame_data; - printk(KERN_DEBUG "%s: => FRAME [skb:%p, q:%d, DA:[%pM] flags:%x " + printk(KERN_DEBUG "%s: => FRAME [skb:%p, q:%d, DA:[%pM] flags:%x s:%d " "mac_ctrl:%04x, phy_ctrl:%08x, timeout:[%d ms]]\n", wiphy_name(ar->hw->wiphy), skb, skb_get_queue_mapping(skb), - ieee80211_get_DA(hdr), arinfo->flags, + ieee80211_get_DA(hdr), arinfo->flags, ar9170_get_seq_h(hdr), le16_to_cpu(txc->mac_control), le32_to_cpu(txc->phy_control), jiffies_to_msecs(arinfo->timeout - jiffies)); } @@ -210,7 +240,9 @@ static void __ar9170_dump_txqueue(struct ar9170 *ar, "mismatch %d != %d\n", skb_queue_len(queue), i); printk(KERN_DEBUG "---[ end ]---\n"); } +#endif /* AR9170_QUEUE_DEBUG || AR9170_TXAGG_DEBUG */ +#ifdef AR9170_QUEUE_DEBUG static void ar9170_dump_txqueue(struct ar9170 *ar, struct sk_buff_head *queue) { @@ -220,7 +252,9 @@ static void ar9170_dump_txqueue(struct ar9170 *ar, __ar9170_dump_txqueue(ar, queue); spin_unlock_irqrestore(&queue->lock, flags); } +#endif /* AR9170_QUEUE_DEBUG */ +#ifdef AR9170_QUEUE_STOP_DEBUG static void __ar9170_dump_txstats(struct ar9170 *ar) { int i; @@ -229,20 +263,27 @@ static void __ar9170_dump_txstats(struct ar9170 *ar) wiphy_name(ar->hw->wiphy)); for (i = 0; i < __AR9170_NUM_TXQ; i++) - printk(KERN_DEBUG "%s: queue:%d limit:%d len:%d waitack:%d\n", - wiphy_name(ar->hw->wiphy), i, ar->tx_stats[i].limit, - ar->tx_stats[i].len, skb_queue_len(&ar->tx_status[i])); + printk(KERN_DEBUG "%s: queue:%d limit:%d len:%d waitack:%d " + " stopped:%d\n", wiphy_name(ar->hw->wiphy), i, + ar->tx_stats[i].limit, ar->tx_stats[i].len, + skb_queue_len(&ar->tx_status[i]), + ieee80211_queue_stopped(ar->hw, i)); } +#endif /* AR9170_QUEUE_STOP_DEBUG */ -static void ar9170_dump_txstats(struct ar9170 *ar) +#ifdef AR9170_TXAGG_DEBUG +static void ar9170_dump_tx_status_ampdu(struct ar9170 *ar) { unsigned long flags; - spin_lock_irqsave(&ar->tx_stats_lock, flags); - __ar9170_dump_txstats(ar); - spin_unlock_irqrestore(&ar->tx_stats_lock, flags); + spin_lock_irqsave(&ar->tx_status_ampdu.lock, flags); + printk(KERN_DEBUG "%s: A-MPDU tx_status queue => \n", + wiphy_name(ar->hw->wiphy)); + __ar9170_dump_txqueue(ar, &ar->tx_status_ampdu); + spin_unlock_irqrestore(&ar->tx_status_ampdu.lock, flags); } -#endif /* AR9170_QUEUE_DEBUG */ + +#endif /* AR9170_TXAGG_DEBUG */ /* caller must guarantee exclusive access for _bin_ queue. */ static void ar9170_recycle_expired(struct ar9170 *ar, @@ -315,6 +356,70 @@ static void ar9170_tx_status(struct ar9170 *ar, struct sk_buff *skb, ieee80211_tx_status_irqsafe(ar->hw, skb); } +static void ar9170_tx_fake_ampdu_status(struct ar9170 *ar) +{ + struct sk_buff_head success; + struct sk_buff *skb; + unsigned int i; + unsigned long queue_bitmap = 0; + + skb_queue_head_init(&success); + + while (skb_queue_len(&ar->tx_status_ampdu) > AR9170_NUM_TX_STATUS) + __skb_queue_tail(&success, skb_dequeue(&ar->tx_status_ampdu)); + + ar9170_recycle_expired(ar, &ar->tx_status_ampdu, &success); + +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_DEBUG "%s: collected %d A-MPDU frames.\n", + wiphy_name(ar->hw->wiphy), skb_queue_len(&success)); + __ar9170_dump_txqueue(ar, &success); +#endif /* AR9170_TXAGG_DEBUG */ + + while ((skb = __skb_dequeue(&success))) { + struct ieee80211_tx_info *txinfo; + + queue_bitmap |= BIT(skb_get_queue_mapping(skb)); + + txinfo = IEEE80211_SKB_CB(skb); + ieee80211_tx_info_clear_status(txinfo); + + txinfo->flags |= IEEE80211_TX_STAT_ACK; + txinfo->status.rates[0].count = 1; + + skb_pull(skb, sizeof(struct ar9170_tx_control)); + ieee80211_tx_status_irqsafe(ar->hw, skb); + } + + for_each_bit(i, &queue_bitmap, BITS_PER_BYTE) { +#ifdef AR9170_QUEUE_STOP_DEBUG + printk(KERN_DEBUG "%s: wake queue %d\n", + wiphy_name(ar->hw->wiphy), i); + __ar9170_dump_txstats(ar); +#endif /* AR9170_QUEUE_STOP_DEBUG */ + ieee80211_wake_queue(ar->hw, i); + } + + if (queue_bitmap) + ar9170_tx(ar); +} + +static void ar9170_tx_ampdu_callback(struct ar9170 *ar, struct sk_buff *skb) +{ + struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb); + struct ar9170_tx_info *arinfo = (void *) txinfo->rate_driver_data; + + arinfo->timeout = jiffies + + msecs_to_jiffies(AR9170_BA_TIMEOUT); + + skb_queue_tail(&ar->tx_status_ampdu, skb); + ar9170_tx_fake_ampdu_status(ar); + ar->tx_ampdu_pending--; + + if (!list_empty(&ar->tx_ampdu_list) && !ar->tx_ampdu_pending) + ar9170_tx_ampdu(ar); +} + void ar9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -336,7 +441,7 @@ void ar9170_tx_callback(struct ar9170 *ar, struct sk_buff *skb) spin_unlock_irqrestore(&ar->tx_stats_lock, flags); if (arinfo->flags & AR9170_TX_FLAG_BLOCK_ACK) { - dev_kfree_skb_any(skb); + ar9170_tx_ampdu_callback(ar, skb); } else if (arinfo->flags & AR9170_TX_FLAG_WAIT_FOR_ACK) { arinfo->timeout = jiffies + msecs_to_jiffies(AR9170_TX_TIMEOUT); @@ -420,6 +525,38 @@ static struct sk_buff *ar9170_get_queued_skb(struct ar9170 *ar, return NULL; } +static void ar9170_handle_block_ack(struct ar9170 *ar, u16 count, u16 r) +{ + struct sk_buff *skb; + struct ieee80211_tx_info *txinfo; + + while (count) { + skb = ar9170_get_queued_skb(ar, NULL, &ar->tx_status_ampdu, r); + if (!skb) + break; + + txinfo = IEEE80211_SKB_CB(skb); + ieee80211_tx_info_clear_status(txinfo); + + /* FIXME: maybe more ? */ + txinfo->status.rates[0].count = 1; + + skb_pull(skb, sizeof(struct ar9170_tx_control)); + ieee80211_tx_status_irqsafe(ar->hw, skb); + count--; + } + +#ifdef AR9170_TXAGG_DEBUG + if (count) { + printk(KERN_DEBUG "%s: got %d more failed mpdus, but no more " + "suitable frames left in tx_status queue.\n", + wiphy_name(ar->hw->wiphy), count); + + ar9170_dump_tx_status_ampdu(ar); + } +#endif /* AR9170_TXAGG_DEBUG */ +} + /* * This worker tries to keeps an maintain tx_status queues. * So we can guarantee that incoming tx_status reports are @@ -456,10 +593,14 @@ static void ar9170_tx_janitor(struct work_struct *work) resched = true; } - if (resched) - queue_delayed_work(ar->hw->workqueue, - &ar->tx_janitor, - msecs_to_jiffies(AR9170_JANITOR_DELAY)); + ar9170_tx_fake_ampdu_status(ar); + + if (!resched) + return; + + ieee80211_queue_delayed_work(ar->hw, + &ar->tx_janitor, + msecs_to_jiffies(AR9170_JANITOR_DELAY)); } void ar9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len) @@ -509,7 +650,7 @@ void ar9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len) * pre-TBTT event */ if (ar->vif && ar->vif->type == NL80211_IFTYPE_AP) - queue_work(ar->hw->workqueue, &ar->beacon_work); + ieee80211_queue_work(ar->hw, &ar->beacon_work); break; case 0xc2: @@ -528,8 +669,15 @@ void ar9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len) break; case 0xc4: + /* BlockACK bitmap */ + break; + case 0xc5: /* BlockACK events */ + ar9170_handle_block_ack(ar, + le16_to_cpu(cmd->ba_fail_cnt.failed), + le16_to_cpu(cmd->ba_fail_cnt.rate)); + ar9170_tx_fake_ampdu_status(ar); break; case 0xc6: @@ -917,8 +1065,10 @@ static void ar9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len) ar9170_rx_phy_status(ar, phy, &status); skb = ar9170_rx_copy_data(buf, mpdu_len); - if (likely(skb)) - ieee80211_rx_irqsafe(ar->hw, skb, &status); + if (likely(skb)) { + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + ieee80211_rx_irqsafe(ar->hw, skb); + } } void ar9170_rx(struct ar9170 *ar, struct sk_buff *skb) @@ -1082,8 +1232,6 @@ static int ar9170_op_start(struct ieee80211_hw *hw) mutex_lock(&ar->mutex); - ar->filter_changed = 0; - /* reinitialize queues statistics */ memset(&ar->tx_stats, 0, sizeof(ar->tx_stats)); for (i = 0; i < __AR9170_NUM_TXQ; i++) @@ -1096,6 +1244,10 @@ static int ar9170_op_start(struct ieee80211_hw *hw) AR9170_FILL_QUEUE(ar->edcf[3], 2, 3, 7, 47); /* VOICE */ AR9170_FILL_QUEUE(ar->edcf[4], 2, 3, 7, 0); /* SPECIAL */ + /* set sane AMPDU defaults */ + ar->global_ampdu_density = 6; + ar->global_ampdu_factor = 3; + ar->bad_hw_nagger = jiffies; err = ar->open(ar); @@ -1138,11 +1290,12 @@ static void ar9170_op_stop(struct ieee80211_hw *hw) if (IS_STARTED(ar)) ar->state = AR9170_IDLE; - flush_workqueue(ar->hw->workqueue); - cancel_delayed_work_sync(&ar->tx_janitor); - cancel_work_sync(&ar->filter_config_work); +#ifdef CONFIG_AR9170_LEDS + cancel_delayed_work_sync(&ar->led_work); +#endif cancel_work_sync(&ar->beacon_work); + mutex_lock(&ar->mutex); if (IS_ACCEPTING_CMD(ar)) { @@ -1157,9 +1310,40 @@ static void ar9170_op_stop(struct ieee80211_hw *hw) skb_queue_purge(&ar->tx_pending[i]); skb_queue_purge(&ar->tx_status[i]); } + skb_queue_purge(&ar->tx_status_ampdu); + mutex_unlock(&ar->mutex); } +static void ar9170_tx_indicate_immba(struct ar9170 *ar, struct sk_buff *skb) +{ + struct ar9170_tx_control *txc = (void *) skb->data; + + txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_IMM_AMPDU); +} + +static void ar9170_tx_copy_phy(struct ar9170 *ar, struct sk_buff *dst, + struct sk_buff *src) +{ + struct ar9170_tx_control *dst_txc, *src_txc; + struct ieee80211_tx_info *dst_info, *src_info; + struct ar9170_tx_info *dst_arinfo, *src_arinfo; + + src_txc = (void *) src->data; + src_info = IEEE80211_SKB_CB(src); + src_arinfo = (void *) src_info->rate_driver_data; + + dst_txc = (void *) dst->data; + dst_info = IEEE80211_SKB_CB(dst); + dst_arinfo = (void *) dst_info->rate_driver_data; + + dst_txc->phy_control = src_txc->phy_control; + + /* same MCS for the whole aggregate */ + memcpy(dst_info->driver_rates, src_info->driver_rates, + sizeof(dst_info->driver_rates)); +} + static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) { struct ieee80211_hdr *hdr; @@ -1228,6 +1412,7 @@ static int ar9170_tx_prepare(struct ar9170 *ar, struct sk_buff *skb) txc->mac_control |= cpu_to_le16(AR9170_TX_MAC_AGGR); arinfo->flags = AR9170_TX_FLAG_BLOCK_ACK; + goto out; } @@ -1358,6 +1543,159 @@ static void ar9170_tx_prepare_phy(struct ar9170 *ar, struct sk_buff *skb) txc->phy_control |= cpu_to_le32(chains << AR9170_TX_PHY_TXCHAIN_SHIFT); } +static bool ar9170_tx_ampdu(struct ar9170 *ar) +{ + struct sk_buff_head agg; + struct ar9170_sta_tid *tid_info = NULL, *tmp; + struct sk_buff *skb, *first = NULL; + unsigned long flags, f2; + unsigned int i = 0; + u16 seq, queue, tmpssn; + bool run = false; + + skb_queue_head_init(&agg); + + spin_lock_irqsave(&ar->tx_ampdu_list_lock, flags); + if (list_empty(&ar->tx_ampdu_list)) { +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_DEBUG "%s: aggregation list is empty.\n", + wiphy_name(ar->hw->wiphy)); +#endif /* AR9170_TXAGG_DEBUG */ + goto out_unlock; + } + + list_for_each_entry_safe(tid_info, tmp, &ar->tx_ampdu_list, list) { + if (tid_info->state != AR9170_TID_STATE_COMPLETE) { +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_DEBUG "%s: dangling aggregation entry!\n", + wiphy_name(ar->hw->wiphy)); +#endif /* AR9170_TXAGG_DEBUG */ + continue; + } + + if (++i > 64) { +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_DEBUG "%s: enough frames aggregated.\n", + wiphy_name(ar->hw->wiphy)); +#endif /* AR9170_TXAGG_DEBUG */ + break; + } + + queue = TID_TO_WME_AC(tid_info->tid); + + if (skb_queue_len(&ar->tx_pending[queue]) >= + AR9170_NUM_TX_AGG_MAX) { +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_DEBUG "%s: queue %d full.\n", + wiphy_name(ar->hw->wiphy), queue); +#endif /* AR9170_TXAGG_DEBUG */ + continue; + } + + list_del_init(&tid_info->list); + + spin_lock_irqsave(&tid_info->queue.lock, f2); + tmpssn = seq = tid_info->ssn; + first = skb_peek(&tid_info->queue); + + if (likely(first)) + tmpssn = ar9170_get_seq(first); + + if (unlikely(tmpssn != seq)) { +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_DEBUG "%s: ssn mismatch [%d != %d]\n.", + wiphy_name(ar->hw->wiphy), seq, tmpssn); +#endif /* AR9170_TXAGG_DEBUG */ + tid_info->ssn = tmpssn; + } + +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_DEBUG "%s: generate A-MPDU for tid:%d ssn:%d with " + "%d queued frames.\n", wiphy_name(ar->hw->wiphy), + tid_info->tid, tid_info->ssn, + skb_queue_len(&tid_info->queue)); + __ar9170_dump_txqueue(ar, &tid_info->queue); +#endif /* AR9170_TXAGG_DEBUG */ + + while ((skb = skb_peek(&tid_info->queue))) { + if (unlikely(ar9170_get_seq(skb) != seq)) + break; + + __skb_unlink(skb, &tid_info->queue); + tid_info->ssn = seq = GET_NEXT_SEQ(seq); + + if (unlikely(skb_get_queue_mapping(skb) != queue)) { +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_DEBUG "%s: tid:%d(q:%d) queue:%d " + "!match.\n", wiphy_name(ar->hw->wiphy), + tid_info->tid, + TID_TO_WME_AC(tid_info->tid), + skb_get_queue_mapping(skb)); +#endif /* AR9170_TXAGG_DEBUG */ + dev_kfree_skb_any(skb); + continue; + } + + if (unlikely(first == skb)) { + ar9170_tx_prepare_phy(ar, skb); + __skb_queue_tail(&agg, skb); + first = skb; + } else { + ar9170_tx_copy_phy(ar, skb, first); + __skb_queue_tail(&agg, skb); + } + + if (unlikely(skb_queue_len(&agg) == + AR9170_NUM_TX_AGG_MAX)) + break; + } + + if (skb_queue_empty(&tid_info->queue)) + tid_info->active = false; + else + list_add_tail(&tid_info->list, + &ar->tx_ampdu_list); + + spin_unlock_irqrestore(&tid_info->queue.lock, f2); + + if (unlikely(skb_queue_empty(&agg))) { +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_DEBUG "%s: queued empty list!\n", + wiphy_name(ar->hw->wiphy)); +#endif /* AR9170_TXAGG_DEBUG */ + continue; + } + + /* + * tell the FW/HW that this is the last frame, + * that way it will wait for the immediate block ack. + */ + if (likely(skb_peek_tail(&agg))) + ar9170_tx_indicate_immba(ar, skb_peek_tail(&agg)); + +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_DEBUG "%s: generated A-MPDU looks like this:\n", + wiphy_name(ar->hw->wiphy)); + __ar9170_dump_txqueue(ar, &agg); +#endif /* AR9170_TXAGG_DEBUG */ + + spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags); + + spin_lock_irqsave(&ar->tx_pending[queue].lock, flags); + skb_queue_splice_tail_init(&agg, &ar->tx_pending[queue]); + spin_unlock_irqrestore(&ar->tx_pending[queue].lock, flags); + run = true; + + spin_lock_irqsave(&ar->tx_ampdu_list_lock, flags); + } + +out_unlock: + spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags); + __skb_queue_purge(&agg); + + return run; +} + static void ar9170_tx(struct ar9170 *ar) { struct sk_buff *skb; @@ -1382,11 +1720,17 @@ static void ar9170_tx(struct ar9170 *ar) printk(KERN_DEBUG "%s: queue %d full\n", wiphy_name(ar->hw->wiphy), i); - __ar9170_dump_txstats(ar); - printk(KERN_DEBUG "stuck frames: ===> \n"); + printk(KERN_DEBUG "%s: stuck frames: ===> \n", + wiphy_name(ar->hw->wiphy)); ar9170_dump_txqueue(ar, &ar->tx_pending[i]); ar9170_dump_txqueue(ar, &ar->tx_status[i]); #endif /* AR9170_QUEUE_DEBUG */ + +#ifdef AR9170_QUEUE_STOP_DEBUG + printk(KERN_DEBUG "%s: stop queue %d\n", + wiphy_name(ar->hw->wiphy), i); + __ar9170_dump_txstats(ar); +#endif /* AR9170_QUEUE_STOP_DEBUG */ ieee80211_stop_queue(ar->hw, i); spin_unlock_irqrestore(&ar->tx_stats_lock, flags); continue; @@ -1401,8 +1745,6 @@ static void ar9170_tx(struct ar9170 *ar) "remaining slots:%d, needed:%d\n", wiphy_name(ar->hw->wiphy), i, remaining_space, frames); - - ar9170_dump_txstats(ar); #endif /* AR9170_QUEUE_DEBUG */ frames = remaining_space; } @@ -1430,6 +1772,9 @@ static void ar9170_tx(struct ar9170 *ar) arinfo->timeout = jiffies + msecs_to_jiffies(AR9170_TX_TIMEOUT); + if (arinfo->flags == AR9170_TX_FLAG_BLOCK_ACK) + ar->tx_ampdu_pending++; + #ifdef AR9170_QUEUE_DEBUG printk(KERN_DEBUG "%s: send frame q:%d =>\n", wiphy_name(ar->hw->wiphy), i); @@ -1438,6 +1783,9 @@ static void ar9170_tx(struct ar9170 *ar) err = ar->tx(ar, skb); if (unlikely(err)) { + if (arinfo->flags == AR9170_TX_FLAG_BLOCK_ACK) + ar->tx_ampdu_pending--; + frames_failed++; dev_kfree_skb_any(skb); } else { @@ -1459,22 +1807,113 @@ static void ar9170_tx(struct ar9170 *ar) if (unlikely(frames_failed)) { #ifdef AR9170_QUEUE_DEBUG - printk(KERN_DEBUG "%s: frames failed =>\n", + printk(KERN_DEBUG "%s: frames failed %d =>\n", wiphy_name(ar->hw->wiphy), frames_failed); #endif /* AR9170_QUEUE_DEBUG */ spin_lock_irqsave(&ar->tx_stats_lock, flags); ar->tx_stats[i].len -= frames_failed; ar->tx_stats[i].count -= frames_failed; +#ifdef AR9170_QUEUE_STOP_DEBUG + printk(KERN_DEBUG "%s: wake queue %d\n", + wiphy_name(ar->hw->wiphy), i); + __ar9170_dump_txstats(ar); +#endif /* AR9170_QUEUE_STOP_DEBUG */ ieee80211_wake_queue(ar->hw, i); spin_unlock_irqrestore(&ar->tx_stats_lock, flags); } } - if (schedule_garbagecollector) - queue_delayed_work(ar->hw->workqueue, - &ar->tx_janitor, - msecs_to_jiffies(AR9170_JANITOR_DELAY)); + if (!schedule_garbagecollector) + return; + + ieee80211_queue_delayed_work(ar->hw, + &ar->tx_janitor, + msecs_to_jiffies(AR9170_JANITOR_DELAY)); +} + +static bool ar9170_tx_ampdu_queue(struct ar9170 *ar, struct sk_buff *skb) +{ + struct ieee80211_tx_info *txinfo; + struct ar9170_sta_info *sta_info; + struct ar9170_sta_tid *agg; + struct sk_buff *iter; + unsigned long flags, f2; + unsigned int max; + u16 tid, seq, qseq; + bool run = false, queue = false; + + tid = ar9170_get_tid(skb); + seq = ar9170_get_seq(skb); + txinfo = IEEE80211_SKB_CB(skb); + sta_info = (void *) txinfo->control.sta->drv_priv; + agg = &sta_info->agg[tid]; + max = sta_info->ampdu_max_len; + + spin_lock_irqsave(&ar->tx_ampdu_list_lock, flags); + + if (unlikely(agg->state != AR9170_TID_STATE_COMPLETE)) { +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_DEBUG "%s: BlockACK session not fully initialized " + "for ESS:%pM tid:%d state:%d.\n", + wiphy_name(ar->hw->wiphy), agg->addr, agg->tid, + agg->state); +#endif /* AR9170_TXAGG_DEBUG */ + goto err_unlock; + } + + if (!agg->active) { + agg->active = true; + agg->ssn = seq; + queue = true; + } + + /* check if seq is within the BA window */ + if (unlikely(!BAW_WITHIN(agg->ssn, max, seq))) { +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_DEBUG "%s: frame with tid:%d seq:%d does not " + "fit into BA window (%d - %d)\n", + wiphy_name(ar->hw->wiphy), tid, seq, agg->ssn, + (agg->ssn + max) & 0xfff); +#endif /* AR9170_TXAGG_DEBUG */ + goto err_unlock; + } + + spin_lock_irqsave(&agg->queue.lock, f2); + + skb_queue_reverse_walk(&agg->queue, iter) { + qseq = ar9170_get_seq(iter); + + if (GET_NEXT_SEQ(qseq) == seq) { + __skb_queue_after(&agg->queue, iter, skb); + goto queued; + } + } + + __skb_queue_head(&agg->queue, skb); + +queued: + spin_unlock_irqrestore(&agg->queue.lock, f2); + +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_DEBUG "%s: new aggregate %p queued.\n", + wiphy_name(ar->hw->wiphy), skb); + __ar9170_dump_txqueue(ar, &agg->queue); +#endif /* AR9170_TXAGG_DEBUG */ + + if (skb_queue_len(&agg->queue) >= AR9170_NUM_TX_AGG_MAX) + run = true; + + if (queue) + list_add_tail(&agg->list, &ar->tx_ampdu_list); + + spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags); + return run; + +err_unlock: + spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags); + dev_kfree_skb_irq(skb); + return false; } int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) @@ -1490,8 +1929,10 @@ int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) info = IEEE80211_SKB_CB(skb); if (info->flags & IEEE80211_TX_CTL_AMPDU) { - /* drop frame, we do not allow TX A-MPDU aggregation yet. */ - goto err_free; + bool run = ar9170_tx_ampdu_queue(ar, skb); + + if (run || !ar->tx_ampdu_pending) + ar9170_tx_ampdu(ar); } else { unsigned int queue = skb_get_queue_mapping(skb); @@ -1529,8 +1970,7 @@ static int ar9170_op_add_interface(struct ieee80211_hw *hw, } ar->cur_filter = 0; - ar->want_filter = AR9170_MAC_REG_FTF_DEFAULTS; - err = ar9170_update_frame_filter(ar); + err = ar9170_update_frame_filter(ar, AR9170_MAC_REG_FTF_DEFAULTS); if (err) goto unlock; @@ -1548,8 +1988,7 @@ static void ar9170_op_remove_interface(struct ieee80211_hw *hw, mutex_lock(&ar->mutex); ar->vif = NULL; - ar->want_filter = 0; - ar9170_update_frame_filter(ar); + ar9170_update_frame_filter(ar, 0); ar9170_set_beacon_timers(ar); dev_kfree_skb(ar->beacon); ar->beacon = NULL; @@ -1592,12 +2031,6 @@ static int ar9170_op_config(struct ieee80211_hw *hw, u32 changed) goto out; } - if (changed & BSS_CHANGED_BEACON_INT) { - err = ar9170_set_beacon_timers(ar); - if (err) - goto out; - } - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { /* adjust slot time for 5 GHz */ @@ -1621,48 +2054,37 @@ out: return err; } -static void ar9170_set_filters(struct work_struct *work) +static u64 ar9170_op_prepare_multicast(struct ieee80211_hw *hw, int mc_count, + struct dev_addr_list *mclist) { - struct ar9170 *ar = container_of(work, struct ar9170, - filter_config_work); - int err; - - if (unlikely(!IS_STARTED(ar))) - return ; - - mutex_lock(&ar->mutex); - if (test_and_clear_bit(AR9170_FILTER_CHANGED_MODE, - &ar->filter_changed)) { - err = ar9170_set_operating_mode(ar); - if (err) - goto unlock; - } + u64 mchash; + int i; - if (test_and_clear_bit(AR9170_FILTER_CHANGED_MULTICAST, - &ar->filter_changed)) { - err = ar9170_update_multicast(ar); - if (err) - goto unlock; - } + /* always get broadcast frames */ + mchash = 1ULL << (0xff >> 2); - if (test_and_clear_bit(AR9170_FILTER_CHANGED_FRAMEFILTER, - &ar->filter_changed)) { - err = ar9170_update_frame_filter(ar); - if (err) - goto unlock; + for (i = 0; i < mc_count; i++) { + if (WARN_ON(!mclist)) + break; + mchash |= 1ULL << (mclist->dmi_addr[5] >> 2); + mclist = mclist->next; } -unlock: - mutex_unlock(&ar->mutex); + return mchash; } static void ar9170_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { struct ar9170 *ar = hw->priv; + if (unlikely(!IS_ACCEPTING_CMD(ar))) + return ; + + mutex_lock(&ar->mutex); + /* mask supported flags */ *new_flags &= FIF_ALLMULTI | FIF_CONTROL | FIF_BCN_PRBRESP_PROMISC | FIF_PROMISC_IN_BSS | FIF_FCSFAIL | FIF_PLCPFAIL; @@ -1672,26 +2094,11 @@ static void ar9170_op_configure_filter(struct ieee80211_hw *hw, * then checking the error flags, later. */ - if (changed_flags & FIF_ALLMULTI) { - if (*new_flags & FIF_ALLMULTI) { - ar->want_mc_hash = ~0ULL; - } else { - u64 mchash; - int i; - - /* always get broadcast frames */ - mchash = 1ULL << (0xff >> 2); + if (changed_flags & FIF_ALLMULTI && *new_flags & FIF_ALLMULTI) + multicast = ~0ULL; - for (i = 0; i < mc_count; i++) { - if (WARN_ON(!mclist)) - break; - mchash |= 1ULL << (mclist->dmi_addr[5] >> 2); - mclist = mclist->next; - } - ar->want_mc_hash = mchash; - } - set_bit(AR9170_FILTER_CHANGED_MULTICAST, &ar->filter_changed); - } + if (multicast != ar->cur_mc_hash) + ar9170_update_multicast(ar, multicast); if (changed_flags & FIF_CONTROL) { u32 filter = AR9170_MAC_REG_FTF_PSPOLL | @@ -1702,24 +2109,22 @@ static void ar9170_op_configure_filter(struct ieee80211_hw *hw, AR9170_MAC_REG_FTF_CFE_ACK; if (*new_flags & FIF_CONTROL) - ar->want_filter = ar->cur_filter | filter; + filter |= ar->cur_filter; else - ar->want_filter = ar->cur_filter & ~filter; + filter &= (~ar->cur_filter); - set_bit(AR9170_FILTER_CHANGED_FRAMEFILTER, - &ar->filter_changed); + ar9170_update_frame_filter(ar, filter); } if (changed_flags & FIF_PROMISC_IN_BSS) { ar->sniffer_enabled = ((*new_flags) & FIF_PROMISC_IN_BSS) != 0; - set_bit(AR9170_FILTER_CHANGED_MODE, - &ar->filter_changed); + ar9170_set_operating_mode(ar); } - if (likely(IS_STARTED(ar))) - queue_work(ar->hw->workqueue, &ar->filter_config_work); + mutex_unlock(&ar->mutex); } + static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, @@ -1737,11 +2142,17 @@ static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw, goto out; } - if (changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) { + if (changed & BSS_CHANGED_BEACON_ENABLED) + ar->enable_beacon = bss_conf->enable_beacon; + + if (changed & BSS_CHANGED_BEACON) { err = ar9170_update_beacon(ar); if (err) goto out; + } + if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_INT)) { err = ar9170_set_beacon_timers(ar); if (err) goto out; @@ -1754,12 +2165,6 @@ static void ar9170_op_bss_info_changed(struct ieee80211_hw *hw, #endif /* CONFIG_AR9170_LEDS */ } - if (changed & BSS_CHANGED_BEACON_INT) { - err = ar9170_set_beacon_timers(ar); - if (err) - goto out; - } - if (changed & BSS_CHANGED_HT) { /* TODO */ err = 0; @@ -1929,6 +2334,50 @@ static void ar9170_sta_notify(struct ieee80211_hw *hw, enum sta_notify_cmd cmd, struct ieee80211_sta *sta) { + struct ar9170 *ar = hw->priv; + struct ar9170_sta_info *sta_info = (void *) sta->drv_priv; + unsigned int i; + + switch (cmd) { + case STA_NOTIFY_ADD: + memset(sta_info, 0, sizeof(*sta_info)); + + if (!sta->ht_cap.ht_supported) + break; + + if (sta->ht_cap.ampdu_density > ar->global_ampdu_density) + ar->global_ampdu_density = sta->ht_cap.ampdu_density; + + if (sta->ht_cap.ampdu_factor < ar->global_ampdu_factor) + ar->global_ampdu_factor = sta->ht_cap.ampdu_factor; + + for (i = 0; i < AR9170_NUM_TID; i++) { + sta_info->agg[i].state = AR9170_TID_STATE_SHUTDOWN; + sta_info->agg[i].active = false; + sta_info->agg[i].ssn = 0; + sta_info->agg[i].retry = 0; + sta_info->agg[i].tid = i; + INIT_LIST_HEAD(&sta_info->agg[i].list); + skb_queue_head_init(&sta_info->agg[i].queue); + } + + sta_info->ampdu_max_len = 1 << (3 + sta->ht_cap.ampdu_factor); + break; + + case STA_NOTIFY_REMOVE: + if (!sta->ht_cap.ht_supported) + break; + + for (i = 0; i < AR9170_NUM_TID; i++) { + sta_info->agg[i].state = AR9170_TID_STATE_INVALID; + skb_queue_purge(&sta_info->agg[i].queue); + } + + break; + + default: + break; + } } static int ar9170_get_stats(struct ieee80211_hw *hw, @@ -1984,18 +2433,65 @@ static int ar9170_ampdu_action(struct ieee80211_hw *hw, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { + struct ar9170 *ar = hw->priv; + struct ar9170_sta_info *sta_info = (void *) sta->drv_priv; + struct ar9170_sta_tid *tid_info = &sta_info->agg[tid]; + unsigned long flags; + + if (!modparam_ht) + return -EOPNOTSUPP; + switch (action) { + case IEEE80211_AMPDU_TX_START: + spin_lock_irqsave(&ar->tx_ampdu_list_lock, flags); + if (tid_info->state != AR9170_TID_STATE_SHUTDOWN || + !list_empty(&tid_info->list)) { + spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags); +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_INFO "%s: A-MPDU [ESS:[%pM] tid:[%d]] " + "is in a very bad state!\n", + wiphy_name(hw->wiphy), sta->addr, tid); +#endif /* AR9170_TXAGG_DEBUG */ + return -EBUSY; + } + + *ssn = tid_info->ssn; + tid_info->state = AR9170_TID_STATE_PROGRESS; + tid_info->active = false; + spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags); + ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid); + break; + + case IEEE80211_AMPDU_TX_STOP: + spin_lock_irqsave(&ar->tx_ampdu_list_lock, flags); + tid_info->state = AR9170_TID_STATE_SHUTDOWN; + list_del_init(&tid_info->list); + tid_info->active = false; + skb_queue_purge(&tid_info->queue); + spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags); + ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid); + break; + + case IEEE80211_AMPDU_TX_OPERATIONAL: +#ifdef AR9170_TXAGG_DEBUG + printk(KERN_INFO "%s: A-MPDU for %pM [tid:%d] Operational.\n", + wiphy_name(hw->wiphy), sta->addr, tid); +#endif /* AR9170_TXAGG_DEBUG */ + spin_lock_irqsave(&ar->tx_ampdu_list_lock, flags); + sta_info->agg[tid].state = AR9170_TID_STATE_COMPLETE; + spin_unlock_irqrestore(&ar->tx_ampdu_list_lock, flags); + break; + case IEEE80211_AMPDU_RX_START: case IEEE80211_AMPDU_RX_STOP: - /* - * Something goes wrong -- RX locks up - * after a while of receiving aggregated - * frames -- not enabling for now. - */ - return -EOPNOTSUPP; + /* Handled by firmware */ + break; + default: return -EOPNOTSUPP; } + + return 0; } static const struct ieee80211_ops ar9170_ops = { @@ -2005,6 +2501,7 @@ static const struct ieee80211_ops ar9170_ops = { .add_interface = ar9170_op_add_interface, .remove_interface = ar9170_op_remove_interface, .config = ar9170_op_config, + .prepare_multicast = ar9170_op_prepare_multicast, .configure_filter = ar9170_op_configure_filter, .conf_tx = ar9170_conf_tx, .bss_info_changed = ar9170_op_bss_info_changed, @@ -2044,14 +2541,16 @@ void *ar9170_alloc(size_t priv_size) mutex_init(&ar->mutex); spin_lock_init(&ar->cmdlock); spin_lock_init(&ar->tx_stats_lock); + spin_lock_init(&ar->tx_ampdu_list_lock); + skb_queue_head_init(&ar->tx_status_ampdu); for (i = 0; i < __AR9170_NUM_TXQ; i++) { skb_queue_head_init(&ar->tx_status[i]); skb_queue_head_init(&ar->tx_pending[i]); } ar9170_rx_reset_rx_mpdu(ar); - INIT_WORK(&ar->filter_config_work, ar9170_set_filters); INIT_WORK(&ar->beacon_work, ar9170_new_beacon); INIT_DELAYED_WORK(&ar->tx_janitor, ar9170_tx_janitor); + INIT_LIST_HEAD(&ar->tx_ampdu_list); /* all hw supports 2.4 GHz, so set channel to 1 by default */ ar->channel = &ar9170_2ghz_chantable[0]; @@ -2065,6 +2564,13 @@ void *ar9170_alloc(size_t priv_size) IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; + if (modparam_ht) { + ar->hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; + } else { + ar9170_band_2GHz.ht_cap.ht_supported = false; + ar9170_band_5GHz.ht_cap.ht_supported = false; + } + ar->hw->queues = __AR9170_NUM_TXQ; ar->hw->extra_tx_headroom = 8; ar->hw->sta_data_size = sizeof(struct ar9170_sta_info); @@ -2086,10 +2592,11 @@ static int ar9170_read_eeprom(struct ar9170 *ar) { #define RW 8 /* number of words to read at once */ #define RB (sizeof(u32) * RW) - DECLARE_MAC_BUF(mbuf); + struct ath_regulatory *regulatory = &ar->common.regulatory; u8 *eeprom = (void *)&ar->eeprom; u8 *addr = ar->eeprom.mac_address; __le32 offsets[RW]; + unsigned int rx_streams, tx_streams, tx_params = 0; int i, j, err, bands = 0; BUILD_BUG_ON(sizeof(ar->eeprom) & 3); @@ -2126,6 +2633,20 @@ static int ar9170_read_eeprom(struct ar9170 *ar) ar->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &ar9170_band_5GHz; bands++; } + + rx_streams = hweight8(ar->eeprom.rx_mask); + tx_streams = hweight8(ar->eeprom.tx_mask); + + if (rx_streams != tx_streams) + tx_params = IEEE80211_HT_MCS_TX_RX_DIFF; + + if (tx_streams >= 1 && tx_streams <= IEEE80211_HT_MCS_TX_MAX_STREAMS) + tx_params = (tx_streams - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + + ar9170_band_2GHz.ht_cap.mcs.tx_params |= tx_params; + ar9170_band_5GHz.ht_cap.mcs.tx_params |= tx_params; + /* * I measured this, a bandswitch takes roughly * 135 ms and a frequency switch about 80. @@ -2138,8 +2659,8 @@ static int ar9170_read_eeprom(struct ar9170 *ar) else ar->hw->channel_change_time = 80 * 1000; - ar->regulatory.current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]); - ar->regulatory.current_rd_ext = le16_to_cpu(ar->eeprom.reg_domain[1]); + regulatory->current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]); + regulatory->current_rd_ext = le16_to_cpu(ar->eeprom.reg_domain[1]); /* second part of wiphy init */ SET_IEEE80211_PERM_ADDR(ar->hw, addr); @@ -2153,11 +2674,12 @@ static int ar9170_reg_notifier(struct wiphy *wiphy, struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ar9170 *ar = hw->priv; - return ath_reg_notifier_apply(wiphy, request, &ar->regulatory); + return ath_reg_notifier_apply(wiphy, request, &ar->common.regulatory); } int ar9170_register(struct ar9170 *ar, struct device *pdev) { + struct ath_regulatory *regulatory = &ar->common.regulatory; int err; /* try to read EEPROM, init MAC addr */ @@ -2165,7 +2687,7 @@ int ar9170_register(struct ar9170 *ar, struct device *pdev) if (err) goto err_out; - err = ath_regd_init(&ar->regulatory, ar->hw->wiphy, + err = ath_regd_init(regulatory, ar->hw->wiphy, ar9170_reg_notifier); if (err) goto err_out; @@ -2174,8 +2696,8 @@ int ar9170_register(struct ar9170 *ar, struct device *pdev) if (err) goto err_out; - if (!ath_is_world_regd(&ar->regulatory)) - regulatory_hint(ar->hw->wiphy, ar->regulatory.alpha2); + if (!ath_is_world_regd(regulatory)) + regulatory_hint(ar->hw->wiphy, regulatory->alpha2); err = ar9170_init_leds(ar); if (err) diff --git a/drivers/net/wireless/ath/ar9170/usb.c b/drivers/net/wireless/ath/ar9170/usb.c index 007eb85fc67e..e0138ac8bf50 100644 --- a/drivers/net/wireless/ath/ar9170/usb.c +++ b/drivers/net/wireless/ath/ar9170/usb.c @@ -783,7 +783,7 @@ static int ar9170_usb_probe(struct usb_interface *intf, aru->req_one_stage_fw = ar9170_requires_one_stage(id); usb_set_intfdata(intf, aru); - SET_IEEE80211_DEV(ar->hw, &udev->dev); + SET_IEEE80211_DEV(ar->hw, &intf->dev); init_usb_anchor(&aru->rx_submitted); init_usb_anchor(&aru->tx_pending); diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h new file mode 100644 index 000000000000..a63e90cbf9e5 --- /dev/null +++ b/drivers/net/wireless/ath/ath.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2008-2009 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 ATH_H +#define ATH_H + +#include <linux/skbuff.h> + +struct reg_dmn_pair_mapping { + u16 regDmnEnum; + u16 reg_5ghz_ctl; + u16 reg_2ghz_ctl; +}; + +struct ath_regulatory { + char alpha2[2]; + u16 country_code; + u16 max_power_level; + u32 tp_scale; + u16 current_rd; + u16 current_rd_ext; + int16_t power_limit; + struct reg_dmn_pair_mapping *regpair; +}; + +struct ath_common { + u16 cachelsz; + struct ath_regulatory regulatory; +}; + +struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, + u32 len, + gfp_t gfp_mask); + +#endif /* ATH_H */ diff --git a/drivers/net/wireless/ath/ath5k/Kconfig b/drivers/net/wireless/ath/ath5k/Kconfig index daf0c83527d8..06d006675d7d 100644 --- a/drivers/net/wireless/ath/ath5k/Kconfig +++ b/drivers/net/wireless/ath/ath5k/Kconfig @@ -1,7 +1,6 @@ config ATH5K tristate "Atheros 5xxx wireless cards support" - depends on PCI && MAC80211 && WLAN_80211 && EXPERIMENTAL - select ATH_COMMON + depends on PCI && MAC80211 && WLAN_80211 select MAC80211_LEDS select LEDS_CLASS select NEW_LEDS diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 6358233bac99..6cd5efcec417 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -27,8 +27,6 @@ #include <linux/types.h> #include <net/mac80211.h> -#include "../regd.h" - /* RX/TX descriptor hw structs * TODO: Driver part should only see sw structs */ #include "desc.h" @@ -308,6 +306,7 @@ struct ath5k_srev_name { #define AR5K_SREV_AR5311B 0x30 /* Spirit */ #define AR5K_SREV_AR5211 0x40 /* Oahu */ #define AR5K_SREV_AR5212 0x50 /* Venice */ +#define AR5K_SREV_AR5212_V4 0x54 /* ??? */ #define AR5K_SREV_AR5213 0x55 /* ??? */ #define AR5K_SREV_AR5213A 0x59 /* Hainan */ #define AR5K_SREV_AR2413 0x78 /* Griffin lite */ @@ -713,8 +712,8 @@ struct ath5k_gain { * Used internaly for reset_tx_queue). * Also see struct struct ieee80211_channel. */ -#define IS_CHAN_XR(_c) ((_c.hw_value & CHANNEL_XR) != 0) -#define IS_CHAN_B(_c) ((_c.hw_value & CHANNEL_B) != 0) +#define IS_CHAN_XR(_c) ((_c->hw_value & CHANNEL_XR) != 0) +#define IS_CHAN_B(_c) ((_c->hw_value & CHANNEL_B) != 0) /* * The following structure is used to map 2GHz channels to @@ -919,6 +918,12 @@ enum ath5k_int { AR5K_INT_NOCARD = 0xffffffff }; +/* Software interrupts used for calibration */ +enum ath5k_software_interrupt { + AR5K_SWI_FULL_CALIBRATION = 0x01, + AR5K_SWI_SHORT_CALIBRATION = 0x02, +}; + /* * Power management */ @@ -1029,14 +1034,16 @@ struct ath5k_hw { enum ath5k_int ah_imr; enum nl80211_iftype ah_op_mode; - enum ath5k_power_mode ah_power_mode; - struct ieee80211_channel ah_current_channel; + struct ieee80211_channel *ah_current_channel; bool ah_turbo; bool ah_calibration; - bool ah_running; bool ah_single_chip; + bool ah_aes_support; bool ah_combined_mic; + enum ath5k_version ah_version; + enum ath5k_radio ah_radio; + u32 ah_phy; u32 ah_mac_srev; u16 ah_mac_version; u16 ah_mac_revision; @@ -1044,13 +1051,6 @@ struct ath5k_hw { u16 ah_radio_5ghz_revision; u16 ah_radio_2ghz_revision; - enum ath5k_version ah_version; - enum ath5k_radio ah_radio; - u32 ah_phy; - - bool ah_5ghz; - bool ah_2ghz; - #define ah_modes ah_capabilities.cap_mode #define ah_ee_version ah_capabilities.cap_eeprom.ee_version @@ -1058,7 +1058,6 @@ struct ath5k_hw { u32 ah_aifs; u32 ah_cw_min; u32 ah_cw_max; - bool ah_software_retry; u32 ah_limit_tx_retries; /* Antenna Control */ @@ -1066,6 +1065,7 @@ struct ath5k_hw { u8 ah_ant_mode; u8 ah_tx_ant; u8 ah_def_ant; + bool ah_software_retry; u8 ah_sta_id[ETH_ALEN]; @@ -1075,10 +1075,8 @@ struct ath5k_hw { u8 ah_bssid[ETH_ALEN]; u8 ah_bssid_mask[ETH_ALEN]; - u32 ah_gpio[AR5K_MAX_GPIO]; int ah_gpio_npins; - struct ath_regulatory ah_regulatory; struct ath5k_capabilities ah_capabilities; struct ath5k_txq_info ah_txq[AR5K_NUM_TX_QUEUES]; @@ -1130,6 +1128,15 @@ struct ath5k_hw { /* noise floor from last periodic calibration */ s32 ah_noise_floor; + /* Calibration timestamp */ + unsigned long ah_cal_tstamp; + + /* Calibration interval (secs) */ + u8 ah_cal_intval; + + /* Software interrupt mask */ + u8 ah_swi_mask; + /* * Function pointers */ @@ -1153,7 +1160,7 @@ struct ath5k_hw { */ /* Attach/Detach Functions */ -extern struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version); +extern struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc); extern void ath5k_hw_detach(struct ath5k_hw *ah); /* LED functions */ @@ -1164,6 +1171,7 @@ extern void ath5k_unregister_leds(struct ath5k_softc *sc); /* Reset Functions */ extern int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial); +extern int ath5k_hw_on_hold(struct ath5k_hw *ah); extern int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, struct ieee80211_channel *channel, bool change_channel); /* Power management functions */ extern int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, bool set_chip, u16 sleep_duration); @@ -1282,6 +1290,7 @@ extern int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *chann /* PHY calibration */ extern int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel); extern int ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq); +extern void ath5k_hw_calibration_poll(struct ath5k_hw *ah); /* Spur mitigation */ bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, struct ieee80211_channel *channel); diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c index c41ef58393e7..71a1bd254517 100644 --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -95,17 +95,17 @@ static int ath5k_hw_post(struct ath5k_hw *ah) * ath5k_hw_attach - Check if hw is supported and init the needed structs * * @sc: The &struct ath5k_softc we got from the driver's attach function - * @mac_version: The mac version id (check out ath5k.h) based on pci id * * Check if the device is supported, perform a POST and initialize the needed * structs. Returns -ENOMEM if we don't have memory for the needed structs, * -ENODEV if the device is not supported or prints an error msg if something * else went wrong. */ -struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) +struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc) { struct ath5k_hw *ah; struct pci_dev *pdev = sc->pdev; + struct ath5k_eeprom_info *ee; int ret; u32 srev; @@ -135,9 +135,15 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) ah->ah_software_retry = false; /* - * Set the mac version based on the pci id + * Find the mac version */ - ah->ah_version = mac_version; + srev = ath5k_hw_reg_read(ah, AR5K_SREV); + if (srev < AR5K_SREV_AR5311) + ah->ah_version = AR5K_AR5210; + else if (srev < AR5K_SREV_AR5212) + ah->ah_version = AR5K_AR5211; + else + ah->ah_version = AR5K_AR5212; /*Fill the ath5k_hw struct with the needed functions*/ ret = ath5k_hw_init_desc_functions(ah); @@ -145,12 +151,11 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) goto err_free; /* Bring device out of sleep and reset it's units */ - ret = ath5k_hw_nic_wakeup(ah, CHANNEL_B, true); + ret = ath5k_hw_nic_wakeup(ah, 0, true); if (ret) goto err_free; /* Get MAC, PHY and RADIO revisions */ - srev = ath5k_hw_reg_read(ah, AR5K_SREV); ah->ah_mac_srev = srev; ah->ah_mac_version = AR5K_REG_MS(srev, AR5K_SREV_VER); ah->ah_mac_revision = AR5K_REG_MS(srev, AR5K_SREV_REV); @@ -253,28 +258,6 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) } /* - * Write PCI-E power save settings - */ - if ((ah->ah_version == AR5K_AR5212) && (pdev->is_pcie)) { - ath5k_hw_reg_write(ah, 0x9248fc00, AR5K_PCIE_SERDES); - ath5k_hw_reg_write(ah, 0x24924924, AR5K_PCIE_SERDES); - /* Shut off RX when elecidle is asserted */ - ath5k_hw_reg_write(ah, 0x28000039, AR5K_PCIE_SERDES); - ath5k_hw_reg_write(ah, 0x53160824, AR5K_PCIE_SERDES); - /* TODO: EEPROM work */ - ath5k_hw_reg_write(ah, 0xe5980579, AR5K_PCIE_SERDES); - /* Shut off PLL and CLKREQ active in L1 */ - ath5k_hw_reg_write(ah, 0x001defff, AR5K_PCIE_SERDES); - /* Preserce other settings */ - ath5k_hw_reg_write(ah, 0x1aaabe40, AR5K_PCIE_SERDES); - ath5k_hw_reg_write(ah, 0xbe105554, AR5K_PCIE_SERDES); - ath5k_hw_reg_write(ah, 0x000e3007, AR5K_PCIE_SERDES); - /* Reset SERDES to load new settings */ - ath5k_hw_reg_write(ah, 0x00000000, AR5K_PCIE_SERDES_RESET); - mdelay(1); - } - - /* * POST */ ret = ath5k_hw_post(ah); @@ -283,7 +266,7 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) /* Enable pci core retry fix on Hainan (5213A) and later chips */ if (srev >= AR5K_SREV_AR5213A) - ath5k_hw_reg_write(ah, AR5K_PCICFG_RETRY_FIX, AR5K_PCICFG); + AR5K_REG_ENABLE_BITS(ah, AR5K_PCICFG, AR5K_PCICFG_RETRY_FIX); /* * Get card capabilities, calibration values etc @@ -295,6 +278,40 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) goto err_free; } + /* + * Write PCI-E power save settings + */ + if ((ah->ah_version == AR5K_AR5212) && (pdev->is_pcie)) { + struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; + + ath5k_hw_reg_write(ah, 0x9248fc00, AR5K_PCIE_SERDES); + ath5k_hw_reg_write(ah, 0x24924924, AR5K_PCIE_SERDES); + + /* Shut off RX when elecidle is asserted */ + ath5k_hw_reg_write(ah, 0x28000039, AR5K_PCIE_SERDES); + ath5k_hw_reg_write(ah, 0x53160824, AR5K_PCIE_SERDES); + + /* If serdes programing is enabled, increase PCI-E + * tx power for systems with long trace from host + * to minicard connector. */ + if (ee->ee_serdes) + ath5k_hw_reg_write(ah, 0xe5980579, AR5K_PCIE_SERDES); + else + ath5k_hw_reg_write(ah, 0xf6800579, AR5K_PCIE_SERDES); + + /* Shut off PLL and CLKREQ active in L1 */ + ath5k_hw_reg_write(ah, 0x001defff, AR5K_PCIE_SERDES); + + /* Preserve other settings */ + ath5k_hw_reg_write(ah, 0x1aaabe40, AR5K_PCIE_SERDES); + ath5k_hw_reg_write(ah, 0xbe105554, AR5K_PCIE_SERDES); + ath5k_hw_reg_write(ah, 0x000e3007, AR5K_PCIE_SERDES); + + /* Reset SERDES to load new settings */ + ath5k_hw_reg_write(ah, 0x00000000, AR5K_PCIE_SERDES_RESET); + mdelay(1); + } + /* Get misc capabilities */ ret = ath5k_hw_set_capabilities(ah); if (ret) { @@ -303,6 +320,12 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) goto err_free; } + /* Crypto settings */ + ee = &ah->ah_capabilities.cap_eeprom; + ah->ah_aes_support = srev >= AR5K_SREV_AR5212_V4 && + (ee->ee_version >= AR5K_EEPROM_VERSION_5_0 && + !AR5K_EEPROM_AES_DIS(ee->ee_misc5)); + if (srev >= AR5K_SREV_AR2414) { ah->ah_combined_mic = true; AR5K_REG_ENABLE_BITS(ah, AR5K_MISC_MODE, @@ -319,6 +342,9 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc, u8 mac_version) ath5k_hw_rfgain_opt_init(ah); + /* turn on HW LEDs */ + ath5k_hw_set_ledstate(ah, AR5K_LED_INIT); + return ah; err_free: kfree(ah); diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 029c1bc7468f..9c6ab5378f6e 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -59,7 +59,7 @@ #include "reg.h" #include "debug.h" -static int ath5k_calinterval = 10; /* Calibrate PHY every 10 secs (TODO: Fixme) */ +static u8 ath5k_calinterval = 10; /* Calibrate PHY every 10 secs (TODO: Fixme) */ static int modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); @@ -84,24 +84,24 @@ MODULE_VERSION("0.6.0 (EXPERIMENTAL)"); /* Known PCI ids */ static const struct pci_device_id ath5k_pci_id_table[] = { - { PCI_VDEVICE(ATHEROS, 0x0207), .driver_data = AR5K_AR5210 }, /* 5210 early */ - { PCI_VDEVICE(ATHEROS, 0x0007), .driver_data = AR5K_AR5210 }, /* 5210 */ - { PCI_VDEVICE(ATHEROS, 0x0011), .driver_data = AR5K_AR5211 }, /* 5311 - this is on AHB bus !*/ - { PCI_VDEVICE(ATHEROS, 0x0012), .driver_data = AR5K_AR5211 }, /* 5211 */ - { PCI_VDEVICE(ATHEROS, 0x0013), .driver_data = AR5K_AR5212 }, /* 5212 */ - { PCI_VDEVICE(3COM_2, 0x0013), .driver_data = AR5K_AR5212 }, /* 3com 5212 */ - { PCI_VDEVICE(3COM, 0x0013), .driver_data = AR5K_AR5212 }, /* 3com 3CRDAG675 5212 */ - { PCI_VDEVICE(ATHEROS, 0x1014), .driver_data = AR5K_AR5212 }, /* IBM minipci 5212 */ - { PCI_VDEVICE(ATHEROS, 0x0014), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ - { PCI_VDEVICE(ATHEROS, 0x0015), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ - { PCI_VDEVICE(ATHEROS, 0x0016), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ - { PCI_VDEVICE(ATHEROS, 0x0017), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ - { PCI_VDEVICE(ATHEROS, 0x0018), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ - { PCI_VDEVICE(ATHEROS, 0x0019), .driver_data = AR5K_AR5212 }, /* 5212 combatible */ - { 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 }, /* PCI-E cards */ - { PCI_VDEVICE(ATHEROS, 0x001d), .driver_data = AR5K_AR5212 }, /* 2417 Nala */ + { PCI_VDEVICE(ATHEROS, 0x0207) }, /* 5210 early */ + { PCI_VDEVICE(ATHEROS, 0x0007) }, /* 5210 */ + { PCI_VDEVICE(ATHEROS, 0x0011) }, /* 5311 - this is on AHB bus !*/ + { PCI_VDEVICE(ATHEROS, 0x0012) }, /* 5211 */ + { PCI_VDEVICE(ATHEROS, 0x0013) }, /* 5212 */ + { PCI_VDEVICE(3COM_2, 0x0013) }, /* 3com 5212 */ + { PCI_VDEVICE(3COM, 0x0013) }, /* 3com 3CRDAG675 5212 */ + { PCI_VDEVICE(ATHEROS, 0x1014) }, /* IBM minipci 5212 */ + { PCI_VDEVICE(ATHEROS, 0x0014) }, /* 5212 combatible */ + { PCI_VDEVICE(ATHEROS, 0x0015) }, /* 5212 combatible */ + { PCI_VDEVICE(ATHEROS, 0x0016) }, /* 5212 combatible */ + { PCI_VDEVICE(ATHEROS, 0x0017) }, /* 5212 combatible */ + { PCI_VDEVICE(ATHEROS, 0x0018) }, /* 5212 combatible */ + { PCI_VDEVICE(ATHEROS, 0x0019) }, /* 5212 combatible */ + { PCI_VDEVICE(ATHEROS, 0x001a) }, /* 2413 Griffin-lite */ + { PCI_VDEVICE(ATHEROS, 0x001b) }, /* 5413 Eagle */ + { PCI_VDEVICE(ATHEROS, 0x001c) }, /* PCI-E cards */ + { PCI_VDEVICE(ATHEROS, 0x001d) }, /* 2417 Nala */ { 0 } }; MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table); @@ -218,6 +218,8 @@ static struct pci_driver ath5k_pci_driver = { * Prototypes - MAC 802.11 stack related functions */ static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ath5k_txq *txq); static int ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan); static int ath5k_reset_wake(struct ath5k_softc *sc); static int ath5k_start(struct ieee80211_hw *hw); @@ -227,10 +229,12 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, static void ath5k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf); static int ath5k_config(struct ieee80211_hw *hw, u32 changed); +static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mc_list); static void ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist); + u64 multicast); static int ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -248,6 +252,8 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes); +static void ath5k_sw_scan_start(struct ieee80211_hw *hw); +static void ath5k_sw_scan_complete(struct ieee80211_hw *hw); static const struct ieee80211_ops ath5k_hw_ops = { .tx = ath5k_tx, @@ -256,6 +262,7 @@ static const struct ieee80211_ops ath5k_hw_ops = { .add_interface = ath5k_add_interface, .remove_interface = ath5k_remove_interface, .config = ath5k_config, + .prepare_multicast = ath5k_prepare_multicast, .configure_filter = ath5k_configure_filter, .set_key = ath5k_set_key, .get_stats = ath5k_get_stats, @@ -265,6 +272,8 @@ static const struct ieee80211_ops ath5k_hw_ops = { .set_tsf = ath5k_set_tsf, .reset_tsf = ath5k_reset_tsf, .bss_info_changed = ath5k_bss_info_changed, + .sw_scan_start = ath5k_sw_scan_start, + .sw_scan_complete = ath5k_sw_scan_complete, }; /* @@ -297,7 +306,8 @@ static void ath5k_desc_free(struct ath5k_softc *sc, static int ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf); static int ath5k_txbuf_setup(struct ath5k_softc *sc, - struct ath5k_buf *bf); + struct ath5k_buf *bf, + struct ath5k_txq *txq); static inline void ath5k_txbuf_free(struct ath5k_softc *sc, struct ath5k_buf *bf) { @@ -369,7 +379,7 @@ static int ath5k_stop_hw(struct ath5k_softc *sc); static irqreturn_t ath5k_intr(int irq, void *dev_id); static void ath5k_tasklet_reset(unsigned long data); -static void ath5k_calibrate(unsigned long data); +static void ath5k_tasklet_calibrate(unsigned long data); /* * Module init/exit functions @@ -464,7 +474,7 @@ ath5k_pci_probe(struct pci_dev *pdev, * DMA to work so force a reasonable value here if it * comes up zero. */ - csz = L1_CACHE_BYTES / sizeof(u32); + csz = L1_CACHE_BYTES >> 2; pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, csz); } /* @@ -512,6 +522,7 @@ ath5k_pci_probe(struct pci_dev *pdev, /* Initialize driver private data */ SET_IEEE80211_DEV(hw, &pdev->dev); hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; @@ -536,7 +547,7 @@ ath5k_pci_probe(struct pci_dev *pdev, __set_bit(ATH_STAT_INVALID, sc->status); sc->iobase = mem; /* So we can unmap it on detach */ - sc->cachelsz = csz * sizeof(u32); /* convert to bytes */ + sc->common.cachelsz = csz << 2; /* convert to bytes */ sc->opmode = NL80211_IFTYPE_STATION; sc->bintval = 1000; mutex_init(&sc->lock); @@ -555,7 +566,7 @@ ath5k_pci_probe(struct pci_dev *pdev, } /* Initialize device */ - sc->ah = ath5k_hw_attach(sc, id->driver_data); + sc->ah = ath5k_hw_attach(sc); if (IS_ERR(sc->ah)) { ret = PTR_ERR(sc->ah); goto err_irq; @@ -666,7 +677,6 @@ ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state) ath5k_led_off(sc); - free_irq(pdev->irq, sc); pci_save_state(pdev); pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); @@ -694,18 +704,8 @@ ath5k_pci_resume(struct pci_dev *pdev) */ pci_write_config_byte(pdev, 0x41, 0); - err = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc); - if (err) { - ATH5K_ERR(sc, "request_irq failed\n"); - goto err_no_irq; - } - ath5k_led_enable(sc); return 0; - -err_no_irq: - pci_disable_device(pdev); - return err; } #endif /* CONFIG_PM */ @@ -718,9 +718,9 @@ static int ath5k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *re { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath5k_softc *sc = hw->priv; - struct ath_regulatory *reg = &sc->ah->ah_regulatory; + struct ath_regulatory *regulatory = &sc->common.regulatory; - return ath_reg_notifier_apply(wiphy, request, reg); + return ath_reg_notifier_apply(wiphy, request, regulatory); } static int @@ -728,6 +728,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) { struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; + struct ath_regulatory *regulatory = &sc->common.regulatory; u8 mac[ETH_ALEN] = {}; int ret; @@ -785,19 +786,25 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) goto err_desc; } sc->bhalq = ret; + sc->cabq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_CAB, 0); + if (IS_ERR(sc->cabq)) { + ATH5K_ERR(sc, "can't setup cab queue\n"); + ret = PTR_ERR(sc->cabq); + goto err_bhal; + } sc->txq = ath5k_txq_setup(sc, AR5K_TX_QUEUE_DATA, AR5K_WME_AC_BK); if (IS_ERR(sc->txq)) { ATH5K_ERR(sc, "can't setup xmit queue\n"); ret = PTR_ERR(sc->txq); - goto err_bhal; + goto err_queues; } tasklet_init(&sc->rxtq, ath5k_tasklet_rx, (unsigned long)sc); tasklet_init(&sc->txtq, ath5k_tasklet_tx, (unsigned long)sc); tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc); + tasklet_init(&sc->calib, ath5k_tasklet_calibrate, (unsigned long)sc); tasklet_init(&sc->beacontq, ath5k_tasklet_beacon, (unsigned long)sc); - setup_timer(&sc->calib_tim, ath5k_calibrate, (unsigned long)sc); ret = ath5k_eeprom_read_mac(ah, mac); if (ret) { @@ -811,9 +818,8 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) memset(sc->bssidmask, 0xff, ETH_ALEN); ath5k_hw_set_bssid_mask(sc->ah, sc->bssidmask); - ah->ah_regulatory.current_rd = - ah->ah_capabilities.cap_eeprom.ee_regdomain; - ret = ath_regd_init(&ah->ah_regulatory, hw->wiphy, ath5k_reg_notifier); + regulatory->current_rd = ah->ah_capabilities.cap_eeprom.ee_regdomain; + ret = ath_regd_init(regulatory, hw->wiphy, ath5k_reg_notifier); if (ret) { ATH5K_ERR(sc, "can't initialize regulatory system\n"); goto err_queues; @@ -825,8 +831,8 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw) goto err_queues; } - if (!ath_is_world_regd(&sc->ah->ah_regulatory)) - regulatory_hint(hw->wiphy, sc->ah->ah_regulatory.alpha2); + if (!ath_is_world_regd(regulatory)) + regulatory_hint(hw->wiphy, regulatory->alpha2); ath5k_init_leds(sc); @@ -1068,10 +1074,9 @@ ath5k_setup_bands(struct ieee80211_hw *hw) } /* - * 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 - * ath5k_init. + * Set/change channels. We always reset the chip. + * To accomplish this we must first cleanup any pending DMA, + * then restart stuff after a la ath5k_init. * * Called with sc->lock. */ @@ -1081,19 +1086,13 @@ ath5k_chan_set(struct ath5k_softc *sc, struct ieee80211_channel *chan) ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "(%u MHz) -> (%u MHz)\n", sc->curchan->center_freq, chan->center_freq); - if (chan->center_freq != sc->curchan->center_freq || - chan->hw_value != sc->curchan->hw_value) { - - /* - * 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. - */ - return ath5k_reset(sc, chan); - } - - return 0; + /* + * 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. + */ + return ath5k_reset(sc, chan); } static void @@ -1114,6 +1113,8 @@ ath5k_mode_setup(struct ath5k_softc *sc) struct ath5k_hw *ah = sc->ah; u32 rfilt; + ah->ah_op_mode = sc->opmode; + /* configure rx filter */ rfilt = sc->filter_flags; ath5k_hw_set_rx_filter(ah, rfilt); @@ -1153,27 +1154,20 @@ static struct sk_buff *ath5k_rx_skb_alloc(struct ath5k_softc *sc, dma_addr_t *skb_addr) { struct sk_buff *skb; - unsigned int off; /* * Allocate buffer with headroom_needed space for the * fake physical layer header at the start. */ - skb = dev_alloc_skb(sc->rxbufsize + sc->cachelsz - 1); + skb = ath_rxbuf_alloc(&sc->common, + sc->rxbufsize + sc->common.cachelsz - 1, + GFP_ATOMIC); if (!skb) { ATH5K_ERR(sc, "can't alloc skbuff of size %u\n", - sc->rxbufsize + sc->cachelsz - 1); + sc->rxbufsize + sc->common.cachelsz - 1); return NULL; } - /* - * Cache-line-align. This is important (for the - * 5210 at least) as not doing so causes bogus data - * in rx'd frames. - */ - off = ((unsigned long)skb->data) % sc->cachelsz; - if (off != 0) - skb_reserve(skb, sc->cachelsz - off); *skb_addr = pci_map_single(sc->pdev, skb->data, sc->rxbufsize, PCI_DMA_FROMDEVICE); @@ -1228,10 +1222,10 @@ ath5k_rxbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) } static int -ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf) +ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf, + struct ath5k_txq *txq) { struct ath5k_hw *ah = sc->ah; - struct ath5k_txq *txq = sc->txq; struct ath5k_desc *ds = bf->desc; struct sk_buff *skb = bf->skb; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1615,10 +1609,10 @@ ath5k_rx_start(struct ath5k_softc *sc) struct ath5k_buf *bf; int ret; - sc->rxbufsize = roundup(IEEE80211_MAX_LEN, sc->cachelsz); + sc->rxbufsize = roundup(IEEE80211_MAX_LEN, sc->common.cachelsz); ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "cachelsz %u rxbufsize %u\n", - sc->cachelsz, sc->rxbufsize); + sc->common.cachelsz, sc->rxbufsize); spin_lock_bh(&sc->rxbuflock); sc->rxlink = NULL; @@ -1747,7 +1741,7 @@ ath5k_check_ibss_tsf(struct ath5k_softc *sc, struct sk_buff *skb, static void ath5k_tasklet_rx(unsigned long data) { - struct ieee80211_rx_status rxs = {}; + struct ieee80211_rx_status *rxs; struct ath5k_rx_status rs = {}; struct sk_buff *skb, *next_skb; dma_addr_t next_skb_addr; @@ -1757,6 +1751,7 @@ ath5k_tasklet_rx(unsigned long data) int ret; int hdrlen; int padsize; + int rx_flag; spin_lock(&sc->rxbuflock); if (list_empty(&sc->rxbuf)) { @@ -1764,7 +1759,7 @@ ath5k_tasklet_rx(unsigned long data) goto unlock; } do { - rxs.flag = 0; + rx_flag = 0; bf = list_first_entry(&sc->rxbuf, struct ath5k_buf, list); BUG_ON(bf->skb == NULL); @@ -1808,7 +1803,7 @@ ath5k_tasklet_rx(unsigned long data) goto accept; } if (rs.rs_status & AR5K_RXERR_MIC) { - rxs.flag |= RX_FLAG_MMIC_ERROR; + rx_flag |= RX_FLAG_MMIC_ERROR; goto accept; } @@ -1846,6 +1841,7 @@ accept: memmove(skb->data + padsize, skb->data, hdrlen); skb_pull(skb, padsize); } + rxs = IEEE80211_SKB_RXCB(skb); /* * always extend the mac timestamp, since this information is @@ -1867,41 +1863,41 @@ accept: * impossible to comply to that. This affects IBSS merge only * right now, so it's not too bad... */ - rxs.mactime = ath5k_extend_tsf(sc->ah, rs.rs_tstamp); - rxs.flag |= RX_FLAG_TSFT; + rxs->mactime = ath5k_extend_tsf(sc->ah, rs.rs_tstamp); + rxs->flag = rx_flag | RX_FLAG_TSFT; - rxs.freq = sc->curchan->center_freq; - rxs.band = sc->curband->band; + rxs->freq = sc->curchan->center_freq; + rxs->band = sc->curband->band; - rxs.noise = sc->ah->ah_noise_floor; - rxs.signal = rxs.noise + rs.rs_rssi; + rxs->noise = sc->ah->ah_noise_floor; + rxs->signal = rxs->noise + rs.rs_rssi; /* An rssi of 35 indicates you should be able use * 54 Mbps reliably. A more elaborate scheme can be used * here but it requires a map of SNR/throughput for each * possible mode used */ - rxs.qual = rs.rs_rssi * 100 / 35; + rxs->qual = rs.rs_rssi * 100 / 35; /* rssi can be more than 35 though, anything above that * should be considered at 100% */ - if (rxs.qual > 100) - rxs.qual = 100; + if (rxs->qual > 100) + rxs->qual = 100; - rxs.antenna = rs.rs_antenna; - rxs.rate_idx = ath5k_hw_to_driver_rix(sc, rs.rs_rate); - rxs.flag |= ath5k_rx_decrypted(sc, ds, skb, &rs); + rxs->antenna = rs.rs_antenna; + rxs->rate_idx = ath5k_hw_to_driver_rix(sc, rs.rs_rate); + rxs->flag |= ath5k_rx_decrypted(sc, ds, skb, &rs); - if (rxs.rate_idx >= 0 && rs.rs_rate == - sc->curband->bitrates[rxs.rate_idx].hw_value_short) - rxs.flag |= RX_FLAG_SHORTPRE; + if (rxs->rate_idx >= 0 && rs.rs_rate == + sc->curband->bitrates[rxs->rate_idx].hw_value_short) + rxs->flag |= RX_FLAG_SHORTPRE; ath5k_debug_dump_skb(sc, skb, "RX ", 0); /* check beacons in IBSS mode */ if (sc->opmode == NL80211_IFTYPE_ADHOC) - ath5k_check_ibss_tsf(sc, skb, &rxs); + ath5k_check_ibss_tsf(sc, skb, rxs); - __ieee80211_rx(sc->hw, skb, &rxs); + ieee80211_rx(sc->hw, skb); bf->skb = next_skb; bf->skbaddr = next_skb_addr; @@ -1994,9 +1990,12 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq) static void ath5k_tasklet_tx(unsigned long data) { + int i; struct ath5k_softc *sc = (void *)data; - ath5k_tx_processq(sc, sc->txq); + for (i=0; i < AR5K_NUM_TX_QUEUES; i++) + if (sc->txqs[i].setup && (sc->ah->ah_txq_isr & BIT(i))) + ath5k_tx_processq(sc, &sc->txqs[i]); } @@ -2078,13 +2077,6 @@ err_unmap: return ret; } -static void ath5k_beacon_disable(struct ath5k_softc *sc) -{ - sc->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA); - ath5k_hw_set_imr(sc->ah, sc->imask); - ath5k_hw_stop_tx_dma(sc->ah, sc->bhalq); -} - /* * Transmit a beacon frame at SWBA. Dynamic updates to the * frame contents are done as needed and the slot time is @@ -2098,6 +2090,7 @@ ath5k_beacon_send(struct ath5k_softc *sc) { struct ath5k_buf *bf = sc->bbuf; struct ath5k_hw *ah = sc->ah; + struct sk_buff *skb; ATH5K_DBG_UNLIMIT(sc, ATH5K_DEBUG_BEACON, "in beacon_send\n"); @@ -2151,6 +2144,12 @@ ath5k_beacon_send(struct ath5k_softc *sc) ATH5K_DBG(sc, ATH5K_DEBUG_BEACON, "TXDP[%u] = %llx (%p)\n", sc->bhalq, (unsigned long long)bf->daddr, bf->desc); + skb = ieee80211_get_buffered_bc(sc->hw, sc->vif); + while (skb) { + ath5k_tx_queue(sc->hw, skb, sc->cabq); + skb = ieee80211_get_buffered_bc(sc->hw, sc->vif); + } + sc->bsent++; } @@ -2271,13 +2270,11 @@ ath5k_beacon_config(struct ath5k_softc *sc) struct ath5k_hw *ah = sc->ah; unsigned long flags; - ath5k_hw_set_imr(ah, 0); + spin_lock_irqsave(&sc->block, flags); sc->bmisscount = 0; sc->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA); - if (sc->opmode == NL80211_IFTYPE_ADHOC || - sc->opmode == NL80211_IFTYPE_MESH_POINT || - sc->opmode == NL80211_IFTYPE_AP) { + if (sc->enable_beacon) { /* * In IBSS mode we use a self-linked tx descriptor and let the * hardware send the beacons automatically. We have to load it @@ -2290,16 +2287,17 @@ ath5k_beacon_config(struct ath5k_softc *sc) sc->imask |= AR5K_INT_SWBA; if (sc->opmode == NL80211_IFTYPE_ADHOC) { - if (ath5k_hw_hasveol(ah)) { - spin_lock_irqsave(&sc->block, flags); + if (ath5k_hw_hasveol(ah)) ath5k_beacon_send(sc); - spin_unlock_irqrestore(&sc->block, flags); - } } else ath5k_beacon_update_timers(sc, -1); + } else { + ath5k_hw_stop_tx_dma(sc->ah, sc->bhalq); } ath5k_hw_set_imr(ah, sc->imask); + mmiowb(); + spin_unlock_irqrestore(&sc->block, flags); } static void ath5k_tasklet_beacon(unsigned long data) @@ -2363,7 +2361,7 @@ ath5k_init(struct ath5k_softc *sc) sc->curband = &sc->sbands[sc->curchan->band]; sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL | AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL | - AR5K_INT_FATAL | AR5K_INT_GLOBAL; + AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_SWI; ret = ath5k_reset(sc, NULL); if (ret) goto done; @@ -2380,8 +2378,8 @@ ath5k_init(struct ath5k_softc *sc) /* Set ack to be sent at low bit-rates */ ath5k_hw_set_ack_bitrate_high(ah, false); - mod_timer(&sc->calib_tim, round_jiffies(jiffies + - msecs_to_jiffies(ath5k_calinterval * 1000))); + /* Set PHY calibration inteval */ + ah->ah_cal_intval = ath5k_calinterval; ret = 0; done: @@ -2445,37 +2443,39 @@ ath5k_stop_hw(struct ath5k_softc *sc) ret = ath5k_stop_locked(sc); if (ret == 0 && !test_bit(ATH_STAT_INVALID, sc->status)) { /* - * Set the chip in full sleep mode. Note that we are - * careful to do this only when bringing the interface - * completely to a stop. When the chip is in this state - * it must be carefully woken up or references to - * registers in the PCI clock domain may freeze the bus - * (and system). This varies by chip and is mostly an - * issue with newer parts that go to sleep more quickly. - */ - if (sc->ah->ah_mac_srev >= 0x78) { - /* - * XXX - * don't put newer MAC revisions > 7.8 to sleep because - * of the above mentioned problems - */ - ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mac version > 7.8, " - "not putting device to sleep\n"); - } else { - ATH5K_DBG(sc, ATH5K_DEBUG_RESET, - "putting device to full sleep\n"); - ath5k_hw_set_power(sc->ah, AR5K_PM_FULL_SLEEP, true, 0); - } + * Don't set the card in full sleep mode! + * + * a) When the device is in this state it must be carefully + * woken up or references to registers in the PCI clock + * domain may freeze the bus (and system). This varies + * by chip and is mostly an issue with newer parts + * (madwifi sources mentioned srev >= 0x78) that go to + * sleep more quickly. + * + * b) On older chips full sleep results a weird behaviour + * during wakeup. I tested various cards with srev < 0x78 + * and they don't wake up after module reload, a second + * module reload is needed to bring the card up again. + * + * Until we figure out what's going on don't enable + * full chip reset on any chip (this is what Legacy HAL + * and Sam's HAL do anyway). Instead Perform a full reset + * on the device (same as initial state after attach) and + * leave it idle (keep MAC/BB on warm reset) */ + ret = ath5k_hw_on_hold(sc->ah); + + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, + "putting device to sleep\n"); } ath5k_txbuf_free(sc, sc->bbuf); mmiowb(); mutex_unlock(&sc->lock); - del_timer_sync(&sc->calib_tim); tasklet_kill(&sc->rxtq); tasklet_kill(&sc->txtq); tasklet_kill(&sc->restq); + tasklet_kill(&sc->calib); tasklet_kill(&sc->beacontq); ath5k_rfkill_hw_stop(sc->ah); @@ -2531,6 +2531,9 @@ ath5k_intr(int irq, void *dev_id) if (status & AR5K_INT_BMISS) { /* TODO */ } + if (status & AR5K_INT_SWI) { + tasklet_schedule(&sc->calib); + } if (status & AR5K_INT_MIB) { /* * These stats are also used for ANI i think @@ -2547,6 +2550,8 @@ ath5k_intr(int irq, void *dev_id) if (unlikely(!counter)) ATH5K_WARN(sc, "too many interrupts, giving up for now\n"); + ath5k_hw_calibration_poll(ah); + return IRQ_HANDLED; } @@ -2563,11 +2568,19 @@ ath5k_tasklet_reset(unsigned long data) * for temperature/environment changes. */ static void -ath5k_calibrate(unsigned long data) +ath5k_tasklet_calibrate(unsigned long data) { struct ath5k_softc *sc = (void *)data; struct ath5k_hw *ah = sc->ah; + /* Only full calibration for now */ + if (ah->ah_swi_mask != AR5K_SWI_FULL_CALIBRATION) + return; + + /* Stop queues so that calibration + * doesn't interfere with tx */ + ieee80211_stop_queues(sc->hw); + ATH5K_DBG(sc, ATH5K_DEBUG_CALIBRATE, "channel %u/%x\n", ieee80211_frequency_to_channel(sc->curchan->center_freq), sc->curchan->hw_value); @@ -2585,8 +2598,11 @@ ath5k_calibrate(unsigned long data) ieee80211_frequency_to_channel( sc->curchan->center_freq)); - mod_timer(&sc->calib_tim, round_jiffies(jiffies + - msecs_to_jiffies(ath5k_calinterval * 1000))); + ah->ah_swi_mask = 0; + + /* Wake queues */ + ieee80211_wake_queues(sc->hw); + } @@ -2598,6 +2614,14 @@ static int ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ath5k_softc *sc = hw->priv; + + return ath5k_tx_queue(hw, skb, sc->txq); +} + +static int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ath5k_txq *txq) +{ + struct ath5k_softc *sc = hw->priv; struct ath5k_buf *bf; unsigned long flags; int hdrlen; @@ -2641,7 +2665,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) bf->skb = skb; - if (ath5k_txbuf_setup(sc, bf)) { + if (ath5k_txbuf_setup(sc, bf, txq)) { bf->skb = NULL; spin_lock_irqsave(&sc->txbuflock, flags); list_add_tail(&bf->list, &sc->txbuf); @@ -2676,7 +2700,7 @@ ath5k_reset(struct ath5k_softc *sc, struct ieee80211_channel *chan) sc->curchan = chan; sc->curband = &sc->sbands[chan->band]; } - ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, true); + ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, chan != NULL); if (ret) { ATH5K_ERR(sc, "can't reset hardware (%d)\n", ret); goto err; @@ -2757,6 +2781,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, } ath5k_hw_set_lladdr(sc->ah, conf->mac_addr); + ath5k_mode_setup(sc); ret = 0; end: @@ -2776,7 +2801,6 @@ ath5k_remove_interface(struct ieee80211_hw *hw, goto end; ath5k_hw_set_lladdr(sc->ah, mac); - ath5k_beacon_disable(sc); sc->vif = NULL; end: mutex_unlock(&sc->lock); @@ -2795,9 +2819,11 @@ ath5k_config(struct ieee80211_hw *hw, u32 changed) mutex_lock(&sc->lock); - ret = ath5k_chan_set(sc, conf->channel); - if (ret < 0) - goto unlock; + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + ret = ath5k_chan_set(sc, conf->channel); + if (ret < 0) + goto unlock; + } if ((changed & IEEE80211_CONF_CHANGE_POWER) && (sc->power_level != conf->power_level)) { @@ -2831,6 +2857,37 @@ unlock: return ret; } +static u64 ath5k_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + u32 mfilt[2], val; + int i; + u8 pos; + + mfilt[0] = 0; + mfilt[1] = 1; + + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + /* calculate XOR of eight 6-bit values */ + val = get_unaligned_le32(mclist->dmi_addr + 0); + pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; + val = get_unaligned_le32(mclist->dmi_addr + 3); + pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; + pos &= 0x3f; + mfilt[pos / 32] |= (1 << (pos % 32)); + /* XXX: we might be able to just do this instead, + * but not sure, needs testing, if we do use this we'd + * neet to inform below to not reset the mcast */ + /* ath5k_hw_set_mcast_filterindex(ah, + * mclist->dmi_addr[5]); */ + mclist = mclist->next; + } + + return ((u64)(mfilt[1]) << 32) | mfilt[0]; +} + #define SUPPORTED_FIF_FLAGS \ FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | \ FIF_PLCPFAIL | FIF_CONTROL | FIF_OTHER_BSS | \ @@ -2856,16 +2913,16 @@ unlock: static void ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; - u32 mfilt[2], val, rfilt; - u8 pos; - int i; + u32 mfilt[2], rfilt; - mfilt[0] = 0; - mfilt[1] = 0; + mutex_lock(&sc->lock); + + mfilt[0] = multicast; + mfilt[1] = multicast >> 32; /* Only deal with supported flags */ changed_flags &= SUPPORTED_FIF_FLAGS; @@ -2891,24 +2948,6 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, if (*new_flags & FIF_ALLMULTI) { mfilt[0] = ~0; mfilt[1] = ~0; - } else { - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - /* calculate XOR of eight 6-bit values */ - val = get_unaligned_le32(mclist->dmi_addr + 0); - pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; - val = get_unaligned_le32(mclist->dmi_addr + 3); - pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val; - pos &= 0x3f; - mfilt[pos / 32] |= (1 << (pos % 32)); - /* XXX: we might be able to just do this instead, - * but not sure, needs testing, if we do use this we'd - * neet to inform below to not reset the mcast */ - /* ath5k_hw_set_mcast_filterindex(ah, - * mclist->dmi_addr[5]); */ - mclist = mclist->next; - } } /* This is the best we can do */ @@ -2932,22 +2971,25 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, /* XXX move these to mac80211, and add a beacon IFF flag to mac80211 */ - if (sc->opmode == NL80211_IFTYPE_MONITOR) - rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON | - AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM; - if (sc->opmode != NL80211_IFTYPE_STATION) - rfilt |= AR5K_RX_FILTER_PROBEREQ; - if (sc->opmode != NL80211_IFTYPE_AP && - sc->opmode != NL80211_IFTYPE_MESH_POINT && - test_bit(ATH_STAT_PROMISC, sc->status)) - rfilt |= AR5K_RX_FILTER_PROM; - if ((sc->opmode == NL80211_IFTYPE_STATION && sc->assoc) || - sc->opmode == NL80211_IFTYPE_ADHOC || - sc->opmode == NL80211_IFTYPE_AP) - rfilt |= AR5K_RX_FILTER_BEACON; - if (sc->opmode == NL80211_IFTYPE_MESH_POINT) - rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON | - AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM; + switch (sc->opmode) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_MONITOR: + rfilt |= AR5K_RX_FILTER_CONTROL | + AR5K_RX_FILTER_BEACON | + AR5K_RX_FILTER_PROBEREQ | + AR5K_RX_FILTER_PROM; + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + rfilt |= AR5K_RX_FILTER_PROBEREQ | + AR5K_RX_FILTER_BEACON; + break; + case NL80211_IFTYPE_STATION: + if (sc->assoc) + rfilt |= AR5K_RX_FILTER_BEACON; + default: + break; + } /* Set filters */ ath5k_hw_set_rx_filter(ah, rfilt); @@ -2957,6 +2999,8 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw, /* Set the cached hw filter flags, this will alter actually * be set in HW */ sc->filter_flags = rfilt; + + mutex_unlock(&sc->lock); } static int @@ -2978,6 +3022,9 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, case ALG_TKIP: break; case ALG_CCMP: + if (sc->ah->ah_aes_support) + break; + return -EOPNOTSUPP; default: WARN_ON(1); @@ -3108,25 +3155,6 @@ out: return ret; } -/* - * Update the beacon and reconfigure the beacon queues. - */ -static void -ath5k_beacon_reconfig(struct ieee80211_hw *hw, struct ieee80211_vif *vif) -{ - int ret; - unsigned long flags; - struct ath5k_softc *sc = hw->priv; - - spin_lock_irqsave(&sc->block, flags); - ret = ath5k_beacon_update(hw, vif); - spin_unlock_irqrestore(&sc->block, flags); - if (ret == 0) { - ath5k_beacon_config(sc); - mmiowb(); - } -} - static void set_beacon_filter(struct ieee80211_hw *hw, bool enable) { @@ -3149,6 +3177,7 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, { struct ath5k_softc *sc = hw->priv; struct ath5k_hw *ah = sc->ah; + unsigned long flags; mutex_lock(&sc->lock); if (WARN_ON(sc->vif != vif)) @@ -3170,15 +3199,37 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, sc->assoc = bss_conf->assoc; if (sc->opmode == NL80211_IFTYPE_STATION) set_beacon_filter(hw, sc->assoc); + ath5k_hw_set_ledstate(sc->ah, sc->assoc ? + AR5K_LED_ASSOC : AR5K_LED_INIT); } - if (changes & BSS_CHANGED_BEACON && - (vif->type == NL80211_IFTYPE_ADHOC || - vif->type == NL80211_IFTYPE_MESH_POINT || - vif->type == NL80211_IFTYPE_AP)) { - ath5k_beacon_reconfig(hw, vif); + if (changes & BSS_CHANGED_BEACON) { + spin_lock_irqsave(&sc->block, flags); + ath5k_beacon_update(hw, vif); + spin_unlock_irqrestore(&sc->block, flags); } + if (changes & BSS_CHANGED_BEACON_ENABLED) + sc->enable_beacon = bss_conf->enable_beacon; + + if (changes & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED | + BSS_CHANGED_BEACON_INT)) + ath5k_beacon_config(sc); + unlock: mutex_unlock(&sc->lock); } + +static void ath5k_sw_scan_start(struct ieee80211_hw *hw) +{ + struct ath5k_softc *sc = hw->priv; + if (!sc->assoc) + ath5k_hw_set_ledstate(sc->ah, AR5K_LED_SCAN); +} + +static void ath5k_sw_scan_complete(struct ieee80211_hw *hw) +{ + struct ath5k_softc *sc = hw->priv; + ath5k_hw_set_ledstate(sc->ah, sc->assoc ? + AR5K_LED_ASSOC : AR5K_LED_INIT); +} diff --git a/drivers/net/wireless/ath/ath5k/base.h b/drivers/net/wireless/ath/ath5k/base.h index f9b7f2f819b7..a28c42f32c9d 100644 --- a/drivers/net/wireless/ath/ath5k/base.h +++ b/drivers/net/wireless/ath/ath5k/base.h @@ -51,6 +51,9 @@ #include "ath5k.h" #include "debug.h" +#include "../regd.h" +#include "../ath.h" + #define ATH_RXBUF 40 /* number of RX buffers */ #define ATH_TXBUF 200 /* number of TX buffers */ #define ATH_BCBUF 1 /* number of beacon buffers */ @@ -112,10 +115,10 @@ struct ath5k_rfkill { * associated with an instance of a device */ struct ath5k_softc { struct pci_dev *pdev; /* for dma mapping */ + struct ath_common common; void __iomem *iobase; /* address of the device */ struct mutex lock; /* dev-level lock */ - /* FIXME: how many does it really need? */ - struct ieee80211_tx_queue_stats tx_stats[16]; + struct ieee80211_tx_queue_stats tx_stats[AR5K_NUM_TX_QUEUES]; struct ieee80211_low_level_stats ll_stats; struct ieee80211_hw *hw; /* IEEE 802.11 common */ struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; @@ -135,7 +138,6 @@ struct ath5k_softc { struct ath5k_desc *desc; /* TX/RX descriptors */ dma_addr_t desc_daddr; /* DMA (physical) address */ size_t desc_len; /* size of TX/RX descriptors */ - u16 cachelsz; /* cache line size */ DECLARE_BITMAP(status, 5); #define ATH_STAT_INVALID 0 /* disable hardware accesses */ @@ -171,14 +173,15 @@ struct ath5k_softc { struct list_head txbuf; /* transmit buffer */ spinlock_t txbuflock; unsigned int txbuf_len; /* buf count in txbuf list */ - struct ath5k_txq txqs[2]; /* beacon and tx */ - - struct ath5k_txq *txq; /* beacon and tx*/ + struct ath5k_txq txqs[AR5K_NUM_TX_QUEUES]; /* tx queues */ + struct ath5k_txq *txq; /* main tx queue */ struct tasklet_struct txtq; /* tx intr tasklet */ struct ath5k_led tx_led; /* tx led */ struct ath5k_rfkill rf_kill; + struct tasklet_struct calib; /* calibration tasklet */ + spinlock_t block; /* protects beacon */ struct tasklet_struct beacontq; /* beacon intr tasklet */ struct ath5k_buf *bbuf; /* beacon buffer */ @@ -187,10 +190,11 @@ struct ath5k_softc { bintval, /* beacon interval in TU */ bsent; unsigned int nexttbtt; /* next beacon time in TU */ + struct ath5k_txq *cabq; /* content after beacon */ - struct timer_list calib_tim; /* calibration timer */ int power_level; /* Requested tx power in dbm */ bool assoc; /* assocate state */ + bool enable_beacon; /* true if beacons are on */ }; #define ath5k_hw_hasbssidmask(_ah) \ @@ -198,4 +202,15 @@ struct ath5k_softc { #define ath5k_hw_hasveol(_ah) \ (ath5k_hw_get_capability(_ah, AR5K_CAP_VEOL, 0, NULL) == 0) +static inline struct ath_common *ath5k_hw_common(struct ath5k_hw *ah) +{ + return &ah->ah_sc->common; +} + +static inline struct ath_regulatory *ath5k_hw_regulatory(struct ath5k_hw *ah) +{ + return &(ath5k_hw_common(ah)->regulatory); + +} + #endif diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index 4904a07e4b59..747508c15d34 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -380,13 +380,15 @@ ath5k_debug_init_device(struct ath5k_softc *sc) sc->debug.debugfs_phydir = debugfs_create_dir(wiphy_name(sc->hw->wiphy), ath5k_global_debugfs); - sc->debug.debugfs_debug = debugfs_create_file("debug", S_IWUSR | S_IRUGO, + sc->debug.debugfs_debug = debugfs_create_file("debug", + S_IWUSR | S_IRUSR, sc->debug.debugfs_phydir, sc, &fops_debug); - sc->debug.debugfs_registers = debugfs_create_file("registers", S_IRUGO, + sc->debug.debugfs_registers = debugfs_create_file("registers", S_IRUSR, sc->debug.debugfs_phydir, sc, &fops_registers); - sc->debug.debugfs_beacon = debugfs_create_file("beacon", S_IWUSR | S_IRUGO, + sc->debug.debugfs_beacon = debugfs_create_file("beacon", + S_IWUSR | S_IRUSR, sc->debug.debugfs_phydir, sc, &fops_beacon); sc->debug.debugfs_reset = debugfs_create_file("reset", S_IWUSR, diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c index c56b494d417a..644962adda97 100644 --- a/drivers/net/wireless/ath/ath5k/eeprom.c +++ b/drivers/net/wireless/ath/ath5k/eeprom.c @@ -167,6 +167,16 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah) ee->ee_rfkill_pin = (u8) AR5K_REG_MS(val, AR5K_EEPROM_RFKILL_GPIO_SEL); ee->ee_rfkill_pol = val & AR5K_EEPROM_RFKILL_POLARITY ? true : false; + /* Check if PCIE_OFFSET points to PCIE_SERDES_SECTION + * and enable serdes programming if needed. + * + * XXX: Serdes values seem to be fixed so + * no need to read them here, we write them + * during ath5k_hw_attach */ + AR5K_EEPROM_READ(AR5K_EEPROM_PCIE_OFFSET, val); + ee->ee_serdes = (val == AR5K_EEPROM_PCIE_SERDES_SECTION) ? + true : false; + return 0; } @@ -404,27 +414,11 @@ static int ath5k_eeprom_read_modes(struct ath5k_hw *ah, u32 *offset, break; } -done: - /* return new offset */ - *offset = o; - - return 0; -} - -/* - * Read turbo mode information on newer EEPROM versions - */ -static int -ath5k_eeprom_read_turbo_modes(struct ath5k_hw *ah, - u32 *offset, unsigned int mode) -{ - struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; - u32 o = *offset; - u16 val; - int ret; - + /* + * Read turbo mode information on newer EEPROM versions + */ if (ee->ee_version < AR5K_EEPROM_VERSION_5_0) - return 0; + goto done; switch (mode){ case AR5K_EEPROM_MODE_11A: @@ -458,6 +452,7 @@ ath5k_eeprom_read_turbo_modes(struct ath5k_hw *ah, break; } +done: /* return new offset */ *offset = o; @@ -494,10 +489,6 @@ ath5k_eeprom_init_modes(struct ath5k_hw *ah) ret = ath5k_eeprom_read_modes(ah, &offset, mode); if (ret) return ret; - - ret = ath5k_eeprom_read_turbo_modes(ah, &offset, mode); - if (ret) - return ret; } /* override for older eeprom versions for better performance */ diff --git a/drivers/net/wireless/ath/ath5k/eeprom.h b/drivers/net/wireless/ath/ath5k/eeprom.h index 64be73a5edae..0123f3521a0b 100644 --- a/drivers/net/wireless/ath/ath5k/eeprom.h +++ b/drivers/net/wireless/ath/ath5k/eeprom.h @@ -19,6 +19,9 @@ /* * Common ar5xxx EEPROM data offsets (set these on AR5K_EEPROM_BASE) */ +#define AR5K_EEPROM_PCIE_OFFSET 0x02 /* Contains offset to PCI-E infos */ +#define AR5K_EEPROM_PCIE_SERDES_SECTION 0x40 /* PCIE_OFFSET points here when + * SERDES infos are present */ #define AR5K_EEPROM_MAGIC 0x003d /* EEPROM Magic number */ #define AR5K_EEPROM_MAGIC_VALUE 0x5aa5 /* Default - found on EEPROM */ #define AR5K_EEPROM_MAGIC_5212 0x0000145c /* 5212 */ @@ -391,6 +394,7 @@ struct ath5k_eeprom_info { u8 ee_rfkill_pin; bool ee_rfkill_pol; bool ee_is_hb63; + bool ee_serdes; u16 ee_misc0; u16 ee_misc1; u16 ee_misc2; diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c index 876725f08b6c..b767c3b67b24 100644 --- a/drivers/net/wireless/ath/ath5k/led.c +++ b/drivers/net/wireless/ath/ath5k/led.c @@ -69,6 +69,8 @@ static const struct pci_device_id ath5k_led_devices[] = { { ATH_SDEVICE(PCI_VENDOR_ID_AZWAVE, 0x1026), ATH_LED(3, 0) }, /* IBM ThinkPad AR5BXB6 (legovini@spiro.fisica.unipd.it) */ { ATH_SDEVICE(PCI_VENDOR_ID_IBM, 0x058a), ATH_LED(1, 0) }, + /* HP Compaq C700 (nitrousnrg@gmail.com) */ + { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 1) }, /* IBM-specific AR5212 (all others) */ { PCI_VDEVICE(ATHEROS, PCI_DEVICE_ID_ATHEROS_AR5212_IBM), ATH_LED(0, 0) }, { } diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index a876ca8d69ef..1a039f2bd732 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -740,13 +740,22 @@ int ath5k_hw_rfregs_init(struct ath5k_hw *ah, struct ieee80211_channel *channel, AR5K_RF_XPD_GAIN, true); } else { - /* TODO: Set high and low gain bits */ - ath5k_hw_rfb_op(ah, rf_regs, - ee->ee_x_gain[ee_mode], + u8 *pdg_curve_to_idx = ee->ee_pdc_to_idx[ee_mode]; + if (ee->ee_pd_gains[ee_mode] > 1) { + ath5k_hw_rfb_op(ah, rf_regs, + pdg_curve_to_idx[0], AR5K_RF_PD_GAIN_LO, true); - ath5k_hw_rfb_op(ah, rf_regs, - ee->ee_x_gain[ee_mode], + ath5k_hw_rfb_op(ah, rf_regs, + pdg_curve_to_idx[1], AR5K_RF_PD_GAIN_HI, true); + } else { + ath5k_hw_rfb_op(ah, rf_regs, + pdg_curve_to_idx[0], + AR5K_RF_PD_GAIN_LO, true); + ath5k_hw_rfb_op(ah, rf_regs, + pdg_curve_to_idx[0], + AR5K_RF_PD_GAIN_HI, true); + } /* Lower synth voltage on Rev 2 */ ath5k_hw_rfb_op(ah, rf_regs, 2, @@ -1085,8 +1094,7 @@ int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) AR5K_PHY_CCKTXCTL_WORLD); } - ah->ah_current_channel.center_freq = channel->center_freq; - ah->ah_current_channel.hw_value = channel->hw_value; + ah->ah_current_channel = channel; ah->ah_turbo = channel->hw_value == CHANNEL_T ? true : false; return 0; @@ -1096,6 +1104,29 @@ int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel) PHY calibration \*****************/ +void +ath5k_hw_calibration_poll(struct ath5k_hw *ah) +{ + /* Calibration interval in jiffies */ + unsigned long cal_intval; + + cal_intval = msecs_to_jiffies(ah->ah_cal_intval * 1000); + + /* Initialize timestamp if needed */ + if (!ah->ah_cal_tstamp) + ah->ah_cal_tstamp = jiffies; + + /* For now we always do full calibration + * Mark software interrupt mask and fire software + * interrupt (bit gets auto-cleared) */ + if (time_is_before_eq_jiffies(ah->ah_cal_tstamp + cal_intval)) { + ah->ah_cal_tstamp = jiffies; + ah->ah_swi_mask = AR5K_SWI_FULL_CALIBRATION; + AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); + } + +} + /** * ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration * @@ -1731,7 +1762,7 @@ ath5k_hw_set_fast_div(struct ath5k_hw *ah, u8 ee_mode, bool enable) void ath5k_hw_set_antenna_mode(struct ath5k_hw *ah, u8 ant_mode) { - struct ieee80211_channel *channel = &ah->ah_current_channel; + struct ieee80211_channel *channel = ah->ah_current_channel; bool use_def_for_tx, update_def_on_tx, use_def_for_rts, fast_div; bool use_def_for_sg; u8 def_ant, tx_ant, ee_mode; @@ -1897,8 +1928,9 @@ ath5k_get_linear_pcdac_min(const u8 *stepL, const u8 *stepR, s16 min_pwrL, min_pwrR; s16 pwr_i; - if (WARN_ON(stepL[0] == stepL[1] || stepR[0] == stepR[1])) - return 0; + /* Some vendors write the same pcdac value twice !!! */ + if (stepL[0] == stepL[1] || stepR[0] == stepR[1]) + return max(pwrL[0], pwrR[0]); if (pwrL[0] == pwrL[1]) min_pwrL = pwrL[0]; @@ -2166,6 +2198,7 @@ static void ath5k_get_max_ctl_power(struct ath5k_hw *ah, struct ieee80211_channel *channel) { + struct ath_regulatory *regulatory = ath5k_hw_regulatory(ah); struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; struct ath5k_edge_power *rep = ee->ee_ctl_pwr; u8 *ctl_val = ee->ee_ctl; @@ -2176,7 +2209,7 @@ ath5k_get_max_ctl_power(struct ath5k_hw *ah, u8 ctl_idx = 0xFF; u32 target = channel->center_freq; - ctl_mode = ath_regd_get_band_ctl(&ah->ah_regulatory, channel->band); + ctl_mode = ath_regd_get_band_ctl(regulatory, channel->band); switch (channel->hw_value & CHANNEL_MODES) { case CHANNEL_A: @@ -3011,7 +3044,7 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower) { /*Just a try M.F.*/ - struct ieee80211_channel *channel = &ah->ah_current_channel; + struct ieee80211_channel *channel = ah->ah_current_channel; u8 ee_mode; ATH5K_TRACE(ah->ah_sc); diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c index 73407b3f53ef..eeebb9aef206 100644 --- a/drivers/net/wireless/ath/ath5k/qcu.c +++ b/drivers/net/wireless/ath/ath5k/qcu.c @@ -362,7 +362,7 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) } if (tq->tqi_ready_time && - (tq->tqi_type != AR5K_TX_QUEUE_ID_CAB)) + (tq->tqi_type != AR5K_TX_QUEUE_CAB)) ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_ready_time, AR5K_QCU_RDYTIMECFG_INTVAL) | AR5K_QCU_RDYTIMECFG_ENABLE, @@ -411,7 +411,6 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), AR5K_QCU_MISC_FRSHED_BCN_SENT_GT | AR5K_QCU_MISC_CBREXP_DIS | - AR5K_QCU_MISC_RDY_VEOL_POLICY | AR5K_QCU_MISC_CBREXP_BCN_DIS); ath5k_hw_reg_write(ah, ((AR5K_TUNE_BEACON_INTERVAL - diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h index 6809b54a2ad7..debad07d9900 100644 --- a/drivers/net/wireless/ath/ath5k/reg.h +++ b/drivers/net/wireless/ath/ath5k/reg.h @@ -339,9 +339,9 @@ #define AR5K_SISR2 0x008c /* Register Address [5211+] */ #define AR5K_SISR2_QCU_TXURN 0x000003ff /* Mask for QCU_TXURN */ #define AR5K_SISR2_QCU_TXURN_S 0 -#define AR5K_SISR2_MCABT 0x00100000 /* Master Cycle Abort */ -#define AR5K_SISR2_SSERR 0x00200000 /* Signaled System Error */ -#define AR5K_SISR2_DPERR 0x00400000 /* Bus parity error */ +#define AR5K_SISR2_MCABT 0x00010000 /* Master Cycle Abort */ +#define AR5K_SISR2_SSERR 0x00020000 /* Signaled System Error */ +#define AR5K_SISR2_DPERR 0x00040000 /* Bus parity error */ #define AR5K_SISR2_TIM 0x01000000 /* [5212+] */ #define AR5K_SISR2_CAB_END 0x02000000 /* [5212+] */ #define AR5K_SISR2_DTIM_SYNC 0x04000000 /* DTIM sync lost [5212+] */ @@ -430,9 +430,9 @@ #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 /* Master Cycle Abort */ -#define AR5K_SIMR2_SSERR 0x00200000 /* Signaled System Error */ -#define AR5K_SIMR2_DPERR 0x00400000 /* Bus parity error */ +#define AR5K_SIMR2_MCABT 0x00010000 /* Master Cycle Abort */ +#define AR5K_SIMR2_SSERR 0x00020000 /* Signaled System Error */ +#define AR5K_SIMR2_DPERR 0x00040000 /* Bus parity error */ #define AR5K_SIMR2_TIM 0x01000000 /* [5212+] */ #define AR5K_SIMR2_CAB_END 0x02000000 /* [5212+] */ #define AR5K_SIMR2_DTIM_SYNC 0x04000000 /* DTIM Sync lost [5212+] */ diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index bd0a97a38d34..34e13c700849 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -258,29 +258,35 @@ int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, if (!set_chip) goto commit; - /* Preserve sleep duration */ data = ath5k_hw_reg_read(ah, AR5K_SLEEP_CTL); + + /* If card is down we 'll get 0xffff... so we + * need to clean this up before we write the register + */ if (data & 0xffc00000) data = 0; else - data = data & 0xfffcffff; + /* Preserve sleep duration etc */ + data = data & ~AR5K_SLEEP_CTL_SLE; - ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL); + ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE, + AR5K_SLEEP_CTL); udelay(15); - for (i = 50; i > 0; i--) { + for (i = 200; i > 0; i--) { /* Check if the chip did wake up */ if ((ath5k_hw_reg_read(ah, AR5K_PCICFG) & AR5K_PCICFG_SPWR_DN) == 0) break; /* Wait a bit and retry */ - udelay(200); - ath5k_hw_reg_write(ah, data, AR5K_SLEEP_CTL); + udelay(50); + ath5k_hw_reg_write(ah, data | AR5K_SLEEP_CTL_SLE_WAKE, + AR5K_SLEEP_CTL); } /* Fail if the chip didn't wake up */ - if (i <= 0) + if (i == 0) return -EIO; break; @@ -290,13 +296,70 @@ int ath5k_hw_set_power(struct ath5k_hw *ah, enum ath5k_power_mode mode, } commit: - ah->ah_power_mode = mode; ath5k_hw_reg_write(ah, staid, AR5K_STA_ID1); return 0; } /* + * Put device on hold + * + * Put MAC and Baseband on warm reset and + * keep that state (don't clean sleep control + * register). After this MAC and Baseband are + * disabled and a full reset is needed to come + * back. This way we save as much power as possible + * without puting the card on full sleep. + */ +int ath5k_hw_on_hold(struct ath5k_hw *ah) +{ + struct pci_dev *pdev = ah->ah_sc->pdev; + u32 bus_flags; + int ret; + + /* Make sure device is awake */ + ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); + if (ret) { + ATH5K_ERR(ah->ah_sc, "failed to wakeup the MAC Chip\n"); + return ret; + } + + /* + * Put chipset on warm reset... + * + * Note: puting PCI core on warm reset on PCI-E cards + * results card to hang and always return 0xffff... so + * we ingore that flag for PCI-E cards. On PCI cards + * this flag gets cleared after 64 PCI clocks. + */ + bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI; + + if (ah->ah_version == AR5K_AR5210) { + ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | + AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA | + AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI); + mdelay(2); + } else { + ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | + AR5K_RESET_CTL_BASEBAND | bus_flags); + } + + if (ret) { + ATH5K_ERR(ah->ah_sc, "failed to put device on warm reset\n"); + return -EIO; + } + + /* ...wakeup again!*/ + ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); + if (ret) { + ATH5K_ERR(ah->ah_sc, "failed to put device on hold\n"); + return ret; + } + + return ret; +} + +/* * Bring up MAC + PHY Chips and program PLL * TODO: Half/Quarter rate support */ @@ -319,6 +382,50 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial) return ret; } + /* + * Put chipset on warm reset... + * + * Note: puting PCI core on warm reset on PCI-E cards + * results card to hang and always return 0xffff... so + * we ingore that flag for PCI-E cards. On PCI cards + * this flag gets cleared after 64 PCI clocks. + */ + bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI; + + if (ah->ah_version == AR5K_AR5210) { + ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | + AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA | + AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI); + mdelay(2); + } else { + ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | + AR5K_RESET_CTL_BASEBAND | bus_flags); + } + + if (ret) { + ATH5K_ERR(ah->ah_sc, "failed to reset the MAC Chip\n"); + return -EIO; + } + + /* ...wakeup again!...*/ + ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); + if (ret) { + ATH5K_ERR(ah->ah_sc, "failed to resume the MAC Chip\n"); + return ret; + } + + /* ...clear reset control register and pull device out of + * warm reset */ + if (ath5k_hw_nic_reset(ah, 0)) { + ATH5K_ERR(ah->ah_sc, "failed to warm reset the MAC Chip\n"); + return -EIO; + } + + /* On initialization skip PLL programming since we don't have + * a channel / mode set yet */ + if (initial) + return 0; + if (ah->ah_version != AR5K_AR5210) { /* * Get channel mode flags @@ -384,39 +491,6 @@ int ath5k_hw_nic_wakeup(struct ath5k_hw *ah, int flags, bool initial) AR5K_PHY_TURBO); } - /* reseting PCI on PCI-E cards results card to hang - * and always return 0xffff... so we ingore that flag - * for PCI-E cards */ - bus_flags = (pdev->is_pcie) ? 0 : AR5K_RESET_CTL_PCI; - - /* Reset chipset */ - if (ah->ah_version == AR5K_AR5210) { - ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | - AR5K_RESET_CTL_MAC | AR5K_RESET_CTL_DMA | - AR5K_RESET_CTL_PHY | AR5K_RESET_CTL_PCI); - mdelay(2); - } else { - ret = ath5k_hw_nic_reset(ah, AR5K_RESET_CTL_PCU | - AR5K_RESET_CTL_BASEBAND | bus_flags); - } - if (ret) { - ATH5K_ERR(ah->ah_sc, "failed to reset the MAC Chip\n"); - return -EIO; - } - - /* ...wakeup again!*/ - ret = ath5k_hw_set_power(ah, AR5K_PM_AWAKE, true, 0); - if (ret) { - ATH5K_ERR(ah->ah_sc, "failed to resume the MAC Chip\n"); - return ret; - } - - /* ...final warm reset */ - if (ath5k_hw_nic_reset(ah, 0)) { - ATH5K_ERR(ah->ah_sc, "failed to warm reset the MAC Chip\n"); - return -EIO; - } - if (ah->ah_version != AR5K_AR5210) { /* ...update PLL if needed */ diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 2d79610bce12..ef5f59c4dd80 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -1,13 +1,18 @@ config ATH9K tristate "Atheros 802.11n wireless cards support" depends on PCI && MAC80211 && WLAN_80211 - select ATH_COMMON select MAC80211_LEDS select LEDS_CLASS select NEW_LEDS ---help--- This module adds support for wireless adapters based on - Atheros IEEE 802.11n AR5008 and AR9001 family of chipsets. + Atheros IEEE 802.11n AR5008, AR9001 and AR9002 family + of chipsets. For a specific list of supported external + cards, laptops that already ship with these cards and + APs that come with these cards refer to to ath9k wiki + products page: + + http://wireless.kernel.org/en/users/Drivers/ath9k/products If you choose to build a module, it'll be called ath9k. @@ -18,6 +23,6 @@ config ATH9K_DEBUG Say Y, if you need ath9k to display debug messages. Pass the debug mask as a module parameter: - modprobe ath9k debug=0x00002000 + modprobe ath9k debug=0x00000200 - Look in ath9k/core.h for possible debug masks + Look in ath9k/debug.h for possible debug masks diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 783bc39eb2ff..ff2c9a26c10c 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -1,5 +1,8 @@ ath9k-y += hw.o \ eeprom.o \ + eeprom_def.o \ + eeprom_4k.o \ + eeprom_9287.o \ mac.o \ calib.o \ ani.o \ @@ -9,7 +12,8 @@ ath9k-y += hw.o \ recv.o \ xmit.o \ virtual.o \ - rc.o + rc.o \ + btcoex.o ath9k-$(CONFIG_PCI) += pci.o ath9k-$(CONFIG_ATHEROS_AR71XX) += ahb.o diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index 0e65c51ba176..5618fc25d52f 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -119,7 +119,7 @@ static int ath_ahb_probe(struct platform_device *pdev) sc->bus_ops = &ath_ahb_bus_ops; sc->irq = irq; - ret = ath_attach(AR5416_AR9100_DEVID, sc); + ret = ath_init_device(AR5416_AR9100_DEVID, sc); if (ret != 0) { dev_err(&pdev->dev, "failed to attach device, err=%d\n", ret); ret = -ENODEV; diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index aad259b4c197..a7cbb07988cf 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -236,36 +236,35 @@ static void ath9k_ani_restart(struct ath_hw *ah) return; aniState = ah->curani; - aniState->listenTime = 0; - if (ah->has_hw_phycounters) { - 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; - } + + if (aniState->ofdmTrigHigh > AR_PHY_COUNTMAX) { + aniState->ofdmPhyErrBase = 0; DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "Writing ofdmbase=%u cckbase=%u\n", - 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, &ah->ah_mibStats); + "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, + "Writing ofdmbase=%u cckbase=%u\n", + 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, &ah->ah_mibStats); + aniState->ofdmPhyErrCount = 0; aniState->cckPhyErrCount = 0; } @@ -530,32 +529,26 @@ void ath9k_ani_reset(struct ath_hw *ah) if (aniState->firstepLevel != 0) ath9k_hw_ani_control(ah, ATH9K_ANI_FIRSTEP_LEVEL, aniState->firstepLevel); - if (ah->has_hw_phycounters) { - 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); - } + 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); } void ath9k_hw_ani_monitor(struct ath_hw *ah, - const struct ath9k_node_stats *stats, struct ath9k_channel *chan) { struct ar5416AniState *aniState; int32_t listenTime; + u32 phyCnt1, phyCnt2; + u32 ofdmPhyErrCnt, cckPhyErrCnt; if (!DO_ANI(ah)) return; aniState = ah->curani; - ah->stats.ast_nodestats = *stats; listenTime = ath9k_hw_ani_get_listen_time(ah); if (listenTime < 0) { @@ -566,50 +559,45 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, aniState->listenTime += listenTime; - if (ah->has_hw_phycounters) { - u32 phyCnt1, phyCnt2; - u32 ofdmPhyErrCnt, cckPhyErrCnt; + ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); - ath9k_hw_update_mibstats(ah, &ah->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, - "phyCnt1 0x%x, resetting " - "counter value to 0x%x\n", - 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, - "phyCnt2 0x%x, resetting " - "counter value to 0x%x\n", - 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; + 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, + "phyCnt1 0x%x, resetting " + "counter value to 0x%x\n", + 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, + "phyCnt2 0x%x, resetting " + "counter value to 0x%x\n", + 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; - ah->stats.ast_ani_ofdmerrs += - ofdmPhyErrCnt - aniState->ofdmPhyErrCount; - aniState->ofdmPhyErrCount = ofdmPhyErrCnt; + ofdmPhyErrCnt = phyCnt1 - aniState->ofdmPhyErrBase; + ah->stats.ast_ani_ofdmerrs += + ofdmPhyErrCnt - aniState->ofdmPhyErrCount; + aniState->ofdmPhyErrCount = ofdmPhyErrCnt; - cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase; - ah->stats.ast_ani_cckerrs += - cckPhyErrCnt - aniState->cckPhyErrCount; - aniState->cckPhyErrCount = cckPhyErrCnt; - } + cckPhyErrCnt = phyCnt2 - aniState->cckPhyErrBase; + ah->stats.ast_ani_cckerrs += + cckPhyErrCnt - aniState->cckPhyErrCount; + aniState->cckPhyErrCount = cckPhyErrCnt; if (aniState->listenTime > 5 * ah->aniperiod) { if (aniState->ofdmPhyErrCount <= aniState->listenTime * @@ -632,11 +620,6 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, } } -bool ath9k_hw_phycounters(struct ath_hw *ah) -{ - return ah->has_hw_phycounters ? true : false; -} - void ath9k_enable_mib_counters(struct ath_hw *ah) { DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Enable MIB counters\n"); @@ -708,8 +691,7 @@ u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah, * any of the MIB counters overflow/trigger so don't assume we're * here because a PHY error counter triggered. */ -void ath9k_hw_procmibevent(struct ath_hw *ah, - const struct ath9k_node_stats *stats) +void ath9k_hw_procmibevent(struct ath_hw *ah) { u32 phyCnt1, phyCnt2; @@ -721,7 +703,6 @@ void ath9k_hw_procmibevent(struct ath_hw *ah, /* Clear the mib counters and save them in the stats */ ath9k_hw_update_mibstats(ah, &ah->ah_mibStats); - ah->stats.ast_nodestats = *stats; if (!DO_ANI(ah)) return; @@ -777,13 +758,11 @@ void ath9k_hw_ani_setup(struct ath_hw *ah) } } -void ath9k_hw_ani_attach(struct ath_hw *ah) +void ath9k_hw_ani_init(struct ath_hw *ah) { int i; - DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Attach ANI\n"); - - ah->has_hw_phycounters = 1; + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Initialize ANI\n"); memset(ah->ani, 0, sizeof(ah->ani)); for (i = 0; i < ARRAY_SIZE(ah->ani); i++) { @@ -799,36 +778,32 @@ void ath9k_hw_ani_attach(struct ath_hw *ah) ATH9K_ANI_CCK_WEAK_SIG_THR; ah->ani[i].spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; ah->ani[i].firstepLevel = ATH9K_ANI_FIRSTEP_LVL; - if (ah->has_hw_phycounters) { - ah->ani[i].ofdmPhyErrBase = - AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH; - ah->ani[i].cckPhyErrBase = - AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH; - } - } - if (ah->has_hw_phycounters) { - DPRINTF(ah->ah_sc, ATH_DBG_ANI, - "Setting OfdmErrBase = 0x%08x\n", - ah->ani[0].ofdmPhyErrBase); - DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n", - ah->ani[0].cckPhyErrBase); - - REG_WRITE(ah, AR_PHY_ERR_1, ah->ani[0].ofdmPhyErrBase); - REG_WRITE(ah, AR_PHY_ERR_2, ah->ani[0].cckPhyErrBase); - ath9k_enable_mib_counters(ah); + ah->ani[i].ofdmPhyErrBase = + AR_PHY_COUNTMAX - ATH9K_ANI_OFDM_TRIG_HIGH; + ah->ani[i].cckPhyErrBase = + AR_PHY_COUNTMAX - ATH9K_ANI_CCK_TRIG_HIGH; } + + DPRINTF(ah->ah_sc, ATH_DBG_ANI, + "Setting OfdmErrBase = 0x%08x\n", + ah->ani[0].ofdmPhyErrBase); + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Setting cckErrBase = 0x%08x\n", + ah->ani[0].cckPhyErrBase); + + REG_WRITE(ah, AR_PHY_ERR_1, ah->ani[0].ofdmPhyErrBase); + REG_WRITE(ah, AR_PHY_ERR_2, ah->ani[0].cckPhyErrBase); + ath9k_enable_mib_counters(ah); + ah->aniperiod = ATH9K_ANI_PERIOD; if (ah->config.enable_ani) ah->proc_phyerr |= HAL_PROCESS_ANI; } -void ath9k_hw_ani_detach(struct ath_hw *ah) +void ath9k_hw_ani_disable(struct ath_hw *ah) { - DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Detach ANI\n"); + DPRINTF(ah->ah_sc, ATH_DBG_ANI, "Disabling ANI\n"); - if (ah->has_hw_phycounters) { - ath9k_hw_disable_mib_counters(ah); - REG_WRITE(ah, AR_PHY_ERR_1, 0); - REG_WRITE(ah, AR_PHY_ERR_2, 0); - } + ath9k_hw_disable_mib_counters(ah); + REG_WRITE(ah, AR_PHY_ERR_1, 0); + REG_WRITE(ah, AR_PHY_ERR_2, 0); } diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index 08b4e7ed5ff0..4e1ab94a5153 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -18,15 +18,10 @@ #define ANI_H #define HAL_PROCESS_ANI 0x00000001 -#define ATH9K_RSSI_EP_MULTIPLIER (1<<7) #define DO_ANI(ah) (((ah)->proc_phyerr & HAL_PROCESS_ANI)) -#define HAL_EP_RND(x, mul) \ - ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) -#define BEACON_RSSI(ahp) \ - HAL_EP_RND(ahp->stats.ast_nodestats.ns_avgbrssi, \ - ATH9K_RSSI_EP_MULTIPLIER) +#define BEACON_RSSI(ahp) (ahp->stats.avgbrssi) #define ATH9K_ANI_OFDM_TRIG_HIGH 500 #define ATH9K_ANI_OFDM_TRIG_LOW 200 @@ -65,13 +60,6 @@ struct ath9k_mib_stats { u32 beacons; }; -struct ath9k_node_stats { - u32 ns_avgbrssi; - u32 ns_avgrssi; - u32 ns_avgtxrssi; - u32 ns_avgtxrate; -}; - struct ar5416AniState { struct ath9k_channel *c; u8 noiseImmunityLevel; @@ -115,24 +103,21 @@ struct ar5416Stats { u32 ast_ani_reset; u32 ast_ani_lzero; u32 ast_ani_lneg; + u32 avgbrssi; struct ath9k_mib_stats ast_mibstats; - struct ath9k_node_stats ast_nodestats; }; #define ah_mibStats stats.ast_mibstats void ath9k_ani_reset(struct ath_hw *ah); void ath9k_hw_ani_monitor(struct ath_hw *ah, - const struct ath9k_node_stats *stats, struct ath9k_channel *chan); -bool ath9k_hw_phycounters(struct ath_hw *ah); void ath9k_enable_mib_counters(struct ath_hw *ah); void ath9k_hw_disable_mib_counters(struct ath_hw *ah); u32 ath9k_hw_GetMibCycleCountsPct(struct ath_hw *ah, u32 *rxc_pcnt, u32 *rxf_pcnt, u32 *txf_pcnt); -void ath9k_hw_procmibevent(struct ath_hw *ah, - const struct ath9k_node_stats *stats); +void ath9k_hw_procmibevent(struct ath_hw *ah); void ath9k_hw_ani_setup(struct ath_hw *ah); -void ath9k_hw_ani_attach(struct ath_hw *ah); -void ath9k_hw_ani_detach(struct ath_hw *ah); +void ath9k_hw_ani_init(struct ath_hw *ah); +void ath9k_hw_ani_disable(struct ath_hw *ah); #endif /* ANI_H */ diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 5efc9345ca0d..1c68a9da22d4 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -25,6 +25,8 @@ #include "hw.h" #include "rc.h" #include "debug.h" +#include "../ath.h" +#include "btcoex.h" struct ath_node; @@ -164,7 +166,6 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, #define WME_NUM_TID 16 #define ATH_TXBUF 512 #define ATH_TXMAXTRY 13 -#define ATH_11N_TXMAXTRY 10 #define ATH_MGT_TXMAXTRY 4 #define WME_BA_BMP_SIZE 64 #define WME_MAX_BA WME_BA_BMP_SIZE @@ -191,12 +192,9 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, #define ATH_AGGR_MIN_QDEPTH 2 #define ATH_AMPDU_SUBFRAME_DEFAULT 32 #define ATH_AMPDU_LIMIT_MAX (64 * 1024 - 1) -#define ATH_AMPDU_LIMIT_DEFAULT ATH_AMPDU_LIMIT_MAX #define IEEE80211_SEQ_SEQ_SHIFT 4 #define IEEE80211_SEQ_MAX 4096 -#define IEEE80211_MIN_AMPDU_BUF 0x8 -#define IEEE80211_HTCAP_MAXRXAMPDU_FACTOR 13 #define IEEE80211_WEP_IVLEN 3 #define IEEE80211_WEP_KIDLEN 1 #define IEEE80211_WEP_CRCLEN 4 @@ -226,6 +224,8 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, #define ATH_DS_TX_BA(_ds) ((_ds)->ds_us.tx.ts_flags & ATH9K_TX_BA) #define ATH_AN_2_TID(_an, _tidno) (&(_an)->tid[(_tidno)]) +#define ATH_TX_COMPLETE_POLL_INT 1000 + enum ATH_AGGR_STATUS { ATH_AGGR_DONE, ATH_AGGR_BAW_CLOSED, @@ -239,8 +239,8 @@ struct ath_txq { spinlock_t axq_lock; u32 axq_depth; u8 axq_aggr_depth; - u32 axq_totalqueued; bool stopped; + bool axq_tx_inprogress; struct ath_buf *axq_linkbuf; /* first desc of the last descriptor that contains CTS */ @@ -272,7 +272,6 @@ struct ath_atx_tid { int sched; int paused; u8 state; - int addba_exchangeattempts; }; struct ath_atx_ac { @@ -292,12 +291,28 @@ struct ath_tx_control { #define ATH_TX_XRETRY 0x02 #define ATH_TX_BAR 0x04 +#define ATH_RSSI_LPF_LEN 10 +#define RSSI_LPF_THRESHOLD -20 +#define ATH9K_RSSI_BAD 0x80 +#define ATH_RSSI_EP_MULTIPLIER (1<<7) +#define ATH_EP_MUL(x, mul) ((x) * (mul)) +#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) +#define ATH_EP_RND(x, mul) \ + ((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul)) + struct ath_node { struct ath_softc *an_sc; struct ath_atx_tid tid[WME_NUM_TID]; struct ath_atx_ac ac[WME_NUM_AC]; u16 maxampdu; u8 mpdudensity; + int last_rssi; }; struct ath_tx { @@ -348,9 +363,9 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, void ath_tx_tasklet(struct ath_softc *sc); void ath_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb); bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno); -int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, - u16 tid, u16 *ssn); -int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); +void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, + u16 tid, u16 *ssn); +void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid); /********/ @@ -440,7 +455,8 @@ struct ath_ani { /* LED Control */ /********************/ -#define ATH_LED_PIN 1 +#define ATH_LED_PIN_DEF 1 +#define ATH_LED_PIN_9287 8 #define ATH_LED_ON_DURATION_IDLE 350 /* in msecs */ #define ATH_LED_OFF_DURATION_IDLE 250 /* in msecs */ @@ -506,6 +522,8 @@ struct ath_led { #define SC_OP_WAIT_FOR_PSPOLL_DATA BIT(17) #define SC_OP_WAIT_FOR_TX_ACK BIT(18) #define SC_OP_BEACON_SYNC BIT(19) +#define SC_OP_BTCOEX_ENABLED BIT(20) +#define SC_OP_BT_PRIORITY_DETECTED BIT(21) struct ath_bus_ops { void (*read_cachesize)(struct ath_softc *sc, int *csz); @@ -519,6 +537,8 @@ struct ath_softc { struct ieee80211_hw *hw; struct device *dev; + struct ath_common common; + spinlock_t wiphy_lock; /* spinlock to protect ath_wiphy data */ struct ath_wiphy *pri_wiphy; struct ath_wiphy **sec_wiphy; /* secondary wiphys (virtual radios); may @@ -541,6 +561,8 @@ struct ath_softc { int irq; spinlock_t sc_resetlock; spinlock_t sc_serial_rw; + spinlock_t ani_lock; + spinlock_t sc_pm_lock; struct mutex mutex; u8 curbssid[ETH_ALEN]; @@ -549,7 +571,6 @@ struct ath_softc { u32 sc_flags; /* SC_OP_* */ u16 curtxpow; u16 curaid; - u16 cachelsz; u8 nbcnvifs; u16 nvifs; u8 tx_chainmask; @@ -557,7 +578,8 @@ struct ath_softc { u32 keymax; DECLARE_BITMAP(keymap, ATH_KEYMAX); u8 splitmic; - atomic_t ps_usecount; + bool ps_enabled; + unsigned long ps_usecount; enum ath9k_int imask; enum ath9k_ht_extprotspacing ht_extprotspacing; enum ath9k_ht_macmode tx_chan_width; @@ -584,12 +606,13 @@ struct ath_softc { int beacon_interval; struct ath_ani ani; - struct ath9k_node_stats nodestats; #ifdef CONFIG_ATH9K_DEBUG struct ath9k_debug debug; #endif struct ath_bus_ops *bus_ops; struct ath_beacon_config cur_beacon_conf; + struct delayed_work tx_complete_work; + struct ath_btcoex_info btcoex_info; }; struct ath_wiphy { @@ -611,6 +634,16 @@ int ath_get_hal_qnum(u16 queue, struct ath_softc *sc); int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc); int ath_cabq_update(struct ath_softc *); +static inline struct ath_common *ath9k_hw_common(struct ath_hw *ah) +{ + return &ah->ah_sc->common; +} + +static inline struct ath_regulatory *ath9k_hw_regulatory(struct ath_hw *ah) +{ + return &(ath9k_hw_common(ah)->regulatory); +} + static inline void ath_read_cachesize(struct ath_softc *sc, int *csz) { sc->bus_ops->read_cachesize(sc, csz); @@ -625,7 +658,7 @@ extern struct ieee80211_ops ath9k_ops; irqreturn_t ath_isr(int irq, void *dev); void ath_cleanup(struct ath_softc *sc); -int ath_attach(u16 devid, struct ath_softc *sc); +int ath_init_device(u16 devid, struct ath_softc *sc); void ath_detach(struct ath_softc *sc); const char *ath_mac_bb_name(u32 mac_bb_version); const char *ath_rf_name(u16 rf_version); @@ -654,27 +687,8 @@ static inline int ath_ahb_init(void) { return 0; }; static inline void ath_ahb_exit(void) {}; #endif -static inline void ath9k_ps_wakeup(struct ath_softc *sc) -{ - if (atomic_inc_return(&sc->ps_usecount) == 1) - if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) { - sc->sc_ah->restore_mode = sc->sc_ah->power_mode; - ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); - } -} - -static inline void ath9k_ps_restore(struct ath_softc *sc) -{ - if (atomic_dec_and_test(&sc->ps_usecount)) - if ((sc->hw->conf.flags & IEEE80211_CONF_PS) && - !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | - SC_OP_WAIT_FOR_CAB | - SC_OP_WAIT_FOR_PSPOLL_DATA | - SC_OP_WAIT_FOR_TX_ACK))) - ath9k_hw_setpower(sc->sc_ah, - sc->sc_ah->restore_mode); -} - +void ath9k_ps_wakeup(struct ath_softc *sc); +void ath9k_ps_restore(struct ath_softc *sc); void ath9k_set_bssid_mask(struct ieee80211_hw *hw); int ath9k_wiphy_add(struct ath_softc *sc); @@ -690,8 +704,10 @@ void ath9k_wiphy_pause_all_forced(struct ath_softc *sc, struct ath_wiphy *selected); bool ath9k_wiphy_scanning(struct ath_softc *sc); void ath9k_wiphy_work(struct work_struct *work); +bool ath9k_all_wiphys_idle(struct ath_softc *sc); void ath9k_iowrite32(struct ath_hw *ah, u32 reg_offset, u32 val); unsigned int ath9k_ioread32(struct ath_hw *ah, u32 reg_offset); +int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype); #endif /* ATH9K_H */ diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 3639a2e6987d..45c4ea57616b 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -674,13 +674,6 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc, intval = conf->beacon_interval & ATH9K_BEACON_PERIOD; - /* - * It looks like mac80211 may end up using beacon interval of zero in - * some cases (at least for mesh point). Avoid getting into an - * infinite loop by using a bit safer value instead.. - */ - if (intval == 0) - intval = 100; /* Pull nexttbtt forward to reflect the current TSF */ @@ -745,6 +738,14 @@ void ath_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) iftype = sc->sc_ah->opmode; } + /* + * It looks like mac80211 may end up using beacon interval of zero in + * some cases (at least for mesh point). Avoid getting into an + * infinite loop by using a bit safer value instead. To be safe, + * do sanity check on beacon interval for all operating modes. + */ + if (cur_conf->beacon_interval == 0) + cur_conf->beacon_interval = 100; switch (iftype) { case NL80211_IFTYPE_AP: diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c new file mode 100644 index 000000000000..8fb356748823 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/btcoex.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2009 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 "ath9k.h" + +static const struct ath_btcoex_config ath_bt_config = { 0, true, true, + ATH_BT_COEX_MODE_SLOTTED, true, true, 2, 5, true }; + + +/* + * Detects if there is any priority bt traffic + */ +static void ath_detect_bt_priority(struct ath_softc *sc) +{ + struct ath_btcoex_info *btinfo = &sc->btcoex_info; + + if (ath9k_hw_gpio_get(sc->sc_ah, btinfo->btpriority_gpio)) + btinfo->bt_priority_cnt++; + + if (time_after(jiffies, btinfo->bt_priority_time + + msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { + if (btinfo->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { + DPRINTF(sc, ATH_DBG_BTCOEX, + "BT priority traffic detected"); + sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED; + } else { + sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; + } + + btinfo->bt_priority_cnt = 0; + btinfo->bt_priority_time = jiffies; + } +} + +/* + * Configures appropriate weight based on stomp type. + */ +static void ath_btcoex_bt_stomp(struct ath_softc *sc, + struct ath_btcoex_info *btinfo, + int stomp_type) +{ + + switch (stomp_type) { + case ATH_BTCOEX_STOMP_ALL: + ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT, + AR_STOMP_ALL_WLAN_WGHT); + break; + case ATH_BTCOEX_STOMP_LOW: + ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT, + AR_STOMP_LOW_WLAN_WGHT); + break; + case ATH_BTCOEX_STOMP_NONE: + ath_btcoex_set_weight(btinfo, AR_BT_COEX_WGHT, + AR_STOMP_NONE_WLAN_WGHT); + break; + default: + DPRINTF(sc, ATH_DBG_BTCOEX, "Invalid Stomptype\n"); + break; + } + + ath9k_hw_btcoex_enable(sc->sc_ah); +} + +/* + * This is the master bt coex timer which runs for every + * 45ms, bt traffic will be given priority during 55% of this + * period while wlan gets remaining 45% + */ + +static void ath_btcoex_period_timer(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *) data; + struct ath_btcoex_info *btinfo = &sc->btcoex_info; + unsigned long flags; + + ath_detect_bt_priority(sc); + + spin_lock_irqsave(&btinfo->btcoex_lock, flags); + + ath_btcoex_bt_stomp(sc, btinfo, btinfo->bt_stomp_type); + + spin_unlock_irqrestore(&btinfo->btcoex_lock, flags); + + if (btinfo->btcoex_period != btinfo->btcoex_no_stomp) { + if (btinfo->hw_timer_enabled) + ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer); + + ath_gen_timer_start(sc->sc_ah, + btinfo->no_stomp_timer, + (ath9k_hw_gettsf32(sc->sc_ah) + + btinfo->btcoex_no_stomp), + btinfo->btcoex_no_stomp * 10); + btinfo->hw_timer_enabled = true; + } + + mod_timer(&btinfo->period_timer, jiffies + + msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD)); +} + +/* + * Generic tsf based hw timer which configures weight + * registers to time slice between wlan and bt traffic + */ + +static void ath_btcoex_no_stomp_timer(void *arg) +{ + struct ath_softc *sc = (struct ath_softc *)arg; + struct ath_btcoex_info *btinfo = &sc->btcoex_info; + unsigned long flags; + + DPRINTF(sc, ATH_DBG_BTCOEX, "no stomp timer running \n"); + + spin_lock_irqsave(&btinfo->btcoex_lock, flags); + + if (btinfo->bt_stomp_type == ATH_BTCOEX_STOMP_LOW) + ath_btcoex_bt_stomp(sc, btinfo, ATH_BTCOEX_STOMP_NONE); + else if (btinfo->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) + ath_btcoex_bt_stomp(sc, btinfo, ATH_BTCOEX_STOMP_LOW); + + spin_unlock_irqrestore(&btinfo->btcoex_lock, flags); +} + +static int ath_init_btcoex_info(struct ath_hw *hw, + struct ath_btcoex_info *btcoex_info) +{ + u32 i; + int qnum; + + qnum = ath_tx_get_qnum(hw->ah_sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE); + + btcoex_info->bt_coex_mode = + (btcoex_info->bt_coex_mode & AR_BT_QCU_THRESH) | + SM(ath_bt_config.bt_time_extend, AR_BT_TIME_EXTEND) | + SM(ath_bt_config.bt_txstate_extend, AR_BT_TXSTATE_EXTEND) | + SM(ath_bt_config.bt_txframe_extend, AR_BT_TX_FRAME_EXTEND) | + SM(ath_bt_config.bt_mode, AR_BT_MODE) | + SM(ath_bt_config.bt_quiet_collision, AR_BT_QUIET) | + SM(ath_bt_config.bt_rxclear_polarity, AR_BT_RX_CLEAR_POLARITY) | + SM(ath_bt_config.bt_priority_time, AR_BT_PRIORITY_TIME) | + SM(ath_bt_config.bt_first_slot_time, AR_BT_FIRST_SLOT_TIME) | + SM(qnum, AR_BT_QCU_THRESH); + + btcoex_info->bt_coex_mode2 = + SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) | + SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) | + AR_BT_DISABLE_BT_ANT; + + btcoex_info->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; + + btcoex_info->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000; + + btcoex_info->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * + btcoex_info->btcoex_period / 100; + + for (i = 0; i < 32; i++) + hw->hw_gen_timers.gen_timer_index[(debruijn32 << i) >> 27] = i; + + setup_timer(&btcoex_info->period_timer, ath_btcoex_period_timer, + (unsigned long) hw->ah_sc); + + btcoex_info->no_stomp_timer = ath_gen_timer_alloc(hw, + ath_btcoex_no_stomp_timer, + ath_btcoex_no_stomp_timer, + (void *)hw->ah_sc, AR_FIRST_NDP_TIMER); + + if (btcoex_info->no_stomp_timer == NULL) + return -ENOMEM; + + spin_lock_init(&btcoex_info->btcoex_lock); + + return 0; +} + +int ath9k_hw_btcoex_init(struct ath_hw *ah) +{ + struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; + int ret = 0; + + if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) { + /* connect bt_active to baseband */ + REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, + (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF | + AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF)); + + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, + AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB); + + /* Set input mux for bt_active to gpio pin */ + REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, + AR_GPIO_INPUT_MUX1_BT_ACTIVE, + btcoex_info->btactive_gpio); + + /* Configure the desired gpio port for input */ + ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio); + } else { + /* btcoex 3-wire */ + REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, + (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB | + AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB)); + + /* Set input mux for bt_prority_async and + * bt_active_async to GPIO pins */ + REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, + AR_GPIO_INPUT_MUX1_BT_ACTIVE, + btcoex_info->btactive_gpio); + + REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, + AR_GPIO_INPUT_MUX1_BT_PRIORITY, + btcoex_info->btpriority_gpio); + + /* Configure the desired GPIO ports for input */ + + ath9k_hw_cfg_gpio_input(ah, btcoex_info->btactive_gpio); + ath9k_hw_cfg_gpio_input(ah, btcoex_info->btpriority_gpio); + + ret = ath_init_btcoex_info(ah, btcoex_info); + } + + return ret; +} + +void ath9k_hw_btcoex_enable(struct ath_hw *ah) +{ + struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; + + if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_2WIRE) { + /* Configure the desired GPIO port for TX_FRAME output */ + ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio, + AR_GPIO_OUTPUT_MUX_AS_TX_FRAME); + } else { + /* + * Program coex mode and weight registers to + * enable coex 3-wire + */ + REG_WRITE(ah, AR_BT_COEX_MODE, btcoex_info->bt_coex_mode); + REG_WRITE(ah, AR_BT_COEX_WEIGHT, btcoex_info->bt_coex_weights); + REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex_info->bt_coex_mode2); + + REG_RMW_FIELD(ah, AR_QUIET1, + AR_QUIET1_QUIET_ACK_CTS_ENABLE, 1); + REG_RMW_FIELD(ah, AR_PCU_MISC, + AR_PCU_BT_ANT_PREVENT_RX, 0); + + ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio, + AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL); + } + + REG_RMW(ah, AR_GPIO_PDPU, + (0x2 << (btcoex_info->btactive_gpio * 2)), + (0x3 << (btcoex_info->btactive_gpio * 2))); + + ah->ah_sc->sc_flags |= SC_OP_BTCOEX_ENABLED; +} + +void ath9k_hw_btcoex_disable(struct ath_hw *ah) +{ + struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; + + ath9k_hw_set_gpio(ah, btcoex_info->wlanactive_gpio, 0); + + ath9k_hw_cfg_output(ah, btcoex_info->wlanactive_gpio, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + + if (btcoex_info->btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) { + REG_WRITE(ah, AR_BT_COEX_MODE, AR_BT_QUIET | AR_BT_MODE); + REG_WRITE(ah, AR_BT_COEX_WEIGHT, 0); + REG_WRITE(ah, AR_BT_COEX_MODE2, 0); + } + + ah->ah_sc->sc_flags &= ~SC_OP_BTCOEX_ENABLED; +} + +/* + * Pause btcoex timer and bt duty cycle timer + */ +void ath_btcoex_timer_pause(struct ath_softc *sc, + struct ath_btcoex_info *btinfo) +{ + + del_timer_sync(&btinfo->period_timer); + + if (btinfo->hw_timer_enabled) + ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer); + + btinfo->hw_timer_enabled = false; +} + +/* + * (Re)start btcoex timers + */ +void ath_btcoex_timer_resume(struct ath_softc *sc, + struct ath_btcoex_info *btinfo) +{ + + DPRINTF(sc, ATH_DBG_BTCOEX, "Starting btcoex timers"); + + /* make sure duty cycle timer is also stopped when resuming */ + if (btinfo->hw_timer_enabled) + ath_gen_timer_stop(sc->sc_ah, btinfo->no_stomp_timer); + + btinfo->bt_priority_cnt = 0; + btinfo->bt_priority_time = jiffies; + sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; + + mod_timer(&btinfo->period_timer, jiffies); +} diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h new file mode 100644 index 000000000000..45568196c59a --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/btcoex.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2009 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 BTCOEX_H +#define BTCOEX_H + +#define ATH_WLANACTIVE_GPIO 5 +#define ATH_BTACTIVE_GPIO 6 + +#define ATH_BTCOEX_DEF_BT_PERIOD 45 +#define ATH_BTCOEX_DEF_DUTY_CYCLE 55 +#define ATH_BTCOEX_BMISS_THRESH 50 + +#define ATH_BT_PRIORITY_TIME_THRESHOLD 1000 /* ms */ +#define ATH_BT_CNT_THRESHOLD 3 + +enum ath_btcoex_scheme { + ATH_BTCOEX_CFG_NONE, + ATH_BTCOEX_CFG_2WIRE, + ATH_BTCOEX_CFG_3WIRE, +}; + +enum ath_stomp_type { + ATH_BTCOEX_NO_STOMP, + ATH_BTCOEX_STOMP_ALL, + ATH_BTCOEX_STOMP_LOW, + ATH_BTCOEX_STOMP_NONE +}; + +enum ath_bt_mode { + ATH_BT_COEX_MODE_LEGACY, /* legacy rx_clear mode */ + ATH_BT_COEX_MODE_UNSLOTTED, /* untimed/unslotted mode */ + ATH_BT_COEX_MODE_SLOTTED, /* slotted mode */ + ATH_BT_COEX_MODE_DISALBED, /* coexistence disabled */ +}; + +struct ath_btcoex_config { + u8 bt_time_extend; + bool bt_txstate_extend; + bool bt_txframe_extend; + enum ath_bt_mode bt_mode; /* coexistence mode */ + bool bt_quiet_collision; + bool bt_rxclear_polarity; /* invert rx_clear as WLAN_ACTIVE*/ + u8 bt_priority_time; + u8 bt_first_slot_time; + bool bt_hold_rx_clear; +}; + +struct ath_btcoex_info { + enum ath_btcoex_scheme btcoex_scheme; + u8 wlanactive_gpio; + u8 btactive_gpio; + u8 btpriority_gpio; + u8 bt_duty_cycle; /* BT duty cycle in percentage */ + int bt_stomp_type; /* Types of BT stomping */ + u32 bt_coex_mode; /* Register setting for AR_BT_COEX_MODE */ + u32 bt_coex_weights; /* Register setting for AR_BT_COEX_WEIGHT */ + u32 bt_coex_mode2; /* Register setting for AR_BT_COEX_MODE2 */ + u32 btcoex_no_stomp; /* in usec */ + u32 btcoex_period; /* in usec */ + u32 bt_priority_cnt; + unsigned long bt_priority_time; + bool hw_timer_enabled; + spinlock_t btcoex_lock; + struct timer_list period_timer; /* Timer for BT period */ + struct ath_gen_timer *no_stomp_timer; /*Timer for no BT stomping*/ +}; + +int ath9k_hw_btcoex_init(struct ath_hw *ah); +void ath9k_hw_btcoex_enable(struct ath_hw *ah); +void ath9k_hw_btcoex_disable(struct ath_hw *ah); +void ath_btcoex_timer_resume(struct ath_softc *sc, + struct ath_btcoex_info *btinfo); +void ath_btcoex_timer_pause(struct ath_softc *sc, + struct ath_btcoex_info *btinfo); + +static inline void ath_btcoex_set_weight(struct ath_btcoex_info *btcoex_info, + u32 bt_weight, + u32 wlan_weight) +{ + btcoex_info->bt_coex_weights = SM(bt_weight, AR_BTCOEX_BT_WGHT) | + SM(wlan_weight, AR_BTCOEX_WL_WGHT); +} + +#endif diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index a32d7e7fecbe..3234995e8881 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -116,7 +116,7 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, "NF calibrated [ctl] [chain 1] is %d\n", nf); nfarray[1] = nf; - if (!AR_SREV_9280(ah)) { + if (!AR_SREV_9280(ah) && !AR_SREV_9287(ah)) { nf = MS(REG_READ(ah, AR_PHY_CH2_CCA), AR_PHY_CH2_MINCCA_PWR); if (nf & 0x100) @@ -154,7 +154,7 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, "NF calibrated [ext] [chain 1] is %d\n", nf); nfarray[4] = nf; - if (!AR_SREV_9280(ah)) { + if (!AR_SREV_9280(ah) && !AR_SREV_9287(ah)) { nf = MS(REG_READ(ah, AR_PHY_CH2_EXT_CCA), AR_PHY_CH2_EXT_MINCCA_PWR); if (nf & 0x100) @@ -613,7 +613,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) if (AR_SREV_9285(ah)) chainmask = 0x9; - else if (AR_SREV_9280(ah)) + else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) chainmask = 0x1B; else chainmask = 0x3F; @@ -691,15 +691,22 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah, void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah) { int i, j; + s16 noise_floor; + + if (AR_SREV_9280(ah)) + noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE; + else if (AR_SREV_9285(ah)) + noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE; + else + noise_floor = AR_PHY_CCA_MAX_AR5416_GOOD_VALUE; 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].privNF = noise_floor; 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; + ah->nfCalHist[i].nfCalBuffer[j] = noise_floor; } } } @@ -722,31 +729,139 @@ s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) static void ath9k_olc_temp_compensation(struct ath_hw *ah) { u32 rddata, i; - int delta, currPDADC, regval; + int delta, currPDADC, regval, slope; rddata = REG_READ(ah, AR_PHY_TX_PWRCTRL4); - currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT); - if (ah->eep_ops->get_eeprom(ah, EEP_DAC_HPWR_5G)) - delta = (currPDADC - ah->initPDADC + 4) / 8; - else - delta = (currPDADC - ah->initPDADC + 5) / 10; - if (delta != ah->PDADCdelta) { - ah->PDADCdelta = delta; - for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) { - regval = ah->originalGain[i] - delta; - if (regval < 0) - regval = 0; + if (OLC_FOR_AR9287_10_LATER) { + if (ah->initPDADC == 0 || currPDADC == 0) { + return; + } else { + slope = ah->eep_ops->get_eeprom(ah, EEP_TEMPSENSE_SLOPE); + if (slope == 0) + delta = 0; + else + delta = ((currPDADC - ah->initPDADC)*4) / slope; + REG_RMW_FIELD(ah, AR_PHY_CH0_TX_PWRCTRL11, + AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta); + REG_RMW_FIELD(ah, AR_PHY_CH1_TX_PWRCTRL11, + AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP, delta); + } + } else { + if (ah->eep_ops->get_eeprom(ah, EEP_DAC_HPWR_5G)) + delta = (currPDADC - ah->initPDADC + 4) / 8; + else + delta = (currPDADC - ah->initPDADC + 5) / 10; + + if (delta != ah->PDADCdelta) { + ah->PDADCdelta = delta; + for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) { + regval = ah->originalGain[i] - delta; + if (regval < 0) + regval = 0; - REG_RMW_FIELD(ah, AR_PHY_TX_GAIN_TBL1 + i * 4, - AR_PHY_TX_GAIN, regval); + REG_RMW_FIELD(ah, AR_PHY_TX_GAIN_TBL1 + i * 4, + AR_PHY_TX_GAIN, regval); + } } } } -static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah) +static void ath9k_hw_9271_pa_cal(struct ath_hw *ah) +{ + u32 regVal; + unsigned int i; + u32 regList [][2] = { + { 0x786c, 0 }, + { 0x7854, 0 }, + { 0x7820, 0 }, + { 0x7824, 0 }, + { 0x7868, 0 }, + { 0x783c, 0 }, + { 0x7838, 0 } , + { 0x7828, 0 } , + }; + + for (i = 0; i < ARRAY_SIZE(regList); i++) + regList[i][1] = REG_READ(ah, regList[i][0]); + + regVal = REG_READ(ah, 0x7834); + regVal &= (~(0x1)); + REG_WRITE(ah, 0x7834, regVal); + regVal = REG_READ(ah, 0x9808); + regVal |= (0x1 << 27); + REG_WRITE(ah, 0x9808, regVal); + + /* 786c,b23,1, pwddac=1 */ + REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1); + /* 7854, b5,1, pdrxtxbb=1 */ + REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1); + /* 7854, b7,1, pdv2i=1 */ + REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1); + /* 7854, b8,1, pddacinterface=1 */ + REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1); + /* 7824,b12,0, offcal=0 */ + REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0); + /* 7838, b1,0, pwddb=0 */ + REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0); + /* 7820,b11,0, enpacal=0 */ + REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0); + /* 7820,b25,1, pdpadrv1=0 */ + REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 0); + /* 7820,b24,0, pdpadrv2=0 */ + REG_RMW_FIELD(ah, AR9285_AN_RF2G1,AR9285_AN_RF2G1_PDPADRV2,0); + /* 7820,b23,0, pdpaout=0 */ + REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0); + /* 783c,b14-16,7, padrvgn2tab_0=7 */ + REG_RMW_FIELD(ah, AR9285_AN_RF2G8,AR9285_AN_RF2G8_PADRVGN2TAB0, 7); + /* + * 7838,b29-31,0, padrvgn1tab_0=0 + * does not matter since we turn it off + */ + REG_RMW_FIELD(ah, AR9285_AN_RF2G7,AR9285_AN_RF2G7_PADRVGN2TAB0, 0); + + REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_CCOMP, 0xfff); + + /* Set: + * localmode=1,bmode=1,bmoderxtx=1,synthon=1, + * txon=1,paon=1,oscon=1,synthon_force=1 + */ + REG_WRITE(ah, AR9285_AN_TOP2, 0xca0358a0); + udelay(30); + REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9271_AN_RF2G6_OFFS, 0); + + /* find off_6_1; */ + for (i = 6; i >= 0; i--) { + regVal = REG_READ(ah, 0x7834); + regVal |= (1 << (20 + i)); + REG_WRITE(ah, 0x7834, regVal); + udelay(1); + //regVal = REG_READ(ah, 0x7834); + regVal &= (~(0x1 << (20 + i))); + regVal |= (MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9) + << (20 + i)); + REG_WRITE(ah, 0x7834, regVal); + } + + /* Empirical offset correction */ +#if 0 + REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9271_AN_RF2G6_OFFS, 0x20); +#endif + + regVal = REG_READ(ah, 0x7834); + regVal |= 0x1; + REG_WRITE(ah, 0x7834, regVal); + regVal = REG_READ(ah, 0x9808); + regVal &= (~(0x1 << 27)); + REG_WRITE(ah, 0x9808, regVal); + + for (i = 0; i < ARRAY_SIZE(regList); i++) + REG_WRITE(ah, regList[i][0], regList[i][1]); +} + +static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah, bool is_reset) { u32 regVal; @@ -762,6 +877,13 @@ static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah) { 0x7838, 0 }, }; + DPRINTF(ah->ah_sc, ATH_DBG_CALIBRATE, "Running PA Calibration\n"); + + /* PA CAL is not needed for high power solution */ + if (ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE) == + AR5416_EEP_TXGAIN_HIGH_POWER) + return; + if (AR_SREV_9285_11(ah)) { REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14)); udelay(10); @@ -784,13 +906,13 @@ static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah) REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0); REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0); REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0); - REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 1); + REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 0); REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0); REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0); REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7); REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0); ccomp_org = MS(REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_CCOMP); - REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, 7); + REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, 0xf); REG_WRITE(ah, AR9285_AN_TOP2, 0xca0358a0); udelay(30); @@ -802,7 +924,6 @@ static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah) regVal |= (1 << (19 + i)); REG_WRITE(ah, 0x7834, regVal); udelay(1); - regVal = REG_READ(ah, 0x7834); regVal &= (~(0x1 << (19 + i))); reg_field = MS(REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9); regVal |= (reg_field << (19 + i)); @@ -821,6 +942,17 @@ static inline void ath9k_hw_9285_pa_cal(struct ath_hw *ah) offs_6_1 = offset>>1; offs_0 = offset & 1; + if ((!is_reset) && (ah->pacal_info.prev_offset == offset)) { + if (ah->pacal_info.max_skipcount < MAX_PACAL_SKIPCOUNT) + ah->pacal_info.max_skipcount = + 2 * ah->pacal_info.max_skipcount; + ah->pacal_info.skipcount = ah->pacal_info.max_skipcount; + } else { + ah->pacal_info.max_skipcount = 1; + ah->pacal_info.skipcount = 0; + ah->pacal_info.prev_offset = offset; + } + REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, offs_6_1); REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, offs_0); @@ -862,14 +994,30 @@ bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, } } + /* Do NF cal only at longer intervals */ if (longcal) { - if (AR_SREV_9285_11_OR_LATER(ah)) - ath9k_hw_9285_pa_cal(ah); + /* Do periodic PAOffset Cal */ + if (AR_SREV_9271(ah)) + ath9k_hw_9271_pa_cal(ah); + else if (AR_SREV_9285_11_OR_LATER(ah)) { + if (!ah->pacal_info.skipcount) + ath9k_hw_9285_pa_cal(ah, false); + else + ah->pacal_info.skipcount--; + } - if (OLC_FOR_AR9280_20_LATER) + if (OLC_FOR_AR9280_20_LATER || OLC_FOR_AR9287_10_LATER) ath9k_olc_temp_compensation(ah); + + /* Get the value from the previous NF cal and update history buffer */ ath9k_hw_getnf(ah, chan); + + /* + * Load the NF from history buffer of the current channel. + * NF is slow time-variant, so it is OK to use a historical value. + */ ath9k_hw_loadnf(ah, ah->curchan); + ath9k_hw_start_nfcal(ah); } @@ -922,8 +1070,11 @@ bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) return false; } else { if (AR_SREV_9280_10_OR_LATER(ah)) { - REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); - REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); + if (!AR_SREV_9287_10_OR_LATER(ah)) + REG_CLR_BIT(ah, AR_PHY_ADC_CTL, + AR_PHY_ADC_CTL_OFF_PWDADC); + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_FLTR_CAL); } /* Calibrate the AGC */ @@ -941,14 +1092,17 @@ bool ath9k_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) } if (AR_SREV_9280_10_OR_LATER(ah)) { - REG_SET_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); - REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); + if (!AR_SREV_9287_10_OR_LATER(ah)) + REG_SET_BIT(ah, AR_PHY_ADC_CTL, + AR_PHY_ADC_CTL_OFF_PWDADC); + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_FLTR_CAL); } } /* Do PA Calibration */ if (AR_SREV_9285_11_OR_LATER(ah)) - ath9k_hw_9285_pa_cal(ah); + ath9k_hw_9285_pa_cal(ah, true); /* Do NF Calibration after DC offset and other calibrations */ REG_WRITE(ah, AR_PHY_AGC_CONTROL, diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index fe5367f14148..019bcbba40ed 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -25,7 +25,9 @@ extern const struct ath9k_percal_data adc_dc_cal_multi_sample; extern const struct ath9k_percal_data adc_dc_cal_single_sample; extern const struct ath9k_percal_data adc_init_dc_cal; -#define AR_PHY_CCA_MAX_GOOD_VALUE -85 +#define AR_PHY_CCA_MAX_AR5416_GOOD_VALUE -85 +#define AR_PHY_CCA_MAX_AR9280_GOOD_VALUE -112 +#define AR_PHY_CCA_MAX_AR9285_GOOD_VALUE -118 #define AR_PHY_CCA_MAX_HIGH_VALUE -62 #define AR_PHY_CCA_MIN_BAD_VALUE -140 #define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT 3 @@ -108,6 +110,13 @@ struct ath9k_nfcal_hist { u8 invalidNFcount; }; +#define MAX_PACAL_SKIPCOUNT 8 +struct ath9k_pacal_info{ + int32_t prev_offset; /* Previous value of PA offset value */ + int8_t max_skipcount; /* Max No. of times PACAL can be skipped */ + int8_t skipcount; /* No. of times the PACAL to be skipped */ +}; + bool ath9k_hw_reset_calvalid(struct ath_hw *ah); void ath9k_hw_start_nfcal(struct ath_hw *ah); void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan); diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 6d20725d6451..2be4c2252047 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -93,6 +93,8 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, int i, qcuOffset = 0, dcuOffset = 0; u32 *qcuBase = &val[0], *dcuBase = &val[4]; + ath9k_ps_wakeup(sc); + REG_WRITE(ah, AR_MACMISC, ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) | (AR_MACMISC_MISC_OBS_BUS_1 << @@ -159,6 +161,8 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, len += snprintf(buf + len, sizeof(buf) - len, "AR_CR: 0x%x \n", REG_READ(ah, AR_CR)); + ath9k_ps_restore(sc); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); } @@ -486,6 +490,83 @@ static const struct file_operations fops_wiphy = { .owner = THIS_MODULE }; +#define PR(str, elem) \ + do { \ + len += snprintf(buf + len, size - len, \ + "%s%13u%11u%10u%10u\n", str, \ + sc->debug.stats.txstats[sc->tx.hwq_map[ATH9K_WME_AC_BE]].elem, \ + sc->debug.stats.txstats[sc->tx.hwq_map[ATH9K_WME_AC_BK]].elem, \ + sc->debug.stats.txstats[sc->tx.hwq_map[ATH9K_WME_AC_VI]].elem, \ + sc->debug.stats.txstats[sc->tx.hwq_map[ATH9K_WME_AC_VO]].elem); \ +} while(0) + +static ssize_t read_file_xmit(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char *buf; + unsigned int len = 0, size = 2048; + ssize_t retval = 0; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return 0; + + len += sprintf(buf, "%30s %10s%10s%10s\n\n", "BE", "BK", "VI", "VO"); + + PR("MPDUs Queued: ", queued); + PR("MPDUs Completed: ", completed); + PR("Aggregates: ", a_aggr); + PR("AMPDUs Queued: ", a_queued); + PR("AMPDUs Completed:", a_completed); + PR("AMPDUs Retried: ", a_retries); + PR("AMPDUs XRetried: ", a_xretries); + PR("FIFO Underrun: ", fifo_underrun); + PR("TXOP Exceeded: ", xtxop); + PR("TXTIMER Expiry: ", timer_exp); + PR("DESC CFG Error: ", desc_cfg_err); + PR("DATA Underrun: ", data_underrun); + PR("DELIM Underrun: ", delim_underrun); + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf) +{ + struct ath_desc *ds = bf->bf_desc; + + if (bf_isampdu(bf)) { + if (bf_isxretried(bf)) + TX_STAT_INC(txq->axq_qnum, a_xretries); + else + TX_STAT_INC(txq->axq_qnum, a_completed); + } else { + TX_STAT_INC(txq->axq_qnum, completed); + } + + if (ds->ds_txstat.ts_status & ATH9K_TXERR_FIFO) + TX_STAT_INC(txq->axq_qnum, fifo_underrun); + if (ds->ds_txstat.ts_status & ATH9K_TXERR_XTXOP) + TX_STAT_INC(txq->axq_qnum, xtxop); + if (ds->ds_txstat.ts_status & ATH9K_TXERR_TIMER_EXPIRED) + TX_STAT_INC(txq->axq_qnum, timer_exp); + if (ds->ds_txstat.ts_flags & ATH9K_TX_DESC_CFG_ERR) + TX_STAT_INC(txq->axq_qnum, desc_cfg_err); + if (ds->ds_txstat.ts_flags & ATH9K_TX_DATA_UNDERRUN) + TX_STAT_INC(txq->axq_qnum, data_underrun); + if (ds->ds_txstat.ts_flags & ATH9K_TX_DELIM_UNDERRUN) + TX_STAT_INC(txq->axq_qnum, delim_underrun); +} + +static const struct file_operations fops_xmit = { + .read = read_file_xmit, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; int ath9k_init_debug(struct ath_softc *sc) { @@ -500,35 +581,42 @@ int ath9k_init_debug(struct ath_softc *sc) goto err; sc->debug.debugfs_debug = debugfs_create_file("debug", - S_IRUGO | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_debug); + S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_debug); if (!sc->debug.debugfs_debug) goto err; - sc->debug.debugfs_dma = debugfs_create_file("dma", S_IRUGO, + sc->debug.debugfs_dma = debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_dma); if (!sc->debug.debugfs_dma) goto err; sc->debug.debugfs_interrupt = debugfs_create_file("interrupt", - S_IRUGO, + S_IRUSR, sc->debug.debugfs_phy, sc, &fops_interrupt); if (!sc->debug.debugfs_interrupt) goto err; sc->debug.debugfs_rcstat = debugfs_create_file("rcstat", - S_IRUGO, + S_IRUSR, sc->debug.debugfs_phy, sc, &fops_rcstat); if (!sc->debug.debugfs_rcstat) goto err; sc->debug.debugfs_wiphy = debugfs_create_file( - "wiphy", S_IRUGO | S_IWUSR, sc->debug.debugfs_phy, sc, + "wiphy", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_wiphy); if (!sc->debug.debugfs_wiphy) goto err; + sc->debug.debugfs_xmit = debugfs_create_file("xmit", + S_IRUSR, + sc->debug.debugfs_phy, + sc, &fops_xmit); + if (!sc->debug.debugfs_xmit) + goto err; + return 0; err: ath9k_exit_debug(sc); @@ -537,6 +625,7 @@ err: void ath9k_exit_debug(struct ath_softc *sc) { + debugfs_remove(sc->debug.debugfs_xmit); debugfs_remove(sc->debug.debugfs_wiphy); debugfs_remove(sc->debug.debugfs_rcstat); debugfs_remove(sc->debug.debugfs_interrupt); diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index edda15bf2c15..7241f4748338 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -30,11 +30,22 @@ enum ATH_DEBUG { ATH_DBG_CONFIG = 0x00000200, ATH_DBG_FATAL = 0x00000400, ATH_DBG_PS = 0x00000800, + ATH_DBG_HWTIMER = 0x00001000, + ATH_DBG_BTCOEX = 0x00002000, ATH_DBG_ANY = 0xffffffff }; #define DBG_DEFAULT (ATH_DBG_FATAL) +struct ath_txq; +struct ath_buf; + +#ifdef CONFIG_ATH9K_DEBUG +#define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++ +#else +#define TX_STAT_INC(q, c) do { } while (0) +#endif + #ifdef CONFIG_ATH9K_DEBUG /** @@ -87,9 +98,45 @@ struct ath_rc_stats { u8 per; }; +/** + * struct ath_tx_stats - Statistics about TX + * @queued: Total MPDUs (non-aggr) queued + * @completed: Total MPDUs (non-aggr) completed + * @a_aggr: Total no. of aggregates queued + * @a_queued: Total AMPDUs queued + * @a_completed: Total AMPDUs completed + * @a_retries: No. of AMPDUs retried (SW) + * @a_xretries: No. of AMPDUs dropped due to xretries + * @fifo_underrun: FIFO underrun occurrences + Valid only for: + - non-aggregate condition. + - first packet of aggregate. + * @xtxop: No. of frames filtered because of TXOP limit + * @timer_exp: Transmit timer expiry + * @desc_cfg_err: Descriptor configuration errors + * @data_urn: TX data underrun errors + * @delim_urn: TX delimiter underrun errors + */ +struct ath_tx_stats { + u32 queued; + u32 completed; + u32 a_aggr; + u32 a_queued; + u32 a_completed; + u32 a_retries; + u32 a_xretries; + u32 fifo_underrun; + u32 xtxop; + u32 timer_exp; + u32 desc_cfg_err; + u32 data_underrun; + u32 delim_underrun; +}; + struct ath_stats { struct ath_interrupt_stats istats; struct ath_rc_stats rcstats[RATE_TABLE_SIZE]; + struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES]; }; struct ath9k_debug { @@ -100,6 +147,7 @@ struct ath9k_debug { struct dentry *debugfs_interrupt; struct dentry *debugfs_rcstat; struct dentry *debugfs_wiphy; + struct dentry *debugfs_xmit; struct ath_stats stats; }; @@ -110,6 +158,8 @@ int ath9k_debug_create_root(void); void ath9k_debug_remove_root(void); void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status); void ath_debug_stat_rc(struct ath_softc *sc, struct sk_buff *skb); +void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf); void ath_debug_stat_retries(struct ath_softc *sc, int rix, int xretries, int retries, u8 per); @@ -148,6 +198,12 @@ static inline void ath_debug_stat_rc(struct ath_softc *sc, { } +static inline void ath_debug_stat_tx(struct ath_softc *sc, + struct ath_txq *txq, + struct ath_buf *bf) +{ +} + static inline void ath_debug_stat_retries(struct ath_softc *sc, int rix, int xretries, int retries, u8 per) { diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index ce0e86c36a82..b6e52d0f8c48 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -16,9 +16,16 @@ #include "ath9k.h" -static void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, - u32 reg, u32 mask, - u32 shift, u32 val) +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)); +} + +void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask, + u32 shift, u32 val) { u32 regVal; @@ -33,19 +40,8 @@ static void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, return; } -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 inline int16_t ath9k_hw_interpolate(u16 target, - u16 srcLeft, u16 srcRight, - int16_t targetLeft, - int16_t targetRight) +int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight, + int16_t targetLeft, int16_t targetRight) { int16_t rv; @@ -59,9 +55,8 @@ static inline int16_t ath9k_hw_interpolate(u16 target, return rv; } -static inline bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, - u16 listSize, u16 *indexL, - u16 *indexR) +bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize, + u16 *indexL, u16 *indexR) { u16 i; @@ -88,16 +83,16 @@ static inline bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, return false; } -static inline bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) +bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data) { struct ath_softc *sc = ah->ah_sc; return sc->bus_ops->eeprom_read(ah, off, data); } -static inline bool ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, - u8 *pVpdList, u16 numIntercepts, - u8 *pRetVpdList) +void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, + u8 *pVpdList, u16 numIntercepts, + u8 *pRetVpdList) { u16 i, k; u8 currPwr = pwrMin; @@ -120,16 +115,14 @@ static inline bool ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, pRetVpdList[i] = (u8) k; currPwr += 2; } - - return true; } -static void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah, - struct ath9k_channel *chan, - struct cal_target_power_leg *powInfo, - u16 numChannels, - struct cal_target_power_leg *pNewPower, - u16 numRates, bool isExtTarget) +void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah, + struct ath9k_channel *chan, + struct cal_target_power_leg *powInfo, + u16 numChannels, + struct cal_target_power_leg *pNewPower, + u16 numRates, bool isExtTarget) { struct chan_centers centers; u16 clo, chi; @@ -150,10 +143,10 @@ static void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah, 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)))) { + } else if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel, + IS_CHAN_2GHZ(chan)) && i > 0 && + freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel, + IS_CHAN_2GHZ(chan))) { lowIndex = i - 1; break; } @@ -179,75 +172,12 @@ static void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah, } } -static void ath9k_get_txgain_index(struct ath_hw *ah, - struct ath9k_channel *chan, - struct calDataPerFreqOpLoop *rawDatasetOpLoop, - u8 *calChans, u16 availPiers, u8 *pwr, u8 *pcdacIdx) -{ - u8 pcdac, i = 0; - u16 idxL = 0, idxR = 0, numPiers; - bool match; - struct chan_centers centers; - - ath9k_hw_get_channel_centers(ah, chan, ¢ers); - - for (numPiers = 0; numPiers < availPiers; numPiers++) - if (calChans[numPiers] == AR5416_BCHAN_UNUSED) - break; - - match = ath9k_hw_get_lower_upper_index( - (u8)FREQ2FBIN(centers.synth_center, IS_CHAN_2GHZ(chan)), - calChans, numPiers, &idxL, &idxR); - if (match) { - pcdac = rawDatasetOpLoop[idxL].pcdac[0][0]; - *pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0]; - } else { - pcdac = rawDatasetOpLoop[idxR].pcdac[0][0]; - *pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] + - rawDatasetOpLoop[idxR].pwrPdg[0][0])/2; - } - - while (pcdac > ah->originalGain[i] && - i < (AR9280_TX_GAIN_TABLE_SIZE - 1)) - i++; - - *pcdacIdx = i; - return; -} - -static void ath9k_olc_get_pdadcs(struct ath_hw *ah, - u32 initTxGain, - int txPower, - u8 *pPDADCValues) -{ - u32 i; - u32 offset; - - REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, - AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); - REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, - AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); - - REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, - AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain); - - offset = txPower; - for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++) - if (i < offset) - pPDADCValues[i] = 0x0; - else - pPDADCValues[i] = 0xFF; -} - - - - -static void ath9k_hw_get_target_powers(struct ath_hw *ah, - struct ath9k_channel *chan, - struct cal_target_power_ht *powInfo, - u16 numChannels, - struct cal_target_power_ht *pNewPower, - u16 numRates, bool isHt40Target) +void ath9k_hw_get_target_powers(struct ath_hw *ah, + struct ath9k_channel *chan, + struct cal_target_power_ht *powInfo, + u16 numChannels, + struct cal_target_power_ht *pNewPower, + u16 numRates, bool isHt40Target) { struct chan_centers centers; u16 clo, chi; @@ -268,10 +198,10 @@ static void ath9k_hw_get_target_powers(struct ath_hw *ah, 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)))) { + if (freq < ath9k_hw_fbin2freq(powInfo[i].bChannel, + IS_CHAN_2GHZ(chan)) && i > 0 && + freq > ath9k_hw_fbin2freq(powInfo[i - 1].bChannel, + IS_CHAN_2GHZ(chan))) { lowIndex = i - 1; break; } @@ -297,9 +227,8 @@ static void ath9k_hw_get_target_powers(struct ath_hw *ah, } } -static u16 ath9k_hw_get_max_edge_power(u16 freq, - struct cal_ctl_edges *pRdEdgesPower, - bool is2GHz, int num_band_edges) +u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower, + bool is2GHz, int num_band_edges) { u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; int i; @@ -325,2451 +254,14 @@ static u16 ath9k_hw_get_max_edge_power(u16 freq, return twiceMaxEdgePower; } -/****************************************/ -/* EEPROM Operations for 4K sized cards */ -/****************************************/ - -static int ath9k_hw_4k_get_eeprom_ver(struct ath_hw *ah) -{ - return ((ah->eeprom.map4k.baseEepHeader.version >> 12) & 0xF); -} - -static int ath9k_hw_4k_get_eeprom_rev(struct ath_hw *ah) -{ - return ((ah->eeprom.map4k.baseEepHeader.version) & 0xFFF); -} - -static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) -{ -#define SIZE_EEPROM_4K (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) - u16 *eep_data = (u16 *)&ah->eeprom.map4k; - int addr, eep_start_loc = 0; - - eep_start_loc = 64; - - if (!ath9k_hw_use_flash(ah)) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Reading from EEPROM, not flash\n"); - } - - for (addr = 0; addr < SIZE_EEPROM_4K; addr++) { - if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Unable to read eeprom region \n"); - return false; - } - eep_data++; - } - - return true; -#undef SIZE_EEPROM_4K -} - -static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) -{ -#define EEPROM_4K_SIZE (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) - struct ar5416_eeprom_4k *eep = - (struct ar5416_eeprom_4k *) &ah->eeprom.map4k; - u16 *eepdata, temp, magic, magic2; - u32 sum = 0, el; - bool need_swap = false; - int i, addr; - - - if (!ath9k_hw_use_flash(ah)) { - if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, - &magic)) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Reading Magic # failed\n"); - return false; - } - - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Read Magic = 0x%04X\n", magic); - - if (magic != AR5416_EEPROM_MAGIC) { - magic2 = swab16(magic); - - if (magic2 == AR5416_EEPROM_MAGIC) { - need_swap = true; - eepdata = (u16 *) (&ah->eeprom); - - for (addr = 0; addr < EEPROM_4K_SIZE; addr++) { - temp = swab16(*eepdata); - *eepdata = temp; - eepdata++; - } - } else { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Invalid EEPROM Magic. " - "endianness mismatch.\n"); - return -EINVAL; - } - } - } - - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n", - need_swap ? "True" : "False"); - - if (need_swap) - el = swab16(ah->eeprom.map4k.baseEepHeader.length); - else - el = ah->eeprom.map4k.baseEepHeader.length; - - if (el > sizeof(struct ar5416_eeprom_4k)) - el = sizeof(struct ar5416_eeprom_4k) / sizeof(u16); - else - el = el / sizeof(u16); - - eepdata = (u16 *)(&ah->eeprom); - - for (i = 0; i < el; i++) - sum ^= *eepdata++; - - if (need_swap) { - u32 integer; - 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; - - integer = swab32(eep->modalHeader.antCtrlCommon); - eep->modalHeader.antCtrlCommon = integer; - - for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) { - integer = swab32(eep->modalHeader.antCtrlChain[i]); - eep->modalHeader.antCtrlChain[i] = integer; - } - - for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) { - word = swab16(eep->modalHeader.spurChans[i].spurChan); - eep->modalHeader.spurChans[i].spurChan = word; - } - } - - if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || - ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Bad EEPROM checksum 0x%x or revision 0x%04x\n", - sum, ah->eep_ops->get_eeprom_ver(ah)); - return -EINVAL; - } - - return 0; -#undef EEPROM_4K_SIZE -} - -static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah, - enum eeprom_param param) -{ - struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; - struct modal_eep_4k_header *pModal = &eep->modalHeader; - struct base_eep_header_4k *pBase = &eep->baseEepHeader; - - switch (param) { - case EEP_NFTHRESH_2: - return pModal->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_2: - return pModal->ob_01; - case EEP_DB_2: - return pModal->db1_01; - 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; - case EEP_FRAC_N_5G: - return 0; - default: - return 0; - } -} - -static void ath9k_hw_get_4k_gain_boundaries_pdadcs(struct ath_hw *ah, - struct ath9k_channel *chan, - struct cal_data_per_freq_4k *pRawDataSet, - u8 *bChans, u16 availPiers, - u16 tPdGainOverlap, int16_t *pMinCalPower, - u16 *pPdGainBoundaries, u8 *pPDADCValues, - u16 numXpdGains) -{ -#define TMP_VAL_VPD_TABLE \ - ((vpdTableI[i][sizeCurrVpdTable - 1] + (ss - maxIndex + 1) * vpdStep)); - int i, j, k; - int16_t ss; - u16 idxL = 0, idxR = 0, numPiers; - static u8 vpdTableL[AR5416_EEP4K_NUM_PD_GAINS] - [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; - static u8 vpdTableR[AR5416_EEP4K_NUM_PD_GAINS] - [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; - static u8 vpdTableI[AR5416_EEP4K_NUM_PD_GAINS] - [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; - - u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR; - u8 minPwrT4[AR5416_EEP4K_NUM_PD_GAINS]; - u8 maxPwrT4[AR5416_EEP4K_NUM_PD_GAINS]; - int16_t vpdStep; - int16_t tmpVal; - u16 sizeCurrVpdTable, maxIndex, tgtIndex; - bool match; - int16_t minDelta = 0; - struct chan_centers centers; -#define PD_GAIN_BOUNDARY_DEFAULT 58; - - 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_EEP4K_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_EEP4K_PD_GAIN_ICEPTS - 1], - pPwrR[AR5416_EEP4K_PD_GAIN_ICEPTS - 1]); - - - ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], - pPwrL, pVpdL, - AR5416_EEP4K_PD_GAIN_ICEPTS, - vpdTableL[i]); - ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], - pPwrR, pVpdR, - AR5416_EEP4K_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_20_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) TMP_VAL_VPD_TABLE; - pPDADCValues[k++] = (u8)((tmpVal > 255) ? - 255 : tmpVal); - ss++; - } - } - } - - while (i < AR5416_EEP4K_PD_GAINS_IN_MASK) { - pPdGainBoundaries[i] = PD_GAIN_BOUNDARY_DEFAULT; - i++; - } - - while (k < AR5416_NUM_PDADC_VALUES) { - pPDADCValues[k] = pPDADCValues[k - 1]; - k++; - } - - return; -#undef TMP_VAL_VPD_TABLE -} - -static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah, - struct ath9k_channel *chan, - int16_t *pTxPowerIndexOffset) -{ - struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k; - struct cal_data_per_freq_4k *pRawDataset; - u8 *pCalBChans = NULL; - u16 pdGainOverlap_t2; - static u8 pdadcValues[AR5416_NUM_PDADC_VALUES]; - u16 gainBoundaries[AR5416_EEP4K_PD_GAINS_IN_MASK]; - u16 numPiers, i, j; - int16_t tMinCalPower; - u16 numXpdGain, xpdMask; - u16 xpdGainValues[AR5416_EEP4K_NUM_PD_GAINS] = { 0, 0 }; - u32 reg32, regOffset, regChainOffset; - - xpdMask = pEepData->modalHeader.xpdGain; - - if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= - AR5416_EEP_MINOR_VER_2) { - pdGainOverlap_t2 = - pEepData->modalHeader.pdGainOverlap; - } else { - pdGainOverlap_t2 = (u16)(MS(REG_READ(ah, AR_PHY_TPCRG5), - AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); - } - - pCalBChans = pEepData->calFreqPier2G; - numPiers = AR5416_EEP4K_NUM_2G_CAL_PIERS; - - numXpdGain = 0; - - for (i = 1; i <= AR5416_EEP4K_PD_GAINS_IN_MASK; i++) { - if ((xpdMask >> (AR5416_EEP4K_PD_GAINS_IN_MASK - i)) & 1) { - if (numXpdGain >= AR5416_EEP4K_NUM_PD_GAINS) - break; - xpdGainValues[numXpdGain] = - (u16)(AR5416_EEP4K_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, 0); - - for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) { - if (AR_SREV_5416_20_OR_LATER(ah) && - (ah->rxchainmask == 5 || ah->txchainmask == 5) && - (i != 0)) { - regChainOffset = (i == 1) ? 0x2000 : 0x1000; - } else - regChainOffset = i * 0x1000; - - if (pEepData->baseEepHeader.txMask & (1 << i)) { - pRawDataset = pEepData->calPierData2G[i]; - - ath9k_hw_get_4k_gain_boundaries_pdadcs(ah, chan, - pRawDataset, pCalBChans, - numPiers, pdGainOverlap_t2, - &tMinCalPower, gainBoundaries, - pdadcValues, numXpdGain); - - if ((i == 0) || AR_SREV_5416_20_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_EEPROM, - "PDADC (%d,%4x): %4.4x %8.8x\n", - i, regChainOffset, regOffset, - reg32); - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "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; -} - -static void ath9k_hw_set_4k_power_per_rate_table(struct ath_hw *ah, - struct ath9k_channel *chan, - int16_t *ratesArray, - u16 cfgCtl, - u16 AntennaReduction, - u16 twiceMaxRegulatoryPower, - u16 powerLimit) -{ - struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k; - u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; - static const u16 tpScaleReductionTable[5] = - { 0, 3, 6, 9, AR5416_MAX_RATE_POWER }; - - int i; - int16_t twiceLargestAntenna; - struct cal_ctl_data_4k *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} - }; - u16 scaledPower = 0, minCtlPower, maxRegAllowedPower; - 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; - u16 twiceMinEdgePower; - - tx_chainmask = ah->txchainmask; - - ath9k_hw_get_channel_centers(ah, chan, ¢ers); - - twiceLargestAntenna = pEepData->modalHeader.antennaGainCh[0]; - - twiceLargestAntenna = (int16_t)min(AntennaReduction - - twiceLargestAntenna, 0); - - maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna; - - if (ah->regulatory.tp_scale != ATH9K_TP_SCALE_MAX) { - maxRegAllowedPower -= - (tpScaleReductionTable[(ah->regulatory.tp_scale)] * 2); - } - - scaledPower = min(powerLimit, maxRegAllowedPower); - scaledPower = max((u16)0, scaledPower); - - 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); - } - - 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 (ah->eep_ops->get_eeprom_ver(ah) == 14 && - ah->eep_ops->get_eeprom_rev(ah) <= 2) - twiceMaxEdgePower = AR5416_MAX_RATE_POWER; - - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "LOOP-Mode ctlMode %d < %d, isHt40CtlMode %d, " - "EXT_ADDITIVE %d\n", - ctlMode, numCtlModes, isHt40CtlMode, - (pCtlMode[ctlMode] & EXT_ADDITIVE)); - - for (i = 0; (i < AR5416_EEP4K_NUM_CTLS) && - pEepData->ctlIndex[i]; i++) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - " 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), - AR5416_EEP4K_NUM_BAND_EDGES); - - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - " 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 = (u8)min(twiceMaxEdgePower, scaledPower); - - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - " 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((u16)targetPowerCck.tPow2x[i], - minCtlPower); - } - break; - case CTL_11G: - for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x); - i++) { - targetPowerOfdm.tPow2x[i] = - min((u16)targetPowerOfdm.tPow2x[i], - minCtlPower); - } - break; - case CTL_2GHT20: - for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); - i++) { - targetPowerHt20.tPow2x[i] = - min((u16)targetPowerHt20.tPow2x[i], - minCtlPower); - } - break; - case CTL_11B_EXT: - targetPowerCckExt.tPow2x[0] = min((u16) - targetPowerCckExt.tPow2x[0], - minCtlPower); - break; - case CTL_11G_EXT: - targetPowerOfdmExt.tPow2x[0] = min((u16) - targetPowerOfdmExt.tPow2x[0], - minCtlPower); - break; - case CTL_2GHT40: - for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); - i++) { - targetPowerHt40.tPow2x[i] = - min((u16)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]; - - 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]; - ratesArray[rateExtCck] = targetPowerCckExt.tPow2x[0]; - } -} - -static void ath9k_hw_4k_set_txpower(struct ath_hw *ah, - struct ath9k_channel *chan, - u16 cfgCtl, - u8 twiceAntennaReduction, - u8 twiceMaxRegulatoryPower, - u8 powerLimit) -{ - struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k; - struct modal_eep_4k_header *pModal = &pEepData->modalHeader; - 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; - } - - ath9k_hw_set_4k_power_per_rate_table(ah, chan, - &ratesArray[0], cfgCtl, - twiceAntennaReduction, - twiceMaxRegulatoryPower, - powerLimit); - - ath9k_hw_set_4k_power_cal_table(ah, chan, &txPowerIndexOffset); - - 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)); - } - - 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->regulatory.max_power_level = - ratesArray[i] + AR5416_PWR_TABLE_OFFSET * 2; - else - ah->regulatory.max_power_level = ratesArray[i]; - -} - -static void ath9k_hw_4k_set_addac(struct ath_hw *ah, - struct ath9k_channel *chan) -{ - struct modal_eep_4k_header *pModal; - struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; - u8 biaslevel; - - if (ah->hw_version.macVersion != AR_SREV_VERSION_9160) - return; - - if (ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_MINOR_VER_7) - return; - - pModal = &eep->modalHeader; - - if (pModal->xpaBiasLvl != 0xff) { - biaslevel = pModal->xpaBiasLvl; - INI_RA(&ah->iniAddac, 7, 1) = - (INI_RA(&ah->iniAddac, 7, 1) & (~0x18)) | biaslevel << 3; - } -} - -static void ath9k_hw_4k_set_gain(struct ath_hw *ah, - struct modal_eep_4k_header *pModal, - struct ar5416_eeprom_4k *eep, - u8 txRxAttenLocal, int regChainOffset) -{ - REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset, - pModal->antCtrlChain[0]); - - 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[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | - SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); - - if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= - AR5416_EEP_MINOR_VER_3) { - txRxAttenLocal = pModal->txRxAttenCh[0]; - - REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, - AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[0]); - REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, - AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]); - REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, - AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, - pModal->xatten2Margin[0]); - REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + regChainOffset, - AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]); - } - - 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[0]); - - if (AR_SREV_9285_11(ah)) - REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14)); -} - -static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, - struct ath9k_channel *chan) -{ - struct modal_eep_4k_header *pModal; - struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; - u8 txRxAttenLocal; - u8 ob[5], db1[5], db2[5]; - u8 ant_div_control1, ant_div_control2; - u32 regVal; - - pModal = &eep->modalHeader; - txRxAttenLocal = 23; - - REG_WRITE(ah, AR_PHY_SWITCH_COM, - ah->eep_ops->get_eeprom_antenna_cfg(ah, chan)); - - /* Single chain for 4K EEPROM*/ - ath9k_hw_4k_set_gain(ah, pModal, eep, txRxAttenLocal, 0); - - /* Initialize Ant Diversity settings from EEPROM */ - if (pModal->version == 3) { - ant_div_control1 = ((pModal->ob_234 >> 12) & 0xf); - ant_div_control2 = ((pModal->db1_234 >> 12) & 0xf); - regVal = REG_READ(ah, 0x99ac); - regVal &= (~(0x7f000000)); - regVal |= ((ant_div_control1 & 0x1) << 24); - regVal |= (((ant_div_control1 >> 1) & 0x1) << 29); - regVal |= (((ant_div_control1 >> 2) & 0x1) << 30); - regVal |= ((ant_div_control2 & 0x3) << 25); - regVal |= (((ant_div_control2 >> 2) & 0x3) << 27); - REG_WRITE(ah, 0x99ac, regVal); - regVal = REG_READ(ah, 0x99ac); - regVal = REG_READ(ah, 0xa208); - regVal &= (~(0x1 << 13)); - regVal |= (((ant_div_control1 >> 3) & 0x1) << 13); - REG_WRITE(ah, 0xa208, regVal); - regVal = REG_READ(ah, 0xa208); - } - - if (pModal->version >= 2) { - ob[0] = (pModal->ob_01 & 0xf); - ob[1] = (pModal->ob_01 >> 4) & 0xf; - ob[2] = (pModal->ob_234 & 0xf); - ob[3] = ((pModal->ob_234 >> 4) & 0xf); - ob[4] = ((pModal->ob_234 >> 8) & 0xf); - - db1[0] = (pModal->db1_01 & 0xf); - db1[1] = ((pModal->db1_01 >> 4) & 0xf); - db1[2] = (pModal->db1_234 & 0xf); - db1[3] = ((pModal->db1_234 >> 4) & 0xf); - db1[4] = ((pModal->db1_234 >> 8) & 0xf); - - db2[0] = (pModal->db2_01 & 0xf); - db2[1] = ((pModal->db2_01 >> 4) & 0xf); - db2[2] = (pModal->db2_234 & 0xf); - db2[3] = ((pModal->db2_234 >> 4) & 0xf); - db2[4] = ((pModal->db2_234 >> 8) & 0xf); - - } else if (pModal->version == 1) { - ob[0] = (pModal->ob_01 & 0xf); - ob[1] = ob[2] = ob[3] = ob[4] = (pModal->ob_01 >> 4) & 0xf; - db1[0] = (pModal->db1_01 & 0xf); - db1[1] = db1[2] = db1[3] = - db1[4] = ((pModal->db1_01 >> 4) & 0xf); - db2[0] = (pModal->db2_01 & 0xf); - db2[1] = db2[2] = db2[3] = - db2[4] = ((pModal->db2_01 >> 4) & 0xf); - } else { - int i; - for (i = 0; i < 5; i++) { - ob[i] = pModal->ob_01; - db1[i] = pModal->db1_01; - db2[i] = pModal->db1_01; - } - } - - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, - AR9285_AN_RF2G3_OB_0, AR9285_AN_RF2G3_OB_0_S, ob[0]); - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, - AR9285_AN_RF2G3_OB_1, AR9285_AN_RF2G3_OB_1_S, ob[1]); - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, - AR9285_AN_RF2G3_OB_2, AR9285_AN_RF2G3_OB_2_S, ob[2]); - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, - AR9285_AN_RF2G3_OB_3, AR9285_AN_RF2G3_OB_3_S, ob[3]); - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, - AR9285_AN_RF2G3_OB_4, AR9285_AN_RF2G3_OB_4_S, ob[4]); - - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, - AR9285_AN_RF2G3_DB1_0, AR9285_AN_RF2G3_DB1_0_S, db1[0]); - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, - AR9285_AN_RF2G3_DB1_1, AR9285_AN_RF2G3_DB1_1_S, db1[1]); - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G3, - AR9285_AN_RF2G3_DB1_2, AR9285_AN_RF2G3_DB1_2_S, db1[2]); - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, - AR9285_AN_RF2G4_DB1_3, AR9285_AN_RF2G4_DB1_3_S, db1[3]); - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, - AR9285_AN_RF2G4_DB1_4, AR9285_AN_RF2G4_DB1_4_S, db1[4]); - - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, - AR9285_AN_RF2G4_DB2_0, AR9285_AN_RF2G4_DB2_0_S, db2[0]); - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, - AR9285_AN_RF2G4_DB2_1, AR9285_AN_RF2G4_DB2_1_S, db2[1]); - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, - AR9285_AN_RF2G4_DB2_2, AR9285_AN_RF2G4_DB2_2_S, db2[2]); - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, - AR9285_AN_RF2G4_DB2_3, AR9285_AN_RF2G4_DB2_3_S, db2[3]); - ath9k_hw_analog_shift_rmw(ah, AR9285_AN_RF2G4, - AR9285_AN_RF2G4_DB2_4, AR9285_AN_RF2G4_DB2_4_S, db2[4]); - - - if (AR_SREV_9285_11(ah)) - REG_WRITE(ah, AR9285_AN_TOP4, AR9285_AN_TOP4_DEFAULT); - - 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); - - 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); - 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); - - 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); - } -} - -static u16 ath9k_hw_4k_get_eeprom_antenna_cfg(struct ath_hw *ah, - struct ath9k_channel *chan) -{ - struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; - struct modal_eep_4k_header *pModal = &eep->modalHeader; - - return pModal->antCtrlCommon & 0xFFFF; -} - -static u8 ath9k_hw_4k_get_num_ant_config(struct ath_hw *ah, - enum ieee80211_band freq_band) -{ - return 1; -} - -static u16 ath9k_hw_4k_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) -{ -#define EEP_MAP4K_SPURCHAN \ - (ah->eeprom.map4k.modalHeader.spurChans[i].spurChan) - - 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->config.spurchans[i][is2GHz]); - - switch (ah->config.spurmode) { - case SPUR_DISABLE: - break; - case SPUR_ENABLE_IOCTL: - spur_val = ah->config.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_MAP4K_SPURCHAN; - break; - } - - return spur_val; - -#undef EEP_MAP4K_SPURCHAN -} - -static struct eeprom_ops eep_4k_ops = { - .check_eeprom = ath9k_hw_4k_check_eeprom, - .get_eeprom = ath9k_hw_4k_get_eeprom, - .fill_eeprom = ath9k_hw_4k_fill_eeprom, - .get_eeprom_ver = ath9k_hw_4k_get_eeprom_ver, - .get_eeprom_rev = ath9k_hw_4k_get_eeprom_rev, - .get_num_ant_config = ath9k_hw_4k_get_num_ant_config, - .get_eeprom_antenna_cfg = ath9k_hw_4k_get_eeprom_antenna_cfg, - .set_board_values = ath9k_hw_4k_set_board_values, - .set_addac = ath9k_hw_4k_set_addac, - .set_txpower = ath9k_hw_4k_set_txpower, - .get_spur_channel = ath9k_hw_4k_get_spur_channel -}; - -/************************************************/ -/* EEPROM Operations for non-4K (Default) cards */ -/************************************************/ - -static int ath9k_hw_def_get_eeprom_ver(struct ath_hw *ah) -{ - return ((ah->eeprom.def.baseEepHeader.version >> 12) & 0xF); -} - -static int ath9k_hw_def_get_eeprom_rev(struct ath_hw *ah) -{ - return ((ah->eeprom.def.baseEepHeader.version) & 0xFFF); -} - -static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah) -{ -#define SIZE_EEPROM_DEF (sizeof(struct ar5416_eeprom_def) / sizeof(u16)) - u16 *eep_data = (u16 *)&ah->eeprom.def; - int addr, ar5416_eep_start_loc = 0x100; - - for (addr = 0; addr < SIZE_EEPROM_DEF; addr++) { - if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc, - eep_data)) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Unable to read eeprom region\n"); - return false; - } - eep_data++; - } - return true; -#undef SIZE_EEPROM_DEF -} - -static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) -{ - struct ar5416_eeprom_def *eep = - (struct ar5416_eeprom_def *) &ah->eeprom.def; - u16 *eepdata, temp, magic, magic2; - u32 sum = 0, el; - bool need_swap = false; - int i, addr, size; - - if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Reading Magic # failed\n"); - return false; - } - - if (!ath9k_hw_use_flash(ah)) { - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Read Magic = 0x%04X\n", magic); - - if (magic != AR5416_EEPROM_MAGIC) { - magic2 = swab16(magic); - - if (magic2 == AR5416_EEPROM_MAGIC) { - size = sizeof(struct ar5416_eeprom_def); - need_swap = true; - eepdata = (u16 *) (&ah->eeprom); - - for (addr = 0; addr < size / sizeof(u16); addr++) { - temp = swab16(*eepdata); - *eepdata = temp; - eepdata++; - } - } else { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Invalid EEPROM Magic. " - "Endianness mismatch.\n"); - return -EINVAL; - } - } - } - - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n", - need_swap ? "True" : "False"); - - if (need_swap) - el = swab16(ah->eeprom.def.baseEepHeader.length); - else - el = ah->eeprom.def.baseEepHeader.length; - - if (el > sizeof(struct ar5416_eeprom_def)) - el = sizeof(struct ar5416_eeprom_def) / sizeof(u16); - else - el = el / sizeof(u16); - - eepdata = (u16 *)(&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 || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || - ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { - DPRINTF(ah->ah_sc, ATH_DBG_FATAL, - "Bad EEPROM checksum 0x%x or revision 0x%04x\n", - sum, ah->eep_ops->get_eeprom_ver(ah)); - return -EINVAL; - } - - return 0; -} - -static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah, - enum eeprom_param param) -{ - struct ar5416_eeprom_def *eep = &ah->eeprom.def; - 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 AR5416_VER_MASK; - case EEP_TX_MASK: - return pBase->txMask; - case EEP_RX_MASK: - return pBase->rxMask; - case EEP_RXGAIN_TYPE: - return pBase->rxGainType; - case EEP_TXGAIN_TYPE: - return pBase->txGainType; - case EEP_OL_PWRCTRL: - if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) - return pBase->openLoopPwrCntl ? true : false; - else - return false; - case EEP_RC_CHAIN_MASK: - if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) - return pBase->rcChainMask; - else - return 0; - case EEP_DAC_HPWR_5G: - if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_20) - return pBase->dacHiPwrMode_5G; - else - return 0; - case EEP_FRAC_N_5G: - if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_22) - return pBase->frac_n_5g; - else - return 0; - default: - return 0; - } -} - -static void ath9k_hw_def_set_gain(struct ath_hw *ah, - struct modal_eep_header *pModal, - struct ar5416_eeprom_def *eep, - u8 txRxAttenLocal, int regChainOffset, int i) -{ - if (AR5416_VER_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)); - } -} - -static void ath9k_hw_def_set_board_values(struct ath_hw *ah, - struct ath9k_channel *chan) -{ - struct modal_eep_header *pModal; - struct ar5416_eeprom_def *eep = &ah->eeprom.def; - int i, regChainOffset; - u8 txRxAttenLocal; - - pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); - txRxAttenLocal = IS_CHAN_2GHZ(chan) ? 23 : 44; - - REG_WRITE(ah, AR_PHY_SWITCH_COM, - ah->eep_ops->get_eeprom_antenna_cfg(ah, chan)); - - for (i = 0; i < AR5416_MAX_CHAINS; i++) { - if (AR_SREV_9280(ah)) { - if (i >= 2) - break; - } - - if (AR_SREV_5416_20_OR_LATER(ah) && - (ah->rxchainmask == 5 || 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_20_OR_LATER(ah)) - ath9k_hw_def_set_gain(ah, pModal, eep, txRxAttenLocal, - regChainOffset, i); - } - - 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); - 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 (AR5416_VER_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 (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) { - if (IS_CHAN_HT40(chan)) - REG_RMW_FIELD(ah, AR_PHY_SETTLING, - AR_PHY_SETTLING_SWITCH, - pModal->swSettleHt40); - } - - if (AR_SREV_9280_20_OR_LATER(ah) && - AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) - REG_RMW_FIELD(ah, AR_PHY_CCK_TX_CTRL, - AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK, - pModal->miscBits); - - - if (AR_SREV_9280_20(ah) && AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_20) { - if (IS_CHAN_2GHZ(chan)) - REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, - eep->baseEepHeader.dacLpMode); - else if (eep->baseEepHeader.dacHiPwrMode_5G) - REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, 0); - else - REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, - eep->baseEepHeader.dacLpMode); - - REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP, - pModal->miscBits >> 2); - - REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL9, - AR_PHY_TX_DESIRED_SCALE_CCK, - eep->baseEepHeader.desiredScaleCCK); - } -} - -static void ath9k_hw_def_set_addac(struct ath_hw *ah, - struct ath9k_channel *chan) -{ -#define XPA_LVL_FREQ(cnt) (pModal->xpaBiasLvlFreq[cnt]) - struct modal_eep_header *pModal; - struct ar5416_eeprom_def *eep = &ah->eeprom.def; - u8 biaslevel; - - if (ah->hw_version.macVersion != AR_SREV_VERSION_9160) - return; - - if (ah->eep_ops->get_eeprom_rev(ah) < 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 = XPA_LVL_FREQ(0) & 0xff; - biaslevel = (u8) (XPA_LVL_FREQ(0) >> 14); - - freqCount++; - - while (freqCount < 3) { - if (XPA_LVL_FREQ(freqCount) == 0x0) - break; - - freqBin = XPA_LVL_FREQ(freqCount) & 0xff; - if (resetFreqBin >= freqBin) - biaslevel = (u8)(XPA_LVL_FREQ(freqCount) >> 14); - else - break; - freqCount++; - } - } - - if (IS_CHAN_2GHZ(chan)) { - INI_RA(&ah->iniAddac, 7, 1) = (INI_RA(&ah->iniAddac, - 7, 1) & (~0x18)) | biaslevel << 3; - } else { - INI_RA(&ah->iniAddac, 6, 1) = (INI_RA(&ah->iniAddac, - 6, 1) & (~0xc0)) | biaslevel << 6; - } -#undef XPA_LVL_FREQ -} - -static void ath9k_hw_get_def_gain_boundaries_pdadcs(struct ath_hw *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_20_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 void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah, - struct ath9k_channel *chan, - int16_t *pTxPowerIndexOffset) -{ -#define SM_PD_GAIN(x) SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##x) -#define SM_PDGAIN_B(x, y) \ - SM((gainBoundaries[x]), AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##y) - - struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; - 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; - - 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; - } - - if (OLC_FOR_AR9280_20_LATER && IS_CHAN_2GHZ(chan)) { - pRawDataset = pEepData->calPierData2G[0]; - ah->initPDADC = ((struct calDataPerFreqOpLoop *) - pRawDataset)->vpdPdg[0][0]; - } - - 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_20_OR_LATER(ah) && - (ah->rxchainmask == 5 || 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]; - - - if (OLC_FOR_AR9280_20_LATER) { - u8 pcdacIdx; - u8 txPower; - - ath9k_get_txgain_index(ah, chan, - (struct calDataPerFreqOpLoop *)pRawDataset, - pCalBChans, numPiers, &txPower, &pcdacIdx); - ath9k_olc_get_pdadcs(ah, pcdacIdx, - txPower/2, pdadcValues); - } else { - ath9k_hw_get_def_gain_boundaries_pdadcs(ah, - chan, pRawDataset, - pCalBChans, numPiers, - pdGainOverlap_t2, - &tMinCalPower, - gainBoundaries, - pdadcValues, - numXpdGain); - } - - if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) { - if (OLC_FOR_AR9280_20_LATER) { - REG_WRITE(ah, - AR_PHY_TPCRG5 + regChainOffset, - SM(0x6, - AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | - SM_PD_GAIN(1) | SM_PD_GAIN(2) | - SM_PD_GAIN(3) | SM_PD_GAIN(4)); - } else { - REG_WRITE(ah, - AR_PHY_TPCRG5 + regChainOffset, - SM(pdGainOverlap_t2, - AR_PHY_TPCRG5_PD_GAIN_OVERLAP)| - SM_PDGAIN_B(0, 1) | - SM_PDGAIN_B(1, 2) | - SM_PDGAIN_B(2, 3) | - SM_PDGAIN_B(3, 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_EEPROM, - "PDADC (%d,%4x): %4.4x %8.8x\n", - i, regChainOffset, regOffset, - reg32); - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "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; -#undef SM_PD_GAIN -#undef SM_PDGAIN_B -} - -static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah, - struct ath9k_channel *chan, - int16_t *ratesArray, - u16 cfgCtl, - u16 AntennaReduction, - u16 twiceMaxRegulatoryPower, - u16 powerLimit) -{ -#define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */ -#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 10 /* 10*log10(3)*2 */ - - struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; - u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; - static const u16 tpScaleReductionTable[5] = - { 0, 3, 6, 9, AR5416_MAX_RATE_POWER }; - - int i; - int16_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} - }; - u16 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; - u16 twiceMinEdgePower; - - tx_chainmask = 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 = (int16_t)min(AntennaReduction - - twiceLargestAntenna, 0); - - maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna; - - if (ah->regulatory.tp_scale != ATH9K_TP_SCALE_MAX) { - maxRegAllowedPower -= - (tpScaleReductionTable[(ah->regulatory.tp_scale)] * 2); - } - - scaledPower = min(powerLimit, maxRegAllowedPower); - - switch (ar5416_get_ntxchains(tx_chainmask)) { - case 1: - break; - case 2: - scaledPower -= REDUCE_SCALED_POWER_BY_TWO_CHAIN; - break; - case 3: - scaledPower -= REDUCE_SCALED_POWER_BY_THREE_CHAIN; - break; - } - - scaledPower = max((u16)0, 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 (ah->eep_ops->get_eeprom_ver(ah) == 14 && - ah->eep_ops->get_eeprom_rev(ah) <= 2) - twiceMaxEdgePower = AR5416_MAX_RATE_POWER; - - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "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_EEPROM, - " 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), AR5416_NUM_BAND_EDGES); - - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - " 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_EEPROM, - " 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((u16)targetPowerCck.tPow2x[i], - minCtlPower); - } - break; - case CTL_11A: - case CTL_11G: - for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x); i++) { - targetPowerOfdm.tPow2x[i] = - min((u16)targetPowerOfdm.tPow2x[i], - minCtlPower); - } - break; - case CTL_5GHT20: - case CTL_2GHT20: - for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++) { - targetPowerHt20.tPow2x[i] = - min((u16)targetPowerHt20.tPow2x[i], - minCtlPower); - } - break; - case CTL_11B_EXT: - targetPowerCckExt.tPow2x[0] = min((u16) - targetPowerCckExt.tPow2x[0], - minCtlPower); - break; - case CTL_11A_EXT: - case CTL_11G_EXT: - targetPowerOfdmExt.tPow2x[0] = min((u16) - targetPowerOfdmExt.tPow2x[0], - minCtlPower); - break; - case CTL_5GHT40: - case CTL_2GHT40: - for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) { - targetPowerHt40.tPow2x[i] = - min((u16)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]; - } - } -} - -static void ath9k_hw_def_set_txpower(struct ath_hw *ah, - struct ath9k_channel *chan, - u16 cfgCtl, - u8 twiceAntennaReduction, - u8 twiceMaxRegulatoryPower, - u8 powerLimit) -{ -#define RT_AR_DELTA(x) (ratesArray[x] - cck_ofdm_delta) - struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; - struct modal_eep_header *pModal = - &(pEepData->modalHeader[IS_CHAN_2GHZ(chan)]); - int16_t ratesArray[Ar5416RateSize]; - int16_t txPowerIndexOffset = 0; - u8 ht40PowerIncForPdadc = 2; - int i, cck_ofdm_delta = 0; - - memset(ratesArray, 0, sizeof(ratesArray)); - - if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= - AR5416_EEP_MINOR_VER_2) { - ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; - } - - ath9k_hw_set_def_power_per_rate_table(ah, chan, - &ratesArray[0], cfgCtl, - twiceAntennaReduction, - twiceMaxRegulatoryPower, - powerLimit); - - ath9k_hw_set_def_power_cal_table(ah, chan, &txPowerIndexOffset); - - 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)) { - if (OLC_FOR_AR9280_20_LATER) { - cck_ofdm_delta = 2; - REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, - ATH9K_POW_SM(RT_AR_DELTA(rate2s), 24) - | ATH9K_POW_SM(RT_AR_DELTA(rate2l), 16) - | ATH9K_POW_SM(ratesArray[rateXr], 8) - | ATH9K_POW_SM(RT_AR_DELTA(rate1l), 0)); - REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, - ATH9K_POW_SM(RT_AR_DELTA(rate11s), 24) - | ATH9K_POW_SM(RT_AR_DELTA(rate11l), 16) - | ATH9K_POW_SM(RT_AR_DELTA(rate5_5s), 8) - | ATH9K_POW_SM(RT_AR_DELTA(rate5_5l), 0)); - } else { - 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)); - if (OLC_FOR_AR9280_20_LATER) { - REG_WRITE(ah, AR_PHY_POWER_TX_RATE9, - ATH9K_POW_SM(ratesArray[rateExtOfdm], 24) - | ATH9K_POW_SM(RT_AR_DELTA(rateExtCck), 16) - | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8) - | ATH9K_POW_SM(RT_AR_DELTA(rateDupCck), 0)); - } else { - 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->regulatory.max_power_level = - ratesArray[i] + AR5416_PWR_TABLE_OFFSET * 2; - else - ah->regulatory.max_power_level = ratesArray[i]; - - switch(ar5416_get_ntxchains(ah->txchainmask)) { - case 1: - break; - case 2: - ah->regulatory.max_power_level += INCREASE_MAXPOW_BY_TWO_CHAIN; - break; - case 3: - ah->regulatory.max_power_level += INCREASE_MAXPOW_BY_THREE_CHAIN; - break; - default: - DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, - "Invalid chainmask configuration\n"); - break; - } -} - -static u8 ath9k_hw_def_get_num_ant_config(struct ath_hw *ah, - enum ieee80211_band freq_band) -{ - struct ar5416_eeprom_def *eep = &ah->eeprom.def; - struct modal_eep_header *pModal = - &(eep->modalHeader[ATH9K_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 u16 ath9k_hw_def_get_eeprom_antenna_cfg(struct ath_hw *ah, - struct ath9k_channel *chan) -{ - struct ar5416_eeprom_def *eep = &ah->eeprom.def; - struct modal_eep_header *pModal = - &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); - - return pModal->antCtrlCommon & 0xFFFF; -} - -static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) -{ -#define EEP_DEF_SPURCHAN \ - (ah->eeprom.def.modalHeader[is2GHz].spurChans[i].spurChan) - - 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->config.spurchans[i][is2GHz]); - - switch (ah->config.spurmode) { - case SPUR_DISABLE: - break; - case SPUR_ENABLE_IOCTL: - spur_val = ah->config.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_DEF_SPURCHAN; - break; - } - - return spur_val; - -#undef EEP_DEF_SPURCHAN -} - -static struct eeprom_ops eep_def_ops = { - .check_eeprom = ath9k_hw_def_check_eeprom, - .get_eeprom = ath9k_hw_def_get_eeprom, - .fill_eeprom = ath9k_hw_def_fill_eeprom, - .get_eeprom_ver = ath9k_hw_def_get_eeprom_ver, - .get_eeprom_rev = ath9k_hw_def_get_eeprom_rev, - .get_num_ant_config = ath9k_hw_def_get_num_ant_config, - .get_eeprom_antenna_cfg = ath9k_hw_def_get_eeprom_antenna_cfg, - .set_board_values = ath9k_hw_def_set_board_values, - .set_addac = ath9k_hw_def_set_addac, - .set_txpower = ath9k_hw_def_set_txpower, - .get_spur_channel = ath9k_hw_def_get_spur_channel -}; - -int ath9k_hw_eeprom_attach(struct ath_hw *ah) +int ath9k_hw_eeprom_init(struct ath_hw *ah) { int status; - if (AR_SREV_9285(ah)) { + if (AR_SREV_9287(ah)) { + ah->eep_map = EEP_MAP_AR9287; + ah->eep_ops = &eep_AR9287_ops; + } else if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) { ah->eep_map = EEP_MAP_4KBITS; ah->eep_ops = &eep_4k_ops; } else { diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h index 67b8bd12941a..4fe33f7eee9d 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.h +++ b/drivers/net/wireless/ath/ath9k/eeprom.h @@ -100,6 +100,8 @@ #define AR5416_VER_MASK (eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) #define OLC_FOR_AR9280_20_LATER (AR_SREV_9280_20_OR_LATER(ah) && \ ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) +#define OLC_FOR_AR9287_10_LATER (AR_SREV_9287_10_OR_LATER(ah) && \ + ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL)) #define AR_EEPROM_RFSILENT_GPIO_SEL 0x001c #define AR_EEPROM_RFSILENT_GPIO_SEL_S 2 @@ -176,6 +178,57 @@ #define AR9280_TX_GAIN_TABLE_SIZE 22 +#define AR9287_EEP_VER 0xE +#define AR9287_EEP_VER_MINOR_MASK 0xFFF +#define AR9287_EEP_MINOR_VER_1 0x1 +#define AR9287_EEP_MINOR_VER_2 0x2 +#define AR9287_EEP_MINOR_VER_3 0x3 +#define AR9287_EEP_MINOR_VER AR9287_EEP_MINOR_VER_3 +#define AR9287_EEP_MINOR_VER_b AR9287_EEP_MINOR_VER +#define AR9287_EEP_NO_BACK_VER AR9287_EEP_MINOR_VER_1 + +#define AR9287_EEP_START_LOC 128 +#define AR9287_NUM_2G_CAL_PIERS 3 +#define AR9287_NUM_2G_CCK_TARGET_POWERS 3 +#define AR9287_NUM_2G_20_TARGET_POWERS 3 +#define AR9287_NUM_2G_40_TARGET_POWERS 3 +#define AR9287_NUM_CTLS 12 +#define AR9287_NUM_BAND_EDGES 4 +#define AR9287_NUM_PD_GAINS 4 +#define AR9287_PD_GAINS_IN_MASK 4 +#define AR9287_PD_GAIN_ICEPTS 1 +#define AR9287_EEPROM_MODAL_SPURS 5 +#define AR9287_MAX_RATE_POWER 63 +#define AR9287_NUM_PDADC_VALUES 128 +#define AR9287_NUM_RATES 16 +#define AR9287_BCHAN_UNUSED 0xFF +#define AR9287_MAX_PWR_RANGE_IN_HALF_DB 64 +#define AR9287_OPFLAGS_11A 0x01 +#define AR9287_OPFLAGS_11G 0x02 +#define AR9287_OPFLAGS_2G_HT40 0x08 +#define AR9287_OPFLAGS_2G_HT20 0x20 +#define AR9287_OPFLAGS_5G_HT40 0x04 +#define AR9287_OPFLAGS_5G_HT20 0x10 +#define AR9287_EEPMISC_BIG_ENDIAN 0x01 +#define AR9287_EEPMISC_WOW 0x02 +#define AR9287_MAX_CHAINS 2 +#define AR9287_ANT_16S 32 +#define AR9287_custdatasize 20 + +#define AR9287_NUM_ANT_CHAIN_FIELDS 6 +#define AR9287_NUM_ANT_COMMON_FIELDS 4 +#define AR9287_SIZE_ANT_CHAIN_FIELD 2 +#define AR9287_SIZE_ANT_COMMON_FIELD 4 +#define AR9287_ANT_CHAIN_MASK 0x3 +#define AR9287_ANT_COMMON_MASK 0xf +#define AR9287_CHAIN_0_IDX 0 +#define AR9287_CHAIN_1_IDX 1 +#define AR9287_DATA_SZ 32 + +#define AR9287_PWR_TABLE_OFFSET_DB -5 + +#define AR9287_CHECKSUM_LOCATION (AR9287_EEP_START_LOC + 1) + enum eeprom_param { EEP_NFTHRESH_5, EEP_NFTHRESH_2, @@ -199,7 +252,11 @@ enum eeprom_param { EEP_OL_PWRCTRL, EEP_RC_CHAIN_MASK, EEP_DAC_HPWR_5G, - EEP_FRAC_N_5G + EEP_FRAC_N_5G, + EEP_DEV_TYPE, + EEP_TEMPSENSE_SLOPE, + EEP_TEMPSENSE_SLOPE_PAL_ON, + EEP_PWR_TABLE_OFFSET }; enum ar5416_rates { @@ -328,46 +385,123 @@ struct calDataPerFreqOpLoop { } __packed; struct modal_eep_4k_header { - u32 antCtrlChain[AR5416_EEP4K_MAX_CHAINS]; - u32 antCtrlCommon; - u8 antennaGainCh[AR5416_EEP4K_MAX_CHAINS]; - u8 switchSettling; - u8 txRxAttenCh[AR5416_EEP4K_MAX_CHAINS]; - u8 rxTxMarginCh[AR5416_EEP4K_MAX_CHAINS]; - u8 adcDesiredSize; - u8 pgaDesiredSize; - u8 xlnaGainCh[AR5416_EEP4K_MAX_CHAINS]; - u8 txEndToXpaOff; - u8 txEndToRxOn; - u8 txFrameToXpaOn; - u8 thresh62; - u8 noiseFloorThreshCh[AR5416_EEP4K_MAX_CHAINS]; - u8 xpdGain; - u8 xpd; - u8 iqCalICh[AR5416_EEP4K_MAX_CHAINS]; - u8 iqCalQCh[AR5416_EEP4K_MAX_CHAINS]; - u8 pdGainOverlap; - u8 ob_01; - u8 db1_01; - u8 xpaBiasLvl; - u8 txFrameToDataStart; - u8 txFrameToPaOn; - u8 ht40PowerIncForPdadc; - u8 bswAtten[AR5416_EEP4K_MAX_CHAINS]; - u8 bswMargin[AR5416_EEP4K_MAX_CHAINS]; - u8 swSettleHt40; - u8 xatten2Db[AR5416_EEP4K_MAX_CHAINS]; - u8 xatten2Margin[AR5416_EEP4K_MAX_CHAINS]; - u8 db2_01; - u8 version; - u16 ob_234; - u16 db1_234; - u16 db2_234; - u8 futureModal[4]; - + u32 antCtrlChain[AR5416_EEP4K_MAX_CHAINS]; + u32 antCtrlCommon; + u8 antennaGainCh[AR5416_EEP4K_MAX_CHAINS]; + u8 switchSettling; + u8 txRxAttenCh[AR5416_EEP4K_MAX_CHAINS]; + u8 rxTxMarginCh[AR5416_EEP4K_MAX_CHAINS]; + u8 adcDesiredSize; + u8 pgaDesiredSize; + u8 xlnaGainCh[AR5416_EEP4K_MAX_CHAINS]; + u8 txEndToXpaOff; + u8 txEndToRxOn; + u8 txFrameToXpaOn; + u8 thresh62; + u8 noiseFloorThreshCh[AR5416_EEP4K_MAX_CHAINS]; + u8 xpdGain; + u8 xpd; + u8 iqCalICh[AR5416_EEP4K_MAX_CHAINS]; + u8 iqCalQCh[AR5416_EEP4K_MAX_CHAINS]; + u8 pdGainOverlap; +#ifdef __BIG_ENDIAN_BITFIELD + u8 ob_1:4, ob_0:4; + u8 db1_1:4, db1_0:4; +#else + u8 ob_0:4, ob_1:4; + u8 db1_0:4, db1_1:4; +#endif + u8 xpaBiasLvl; + u8 txFrameToDataStart; + u8 txFrameToPaOn; + u8 ht40PowerIncForPdadc; + u8 bswAtten[AR5416_EEP4K_MAX_CHAINS]; + u8 bswMargin[AR5416_EEP4K_MAX_CHAINS]; + u8 swSettleHt40; + u8 xatten2Db[AR5416_EEP4K_MAX_CHAINS]; + u8 xatten2Margin[AR5416_EEP4K_MAX_CHAINS]; +#ifdef __BIG_ENDIAN_BITFIELD + u8 db2_1:4, db2_0:4; +#else + u8 db2_0:4, db2_1:4; +#endif + u8 version; +#ifdef __BIG_ENDIAN_BITFIELD + u8 ob_3:4, ob_2:4; + u8 antdiv_ctl1:4, ob_4:4; + u8 db1_3:4, db1_2:4; + u8 antdiv_ctl2:4, db1_4:4; + u8 db2_2:4, db2_3:4; + u8 reserved:4, db2_4:4; +#else + u8 ob_2:4, ob_3:4; + u8 ob_4:4, antdiv_ctl1:4; + u8 db1_2:4, db1_3:4; + u8 db1_4:4, antdiv_ctl2:4; + u8 db2_2:4, db2_3:4; + u8 db2_4:4, reserved:4; +#endif + u8 futureModal[4]; struct spur_chan spurChans[AR5416_EEPROM_MODAL_SPURS]; } __packed; +struct base_eep_ar9287_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 openLoopPwrCntl; + int8_t pwrTableOffset; + int8_t tempSensSlope; + int8_t tempSensSlopePalOn; + u8 futureBase[29]; +} __packed; + +struct modal_eep_ar9287_header { + u32 antCtrlChain[AR9287_MAX_CHAINS]; + u32 antCtrlCommon; + int8_t antennaGainCh[AR9287_MAX_CHAINS]; + u8 switchSettling; + u8 txRxAttenCh[AR9287_MAX_CHAINS]; + u8 rxTxMarginCh[AR9287_MAX_CHAINS]; + int8_t adcDesiredSize; + u8 txEndToXpaOff; + u8 txEndToRxOn; + u8 txFrameToXpaOn; + u8 thresh62; + int8_t noiseFloorThreshCh[AR9287_MAX_CHAINS]; + u8 xpdGain; + u8 xpd; + int8_t iqCalICh[AR9287_MAX_CHAINS]; + int8_t iqCalQCh[AR9287_MAX_CHAINS]; + u8 pdGainOverlap; + u8 xpaBiasLvl; + u8 txFrameToDataStart; + u8 txFrameToPaOn; + u8 ht40PowerIncForPdadc; + u8 bswAtten[AR9287_MAX_CHAINS]; + u8 bswMargin[AR9287_MAX_CHAINS]; + u8 swSettleHt40; + u8 version; + u8 db1; + u8 db2; + u8 ob_cck; + u8 ob_psk; + u8 ob_qam; + u8 ob_pal_off; + u8 futureModal[30]; + struct spur_chan spurChans[AR9287_EEPROM_MODAL_SPURS]; +} __packed; struct cal_data_per_freq { u8 pwrPdg[AR5416_NUM_PD_GAINS][AR5416_PD_GAIN_ICEPTS]; @@ -402,6 +536,28 @@ struct cal_ctl_edges { } __packed; #endif +struct cal_data_op_loop_ar9287 { + u8 pwrPdg[2][5]; + u8 vpdPdg[2][5]; + u8 pcdac[2][5]; + u8 empty[2][5]; +} __packed; + +struct cal_data_per_freq_ar9287 { + u8 pwrPdg[AR9287_NUM_PD_GAINS][AR9287_PD_GAIN_ICEPTS]; + u8 vpdPdg[AR9287_NUM_PD_GAINS][AR9287_PD_GAIN_ICEPTS]; +} __packed; + +union cal_data_per_freq_ar9287_u { + struct cal_data_op_loop_ar9287 calDataOpen; + struct cal_data_per_freq_ar9287 calDataClose; +} __packed; + +struct cal_ctl_data_ar9287 { + struct cal_ctl_edges + ctlEdges[AR9287_MAX_CHAINS][AR9287_NUM_BAND_EDGES]; +} __packed; + struct cal_ctl_data { struct cal_ctl_edges ctlEdges[AR5416_MAX_CHAINS][AR5416_NUM_BAND_EDGES]; @@ -461,6 +617,26 @@ struct ar5416_eeprom_4k { u8 padding; } __packed; +struct ar9287_eeprom { + struct base_eep_ar9287_header baseEepHeader; + u8 custData[AR9287_DATA_SZ]; + struct modal_eep_ar9287_header modalHeader; + u8 calFreqPier2G[AR9287_NUM_2G_CAL_PIERS]; + union cal_data_per_freq_ar9287_u + calPierData2G[AR9287_MAX_CHAINS][AR9287_NUM_2G_CAL_PIERS]; + struct cal_target_power_leg + calTargetPowerCck[AR9287_NUM_2G_CCK_TARGET_POWERS]; + struct cal_target_power_leg + calTargetPower2G[AR9287_NUM_2G_20_TARGET_POWERS]; + struct cal_target_power_ht + calTargetPower2GHT20[AR9287_NUM_2G_20_TARGET_POWERS]; + struct cal_target_power_ht + calTargetPower2GHT40[AR9287_NUM_2G_40_TARGET_POWERS]; + u8 ctlIndex[AR9287_NUM_CTLS]; + struct cal_ctl_data_ar9287 ctlData[AR9287_NUM_CTLS]; + u8 padding; +} __packed; + enum reg_ext_bitmap { REG_EXT_JAPAN_MIDBAND = 1, REG_EXT_FCC_DFS_HT40 = 2, @@ -480,6 +656,7 @@ struct ath9k_country_entry { enum ath9k_eep_map { EEP_MAP_DEFAULT = 0x0, EEP_MAP_4KBITS, + EEP_MAP_AR9287, EEP_MAP_MAX }; @@ -500,10 +677,39 @@ struct eeprom_ops { u16 (*get_spur_channel)(struct ath_hw *ah, u16 i, bool is2GHz); }; +void ath9k_hw_analog_shift_rmw(struct ath_hw *ah, u32 reg, u32 mask, + u32 shift, u32 val); +int16_t ath9k_hw_interpolate(u16 target, u16 srcLeft, u16 srcRight, + int16_t targetLeft, + int16_t targetRight); +bool ath9k_hw_get_lower_upper_index(u8 target, u8 *pList, u16 listSize, + u16 *indexL, u16 *indexR); +bool ath9k_hw_nvram_read(struct ath_hw *ah, u32 off, u16 *data); +void ath9k_hw_fill_vpd_table(u8 pwrMin, u8 pwrMax, u8 *pPwrList, + u8 *pVpdList, u16 numIntercepts, + u8 *pRetVpdList); +void ath9k_hw_get_legacy_target_powers(struct ath_hw *ah, + struct ath9k_channel *chan, + struct cal_target_power_leg *powInfo, + u16 numChannels, + struct cal_target_power_leg *pNewPower, + u16 numRates, bool isExtTarget); +void ath9k_hw_get_target_powers(struct ath_hw *ah, + struct ath9k_channel *chan, + struct cal_target_power_ht *powInfo, + u16 numChannels, + struct cal_target_power_ht *pNewPower, + u16 numRates, bool isHt40Target); +u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower, + bool is2GHz, int num_band_edges); +int ath9k_hw_eeprom_init(struct ath_hw *ah); + #define ar5416_get_ntxchains(_txchainmask) \ (((_txchainmask >> 2) & 1) + \ ((_txchainmask >> 1) & 1) + (_txchainmask & 1)) -int ath9k_hw_eeprom_attach(struct ath_hw *ah); +extern const struct eeprom_ops eep_def_ops; +extern const struct eeprom_ops eep_4k_ops; +extern const struct eeprom_ops eep_AR9287_ops; #endif /* EEPROM_H */ diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c new file mode 100644 index 000000000000..b8eca7be5f3a --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -0,0 +1,1188 @@ +/* + * Copyright (c) 2008-2009 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 "ath9k.h" + +static int ath9k_hw_4k_get_eeprom_ver(struct ath_hw *ah) +{ + return ((ah->eeprom.map4k.baseEepHeader.version >> 12) & 0xF); +} + +static int ath9k_hw_4k_get_eeprom_rev(struct ath_hw *ah) +{ + return ((ah->eeprom.map4k.baseEepHeader.version) & 0xFFF); +} + +static bool ath9k_hw_4k_fill_eeprom(struct ath_hw *ah) +{ +#define SIZE_EEPROM_4K (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) + u16 *eep_data = (u16 *)&ah->eeprom.map4k; + int addr, eep_start_loc = 0; + + eep_start_loc = 64; + + if (!ath9k_hw_use_flash(ah)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Reading from EEPROM, not flash\n"); + } + + for (addr = 0; addr < SIZE_EEPROM_4K; addr++) { + if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Unable to read eeprom region \n"); + return false; + } + eep_data++; + } + + return true; +#undef SIZE_EEPROM_4K +} + +static int ath9k_hw_4k_check_eeprom(struct ath_hw *ah) +{ +#define EEPROM_4K_SIZE (sizeof(struct ar5416_eeprom_4k) / sizeof(u16)) + struct ar5416_eeprom_4k *eep = + (struct ar5416_eeprom_4k *) &ah->eeprom.map4k; + u16 *eepdata, temp, magic, magic2; + u32 sum = 0, el; + bool need_swap = false; + int i, addr; + + + if (!ath9k_hw_use_flash(ah)) { + if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, + &magic)) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Reading Magic # failed\n"); + return false; + } + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Read Magic = 0x%04X\n", magic); + + if (magic != AR5416_EEPROM_MAGIC) { + magic2 = swab16(magic); + + if (magic2 == AR5416_EEPROM_MAGIC) { + need_swap = true; + eepdata = (u16 *) (&ah->eeprom); + + for (addr = 0; addr < EEPROM_4K_SIZE; addr++) { + temp = swab16(*eepdata); + *eepdata = temp; + eepdata++; + } + } else { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Invalid EEPROM Magic. " + "endianness mismatch.\n"); + return -EINVAL; + } + } + } + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n", + need_swap ? "True" : "False"); + + if (need_swap) + el = swab16(ah->eeprom.map4k.baseEepHeader.length); + else + el = ah->eeprom.map4k.baseEepHeader.length; + + if (el > sizeof(struct ar5416_eeprom_4k)) + el = sizeof(struct ar5416_eeprom_4k) / sizeof(u16); + else + el = el / sizeof(u16); + + eepdata = (u16 *)(&ah->eeprom); + + for (i = 0; i < el; i++) + sum ^= *eepdata++; + + if (need_swap) { + u32 integer; + 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; + + integer = swab32(eep->modalHeader.antCtrlCommon); + eep->modalHeader.antCtrlCommon = integer; + + for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) { + integer = swab32(eep->modalHeader.antCtrlChain[i]); + eep->modalHeader.antCtrlChain[i] = integer; + } + + for (i = 0; i < AR5416_EEPROM_MODAL_SPURS; i++) { + word = swab16(eep->modalHeader.spurChans[i].spurChan); + eep->modalHeader.spurChans[i].spurChan = word; + } + } + + if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || + ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Bad EEPROM checksum 0x%x or revision 0x%04x\n", + sum, ah->eep_ops->get_eeprom_ver(ah)); + return -EINVAL; + } + + return 0; +#undef EEPROM_4K_SIZE +} + +static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah, + enum eeprom_param param) +{ + struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; + struct modal_eep_4k_header *pModal = &eep->modalHeader; + struct base_eep_header_4k *pBase = &eep->baseEepHeader; + + switch (param) { + case EEP_NFTHRESH_2: + return pModal->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_2: + return pModal->ob_0; + case EEP_DB_2: + return pModal->db1_1; + 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; + case EEP_FRAC_N_5G: + return 0; + default: + return 0; + } +} + +static void ath9k_hw_get_4k_gain_boundaries_pdadcs(struct ath_hw *ah, + struct ath9k_channel *chan, + struct cal_data_per_freq_4k *pRawDataSet, + u8 *bChans, u16 availPiers, + u16 tPdGainOverlap, int16_t *pMinCalPower, + u16 *pPdGainBoundaries, u8 *pPDADCValues, + u16 numXpdGains) +{ +#define TMP_VAL_VPD_TABLE \ + ((vpdTableI[i][sizeCurrVpdTable - 1] + (ss - maxIndex + 1) * vpdStep)); + int i, j, k; + int16_t ss; + u16 idxL = 0, idxR = 0, numPiers; + static u8 vpdTableL[AR5416_EEP4K_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + static u8 vpdTableR[AR5416_EEP4K_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + static u8 vpdTableI[AR5416_EEP4K_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + + u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR; + u8 minPwrT4[AR5416_EEP4K_NUM_PD_GAINS]; + u8 maxPwrT4[AR5416_EEP4K_NUM_PD_GAINS]; + int16_t vpdStep; + int16_t tmpVal; + u16 sizeCurrVpdTable, maxIndex, tgtIndex; + bool match; + int16_t minDelta = 0; + struct chan_centers centers; +#define PD_GAIN_BOUNDARY_DEFAULT 58; + + 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_EEP4K_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_EEP4K_PD_GAIN_ICEPTS - 1], + pPwrR[AR5416_EEP4K_PD_GAIN_ICEPTS - 1]); + + + ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], + pPwrL, pVpdL, + AR5416_EEP4K_PD_GAIN_ICEPTS, + vpdTableL[i]); + ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], + pPwrR, pVpdR, + AR5416_EEP4K_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_20_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) TMP_VAL_VPD_TABLE; + pPDADCValues[k++] = (u8)((tmpVal > 255) ? + 255 : tmpVal); + ss++; + } + } + } + + while (i < AR5416_EEP4K_PD_GAINS_IN_MASK) { + pPdGainBoundaries[i] = PD_GAIN_BOUNDARY_DEFAULT; + i++; + } + + while (k < AR5416_NUM_PDADC_VALUES) { + pPDADCValues[k] = pPDADCValues[k - 1]; + k++; + } + + return; +#undef TMP_VAL_VPD_TABLE +} + +static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah, + struct ath9k_channel *chan, + int16_t *pTxPowerIndexOffset) +{ + struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k; + struct cal_data_per_freq_4k *pRawDataset; + u8 *pCalBChans = NULL; + u16 pdGainOverlap_t2; + static u8 pdadcValues[AR5416_NUM_PDADC_VALUES]; + u16 gainBoundaries[AR5416_EEP4K_PD_GAINS_IN_MASK]; + u16 numPiers, i, j; + int16_t tMinCalPower; + u16 numXpdGain, xpdMask; + u16 xpdGainValues[AR5416_EEP4K_NUM_PD_GAINS] = { 0, 0 }; + u32 reg32, regOffset, regChainOffset; + + xpdMask = pEepData->modalHeader.xpdGain; + + if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_2) { + pdGainOverlap_t2 = + pEepData->modalHeader.pdGainOverlap; + } else { + pdGainOverlap_t2 = (u16)(MS(REG_READ(ah, AR_PHY_TPCRG5), + AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); + } + + pCalBChans = pEepData->calFreqPier2G; + numPiers = AR5416_EEP4K_NUM_2G_CAL_PIERS; + + numXpdGain = 0; + + for (i = 1; i <= AR5416_EEP4K_PD_GAINS_IN_MASK; i++) { + if ((xpdMask >> (AR5416_EEP4K_PD_GAINS_IN_MASK - i)) & 1) { + if (numXpdGain >= AR5416_EEP4K_NUM_PD_GAINS) + break; + xpdGainValues[numXpdGain] = + (u16)(AR5416_EEP4K_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, 0); + + for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) { + if (AR_SREV_5416_20_OR_LATER(ah) && + (ah->rxchainmask == 5 || ah->txchainmask == 5) && + (i != 0)) { + regChainOffset = (i == 1) ? 0x2000 : 0x1000; + } else + regChainOffset = i * 0x1000; + + if (pEepData->baseEepHeader.txMask & (1 << i)) { + pRawDataset = pEepData->calPierData2G[i]; + + ath9k_hw_get_4k_gain_boundaries_pdadcs(ah, chan, + pRawDataset, pCalBChans, + numPiers, pdGainOverlap_t2, + &tMinCalPower, gainBoundaries, + pdadcValues, numXpdGain); + + if ((i == 0) || AR_SREV_5416_20_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_EEPROM, + "PDADC (%d,%4x): %4.4x %8.8x\n", + i, regChainOffset, regOffset, + reg32); + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "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; +} + +static void ath9k_hw_set_4k_power_per_rate_table(struct ath_hw *ah, + struct ath9k_channel *chan, + int16_t *ratesArray, + u16 cfgCtl, + u16 AntennaReduction, + u16 twiceMaxRegulatoryPower, + u16 powerLimit) +{ +#define CMP_TEST_GRP \ + (((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)) + + struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); + int i; + int16_t twiceLargestAntenna; + u16 twiceMinEdgePower; + u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + u16 scaledPower = 0, minCtlPower, maxRegAllowedPower; + u16 numCtlModes, *pCtlMode, ctlMode, freq; + struct chan_centers centers; + struct cal_ctl_data_4k *rep; + struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k; + static const u16 tpScaleReductionTable[5] = + { 0, 3, 6, 9, AR5416_MAX_RATE_POWER }; + 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} + }; + u16 ctlModesFor11g[] = + { CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT, + CTL_2GHT40 + }; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + twiceLargestAntenna = pEepData->modalHeader.antennaGainCh[0]; + twiceLargestAntenna = (int16_t)min(AntennaReduction - + twiceLargestAntenna, 0); + + maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna; + if (regulatory->tp_scale != ATH9K_TP_SCALE_MAX) { + maxRegAllowedPower -= + (tpScaleReductionTable[(regulatory->tp_scale)] * 2); + } + + scaledPower = min(powerLimit, maxRegAllowedPower); + scaledPower = max((u16)0, scaledPower); + + 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); + } + + 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 (ah->eep_ops->get_eeprom_ver(ah) == 14 && + ah->eep_ops->get_eeprom_rev(ah) <= 2) + twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + + for (i = 0; (i < AR5416_EEP4K_NUM_CTLS) && + pEepData->ctlIndex[i]; i++) { + + if (CMP_TEST_GRP) { + rep = &(pEepData->ctlData[i]); + + twiceMinEdgePower = ath9k_hw_get_max_edge_power( + freq, + rep->ctlEdges[ + ar5416_get_ntxchains(ah->txchainmask) - 1], + IS_CHAN_2GHZ(chan), + AR5416_EEP4K_NUM_BAND_EDGES); + + if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { + twiceMaxEdgePower = + min(twiceMaxEdgePower, + twiceMinEdgePower); + } else { + twiceMaxEdgePower = twiceMinEdgePower; + break; + } + } + } + + minCtlPower = (u8)min(twiceMaxEdgePower, scaledPower); + + switch (pCtlMode[ctlMode]) { + case CTL_11B: + for (i = 0; i < ARRAY_SIZE(targetPowerCck.tPow2x); i++) { + targetPowerCck.tPow2x[i] = + min((u16)targetPowerCck.tPow2x[i], + minCtlPower); + } + break; + case CTL_11G: + for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x); i++) { + targetPowerOfdm.tPow2x[i] = + min((u16)targetPowerOfdm.tPow2x[i], + minCtlPower); + } + break; + case CTL_2GHT20: + for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++) { + targetPowerHt20.tPow2x[i] = + min((u16)targetPowerHt20.tPow2x[i], + minCtlPower); + } + break; + case CTL_11B_EXT: + targetPowerCckExt.tPow2x[0] = + min((u16)targetPowerCckExt.tPow2x[0], + minCtlPower); + break; + case CTL_11G_EXT: + targetPowerOfdmExt.tPow2x[0] = + min((u16)targetPowerOfdmExt.tPow2x[0], + minCtlPower); + break; + case CTL_2GHT40: + for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) { + targetPowerHt40.tPow2x[i] = + min((u16)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]; + + 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]; + ratesArray[rateExtCck] = targetPowerCckExt.tPow2x[0]; + } + +#undef CMP_TEST_GRP +} + +static void ath9k_hw_4k_set_txpower(struct ath_hw *ah, + struct ath9k_channel *chan, + u16 cfgCtl, + u8 twiceAntennaReduction, + u8 twiceMaxRegulatoryPower, + u8 powerLimit) +{ + struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); + struct ar5416_eeprom_4k *pEepData = &ah->eeprom.map4k; + struct modal_eep_4k_header *pModal = &pEepData->modalHeader; + 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; + } + + ath9k_hw_set_4k_power_per_rate_table(ah, chan, + &ratesArray[0], cfgCtl, + twiceAntennaReduction, + twiceMaxRegulatoryPower, + powerLimit); + + ath9k_hw_set_4k_power_cal_table(ah, chan, &txPowerIndexOffset); + + 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; + } + + + /* Update regulatory */ + + i = rate6mb; + if (IS_CHAN_HT40(chan)) + i = rateHt40_0; + else if (IS_CHAN_HT20(chan)) + i = rateHt20_0; + + regulatory->max_power_level = ratesArray[i]; + + if (AR_SREV_9280_10_OR_LATER(ah)) { + for (i = 0; i < Ar5416RateSize; i++) + ratesArray[i] -= AR5416_PWR_TABLE_OFFSET * 2; + } + + /* OFDM power per rate */ + 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)); + + /* CCK power per rate */ + 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)); + + /* HT20 power per rate */ + 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)); + + /* HT40 power per rate */ + 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)); + } +} + +static void ath9k_hw_4k_set_addac(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct modal_eep_4k_header *pModal; + struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; + u8 biaslevel; + + if (ah->hw_version.macVersion != AR_SREV_VERSION_9160) + return; + + if (ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_MINOR_VER_7) + return; + + pModal = &eep->modalHeader; + + if (pModal->xpaBiasLvl != 0xff) { + biaslevel = pModal->xpaBiasLvl; + INI_RA(&ah->iniAddac, 7, 1) = + (INI_RA(&ah->iniAddac, 7, 1) & (~0x18)) | biaslevel << 3; + } +} + +static void ath9k_hw_4k_set_gain(struct ath_hw *ah, + struct modal_eep_4k_header *pModal, + struct ar5416_eeprom_4k *eep, + u8 txRxAttenLocal) +{ + REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0, + pModal->antCtrlChain[0]); + + REG_WRITE(ah, AR_PHY_TIMING_CTRL4(0), + (REG_READ(ah, AR_PHY_TIMING_CTRL4(0)) & + ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | + AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | + SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | + SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); + + if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_3) { + txRxAttenLocal = pModal->txRxAttenCh[0]; + + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, + AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[0]); + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, + AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]); + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, + AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, + pModal->xatten2Margin[0]); + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, + AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]); + + /* Set the block 1 value to block 0 value */ + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000, + AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, + pModal->bswMargin[0]); + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000, + AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]); + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000, + AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, + pModal->xatten2Margin[0]); + REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000, + AR_PHY_GAIN_2GHZ_XATTEN2_DB, + pModal->xatten2Db[0]); + } + + REG_RMW_FIELD(ah, AR_PHY_RXGAIN, + AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); + REG_RMW_FIELD(ah, AR_PHY_RXGAIN, + AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]); + + REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000, + AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); + REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000, + AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]); + + if (AR_SREV_9285_11(ah)) + REG_WRITE(ah, AR9285_AN_TOP4, (AR9285_AN_TOP4_DEFAULT | 0x14)); +} + +/* + * Read EEPROM header info and program the device for correct operation + * given the channel value. + */ +static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct modal_eep_4k_header *pModal; + struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; + u8 txRxAttenLocal; + u8 ob[5], db1[5], db2[5]; + u8 ant_div_control1, ant_div_control2; + u32 regVal; + + pModal = &eep->modalHeader; + txRxAttenLocal = 23; + + REG_WRITE(ah, AR_PHY_SWITCH_COM, + ah->eep_ops->get_eeprom_antenna_cfg(ah, chan)); + + /* Single chain for 4K EEPROM*/ + ath9k_hw_4k_set_gain(ah, pModal, eep, txRxAttenLocal); + + /* Initialize Ant Diversity settings from EEPROM */ + if (pModal->version >= 3) { + ant_div_control1 = pModal->antdiv_ctl1; + ant_div_control2 = pModal->antdiv_ctl2; + + regVal = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); + regVal &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL)); + + regVal |= SM(ant_div_control1, + AR_PHY_9285_ANT_DIV_CTL); + regVal |= SM(ant_div_control2, + AR_PHY_9285_ANT_DIV_ALT_LNACONF); + regVal |= SM((ant_div_control2 >> 2), + AR_PHY_9285_ANT_DIV_MAIN_LNACONF); + regVal |= SM((ant_div_control1 >> 1), + AR_PHY_9285_ANT_DIV_ALT_GAINTB); + regVal |= SM((ant_div_control1 >> 2), + AR_PHY_9285_ANT_DIV_MAIN_GAINTB); + + + REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal); + regVal = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); + regVal = REG_READ(ah, AR_PHY_CCK_DETECT); + regVal &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + regVal |= SM((ant_div_control1 >> 3), + AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + + REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal); + regVal = REG_READ(ah, AR_PHY_CCK_DETECT); + } + + if (pModal->version >= 2) { + ob[0] = pModal->ob_0; + ob[1] = pModal->ob_1; + ob[2] = pModal->ob_2; + ob[3] = pModal->ob_3; + ob[4] = pModal->ob_4; + + db1[0] = pModal->db1_0; + db1[1] = pModal->db1_1; + db1[2] = pModal->db1_2; + db1[3] = pModal->db1_3; + db1[4] = pModal->db1_4; + + db2[0] = pModal->db2_0; + db2[1] = pModal->db2_1; + db2[2] = pModal->db2_2; + db2[3] = pModal->db2_3; + db2[4] = pModal->db2_4; + } else if (pModal->version == 1) { + ob[0] = pModal->ob_0; + ob[1] = ob[2] = ob[3] = ob[4] = pModal->ob_1; + db1[0] = pModal->db1_0; + db1[1] = db1[2] = db1[3] = db1[4] = pModal->db1_1; + db2[0] = pModal->db2_0; + db2[1] = db2[2] = db2[3] = db2[4] = pModal->db2_1; + } else { + int i; + + for (i = 0; i < 5; i++) { + ob[i] = pModal->ob_0; + db1[i] = pModal->db1_0; + db2[i] = pModal->db1_0; + } + } + + if (AR_SREV_9271(ah)) { + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G3, + AR9271_AN_RF2G3_OB_cck, + AR9271_AN_RF2G3_OB_cck_S, + ob[0]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G3, + AR9271_AN_RF2G3_OB_psk, + AR9271_AN_RF2G3_OB_psk_S, + ob[1]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G3, + AR9271_AN_RF2G3_OB_qam, + AR9271_AN_RF2G3_OB_qam_S, + ob[2]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G3, + AR9271_AN_RF2G3_DB_1, + AR9271_AN_RF2G3_DB_1_S, + db1[0]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G4, + AR9271_AN_RF2G4_DB_2, + AR9271_AN_RF2G4_DB_2_S, + db2[0]); + } else { + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G3, + AR9285_AN_RF2G3_OB_0, + AR9285_AN_RF2G3_OB_0_S, + ob[0]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G3, + AR9285_AN_RF2G3_OB_1, + AR9285_AN_RF2G3_OB_1_S, + ob[1]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G3, + AR9285_AN_RF2G3_OB_2, + AR9285_AN_RF2G3_OB_2_S, + ob[2]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G3, + AR9285_AN_RF2G3_OB_3, + AR9285_AN_RF2G3_OB_3_S, + ob[3]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G3, + AR9285_AN_RF2G3_OB_4, + AR9285_AN_RF2G3_OB_4_S, + ob[4]); + + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G3, + AR9285_AN_RF2G3_DB1_0, + AR9285_AN_RF2G3_DB1_0_S, + db1[0]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G3, + AR9285_AN_RF2G3_DB1_1, + AR9285_AN_RF2G3_DB1_1_S, + db1[1]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G3, + AR9285_AN_RF2G3_DB1_2, + AR9285_AN_RF2G3_DB1_2_S, + db1[2]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB1_3, + AR9285_AN_RF2G4_DB1_3_S, + db1[3]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB1_4, + AR9285_AN_RF2G4_DB1_4_S, db1[4]); + + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB2_0, + AR9285_AN_RF2G4_DB2_0_S, + db2[0]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB2_1, + AR9285_AN_RF2G4_DB2_1_S, + db2[1]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB2_2, + AR9285_AN_RF2G4_DB2_2_S, + db2[2]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB2_3, + AR9285_AN_RF2G4_DB2_3_S, + db2[3]); + ath9k_hw_analog_shift_rmw(ah, + AR9285_AN_RF2G4, + AR9285_AN_RF2G4_DB2_4, + AR9285_AN_RF2G4_DB2_4_S, + db2[4]); + } + + + if (AR_SREV_9285_11(ah)) + REG_WRITE(ah, AR9285_AN_TOP4, AR9285_AN_TOP4_DEFAULT); + + 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); + + 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); + 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); + + 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); + } +} + +static u16 ath9k_hw_4k_get_eeprom_antenna_cfg(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; + struct modal_eep_4k_header *pModal = &eep->modalHeader; + + return pModal->antCtrlCommon & 0xFFFF; +} + +static u8 ath9k_hw_4k_get_num_ant_config(struct ath_hw *ah, + enum ieee80211_band freq_band) +{ + return 1; +} + +static u16 ath9k_hw_4k_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) +{ +#define EEP_MAP4K_SPURCHAN \ + (ah->eeprom.map4k.modalHeader.spurChans[i].spurChan) + + 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->config.spurchans[i][is2GHz]); + + switch (ah->config.spurmode) { + case SPUR_DISABLE: + break; + case SPUR_ENABLE_IOCTL: + spur_val = ah->config.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_MAP4K_SPURCHAN; + break; + } + + return spur_val; + +#undef EEP_MAP4K_SPURCHAN +} + +const struct eeprom_ops eep_4k_ops = { + .check_eeprom = ath9k_hw_4k_check_eeprom, + .get_eeprom = ath9k_hw_4k_get_eeprom, + .fill_eeprom = ath9k_hw_4k_fill_eeprom, + .get_eeprom_ver = ath9k_hw_4k_get_eeprom_ver, + .get_eeprom_rev = ath9k_hw_4k_get_eeprom_rev, + .get_num_ant_config = ath9k_hw_4k_get_num_ant_config, + .get_eeprom_antenna_cfg = ath9k_hw_4k_get_eeprom_antenna_cfg, + .set_board_values = ath9k_hw_4k_set_board_values, + .set_addac = ath9k_hw_4k_set_addac, + .set_txpower = ath9k_hw_4k_set_txpower, + .get_spur_channel = ath9k_hw_4k_get_spur_channel +}; diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c new file mode 100644 index 000000000000..c20c21a79b21 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -0,0 +1,1177 @@ +/* + * Copyright (c) 2008-2009 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 "ath9k.h" + +static int ath9k_hw_AR9287_get_eeprom_ver(struct ath_hw *ah) +{ + return (ah->eeprom.map9287.baseEepHeader.version >> 12) & 0xF; +} + +static int ath9k_hw_AR9287_get_eeprom_rev(struct ath_hw *ah) +{ + return (ah->eeprom.map9287.baseEepHeader.version) & 0xFFF; +} + +static bool ath9k_hw_AR9287_fill_eeprom(struct ath_hw *ah) +{ + struct ar9287_eeprom *eep = &ah->eeprom.map9287; + u16 *eep_data; + int addr, eep_start_loc = AR9287_EEP_START_LOC; + eep_data = (u16 *)eep; + + if (!ath9k_hw_use_flash(ah)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Reading from EEPROM, not flash\n"); + } + + for (addr = 0; addr < sizeof(struct ar9287_eeprom) / sizeof(u16); + addr++) { + if (!ath9k_hw_nvram_read(ah, addr + eep_start_loc, eep_data)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Unable to read eeprom region \n"); + return false; + } + eep_data++; + } + return true; +} + +static int ath9k_hw_AR9287_check_eeprom(struct ath_hw *ah) +{ + u32 sum = 0, el, integer; + u16 temp, word, magic, magic2, *eepdata; + int i, addr; + bool need_swap = false; + struct ar9287_eeprom *eep = &ah->eeprom.map9287; + + if (!ath9k_hw_use_flash(ah)) { + if (!ath9k_hw_nvram_read + (ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Reading Magic # failed\n"); + return false; + } + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Read Magic = 0x%04X\n", magic); + if (magic != AR5416_EEPROM_MAGIC) { + magic2 = swab16(magic); + + if (magic2 == AR5416_EEPROM_MAGIC) { + need_swap = true; + eepdata = (u16 *)(&ah->eeprom); + + for (addr = 0; + addr < sizeof(struct ar9287_eeprom) / sizeof(u16); + addr++) { + temp = swab16(*eepdata); + *eepdata = temp; + eepdata++; + } + } else { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Invalid EEPROM Magic. " + "endianness mismatch.\n"); + return -EINVAL; + } + } + } + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n", need_swap ? + "True" : "False"); + + if (need_swap) + el = swab16(ah->eeprom.map9287.baseEepHeader.length); + else + el = ah->eeprom.map9287.baseEepHeader.length; + + if (el > sizeof(struct ar9287_eeprom)) + el = sizeof(struct ar9287_eeprom) / sizeof(u16); + else + el = el / sizeof(u16); + + eepdata = (u16 *)(&ah->eeprom); + for (i = 0; i < el; i++) + sum ^= *eepdata++; + + if (need_swap) { + 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; + + integer = swab32(eep->modalHeader.antCtrlCommon); + eep->modalHeader.antCtrlCommon = integer; + + for (i = 0; i < AR9287_MAX_CHAINS; i++) { + integer = swab32(eep->modalHeader.antCtrlChain[i]); + eep->modalHeader.antCtrlChain[i] = integer; + } + + for (i = 0; i < AR9287_EEPROM_MODAL_SPURS; i++) { + word = swab16(eep->modalHeader.spurChans[i].spurChan); + eep->modalHeader.spurChans[i].spurChan = word; + } + } + + if (sum != 0xffff || ah->eep_ops->get_eeprom_ver(ah) != AR9287_EEP_VER + || ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Bad EEPROM checksum 0x%x or revision 0x%04x\n", + sum, ah->eep_ops->get_eeprom_ver(ah)); + return -EINVAL; + } + + return 0; +} + +static u32 ath9k_hw_AR9287_get_eeprom(struct ath_hw *ah, + enum eeprom_param param) +{ + struct ar9287_eeprom *eep = &ah->eeprom.map9287; + struct modal_eep_ar9287_header *pModal = &eep->modalHeader; + struct base_eep_ar9287_header *pBase = &eep->baseEepHeader; + u16 ver_minor; + + ver_minor = pBase->version & AR9287_EEP_VER_MINOR_MASK; + switch (param) { + case EEP_NFTHRESH_2: + return pModal->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_MINOR_REV: + return ver_minor; + case EEP_TX_MASK: + return pBase->txMask; + case EEP_RX_MASK: + return pBase->rxMask; + case EEP_DEV_TYPE: + return pBase->deviceType; + case EEP_OL_PWRCTRL: + return pBase->openLoopPwrCntl; + case EEP_TEMPSENSE_SLOPE: + if (ver_minor >= AR9287_EEP_MINOR_VER_2) + return pBase->tempSensSlope; + else + return 0; + case EEP_TEMPSENSE_SLOPE_PAL_ON: + if (ver_minor >= AR9287_EEP_MINOR_VER_3) + return pBase->tempSensSlopePalOn; + else + return 0; + default: + return 0; + } +} + + +static void ath9k_hw_get_AR9287_gain_boundaries_pdadcs(struct ath_hw *ah, + struct ath9k_channel *chan, + struct cal_data_per_freq_ar9287 *pRawDataSet, + u8 *bChans, u16 availPiers, + u16 tPdGainOverlap, int16_t *pMinCalPower, + u16 *pPdGainBoundaries, u8 *pPDADCValues, + u16 numXpdGains) +{ +#define TMP_VAL_VPD_TABLE \ + ((vpdTableI[i][sizeCurrVpdTable - 1] + (ss - maxIndex + 1) * vpdStep)); + + int i, j, k; + int16_t ss; + u16 idxL = 0, idxR = 0, numPiers; + u8 *pVpdL, *pVpdR, *pPwrL, *pPwrR; + u8 minPwrT4[AR9287_NUM_PD_GAINS]; + u8 maxPwrT4[AR9287_NUM_PD_GAINS]; + int16_t vpdStep; + int16_t tmpVal; + u16 sizeCurrVpdTable, maxIndex, tgtIndex; + bool match; + int16_t minDelta = 0; + struct chan_centers centers; + static u8 vpdTableL[AR5416_EEP4K_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + static u8 vpdTableR[AR5416_EEP4K_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + static u8 vpdTableI[AR5416_EEP4K_NUM_PD_GAINS] + [AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + for (numPiers = 0; numPiers < availPiers; numPiers++) { + if (bChans[numPiers] == AR9287_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], + AR9287_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[AR9287_PD_GAIN_ICEPTS - 1], + pPwrR[AR9287_PD_GAIN_ICEPTS - 1]); + + ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], + pPwrL, pVpdL, + AR9287_PD_GAIN_ICEPTS, + vpdTableL[i]); + ath9k_hw_fill_vpd_table(minPwrT4[i], maxPwrT4[i], + pPwrR, pVpdR, + AR9287_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_20_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 < (AR9287_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 < (AR9287_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 < (AR9287_NUM_PDADC_VALUES - 1))) { + tmpVal = (int16_t) TMP_VAL_VPD_TABLE; + pPDADCValues[k++] = (u8)((tmpVal > 255) ? + 255 : tmpVal); + ss++; + } + } + } + + while (i < AR9287_PD_GAINS_IN_MASK) { + pPdGainBoundaries[i] = pPdGainBoundaries[i-1]; + i++; + } + + while (k < AR9287_NUM_PDADC_VALUES) { + pPDADCValues[k] = pPDADCValues[k-1]; + k++; + } + +#undef TMP_VAL_VPD_TABLE +} + +static void ar9287_eeprom_get_tx_gain_index(struct ath_hw *ah, + struct ath9k_channel *chan, + struct cal_data_op_loop_ar9287 *pRawDatasetOpLoop, + u8 *pCalChans, u16 availPiers, + int8_t *pPwr) +{ + u16 idxL = 0, idxR = 0, numPiers; + bool match; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + for (numPiers = 0; numPiers < availPiers; numPiers++) { + if (pCalChans[numPiers] == AR9287_BCHAN_UNUSED) + break; + } + + match = ath9k_hw_get_lower_upper_index( + (u8)FREQ2FBIN(centers.synth_center, IS_CHAN_2GHZ(chan)), + pCalChans, numPiers, + &idxL, &idxR); + + if (match) { + *pPwr = (int8_t) pRawDatasetOpLoop[idxL].pwrPdg[0][0]; + } else { + *pPwr = ((int8_t) pRawDatasetOpLoop[idxL].pwrPdg[0][0] + + (int8_t) pRawDatasetOpLoop[idxR].pwrPdg[0][0])/2; + } + +} + +static void ar9287_eeprom_olpc_set_pdadcs(struct ath_hw *ah, + int32_t txPower, u16 chain) +{ + u32 tmpVal; + u32 a; + + tmpVal = REG_READ(ah, 0xa270); + tmpVal = tmpVal & 0xFCFFFFFF; + tmpVal = tmpVal | (0x3 << 24); + REG_WRITE(ah, 0xa270, tmpVal); + + tmpVal = REG_READ(ah, 0xb270); + tmpVal = tmpVal & 0xFCFFFFFF; + tmpVal = tmpVal | (0x3 << 24); + REG_WRITE(ah, 0xb270, tmpVal); + + if (chain == 0) { + tmpVal = REG_READ(ah, 0xa398); + tmpVal = tmpVal & 0xff00ffff; + a = (txPower)&0xff; + tmpVal = tmpVal | (a << 16); + REG_WRITE(ah, 0xa398, tmpVal); + } + + if (chain == 1) { + tmpVal = REG_READ(ah, 0xb398); + tmpVal = tmpVal & 0xff00ffff; + a = (txPower)&0xff; + tmpVal = tmpVal | (a << 16); + REG_WRITE(ah, 0xb398, tmpVal); + } +} + +static void ath9k_hw_set_AR9287_power_cal_table(struct ath_hw *ah, + struct ath9k_channel *chan, + int16_t *pTxPowerIndexOffset) +{ + struct cal_data_per_freq_ar9287 *pRawDataset; + struct cal_data_op_loop_ar9287 *pRawDatasetOpenLoop; + u8 *pCalBChans = NULL; + u16 pdGainOverlap_t2; + u8 pdadcValues[AR9287_NUM_PDADC_VALUES]; + u16 gainBoundaries[AR9287_PD_GAINS_IN_MASK]; + u16 numPiers = 0, i, j; + int16_t tMinCalPower; + u16 numXpdGain, xpdMask; + u16 xpdGainValues[AR9287_NUM_PD_GAINS] = {0, 0, 0, 0}; + u32 reg32, regOffset, regChainOffset; + int16_t modalIdx, diff = 0; + struct ar9287_eeprom *pEepData = &ah->eeprom.map9287; + modalIdx = IS_CHAN_2GHZ(chan) ? 1 : 0; + xpdMask = pEepData->modalHeader.xpdGain; + if ((pEepData->baseEepHeader.version & AR9287_EEP_VER_MINOR_MASK) >= + AR9287_EEP_MINOR_VER_2) + pdGainOverlap_t2 = pEepData->modalHeader.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 = AR9287_NUM_2G_CAL_PIERS; + if (ath9k_hw_AR9287_get_eeprom(ah, EEP_OL_PWRCTRL)) { + pRawDatasetOpenLoop = + (struct cal_data_op_loop_ar9287 *) + pEepData->calPierData2G[0]; + ah->initPDADC = pRawDatasetOpenLoop->vpdPdg[0][0]; + } + } + + numXpdGain = 0; + for (i = 1; i <= AR9287_PD_GAINS_IN_MASK; i++) { + if ((xpdMask >> (AR9287_PD_GAINS_IN_MASK - i)) & 1) { + if (numXpdGain >= AR9287_NUM_PD_GAINS) + break; + xpdGainValues[numXpdGain] = + (u16)(AR9287_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 < AR9287_MAX_CHAINS; i++) { + regChainOffset = i * 0x1000; + if (pEepData->baseEepHeader.txMask & (1 << i)) { + pRawDatasetOpenLoop = (struct cal_data_op_loop_ar9287 *) + pEepData->calPierData2G[i]; + if (ath9k_hw_AR9287_get_eeprom(ah, EEP_OL_PWRCTRL)) { + int8_t txPower; + ar9287_eeprom_get_tx_gain_index(ah, chan, + pRawDatasetOpenLoop, + pCalBChans, numPiers, + &txPower); + ar9287_eeprom_olpc_set_pdadcs(ah, txPower, i); + } else { + pRawDataset = + (struct cal_data_per_freq_ar9287 *) + pEepData->calPierData2G[i]; + ath9k_hw_get_AR9287_gain_boundaries_pdadcs( + ah, chan, pRawDataset, + pCalBChans, numPiers, + pdGainOverlap_t2, + &tMinCalPower, gainBoundaries, + pdadcValues, numXpdGain); + } + + if (i == 0) { + if (!ath9k_hw_AR9287_get_eeprom( + ah, EEP_OL_PWRCTRL)) { + 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)); + } + } + + if ((int32_t)AR9287_PWR_TABLE_OFFSET_DB != + pEepData->baseEepHeader.pwrTableOffset) { + diff = (u16) + (pEepData->baseEepHeader.pwrTableOffset + - (int32_t)AR9287_PWR_TABLE_OFFSET_DB); + diff *= 2; + + for (j = 0; + j < ((u16)AR9287_NUM_PDADC_VALUES-diff); + j++) + pdadcValues[j] = pdadcValues[j+diff]; + + for (j = (u16)(AR9287_NUM_PDADC_VALUES-diff); + j < AR9287_NUM_PDADC_VALUES; j++) + pdadcValues[j] = + pdadcValues[ + AR9287_NUM_PDADC_VALUES-diff]; + } + + if (!ath9k_hw_AR9287_get_eeprom(ah, EEP_OL_PWRCTRL)) { + 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_EEPROM, + "PDADC (%d,%4x): %4.4x %8.8x\n", + i, regChainOffset, regOffset, + reg32); + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "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; +} + +static void ath9k_hw_set_AR9287_power_per_rate_table(struct ath_hw *ah, + struct ath9k_channel *chan, int16_t *ratesArray, u16 cfgCtl, + u16 AntennaReduction, u16 twiceMaxRegulatoryPower, + u16 powerLimit) +{ +#define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 +#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 10 + struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); + u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + static const u16 tpScaleReductionTable[5] = + { 0, 3, 6, 9, AR5416_MAX_RATE_POWER }; + int i; + int16_t twiceLargestAntenna; + struct cal_ctl_data_ar9287 *rep; + struct cal_target_power_leg targetPowerOfdm = {0, {0, 0, 0, 0} }, + 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} }; + u16 scaledPower = 0, minCtlPower, maxRegAllowedPower; + u16 ctlModesFor11g[] = + {CTL_11B, CTL_11G, CTL_2GHT20, + CTL_11B_EXT, CTL_11G_EXT, CTL_2GHT40}; + u16 numCtlModes = 0, *pCtlMode = NULL, ctlMode, freq; + struct chan_centers centers; + int tx_chainmask; + u16 twiceMinEdgePower; + struct ar9287_eeprom *pEepData = &ah->eeprom.map9287; + tx_chainmask = ah->txchainmask; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + twiceLargestAntenna = max(pEepData->modalHeader.antennaGainCh[0], + pEepData->modalHeader.antennaGainCh[1]); + + twiceLargestAntenna = (int16_t)min((AntennaReduction) - + twiceLargestAntenna, 0); + + maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna; + if (regulatory->tp_scale != ATH9K_TP_SCALE_MAX) + maxRegAllowedPower -= + (tpScaleReductionTable[(regulatory->tp_scale)] * 2); + + scaledPower = min(powerLimit, maxRegAllowedPower); + + switch (ar5416_get_ntxchains(tx_chainmask)) { + case 1: + break; + case 2: + scaledPower -= REDUCE_SCALED_POWER_BY_TWO_CHAIN; + break; + case 3: + scaledPower -= REDUCE_SCALED_POWER_BY_THREE_CHAIN; + break; + } + scaledPower = max((u16)0, 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, + AR9287_NUM_2G_CCK_TARGET_POWERS, + &targetPowerCck, 4, false); + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData->calTargetPower2G, + AR9287_NUM_2G_20_TARGET_POWERS, + &targetPowerOfdm, 4, false); + ath9k_hw_get_target_powers(ah, chan, + pEepData->calTargetPower2GHT20, + AR9287_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, + AR9287_NUM_2G_40_TARGET_POWERS, + &targetPowerHt40, 8, true); + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData->calTargetPowerCck, + AR9287_NUM_2G_CCK_TARGET_POWERS, + &targetPowerCckExt, 4, true); + ath9k_hw_get_legacy_target_powers(ah, chan, + pEepData->calTargetPower2G, + AR9287_NUM_2G_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 (ah->eep_ops->get_eeprom_ver(ah) == 14 && + ah->eep_ops->get_eeprom_rev(ah) <= 2) + twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + + for (i = 0; (i < AR9287_NUM_CTLS) && pEepData->ctlIndex[i]; i++) { + 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), AR5416_NUM_BAND_EDGES); + + if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) + twiceMaxEdgePower = min( + twiceMaxEdgePower, + twiceMinEdgePower); + else { + twiceMaxEdgePower = twiceMinEdgePower; + break; + } + } + } + + minCtlPower = (u8)min(twiceMaxEdgePower, scaledPower); + + switch (pCtlMode[ctlMode]) { + case CTL_11B: + for (i = 0; + i < ARRAY_SIZE(targetPowerCck.tPow2x); + i++) { + targetPowerCck.tPow2x[i] = (u8)min( + (u16)targetPowerCck.tPow2x[i], + minCtlPower); + } + break; + case CTL_11A: + case CTL_11G: + for (i = 0; + i < ARRAY_SIZE(targetPowerOfdm.tPow2x); + i++) { + targetPowerOfdm.tPow2x[i] = (u8)min( + (u16)targetPowerOfdm.tPow2x[i], + minCtlPower); + } + break; + case CTL_5GHT20: + case CTL_2GHT20: + for (i = 0; + i < ARRAY_SIZE(targetPowerHt20.tPow2x); + i++) { + targetPowerHt20.tPow2x[i] = (u8)min( + (u16)targetPowerHt20.tPow2x[i], + minCtlPower); + } + break; + case CTL_11B_EXT: + targetPowerCckExt.tPow2x[0] = (u8)min( + (u16)targetPowerCckExt.tPow2x[0], + minCtlPower); + break; + case CTL_11A_EXT: + case CTL_11G_EXT: + targetPowerOfdmExt.tPow2x[0] = (u8)min( + (u16)targetPowerOfdmExt.tPow2x[0], + minCtlPower); + break; + case CTL_5GHT40: + case CTL_2GHT40: + for (i = 0; + i < ARRAY_SIZE(targetPowerHt40.tPow2x); + i++) { + targetPowerHt40.tPow2x[i] = (u8)min( + (u16)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]; + } + +#undef REDUCE_SCALED_POWER_BY_TWO_CHAIN +#undef REDUCE_SCALED_POWER_BY_THREE_CHAIN +} + +static void ath9k_hw_AR9287_set_txpower(struct ath_hw *ah, + struct ath9k_channel *chan, u16 cfgCtl, + u8 twiceAntennaReduction, + u8 twiceMaxRegulatoryPower, + u8 powerLimit) +{ +#define INCREASE_MAXPOW_BY_TWO_CHAIN 6 +#define INCREASE_MAXPOW_BY_THREE_CHAIN 10 + struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); + struct ar9287_eeprom *pEepData = &ah->eeprom.map9287; + struct modal_eep_ar9287_header *pModal = &pEepData->modalHeader; + int16_t ratesArray[Ar5416RateSize]; + int16_t txPowerIndexOffset = 0; + u8 ht40PowerIncForPdadc = 2; + int i; + + memset(ratesArray, 0, sizeof(ratesArray)); + + if ((pEepData->baseEepHeader.version & AR9287_EEP_VER_MINOR_MASK) >= + AR9287_EEP_MINOR_VER_2) + ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; + + ath9k_hw_set_AR9287_power_per_rate_table(ah, chan, + &ratesArray[0], cfgCtl, + twiceAntennaReduction, + twiceMaxRegulatoryPower, + powerLimit); + + ath9k_hw_set_AR9287_power_cal_table(ah, chan, &txPowerIndexOffset); + + for (i = 0; i < ARRAY_SIZE(ratesArray); i++) { + ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]); + if (ratesArray[i] > AR9287_MAX_RATE_POWER) + ratesArray[i] = AR9287_MAX_RATE_POWER; + } + + if (AR_SREV_9280_10_OR_LATER(ah)) { + for (i = 0; i < Ar5416RateSize; i++) + ratesArray[i] -= AR9287_PWR_TABLE_OFFSET_DB * 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)) { + if (ath9k_hw_AR9287_get_eeprom(ah, EEP_OL_PWRCTRL)) { + REG_WRITE(ah, AR_PHY_POWER_TX_RATE7, + ATH9K_POW_SM(ratesArray[rateHt40_3], 24) + | ATH9K_POW_SM(ratesArray[rateHt40_2], 16) + | ATH9K_POW_SM(ratesArray[rateHt40_1], 8) + | ATH9K_POW_SM(ratesArray[rateHt40_0], 0)); + + REG_WRITE(ah, AR_PHY_POWER_TX_RATE8, + ATH9K_POW_SM(ratesArray[rateHt40_7], 24) + | ATH9K_POW_SM(ratesArray[rateHt40_6], 16) + | ATH9K_POW_SM(ratesArray[rateHt40_5], 8) + | ATH9K_POW_SM(ratesArray[rateHt40_4], 0)); + } else { + 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)); + } + + if (IS_CHAN_2GHZ(chan)) + i = rate1l; + else + i = rate6mb; + + if (AR_SREV_9280_10_OR_LATER(ah)) + regulatory->max_power_level = + ratesArray[i] + AR9287_PWR_TABLE_OFFSET_DB * 2; + else + regulatory->max_power_level = ratesArray[i]; + + switch (ar5416_get_ntxchains(ah->txchainmask)) { + case 1: + break; + case 2: + regulatory->max_power_level += + INCREASE_MAXPOW_BY_TWO_CHAIN; + break; + case 3: + regulatory->max_power_level += + INCREASE_MAXPOW_BY_THREE_CHAIN; + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Invalid chainmask configuration\n"); + break; + } +} + +static void ath9k_hw_AR9287_set_addac(struct ath_hw *ah, + struct ath9k_channel *chan) +{ +} + +static void ath9k_hw_AR9287_set_board_values(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct ar9287_eeprom *eep = &ah->eeprom.map9287; + struct modal_eep_ar9287_header *pModal = &eep->modalHeader; + u16 antWrites[AR9287_ANT_16S]; + u32 regChainOffset; + u8 txRxAttenLocal; + int i, j, offset_num; + + pModal = &eep->modalHeader; + + antWrites[0] = (u16)((pModal->antCtrlCommon >> 28) & 0xF); + antWrites[1] = (u16)((pModal->antCtrlCommon >> 24) & 0xF); + antWrites[2] = (u16)((pModal->antCtrlCommon >> 20) & 0xF); + antWrites[3] = (u16)((pModal->antCtrlCommon >> 16) & 0xF); + antWrites[4] = (u16)((pModal->antCtrlCommon >> 12) & 0xF); + antWrites[5] = (u16)((pModal->antCtrlCommon >> 8) & 0xF); + antWrites[6] = (u16)((pModal->antCtrlCommon >> 4) & 0xF); + antWrites[7] = (u16)(pModal->antCtrlCommon & 0xF); + + offset_num = 8; + + for (i = 0, j = offset_num; i < AR9287_MAX_CHAINS; i++) { + antWrites[j++] = (u16)((pModal->antCtrlChain[i] >> 28) & 0xf); + antWrites[j++] = (u16)((pModal->antCtrlChain[i] >> 10) & 0x3); + antWrites[j++] = (u16)((pModal->antCtrlChain[i] >> 8) & 0x3); + antWrites[j++] = 0; + antWrites[j++] = (u16)((pModal->antCtrlChain[i] >> 6) & 0x3); + antWrites[j++] = (u16)((pModal->antCtrlChain[i] >> 4) & 0x3); + antWrites[j++] = (u16)((pModal->antCtrlChain[i] >> 2) & 0x3); + antWrites[j++] = (u16)(pModal->antCtrlChain[i] & 0x3); + } + + REG_WRITE(ah, AR_PHY_SWITCH_COM, + ah->eep_ops->get_eeprom_antenna_cfg(ah, chan)); + + for (i = 0; i < AR9287_MAX_CHAINS; i++) { + 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)); + + txRxAttenLocal = pModal->txRxAttenCh[i]; + + 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_RXGAIN + regChainOffset, + AR9280_PHY_RXGAIN_TXRX_ATTEN, + txRxAttenLocal); + REG_RMW_FIELD(ah, AR_PHY_RXGAIN + regChainOffset, + AR9280_PHY_RXGAIN_TXRX_MARGIN, + pModal->rxTxMarginCh[i]); + } + + + if (IS_CHAN_HT40(chan)) + REG_RMW_FIELD(ah, AR_PHY_SETTLING, + AR_PHY_SETTLING_SWITCH, pModal->swSettleHt40); + else + 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); + + 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); + + 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); + + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_RF2G3_CH0, AR9287_AN_RF2G3_DB1, + AR9287_AN_RF2G3_DB1_S, pModal->db1); + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_RF2G3_CH0, AR9287_AN_RF2G3_DB2, + AR9287_AN_RF2G3_DB2_S, pModal->db2); + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_RF2G3_CH0, + AR9287_AN_RF2G3_OB_CCK, + AR9287_AN_RF2G3_OB_CCK_S, pModal->ob_cck); + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_RF2G3_CH0, + AR9287_AN_RF2G3_OB_PSK, + AR9287_AN_RF2G3_OB_PSK_S, pModal->ob_psk); + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_RF2G3_CH0, + AR9287_AN_RF2G3_OB_QAM, + AR9287_AN_RF2G3_OB_QAM_S, pModal->ob_qam); + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_RF2G3_CH0, + AR9287_AN_RF2G3_OB_PAL_OFF, + AR9287_AN_RF2G3_OB_PAL_OFF_S, + pModal->ob_pal_off); + + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_RF2G3_CH1, + AR9287_AN_RF2G3_DB1, AR9287_AN_RF2G3_DB1_S, + pModal->db1); + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_RF2G3_CH1, AR9287_AN_RF2G3_DB2, + AR9287_AN_RF2G3_DB2_S, pModal->db2); + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_RF2G3_CH1, + AR9287_AN_RF2G3_OB_CCK, + AR9287_AN_RF2G3_OB_CCK_S, pModal->ob_cck); + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_RF2G3_CH1, + AR9287_AN_RF2G3_OB_PSK, + AR9287_AN_RF2G3_OB_PSK_S, pModal->ob_psk); + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_RF2G3_CH1, + AR9287_AN_RF2G3_OB_QAM, + AR9287_AN_RF2G3_OB_QAM_S, pModal->ob_qam); + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_RF2G3_CH1, + AR9287_AN_RF2G3_OB_PAL_OFF, + AR9287_AN_RF2G3_OB_PAL_OFF_S, + pModal->ob_pal_off); + + 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); + + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_TOP2, + AR9287_AN_TOP2_XPABIAS_LVL, + AR9287_AN_TOP2_XPABIAS_LVL_S, + pModal->xpaBiasLvl); +} + +static u8 ath9k_hw_AR9287_get_num_ant_config(struct ath_hw *ah, + enum ieee80211_band freq_band) +{ + return 1; +} + +static u16 ath9k_hw_AR9287_get_eeprom_antenna_cfg(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct ar9287_eeprom *eep = &ah->eeprom.map9287; + struct modal_eep_ar9287_header *pModal = &eep->modalHeader; + + return pModal->antCtrlCommon & 0xFFFF; +} + +static u16 ath9k_hw_AR9287_get_spur_channel(struct ath_hw *ah, + u16 i, bool is2GHz) +{ +#define EEP_MAP9287_SPURCHAN \ + (ah->eeprom.map9287.modalHeader.spurChans[i].spurChan) + 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->config.spurchans[i][is2GHz]); + + switch (ah->config.spurmode) { + case SPUR_DISABLE: + break; + case SPUR_ENABLE_IOCTL: + spur_val = ah->config.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_MAP9287_SPURCHAN; + break; + } + + return spur_val; + +#undef EEP_MAP9287_SPURCHAN +} + +const struct eeprom_ops eep_AR9287_ops = { + .check_eeprom = ath9k_hw_AR9287_check_eeprom, + .get_eeprom = ath9k_hw_AR9287_get_eeprom, + .fill_eeprom = ath9k_hw_AR9287_fill_eeprom, + .get_eeprom_ver = ath9k_hw_AR9287_get_eeprom_ver, + .get_eeprom_rev = ath9k_hw_AR9287_get_eeprom_rev, + .get_num_ant_config = ath9k_hw_AR9287_get_num_ant_config, + .get_eeprom_antenna_cfg = ath9k_hw_AR9287_get_eeprom_antenna_cfg, + .set_board_values = ath9k_hw_AR9287_set_board_values, + .set_addac = ath9k_hw_AR9287_set_addac, + .set_txpower = ath9k_hw_AR9287_set_txpower, + .get_spur_channel = ath9k_hw_AR9287_get_spur_channel +}; diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c new file mode 100644 index 000000000000..ae7fb5dcb266 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -0,0 +1,1387 @@ +/* + * Copyright (c) 2008-2009 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 "ath9k.h" + +static void ath9k_get_txgain_index(struct ath_hw *ah, + struct ath9k_channel *chan, + struct calDataPerFreqOpLoop *rawDatasetOpLoop, + u8 *calChans, u16 availPiers, u8 *pwr, u8 *pcdacIdx) +{ + u8 pcdac, i = 0; + u16 idxL = 0, idxR = 0, numPiers; + bool match; + struct chan_centers centers; + + ath9k_hw_get_channel_centers(ah, chan, ¢ers); + + for (numPiers = 0; numPiers < availPiers; numPiers++) + if (calChans[numPiers] == AR5416_BCHAN_UNUSED) + break; + + match = ath9k_hw_get_lower_upper_index( + (u8)FREQ2FBIN(centers.synth_center, IS_CHAN_2GHZ(chan)), + calChans, numPiers, &idxL, &idxR); + if (match) { + pcdac = rawDatasetOpLoop[idxL].pcdac[0][0]; + *pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0]; + } else { + pcdac = rawDatasetOpLoop[idxR].pcdac[0][0]; + *pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] + + rawDatasetOpLoop[idxR].pwrPdg[0][0])/2; + } + + while (pcdac > ah->originalGain[i] && + i < (AR9280_TX_GAIN_TABLE_SIZE - 1)) + i++; + + *pcdacIdx = i; + return; +} + +static void ath9k_olc_get_pdadcs(struct ath_hw *ah, + u32 initTxGain, + int txPower, + u8 *pPDADCValues) +{ + u32 i; + u32 offset; + + REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, + AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); + REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, + AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); + + REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, + AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain); + + offset = txPower; + for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++) + if (i < offset) + pPDADCValues[i] = 0x0; + else + pPDADCValues[i] = 0xFF; +} + +static int ath9k_hw_def_get_eeprom_ver(struct ath_hw *ah) +{ + return ((ah->eeprom.def.baseEepHeader.version >> 12) & 0xF); +} + +static int ath9k_hw_def_get_eeprom_rev(struct ath_hw *ah) +{ + return ((ah->eeprom.def.baseEepHeader.version) & 0xFFF); +} + +static bool ath9k_hw_def_fill_eeprom(struct ath_hw *ah) +{ +#define SIZE_EEPROM_DEF (sizeof(struct ar5416_eeprom_def) / sizeof(u16)) + u16 *eep_data = (u16 *)&ah->eeprom.def; + int addr, ar5416_eep_start_loc = 0x100; + + for (addr = 0; addr < SIZE_EEPROM_DEF; addr++) { + if (!ath9k_hw_nvram_read(ah, addr + ar5416_eep_start_loc, + eep_data)) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Unable to read eeprom region\n"); + return false; + } + eep_data++; + } + return true; +#undef SIZE_EEPROM_DEF +} + +static int ath9k_hw_def_check_eeprom(struct ath_hw *ah) +{ + struct ar5416_eeprom_def *eep = + (struct ar5416_eeprom_def *) &ah->eeprom.def; + u16 *eepdata, temp, magic, magic2; + u32 sum = 0, el; + bool need_swap = false; + int i, addr, size; + + if (!ath9k_hw_nvram_read(ah, AR5416_EEPROM_MAGIC_OFFSET, &magic)) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Reading Magic # failed\n"); + return false; + } + + if (!ath9k_hw_use_flash(ah)) { + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Read Magic = 0x%04X\n", magic); + + if (magic != AR5416_EEPROM_MAGIC) { + magic2 = swab16(magic); + + if (magic2 == AR5416_EEPROM_MAGIC) { + size = sizeof(struct ar5416_eeprom_def); + need_swap = true; + eepdata = (u16 *) (&ah->eeprom); + + for (addr = 0; addr < size / sizeof(u16); addr++) { + temp = swab16(*eepdata); + *eepdata = temp; + eepdata++; + } + } else { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Invalid EEPROM Magic. " + "Endianness mismatch.\n"); + return -EINVAL; + } + } + } + + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, "need_swap = %s.\n", + need_swap ? "True" : "False"); + + if (need_swap) + el = swab16(ah->eeprom.def.baseEepHeader.length); + else + el = ah->eeprom.def.baseEepHeader.length; + + if (el > sizeof(struct ar5416_eeprom_def)) + el = sizeof(struct ar5416_eeprom_def) / sizeof(u16); + else + el = el / sizeof(u16); + + eepdata = (u16 *)(&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 || ah->eep_ops->get_eeprom_ver(ah) != AR5416_EEP_VER || + ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_NO_BACK_VER) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Bad EEPROM checksum 0x%x or revision 0x%04x\n", + sum, ah->eep_ops->get_eeprom_ver(ah)); + return -EINVAL; + } + + return 0; +} + +static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah, + enum eeprom_param param) +{ + struct ar5416_eeprom_def *eep = &ah->eeprom.def; + 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 AR5416_VER_MASK; + case EEP_TX_MASK: + return pBase->txMask; + case EEP_RX_MASK: + return pBase->rxMask; + case EEP_RXGAIN_TYPE: + return pBase->rxGainType; + case EEP_TXGAIN_TYPE: + return pBase->txGainType; + case EEP_OL_PWRCTRL: + if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) + return pBase->openLoopPwrCntl ? true : false; + else + return false; + case EEP_RC_CHAIN_MASK: + if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) + return pBase->rcChainMask; + else + return 0; + case EEP_DAC_HPWR_5G: + if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_20) + return pBase->dacHiPwrMode_5G; + else + return 0; + case EEP_FRAC_N_5G: + if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_22) + return pBase->frac_n_5g; + else + return 0; + default: + return 0; + } +} + +static void ath9k_hw_def_set_gain(struct ath_hw *ah, + struct modal_eep_header *pModal, + struct ar5416_eeprom_def *eep, + u8 txRxAttenLocal, int regChainOffset, int i) +{ + if (AR5416_VER_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)); + } +} + +static void ath9k_hw_def_set_board_values(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct modal_eep_header *pModal; + struct ar5416_eeprom_def *eep = &ah->eeprom.def; + int i, regChainOffset; + u8 txRxAttenLocal; + + pModal = &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); + txRxAttenLocal = IS_CHAN_2GHZ(chan) ? 23 : 44; + + REG_WRITE(ah, AR_PHY_SWITCH_COM, + ah->eep_ops->get_eeprom_antenna_cfg(ah, chan)); + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + if (AR_SREV_9280(ah)) { + if (i >= 2) + break; + } + + if (AR_SREV_5416_20_OR_LATER(ah) && + (ah->rxchainmask == 5 || 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_20_OR_LATER(ah)) + ath9k_hw_def_set_gain(ah, pModal, eep, txRxAttenLocal, + regChainOffset, i); + } + + 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); + 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 (AR5416_VER_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 (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_3) { + if (IS_CHAN_HT40(chan)) + REG_RMW_FIELD(ah, AR_PHY_SETTLING, + AR_PHY_SETTLING_SWITCH, + pModal->swSettleHt40); + } + + if (AR_SREV_9280_20_OR_LATER(ah) && + AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_19) + REG_RMW_FIELD(ah, AR_PHY_CCK_TX_CTRL, + AR_PHY_CCK_TX_CTRL_TX_DAC_SCALE_CCK, + pModal->miscBits); + + + if (AR_SREV_9280_20(ah) && AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_20) { + if (IS_CHAN_2GHZ(chan)) + REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, + eep->baseEepHeader.dacLpMode); + else if (eep->baseEepHeader.dacHiPwrMode_5G) + REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, 0); + else + REG_RMW_FIELD(ah, AR_AN_TOP1, AR_AN_TOP1_DACIPMODE, + eep->baseEepHeader.dacLpMode); + + REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, AR_PHY_FRAME_CTL_TX_CLIP, + pModal->miscBits >> 2); + + REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL9, + AR_PHY_TX_DESIRED_SCALE_CCK, + eep->baseEepHeader.desiredScaleCCK); + } +} + +static void ath9k_hw_def_set_addac(struct ath_hw *ah, + struct ath9k_channel *chan) +{ +#define XPA_LVL_FREQ(cnt) (pModal->xpaBiasLvlFreq[cnt]) + struct modal_eep_header *pModal; + struct ar5416_eeprom_def *eep = &ah->eeprom.def; + u8 biaslevel; + + if (ah->hw_version.macVersion != AR_SREV_VERSION_9160) + return; + + if (ah->eep_ops->get_eeprom_rev(ah) < 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 = XPA_LVL_FREQ(0) & 0xff; + biaslevel = (u8) (XPA_LVL_FREQ(0) >> 14); + + freqCount++; + + while (freqCount < 3) { + if (XPA_LVL_FREQ(freqCount) == 0x0) + break; + + freqBin = XPA_LVL_FREQ(freqCount) & 0xff; + if (resetFreqBin >= freqBin) + biaslevel = (u8)(XPA_LVL_FREQ(freqCount) >> 14); + else + break; + freqCount++; + } + } + + if (IS_CHAN_2GHZ(chan)) { + INI_RA(&ah->iniAddac, 7, 1) = (INI_RA(&ah->iniAddac, + 7, 1) & (~0x18)) | biaslevel << 3; + } else { + INI_RA(&ah->iniAddac, 6, 1) = (INI_RA(&ah->iniAddac, + 6, 1) & (~0xc0)) | biaslevel << 6; + } +#undef XPA_LVL_FREQ +} + +static void ath9k_hw_get_def_gain_boundaries_pdadcs(struct ath_hw *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_20_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 void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah, + struct ath9k_channel *chan, + int16_t *pTxPowerIndexOffset) +{ +#define SM_PD_GAIN(x) SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##x) +#define SM_PDGAIN_B(x, y) \ + SM((gainBoundaries[x]), AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_##y) + + struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; + 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; + + 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; + } + + if (OLC_FOR_AR9280_20_LATER && IS_CHAN_2GHZ(chan)) { + pRawDataset = pEepData->calPierData2G[0]; + ah->initPDADC = ((struct calDataPerFreqOpLoop *) + pRawDataset)->vpdPdg[0][0]; + } + + 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_20_OR_LATER(ah) && + (ah->rxchainmask == 5 || 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]; + + + if (OLC_FOR_AR9280_20_LATER) { + u8 pcdacIdx; + u8 txPower; + + ath9k_get_txgain_index(ah, chan, + (struct calDataPerFreqOpLoop *)pRawDataset, + pCalBChans, numPiers, &txPower, &pcdacIdx); + ath9k_olc_get_pdadcs(ah, pcdacIdx, + txPower/2, pdadcValues); + } else { + ath9k_hw_get_def_gain_boundaries_pdadcs(ah, + chan, pRawDataset, + pCalBChans, numPiers, + pdGainOverlap_t2, + &tMinCalPower, + gainBoundaries, + pdadcValues, + numXpdGain); + } + + if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) { + if (OLC_FOR_AR9280_20_LATER) { + REG_WRITE(ah, + AR_PHY_TPCRG5 + regChainOffset, + SM(0x6, + AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | + SM_PD_GAIN(1) | SM_PD_GAIN(2) | + SM_PD_GAIN(3) | SM_PD_GAIN(4)); + } else { + REG_WRITE(ah, + AR_PHY_TPCRG5 + regChainOffset, + SM(pdGainOverlap_t2, + AR_PHY_TPCRG5_PD_GAIN_OVERLAP)| + SM_PDGAIN_B(0, 1) | + SM_PDGAIN_B(1, 2) | + SM_PDGAIN_B(2, 3) | + SM_PDGAIN_B(3, 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_EEPROM, + "PDADC (%d,%4x): %4.4x %8.8x\n", + i, regChainOffset, regOffset, + reg32); + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "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; +#undef SM_PD_GAIN +#undef SM_PDGAIN_B +} + +static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah, + struct ath9k_channel *chan, + int16_t *ratesArray, + u16 cfgCtl, + u16 AntennaReduction, + u16 twiceMaxRegulatoryPower, + u16 powerLimit) +{ +#define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */ +#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 10 /* 10*log10(3)*2 */ + + struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); + struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; + u16 twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + static const u16 tpScaleReductionTable[5] = + { 0, 3, 6, 9, AR5416_MAX_RATE_POWER }; + + int i; + int16_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} + }; + u16 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; + u16 twiceMinEdgePower; + + tx_chainmask = 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 = (int16_t)min(AntennaReduction - + twiceLargestAntenna, 0); + + maxRegAllowedPower = twiceMaxRegulatoryPower + twiceLargestAntenna; + + if (regulatory->tp_scale != ATH9K_TP_SCALE_MAX) { + maxRegAllowedPower -= + (tpScaleReductionTable[(regulatory->tp_scale)] * 2); + } + + scaledPower = min(powerLimit, maxRegAllowedPower); + + switch (ar5416_get_ntxchains(tx_chainmask)) { + case 1: + break; + case 2: + scaledPower -= REDUCE_SCALED_POWER_BY_TWO_CHAIN; + break; + case 3: + scaledPower -= REDUCE_SCALED_POWER_BY_THREE_CHAIN; + break; + } + + scaledPower = max((u16)0, 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 (ah->eep_ops->get_eeprom_ver(ah) == 14 && + ah->eep_ops->get_eeprom_rev(ah) <= 2) + twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + + for (i = 0; (i < AR5416_NUM_CTLS) && pEepData->ctlIndex[i]; i++) { + 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), AR5416_NUM_BAND_EDGES); + + if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { + twiceMaxEdgePower = min(twiceMaxEdgePower, + twiceMinEdgePower); + } else { + twiceMaxEdgePower = twiceMinEdgePower; + break; + } + } + } + + minCtlPower = min(twiceMaxEdgePower, scaledPower); + + switch (pCtlMode[ctlMode]) { + case CTL_11B: + for (i = 0; i < ARRAY_SIZE(targetPowerCck.tPow2x); i++) { + targetPowerCck.tPow2x[i] = + min((u16)targetPowerCck.tPow2x[i], + minCtlPower); + } + break; + case CTL_11A: + case CTL_11G: + for (i = 0; i < ARRAY_SIZE(targetPowerOfdm.tPow2x); i++) { + targetPowerOfdm.tPow2x[i] = + min((u16)targetPowerOfdm.tPow2x[i], + minCtlPower); + } + break; + case CTL_5GHT20: + case CTL_2GHT20: + for (i = 0; i < ARRAY_SIZE(targetPowerHt20.tPow2x); i++) { + targetPowerHt20.tPow2x[i] = + min((u16)targetPowerHt20.tPow2x[i], + minCtlPower); + } + break; + case CTL_11B_EXT: + targetPowerCckExt.tPow2x[0] = min((u16) + targetPowerCckExt.tPow2x[0], + minCtlPower); + break; + case CTL_11A_EXT: + case CTL_11G_EXT: + targetPowerOfdmExt.tPow2x[0] = min((u16) + targetPowerOfdmExt.tPow2x[0], + minCtlPower); + break; + case CTL_5GHT40: + case CTL_2GHT40: + for (i = 0; i < ARRAY_SIZE(targetPowerHt40.tPow2x); i++) { + targetPowerHt40.tPow2x[i] = + min((u16)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]; + } + } +} + +static void ath9k_hw_def_set_txpower(struct ath_hw *ah, + struct ath9k_channel *chan, + u16 cfgCtl, + u8 twiceAntennaReduction, + u8 twiceMaxRegulatoryPower, + u8 powerLimit) +{ +#define RT_AR_DELTA(x) (ratesArray[x] - cck_ofdm_delta) + struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); + struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; + struct modal_eep_header *pModal = + &(pEepData->modalHeader[IS_CHAN_2GHZ(chan)]); + int16_t ratesArray[Ar5416RateSize]; + int16_t txPowerIndexOffset = 0; + u8 ht40PowerIncForPdadc = 2; + int i, cck_ofdm_delta = 0; + + memset(ratesArray, 0, sizeof(ratesArray)); + + if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_2) { + ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; + } + + ath9k_hw_set_def_power_per_rate_table(ah, chan, + &ratesArray[0], cfgCtl, + twiceAntennaReduction, + twiceMaxRegulatoryPower, + powerLimit); + + ath9k_hw_set_def_power_cal_table(ah, chan, &txPowerIndexOffset); + + 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)) { + if (OLC_FOR_AR9280_20_LATER) { + cck_ofdm_delta = 2; + REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, + ATH9K_POW_SM(RT_AR_DELTA(rate2s), 24) + | ATH9K_POW_SM(RT_AR_DELTA(rate2l), 16) + | ATH9K_POW_SM(ratesArray[rateXr], 8) + | ATH9K_POW_SM(RT_AR_DELTA(rate1l), 0)); + REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, + ATH9K_POW_SM(RT_AR_DELTA(rate11s), 24) + | ATH9K_POW_SM(RT_AR_DELTA(rate11l), 16) + | ATH9K_POW_SM(RT_AR_DELTA(rate5_5s), 8) + | ATH9K_POW_SM(RT_AR_DELTA(rate5_5l), 0)); + } else { + 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)); + if (OLC_FOR_AR9280_20_LATER) { + REG_WRITE(ah, AR_PHY_POWER_TX_RATE9, + ATH9K_POW_SM(ratesArray[rateExtOfdm], 24) + | ATH9K_POW_SM(RT_AR_DELTA(rateExtCck), 16) + | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8) + | ATH9K_POW_SM(RT_AR_DELTA(rateDupCck), 0)); + } else { + 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)) + regulatory->max_power_level = + ratesArray[i] + AR5416_PWR_TABLE_OFFSET * 2; + else + regulatory->max_power_level = ratesArray[i]; + + switch(ar5416_get_ntxchains(ah->txchainmask)) { + case 1: + break; + case 2: + regulatory->max_power_level += INCREASE_MAXPOW_BY_TWO_CHAIN; + break; + case 3: + regulatory->max_power_level += INCREASE_MAXPOW_BY_THREE_CHAIN; + break; + default: + DPRINTF(ah->ah_sc, ATH_DBG_EEPROM, + "Invalid chainmask configuration\n"); + break; + } +} + +static u8 ath9k_hw_def_get_num_ant_config(struct ath_hw *ah, + enum ieee80211_band freq_band) +{ + struct ar5416_eeprom_def *eep = &ah->eeprom.def; + struct modal_eep_header *pModal = + &(eep->modalHeader[ATH9K_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 u16 ath9k_hw_def_get_eeprom_antenna_cfg(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct ar5416_eeprom_def *eep = &ah->eeprom.def; + struct modal_eep_header *pModal = + &(eep->modalHeader[IS_CHAN_2GHZ(chan)]); + + return pModal->antCtrlCommon & 0xFFFF; +} + +static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) +{ +#define EEP_DEF_SPURCHAN \ + (ah->eeprom.def.modalHeader[is2GHz].spurChans[i].spurChan) + + 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->config.spurchans[i][is2GHz]); + + switch (ah->config.spurmode) { + case SPUR_DISABLE: + break; + case SPUR_ENABLE_IOCTL: + spur_val = ah->config.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_DEF_SPURCHAN; + break; + } + + return spur_val; + +#undef EEP_DEF_SPURCHAN +} + +const struct eeprom_ops eep_def_ops = { + .check_eeprom = ath9k_hw_def_check_eeprom, + .get_eeprom = ath9k_hw_def_get_eeprom, + .fill_eeprom = ath9k_hw_def_fill_eeprom, + .get_eeprom_ver = ath9k_hw_def_get_eeprom_ver, + .get_eeprom_rev = ath9k_hw_def_get_eeprom_rev, + .get_num_ant_config = ath9k_hw_def_get_num_ant_config, + .get_eeprom_antenna_cfg = ath9k_hw_def_get_eeprom_antenna_cfg, + .set_board_values = ath9k_hw_def_set_board_values, + .set_addac = ath9k_hw_def_set_addac, + .set_txpower = ath9k_hw_def_set_txpower, + .get_spur_channel = ath9k_hw_def_get_spur_channel +}; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 34935a8ee59d..e340dacc6ebe 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -380,12 +380,15 @@ static const char *ath9k_hw_devname(u16 devid) return "Atheros 9280"; case AR9285_DEVID_PCIE: return "Atheros 9285"; + case AR5416_DEVID_AR9287_PCI: + case AR5416_DEVID_AR9287_PCIE: + return "Atheros 9287"; } return NULL; } -static void ath9k_hw_set_defaults(struct ath_hw *ah) +static void ath9k_hw_init_config(struct ath_hw *ah) { int i; @@ -404,7 +407,7 @@ static void ath9k_hw_set_defaults(struct ath_hw *ah) ah->config.cck_trig_high = 200; ah->config.cck_trig_low = 100; ah->config.enable_ani = 1; - ah->config.diversity_control = 0; + ah->config.diversity_control = ATH9K_ANT_VARIABLE; ah->config.antenna_switch_swap = 0; for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { @@ -434,37 +437,24 @@ static void ath9k_hw_set_defaults(struct ath_hw *ah) ah->config.serialize_regmode = SER_REG_MODE_AUTO; } -static struct ath_hw *ath9k_hw_newstate(u16 devid, struct ath_softc *sc, - int *status) +static void ath9k_hw_init_defaults(struct ath_hw *ah) { - struct ath_hw *ah; + struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); - ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); - if (ah == NULL) { - DPRINTF(sc, ATH_DBG_FATAL, - "Cannot allocate memory for state block\n"); - *status = -ENOMEM; - return NULL; - } + regulatory->country_code = CTRY_DEFAULT; + regulatory->power_limit = MAX_RATE_POWER; + regulatory->tp_scale = ATH9K_TP_SCALE_MAX; - ah->ah_sc = sc; ah->hw_version.magic = AR5416_MAGIC; - ah->regulatory.country_code = CTRY_DEFAULT; - ah->hw_version.devid = devid; ah->hw_version.subvendorid = 0; ah->ah_flags = 0; - if ((devid == AR5416_AR9100_DEVID)) + if (ah->hw_version.devid == AR5416_AR9100_DEVID) ah->hw_version.macVersion = AR_SREV_VERSION_9100; if (!AR_SREV_9100(ah)) ah->ah_flags = AH_USE_EEPROM; - ah->regulatory.power_limit = MAX_RATE_POWER; - ah->regulatory.tp_scale = ATH9K_TP_SCALE_MAX; ah->atim_window = 0; - ah->diversity_control = ah->config.diversity_control; - ah->antenna_switch_swap = - ah->config.antenna_switch_swap; ah->sta_id1_defaults = AR_STA_ID1_CRPT_MIC_ENABLE; ah->beacon_interval = 100; ah->enable_32kHz_clock = DONT_USE_32KHZ; @@ -475,7 +465,7 @@ static struct ath_hw *ath9k_hw_newstate(u16 devid, struct ath_softc *sc, ah->gbeacon_rate = 0; - return ah; + ah->power_mode = ATH9K_PM_UNDEFINED; } static int ath9k_hw_rfattach(struct ath_hw *ah) @@ -588,7 +578,7 @@ static void ath9k_hw_init_txgain_ini(struct ath_hw *ah) } } -static int ath9k_hw_post_attach(struct ath_hw *ah) +static int ath9k_hw_post_init(struct ath_hw *ah) { int ecode; @@ -599,7 +589,7 @@ static int ath9k_hw_post_attach(struct ath_hw *ah) if (ecode != 0) return ecode; - ecode = ath9k_hw_eeprom_attach(ah); + ecode = ath9k_hw_eeprom_init(ah); if (ecode != 0) return ecode; @@ -612,70 +602,52 @@ static int ath9k_hw_post_attach(struct ath_hw *ah) if (!AR_SREV_9100(ah)) { ath9k_hw_ani_setup(ah); - ath9k_hw_ani_attach(ah); + ath9k_hw_ani_init(ah); } return 0; } -static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc, - int *status) +static bool ath9k_hw_devid_supported(u16 devid) { - struct ath_hw *ah; - int ecode; - u32 i, j; - - ah = ath9k_hw_newstate(devid, sc, status); - if (ah == NULL) - return NULL; - - ath9k_hw_set_defaults(ah); - - if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { - DPRINTF(sc, ATH_DBG_FATAL, "Couldn't reset chip\n"); - ecode = -EIO; - goto bad; - } - - if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) { - DPRINTF(sc, ATH_DBG_FATAL, "Couldn't wakeup chip\n"); - ecode = -EIO; - goto bad; - } - - if (ah->config.serialize_regmode == SER_REG_MODE_AUTO) { - if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI || - (AR_SREV_9280(ah) && !ah->is_pciexpress)) { - ah->config.serialize_regmode = - SER_REG_MODE_ON; - } else { - ah->config.serialize_regmode = - SER_REG_MODE_OFF; - } - } - - DPRINTF(sc, ATH_DBG_RESET, "serialize_regmode is %d\n", - ah->config.serialize_regmode); - - if ((ah->hw_version.macVersion != AR_SREV_VERSION_5416_PCI) && - (ah->hw_version.macVersion != AR_SREV_VERSION_5416_PCIE) && - (ah->hw_version.macVersion != AR_SREV_VERSION_9160) && - (!AR_SREV_9100(ah)) && (!AR_SREV_9280(ah)) && (!AR_SREV_9285(ah))) { - DPRINTF(sc, ATH_DBG_FATAL, - "Mac Chip Rev 0x%02x.%x is not supported by " - "this driver\n", ah->hw_version.macVersion, - ah->hw_version.macRev); - ecode = -EOPNOTSUPP; - goto bad; + switch (devid) { + case AR5416_DEVID_PCI: + case AR5416_DEVID_PCIE: + case AR5416_AR9100_DEVID: + case AR9160_DEVID_PCI: + case AR9280_DEVID_PCI: + case AR9280_DEVID_PCIE: + case AR9285_DEVID_PCIE: + case AR5416_DEVID_AR9287_PCI: + case AR5416_DEVID_AR9287_PCIE: + return true; + default: + break; } + return false; +} - if (AR_SREV_9100(ah)) { - ah->iq_caldata.calData = &iq_cal_multi_sample; - ah->supp_cals = IQ_MISMATCH_CAL; - ah->is_pciexpress = false; +static bool ath9k_hw_macversion_supported(u32 macversion) +{ + switch (macversion) { + case AR_SREV_VERSION_5416_PCI: + case AR_SREV_VERSION_5416_PCIE: + case AR_SREV_VERSION_9160: + case AR_SREV_VERSION_9100: + case AR_SREV_VERSION_9280: + case AR_SREV_VERSION_9285: + case AR_SREV_VERSION_9287: + return true; + /* Not yet */ + case AR_SREV_VERSION_9271: + default: + break; } - ah->hw_version.phyRev = REG_READ(ah, AR_PHY_CHIP_ID); + return false; +} +static void ath9k_hw_init_cal_settings(struct ath_hw *ah) +{ if (AR_SREV_9160_10_OR_LATER(ah)) { if (AR_SREV_9280_10_OR_LATER(ah)) { ah->iq_caldata.calData = &iq_cal_single_sample; @@ -696,12 +668,49 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc, } ah->supp_cals = ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL; } +} - ah->ani_function = ATH9K_ANI_ALL; - if (AR_SREV_9280_10_OR_LATER(ah)) - ah->ani_function &= ~ATH9K_ANI_NOISE_IMMUNITY_LEVEL; +static void ath9k_hw_init_mode_regs(struct ath_hw *ah) +{ + if (AR_SREV_9271(ah)) { + INIT_INI_ARRAY(&ah->iniModes, ar9271Modes_9271_1_0, + ARRAY_SIZE(ar9271Modes_9271_1_0), 6); + INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271_1_0, + ARRAY_SIZE(ar9271Common_9271_1_0), 2); + return; + } + + if (AR_SREV_9287_11_OR_LATER(ah)) { + INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_1, + ARRAY_SIZE(ar9287Modes_9287_1_1), 6); + INIT_INI_ARRAY(&ah->iniCommon, ar9287Common_9287_1_1, + ARRAY_SIZE(ar9287Common_9287_1_1), 2); + if (ah->config.pcie_clock_req) + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9287PciePhy_clkreq_off_L1_9287_1_1, + ARRAY_SIZE(ar9287PciePhy_clkreq_off_L1_9287_1_1), 2); + else + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9287PciePhy_clkreq_always_on_L1_9287_1_1, + ARRAY_SIZE(ar9287PciePhy_clkreq_always_on_L1_9287_1_1), + 2); + } else if (AR_SREV_9287_10_OR_LATER(ah)) { + INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_0, + ARRAY_SIZE(ar9287Modes_9287_1_0), 6); + INIT_INI_ARRAY(&ah->iniCommon, ar9287Common_9287_1_0, + ARRAY_SIZE(ar9287Common_9287_1_0), 2); + + if (ah->config.pcie_clock_req) + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9287PciePhy_clkreq_off_L1_9287_1_0, + ARRAY_SIZE(ar9287PciePhy_clkreq_off_L1_9287_1_0), 2); + else + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9287PciePhy_clkreq_always_on_L1_9287_1_0, + ARRAY_SIZE(ar9287PciePhy_clkreq_always_on_L1_9287_1_0), + 2); + } else if (AR_SREV_9285_12_OR_LATER(ah)) { - if (AR_SREV_9285_12_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar9285Modes_9285_1_2, ARRAY_SIZE(ar9285Modes_9285_1_2), 6); @@ -832,17 +841,32 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc, INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac, ARRAY_SIZE(ar5416Addac), 2); } +} - if (ah->is_pciexpress) - ath9k_hw_configpcipowersave(ah, 0); - else - ath9k_hw_disablepcie(ah); - - ecode = ath9k_hw_post_attach(ah); - if (ecode != 0) - goto bad; +static void ath9k_hw_init_mode_gain_regs(struct ath_hw *ah) +{ + if (AR_SREV_9287_11(ah)) + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9287Modes_rx_gain_9287_1_1, + ARRAY_SIZE(ar9287Modes_rx_gain_9287_1_1), 6); + else if (AR_SREV_9287_10(ah)) + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9287Modes_rx_gain_9287_1_0, + ARRAY_SIZE(ar9287Modes_rx_gain_9287_1_0), 6); + else if (AR_SREV_9280_20(ah)) + ath9k_hw_init_rxgain_ini(ah); - if (AR_SREV_9285_12_OR_LATER(ah)) { + if (AR_SREV_9287_11(ah)) { + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9287Modes_tx_gain_9287_1_1, + ARRAY_SIZE(ar9287Modes_tx_gain_9287_1_1), 6); + } else if (AR_SREV_9287_10(ah)) { + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9287Modes_tx_gain_9287_1_0, + ARRAY_SIZE(ar9287Modes_tx_gain_9287_1_0), 6); + } else if (AR_SREV_9280_20(ah)) { + ath9k_hw_init_txgain_ini(ah); + } else if (AR_SREV_9285_12_OR_LATER(ah)) { u32 txgain_type = ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE); /* txgain table */ @@ -857,16 +881,11 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc, } } +} - /* rxgain table */ - if (AR_SREV_9280_20(ah)) - ath9k_hw_init_rxgain_ini(ah); - - /* txgain table */ - if (AR_SREV_9280_20(ah)) - ath9k_hw_init_txgain_ini(ah); - - ath9k_hw_fill_cap_info(ah); +static void ath9k_hw_init_11a_eeprom_fix(struct ath_hw *ah) +{ + u32 i, j; if ((ah->hw_version.devid == AR9280_DEVID_PCI) && test_bit(ATH9K_MODE_11A, ah->caps.wireless_modes)) { @@ -885,29 +904,97 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc, } } } +} + +int ath9k_hw_init(struct ath_hw *ah) +{ + int r = 0; + + if (!ath9k_hw_devid_supported(ah->hw_version.devid)) + return -EOPNOTSUPP; + + ath9k_hw_init_defaults(ah); + ath9k_hw_init_config(ah); + + if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Couldn't reset chip\n"); + return -EIO; + } + + if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Couldn't wakeup chip\n"); + return -EIO; + } + + if (ah->config.serialize_regmode == SER_REG_MODE_AUTO) { + if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI || + (AR_SREV_9280(ah) && !ah->is_pciexpress)) { + ah->config.serialize_regmode = + SER_REG_MODE_ON; + } else { + ah->config.serialize_regmode = + SER_REG_MODE_OFF; + } + } - ecode = ath9k_hw_init_macaddr(ah); - if (ecode != 0) { - DPRINTF(sc, ATH_DBG_FATAL, + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "serialize_regmode is %d\n", + ah->config.serialize_regmode); + + if (!ath9k_hw_macversion_supported(ah->hw_version.macVersion)) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, + "Mac Chip Rev 0x%02x.%x is not supported by " + "this driver\n", ah->hw_version.macVersion, + ah->hw_version.macRev); + return -EOPNOTSUPP; + } + + if (AR_SREV_9100(ah)) { + ah->iq_caldata.calData = &iq_cal_multi_sample; + ah->supp_cals = IQ_MISMATCH_CAL; + ah->is_pciexpress = false; + } + + if (AR_SREV_9271(ah)) + ah->is_pciexpress = false; + + ah->hw_version.phyRev = REG_READ(ah, AR_PHY_CHIP_ID); + + ath9k_hw_init_cal_settings(ah); + + ah->ani_function = ATH9K_ANI_ALL; + if (AR_SREV_9280_10_OR_LATER(ah)) + ah->ani_function &= ~ATH9K_ANI_NOISE_IMMUNITY_LEVEL; + + ath9k_hw_init_mode_regs(ah); + + if (ah->is_pciexpress) + ath9k_hw_configpcipowersave(ah, 0); + else + ath9k_hw_disablepcie(ah); + + r = ath9k_hw_post_init(ah); + if (r) + return r; + + ath9k_hw_init_mode_gain_regs(ah); + ath9k_hw_fill_cap_info(ah); + ath9k_hw_init_11a_eeprom_fix(ah); + + r = ath9k_hw_init_macaddr(ah); + if (r) { + DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Failed to initialize MAC address\n"); - goto bad; + return r; } - if (AR_SREV_9285(ah)) + if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) ah->tx_trig_level = (AR_FTRIG_256B >> AR_FTRIG_S); else ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S); ath9k_init_nfcal_hist_buffer(ah); - return ah; -bad: - if (ah) - ath9k_hw_detach(ah); - if (status) - *status = ecode; - - return NULL; + return 0; } static void ath9k_hw_init_bb(struct ath_hw *ah, @@ -1146,33 +1233,12 @@ const char *ath9k_hw_probe(u16 vendorid, u16 devid) void ath9k_hw_detach(struct ath_hw *ah) { if (!AR_SREV_9100(ah)) - ath9k_hw_ani_detach(ah); + ath9k_hw_ani_disable(ah); - ath9k_hw_rfdetach(ah); + ath9k_hw_rf_free(ah); ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); kfree(ah); -} - -struct ath_hw *ath9k_hw_attach(u16 devid, struct ath_softc *sc, int *error) -{ - struct ath_hw *ah = NULL; - - switch (devid) { - case AR5416_DEVID_PCI: - case AR5416_DEVID_PCIE: - case AR5416_AR9100_DEVID: - case AR9160_DEVID_PCI: - case AR9280_DEVID_PCI: - case AR9280_DEVID_PCIE: - case AR9285_DEVID_PCIE: - ah = ath9k_hw_do_attach(devid, sc, error); - break; - default: - *error = -ENXIO; - break; - } - - return ah; + ah = NULL; } /*******/ @@ -1182,6 +1248,27 @@ struct ath_hw *ath9k_hw_attach(u16 devid, struct ath_softc *sc, int *error) static void ath9k_hw_override_ini(struct ath_hw *ah, struct ath9k_channel *chan) { + u32 val; + + if (AR_SREV_9271(ah)) { + /* + * Enable spectral scan to solution for issues with stuck + * beacons on AR9271 1.0. The beacon stuck issue is not seeon on + * AR9271 1.1 + */ + if (AR_SREV_9271_10(ah)) { + val = REG_READ(ah, AR_PHY_SPECTRAL_SCAN) | AR_PHY_SPECTRAL_SCAN_ENABLE; + REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, val); + } + else if (AR_SREV_9271_11(ah)) + /* + * change AR_PHY_RF_CTL3 setting to fix MAC issue + * present on AR9271 1.1 + */ + REG_WRITE(ah, AR_PHY_RF_CTL3, 0x3a020001); + return; + } + /* * Set the RX_ABORT and RX_DIS and clear if off only after * RXE is set for MAC. This prevents frames with corrupted @@ -1193,7 +1280,10 @@ static void ath9k_hw_override_ini(struct ath_hw *ah, if (!AR_SREV_5416_20_OR_LATER(ah) || AR_SREV_9280_10_OR_LATER(ah)) return; - + /* + * Disable BB clock gating + * Necessary to avoid issues on AR5416 2.0 + */ REG_WRITE(ah, 0x9800 + (651 << 2), 0x11); } @@ -1245,11 +1335,21 @@ static void ath9k_olc_init(struct ath_hw *ah) { u32 i; - for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++) - ah->originalGain[i] = - MS(REG_READ(ah, AR_PHY_TX_GAIN_TBL1 + i * 4), - AR_PHY_TX_GAIN); - ah->PDADCdelta = 0; + if (OLC_FOR_AR9287_10_LATER) { + REG_SET_BIT(ah, AR_PHY_TX_PWRCTRL9, + AR_PHY_TX_PWRCTRL9_RES_DC_REMOVAL); + ath9k_hw_analog_shift_rmw(ah, AR9287_AN_TXPC0, + AR9287_AN_TXPC0_TXPCMODE, + AR9287_AN_TXPC0_TXPCMODE_S, + AR9287_AN_TXPC0_TXPCMODE_TEMPSENSE); + udelay(100); + } else { + for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++) + ah->originalGain[i] = + MS(REG_READ(ah, AR_PHY_TX_GAIN_TBL1 + i * 4), + AR_PHY_TX_GAIN); + ah->PDADCdelta = 0; + } } static u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, @@ -1271,6 +1371,7 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, struct ath9k_channel *chan, enum ath9k_ht_macmode macmode) { + struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); int i, regWrites = 0; struct ieee80211_channel *channel = chan->chan; u32 modesIndex, freqIndex; @@ -1341,10 +1442,11 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, DO_DELAY(regWrites); } - if (AR_SREV_9280(ah)) + if (AR_SREV_9280(ah) || AR_SREV_9287_10_OR_LATER(ah)) REG_WRITE_ARRAY(&ah->iniModesRxGain, modesIndex, regWrites); - if (AR_SREV_9280(ah) || AR_SREV_9285_12_OR_LATER(ah)) + if (AR_SREV_9280(ah) || AR_SREV_9285_12_OR_LATER(ah) || + AR_SREV_9287_10_OR_LATER(ah)) REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); for (i = 0; i < ah->iniCommon.ia_rows; i++) { @@ -1376,11 +1478,11 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, ath9k_olc_init(ah); ah->eep_ops->set_txpower(ah, chan, - ath9k_regd_get_ctl(&ah->regulatory, chan), + ath9k_regd_get_ctl(regulatory, chan), channel->max_antenna_gain * 2, channel->max_power * 2, min((u32) MAX_RATE_POWER, - (u32) ah->regulatory.power_limit)); + (u32) regulatory->power_limit)); if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) { DPRINTF(ah->ah_sc, ATH_DBG_FATAL, @@ -1424,23 +1526,48 @@ static inline void ath9k_hw_set_dma(struct ath_hw *ah) { u32 regval; + /* + * set AHB_MODE not to do cacheline prefetches + */ regval = REG_READ(ah, AR_AHB_MODE); REG_WRITE(ah, AR_AHB_MODE, regval | AR_AHB_PREFETCH_RD_EN); + /* + * let mac dma reads be in 128 byte chunks + */ regval = REG_READ(ah, AR_TXCFG) & ~AR_TXCFG_DMASZ_MASK; REG_WRITE(ah, AR_TXCFG, regval | AR_TXCFG_DMASZ_128B); + /* + * Restore TX Trigger Level to its pre-reset value. + * The initial value depends on whether aggregation is enabled, and is + * adjusted whenever underruns are detected. + */ REG_RMW_FIELD(ah, AR_TXCFG, AR_FTRIG, ah->tx_trig_level); + /* + * let mac dma writes be in 128 byte chunks + */ regval = REG_READ(ah, AR_RXCFG) & ~AR_RXCFG_DMASZ_MASK; REG_WRITE(ah, AR_RXCFG, regval | AR_RXCFG_DMASZ_128B); + /* + * Setup receive FIFO threshold to hold off TX activities + */ REG_WRITE(ah, AR_RXFIFO_CFG, 0x200); + /* + * reduce the number of usable entries in PCU TXBUF to avoid + * wrap around issues. + */ if (AR_SREV_9285(ah)) { + /* For AR9285 the number of Fifos are reduced to half. + * So set the usable tx buf size also to half to + * avoid data/delimiter underruns + */ REG_WRITE(ah, AR_PCU_TXBUF_CTRL, AR_9285_PCU_TXBUF_CTRL_USABLE_SIZE); - } else { + } else if (!AR_SREV_9271(ah)) { REG_WRITE(ah, AR_PCU_TXBUF_CTRL, AR_PCU_TXBUF_CTRL_USABLE_SIZE); } @@ -1673,6 +1800,7 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, struct ath9k_channel *chan, enum ath9k_ht_macmode macmode) { + struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); struct ieee80211_channel *channel = chan->chan; u32 synthDelay, qnum; @@ -1705,11 +1833,11 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, } ah->eep_ops->set_txpower(ah, chan, - ath9k_regd_get_ctl(&ah->regulatory, chan), + ath9k_regd_get_ctl(regulatory, chan), channel->max_antenna_gain * 2, channel->max_power * 2, min((u32) MAX_RATE_POWER, - (u32) ah->regulatory.power_limit)); + (u32) regulatory->power_limit)); synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; if (IS_CHAN_B(chan)) @@ -2246,14 +2374,39 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_mark_phy_inactive(ah); + if (AR_SREV_9271(ah) && ah->htc_reset_init) { + REG_WRITE(ah, + AR9271_RESET_POWER_DOWN_CONTROL, + AR9271_RADIO_RF_RST); + udelay(50); + } + if (!ath9k_hw_chip_reset(ah, chan)) { DPRINTF(ah->ah_sc, ATH_DBG_FATAL, "Chip reset failed\n"); return -EINVAL; } + if (AR_SREV_9271(ah) && ah->htc_reset_init) { + ah->htc_reset_init = false; + REG_WRITE(ah, + AR9271_RESET_POWER_DOWN_CONTROL, + AR9271_GATE_MAC_CTL); + udelay(50); + } + if (AR_SREV_9280_10_OR_LATER(ah)) REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); + if (AR_SREV_9287_12_OR_LATER(ah)) { + /* Enable ASYNC FIFO */ + REG_SET_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3, + AR_MAC_PCU_ASYNC_FIFO_REG3_DATAPATH_SEL); + REG_SET_BIT(ah, AR_PHY_MODE, AR_PHY_MODE_ASYNCFIFO); + REG_CLR_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3, + AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET); + REG_SET_BIT(ah, AR_MAC_PCU_ASYNC_FIFO_REG3, + AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET); + } r = ath9k_hw_process_ini(ah, chan, sc->tx_chan_width); if (r) return r; @@ -2330,6 +2483,27 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_init_user_settings(ah); + if (AR_SREV_9287_12_OR_LATER(ah)) { + REG_WRITE(ah, AR_D_GBL_IFS_SIFS, + AR_D_GBL_IFS_SIFS_ASYNC_FIFO_DUR); + REG_WRITE(ah, AR_D_GBL_IFS_SLOT, + AR_D_GBL_IFS_SLOT_ASYNC_FIFO_DUR); + REG_WRITE(ah, AR_D_GBL_IFS_EIFS, + AR_D_GBL_IFS_EIFS_ASYNC_FIFO_DUR); + + REG_WRITE(ah, AR_TIME_OUT, AR_TIME_OUT_ACK_CTS_ASYNC_FIFO_DUR); + REG_WRITE(ah, AR_USEC, AR_USEC_ASYNC_FIFO_DUR); + + REG_SET_BIT(ah, AR_MAC_PCU_LOGIC_ANALYZER, + AR_MAC_PCU_LOGIC_ANALYZER_DISBUG20768); + REG_RMW_FIELD(ah, AR_AHB_MODE, AR_AHB_CUSTOM_BURST_EN, + AR_AHB_CUSTOM_BURST_ASYNC_FIFO_VAL); + } + if (AR_SREV_9287_12_OR_LATER(ah)) { + REG_SET_BIT(ah, AR_PCU_MISC_MODE2, + AR_PCU_MISC_MODE2_ENABLE_AGGWEP); + } + REG_WRITE(ah, AR_STA_ID1, REG_READ(ah, AR_STA_ID1) | AR_STA_ID1_PRESERVE_SEQNUM); @@ -2345,7 +2519,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_init_bb(ah, chan); if (!ath9k_hw_init_cal(ah, chan)) - return -EIO;; + return -EIO; rx_chainmask = ah->rxchainmask; if ((rx_chainmask == 0x5) || (rx_chainmask == 0x3)) { @@ -2355,6 +2529,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, REG_WRITE(ah, AR_CFG_LED, saveLedState | AR_CFG_SCLK_32KHZ); + /* + * For big endian systems turn on swapping for descriptors + */ if (AR_SREV_9100(ah)) { u32 mask; mask = REG_READ(ah, AR_CFG); @@ -2369,11 +2546,18 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, "Setting CFG 0x%x\n", REG_READ(ah, AR_CFG)); } } else { + /* Configure AR9271 target WLAN */ + if (AR_SREV_9271(ah)) + REG_WRITE(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB); #ifdef __BIG_ENDIAN - REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD); + else + REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD); #endif } + if (ah->ah_sc->sc_flags & SC_OP_BTCOEX_ENABLED) + ath9k_hw_btcoex_enable(ah); + return 0; } @@ -2412,9 +2596,6 @@ bool ath9k_hw_keyreset(struct ath_hw *ah, u16 entry) } - if (ah->curchan == NULL) - return true; - return true; } @@ -2728,7 +2909,8 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah, int setChip) return true; } -bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode) +static bool ath9k_hw_setpower_nolock(struct ath_hw *ah, + enum ath9k_power_mode mode) { int status = true, setChip = true; static const char *modes[] = { @@ -2738,6 +2920,9 @@ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode) "UNDEFINED" }; + if (ah->power_mode == mode) + return status; + DPRINTF(ah->ah_sc, ATH_DBG_RESET, "%s -> %s\n", modes[ah->power_mode], modes[mode]); @@ -2762,6 +2947,51 @@ bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode) return status; } +bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(&ah->ah_sc->sc_pm_lock, flags); + ret = ath9k_hw_setpower_nolock(ah, mode); + spin_unlock_irqrestore(&ah->ah_sc->sc_pm_lock, flags); + + return ret; +} + +void ath9k_ps_wakeup(struct ath_softc *sc) +{ + unsigned long flags; + + spin_lock_irqsave(&sc->sc_pm_lock, flags); + if (++sc->ps_usecount != 1) + goto unlock; + + ath9k_hw_setpower_nolock(sc->sc_ah, ATH9K_PM_AWAKE); + + unlock: + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); +} + +void ath9k_ps_restore(struct ath_softc *sc) +{ + unsigned long flags; + + spin_lock_irqsave(&sc->sc_pm_lock, flags); + if (--sc->ps_usecount != 0) + goto unlock; + + if (sc->ps_enabled && + !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | + SC_OP_WAIT_FOR_CAB | + SC_OP_WAIT_FOR_PSPOLL_DATA | + SC_OP_WAIT_FOR_TX_ACK))) + ath9k_hw_setpower_nolock(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); + + unlock: + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); +} + /* * Helper for ASPM support. * @@ -2790,7 +3020,7 @@ void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore) /* * AR9280 2.0 or later chips use SerDes values from the * initvals.h initialized depending on chipset during - * ath9k_hw_do_attach() + * ath9k_hw_init() */ for (i = 0; i < ah->iniPcieSerdes.ia_rows; i++) { REG_WRITE(ah, INI_RA(&ah->iniPcieSerdes, i, 0), @@ -2851,7 +3081,7 @@ void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore) if (ah->config.pcie_waen) { REG_WRITE(ah, AR_WA, ah->config.pcie_waen); } else { - if (AR_SREV_9285(ah)) + if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT); /* * On AR9280 chips bit 22 of 0x4004 needs to be set to @@ -2985,6 +3215,23 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked) if (AR_SREV_9100(ah)) return true; + if (isr & AR_ISR_GENTMR) { + u32 s5_s; + + s5_s = REG_READ(ah, AR_ISR_S5_S); + if (isr & AR_ISR_GENTMR) { + ah->intr_gen_timer_trigger = + MS(s5_s, AR_ISR_S5_GENTIMER_TRIG); + + ah->intr_gen_timer_thresh = + MS(s5_s, AR_ISR_S5_GENTIMER_THRESH); + + if (ah->intr_gen_timer_trigger) + *masked |= ATH9K_INT_GENTIMER; + + } + } + if (sync_cause) { fatal_int = (sync_cause & @@ -3021,11 +3268,6 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked) return true; } -enum ath9k_int ath9k_hw_intrget(struct ath_hw *ah) -{ - return ah->mask_reg; -} - enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints) { u32 omask = ah->mask_reg; @@ -3263,27 +3505,30 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, void ath9k_hw_fill_cap_info(struct ath_hw *ah) { struct ath9k_hw_capabilities *pCap = &ah->caps; + struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); + struct ath_btcoex_info *btcoex_info = &ah->ah_sc->btcoex_info; + u16 capField = 0, eeval; eeval = ah->eep_ops->get_eeprom(ah, EEP_REG_0); - ah->regulatory.current_rd = eeval; + regulatory->current_rd = eeval; eeval = ah->eep_ops->get_eeprom(ah, EEP_REG_1); if (AR_SREV_9285_10_OR_LATER(ah)) eeval |= AR9285_RDEXT_DEFAULT; - ah->regulatory.current_rd_ext = eeval; + regulatory->current_rd_ext = eeval; capField = ah->eep_ops->get_eeprom(ah, EEP_OP_CAP); if (ah->opmode != NL80211_IFTYPE_AP && ah->hw_version.subvendorid == AR_SUBVENDOR_ID_NEW_A) { - if (ah->regulatory.current_rd == 0x64 || - ah->regulatory.current_rd == 0x65) - ah->regulatory.current_rd += 5; - else if (ah->regulatory.current_rd == 0x41) - ah->regulatory.current_rd = 0x43; + if (regulatory->current_rd == 0x64 || + regulatory->current_rd == 0x65) + regulatory->current_rd += 5; + else if (regulatory->current_rd == 0x41) + regulatory->current_rd = 0x43; DPRINTF(ah->ah_sc, ATH_DBG_REGULATORY, - "regdomain mapped to 0x%x\n", ah->regulatory.current_rd); + "regdomain mapped to 0x%x\n", regulatory->current_rd); } eeval = ah->eep_ops->get_eeprom(ah, EEP_OP_MODE); @@ -3305,7 +3550,6 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah) } if (eeval & AR5416_OPFLAGS_11G) { - set_bit(ATH9K_MODE_11B, pCap->wireless_modes); set_bit(ATH9K_MODE_11G, pCap->wireless_modes); if (ah->config.ht_enable) { if (!(eeval & AR5416_OPFLAGS_N_2G_HT20)) @@ -3321,10 +3565,17 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah) } pCap->tx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_TX_MASK); + /* + * For AR9271 we will temporarilly uses the rx chainmax as read from + * the EEPROM. + */ if ((ah->hw_version.devid == AR5416_DEVID_PCI) && - !(eeval & AR5416_OPFLAGS_11A)) + !(eeval & AR5416_OPFLAGS_11A) && + !(AR_SREV_9271(ah))) + /* CB71: GPIO 0 is pulled down to indicate 3 rx chains */ pCap->rx_chainmask = ath9k_hw_gpio_get(ah, 0) ? 0x5 : 0x7; else + /* Use rx_chainmask from EEPROM. */ pCap->rx_chainmask = ah->eep_ops->get_eeprom(ah, EEP_RX_MASK); if (!(AR_SREV_9280(ah) && (ah->hw_version.macRev == 0))) @@ -3412,7 +3663,7 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah) else pCap->hw_caps |= ATH9K_HW_CAP_4KB_SPLITTRANS; - if (ah->regulatory.current_rd_ext & (1 << REG_EXT_JAPAN_MIDBAND)) { + if (regulatory->current_rd_ext & (1 << REG_EXT_JAPAN_MIDBAND)) { pCap->reg_cap = AR_EEPROM_EEREGCAP_EN_KK_NEW_11A | AR_EEPROM_EEREGCAP_EN_KK_U1_EVEN | @@ -3432,15 +3683,22 @@ void ath9k_hw_fill_cap_info(struct ath_hw *ah) ah->eep_ops->get_num_ant_config(ah, ATH9K_HAL_FREQ_BAND_2GHZ); if (AR_SREV_9280_10_OR_LATER(ah) && btcoex_enable) { - pCap->hw_caps |= ATH9K_HW_CAP_BT_COEX; - ah->btactive_gpio = 6; - ah->wlanactive_gpio = 5; + btcoex_info->btactive_gpio = ATH_BTACTIVE_GPIO; + btcoex_info->wlanactive_gpio = ATH_WLANACTIVE_GPIO; + + if (AR_SREV_9285(ah)) + btcoex_info->btcoex_scheme = ATH_BTCOEX_CFG_3WIRE; + else + btcoex_info->btcoex_scheme = ATH_BTCOEX_CFG_2WIRE; + } else { + btcoex_info->btcoex_scheme = ATH_BTCOEX_CFG_NONE; } } bool ath9k_hw_getcapability(struct ath_hw *ah, enum ath9k_capability_type type, u32 capability, u32 *result) { + struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); switch (type) { case ATH9K_CAP_CIPHER: switch (capability) { @@ -3489,13 +3747,13 @@ bool ath9k_hw_getcapability(struct ath_hw *ah, enum ath9k_capability_type type, case 0: return 0; case 1: - *result = ah->regulatory.power_limit; + *result = regulatory->power_limit; return 0; case 2: - *result = ah->regulatory.max_power_level; + *result = regulatory->max_power_level; return 0; case 3: - *result = ah->regulatory.tp_scale; + *result = regulatory->tp_scale; return 0; } return false; @@ -3595,7 +3853,9 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) if (gpio >= ah->caps.num_gpio_pins) return 0xffffffff; - if (AR_SREV_9285_10_OR_LATER(ah)) + if (AR_SREV_9287_10_OR_LATER(ah)) + return MS_REG_READ(AR9287, gpio) != 0; + else if (AR_SREV_9285_10_OR_LATER(ah)) return MS_REG_READ(AR9285, gpio) != 0; else if (AR_SREV_9280_10_OR_LATER(ah)) return MS_REG_READ(AR928X, gpio) != 0; @@ -3673,7 +3933,7 @@ bool ath9k_hw_setantennaswitch(struct ath_hw *ah, break; } } else { - ah->diversity_control = settings; + ah->config.diversity_control = settings; } return true; @@ -3731,17 +3991,18 @@ bool ath9k_hw_disable(struct ath_hw *ah) void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit) { + struct ath_regulatory *regulatory = ath9k_hw_regulatory(ah); struct ath9k_channel *chan = ah->curchan; struct ieee80211_channel *channel = chan->chan; - ah->regulatory.power_limit = min(limit, (u32) MAX_RATE_POWER); + regulatory->power_limit = min(limit, (u32) MAX_RATE_POWER); ah->eep_ops->set_txpower(ah, chan, - ath9k_regd_get_ctl(&ah->regulatory, chan), + ath9k_regd_get_ctl(regulatory, chan), channel->max_antenna_gain * 2, channel->max_power * 2, min((u32) MAX_RATE_POWER, - (u32) ah->regulatory.power_limit)); + (u32) regulatory->power_limit)); } void ath9k_hw_setmac(struct ath_hw *ah, const u8 *mac) @@ -3791,29 +4052,22 @@ void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64) void ath9k_hw_reset_tsf(struct ath_hw *ah) { - int count; + ath9k_ps_wakeup(ah->ah_sc); + if (!ath9k_hw_wait(ah, AR_SLP32_MODE, AR_SLP32_TSF_WRITE_STATUS, 0, + AH_TSF_WRITE_TIMEOUT)) + DPRINTF(ah->ah_sc, ATH_DBG_RESET, + "AR_SLP32_TSF_WRITE_STATUS limit exceeded\n"); - 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, - "AR_SLP32_TSF_WRITE_STATUS limit exceeded\n"); - break; - } - udelay(10); - } REG_WRITE(ah, AR_RESET_TSF, AR_RESET_TSF_ONCE); + ath9k_ps_restore(ah->ah_sc); } -bool ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting) +void ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting) { if (setting) ah->misc_mode |= AR_PCU_TX_ADD_TSF; else ah->misc_mode &= ~AR_PCU_TX_ADD_TSF; - - return true; } bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us) @@ -3842,29 +4096,197 @@ void ath9k_hw_set11nmac2040(struct ath_hw *ah, enum ath9k_ht_macmode mode) REG_WRITE(ah, AR_2040_MODE, macmode); } -/***************************/ -/* Bluetooth Coexistence */ -/***************************/ +/* HW Generic timers configuration */ -void ath9k_hw_btcoex_enable(struct ath_hw *ah) +static const struct ath_gen_timer_configuration gen_tmr_configuration[] = { - /* connect bt_active to baseband */ - REG_CLR_BIT(ah, AR_GPIO_INPUT_EN_VAL, - (AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_DEF | - AR_GPIO_INPUT_EN_VAL_BT_FREQUENCY_DEF)); + {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, + {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, + {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, + {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, + {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, + {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, + {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, + {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, + {AR_NEXT_NDP2_TIMER, AR_NDP2_PERIOD, AR_NDP2_TIMER_MODE, 0x0001}, + {AR_NEXT_NDP2_TIMER + 1*4, AR_NDP2_PERIOD + 1*4, + AR_NDP2_TIMER_MODE, 0x0002}, + {AR_NEXT_NDP2_TIMER + 2*4, AR_NDP2_PERIOD + 2*4, + AR_NDP2_TIMER_MODE, 0x0004}, + {AR_NEXT_NDP2_TIMER + 3*4, AR_NDP2_PERIOD + 3*4, + AR_NDP2_TIMER_MODE, 0x0008}, + {AR_NEXT_NDP2_TIMER + 4*4, AR_NDP2_PERIOD + 4*4, + AR_NDP2_TIMER_MODE, 0x0010}, + {AR_NEXT_NDP2_TIMER + 5*4, AR_NDP2_PERIOD + 5*4, + AR_NDP2_TIMER_MODE, 0x0020}, + {AR_NEXT_NDP2_TIMER + 6*4, AR_NDP2_PERIOD + 6*4, + AR_NDP2_TIMER_MODE, 0x0040}, + {AR_NEXT_NDP2_TIMER + 7*4, AR_NDP2_PERIOD + 7*4, + AR_NDP2_TIMER_MODE, 0x0080} +}; + +/* HW generic timer primitives */ + +/* compute and clear index of rightmost 1 */ +static u32 rightmost_index(struct ath_gen_timer_table *timer_table, u32 *mask) +{ + u32 b; - REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, - AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB); + b = *mask; + b &= (0-b); + *mask &= ~b; + b *= debruijn32; + b >>= 27; + + return timer_table->gen_timer_index[b]; +} + +u32 ath9k_hw_gettsf32(struct ath_hw *ah) +{ + return REG_READ(ah, AR_TSF_L32); +} - /* Set input mux for bt_active to gpio pin */ - REG_RMW_FIELD(ah, AR_GPIO_INPUT_MUX1, - AR_GPIO_INPUT_MUX1_BT_ACTIVE, - ah->btactive_gpio); +struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, + void (*trigger)(void *), + void (*overflow)(void *), + void *arg, + u8 timer_index) +{ + struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; + struct ath_gen_timer *timer; - /* Configure the desired gpio port for input */ - ath9k_hw_cfg_gpio_input(ah, ah->btactive_gpio); + timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL); - /* Configure the desired GPIO port for TX_FRAME output */ - ath9k_hw_cfg_output(ah, ah->wlanactive_gpio, - AR_GPIO_OUTPUT_MUX_AS_TX_FRAME); + if (timer == NULL) { + printk(KERN_DEBUG "Failed to allocate memory" + "for hw timer[%d]\n", timer_index); + return NULL; + } + + /* allocate a hardware generic timer slot */ + timer_table->timers[timer_index] = timer; + timer->index = timer_index; + timer->trigger = trigger; + timer->overflow = overflow; + timer->arg = arg; + + return timer; +} + +void ath_gen_timer_start(struct ath_hw *ah, + struct ath_gen_timer *timer, + u32 timer_next, u32 timer_period) +{ + struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; + u32 tsf; + + BUG_ON(!timer_period); + + set_bit(timer->index, &timer_table->timer_mask.timer_bits); + + tsf = ath9k_hw_gettsf32(ah); + + DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER, "curent tsf %x period %x" + "timer_next %x\n", tsf, timer_period, timer_next); + + /* + * Pull timer_next forward if the current TSF already passed it + * because of software latency + */ + if (timer_next < tsf) + timer_next = tsf + timer_period; + + /* + * Program generic timer registers + */ + REG_WRITE(ah, gen_tmr_configuration[timer->index].next_addr, + timer_next); + REG_WRITE(ah, gen_tmr_configuration[timer->index].period_addr, + timer_period); + REG_SET_BIT(ah, gen_tmr_configuration[timer->index].mode_addr, + gen_tmr_configuration[timer->index].mode_mask); + + /* Enable both trigger and thresh interrupt masks */ + REG_SET_BIT(ah, AR_IMR_S5, + (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) | + SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); + + if ((ah->ah_sc->imask & ATH9K_INT_GENTIMER) == 0) { + ath9k_hw_set_interrupts(ah, 0); + ah->ah_sc->imask |= ATH9K_INT_GENTIMER; + ath9k_hw_set_interrupts(ah, ah->ah_sc->imask); + } +} + +void ath_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) +{ + struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; + + if ((timer->index < AR_FIRST_NDP_TIMER) || + (timer->index >= ATH_MAX_GEN_TIMER)) { + return; + } + + /* Clear generic timer enable bits. */ + REG_CLR_BIT(ah, gen_tmr_configuration[timer->index].mode_addr, + gen_tmr_configuration[timer->index].mode_mask); + + /* Disable both trigger and thresh interrupt masks */ + REG_CLR_BIT(ah, AR_IMR_S5, + (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) | + SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); + + clear_bit(timer->index, &timer_table->timer_mask.timer_bits); + + /* if no timer is enabled, turn off interrupt mask */ + if (timer_table->timer_mask.val == 0) { + ath9k_hw_set_interrupts(ah, 0); + ah->ah_sc->imask &= ~ATH9K_INT_GENTIMER; + ath9k_hw_set_interrupts(ah, ah->ah_sc->imask); + } +} + +void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer) +{ + struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; + + /* free the hardware generic timer slot */ + timer_table->timers[timer->index] = NULL; + kfree(timer); +} + +/* + * Generic Timer Interrupts handling + */ +void ath_gen_timer_isr(struct ath_hw *ah) +{ + struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; + struct ath_gen_timer *timer; + u32 trigger_mask, thresh_mask, index; + + /* get hardware generic timer interrupt status */ + trigger_mask = ah->intr_gen_timer_trigger; + thresh_mask = ah->intr_gen_timer_thresh; + trigger_mask &= timer_table->timer_mask.val; + thresh_mask &= timer_table->timer_mask.val; + + trigger_mask &= ~thresh_mask; + + while (thresh_mask) { + index = rightmost_index(timer_table, &thresh_mask); + timer = timer_table->timers[index]; + BUG_ON(!timer); + DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER, + "TSF overflow for Gen timer %d\n", index); + timer->overflow(timer->arg); + } + + while (trigger_mask) { + index = rightmost_index(timer_table, &trigger_mask); + timer = timer_table->timers[index]; + BUG_ON(!timer); + DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER, + "Gen timer[%d] trigger\n", index); + timer->trigger(timer->arg); + } } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 9d0b31ad4603..5ca6ffa70912 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -42,6 +42,9 @@ #define AR_SUBVENDOR_ID_NEW_A 0x7065 #define AR5416_MAGIC 0x19641014 +#define AR5416_DEVID_AR9287_PCI 0x002D +#define AR5416_DEVID_AR9287_PCIE 0x002E + /* Register read/write primitives */ #define REG_WRITE(_ah, _reg, _val) ath9k_iowrite32((_ah), (_reg), (_val)) #define REG_READ(_ah, _reg) ath9k_ioread32((_ah), (_reg)) @@ -76,6 +79,7 @@ #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_TX_FRAME 3 +#define AR_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL 4 #define AR_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED 5 #define AR_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED 6 @@ -95,6 +99,7 @@ #define MAX_RATE_POWER 63 #define AH_WAIT_TIMEOUT 100000 /* (us) */ +#define AH_TSF_WRITE_TIMEOUT 100 /* (us) */ #define AH_TIME_QUANTUM 10 #define AR_KEYTABLE_SIZE 128 #define POWER_UP_TIME 200000 @@ -113,15 +118,20 @@ 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 + ATH9K_MODE_11G, + ATH9K_MODE_11NA_HT20, + ATH9K_MODE_11NG_HT20, + ATH9K_MODE_11NA_HT40PLUS, + ATH9K_MODE_11NA_HT40MINUS, + ATH9K_MODE_11NG_HT40PLUS, + ATH9K_MODE_11NG_HT40MINUS, + ATH9K_MODE_MAX, +}; + +enum ath9k_ant_setting { + ATH9K_ANT_VARIABLE = 0, + ATH9K_ANT_FIXED_A, + ATH9K_ANT_FIXED_B }; enum ath9k_hw_caps { @@ -142,7 +152,6 @@ enum ath9k_hw_caps { ATH9K_HW_CAP_ENHANCEDPM = BIT(14), ATH9K_HW_CAP_AUTOSLEEP = BIT(15), ATH9K_HW_CAP_4KB_SPLITTRANS = BIT(16), - ATH9K_HW_CAP_BT_COEX = BIT(17) }; enum ath9k_capability_type { @@ -188,7 +197,7 @@ struct ath9k_ops_config { u32 cck_trig_high; u32 cck_trig_low; u32 enable_ani; - u16 diversity_control; + enum ath9k_ant_setting diversity_control; u16 antenna_switch_swap; int serialize_regmode; bool intr_mitigation; @@ -229,6 +238,7 @@ enum ath9k_int { ATH9K_INT_GPIO = 0x01000000, ATH9K_INT_CABEND = 0x02000000, ATH9K_INT_TSFOOR = 0x04000000, + ATH9K_INT_GENTIMER = 0x08000000, ATH9K_INT_CST = 0x10000000, ATH9K_INT_GTT = 0x20000000, ATH9K_INT_FATAL = 0x40000000, @@ -327,12 +337,6 @@ enum ath9k_power_mode { ATH9K_PM_UNDEFINED }; -enum ath9k_ant_setting { - ATH9K_ANT_VARIABLE = 0, - ATH9K_ANT_FIXED_A, - ATH9K_ANT_FIXED_B -}; - enum ath9k_tp_scale { ATH9K_TP_SCALE_MAX = 0, ATH9K_TP_SCALE_50, @@ -388,18 +392,53 @@ struct ath9k_hw_version { u16 analog2GhzRev; }; +/* Generic TSF timer definitions */ + +#define ATH_MAX_GEN_TIMER 16 + +#define AR_GENTMR_BIT(_index) (1 << (_index)) + +/* + * Using de Bruijin sequence to to look up 1's index in a 32 bit number + * debruijn32 = 0000 0111 0111 1100 1011 0101 0011 0001 + */ +#define debruijn32 0x077CB531UL + +struct ath_gen_timer_configuration { + u32 next_addr; + u32 period_addr; + u32 mode_addr; + u32 mode_mask; +}; + +struct ath_gen_timer { + void (*trigger)(void *arg); + void (*overflow)(void *arg); + void *arg; + u8 index; +}; + +struct ath_gen_timer_table { + u32 gen_timer_index[32]; + struct ath_gen_timer *timers[ATH_MAX_GEN_TIMER]; + union { + unsigned long timer_bits; + u16 val; + } timer_mask; +}; + struct ath_hw { struct ath_softc *ah_sc; struct ath9k_hw_version hw_version; struct ath9k_ops_config config; struct ath9k_hw_capabilities caps; - struct ath_regulatory regulatory; struct ath9k_channel channels[38]; struct ath9k_channel *curchan; union { struct ar5416_eeprom_def def; struct ar5416_eeprom_4k map4k; + struct ar9287_eeprom map9287; } eeprom; const struct eeprom_ops *eep_ops; enum ath9k_eep_map eep_map; @@ -411,15 +450,15 @@ struct ath_hw { u16 rfsilent; u32 rfkill_gpio; u32 rfkill_polarity; - u32 btactive_gpio; - u32 wlanactive_gpio; u32 ah_flags; + bool htc_reset_init; + enum nl80211_iftype opmode; enum ath9k_power_mode power_mode; - enum ath9k_power_mode restore_mode; struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; + struct ath9k_pacal_info pacal_info; struct ar5416Stats stats; struct ath9k_tx_queue_info txq[ATH9K_NUM_TX_QUEUES]; @@ -432,8 +471,6 @@ struct ath_hw { u32 txurn_interrupt_mask; bool chip_fullsleep; u32 atim_window; - u16 antenna_switch_swap; - enum ath9k_ant_setting diversity_control; /* Calibration */ enum ath9k_cal_types supp_cals; @@ -502,7 +539,6 @@ struct ath_hw { /* ANI */ u32 proc_phyerr; - bool has_hw_phycounters; u32 aniperiod; struct ar5416AniState *curani; struct ar5416AniState ani[255]; @@ -520,6 +556,7 @@ struct ath_hw { u32 originalGain[22]; int initPDADC; int PDADCdelta; + u8 led_pin; struct ar5416IniArray iniModes; struct ar5416IniArray iniCommon; @@ -536,13 +573,17 @@ struct ath_hw { struct ar5416IniArray iniModesAdditional; struct ar5416IniArray iniModesRxGain; struct ar5416IniArray iniModesTxGain; + + u32 intr_gen_timer_trigger; + u32 intr_gen_timer_thresh; + struct ath_gen_timer_table hw_gen_timers; }; -/* Attach, Detach, Reset */ +/* Initialization, Detach, Reset */ const char *ath9k_hw_probe(u16 vendorid, u16 devid); void ath9k_hw_detach(struct ath_hw *ah); -struct ath_hw *ath9k_hw_attach(u16 devid, struct ath_softc *sc, int *error); -void ath9k_hw_rfdetach(struct ath_hw *ah); +int ath9k_hw_init(struct ath_hw *ah); +void ath9k_hw_rf_free(struct ath_hw *ah); int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, bool bChannelChange); void ath9k_hw_fill_cap_info(struct ath_hw *ah); @@ -596,7 +637,7 @@ void ath9k_hw_write_associd(struct ath_softc *sc); u64 ath9k_hw_gettsf64(struct ath_hw *ah); void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64); void ath9k_hw_reset_tsf(struct ath_hw *ah); -bool ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting); +void ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting); bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us); void ath9k_hw_set11nmac2040(struct ath_hw *ah, enum ath9k_ht_macmode mode); void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period); @@ -609,9 +650,19 @@ void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore); /* Interrupt Handling */ bool ath9k_hw_intrpend(struct ath_hw *ah); bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked); -enum ath9k_int ath9k_hw_intrget(struct ath_hw *ah); enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints); -void ath9k_hw_btcoex_enable(struct ath_hw *ah); +/* Generic hw timer primitives */ +struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, + void (*trigger)(void *), + void (*overflow)(void *), + void *arg, + u8 timer_index); +void ath_gen_timer_start(struct ath_hw *ah, struct ath_gen_timer *timer, + u32 timer_next, u32 timer_period); +void ath_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer); +void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer); +void ath_gen_timer_isr(struct ath_hw *hw); +u32 ath9k_hw_gettsf32(struct ath_hw *ah); #endif diff --git a/drivers/net/wireless/ath/ath9k/initvals.h b/drivers/net/wireless/ath/ath9k/initvals.h index e2f0a34b79a1..8622265a030a 100644 --- a/drivers/net/wireless/ath/ath9k/initvals.h +++ b/drivers/net/wireless/ath/ath9k/initvals.h @@ -2782,7 +2782,7 @@ static const u32 ar9280Common_9280_2[][2] = { { 0x00008338, 0x00ff0000 }, { 0x0000833c, 0x00000000 }, { 0x00008340, 0x000107ff }, - { 0x00008344, 0x00581043 }, + { 0x00008344, 0x00481043 }, { 0x00009808, 0x00000000 }, { 0x0000980c, 0xafa68e30 }, { 0x00009810, 0xfd14e000 }, @@ -3439,7 +3439,7 @@ static const u32 ar9280PciePhy_clkreq_always_on_L1_9280[][2] = { {0x00004044, 0x00000000 }, }; -/* AR9285 */ +/* AR9285 Revsion 10*/ static const u_int32_t ar9285Modes_9285[][6] = { { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, @@ -3955,7 +3955,7 @@ static const u_int32_t ar9285Common_9285[][2] = { { 0x00008338, 0x00000000 }, { 0x0000833c, 0x00000000 }, { 0x00008340, 0x00010380 }, - { 0x00008344, 0x00581043 }, + { 0x00008344, 0x00481043 }, { 0x00009808, 0x00000000 }, { 0x0000980c, 0xafe68e30 }, { 0x00009810, 0xfd14e000 }, @@ -4121,8 +4121,9 @@ static const u_int32_t ar9285PciePhy_clkreq_off_L1_9285[][2] = { {0x00004044, 0x00000000 }, }; -/* AR9285 v1_2 PCI Register Writes. Created: 03/04/09 */ +/* AR9285 v1_2 PCI Register Writes. Created: 04/13/09 */ static const u_int32_t ar9285Modes_9285_1_2[][6] = { + /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ { 0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160, 0x000001e0 }, { 0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c, 0x000001e0 }, { 0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38, 0x00001180 }, @@ -4132,13 +4133,14 @@ static const u_int32_t ar9285Modes_9285_1_2[][6] = { { 0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440, 0x00006880 }, { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x00000303 }, { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, - { 0x00009824, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e }, { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 }, { 0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e }, { 0x00009844, 0x0372161e, 0x0372161e, 0x03721620, 0x03721620, 0x037216a0 }, { 0x00009848, 0x00001066, 0x00001066, 0x00001053, 0x00001053, 0x00001059 }, + { 0x0000a848, 0x00001066, 0x00001066, 0x00001053, 0x00001053, 0x00001059 }, { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 }, { 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e }, { 0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e, 0x3139605e }, @@ -4156,7 +4158,7 @@ static const u_int32_t ar9285Modes_9285_1_2[][6] = { { 0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00 }, { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, - { 0x000099c8, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329 }, + { 0x000099c8, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f }, { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 }, { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 }, { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, @@ -4419,6 +4421,7 @@ static const u_int32_t ar9285Modes_9285_1_2[][6] = { { 0x0000abfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, { 0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004 }, { 0x0000a20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000, 0x0001f000 }, + { 0x0000b20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000, 0x0001f000 }, { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, { 0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000, 0x0004a000 }, @@ -4598,7 +4601,7 @@ static const u_int32_t ar9285Common_9285_1_2[][2] = { { 0x00008258, 0x00000000 }, { 0x0000825c, 0x400000ff }, { 0x00008260, 0x00080922 }, - { 0x00008264, 0xa8a00010 }, + { 0x00008264, 0x88a00010 }, { 0x00008270, 0x00000000 }, { 0x00008274, 0x40000000 }, { 0x00008278, 0x003e4180 }, @@ -4618,7 +4621,7 @@ static const u_int32_t ar9285Common_9285_1_2[][2] = { { 0x00008338, 0x00ff0000 }, { 0x0000833c, 0x00000000 }, { 0x00008340, 0x00010380 }, - { 0x00008344, 0x00581043 }, + { 0x00008344, 0x00481043 }, { 0x00009808, 0x00000000 }, { 0x0000980c, 0xafe68e30 }, { 0x00009810, 0xfd14e000 }, @@ -4647,7 +4650,7 @@ static const u_int32_t ar9285Common_9285_1_2[][2] = { { 0x00009954, 0x5f3ca3de }, { 0x00009958, 0x2108ecff }, { 0x00009968, 0x000003ce }, - { 0x00009970, 0x192bb515 }, + { 0x00009970, 0x192bb514 }, { 0x00009974, 0x00000000 }, { 0x00009978, 0x00000001 }, { 0x0000997c, 0x00000000 }, @@ -4725,7 +4728,7 @@ static const u_int32_t ar9285Common_9285_1_2[][2] = { { 0x00007800, 0x00140000 }, { 0x00007804, 0x0e4548d8 }, { 0x00007808, 0x54214514 }, - { 0x0000780c, 0x02025820 }, + { 0x0000780c, 0x02025830 }, { 0x00007810, 0x71c0d388 }, { 0x00007814, 0x924934a8 }, { 0x0000781c, 0x00000000 }, @@ -4752,18 +4755,18 @@ static const u_int32_t ar9285Common_9285_1_2[][2] = { static const u_int32_t ar9285Modes_high_power_tx_gain_9285_1_2[][6] = { /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, - { 0x0000a304, 0x00000000, 0x00000000, 0x00005200, 0x00005200, 0x00000000 }, - { 0x0000a308, 0x00000000, 0x00000000, 0x00007201, 0x00007201, 0x00000000 }, + { 0x0000a304, 0x00000000, 0x00000000, 0x00006200, 0x00006200, 0x00000000 }, + { 0x0000a308, 0x00000000, 0x00000000, 0x00008201, 0x00008201, 0x00000000 }, { 0x0000a30c, 0x00000000, 0x00000000, 0x0000b240, 0x0000b240, 0x00000000 }, { 0x0000a310, 0x00000000, 0x00000000, 0x0000d241, 0x0000d241, 0x00000000 }, - { 0x0000a314, 0x00000000, 0x00000000, 0x0000f440, 0x0000f440, 0x00000000 }, - { 0x0000a318, 0x00000000, 0x00000000, 0x00014640, 0x00014640, 0x00000000 }, - { 0x0000a31c, 0x00000000, 0x00000000, 0x00018680, 0x00018680, 0x00000000 }, - { 0x0000a320, 0x00000000, 0x00000000, 0x00019841, 0x00019841, 0x00000000 }, - { 0x0000a324, 0x00000000, 0x00000000, 0x0001ca40, 0x0001ca40, 0x00000000 }, - { 0x0000a328, 0x00000000, 0x00000000, 0x0001fa80, 0x0001fa80, 0x00000000 }, - { 0x0000a32c, 0x00000000, 0x00000000, 0x00023ac0, 0x00023ac0, 0x00000000 }, - { 0x0000a330, 0x00000000, 0x00000000, 0x0002ab40, 0x0002ab40, 0x00000000 }, + { 0x0000a314, 0x00000000, 0x00000000, 0x0000f600, 0x0000f600, 0x00000000 }, + { 0x0000a318, 0x00000000, 0x00000000, 0x00012800, 0x00012800, 0x00000000 }, + { 0x0000a31c, 0x00000000, 0x00000000, 0x00016802, 0x00016802, 0x00000000 }, + { 0x0000a320, 0x00000000, 0x00000000, 0x0001b805, 0x0001b805, 0x00000000 }, + { 0x0000a324, 0x00000000, 0x00000000, 0x00021a80, 0x00021a80, 0x00000000 }, + { 0x0000a328, 0x00000000, 0x00000000, 0x00028b00, 0x00028b00, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x0002ab40, 0x0002ab40, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x0002cd80, 0x0002cd80, 0x00000000 }, { 0x0000a334, 0x00000000, 0x00000000, 0x00033d82, 0x00033d82, 0x00000000 }, { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 }, { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 }, @@ -4776,13 +4779,13 @@ static const u_int32_t ar9285Modes_high_power_tx_gain_9285_1_2[][6] = { { 0x00007838, 0xfac68803, 0xfac68803, 0xfac68803, 0xfac68803, 0xfac68803 }, { 0x0000786c, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe, 0x08609ebe }, { 0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00 }, - { 0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a21a652, 0x0a21a652, 0x0a22a652 }, - { 0x0000a278, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce }, - { 0x0000a27c, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce, 0x050701ce }, - { 0x0000a394, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce }, - { 0x0000a398, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce }, - { 0x0000a3dc, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce, 0x1ce739ce }, - { 0x0000a3e0, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce, 0x000001ce }, + { 0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a216652, 0x0a216652, 0x0a22a652 }, + { 0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7 }, + { 0x0000a27c, 0x050380e7, 0x050380e7, 0x050380e7, 0x050380e7, 0x050380e7 }, + { 0x0000a394, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7 }, + { 0x0000a398, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7 }, + { 0x0000a3dc, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7 }, + { 0x0000a3e0, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7, 0x000000e7 }, }; static const u_int32_t ar9285Modes_original_tx_gain_9285_1_2[][6] = { @@ -4846,3 +4849,2185 @@ static const u_int32_t ar9285PciePhy_clkreq_off_L1_9285_1_2[][2] = { {0x00004040, 0x00043007 }, {0x00004044, 0x00000000 }, }; + +/* AR9287 Revision 10 */ +static const u_int32_t ar9287Modes_9287_1_0[][6] = { + /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ + { 0x00001030, 0x00000000, 0x00000000, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000000, 0x00000000, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000000, 0x00000000, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008 }, + { 0x00008014, 0x00000000, 0x00000000, 0x10801600, 0x08400b00, 0x06e006e0 }, + { 0x0000801c, 0x00000000, 0x00000000, 0x12e00057, 0x12e0002b, 0x0988004f }, + { 0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810, 0x08f04810 }, + { 0x000081d0, 0x00003200, 0x00003200, 0x0000320a, 0x0000320a, 0x0000320a }, + { 0x00008318, 0x00000000, 0x00000000, 0x00006880, 0x00003440, 0x00006880 }, + { 0x00009804, 0x00000000, 0x00000000, 0x000003c4, 0x00000300, 0x00000303 }, + { 0x00009820, 0x00000000, 0x00000000, 0x02020200, 0x02020200, 0x02020200 }, + { 0x00009824, 0x00000000, 0x00000000, 0x01000e0e, 0x01000e0e, 0x01000e0e }, + { 0x00009828, 0x00000000, 0x00000000, 0x0a020001, 0x0a020001, 0x0a020001 }, + { 0x00009834, 0x00000000, 0x00000000, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009838, 0x00000003, 0x00000003, 0x00000007, 0x00000007, 0x00000007 }, + { 0x00009840, 0x206a002e, 0x206a002e, 0x206a012e, 0x206a012e, 0x206a012e }, + { 0x00009844, 0x03720000, 0x03720000, 0x037216a0, 0x037216a0, 0x037216a0 }, + { 0x00009850, 0x60000000, 0x60000000, 0x6d4000e2, 0x6c4000e2, 0x6c4000e2 }, + { 0x00009858, 0x7c000d00, 0x7c000d00, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e }, + { 0x0000985c, 0x3100005e, 0x3100005e, 0x3139605e, 0x31395d5e, 0x31395d5e }, + { 0x00009860, 0x00058d00, 0x00058d00, 0x00058d20, 0x00058d20, 0x00058d18 }, + { 0x00009864, 0x00000e00, 0x00000e00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x000040c0, 0x000040c0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 }, + { 0x0000986c, 0x00000080, 0x00000080, 0x06903881, 0x06903881, 0x06903881 }, + { 0x00009914, 0x00000000, 0x00000000, 0x00001130, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x00000000, 0x00000000, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8a01, 0xd00a8a01, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d }, + { 0x00009944, 0xefbc0000, 0xefbc0000, 0xefbc1010, 0xefbc1010, 0xefbc1010 }, + { 0x00009960, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000010 }, + { 0x0000a960, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000010 }, + { 0x00009964, 0x00000000, 0x00000000, 0x00000210, 0x00000210, 0x00000210 }, + { 0x0000c968, 0x00000200, 0x00000200, 0x000003ce, 0x000003ce, 0x000003ce }, + { 0x000099b8, 0x00000000, 0x00000000, 0x0000001c, 0x0000001c, 0x0000001c }, + { 0x000099bc, 0x00000000, 0x00000000, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x000099c0, 0x00000000, 0x00000000, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, + { 0x0000a204, 0x00000440, 0x00000440, 0x00000444, 0x00000444, 0x00000444 }, + { 0x0000a20c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000b20c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a21c, 0x1803800a, 0x1803800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a250, 0x00000000, 0x00000000, 0x0004a000, 0x0004a000, 0x0004a000 }, + { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e }, + { 0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, +}; + +static const u_int32_t ar9287Common_9287_1_0[][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 }, + { 0x00007020, 0x00000000 }, + { 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 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0xffffffff }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x18487320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c0, 0x00000000 }, + { 0x000081c4, 0x00000000 }, + { 0x000081d4, 0x00000000 }, + { 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 }, + { 0x00008264, 0xa8a00010 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x000000ff }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x0000829c, 0x00000000 }, + { 0x00008300, 0x00000040 }, + { 0x00008314, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000007 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00ff0000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x000107ff }, + { 0x00008344, 0x01c81043 }, + { 0x00008360, 0xffffffff }, + { 0x00008364, 0xffffffff }, + { 0x00008368, 0x00000000 }, + { 0x00008370, 0x00000000 }, + { 0x00008374, 0x000000ff }, + { 0x00008378, 0x00000000 }, + { 0x0000837c, 0x00000000 }, + { 0x00008380, 0xffffffff }, + { 0x00008384, 0xffffffff }, + { 0x00008390, 0x0fffffff }, + { 0x00008394, 0x0fffffff }, + { 0x00008398, 0x00000000 }, + { 0x0000839c, 0x00000000 }, + { 0x000083a0, 0x00000000 }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xafe68e30 }, + { 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, 0x10002310 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x04900000 }, + { 0x0000a920, 0x04900000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009930, 0x00000000 }, + { 0x0000a930, 0x00000000 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009948, 0x9280c00a }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0x5f3ca3de }, + { 0x00009958, 0x0108ecff }, + { 0x00009940, 0x14750604 }, + { 0x0000c95c, 0x004b6a8e }, + { 0x00009970, 0x990bb515 }, + { 0x00009974, 0x00000000 }, + { 0x00009978, 0x00000001 }, + { 0x0000997c, 0x00000000 }, + { 0x000099a0, 0x00000000 }, + { 0x000099a4, 0x00000001 }, + { 0x000099a8, 0x201fff00 }, + { 0x000099ac, 0x0c6f0000 }, + { 0x000099b0, 0x03051000 }, + { 0x000099b4, 0x00000820 }, + { 0x000099c4, 0x06336f77 }, + { 0x000099c8, 0x6af65329 }, + { 0x000099cc, 0x08f186c8 }, + { 0x000099d0, 0x00046384 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000000 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099f0, 0x00000000 }, + { 0x000099fc, 0x00001042 }, + { 0x0000a1f4, 0x00fffeff }, + { 0x0000a1f8, 0x00f5f9ff }, + { 0x0000a1fc, 0xb79f6427 }, + { 0x0000a208, 0x803e4788 }, + { 0x0000a210, 0x4080a333 }, + { 0x0000a214, 0x40206c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x01834061 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x000003b5 }, + { 0x0000a22c, 0x233f7180 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a23c, 0x13c889af }, + { 0x0000a240, 0x38490a20 }, + { 0x0000a244, 0x00000000 }, + { 0x0000a248, 0xfffffffc }, + { 0x0000a24c, 0x00000000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0cdbd380 }, + { 0x0000a25c, 0x0f0f0f01 }, + { 0x0000a260, 0xdfa91f01 }, + { 0x0000a264, 0x00418a11 }, + { 0x0000b264, 0x00418a11 }, + { 0x0000a268, 0x00000000 }, + { 0x0000a26c, 0x0e79e5c6 }, + { 0x0000b26c, 0x0e79e5c6 }, + { 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 }, + { 0x0000b398, 0x000001ce }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3c8, 0x00000246 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3dc, 0x1ce739ce }, + { 0x0000a3e0, 0x000001ce }, + { 0x0000a3e4, 0x00000000 }, + { 0x0000a3e8, 0x18c43433 }, + { 0x0000a3ec, 0x00f70081 }, + { 0x0000a3f0, 0x01036a1e }, + { 0x0000a3f4, 0x00000000 }, + { 0x0000b3f4, 0x00000000 }, + { 0x0000a7d8, 0x00000001 }, + { 0x00007800, 0x00000800 }, + { 0x00007804, 0x6c35ffb0 }, + { 0x00007808, 0x6db6c000 }, + { 0x0000780c, 0x6db6cb30 }, + { 0x00007810, 0x6db6cb6c }, + { 0x00007814, 0x0501e200 }, + { 0x00007818, 0x0094128d }, + { 0x0000781c, 0x976ee392 }, + { 0x00007820, 0xf75ff6fc }, + { 0x00007824, 0x00040000 }, + { 0x00007828, 0xdb003012 }, + { 0x0000782c, 0x04924914 }, + { 0x00007830, 0x21084210 }, + { 0x00007834, 0x00140000 }, + { 0x00007838, 0x0e4548d8 }, + { 0x0000783c, 0x54214514 }, + { 0x00007840, 0x02025820 }, + { 0x00007844, 0x71c0d388 }, + { 0x00007848, 0x934934a8 }, + { 0x00007850, 0x00000000 }, + { 0x00007854, 0x00000800 }, + { 0x00007858, 0x6c35ffb0 }, + { 0x0000785c, 0x6db6c000 }, + { 0x00007860, 0x6db6cb2c }, + { 0x00007864, 0x6db6cb6c }, + { 0x00007868, 0x0501e200 }, + { 0x0000786c, 0x0094128d }, + { 0x00007870, 0x976ee392 }, + { 0x00007874, 0xf75ff6fc }, + { 0x00007878, 0x00040000 }, + { 0x0000787c, 0xdb003012 }, + { 0x00007880, 0x04924914 }, + { 0x00007884, 0x21084210 }, + { 0x00007888, 0x001b6db0 }, + { 0x0000788c, 0x00376b63 }, + { 0x00007890, 0x06db6db6 }, + { 0x00007894, 0x006d8000 }, + { 0x00007898, 0x48100000 }, + { 0x0000789c, 0x00000000 }, + { 0x000078a0, 0x08000000 }, + { 0x000078a4, 0x0007ffd8 }, + { 0x000078a8, 0x0007ffd8 }, + { 0x000078ac, 0x001c0020 }, + { 0x000078b0, 0x000611eb }, + { 0x000078b4, 0x40008080 }, + { 0x000078b8, 0x2a850160 }, +}; + +static const u_int32_t ar9287Modes_tx_gain_9287_1_0[][6] = { + /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ + { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a304, 0x00000000, 0x00000000, 0x00004002, 0x00004002, 0x00004002 }, + { 0x0000a308, 0x00000000, 0x00000000, 0x00008004, 0x00008004, 0x00008004 }, + { 0x0000a30c, 0x00000000, 0x00000000, 0x0000c00a, 0x0000c00a, 0x0000c00a }, + { 0x0000a310, 0x00000000, 0x00000000, 0x0001000c, 0x0001000c, 0x0001000c }, + { 0x0000a314, 0x00000000, 0x00000000, 0x0001420b, 0x0001420b, 0x0001420b }, + { 0x0000a318, 0x00000000, 0x00000000, 0x0001824a, 0x0001824a, 0x0001824a }, + { 0x0000a31c, 0x00000000, 0x00000000, 0x0001c44a, 0x0001c44a, 0x0001c44a }, + { 0x0000a320, 0x00000000, 0x00000000, 0x0002064a, 0x0002064a, 0x0002064a }, + { 0x0000a324, 0x00000000, 0x00000000, 0x0002484a, 0x0002484a, 0x0002484a }, + { 0x0000a328, 0x00000000, 0x00000000, 0x00028a4a, 0x00028a4a, 0x00028a4a }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x0002cc4a, 0x0002cc4a, 0x0002cc4a }, + { 0x0000a330, 0x00000000, 0x00000000, 0x00030e4a, 0x00030e4a, 0x00030e4a }, + { 0x0000a334, 0x00000000, 0x00000000, 0x00034e8a, 0x00034e8a, 0x00034e8a }, + { 0x0000a338, 0x00000000, 0x00000000, 0x00038e8c, 0x00038e8c, 0x00038e8c }, + { 0x0000a33c, 0x00000000, 0x00000000, 0x0003cecc, 0x0003cecc, 0x0003cecc }, + { 0x0000a340, 0x00000000, 0x00000000, 0x00040ed4, 0x00040ed4, 0x00040ed4 }, + { 0x0000a344, 0x00000000, 0x00000000, 0x00044edc, 0x00044edc, 0x00044edc }, + { 0x0000a348, 0x00000000, 0x00000000, 0x00048ede, 0x00048ede, 0x00048ede }, + { 0x0000a34c, 0x00000000, 0x00000000, 0x0004cf1e, 0x0004cf1e, 0x0004cf1e }, + { 0x0000a350, 0x00000000, 0x00000000, 0x00050f5e, 0x00050f5e, 0x00050f5e }, + { 0x0000a354, 0x00000000, 0x00000000, 0x00054f9e, 0x00054f9e, 0x00054f9e }, + { 0x0000a780, 0x00000000, 0x00000000, 0x00000060, 0x00000060, 0x00000060 }, + { 0x0000a784, 0x00000000, 0x00000000, 0x00004062, 0x00004062, 0x00004062 }, + { 0x0000a788, 0x00000000, 0x00000000, 0x00008064, 0x00008064, 0x00008064 }, + { 0x0000a78c, 0x00000000, 0x00000000, 0x0000c0a4, 0x0000c0a4, 0x0000c0a4 }, + { 0x0000a790, 0x00000000, 0x00000000, 0x000100b0, 0x000100b0, 0x000100b0 }, + { 0x0000a794, 0x00000000, 0x00000000, 0x000140b2, 0x000140b2, 0x000140b2 }, + { 0x0000a798, 0x00000000, 0x00000000, 0x000180b4, 0x000180b4, 0x000180b4 }, + { 0x0000a79c, 0x00000000, 0x00000000, 0x0001c0f4, 0x0001c0f4, 0x0001c0f4 }, + { 0x0000a7a0, 0x00000000, 0x00000000, 0x00020134, 0x00020134, 0x00020134 }, + { 0x0000a7a4, 0x00000000, 0x00000000, 0x000240fe, 0x000240fe, 0x000240fe }, + { 0x0000a7a8, 0x00000000, 0x00000000, 0x0002813e, 0x0002813e, 0x0002813e }, + { 0x0000a7ac, 0x00000000, 0x00000000, 0x0002c17e, 0x0002c17e, 0x0002c17e }, + { 0x0000a7b0, 0x00000000, 0x00000000, 0x000301be, 0x000301be, 0x000301be }, + { 0x0000a7b4, 0x00000000, 0x00000000, 0x000341fe, 0x000341fe, 0x000341fe }, + { 0x0000a7b8, 0x00000000, 0x00000000, 0x000341fe, 0x000341fe, 0x000341fe }, + { 0x0000a7bc, 0x00000000, 0x00000000, 0x000341fe, 0x000341fe, 0x000341fe }, + { 0x0000a7c0, 0x00000000, 0x00000000, 0x000341fe, 0x000341fe, 0x000341fe }, + { 0x0000a7c4, 0x00000000, 0x00000000, 0x000341fe, 0x000341fe, 0x000341fe }, + { 0x0000a7c8, 0x00000000, 0x00000000, 0x000341fe, 0x000341fe, 0x000341fe }, + { 0x0000a7cc, 0x00000000, 0x00000000, 0x000341fe, 0x000341fe, 0x000341fe }, + { 0x0000a7d0, 0x00000000, 0x00000000, 0x000341fe, 0x000341fe, 0x000341fe }, + { 0x0000a7d4, 0x00000000, 0x00000000, 0x000341fe, 0x000341fe, 0x000341fe }, + { 0x0000a274, 0x0a180000, 0x0a180000, 0x0a1aa000, 0x0a1aa000, 0x0a1aa000 }, +}; + + +static const u_int32_t ar9287Modes_rx_gain_9287_1_0[][6] = { + /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ + { 0x00009a00, 0x00000000, 0x00000000, 0x0000a120, 0x0000a120, 0x0000a120 }, + { 0x00009a04, 0x00000000, 0x00000000, 0x0000a124, 0x0000a124, 0x0000a124 }, + { 0x00009a08, 0x00000000, 0x00000000, 0x0000a128, 0x0000a128, 0x0000a128 }, + { 0x00009a0c, 0x00000000, 0x00000000, 0x0000a12c, 0x0000a12c, 0x0000a12c }, + { 0x00009a10, 0x00000000, 0x00000000, 0x0000a130, 0x0000a130, 0x0000a130 }, + { 0x00009a14, 0x00000000, 0x00000000, 0x0000a194, 0x0000a194, 0x0000a194 }, + { 0x00009a18, 0x00000000, 0x00000000, 0x0000a198, 0x0000a198, 0x0000a198 }, + { 0x00009a1c, 0x00000000, 0x00000000, 0x0000a20c, 0x0000a20c, 0x0000a20c }, + { 0x00009a20, 0x00000000, 0x00000000, 0x0000a210, 0x0000a210, 0x0000a210 }, + { 0x00009a24, 0x00000000, 0x00000000, 0x0000a284, 0x0000a284, 0x0000a284 }, + { 0x00009a28, 0x00000000, 0x00000000, 0x0000a288, 0x0000a288, 0x0000a288 }, + { 0x00009a2c, 0x00000000, 0x00000000, 0x0000a28c, 0x0000a28c, 0x0000a28c }, + { 0x00009a30, 0x00000000, 0x00000000, 0x0000a290, 0x0000a290, 0x0000a290 }, + { 0x00009a34, 0x00000000, 0x00000000, 0x0000a294, 0x0000a294, 0x0000a294 }, + { 0x00009a38, 0x00000000, 0x00000000, 0x0000a2a0, 0x0000a2a0, 0x0000a2a0 }, + { 0x00009a3c, 0x00000000, 0x00000000, 0x0000a2a4, 0x0000a2a4, 0x0000a2a4 }, + { 0x00009a40, 0x00000000, 0x00000000, 0x0000a2a8, 0x0000a2a8, 0x0000a2a8 }, + { 0x00009a44, 0x00000000, 0x00000000, 0x0000a2ac, 0x0000a2ac, 0x0000a2ac }, + { 0x00009a48, 0x00000000, 0x00000000, 0x0000a2b0, 0x0000a2b0, 0x0000a2b0 }, + { 0x00009a4c, 0x00000000, 0x00000000, 0x0000a2b4, 0x0000a2b4, 0x0000a2b4 }, + { 0x00009a50, 0x00000000, 0x00000000, 0x0000a2b8, 0x0000a2b8, 0x0000a2b8 }, + { 0x00009a54, 0x00000000, 0x00000000, 0x0000a2c4, 0x0000a2c4, 0x0000a2c4 }, + { 0x00009a58, 0x00000000, 0x00000000, 0x0000a708, 0x0000a708, 0x0000a708 }, + { 0x00009a5c, 0x00000000, 0x00000000, 0x0000a70c, 0x0000a70c, 0x0000a70c }, + { 0x00009a60, 0x00000000, 0x00000000, 0x0000a710, 0x0000a710, 0x0000a710 }, + { 0x00009a64, 0x00000000, 0x00000000, 0x0000ab04, 0x0000ab04, 0x0000ab04 }, + { 0x00009a68, 0x00000000, 0x00000000, 0x0000ab08, 0x0000ab08, 0x0000ab08 }, + { 0x00009a6c, 0x00000000, 0x00000000, 0x0000ab0c, 0x0000ab0c, 0x0000ab0c }, + { 0x00009a70, 0x00000000, 0x00000000, 0x0000ab10, 0x0000ab10, 0x0000ab10 }, + { 0x00009a74, 0x00000000, 0x00000000, 0x0000ab14, 0x0000ab14, 0x0000ab14 }, + { 0x00009a78, 0x00000000, 0x00000000, 0x0000ab18, 0x0000ab18, 0x0000ab18 }, + { 0x00009a7c, 0x00000000, 0x00000000, 0x0000ab8c, 0x0000ab8c, 0x0000ab8c }, + { 0x00009a80, 0x00000000, 0x00000000, 0x0000ab90, 0x0000ab90, 0x0000ab90 }, + { 0x00009a84, 0x00000000, 0x00000000, 0x0000ab94, 0x0000ab94, 0x0000ab94 }, + { 0x00009a88, 0x00000000, 0x00000000, 0x0000ab98, 0x0000ab98, 0x0000ab98 }, + { 0x00009a8c, 0x00000000, 0x00000000, 0x0000aba4, 0x0000aba4, 0x0000aba4 }, + { 0x00009a90, 0x00000000, 0x00000000, 0x0000aba8, 0x0000aba8, 0x0000aba8 }, + { 0x00009a94, 0x00000000, 0x00000000, 0x0000cb04, 0x0000cb04, 0x0000cb04 }, + { 0x00009a98, 0x00000000, 0x00000000, 0x0000cb08, 0x0000cb08, 0x0000cb08 }, + { 0x00009a9c, 0x00000000, 0x00000000, 0x0000cb0c, 0x0000cb0c, 0x0000cb0c }, + { 0x00009aa0, 0x00000000, 0x00000000, 0x0000cb10, 0x0000cb10, 0x0000cb10 }, + { 0x00009aa4, 0x00000000, 0x00000000, 0x0000cb14, 0x0000cb14, 0x0000cb14 }, + { 0x00009aa8, 0x00000000, 0x00000000, 0x0000cb18, 0x0000cb18, 0x0000cb18 }, + { 0x00009aac, 0x00000000, 0x00000000, 0x0000cb8c, 0x0000cb8c, 0x0000cb8c }, + { 0x00009ab0, 0x00000000, 0x00000000, 0x0000cb90, 0x0000cb90, 0x0000cb90 }, + { 0x00009ab4, 0x00000000, 0x00000000, 0x0000cf18, 0x0000cf18, 0x0000cf18 }, + { 0x00009ab8, 0x00000000, 0x00000000, 0x0000cf24, 0x0000cf24, 0x0000cf24 }, + { 0x00009abc, 0x00000000, 0x00000000, 0x0000cf28, 0x0000cf28, 0x0000cf28 }, + { 0x00009ac0, 0x00000000, 0x00000000, 0x0000d314, 0x0000d314, 0x0000d314 }, + { 0x00009ac4, 0x00000000, 0x00000000, 0x0000d318, 0x0000d318, 0x0000d318 }, + { 0x00009ac8, 0x00000000, 0x00000000, 0x0000d38c, 0x0000d38c, 0x0000d38c }, + { 0x00009acc, 0x00000000, 0x00000000, 0x0000d390, 0x0000d390, 0x0000d390 }, + { 0x00009ad0, 0x00000000, 0x00000000, 0x0000d394, 0x0000d394, 0x0000d394 }, + { 0x00009ad4, 0x00000000, 0x00000000, 0x0000d398, 0x0000d398, 0x0000d398 }, + { 0x00009ad8, 0x00000000, 0x00000000, 0x0000d3a4, 0x0000d3a4, 0x0000d3a4 }, + { 0x00009adc, 0x00000000, 0x00000000, 0x0000d3a8, 0x0000d3a8, 0x0000d3a8 }, + { 0x00009ae0, 0x00000000, 0x00000000, 0x0000d3ac, 0x0000d3ac, 0x0000d3ac }, + { 0x00009ae4, 0x00000000, 0x00000000, 0x0000d3b0, 0x0000d3b0, 0x0000d3b0 }, + { 0x00009ae8, 0x00000000, 0x00000000, 0x0000f380, 0x0000f380, 0x0000f380 }, + { 0x00009aec, 0x00000000, 0x00000000, 0x0000f384, 0x0000f384, 0x0000f384 }, + { 0x00009af0, 0x00000000, 0x00000000, 0x0000f388, 0x0000f388, 0x0000f388 }, + { 0x00009af4, 0x00000000, 0x00000000, 0x0000f710, 0x0000f710, 0x0000f710 }, + { 0x00009af8, 0x00000000, 0x00000000, 0x0000f714, 0x0000f714, 0x0000f714 }, + { 0x00009afc, 0x00000000, 0x00000000, 0x0000f718, 0x0000f718, 0x0000f718 }, + { 0x00009b00, 0x00000000, 0x00000000, 0x0000fb10, 0x0000fb10, 0x0000fb10 }, + { 0x00009b04, 0x00000000, 0x00000000, 0x0000fb14, 0x0000fb14, 0x0000fb14 }, + { 0x00009b08, 0x00000000, 0x00000000, 0x0000fb18, 0x0000fb18, 0x0000fb18 }, + { 0x00009b0c, 0x00000000, 0x00000000, 0x0000fb8c, 0x0000fb8c, 0x0000fb8c }, + { 0x00009b10, 0x00000000, 0x00000000, 0x0000fb90, 0x0000fb90, 0x0000fb90 }, + { 0x00009b14, 0x00000000, 0x00000000, 0x0000fb94, 0x0000fb94, 0x0000fb94 }, + { 0x00009b18, 0x00000000, 0x00000000, 0x0000ff8c, 0x0000ff8c, 0x0000ff8c }, + { 0x00009b1c, 0x00000000, 0x00000000, 0x0000ff90, 0x0000ff90, 0x0000ff90 }, + { 0x00009b20, 0x00000000, 0x00000000, 0x0000ff94, 0x0000ff94, 0x0000ff94 }, + { 0x00009b24, 0x00000000, 0x00000000, 0x0000ffa0, 0x0000ffa0, 0x0000ffa0 }, + { 0x00009b28, 0x00000000, 0x00000000, 0x0000ffa4, 0x0000ffa4, 0x0000ffa4 }, + { 0x00009b2c, 0x00000000, 0x00000000, 0x0000ffa8, 0x0000ffa8, 0x0000ffa8 }, + { 0x00009b30, 0x00000000, 0x00000000, 0x0000ffac, 0x0000ffac, 0x0000ffac }, + { 0x00009b34, 0x00000000, 0x00000000, 0x0000ffb0, 0x0000ffb0, 0x0000ffb0 }, + { 0x00009b38, 0x00000000, 0x00000000, 0x0000ffb4, 0x0000ffb4, 0x0000ffb4 }, + { 0x00009b3c, 0x00000000, 0x00000000, 0x0000ffa1, 0x0000ffa1, 0x0000ffa1 }, + { 0x00009b40, 0x00000000, 0x00000000, 0x0000ffa5, 0x0000ffa5, 0x0000ffa5 }, + { 0x00009b44, 0x00000000, 0x00000000, 0x0000ffa9, 0x0000ffa9, 0x0000ffa9 }, + { 0x00009b48, 0x00000000, 0x00000000, 0x0000ffad, 0x0000ffad, 0x0000ffad }, + { 0x00009b4c, 0x00000000, 0x00000000, 0x0000ffb1, 0x0000ffb1, 0x0000ffb1 }, + { 0x00009b50, 0x00000000, 0x00000000, 0x0000ffb5, 0x0000ffb5, 0x0000ffb5 }, + { 0x00009b54, 0x00000000, 0x00000000, 0x0000ffb9, 0x0000ffb9, 0x0000ffb9 }, + { 0x00009b58, 0x00000000, 0x00000000, 0x0000ffc5, 0x0000ffc5, 0x0000ffc5 }, + { 0x00009b5c, 0x00000000, 0x00000000, 0x0000ffc9, 0x0000ffc9, 0x0000ffc9 }, + { 0x00009b60, 0x00000000, 0x00000000, 0x0000ffcd, 0x0000ffcd, 0x0000ffcd }, + { 0x00009b64, 0x00000000, 0x00000000, 0x0000ffd1, 0x0000ffd1, 0x0000ffd1 }, + { 0x00009b68, 0x00000000, 0x00000000, 0x0000ffd5, 0x0000ffd5, 0x0000ffd5 }, + { 0x00009b6c, 0x00000000, 0x00000000, 0x0000ffc2, 0x0000ffc2, 0x0000ffc2 }, + { 0x00009b70, 0x00000000, 0x00000000, 0x0000ffc6, 0x0000ffc6, 0x0000ffc6 }, + { 0x00009b74, 0x00000000, 0x00000000, 0x0000ffca, 0x0000ffca, 0x0000ffca }, + { 0x00009b78, 0x00000000, 0x00000000, 0x0000ffce, 0x0000ffce, 0x0000ffce }, + { 0x00009b7c, 0x00000000, 0x00000000, 0x0000ffd2, 0x0000ffd2, 0x0000ffd2 }, + { 0x00009b80, 0x00000000, 0x00000000, 0x0000ffd6, 0x0000ffd6, 0x0000ffd6 }, + { 0x00009b84, 0x00000000, 0x00000000, 0x0000ffda, 0x0000ffda, 0x0000ffda }, + { 0x00009b88, 0x00000000, 0x00000000, 0x0000ffc7, 0x0000ffc7, 0x0000ffc7 }, + { 0x00009b8c, 0x00000000, 0x00000000, 0x0000ffcb, 0x0000ffcb, 0x0000ffcb }, + { 0x00009b90, 0x00000000, 0x00000000, 0x0000ffcf, 0x0000ffcf, 0x0000ffcf }, + { 0x00009b94, 0x00000000, 0x00000000, 0x0000ffd3, 0x0000ffd3, 0x0000ffd3 }, + { 0x00009b98, 0x00000000, 0x00000000, 0x0000ffd7, 0x0000ffd7, 0x0000ffd7 }, + { 0x00009b9c, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009ba0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009ba4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009ba8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bac, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bb0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bb4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bb8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bbc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bc0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bc4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bc8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bcc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bd0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bd4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bd8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bdc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009be0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009be4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009be8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bec, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bf0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bf4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bf8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bfc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000aa00, 0x00000000, 0x00000000, 0x0000a120, 0x0000a120, 0x0000a120 }, + { 0x0000aa04, 0x00000000, 0x00000000, 0x0000a124, 0x0000a124, 0x0000a124 }, + { 0x0000aa08, 0x00000000, 0x00000000, 0x0000a128, 0x0000a128, 0x0000a128 }, + { 0x0000aa0c, 0x00000000, 0x00000000, 0x0000a12c, 0x0000a12c, 0x0000a12c }, + { 0x0000aa10, 0x00000000, 0x00000000, 0x0000a130, 0x0000a130, 0x0000a130 }, + { 0x0000aa14, 0x00000000, 0x00000000, 0x0000a194, 0x0000a194, 0x0000a194 }, + { 0x0000aa18, 0x00000000, 0x00000000, 0x0000a198, 0x0000a198, 0x0000a198 }, + { 0x0000aa1c, 0x00000000, 0x00000000, 0x0000a20c, 0x0000a20c, 0x0000a20c }, + { 0x0000aa20, 0x00000000, 0x00000000, 0x0000a210, 0x0000a210, 0x0000a210 }, + { 0x0000aa24, 0x00000000, 0x00000000, 0x0000a284, 0x0000a284, 0x0000a284 }, + { 0x0000aa28, 0x00000000, 0x00000000, 0x0000a288, 0x0000a288, 0x0000a288 }, + { 0x0000aa2c, 0x00000000, 0x00000000, 0x0000a28c, 0x0000a28c, 0x0000a28c }, + { 0x0000aa30, 0x00000000, 0x00000000, 0x0000a290, 0x0000a290, 0x0000a290 }, + { 0x0000aa34, 0x00000000, 0x00000000, 0x0000a294, 0x0000a294, 0x0000a294 }, + { 0x0000aa38, 0x00000000, 0x00000000, 0x0000a2a0, 0x0000a2a0, 0x0000a2a0 }, + { 0x0000aa3c, 0x00000000, 0x00000000, 0x0000a2a4, 0x0000a2a4, 0x0000a2a4 }, + { 0x0000aa40, 0x00000000, 0x00000000, 0x0000a2a8, 0x0000a2a8, 0x0000a2a8 }, + { 0x0000aa44, 0x00000000, 0x00000000, 0x0000a2ac, 0x0000a2ac, 0x0000a2ac }, + { 0x0000aa48, 0x00000000, 0x00000000, 0x0000a2b0, 0x0000a2b0, 0x0000a2b0 }, + { 0x0000aa4c, 0x00000000, 0x00000000, 0x0000a2b4, 0x0000a2b4, 0x0000a2b4 }, + { 0x0000aa50, 0x00000000, 0x00000000, 0x0000a2b8, 0x0000a2b8, 0x0000a2b8 }, + { 0x0000aa54, 0x00000000, 0x00000000, 0x0000a2c4, 0x0000a2c4, 0x0000a2c4 }, + { 0x0000aa58, 0x00000000, 0x00000000, 0x0000a708, 0x0000a708, 0x0000a708 }, + { 0x0000aa5c, 0x00000000, 0x00000000, 0x0000a70c, 0x0000a70c, 0x0000a70c }, + { 0x0000aa60, 0x00000000, 0x00000000, 0x0000a710, 0x0000a710, 0x0000a710 }, + { 0x0000aa64, 0x00000000, 0x00000000, 0x0000ab04, 0x0000ab04, 0x0000ab04 }, + { 0x0000aa68, 0x00000000, 0x00000000, 0x0000ab08, 0x0000ab08, 0x0000ab08 }, + { 0x0000aa6c, 0x00000000, 0x00000000, 0x0000ab0c, 0x0000ab0c, 0x0000ab0c }, + { 0x0000aa70, 0x00000000, 0x00000000, 0x0000ab10, 0x0000ab10, 0x0000ab10 }, + { 0x0000aa74, 0x00000000, 0x00000000, 0x0000ab14, 0x0000ab14, 0x0000ab14 }, + { 0x0000aa78, 0x00000000, 0x00000000, 0x0000ab18, 0x0000ab18, 0x0000ab18 }, + { 0x0000aa7c, 0x00000000, 0x00000000, 0x0000ab8c, 0x0000ab8c, 0x0000ab8c }, + { 0x0000aa80, 0x00000000, 0x00000000, 0x0000ab90, 0x0000ab90, 0x0000ab90 }, + { 0x0000aa84, 0x00000000, 0x00000000, 0x0000ab94, 0x0000ab94, 0x0000ab94 }, + { 0x0000aa88, 0x00000000, 0x00000000, 0x0000ab98, 0x0000ab98, 0x0000ab98 }, + { 0x0000aa8c, 0x00000000, 0x00000000, 0x0000aba4, 0x0000aba4, 0x0000aba4 }, + { 0x0000aa90, 0x00000000, 0x00000000, 0x0000aba8, 0x0000aba8, 0x0000aba8 }, + { 0x0000aa94, 0x00000000, 0x00000000, 0x0000cb04, 0x0000cb04, 0x0000cb04 }, + { 0x0000aa98, 0x00000000, 0x00000000, 0x0000cb08, 0x0000cb08, 0x0000cb08 }, + { 0x0000aa9c, 0x00000000, 0x00000000, 0x0000cb0c, 0x0000cb0c, 0x0000cb0c }, + { 0x0000aaa0, 0x00000000, 0x00000000, 0x0000cb10, 0x0000cb10, 0x0000cb10 }, + { 0x0000aaa4, 0x00000000, 0x00000000, 0x0000cb14, 0x0000cb14, 0x0000cb14 }, + { 0x0000aaa8, 0x00000000, 0x00000000, 0x0000cb18, 0x0000cb18, 0x0000cb18 }, + { 0x0000aaac, 0x00000000, 0x00000000, 0x0000cb8c, 0x0000cb8c, 0x0000cb8c }, + { 0x0000aab0, 0x00000000, 0x00000000, 0x0000cb90, 0x0000cb90, 0x0000cb90 }, + { 0x0000aab4, 0x00000000, 0x00000000, 0x0000cf18, 0x0000cf18, 0x0000cf18 }, + { 0x0000aab8, 0x00000000, 0x00000000, 0x0000cf24, 0x0000cf24, 0x0000cf24 }, + { 0x0000aabc, 0x00000000, 0x00000000, 0x0000cf28, 0x0000cf28, 0x0000cf28 }, + { 0x0000aac0, 0x00000000, 0x00000000, 0x0000d314, 0x0000d314, 0x0000d314 }, + { 0x0000aac4, 0x00000000, 0x00000000, 0x0000d318, 0x0000d318, 0x0000d318 }, + { 0x0000aac8, 0x00000000, 0x00000000, 0x0000d38c, 0x0000d38c, 0x0000d38c }, + { 0x0000aacc, 0x00000000, 0x00000000, 0x0000d390, 0x0000d390, 0x0000d390 }, + { 0x0000aad0, 0x00000000, 0x00000000, 0x0000d394, 0x0000d394, 0x0000d394 }, + { 0x0000aad4, 0x00000000, 0x00000000, 0x0000d398, 0x0000d398, 0x0000d398 }, + { 0x0000aad8, 0x00000000, 0x00000000, 0x0000d3a4, 0x0000d3a4, 0x0000d3a4 }, + { 0x0000aadc, 0x00000000, 0x00000000, 0x0000d3a8, 0x0000d3a8, 0x0000d3a8 }, + { 0x0000aae0, 0x00000000, 0x00000000, 0x0000d3ac, 0x0000d3ac, 0x0000d3ac }, + { 0x0000aae4, 0x00000000, 0x00000000, 0x0000d3b0, 0x0000d3b0, 0x0000d3b0 }, + { 0x0000aae8, 0x00000000, 0x00000000, 0x0000f380, 0x0000f380, 0x0000f380 }, + { 0x0000aaec, 0x00000000, 0x00000000, 0x0000f384, 0x0000f384, 0x0000f384 }, + { 0x0000aaf0, 0x00000000, 0x00000000, 0x0000f388, 0x0000f388, 0x0000f388 }, + { 0x0000aaf4, 0x00000000, 0x00000000, 0x0000f710, 0x0000f710, 0x0000f710 }, + { 0x0000aaf8, 0x00000000, 0x00000000, 0x0000f714, 0x0000f714, 0x0000f714 }, + { 0x0000aafc, 0x00000000, 0x00000000, 0x0000f718, 0x0000f718, 0x0000f718 }, + { 0x0000ab00, 0x00000000, 0x00000000, 0x0000fb10, 0x0000fb10, 0x0000fb10 }, + { 0x0000ab04, 0x00000000, 0x00000000, 0x0000fb14, 0x0000fb14, 0x0000fb14 }, + { 0x0000ab08, 0x00000000, 0x00000000, 0x0000fb18, 0x0000fb18, 0x0000fb18 }, + { 0x0000ab0c, 0x00000000, 0x00000000, 0x0000fb8c, 0x0000fb8c, 0x0000fb8c }, + { 0x0000ab10, 0x00000000, 0x00000000, 0x0000fb90, 0x0000fb90, 0x0000fb90 }, + { 0x0000ab14, 0x00000000, 0x00000000, 0x0000fb94, 0x0000fb94, 0x0000fb94 }, + { 0x0000ab18, 0x00000000, 0x00000000, 0x0000ff8c, 0x0000ff8c, 0x0000ff8c }, + { 0x0000ab1c, 0x00000000, 0x00000000, 0x0000ff90, 0x0000ff90, 0x0000ff90 }, + { 0x0000ab20, 0x00000000, 0x00000000, 0x0000ff94, 0x0000ff94, 0x0000ff94 }, + { 0x0000ab24, 0x00000000, 0x00000000, 0x0000ffa0, 0x0000ffa0, 0x0000ffa0 }, + { 0x0000ab28, 0x00000000, 0x00000000, 0x0000ffa4, 0x0000ffa4, 0x0000ffa4 }, + { 0x0000ab2c, 0x00000000, 0x00000000, 0x0000ffa8, 0x0000ffa8, 0x0000ffa8 }, + { 0x0000ab30, 0x00000000, 0x00000000, 0x0000ffac, 0x0000ffac, 0x0000ffac }, + { 0x0000ab34, 0x00000000, 0x00000000, 0x0000ffb0, 0x0000ffb0, 0x0000ffb0 }, + { 0x0000ab38, 0x00000000, 0x00000000, 0x0000ffb4, 0x0000ffb4, 0x0000ffb4 }, + { 0x0000ab3c, 0x00000000, 0x00000000, 0x0000ffa1, 0x0000ffa1, 0x0000ffa1 }, + { 0x0000ab40, 0x00000000, 0x00000000, 0x0000ffa5, 0x0000ffa5, 0x0000ffa5 }, + { 0x0000ab44, 0x00000000, 0x00000000, 0x0000ffa9, 0x0000ffa9, 0x0000ffa9 }, + { 0x0000ab48, 0x00000000, 0x00000000, 0x0000ffad, 0x0000ffad, 0x0000ffad }, + { 0x0000ab4c, 0x00000000, 0x00000000, 0x0000ffb1, 0x0000ffb1, 0x0000ffb1 }, + { 0x0000ab50, 0x00000000, 0x00000000, 0x0000ffb5, 0x0000ffb5, 0x0000ffb5 }, + { 0x0000ab54, 0x00000000, 0x00000000, 0x0000ffb9, 0x0000ffb9, 0x0000ffb9 }, + { 0x0000ab58, 0x00000000, 0x00000000, 0x0000ffc5, 0x0000ffc5, 0x0000ffc5 }, + { 0x0000ab5c, 0x00000000, 0x00000000, 0x0000ffc9, 0x0000ffc9, 0x0000ffc9 }, + { 0x0000ab60, 0x00000000, 0x00000000, 0x0000ffcd, 0x0000ffcd, 0x0000ffcd }, + { 0x0000ab64, 0x00000000, 0x00000000, 0x0000ffd1, 0x0000ffd1, 0x0000ffd1 }, + { 0x0000ab68, 0x00000000, 0x00000000, 0x0000ffd5, 0x0000ffd5, 0x0000ffd5 }, + { 0x0000ab6c, 0x00000000, 0x00000000, 0x0000ffc2, 0x0000ffc2, 0x0000ffc2 }, + { 0x0000ab70, 0x00000000, 0x00000000, 0x0000ffc6, 0x0000ffc6, 0x0000ffc6 }, + { 0x0000ab74, 0x00000000, 0x00000000, 0x0000ffca, 0x0000ffca, 0x0000ffca }, + { 0x0000ab78, 0x00000000, 0x00000000, 0x0000ffce, 0x0000ffce, 0x0000ffce }, + { 0x0000ab7c, 0x00000000, 0x00000000, 0x0000ffd2, 0x0000ffd2, 0x0000ffd2 }, + { 0x0000ab80, 0x00000000, 0x00000000, 0x0000ffd6, 0x0000ffd6, 0x0000ffd6 }, + { 0x0000ab84, 0x00000000, 0x00000000, 0x0000ffda, 0x0000ffda, 0x0000ffda }, + { 0x0000ab88, 0x00000000, 0x00000000, 0x0000ffc7, 0x0000ffc7, 0x0000ffc7 }, + { 0x0000ab8c, 0x00000000, 0x00000000, 0x0000ffcb, 0x0000ffcb, 0x0000ffcb }, + { 0x0000ab90, 0x00000000, 0x00000000, 0x0000ffcf, 0x0000ffcf, 0x0000ffcf }, + { 0x0000ab94, 0x00000000, 0x00000000, 0x0000ffd3, 0x0000ffd3, 0x0000ffd3 }, + { 0x0000ab98, 0x00000000, 0x00000000, 0x0000ffd7, 0x0000ffd7, 0x0000ffd7 }, + { 0x0000ab9c, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000aba0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000aba4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000aba8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abac, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abb0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abb4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abb8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abbc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abc0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abc4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abc8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abcc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abd0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abd4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abd8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abdc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abe0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abe4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abe8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abec, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abf0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abf4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abf8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abfc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009848, 0x00000000, 0x00000000, 0x00001067, 0x00001067, 0x00001067 }, + { 0x0000a848, 0x00000000, 0x00000000, 0x00001067, 0x00001067, 0x00001067 }, +}; + +static const u_int32_t ar9287PciePhy_clkreq_always_on_L1_9287_1_0[][2] = { + {0x00004040, 0x9248fd00 }, + {0x00004040, 0x24924924 }, + {0x00004040, 0xa8000019 }, + {0x00004040, 0x13160820 }, + {0x00004040, 0xe5980560 }, + {0x00004040, 0xc01dcffd }, + {0x00004040, 0x1aaabe41 }, + {0x00004040, 0xbe105554 }, + {0x00004040, 0x00043007 }, + {0x00004044, 0x00000000 }, +}; + +static const u_int32_t ar9287PciePhy_clkreq_off_L1_9287_1_0[][2] = { + {0x00004040, 0x9248fd00 }, + {0x00004040, 0x24924924 }, + {0x00004040, 0xa8000019 }, + {0x00004040, 0x13160820 }, + {0x00004040, 0xe5980560 }, + {0x00004040, 0xc01dcffc }, + {0x00004040, 0x1aaabe41 }, + {0x00004040, 0xbe105554 }, + {0x00004040, 0x00043007 }, + {0x00004044, 0x00000000 }, +}; + +/* AR9287 Revision 11 */ + +static const u_int32_t ar9287Modes_9287_1_1[][6] = { + /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ + { 0x00001030, 0x00000000, 0x00000000, 0x000002c0, 0x00000160, 0x000001e0 }, + { 0x00001070, 0x00000000, 0x00000000, 0x00000318, 0x0000018c, 0x000001e0 }, + { 0x000010b0, 0x00000000, 0x00000000, 0x00007c70, 0x00003e38, 0x00001180 }, + { 0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008 }, + { 0x00008014, 0x00000000, 0x00000000, 0x10801600, 0x08400b00, 0x06e006e0 }, + { 0x0000801c, 0x00000000, 0x00000000, 0x12e00057, 0x12e0002b, 0x0988004f }, + { 0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810, 0x08f04810 }, + { 0x000081d0, 0x00003200, 0x00003200, 0x0000320a, 0x0000320a, 0x0000320a }, + { 0x00008318, 0x00000000, 0x00000000, 0x00006880, 0x00003440, 0x00006880 }, + { 0x00009804, 0x00000000, 0x00000000, 0x000003c4, 0x00000300, 0x00000303 }, + { 0x00009820, 0x00000000, 0x00000000, 0x02020200, 0x02020200, 0x02020200 }, + { 0x00009824, 0x00000000, 0x00000000, 0x01000e0e, 0x01000e0e, 0x01000e0e }, + { 0x00009828, 0x00000000, 0x00000000, 0x3a020001, 0x3a020001, 0x3a020001 }, + { 0x00009834, 0x00000000, 0x00000000, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009838, 0x00000003, 0x00000003, 0x00000007, 0x00000007, 0x00000007 }, + { 0x00009840, 0x206a002e, 0x206a002e, 0x206a012e, 0x206a012e, 0x206a012e }, + { 0x00009844, 0x03720000, 0x03720000, 0x037216a0, 0x037216a0, 0x037216a0 }, + { 0x00009850, 0x60000000, 0x60000000, 0x6d4000e2, 0x6c4000e2, 0x6c4000e2 }, + { 0x00009858, 0x7c000d00, 0x7c000d00, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e }, + { 0x0000985c, 0x3100005e, 0x3100005e, 0x3139605e, 0x31395d5e, 0x31395d5e }, + { 0x00009860, 0x00058d00, 0x00058d00, 0x00058d20, 0x00058d20, 0x00058d18 }, + { 0x00009864, 0x00000e00, 0x00000e00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x000040c0, 0x000040c0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 }, + { 0x0000986c, 0x00000080, 0x00000080, 0x06903881, 0x06903881, 0x06903881 }, + { 0x00009914, 0x00000000, 0x00000000, 0x00001130, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x00000000, 0x00000000, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8a01, 0xd00a8a01, 0xd00a8a0d, 0xd00a8a0d, 0xd00a8a0d }, + { 0x00009944, 0xefbc0000, 0xefbc0000, 0xefbc1010, 0xefbc1010, 0xefbc1010 }, + { 0x00009960, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000010 }, + { 0x0000a960, 0x00000000, 0x00000000, 0x00000010, 0x00000010, 0x00000010 }, + { 0x00009964, 0x00000000, 0x00000000, 0x00000210, 0x00000210, 0x00000210 }, + { 0x0000c968, 0x00000200, 0x00000200, 0x000003ce, 0x000003ce, 0x000003ce }, + { 0x000099b8, 0x00000000, 0x00000000, 0x0000001c, 0x0000001c, 0x0000001c }, + { 0x000099bc, 0x00000000, 0x00000000, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x000099c0, 0x00000000, 0x00000000, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, + { 0x0000a204, 0x00000440, 0x00000440, 0x00000444, 0x00000444, 0x00000444 }, + { 0x0000a20c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000b20c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a21c, 0x1803800a, 0x1803800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a250, 0x00000000, 0x00000000, 0x0004a000, 0x0004a000, 0x0004a000 }, + { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e }, + { 0x0000a3d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, +}; + +static const u_int32_t ar9287Common_9287_1_1[][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 }, + { 0x00007020, 0x00000000 }, + { 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 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0xffffffff }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x18487320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c0, 0x00000000 }, + { 0x000081c4, 0x00000000 }, + { 0x000081d4, 0x00000000 }, + { 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 }, + { 0x00008264, 0x88a00010 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x000000ff }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x0000829c, 0x00000000 }, + { 0x00008300, 0x00000040 }, + { 0x00008314, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000007 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00ff0000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x000107ff }, + { 0x00008344, 0x01c81043 }, + { 0x00008360, 0xffffffff }, + { 0x00008364, 0xffffffff }, + { 0x00008368, 0x00000000 }, + { 0x00008370, 0x00000000 }, + { 0x00008374, 0x000000ff }, + { 0x00008378, 0x00000000 }, + { 0x0000837c, 0x00000000 }, + { 0x00008380, 0xffffffff }, + { 0x00008384, 0xffffffff }, + { 0x00008390, 0x0fffffff }, + { 0x00008394, 0x0fffffff }, + { 0x00008398, 0x00000000 }, + { 0x0000839c, 0x00000000 }, + { 0x000083a0, 0x00000000 }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xafe68e30 }, + { 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, 0x10002310 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x04900000 }, + { 0x0000a920, 0x04900000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009930, 0x00000000 }, + { 0x0000a930, 0x00000000 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009948, 0x9280c00a }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0x5f3ca3de }, + { 0x00009958, 0x0108ecff }, + { 0x00009940, 0x14750604 }, + { 0x0000c95c, 0x004b6a8e }, + { 0x00009970, 0x990bb514 }, + { 0x00009974, 0x00000000 }, + { 0x00009978, 0x00000001 }, + { 0x0000997c, 0x00000000 }, + { 0x000099a0, 0x00000000 }, + { 0x000099a4, 0x00000001 }, + { 0x000099a8, 0x201fff00 }, + { 0x000099ac, 0x0c6f0000 }, + { 0x000099b0, 0x03051000 }, + { 0x000099b4, 0x00000820 }, + { 0x000099c4, 0x06336f77 }, + { 0x000099c8, 0x6af6532f }, + { 0x000099cc, 0x08f186c8 }, + { 0x000099d0, 0x00046384 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000000 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099f0, 0x00000000 }, + { 0x000099fc, 0x00001042 }, + { 0x0000a1f4, 0x00fffeff }, + { 0x0000a1f8, 0x00f5f9ff }, + { 0x0000a1fc, 0xb79f6427 }, + { 0x0000a208, 0x803e4788 }, + { 0x0000a210, 0x4080a333 }, + { 0x0000a214, 0x40206c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x01834061 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x000003b5 }, + { 0x0000a22c, 0x233f7180 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a23c, 0x13c889af }, + { 0x0000a240, 0x38490a20 }, + { 0x0000a244, 0x00000000 }, + { 0x0000a248, 0xfffffffc }, + { 0x0000a24c, 0x00000000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0cdbd380 }, + { 0x0000a25c, 0x0f0f0f01 }, + { 0x0000a260, 0xdfa91f01 }, + { 0x0000a264, 0x00418a11 }, + { 0x0000b264, 0x00418a11 }, + { 0x0000a268, 0x00000000 }, + { 0x0000a26c, 0x0e79e5c6 }, + { 0x0000b26c, 0x0e79e5c6 }, + { 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 }, + { 0x0000b398, 0x000001ce }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3c8, 0x00000246 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3dc, 0x1ce739ce }, + { 0x0000a3e0, 0x000001ce }, + { 0x0000a3e4, 0x00000000 }, + { 0x0000a3e8, 0x18c43433 }, + { 0x0000a3ec, 0x00f70081 }, + { 0x0000a3f0, 0x01036a1e }, + { 0x0000a3f4, 0x00000000 }, + { 0x0000b3f4, 0x00000000 }, + { 0x0000a7d8, 0x000003f1 }, + { 0x00007800, 0x00000800 }, + { 0x00007804, 0x6c35ffc2 }, + { 0x00007808, 0x6db6c000 }, + { 0x0000780c, 0x6db6cb30 }, + { 0x00007810, 0x6db6cb6c }, + { 0x00007814, 0x0501e200 }, + { 0x00007818, 0x0094128d }, + { 0x0000781c, 0x976ee392 }, + { 0x00007820, 0xf75ff6fc }, + { 0x00007824, 0x00040000 }, + { 0x00007828, 0xdb003012 }, + { 0x0000782c, 0x04924914 }, + { 0x00007830, 0x21084210 }, + { 0x00007834, 0x00140000 }, + { 0x00007838, 0x0e4548d8 }, + { 0x0000783c, 0x54214514 }, + { 0x00007840, 0x02025830 }, + { 0x00007844, 0x71c0d388 }, + { 0x00007848, 0x934934a8 }, + { 0x00007850, 0x00000000 }, + { 0x00007854, 0x00000800 }, + { 0x00007858, 0x6c35ffc2 }, + { 0x0000785c, 0x6db6c000 }, + { 0x00007860, 0x6db6cb30 }, + { 0x00007864, 0x6db6cb6c }, + { 0x00007868, 0x0501e200 }, + { 0x0000786c, 0x0094128d }, + { 0x00007870, 0x976ee392 }, + { 0x00007874, 0xf75ff6fc }, + { 0x00007878, 0x00040000 }, + { 0x0000787c, 0xdb003012 }, + { 0x00007880, 0x04924914 }, + { 0x00007884, 0x21084210 }, + { 0x00007888, 0x001b6db0 }, + { 0x0000788c, 0x00376b63 }, + { 0x00007890, 0x06db6db6 }, + { 0x00007894, 0x006d8000 }, + { 0x00007898, 0x48100000 }, + { 0x0000789c, 0x00000000 }, + { 0x000078a0, 0x08000000 }, + { 0x000078a4, 0x0007ffd8 }, + { 0x000078a8, 0x0007ffd8 }, + { 0x000078ac, 0x001c0020 }, + { 0x000078b0, 0x00060aeb }, + { 0x000078b4, 0x40008080 }, + { 0x000078b8, 0x2a850160 }, +}; + +static const u_int32_t ar9287Modes_tx_gain_9287_1_1[][6] = { + /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ + { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a304, 0x00000000, 0x00000000, 0x00004002, 0x00004002, 0x00004002 }, + { 0x0000a308, 0x00000000, 0x00000000, 0x00008004, 0x00008004, 0x00008004 }, + { 0x0000a30c, 0x00000000, 0x00000000, 0x0000c00a, 0x0000c00a, 0x0000c00a }, + { 0x0000a310, 0x00000000, 0x00000000, 0x0001000c, 0x0001000c, 0x0001000c }, + { 0x0000a314, 0x00000000, 0x00000000, 0x0001420b, 0x0001420b, 0x0001420b }, + { 0x0000a318, 0x00000000, 0x00000000, 0x0001824a, 0x0001824a, 0x0001824a }, + { 0x0000a31c, 0x00000000, 0x00000000, 0x0001c44a, 0x0001c44a, 0x0001c44a }, + { 0x0000a320, 0x00000000, 0x00000000, 0x0002064a, 0x0002064a, 0x0002064a }, + { 0x0000a324, 0x00000000, 0x00000000, 0x0002484a, 0x0002484a, 0x0002484a }, + { 0x0000a328, 0x00000000, 0x00000000, 0x00028a4a, 0x00028a4a, 0x00028a4a }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x0002cc4a, 0x0002cc4a, 0x0002cc4a }, + { 0x0000a330, 0x00000000, 0x00000000, 0x00030e4a, 0x00030e4a, 0x00030e4a }, + { 0x0000a334, 0x00000000, 0x00000000, 0x00034e8a, 0x00034e8a, 0x00034e8a }, + { 0x0000a338, 0x00000000, 0x00000000, 0x00038e8c, 0x00038e8c, 0x00038e8c }, + { 0x0000a33c, 0x00000000, 0x00000000, 0x0003cecc, 0x0003cecc, 0x0003cecc }, + { 0x0000a340, 0x00000000, 0x00000000, 0x00040ed4, 0x00040ed4, 0x00040ed4 }, + { 0x0000a344, 0x00000000, 0x00000000, 0x00044edc, 0x00044edc, 0x00044edc }, + { 0x0000a348, 0x00000000, 0x00000000, 0x00048ede, 0x00048ede, 0x00048ede }, + { 0x0000a34c, 0x00000000, 0x00000000, 0x0004cf1e, 0x0004cf1e, 0x0004cf1e }, + { 0x0000a350, 0x00000000, 0x00000000, 0x00050f5e, 0x00050f5e, 0x00050f5e }, + { 0x0000a354, 0x00000000, 0x00000000, 0x00054f9e, 0x00054f9e, 0x00054f9e }, + { 0x0000a780, 0x00000000, 0x00000000, 0x00000062, 0x00000062, 0x00000062 }, + { 0x0000a784, 0x00000000, 0x00000000, 0x00004064, 0x00004064, 0x00004064 }, + { 0x0000a788, 0x00000000, 0x00000000, 0x000080a4, 0x000080a4, 0x000080a4 }, + { 0x0000a78c, 0x00000000, 0x00000000, 0x0000c0aa, 0x0000c0aa, 0x0000c0aa }, + { 0x0000a790, 0x00000000, 0x00000000, 0x000100ac, 0x000100ac, 0x000100ac }, + { 0x0000a794, 0x00000000, 0x00000000, 0x000140b4, 0x000140b4, 0x000140b4 }, + { 0x0000a798, 0x00000000, 0x00000000, 0x000180f4, 0x000180f4, 0x000180f4 }, + { 0x0000a79c, 0x00000000, 0x00000000, 0x0001c134, 0x0001c134, 0x0001c134 }, + { 0x0000a7a0, 0x00000000, 0x00000000, 0x00020174, 0x00020174, 0x00020174 }, + { 0x0000a7a4, 0x00000000, 0x00000000, 0x0002417c, 0x0002417c, 0x0002417c }, + { 0x0000a7a8, 0x00000000, 0x00000000, 0x0002817e, 0x0002817e, 0x0002817e }, + { 0x0000a7ac, 0x00000000, 0x00000000, 0x0002c1be, 0x0002c1be, 0x0002c1be }, + { 0x0000a7b0, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe }, + { 0x0000a7b4, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe }, + { 0x0000a7b8, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe }, + { 0x0000a7bc, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe }, + { 0x0000a7c0, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe }, + { 0x0000a7c4, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe }, + { 0x0000a7c8, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe }, + { 0x0000a7cc, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe }, + { 0x0000a7d0, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe }, + { 0x0000a7d4, 0x00000000, 0x00000000, 0x000301fe, 0x000301fe, 0x000301fe }, + { 0x0000a274, 0x0a180000, 0x0a180000, 0x0a1aa000, 0x0a1aa000, 0x0a1aa000 }, +}; + +static const u_int32_t ar9287Modes_rx_gain_9287_1_1[][6] = { + /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ + { 0x00009a00, 0x00000000, 0x00000000, 0x0000a120, 0x0000a120, 0x0000a120 }, + { 0x00009a04, 0x00000000, 0x00000000, 0x0000a124, 0x0000a124, 0x0000a124 }, + { 0x00009a08, 0x00000000, 0x00000000, 0x0000a128, 0x0000a128, 0x0000a128 }, + { 0x00009a0c, 0x00000000, 0x00000000, 0x0000a12c, 0x0000a12c, 0x0000a12c }, + { 0x00009a10, 0x00000000, 0x00000000, 0x0000a130, 0x0000a130, 0x0000a130 }, + { 0x00009a14, 0x00000000, 0x00000000, 0x0000a194, 0x0000a194, 0x0000a194 }, + { 0x00009a18, 0x00000000, 0x00000000, 0x0000a198, 0x0000a198, 0x0000a198 }, + { 0x00009a1c, 0x00000000, 0x00000000, 0x0000a20c, 0x0000a20c, 0x0000a20c }, + { 0x00009a20, 0x00000000, 0x00000000, 0x0000a210, 0x0000a210, 0x0000a210 }, + { 0x00009a24, 0x00000000, 0x00000000, 0x0000a284, 0x0000a284, 0x0000a284 }, + { 0x00009a28, 0x00000000, 0x00000000, 0x0000a288, 0x0000a288, 0x0000a288 }, + { 0x00009a2c, 0x00000000, 0x00000000, 0x0000a28c, 0x0000a28c, 0x0000a28c }, + { 0x00009a30, 0x00000000, 0x00000000, 0x0000a290, 0x0000a290, 0x0000a290 }, + { 0x00009a34, 0x00000000, 0x00000000, 0x0000a294, 0x0000a294, 0x0000a294 }, + { 0x00009a38, 0x00000000, 0x00000000, 0x0000a2a0, 0x0000a2a0, 0x0000a2a0 }, + { 0x00009a3c, 0x00000000, 0x00000000, 0x0000a2a4, 0x0000a2a4, 0x0000a2a4 }, + { 0x00009a40, 0x00000000, 0x00000000, 0x0000a2a8, 0x0000a2a8, 0x0000a2a8 }, + { 0x00009a44, 0x00000000, 0x00000000, 0x0000a2ac, 0x0000a2ac, 0x0000a2ac }, + { 0x00009a48, 0x00000000, 0x00000000, 0x0000a2b0, 0x0000a2b0, 0x0000a2b0 }, + { 0x00009a4c, 0x00000000, 0x00000000, 0x0000a2b4, 0x0000a2b4, 0x0000a2b4 }, + { 0x00009a50, 0x00000000, 0x00000000, 0x0000a2b8, 0x0000a2b8, 0x0000a2b8 }, + { 0x00009a54, 0x00000000, 0x00000000, 0x0000a2c4, 0x0000a2c4, 0x0000a2c4 }, + { 0x00009a58, 0x00000000, 0x00000000, 0x0000a708, 0x0000a708, 0x0000a708 }, + { 0x00009a5c, 0x00000000, 0x00000000, 0x0000a70c, 0x0000a70c, 0x0000a70c }, + { 0x00009a60, 0x00000000, 0x00000000, 0x0000a710, 0x0000a710, 0x0000a710 }, + { 0x00009a64, 0x00000000, 0x00000000, 0x0000ab04, 0x0000ab04, 0x0000ab04 }, + { 0x00009a68, 0x00000000, 0x00000000, 0x0000ab08, 0x0000ab08, 0x0000ab08 }, + { 0x00009a6c, 0x00000000, 0x00000000, 0x0000ab0c, 0x0000ab0c, 0x0000ab0c }, + { 0x00009a70, 0x00000000, 0x00000000, 0x0000ab10, 0x0000ab10, 0x0000ab10 }, + { 0x00009a74, 0x00000000, 0x00000000, 0x0000ab14, 0x0000ab14, 0x0000ab14 }, + { 0x00009a78, 0x00000000, 0x00000000, 0x0000ab18, 0x0000ab18, 0x0000ab18 }, + { 0x00009a7c, 0x00000000, 0x00000000, 0x0000ab8c, 0x0000ab8c, 0x0000ab8c }, + { 0x00009a80, 0x00000000, 0x00000000, 0x0000ab90, 0x0000ab90, 0x0000ab90 }, + { 0x00009a84, 0x00000000, 0x00000000, 0x0000ab94, 0x0000ab94, 0x0000ab94 }, + { 0x00009a88, 0x00000000, 0x00000000, 0x0000ab98, 0x0000ab98, 0x0000ab98 }, + { 0x00009a8c, 0x00000000, 0x00000000, 0x0000aba4, 0x0000aba4, 0x0000aba4 }, + { 0x00009a90, 0x00000000, 0x00000000, 0x0000aba8, 0x0000aba8, 0x0000aba8 }, + { 0x00009a94, 0x00000000, 0x00000000, 0x0000cb04, 0x0000cb04, 0x0000cb04 }, + { 0x00009a98, 0x00000000, 0x00000000, 0x0000cb08, 0x0000cb08, 0x0000cb08 }, + { 0x00009a9c, 0x00000000, 0x00000000, 0x0000cb0c, 0x0000cb0c, 0x0000cb0c }, + { 0x00009aa0, 0x00000000, 0x00000000, 0x0000cb10, 0x0000cb10, 0x0000cb10 }, + { 0x00009aa4, 0x00000000, 0x00000000, 0x0000cb14, 0x0000cb14, 0x0000cb14 }, + { 0x00009aa8, 0x00000000, 0x00000000, 0x0000cb18, 0x0000cb18, 0x0000cb18 }, + { 0x00009aac, 0x00000000, 0x00000000, 0x0000cb8c, 0x0000cb8c, 0x0000cb8c }, + { 0x00009ab0, 0x00000000, 0x00000000, 0x0000cb90, 0x0000cb90, 0x0000cb90 }, + { 0x00009ab4, 0x00000000, 0x00000000, 0x0000cf18, 0x0000cf18, 0x0000cf18 }, + { 0x00009ab8, 0x00000000, 0x00000000, 0x0000cf24, 0x0000cf24, 0x0000cf24 }, + { 0x00009abc, 0x00000000, 0x00000000, 0x0000cf28, 0x0000cf28, 0x0000cf28 }, + { 0x00009ac0, 0x00000000, 0x00000000, 0x0000d314, 0x0000d314, 0x0000d314 }, + { 0x00009ac4, 0x00000000, 0x00000000, 0x0000d318, 0x0000d318, 0x0000d318 }, + { 0x00009ac8, 0x00000000, 0x00000000, 0x0000d38c, 0x0000d38c, 0x0000d38c }, + { 0x00009acc, 0x00000000, 0x00000000, 0x0000d390, 0x0000d390, 0x0000d390 }, + { 0x00009ad0, 0x00000000, 0x00000000, 0x0000d394, 0x0000d394, 0x0000d394 }, + { 0x00009ad4, 0x00000000, 0x00000000, 0x0000d398, 0x0000d398, 0x0000d398 }, + { 0x00009ad8, 0x00000000, 0x00000000, 0x0000d3a4, 0x0000d3a4, 0x0000d3a4 }, + { 0x00009adc, 0x00000000, 0x00000000, 0x0000d3a8, 0x0000d3a8, 0x0000d3a8 }, + { 0x00009ae0, 0x00000000, 0x00000000, 0x0000d3ac, 0x0000d3ac, 0x0000d3ac }, + { 0x00009ae4, 0x00000000, 0x00000000, 0x0000d3b0, 0x0000d3b0, 0x0000d3b0 }, + { 0x00009ae8, 0x00000000, 0x00000000, 0x0000f380, 0x0000f380, 0x0000f380 }, + { 0x00009aec, 0x00000000, 0x00000000, 0x0000f384, 0x0000f384, 0x0000f384 }, + { 0x00009af0, 0x00000000, 0x00000000, 0x0000f388, 0x0000f388, 0x0000f388 }, + { 0x00009af4, 0x00000000, 0x00000000, 0x0000f710, 0x0000f710, 0x0000f710 }, + { 0x00009af8, 0x00000000, 0x00000000, 0x0000f714, 0x0000f714, 0x0000f714 }, + { 0x00009afc, 0x00000000, 0x00000000, 0x0000f718, 0x0000f718, 0x0000f718 }, + { 0x00009b00, 0x00000000, 0x00000000, 0x0000fb10, 0x0000fb10, 0x0000fb10 }, + { 0x00009b04, 0x00000000, 0x00000000, 0x0000fb14, 0x0000fb14, 0x0000fb14 }, + { 0x00009b08, 0x00000000, 0x00000000, 0x0000fb18, 0x0000fb18, 0x0000fb18 }, + { 0x00009b0c, 0x00000000, 0x00000000, 0x0000fb8c, 0x0000fb8c, 0x0000fb8c }, + { 0x00009b10, 0x00000000, 0x00000000, 0x0000fb90, 0x0000fb90, 0x0000fb90 }, + { 0x00009b14, 0x00000000, 0x00000000, 0x0000fb94, 0x0000fb94, 0x0000fb94 }, + { 0x00009b18, 0x00000000, 0x00000000, 0x0000ff8c, 0x0000ff8c, 0x0000ff8c }, + { 0x00009b1c, 0x00000000, 0x00000000, 0x0000ff90, 0x0000ff90, 0x0000ff90 }, + { 0x00009b20, 0x00000000, 0x00000000, 0x0000ff94, 0x0000ff94, 0x0000ff94 }, + { 0x00009b24, 0x00000000, 0x00000000, 0x0000ffa0, 0x0000ffa0, 0x0000ffa0 }, + { 0x00009b28, 0x00000000, 0x00000000, 0x0000ffa4, 0x0000ffa4, 0x0000ffa4 }, + { 0x00009b2c, 0x00000000, 0x00000000, 0x0000ffa8, 0x0000ffa8, 0x0000ffa8 }, + { 0x00009b30, 0x00000000, 0x00000000, 0x0000ffac, 0x0000ffac, 0x0000ffac }, + { 0x00009b34, 0x00000000, 0x00000000, 0x0000ffb0, 0x0000ffb0, 0x0000ffb0 }, + { 0x00009b38, 0x00000000, 0x00000000, 0x0000ffb4, 0x0000ffb4, 0x0000ffb4 }, + { 0x00009b3c, 0x00000000, 0x00000000, 0x0000ffa1, 0x0000ffa1, 0x0000ffa1 }, + { 0x00009b40, 0x00000000, 0x00000000, 0x0000ffa5, 0x0000ffa5, 0x0000ffa5 }, + { 0x00009b44, 0x00000000, 0x00000000, 0x0000ffa9, 0x0000ffa9, 0x0000ffa9 }, + { 0x00009b48, 0x00000000, 0x00000000, 0x0000ffad, 0x0000ffad, 0x0000ffad }, + { 0x00009b4c, 0x00000000, 0x00000000, 0x0000ffb1, 0x0000ffb1, 0x0000ffb1 }, + { 0x00009b50, 0x00000000, 0x00000000, 0x0000ffb5, 0x0000ffb5, 0x0000ffb5 }, + { 0x00009b54, 0x00000000, 0x00000000, 0x0000ffb9, 0x0000ffb9, 0x0000ffb9 }, + { 0x00009b58, 0x00000000, 0x00000000, 0x0000ffc5, 0x0000ffc5, 0x0000ffc5 }, + { 0x00009b5c, 0x00000000, 0x00000000, 0x0000ffc9, 0x0000ffc9, 0x0000ffc9 }, + { 0x00009b60, 0x00000000, 0x00000000, 0x0000ffcd, 0x0000ffcd, 0x0000ffcd }, + { 0x00009b64, 0x00000000, 0x00000000, 0x0000ffd1, 0x0000ffd1, 0x0000ffd1 }, + { 0x00009b68, 0x00000000, 0x00000000, 0x0000ffd5, 0x0000ffd5, 0x0000ffd5 }, + { 0x00009b6c, 0x00000000, 0x00000000, 0x0000ffc2, 0x0000ffc2, 0x0000ffc2 }, + { 0x00009b70, 0x00000000, 0x00000000, 0x0000ffc6, 0x0000ffc6, 0x0000ffc6 }, + { 0x00009b74, 0x00000000, 0x00000000, 0x0000ffca, 0x0000ffca, 0x0000ffca }, + { 0x00009b78, 0x00000000, 0x00000000, 0x0000ffce, 0x0000ffce, 0x0000ffce }, + { 0x00009b7c, 0x00000000, 0x00000000, 0x0000ffd2, 0x0000ffd2, 0x0000ffd2 }, + { 0x00009b80, 0x00000000, 0x00000000, 0x0000ffd6, 0x0000ffd6, 0x0000ffd6 }, + { 0x00009b84, 0x00000000, 0x00000000, 0x0000ffda, 0x0000ffda, 0x0000ffda }, + { 0x00009b88, 0x00000000, 0x00000000, 0x0000ffc7, 0x0000ffc7, 0x0000ffc7 }, + { 0x00009b8c, 0x00000000, 0x00000000, 0x0000ffcb, 0x0000ffcb, 0x0000ffcb }, + { 0x00009b90, 0x00000000, 0x00000000, 0x0000ffcf, 0x0000ffcf, 0x0000ffcf }, + { 0x00009b94, 0x00000000, 0x00000000, 0x0000ffd3, 0x0000ffd3, 0x0000ffd3 }, + { 0x00009b98, 0x00000000, 0x00000000, 0x0000ffd7, 0x0000ffd7, 0x0000ffd7 }, + { 0x00009b9c, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009ba0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009ba4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009ba8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bac, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bb0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bb4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bb8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bbc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bc0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bc4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bc8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bcc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bd0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bd4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bd8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bdc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009be0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009be4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009be8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bec, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bf0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bf4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bf8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009bfc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000aa00, 0x00000000, 0x00000000, 0x0000a120, 0x0000a120, 0x0000a120 }, + { 0x0000aa04, 0x00000000, 0x00000000, 0x0000a124, 0x0000a124, 0x0000a124 }, + { 0x0000aa08, 0x00000000, 0x00000000, 0x0000a128, 0x0000a128, 0x0000a128 }, + { 0x0000aa0c, 0x00000000, 0x00000000, 0x0000a12c, 0x0000a12c, 0x0000a12c }, + { 0x0000aa10, 0x00000000, 0x00000000, 0x0000a130, 0x0000a130, 0x0000a130 }, + { 0x0000aa14, 0x00000000, 0x00000000, 0x0000a194, 0x0000a194, 0x0000a194 }, + { 0x0000aa18, 0x00000000, 0x00000000, 0x0000a198, 0x0000a198, 0x0000a198 }, + { 0x0000aa1c, 0x00000000, 0x00000000, 0x0000a20c, 0x0000a20c, 0x0000a20c }, + { 0x0000aa20, 0x00000000, 0x00000000, 0x0000a210, 0x0000a210, 0x0000a210 }, + { 0x0000aa24, 0x00000000, 0x00000000, 0x0000a284, 0x0000a284, 0x0000a284 }, + { 0x0000aa28, 0x00000000, 0x00000000, 0x0000a288, 0x0000a288, 0x0000a288 }, + { 0x0000aa2c, 0x00000000, 0x00000000, 0x0000a28c, 0x0000a28c, 0x0000a28c }, + { 0x0000aa30, 0x00000000, 0x00000000, 0x0000a290, 0x0000a290, 0x0000a290 }, + { 0x0000aa34, 0x00000000, 0x00000000, 0x0000a294, 0x0000a294, 0x0000a294 }, + { 0x0000aa38, 0x00000000, 0x00000000, 0x0000a2a0, 0x0000a2a0, 0x0000a2a0 }, + { 0x0000aa3c, 0x00000000, 0x00000000, 0x0000a2a4, 0x0000a2a4, 0x0000a2a4 }, + { 0x0000aa40, 0x00000000, 0x00000000, 0x0000a2a8, 0x0000a2a8, 0x0000a2a8 }, + { 0x0000aa44, 0x00000000, 0x00000000, 0x0000a2ac, 0x0000a2ac, 0x0000a2ac }, + { 0x0000aa48, 0x00000000, 0x00000000, 0x0000a2b0, 0x0000a2b0, 0x0000a2b0 }, + { 0x0000aa4c, 0x00000000, 0x00000000, 0x0000a2b4, 0x0000a2b4, 0x0000a2b4 }, + { 0x0000aa50, 0x00000000, 0x00000000, 0x0000a2b8, 0x0000a2b8, 0x0000a2b8 }, + { 0x0000aa54, 0x00000000, 0x00000000, 0x0000a2c4, 0x0000a2c4, 0x0000a2c4 }, + { 0x0000aa58, 0x00000000, 0x00000000, 0x0000a708, 0x0000a708, 0x0000a708 }, + { 0x0000aa5c, 0x00000000, 0x00000000, 0x0000a70c, 0x0000a70c, 0x0000a70c }, + { 0x0000aa60, 0x00000000, 0x00000000, 0x0000a710, 0x0000a710, 0x0000a710 }, + { 0x0000aa64, 0x00000000, 0x00000000, 0x0000ab04, 0x0000ab04, 0x0000ab04 }, + { 0x0000aa68, 0x00000000, 0x00000000, 0x0000ab08, 0x0000ab08, 0x0000ab08 }, + { 0x0000aa6c, 0x00000000, 0x00000000, 0x0000ab0c, 0x0000ab0c, 0x0000ab0c }, + { 0x0000aa70, 0x00000000, 0x00000000, 0x0000ab10, 0x0000ab10, 0x0000ab10 }, + { 0x0000aa74, 0x00000000, 0x00000000, 0x0000ab14, 0x0000ab14, 0x0000ab14 }, + { 0x0000aa78, 0x00000000, 0x00000000, 0x0000ab18, 0x0000ab18, 0x0000ab18 }, + { 0x0000aa7c, 0x00000000, 0x00000000, 0x0000ab8c, 0x0000ab8c, 0x0000ab8c }, + { 0x0000aa80, 0x00000000, 0x00000000, 0x0000ab90, 0x0000ab90, 0x0000ab90 }, + { 0x0000aa84, 0x00000000, 0x00000000, 0x0000ab94, 0x0000ab94, 0x0000ab94 }, + { 0x0000aa88, 0x00000000, 0x00000000, 0x0000ab98, 0x0000ab98, 0x0000ab98 }, + { 0x0000aa8c, 0x00000000, 0x00000000, 0x0000aba4, 0x0000aba4, 0x0000aba4 }, + { 0x0000aa90, 0x00000000, 0x00000000, 0x0000aba8, 0x0000aba8, 0x0000aba8 }, + { 0x0000aa94, 0x00000000, 0x00000000, 0x0000cb04, 0x0000cb04, 0x0000cb04 }, + { 0x0000aa98, 0x00000000, 0x00000000, 0x0000cb08, 0x0000cb08, 0x0000cb08 }, + { 0x0000aa9c, 0x00000000, 0x00000000, 0x0000cb0c, 0x0000cb0c, 0x0000cb0c }, + { 0x0000aaa0, 0x00000000, 0x00000000, 0x0000cb10, 0x0000cb10, 0x0000cb10 }, + { 0x0000aaa4, 0x00000000, 0x00000000, 0x0000cb14, 0x0000cb14, 0x0000cb14 }, + { 0x0000aaa8, 0x00000000, 0x00000000, 0x0000cb18, 0x0000cb18, 0x0000cb18 }, + { 0x0000aaac, 0x00000000, 0x00000000, 0x0000cb8c, 0x0000cb8c, 0x0000cb8c }, + { 0x0000aab0, 0x00000000, 0x00000000, 0x0000cb90, 0x0000cb90, 0x0000cb90 }, + { 0x0000aab4, 0x00000000, 0x00000000, 0x0000cf18, 0x0000cf18, 0x0000cf18 }, + { 0x0000aab8, 0x00000000, 0x00000000, 0x0000cf24, 0x0000cf24, 0x0000cf24 }, + { 0x0000aabc, 0x00000000, 0x00000000, 0x0000cf28, 0x0000cf28, 0x0000cf28 }, + { 0x0000aac0, 0x00000000, 0x00000000, 0x0000d314, 0x0000d314, 0x0000d314 }, + { 0x0000aac4, 0x00000000, 0x00000000, 0x0000d318, 0x0000d318, 0x0000d318 }, + { 0x0000aac8, 0x00000000, 0x00000000, 0x0000d38c, 0x0000d38c, 0x0000d38c }, + { 0x0000aacc, 0x00000000, 0x00000000, 0x0000d390, 0x0000d390, 0x0000d390 }, + { 0x0000aad0, 0x00000000, 0x00000000, 0x0000d394, 0x0000d394, 0x0000d394 }, + { 0x0000aad4, 0x00000000, 0x00000000, 0x0000d398, 0x0000d398, 0x0000d398 }, + { 0x0000aad8, 0x00000000, 0x00000000, 0x0000d3a4, 0x0000d3a4, 0x0000d3a4 }, + { 0x0000aadc, 0x00000000, 0x00000000, 0x0000d3a8, 0x0000d3a8, 0x0000d3a8 }, + { 0x0000aae0, 0x00000000, 0x00000000, 0x0000d3ac, 0x0000d3ac, 0x0000d3ac }, + { 0x0000aae4, 0x00000000, 0x00000000, 0x0000d3b0, 0x0000d3b0, 0x0000d3b0 }, + { 0x0000aae8, 0x00000000, 0x00000000, 0x0000f380, 0x0000f380, 0x0000f380 }, + { 0x0000aaec, 0x00000000, 0x00000000, 0x0000f384, 0x0000f384, 0x0000f384 }, + { 0x0000aaf0, 0x00000000, 0x00000000, 0x0000f388, 0x0000f388, 0x0000f388 }, + { 0x0000aaf4, 0x00000000, 0x00000000, 0x0000f710, 0x0000f710, 0x0000f710 }, + { 0x0000aaf8, 0x00000000, 0x00000000, 0x0000f714, 0x0000f714, 0x0000f714 }, + { 0x0000aafc, 0x00000000, 0x00000000, 0x0000f718, 0x0000f718, 0x0000f718 }, + { 0x0000ab00, 0x00000000, 0x00000000, 0x0000fb10, 0x0000fb10, 0x0000fb10 }, + { 0x0000ab04, 0x00000000, 0x00000000, 0x0000fb14, 0x0000fb14, 0x0000fb14 }, + { 0x0000ab08, 0x00000000, 0x00000000, 0x0000fb18, 0x0000fb18, 0x0000fb18 }, + { 0x0000ab0c, 0x00000000, 0x00000000, 0x0000fb8c, 0x0000fb8c, 0x0000fb8c }, + { 0x0000ab10, 0x00000000, 0x00000000, 0x0000fb90, 0x0000fb90, 0x0000fb90 }, + { 0x0000ab14, 0x00000000, 0x00000000, 0x0000fb94, 0x0000fb94, 0x0000fb94 }, + { 0x0000ab18, 0x00000000, 0x00000000, 0x0000ff8c, 0x0000ff8c, 0x0000ff8c }, + { 0x0000ab1c, 0x00000000, 0x00000000, 0x0000ff90, 0x0000ff90, 0x0000ff90 }, + { 0x0000ab20, 0x00000000, 0x00000000, 0x0000ff94, 0x0000ff94, 0x0000ff94 }, + { 0x0000ab24, 0x00000000, 0x00000000, 0x0000ffa0, 0x0000ffa0, 0x0000ffa0 }, + { 0x0000ab28, 0x00000000, 0x00000000, 0x0000ffa4, 0x0000ffa4, 0x0000ffa4 }, + { 0x0000ab2c, 0x00000000, 0x00000000, 0x0000ffa8, 0x0000ffa8, 0x0000ffa8 }, + { 0x0000ab30, 0x00000000, 0x00000000, 0x0000ffac, 0x0000ffac, 0x0000ffac }, + { 0x0000ab34, 0x00000000, 0x00000000, 0x0000ffb0, 0x0000ffb0, 0x0000ffb0 }, + { 0x0000ab38, 0x00000000, 0x00000000, 0x0000ffb4, 0x0000ffb4, 0x0000ffb4 }, + { 0x0000ab3c, 0x00000000, 0x00000000, 0x0000ffa1, 0x0000ffa1, 0x0000ffa1 }, + { 0x0000ab40, 0x00000000, 0x00000000, 0x0000ffa5, 0x0000ffa5, 0x0000ffa5 }, + { 0x0000ab44, 0x00000000, 0x00000000, 0x0000ffa9, 0x0000ffa9, 0x0000ffa9 }, + { 0x0000ab48, 0x00000000, 0x00000000, 0x0000ffad, 0x0000ffad, 0x0000ffad }, + { 0x0000ab4c, 0x00000000, 0x00000000, 0x0000ffb1, 0x0000ffb1, 0x0000ffb1 }, + { 0x0000ab50, 0x00000000, 0x00000000, 0x0000ffb5, 0x0000ffb5, 0x0000ffb5 }, + { 0x0000ab54, 0x00000000, 0x00000000, 0x0000ffb9, 0x0000ffb9, 0x0000ffb9 }, + { 0x0000ab58, 0x00000000, 0x00000000, 0x0000ffc5, 0x0000ffc5, 0x0000ffc5 }, + { 0x0000ab5c, 0x00000000, 0x00000000, 0x0000ffc9, 0x0000ffc9, 0x0000ffc9 }, + { 0x0000ab60, 0x00000000, 0x00000000, 0x0000ffcd, 0x0000ffcd, 0x0000ffcd }, + { 0x0000ab64, 0x00000000, 0x00000000, 0x0000ffd1, 0x0000ffd1, 0x0000ffd1 }, + { 0x0000ab68, 0x00000000, 0x00000000, 0x0000ffd5, 0x0000ffd5, 0x0000ffd5 }, + { 0x0000ab6c, 0x00000000, 0x00000000, 0x0000ffc2, 0x0000ffc2, 0x0000ffc2 }, + { 0x0000ab70, 0x00000000, 0x00000000, 0x0000ffc6, 0x0000ffc6, 0x0000ffc6 }, + { 0x0000ab74, 0x00000000, 0x00000000, 0x0000ffca, 0x0000ffca, 0x0000ffca }, + { 0x0000ab78, 0x00000000, 0x00000000, 0x0000ffce, 0x0000ffce, 0x0000ffce }, + { 0x0000ab7c, 0x00000000, 0x00000000, 0x0000ffd2, 0x0000ffd2, 0x0000ffd2 }, + { 0x0000ab80, 0x00000000, 0x00000000, 0x0000ffd6, 0x0000ffd6, 0x0000ffd6 }, + { 0x0000ab84, 0x00000000, 0x00000000, 0x0000ffda, 0x0000ffda, 0x0000ffda }, + { 0x0000ab88, 0x00000000, 0x00000000, 0x0000ffc7, 0x0000ffc7, 0x0000ffc7 }, + { 0x0000ab8c, 0x00000000, 0x00000000, 0x0000ffcb, 0x0000ffcb, 0x0000ffcb }, + { 0x0000ab90, 0x00000000, 0x00000000, 0x0000ffcf, 0x0000ffcf, 0x0000ffcf }, + { 0x0000ab94, 0x00000000, 0x00000000, 0x0000ffd3, 0x0000ffd3, 0x0000ffd3 }, + { 0x0000ab98, 0x00000000, 0x00000000, 0x0000ffd7, 0x0000ffd7, 0x0000ffd7 }, + { 0x0000ab9c, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000aba0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000aba4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000aba8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abac, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abb0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abb4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abb8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abbc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abc0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abc4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abc8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abcc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abd0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abd4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abd8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abdc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abe0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abe4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abe8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abec, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abf0, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abf4, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abf8, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x0000abfc, 0x00000000, 0x00000000, 0x0000ffdb, 0x0000ffdb, 0x0000ffdb }, + { 0x00009848, 0x00000000, 0x00000000, 0x00001067, 0x00001067, 0x00001067 }, + { 0x0000a848, 0x00000000, 0x00000000, 0x00001067, 0x00001067, 0x00001067 }, +}; + +static const u_int32_t ar9287PciePhy_clkreq_always_on_L1_9287_1_1[][2] = { + {0x00004040, 0x9248fd00 }, + {0x00004040, 0x24924924 }, + {0x00004040, 0xa8000019 }, + {0x00004040, 0x13160820 }, + {0x00004040, 0xe5980560 }, + {0x00004040, 0xc01dcffd }, + {0x00004040, 0x1aaabe41 }, + {0x00004040, 0xbe105554 }, + {0x00004040, 0x00043007 }, + {0x00004044, 0x00000000 }, +}; + +static const u_int32_t ar9287PciePhy_clkreq_off_L1_9287_1_1[][2] = { + {0x00004040, 0x9248fd00 }, + {0x00004040, 0x24924924 }, + {0x00004040, 0xa8000019 }, + {0x00004040, 0x13160820 }, + {0x00004040, 0xe5980560 }, + {0x00004040, 0xc01dcffc }, + {0x00004040, 0x1aaabe41 }, + {0x00004040, 0xbe105554 }, + {0x00004040, 0x00043007 }, + {0x00004044, 0x00000000 }, +}; + + +/* AR9271 initialization values automaticaly created: 03/23/09 */ +static const u_int32_t ar9271Modes_9271_1_0[][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, 0x206a012e, 0x206a012e, 0x206a012e }, + { 0x00009844, 0x0372161e, 0x0372161e, 0x03721620, 0x03721620, 0x037216a0 }, + { 0x00009848, 0x00001066, 0x00001066, 0x00001053, 0x00001053, 0x00001059 }, + { 0x0000a848, 0x00001066, 0x00001066, 0x00001053, 0x00001053, 0x00001059 }, + { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 }, + { 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e, 0x3139605e }, + { 0x00009860, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18 }, + { 0x00009864, 0x0000fe00, 0x0000fe00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 }, + { 0x0000986c, 0x06903081, 0x06903081, 0x06903881, 0x06903881, 0x06903881 }, + { 0x00009914, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898, 0x000007d0 }, + { 0x00009918, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d }, + { 0x00009944, 0xffbc1010, 0xffbc1010, 0xffbc1020, 0xffbc1020, 0xffbc1010 }, + { 0x00009960, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00009964, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099b8, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c }, + { 0x000099bc, 0x00000600, 0x00000600, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, + { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, + { 0x000099c8, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329, 0x6af65329 }, + { 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, 0x00000000, 0x00000000, 0x00058084, 0x00058084, 0x00000000 }, + { 0x00009a04, 0x00000000, 0x00000000, 0x00058088, 0x00058088, 0x00000000 }, + { 0x00009a08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c, 0x00000000 }, + { 0x00009a0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100, 0x00000000 }, + { 0x00009a10, 0x00000000, 0x00000000, 0x00058104, 0x00058104, 0x00000000 }, + { 0x00009a14, 0x00000000, 0x00000000, 0x00058108, 0x00058108, 0x00000000 }, + { 0x00009a18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c, 0x00000000 }, + { 0x00009a1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110, 0x00000000 }, + { 0x00009a20, 0x00000000, 0x00000000, 0x00058114, 0x00058114, 0x00000000 }, + { 0x00009a24, 0x00000000, 0x00000000, 0x00058180, 0x00058180, 0x00000000 }, + { 0x00009a28, 0x00000000, 0x00000000, 0x00058184, 0x00058184, 0x00000000 }, + { 0x00009a2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188, 0x00000000 }, + { 0x00009a30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c, 0x00000000 }, + { 0x00009a34, 0x00000000, 0x00000000, 0x00058190, 0x00058190, 0x00000000 }, + { 0x00009a38, 0x00000000, 0x00000000, 0x00058194, 0x00058194, 0x00000000 }, + { 0x00009a3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0, 0x00000000 }, + { 0x00009a40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c, 0x00000000 }, + { 0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 }, + { 0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 }, + { 0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 }, + { 0x00009a50, 0x00000000, 0x00000000, 0x00058220, 0x00058220, 0x00000000 }, + { 0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 }, + { 0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 }, + { 0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 }, + { 0x00009a60, 0x00000000, 0x00000000, 0x00058308, 0x00058308, 0x00000000 }, + { 0x00009a64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c, 0x00000000 }, + { 0x00009a68, 0x00000000, 0x00000000, 0x00058380, 0x00058380, 0x00000000 }, + { 0x00009a6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384, 0x00000000 }, + { 0x00009a70, 0x00000000, 0x00000000, 0x00068700, 0x00068700, 0x00000000 }, + { 0x00009a74, 0x00000000, 0x00000000, 0x00068704, 0x00068704, 0x00000000 }, + { 0x00009a78, 0x00000000, 0x00000000, 0x00068708, 0x00068708, 0x00000000 }, + { 0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 }, + { 0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 }, + { 0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 }, + { 0x00009a88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, + { 0x00009a8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, + { 0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, + { 0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 }, + { 0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 }, + { 0x00009a9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84, 0x00000000 }, + { 0x00009aa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88, 0x00000000 }, + { 0x00009aa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c, 0x00000000 }, + { 0x00009aa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90, 0x00000000 }, + { 0x00009aac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80, 0x00000000 }, + { 0x00009ab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84, 0x00000000 }, + { 0x00009ab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88, 0x00000000 }, + { 0x00009ab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c, 0x00000000 }, + { 0x00009abc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90, 0x00000000 }, + { 0x00009ac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c, 0x00000000 }, + { 0x00009ac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310, 0x00000000 }, + { 0x00009ac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384, 0x00000000 }, + { 0x00009acc, 0x00000000, 0x00000000, 0x000db388, 0x000db388, 0x00000000 }, + { 0x00009ad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324, 0x00000000 }, + { 0x00009ad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704, 0x00000000 }, + { 0x00009ad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4, 0x00000000 }, + { 0x00009adc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8, 0x00000000 }, + { 0x00009ae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710, 0x00000000 }, + { 0x00009ae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714, 0x00000000 }, + { 0x00009ae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720, 0x00000000 }, + { 0x00009aec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724, 0x00000000 }, + { 0x00009af0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728, 0x00000000 }, + { 0x00009af4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c, 0x00000000 }, + { 0x00009af8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0, 0x00000000 }, + { 0x00009afc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4, 0x00000000 }, + { 0x00009b00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8, 0x00000000 }, + { 0x00009b04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0, 0x00000000 }, + { 0x00009b08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4, 0x00000000 }, + { 0x00009b0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8, 0x00000000 }, + { 0x00009b10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5, 0x00000000 }, + { 0x00009b14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9, 0x00000000 }, + { 0x00009b18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad, 0x00000000 }, + { 0x00009b1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1, 0x00000000 }, + { 0x00009b20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5, 0x00000000 }, + { 0x00009b24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9, 0x00000000 }, + { 0x00009b28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5, 0x00000000 }, + { 0x00009b2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9, 0x00000000 }, + { 0x00009b30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1, 0x00000000 }, + { 0x00009b34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5, 0x00000000 }, + { 0x00009b38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9, 0x00000000 }, + { 0x00009b3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6, 0x00000000 }, + { 0x00009b40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca, 0x00000000 }, + { 0x00009b44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce, 0x00000000 }, + { 0x00009b48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2, 0x00000000 }, + { 0x00009b4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6, 0x00000000 }, + { 0x00009b50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3, 0x00000000 }, + { 0x00009b54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7, 0x00000000 }, + { 0x00009b58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb, 0x00000000 }, + { 0x00009b5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf, 0x00000000 }, + { 0x00009b60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7, 0x00000000 }, + { 0x00009b64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009b9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009ba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009ba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009ba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009be0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009be4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009be8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x00009bfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000aa00, 0x00000000, 0x00000000, 0x00058084, 0x00058084, 0x00000000 }, + { 0x0000aa04, 0x00000000, 0x00000000, 0x00058088, 0x00058088, 0x00000000 }, + { 0x0000aa08, 0x00000000, 0x00000000, 0x0005808c, 0x0005808c, 0x00000000 }, + { 0x0000aa0c, 0x00000000, 0x00000000, 0x00058100, 0x00058100, 0x00000000 }, + { 0x0000aa10, 0x00000000, 0x00000000, 0x00058104, 0x00058104, 0x00000000 }, + { 0x0000aa14, 0x00000000, 0x00000000, 0x00058108, 0x00058108, 0x00000000 }, + { 0x0000aa18, 0x00000000, 0x00000000, 0x0005810c, 0x0005810c, 0x00000000 }, + { 0x0000aa1c, 0x00000000, 0x00000000, 0x00058110, 0x00058110, 0x00000000 }, + { 0x0000aa20, 0x00000000, 0x00000000, 0x00058114, 0x00058114, 0x00000000 }, + { 0x0000aa24, 0x00000000, 0x00000000, 0x00058180, 0x00058180, 0x00000000 }, + { 0x0000aa28, 0x00000000, 0x00000000, 0x00058184, 0x00058184, 0x00000000 }, + { 0x0000aa2c, 0x00000000, 0x00000000, 0x00058188, 0x00058188, 0x00000000 }, + { 0x0000aa30, 0x00000000, 0x00000000, 0x0005818c, 0x0005818c, 0x00000000 }, + { 0x0000aa34, 0x00000000, 0x00000000, 0x00058190, 0x00058190, 0x00000000 }, + { 0x0000aa38, 0x00000000, 0x00000000, 0x00058194, 0x00058194, 0x00000000 }, + { 0x0000aa3c, 0x00000000, 0x00000000, 0x000581a0, 0x000581a0, 0x00000000 }, + { 0x0000aa40, 0x00000000, 0x00000000, 0x0005820c, 0x0005820c, 0x00000000 }, + { 0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 }, + { 0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 }, + { 0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 }, + { 0x0000aa50, 0x00000000, 0x00000000, 0x00058220, 0x00058220, 0x00000000 }, + { 0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 }, + { 0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 }, + { 0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 }, + { 0x0000aa60, 0x00000000, 0x00000000, 0x00058308, 0x00058308, 0x00000000 }, + { 0x0000aa64, 0x00000000, 0x00000000, 0x0005830c, 0x0005830c, 0x00000000 }, + { 0x0000aa68, 0x00000000, 0x00000000, 0x00058380, 0x00058380, 0x00000000 }, + { 0x0000aa6c, 0x00000000, 0x00000000, 0x00058384, 0x00058384, 0x00000000 }, + { 0x0000aa70, 0x00000000, 0x00000000, 0x00068700, 0x00068700, 0x00000000 }, + { 0x0000aa74, 0x00000000, 0x00000000, 0x00068704, 0x00068704, 0x00000000 }, + { 0x0000aa78, 0x00000000, 0x00000000, 0x00068708, 0x00068708, 0x00000000 }, + { 0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 }, + { 0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 }, + { 0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 }, + { 0x0000aa88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, + { 0x0000aa8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, + { 0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, + { 0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 }, + { 0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 }, + { 0x0000aa9c, 0x00000000, 0x00000000, 0x00078b84, 0x00078b84, 0x00000000 }, + { 0x0000aaa0, 0x00000000, 0x00000000, 0x00078b88, 0x00078b88, 0x00000000 }, + { 0x0000aaa4, 0x00000000, 0x00000000, 0x00078b8c, 0x00078b8c, 0x00000000 }, + { 0x0000aaa8, 0x00000000, 0x00000000, 0x00078b90, 0x00078b90, 0x00000000 }, + { 0x0000aaac, 0x00000000, 0x00000000, 0x000caf80, 0x000caf80, 0x00000000 }, + { 0x0000aab0, 0x00000000, 0x00000000, 0x000caf84, 0x000caf84, 0x00000000 }, + { 0x0000aab4, 0x00000000, 0x00000000, 0x000caf88, 0x000caf88, 0x00000000 }, + { 0x0000aab8, 0x00000000, 0x00000000, 0x000caf8c, 0x000caf8c, 0x00000000 }, + { 0x0000aabc, 0x00000000, 0x00000000, 0x000caf90, 0x000caf90, 0x00000000 }, + { 0x0000aac0, 0x00000000, 0x00000000, 0x000db30c, 0x000db30c, 0x00000000 }, + { 0x0000aac4, 0x00000000, 0x00000000, 0x000db310, 0x000db310, 0x00000000 }, + { 0x0000aac8, 0x00000000, 0x00000000, 0x000db384, 0x000db384, 0x00000000 }, + { 0x0000aacc, 0x00000000, 0x00000000, 0x000db388, 0x000db388, 0x00000000 }, + { 0x0000aad0, 0x00000000, 0x00000000, 0x000db324, 0x000db324, 0x00000000 }, + { 0x0000aad4, 0x00000000, 0x00000000, 0x000eb704, 0x000eb704, 0x00000000 }, + { 0x0000aad8, 0x00000000, 0x00000000, 0x000eb6a4, 0x000eb6a4, 0x00000000 }, + { 0x0000aadc, 0x00000000, 0x00000000, 0x000eb6a8, 0x000eb6a8, 0x00000000 }, + { 0x0000aae0, 0x00000000, 0x00000000, 0x000eb710, 0x000eb710, 0x00000000 }, + { 0x0000aae4, 0x00000000, 0x00000000, 0x000eb714, 0x000eb714, 0x00000000 }, + { 0x0000aae8, 0x00000000, 0x00000000, 0x000eb720, 0x000eb720, 0x00000000 }, + { 0x0000aaec, 0x00000000, 0x00000000, 0x000eb724, 0x000eb724, 0x00000000 }, + { 0x0000aaf0, 0x00000000, 0x00000000, 0x000eb728, 0x000eb728, 0x00000000 }, + { 0x0000aaf4, 0x00000000, 0x00000000, 0x000eb72c, 0x000eb72c, 0x00000000 }, + { 0x0000aaf8, 0x00000000, 0x00000000, 0x000eb7a0, 0x000eb7a0, 0x00000000 }, + { 0x0000aafc, 0x00000000, 0x00000000, 0x000eb7a4, 0x000eb7a4, 0x00000000 }, + { 0x0000ab00, 0x00000000, 0x00000000, 0x000eb7a8, 0x000eb7a8, 0x00000000 }, + { 0x0000ab04, 0x00000000, 0x00000000, 0x000eb7b0, 0x000eb7b0, 0x00000000 }, + { 0x0000ab08, 0x00000000, 0x00000000, 0x000eb7b4, 0x000eb7b4, 0x00000000 }, + { 0x0000ab0c, 0x00000000, 0x00000000, 0x000eb7b8, 0x000eb7b8, 0x00000000 }, + { 0x0000ab10, 0x00000000, 0x00000000, 0x000eb7a5, 0x000eb7a5, 0x00000000 }, + { 0x0000ab14, 0x00000000, 0x00000000, 0x000eb7a9, 0x000eb7a9, 0x00000000 }, + { 0x0000ab18, 0x00000000, 0x00000000, 0x000eb7ad, 0x000eb7ad, 0x00000000 }, + { 0x0000ab1c, 0x00000000, 0x00000000, 0x000eb7b1, 0x000eb7b1, 0x00000000 }, + { 0x0000ab20, 0x00000000, 0x00000000, 0x000eb7b5, 0x000eb7b5, 0x00000000 }, + { 0x0000ab24, 0x00000000, 0x00000000, 0x000eb7b9, 0x000eb7b9, 0x00000000 }, + { 0x0000ab28, 0x00000000, 0x00000000, 0x000eb7c5, 0x000eb7c5, 0x00000000 }, + { 0x0000ab2c, 0x00000000, 0x00000000, 0x000eb7c9, 0x000eb7c9, 0x00000000 }, + { 0x0000ab30, 0x00000000, 0x00000000, 0x000eb7d1, 0x000eb7d1, 0x00000000 }, + { 0x0000ab34, 0x00000000, 0x00000000, 0x000eb7d5, 0x000eb7d5, 0x00000000 }, + { 0x0000ab38, 0x00000000, 0x00000000, 0x000eb7d9, 0x000eb7d9, 0x00000000 }, + { 0x0000ab3c, 0x00000000, 0x00000000, 0x000eb7c6, 0x000eb7c6, 0x00000000 }, + { 0x0000ab40, 0x00000000, 0x00000000, 0x000eb7ca, 0x000eb7ca, 0x00000000 }, + { 0x0000ab44, 0x00000000, 0x00000000, 0x000eb7ce, 0x000eb7ce, 0x00000000 }, + { 0x0000ab48, 0x00000000, 0x00000000, 0x000eb7d2, 0x000eb7d2, 0x00000000 }, + { 0x0000ab4c, 0x00000000, 0x00000000, 0x000eb7d6, 0x000eb7d6, 0x00000000 }, + { 0x0000ab50, 0x00000000, 0x00000000, 0x000eb7c3, 0x000eb7c3, 0x00000000 }, + { 0x0000ab54, 0x00000000, 0x00000000, 0x000eb7c7, 0x000eb7c7, 0x00000000 }, + { 0x0000ab58, 0x00000000, 0x00000000, 0x000eb7cb, 0x000eb7cb, 0x00000000 }, + { 0x0000ab5c, 0x00000000, 0x00000000, 0x000eb7cf, 0x000eb7cf, 0x00000000 }, + { 0x0000ab60, 0x00000000, 0x00000000, 0x000eb7d7, 0x000eb7d7, 0x00000000 }, + { 0x0000ab64, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab68, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab6c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab70, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab74, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab78, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab7c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab80, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab84, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab88, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab8c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab90, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab94, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab98, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000ab9c, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000aba0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000aba4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000aba8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abac, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abb0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abb4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abb8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abbc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abc0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abc4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abc8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abcc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abd0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abd4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abd8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abdc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abe0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abe4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abe8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abec, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abf0, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abf4, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abf8, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000abfc, 0x00000000, 0x00000000, 0x000eb7db, 0x000eb7db, 0x00000000 }, + { 0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004 }, + { 0x0000a20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000, 0x0001f000 }, + { 0x0000b20c, 0x00000014, 0x00000014, 0x0001f000, 0x0001f000, 0x0001f000 }, + { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, + { 0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000, 0x0004a000 }, + { 0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a218652, 0x0a218652, 0x0a22a652 }, + { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000 }, + { 0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000 }, + { 0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608, 0x00000000 }, + { 0x0000a310, 0x00000000, 0x00000000, 0x0001e610, 0x0001e610, 0x00000000 }, + { 0x0000a314, 0x00000000, 0x00000000, 0x0002d6d0, 0x0002d6d0, 0x00000000 }, + { 0x0000a318, 0x00000000, 0x00000000, 0x00039758, 0x00039758, 0x00000000 }, + { 0x0000a31c, 0x00000000, 0x00000000, 0x0003b759, 0x0003b759, 0x00000000 }, + { 0x0000a320, 0x00000000, 0x00000000, 0x0003d75a, 0x0003d75a, 0x00000000 }, + { 0x0000a324, 0x00000000, 0x00000000, 0x0004175c, 0x0004175c, 0x00000000 }, + { 0x0000a328, 0x00000000, 0x00000000, 0x0004575e, 0x0004575e, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x0004979f, 0x0004979f, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x0004d7df, 0x0004d7df, 0x00000000 }, + { 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000 }, + { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 }, + { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 }, + { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e }, +}; + +static const u_int32_t ar9271Common_9271_1_0[][2] = { + { 0x0000000c, 0x00000000 }, + { 0x00000030, 0x00020045 }, + { 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 }, + { 0x00008004, 0x00000000 }, + { 0x00008008, 0x00000000 }, + { 0x0000800c, 0x00000000 }, + { 0x00008018, 0x00000700 }, + { 0x00008020, 0x00000000 }, + { 0x00008038, 0x00000000 }, + { 0x0000803c, 0x00000000 }, + { 0x00008048, 0x00000000 }, + { 0x00008054, 0x00000000 }, + { 0x00008058, 0x02000000 }, + { 0x0000805c, 0x000fc78f }, + { 0x00008060, 0x0000000f }, + { 0x00008064, 0x00000000 }, + { 0x00008070, 0x00000000 }, + { 0x000080b0, 0x00000000 }, + { 0x000080b4, 0x00000000 }, + { 0x000080b8, 0x00000000 }, + { 0x000080bc, 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, 0x08f04814 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0xffffffff }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x32143320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c0, 0x00000000 }, + { 0x000081d0, 0x0000320a }, + { 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 }, + { 0x00008264, 0xa8a00010 }, + { 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, 0x00000001 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00ff0000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x00010380 }, + { 0x00008344, 0x00581043 }, + { 0x00007010, 0x00000030 }, + { 0x00007034, 0x00000002 }, + { 0x00007038, 0x000004c2 }, + { 0x00007800, 0x00140000 }, + { 0x00007804, 0x0e4548d8 }, + { 0x00007808, 0x54214514 }, + { 0x0000780c, 0x02025820 }, + { 0x00007810, 0x71c0d388 }, + { 0x00007814, 0x924934a8 }, + { 0x0000781c, 0x00000000 }, + { 0x00007820, 0x00000c04 }, + { 0x00007824, 0x00d86bff }, + { 0x00007828, 0x66964300 }, + { 0x0000782c, 0x8db6d961 }, + { 0x00007830, 0x8db6d96c }, + { 0x00007834, 0x6140008b }, + { 0x00007838, 0x00000029 }, + { 0x0000783c, 0x72ee0a72 }, + { 0x00007840, 0xbbfffffc }, + { 0x00007844, 0x000c0db6 }, + { 0x00007848, 0x6db61b6f }, + { 0x0000784c, 0x6d9b66db }, + { 0x00007850, 0x6d8c6dba }, + { 0x00007854, 0x00040000 }, + { 0x00007858, 0xdb003012 }, + { 0x0000785c, 0x04924914 }, + { 0x00007860, 0x21084210 }, + { 0x00007864, 0xf7d7ffde }, + { 0x00007868, 0xc2034080 }, + { 0x0000786c, 0x48609eb4 }, + { 0x00007870, 0x10142c00 }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xafe68e30 }, + { 0x00009810, 0xfd14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x0000984c, 0x0040233c }, + { 0x00009854, 0x00000044 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x00009910, 0x30002310 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x04900000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009940, 0x14750604 }, + { 0x00009948, 0x9280c00a }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0x5f3ca3de }, + { 0x00009958, 0x0108ecff }, + { 0x00009968, 0x000003ce }, + { 0x00009970, 0x192bb515 }, + { 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, 0x2def0400 }, + { 0x000099b0, 0x03051000 }, + { 0x000099b4, 0x00000820 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000000 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099f0, 0x00000000 }, + { 0x0000a1f4, 0x00000000 }, + { 0x0000a1f8, 0x71733d01 }, + { 0x0000a1fc, 0xd0ad5c12 }, + { 0x0000a208, 0x803e68c8 }, + { 0x0000a210, 0x4080a333 }, + { 0x0000a214, 0x00206c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x01834061 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x000003b5 }, + { 0x0000a22c, 0x00000000 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a244, 0x00000000 }, + { 0x0000a248, 0xfffffffc }, + { 0x0000a24c, 0x00000000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0ccb5380 }, + { 0x0000a25c, 0x15151501 }, + { 0x0000a260, 0xdfa90f01 }, + { 0x0000a268, 0x00000000 }, + { 0x0000a26c, 0x0ebae9e6 }, + { 0x0000a278, 0x3bdef7bd }, + { 0x0000a27c, 0x050e83bd }, + { 0x0000a388, 0x0c000000 }, + { 0x0000a38c, 0x20202020 }, + { 0x0000a390, 0x20202020 }, + { 0x0000a394, 0x3bdef7bd }, + { 0x0000a398, 0x000003bd }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3a0, 0x00000000 }, + { 0x0000a3a4, 0x00000000 }, + { 0x0000a3a8, 0x00000000 }, + { 0x0000a3ac, 0x00000000 }, + { 0x0000a3b0, 0x00000000 }, + { 0x0000a3b4, 0x00000000 }, + { 0x0000a3b8, 0x00000000 }, + { 0x0000a3bc, 0x00000000 }, + { 0x0000a3c0, 0x00000000 }, + { 0x0000a3c4, 0x00000000 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3dc, 0x3bdef7bd }, + { 0x0000a3e0, 0x000003bd }, + { 0x0000a3e4, 0x00000000 }, + { 0x0000a3e8, 0x18c43433 }, + { 0x0000a3ec, 0x00f70081 }, + { 0x0000a3f0, 0x01036a2f }, + { 0x0000a3f4, 0x00000000 }, + { 0x0000d270, 0x0d820820 }, + { 0x0000d35c, 0x07ffffef }, + { 0x0000d360, 0x0fffffe7 }, + { 0x0000d364, 0x17ffffe5 }, + { 0x0000d368, 0x1fffffe4 }, + { 0x0000d36c, 0x37ffffe3 }, + { 0x0000d370, 0x3fffffe3 }, + { 0x0000d374, 0x57ffffe3 }, + { 0x0000d378, 0x5fffffe2 }, + { 0x0000d37c, 0x7fffffe2 }, + { 0x0000d380, 0x7f3c7bba }, + { 0x0000d384, 0xf3307ff0 }, +}; diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 8ae4ec21667b..800bfab94635 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -40,20 +40,15 @@ u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q) return REG_READ(ah, AR_QTXDP(q)); } -bool ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp) +void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp) { REG_WRITE(ah, AR_QTXDP(q), txdp); - - return true; } -bool ath9k_hw_txstart(struct ath_hw *ah, u32 q) +void ath9k_hw_txstart(struct ath_hw *ah, u32 q) { DPRINTF(ah->ah_sc, ATH_DBG_QUEUE, "Enable TXE on queue: %u\n", q); - REG_WRITE(ah, AR_Q_TXE, 1 << q); - - return true; } u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q) @@ -178,7 +173,7 @@ bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q) #undef ATH9K_TIME_QUANTUM } -bool ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds, +void ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds, u32 segLen, bool firstSeg, bool lastSeg, const struct ath_desc *ds0) { @@ -202,8 +197,6 @@ bool ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds, 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_hw *ah, struct ath_desc *ds) @@ -825,13 +818,29 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, 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_PostDelimCRCErr) { + ds->ds_rxstat.rs_rssi = ATH9K_RSSI_BAD; + ds->ds_rxstat.rs_rssi_ctl0 = ATH9K_RSSI_BAD; + ds->ds_rxstat.rs_rssi_ctl1 = ATH9K_RSSI_BAD; + ds->ds_rxstat.rs_rssi_ctl2 = ATH9K_RSSI_BAD; + ds->ds_rxstat.rs_rssi_ext0 = ATH9K_RSSI_BAD; + ds->ds_rxstat.rs_rssi_ext1 = ATH9K_RSSI_BAD; + ds->ds_rxstat.rs_rssi_ext2 = ATH9K_RSSI_BAD; + } else { + 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 @@ -872,7 +881,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, return 0; } -bool ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, +void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, u32 size, u32 flags) { struct ar5416_desc *ads = AR5416DESC(ds); @@ -885,8 +894,6 @@ bool ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, ads->ds_rxstatus8 &= ~AR_RxDone; if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) memset(&(ads->u), 0, sizeof(ads->u)); - - return true; } bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set) diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 1176bce8b76c..7b3982295a43 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -628,12 +628,12 @@ struct ath9k_channel; struct ath_rate_table; u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q); -bool ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp); -bool ath9k_hw_txstart(struct ath_hw *ah, u32 q); +void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp); +void ath9k_hw_txstart(struct ath_hw *ah, u32 q); u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q); bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel); bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q); -bool ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds, +void ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds, u32 segLen, bool firstSeg, bool lastSeg, const struct ath_desc *ds0); void ath9k_hw_cleartxdesc(struct ath_hw *ah, struct ath_desc *ds); @@ -668,7 +668,7 @@ bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q); bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q); int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, u32 pa, struct ath_desc *nds, u64 tsf); -bool ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, +void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, u32 size, u32 flags); bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set); void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 66a6c1f5022a..4fae699a53c2 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -17,8 +17,6 @@ #include <linux/nl80211.h> #include "ath9k.h" -#define ATH_PCI_VERSION "0.1" - static char *dev_info = "ath9k"; MODULE_AUTHOR("Atheros Communications"); @@ -342,6 +340,7 @@ static void ath_ani_calibrate(unsigned long data) * don't calibrate when we're scanning. * we are most likely not on our home channel. */ + spin_lock(&sc->ani_lock); if (sc->sc_flags & SC_OP_SCANNING) goto set_timer; @@ -385,7 +384,7 @@ static void ath_ani_calibrate(unsigned long data) if (longcal || shortcal || aniflag) { /* Call ANI routine if necessary */ if (aniflag) - ath9k_hw_ani_monitor(ah, &sc->nodestats, ah->curchan); + ath9k_hw_ani_monitor(ah, ah->curchan); /* Perform calibration if necessary */ if (longcal || shortcal) { @@ -405,6 +404,7 @@ static void ath_ani_calibrate(unsigned long data) ath9k_ps_restore(sc); set_timer: + spin_unlock(&sc->ani_lock); /* * Set timer interval based on previous results. * The interval must be the shortest necessary to satisfy ANI, @@ -439,8 +439,8 @@ static void ath_start_ani(struct ath_softc *sc) */ void ath_update_chainmask(struct ath_softc *sc, int is_ht) { - if (is_ht || - (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BT_COEX)) { + if ((sc->sc_flags & SC_OP_SCANNING) || is_ht || + (sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE)) { sc->tx_chainmask = sc->sc_ah->caps.tx_chainmask; sc->rx_chainmask = sc->sc_ah->caps.rx_chainmask; } else { @@ -460,9 +460,10 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta) if (sc->sc_flags & SC_OP_TXAGGR) { ath_tx_node_init(sc, an); - an->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR + + an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + sta->ht_cap.ampdu_factor); an->mpdudensity = parse_mpdudensity(sta->ht_cap.ampdu_density); + an->last_rssi = ATH_RSSI_DUMMY_MARKER; } } @@ -496,8 +497,7 @@ static void ath9k_tasklet(unsigned long data) if (status & ATH9K_INT_TX) ath_tx_tasklet(sc); - if ((status & ATH9K_INT_TSFOOR) && - (sc->hw->conf.flags & IEEE80211_CONF_PS)) { + if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) { /* * TSF sync does not look correct; remain awake to sync with * the next Beacon. @@ -589,7 +589,7 @@ irqreturn_t ath_isr(int irq, void *dev) * it will clear whatever condition caused * the interrupt. */ - ath9k_hw_procmibevent(ah, &sc->nodestats); + ath9k_hw_procmibevent(ah); ath9k_hw_set_interrupts(ah, sc->imask); } @@ -602,6 +602,10 @@ irqreturn_t ath_isr(int irq, void *dev) sc->sc_flags |= SC_OP_WAIT_FOR_BEACON; } + if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) + if (status & ATH9K_INT_GENTIMER) + ath_gen_timer_isr(ah); + chip_reset: ath_debug_stat_interrupt(sc, status); @@ -885,8 +889,7 @@ static void ath_key_delete(struct ath_softc *sc, struct ieee80211_key_conf *key) static void setup_ht_cap(struct ath_softc *sc, struct ieee80211_sta_ht_cap *ht_info) { -#define ATH9K_HT_CAP_MAXRXAMPDU_65536 0x3 /* 2 ^ 16 */ -#define ATH9K_HT_CAP_MPDUDENSITY_8 0x6 /* 8 usec */ + u8 tx_streams, rx_streams; ht_info->ht_supported = true; ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | @@ -894,64 +897,61 @@ static void setup_ht_cap(struct ath_softc *sc, IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_DSSSCCK40; - ht_info->ampdu_factor = ATH9K_HT_CAP_MAXRXAMPDU_65536; - ht_info->ampdu_density = ATH9K_HT_CAP_MPDUDENSITY_8; + ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; /* set up supported mcs set */ memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + tx_streams = !(sc->tx_chainmask & (sc->tx_chainmask - 1)) ? 1 : 2; + rx_streams = !(sc->rx_chainmask & (sc->rx_chainmask - 1)) ? 1 : 2; - switch(sc->rx_chainmask) { - case 1: - ht_info->mcs.rx_mask[0] = 0xff; - break; - case 3: - case 5: - case 7: - default: - ht_info->mcs.rx_mask[0] = 0xff; - ht_info->mcs.rx_mask[1] = 0xff; - break; + if (tx_streams != rx_streams) { + DPRINTF(sc, ATH_DBG_CONFIG, "TX streams %d, RX streams: %d\n", + tx_streams, rx_streams); + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + ht_info->mcs.tx_params |= ((tx_streams - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); } - ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + ht_info->mcs.rx_mask[0] = 0xff; + if (rx_streams >= 2) + ht_info->mcs.rx_mask[1] = 0xff; + + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; } static void ath9k_bss_assoc_info(struct ath_softc *sc, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf) { - struct ath_vif *avp = (void *)vif->drv_priv; if (bss_conf->assoc) { DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info ASSOC %d, bssid: %pM\n", bss_conf->aid, sc->curbssid); /* New association, store aid */ - if (avp->av_opmode == NL80211_IFTYPE_STATION) { - sc->curaid = bss_conf->aid; - ath9k_hw_write_associd(sc); + sc->curaid = bss_conf->aid; + ath9k_hw_write_associd(sc); - /* - * Request a re-configuration of Beacon related timers - * on the receipt of the first Beacon frame (i.e., - * after time sync with the AP). - */ - sc->sc_flags |= SC_OP_BEACON_SYNC; - } + /* + * Request a re-configuration of Beacon related timers + * on the receipt of the first Beacon frame (i.e., + * after time sync with the AP). + */ + sc->sc_flags |= SC_OP_BEACON_SYNC; /* Configure the beacon */ ath_beacon_config(sc, vif); /* Reset rssi stats */ - sc->nodestats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; - sc->nodestats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; - sc->nodestats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; - sc->nodestats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER; + sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; ath_start_ani(sc); } else { DPRINTF(sc, ATH_DBG_CONFIG, "Bss Info DISASSOC\n"); sc->curaid = 0; + /* Stop ANI */ + del_timer_sync(&sc->ani.timer); } } @@ -969,15 +969,16 @@ static void ath_led_blink_work(struct work_struct *work) if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) || (sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE)) - ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0); + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); else - ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0); - queue_delayed_work(sc->hw->workqueue, &sc->ath_led_blink_work, - (sc->sc_flags & SC_OP_LED_ON) ? - msecs_to_jiffies(sc->led_off_duration) : - msecs_to_jiffies(sc->led_on_duration)); + ieee80211_queue_delayed_work(sc->hw, + &sc->ath_led_blink_work, + (sc->sc_flags & SC_OP_LED_ON) ? + msecs_to_jiffies(sc->led_off_duration) : + msecs_to_jiffies(sc->led_on_duration)); sc->led_on_duration = sc->led_on_cnt ? max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) : @@ -1002,7 +1003,7 @@ static void ath_led_brightness(struct led_classdev *led_cdev, case LED_OFF: if (led->led_type == ATH_LED_ASSOC || led->led_type == ATH_LED_RADIO) { - ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, (led->led_type == ATH_LED_RADIO)); sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; if (led->led_type == ATH_LED_RADIO) @@ -1014,10 +1015,10 @@ static void ath_led_brightness(struct led_classdev *led_cdev, case LED_FULL: if (led->led_type == ATH_LED_ASSOC) { sc->sc_flags |= SC_OP_LED_ASSOCIATED; - queue_delayed_work(sc->hw->workqueue, - &sc->ath_led_blink_work, 0); + ieee80211_queue_delayed_work(sc->hw, + &sc->ath_led_blink_work, 0); } else if (led->led_type == ATH_LED_RADIO) { - ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0); + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); sc->sc_flags |= SC_OP_LED_ON; } else { sc->led_on_cnt++; @@ -1057,13 +1058,12 @@ static void ath_unregister_led(struct ath_led *led) static void ath_deinit_leds(struct ath_softc *sc) { - cancel_delayed_work_sync(&sc->ath_led_blink_work); ath_unregister_led(&sc->assoc_led); sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; ath_unregister_led(&sc->tx_led); ath_unregister_led(&sc->rx_led); ath_unregister_led(&sc->radio_led); - ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); } static void ath_init_leds(struct ath_softc *sc) @@ -1071,11 +1071,16 @@ static void ath_init_leds(struct ath_softc *sc) char *trigger; int ret; + if (AR_SREV_9287(sc->sc_ah)) + sc->sc_ah->led_pin = ATH_LED_PIN_9287; + else + sc->sc_ah->led_pin = ATH_LED_PIN_DEF; + /* Configure gpio 1 for output */ - ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN, + ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); /* LED off, active low */ - ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); INIT_DELAYED_WORK(&sc->ath_led_blink_work, ath_led_blink_work); @@ -1114,6 +1119,7 @@ static void ath_init_leds(struct ath_softc *sc) return; fail: + cancel_delayed_work_sync(&sc->ath_led_blink_work); ath_deinit_leds(sc); } @@ -1153,9 +1159,9 @@ void ath_radio_enable(struct ath_softc *sc) ath9k_hw_set_interrupts(ah, sc->imask); /* Enable LED */ - ath9k_hw_cfg_output(ah, ATH_LED_PIN, + ath9k_hw_cfg_output(ah, ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); - ath9k_hw_set_gpio(ah, ATH_LED_PIN, 0); + ath9k_hw_set_gpio(ah, ah->led_pin, 0); ieee80211_wake_queues(sc->hw); ath9k_ps_restore(sc); @@ -1171,8 +1177,8 @@ void ath_radio_disable(struct ath_softc *sc) ieee80211_stop_queues(sc->hw); /* Disable LED */ - ath9k_hw_set_gpio(ah, ATH_LED_PIN, 1); - ath9k_hw_cfg_gpio_input(ah, ATH_LED_PIN); + ath9k_hw_set_gpio(ah, ah->led_pin, 1); + ath9k_hw_cfg_gpio_input(ah, ah->led_pin); /* Disable interrupts */ ath9k_hw_set_interrupts(ah, 0); @@ -1253,8 +1259,6 @@ void ath_detach(struct ath_softc *sc) DPRINTF(sc, ATH_DBG_CONFIG, "Detach ATH hw\n"); ath_deinit_leds(sc); - cancel_work_sync(&sc->chan_work); - cancel_delayed_work_sync(&sc->wiphy_work); for (i = 0; i < sc->num_sec_wiphy; i++) { struct ath_wiphy *aphy = sc->sec_wiphy[i]; @@ -1279,9 +1283,13 @@ void ath_detach(struct ath_softc *sc) if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->tx.txq[i]); + if ((sc->btcoex_info.no_stomp_timer) && + sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) + ath_gen_timer_free(sc->sc_ah, sc->btcoex_info.no_stomp_timer); + ath9k_hw_detach(sc->sc_ah); + sc->sc_ah = NULL; ath9k_exit_debug(sc); - ath9k_ps_restore(sc); } static int ath9k_reg_notifier(struct wiphy *wiphy, @@ -1290,16 +1298,21 @@ static int ath9k_reg_notifier(struct wiphy *wiphy, struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; - struct ath_regulatory *reg = &sc->sc_ah->regulatory; + struct ath_regulatory *reg = &sc->common.regulatory; return ath_reg_notifier_apply(wiphy, request, reg); } -static int ath_init(u16 devid, struct ath_softc *sc) +/* + * Initialize and fill ath_softc, ath_sofct is the + * "Software Carrier" struct. Historically it has existed + * to allow the separation between hardware specific + * variables (now in ath_hw) and driver specific variables. + */ +static int ath_init_softc(u16 devid, struct ath_softc *sc) { struct ath_hw *ah = NULL; - int status; - int error = 0, i; + int r = 0, i; int csz = 0; /* XXX: hardware will not be ready until ath_open() being called */ @@ -1311,6 +1324,8 @@ static int ath_init(u16 devid, struct ath_softc *sc) spin_lock_init(&sc->wiphy_lock); spin_lock_init(&sc->sc_resetlock); spin_lock_init(&sc->sc_serial_rw); + spin_lock_init(&sc->ani_lock); + spin_lock_init(&sc->sc_pm_lock); mutex_init(&sc->mutex); tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet, @@ -1322,16 +1337,25 @@ static int ath_init(u16 devid, struct ath_softc *sc) */ ath_read_cachesize(sc, &csz); /* XXX assert csz is non-zero */ - sc->cachelsz = csz << 2; /* convert to bytes */ + sc->common.cachelsz = csz << 2; /* convert to bytes */ + + ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); + if (!ah) { + r = -ENOMEM; + goto bad_no_ah; + } + + ah->ah_sc = sc; + ah->hw_version.devid = devid; + sc->sc_ah = ah; - ah = ath9k_hw_attach(devid, sc, &status); - if (ah == NULL) { + r = ath9k_hw_init(ah); + if (r) { DPRINTF(sc, ATH_DBG_FATAL, - "Unable to attach hardware; HAL status %d\n", status); - error = -ENXIO; + "Unable to initialize hardware; " + "initialization status: %d\n", r); goto bad; } - sc->sc_ah = ah; /* Get the hardware key cache size. */ sc->keymax = ah->caps.keycache_size; @@ -1349,9 +1373,6 @@ static int ath_init(u16 devid, struct ath_softc *sc) for (i = 0; i < sc->keymax; i++) ath9k_hw_keyreset(ah, (u16) i); - if (error) - goto bad; - /* default to MONITOR mode */ sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR; @@ -1371,14 +1392,14 @@ static int ath_init(u16 devid, struct ath_softc *sc) if (sc->beacon.beaconq == -1) { DPRINTF(sc, ATH_DBG_FATAL, "Unable to setup a beacon xmit queue\n"); - error = -EIO; + r = -EIO; goto bad2; } sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); if (sc->beacon.cabq == NULL) { DPRINTF(sc, ATH_DBG_FATAL, "Unable to setup CAB xmit queue\n"); - error = -EIO; + r = -EIO; goto bad2; } @@ -1393,26 +1414,26 @@ static int ath_init(u16 devid, struct ath_softc *sc) if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) { DPRINTF(sc, ATH_DBG_FATAL, "Unable to setup xmit queue for BK traffic\n"); - error = -EIO; + r = -EIO; goto bad2; } if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) { DPRINTF(sc, ATH_DBG_FATAL, "Unable to setup xmit queue for BE traffic\n"); - error = -EIO; + r = -EIO; goto bad2; } if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) { DPRINTF(sc, ATH_DBG_FATAL, "Unable to setup xmit queue for VI traffic\n"); - error = -EIO; + r = -EIO; goto bad2; } if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) { DPRINTF(sc, ATH_DBG_FATAL, "Unable to setup xmit queue for VO traffic\n"); - error = -EIO; + r = -EIO; goto bad2; } @@ -1496,8 +1517,11 @@ static int ath_init(u16 devid, struct ath_softc *sc) ARRAY_SIZE(ath9k_5ghz_chantable); } - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BT_COEX) - ath9k_hw_btcoex_enable(sc->sc_ah); + if (sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE) { + r = ath9k_hw_btcoex_init(ah); + if (r) + goto bad2; + } return 0; bad2: @@ -1506,11 +1530,12 @@ bad2: if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->tx.txq[i]); bad: - if (ah) - ath9k_hw_detach(ah); + ath9k_hw_detach(ah); + sc->sc_ah = NULL; +bad_no_ah: ath9k_exit_debug(sc); - return error; + return r; } void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) @@ -1536,7 +1561,8 @@ void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->max_rates = 4; hw->channel_change_time = 5000; hw->max_listen_interval = 10; - hw->max_rate_tries = ATH_11N_TXMAXTRY; + /* Hardware supports 10 but we use 4 */ + hw->max_rate_tries = 4; hw->sta_data_size = sizeof(struct ath_node); hw->vif_data_size = sizeof(struct ath_vif); @@ -1549,7 +1575,8 @@ void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) &sc->sbands[IEEE80211_BAND_5GHZ]; } -int ath_attach(u16 devid, struct ath_softc *sc) +/* Device driver core initialization */ +int ath_init_device(u16 devid, struct ath_softc *sc) { struct ieee80211_hw *hw = sc->hw; int error = 0, i; @@ -1557,7 +1584,7 @@ int ath_attach(u16 devid, struct ath_softc *sc) DPRINTF(sc, ATH_DBG_CONFIG, "Attach ATH hw\n"); - error = ath_init(devid, sc); + error = ath_init_softc(devid, sc); if (error != 0) return error; @@ -1567,12 +1594,12 @@ int ath_attach(u16 devid, struct ath_softc *sc) ath_set_hw_capab(sc, hw); - error = ath_regd_init(&sc->sc_ah->regulatory, sc->hw->wiphy, + error = ath_regd_init(&sc->common.regulatory, sc->hw->wiphy, ath9k_reg_notifier); if (error) return error; - reg = &sc->sc_ah->regulatory; + reg = &sc->common.regulatory; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); @@ -1615,6 +1642,7 @@ error_attach: ath_tx_cleanupq(sc, &sc->tx.txq[i]); ath9k_hw_detach(sc->sc_ah); + sc->sc_ah = NULL; ath9k_exit_debug(sc); return error; @@ -1973,6 +2001,18 @@ static int ath9k_start(struct ieee80211_hw *hw) ieee80211_wake_queues(hw); + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); + + if ((sc->btcoex_info.btcoex_scheme != ATH_BTCOEX_CFG_NONE) && + !(sc->sc_flags & SC_OP_BTCOEX_ENABLED)) { + ath_btcoex_set_weight(&sc->btcoex_info, AR_BT_COEX_WGHT, + AR_STOMP_LOW_WLAN_WGHT); + ath9k_hw_btcoex_enable(sc->sc_ah); + + if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) + ath_btcoex_timer_resume(sc, &sc->btcoex_info); + } + mutex_unlock: mutex_unlock(&sc->mutex); @@ -1994,7 +2034,7 @@ static int ath9k_tx(struct ieee80211_hw *hw, goto exit; } - if (sc->hw->conf.flags & IEEE80211_CONF_PS) { + if (sc->ps_enabled) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; /* * mac80211 does not set PM field for normal data frames, so we @@ -2083,22 +2123,35 @@ static void ath9k_stop(struct ieee80211_hw *hw) struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + mutex_lock(&sc->mutex); + aphy->state = ATH_WIPHY_INACTIVE; + cancel_delayed_work_sync(&sc->ath_led_blink_work); + cancel_delayed_work_sync(&sc->tx_complete_work); + + if (!sc->num_sec_wiphy) { + cancel_delayed_work_sync(&sc->wiphy_work); + cancel_work_sync(&sc->chan_work); + } + if (sc->sc_flags & SC_OP_INVALID) { DPRINTF(sc, ATH_DBG_ANY, "Device not present\n"); + mutex_unlock(&sc->mutex); return; } - mutex_lock(&sc->mutex); - - ieee80211_stop_queues(hw); - if (ath9k_wiphy_started(sc)) { mutex_unlock(&sc->mutex); return; /* another wiphy still in use */ } + if (sc->sc_flags & SC_OP_BTCOEX_ENABLED) { + ath9k_hw_btcoex_disable(sc->sc_ah); + if (sc->btcoex_info.btcoex_scheme == ATH_BTCOEX_CFG_3WIRE) + ath_btcoex_timer_pause(sc, &sc->btcoex_info); + } + /* make sure h/w will not generate any interrupt * before setting the invalid flag. */ ath9k_hw_set_interrupts(sc->sc_ah, 0); @@ -2115,6 +2168,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) /* disable HAL and put h/w to sleep */ ath9k_hw_disable(sc->sc_ah); ath9k_hw_configpcipowersave(sc->sc_ah, 1); + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); sc->sc_flags |= SC_OP_INVALID; @@ -2189,14 +2243,15 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, if ((conf->type == NL80211_IFTYPE_STATION) || (conf->type == NL80211_IFTYPE_ADHOC) || (conf->type == NL80211_IFTYPE_MESH_POINT)) { - if (ath9k_hw_phycounters(sc->sc_ah)) - sc->imask |= ATH9K_INT_MIB; + sc->imask |= ATH9K_INT_MIB; sc->imask |= ATH9K_INT_TSFOOR; } ath9k_hw_set_interrupts(sc->sc_ah, sc->imask); - if (conf->type == NL80211_IFTYPE_AP) + if (conf->type == NL80211_IFTYPE_AP || + conf->type == NL80211_IFTYPE_ADHOC || + conf->type == NL80211_IFTYPE_MONITOR) ath_start_ani(sc); out: @@ -2249,9 +2304,28 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) struct ath_softc *sc = aphy->sc; struct ieee80211_conf *conf = &hw->conf; struct ath_hw *ah = sc->sc_ah; + bool all_wiphys_idle = false, disable_radio = false; mutex_lock(&sc->mutex); + /* Leave this as the first check */ + if (changed & IEEE80211_CONF_CHANGE_IDLE) { + + spin_lock_bh(&sc->wiphy_lock); + all_wiphys_idle = ath9k_all_wiphys_idle(sc); + spin_unlock_bh(&sc->wiphy_lock); + + if (conf->flags & IEEE80211_CONF_IDLE){ + if (all_wiphys_idle) + disable_radio = true; + } + else if (all_wiphys_idle) { + ath_radio_enable(sc); + DPRINTF(sc, ATH_DBG_CONFIG, + "not-idle: enabling radio\n"); + } + } + if (changed & IEEE80211_CONF_CHANGE_PS) { if (conf->flags & IEEE80211_CONF_PS) { if (!(ah->caps.hw_caps & @@ -2263,8 +2337,9 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } ath9k_hw_setrxabort(sc->sc_ah, 1); } - ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); + sc->ps_enabled = true; } else { + sc->ps_enabled = false; ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { @@ -2319,6 +2394,11 @@ skip_chan_change: if (changed & IEEE80211_CONF_CHANGE_POWER) sc->config.txpowlimit = 2 * conf->power_level; + if (disable_radio) { + DPRINTF(sc, ATH_DBG_CONFIG, "idle: disabling radio\n"); + ath_radio_disable(sc); + } + mutex_unlock(&sc->mutex); return 0; @@ -2328,6 +2408,7 @@ skip_chan_change: (FIF_PROMISC_IN_BSS | \ FIF_ALLMULTI | \ FIF_CONTROL | \ + FIF_PSPOLL | \ FIF_OTHER_BSS | \ FIF_BCN_PRBRESP_PROMISC | \ FIF_FCSFAIL) @@ -2336,8 +2417,7 @@ skip_chan_change: 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) + u64 multicast) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; @@ -2638,19 +2718,11 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, case IEEE80211_AMPDU_RX_STOP: break; case IEEE80211_AMPDU_TX_START: - ret = ath_tx_aggr_start(sc, sta, tid, ssn); - if (ret < 0) - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to start TX aggregation\n"); - else - ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid); + ath_tx_aggr_start(sc, sta, tid, ssn); + ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid); break; case IEEE80211_AMPDU_TX_STOP: - ret = ath_tx_aggr_stop(sc, sta, tid); - if (ret < 0) - DPRINTF(sc, ATH_DBG_FATAL, - "Unable to stop TX aggregation\n"); - + ath_tx_aggr_stop(sc, sta, tid); ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: @@ -2668,6 +2740,7 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw) struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + mutex_lock(&sc->mutex); if (ath9k_wiphy_scanning(sc)) { printk(KERN_DEBUG "ath9k: Two wiphys trying to scan at the " "same time\n"); @@ -2675,14 +2748,16 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw) * Do not allow the concurrent scanning state for now. This * could be improved with scanning control moved into ath9k. */ + mutex_unlock(&sc->mutex); return; } aphy->state = ATH_WIPHY_SCAN; ath9k_wiphy_pause_all_forced(sc, aphy); - mutex_lock(&sc->mutex); + spin_lock_bh(&sc->ani_lock); sc->sc_flags |= SC_OP_SCANNING; + spin_unlock_bh(&sc->ani_lock); mutex_unlock(&sc->mutex); } @@ -2692,9 +2767,11 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) struct ath_softc *sc = aphy->sc; mutex_lock(&sc->mutex); + spin_lock_bh(&sc->ani_lock); aphy->state = ATH_WIPHY_ACTIVE; sc->sc_flags &= ~SC_OP_SCANNING; sc->sc_flags |= SC_OP_FULL_RESET; + spin_unlock_bh(&sc->ani_lock); mutex_unlock(&sc->mutex); } @@ -2728,7 +2805,8 @@ static struct { { AR_SREV_VERSION_9100, "9100" }, { AR_SREV_VERSION_9160, "9160" }, { AR_SREV_VERSION_9280, "9280" }, - { AR_SREV_VERSION_9285, "9285" } + { AR_SREV_VERSION_9285, "9285" }, + { AR_SREV_VERSION_9287, "9287" } }; static struct { diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 170c5b32e49b..685a8cebb468 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -25,6 +25,8 @@ static struct pci_device_id ath_pci_id_table[] __devinitdata = { { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ { PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */ + { PCI_VDEVICE(ATHEROS, 0x002D) }, /* PCI */ + { PCI_VDEVICE(ATHEROS, 0x002E) }, /* PCI-E */ { 0 } }; @@ -176,7 +178,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) sc->mem = mem; sc->bus_ops = &ath_pci_bus_ops; - if (ath_attach(id->device, sc) != 0) { + if (ath_init_device(id->device, sc) != 0) { ret = -ENODEV; goto bad3; } @@ -234,7 +236,7 @@ static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state) struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; - ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); pci_save_state(pdev); pci_disable_device(pdev); @@ -251,10 +253,12 @@ static int ath_pci_resume(struct pci_dev *pdev) u32 val; int 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 @@ -265,9 +269,9 @@ static int ath_pci_resume(struct pci_dev *pdev) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); /* Enable LED */ - ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN, + ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); - ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); return 0; } diff --git a/drivers/net/wireless/ath/ath9k/phy.c b/drivers/net/wireless/ath/ath9k/phy.c index aaa941561c36..63bf9a307c6a 100644 --- a/drivers/net/wireless/ath/ath9k/phy.c +++ b/drivers/net/wireless/ath/ath9k/phy.c @@ -264,44 +264,23 @@ ath9k_hw_set_rf_regs(struct ath_hw *ah, struct ath9k_channel *chan, } void -ath9k_hw_rfdetach(struct ath_hw *ah) +ath9k_hw_rf_free(struct ath_hw *ah) { - if (ah->analogBank0Data != NULL) { - kfree(ah->analogBank0Data); - ah->analogBank0Data = NULL; - } - if (ah->analogBank1Data != NULL) { - kfree(ah->analogBank1Data); - ah->analogBank1Data = NULL; - } - if (ah->analogBank2Data != NULL) { - kfree(ah->analogBank2Data); - ah->analogBank2Data = NULL; - } - if (ah->analogBank3Data != NULL) { - kfree(ah->analogBank3Data); - ah->analogBank3Data = NULL; - } - if (ah->analogBank6Data != NULL) { - kfree(ah->analogBank6Data); - ah->analogBank6Data = NULL; - } - if (ah->analogBank6TPCData != NULL) { - kfree(ah->analogBank6TPCData); - ah->analogBank6TPCData = NULL; - } - if (ah->analogBank7Data != NULL) { - kfree(ah->analogBank7Data); - ah->analogBank7Data = NULL; - } - if (ah->addac5416_21 != NULL) { - kfree(ah->addac5416_21); - ah->addac5416_21 = NULL; - } - if (ah->bank6Temp != NULL) { - kfree(ah->bank6Temp); - ah->bank6Temp = NULL; - } +#define ATH_FREE_BANK(bank) do { \ + kfree(bank); \ + bank = NULL; \ + } while (0); + + ATH_FREE_BANK(ah->analogBank0Data); + ATH_FREE_BANK(ah->analogBank1Data); + ATH_FREE_BANK(ah->analogBank2Data); + ATH_FREE_BANK(ah->analogBank3Data); + ATH_FREE_BANK(ah->analogBank6Data); + ATH_FREE_BANK(ah->analogBank6TPCData); + ATH_FREE_BANK(ah->analogBank7Data); + ATH_FREE_BANK(ah->addac5416_21); + ATH_FREE_BANK(ah->bank6Temp); +#undef ATH_FREE_BANK } bool ath9k_hw_init_rf(struct ath_hw *ah, int *status) @@ -374,18 +353,16 @@ ath9k_hw_decrease_chain_power(struct ath_hw *ah, struct ath9k_channel *chan) u32 bank6SelMask; u32 *bank6Temp = ah->bank6Temp; - switch (ah->diversity_control) { + switch (ah->config.diversity_control) { case ATH9K_ANT_FIXED_A: bank6SelMask = - (ah-> - antenna_switch_swap & ANTSWAP_AB) ? REDUCE_CHAIN_0 : - REDUCE_CHAIN_1; + (ah->config.antenna_switch_swap & ANTSWAP_AB) ? + REDUCE_CHAIN_0 : REDUCE_CHAIN_1; break; case ATH9K_ANT_FIXED_B: bank6SelMask = - (ah-> - antenna_switch_swap & ANTSWAP_AB) ? REDUCE_CHAIN_1 : - REDUCE_CHAIN_0; + (ah->config.antenna_switch_swap & ANTSWAP_AB) ? + REDUCE_CHAIN_1 : REDUCE_CHAIN_0; break; case ATH9K_ANT_VARIABLE: return; diff --git a/drivers/net/wireless/ath/ath9k/phy.h b/drivers/net/wireless/ath/ath9k/phy.h index c70f530642f6..dfda6f444648 100644 --- a/drivers/net/wireless/ath/ath9k/phy.h +++ b/drivers/net/wireless/ath/ath9k/phy.h @@ -185,6 +185,9 @@ bool ath9k_hw_init_rf(struct ath_hw *ah, #define AR_PHY_PLL_CTL_44_2133 0xeb #define AR_PHY_PLL_CTL_40_2133 0xea +#define AR_PHY_SPECTRAL_SCAN 0x9912 +#define AR_PHY_SPECTRAL_SCAN_ENABLE 0x1 + #define AR_PHY_RX_DELAY 0x9914 #define AR_PHY_SEARCH_START_DELAY 0x9918 #define AR_PHY_RX_DELAY_DELAY 0x00003FFF @@ -309,7 +312,25 @@ bool ath9k_hw_init_rf(struct ath_hw *ah, #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_MULTICHAIN_GAIN_CTL 0x99ac +#define AR_PHY_9285_ANT_DIV_CTL_ALL 0x7f000000 +#define AR_PHY_9285_ANT_DIV_CTL 0x01000000 +#define AR_PHY_9285_ANT_DIV_CTL_S 24 +#define AR_PHY_9285_ANT_DIV_ALT_LNACONF 0x06000000 +#define AR_PHY_9285_ANT_DIV_ALT_LNACONF_S 25 +#define AR_PHY_9285_ANT_DIV_MAIN_LNACONF 0x18000000 +#define AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S 27 +#define AR_PHY_9285_ANT_DIV_ALT_GAINTB 0x20000000 +#define AR_PHY_9285_ANT_DIV_ALT_GAINTB_S 29 +#define AR_PHY_9285_ANT_DIV_MAIN_GAINTB 0x40000000 +#define AR_PHY_9285_ANT_DIV_MAIN_GAINTB_S 30 +#define AR_PHY_9285_ANT_DIV_LNA1 2 +#define AR_PHY_9285_ANT_DIV_LNA2 1 +#define AR_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2 3 +#define AR_PHY_9285_ANT_DIV_LNA1_MINUS_LNA2 0 +#define AR_PHY_9285_ANT_DIV_GAINTB_0 0 +#define AR_PHY_9285_ANT_DIV_GAINTB_1 1 #define AR_PHY_EXT_CCA0 0x99b8 #define AR_PHY_EXT_CCA0_THRESH62 0x000000FF @@ -375,6 +396,7 @@ bool ath9k_hw_init_rf(struct ath_hw *ah, #define AR_PHY_CHAN_INFO_GAIN 0x9CFC #define AR_PHY_MODE 0xA200 +#define AR_PHY_MODE_ASYNCFIFO 0x80 #define AR_PHY_MODE_AR2133 0x08 #define AR_PHY_MODE_AR5111 0x00 #define AR_PHY_MODE_AR5112 0x08 @@ -397,6 +419,7 @@ bool ath9k_hw_init_rf(struct ath_hw *ah, #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_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV_S 13 #define AR_PHY_GAIN_2GHZ 0xA20C #define AR_PHY_GAIN_2GHZ_RXTX_MARGIN 0x00FC0000 @@ -467,11 +490,18 @@ bool ath9k_hw_init_rf(struct ath_hw *ah, #define AR_PHY_TX_PWRCTRL9 0xa27C #define AR_PHY_TX_DESIRED_SCALE_CCK 0x00007C00 #define AR_PHY_TX_DESIRED_SCALE_CCK_S 10 +#define AR_PHY_TX_PWRCTRL9_RES_DC_REMOVAL 0x80000000 +#define AR_PHY_TX_PWRCTRL9_RES_DC_REMOVAL_S 31 #define AR_PHY_TX_GAIN_TBL1 0xa300 #define AR_PHY_TX_GAIN 0x0007F000 #define AR_PHY_TX_GAIN_S 12 +#define AR_PHY_CH0_TX_PWRCTRL11 0xa398 +#define AR_PHY_CH1_TX_PWRCTRL11 0xb398 +#define AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP 0x0000FC00 +#define AR_PHY_TX_PWRCTRL_OLPC_TEMP_COMP_S 10 + #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 diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index ba06e78b2f50..16a271787b85 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -22,133 +22,132 @@ static const struct ath_rate_table ar5416_11na_ratetable = { { { VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ 5400, 0x0b, 0x00, 12, - 0, 2, 1, 0, 0, 0, 0, 0 }, + 0, 0, 0, 0, 0, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ 7800, 0x0f, 0x00, 18, - 0, 3, 1, 1, 1, 1, 1, 0 }, + 0, 1, 1, 1, 1, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ 10000, 0x0a, 0x00, 24, - 2, 4, 2, 2, 2, 2, 2, 0 }, + 2, 2, 2, 2, 2, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ 13900, 0x0e, 0x00, 36, - 2, 6, 2, 3, 3, 3, 3, 0 }, + 2, 3, 3, 3, 3, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ 17300, 0x09, 0x00, 48, - 4, 10, 3, 4, 4, 4, 4, 0 }, + 4, 4, 4, 4, 4, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ 23000, 0x0d, 0x00, 72, - 4, 14, 3, 5, 5, 5, 5, 0 }, + 4, 5, 5, 5, 5, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ 27400, 0x08, 0x00, 96, - 4, 20, 3, 6, 6, 6, 6, 0 }, + 4, 6, 6, 6, 6, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ 29300, 0x0c, 0x00, 108, - 4, 23, 3, 7, 7, 7, 7, 0 }, - { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */ + 4, 7, 7, 7, 7, 0 }, + { VALID_2040, VALID_2040, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */ 6400, 0x80, 0x00, 0, - 0, 2, 3, 8, 24, 8, 24, 3216 }, + 0, 8, 24, 8, 24, 3216 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */ 12700, 0x81, 0x00, 1, - 2, 4, 3, 9, 25, 9, 25, 6434 }, + 2, 9, 25, 9, 25, 6434 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */ 18800, 0x82, 0x00, 2, - 2, 6, 3, 10, 26, 10, 26, 9650 }, + 2, 10, 26, 10, 26, 9650 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */ 25000, 0x83, 0x00, 3, - 4, 10, 3, 11, 27, 11, 27, 12868 }, + 4, 11, 27, 11, 27, 12868 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */ 36700, 0x84, 0x00, 4, - 4, 14, 3, 12, 28, 12, 28, 19304 }, + 4, 12, 28, 12, 28, 19304 }, { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */ 48100, 0x85, 0x00, 5, - 4, 20, 3, 13, 29, 13, 29, 25740 }, + 4, 13, 29, 13, 29, 25740 }, { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */ 53500, 0x86, 0x00, 6, - 4, 23, 3, 14, 30, 14, 30, 28956 }, + 4, 14, 30, 14, 30, 28956 }, { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */ 59000, 0x87, 0x00, 7, - 4, 25, 3, 15, 31, 15, 32, 32180 }, + 4, 15, 31, 15, 32, 32180 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */ 12700, 0x88, 0x00, - 8, 0, 2, 3, 16, 33, 16, 33, 6430 }, + 8, 3, 16, 33, 16, 33, 6430 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */ 24800, 0x89, 0x00, 9, - 2, 4, 3, 17, 34, 17, 34, 12860 }, + 2, 17, 34, 17, 34, 12860 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */ 36600, 0x8a, 0x00, 10, - 2, 6, 3, 18, 35, 18, 35, 19300 }, + 2, 18, 35, 18, 35, 19300 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */ 48100, 0x8b, 0x00, 11, - 4, 10, 3, 19, 36, 19, 36, 25736 }, + 4, 19, 36, 19, 36, 25736 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */ 69500, 0x8c, 0x00, 12, - 4, 14, 3, 20, 37, 20, 37, 38600 }, + 4, 20, 37, 20, 37, 38600 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */ 89500, 0x8d, 0x00, 13, - 4, 20, 3, 21, 38, 21, 38, 51472 }, + 4, 21, 38, 21, 38, 51472 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */ 98900, 0x8e, 0x00, 14, - 4, 23, 3, 22, 39, 22, 39, 57890 }, + 4, 22, 39, 22, 39, 57890 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */ 108300, 0x8f, 0x00, 15, - 4, 25, 3, 23, 40, 23, 41, 64320 }, + 4, 23, 40, 23, 41, 64320 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */ 13200, 0x80, 0x00, 0, - 0, 2, 3, 8, 24, 24, 24, 6684 }, + 0, 8, 24, 24, 24, 6684 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */ 25900, 0x81, 0x00, 1, - 2, 4, 3, 9, 25, 25, 25, 13368 }, + 2, 9, 25, 25, 25, 13368 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */ 38600, 0x82, 0x00, 2, - 2, 6, 3, 10, 26, 26, 26, 20052 }, + 2, 10, 26, 26, 26, 20052 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */ 49800, 0x83, 0x00, 3, - 4, 10, 3, 11, 27, 27, 27, 26738 }, + 4, 11, 27, 27, 27, 26738 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */ 72200, 0x84, 0x00, 4, - 4, 14, 3, 12, 28, 28, 28, 40104 }, + 4, 12, 28, 28, 28, 40104 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */ 92900, 0x85, 0x00, 5, - 4, 20, 3, 13, 29, 29, 29, 53476 }, + 4, 13, 29, 29, 29, 53476 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 121500, /* 121.5 Mb */ 102700, 0x86, 0x00, 6, - 4, 23, 3, 14, 30, 30, 30, 60156 }, + 4, 14, 30, 30, 30, 60156 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */ 112000, 0x87, 0x00, 7, - 4, 25, 3, 15, 31, 32, 32, 66840 }, + 4, 15, 31, 32, 32, 66840 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */ 122000, 0x87, 0x00, 7, - 4, 25, 3, 15, 31, 32, 32, 74200 }, + 4, 15, 31, 32, 32, 74200 }, { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */ 25800, 0x88, 0x00, 8, - 0, 2, 3, 16, 33, 33, 33, 13360 }, + 0, 16, 33, 33, 33, 13360 }, { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */ 49800, 0x89, 0x00, 9, - 2, 4, 3, 17, 34, 34, 34, 26720 }, + 2, 17, 34, 34, 34, 26720 }, { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */ 71900, 0x8a, 0x00, 10, - 2, 6, 3, 18, 35, 35, 35, 40080 }, + 2, 18, 35, 35, 35, 40080 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */ 92500, 0x8b, 0x00, 11, - 4, 10, 3, 19, 36, 36, 36, 53440 }, + 4, 19, 36, 36, 36, 53440 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */ 130300, 0x8c, 0x00, 12, - 4, 14, 3, 20, 37, 37, 37, 80160 }, + 4, 20, 37, 37, 37, 80160 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */ 162800, 0x8d, 0x00, 13, - 4, 20, 3, 21, 38, 38, 38, 106880 }, + 4, 21, 38, 38, 38, 106880 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */ 178200, 0x8e, 0x00, 14, - 4, 23, 3, 22, 39, 39, 39, 120240 }, + 4, 22, 39, 39, 39, 120240 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */ 192100, 0x8f, 0x00, 15, - 4, 25, 3, 23, 40, 41, 41, 133600 }, + 4, 23, 40, 41, 41, 133600 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */ 207000, 0x8f, 0x00, 15, - 4, 25, 3, 23, 40, 41, 41, 148400 }, + 4, 23, 40, 41, 41, 148400 }, }, 50, /* probe interval */ - 50, /* rssi reduce interval */ WLAN_RC_HT_FLAG, /* Phy rates allowed initially */ }; @@ -160,145 +159,144 @@ static const struct ath_rate_table ar5416_11ng_ratetable = { { { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ 900, 0x1b, 0x00, 2, - 0, 0, 1, 0, 0, 0, 0, 0 }, + 0, 0, 0, 0, 0, 0 }, { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */ 1900, 0x1a, 0x04, 4, - 1, 1, 1, 1, 1, 1, 1, 0 }, + 1, 1, 1, 1, 1, 0 }, { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */ 4900, 0x19, 0x04, 11, - 2, 2, 2, 2, 2, 2, 2, 0 }, + 2, 2, 2, 2, 2, 0 }, { VALID_ALL, VALID_ALL, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */ 8100, 0x18, 0x04, 22, - 3, 3, 2, 3, 3, 3, 3, 0 }, + 3, 3, 3, 3, 3, 0 }, { INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ 5400, 0x0b, 0x00, 12, - 4, 2, 1, 4, 4, 4, 4, 0 }, + 4, 4, 4, 4, 4, 0 }, { INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ 7800, 0x0f, 0x00, 18, - 4, 3, 1, 5, 5, 5, 5, 0 }, + 4, 5, 5, 5, 5, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ 10100, 0x0a, 0x00, 24, - 6, 4, 1, 6, 6, 6, 6, 0 }, + 6, 6, 6, 6, 6, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ 14100, 0x0e, 0x00, 36, - 6, 6, 2, 7, 7, 7, 7, 0 }, + 6, 7, 7, 7, 7, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ 17700, 0x09, 0x00, 48, - 8, 10, 3, 8, 8, 8, 8, 0 }, + 8, 8, 8, 8, 8, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ 23700, 0x0d, 0x00, 72, - 8, 14, 3, 9, 9, 9, 9, 0 }, + 8, 9, 9, 9, 9, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ 27400, 0x08, 0x00, 96, - 8, 20, 3, 10, 10, 10, 10, 0 }, + 8, 10, 10, 10, 10, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ 30900, 0x0c, 0x00, 108, - 8, 23, 3, 11, 11, 11, 11, 0 }, + 8, 11, 11, 11, 11, 0 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_SS, 6500, /* 6.5 Mb */ 6400, 0x80, 0x00, 0, - 4, 2, 3, 12, 28, 12, 28, 3216 }, + 4, 12, 28, 12, 28, 3216 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 13000, /* 13 Mb */ 12700, 0x81, 0x00, 1, - 6, 4, 3, 13, 29, 13, 29, 6434 }, + 6, 13, 29, 13, 29, 6434 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 19500, /* 19.5 Mb */ 18800, 0x82, 0x00, 2, - 6, 6, 3, 14, 30, 14, 30, 9650 }, + 6, 14, 30, 14, 30, 9650 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 26000, /* 26 Mb */ 25000, 0x83, 0x00, 3, - 8, 10, 3, 15, 31, 15, 31, 12868 }, + 8, 15, 31, 15, 31, 12868 }, { VALID_20, VALID_20, WLAN_RC_PHY_HT_20_SS, 39000, /* 39 Mb */ 36700, 0x84, 0x00, 4, - 8, 14, 3, 16, 32, 16, 32, 19304 }, + 8, 16, 32, 16, 32, 19304 }, { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 52000, /* 52 Mb */ 48100, 0x85, 0x00, 5, - 8, 20, 3, 17, 33, 17, 33, 25740 }, + 8, 17, 33, 17, 33, 25740 }, { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 58500, /* 58.5 Mb */ 53500, 0x86, 0x00, 6, - 8, 23, 3, 18, 34, 18, 34, 28956 }, + 8, 18, 34, 18, 34, 28956 }, { INVALID, VALID_20, WLAN_RC_PHY_HT_20_SS, 65000, /* 65 Mb */ 59000, 0x87, 0x00, 7, - 8, 25, 3, 19, 35, 19, 36, 32180 }, + 8, 19, 35, 19, 36, 32180 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 13000, /* 13 Mb */ 12700, 0x88, 0x00, 8, - 4, 2, 3, 20, 37, 20, 37, 6430 }, + 4, 20, 37, 20, 37, 6430 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 26000, /* 26 Mb */ 24800, 0x89, 0x00, 9, - 6, 4, 3, 21, 38, 21, 38, 12860 }, + 6, 21, 38, 21, 38, 12860 }, { INVALID, INVALID, WLAN_RC_PHY_HT_20_DS, 39000, /* 39 Mb */ 36600, 0x8a, 0x00, 10, - 6, 6, 3, 22, 39, 22, 39, 19300 }, + 6, 22, 39, 22, 39, 19300 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 52000, /* 52 Mb */ 48100, 0x8b, 0x00, 11, - 8, 10, 3, 23, 40, 23, 40, 25736 }, + 8, 23, 40, 23, 40, 25736 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 78000, /* 78 Mb */ 69500, 0x8c, 0x00, 12, - 8, 14, 3, 24, 41, 24, 41, 38600 }, + 8, 24, 41, 24, 41, 38600 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 104000, /* 104 Mb */ 89500, 0x8d, 0x00, 13, - 8, 20, 3, 25, 42, 25, 42, 51472 }, + 8, 25, 42, 25, 42, 51472 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 117000, /* 117 Mb */ 98900, 0x8e, 0x00, 14, - 8, 23, 3, 26, 43, 26, 44, 57890 }, + 8, 26, 43, 26, 44, 57890 }, { VALID_20, INVALID, WLAN_RC_PHY_HT_20_DS, 130000, /* 130 Mb */ 108300, 0x8f, 0x00, 15, - 8, 25, 3, 27, 44, 27, 45, 64320 }, + 8, 27, 44, 27, 45, 64320 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 13500, /* 13.5 Mb */ 13200, 0x80, 0x00, 0, - 8, 2, 3, 12, 28, 28, 28, 6684 }, + 8, 12, 28, 28, 28, 6684 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 27500, /* 27.0 Mb */ 25900, 0x81, 0x00, 1, - 8, 4, 3, 13, 29, 29, 29, 13368 }, + 8, 13, 29, 29, 29, 13368 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 40500, /* 40.5 Mb */ 38600, 0x82, 0x00, 2, - 8, 6, 3, 14, 30, 30, 30, 20052 }, + 8, 14, 30, 30, 30, 20052 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 54000, /* 54 Mb */ 49800, 0x83, 0x00, 3, - 8, 10, 3, 15, 31, 31, 31, 26738 }, + 8, 15, 31, 31, 31, 26738 }, { VALID_40, VALID_40, WLAN_RC_PHY_HT_40_SS, 81500, /* 81 Mb */ 72200, 0x84, 0x00, 4, - 8, 14, 3, 16, 32, 32, 32, 40104 }, + 8, 16, 32, 32, 32, 40104 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 108000, /* 108 Mb */ 92900, 0x85, 0x00, 5, - 8, 20, 3, 17, 33, 33, 33, 53476 }, + 8, 17, 33, 33, 33, 53476 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 121500, /* 121.5 Mb */ 102700, 0x86, 0x00, 6, - 8, 23, 3, 18, 34, 34, 34, 60156 }, + 8, 18, 34, 34, 34, 60156 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS, 135000, /* 135 Mb */ 112000, 0x87, 0x00, 7, - 8, 23, 3, 19, 35, 36, 36, 66840 }, + 8, 19, 35, 36, 36, 66840 }, { INVALID, VALID_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, /* 150 Mb */ 122000, 0x87, 0x00, 7, - 8, 25, 3, 19, 35, 36, 36, 74200 }, + 8, 19, 35, 36, 36, 74200 }, { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 27000, /* 27 Mb */ 25800, 0x88, 0x00, 8, - 8, 2, 3, 20, 37, 37, 37, 13360 }, + 8, 20, 37, 37, 37, 13360 }, { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 54000, /* 54 Mb */ 49800, 0x89, 0x00, 9, - 8, 4, 3, 21, 38, 38, 38, 26720 }, + 8, 21, 38, 38, 38, 26720 }, { INVALID, INVALID, WLAN_RC_PHY_HT_40_DS, 81000, /* 81 Mb */ 71900, 0x8a, 0x00, 10, - 8, 6, 3, 22, 39, 39, 39, 40080 }, + 8, 22, 39, 39, 39, 40080 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 108000, /* 108 Mb */ 92500, 0x8b, 0x00, 11, - 8, 10, 3, 23, 40, 40, 40, 53440 }, + 8, 23, 40, 40, 40, 53440 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 162000, /* 162 Mb */ 130300, 0x8c, 0x00, 12, - 8, 14, 3, 24, 41, 41, 41, 80160 }, + 8, 24, 41, 41, 41, 80160 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 216000, /* 216 Mb */ 162800, 0x8d, 0x00, 13, - 8, 20, 3, 25, 42, 42, 42, 106880 }, + 8, 25, 42, 42, 42, 106880 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 243000, /* 243 Mb */ 178200, 0x8e, 0x00, 14, - 8, 23, 3, 26, 43, 43, 43, 120240 }, + 8, 26, 43, 43, 43, 120240 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS, 270000, /* 270 Mb */ 192100, 0x8f, 0x00, 15, - 8, 23, 3, 27, 44, 45, 45, 133600 }, + 8, 27, 44, 45, 45, 133600 }, { VALID_40, INVALID, WLAN_RC_PHY_HT_40_DS_HGI, 300000, /* 300 Mb */ 207000, 0x8f, 0x00, 15, - 8, 25, 3, 27, 44, 45, 45, 148400 }, + 8, 27, 44, 45, 45, 148400 }, }, 50, /* probe interval */ - 50, /* rssi reduce interval */ WLAN_RC_HT_FLAG, /* Phy rates allowed initially */ }; @@ -307,31 +305,30 @@ static const struct ath_rate_table ar5416_11a_ratetable = { { { VALID, VALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ 5400, 0x0b, 0x00, (0x80|12), - 0, 2, 1, 0, 0 }, + 0, 0, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ 7800, 0x0f, 0x00, 18, - 0, 3, 1, 1, 0 }, + 0, 1, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ 10000, 0x0a, 0x00, (0x80|24), - 2, 4, 2, 2, 0 }, + 2, 2, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ 13900, 0x0e, 0x00, 36, - 2, 6, 2, 3, 0 }, + 2, 3, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ 17300, 0x09, 0x00, (0x80|48), - 4, 10, 3, 4, 0 }, + 4, 4, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ 23000, 0x0d, 0x00, 72, - 4, 14, 3, 5, 0 }, + 4, 5, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ 27400, 0x08, 0x00, 96, - 4, 19, 3, 6, 0 }, + 4, 6, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ 29300, 0x0c, 0x00, 108, - 4, 23, 3, 7, 0 }, + 4, 7, 0 }, }, 50, /* probe interval */ - 50, /* rssi reduce interval */ 0, /* Phy rates allowed initially */ }; @@ -340,64 +337,42 @@ static const struct ath_rate_table ar5416_11g_ratetable = { { { VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ 900, 0x1b, 0x00, 2, - 0, 0, 1, 0, 0 }, + 0, 0, 0 }, { VALID, VALID, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */ 1900, 0x1a, 0x04, 4, - 1, 1, 1, 1, 0 }, + 1, 1, 0 }, { VALID, VALID, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */ 4900, 0x19, 0x04, 11, - 2, 2, 2, 2, 0 }, + 2, 2, 0 }, { VALID, VALID, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */ 8100, 0x18, 0x04, 22, - 3, 3, 2, 3, 0 }, + 3, 3, 0 }, { INVALID, INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ 5400, 0x0b, 0x00, 12, - 4, 2, 1, 4, 0 }, + 4, 4, 0 }, { INVALID, INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ 7800, 0x0f, 0x00, 18, - 4, 3, 1, 5, 0 }, + 4, 5, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ 10000, 0x0a, 0x00, 24, - 6, 4, 1, 6, 0 }, + 6, 6, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ 13900, 0x0e, 0x00, 36, - 6, 6, 2, 7, 0 }, + 6, 7, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ 17300, 0x09, 0x00, 48, - 8, 10, 3, 8, 0 }, + 8, 8, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ 23000, 0x0d, 0x00, 72, - 8, 14, 3, 9, 0 }, + 8, 9, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ 27400, 0x08, 0x00, 96, - 8, 19, 3, 10, 0 }, + 8, 10, 0 }, { VALID, VALID, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ 29300, 0x0c, 0x00, 108, - 8, 23, 3, 11, 0 }, + 8, 11, 0 }, }, 50, /* probe interval */ - 50, /* rssi reduce interval */ - 0, /* Phy rates allowed initially */ -}; - -static const struct ath_rate_table ar5416_11b_ratetable = { - 4, - { - { VALID, VALID, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ - 900, 0x1b, 0x00, (0x80|2), - 0, 0, 1, 0, 0 }, - { VALID, VALID, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */ - 1800, 0x1a, 0x04, (0x80|4), - 1, 1, 1, 1, 0 }, - { VALID, VALID, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */ - 4300, 0x19, 0x04, (0x80|11), - 1, 2, 2, 2, 0 }, - { VALID, VALID, WLAN_RC_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 */ }; @@ -454,13 +429,6 @@ static inline void ath_rc_set_valid_txmask(struct ath_rate_priv *ath_rc_priv, ath_rc_priv->valid_rate_index[index] = valid_tx_rate ? 1 : 0; } -static inline int ath_rc_isvalid_txmask(struct ath_rate_priv *ath_rc_priv, - u8 index) -{ - ASSERT(index <= ath_rc_priv->rate_table_size); - return ath_rc_priv->valid_rate_index[index]; -} - static inline int ath_rc_get_nextvalid_txrate(const struct ath_rate_table *rate_table, struct ath_rate_priv *ath_rc_priv, @@ -495,15 +463,13 @@ static int ath_rc_valid_phyrate(u32 phy, u32 capflag, int ignore_cw) if (!ignore_cw && WLAN_RC_PHY_HT(phy)) if (WLAN_RC_PHY_40(phy) && !(capflag & WLAN_RC_40_FLAG)) return 0; - if (!WLAN_RC_PHY_40(phy) && (capflag & WLAN_RC_40_FLAG)) - return 0; return 1; } static inline int -ath_rc_get_nextlowervalid_txrate(const struct ath_rate_table *rate_table, - struct ath_rate_priv *ath_rc_priv, - u8 cur_valid_txrate, u8 *next_idx) +ath_rc_get_lower_rix(const struct ath_rate_table *rate_table, + struct ath_rate_priv *ath_rc_priv, + u8 cur_valid_txrate, u8 *next_idx) { int8_t i; @@ -629,52 +595,20 @@ static u8 ath_rc_setvalid_htrates(struct ath_rate_priv *ath_rc_priv, return hi; } -static u8 ath_rc_ratefind_ht(struct ath_softc *sc, - struct ath_rate_priv *ath_rc_priv, - const struct ath_rate_table *rate_table, - int *is_probing) +/* Finds the highest rate index we can use */ +static u8 ath_rc_get_highest_rix(struct ath_softc *sc, + struct ath_rate_priv *ath_rc_priv, + const struct ath_rate_table *rate_table, + int *is_probing) { - u32 dt, best_thruput, this_thruput, now_msec; + u32 best_thruput, this_thruput, now_msec; u8 rate, next_rate, best_rate, maxindex, minindex; - int8_t rssi_last, rssi_reduce = 0, index = 0; - - *is_probing = 0; - - rssi_last = median(ath_rc_priv->rssi_last, - ath_rc_priv->rssi_last_prev, - ath_rc_priv->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 - */ + int8_t index = 0; now_msec = jiffies_to_msecs(jiffies); - dt = now_msec - ath_rc_priv->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) - */ - + *is_probing = 0; best_thruput = 0; maxindex = ath_rc_priv->max_valid_rate-1; - minindex = 0; best_rate = minindex; @@ -700,7 +634,7 @@ static u8 ath_rc_ratefind_ht(struct ath_softc *sc, * 10-15 and we would be worse off then staying * at the current rate. */ - per_thres = ath_rc_priv->state[rate].per; + per_thres = ath_rc_priv->per[rate]; if (per_thres < 12) per_thres = 12; @@ -714,7 +648,6 @@ static u8 ath_rc_ratefind_ht(struct ath_softc *sc, } rate = best_rate; - ath_rc_priv->rssi_last_lookup = rssi_last; /* * Must check the actual rate (ratekbps) to account for @@ -741,10 +674,18 @@ static u8 ath_rc_ratefind_ht(struct ath_softc *sc, if (rate > (ath_rc_priv->rate_table_size - 1)) rate = ath_rc_priv->rate_table_size - 1; - ASSERT((rate_table->info[rate].valid && - (ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG)) || - (rate_table->info[rate].valid_single_stream && - !(ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG))); + if (rate_table->info[rate].valid && + (ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG)) + return rate; + + if (rate_table->info[rate].valid_single_stream && + !(ath_rc_priv->ht_cap & WLAN_RC_DS_FLAG)); + return rate; + + /* This should not happen */ + WARN_ON(1); + + rate = ath_rc_priv->valid_rate_index[0]; return rate; } @@ -796,7 +737,6 @@ static void ath_rc_rate_set_rtscts(struct ath_softc *sc, * just CTS. Note that this is only done for OFDM/HT unicast frames. */ if ((sc->sc_flags & SC_OP_PROTECT_ENABLE) && - !(tx_info->flags & IEEE80211_TX_CTL_NO_ACK) && (rate_table->info[rix].phy == WLAN_RC_PHY_OFDM || WLAN_RC_PHY_HT(rate_table->info[rix].phy))) { rates[0].flags |= IEEE80211_TX_RC_USE_CTS_PROTECT; @@ -806,50 +746,37 @@ static void ath_rc_rate_set_rtscts(struct ath_softc *sc, tx_info->control.rts_cts_rate_idx = cix; } -static u8 ath_rc_rate_getidx(struct ath_softc *sc, - struct ath_rate_priv *ath_rc_priv, - const struct ath_rate_table *rate_table, - u8 rix, u16 stepdown, - u16 min_rate) -{ - u32 j; - u8 nextindex = 0; - - if (min_rate) { - for (j = RATE_TABLE_SIZE; j > 0; j--) { - if (ath_rc_get_nextlowervalid_txrate(rate_table, - ath_rc_priv, rix, &nextindex)) - rix = nextindex; - else - break; - } - } else { - for (j = stepdown; j > 0; j--) { - if (ath_rc_get_nextlowervalid_txrate(rate_table, - ath_rc_priv, rix, &nextindex)) - rix = nextindex; - else - break; - } - } - return rix; -} - -static void ath_rc_ratefind(struct ath_softc *sc, - struct ath_rate_priv *ath_rc_priv, - struct ieee80211_tx_rate_control *txrc) +static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, + struct ieee80211_tx_rate_control *txrc) { + struct ath_softc *sc = priv; + struct ath_rate_priv *ath_rc_priv = priv_sta; const struct ath_rate_table *rate_table; struct sk_buff *skb = txrc->skb; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_rate *rates = tx_info->control.rates; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; __le16 fc = hdr->frame_control; - u8 try_per_rate = 0, i = 0, rix, nrix; + u8 try_per_rate, i = 0, rix, nrix; int is_probe = 0; + if (rate_control_send_low(sta, priv_sta, txrc)) + return; + + /* + * For Multi Rate Retry we use a different number of + * retry attempt counts. This ends up looking like this: + * + * MRR[0] = 2 + * MRR[1] = 2 + * MRR[2] = 2 + * MRR[3] = 4 + * + */ + try_per_rate = sc->hw->max_rate_tries; + rate_table = sc->cur_rate_table; - rix = ath_rc_ratefind_ht(sc, ath_rc_priv, rate_table, &is_probe); + rix = ath_rc_get_highest_rix(sc, ath_rc_priv, rate_table, &is_probe); nrix = rix; if (is_probe) { @@ -858,18 +785,15 @@ static void ath_rc_ratefind(struct ath_softc *sc, ath_rc_rate_set_series(rate_table, &rates[i++], txrc, 1, nrix, 0); - try_per_rate = (ATH_11N_TXMAXTRY/4); /* 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, 0); + ath_rc_get_lower_rix(rate_table, ath_rc_priv, rix, &nrix); ath_rc_rate_set_series(rate_table, &rates[i++], txrc, try_per_rate, nrix, 0); tx_info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; } else { - try_per_rate = (ATH_11N_TXMAXTRY/4); /* Set the choosen rate. No RTS for first series entry. */ ath_rc_rate_set_series(rate_table, &rates[i++], txrc, try_per_rate, nrix, 0); @@ -877,18 +801,14 @@ static void ath_rc_ratefind(struct ath_softc *sc, /* Fill in the other rates for multirate retry */ for ( ; i < 4; i++) { - u8 try_num; - u8 min_rate; + /* Use twice the number of tries for the last MRR segment. */ + if (i + 1 == 4) + try_per_rate = 4; - try_num = ((i + 1) == 4) ? - ATH_11N_TXMAXTRY - (try_per_rate * i) : try_per_rate ; - min_rate = (((i + 1) == 4) && 0); - - nrix = ath_rc_rate_getidx(sc, ath_rc_priv, - rate_table, nrix, 1, min_rate); + ath_rc_get_lower_rix(rate_table, ath_rc_priv, rix, &nrix); /* All other rates in the series have RTS enabled */ ath_rc_rate_set_series(rate_table, &rates[i], txrc, - try_num, nrix, 1); + try_per_rate, nrix, 1); } /* @@ -925,9 +845,8 @@ static void ath_rc_ratefind(struct ath_softc *sc, * * FIXME: Fix duration */ - if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK) && - (ieee80211_has_morefrags(fc) || - (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG))) { + if (ieee80211_has_morefrags(fc) || + (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)) { rates[1].count = rates[2].count = rates[3].count = 0; rates[1].idx = rates[2].idx = rates[3].idx = 0; rates[0].count = ATH_TXMAXTRY; @@ -960,13 +879,13 @@ static bool ath_rc_update_per(struct ath_softc *sc, 100 * 9 / 10 }; - last_per = ath_rc_priv->state[tx_rate].per; + last_per = ath_rc_priv->per[tx_rate]; if (xretries) { if (xretries == 1) { - ath_rc_priv->state[tx_rate].per += 30; - if (ath_rc_priv->state[tx_rate].per > 100) - ath_rc_priv->state[tx_rate].per = 100; + ath_rc_priv->per[tx_rate] += 30; + if (ath_rc_priv->per[tx_rate] > 100) + ath_rc_priv->per[tx_rate] = 100; } else { /* xretries == 2 */ count = ARRAY_SIZE(nretry_to_per_lookup); @@ -974,7 +893,7 @@ static bool ath_rc_update_per(struct ath_softc *sc, retries = count - 1; /* new_PER = 7/8*old_PER + 1/8*(currentPER) */ - ath_rc_priv->state[tx_rate].per = + ath_rc_priv->per[tx_rate] = (u8)(last_per - (last_per >> 3) + (100 >> 3)); } @@ -1010,18 +929,14 @@ static bool ath_rc_update_per(struct ath_softc *sc, n_frames = tx_info_priv->n_frames * (retries + 1); cur_per = (100 * n_bad_frames / n_frames) >> 3; new_per = (u8)(last_per - (last_per >> 3) + cur_per); - ath_rc_priv->state[tx_rate].per = new_per; + ath_rc_priv->per[tx_rate] = new_per; } } else { - ath_rc_priv->state[tx_rate].per = + ath_rc_priv->per[tx_rate] = (u8)(last_per - (last_per >> 3) + (nretry_to_per_lookup[retries] >> 3)); } - ath_rc_priv->rssi_last_prev2 = ath_rc_priv->rssi_last_prev; - ath_rc_priv->rssi_last_prev = ath_rc_priv->rssi_last; - ath_rc_priv->rssi_last = tx_info_priv->tx.ts_rssi; - ath_rc_priv->rssi_time = now_msec; /* * If we got at most one retry then increase the max rate if @@ -1045,8 +960,8 @@ static bool ath_rc_update_per(struct ath_softc *sc, ath_rc_priv->probe_rate; probe_rate = ath_rc_priv->probe_rate; - if (ath_rc_priv->state[probe_rate].per > 30) - ath_rc_priv->state[probe_rate].per = 20; + if (ath_rc_priv->per[probe_rate] > 30) + ath_rc_priv->per[probe_rate] = 20; ath_rc_priv->probe_rate = 0; @@ -1065,18 +980,9 @@ static bool ath_rc_update_per(struct ath_softc *sc, /* * Don't update anything. We don't know if * this was because of collisions or poor signal. - * - * Later: if rssi_ack is close to - * ath_rc_priv->state[txRate].rssi_thres and we see lots - * of retries, then we could increase - * ath_rc_priv->state[txRate].rssi_thres. */ ath_rc_priv->hw_maxretry_pktcnt = 0; } else { - int32_t rssi_ackAvg; - int8_t rssi_thres; - int8_t rssi_ack_vmin; - /* * It worked with no retries. First ignore bogus (small) * rssi_ack values. @@ -1086,43 +992,9 @@ static bool ath_rc_update_per(struct ath_softc *sc, ath_rc_priv->hw_maxretry_pktcnt++; } - if (tx_info_priv->tx.ts_rssi < - rate_table->info[tx_rate].rssi_ack_validmin) - goto exit; - - /* Average the rssi */ - if (tx_rate != ath_rc_priv->rssi_sum_rate) { - ath_rc_priv->rssi_sum_rate = tx_rate; - ath_rc_priv->rssi_sum = - ath_rc_priv->rssi_sum_cnt = 0; - } - - ath_rc_priv->rssi_sum += tx_info_priv->tx.ts_rssi; - ath_rc_priv->rssi_sum_cnt++; - - if (ath_rc_priv->rssi_sum_cnt < 4) - goto exit; - - rssi_ackAvg = - (ath_rc_priv->rssi_sum + 2) / 4; - rssi_thres = - ath_rc_priv->state[tx_rate].rssi_thres; - rssi_ack_vmin = - rate_table->info[tx_rate].rssi_ack_validmin; - - ath_rc_priv->rssi_sum = - ath_rc_priv->rssi_sum_cnt = 0; - - /* Now reduce the current rssi threshold */ - if ((rssi_ackAvg < rssi_thres + 2) && - (rssi_thres > rssi_ack_vmin)) { - ath_rc_priv->state[tx_rate].rssi_thres--; - } - - state_change = true; } } -exit: + return state_change; } @@ -1134,11 +1006,6 @@ static void ath_rc_update_ht(struct ath_softc *sc, struct ath_tx_info_priv *tx_info_priv, int tx_rate, int xretries, int retries) { -#define CHK_RSSI(rate) \ - ((ath_rc_priv->state[(rate)].rssi_thres + \ - rate_table->info[(rate)].rssi_ack_deltamin) > \ - ath_rc_priv->state[(rate)+1].rssi_thres) - u32 now_msec = jiffies_to_msecs(jiffies); int rate; u8 last_per; @@ -1149,14 +1016,7 @@ static void ath_rc_update_ht(struct ath_softc *sc, if ((tx_rate < 0) || (tx_rate > rate_table->rate_cnt)) return; - /* To compensate for some imbalance between ctrl and ext. channel */ - - if (WLAN_RC_PHY_40(rate_table->info[tx_rate].phy)) - tx_info_priv->tx.ts_rssi = - tx_info_priv->tx.ts_rssi < 3 ? 0 : - tx_info_priv->tx.ts_rssi - 3; - - last_per = ath_rc_priv->state[tx_rate].per; + last_per = ath_rc_priv->per[tx_rate]; /* Update PER first */ state_change = ath_rc_update_per(sc, rate_table, ath_rc_priv, @@ -1167,114 +1027,52 @@ static void ath_rc_update_ht(struct ath_softc *sc, * If this rate looks bad (high PER) then stop using it for * a while (except if we are probing). */ - if (ath_rc_priv->state[tx_rate].per >= 55 && tx_rate > 0 && + if (ath_rc_priv->per[tx_rate] >= 55 && tx_rate > 0 && rate_table->info[tx_rate].ratekbps <= rate_table->info[ath_rc_priv->rate_max_phy].ratekbps) { - ath_rc_get_nextlowervalid_txrate(rate_table, ath_rc_priv, - (u8)tx_rate, &ath_rc_priv->rate_max_phy); + ath_rc_get_lower_rix(rate_table, ath_rc_priv, + (u8)tx_rate, &ath_rc_priv->rate_max_phy); /* Don't probe for a little while. */ ath_rc_priv->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 < size - 1; rate++) { - if (rate_table->info[rate+1].phy != - rate_table->info[tx_rate].phy) - break; - - if (CHK_RSSI(rate)) { - ath_rc_priv->state[rate+1].rssi_thres = - ath_rc_priv->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 (CHK_RSSI(rate)) { - if (ath_rc_priv->state[rate+1].rssi_thres < - rate_table->info[rate].rssi_ack_deltamin) - ath_rc_priv->state[rate].rssi_thres = 0; - else { - ath_rc_priv->state[rate].rssi_thres = - ath_rc_priv->state[rate+1].rssi_thres - - rate_table->info[rate].rssi_ack_deltamin; - } - - if (ath_rc_priv->state[rate].rssi_thres < - rate_table->info[rate].rssi_ack_validmin) { - ath_rc_priv->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 (ath_rc_priv->state[tx_rate].per < last_per) { + if (ath_rc_priv->per[tx_rate] < last_per) { for (rate = tx_rate - 1; rate >= 0; rate--) { - if (rate_table->info[rate].phy != - rate_table->info[tx_rate].phy) - break; - if (ath_rc_priv->state[rate].per > - ath_rc_priv->state[rate+1].per) { - ath_rc_priv->state[rate].per = - ath_rc_priv->state[rate+1].per; + if (ath_rc_priv->per[rate] > + ath_rc_priv->per[rate+1]) { + ath_rc_priv->per[rate] = + ath_rc_priv->per[rate+1]; } } } /* Maintain monotonicity for rates above the current rate */ for (rate = tx_rate; rate < size - 1; rate++) { - if (ath_rc_priv->state[rate+1].per < - ath_rc_priv->state[rate].per) - ath_rc_priv->state[rate+1].per = - ath_rc_priv->state[rate].per; - } - - /* Every so often, we reduce the thresholds and - * PER (different for CCK and OFDM). */ - if (now_msec - ath_rc_priv->rssi_down_time >= - rate_table->rssi_reduce_interval) { - - for (rate = 0; rate < size; rate++) { - if (ath_rc_priv->state[rate].rssi_thres > - rate_table->info[rate].rssi_ack_validmin) - ath_rc_priv->state[rate].rssi_thres -= 1; - } - ath_rc_priv->rssi_down_time = now_msec; + if (ath_rc_priv->per[rate+1] < + ath_rc_priv->per[rate]) + ath_rc_priv->per[rate+1] = + ath_rc_priv->per[rate]; } /* Every so often, we reduce the thresholds * and PER (different for CCK and OFDM). */ if (now_msec - ath_rc_priv->per_down_time >= - rate_table->rssi_reduce_interval) { + rate_table->probe_interval) { for (rate = 0; rate < size; rate++) { - ath_rc_priv->state[rate].per = - 7 * ath_rc_priv->state[rate].per / 8; + ath_rc_priv->per[rate] = + 7 * ath_rc_priv->per[rate] / 8; } ath_rc_priv->per_down_time = now_msec; } ath_debug_stat_retries(sc, tx_rate, xretries, retries, - ath_rc_priv->state[tx_rate].per); + ath_rc_priv->per[tx_rate]); -#undef CHK_RSSI } static int ath_rc_get_rateindex(const struct ath_rate_table *rate_table, @@ -1410,9 +1208,7 @@ static void ath_rc_init(struct ath_softc *sc, /* Initialize thresholds according to the global rate table */ for (i = 0 ; i < ath_rc_priv->rate_table_size; i++) { - ath_rc_priv->state[i].rssi_thres = - rate_table->info[i].rssi_ack_validmin; - ath_rc_priv->state[i].per = 0; + ath_rc_priv->per[i] = 0; } /* Determine the valid rates */ @@ -1521,7 +1317,7 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, /* * 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 + * Adjust the long retry as if the frame was tried hw->max_rate_tries * times. This affects how ratectrl updates PER for the failed rate. */ if (tx_info_priv->tx.ts_flags & @@ -1536,7 +1332,7 @@ static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, tx_status = 1; ath_rc_tx_status(sc, ath_rc_priv, tx_info, final_ts_idx, tx_status, - (is_underrun) ? ATH_11N_TXMAXTRY : + (is_underrun) ? sc->hw->max_rate_tries : tx_info_priv->tx.ts_longretry); /* Check if aggregation has to be enabled for this tid */ @@ -1560,31 +1356,6 @@ exit: kfree(tx_info_priv); } -static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, - struct ieee80211_tx_rate_control *txrc) -{ - struct ieee80211_supported_band *sband = txrc->sband; - struct sk_buff *skb = txrc->skb; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ath_softc *sc = priv; - struct ath_rate_priv *ath_rc_priv = priv_sta; - __le16 fc = hdr->frame_control; - - /* lowest rate for management and NO_ACK frames */ - if (!ieee80211_is_data(fc) || - tx_info->flags & IEEE80211_TX_CTL_NO_ACK || !sta) { - tx_info->control.rates[0].idx = rate_lowest_index(sband, sta); - tx_info->control.rates[0].count = - (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) ? - 1 : ATH_MGT_TXMAXTRY; - return; - } - - /* Find tx rate for unicast frames */ - ath_rc_ratefind(sc, ath_rc_priv, txrc); -} - static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta) { @@ -1697,7 +1468,6 @@ static void *ath_rate_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp return NULL; } - rate_priv->rssi_down_time = jiffies_to_msecs(jiffies); rate_priv->tx_triglevel_max = sc->sc_ah->caps.tx_triglevel_max; return rate_priv; @@ -1725,8 +1495,6 @@ static struct rate_control_ops ath_rate_ops = { void ath_rate_attach(struct ath_softc *sc) { - 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] = diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h index e3abd76103fd..fa21a628ddd0 100644 --- a/drivers/net/wireless/ath/ath9k/rc.h +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -112,8 +112,6 @@ struct ath_rate_table { u8 short_preamble; u8 dot11rate; u8 ctrl_rate; - int8_t rssi_ack_validmin; - int8_t rssi_ack_deltamin; u8 base_index; u8 cw40index; u8 sgi_index; @@ -121,15 +119,9 @@ struct ath_rate_table { u32 max_4ms_framelen; } info[RATE_TABLE_SIZE]; u32 probe_interval; - u32 rssi_reduce_interval; u8 initial_ratemax; }; -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_rateset { u8 rs_nrates; u8 rs_rates[ATH_RATE_MAX]; @@ -138,22 +130,14 @@ struct ath_rateset { /** * struct ath_rate_priv - Rate Control priv data * @state: RC state - * @rssi_last: last ACK rssi - * @rssi_last_lookup: last ACK rssi used for lookup - * @rssi_last_prev: previous last ACK rssi - * @rssi_last_prev2: 2nd previous last ACK rssi - * @rssi_sum_cnt: count of rssi_sum for averaging - * @rssi_sum_rate: rate that we are averaging - * @rssi_sum: running sum of rssi for averaging * @probe_rate: rate we are probing at - * @rssi_time: msec timestamp for last ack rssi - * @rssi_down_time: msec timestamp for last down step * @probe_time: msec timestamp for last probe * @hw_maxretry_pktcnt: num of packets since we got HW max retry error * @max_valid_rate: maximum number of valid rate * @per_down_time: msec timestamp for last PER down step * @valid_phy_ratecnt: valid rate count * @rate_max_phy: phy index for the max rate + * @per: PER for every valid rate in % * @probe_interval: interval for ratectrl to probe for other rates * @prev_data_rix: rate idx of last data frame * @ht_cap: HT capabilities @@ -161,13 +145,6 @@ struct ath_rateset { * @neg_ht_rates: Negotiated HT rates */ struct ath_rate_priv { - int8_t rssi_last; - int8_t rssi_last_lookup; - int8_t rssi_last_prev; - int8_t rssi_last_prev2; - int32_t rssi_sum_cnt; - int32_t rssi_sum_rate; - int32_t rssi_sum; u8 rate_table_size; u8 probe_rate; u8 hw_maxretry_pktcnt; @@ -177,14 +154,12 @@ struct ath_rate_priv { u8 valid_phy_ratecnt[WLAN_RC_PHY_MAX]; u8 valid_phy_rateidx[WLAN_RC_PHY_MAX][RATE_TABLE_SIZE]; u8 rate_max_phy; - u32 rssi_time; - u32 rssi_down_time; + u8 per[RATE_TABLE_SIZE]; u32 probe_time; u32 per_down_time; u32 probe_interval; u32 prev_data_rix; u32 tx_triglevel_max; - struct ath_tx_ratectrl_state state[RATE_TABLE_SIZE]; struct ath_rateset neg_rates; struct ath_rateset neg_ht_rates; struct ath_rate_softc *asc; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index cece1c4c6bda..52e62daad3ce 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -100,38 +100,6 @@ static u64 ath_extend_tsf(struct ath_softc *sc, u32 rstamp) return (tsf & ~0x7fff) | rstamp; } -static struct sk_buff *ath_rxbuf_alloc(struct ath_softc *sc, u32 len, gfp_t gfp_mask) -{ - 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. - */ - - /* Note: the kernel can allocate a value greater than - * what we ask it to give us. We really only need 4 KB as that - * is this hardware supports and in fact we need at least 3849 - * as that is the MAX AMSDU size this hardware supports. - * Unfortunately this means we may get 8 KB here from the - * kernel... and that is actually what is observed on some - * systems :( */ - skb = __dev_alloc_skb(len + sc->cachelsz - 1, gfp_mask); - if (skb != NULL) { - off = ((unsigned long) skb->data) % sc->cachelsz; - if (off != 0) - skb_reserve(skb, sc->cachelsz - off); - } else { - DPRINTF(sc, ATH_DBG_FATAL, - "skbuff alloc of size %u failed\n", len); - return NULL; - } - - return skb; -} - /* * For Decrypt or Demic errors, we only mark packet status here and always push * up the frame up to let mac80211 handle the actual error case, be it no @@ -145,6 +113,10 @@ static int ath_rx_prepare(struct sk_buff *skb, struct ath_desc *ds, u8 ratecode; __le16 fc; struct ieee80211_hw *hw; + struct ieee80211_sta *sta; + struct ath_node *an; + int last_rssi = ATH_RSSI_DUMMY_MARKER; + hdr = (struct ieee80211_hdr *)skb->data; fc = hdr->frame_control; @@ -229,17 +201,61 @@ static int ath_rx_prepare(struct sk_buff *skb, struct ath_desc *ds, } } + rcu_read_lock(); + sta = ieee80211_find_sta(sc->hw, hdr->addr2); + if (sta) { + an = (struct ath_node *) sta->drv_priv; + if (ds->ds_rxstat.rs_rssi != ATH9K_RSSI_BAD && + !ds->ds_rxstat.rs_moreaggr) + ATH_RSSI_LPF(an->last_rssi, ds->ds_rxstat.rs_rssi); + last_rssi = an->last_rssi; + } + rcu_read_unlock(); + + if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) + ds->ds_rxstat.rs_rssi = ATH_EP_RND(last_rssi, + ATH_RSSI_EP_MULTIPLIER); + if (ds->ds_rxstat.rs_rssi < 0) + ds->ds_rxstat.rs_rssi = 0; + else if (ds->ds_rxstat.rs_rssi > 127) + ds->ds_rxstat.rs_rssi = 127; + + /* Update Beacon RSSI, this is used by ANI. */ + if (ieee80211_is_beacon(fc)) + sc->sc_ah->stats.avgbrssi = ds->ds_rxstat.rs_rssi; + rx_status->mactime = ath_extend_tsf(sc, ds->ds_rxstat.rs_tstamp); rx_status->band = hw->conf.channel->band; rx_status->freq = hw->conf.channel->center_freq; rx_status->noise = sc->ani.noise_floor; - rx_status->signal = rx_status->noise + ds->ds_rxstat.rs_rssi; + rx_status->signal = ATH_DEFAULT_NOISE_FLOOR + ds->ds_rxstat.rs_rssi; rx_status->antenna = ds->ds_rxstat.rs_antenna; - /* at 45 you will be able to use MCS 15 reliably. A more elaborate - * scheme can be used here but it requires tables of SNR/throughput for - * each possible mode used. */ - rx_status->qual = ds->ds_rxstat.rs_rssi * 100 / 45; + /* + * Theory for reporting quality: + * + * At a hardware RSSI of 45 you will be able to use MCS 7 reliably. + * At a hardware RSSI of 45 you will be able to use MCS 15 reliably. + * At a hardware RSSI of 35 you should be able use 54 Mbps reliably. + * + * MCS 7 is the highets MCS index usable by a 1-stream device. + * MCS 15 is the highest MCS index usable by a 2-stream device. + * + * All ath9k devices are either 1-stream or 2-stream. + * + * How many bars you see is derived from the qual reporting. + * + * A more elaborate scheme can be used here but it requires tables + * of SNR/throughput for each possible mode used. For the MCS table + * you can refer to the wireless wiki: + * + * http://wireless.kernel.org/en/developers/Documentation/ieee80211/802.11n + * + */ + if (conf_is_ht(&hw->conf)) + rx_status->qual = ds->ds_rxstat.rs_rssi * 100 / 45; + else + rx_status->qual = ds->ds_rxstat.rs_rssi * 100 / 35; /* rssi can be more than 45 though, anything above that * should be considered at 100% */ @@ -288,10 +304,10 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) spin_lock_init(&sc->rx.rxbuflock); sc->rx.bufsize = roundup(IEEE80211_MAX_MPDU_LEN, - min(sc->cachelsz, (u16)64)); + min(sc->common.cachelsz, (u16)64)); DPRINTF(sc, ATH_DBG_CONFIG, "cachelsz %u rxbufsize %u\n", - sc->cachelsz, sc->rx.bufsize); + sc->common.cachelsz, sc->rx.bufsize); /* Initialize rx descriptors */ @@ -304,7 +320,7 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) } list_for_each_entry(bf, &sc->rx.rxbuf, list) { - skb = ath_rxbuf_alloc(sc, sc->rx.bufsize, GFP_KERNEL); + skb = ath_rxbuf_alloc(&sc->common, sc->rx.bufsize, GFP_KERNEL); if (skb == NULL) { error = -ENOMEM; goto err; @@ -404,15 +420,15 @@ u32 ath_calcrxfilter(struct ath_softc *sc) else rfilt |= ATH9K_RX_FILTER_BEACON; - /* If in HOSTAP mode, want to enable reception of PSPOLL frames */ - if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) + if (sc->rx.rxfilter & FIF_PSPOLL) rfilt |= ATH9K_RX_FILTER_PSPOLL; - if (sc->sec_wiphy) { + if (sc->sec_wiphy || (sc->rx.rxfilter & FIF_OTHER_BSS)) { /* TODO: only needed if more than one BSSID is in use in * station/adhoc mode */ - /* TODO: for older chips, may need to add ATH9K_RX_FILTER_PROM - */ + /* The following may also be needed for other older chips */ + if (sc->sc_ah->hw_version.macVersion == AR_SREV_VERSION_9160) + rfilt |= ATH9K_RX_FILTER_PROM; rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL; } @@ -505,11 +521,6 @@ static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb) return false; } -static void ath_rx_ps_back_to_sleep(struct ath_softc *sc) -{ - sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB); -} - static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) { struct ieee80211_mgmt *mgmt; @@ -521,6 +532,8 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) if (memcmp(sc->curbssid, mgmt->bssid, ETH_ALEN) != 0) return; /* not from our current AP */ + sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON; + if (sc->sc_flags & SC_OP_BEACON_SYNC) { sc->sc_flags &= ~SC_OP_BEACON_SYNC; DPRINTF(sc, ATH_DBG_PS, "Reconfigure Beacon timers based on " @@ -528,14 +541,6 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) ath_beacon_config(sc, NULL); } - if (!(sc->hw->conf.flags & IEEE80211_CONF_PS)) { - /* We are not in PS mode anymore; remain awake */ - DPRINTF(sc, ATH_DBG_PS, "Not in PS mode anymore, remain " - "awake\n"); - sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | SC_OP_WAIT_FOR_CAB); - return; - } - if (ath_beacon_dtim_pending_cab(skb)) { /* * Remain awake waiting for buffered broadcast/multicast @@ -556,11 +561,9 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) * fails to send a frame indicating that all CAB frames have * been delivered. */ + sc->sc_flags &= ~SC_OP_WAIT_FOR_CAB; DPRINTF(sc, ATH_DBG_PS, "PS wait for CAB frames timed out\n"); } - - /* No more broadcast/multicast frames to be received at this point. */ - ath_rx_ps_back_to_sleep(sc); } static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) @@ -578,13 +581,13 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) ieee80211_is_action(hdr->frame_control)) && is_multicast_ether_addr(hdr->addr1) && !ieee80211_has_moredata(hdr->frame_control)) { - DPRINTF(sc, ATH_DBG_PS, "All PS CAB frames received, back to " - "sleep\n"); /* * No more broadcast/multicast frames to be received at this * point. */ - ath_rx_ps_back_to_sleep(sc); + sc->sc_flags &= ~SC_OP_WAIT_FOR_CAB; + DPRINTF(sc, ATH_DBG_PS, "All PS CAB frames received, back to " + "sleep\n"); } else if ((sc->sc_flags & SC_OP_WAIT_FOR_PSPOLL_DATA) && !is_multicast_ether_addr(hdr->addr1) && !ieee80211_has_morefrags(hdr->frame_control)) { @@ -619,13 +622,18 @@ static void ath_rx_send_to_mac80211(struct ath_softc *sc, struct sk_buff *skb, if (aphy == NULL) continue; nskb = skb_copy(skb, GFP_ATOMIC); - if (nskb) - __ieee80211_rx(aphy->hw, nskb, rx_status); + if (nskb) { + memcpy(IEEE80211_SKB_RXCB(nskb), rx_status, + sizeof(*rx_status)); + ieee80211_rx(aphy->hw, nskb); + } } - __ieee80211_rx(sc->hw, skb, rx_status); + memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status)); + ieee80211_rx(sc->hw, skb); } else { /* Deliver unicast frames based on receiver address */ - __ieee80211_rx(ath_get_virt_hw(sc, hdr), skb, rx_status); + memcpy(IEEE80211_SKB_RXCB(skb), rx_status, sizeof(*rx_status)); + ieee80211_rx(ath_get_virt_hw(sc, hdr), skb); } } @@ -738,7 +746,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) /* Ensure we always have an skb to requeue once we are done * processing the current buffer's skb */ - requeue_skb = ath_rxbuf_alloc(sc, sc->rx.bufsize, GFP_ATOMIC); + requeue_skb = ath_rxbuf_alloc(&sc->common, sc->rx.bufsize, GFP_ATOMIC); /* If there is no memory we ignore the current RX'd frame, * tell hardware it can give us a new frame using the old @@ -753,7 +761,6 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) DMA_FROM_DEVICE); skb_put(skb, ds->ds_rxstat.rs_datalen); - skb->protocol = cpu_to_be16(ETH_P_CONTROL); /* see if any padding is done by the hw and remove it */ hdr = (struct ieee80211_hdr *)skb->data; diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 52605246679f..3ddb243f0000 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -234,7 +234,15 @@ #define AR_IMR_S5 0x00b8 #define AR_IMR_S5_TIM_TIMER 0x00000010 #define AR_IMR_S5_DTIM_TIMER 0x00000020 - +#define AR_ISR_S5_GENTIMER_TRIG 0x0000FF80 +#define AR_ISR_S5_GENTIMER_TRIG_S 0 +#define AR_ISR_S5_GENTIMER_THRESH 0xFF800000 +#define AR_ISR_S5_GENTIMER_THRESH_S 16 +#define AR_ISR_S5_S 0x00d8 +#define AR_IMR_S5_GENTIMER_TRIG 0x0000FF80 +#define AR_IMR_S5_GENTIMER_TRIG_S 0 +#define AR_IMR_S5_GENTIMER_THRESH 0xFF800000 +#define AR_IMR_S5_GENTIMER_THRESH_S 16 #define AR_IMR 0x00a0 #define AR_IMR_RXOK 0x00000001 @@ -574,6 +582,7 @@ #define AR_D_GBL_IFS_SIFS 0x1030 #define AR_D_GBL_IFS_SIFS_M 0x0000FFFF +#define AR_D_GBL_IFS_SIFS_ASYNC_FIFO_DUR 0x000003AB #define AR_D_GBL_IFS_SIFS_RESV0 0xFFFFFFFF #define AR_D_TXBLK_BASE 0x1038 @@ -589,10 +598,12 @@ #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_SLOT_ASYNC_FIFO_DUR 0x00000420 #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_EIFS_ASYNC_FIFO_DUR 0x0000A5EB #define AR_D_GBL_IFS_MISC 0x10f0 #define AR_D_GBL_IFS_MISC_LFSR_SLICE_SEL 0x00000007 @@ -738,6 +749,13 @@ #define AR_SREV_REVISION_9285_10 0 #define AR_SREV_REVISION_9285_11 1 #define AR_SREV_REVISION_9285_12 2 +#define AR_SREV_VERSION_9287 0x180 +#define AR_SREV_REVISION_9287_10 0 +#define AR_SREV_REVISION_9287_11 1 +#define AR_SREV_REVISION_9287_12 2 +#define AR_SREV_VERSION_9271 0x140 +#define AR_SREV_REVISION_9271_10 0 +#define AR_SREV_REVISION_9271_11 1 #define AR_SREV_5416(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \ @@ -794,6 +812,36 @@ (AR_SREV_9285(ah) && ((_ah)->hw_version.macRev >= \ AR_SREV_REVISION_9285_12))) +#define AR_SREV_9287(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287)) +#define AR_SREV_9287_10_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9287)) +#define AR_SREV_9287_10(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9287_10)) +#define AR_SREV_9287_11(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9287_11)) +#define AR_SREV_9287_11_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion > AR_SREV_VERSION_9287) || \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287) && \ + ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9287_11))) +#define AR_SREV_9287_12(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9287_12)) +#define AR_SREV_9287_12_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion > AR_SREV_VERSION_9287) || \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9287) && \ + ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9287_12))) +#define AR_SREV_9271(_ah) \ + (((_ah))->hw_version.macVersion == AR_SREV_VERSION_9271) +#define AR_SREV_9271_10(_ah) \ + (AR_SREV_9271(_ah) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9271_10)) +#define AR_SREV_9271_11(_ah) \ + (AR_SREV_9271(_ah) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9271_11)) + #define AR_RADIO_SREV_MAJOR 0xf0 #define AR_RAD5133_SREV_MAJOR 0xc0 #define AR_RAD2133_SREV_MAJOR 0xd0 @@ -809,6 +857,9 @@ #define AR_AHB_PAGE_SIZE_1K 0x00000000 #define AR_AHB_PAGE_SIZE_2K 0x00000008 #define AR_AHB_PAGE_SIZE_4K 0x00000010 +#define AR_AHB_CUSTOM_BURST_EN 0x000000C0 +#define AR_AHB_CUSTOM_BURST_EN_S 6 +#define AR_AHB_CUSTOM_BURST_ASYNC_FIFO_VAL 3 #define AR_INTR_RTC_IRQ 0x00000001 #define AR_INTR_MAC_IRQ 0x00000002 @@ -885,6 +936,7 @@ enum { #define AR_NUM_GPIO 14 #define AR928X_NUM_GPIO 10 #define AR9285_NUM_GPIO 12 +#define AR9287_NUM_GPIO 11 #define AR_GPIO_IN_OUT 0x4048 #define AR_GPIO_IN_VAL 0x0FFFC000 @@ -893,6 +945,8 @@ enum { #define AR928X_GPIO_IN_VAL_S 10 #define AR9285_GPIO_IN_VAL 0x00FFF000 #define AR9285_GPIO_IN_VAL_S 12 +#define AR9287_GPIO_IN_VAL 0x003FF800 +#define AR9287_GPIO_IN_VAL_S 11 #define AR_GPIO_OE_OUT 0x404c #define AR_GPIO_OE_OUT_DRV 0x3 @@ -916,6 +970,8 @@ enum { #define AR_GPIO_INPUT_EN_VAL_RFSILENT_DEF_S 7 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB 0x00001000 #define AR_GPIO_INPUT_EN_VAL_BT_ACTIVE_BB_S 12 +#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB 0x00001000 +#define AR_GPIO_INPUT_EN_VAL_BT_PRIORITY_BB_S 1 #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 @@ -924,6 +980,8 @@ enum { #define AR_GPIO_INPUT_MUX1 0x4058 #define AR_GPIO_INPUT_MUX1_BT_ACTIVE 0x000f0000 #define AR_GPIO_INPUT_MUX1_BT_ACTIVE_S 16 +#define AR_GPIO_INPUT_MUX1_BT_PRIORITY 0x00000f00 +#define AR_GPIO_INPUT_MUX1_BT_PRIORITY_S 8 #define AR_GPIO_INPUT_MUX2 0x405c #define AR_GPIO_INPUT_MUX2_CLK25 0x0000000f @@ -949,6 +1007,8 @@ enum { #define AR_OBS 0x4080 +#define AR_GPIO_PDPU 0x4088 + #define AR_PCIE_MSI 0x4094 #define AR_PCIE_MSI_ENABLE 0x00000001 @@ -1115,12 +1175,32 @@ enum { #define AR9285_AN_RF2G4_DB2_4 0x00003800 #define AR9285_AN_RF2G4_DB2_4_S 11 +/* AR9271 : 0x7828, 0x782c different setting from AR9285 */ +#define AR9271_AN_RF2G3_OB_cck 0x001C0000 +#define AR9271_AN_RF2G3_OB_cck_S 18 +#define AR9271_AN_RF2G3_OB_psk 0x00038000 +#define AR9271_AN_RF2G3_OB_psk_S 15 +#define AR9271_AN_RF2G3_OB_qam 0x00007000 +#define AR9271_AN_RF2G3_OB_qam_S 12 + +#define AR9271_AN_RF2G3_DB_1 0x00E00000 +#define AR9271_AN_RF2G3_DB_1_S 21 + +#define AR9271_AN_RF2G3_CCOMP 0xFFF +#define AR9271_AN_RF2G3_CCOMP_S 0 + +#define AR9271_AN_RF2G4_DB_2 0xE0000000 +#define AR9271_AN_RF2G4_DB_2_S 29 + #define AR9285_AN_RF2G6 0x7834 #define AR9285_AN_RF2G6_CCOMP 0x00007800 #define AR9285_AN_RF2G6_CCOMP_S 11 #define AR9285_AN_RF2G6_OFFS 0x03f00000 #define AR9285_AN_RF2G6_OFFS_S 20 +#define AR9271_AN_RF2G6_OFFS 0x07f00000 +#define AR9271_AN_RF2G6_OFFS_S 20 + #define AR9285_AN_RF2G7 0x7838 #define AR9285_AN_RF2G7_PWDDB 0x00000002 #define AR9285_AN_RF2G7_PWDDB_S 1 @@ -1154,6 +1234,38 @@ enum { #define AR9285_AN_TOP4 0x7870 #define AR9285_AN_TOP4_DEFAULT 0x10142c00 +#define AR9287_AN_RF2G3_CH0 0x7808 +#define AR9287_AN_RF2G3_CH1 0x785c +#define AR9287_AN_RF2G3_DB1 0xE0000000 +#define AR9287_AN_RF2G3_DB1_S 29 +#define AR9287_AN_RF2G3_DB2 0x1C000000 +#define AR9287_AN_RF2G3_DB2_S 26 +#define AR9287_AN_RF2G3_OB_CCK 0x03800000 +#define AR9287_AN_RF2G3_OB_CCK_S 23 +#define AR9287_AN_RF2G3_OB_PSK 0x00700000 +#define AR9287_AN_RF2G3_OB_PSK_S 20 +#define AR9287_AN_RF2G3_OB_QAM 0x000E0000 +#define AR9287_AN_RF2G3_OB_QAM_S 17 +#define AR9287_AN_RF2G3_OB_PAL_OFF 0x0001C000 +#define AR9287_AN_RF2G3_OB_PAL_OFF_S 14 + +#define AR9287_AN_TXPC0 0x7898 +#define AR9287_AN_TXPC0_TXPCMODE 0x0000C000 +#define AR9287_AN_TXPC0_TXPCMODE_S 14 +#define AR9287_AN_TXPC0_TXPCMODE_NORMAL 0 +#define AR9287_AN_TXPC0_TXPCMODE_TEST 1 +#define AR9287_AN_TXPC0_TXPCMODE_TEMPSENSE 2 +#define AR9287_AN_TXPC0_TXPCMODE_ATBTEST 3 + +#define AR9287_AN_TOP2 0x78b4 +#define AR9287_AN_TOP2_XPABIAS_LVL 0xC0000000 +#define AR9287_AN_TOP2_XPABIAS_LVL_S 30 + +/* AR9271 specific stuff */ +#define AR9271_RESET_POWER_DOWN_CONTROL 0x50044 +#define AR9271_RADIO_RF_RST 0x20 +#define AR9271_GATE_MAC_CTL 0x4000 + #define AR_STA_ID0 0x8000 #define AR_STA_ID1 0x8004 #define AR_STA_ID1_SADH_MASK 0x0000FFFF @@ -1188,6 +1300,7 @@ enum { #define AR_TIME_OUT_ACK_S 0 #define AR_TIME_OUT_CTS 0x3FFF0000 #define AR_TIME_OUT_CTS_S 16 +#define AR_TIME_OUT_ACK_CTS_ASYNC_FIFO_DUR 0x16001D56 #define AR_RSSI_THR 0x8018 #define AR_RSSI_THR_MASK 0x000000FF @@ -1203,6 +1316,7 @@ enum { #define AR_USEC_TX_LAT_S 14 #define AR_USEC_RX_LAT 0x1F800000 #define AR_USEC_RX_LAT_S 23 +#define AR_USEC_ASYNC_FIFO_DUR 0x12e00074 #define AR_RESET_TSF 0x8020 #define AR_RESET_TSF_ONCE 0x01000000 @@ -1328,6 +1442,7 @@ enum { #define AR_QUIET1_NEXT_QUIET_M 0x0000ffff #define AR_QUIET1_QUIET_ENABLE 0x00010000 #define AR_QUIET1_QUIET_ACK_CTS_ENABLE 0x00020000 +#define AR_QUIET1_QUIET_ACK_CTS_ENABLE_S 17 #define AR_QUIET2 0x8100 #define AR_QUIET2_QUIET_PERIOD_S 0 #define AR_QUIET2_QUIET_PERIOD_M 0x0000ffff @@ -1373,6 +1488,8 @@ enum { #define AR_PCU_CLEAR_VMF 0x01000000 #define AR_PCU_CLEAR_BA_VALID 0x04000000 +#define AR_PCU_BT_ANT_PREVENT_RX 0x00100000 +#define AR_PCU_BT_ANT_PREVENT_RX_S 20 #define AR_FILT_OFDM 0x8124 #define AR_FILT_OFDM_COUNT 0x00FFFFFF @@ -1400,6 +1517,46 @@ enum { #define AR_PHY_ERR_3_COUNT 0x00FFFFFF #define AR_PHY_ERR_MASK_3 0x816c +#define AR_BT_COEX_MODE 0x8170 +#define AR_BT_TIME_EXTEND 0x000000ff +#define AR_BT_TIME_EXTEND_S 0 +#define AR_BT_TXSTATE_EXTEND 0x00000100 +#define AR_BT_TXSTATE_EXTEND_S 8 +#define AR_BT_TX_FRAME_EXTEND 0x00000200 +#define AR_BT_TX_FRAME_EXTEND_S 9 +#define AR_BT_MODE 0x00000c00 +#define AR_BT_MODE_S 10 +#define AR_BT_QUIET 0x00001000 +#define AR_BT_QUIET_S 12 +#define AR_BT_QCU_THRESH 0x0001e000 +#define AR_BT_QCU_THRESH_S 13 +#define AR_BT_RX_CLEAR_POLARITY 0x00020000 +#define AR_BT_RX_CLEAR_POLARITY_S 17 +#define AR_BT_PRIORITY_TIME 0x00fc0000 +#define AR_BT_PRIORITY_TIME_S 18 +#define AR_BT_FIRST_SLOT_TIME 0xff000000 +#define AR_BT_FIRST_SLOT_TIME_S 24 + +#define AR_BT_COEX_WEIGHT 0x8174 +#define AR_BT_COEX_WGHT 0xff55 +#define AR_STOMP_ALL_WLAN_WGHT 0xffcc +#define AR_STOMP_LOW_WLAN_WGHT 0xaaa8 +#define AR_STOMP_NONE_WLAN_WGHT 0xaa00 +#define AR_BTCOEX_BT_WGHT 0x0000ffff +#define AR_BTCOEX_BT_WGHT_S 0 +#define AR_BTCOEX_WL_WGHT 0xffff0000 +#define AR_BTCOEX_WL_WGHT_S 16 + +#define AR_BT_COEX_MODE2 0x817c +#define AR_BT_BCN_MISS_THRESH 0x000000ff +#define AR_BT_BCN_MISS_THRESH_S 0 +#define AR_BT_BCN_MISS_CNT 0x0000ff00 +#define AR_BT_BCN_MISS_CNT_S 8 +#define AR_BT_HOLD_RX_CLEAR 0x00010000 +#define AR_BT_HOLD_RX_CLEAR_S 16 +#define AR_BT_DISABLE_BT_ANT 0x00100000 +#define AR_BT_DISABLE_BT_ANT_S 20 + #define AR_TXSIFS 0x81d0 #define AR_TXSIFS_TIME 0x000000FF #define AR_TXSIFS_TX_LATENCY 0x00000F00 @@ -1416,7 +1573,10 @@ enum { #define AR_TXOP_8_11 0x81f8 #define AR_TXOP_12_15 0x81fc - +#define AR_NEXT_NDP2_TIMER 0x8180 +#define AR_FIRST_NDP_TIMER 7 +#define AR_NDP2_PERIOD 0x81a0 +#define AR_NDP2_TIMER_MODE 0x81c0 #define AR_NEXT_TBTT_TIMER 0x8200 #define AR_NEXT_DMA_BEACON_ALERT 0x8204 #define AR_NEXT_SWBA 0x8208 @@ -1468,6 +1628,10 @@ enum { #define AR_SLP_MIB_CLEAR 0x00000001 #define AR_SLP_MIB_PENDING 0x00000002 +#define AR_MAC_PCU_LOGIC_ANALYZER 0x8264 +#define AR_MAC_PCU_LOGIC_ANALYZER_DISBUG20768 0x20000000 + + #define AR_2040_MODE 0x8318 #define AR_2040_JOINED_RX_CLEAR 0x00000001 @@ -1485,6 +1649,39 @@ enum { #define AR_PCU_MISC_MODE2_MGMT_CRYPTO_ENABLE 0x00000002 #define AR_PCU_MISC_MODE2_NO_CRYPTO_FOR_NON_DATA_PKT 0x00000004 +#define AR_PCU_MISC_MODE2_RESERVED 0x00000038 +#define AR_PCU_MISC_MODE2_ADHOC_MCAST_KEYID_ENABLE 0x00000040 +#define AR_PCU_MISC_MODE2_CFP_IGNORE 0x00000080 +#define AR_PCU_MISC_MODE2_MGMT_QOS 0x0000FF00 +#define AR_PCU_MISC_MODE2_MGMT_QOS_S 8 +#define AR_PCU_MISC_MODE2_ENABLE_LOAD_NAV_BEACON_DURATION 0x00010000 +#define AR_PCU_MISC_MODE2_ENABLE_AGGWEP 0x00020000 +#define AR_PCU_MISC_MODE2_HWWAR1 0x00100000 +#define AR_PCU_MISC_MODE2_HWWAR2 0x02000000 +#define AR_PCU_MISC_MODE2_RESERVED2 0xFFFE0000 + +#define AR_MAC_PCU_ASYNC_FIFO_REG3 0x8358 +#define AR_MAC_PCU_ASYNC_FIFO_REG3_DATAPATH_SEL 0x00000400 +#define AR_MAC_PCU_ASYNC_FIFO_REG3_SOFT_RESET 0x80000000 + + +#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_AES_MUTE_MASK1_SEQ_S 0 +#define AR_AES_MUTE_MASK1_FC_MGMT 0xFFFF0000 +#define AR_AES_MUTE_MASK1_FC_MGMT_S 16 + +#define AR_RATE_DURATION_0 0x8700 +#define AR_RATE_DURATION_31 0x87CC +#define AR_RATE_DURATION_32 0x8780 +#define AR_RATE_DURATION(_n) (AR_RATE_DURATION_0 + ((_n)<<2)) + + #define AR_KEYTABLE_0 0x8800 #define AR_KEYTABLE(_n) (AR_KEYTABLE_0 + ((_n)*32)) #define AR_KEY_CACHE_SIZE 128 diff --git a/drivers/net/wireless/ath/ath9k/virtual.c b/drivers/net/wireless/ath/ath9k/virtual.c index 1ff429b027d7..19b88f8177fd 100644 --- a/drivers/net/wireless/ath/ath9k/virtual.c +++ b/drivers/net/wireless/ath/ath9k/virtual.c @@ -351,7 +351,7 @@ void ath9k_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) * Drop from tasklet to work to allow mutex for channel * change. */ - queue_work(aphy->sc->hw->workqueue, + ieee80211_queue_work(aphy->sc->hw, &aphy->sc->chan_work); } } @@ -367,7 +367,7 @@ static void ath9k_mark_paused(struct ath_wiphy *aphy) struct ath_softc *sc = aphy->sc; aphy->state = ATH_WIPHY_PAUSED; if (!__ath9k_wiphy_pausing(sc)) - queue_work(sc->hw->workqueue, &sc->chan_work); + ieee80211_queue_work(sc->hw, &sc->chan_work); } static void ath9k_pause_iter(void *data, u8 *mac, struct ieee80211_vif *vif) @@ -521,7 +521,7 @@ int ath9k_wiphy_select(struct ath_wiphy *aphy) spin_unlock_bh(&sc->wiphy_lock); ath_radio_disable(sc); ath_radio_enable(sc); - queue_work(aphy->sc->hw->workqueue, + ieee80211_queue_work(aphy->sc->hw, &aphy->sc->chan_work); return -EBUSY; /* previous select still in progress */ } @@ -541,7 +541,7 @@ int ath9k_wiphy_select(struct ath_wiphy *aphy) if (now) { /* Ready to request channel change immediately */ - queue_work(aphy->sc->hw->workqueue, &aphy->sc->chan_work); + ieee80211_queue_work(aphy->sc->hw, &aphy->sc->chan_work); } /* @@ -648,8 +648,9 @@ try_again: "change\n"); } - queue_delayed_work(sc->hw->workqueue, &sc->wiphy_work, - sc->wiphy_scheduler_int); + ieee80211_queue_delayed_work(sc->hw, + &sc->wiphy_work, + sc->wiphy_scheduler_int); } void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int) @@ -657,6 +658,23 @@ void ath9k_wiphy_set_scheduler(struct ath_softc *sc, unsigned int msec_int) cancel_delayed_work_sync(&sc->wiphy_work); sc->wiphy_scheduler_int = msecs_to_jiffies(msec_int); if (sc->wiphy_scheduler_int) - queue_delayed_work(sc->hw->workqueue, &sc->wiphy_work, - sc->wiphy_scheduler_int); + ieee80211_queue_delayed_work(sc->hw, &sc->wiphy_work, + sc->wiphy_scheduler_int); +} + +/* caller must hold wiphy_lock */ +bool ath9k_all_wiphys_idle(struct ath_softc *sc) +{ + unsigned int i; + if (sc->pri_wiphy->state != ATH_WIPHY_INACTIVE) { + return false; + } + for (i = 0; i < sc->num_sec_wiphy; i++) { + struct ath_wiphy *aphy = sc->sec_wiphy[i]; + if (!aphy) + continue; + if (aphy->state != ATH_WIPHY_INACTIVE) + return false; + } + return true; } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 4ccf48e396df..42551a48c8ac 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -59,6 +59,7 @@ static void ath_tx_send_ht_normal(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, struct list_head *bf_head); static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, + struct ath_txq *txq, struct list_head *bf_q, int txok, int sendbar); static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, @@ -73,18 +74,6 @@ static void ath_tx_rc_status(struct ath_buf *bf, struct ath_desc *ds, /* Aggregation logic */ /*********************/ -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->state & AGGR_ADDBA_COMPLETE || - tid->state & AGGR_ADDBA_PROGRESS) - return 1; - else - return 0; -} - static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid) { struct ath_atx_ac *ac = tid->ac; @@ -224,7 +213,7 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq, ath_tx_update_baw(sc, tid, bf->bf_seqno); spin_unlock(&txq->axq_lock); - ath_tx_complete_buf(sc, bf, &bf_head, 0, 0); + ath_tx_complete_buf(sc, bf, txq, &bf_head, 0, 0); spin_lock(&txq->axq_lock); } @@ -232,13 +221,15 @@ static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq, tid->baw_tail = tid->baw_head; } -static void ath_tx_set_retry(struct ath_softc *sc, struct ath_buf *bf) +static void ath_tx_set_retry(struct ath_softc *sc, struct ath_txq *txq, + struct ath_buf *bf) { struct sk_buff *skb; struct ieee80211_hdr *hdr; bf->bf_state.bf_type |= BUF_RETRY; bf->bf_retries++; + TX_STAT_INC(txq->axq_qnum, a_retries); skb = bf->bf_mpdu; hdr = (struct ieee80211_hdr *)skb->data; @@ -250,7 +241,10 @@ static struct ath_buf* ath_clone_txbuf(struct ath_softc *sc, struct ath_buf *bf) struct ath_buf *tbf; spin_lock_bh(&sc->tx.txbuflock); - ASSERT(!list_empty((&sc->tx.txbuf))); + if (WARN_ON(list_empty(&sc->tx.txbuf))) { + spin_unlock_bh(&sc->tx.txbuflock); + return NULL; + } tbf = list_first_entry(&sc->tx.txbuf, struct ath_buf, list); list_del(&tbf->list); spin_unlock_bh(&sc->tx.txbuflock); @@ -337,7 +331,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, if (!(tid->state & AGGR_CLEANUP) && ds->ds_txstat.ts_flags != ATH9K_TX_SW_ABORTED) { if (bf->bf_retries < ATH_MAX_SW_RETRIES) { - ath_tx_set_retry(sc, bf); + ath_tx_set_retry(sc, txq, bf); txpending = 1; } else { bf->bf_state.bf_type |= BUF_XRETRY; @@ -384,13 +378,31 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, ath_tx_rc_status(bf, ds, nbad, txok, false); } - ath_tx_complete_buf(sc, bf, &bf_head, !txfail, sendbar); + ath_tx_complete_buf(sc, bf, txq, &bf_head, !txfail, sendbar); } else { /* retry the un-acked ones */ if (bf->bf_next == NULL && bf_last->bf_stale) { struct ath_buf *tbf; tbf = ath_clone_txbuf(sc, bf_last); + /* + * Update tx baw and complete the frame with + * failed status if we run out of tx buf + */ + if (!tbf) { + spin_lock_bh(&txq->axq_lock); + ath_tx_update_baw(sc, tid, + bf->bf_seqno); + spin_unlock_bh(&txq->axq_lock); + + bf->bf_state.bf_type |= BUF_XRETRY; + ath_tx_rc_status(bf, ds, nbad, + 0, false); + ath_tx_complete_buf(sc, bf, txq, + &bf_head, 0, 0); + break; + } + ath9k_hw_cleartxdesc(sc->sc_ah, tbf->bf_desc); list_add_tail(&tbf->list, &bf_head); } else { @@ -414,7 +426,6 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, if (tid->state & AGGR_CLEANUP) { if (tid->baw_head == tid->baw_tail) { tid->state &= ~AGGR_ADDBA_COMPLETE; - tid->addba_exchangeattempts = 0; tid->state &= ~AGGR_CLEANUP; /* send buffered frames as singles */ @@ -447,7 +458,7 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, struct ieee80211_tx_rate *rates; struct ath_tx_info_priv *tx_info_priv; u32 max_4ms_framelen, frmlen; - u16 aggr_limit, legacy = 0, maxampdu; + u16 aggr_limit, legacy = 0; int i; skb = bf->bf_mpdu; @@ -482,16 +493,20 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, if (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE || legacy) return 0; - aggr_limit = min(max_4ms_framelen, (u32)ATH_AMPDU_LIMIT_DEFAULT); + if (sc->sc_flags & SC_OP_BT_PRIORITY_DETECTED) + aggr_limit = min((max_4ms_framelen * 3) / 8, + (u32)ATH_AMPDU_LIMIT_MAX); + else + aggr_limit = min(max_4ms_framelen, + (u32)ATH_AMPDU_LIMIT_MAX); /* * 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 = tid->an->maxampdu; - if (maxampdu) - aggr_limit = min(aggr_limit, maxampdu); + if (tid->an->maxampdu) + aggr_limit = min(aggr_limit, tid->an->maxampdu); return aggr_limit; } @@ -499,7 +514,6 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, /* * 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_atx_tid *tid, struct ath_buf *bf, u16 frmlen) @@ -507,7 +521,7 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, const struct ath_rate_table *rt = sc->cur_rate_table; struct sk_buff *skb = bf->bf_mpdu; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - u32 nsymbits, nsymbols, mpdudensity; + u32 nsymbits, nsymbols; u16 minlen; u8 rc, flags, rix; int width, half_gi, ndelim, mindelim; @@ -529,14 +543,12 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, * 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 = tid->an->mpdudensity; - - /* + * * If there is no mpdu density restriction, no further calculation * is needed. */ - if (mpdudensity == 0) + + if (tid->an->mpdudensity == 0) return ndelim; rix = tx_info->control.rates[0].idx; @@ -546,9 +558,9 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, half_gi = (flags & IEEE80211_TX_RC_SHORT_GI) ? 1 : 0; if (half_gi) - nsymbols = NUM_SYMBOLS_PER_USEC_HALFGI(mpdudensity); + nsymbols = NUM_SYMBOLS_PER_USEC_HALFGI(tid->an->mpdudensity); else - nsymbols = NUM_SYMBOLS_PER_USEC(mpdudensity); + nsymbols = NUM_SYMBOLS_PER_USEC(tid->an->mpdudensity); if (nsymbols == 0) nsymbols = 1; @@ -565,6 +577,7 @@ static int ath_compute_num_delims(struct ath_softc *sc, struct ath_atx_tid *tid, } static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, + struct ath_txq *txq, struct ath_atx_tid *tid, struct list_head *bf_q) { @@ -629,6 +642,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, bf_prev->bf_desc->ds_link = bf->bf_daddr; } bf_prev = bf; + } while (!list_empty(&tid->buf_q)); bf_first->bf_al = al; @@ -651,7 +665,7 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, INIT_LIST_HEAD(&bf_q); - status = ath_tx_form_aggr(sc, tid, &bf_q); + status = ath_tx_form_aggr(sc, txq, tid, &bf_q); /* * no frames picked up to be aggregated; @@ -682,30 +696,26 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq, txq->axq_aggr_depth++; ath_tx_txqaddbuf(sc, txq, &bf_q); + TX_STAT_INC(txq->axq_qnum, a_aggr); } while (txq->axq_depth < ATH_AGGR_MIN_QDEPTH && status != ATH_AGGR_BAW_CLOSED); } -int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, - u16 tid, u16 *ssn) +void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, + u16 tid, u16 *ssn) { struct ath_atx_tid *txtid; struct ath_node *an; an = (struct ath_node *)sta->drv_priv; - - if (sc->sc_flags & SC_OP_TXAGGR) { - txtid = ATH_AN_2_TID(an, tid); - txtid->state |= AGGR_ADDBA_PROGRESS; - ath_tx_pause_tid(sc, txtid); - *ssn = txtid->seq_start; - } - - return 0; + txtid = ATH_AN_2_TID(an, tid); + txtid->state |= AGGR_ADDBA_PROGRESS; + ath_tx_pause_tid(sc, txtid); + *ssn = txtid->seq_start; } -int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) +void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) { struct ath_node *an = (struct ath_node *)sta->drv_priv; struct ath_atx_tid *txtid = ATH_AN_2_TID(an, tid); @@ -715,12 +725,11 @@ int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) INIT_LIST_HEAD(&bf_head); if (txtid->state & AGGR_CLEANUP) - return 0; + return; if (!(txtid->state & AGGR_ADDBA_COMPLETE)) { txtid->state &= ~AGGR_ADDBA_PROGRESS; - txtid->addba_exchangeattempts = 0; - return 0; + return; } ath_tx_pause_tid(sc, txtid); @@ -739,7 +748,7 @@ int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) } list_move_tail(&bf->list, &bf_head); ath_tx_update_baw(sc, txtid, bf->bf_seqno); - ath_tx_complete_buf(sc, bf, &bf_head, 0, 0); + ath_tx_complete_buf(sc, bf, txq, &bf_head, 0, 0); } spin_unlock_bh(&txq->axq_lock); @@ -747,11 +756,8 @@ int ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) txtid->state |= AGGR_CLEANUP; } else { txtid->state &= ~AGGR_ADDBA_COMPLETE; - txtid->addba_exchangeattempts = 0; ath_tx_flush_tid(sc, txtid); } - - return 0; } void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) @@ -780,14 +786,8 @@ bool ath_tx_aggr_check(struct ath_softc *sc, struct ath_node *an, u8 tidno) txtid = ATH_AN_2_TID(an, tidno); - if (!(txtid->state & AGGR_ADDBA_COMPLETE)) { - if (!(txtid->state & AGGR_ADDBA_PROGRESS) && - (txtid->addba_exchangeattempts < ADDBA_EXCHANGE_ATTEMPTS)) { - txtid->addba_exchangeattempts++; + if (!(txtid->state & (AGGR_ADDBA_COMPLETE | AGGR_ADDBA_PROGRESS))) return true; - } - } - return false; } @@ -870,14 +870,14 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) spin_lock_init(&txq->axq_lock); txq->axq_depth = 0; txq->axq_aggr_depth = 0; - txq->axq_totalqueued = 0; txq->axq_linkbuf = NULL; + txq->axq_tx_inprogress = false; sc->tx.txqsetup |= 1<<qnum; } return &sc->tx.txq[qnum]; } -static int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype) +int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype) { int qnum; @@ -1035,9 +1035,13 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq, bool retry_tx) if (bf_isampdu(bf)) ath_tx_complete_aggr(sc, txq, bf, &bf_head, 0); else - ath_tx_complete_buf(sc, bf, &bf_head, 0, 0); + ath_tx_complete_buf(sc, bf, txq, &bf_head, 0, 0); } + spin_lock_bh(&txq->axq_lock); + txq->axq_tx_inprogress = false; + spin_unlock_bh(&txq->axq_lock); + /* flush any pending frames if aggregation is enabled */ if (sc->sc_flags & SC_OP_TXAGGR) { if (!retry_tx) { @@ -1118,8 +1122,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) if (tid->paused) continue; - if ((txq->axq_depth % 2) == 0) - ath_tx_sched_aggr(sc, txq, tid); + ath_tx_sched_aggr(sc, txq, tid); /* * add tid to round-robin queue if more frames @@ -1183,7 +1186,6 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, 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, @@ -1231,6 +1233,7 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid, bf = list_first_entry(bf_head, struct ath_buf, list); bf->bf_state.bf_type |= BUF_AMPDU; + TX_STAT_INC(txctl->txq->axq_qnum, a_queued); /* * Do not queue to h/w when any of the following conditions is true: @@ -1277,6 +1280,7 @@ static void ath_tx_send_ht_normal(struct ath_softc *sc, struct ath_txq *txq, bf->bf_lastbf = bf; ath_buf_set_rate(sc, bf); ath_tx_txqaddbuf(sc, txq, bf_head); + TX_STAT_INC(txq->axq_qnum, queued); } static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, @@ -1290,6 +1294,7 @@ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, bf->bf_nframes = 1; ath_buf_set_rate(sc, bf); ath_tx_txqaddbuf(sc, txq, bf_head); + TX_STAT_INC(txq->axq_qnum, queued); } static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb) @@ -1636,7 +1641,7 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf, goto tx_done; } - if (ath_aggr_query(sc, an, bf->bf_tidno)) { + if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { /* * Try aggregation if it's a unicast data frame * and the destination is HT capable. @@ -1815,6 +1820,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, } static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, + struct ath_txq *txq, struct list_head *bf_q, int txok, int sendbar) { @@ -1822,7 +1828,6 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, unsigned long flags; int tx_flags = 0; - if (sendbar) tx_flags = ATH_TX_BAR; @@ -1835,6 +1840,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE); ath_tx_complete(sc, skb, tx_flags); + ath_debug_stat_tx(sc, txq, bf); /* * Return the list of ath_buf of this mpdu to free queue @@ -1962,19 +1968,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) if (bf->bf_stale) { bf_held = bf; if (list_is_last(&bf_held->list, &txq->axq_q)) { - txq->axq_link = NULL; - txq->axq_linkbuf = NULL; spin_unlock_bh(&txq->axq_lock); - - /* - * The holding descriptor is the last - * descriptor in queue. It's safe to remove - * the last holding descriptor in BH context. - */ - spin_lock_bh(&sc->tx.txbuflock); - list_move_tail(&bf_held->list, &sc->tx.txbuf); - spin_unlock_bh(&sc->tx.txbuflock); - break; } else { bf = list_entry(bf_held->list.next, @@ -2011,6 +2005,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) txq->axq_aggr_depth--; txok = (ds->ds_txstat.ts_status == 0); + txq->axq_tx_inprogress = false; spin_unlock_bh(&txq->axq_lock); if (bf_held) { @@ -2033,7 +2028,7 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) if (bf_isampdu(bf)) ath_tx_complete_aggr(sc, txq, bf, &bf_head, txok); else - ath_tx_complete_buf(sc, bf, &bf_head, txok, 0); + ath_tx_complete_buf(sc, bf, txq, &bf_head, txok, 0); ath_wake_mac80211_queue(sc, txq); @@ -2044,6 +2039,40 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) } } +static void ath_tx_complete_poll_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, + tx_complete_work.work); + struct ath_txq *txq; + int i; + bool needreset = false; + + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) { + txq = &sc->tx.txq[i]; + spin_lock_bh(&txq->axq_lock); + if (txq->axq_depth) { + if (txq->axq_tx_inprogress) { + needreset = true; + spin_unlock_bh(&txq->axq_lock); + break; + } else { + txq->axq_tx_inprogress = true; + } + } + spin_unlock_bh(&txq->axq_lock); + } + + if (needreset) { + DPRINTF(sc, ATH_DBG_RESET, "tx hung, resetting the chip\n"); + ath_reset(sc, false); + } + + ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, + msecs_to_jiffies(ATH_TX_COMPLETE_POLL_INT)); +} + + void ath_tx_tasklet(struct ath_softc *sc) { @@ -2084,6 +2113,8 @@ int ath_tx_init(struct ath_softc *sc, int nbufs) goto err; } + INIT_DELAYED_WORK(&sc->tx_complete_work, ath_tx_complete_poll_work); + err: if (error != 0) ath_tx_cleanup(sc); @@ -2122,7 +2153,6 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) tid->ac = &an->ac[acno]; tid->state &= ~AGGR_ADDBA_COMPLETE; tid->state &= ~AGGR_ADDBA_PROGRESS; - tid->addba_exchangeattempts = 0; } for (acno = 0, ac = &an->ac[acno]; @@ -2179,7 +2209,6 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) tid->sched = false; ath_tid_drain(sc, txq, tid); tid->state &= ~AGGR_ADDBA_COMPLETE; - tid->addba_exchangeattempts = 0; tid->state &= ~AGGR_CLEANUP; } } diff --git a/drivers/net/wireless/ath/main.c b/drivers/net/wireless/ath/main.c index 9949b11cb151..487193f1de1a 100644 --- a/drivers/net/wireless/ath/main.c +++ b/drivers/net/wireless/ath/main.c @@ -17,6 +17,42 @@ #include <linux/kernel.h> #include <linux/module.h> +#include "ath.h" + MODULE_AUTHOR("Atheros Communications"); MODULE_DESCRIPTION("Shared library for Atheros wireless LAN cards."); MODULE_LICENSE("Dual BSD/GPL"); + +struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, + u32 len, + gfp_t gfp_mask) +{ + 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. + */ + + /* Note: the kernel can allocate a value greater than + * what we ask it to give us. We really only need 4 KB as that + * is this hardware supports and in fact we need at least 3849 + * as that is the MAX AMSDU size this hardware supports. + * Unfortunately this means we may get 8 KB here from the + * kernel... and that is actually what is observed on some + * systems :( */ + skb = __dev_alloc_skb(len + common->cachelsz - 1, gfp_mask); + if (skb != NULL) { + off = ((unsigned long) skb->data) % common->cachelsz; + if (off != 0) + skb_reserve(skb, common->cachelsz - off); + } else { + printk(KERN_ERR "skbuff alloc of size %u failed\n", len); + return NULL; + } + + return skb; +} +EXPORT_SYMBOL(ath_rxbuf_alloc); diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index bf3d25ba7be1..077bcc142cde 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -586,7 +586,5 @@ u32 ath_regd_get_band_ctl(struct ath_regulatory *reg, default: return NO_CTL; } - - return NO_CTL; } EXPORT_SYMBOL(ath_regd_get_band_ctl); diff --git a/drivers/net/wireless/ath/regd.h b/drivers/net/wireless/ath/regd.h index 07291ccb23f2..4d3c53674e5a 100644 --- a/drivers/net/wireless/ath/regd.h +++ b/drivers/net/wireless/ath/regd.h @@ -18,9 +18,10 @@ #define REGD_H #include <linux/nl80211.h> - #include <net/cfg80211.h> +#include "ath.h" + #define NO_CTL 0xff #define SD_NO_CTL 0xE0 #define NO_CTL 0xff @@ -47,29 +48,12 @@ #define CHANNEL_HALF_BW 10 #define CHANNEL_QUARTER_BW 5 -struct reg_dmn_pair_mapping { - u16 regDmnEnum; - u16 reg_5ghz_ctl; - u16 reg_2ghz_ctl; -}; - struct country_code_to_enum_rd { u16 countryCode; u16 regDmnEnum; const char *isoName; }; -struct ath_regulatory { - char alpha2[2]; - u16 country_code; - u16 max_power_level; - u32 tp_scale; - u16 current_rd; - u16 current_rd_ext; - int16_t power_limit; - struct reg_dmn_pair_mapping *regpair; -}; - enum CountryCode { CTRY_ALBANIA = 8, CTRY_ALGERIA = 12, diff --git a/drivers/net/wireless/ath/regd_common.h b/drivers/net/wireless/ath/regd_common.h index 4d0e298cd1c7..ad6d938d3cf6 100644 --- a/drivers/net/wireless/ath/regd_common.h +++ b/drivers/net/wireless/ath/regd_common.h @@ -450,7 +450,7 @@ static struct country_code_to_enum_rd allCountries[] = { {CTRY_SWITZERLAND, ETSI1_WORLD, "CH"}, {CTRY_SYRIA, NULL1_WORLD, "SY"}, {CTRY_TAIWAN, APL3_FCCA, "TW"}, - {CTRY_THAILAND, NULL1_WORLD, "TH"}, + {CTRY_THAILAND, FCC3_WORLD, "TH"}, {CTRY_TRINIDAD_Y_TOBAGO, ETSI4_WORLD, "TT"}, {CTRY_TUNISIA, ETSI3_WORLD, "TN"}, {CTRY_TURKEY, ETSI3_WORLD, "TR"}, diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index 291a94bd46fd..a3b36b3a9d67 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -781,7 +781,7 @@ static void tx_update_descriptor(struct atmel_private *priv, int is_bcast, priv->tx_free_mem -= len; } -static int start_tx(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) { static const u8 SNAP_RFC1024[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; struct atmel_private *priv = netdev_priv(dev); @@ -793,13 +793,13 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev) !(*priv->present_callback)(priv->card)) { dev->stats.tx_errors++; dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } if (priv->station_state != STATION_STATE_READY) { dev->stats.tx_errors++; dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } /* first ensure the timer func cannot run */ @@ -856,7 +856,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev) spin_unlock_bh(&priv->timerlock); dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } static void atmel_transmit_management_frame(struct atmel_private *priv, diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 67f564e37225..2af3b3522322 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -80,16 +80,14 @@ config B43_NPHY SAY N. config B43_PHY_LP - bool "IEEE 802.11g LP-PHY support (BROKEN)" - depends on B43 && EXPERIMENTAL && BROKEN + bool "Support for low-power (LP-PHY) devices (EXPERIMENTAL)" + depends on B43 && EXPERIMENTAL + default y ---help--- Support for the LP-PHY. - The LP-PHY is an IEEE 802.11g based PHY built into some notebooks - and embedded devices. - - THIS IS BROKEN AND DOES NOT WORK YET. - - SAY N. + The LP-PHY is a low-power PHY built into some notebooks + and embedded devices. It supports 802.11a/g + (802.11a support is optional, and currently disabled). # This config option automatically enables b43 LEDS support, # if it's possible. diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index 40448067e4cc..a1b3b731935b 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -142,6 +142,17 @@ #define B43_BFL_BTCMOD 0x4000 /* BFL_BTCOEXIST is given in alternate GPIOs */ #define B43_BFL_ALTIQ 0x8000 /* alternate I/Q settings */ +/* SPROM boardflags_hi values */ +#define B43_BFH_NOPA 0x0001 /* has no PA */ +#define B43_BFH_RSSIINV 0x0002 /* RSSI uses positive slope (not TSSI) */ +#define B43_BFH_PAREF 0x0004 /* uses the PARef LDO */ +#define B43_BFH_3TSWITCH 0x0008 /* uses a triple throw switch shared + * with bluetooth */ +#define B43_BFH_PHASESHIFT 0x0010 /* can support phase shifter */ +#define B43_BFH_BUCKBOOST 0x0020 /* has buck/booster */ +#define B43_BFH_FEM_BT 0x0040 /* has FEM and switch to share antenna + * with bluetooth */ + /* GPIO register offset, in both ChipCommon and PCI core. */ #define B43_GPIO_CONTROL 0x6c @@ -482,6 +493,10 @@ enum { /* Max size of a security key */ #define B43_SEC_KEYSIZE 16 +/* Max number of group keys */ +#define B43_NR_GROUP_KEYS 4 +/* Max number of pairwise keys */ +#define B43_NR_PAIRWISE_KEYS 50 /* Security algorithms. */ enum { B43_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */ @@ -628,7 +643,7 @@ struct b43_wl { u8 mac_addr[ETH_ALEN]; /* Current BSSID */ u8 bssid[ETH_ALEN]; - /* Interface type. (IEEE80211_IF_TYPE_XXX) */ + /* Interface type. (NL80211_IFTYPE_XXX) */ int if_type; /* Is the card operating in AP, STA or IBSS mode? */ bool operating; @@ -808,8 +823,7 @@ struct b43_wldev { /* encryption/decryption */ u16 ktp; /* Key table pointer */ - u8 max_nr_keys; - struct b43_key key[58]; + struct b43_key key[B43_NR_GROUP_KEYS * 2 + B43_NR_PAIRWISE_KEYS]; /* Firmware data */ struct b43_firmware fw; @@ -834,7 +848,7 @@ static inline struct b43_wldev *dev_to_b43_wldev(struct device *dev) return ssb_get_drvdata(ssb_dev); } -/* Is the device operating in a specified mode (IEEE80211_IF_TYPE_XXX). */ +/* Is the device operating in a specified mode (NL80211_IFTYPE_XXX). */ static inline int b43_is_mode(struct b43_wl *wl, int type) { return (wl->operating && wl->if_type == type); diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index 7964cc32b258..289aaf6dfe79 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -1188,7 +1188,7 @@ static int dma_tx_fragment(struct b43_dmaring *ring, header = &(ring->txhdr_cache[(slot / TX_SLOTS_PER_FRAME) * hdrsize]); cookie = generate_cookie(ring, slot); err = b43_generate_txhdr(ring->dev, header, - skb->data, skb->len, info, cookie); + skb, info, cookie); if (unlikely(err)) { ring->current_slot = old_top_slot; ring->used_slots = old_used_slots; @@ -1334,13 +1334,22 @@ int b43_dma_tx(struct b43_wldev *dev, struct sk_buff *skb) spin_lock_irqsave(&ring->lock, flags); B43_WARN_ON(!ring->tx); - /* Check if the queue was stopped in mac80211, - * but we got called nevertheless. - * That would be a mac80211 bug. */ - B43_WARN_ON(ring->stopped); - if (unlikely(free_slots(ring) < TX_SLOTS_PER_FRAME)) { - b43warn(dev->wl, "DMA queue overflow\n"); + if (unlikely(ring->stopped)) { + /* We get here only because of a bug in mac80211. + * Because of a race, one packet may be queued after + * the queue is stopped, thus we got called when we shouldn't. + * For now, just refuse the transmit. */ + if (b43_debug(dev, B43_DBG_DMAVERBOSE)) + b43err(dev->wl, "Packet after queue stopped\n"); + err = -ENOSPC; + goto out_unlock; + } + + if (unlikely(WARN_ON(free_slots(ring) < TX_SLOTS_PER_FRAME))) { + /* If we get here, we have a real error with the queue + * full, but queues not stopped. */ + b43err(dev->wl, "DMA queue overflow\n"); err = -ENOSPC; goto out_unlock; } diff --git a/drivers/net/wireless/b43/lo.c b/drivers/net/wireless/b43/lo.c index 22d0fbd83a62..976104f634a1 100644 --- a/drivers/net/wireless/b43/lo.c +++ b/drivers/net/wireless/b43/lo.c @@ -477,7 +477,7 @@ static void lo_measure_setup(struct b43_wldev *dev, } else b43_phy_write(dev, B43_PHY_CCK(0x2B), 0x0802); if (phy->rev >= 2) - b43_dummy_transmission(dev); + b43_dummy_transmission(dev, false, true); b43_gphy_channel_switch(dev, 6, 0); b43_radio_read16(dev, 0x51); /* dummy read */ if (phy->type == B43_PHYTYPE_G) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index e71c8d9cd706..0f168443ad49 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -80,6 +80,10 @@ static int modparam_nohwcrypt; module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); +static int modparam_hwtkip; +module_param_named(hwtkip, modparam_hwtkip, int, 0444); +MODULE_PARM_DESC(hwtkip, "Enable hardware tkip."); + static int modparam_qos = 1; module_param_named(qos, modparam_qos, int, 0444); MODULE_PARM_DESC(qos, "Enable QOS support (default on)"); @@ -395,9 +399,8 @@ u32 __b43_shm_read32(struct b43_wldev *dev, u16 routing, u16 offset) /* Unaligned access */ b43_shm_control_word(dev, routing, offset >> 2); ret = b43_read16(dev, B43_MMIO_SHM_DATA_UNALIGNED); - ret <<= 16; b43_shm_control_word(dev, routing, (offset >> 2) + 1); - ret |= b43_read16(dev, B43_MMIO_SHM_DATA); + ret |= ((u32)b43_read16(dev, B43_MMIO_SHM_DATA)) << 16; goto out; } @@ -464,9 +467,10 @@ void __b43_shm_write32(struct b43_wldev *dev, u16 routing, u16 offset, u32 value /* Unaligned access */ b43_shm_control_word(dev, routing, offset >> 2); b43_write16(dev, B43_MMIO_SHM_DATA_UNALIGNED, - (value >> 16) & 0xffff); + value & 0xFFFF); b43_shm_control_word(dev, routing, (offset >> 2) + 1); - b43_write16(dev, B43_MMIO_SHM_DATA, value & 0xffff); + b43_write16(dev, B43_MMIO_SHM_DATA, + (value >> 16) & 0xFFFF); return; } offset >>= 2; @@ -691,9 +695,9 @@ static void b43_synchronize_irq(struct b43_wldev *dev) } /* DummyTransmission function, as documented on - * http://bcm-specs.sipsolutions.net/DummyTransmission + * http://bcm-v4.sipsolutions.net/802.11/DummyTransmission */ -void b43_dummy_transmission(struct b43_wldev *dev) +void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on) { struct b43_wl *wl = dev->wl; struct b43_phy *phy = &dev->phy; @@ -707,19 +711,12 @@ void b43_dummy_transmission(struct b43_wldev *dev) 0x00000000, }; - switch (phy->type) { - case B43_PHYTYPE_A: + if (ofdm) { max_loop = 0x1E; buffer[0] = 0x000201CC; - break; - case B43_PHYTYPE_B: - case B43_PHYTYPE_G: + } else { max_loop = 0xFA; buffer[0] = 0x000B846E; - break; - default: - B43_WARN_ON(1); - return; } spin_lock_irq(&wl->irq_lock); @@ -728,20 +725,35 @@ void b43_dummy_transmission(struct b43_wldev *dev) for (i = 0; i < 5; i++) b43_ram_write(dev, i * 4, buffer[i]); - /* Commit writes */ - b43_read32(dev, B43_MMIO_MACCTL); - b43_write16(dev, 0x0568, 0x0000); - b43_write16(dev, 0x07C0, 0x0000); - value = ((phy->type == B43_PHYTYPE_A) ? 1 : 0); + if (dev->dev->id.revision < 11) + b43_write16(dev, 0x07C0, 0x0000); + else + b43_write16(dev, 0x07C0, 0x0100); + value = (ofdm ? 0x41 : 0x40); b43_write16(dev, 0x050C, value); + if ((phy->type == B43_PHYTYPE_N) || (phy->type == B43_PHYTYPE_LP)) + b43_write16(dev, 0x0514, 0x1A02); b43_write16(dev, 0x0508, 0x0000); b43_write16(dev, 0x050A, 0x0000); b43_write16(dev, 0x054C, 0x0000); b43_write16(dev, 0x056A, 0x0014); b43_write16(dev, 0x0568, 0x0826); b43_write16(dev, 0x0500, 0x0000); - b43_write16(dev, 0x0502, 0x0030); + if (!pa_on && (phy->type == B43_PHYTYPE_N)) { + //SPEC TODO + } + + switch (phy->type) { + case B43_PHYTYPE_N: + b43_write16(dev, 0x0502, 0x00D0); + break; + case B43_PHYTYPE_LP: + b43_write16(dev, 0x0502, 0x0050); + break; + default: + b43_write16(dev, 0x0502, 0x0030); + } if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) b43_radio_write16(dev, 0x0051, 0x0017); @@ -796,18 +808,19 @@ static void key_write(struct b43_wldev *dev, static void keymac_write(struct b43_wldev *dev, u8 index, const u8 *addr) { u32 addrtmp[2] = { 0, 0, }; - u8 per_sta_keys_start = 8; + u8 pairwise_keys_start = B43_NR_GROUP_KEYS * 2; if (b43_new_kidx_api(dev)) - per_sta_keys_start = 4; + pairwise_keys_start = B43_NR_GROUP_KEYS; - B43_WARN_ON(index < per_sta_keys_start); - /* We have two default TX keys and possibly two default RX keys. + B43_WARN_ON(index < pairwise_keys_start); + /* We have four default TX keys and possibly four default RX keys. * Physical mac 0 is mapped to physical key 4 or 8, depending * on the firmware version. * So we must adjust the index here. */ - index -= per_sta_keys_start; + index -= pairwise_keys_start; + B43_WARN_ON(index >= B43_NR_PAIRWISE_KEYS); if (addr) { addrtmp[0] = addr[0]; @@ -818,27 +831,90 @@ static void keymac_write(struct b43_wldev *dev, u8 index, const u8 *addr) addrtmp[1] |= ((u32) (addr[5]) << 8); } - if (dev->dev->id.revision >= 5) { - /* Receive match transmitter address mechanism */ - b43_shm_write32(dev, B43_SHM_RCMTA, - (index * 2) + 0, addrtmp[0]); - b43_shm_write16(dev, B43_SHM_RCMTA, - (index * 2) + 1, addrtmp[1]); - } else { - /* RXE (Receive Engine) and - * PSM (Programmable State Machine) mechanism - */ - if (index < 8) { - /* TODO write to RCM 16, 19, 22 and 25 */ - } else { - b43_shm_write32(dev, B43_SHM_SHARED, - B43_SHM_SH_PSM + (index * 6) + 0, - addrtmp[0]); - b43_shm_write16(dev, B43_SHM_SHARED, - B43_SHM_SH_PSM + (index * 6) + 4, - addrtmp[1]); - } + /* Receive match transmitter address (RCMTA) mechanism */ + b43_shm_write32(dev, B43_SHM_RCMTA, + (index * 2) + 0, addrtmp[0]); + b43_shm_write16(dev, B43_SHM_RCMTA, + (index * 2) + 1, addrtmp[1]); +} + +/* The ucode will use phase1 key with TEK key to decrypt rx packets. + * When a packet is received, the iv32 is checked. + * - if it doesn't the packet is returned without modification (and software + * decryption can be done). That's what happen when iv16 wrap. + * - if it does, the rc4 key is computed, and decryption is tried. + * Either it will success and B43_RX_MAC_DEC is returned, + * either it fails and B43_RX_MAC_DEC|B43_RX_MAC_DECERR is returned + * and the packet is not usable (it got modified by the ucode). + * So in order to never have B43_RX_MAC_DECERR, we should provide + * a iv32 and phase1key that match. Because we drop packets in case of + * B43_RX_MAC_DECERR, if we have a correct iv32 but a wrong phase1key, all + * packets will be lost without higher layer knowing (ie no resync possible + * until next wrap). + * + * NOTE : this should support 50 key like RCMTA because + * (B43_SHM_SH_KEYIDXBLOCK - B43_SHM_SH_TKIPTSCTTAK)/14 = 50 + */ +static void rx_tkip_phase1_write(struct b43_wldev *dev, u8 index, u32 iv32, + u16 *phase1key) +{ + unsigned int i; + u32 offset; + u8 pairwise_keys_start = B43_NR_GROUP_KEYS * 2; + + if (!modparam_hwtkip) + return; + + if (b43_new_kidx_api(dev)) + pairwise_keys_start = B43_NR_GROUP_KEYS; + + B43_WARN_ON(index < pairwise_keys_start); + /* We have four default TX keys and possibly four default RX keys. + * Physical mac 0 is mapped to physical key 4 or 8, depending + * on the firmware version. + * So we must adjust the index here. + */ + index -= pairwise_keys_start; + B43_WARN_ON(index >= B43_NR_PAIRWISE_KEYS); + + if (b43_debug(dev, B43_DBG_KEYS)) { + b43dbg(dev->wl, "rx_tkip_phase1_write : idx 0x%x, iv32 0x%x\n", + index, iv32); + } + /* Write the key to the RX tkip shared mem */ + offset = B43_SHM_SH_TKIPTSCTTAK + index * (10 + 4); + for (i = 0; i < 10; i += 2) { + b43_shm_write16(dev, B43_SHM_SHARED, offset + i, + phase1key ? phase1key[i / 2] : 0); } + b43_shm_write16(dev, B43_SHM_SHARED, offset + i, iv32); + b43_shm_write16(dev, B43_SHM_SHARED, offset + i + 2, iv32 >> 16); +} + +static void b43_op_update_tkip_key(struct ieee80211_hw *hw, + struct ieee80211_key_conf *keyconf, const u8 *addr, + u32 iv32, u16 *phase1key) +{ + struct b43_wl *wl = hw_to_b43_wl(hw); + struct b43_wldev *dev; + int index = keyconf->hw_key_idx; + + if (B43_WARN_ON(!modparam_hwtkip)) + return; + + mutex_lock(&wl->mutex); + + dev = wl->current_dev; + if (!dev || b43_status(dev) < B43_STAT_INITIALIZED) + goto out_unlock; + + keymac_write(dev, index, NULL); /* First zero out mac to avoid race */ + + rx_tkip_phase1_write(dev, index, iv32, phase1key); + keymac_write(dev, index, addr); + +out_unlock: + mutex_unlock(&wl->mutex); } static void do_key_write(struct b43_wldev *dev, @@ -846,20 +922,33 @@ static void do_key_write(struct b43_wldev *dev, const u8 *key, size_t key_len, const u8 *mac_addr) { u8 buf[B43_SEC_KEYSIZE] = { 0, }; - u8 per_sta_keys_start = 8; + u8 pairwise_keys_start = B43_NR_GROUP_KEYS * 2; if (b43_new_kidx_api(dev)) - per_sta_keys_start = 4; + pairwise_keys_start = B43_NR_GROUP_KEYS; - B43_WARN_ON(index >= dev->max_nr_keys); + B43_WARN_ON(index >= ARRAY_SIZE(dev->key)); B43_WARN_ON(key_len > B43_SEC_KEYSIZE); - if (index >= per_sta_keys_start) + if (index >= pairwise_keys_start) keymac_write(dev, index, NULL); /* First zero out mac. */ + if (algorithm == B43_SEC_ALGO_TKIP) { + /* + * We should provide an initial iv32, phase1key pair. + * We could start with iv32=0 and compute the corresponding + * phase1key, but this means calling ieee80211_get_tkip_key + * with a fake skb (or export other tkip function). + * Because we are lazy we hope iv32 won't start with + * 0xffffffff and let's b43_op_update_tkip_key provide a + * correct pair. + */ + rx_tkip_phase1_write(dev, index, 0xffffffff, (u16*)buf); + } else if (index >= pairwise_keys_start) /* clear it */ + rx_tkip_phase1_write(dev, index, 0, NULL); if (key) memcpy(buf, key, key_len); key_write(dev, index, algorithm, buf); - if (index >= per_sta_keys_start) + if (index >= pairwise_keys_start) keymac_write(dev, index, mac_addr); dev->key[index].algorithm = algorithm; @@ -872,21 +961,33 @@ static int b43_key_write(struct b43_wldev *dev, struct ieee80211_key_conf *keyconf) { int i; - int sta_keys_start; - + int pairwise_keys_start; + + /* For ALG_TKIP the key is encoded as a 256-bit (32 byte) data block: + * - Temporal Encryption Key (128 bits) + * - Temporal Authenticator Tx MIC Key (64 bits) + * - Temporal Authenticator Rx MIC Key (64 bits) + * + * Hardware only store TEK + */ + if (algorithm == B43_SEC_ALGO_TKIP && key_len == 32) + key_len = 16; if (key_len > B43_SEC_KEYSIZE) return -EINVAL; - for (i = 0; i < dev->max_nr_keys; i++) { + for (i = 0; i < ARRAY_SIZE(dev->key); i++) { /* Check that we don't already have this key. */ B43_WARN_ON(dev->key[i].keyconf == keyconf); } if (index < 0) { /* Pairwise key. Get an empty slot for the key. */ if (b43_new_kidx_api(dev)) - sta_keys_start = 4; + pairwise_keys_start = B43_NR_GROUP_KEYS; else - sta_keys_start = 8; - for (i = sta_keys_start; i < dev->max_nr_keys; i++) { + pairwise_keys_start = B43_NR_GROUP_KEYS * 2; + for (i = pairwise_keys_start; + i < pairwise_keys_start + B43_NR_PAIRWISE_KEYS; + i++) { + B43_WARN_ON(i >= ARRAY_SIZE(dev->key)); if (!dev->key[i].keyconf) { /* found empty */ index = i; @@ -914,7 +1015,7 @@ static int b43_key_write(struct b43_wldev *dev, static int b43_key_clear(struct b43_wldev *dev, int index) { - if (B43_WARN_ON((index < 0) || (index >= dev->max_nr_keys))) + if (B43_WARN_ON((index < 0) || (index >= ARRAY_SIZE(dev->key)))) return -EINVAL; do_key_write(dev, index, B43_SEC_ALGO_NONE, NULL, B43_SEC_KEYSIZE, NULL); @@ -929,16 +1030,19 @@ static int b43_key_clear(struct b43_wldev *dev, int index) static void b43_clear_keys(struct b43_wldev *dev) { - int i; + int i, count; - for (i = 0; i < dev->max_nr_keys; i++) + if (b43_new_kidx_api(dev)) + count = B43_NR_GROUP_KEYS + B43_NR_PAIRWISE_KEYS; + else + count = B43_NR_GROUP_KEYS * 2 + B43_NR_PAIRWISE_KEYS; + for (i = 0; i < count; i++) b43_key_clear(dev, i); } static void b43_dump_keymemory(struct b43_wldev *dev) { - unsigned int i, index, offset; - DECLARE_MAC_BUF(macbuf); + unsigned int i, index, count, offset, pairwise_keys_start; u8 mac[ETH_ALEN]; u16 algo; u32 rcmta0; @@ -952,7 +1056,14 @@ static void b43_dump_keymemory(struct b43_wldev *dev) hf = b43_hf_read(dev); b43dbg(dev->wl, "Hardware key memory dump: USEDEFKEYS=%u\n", !!(hf & B43_HF_USEDEFKEYS)); - for (index = 0; index < dev->max_nr_keys; index++) { + if (b43_new_kidx_api(dev)) { + pairwise_keys_start = B43_NR_GROUP_KEYS; + count = B43_NR_GROUP_KEYS + B43_NR_PAIRWISE_KEYS; + } else { + pairwise_keys_start = B43_NR_GROUP_KEYS * 2; + count = B43_NR_GROUP_KEYS * 2 + B43_NR_PAIRWISE_KEYS; + } + for (index = 0; index < count; index++) { key = &(dev->key[index]); printk(KERN_DEBUG "Key slot %02u: %s", index, (key->keyconf == NULL) ? " " : "*"); @@ -966,15 +1077,22 @@ static void b43_dump_keymemory(struct b43_wldev *dev) B43_SHM_SH_KEYIDXBLOCK + (index * 2)); printk(" Algo: %04X/%02X", algo, key->algorithm); - if (index >= 4) { + if (index >= pairwise_keys_start) { + if (key->algorithm == B43_SEC_ALGO_TKIP) { + printk(" TKIP: "); + offset = B43_SHM_SH_TKIPTSCTTAK + (index - 4) * (10 + 4); + for (i = 0; i < 14; i += 2) { + u16 tmp = b43_shm_read16(dev, B43_SHM_SHARED, offset + i); + printk("%02X%02X", (tmp & 0xFF), ((tmp >> 8) & 0xFF)); + } + } rcmta0 = b43_shm_read32(dev, B43_SHM_RCMTA, - ((index - 4) * 2) + 0); + ((index - pairwise_keys_start) * 2) + 0); rcmta1 = b43_shm_read16(dev, B43_SHM_RCMTA, - ((index - 4) * 2) + 1); + ((index - pairwise_keys_start) * 2) + 1); *((__le32 *)(&mac[0])) = cpu_to_le32(rcmta0); *((__le16 *)(&mac[4])) = cpu_to_le16(rcmta1); - printk(" MAC: %s", - print_mac(macbuf, mac)); + printk(" MAC: %pM", mac); } else printk(" DEFAULT KEY"); printk("\n"); @@ -1338,7 +1456,8 @@ static u16 b43_antenna_to_phyctl(int antenna) return B43_TXH_PHY_ANT2; case B43_ANTENNA3: return B43_TXH_PHY_ANT3; - case B43_ANTENNA_AUTO: + case B43_ANTENNA_AUTO0: + case B43_ANTENNA_AUTO1: return B43_TXH_PHY_ANT01AUTO; } B43_WARN_ON(1); @@ -1431,113 +1550,6 @@ static void b43_write_beacon_template(struct b43_wldev *dev, b43dbg(dev->wl, "Updated beacon template at 0x%x\n", ram_offset); } -static void b43_write_probe_resp_plcp(struct b43_wldev *dev, - u16 shm_offset, u16 size, - struct ieee80211_rate *rate) -{ - struct b43_plcp_hdr4 plcp; - u32 tmp; - __le16 dur; - - plcp.data = 0; - b43_generate_plcp_hdr(&plcp, size + FCS_LEN, rate->hw_value); - dur = ieee80211_generic_frame_duration(dev->wl->hw, - dev->wl->vif, size, - rate); - /* Write PLCP in two parts and timing for packet transfer */ - tmp = le32_to_cpu(plcp.data); - b43_shm_write16(dev, B43_SHM_SHARED, shm_offset, tmp & 0xFFFF); - b43_shm_write16(dev, B43_SHM_SHARED, shm_offset + 2, tmp >> 16); - b43_shm_write16(dev, B43_SHM_SHARED, shm_offset + 6, le16_to_cpu(dur)); -} - -/* Instead of using custom probe response template, this function - * just patches custom beacon template by: - * 1) Changing packet type - * 2) Patching duration field - * 3) Stripping TIM - */ -static const u8 *b43_generate_probe_resp(struct b43_wldev *dev, - u16 *dest_size, - struct ieee80211_rate *rate) -{ - const u8 *src_data; - u8 *dest_data; - u16 src_size, elem_size, src_pos, dest_pos; - __le16 dur; - struct ieee80211_hdr *hdr; - size_t ie_start; - - src_size = dev->wl->current_beacon->len; - src_data = (const u8 *)dev->wl->current_beacon->data; - - /* Get the start offset of the variable IEs in the packet. */ - ie_start = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); - B43_WARN_ON(ie_start != offsetof(struct ieee80211_mgmt, u.beacon.variable)); - - if (B43_WARN_ON(src_size < ie_start)) - return NULL; - - dest_data = kmalloc(src_size, GFP_ATOMIC); - if (unlikely(!dest_data)) - return NULL; - - /* Copy the static data and all Information Elements, except the TIM. */ - memcpy(dest_data, src_data, ie_start); - src_pos = ie_start; - dest_pos = ie_start; - for ( ; src_pos < src_size - 2; src_pos += elem_size) { - elem_size = src_data[src_pos + 1] + 2; - if (src_data[src_pos] == 5) { - /* This is the TIM. */ - continue; - } - memcpy(dest_data + dest_pos, src_data + src_pos, - elem_size); - dest_pos += elem_size; - } - *dest_size = dest_pos; - hdr = (struct ieee80211_hdr *)dest_data; - - /* Set the frame control. */ - hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_PROBE_RESP); - dur = ieee80211_generic_frame_duration(dev->wl->hw, - dev->wl->vif, *dest_size, - rate); - hdr->duration_id = dur; - - return dest_data; -} - -static void b43_write_probe_resp_template(struct b43_wldev *dev, - u16 ram_offset, - u16 shm_size_offset, - struct ieee80211_rate *rate) -{ - const u8 *probe_resp_data; - u16 size; - - size = dev->wl->current_beacon->len; - probe_resp_data = b43_generate_probe_resp(dev, &size, rate); - if (unlikely(!probe_resp_data)) - return; - - /* Looks like PLCP headers plus packet timings are stored for - * all possible basic rates - */ - b43_write_probe_resp_plcp(dev, 0x31A, size, &b43_b_ratetable[0]); - b43_write_probe_resp_plcp(dev, 0x32C, size, &b43_b_ratetable[1]); - b43_write_probe_resp_plcp(dev, 0x33E, size, &b43_b_ratetable[2]); - b43_write_probe_resp_plcp(dev, 0x350, size, &b43_b_ratetable[3]); - - size = min((size_t) size, 0x200 - sizeof(struct b43_plcp_hdr6)); - b43_write_template_common(dev, probe_resp_data, - size, ram_offset, shm_size_offset, - rate->hw_value); - kfree(probe_resp_data); -} - static void b43_upload_beacon0(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; @@ -1545,10 +1557,6 @@ static void b43_upload_beacon0(struct b43_wldev *dev) if (wl->beacon0_uploaded) return; b43_write_beacon_template(dev, 0x68, 0x18); - /* FIXME: Probe resp upload doesn't really belong here, - * but we don't use that feature anyway. */ - b43_write_probe_resp_template(dev, 0x268, 0x4A, - &__b43_ratetable[3]); wl->beacon0_uploaded = 1; } @@ -1656,7 +1664,7 @@ static void b43_update_templates(struct b43_wl *wl) wl->current_beacon = beacon; wl->beacon0_uploaded = 0; wl->beacon1_uploaded = 0; - queue_work(wl->hw->workqueue, &wl->beacon_update_trigger); + ieee80211_queue_work(wl->hw, &wl->beacon_update_trigger); } static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int) @@ -2061,8 +2069,12 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) filename = "ucode5"; else if ((rev >= 11) && (rev <= 12)) filename = "ucode11"; - else if (rev >= 13) + else if (rev == 13) filename = "ucode13"; + else if (rev == 14) + filename = "ucode14"; + else if (rev >= 15) + filename = "ucode15"; else goto err_no_ucode; err = b43_do_request_fw(ctx, filename, &fw->ucode); @@ -2110,6 +2122,16 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) else goto err_no_initvals; break; + case B43_PHYTYPE_LP: + if (rev == 13) + filename = "lp0initvals13"; + else if (rev == 14) + filename = "lp0initvals14"; + else if (rev >= 15) + filename = "lp0initvals15"; + else + goto err_no_initvals; + break; default: goto err_no_initvals; } @@ -2144,6 +2166,16 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx) else goto err_no_initvals; break; + case B43_PHYTYPE_LP: + if (rev == 13) + filename = "lp0bsinitvals13"; + else if (rev == 14) + filename = "lp0bsinitvals14"; + else if (rev >= 15) + filename = "lp0bsinitvals15"; + else + goto err_no_initvals; + break; default: goto err_no_initvals; } @@ -2671,6 +2703,7 @@ static void b43_rate_memory_init(struct b43_wldev *dev) case B43_PHYTYPE_A: case B43_PHYTYPE_G: case B43_PHYTYPE_N: + case B43_PHYTYPE_LP: b43_rate_memory_write(dev, B43_OFDM_RATE_6MB, 1); b43_rate_memory_write(dev, B43_OFDM_RATE_12MB, 1); b43_rate_memory_write(dev, B43_OFDM_RATE_18MB, 1); @@ -2916,7 +2949,7 @@ out_requeue: delay = msecs_to_jiffies(50); else delay = round_jiffies_relative(HZ * 15); - queue_delayed_work(wl->hw->workqueue, &dev->periodic_work, delay); + ieee80211_queue_delayed_work(wl->hw, &dev->periodic_work, delay); out: mutex_unlock(&wl->mutex); } @@ -2927,15 +2960,16 @@ static void b43_periodic_tasks_setup(struct b43_wldev *dev) dev->periodic_state = 0; INIT_DELAYED_WORK(work, b43_periodic_work_handler); - queue_delayed_work(dev->wl->hw->workqueue, work, 0); + ieee80211_queue_delayed_work(dev->wl->hw, work, 0); } /* Check if communication with the device works correctly. */ static int b43_validate_chipaccess(struct b43_wldev *dev) { - u32 v, backup; + u32 v, backup0, backup4; - backup = b43_shm_read32(dev, B43_SHM_SHARED, 0); + backup0 = b43_shm_read32(dev, B43_SHM_SHARED, 0); + backup4 = b43_shm_read32(dev, B43_SHM_SHARED, 4); /* Check for read/write and endianness problems. */ b43_shm_write32(dev, B43_SHM_SHARED, 0, 0x55AAAA55); @@ -2945,7 +2979,23 @@ static int b43_validate_chipaccess(struct b43_wldev *dev) if (b43_shm_read32(dev, B43_SHM_SHARED, 0) != 0xAA5555AA) goto error; - b43_shm_write32(dev, B43_SHM_SHARED, 0, backup); + /* Check if unaligned 32bit SHM_SHARED access works properly. + * However, don't bail out on failure, because it's noncritical. */ + b43_shm_write16(dev, B43_SHM_SHARED, 0, 0x1122); + b43_shm_write16(dev, B43_SHM_SHARED, 2, 0x3344); + b43_shm_write16(dev, B43_SHM_SHARED, 4, 0x5566); + b43_shm_write16(dev, B43_SHM_SHARED, 6, 0x7788); + if (b43_shm_read32(dev, B43_SHM_SHARED, 2) != 0x55663344) + b43warn(dev->wl, "Unaligned 32bit SHM read access is broken\n"); + b43_shm_write32(dev, B43_SHM_SHARED, 2, 0xAABBCCDD); + if (b43_shm_read16(dev, B43_SHM_SHARED, 0) != 0x1122 || + b43_shm_read16(dev, B43_SHM_SHARED, 2) != 0xCCDD || + b43_shm_read16(dev, B43_SHM_SHARED, 4) != 0xAABB || + b43_shm_read16(dev, B43_SHM_SHARED, 6) != 0x7788) + b43warn(dev->wl, "Unaligned 32bit SHM write access is broken\n"); + + b43_shm_write32(dev, B43_SHM_SHARED, 0, backup0); + b43_shm_write32(dev, B43_SHM_SHARED, 4, backup4); if ((dev->dev->id.revision >= 3) && (dev->dev->id.revision <= 10)) { /* The 32bit register shadows the two 16bit registers @@ -2972,17 +3022,14 @@ error: static void b43_security_init(struct b43_wldev *dev) { - dev->max_nr_keys = (dev->dev->id.revision >= 5) ? 58 : 20; - B43_WARN_ON(dev->max_nr_keys > ARRAY_SIZE(dev->key)); dev->ktp = b43_shm_read16(dev, B43_SHM_SHARED, B43_SHM_SH_KTP); /* KTP is a word address, but we address SHM bytewise. * So multiply by two. */ dev->ktp *= 2; - if (dev->dev->id.revision >= 5) { - /* Number of RCMTA address slots */ - b43_write16(dev, B43_MMIO_RCMTA_COUNT, dev->max_nr_keys - 8); - } + /* Number of RCMTA address slots */ + b43_write16(dev, B43_MMIO_RCMTA_COUNT, B43_NR_PAIRWISE_KEYS); + /* Clear the key memory. */ b43_clear_keys(dev); } @@ -3687,8 +3734,10 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, switch (cmd) { case SET_KEY: - if (algorithm == B43_SEC_ALGO_TKIP) { - /* FIXME: No TKIP hardware encryption for now. */ + if (algorithm == B43_SEC_ALGO_TKIP && + (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE) || + !modparam_hwtkip)) { + /* We support only pairwise key */ err = -EOPNOTSUPP; goto out_unlock; } @@ -3718,6 +3767,8 @@ static int b43_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, b43_hf_read(dev) & ~B43_HF_USEDEFKEYS); } key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + if (algorithm == B43_SEC_ALGO_TKIP) + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; break; case DISABLE_KEY: { err = b43_key_clear(dev, key->hw_key_idx); @@ -3746,7 +3797,7 @@ out_unlock: static void b43_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, unsigned int *fflags, - int mc_count, struct dev_addr_list *mc_list) + u64 multicast) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev = wl->current_dev; @@ -3885,7 +3936,7 @@ static int b43_phy_versioning(struct b43_wldev *dev) #endif #ifdef CONFIG_B43_PHY_LP case B43_PHYTYPE_LP: - if (phy_rev > 1) + if (phy_rev > 2) unsupported = 1; break; #endif @@ -3942,7 +3993,7 @@ static int b43_phy_versioning(struct b43_wldev *dev) unsupported = 1; break; case B43_PHYTYPE_LP: - if (radio_ver != 0x2062) + if (radio_ver != 0x2062 && radio_ver != 0x2063) unsupported = 1; break; default: @@ -4445,6 +4496,7 @@ static const struct ieee80211_ops b43_hw_ops = { .bss_info_changed = b43_op_bss_info_changed, .configure_filter = b43_op_configure_filter, .set_key = b43_op_set_key, + .update_tkip_key = b43_op_update_tkip_key, .get_stats = b43_op_get_stats, .get_tx_stats = b43_op_get_tx_stats, .get_tsf = b43_op_get_tsf, @@ -4580,9 +4632,12 @@ static int b43_wireless_core_attach(struct b43_wldev *dev) case B43_PHYTYPE_A: have_5ghz_phy = 1; break; + case B43_PHYTYPE_LP: //FIXME not always! +#if 0 //FIXME enabling 5GHz causes a NULL pointer dereference + have_5ghz_phy = 1; +#endif case B43_PHYTYPE_G: case B43_PHYTYPE_N: - case B43_PHYTYPE_LP: have_2ghz_phy = 1; break; default: @@ -4597,7 +4652,8 @@ static int b43_wireless_core_attach(struct b43_wldev *dev) } if (1 /* disable A-PHY */) { /* FIXME: For now we disable the A-PHY on multi-PHY devices. */ - if (dev->phy.type != B43_PHYTYPE_N) { + if (dev->phy.type != B43_PHYTYPE_N && + dev->phy.type != B43_PHYTYPE_LP) { have_2ghz_phy = 1; have_5ghz_phy = 0; } @@ -4873,7 +4929,7 @@ void b43_controller_restart(struct b43_wldev *dev, const char *reason) if (b43_status(dev) < B43_STAT_INITIALIZED) return; b43info(dev->wl, "Controller RESET (%s) ...\n", reason); - queue_work(dev->wl->hw->workqueue, &dev->restart_work); + ieee80211_queue_work(dev->wl->hw, &dev->restart_work); } #ifdef CONFIG_PM diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/b43/main.h index 950fb1b0546d..0406e06781d4 100644 --- a/drivers/net/wireless/b43/main.h +++ b/drivers/net/wireless/b43/main.h @@ -123,7 +123,7 @@ void __b43_shm_write16(struct b43_wldev *dev, u16 routing, u16 offset, u16 value u64 b43_hf_read(struct b43_wldev *dev); void b43_hf_write(struct b43_wldev *dev, u64 value); -void b43_dummy_transmission(struct b43_wldev *dev); +void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on); void b43_wireless_core_reset(struct b43_wldev *dev, u32 flags); diff --git a/drivers/net/wireless/b43/phy_a.c b/drivers/net/wireless/b43/phy_a.c index 816e028a2620..809ec97031cb 100644 --- a/drivers/net/wireless/b43/phy_a.c +++ b/drivers/net/wireless/b43/phy_a.c @@ -531,7 +531,7 @@ static void b43_aphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) tmp = b43_phy_read(dev, B43_PHY_BBANDCFG); tmp &= ~B43_PHY_BBANDCFG_RXANT; - tmp |= (autodiv ? B43_ANTENNA_AUTO0 : antenna) + tmp |= (autodiv ? B43_ANTENNA_AUTO1 : antenna) << B43_PHY_BBANDCFG_RXANT_SHIFT; b43_phy_write(dev, B43_PHY_BBANDCFG, tmp); diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index 6d241622210e..6e704becda6f 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -240,22 +240,44 @@ void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value) dev->phy.ops->phy_write(dev, reg, value); } +void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg) +{ + assert_mac_suspended(dev); + dev->phy.ops->phy_write(dev, destreg, + dev->phy.ops->phy_read(dev, srcreg)); +} + void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask) { - b43_phy_write(dev, offset, - b43_phy_read(dev, offset) & mask); + if (dev->phy.ops->phy_maskset) { + assert_mac_suspended(dev); + dev->phy.ops->phy_maskset(dev, offset, mask, 0); + } else { + b43_phy_write(dev, offset, + b43_phy_read(dev, offset) & mask); + } } void b43_phy_set(struct b43_wldev *dev, u16 offset, u16 set) { - b43_phy_write(dev, offset, - b43_phy_read(dev, offset) | set); + if (dev->phy.ops->phy_maskset) { + assert_mac_suspended(dev); + dev->phy.ops->phy_maskset(dev, offset, 0xFFFF, set); + } else { + b43_phy_write(dev, offset, + b43_phy_read(dev, offset) | set); + } } void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) { - b43_phy_write(dev, offset, - (b43_phy_read(dev, offset) & mask) | set); + if (dev->phy.ops->phy_maskset) { + assert_mac_suspended(dev); + dev->phy.ops->phy_maskset(dev, offset, mask, set); + } else { + b43_phy_write(dev, offset, + (b43_phy_read(dev, offset) & mask) | set); + } } int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel) @@ -352,7 +374,7 @@ void b43_phy_txpower_check(struct b43_wldev *dev, unsigned int flags) /* We must adjust the transmission power in hardware. * Schedule b43_phy_txpower_adjust_work(). */ - queue_work(dev->wl->hw->workqueue, &dev->wl->txpower_adjust_work); + ieee80211_queue_work(dev->wl->hw, &dev->wl->txpower_adjust_work); } int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset) diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h index 44cc918e4fc6..28e384633c34 100644 --- a/drivers/net/wireless/b43/phy_common.h +++ b/drivers/net/wireless/b43/phy_common.h @@ -49,11 +49,11 @@ enum b43_interference_mitigation { /* Antenna identifiers */ enum { - B43_ANTENNA0, /* Antenna 0 */ - B43_ANTENNA1, /* Antenna 0 */ - B43_ANTENNA_AUTO1, /* Automatic, starting with antenna 1 */ - B43_ANTENNA_AUTO0, /* Automatic, starting with antenna 0 */ - B43_ANTENNA2, + B43_ANTENNA0 = 0, /* Antenna 0 */ + B43_ANTENNA1 = 1, /* Antenna 1 */ + B43_ANTENNA_AUTO0 = 2, /* Automatic, starting with antenna 0 */ + B43_ANTENNA_AUTO1 = 3, /* Automatic, starting with antenna 1 */ + B43_ANTENNA2 = 4, B43_ANTENNA3 = 8, B43_ANTENNA_AUTO = B43_ANTENNA_AUTO0, @@ -95,6 +95,8 @@ enum b43_txpwr_result { * Must not be NULL. * @phy_write: Write to a PHY register. * Must not be NULL. + * @phy_maskset: Maskset a PHY register, taking shortcuts. + * If it is NULL, a generic algorithm is used. * @radio_read: Read from a Radio register. * Must not be NULL. * @radio_write: Write to a Radio register. @@ -154,6 +156,7 @@ struct b43_phy_operations { /* Register access */ u16 (*phy_read)(struct b43_wldev *dev, u16 reg); void (*phy_write)(struct b43_wldev *dev, u16 reg, u16 value); + void (*phy_maskset)(struct b43_wldev *dev, u16 reg, u16 mask, u16 set); u16 (*radio_read)(struct b43_wldev *dev, u16 reg); void (*radio_write)(struct b43_wldev *dev, u16 reg, u16 value); @@ -291,6 +294,11 @@ u16 b43_phy_read(struct b43_wldev *dev, u16 reg); void b43_phy_write(struct b43_wldev *dev, u16 reg, u16 value); /** + * b43_phy_copy - copy contents of 16bit PHY register to another + */ +void b43_phy_copy(struct b43_wldev *dev, u16 destreg, u16 srcreg); + +/** * b43_phy_mask - Mask a PHY register with a mask */ void b43_phy_mask(struct b43_wldev *dev, u16 offset, u16 mask); diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/b43/phy_g.c index 5300232449f6..bdff9afe3f81 100644 --- a/drivers/net/wireless/b43/phy_g.c +++ b/drivers/net/wireless/b43/phy_g.c @@ -333,7 +333,7 @@ static void b43_set_all_gains(struct b43_wldev *dev, b43_phy_maskset(dev, 0x04A1, 0xBFBF, tmp); b43_phy_maskset(dev, 0x04A2, 0xBFBF, tmp); } - b43_dummy_transmission(dev); + b43_dummy_transmission(dev, false, true); } static void b43_set_original_gains(struct b43_wldev *dev) @@ -365,7 +365,7 @@ static void b43_set_original_gains(struct b43_wldev *dev) b43_phy_maskset(dev, 0x04A0, 0xBFBF, 0x4040); b43_phy_maskset(dev, 0x04A1, 0xBFBF, 0x4040); b43_phy_maskset(dev, 0x04A2, 0xBFBF, 0x4000); - b43_dummy_transmission(dev); + b43_dummy_transmission(dev, false, true); } /* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ @@ -1964,7 +1964,7 @@ static void b43_phy_init_pctl(struct b43_wldev *dev) } b43_set_txpower_g(dev, &bbatt, &rfatt, 0); } - b43_dummy_transmission(dev); + b43_dummy_transmission(dev, false, true); gphy->cur_idle_tssi = b43_phy_read(dev, B43_PHY_ITSSI); if (B43_DEBUG) { /* Current-Idle-TSSI sanity check. */ @@ -2664,7 +2664,7 @@ static void b43_gphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) tmp = b43_phy_read(dev, B43_PHY_BBANDCFG); tmp &= ~B43_PHY_BBANDCFG_RXANT; - tmp |= (autodiv ? B43_ANTENNA_AUTO0 : antenna) + tmp |= (autodiv ? B43_ANTENNA_AUTO1 : antenna) << B43_PHY_BBANDCFG_RXANT_SHIFT; b43_phy_write(dev, B43_PHY_BBANDCFG, tmp); diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c index ea0d3a3a6a64..1ab00b034cbd 100644 --- a/drivers/net/wireless/b43/phy_lp.c +++ b/drivers/net/wireless/b43/phy_lp.c @@ -29,6 +29,25 @@ #include "tables_lpphy.h" +static inline u16 channel2freq_lp(u8 channel) +{ + if (channel < 14) + return (2407 + 5 * channel); + else if (channel == 14) + return 2484; + else if (channel < 184) + return (5000 + 5 * channel); + else + return (4000 + 5 * channel); +} + +static unsigned int b43_lpphy_op_get_default_chan(struct b43_wldev *dev) +{ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + return 1; + return 36; +} + static int b43_lpphy_op_allocate(struct b43_wldev *dev) { struct b43_phy_lp *lpphy; @@ -59,14 +78,325 @@ static void b43_lpphy_op_free(struct b43_wldev *dev) dev->phy.lp = NULL; } +static void lpphy_read_band_sprom(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + struct ssb_bus *bus = dev->dev->bus; + u16 cckpo, maxpwr; + u32 ofdmpo; + int i; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + lpphy->tx_isolation_med_band = bus->sprom.tri2g; + lpphy->bx_arch = bus->sprom.bxa2g; + lpphy->rx_pwr_offset = bus->sprom.rxpo2g; + lpphy->rssi_vf = bus->sprom.rssismf2g; + lpphy->rssi_vc = bus->sprom.rssismc2g; + lpphy->rssi_gs = bus->sprom.rssisav2g; + lpphy->txpa[0] = bus->sprom.pa0b0; + lpphy->txpa[1] = bus->sprom.pa0b1; + lpphy->txpa[2] = bus->sprom.pa0b2; + maxpwr = bus->sprom.maxpwr_bg; + lpphy->max_tx_pwr_med_band = maxpwr; + cckpo = bus->sprom.cck2gpo; + ofdmpo = bus->sprom.ofdm2gpo; + if (cckpo) { + for (i = 0; i < 4; i++) { + lpphy->tx_max_rate[i] = + maxpwr - (ofdmpo & 0xF) * 2; + ofdmpo >>= 4; + } + ofdmpo = bus->sprom.ofdm2gpo; + for (i = 4; i < 15; i++) { + lpphy->tx_max_rate[i] = + maxpwr - (ofdmpo & 0xF) * 2; + ofdmpo >>= 4; + } + } else { + ofdmpo &= 0xFF; + for (i = 0; i < 4; i++) + lpphy->tx_max_rate[i] = maxpwr; + for (i = 4; i < 15; i++) + lpphy->tx_max_rate[i] = maxpwr - ofdmpo; + } + } else { /* 5GHz */ + lpphy->tx_isolation_low_band = bus->sprom.tri5gl; + lpphy->tx_isolation_med_band = bus->sprom.tri5g; + lpphy->tx_isolation_hi_band = bus->sprom.tri5gh; + lpphy->bx_arch = bus->sprom.bxa5g; + lpphy->rx_pwr_offset = bus->sprom.rxpo5g; + lpphy->rssi_vf = bus->sprom.rssismf5g; + lpphy->rssi_vc = bus->sprom.rssismc5g; + lpphy->rssi_gs = bus->sprom.rssisav5g; + lpphy->txpa[0] = bus->sprom.pa1b0; + lpphy->txpa[1] = bus->sprom.pa1b1; + lpphy->txpa[2] = bus->sprom.pa1b2; + lpphy->txpal[0] = bus->sprom.pa1lob0; + lpphy->txpal[1] = bus->sprom.pa1lob1; + lpphy->txpal[2] = bus->sprom.pa1lob2; + lpphy->txpah[0] = bus->sprom.pa1hib0; + lpphy->txpah[1] = bus->sprom.pa1hib1; + lpphy->txpah[2] = bus->sprom.pa1hib2; + maxpwr = bus->sprom.maxpwr_al; + ofdmpo = bus->sprom.ofdm5glpo; + lpphy->max_tx_pwr_low_band = maxpwr; + for (i = 4; i < 12; i++) { + lpphy->tx_max_ratel[i] = maxpwr - (ofdmpo & 0xF) * 2; + ofdmpo >>= 4; + } + maxpwr = bus->sprom.maxpwr_a; + ofdmpo = bus->sprom.ofdm5gpo; + lpphy->max_tx_pwr_med_band = maxpwr; + for (i = 4; i < 12; i++) { + lpphy->tx_max_rate[i] = maxpwr - (ofdmpo & 0xF) * 2; + ofdmpo >>= 4; + } + maxpwr = bus->sprom.maxpwr_ah; + ofdmpo = bus->sprom.ofdm5ghpo; + lpphy->max_tx_pwr_hi_band = maxpwr; + for (i = 4; i < 12; i++) { + lpphy->tx_max_rateh[i] = maxpwr - (ofdmpo & 0xF) * 2; + ofdmpo >>= 4; + } + } +} + +static void lpphy_adjust_gain_table(struct b43_wldev *dev, u32 freq) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + u16 temp[3]; + u16 isolation; + + B43_WARN_ON(dev->phy.rev >= 2); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + isolation = lpphy->tx_isolation_med_band; + else if (freq <= 5320) + isolation = lpphy->tx_isolation_low_band; + else if (freq <= 5700) + isolation = lpphy->tx_isolation_med_band; + else + isolation = lpphy->tx_isolation_hi_band; + + temp[0] = ((isolation - 26) / 12) << 12; + temp[1] = temp[0] + 0x1000; + temp[2] = temp[0] + 0x2000; + + b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), 3, temp); + b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), 3, temp); +} + static void lpphy_table_init(struct b43_wldev *dev) { - //TODO + u32 freq = channel2freq_lp(b43_lpphy_op_get_default_chan(dev)); + + if (dev->phy.rev < 2) + lpphy_rev0_1_table_init(dev); + else + lpphy_rev2plus_table_init(dev); + + lpphy_init_tx_gain_table(dev); + + if (dev->phy.rev < 2) + lpphy_adjust_gain_table(dev, freq); } static void lpphy_baseband_rev0_1_init(struct b43_wldev *dev) { - B43_WARN_ON(1);//TODO rev < 2 not supported, yet. + struct ssb_bus *bus = dev->dev->bus; + struct b43_phy_lp *lpphy = dev->phy.lp; + u16 tmp, tmp2; + + b43_phy_mask(dev, B43_LPPHY_AFE_DAC_CTL, 0xF7FF); + b43_phy_write(dev, B43_LPPHY_AFE_CTL, 0); + b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, 0); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, 0); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0); + b43_phy_set(dev, B43_LPPHY_AFE_DAC_CTL, 0x0004); + b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0x0078); + b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x5800); + b43_phy_write(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x0016); + b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_0, 0xFFF8, 0x0004); + b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0x00FF, 0x5400); + b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0x00FF, 0x2400); + b43_phy_maskset(dev, B43_LPPHY_LOWGAINDB, 0x00FF, 0x2100); + b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0xFF00, 0x0006); + b43_phy_mask(dev, B43_LPPHY_RX_RADIO_CTL, 0xFFFE); + b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x0005); + b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFC1F, 0x0180); + b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0x83FF, 0x3C00); + b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFFF0, 0x0005); + b43_phy_maskset(dev, B43_LPPHY_GAIN_MISMATCH_LIMIT, 0xFFC0, 0x001A); + b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0xFF00, 0x00B3); + b43_phy_maskset(dev, B43_LPPHY_CRS_ED_THRESH, 0x00FF, 0xAD00); + b43_phy_maskset(dev, B43_LPPHY_INPUT_PWRDB, + 0xFF00, lpphy->rx_pwr_offset); + if ((bus->sprom.boardflags_lo & B43_BFL_FEM) && + ((b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) || + (bus->sprom.boardflags_hi & B43_BFH_PAREF))) { + ssb_pmu_set_ldo_voltage(&bus->chipco, LDO_PAREF, 0x28); + ssb_pmu_set_ldo_paref(&bus->chipco, true); + if (dev->phy.rev == 0) { + b43_phy_maskset(dev, B43_LPPHY_LP_RF_SIGNAL_LUT, + 0xFFCF, 0x0010); + } + b43_lptab_write(dev, B43_LPTAB16(11, 7), 60); + } else { + ssb_pmu_set_ldo_paref(&bus->chipco, false); + b43_phy_maskset(dev, B43_LPPHY_LP_RF_SIGNAL_LUT, + 0xFFCF, 0x0020); + b43_lptab_write(dev, B43_LPTAB16(11, 7), 100); + } + tmp = lpphy->rssi_vf | lpphy->rssi_vc << 4 | 0xA000; + b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_0, tmp); + if (bus->sprom.boardflags_hi & B43_BFH_RSSIINV) + b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x0AAA); + else + b43_phy_maskset(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0xF000, 0x02AA); + b43_lptab_write(dev, B43_LPTAB16(11, 1), 24); + b43_phy_maskset(dev, B43_LPPHY_RX_RADIO_CTL, + 0xFFF9, (lpphy->bx_arch << 1)); + if (dev->phy.rev == 1 && + (bus->sprom.boardflags_hi & B43_BFH_FEM_BT)) { + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0x3F00, 0x0900); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0B00); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0400); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0B00); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_5, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_5, 0xC0FF, 0x0900); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_6, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_6, 0xC0FF, 0x0B00); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_7, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_7, 0xC0FF, 0x0900); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_8, 0xC0FF, 0x0B00); + } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ || + (bus->boardinfo.type == 0x048A) || ((dev->phy.rev == 0) && + (bus->sprom.boardflags_lo & B43_BFL_FEM))) { + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0001); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0400); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0001); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0500); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0002); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0800); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0002); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0A00); + } else if (dev->phy.rev == 1 || + (bus->sprom.boardflags_lo & B43_BFL_FEM)) { + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x0004); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0800); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x0004); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0C00); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0002); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0100); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0002); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0300); + } else { + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_1, 0xC0FF, 0x0900); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xFFC0, 0x000A); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_2, 0xC0FF, 0x0B00); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xFFC0, 0x0006); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_3, 0xC0FF, 0x0500); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xFFC0, 0x0006); + b43_phy_maskset(dev, B43_LPPHY_TR_LOOKUP_4, 0xC0FF, 0x0700); + } + if (dev->phy.rev == 1 && (bus->sprom.boardflags_hi & B43_BFH_PAREF)) { + b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_5, B43_LPPHY_TR_LOOKUP_1); + b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_6, B43_LPPHY_TR_LOOKUP_2); + b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_7, B43_LPPHY_TR_LOOKUP_3); + b43_phy_copy(dev, B43_LPPHY_TR_LOOKUP_8, B43_LPPHY_TR_LOOKUP_4); + } + if ((bus->sprom.boardflags_hi & B43_BFH_FEM_BT) && + (bus->chip_id == 0x5354) && + (bus->chip_package == SSB_CHIPPACK_BCM4712S)) { + b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x0006); + b43_phy_write(dev, B43_LPPHY_GPIO_SELECT, 0x0005); + b43_phy_write(dev, B43_LPPHY_GPIO_OUTEN, 0xFFFF); + //FIXME the Broadcom driver caches & delays this HF write! + b43_hf_write(dev, b43_hf_read(dev) | B43_HF_PR45960W); + } + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_phy_set(dev, B43_LPPHY_LP_PHY_CTL, 0x8000); + b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x0040); + b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0x00FF, 0xA400); + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xF0FF, 0x0B00); + b43_phy_maskset(dev, B43_LPPHY_SYNCPEAKCNT, 0xFFF8, 0x0007); + b43_phy_maskset(dev, B43_LPPHY_DSSS_CONFIRM_CNT, 0xFFF8, 0x0003); + b43_phy_maskset(dev, B43_LPPHY_DSSS_CONFIRM_CNT, 0xFFC7, 0x0020); + b43_phy_mask(dev, B43_LPPHY_IDLEAFTERPKTRXTO, 0x00FF); + } else { /* 5GHz */ + b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0x7FFF); + b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFBF); + } + if (dev->phy.rev == 1) { + tmp = b43_phy_read(dev, B43_LPPHY_CLIPCTRTHRESH); + tmp2 = (tmp & 0x03E0) >> 5; + tmp2 |= tmp2 << 5; + b43_phy_write(dev, B43_LPPHY_4C3, tmp2); + tmp = b43_phy_read(dev, B43_LPPHY_GAINDIRECTMISMATCH); + tmp2 = (tmp & 0x1F00) >> 8; + tmp2 |= tmp2 << 5; + b43_phy_write(dev, B43_LPPHY_4C4, tmp2); + tmp = b43_phy_read(dev, B43_LPPHY_VERYLOWGAINDB); + tmp2 = tmp & 0x00FF; + tmp2 |= tmp << 8; + b43_phy_write(dev, B43_LPPHY_4C5, tmp2); + } +} + +static void lpphy_save_dig_flt_state(struct b43_wldev *dev) +{ + static const u16 addr[] = { + B43_PHY_OFDM(0xC1), + B43_PHY_OFDM(0xC2), + B43_PHY_OFDM(0xC3), + B43_PHY_OFDM(0xC4), + B43_PHY_OFDM(0xC5), + B43_PHY_OFDM(0xC6), + B43_PHY_OFDM(0xC7), + B43_PHY_OFDM(0xC8), + B43_PHY_OFDM(0xCF), + }; + + static const u16 coefs[] = { + 0xDE5E, 0xE832, 0xE331, 0x4D26, + 0x0026, 0x1420, 0x0020, 0xFE08, + 0x0008, + }; + + struct b43_phy_lp *lpphy = dev->phy.lp; + int i; + + for (i = 0; i < ARRAY_SIZE(addr); i++) { + lpphy->dig_flt_state[i] = b43_phy_read(dev, addr[i]); + b43_phy_write(dev, addr[i], coefs[i]); + } +} + +static void lpphy_restore_dig_flt_state(struct b43_wldev *dev) +{ + static const u16 addr[] = { + B43_PHY_OFDM(0xC1), + B43_PHY_OFDM(0xC2), + B43_PHY_OFDM(0xC3), + B43_PHY_OFDM(0xC4), + B43_PHY_OFDM(0xC5), + B43_PHY_OFDM(0xC6), + B43_PHY_OFDM(0xC7), + B43_PHY_OFDM(0xC8), + B43_PHY_OFDM(0xCF), + }; + + struct b43_phy_lp *lpphy = dev->phy.lp; + int i; + + for (i = 0; i < ARRAY_SIZE(addr); i++) + b43_phy_write(dev, addr[i], lpphy->dig_flt_state[i]); } static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev) @@ -83,7 +413,7 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev) b43_phy_write(dev, B43_PHY_OFDM(0xF9), 0); b43_phy_write(dev, B43_LPPHY_TR_LOOKUP_1, 0); b43_phy_set(dev, B43_LPPHY_ADC_COMPENSATION_CTL, 0x10); - b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0x78); + b43_phy_maskset(dev, B43_LPPHY_OFDMSYNCTHRESH0, 0xFF00, 0xB4); b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xF8FF, 0x200); b43_phy_maskset(dev, B43_LPPHY_DCOFFSETTRANSIENT, 0xFF00, 0x7F); b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xFF0F, 0x40); @@ -91,7 +421,12 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev) b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x4000); b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x2000); b43_phy_set(dev, B43_PHY_OFDM(0x10A), 0x1); - b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x10); + if (bus->boardinfo.rev >= 0x18) { + b43_lptab_write(dev, B43_LPTAB32(17, 65), 0xEC); + b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x14); + } else { + b43_phy_maskset(dev, B43_PHY_OFDM(0x10A), 0xFF01, 0x10); + } b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0xFF00, 0xF4); b43_phy_maskset(dev, B43_PHY_OFDM(0xDF), 0x00FF, 0xF100); b43_phy_write(dev, B43_LPPHY_CLIPTHRESH, 0x48); @@ -100,7 +435,7 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev) b43_phy_maskset(dev, B43_LPPHY_PWR_THRESH1, 0xFFF0, 0x9); b43_phy_mask(dev, B43_LPPHY_GAINDIRECTMISMATCH, ~0xF); b43_phy_maskset(dev, B43_LPPHY_VERYLOWGAINDB, 0x00FF, 0x5500); - b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xF81F, 0xA0); + b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFC1F, 0xA0); b43_phy_maskset(dev, B43_LPPHY_GAINDIRECTMISMATCH, 0xE0FF, 0x300); b43_phy_maskset(dev, B43_LPPHY_HIGAINDB, 0x00FF, 0x2A00); if ((bus->chip_id == 0x4325) && (bus->chip_rev == 0)) { @@ -121,8 +456,10 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev) b43_phy_maskset(dev, B43_LPPHY_CLIPCTRTHRESH, 0xFFE0, 0x12); b43_phy_maskset(dev, B43_LPPHY_GAINMISMATCH, 0x0FFF, 0x9000); - b43_lptab_write(dev, B43_LPTAB16(0x08, 0x14), 0); - b43_lptab_write(dev, B43_LPTAB16(0x08, 0x12), 0x40); + if ((bus->chip_id == 0x4325) && (bus->chip_rev == 0)) { + b43_lptab_write(dev, B43_LPTAB16(0x08, 0x14), 0); + b43_lptab_write(dev, B43_LPTAB16(0x08, 0x12), 0x40); + } if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x40); @@ -130,6 +467,7 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev) b43_phy_maskset(dev, B43_LPPHY_SYNCPEAKCNT, 0xFFF8, 0x6); b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0x00FF, 0x9D00); b43_phy_maskset(dev, B43_LPPHY_MINPWR_LEVEL, 0xFF00, 0xA1); + b43_phy_mask(dev, B43_LPPHY_IDLEAFTERPKTRXTO, 0x00FF); } else /* 5GHz */ b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, ~0x40); @@ -142,6 +480,14 @@ static void lpphy_baseband_rev2plus_init(struct b43_wldev *dev) b43_phy_write(dev, B43_LPPHY_AFE_RSSI_CTL_1, 0x2000 | ((u16)lpphy->rssi_gs << 10) | ((u16)lpphy->rssi_vc << 4) | lpphy->rssi_vf); + + if ((bus->chip_id == 0x4325) && (bus->chip_rev == 0)) { + b43_phy_set(dev, B43_LPPHY_AFE_ADC_CTL_0, 0x1C); + b43_phy_maskset(dev, B43_LPPHY_AFE_CTL, 0x00FF, 0x8800); + b43_phy_maskset(dev, B43_LPPHY_AFE_ADC_CTL_1, 0xFC3C, 0x0400); + } + + lpphy_save_dig_flt_state(dev); } static void lpphy_baseband_init(struct b43_wldev *dev) @@ -161,8 +507,9 @@ struct b2062_freqdata { /* Initialize the 2062 radio. */ static void lpphy_2062_init(struct b43_wldev *dev) { + struct b43_phy_lp *lpphy = dev->phy.lp; struct ssb_bus *bus = dev->dev->bus; - u32 crystalfreq, pdiv, tmp, ref; + u32 crystalfreq, tmp, ref; unsigned int i; const struct b2062_freqdata *fd = NULL; @@ -186,10 +533,15 @@ static void lpphy_2062_init(struct b43_wldev *dev) b43_radio_write(dev, B2062_N_TX_CTL3, 0); b43_radio_write(dev, B2062_N_TX_CTL4, 0); b43_radio_write(dev, B2062_N_TX_CTL5, 0); + b43_radio_write(dev, B2062_N_TX_CTL6, 0); b43_radio_write(dev, B2062_N_PDN_CTL0, 0x40); b43_radio_write(dev, B2062_N_PDN_CTL0, 0); b43_radio_write(dev, B2062_N_CALIB_TS, 0x10); b43_radio_write(dev, B2062_N_CALIB_TS, 0); + if (dev->phy.rev > 0) { + b43_radio_write(dev, B2062_S_BG_CTL1, + (b43_radio_read(dev, B2062_N_COMM2) >> 1) | 0x80); + } if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) b43_radio_set(dev, B2062_N_TSSI_CTL0, 0x1); else @@ -201,23 +553,27 @@ static void lpphy_2062_init(struct b43_wldev *dev) B43_WARN_ON(!(bus->chipco.capabilities & SSB_CHIPCO_CAP_PMU)); B43_WARN_ON(crystalfreq == 0); - if (crystalfreq >= 30000000) { - pdiv = 1; + if (crystalfreq <= 30000000) { + lpphy->pdiv = 1; b43_radio_mask(dev, B2062_S_RFPLL_CTL1, 0xFFFB); } else { - pdiv = 2; + lpphy->pdiv = 2; b43_radio_set(dev, B2062_S_RFPLL_CTL1, 0x4); } - tmp = (800000000 * pdiv + crystalfreq) / (32000000 * pdiv); - tmp = (tmp - 1) & 0xFF; + tmp = (((800000000 * lpphy->pdiv + crystalfreq) / + (2 * crystalfreq)) - 8) & 0xFF; + b43_radio_write(dev, B2062_S_RFPLL_CTL7, tmp); + + tmp = (((100 * crystalfreq + 16000000 * lpphy->pdiv) / + (32000000 * lpphy->pdiv)) - 1) & 0xFF; b43_radio_write(dev, B2062_S_RFPLL_CTL18, tmp); - tmp = (2 * crystalfreq + 1000000 * pdiv) / (2000000 * pdiv); - tmp = ((tmp & 0xFF) - 1) & 0xFFFF; + tmp = (((2 * crystalfreq + 1000000 * lpphy->pdiv) / + (2000000 * lpphy->pdiv)) - 1) & 0xFF; b43_radio_write(dev, B2062_S_RFPLL_CTL19, tmp); - ref = (1000 * pdiv + 2 * crystalfreq) / (2000 * pdiv); + ref = (1000 * lpphy->pdiv + 2 * crystalfreq) / (2000 * lpphy->pdiv); ref &= 0xFFFF; for (i = 0; i < ARRAY_SIZE(freqdata_tab); i++) { if (ref < freqdata_tab[i].freq) { @@ -241,12 +597,78 @@ static void lpphy_2062_init(struct b43_wldev *dev) /* Initialize the 2063 radio. */ static void lpphy_2063_init(struct b43_wldev *dev) { - //TODO + b2063_upload_init_table(dev); + b43_radio_write(dev, B2063_LOGEN_SP5, 0); + b43_radio_set(dev, B2063_COMM8, 0x38); + b43_radio_write(dev, B2063_REG_SP1, 0x56); + b43_radio_mask(dev, B2063_RX_BB_CTL2, ~0x2); + b43_radio_write(dev, B2063_PA_SP7, 0); + b43_radio_write(dev, B2063_TX_RF_SP6, 0x20); + b43_radio_write(dev, B2063_TX_RF_SP9, 0x40); + if (dev->phy.rev == 2) { + b43_radio_write(dev, B2063_PA_SP3, 0xa0); + b43_radio_write(dev, B2063_PA_SP4, 0xa0); + b43_radio_write(dev, B2063_PA_SP2, 0x18); + } else { + b43_radio_write(dev, B2063_PA_SP3, 0x20); + b43_radio_write(dev, B2063_PA_SP2, 0x20); + } } +struct lpphy_stx_table_entry { + u16 phy_offset; + u16 phy_shift; + u16 rf_addr; + u16 rf_shift; + u16 mask; +}; + +static const struct lpphy_stx_table_entry lpphy_stx_table[] = { + { .phy_offset = 2, .phy_shift = 6, .rf_addr = 0x3d, .rf_shift = 3, .mask = 0x01, }, + { .phy_offset = 1, .phy_shift = 12, .rf_addr = 0x4c, .rf_shift = 1, .mask = 0x01, }, + { .phy_offset = 1, .phy_shift = 8, .rf_addr = 0x50, .rf_shift = 0, .mask = 0x7f, }, + { .phy_offset = 0, .phy_shift = 8, .rf_addr = 0x44, .rf_shift = 0, .mask = 0xff, }, + { .phy_offset = 1, .phy_shift = 0, .rf_addr = 0x4a, .rf_shift = 0, .mask = 0xff, }, + { .phy_offset = 0, .phy_shift = 4, .rf_addr = 0x4d, .rf_shift = 0, .mask = 0xff, }, + { .phy_offset = 1, .phy_shift = 4, .rf_addr = 0x4e, .rf_shift = 0, .mask = 0xff, }, + { .phy_offset = 0, .phy_shift = 12, .rf_addr = 0x4f, .rf_shift = 0, .mask = 0x0f, }, + { .phy_offset = 1, .phy_shift = 0, .rf_addr = 0x4f, .rf_shift = 4, .mask = 0x0f, }, + { .phy_offset = 3, .phy_shift = 0, .rf_addr = 0x49, .rf_shift = 0, .mask = 0x0f, }, + { .phy_offset = 4, .phy_shift = 3, .rf_addr = 0x46, .rf_shift = 4, .mask = 0x07, }, + { .phy_offset = 3, .phy_shift = 15, .rf_addr = 0x46, .rf_shift = 0, .mask = 0x01, }, + { .phy_offset = 4, .phy_shift = 0, .rf_addr = 0x46, .rf_shift = 1, .mask = 0x07, }, + { .phy_offset = 3, .phy_shift = 8, .rf_addr = 0x48, .rf_shift = 4, .mask = 0x07, }, + { .phy_offset = 3, .phy_shift = 11, .rf_addr = 0x48, .rf_shift = 0, .mask = 0x0f, }, + { .phy_offset = 3, .phy_shift = 4, .rf_addr = 0x49, .rf_shift = 4, .mask = 0x0f, }, + { .phy_offset = 2, .phy_shift = 15, .rf_addr = 0x45, .rf_shift = 0, .mask = 0x01, }, + { .phy_offset = 5, .phy_shift = 13, .rf_addr = 0x52, .rf_shift = 4, .mask = 0x07, }, + { .phy_offset = 6, .phy_shift = 0, .rf_addr = 0x52, .rf_shift = 7, .mask = 0x01, }, + { .phy_offset = 5, .phy_shift = 3, .rf_addr = 0x41, .rf_shift = 5, .mask = 0x07, }, + { .phy_offset = 5, .phy_shift = 6, .rf_addr = 0x41, .rf_shift = 0, .mask = 0x0f, }, + { .phy_offset = 5, .phy_shift = 10, .rf_addr = 0x42, .rf_shift = 5, .mask = 0x07, }, + { .phy_offset = 4, .phy_shift = 15, .rf_addr = 0x42, .rf_shift = 0, .mask = 0x01, }, + { .phy_offset = 5, .phy_shift = 0, .rf_addr = 0x42, .rf_shift = 1, .mask = 0x07, }, + { .phy_offset = 4, .phy_shift = 11, .rf_addr = 0x43, .rf_shift = 4, .mask = 0x0f, }, + { .phy_offset = 4, .phy_shift = 7, .rf_addr = 0x43, .rf_shift = 0, .mask = 0x0f, }, + { .phy_offset = 4, .phy_shift = 6, .rf_addr = 0x45, .rf_shift = 1, .mask = 0x01, }, + { .phy_offset = 2, .phy_shift = 7, .rf_addr = 0x40, .rf_shift = 4, .mask = 0x0f, }, + { .phy_offset = 2, .phy_shift = 11, .rf_addr = 0x40, .rf_shift = 0, .mask = 0x0f, }, +}; + static void lpphy_sync_stx(struct b43_wldev *dev) { - //TODO + const struct lpphy_stx_table_entry *e; + unsigned int i; + u16 tmp; + + for (i = 0; i < ARRAY_SIZE(lpphy_stx_table); i++) { + e = &lpphy_stx_table[i]; + tmp = b43_radio_read(dev, e->rf_addr); + tmp >>= e->rf_shift; + tmp <<= e->phy_shift; + b43_phy_maskset(dev, B43_PHY_OFDM(0xF2 + e->phy_offset), + ~(e->mask << e->phy_shift), tmp); + } } static void lpphy_radio_init(struct b43_wldev *dev) @@ -257,17 +679,381 @@ static void lpphy_radio_init(struct b43_wldev *dev) b43_phy_mask(dev, B43_LPPHY_FOURWIRE_CTL, 0xFFFD); udelay(1); - if (dev->phy.rev < 2) { + if (dev->phy.radio_ver == 0x2062) { lpphy_2062_init(dev); } else { lpphy_2063_init(dev); lpphy_sync_stx(dev); b43_phy_write(dev, B43_PHY_OFDM(0xF0), 0x5F80); b43_phy_write(dev, B43_PHY_OFDM(0xF1), 0); - //TODO Do something on the backplane + if (dev->dev->bus->chip_id == 0x4325) { + // TODO SSB PMU recalibration + } + } +} + +struct lpphy_iq_est { u32 iq_prod, i_pwr, q_pwr; }; + +static void lpphy_set_rc_cap(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + + u8 rc_cap = (lpphy->rc_cap & 0x1F) >> 1; + + if (dev->phy.rev == 1) //FIXME check channel 14! + rc_cap = min_t(u8, rc_cap + 5, 15); + + b43_radio_write(dev, B2062_N_RXBB_CALIB2, + max_t(u8, lpphy->rc_cap - 4, 0x80)); + b43_radio_write(dev, B2062_N_TX_CTL_A, rc_cap | 0x80); + b43_radio_write(dev, B2062_S_RXG_CNT16, + ((lpphy->rc_cap & 0x1F) >> 2) | 0x80); +} + +static u8 lpphy_get_bb_mult(struct b43_wldev *dev) +{ + return (b43_lptab_read(dev, B43_LPTAB16(0, 87)) & 0xFF00) >> 8; +} + +static void lpphy_set_bb_mult(struct b43_wldev *dev, u8 bb_mult) +{ + b43_lptab_write(dev, B43_LPTAB16(0, 87), (u16)bb_mult << 8); +} + +static void lpphy_set_deaf(struct b43_wldev *dev, bool user) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + + if (user) + lpphy->crs_usr_disable = 1; + else + lpphy->crs_sys_disable = 1; + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFF1F, 0x80); +} + +static void lpphy_clear_deaf(struct b43_wldev *dev, bool user) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + + if (user) + lpphy->crs_usr_disable = 0; + else + lpphy->crs_sys_disable = 0; + + if (!lpphy->crs_usr_disable && !lpphy->crs_sys_disable) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, + 0xFF1F, 0x60); + else + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, + 0xFF1F, 0x20); } } +static void lpphy_disable_crs(struct b43_wldev *dev, bool user) +{ + lpphy_set_deaf(dev, user); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, 0x1); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFB); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x4); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFF7); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x10); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFDF); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x20); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFBF); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x7); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x38); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFF3F); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0x100); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFDFF); + b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL0, 0); + b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL1, 1); + b43_phy_write(dev, B43_LPPHY_PS_CTL_OVERRIDE_VAL2, 0x20); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFBFF); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xF7FF); + b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, 0); + b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, 0x45AF); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, 0x3FF); +} + +static void lpphy_restore_crs(struct b43_wldev *dev, bool user) +{ + lpphy_clear_deaf(dev, user); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFF80); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFC00); +} + +struct lpphy_tx_gains { u16 gm, pga, pad, dac; }; + +static struct lpphy_tx_gains lpphy_get_tx_gains(struct b43_wldev *dev) +{ + struct lpphy_tx_gains gains; + u16 tmp; + + gains.dac = (b43_phy_read(dev, B43_LPPHY_AFE_DAC_CTL) & 0x380) >> 7; + if (dev->phy.rev < 2) { + tmp = b43_phy_read(dev, + B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL) & 0x7FF; + gains.gm = tmp & 0x0007; + gains.pga = (tmp & 0x0078) >> 3; + gains.pad = (tmp & 0x780) >> 7; + } else { + tmp = b43_phy_read(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL); + gains.pad = b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0xFF; + gains.gm = tmp & 0xFF; + gains.pga = (tmp >> 8) & 0xFF; + } + + return gains; +} + +static void lpphy_set_dac_gain(struct b43_wldev *dev, u16 dac) +{ + u16 ctl = b43_phy_read(dev, B43_LPPHY_AFE_DAC_CTL) & 0xC7F; + ctl |= dac << 7; + b43_phy_maskset(dev, B43_LPPHY_AFE_DAC_CTL, 0xF000, ctl); +} + +static void lpphy_set_tx_gains(struct b43_wldev *dev, + struct lpphy_tx_gains gains) +{ + u16 rf_gain, pa_gain; + + if (dev->phy.rev < 2) { + rf_gain = (gains.pad << 7) | (gains.pga << 3) | gains.gm; + b43_phy_maskset(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, + 0xF800, rf_gain); + } else { + pa_gain = b43_phy_read(dev, B43_PHY_OFDM(0xFB)) & 0x1FC0; + pa_gain <<= 2; + b43_phy_write(dev, B43_LPPHY_TX_GAIN_CTL_OVERRIDE_VAL, + (gains.pga << 8) | gains.gm); + b43_phy_maskset(dev, B43_PHY_OFDM(0xFB), + 0x8000, gains.pad | pa_gain); + b43_phy_write(dev, B43_PHY_OFDM(0xFC), + (gains.pga << 8) | gains.gm); + b43_phy_maskset(dev, B43_PHY_OFDM(0xFD), + 0x8000, gains.pad | pa_gain); + } + lpphy_set_dac_gain(dev, gains.dac); + if (dev->phy.rev < 2) { + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF, 1 << 8); + } else { + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFF7F, 1 << 7); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2, 0xBFFF, 1 << 14); + } + b43_phy_maskset(dev, B43_LPPHY_AFE_CTL_OVR, 0xFFBF, 1 << 6); +} + +static void lpphy_rev0_1_set_rx_gain(struct b43_wldev *dev, u32 gain) +{ + u16 trsw = gain & 0x1; + u16 lna = (gain & 0xFFFC) | ((gain & 0xC) >> 2); + u16 ext_lna = (gain & 2) >> 1; + + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFE, trsw); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, + 0xFBFF, ext_lna << 10); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, + 0xF7FF, ext_lna << 11); + b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, lna); +} + +static void lpphy_rev2plus_set_rx_gain(struct b43_wldev *dev, u32 gain) +{ + u16 low_gain = gain & 0xFFFF; + u16 high_gain = (gain >> 16) & 0xF; + u16 ext_lna = (gain >> 21) & 0x1; + u16 trsw = ~(gain >> 20) & 0x1; + u16 tmp; + + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFE, trsw); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, + 0xFDFF, ext_lna << 9); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, + 0xFBFF, ext_lna << 10); + b43_phy_write(dev, B43_LPPHY_RX_GAIN_CTL_OVERRIDE_VAL, low_gain); + b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFF0, high_gain); + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + tmp = (gain >> 2) & 0x3; + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, + 0xE7FF, tmp<<11); + b43_phy_maskset(dev, B43_PHY_OFDM(0xE6), 0xFFE7, tmp << 3); + } +} + +static void lpphy_disable_rx_gain_override(struct b43_wldev *dev) +{ + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFFE); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFEF); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xFFBF); + if (dev->phy.rev >= 2) { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFEFF); + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFBFF); + b43_phy_mask(dev, B43_PHY_OFDM(0xE5), 0xFFF7); + } + } else { + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_2, 0xFDFF); + } +} + +static void lpphy_enable_rx_gain_override(struct b43_wldev *dev) +{ + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x1); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x10); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x40); + if (dev->phy.rev >= 2) { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x100); + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x400); + b43_phy_set(dev, B43_PHY_OFDM(0xE5), 0x8); + } + } else { + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_2, 0x200); + } +} + +static void lpphy_set_rx_gain(struct b43_wldev *dev, u32 gain) +{ + if (dev->phy.rev < 2) + lpphy_rev0_1_set_rx_gain(dev, gain); + else + lpphy_rev2plus_set_rx_gain(dev, gain); + lpphy_enable_rx_gain_override(dev); +} + +static void lpphy_set_rx_gain_by_index(struct b43_wldev *dev, u16 idx) +{ + u32 gain = b43_lptab_read(dev, B43_LPTAB16(12, idx)); + lpphy_set_rx_gain(dev, gain); +} + +static void lpphy_stop_ddfs(struct b43_wldev *dev) +{ + b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0xFFFD); + b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0xFFDF); +} + +static void lpphy_run_ddfs(struct b43_wldev *dev, int i_on, int q_on, + int incr1, int incr2, int scale_idx) +{ + lpphy_stop_ddfs(dev); + b43_phy_mask(dev, B43_LPPHY_AFE_DDFS_POINTER_INIT, 0xFF80); + b43_phy_mask(dev, B43_LPPHY_AFE_DDFS_POINTER_INIT, 0x80FF); + b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS_INCR_INIT, 0xFF80, incr1); + b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS_INCR_INIT, 0x80FF, incr2 << 8); + b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFF7, i_on << 3); + b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFFEF, q_on << 4); + b43_phy_maskset(dev, B43_LPPHY_AFE_DDFS, 0xFF9F, scale_idx << 5); + b43_phy_mask(dev, B43_LPPHY_AFE_DDFS, 0xFFFB); + b43_phy_set(dev, B43_LPPHY_AFE_DDFS, 0x2); + b43_phy_set(dev, B43_LPPHY_LP_PHY_CTL, 0x20); +} + +static bool lpphy_rx_iq_est(struct b43_wldev *dev, u16 samples, u8 time, + struct lpphy_iq_est *iq_est) +{ + int i; + + b43_phy_mask(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFF7); + b43_phy_write(dev, B43_LPPHY_IQ_NUM_SMPLS_ADDR, samples); + b43_phy_maskset(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xFF00, time); + b43_phy_mask(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0xFEFF); + b43_phy_set(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR, 0x200); + + for (i = 0; i < 500; i++) { + if (!(b43_phy_read(dev, + B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200)) + break; + msleep(1); + } + + if ((b43_phy_read(dev, B43_LPPHY_IQ_ENABLE_WAIT_TIME_ADDR) & 0x200)) { + b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x8); + return false; + } + + iq_est->iq_prod = b43_phy_read(dev, B43_LPPHY_IQ_ACC_HI_ADDR); + iq_est->iq_prod <<= 16; + iq_est->iq_prod |= b43_phy_read(dev, B43_LPPHY_IQ_ACC_LO_ADDR); + + iq_est->i_pwr = b43_phy_read(dev, B43_LPPHY_IQ_I_PWR_ACC_HI_ADDR); + iq_est->i_pwr <<= 16; + iq_est->i_pwr |= b43_phy_read(dev, B43_LPPHY_IQ_I_PWR_ACC_LO_ADDR); + + iq_est->q_pwr = b43_phy_read(dev, B43_LPPHY_IQ_Q_PWR_ACC_HI_ADDR); + iq_est->q_pwr <<= 16; + iq_est->q_pwr |= b43_phy_read(dev, B43_LPPHY_IQ_Q_PWR_ACC_LO_ADDR); + + b43_phy_set(dev, B43_LPPHY_CRSGAIN_CTL, 0x8); + return true; +} + +static int lpphy_loopback(struct b43_wldev *dev) +{ + struct lpphy_iq_est iq_est; + int i, index = -1; + u32 tmp; + + memset(&iq_est, 0, sizeof(iq_est)); + + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xFFFC, 0x3); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x3); + b43_phy_set(dev, B43_LPPHY_AFE_CTL_OVR, 1); + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFFE); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x800); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x800); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x8); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x8); + b43_radio_write(dev, B2062_N_TX_CTL_A, 0x80); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_0, 0x80); + b43_phy_set(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0x80); + for (i = 0; i < 32; i++) { + lpphy_set_rx_gain_by_index(dev, i); + lpphy_run_ddfs(dev, 1, 1, 5, 5, 0); + if (!(lpphy_rx_iq_est(dev, 1000, 32, &iq_est))) + continue; + tmp = (iq_est.i_pwr + iq_est.q_pwr) / 1000; + if ((tmp > 4000) && (tmp < 10000)) { + index = i; + break; + } + } + lpphy_stop_ddfs(dev); + return index; +} + +/* Fixed-point division algorithm using only integer math. */ +static u32 lpphy_qdiv_roundup(u32 dividend, u32 divisor, u8 precision) +{ + u32 quotient, remainder; + + if (divisor == 0) + return 0; + + quotient = dividend / divisor; + remainder = dividend % divisor; + + while (precision > 0) { + quotient <<= 1; + if (remainder << 1 >= divisor) { + quotient++; + remainder = (remainder << 1) - divisor; + } + precision--; + } + + if (remainder << 1 >= divisor) + quotient++; + + return quotient; +} + /* Read the TX power control mode from hardware. */ static void lpphy_read_tx_pctl_mode_from_hardware(struct b43_wldev *dev) { @@ -322,9 +1108,9 @@ static void lpphy_set_tx_power_control(struct b43_wldev *dev, struct b43_phy_lp *lpphy = dev->phy.lp; enum b43_lpphy_txpctl_mode oldmode; - oldmode = lpphy->txpctl_mode; lpphy_read_tx_pctl_mode_from_hardware(dev); - if (lpphy->txpctl_mode == mode) + oldmode = lpphy->txpctl_mode; + if (oldmode == mode) return; lpphy->txpctl_mode = mode; @@ -345,13 +1131,186 @@ static void lpphy_set_tx_power_control(struct b43_wldev *dev, } if (dev->phy.rev >= 2) { if (mode == B43_LPPHY_TXPCTL_HW) - b43_phy_maskset(dev, B43_PHY_OFDM(0xD0), 0xFD, 0x2); + b43_phy_set(dev, B43_PHY_OFDM(0xD0), 0x2); else - b43_phy_maskset(dev, B43_PHY_OFDM(0xD0), 0xFD, 0); + b43_phy_mask(dev, B43_PHY_OFDM(0xD0), 0xFFFD); } lpphy_write_tx_pctl_mode_to_hardware(dev); } +static int b43_lpphy_op_switch_channel(struct b43_wldev *dev, + unsigned int new_channel); + +static void lpphy_rev0_1_rc_calib(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + struct lpphy_iq_est iq_est; + struct lpphy_tx_gains tx_gains; + static const u32 ideal_pwr_table[21] = { + 0x10000, 0x10557, 0x10e2d, 0x113e0, 0x10f22, 0x0ff64, + 0x0eda2, 0x0e5d4, 0x0efd1, 0x0fbe8, 0x0b7b8, 0x04b35, + 0x01a5e, 0x00a0b, 0x00444, 0x001fd, 0x000ff, 0x00088, + 0x0004c, 0x0002c, 0x0001a, + }; + bool old_txg_ovr; + u8 old_bbmult; + u16 old_rf_ovr, old_rf_ovrval, old_afe_ovr, old_afe_ovrval, + old_rf2_ovr, old_rf2_ovrval, old_phy_ctl; + enum b43_lpphy_txpctl_mode old_txpctl; + u32 normal_pwr, ideal_pwr, mean_sq_pwr, tmp = 0, mean_sq_pwr_min = 0; + int loopback, i, j, inner_sum, err; + + memset(&iq_est, 0, sizeof(iq_est)); + + err = b43_lpphy_op_switch_channel(dev, 7); + if (err) { + b43dbg(dev->wl, + "RC calib: Failed to switch to channel 7, error = %d\n", + err); + } + old_txg_ovr = !!(b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR) & 0x40); + old_bbmult = lpphy_get_bb_mult(dev); + if (old_txg_ovr) + tx_gains = lpphy_get_tx_gains(dev); + old_rf_ovr = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_0); + old_rf_ovrval = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_VAL_0); + old_afe_ovr = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVR); + old_afe_ovrval = b43_phy_read(dev, B43_LPPHY_AFE_CTL_OVRVAL); + old_rf2_ovr = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_2); + old_rf2_ovrval = b43_phy_read(dev, B43_LPPHY_RF_OVERRIDE_2_VAL); + old_phy_ctl = b43_phy_read(dev, B43_LPPHY_LP_PHY_CTL); + lpphy_read_tx_pctl_mode_from_hardware(dev); + old_txpctl = lpphy->txpctl_mode; + + lpphy_set_tx_power_control(dev, B43_LPPHY_TXPCTL_OFF); + lpphy_disable_crs(dev, true); + loopback = lpphy_loopback(dev); + if (loopback == -1) + goto finish; + lpphy_set_rx_gain_by_index(dev, loopback); + b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFFBF, 0x40); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFFF8, 0x1); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFFC7, 0x8); + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, 0xFF3F, 0xC0); + for (i = 128; i <= 159; i++) { + b43_radio_write(dev, B2062_N_RXBB_CALIB2, i); + inner_sum = 0; + for (j = 5; j <= 25; j++) { + lpphy_run_ddfs(dev, 1, 1, j, j, 0); + if (!(lpphy_rx_iq_est(dev, 1000, 32, &iq_est))) + goto finish; + mean_sq_pwr = iq_est.i_pwr + iq_est.q_pwr; + if (j == 5) + tmp = mean_sq_pwr; + ideal_pwr = ((ideal_pwr_table[j-5] >> 3) + 1) >> 1; + normal_pwr = lpphy_qdiv_roundup(mean_sq_pwr, tmp, 12); + mean_sq_pwr = ideal_pwr - normal_pwr; + mean_sq_pwr *= mean_sq_pwr; + inner_sum += mean_sq_pwr; + if ((i == 128) || (inner_sum < mean_sq_pwr_min)) { + lpphy->rc_cap = i; + mean_sq_pwr_min = inner_sum; + } + } + } + lpphy_stop_ddfs(dev); + +finish: + lpphy_restore_crs(dev, true); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, old_rf_ovrval); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_0, old_rf_ovr); + b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVRVAL, old_afe_ovrval); + b43_phy_write(dev, B43_LPPHY_AFE_CTL_OVR, old_afe_ovr); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2_VAL, old_rf2_ovrval); + b43_phy_write(dev, B43_LPPHY_RF_OVERRIDE_2, old_rf2_ovr); + b43_phy_write(dev, B43_LPPHY_LP_PHY_CTL, old_phy_ctl); + + lpphy_set_bb_mult(dev, old_bbmult); + if (old_txg_ovr) { + /* + * SPEC FIXME: The specs say "get_tx_gains" here, which is + * illogical. According to lwfinger, vendor driver v4.150.10.5 + * has a Set here, while v4.174.64.19 has a Get - regression in + * the vendor driver? This should be tested this once the code + * is testable. + */ + lpphy_set_tx_gains(dev, tx_gains); + } + lpphy_set_tx_power_control(dev, old_txpctl); + if (lpphy->rc_cap) + lpphy_set_rc_cap(dev); +} + +static void lpphy_rev2plus_rc_calib(struct b43_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000; + u8 tmp = b43_radio_read(dev, B2063_RX_BB_SP8) & 0xFF; + int i; + + b43_radio_write(dev, B2063_RX_BB_SP8, 0x0); + b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E); + b43_radio_mask(dev, B2063_PLL_SP1, 0xF7); + b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7C); + b43_radio_write(dev, B2063_RC_CALIB_CTL2, 0x15); + b43_radio_write(dev, B2063_RC_CALIB_CTL3, 0x70); + b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0x52); + b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x1); + b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7D); + + for (i = 0; i < 10000; i++) { + if (b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2) + break; + msleep(1); + } + + if (!(b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2)) + b43_radio_write(dev, B2063_RX_BB_SP8, tmp); + + tmp = b43_radio_read(dev, B2063_TX_BB_SP3) & 0xFF; + + b43_radio_write(dev, B2063_TX_BB_SP3, 0x0); + b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E); + b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7C); + b43_radio_write(dev, B2063_RC_CALIB_CTL2, 0x55); + b43_radio_write(dev, B2063_RC_CALIB_CTL3, 0x76); + + if (crystal_freq == 24000000) { + b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0xFC); + b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x0); + } else { + b43_radio_write(dev, B2063_RC_CALIB_CTL4, 0x13); + b43_radio_write(dev, B2063_RC_CALIB_CTL5, 0x1); + } + + b43_radio_write(dev, B2063_PA_SP7, 0x7D); + + for (i = 0; i < 10000; i++) { + if (b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2) + break; + msleep(1); + } + + if (!(b43_radio_read(dev, B2063_RC_CALIB_CTL6) & 0x2)) + b43_radio_write(dev, B2063_TX_BB_SP3, tmp); + + b43_radio_write(dev, B2063_RC_CALIB_CTL1, 0x7E); +} + +static void lpphy_calibrate_rc(struct b43_wldev *dev) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + + if (dev->phy.rev >= 2) { + lpphy_rev2plus_rc_calib(dev); + } else if (!lpphy->rc_cap) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + lpphy_rev0_1_rc_calib(dev); + } else { + lpphy_set_rc_cap(dev); + } +} + static void lpphy_set_tx_power_by_index(struct b43_wldev *dev, u8 index) { struct b43_phy_lp *lpphy = dev->phy.lp; @@ -423,32 +1382,110 @@ static void lpphy_calibration(struct b43_wldev *dev) b43_mac_enable(dev); } -/* Initialize TX power control */ -static void lpphy_tx_pctl_init(struct b43_wldev *dev) +static void lpphy_set_tssi_mux(struct b43_wldev *dev, enum tssi_mux_mode mode) { - if (0/*FIXME HWPCTL capable */) { - //TODO - } else { /* This device is only software TX power control capable. */ - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - //TODO + if (mode != TSSI_MUX_EXT) { + b43_radio_set(dev, B2063_PA_SP1, 0x2); + b43_phy_set(dev, B43_PHY_OFDM(0xF3), 0x1000); + b43_radio_write(dev, B2063_PA_CTL10, 0x51); + if (mode == TSSI_MUX_POSTPA) { + b43_radio_mask(dev, B2063_PA_SP1, 0xFFFE); + b43_phy_mask(dev, B43_LPPHY_AFE_CTL_OVRVAL, 0xFFC7); } else { - //TODO + b43_radio_maskset(dev, B2063_PA_SP1, 0xFFFE, 0x1); + b43_phy_maskset(dev, B43_LPPHY_AFE_CTL_OVRVAL, + 0xFFC7, 0x20); } - //TODO set BB multiplier to 0x0096 + } else { + B43_WARN_ON(1); } } -static int b43_lpphy_op_init(struct b43_wldev *dev) +static void lpphy_tx_pctl_init_hw(struct b43_wldev *dev) { - /* TODO: band SPROM */ - lpphy_baseband_init(dev); - lpphy_radio_init(dev); - //TODO calibrate RC - //TODO set channel - lpphy_tx_pctl_init(dev); - //TODO full calib + u16 tmp; + int i; - return 0; + //SPEC TODO Call LP PHY Clear TX Power offsets + for (i = 0; i < 64; i++) { + if (dev->phy.rev >= 2) + b43_lptab_write(dev, B43_LPTAB32(7, i + 1), i); + else + b43_lptab_write(dev, B43_LPTAB32(10, i + 1), i); + } + + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0xFF00, 0xFF); + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0x8FFF, 0x5000); + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, 0xFFC0, 0x1F); + if (dev->phy.rev < 2) { + b43_phy_mask(dev, B43_LPPHY_LP_PHY_CTL, 0xEFFF); + b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xDFFF, 0x2000); + } else { + b43_phy_mask(dev, B43_PHY_OFDM(0x103), 0xFFFE); + b43_phy_maskset(dev, B43_PHY_OFDM(0x103), 0xFFFB, 0x4); + b43_phy_maskset(dev, B43_PHY_OFDM(0x103), 0xFFEF, 0x10); + b43_radio_maskset(dev, B2063_IQ_CALIB_CTL2, 0xF3, 0x1); + lpphy_set_tssi_mux(dev, TSSI_MUX_POSTPA); + } + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, 0x7FFF, 0x8000); + b43_phy_mask(dev, B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT, 0xFF); + b43_phy_write(dev, B43_LPPHY_TX_PWR_CTL_DELTAPWR_LIMIT, 0xA); + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, + (u16)~B43_LPPHY_TX_PWR_CTL_CMD_MODE, + B43_LPPHY_TX_PWR_CTL_CMD_MODE_OFF); + b43_phy_mask(dev, B43_LPPHY_TX_PWR_CTL_NNUM, 0xF8FF); + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_CMD, + (u16)~B43_LPPHY_TX_PWR_CTL_CMD_MODE, + B43_LPPHY_TX_PWR_CTL_CMD_MODE_SW); + + if (dev->phy.rev < 2) { + b43_phy_maskset(dev, B43_LPPHY_RF_OVERRIDE_0, 0xEFFF, 0x1000); + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_VAL_0, 0xEFFF); + } else { + lpphy_set_tx_power_by_index(dev, 0x7F); + } + + b43_dummy_transmission(dev, true, true); + + tmp = b43_phy_read(dev, B43_LPPHY_TX_PWR_CTL_STAT); + if (tmp & 0x8000) { + b43_phy_maskset(dev, B43_LPPHY_TX_PWR_CTL_IDLETSSI, + 0xFFC0, (tmp & 0xFF) - 32); + } + + b43_phy_mask(dev, B43_LPPHY_RF_OVERRIDE_0, 0xEFFF); + + // (SPEC?) TODO Set "Target TX frequency" variable to 0 + // SPEC FIXME "Set BB Multiplier to 0xE000" impossible - bb_mult is u8! +} + +static void lpphy_tx_pctl_init_sw(struct b43_wldev *dev) +{ + struct lpphy_tx_gains gains; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + gains.gm = 4; + gains.pad = 12; + gains.pga = 12; + gains.dac = 0; + } else { + gains.gm = 7; + gains.pad = 14; + gains.pga = 15; + gains.dac = 0; + } + lpphy_set_tx_gains(dev, gains); + lpphy_set_bb_mult(dev, 150); +} + +/* Initialize TX power control */ +static void lpphy_tx_pctl_init(struct b43_wldev *dev) +{ + if (0/*FIXME HWPCTL capable */) { + lpphy_tx_pctl_init_hw(dev); + } else { /* This device is only software TX power control capable. */ + lpphy_tx_pctl_init_sw(dev); + } } static u16 b43_lpphy_op_read(struct b43_wldev *dev, u16 reg) @@ -463,6 +1500,14 @@ static void b43_lpphy_op_write(struct b43_wldev *dev, u16 reg, u16 value) b43_write16(dev, B43_MMIO_PHY_DATA, value); } +static void b43_lpphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, + u16 set) +{ + b43_write16(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16(dev, B43_MMIO_PHY_DATA, + (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); +} + static u16 b43_lpphy_op_radio_read(struct b43_wldev *dev, u16 reg) { /* Register 1 is a 32-bit register. */ @@ -493,23 +1538,681 @@ static void b43_lpphy_op_software_rfkill(struct b43_wldev *dev, //TODO } +struct b206x_channel { + u8 channel; + u16 freq; + u8 data[12]; +}; + +static const struct b206x_channel b2062_chantbl[] = { + { .channel = 1, .freq = 2412, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 2, .freq = 2417, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 3, .freq = 2422, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 4, .freq = 2427, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 5, .freq = 2432, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 6, .freq = 2437, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 7, .freq = 2442, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 8, .freq = 2447, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 9, .freq = 2452, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 10, .freq = 2457, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 11, .freq = 2462, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 12, .freq = 2467, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 13, .freq = 2472, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 14, .freq = 2484, .data[0] = 0xFF, .data[1] = 0xFF, + .data[2] = 0xB5, .data[3] = 0x1B, .data[4] = 0x24, .data[5] = 0x32, + .data[6] = 0x32, .data[7] = 0x88, .data[8] = 0x88, }, + { .channel = 34, .freq = 5170, .data[0] = 0x00, .data[1] = 0x22, + .data[2] = 0x20, .data[3] = 0x84, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 38, .freq = 5190, .data[0] = 0x00, .data[1] = 0x11, + .data[2] = 0x10, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 42, .freq = 5210, .data[0] = 0x00, .data[1] = 0x11, + .data[2] = 0x10, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 46, .freq = 5230, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 36, .freq = 5180, .data[0] = 0x00, .data[1] = 0x11, + .data[2] = 0x20, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 40, .freq = 5200, .data[0] = 0x00, .data[1] = 0x11, + .data[2] = 0x10, .data[3] = 0x84, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 44, .freq = 5220, .data[0] = 0x00, .data[1] = 0x11, + .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 48, .freq = 5240, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 52, .freq = 5260, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 56, .freq = 5280, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x83, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 60, .freq = 5300, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x63, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 64, .freq = 5320, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x62, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 100, .freq = 5500, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x30, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 104, .freq = 5520, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 108, .freq = 5540, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 112, .freq = 5560, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x20, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 116, .freq = 5580, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x10, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 120, .freq = 5600, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 124, .freq = 5620, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 128, .freq = 5640, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 132, .freq = 5660, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 136, .freq = 5680, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 140, .freq = 5700, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 149, .freq = 5745, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 153, .freq = 5765, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 157, .freq = 5785, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 161, .freq = 5805, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 165, .freq = 5825, .data[0] = 0x00, .data[1] = 0x00, + .data[2] = 0x00, .data[3] = 0x00, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x37, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 184, .freq = 4920, .data[0] = 0x55, .data[1] = 0x77, + .data[2] = 0x90, .data[3] = 0xF7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 188, .freq = 4940, .data[0] = 0x44, .data[1] = 0x77, + .data[2] = 0x80, .data[3] = 0xE7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 192, .freq = 4960, .data[0] = 0x44, .data[1] = 0x66, + .data[2] = 0x80, .data[3] = 0xE7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 196, .freq = 4980, .data[0] = 0x33, .data[1] = 0x66, + .data[2] = 0x70, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 200, .freq = 5000, .data[0] = 0x22, .data[1] = 0x55, + .data[2] = 0x60, .data[3] = 0xD7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 204, .freq = 5020, .data[0] = 0x22, .data[1] = 0x55, + .data[2] = 0x60, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 208, .freq = 5040, .data[0] = 0x22, .data[1] = 0x44, + .data[2] = 0x50, .data[3] = 0xC7, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0xFF, }, + { .channel = 212, .freq = 5060, .data[0] = 0x11, .data[1] = 0x44, + .data[2] = 0x50, .data[3] = 0xA5, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, + { .channel = 216, .freq = 5080, .data[0] = 0x00, .data[1] = 0x44, + .data[2] = 0x40, .data[3] = 0xB6, .data[4] = 0x3C, .data[5] = 0x77, + .data[6] = 0x35, .data[7] = 0xFF, .data[8] = 0x88, }, +}; + +static const struct b206x_channel b2063_chantbl[] = { + { .channel = 1, .freq = 2412, .data[0] = 0x6F, .data[1] = 0x3C, + .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 2, .freq = 2417, .data[0] = 0x6F, .data[1] = 0x3C, + .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 3, .freq = 2422, .data[0] = 0x6F, .data[1] = 0x3C, + .data[2] = 0x3C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 4, .freq = 2427, .data[0] = 0x6F, .data[1] = 0x2C, + .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 5, .freq = 2432, .data[0] = 0x6F, .data[1] = 0x2C, + .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 6, .freq = 2437, .data[0] = 0x6F, .data[1] = 0x2C, + .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 7, .freq = 2442, .data[0] = 0x6F, .data[1] = 0x2C, + .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 8, .freq = 2447, .data[0] = 0x6F, .data[1] = 0x2C, + .data[2] = 0x2C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 9, .freq = 2452, .data[0] = 0x6F, .data[1] = 0x1C, + .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 10, .freq = 2457, .data[0] = 0x6F, .data[1] = 0x1C, + .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 11, .freq = 2462, .data[0] = 0x6E, .data[1] = 0x1C, + .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 12, .freq = 2467, .data[0] = 0x6E, .data[1] = 0x1C, + .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 13, .freq = 2472, .data[0] = 0x6E, .data[1] = 0x1C, + .data[2] = 0x1C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 14, .freq = 2484, .data[0] = 0x6E, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x04, .data[4] = 0x05, .data[5] = 0x05, + .data[6] = 0x05, .data[7] = 0x05, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x80, .data[11] = 0x70, }, + { .channel = 34, .freq = 5170, .data[0] = 0x6A, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x02, .data[5] = 0x05, + .data[6] = 0x0D, .data[7] = 0x0D, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 36, .freq = 5180, .data[0] = 0x6A, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x05, + .data[6] = 0x0D, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 38, .freq = 5190, .data[0] = 0x6A, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04, + .data[6] = 0x0C, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x80, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 40, .freq = 5200, .data[0] = 0x69, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04, + .data[6] = 0x0C, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x70, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 42, .freq = 5210, .data[0] = 0x69, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x01, .data[5] = 0x04, + .data[6] = 0x0B, .data[7] = 0x0C, .data[8] = 0x77, .data[9] = 0x70, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 44, .freq = 5220, .data[0] = 0x69, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x04, + .data[6] = 0x0B, .data[7] = 0x0B, .data[8] = 0x77, .data[9] = 0x60, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 46, .freq = 5230, .data[0] = 0x69, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x03, + .data[6] = 0x0A, .data[7] = 0x0B, .data[8] = 0x77, .data[9] = 0x60, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 48, .freq = 5240, .data[0] = 0x69, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x03, + .data[6] = 0x0A, .data[7] = 0x0A, .data[8] = 0x77, .data[9] = 0x60, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 52, .freq = 5260, .data[0] = 0x68, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x02, + .data[6] = 0x09, .data[7] = 0x09, .data[8] = 0x77, .data[9] = 0x60, + .data[10] = 0x20, .data[11] = 0x00, }, + { .channel = 56, .freq = 5280, .data[0] = 0x68, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x01, + .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50, + .data[10] = 0x10, .data[11] = 0x00, }, + { .channel = 60, .freq = 5300, .data[0] = 0x68, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x01, + .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50, + .data[10] = 0x10, .data[11] = 0x00, }, + { .channel = 64, .freq = 5320, .data[0] = 0x67, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x08, .data[7] = 0x08, .data[8] = 0x77, .data[9] = 0x50, + .data[10] = 0x10, .data[11] = 0x00, }, + { .channel = 100, .freq = 5500, .data[0] = 0x64, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x02, .data[7] = 0x01, .data[8] = 0x77, .data[9] = 0x20, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 104, .freq = 5520, .data[0] = 0x64, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x01, .data[7] = 0x01, .data[8] = 0x77, .data[9] = 0x20, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 108, .freq = 5540, .data[0] = 0x63, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x01, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 112, .freq = 5560, .data[0] = 0x63, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 116, .freq = 5580, .data[0] = 0x62, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x10, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 120, .freq = 5600, .data[0] = 0x62, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 124, .freq = 5620, .data[0] = 0x62, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 128, .freq = 5640, .data[0] = 0x61, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 132, .freq = 5660, .data[0] = 0x61, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 136, .freq = 5680, .data[0] = 0x61, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 140, .freq = 5700, .data[0] = 0x60, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 149, .freq = 5745, .data[0] = 0x60, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 153, .freq = 5765, .data[0] = 0x60, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 157, .freq = 5785, .data[0] = 0x60, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 161, .freq = 5805, .data[0] = 0x60, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 165, .freq = 5825, .data[0] = 0x60, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x00, .data[5] = 0x00, + .data[6] = 0x00, .data[7] = 0x00, .data[8] = 0x77, .data[9] = 0x00, + .data[10] = 0x00, .data[11] = 0x00, }, + { .channel = 184, .freq = 4920, .data[0] = 0x6E, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x09, .data[5] = 0x0E, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xC0, + .data[10] = 0x50, .data[11] = 0x00, }, + { .channel = 188, .freq = 4940, .data[0] = 0x6E, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x09, .data[5] = 0x0D, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xB0, + .data[10] = 0x50, .data[11] = 0x00, }, + { .channel = 192, .freq = 4960, .data[0] = 0x6E, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0C, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xB0, + .data[10] = 0x50, .data[11] = 0x00, }, + { .channel = 196, .freq = 4980, .data[0] = 0x6D, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0C, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0, + .data[10] = 0x40, .data[11] = 0x00, }, + { .channel = 200, .freq = 5000, .data[0] = 0x6D, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0B, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0, + .data[10] = 0x40, .data[11] = 0x00, }, + { .channel = 204, .freq = 5020, .data[0] = 0x6D, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x08, .data[5] = 0x0A, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0xA0, + .data[10] = 0x40, .data[11] = 0x00, }, + { .channel = 208, .freq = 5040, .data[0] = 0x6C, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x07, .data[5] = 0x09, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90, + .data[10] = 0x40, .data[11] = 0x00, }, + { .channel = 212, .freq = 5060, .data[0] = 0x6C, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x06, .data[5] = 0x08, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90, + .data[10] = 0x40, .data[11] = 0x00, }, + { .channel = 216, .freq = 5080, .data[0] = 0x6C, .data[1] = 0x0C, + .data[2] = 0x0C, .data[3] = 0x00, .data[4] = 0x05, .data[5] = 0x08, + .data[6] = 0x0F, .data[7] = 0x0F, .data[8] = 0x77, .data[9] = 0x90, + .data[10] = 0x40, .data[11] = 0x00, }, +}; + +static void lpphy_b2062_reset_pll_bias(struct b43_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + + b43_radio_write(dev, B2062_S_RFPLL_CTL2, 0xFF); + udelay(20); + if (bus->chip_id == 0x5354) { + b43_radio_write(dev, B2062_N_COMM1, 4); + b43_radio_write(dev, B2062_S_RFPLL_CTL2, 4); + } else { + b43_radio_write(dev, B2062_S_RFPLL_CTL2, 0); + } + udelay(5); +} + +static void lpphy_b2062_vco_calib(struct b43_wldev *dev) +{ + b43_radio_write(dev, B2062_S_RFPLL_CTL21, 0x42); + b43_radio_write(dev, B2062_S_RFPLL_CTL21, 0x62); + udelay(200); +} + +static int lpphy_b2062_tune(struct b43_wldev *dev, + unsigned int channel) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + struct ssb_bus *bus = dev->dev->bus; + const struct b206x_channel *chandata = NULL; + u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000; + u32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7, tmp8, tmp9; + int i, err = 0; + + for (i = 0; i < ARRAY_SIZE(b2062_chantbl); i++) { + if (b2062_chantbl[i].channel == channel) { + chandata = &b2062_chantbl[i]; + break; + } + } + + if (B43_WARN_ON(!chandata)) + return -EINVAL; + + b43_radio_set(dev, B2062_S_RFPLL_CTL14, 0x04); + b43_radio_write(dev, B2062_N_LGENA_TUNE0, chandata->data[0]); + b43_radio_write(dev, B2062_N_LGENA_TUNE2, chandata->data[1]); + b43_radio_write(dev, B2062_N_LGENA_TUNE3, chandata->data[2]); + b43_radio_write(dev, B2062_N_TX_TUNE, chandata->data[3]); + b43_radio_write(dev, B2062_S_LGENG_CTL1, chandata->data[4]); + b43_radio_write(dev, B2062_N_LGENA_CTL5, chandata->data[5]); + b43_radio_write(dev, B2062_N_LGENA_CTL6, chandata->data[6]); + b43_radio_write(dev, B2062_N_TX_PGA, chandata->data[7]); + b43_radio_write(dev, B2062_N_TX_PAD, chandata->data[8]); + + tmp1 = crystal_freq / 1000; + tmp2 = lpphy->pdiv * 1000; + b43_radio_write(dev, B2062_S_RFPLL_CTL33, 0xCC); + b43_radio_write(dev, B2062_S_RFPLL_CTL34, 0x07); + lpphy_b2062_reset_pll_bias(dev); + tmp3 = tmp2 * channel2freq_lp(channel); + if (channel2freq_lp(channel) < 4000) + tmp3 *= 2; + tmp4 = 48 * tmp1; + tmp6 = tmp3 / tmp4; + tmp7 = tmp3 % tmp4; + b43_radio_write(dev, B2062_S_RFPLL_CTL26, tmp6); + tmp5 = tmp7 * 0x100; + tmp6 = tmp5 / tmp4; + tmp7 = tmp5 % tmp4; + b43_radio_write(dev, B2062_S_RFPLL_CTL27, tmp6); + tmp5 = tmp7 * 0x100; + tmp6 = tmp5 / tmp4; + tmp7 = tmp5 % tmp4; + b43_radio_write(dev, B2062_S_RFPLL_CTL28, tmp6); + tmp5 = tmp7 * 0x100; + tmp6 = tmp5 / tmp4; + tmp7 = tmp5 % tmp4; + b43_radio_write(dev, B2062_S_RFPLL_CTL29, tmp6 + ((2 * tmp7) / tmp4)); + tmp8 = b43_radio_read(dev, B2062_S_RFPLL_CTL19); + tmp9 = ((2 * tmp3 * (tmp8 + 1)) + (3 * tmp1)) / (6 * tmp1); + b43_radio_write(dev, B2062_S_RFPLL_CTL23, (tmp9 >> 8) + 16); + b43_radio_write(dev, B2062_S_RFPLL_CTL24, tmp9 & 0xFF); + + lpphy_b2062_vco_calib(dev); + if (b43_radio_read(dev, B2062_S_RFPLL_CTL3) & 0x10) { + b43_radio_write(dev, B2062_S_RFPLL_CTL33, 0xFC); + b43_radio_write(dev, B2062_S_RFPLL_CTL34, 0); + lpphy_b2062_reset_pll_bias(dev); + lpphy_b2062_vco_calib(dev); + if (b43_radio_read(dev, B2062_S_RFPLL_CTL3) & 0x10) + err = -EIO; + } + + b43_radio_mask(dev, B2062_S_RFPLL_CTL14, ~0x04); + return err; +} + + +/* This was previously called lpphy_japan_filter */ +static void lpphy_set_analog_filter(struct b43_wldev *dev, int channel) +{ + struct b43_phy_lp *lpphy = dev->phy.lp; + u16 tmp = (channel == 14); //SPEC FIXME check japanwidefilter! + + if (dev->phy.rev < 2) { //SPEC FIXME Isn't this rev0/1-specific? + b43_phy_maskset(dev, B43_LPPHY_LP_PHY_CTL, 0xFCFF, tmp << 9); + if ((dev->phy.rev == 1) && (lpphy->rc_cap)) + lpphy_set_rc_cap(dev); + } else { + b43_radio_write(dev, B2063_TX_BB_SP3, 0x3F); + } +} + +static void lpphy_b2063_vco_calib(struct b43_wldev *dev) +{ + u16 tmp; + + b43_radio_mask(dev, B2063_PLL_SP1, ~0x40); + tmp = b43_radio_read(dev, B2063_PLL_JTAG_CALNRST) & 0xF8; + b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp); + udelay(1); + b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x4); + udelay(1); + b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x6); + udelay(1); + b43_radio_write(dev, B2063_PLL_JTAG_CALNRST, tmp | 0x7); + udelay(300); + b43_radio_set(dev, B2063_PLL_SP1, 0x40); +} + +static int lpphy_b2063_tune(struct b43_wldev *dev, + unsigned int channel) +{ + struct ssb_bus *bus = dev->dev->bus; + + static const struct b206x_channel *chandata = NULL; + u32 crystal_freq = bus->chipco.pmu.crystalfreq * 1000; + u32 freqref, vco_freq, val1, val2, val3, timeout, timeoutref, count; + u16 old_comm15, scale; + u32 tmp1, tmp2, tmp3, tmp4, tmp5, tmp6; + int i, div = (crystal_freq <= 26000000 ? 1 : 2); + + for (i = 0; i < ARRAY_SIZE(b2063_chantbl); i++) { + if (b2063_chantbl[i].channel == channel) { + chandata = &b2063_chantbl[i]; + break; + } + } + + if (B43_WARN_ON(!chandata)) + return -EINVAL; + + b43_radio_write(dev, B2063_LOGEN_VCOBUF1, chandata->data[0]); + b43_radio_write(dev, B2063_LOGEN_MIXER2, chandata->data[1]); + b43_radio_write(dev, B2063_LOGEN_BUF2, chandata->data[2]); + b43_radio_write(dev, B2063_LOGEN_RCCR1, chandata->data[3]); + b43_radio_write(dev, B2063_A_RX_1ST3, chandata->data[4]); + b43_radio_write(dev, B2063_A_RX_2ND1, chandata->data[5]); + b43_radio_write(dev, B2063_A_RX_2ND4, chandata->data[6]); + b43_radio_write(dev, B2063_A_RX_2ND7, chandata->data[7]); + b43_radio_write(dev, B2063_A_RX_PS6, chandata->data[8]); + b43_radio_write(dev, B2063_TX_RF_CTL2, chandata->data[9]); + b43_radio_write(dev, B2063_TX_RF_CTL5, chandata->data[10]); + b43_radio_write(dev, B2063_PA_CTL11, chandata->data[11]); + + old_comm15 = b43_radio_read(dev, B2063_COMM15); + b43_radio_set(dev, B2063_COMM15, 0x1E); + + if (chandata->freq > 4000) /* spec says 2484, but 4000 is safer */ + vco_freq = chandata->freq << 1; + else + vco_freq = chandata->freq << 2; + + freqref = crystal_freq * 3; + val1 = lpphy_qdiv_roundup(crystal_freq, 1000000, 16); + val2 = lpphy_qdiv_roundup(crystal_freq, 1000000 * div, 16); + val3 = lpphy_qdiv_roundup(vco_freq, 3, 16); + timeout = ((((8 * crystal_freq) / (div * 5000000)) + 1) >> 1) - 1; + b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB3, 0x2); + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB6, + 0xFFF8, timeout >> 2); + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB7, + 0xFF9F,timeout << 5); + + timeoutref = ((((8 * crystal_freq) / (div * (timeout + 1))) + + 999999) / 1000000) + 1; + b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB5, timeoutref); + + count = lpphy_qdiv_roundup(val3, val2 + 16, 16); + count *= (timeout + 1) * (timeoutref + 1); + count--; + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_VCO_CALIB7, + 0xF0, count >> 8); + b43_radio_write(dev, B2063_PLL_JTAG_PLL_VCO_CALIB8, count & 0xFF); + + tmp1 = ((val3 * 62500) / freqref) << 4; + tmp2 = ((val3 * 62500) % freqref) << 4; + while (tmp2 >= freqref) { + tmp1++; + tmp2 -= freqref; + } + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG1, 0xFFE0, tmp1 >> 4); + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG2, 0xFE0F, tmp1 << 4); + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_SG2, 0xFFF0, tmp1 >> 16); + b43_radio_write(dev, B2063_PLL_JTAG_PLL_SG3, (tmp2 >> 8) & 0xFF); + b43_radio_write(dev, B2063_PLL_JTAG_PLL_SG4, tmp2 & 0xFF); + + b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF1, 0xB9); + b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF2, 0x88); + b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF3, 0x28); + b43_radio_write(dev, B2063_PLL_JTAG_PLL_LF4, 0x63); + + tmp3 = ((41 * (val3 - 3000)) /1200) + 27; + tmp4 = lpphy_qdiv_roundup(132000 * tmp1, 8451, 16); + + if ((tmp4 + tmp3 - 1) / tmp3 > 60) { + scale = 1; + tmp5 = ((tmp4 + tmp3) / (tmp3 << 1)) - 8; + } else { + scale = 0; + tmp5 = ((tmp4 + (tmp3 >> 1)) / tmp3) - 8; + } + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP2, 0xFFC0, tmp5); + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP2, 0xFFBF, scale << 6); + + tmp6 = lpphy_qdiv_roundup(100 * val1, val3, 16); + tmp6 *= (tmp5 * 8) * (scale + 1); + if (tmp6 > 150) + tmp6 = 0; + + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP3, 0xFFE0, tmp6); + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_CP3, 0xFFDF, scale << 5); + + b43_radio_maskset(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0xFFFB, 0x4); + if (crystal_freq > 26000000) + b43_radio_set(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0x2); + else + b43_radio_mask(dev, B2063_PLL_JTAG_PLL_XTAL_12, 0xFD); + + if (val1 == 45) + b43_radio_set(dev, B2063_PLL_JTAG_PLL_VCO1, 0x2); + else + b43_radio_mask(dev, B2063_PLL_JTAG_PLL_VCO1, 0xFD); + + b43_radio_set(dev, B2063_PLL_SP2, 0x3); + udelay(1); + b43_radio_mask(dev, B2063_PLL_SP2, 0xFFFC); + lpphy_b2063_vco_calib(dev); + b43_radio_write(dev, B2063_COMM15, old_comm15); + + return 0; +} + static int b43_lpphy_op_switch_channel(struct b43_wldev *dev, unsigned int new_channel) { - //TODO + struct b43_phy_lp *lpphy = dev->phy.lp; + int err; + + if (dev->phy.radio_ver == 0x2063) { + err = lpphy_b2063_tune(dev, new_channel); + if (err) + return err; + } else { + err = lpphy_b2062_tune(dev, new_channel); + if (err) + return err; + lpphy_set_analog_filter(dev, new_channel); + lpphy_adjust_gain_table(dev, channel2freq_lp(new_channel)); + } + + lpphy->channel = new_channel; + b43_write16(dev, B43_MMIO_CHANNEL, new_channel); + return 0; } -static unsigned int b43_lpphy_op_get_default_chan(struct b43_wldev *dev) +static int b43_lpphy_op_init(struct b43_wldev *dev) { - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - return 1; - return 36; + int err; + + lpphy_read_band_sprom(dev); //FIXME should this be in prepare_structs? + lpphy_baseband_init(dev); + lpphy_radio_init(dev); + lpphy_calibrate_rc(dev); + err = b43_lpphy_op_switch_channel(dev, 7); + if (err) { + b43dbg(dev->wl, "Switch to channel 7 failed, error = %d.\n", + err); + } + lpphy_tx_pctl_init(dev); + lpphy_calibration(dev); + //TODO ACI init + + return 0; } static void b43_lpphy_op_set_rx_antenna(struct b43_wldev *dev, int antenna) { - //TODO + if (dev->phy.rev >= 2) + return; // rev2+ doesn't support antenna diversity + + if (B43_WARN_ON(antenna > B43_ANTENNA_AUTO1)) + return; + + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFD, antenna & 0x2); + b43_phy_maskset(dev, B43_LPPHY_CRSGAIN_CTL, 0xFFFE, antenna & 0x1); } static void b43_lpphy_op_adjust_txpower(struct b43_wldev *dev) @@ -524,7 +2227,6 @@ static enum b43_txpwr_result b43_lpphy_op_recalc_txpower(struct b43_wldev *dev, return B43_TXPWR_RES_DONE; } - const struct b43_phy_operations b43_phyops_lp = { .allocate = b43_lpphy_op_allocate, .free = b43_lpphy_op_free, @@ -532,6 +2234,7 @@ const struct b43_phy_operations b43_phyops_lp = { .init = b43_lpphy_op_init, .phy_read = b43_lpphy_op_read, .phy_write = b43_lpphy_op_write, + .phy_maskset = b43_lpphy_op_maskset, .radio_read = b43_lpphy_op_radio_read, .radio_write = b43_lpphy_op_radio_write, .software_rfkill = b43_lpphy_op_software_rfkill, diff --git a/drivers/net/wireless/b43/phy_lp.h b/drivers/net/wireless/b43/phy_lp.h index 18370b4ac38e..c3232c17b60a 100644 --- a/drivers/net/wireless/b43/phy_lp.h +++ b/drivers/net/wireless/b43/phy_lp.h @@ -273,12 +273,19 @@ #define B43_LPPHY_AFE_DDFS_POINTER_INIT B43_PHY_OFDM(0xB8) /* AFE DDFS pointer init */ #define B43_LPPHY_AFE_DDFS_INCR_INIT B43_PHY_OFDM(0xB9) /* AFE DDFS incr init */ #define B43_LPPHY_MRCNOISEREDUCTION B43_PHY_OFDM(0xBA) /* mrcNoiseReduction */ -#define B43_LPPHY_TRLOOKUP3 B43_PHY_OFDM(0xBB) /* TRLookup3 */ -#define B43_LPPHY_TRLOOKUP4 B43_PHY_OFDM(0xBC) /* TRLookup4 */ +#define B43_LPPHY_TR_LOOKUP_3 B43_PHY_OFDM(0xBB) /* TR Lookup 3 */ +#define B43_LPPHY_TR_LOOKUP_4 B43_PHY_OFDM(0xBC) /* TR Lookup 4 */ #define B43_LPPHY_RADAR_FIFO_STAT B43_PHY_OFDM(0xBD) /* Radar FIFO Status */ #define B43_LPPHY_GPIO_OUTEN B43_PHY_OFDM(0xBE) /* GPIO Out enable */ #define B43_LPPHY_GPIO_SELECT B43_PHY_OFDM(0xBF) /* GPIO Select */ #define B43_LPPHY_GPIO_OUT B43_PHY_OFDM(0xC0) /* GPIO Out */ +#define B43_LPPHY_4C3 B43_PHY_OFDM(0xC3) /* unknown, used during BB init */ +#define B43_LPPHY_4C4 B43_PHY_OFDM(0xC4) /* unknown, used during BB init */ +#define B43_LPPHY_4C5 B43_PHY_OFDM(0xC5) /* unknown, used during BB init */ +#define B43_LPPHY_TR_LOOKUP_5 B43_PHY_OFDM(0xC7) /* TR Lookup 5 */ +#define B43_LPPHY_TR_LOOKUP_6 B43_PHY_OFDM(0xC8) /* TR Lookup 6 */ +#define B43_LPPHY_TR_LOOKUP_7 B43_PHY_OFDM(0xC9) /* TR Lookup 7 */ +#define B43_LPPHY_TR_LOOKUP_8 B43_PHY_OFDM(0xCA) /* TR Lookup 8 */ @@ -818,14 +825,30 @@ struct b43_phy_lp { enum b43_lpphy_txpctl_mode txpctl_mode; /* Transmit isolation medium band */ - u8 tx_isolation_med_band; /* FIXME initial value? */ + u8 tx_isolation_med_band; /* Transmit isolation low band */ - u8 tx_isolation_low_band; /* FIXME initial value? */ + u8 tx_isolation_low_band; /* Transmit isolation high band */ - u8 tx_isolation_hi_band; /* FIXME initial value? */ + u8 tx_isolation_hi_band; + + /* Max transmit power medium band */ + u16 max_tx_pwr_med_band; + /* Max transmit power low band */ + u16 max_tx_pwr_low_band; + /* Max transmit power high band */ + u16 max_tx_pwr_hi_band; + + /* FIXME What are these used for? */ + /* FIXME Is 15 the correct array size? */ + u16 tx_max_rate[15]; + u16 tx_max_ratel[15]; + u16 tx_max_rateh[15]; + + /* Transmit power arrays */ + s16 txpa[3], txpal[3], txpah[3]; /* Receive power offset */ - u8 rx_pwr_offset; /* FIXME initial value? */ + u8 rx_pwr_offset; /* TSSI transmit count */ u16 tssi_tx_count; @@ -841,16 +864,16 @@ struct b43_phy_lp { s8 tx_pwr_idx_over; /* FIXME initial value? */ /* RSSI vf */ - u8 rssi_vf; /* FIXME initial value? */ + u8 rssi_vf; /* RSSI vc */ - u8 rssi_vc; /* FIXME initial value? */ + u8 rssi_vc; /* RSSI gs */ - u8 rssi_gs; /* FIXME initial value? */ + u8 rssi_gs; /* RC cap */ u8 rc_cap; /* FIXME initial value? */ /* BX arch */ - u8 bx_arch; /* FIXME initial value? */ + u8 bx_arch; /* Full calibration channel */ u8 full_calib_chan; /* FIXME initial value? */ @@ -858,8 +881,23 @@ struct b43_phy_lp { /* Transmit iqlocal best coeffs */ bool tx_iqloc_best_coeffs_valid; u8 tx_iqloc_best_coeffs[11]; + + /* Used for "Save/Restore Dig Filt State" */ + u16 dig_flt_state[9]; + + bool crs_usr_disable, crs_sys_disable; + + unsigned int pdiv; + + /* The channel we are tuned to */ + u8 channel; }; +enum tssi_mux_mode { + TSSI_MUX_PREPA, + TSSI_MUX_POSTPA, + TSSI_MUX_EXT, +}; struct b43_phy_operations; extern const struct b43_phy_operations b43_phyops_lp; diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index be7b5604947b..992318a78077 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -137,7 +137,8 @@ static void b43_radio_init2055_post(struct b43_wldev *dev) b43_radio_mask(dev, B2055_MASTER1, 0xFFF3); msleep(1); - if ((sprom->revision != 4) || !(sprom->boardflags_hi & 0x0002)) { + if ((sprom->revision != 4) || + !(sprom->boardflags_hi & B43_BFH_RSSIINV)) { if ((binfo->vendor != PCI_VENDOR_ID_BROADCOM) || (binfo->type != 0x46D) || (binfo->rev < 0x41)) { diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c index 69138e8c1db6..3fd653c78b10 100644 --- a/drivers/net/wireless/b43/pio.c +++ b/drivers/net/wireless/b43/pio.c @@ -461,8 +461,8 @@ static int pio_tx_frame(struct b43_pio_txqueue *q, cookie = generate_cookie(q, pack); hdrlen = b43_txhdr_size(q->dev); - err = b43_generate_txhdr(q->dev, (u8 *)&txhdr, skb->data, - skb->len, info, cookie); + err = b43_generate_txhdr(q->dev, (u8 *)&txhdr, skb, + info, cookie); if (err) return err; @@ -783,7 +783,7 @@ void b43_pio_rx(struct b43_pio_rxqueue *q) { /* Due to latency issues we must run the RX path in * a workqueue to be able to schedule between packets. */ - queue_work(q->dev->wl->hw->workqueue, &q->rx_work); + ieee80211_queue_work(q->dev->wl->hw, &q->rx_work); } static void b43_pio_tx_suspend_queue(struct b43_pio_txqueue *q) diff --git a/drivers/net/wireless/b43/tables_lpphy.c b/drivers/net/wireless/b43/tables_lpphy.c index 4ea734dce218..c784def19b19 100644 --- a/drivers/net/wireless/b43/tables_lpphy.c +++ b/drivers/net/wireless/b43/tables_lpphy.c @@ -28,22 +28,22 @@ #include "phy_lp.h" -/* Entry of the 2062 radio init table */ -struct b2062_init_tab_entry { +/* Entry of the 2062/2063 radio init table */ +struct b206x_init_tab_entry { u16 offset; u16 value_a; u16 value_g; u8 flags; }; -#define B2062_FLAG_A 0x01 /* Flag: Init in A mode */ -#define B2062_FLAG_G 0x02 /* Flag: Init in G mode */ +#define B206X_FLAG_A 0x01 /* Flag: Init in A mode */ +#define B206X_FLAG_G 0x02 /* Flag: Init in G mode */ -static const struct b2062_init_tab_entry b2062_init_tab[] = { +static const struct b206x_init_tab_entry b2062_init_tab[] = { /* { .offset = B2062_N_COMM1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = 0x0001, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_N_COMM4, .value_a = 0x0001, .value_g = 0x0000, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_N_COMM4, .value_a = 0x0001, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_COMM5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ @@ -56,42 +56,42 @@ static const struct b2062_init_tab_entry b2062_init_tab[] = { /* { .offset = B2062_N_COMM14, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_COMM15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_PDN_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_N_PDN_CTL1, .value_a = 0x0000, .value_g = 0x00CA, .flags = B2062_FLAG_G, }, + { .offset = B2062_N_PDN_CTL1, .value_a = 0x0000, .value_g = 0x00CA, .flags = B206X_FLAG_G, }, /* { .offset = B2062_N_PDN_CTL2, .value_a = 0x0018, .value_g = 0x0018, .flags = 0, }, */ - { .offset = B2062_N_PDN_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_N_PDN_CTL4, .value_a = 0x0015, .value_g = 0x002A, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_N_PDN_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_N_PDN_CTL4, .value_a = 0x0015, .value_g = 0x002A, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_GEN_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_IQ_CALIB, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ - { .offset = B2062_N_LGENC, .value_a = 0x00DB, .value_g = 0x00FF, .flags = B2062_FLAG_A, }, + { .offset = B2062_N_LGENC, .value_a = 0x00DB, .value_g = 0x00FF, .flags = B206X_FLAG_A, }, /* { .offset = B2062_N_LGENA_LPF, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ /* { .offset = B2062_N_LGENA_BIAS0, .value_a = 0x0041, .value_g = 0x0041, .flags = 0, }, */ /* { .offset = B2062_N_LGNEA_BIAS1, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ /* { .offset = B2062_N_LGENA_CTL0, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ /* { .offset = B2062_N_LGENA_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_LGENA_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_N_LGENA_TUNE0, .value_a = 0x00DD, .value_g = 0x0000, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_N_LGENA_TUNE0, .value_a = 0x00DD, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_LGENA_TUNE1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_N_LGENA_TUNE2, .value_a = 0x00DD, .value_g = 0x0000, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_N_LGENA_TUNE3, .value_a = 0x0077, .value_g = 0x00B5, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_N_LGENA_CTL3, .value_a = 0x0000, .value_g = 0x00FF, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_N_LGENA_TUNE2, .value_a = 0x00DD, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_N_LGENA_TUNE3, .value_a = 0x0077, .value_g = 0x00B5, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_N_LGENA_CTL3, .value_a = 0x0000, .value_g = 0x00FF, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_LGENA_CTL4, .value_a = 0x001F, .value_g = 0x001F, .flags = 0, }, */ /* { .offset = B2062_N_LGENA_CTL5, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ /* { .offset = B2062_N_LGENA_CTL6, .value_a = 0x0032, .value_g = 0x0032, .flags = 0, }, */ - { .offset = B2062_N_LGENA_CTL7, .value_a = 0x0033, .value_g = 0x0033, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_N_LGENA_CTL7, .value_a = 0x0033, .value_g = 0x0033, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_RXA_CTL0, .value_a = 0x0009, .value_g = 0x0009, .flags = 0, }, */ - { .offset = B2062_N_RXA_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = B2062_FLAG_G, }, + { .offset = B2062_N_RXA_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, /* { .offset = B2062_N_RXA_CTL2, .value_a = 0x0018, .value_g = 0x0018, .flags = 0, }, */ /* { .offset = B2062_N_RXA_CTL3, .value_a = 0x0027, .value_g = 0x0027, .flags = 0, }, */ /* { .offset = B2062_N_RXA_CTL4, .value_a = 0x0028, .value_g = 0x0028, .flags = 0, }, */ /* { .offset = B2062_N_RXA_CTL5, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ /* { .offset = B2062_N_RXA_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_RXA_CTL7, .value_a = 0x0008, .value_g = 0x0008, .flags = 0, }, */ - { .offset = B2062_N_RXBB_CTL0, .value_a = 0x0082, .value_g = 0x0080, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_N_RXBB_CTL0, .value_a = 0x0082, .value_g = 0x0080, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_RXBB_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_GAIN0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_N_RXBB_GAIN1, .value_a = 0x0004, .value_g = 0x0004, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_N_RXBB_GAIN2, .value_a = 0x0000, .value_g = 0x0000, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_N_RXBB_GAIN1, .value_a = 0x0004, .value_g = 0x0004, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_N_RXBB_GAIN2, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_RXBB_GAIN3, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_RSSI0, .value_a = 0x0043, .value_g = 0x0043, .flags = 0, }, */ /* { .offset = B2062_N_RXBB_RSSI1, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ @@ -112,8 +112,8 @@ static const struct b2062_init_tab_entry b2062_init_tab[] = { /* { .offset = B2062_N_TX_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_TX_CTL2, .value_a = 0x0084, .value_g = 0x0084, .flags = 0, }, */ /* { .offset = B2062_N_TX_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_N_TX_CTL4, .value_a = 0x0003, .value_g = 0x0003, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_N_TX_CTL5, .value_a = 0x0002, .value_g = 0x0002, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_N_TX_CTL4, .value_a = 0x0003, .value_g = 0x0003, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_N_TX_CTL5, .value_a = 0x0002, .value_g = 0x0002, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_TX_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_TX_CTL7, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ /* { .offset = B2062_N_TX_CTL8, .value_a = 0x0082, .value_g = 0x0082, .flags = 0, }, */ @@ -121,7 +121,7 @@ static const struct b2062_init_tab_entry b2062_init_tab[] = { /* { .offset = B2062_N_TX_CTL_A, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_N_TX_GC2G, .value_a = 0x00FF, .value_g = 0x00FF, .flags = 0, }, */ /* { .offset = B2062_N_TX_GC5G, .value_a = 0x00FF, .value_g = 0x00FF, .flags = 0, }, */ - { .offset = B2062_N_TX_TUNE, .value_a = 0x0088, .value_g = 0x001B, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_N_TX_TUNE, .value_a = 0x0088, .value_g = 0x001B, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_N_TX_PAD, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ /* { .offset = B2062_N_TX_PGA, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ /* { .offset = B2062_N_TX_PADAUX, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ @@ -150,7 +150,7 @@ static const struct b2062_init_tab_entry b2062_init_tab[] = { /* { .offset = B2062_S_RADIO_ID_CODE, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_COMM4, .value_a = 0x0001, .value_g = 0x0000, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_S_COMM4, .value_a = 0x0001, .value_g = 0x0000, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_COMM5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ @@ -162,24 +162,24 @@ static const struct b2062_init_tab_entry b2062_init_tab[] = { /* { .offset = B2062_S_COMM13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM14, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_COMM15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_PDS_CTL0, .value_a = 0x00FF, .value_g = 0x00FF, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_S_PDS_CTL0, .value_a = 0x00FF, .value_g = 0x00FF, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_PDS_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_PDS_CTL2, .value_a = 0x008E, .value_g = 0x008E, .flags = 0, }, */ /* { .offset = B2062_S_PDS_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_BG_CTL0, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ /* { .offset = B2062_S_BG_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_BG_CTL2, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ - { .offset = B2062_S_LGENG_CTL0, .value_a = 0x00F8, .value_g = 0x00D8, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_LGENG_CTL1, .value_a = 0x003C, .value_g = 0x0024, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_S_LGENG_CTL0, .value_a = 0x00F8, .value_g = 0x00D8, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_LGENG_CTL1, .value_a = 0x003C, .value_g = 0x0024, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_LGENG_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_LGENG_CTL3, .value_a = 0x0041, .value_g = 0x0041, .flags = 0, }, */ /* { .offset = B2062_S_LGENG_CTL4, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ /* { .offset = B2062_S_LGENG_CTL5, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ /* { .offset = B2062_S_LGENG_CTL6, .value_a = 0x0022, .value_g = 0x0022, .flags = 0, }, */ /* { .offset = B2062_S_LGENG_CTL7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_LGENG_CTL8, .value_a = 0x0088, .value_g = 0x0080, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_S_LGENG_CTL8, .value_a = 0x0088, .value_g = 0x0080, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_LGENG_CTL9, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ - { .offset = B2062_S_LGENG_CTL10, .value_a = 0x0088, .value_g = 0x0080, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_S_LGENG_CTL10, .value_a = 0x0088, .value_g = 0x0080, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_LGENG_CTL11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL0, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL1, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ @@ -198,41 +198,41 @@ static const struct b2062_init_tab_entry b2062_init_tab[] = { /* { .offset = B2062_S_REFPLL_CTL14, .value_a = 0x0075, .value_g = 0x0075, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL15, .value_a = 0x00B4, .value_g = 0x00B4, .flags = 0, }, */ /* { .offset = B2062_S_REFPLL_CTL16, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_RFPLL_CTL0, .value_a = 0x0098, .value_g = 0x0098, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL1, .value_a = 0x0010, .value_g = 0x0010, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL0, .value_a = 0x0098, .value_g = 0x0098, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL1, .value_a = 0x0010, .value_g = 0x0010, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_RFPLL_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_RFPLL_CTL5, .value_a = 0x0043, .value_g = 0x0043, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL6, .value_a = 0x0047, .value_g = 0x0047, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL7, .value_a = 0x000C, .value_g = 0x000C, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL8, .value_a = 0x0011, .value_g = 0x0011, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL9, .value_a = 0x0011, .value_g = 0x0011, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL10, .value_a = 0x000E, .value_g = 0x000E, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL11, .value_a = 0x0008, .value_g = 0x0008, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL12, .value_a = 0x0033, .value_g = 0x0033, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL13, .value_a = 0x000A, .value_g = 0x000A, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL14, .value_a = 0x0006, .value_g = 0x0006, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL5, .value_a = 0x0043, .value_g = 0x0043, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL6, .value_a = 0x0047, .value_g = 0x0047, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL7, .value_a = 0x000C, .value_g = 0x000C, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL8, .value_a = 0x0011, .value_g = 0x0011, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL9, .value_a = 0x0011, .value_g = 0x0011, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL10, .value_a = 0x000E, .value_g = 0x000E, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL11, .value_a = 0x0008, .value_g = 0x0008, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL12, .value_a = 0x0033, .value_g = 0x0033, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL13, .value_a = 0x000A, .value_g = 0x000A, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL14, .value_a = 0x0006, .value_g = 0x0006, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_RFPLL_CTL15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL16, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL17, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_RFPLL_CTL18, .value_a = 0x003E, .value_g = 0x003E, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL19, .value_a = 0x0013, .value_g = 0x0013, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL18, .value_a = 0x003E, .value_g = 0x003E, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL19, .value_a = 0x0013, .value_g = 0x0013, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_RFPLL_CTL20, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_RFPLL_CTL21, .value_a = 0x0062, .value_g = 0x0062, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL22, .value_a = 0x0007, .value_g = 0x0007, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL23, .value_a = 0x0016, .value_g = 0x0016, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL24, .value_a = 0x005C, .value_g = 0x005C, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL25, .value_a = 0x0095, .value_g = 0x0095, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL21, .value_a = 0x0062, .value_g = 0x0062, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL22, .value_a = 0x0007, .value_g = 0x0007, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL23, .value_a = 0x0016, .value_g = 0x0016, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL24, .value_a = 0x005C, .value_g = 0x005C, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL25, .value_a = 0x0095, .value_g = 0x0095, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_RFPLL_CTL26, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL27, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL28, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RFPLL_CTL29, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_RFPLL_CTL30, .value_a = 0x00A0, .value_g = 0x00A0, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL31, .value_a = 0x0004, .value_g = 0x0004, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL30, .value_a = 0x00A0, .value_g = 0x00A0, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL31, .value_a = 0x0004, .value_g = 0x0004, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_RFPLL_CTL32, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ - { .offset = B2062_S_RFPLL_CTL33, .value_a = 0x00CC, .value_g = 0x00CC, .flags = B2062_FLAG_A | B2062_FLAG_G, }, - { .offset = B2062_S_RFPLL_CTL34, .value_a = 0x0007, .value_g = 0x0007, .flags = B2062_FLAG_A | B2062_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL33, .value_a = 0x00CC, .value_g = 0x00CC, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2062_S_RFPLL_CTL34, .value_a = 0x0007, .value_g = 0x0007, .flags = B206X_FLAG_A | B206X_FLAG_G, }, /* { .offset = B2062_S_RXG_CNT0, .value_a = 0x0010, .value_g = 0x0010, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ @@ -241,7 +241,7 @@ static const struct b2062_init_tab_entry b2062_init_tab[] = { /* { .offset = B2062_S_RXG_CNT5, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT6, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT7, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ - { .offset = B2062_S_RXG_CNT8, .value_a = 0x000F, .value_g = 0x000F, .flags = B2062_FLAG_A, }, + { .offset = B2062_S_RXG_CNT8, .value_a = 0x000F, .value_g = 0x000F, .flags = B206X_FLAG_A, }, /* { .offset = B2062_S_RXG_CNT9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT10, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ /* { .offset = B2062_S_RXG_CNT11, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ @@ -253,19 +253,337 @@ static const struct b2062_init_tab_entry b2062_init_tab[] = { /* { .offset = B2062_S_RXG_CNT17, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ }; +static const struct b206x_init_tab_entry b2063_init_tab[] = { + { .offset = B2063_COMM1, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + /* { .offset = B2063_COMM2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_COMM10, .value_a = 0x0001, .value_g = 0x0000, .flags = B206X_FLAG_A, }, + /* { .offset = B2063_COMM11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_COMM14, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ + /* { .offset = B2063_COMM15, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ + { .offset = B2063_COMM16, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM17, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM18, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM19, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM20, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM21, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM22, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM23, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + { .offset = B2063_COMM24, .value_a = 0x0000, .value_g = 0x0000, .flags = B206X_FLAG_G, }, + /* { .offset = B2063_PWR_SWITCH_CTL, .value_a = 0x007f, .value_g = 0x007f, .flags = 0, }, */ + /* { .offset = B2063_PLL_SP1, .value_a = 0x003f, .value_g = 0x003f, .flags = 0, }, */ + /* { .offset = B2063_PLL_SP2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_LOGEN_SP1, .value_a = 0x00e8, .value_g = 0x00d4, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2063_LOGEN_SP2, .value_a = 0x00a7, .value_g = 0x0053, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_LOGEN_SP3, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + { .offset = B2063_LOGEN_SP4, .value_a = 0x00f0, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_LOGEN_SP5, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + { .offset = B2063_G_RX_SP1, .value_a = 0x001f, .value_g = 0x005e, .flags = B206X_FLAG_G, }, + { .offset = B2063_G_RX_SP2, .value_a = 0x007f, .value_g = 0x007e, .flags = B206X_FLAG_G, }, + { .offset = B2063_G_RX_SP3, .value_a = 0x0030, .value_g = 0x00f0, .flags = B206X_FLAG_G, }, + /* { .offset = B2063_G_RX_SP4, .value_a = 0x0035, .value_g = 0x0035, .flags = 0, }, */ + /* { .offset = B2063_G_RX_SP5, .value_a = 0x003f, .value_g = 0x003f, .flags = 0, }, */ + /* { .offset = B2063_G_RX_SP6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_G_RX_SP7, .value_a = 0x007f, .value_g = 0x007f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_G_RX_SP8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_SP9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_G_RX_SP10, .value_a = 0x000c, .value_g = 0x000c, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_G_RX_SP11, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_A_RX_SP1, .value_a = 0x003c, .value_g = 0x003f, .flags = B206X_FLAG_A, }, + { .offset = B2063_A_RX_SP2, .value_a = 0x00fc, .value_g = 0x00fe, .flags = B206X_FLAG_A, }, + /* { .offset = B2063_A_RX_SP3, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + /* { .offset = B2063_A_RX_SP4, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + /* { .offset = B2063_A_RX_SP5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_SP6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_A_RX_SP7, .value_a = 0x0008, .value_g = 0x0008, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_RX_BB_SP1, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_SP2, .value_a = 0x0022, .value_g = 0x0022, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_SP3, .value_a = 0x00a8, .value_g = 0x00a8, .flags = 0, }, */ + { .offset = B2063_RX_BB_SP4, .value_a = 0x0060, .value_g = 0x0060, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_RX_BB_SP5, .value_a = 0x0011, .value_g = 0x0011, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_SP6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_SP7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_RX_BB_SP8, .value_a = 0x0030, .value_g = 0x0030, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_TX_RF_SP1, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP2, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ + { .offset = B2063_TX_RF_SP3, .value_a = 0x000c, .value_g = 0x000b, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2063_TX_RF_SP4, .value_a = 0x0010, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_TX_RF_SP5, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP6, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP7, .value_a = 0x0068, .value_g = 0x0068, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP8, .value_a = 0x0068, .value_g = 0x0068, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP9, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP10, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP11, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP12, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP13, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP14, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP15, .value_a = 0x00c0, .value_g = 0x00c0, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP16, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_SP17, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + { .offset = B2063_PA_SP1, .value_a = 0x003d, .value_g = 0x00fd, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_PA_SP2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ + /* { .offset = B2063_PA_SP3, .value_a = 0x0096, .value_g = 0x0096, .flags = 0, }, */ + /* { .offset = B2063_PA_SP4, .value_a = 0x005a, .value_g = 0x005a, .flags = 0, }, */ + /* { .offset = B2063_PA_SP5, .value_a = 0x007f, .value_g = 0x007f, .flags = 0, }, */ + /* { .offset = B2063_PA_SP6, .value_a = 0x007f, .value_g = 0x007f, .flags = 0, }, */ + /* { .offset = B2063_PA_SP7, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + { .offset = B2063_TX_BB_SP1, .value_a = 0x0002, .value_g = 0x0002, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_TX_BB_SP2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_BB_SP3, .value_a = 0x0030, .value_g = 0x0030, .flags = 0, }, */ + /* { .offset = B2063_REG_SP1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_BANDGAP_CTL1, .value_a = 0x0056, .value_g = 0x0056, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_BANDGAP_CTL2, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ + /* { .offset = B2063_LPO_CTL1, .value_a = 0x000e, .value_g = 0x000e, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL1, .value_a = 0x007e, .value_g = 0x007e, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL2, .value_a = 0x0015, .value_g = 0x0015, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL3, .value_a = 0x000f, .value_g = 0x000f, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RC_CALIB_CTL10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_CALNRST, .value_a = 0x0004, .value_g = 0x0004, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_IN_PLL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_IN_PLL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_CP1, .value_a = 0x00cf, .value_g = 0x00cf, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_CP2, .value_a = 0x0059, .value_g = 0x0059, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_CP3, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_CP4, .value_a = 0x0042, .value_g = 0x0042, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_LF1, .value_a = 0x00db, .value_g = 0x00db, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_LF2, .value_a = 0x0094, .value_g = 0x0094, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_LF3, .value_a = 0x0028, .value_g = 0x0028, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_LF4, .value_a = 0x0063, .value_g = 0x0063, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_SG1, .value_a = 0x0007, .value_g = 0x0007, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_SG2, .value_a = 0x00d3, .value_g = 0x00d3, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_SG3, .value_a = 0x00b1, .value_g = 0x00b1, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_SG4, .value_a = 0x003b, .value_g = 0x003b, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_SG5, .value_a = 0x0006, .value_g = 0x0006, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO1, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ + { .offset = B2063_PLL_JTAG_PLL_VCO2, .value_a = 0x00f7, .value_g = 0x00f7, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB3, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB5, .value_a = 0x0009, .value_g = 0x0009, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB6, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB7, .value_a = 0x0016, .value_g = 0x0016, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB8, .value_a = 0x006b, .value_g = 0x006b, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_VCO_CALIB10, .value_a = 0x00b3, .value_g = 0x00b3, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_XTAL_12, .value_a = 0x0004, .value_g = 0x0004, .flags = 0, }, */ + /* { .offset = B2063_PLL_JTAG_PLL_XTAL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_ACL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_ACL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_ACL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_ACL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_ACL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_INPUTS, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_WAITCNT, .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVR1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVR2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL3, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL4, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL5, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL6, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_OVAL7, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CALVLD1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CALVLD2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LO_CALIB_CVAL7, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_CALIB_EN, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_PEAKDET1, .value_a = 0x00ff, .value_g = 0x00ff, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_RCCR1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_VCOBUF1, .value_a = 0x0060, .value_g = 0x0060, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_MIXER1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_MIXER2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_BUF1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_BUF2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_DIV1, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_DIV2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_DIV3, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_CBUFRX1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_CBUFRX2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_CBUFTX1, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_CBUFTX2, .value_a = 0x0066, .value_g = 0x0066, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_IDAC1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_SPARE1, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_SPARE2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_LOGEN_SPARE3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_1ST1, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_G_RX_1ST2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_1ST3, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND1, .value_a = 0x0030, .value_g = 0x0030, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND2, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND3, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND5, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND7, .value_a = 0x0035, .value_g = 0x0035, .flags = 0, }, */ + /* { .offset = B2063_G_RX_2ND8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_PS1, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_G_RX_PS2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_PS3, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_G_RX_PS4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_PS5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_MIX1, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ + /* { .offset = B2063_G_RX_MIX2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_G_RX_MIX3, .value_a = 0x0071, .value_g = 0x0071, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2063_G_RX_MIX4, .value_a = 0x0071, .value_g = 0x0071, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_G_RX_MIX5, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ + /* { .offset = B2063_G_RX_MIX6, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2063_G_RX_MIX7, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ + /* { .offset = B2063_G_RX_MIX8, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2063_G_RX_PDET1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_SPARES1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_SPARES2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_G_RX_SPARES3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_1ST1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_A_RX_1ST2, .value_a = 0x00f0, .value_g = 0x0030, .flags = B206X_FLAG_A, }, + /* { .offset = B2063_A_RX_1ST3, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ + /* { .offset = B2063_A_RX_1ST4, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_A_RX_1ST5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND1, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND4, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_2ND7, .value_a = 0x0005, .value_g = 0x0005, .flags = 0, }, */ + /* { .offset = B2063_A_RX_PS1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_PS2, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_A_RX_PS3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_PS4, .value_a = 0x0033, .value_g = 0x0033, .flags = 0, }, */ + /* { .offset = B2063_A_RX_PS5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_A_RX_PS6, .value_a = 0x0077, .value_g = 0x0077, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_A_RX_MIX1, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2063_A_RX_MIX2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_MIX3, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ + { .offset = B2063_A_RX_MIX4, .value_a = 0x0003, .value_g = 0x0003, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2063_A_RX_MIX5, .value_a = 0x000f, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + { .offset = B2063_A_RX_MIX6, .value_a = 0x000f, .value_g = 0x000f, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_A_RX_MIX7, .value_a = 0x0044, .value_g = 0x0044, .flags = 0, }, */ + /* { .offset = B2063_A_RX_MIX8, .value_a = 0x0001, .value_g = 0x0001, .flags = 0, }, */ + /* { .offset = B2063_A_RX_PWRDET1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_SPARE1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_SPARE2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_A_RX_SPARE3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_RX_TIA_CTL1, .value_a = 0x0077, .value_g = 0x0077, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_RX_TIA_CTL2, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ + { .offset = B2063_RX_TIA_CTL3, .value_a = 0x0077, .value_g = 0x0077, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_RX_TIA_CTL4, .value_a = 0x0058, .value_g = 0x0058, .flags = 0, }, */ + /* { .offset = B2063_RX_TIA_CTL5, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RX_TIA_CTL6, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL1, .value_a = 0x0074, .value_g = 0x0074, .flags = 0, }, */ + { .offset = B2063_RX_BB_CTL2, .value_a = 0x0004, .value_g = 0x0004, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_RX_BB_CTL3, .value_a = 0x00a2, .value_g = 0x00a2, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL4, .value_a = 0x00aa, .value_g = 0x00aa, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL5, .value_a = 0x0024, .value_g = 0x0024, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL6, .value_a = 0x00a9, .value_g = 0x00a9, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL7, .value_a = 0x0028, .value_g = 0x0028, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL8, .value_a = 0x0010, .value_g = 0x0010, .flags = 0, }, */ + /* { .offset = B2063_RX_BB_CTL9, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL1, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_IDAC_LO_RF_I, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_IDAC_LO_RF_Q, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_IDAC_LO_BB_I, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_IDAC_LO_BB_Q, .value_a = 0x0088, .value_g = 0x0088, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL2, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL3, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL4, .value_a = 0x00b8, .value_g = 0x00b8, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL5, .value_a = 0x0080, .value_g = 0x0080, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL6, .value_a = 0x0038, .value_g = 0x0038, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL7, .value_a = 0x0078, .value_g = 0x0078, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL8, .value_a = 0x00c0, .value_g = 0x00c0, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL9, .value_a = 0x0003, .value_g = 0x0003, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL10, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL14, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_RF_CTL15, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_PA_CTL1, .value_a = 0x0000, .value_g = 0x0004, .flags = B206X_FLAG_A, }, + /* { .offset = B2063_PA_CTL2, .value_a = 0x000c, .value_g = 0x000c, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL3, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL4, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL5, .value_a = 0x0096, .value_g = 0x0096, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL6, .value_a = 0x0077, .value_g = 0x0077, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL7, .value_a = 0x005a, .value_g = 0x005a, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL8, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL9, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL10, .value_a = 0x0021, .value_g = 0x0021, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL11, .value_a = 0x0070, .value_g = 0x0070, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL12, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_PA_CTL13, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_BB_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_BB_CTL2, .value_a = 0x00b3, .value_g = 0x00b3, .flags = 0, }, */ + /* { .offset = B2063_TX_BB_CTL3, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2063_TX_BB_CTL4, .value_a = 0x000b, .value_g = 0x000b, .flags = 0, }, */ + /* { .offset = B2063_GPIO_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + { .offset = B2063_VREG_CTL1, .value_a = 0x0003, .value_g = 0x0003, .flags = B206X_FLAG_A | B206X_FLAG_G, }, + /* { .offset = B2063_AMUX_CTL1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_IQ_CALIB_GVAR, .value_a = 0x00b3, .value_g = 0x00b3, .flags = 0, }, */ + /* { .offset = B2063_IQ_CALIB_CTL1, .value_a = 0x0055, .value_g = 0x0055, .flags = 0, }, */ + /* { .offset = B2063_IQ_CALIB_CTL2, .value_a = 0x0030, .value_g = 0x0030, .flags = 0, }, */ + /* { .offset = B2063_TEMPSENSE_CTL1, .value_a = 0x0046, .value_g = 0x0046, .flags = 0, }, */ + /* { .offset = B2063_TEMPSENSE_CTL2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_RX_LOOPBACK1, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_TX_RX_LOOPBACK2, .value_a = 0x0000, .value_g = 0x0000, .flags = 0, }, */ + /* { .offset = B2063_EXT_TSSI_CTL1, .value_a = 0x0021, .value_g = 0x0021, .flags = 0, }, */ + /* { .offset = B2063_EXT_TSSI_CTL2, .value_a = 0x0023, .value_g = 0x0023, .flags = 0, }, */ + /* { .offset = B2063_AFE_CTL , .value_a = 0x0002, .value_g = 0x0002, .flags = 0, }, */ +}; + void b2062_upload_init_table(struct b43_wldev *dev) { - const struct b2062_init_tab_entry *e; + const struct b206x_init_tab_entry *e; unsigned int i; for (i = 0; i < ARRAY_SIZE(b2062_init_tab); i++) { e = &b2062_init_tab[i]; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - if (!(e->flags & B2062_FLAG_G)) + if (!(e->flags & B206X_FLAG_G)) continue; b43_radio_write(dev, e->offset, e->value_g); } else { - if (!(e->flags & B2062_FLAG_A)) + if (!(e->flags & B206X_FLAG_A)) + continue; + b43_radio_write(dev, e->offset, e->value_a); + } + } +} + +void b2063_upload_init_table(struct b43_wldev *dev) +{ + const struct b206x_init_tab_entry *e; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(b2063_init_tab); i++) { + e = &b2063_init_tab[i]; + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if (!(e->flags & B206X_FLAG_G)) + continue; + b43_radio_write(dev, e->offset, e->value_g); + } else { + if (!(e->flags & B206X_FLAG_A)) continue; b43_radio_write(dev, e->offset, e->value_a); } @@ -306,30 +624,35 @@ u32 b43_lptab_read(struct b43_wldev *dev, u32 offset) void b43_lptab_read_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, void *_data) { - u32 type, value; + u32 type; u8 *data = _data; unsigned int i; type = offset & B43_LPTAB_TYPEMASK; + offset &= ~B43_LPTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); + for (i = 0; i < nr_elements; i++) { - value = b43_lptab_read(dev, offset); switch (type) { case B43_LPTAB_8BIT: - *data = value; + *data = b43_phy_read(dev, B43_LPPHY_TABLEDATALO) & 0xFF; data++; break; case B43_LPTAB_16BIT: - *((u16 *)data) = value; + *((u16 *)data) = b43_phy_read(dev, B43_LPPHY_TABLEDATALO); data += 2; break; case B43_LPTAB_32BIT: - *((u32 *)data) = value; + *((u32 *)data) = b43_phy_read(dev, B43_LPPHY_TABLEDATAHI); + *((u32 *)data) <<= 16; + *((u32 *)data) |= b43_phy_read(dev, B43_LPPHY_TABLEDATALO); data += 4; break; default: B43_WARN_ON(1); } - offset++; } } @@ -370,25 +693,1764 @@ void b43_lptab_write_bulk(struct b43_wldev *dev, u32 offset, unsigned int i; type = offset & B43_LPTAB_TYPEMASK; + offset &= ~B43_LPTAB_TYPEMASK; + B43_WARN_ON(offset > 0xFFFF); + + b43_phy_write(dev, B43_LPPHY_TABLE_ADDR, offset); + for (i = 0; i < nr_elements; i++) { switch (type) { case B43_LPTAB_8BIT: value = *data; data++; + B43_WARN_ON(value & ~0xFF); + b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); break; case B43_LPTAB_16BIT: value = *((u16 *)data); data += 2; + B43_WARN_ON(value & ~0xFFFF); + b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); break; case B43_LPTAB_32BIT: value = *((u32 *)data); data += 4; + b43_phy_write(dev, B43_LPPHY_TABLEDATAHI, value >> 16); + b43_phy_write(dev, B43_LPPHY_TABLEDATALO, value); break; default: B43_WARN_ON(1); - value = 0; } - b43_lptab_write(dev, offset, value); - offset++; + } +} + +static const u8 lpphy_min_sig_sq_table[] = { + 0xde, 0xdc, 0xda, 0xd8, 0xd6, 0xd4, 0xd2, 0xcf, 0xcd, + 0xca, 0xc7, 0xc4, 0xc1, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0x00, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xc1, 0xc4, 0xc7, 0xca, 0xcd, + 0xcf, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde, +}; + +static const u16 lpphy_rev01_noise_scale_table[] = { + 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, + 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa400, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, + 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0xa4a4, 0x00a4, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4c00, 0x2d36, + 0x0000, 0x0000, 0x4c00, 0x2d36, +}; + +static const u16 lpphy_rev2plus_noise_scale_table[] = { + 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, + 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, + 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x0000, + 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, + 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, + 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, 0x00a4, + 0x00a4, +}; + +static const u16 lpphy_crs_gain_nft_table[] = { + 0x0366, 0x036a, 0x036f, 0x0364, 0x0367, 0x036d, 0x0374, 0x037f, 0x036f, + 0x037b, 0x038a, 0x0378, 0x0367, 0x036d, 0x0375, 0x0381, 0x0374, 0x0381, + 0x0392, 0x03a9, 0x03c4, 0x03e1, 0x0001, 0x001f, 0x0040, 0x005e, 0x007f, + 0x009e, 0x00bd, 0x00dd, 0x00fd, 0x011d, 0x013d, +}; + +static const u16 lpphy_rev01_filter_control_table[] = { + 0xa0fc, 0x10fc, 0x10db, 0x20b7, 0xff93, 0x10bf, 0x109b, 0x2077, 0xff53, + 0x0127, +}; + +static const u32 lpphy_rev2plus_filter_control_table[] = { + 0x000141fc, 0x000021fc, 0x000021b7, 0x0000416f, 0x0001ff27, 0x0000217f, + 0x00002137, 0x000040ef, 0x0001fea7, 0x0000024f, +}; + +static const u32 lpphy_rev01_ps_control_table[] = { + 0x00010000, 0x000000a0, 0x00040000, 0x00000048, 0x08080101, 0x00000080, + 0x08080101, 0x00000040, 0x08080101, 0x000000c0, 0x08a81501, 0x000000c0, + 0x0fe8fd01, 0x000000c0, 0x08300105, 0x000000c0, 0x08080201, 0x000000c0, + 0x08280205, 0x000000c0, 0xe80802fe, 0x000000c7, 0x28080206, 0x000000c0, + 0x08080202, 0x000000c0, 0x0ba87602, 0x000000c0, 0x1068013d, 0x000000c0, + 0x10280105, 0x000000c0, 0x08880102, 0x000000c0, 0x08280106, 0x000000c0, + 0xe80801fd, 0x000000c7, 0xa8080115, 0x000000c0, +}; + +static const u32 lpphy_rev2plus_ps_control_table[] = { + 0x00e38e08, 0x00e08e38, 0x00000000, 0x00000000, 0x00000000, 0x00002080, + 0x00006180, 0x00003002, 0x00000040, 0x00002042, 0x00180047, 0x00080043, + 0x00000041, 0x000020c1, 0x00046006, 0x00042002, 0x00040000, 0x00002003, + 0x00180006, 0x00080002, +}; + +static const u8 lpphy_pll_fraction_table[] = { + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +}; + +static const u16 lpphy_iqlo_cal_table[] = { + 0x0200, 0x0300, 0x0400, 0x0600, 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, + 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0200, 0x0300, 0x0400, 0x0600, + 0x0800, 0x0b00, 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, + 0x1007, 0x1707, 0x2007, 0x2d07, 0x4007, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x4000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const u16 lpphy_rev0_ofdm_cck_gain_table[] = { + 0x0001, 0x0001, 0x0001, 0x0001, 0x1001, 0x2001, 0x3001, 0x4001, 0x5001, + 0x6001, 0x7001, 0x7011, 0x7021, 0x2035, 0x2045, 0x2055, 0x2065, 0x2075, + 0x006d, 0x007d, 0x014d, 0x015d, 0x115d, 0x035d, 0x135d, 0x055d, 0x155d, + 0x0d5d, 0x1d5d, 0x2d5d, 0x555d, 0x655d, 0x755d, +}; + +static const u16 lpphy_rev1_ofdm_cck_gain_table[] = { + 0x5000, 0x6000, 0x7000, 0x0001, 0x1001, 0x2001, 0x3001, 0x4001, 0x5001, + 0x6001, 0x7001, 0x7011, 0x7021, 0x2035, 0x2045, 0x2055, 0x2065, 0x2075, + 0x006d, 0x007d, 0x014d, 0x015d, 0x115d, 0x035d, 0x135d, 0x055d, 0x155d, + 0x0d5d, 0x1d5d, 0x2d5d, 0x555d, 0x655d, 0x755d, +}; + +static const u16 lpphy_gain_delta_table[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const u32 lpphy_tx_power_control_table[] = { + 0x00000050, 0x0000004f, 0x0000004e, 0x0000004d, 0x0000004c, 0x0000004b, + 0x0000004a, 0x00000049, 0x00000048, 0x00000047, 0x00000046, 0x00000045, + 0x00000044, 0x00000043, 0x00000042, 0x00000041, 0x00000040, 0x0000003f, + 0x0000003e, 0x0000003d, 0x0000003c, 0x0000003b, 0x0000003a, 0x00000039, + 0x00000038, 0x00000037, 0x00000036, 0x00000035, 0x00000034, 0x00000033, + 0x00000032, 0x00000031, 0x00000030, 0x0000002f, 0x0000002e, 0x0000002d, + 0x0000002c, 0x0000002b, 0x0000002a, 0x00000029, 0x00000028, 0x00000027, + 0x00000026, 0x00000025, 0x00000024, 0x00000023, 0x00000022, 0x00000021, + 0x00000020, 0x0000001f, 0x0000001e, 0x0000001d, 0x0000001c, 0x0000001b, + 0x0000001a, 0x00000019, 0x00000018, 0x00000017, 0x00000016, 0x00000015, + 0x00000014, 0x00000013, 0x00000012, 0x00000011, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x000075a0, 0x000075a0, 0x000075a1, 0x000075a1, 0x000075a2, 0x000075a2, + 0x000075a3, 0x000075a3, 0x000074b0, 0x000074b0, 0x000074b1, 0x000074b1, + 0x000074b2, 0x000074b2, 0x000074b3, 0x000074b3, 0x00006d20, 0x00006d20, + 0x00006d21, 0x00006d21, 0x00006d22, 0x00006d22, 0x00006d23, 0x00006d23, + 0x00004660, 0x00004660, 0x00004661, 0x00004661, 0x00004662, 0x00004662, + 0x00004663, 0x00004663, 0x00003e60, 0x00003e60, 0x00003e61, 0x00003e61, + 0x00003e62, 0x00003e62, 0x00003e63, 0x00003e63, 0x00003660, 0x00003660, + 0x00003661, 0x00003661, 0x00003662, 0x00003662, 0x00003663, 0x00003663, + 0x00002e60, 0x00002e60, 0x00002e61, 0x00002e61, 0x00002e62, 0x00002e62, + 0x00002e63, 0x00002e63, 0x00002660, 0x00002660, 0x00002661, 0x00002661, + 0x00002662, 0x00002662, 0x00002663, 0x00002663, 0x000025e0, 0x000025e0, + 0x000025e1, 0x000025e1, 0x000025e2, 0x000025e2, 0x000025e3, 0x000025e3, + 0x00001de0, 0x00001de0, 0x00001de1, 0x00001de1, 0x00001de2, 0x00001de2, + 0x00001de3, 0x00001de3, 0x00001d60, 0x00001d60, 0x00001d61, 0x00001d61, + 0x00001d62, 0x00001d62, 0x00001d63, 0x00001d63, 0x00001560, 0x00001560, + 0x00001561, 0x00001561, 0x00001562, 0x00001562, 0x00001563, 0x00001563, + 0x00000d60, 0x00000d60, 0x00000d61, 0x00000d61, 0x00000d62, 0x00000d62, + 0x00000d63, 0x00000d63, 0x00000ce0, 0x00000ce0, 0x00000ce1, 0x00000ce1, + 0x00000ce2, 0x00000ce2, 0x00000ce3, 0x00000ce3, 0x00000e10, 0x00000e10, + 0x00000e11, 0x00000e11, 0x00000e12, 0x00000e12, 0x00000e13, 0x00000e13, + 0x00000bf0, 0x00000bf0, 0x00000bf1, 0x00000bf1, 0x00000bf2, 0x00000bf2, + 0x00000bf3, 0x00000bf3, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x04200000, 0x04000000, + 0x04200000, 0x04000000, 0x04200000, 0x04000000, 0x000000ff, 0x000002fc, + 0x0000fa08, 0x00000305, 0x00000206, 0x00000304, 0x0000fb04, 0x0000fcff, + 0x000005fb, 0x0000fd01, 0x00000401, 0x00000006, 0x0000ff03, 0x000007fc, + 0x0000fc08, 0x00000203, 0x0000fffb, 0x00000600, 0x0000fa01, 0x0000fc03, + 0x0000fe06, 0x0000fe00, 0x00000102, 0x000007fd, 0x000004fb, 0x000006ff, + 0x000004fd, 0x0000fdfa, 0x000007fb, 0x0000fdfa, 0x0000fa06, 0x00000500, + 0x0000f902, 0x000007fa, 0x0000fafa, 0x00000500, 0x000007fa, 0x00000700, + 0x00000305, 0x000004ff, 0x00000801, 0x00000503, 0x000005f9, 0x00000404, + 0x0000fb08, 0x000005fd, 0x00000501, 0x00000405, 0x0000fb03, 0x000007fc, + 0x00000403, 0x00000303, 0x00000402, 0x0000faff, 0x0000fe05, 0x000005fd, + 0x0000fe01, 0x000007fa, 0x00000202, 0x00000504, 0x00000102, 0x000008fe, + 0x0000fa04, 0x0000fafc, 0x0000fe08, 0x000000f9, 0x000002fa, 0x000003fe, + 0x00000304, 0x000004f9, 0x00000100, 0x0000fd06, 0x000008fc, 0x00000701, + 0x00000504, 0x0000fdfe, 0x0000fdfc, 0x000003fe, 0x00000704, 0x000002fc, + 0x000004f9, 0x0000fdfd, 0x0000fa07, 0x00000205, 0x000003fd, 0x000005fb, + 0x000004f9, 0x00000804, 0x0000fc06, 0x0000fcf9, 0x00000100, 0x0000fe05, + 0x00000408, 0x0000fb02, 0x00000304, 0x000006fe, 0x000004fa, 0x00000305, + 0x000008fc, 0x00000102, 0x000001fd, 0x000004fc, 0x0000fe03, 0x00000701, + 0x000001fb, 0x000001f9, 0x00000206, 0x000006fd, 0x00000508, 0x00000700, + 0x00000304, 0x000005fe, 0x000005ff, 0x0000fa04, 0x00000303, 0x0000fefb, + 0x000007f9, 0x0000fefc, 0x000004fd, 0x000005fc, 0x0000fffd, 0x0000fc08, + 0x0000fbf9, 0x0000fd07, 0x000008fb, 0x0000fe02, 0x000006fb, 0x00000702, +}; + +static const u32 lpphy_gain_idx_table[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x10000001, 0x00000000, 0x20000082, 0x00000000, 0x40000104, 0x00000000, + 0x60004207, 0x00000001, 0x7000838a, 0x00000001, 0xd021050d, 0x00000001, + 0xe041c683, 0x00000001, 0x50828805, 0x00000000, 0x80e34288, 0x00000000, + 0xb144040b, 0x00000000, 0xe1a6058e, 0x00000000, 0x12064711, 0x00000001, + 0xb0a18612, 0x00000010, 0xe1024794, 0x00000010, 0x11630915, 0x00000011, + 0x31c3ca1b, 0x00000011, 0xc1848a9c, 0x00000018, 0xf1e50da0, 0x00000018, + 0x22468e21, 0x00000019, 0x4286d023, 0x00000019, 0xa347d0a4, 0x00000019, + 0xb36811a6, 0x00000019, 0xf3e89227, 0x00000019, 0x0408d329, 0x0000001a, + 0x244953aa, 0x0000001a, 0x346994ab, 0x0000001a, 0x54aa152c, 0x0000001a, + 0x64ca55ad, 0x0000001a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x10000001, 0x00000000, 0x20000082, 0x00000000, + 0x40000104, 0x00000000, 0x60004207, 0x00000001, 0x7000838a, 0x00000001, + 0xd021050d, 0x00000001, 0xe041c683, 0x00000001, 0x50828805, 0x00000000, + 0x80e34288, 0x00000000, 0xb144040b, 0x00000000, 0xe1a6058e, 0x00000000, + 0x12064711, 0x00000001, 0xb0a18612, 0x00000010, 0xe1024794, 0x00000010, + 0x11630915, 0x00000011, 0x31c3ca1b, 0x00000011, 0xc1848a9c, 0x00000018, + 0xf1e50da0, 0x00000018, 0x22468e21, 0x00000019, 0x4286d023, 0x00000019, + 0xa347d0a4, 0x00000019, 0xb36811a6, 0x00000019, 0xf3e89227, 0x00000019, + 0x0408d329, 0x0000001a, 0x244953aa, 0x0000001a, 0x346994ab, 0x0000001a, + 0x54aa152c, 0x0000001a, 0x64ca55ad, 0x0000001a, +}; + +static const u16 lpphy_aux_gain_idx_table[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0002, 0x0004, 0x0016, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0016, +}; + +static const u32 lpphy_gain_value_table[] = { + 0x00000008, 0x0000000e, 0x00000014, 0x0000001a, 0x000000fb, 0x00000004, + 0x00000008, 0x0000000d, 0x00000001, 0x00000004, 0x00000007, 0x0000000a, + 0x0000000d, 0x00000010, 0x00000012, 0x00000015, 0x00000000, 0x00000006, + 0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x00000012, 0x00000000, + 0x00000000, 0x00000000, 0x00000018, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000003, 0x00000006, 0x00000009, 0x0000000c, 0x0000000f, + 0x00000012, 0x00000015, 0x00000018, 0x0000001b, 0x0000001e, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000009, 0x000000f1, + 0x00000000, 0x00000000, +}; + +static const u16 lpphy_gain_table[] = { + 0x0000, 0x0400, 0x0800, 0x0802, 0x0804, 0x0806, 0x0807, 0x0808, 0x080a, + 0x080b, 0x080c, 0x080e, 0x080f, 0x0810, 0x0812, 0x0813, 0x0814, 0x0816, + 0x0817, 0x081a, 0x081b, 0x081f, 0x0820, 0x0824, 0x0830, 0x0834, 0x0837, + 0x083b, 0x083f, 0x0840, 0x0844, 0x0857, 0x085b, 0x085f, 0x08d7, 0x08db, + 0x08df, 0x0957, 0x095b, 0x095f, 0x0b57, 0x0b5b, 0x0b5f, 0x0f5f, 0x135f, + 0x175f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const u32 lpphy_a0_gain_idx_table[] = { + 0x001111e0, 0x00652051, 0x00606055, 0x005b005a, 0x00555060, 0x00511065, + 0x004c806b, 0x0047d072, 0x00444078, 0x00400080, 0x003ca087, 0x0039408f, + 0x0035e098, 0x0032e0a1, 0x003030aa, 0x002d80b4, 0x002ae0bf, 0x002880ca, + 0x002640d6, 0x002410e3, 0x002220f0, 0x002020ff, 0x001e510e, 0x001ca11e, + 0x001b012f, 0x00199140, 0x00182153, 0x0016c168, 0x0015817d, 0x00145193, + 0x001321ab, 0x001211c5, 0x001111e0, 0x001021fc, 0x000f321a, 0x000e523a, + 0x000d925c, 0x000cd27f, 0x000c12a5, 0x000b62cd, 0x000ac2f8, 0x000a2325, + 0x00099355, 0x00091387, 0x000883bd, 0x000813f5, 0x0007a432, 0x00073471, + 0x0006c4b5, 0x000664fc, 0x00061547, 0x0005b598, 0x000565ec, 0x00051646, + 0x0004d6a5, 0x0004870a, 0x00044775, 0x000407e6, 0x0003d85e, 0x000398dd, + 0x00036963, 0x000339f2, 0x00030a89, 0x0002db28, +}; + +static const u16 lpphy_a0_aux_gain_idx_table[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0002, 0x0014, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0014, +}; + +static const u32 lpphy_a0_gain_value_table[] = { + 0x00000008, 0x0000000e, 0x00000014, 0x0000001a, 0x000000fb, 0x00000004, + 0x00000008, 0x0000000d, 0x00000001, 0x00000004, 0x00000007, 0x0000000a, + 0x0000000d, 0x00000010, 0x00000012, 0x00000015, 0x00000000, 0x00000006, + 0x0000000c, 0x00000000, 0x00000000, 0x00000000, 0x00000012, 0x00000000, + 0x00000000, 0x00000000, 0x00000018, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0000001e, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000003, 0x00000006, 0x00000009, 0x0000000c, 0x0000000f, + 0x00000012, 0x00000015, 0x00000018, 0x0000001b, 0x0000001e, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000000f, 0x000000f7, + 0x00000000, 0x00000000, +}; + +static const u16 lpphy_a0_gain_table[] = { + 0x0000, 0x0002, 0x0004, 0x0006, 0x0007, 0x0008, 0x000a, 0x000b, 0x000c, + 0x000e, 0x000f, 0x0010, 0x0012, 0x0013, 0x0014, 0x0016, 0x0017, 0x001a, + 0x001b, 0x001f, 0x0020, 0x0024, 0x0030, 0x0034, 0x0037, 0x003b, 0x003f, + 0x0040, 0x0044, 0x0057, 0x005b, 0x005f, 0x00d7, 0x00db, 0x00df, 0x0157, + 0x015b, 0x015f, 0x0357, 0x035b, 0x035f, 0x075f, 0x0b5f, 0x0f5f, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; + +static const u16 lpphy_sw_control_table[] = { + 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0128, + 0x0128, 0x0009, 0x0009, 0x0028, 0x0028, 0x0028, 0x0028, 0x0009, 0x0009, + 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0018, 0x0018, 0x0018, + 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0128, 0x0128, 0x0009, 0x0009, + 0x0028, 0x0028, 0x0028, 0x0028, 0x0128, 0x0128, 0x0009, 0x0009, 0x0028, + 0x0028, 0x0028, 0x0028, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, 0x0009, + 0x0009, 0x0009, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, + 0x0018, +}; + +static const u8 lpphy_hf_table[] = { + 0x4b, 0x36, 0x24, 0x18, 0x49, 0x34, 0x23, 0x17, 0x48, + 0x33, 0x23, 0x17, 0x48, 0x33, 0x23, 0x17, +}; + +static const u32 lpphy_papd_eps_table[] = { + 0x00000000, 0x00013ffc, 0x0001dff3, 0x0001bff0, 0x00023fe9, 0x00021fdf, + 0x00028fdf, 0x00033fd2, 0x00039fcb, 0x00043fc7, 0x0004efc2, 0x00055fb5, + 0x0005cfb0, 0x00063fa8, 0x00068fa3, 0x00071f98, 0x0007ef92, 0x00084f8b, + 0x0008df82, 0x00097f77, 0x0009df69, 0x000a3f62, 0x000adf57, 0x000b6f4c, + 0x000bff41, 0x000c9f39, 0x000cff30, 0x000dbf27, 0x000e4f1e, 0x000edf16, + 0x000f7f13, 0x00102f11, 0x00110f10, 0x0011df11, 0x0012ef15, 0x00143f1c, + 0x00158f27, 0x00172f35, 0x00193f47, 0x001baf5f, 0x001e6f7e, 0x0021cfa4, + 0x0025bfd2, 0x002a2008, 0x002fb047, 0x00360090, 0x003d40e0, 0x0045c135, + 0x004fb189, 0x005ae1d7, 0x0067221d, 0x0075025a, 0x007ff291, 0x007ff2bf, + 0x007ff2e3, 0x007ff2ff, 0x007ff315, 0x007ff329, 0x007ff33f, 0x007ff356, + 0x007ff36e, 0x007ff39c, 0x007ff441, 0x007ff506, +}; + +static const u32 lpphy_papd_mult_table[] = { + 0x001111e0, 0x00652051, 0x00606055, 0x005b005a, 0x00555060, 0x00511065, + 0x004c806b, 0x0047d072, 0x00444078, 0x00400080, 0x003ca087, 0x0039408f, + 0x0035e098, 0x0032e0a1, 0x003030aa, 0x002d80b4, 0x002ae0bf, 0x002880ca, + 0x002640d6, 0x002410e3, 0x002220f0, 0x002020ff, 0x001e510e, 0x001ca11e, + 0x001b012f, 0x00199140, 0x00182153, 0x0016c168, 0x0015817d, 0x00145193, + 0x001321ab, 0x001211c5, 0x001111e0, 0x001021fc, 0x000f321a, 0x000e523a, + 0x000d925c, 0x000cd27f, 0x000c12a5, 0x000b62cd, 0x000ac2f8, 0x000a2325, + 0x00099355, 0x00091387, 0x000883bd, 0x000813f5, 0x0007a432, 0x00073471, + 0x0006c4b5, 0x000664fc, 0x00061547, 0x0005b598, 0x000565ec, 0x00051646, + 0x0004d6a5, 0x0004870a, 0x00044775, 0x000407e6, 0x0003d85e, 0x000398dd, + 0x00036963, 0x000339f2, 0x00030a89, 0x0002db28, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev0_nopa_tx_gain_table[] = { + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 152, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 147, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 143, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 139, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 135, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 131, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 128, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 124, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 121, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 117, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 114, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 111, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 107, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 104, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 101, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 99, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 96, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 93, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 90, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 88, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 85, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 83, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 81, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 78, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 76, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 74, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev0_2ghz_tx_gain_table[] = { + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 71, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 69, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 58, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 58, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 57, }, + { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 83, }, + { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 81, }, + { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 78, }, + { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 76, }, + { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 74, }, + { .gm = 4, .pga = 4, .pad = 2, .dac = 0, .bb_mult = 72, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev0_5ghz_tx_gain_table[] = { + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 99, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 96, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 93, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 90, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 88, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 85, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 83, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 81, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 78, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 76, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 74, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 55, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 55, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 73, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev1_nopa_tx_gain_table[] = { + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 152, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 147, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 143, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 139, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 135, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 131, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 128, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 124, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 121, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 117, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 114, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 111, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 107, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 104, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 101, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 99, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 96, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 93, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 90, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 88, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 85, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 83, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 81, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 78, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 76, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 74, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev1_2ghz_tx_gain_table[] = { + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 85, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 81, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 78, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 76, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 74, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 73, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 71, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 69, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 73, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 71, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 10, .pad = 6, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 72, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 10, .pad = 5, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 9, .pad = 5, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 71, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 69, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 9, .pad = 4, .dac = 0, .bb_mult = 58, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 70, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 8, .pad = 4, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 7, .pad = 4, .dac = 0, .bb_mult = 59, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 67, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 62, }, + { .gm = 4, .pga = 7, .pad = 3, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 65, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 63, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 61, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 60, }, + { .gm = 4, .pga = 6, .pad = 3, .dac = 0, .bb_mult = 58, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 68, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 66, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 64, }, + { .gm = 4, .pga = 5, .pad = 3, .dac = 0, .bb_mult = 62, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev1_5ghz_tx_gain_table[] = { + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 99, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 96, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 93, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 90, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 88, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 85, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 83, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 81, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 78, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 76, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 74, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 15, .dac = 0, .bb_mult = 55, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 15, .pad = 14, .dac = 0, .bb_mult = 55, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 13, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 72, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 12, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 73, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 11, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 71, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 15, .pad = 10, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 15, .pad = 9, .dac = 0, .bb_mult = 56, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 14, .pad = 9, .dac = 0, .bb_mult = 58, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 9, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 60, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 13, .pad = 8, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 8, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 12, .pad = 7, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 70, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 68, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 66, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 61, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 59, }, + { .gm = 7, .pga = 11, .pad = 7, .dac = 0, .bb_mult = 57, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 69, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 67, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 65, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 63, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 62, }, + { .gm = 7, .pga = 11, .pad = 6, .dac = 0, .bb_mult = 60, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev2_nopa_tx_gain_table[] = { + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 152, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 147, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 143, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 139, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 135, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 131, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 128, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 124, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 121, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 117, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 114, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 111, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 107, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 104, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 101, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 99, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 96, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 93, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 90, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 88, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 85, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 83, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 81, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 78, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 76, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 74, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 72, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 70, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 68, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 66, }, + { .gm = 255, .pga = 255, .pad = 203, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 197, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 192, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 186, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 181, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 176, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 171, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 166, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 161, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 157, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 152, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 148, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 144, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 140, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 136, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 132, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 128, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 124, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 121, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 117, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 114, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 111, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 108, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 105, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 102, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 99, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 96, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 93, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 91, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 88, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 86, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 83, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 81, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 79, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 76, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 74, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 72, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 70, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 68, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 66, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 255, .pad = 64, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 248, .pad = 64, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 248, .pad = 62, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 241, .pad = 62, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 241, .pad = 60, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 234, .pad = 60, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 234, .pad = 59, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 227, .pad = 59, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 227, .pad = 57, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 221, .pad = 57, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 221, .pad = 55, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 215, .pad = 55, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 215, .pad = 54, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 208, .pad = 54, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 208, .pad = 52, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 203, .pad = 52, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 203, .pad = 51, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 197, .pad = 51, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 197, .pad = 49, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 191, .pad = 49, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 191, .pad = 48, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 186, .pad = 48, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 186, .pad = 47, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 181, .pad = 47, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 181, .pad = 45, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 175, .pad = 45, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 175, .pad = 44, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 170, .pad = 44, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 170, .pad = 43, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 166, .pad = 43, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 166, .pad = 42, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 161, .pad = 42, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 161, .pad = 40, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 156, .pad = 40, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 156, .pad = 39, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 152, .pad = 39, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 152, .pad = 38, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 148, .pad = 38, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 148, .pad = 37, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 143, .pad = 37, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 143, .pad = 36, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 139, .pad = 36, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 139, .pad = 35, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 135, .pad = 35, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 135, .pad = 34, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 132, .pad = 34, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 132, .pad = 33, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 128, .pad = 33, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 128, .pad = 32, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 124, .pad = 32, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 124, .pad = 31, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 121, .pad = 31, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 121, .pad = 30, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 117, .pad = 30, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 117, .pad = 29, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 114, .pad = 29, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 114, .pad = 29, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 111, .pad = 29, .dac = 0, .bb_mult = 64, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev2_2ghz_tx_gain_table[] = { + { .gm = 7, .pga = 99, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 96, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 93, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 90, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 88, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 85, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 83, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 81, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 78, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 76, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 74, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 72, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 70, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 68, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 66, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 62, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 62, .pad = 248, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 60, .pad = 248, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 60, .pad = 241, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 59, .pad = 241, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 59, .pad = 234, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 57, .pad = 234, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 57, .pad = 227, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 55, .pad = 227, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 55, .pad = 221, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 54, .pad = 221, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 54, .pad = 215, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 52, .pad = 215, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 52, .pad = 208, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 51, .pad = 208, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 51, .pad = 203, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 49, .pad = 203, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 49, .pad = 197, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 48, .pad = 197, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 48, .pad = 191, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 47, .pad = 191, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 47, .pad = 186, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 45, .pad = 186, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 45, .pad = 181, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 44, .pad = 181, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 44, .pad = 175, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 43, .pad = 175, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 43, .pad = 170, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 42, .pad = 170, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 42, .pad = 166, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 40, .pad = 166, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 40, .pad = 161, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 39, .pad = 161, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 39, .pad = 156, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 38, .pad = 156, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 38, .pad = 152, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 37, .pad = 152, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 37, .pad = 148, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 36, .pad = 148, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 36, .pad = 143, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 35, .pad = 143, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 35, .pad = 139, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 34, .pad = 139, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 34, .pad = 135, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 33, .pad = 135, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 33, .pad = 132, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 32, .pad = 132, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 32, .pad = 128, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 31, .pad = 128, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 31, .pad = 124, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 30, .pad = 124, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 30, .pad = 121, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 29, .pad = 121, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 29, .pad = 117, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 29, .pad = 117, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 29, .pad = 114, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 28, .pad = 114, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 28, .pad = 111, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 27, .pad = 111, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 27, .pad = 108, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 26, .pad = 108, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 26, .pad = 104, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 25, .pad = 104, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 25, .pad = 102, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 25, .pad = 102, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 25, .pad = 99, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 24, .pad = 99, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 24, .pad = 96, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 23, .pad = 96, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 23, .pad = 93, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 23, .pad = 93, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 23, .pad = 90, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 22, .pad = 90, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 22, .pad = 88, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 21, .pad = 88, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 21, .pad = 85, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 21, .pad = 85, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 21, .pad = 83, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 20, .pad = 83, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 20, .pad = 81, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 20, .pad = 81, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 20, .pad = 78, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 19, .pad = 78, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 19, .pad = 76, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 19, .pad = 76, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 19, .pad = 74, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 18, .pad = 74, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 18, .pad = 72, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 18, .pad = 72, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 18, .pad = 70, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 17, .pad = 70, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 17, .pad = 68, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 17, .pad = 68, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 17, .pad = 66, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 16, .pad = 66, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 16, .pad = 64, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 16, .pad = 64, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 16, .pad = 62, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 62, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 60, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 60, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 15, .pad = 59, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 59, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 57, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 57, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 55, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 55, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 14, .pad = 54, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 54, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 52, .dac = 0, .bb_mult = 64, }, + { .gm = 7, .pga = 13, .pad = 52, .dac = 0, .bb_mult = 64, }, +}; + +static struct lpphy_tx_gain_table_entry lpphy_rev2_5ghz_tx_gain_table[] = { + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 152, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 147, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 143, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 139, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 135, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 131, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 128, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 124, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 121, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 117, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 114, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 111, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 107, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 104, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 101, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 99, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 96, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 93, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 90, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 88, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 85, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 83, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 81, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 78, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 76, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 74, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 72, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 70, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 68, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 66, }, + { .gm = 255, .pga = 255, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 248, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 241, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 234, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 227, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 221, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 215, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 208, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 203, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 197, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 191, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 186, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 181, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 175, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 170, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 166, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 161, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 156, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 152, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 148, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 143, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 139, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 135, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 132, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 128, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 124, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 121, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 117, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 114, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 111, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 108, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 104, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 102, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 99, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 96, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 93, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 90, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 88, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 85, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 83, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 81, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 78, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 76, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 74, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 72, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 70, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 68, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 66, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 64, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 62, .pad = 255, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 62, .pad = 248, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 60, .pad = 248, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 60, .pad = 241, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 59, .pad = 241, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 59, .pad = 234, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 57, .pad = 234, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 57, .pad = 227, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 55, .pad = 227, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 55, .pad = 221, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 54, .pad = 221, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 54, .pad = 215, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 52, .pad = 215, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 52, .pad = 208, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 51, .pad = 208, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 51, .pad = 203, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 49, .pad = 203, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 49, .pad = 197, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 48, .pad = 197, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 48, .pad = 191, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 47, .pad = 191, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 47, .pad = 186, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 45, .pad = 186, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 45, .pad = 181, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 44, .pad = 181, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 44, .pad = 175, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 43, .pad = 175, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 43, .pad = 170, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 42, .pad = 170, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 42, .pad = 166, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 40, .pad = 166, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 40, .pad = 161, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 39, .pad = 161, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 39, .pad = 156, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 38, .pad = 156, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 38, .pad = 152, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 37, .pad = 152, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 37, .pad = 148, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 36, .pad = 148, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 36, .pad = 143, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 35, .pad = 143, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 35, .pad = 139, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 34, .pad = 139, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 34, .pad = 135, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 33, .pad = 135, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 33, .pad = 132, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 32, .pad = 132, .dac = 0, .bb_mult = 64, }, + { .gm = 255, .pga = 32, .pad = 128, .dac = 0, .bb_mult = 64, }, +}; + +void lpphy_rev0_1_table_init(struct b43_wldev *dev) +{ + B43_WARN_ON(dev->phy.rev >= 2); + + b43_lptab_write_bulk(dev, B43_LPTAB8(2, 0), + ARRAY_SIZE(lpphy_min_sig_sq_table), lpphy_min_sig_sq_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(1, 0), + ARRAY_SIZE(lpphy_rev01_noise_scale_table), lpphy_rev01_noise_scale_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0), + ARRAY_SIZE(lpphy_crs_gain_nft_table), lpphy_crs_gain_nft_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(8, 0), + ARRAY_SIZE(lpphy_rev01_filter_control_table), lpphy_rev01_filter_control_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(9, 0), + ARRAY_SIZE(lpphy_rev01_ps_control_table), lpphy_rev01_ps_control_table); + b43_lptab_write_bulk(dev, B43_LPTAB8(6, 0), + ARRAY_SIZE(lpphy_pll_fraction_table), lpphy_pll_fraction_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(0, 0), + ARRAY_SIZE(lpphy_iqlo_cal_table), lpphy_iqlo_cal_table); + if (dev->phy.rev == 0) { + b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), + ARRAY_SIZE(lpphy_rev0_ofdm_cck_gain_table), lpphy_rev0_ofdm_cck_gain_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), + ARRAY_SIZE(lpphy_rev0_ofdm_cck_gain_table), lpphy_rev0_ofdm_cck_gain_table); + } else { + b43_lptab_write_bulk(dev, B43_LPTAB16(13, 0), + ARRAY_SIZE(lpphy_rev1_ofdm_cck_gain_table), lpphy_rev1_ofdm_cck_gain_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(12, 0), + ARRAY_SIZE(lpphy_rev1_ofdm_cck_gain_table), lpphy_rev1_ofdm_cck_gain_table); +} + b43_lptab_write_bulk(dev, B43_LPTAB16(15, 0), + ARRAY_SIZE(lpphy_gain_delta_table), lpphy_gain_delta_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0), + ARRAY_SIZE(lpphy_tx_power_control_table), lpphy_tx_power_control_table); +} + +void lpphy_rev2plus_table_init(struct b43_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + int i; + + B43_WARN_ON(dev->phy.rev < 2); + + for (i = 0; i < 704; i++) + b43_lptab_write(dev, B43_LPTAB32(7, i), 0); + + b43_lptab_write_bulk(dev, B43_LPTAB8(2, 0), + ARRAY_SIZE(lpphy_min_sig_sq_table), lpphy_min_sig_sq_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(1, 0), + ARRAY_SIZE(lpphy_rev2plus_noise_scale_table), lpphy_rev2plus_noise_scale_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(11, 0), + ARRAY_SIZE(lpphy_rev2plus_filter_control_table), lpphy_rev2plus_filter_control_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(12, 0), + ARRAY_SIZE(lpphy_rev2plus_ps_control_table), lpphy_rev2plus_ps_control_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(13, 0), + ARRAY_SIZE(lpphy_gain_idx_table), lpphy_gain_idx_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0), + ARRAY_SIZE(lpphy_aux_gain_idx_table), lpphy_aux_gain_idx_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(15, 0), + ARRAY_SIZE(lpphy_sw_control_table), lpphy_sw_control_table); + b43_lptab_write_bulk(dev, B43_LPTAB8(16, 0), + ARRAY_SIZE(lpphy_hf_table), lpphy_hf_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(17, 0), + ARRAY_SIZE(lpphy_gain_value_table), lpphy_gain_value_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(18, 0), + ARRAY_SIZE(lpphy_gain_table), lpphy_gain_table); + b43_lptab_write_bulk(dev, B43_LPTAB8(6, 0), + ARRAY_SIZE(lpphy_pll_fraction_table), lpphy_pll_fraction_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(0, 0), + ARRAY_SIZE(lpphy_iqlo_cal_table), lpphy_iqlo_cal_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(9, 0), + ARRAY_SIZE(lpphy_papd_eps_table), lpphy_papd_eps_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(10, 0), + ARRAY_SIZE(lpphy_papd_mult_table), lpphy_papd_mult_table); + + if ((bus->chip_id == 0x4325) && (bus->chip_rev == 0)) { + b43_lptab_write_bulk(dev, B43_LPTAB32(13, 0), + ARRAY_SIZE(lpphy_a0_gain_idx_table), lpphy_a0_gain_idx_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(14, 0), + ARRAY_SIZE(lpphy_a0_aux_gain_idx_table), lpphy_a0_aux_gain_idx_table); + b43_lptab_write_bulk(dev, B43_LPTAB32(17, 0), + ARRAY_SIZE(lpphy_a0_gain_value_table), lpphy_a0_gain_value_table); + b43_lptab_write_bulk(dev, B43_LPTAB16(18, 0), + ARRAY_SIZE(lpphy_a0_gain_table), lpphy_a0_gain_table); + } +} + +static void lpphy_rev0_1_write_gain_table(struct b43_wldev *dev, int offset, + struct lpphy_tx_gain_table_entry data) +{ + u32 tmp; + + B43_WARN_ON(dev->phy.rev >= 2); + + tmp = data.pad << 11; + tmp |= data.pga << 7; + tmp |= data.gm << 4; + tmp |= data.dac; + b43_lptab_write(dev, B43_LPTAB32(10, 0xC0 + offset), tmp); + tmp = data.bb_mult << 20; + b43_lptab_write(dev, B43_LPTAB32(10, 0x140 + offset), tmp); +} + +static void lpphy_rev2plus_write_gain_table(struct b43_wldev *dev, int offset, + struct lpphy_tx_gain_table_entry data) +{ + u32 tmp; + + B43_WARN_ON(dev->phy.rev < 2); + + tmp = data.pad << 16; + tmp |= data.pga << 8; + tmp |= data.gm; + if (dev->phy.rev >= 3) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + tmp |= 0x10 << 24; + else + tmp |= 0x70 << 24; + } else { + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + tmp |= 0x14 << 24; + else + tmp |= 0x7F << 24; + } + b43_lptab_write(dev, B43_LPTAB32(7, 0xC0 + offset), tmp); + tmp = data.bb_mult << 20; + tmp |= data.dac << 28; + b43_lptab_write(dev, B43_LPTAB32(7, 0x140 + offset), tmp); +} + +void lpphy_write_gain_table(struct b43_wldev *dev, int offset, + struct lpphy_tx_gain_table_entry data) +{ + if (dev->phy.rev >= 2) + lpphy_rev2plus_write_gain_table(dev, offset, data); + else + lpphy_rev0_1_write_gain_table(dev, offset, data); +} + +void lpphy_write_gain_table_bulk(struct b43_wldev *dev, int offset, int count, + struct lpphy_tx_gain_table_entry *table) +{ + int i; + + for (i = offset; i < count; i++) + lpphy_write_gain_table(dev, i, table[i]); +} + +void lpphy_init_tx_gain_table(struct b43_wldev *dev) +{ + struct ssb_bus *bus = dev->dev->bus; + + switch (dev->phy.rev) { + case 0: + if ((bus->sprom.boardflags_hi & B43_BFH_NOPA) || + (bus->sprom.boardflags_lo & B43_BFL_HGPA)) + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev0_nopa_tx_gain_table); + else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev0_2ghz_tx_gain_table); + else + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev0_5ghz_tx_gain_table); + break; + case 1: + if ((bus->sprom.boardflags_hi & B43_BFH_NOPA) || + (bus->sprom.boardflags_lo & B43_BFL_HGPA)) + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev1_nopa_tx_gain_table); + else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev1_2ghz_tx_gain_table); + else + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev1_5ghz_tx_gain_table); + break; + default: + if (bus->sprom.boardflags_hi & B43_BFH_NOPA) + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev2_nopa_tx_gain_table); + else if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev2_2ghz_tx_gain_table); + else + lpphy_write_gain_table_bulk(dev, 0, 128, + lpphy_rev2_5ghz_tx_gain_table); } } diff --git a/drivers/net/wireless/b43/tables_lpphy.h b/drivers/net/wireless/b43/tables_lpphy.h index 0b8d02895a5d..84f1d265f657 100644 --- a/drivers/net/wireless/b43/tables_lpphy.h +++ b/drivers/net/wireless/b43/tables_lpphy.h @@ -26,6 +26,19 @@ void b43_lptab_write_bulk(struct b43_wldev *dev, u32 offset, unsigned int nr_elements, const void *data); void b2062_upload_init_table(struct b43_wldev *dev); +void b2063_upload_init_table(struct b43_wldev *dev); +struct lpphy_tx_gain_table_entry { + u8 gm, pga, pad, dac, bb_mult; +}; + +void lpphy_write_gain_table(struct b43_wldev *dev, int offset, + struct lpphy_tx_gain_table_entry data); +void lpphy_write_gain_table_bulk(struct b43_wldev *dev, int offset, int count, + struct lpphy_tx_gain_table_entry *table); + +void lpphy_rev0_1_table_init(struct b43_wldev *dev); +void lpphy_rev2plus_table_init(struct b43_wldev *dev); +void lpphy_init_tx_gain_table(struct b43_wldev *dev); #endif /* B43_TABLES_LPPHY_H_ */ diff --git a/drivers/net/wireless/b43/wa.c b/drivers/net/wireless/b43/wa.c index e1e20f69f6d7..97c79161c208 100644 --- a/drivers/net/wireless/b43/wa.c +++ b/drivers/net/wireless/b43/wa.c @@ -37,7 +37,7 @@ static void b43_wa_papd(struct b43_wldev *dev) backup = b43_ofdmtab_read16(dev, B43_OFDMTAB_PWRDYN2, 0); b43_ofdmtab_write16(dev, B43_OFDMTAB_PWRDYN2, 0, 7); b43_ofdmtab_write16(dev, B43_OFDMTAB_UNKNOWN_APHY, 0, 0); - b43_dummy_transmission(dev); + b43_dummy_transmission(dev, true, true); b43_ofdmtab_write16(dev, B43_OFDMTAB_PWRDYN2, 0, backup); } @@ -628,7 +628,7 @@ void b43_wa_all(struct b43_wldev *dev) B43_WARN_ON(1); } b43_wa_boards_g(dev); - } else { /* No N PHY support so far */ + } else { /* No N PHY support so far, LP PHY is in phy_lp.c */ B43_WARN_ON(1); } diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 55f36a7254d9..e7075d2c7757 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -180,11 +180,12 @@ static u8 b43_calc_fallback_rate(u8 bitrate) /* Generate a TX data header. */ int b43_generate_txhdr(struct b43_wldev *dev, u8 *_txhdr, - const unsigned char *fragment_data, - unsigned int fragment_len, + struct sk_buff *skb_frag, struct ieee80211_tx_info *info, u16 cookie) { + const unsigned char *fragment_data = skb_frag->data; + unsigned int fragment_len = skb_frag->len; struct b43_txhdr *txhdr = (struct b43_txhdr *)_txhdr; const struct b43_phy *phy = &dev->phy; const struct ieee80211_hdr *wlhdr = @@ -237,7 +238,7 @@ int b43_generate_txhdr(struct b43_wldev *dev, int wlhdr_len; size_t iv_len; - B43_WARN_ON(key_idx >= dev->max_nr_keys); + B43_WARN_ON(key_idx >= ARRAY_SIZE(dev->key)); key = &(dev->key[key_idx]); if (unlikely(!key->keyconf)) { @@ -258,9 +259,26 @@ int b43_generate_txhdr(struct b43_wldev *dev, mac_ctl |= (key->algorithm << B43_TXH_MAC_KEYALG_SHIFT) & B43_TXH_MAC_KEYALG; wlhdr_len = ieee80211_hdrlen(fctl); - iv_len = min((size_t) info->control.hw_key->iv_len, - ARRAY_SIZE(txhdr->iv)); - memcpy(txhdr->iv, ((u8 *) wlhdr) + wlhdr_len, iv_len); + if (key->algorithm == B43_SEC_ALGO_TKIP) { + u16 phase1key[5]; + int i; + /* we give the phase1key and iv16 here, the key is stored in + * shm. With that the hardware can do phase 2 and encryption. + */ + ieee80211_get_tkip_key(info->control.hw_key, skb_frag, + IEEE80211_TKIP_P1_KEY, (u8*)phase1key); + /* phase1key is in host endian */ + for (i = 0; i < 5; i++) + phase1key[i] = cpu_to_le16(phase1key[i]); + + memcpy(txhdr->iv, phase1key, 10); + /* iv16 */ + memcpy(txhdr->iv + 10, ((u8 *) wlhdr) + wlhdr_len, 3); + } else { + iv_len = min((size_t) info->control.hw_key->iv_len, + ARRAY_SIZE(txhdr->iv)); + memcpy(txhdr->iv, ((u8 *) wlhdr) + wlhdr_len, iv_len); + } } if (b43_is_old_txhdr_format(dev)) { b43_generate_plcp_hdr((struct b43_plcp_hdr4 *)(&txhdr->old_format.plcp), @@ -578,7 +596,7 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) * key index, but the ucode passed it slightly different. */ keyidx = b43_kidx_to_raw(dev, keyidx); - B43_WARN_ON(keyidx >= dev->max_nr_keys); + B43_WARN_ON(keyidx >= ARRAY_SIZE(dev->key)); if (dev->key[keyidx].algorithm != B43_SEC_ALGO_NONE) { wlhdr_len = ieee80211_hdrlen(fctl); @@ -655,6 +673,7 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) status.freq = chanid + 2400; break; case B43_PHYTYPE_N: + case B43_PHYTYPE_LP: /* chanid is the SHM channel cookie. Which is the plain * channel number in b43. */ if (chanstat & B43_RX_CHAN_5GHZ) { @@ -670,7 +689,8 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) goto drop; } - ieee80211_rx_irqsafe(dev->wl->hw, skb, &status); + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + ieee80211_rx_irqsafe(dev->wl->hw, skb); return; drop: diff --git a/drivers/net/wireless/b43/xmit.h b/drivers/net/wireless/b43/xmit.h index 4fb2a190f7a7..3530de871873 100644 --- a/drivers/net/wireless/b43/xmit.h +++ b/drivers/net/wireless/b43/xmit.h @@ -176,8 +176,7 @@ size_t b43_txhdr_size(struct b43_wldev *dev) int b43_generate_txhdr(struct b43_wldev *dev, u8 * txhdr, - const unsigned char *fragment_data, - unsigned int fragment_len, + struct sk_buff *skb_frag, struct ieee80211_tx_info *txctl, u16 cookie); /* Transmit Status */ diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c index 2f90fb9f5367..866403415811 100644 --- a/drivers/net/wireless/b43legacy/dma.c +++ b/drivers/net/wireless/b43legacy/dma.c @@ -1366,15 +1366,25 @@ int b43legacy_dma_tx(struct b43legacy_wldev *dev, ring = priority_to_txring(dev, skb_get_queue_mapping(skb)); spin_lock_irqsave(&ring->lock, flags); B43legacy_WARN_ON(!ring->tx); - if (unlikely(free_slots(ring) < SLOTS_PER_PACKET)) { - b43legacywarn(dev->wl, "DMA queue overflow\n"); + + if (unlikely(ring->stopped)) { + /* We get here only because of a bug in mac80211. + * Because of a race, one packet may be queued after + * the queue is stopped, thus we got called when we shouldn't. + * For now, just refuse the transmit. */ + if (b43legacy_debug(dev, B43legacy_DBG_DMAVERBOSE)) + b43legacyerr(dev->wl, "Packet after queue stopped\n"); + err = -ENOSPC; + goto out_unlock; + } + + if (unlikely(WARN_ON(free_slots(ring) < SLOTS_PER_PACKET))) { + /* If we get here, we have a real error with the queue + * full, but queues not stopped. */ + b43legacyerr(dev->wl, "DMA queue overflow\n"); err = -ENOSPC; goto out_unlock; } - /* Check if the queue was stopped in mac80211, - * but we got called nevertheless. - * That would be a mac80211 bug. */ - B43legacy_BUG_ON(ring->stopped); err = dma_tx_fragment(ring, skb); if (unlikely(err == -ENOKEY)) { diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index c4973c1942bf..b166a6f9f055 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -1252,7 +1252,7 @@ static void b43legacy_update_templates(struct b43legacy_wl *wl) wl->current_beacon = beacon; wl->beacon0_uploaded = 0; wl->beacon1_uploaded = 0; - queue_work(wl->hw->workqueue, &wl->beacon_update_trigger); + ieee80211_queue_work(wl->hw, &wl->beacon_update_trigger); } static void b43legacy_set_beacon_int(struct b43legacy_wldev *dev, @@ -2300,7 +2300,7 @@ out_requeue: delay = msecs_to_jiffies(50); else delay = round_jiffies_relative(HZ * 15); - queue_delayed_work(wl->hw->workqueue, &dev->periodic_work, delay); + ieee80211_queue_delayed_work(wl->hw, &dev->periodic_work, delay); out: mutex_unlock(&wl->mutex); } @@ -2311,7 +2311,7 @@ static void b43legacy_periodic_tasks_setup(struct b43legacy_wldev *dev) dev->periodic_state = 0; INIT_DELAYED_WORK(work, b43legacy_periodic_work_handler); - queue_delayed_work(dev->wl->hw->workqueue, work, 0); + ieee80211_queue_delayed_work(dev->wl->hw, work, 0); } /* Validate access to the chip (SHM) */ @@ -2836,9 +2836,7 @@ static void b43legacy_op_bss_info_changed(struct ieee80211_hw *hw, static void b43legacy_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, - unsigned int *fflags, - int mc_count, - struct dev_addr_list *mc_list) + unsigned int *fflags,u64 multicast) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev = wl->current_dev; @@ -3885,7 +3883,7 @@ void b43legacy_controller_restart(struct b43legacy_wldev *dev, if (b43legacy_status(dev) < B43legacy_STAT_INITIALIZED) return; b43legacyinfo(dev->wl, "Controller RESET (%s) ...\n", reason); - queue_work(dev->wl->hw->workqueue, &dev->restart_work); + ieee80211_queue_work(dev->wl->hw, &dev->restart_work); } #ifdef CONFIG_PM diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c index b8e39dd06e99..f79cee82601b 100644 --- a/drivers/net/wireless/b43legacy/xmit.c +++ b/drivers/net/wireless/b43legacy/xmit.c @@ -591,7 +591,8 @@ void b43legacy_rx(struct b43legacy_wldev *dev, } dev->stats.last_rx = jiffies; - ieee80211_rx_irqsafe(dev->wl->hw, skb, &status); + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + ieee80211_rx_irqsafe(dev->wl->hw, skb); return; drop: diff --git a/drivers/net/wireless/hostap/hostap_80211.h b/drivers/net/wireless/hostap/hostap_80211.h index 2e9fb0f383fc..7f9d8d976aa8 100644 --- a/drivers/net/wireless/hostap/hostap_80211.h +++ b/drivers/net/wireless/hostap/hostap_80211.h @@ -3,6 +3,7 @@ #include <linux/types.h> #include <linux/skbuff.h> +#include <linux/netdevice.h> struct hostap_ieee80211_mgmt { __le16 frame_control; @@ -85,8 +86,11 @@ void hostap_dump_rx_80211(const char *name, struct sk_buff *skb, struct hostap_80211_rx_status *rx_stats); void hostap_dump_tx_80211(const char *name, struct sk_buff *skb); -int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev); -int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev); -int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev); +netdev_tx_t hostap_data_start_xmit(struct sk_buff *skb, + struct net_device *dev); +netdev_tx_t hostap_mgmt_start_xmit(struct sk_buff *skb, + struct net_device *dev); +netdev_tx_t hostap_master_start_xmit(struct sk_buff *skb, + struct net_device *dev); #endif /* HOSTAP_80211_H */ diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c index d313b005114e..90108b698f11 100644 --- a/drivers/net/wireless/hostap/hostap_80211_tx.c +++ b/drivers/net/wireless/hostap/hostap_80211_tx.c @@ -53,7 +53,8 @@ void hostap_dump_tx_80211(const char *name, struct sk_buff *skb) /* hard_start_xmit function for data interfaces (wlan#, wlan#wds#, wlan#sta) * Convert Ethernet header into a suitable IEEE 802.11 header depending on * device configuration. */ -int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev) +netdev_tx_t hostap_data_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct hostap_interface *iface; local_info_t *local; @@ -75,7 +76,7 @@ int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev) printk(KERN_DEBUG "%s: hostap_data_start_xmit: short skb " "(len=%d)\n", dev->name, skb->len); kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } if (local->ddev != dev) { @@ -89,14 +90,14 @@ int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev) printk(KERN_DEBUG "%s: prism2_tx: trying to use " "AP device with Ethernet net dev\n", dev->name); kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } } else { if (local->iw_mode == IW_MODE_REPEAT) { printk(KERN_DEBUG "%s: prism2_tx: trying to use " "non-WDS link in Repeater mode\n", dev->name); kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } else if (local->iw_mode == IW_MODE_INFRA && (local->wds_type & HOSTAP_WDS_AP_CLIENT) && memcmp(skb->data + ETH_ALEN, dev->dev_addr, @@ -210,13 +211,13 @@ int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev) skb = skb_unshare(skb, GFP_ATOMIC); if (skb == NULL) { iface->stats.tx_dropped++; - return 0; + return NETDEV_TX_OK; } if (pskb_expand_head(skb, need_headroom, need_tailroom, GFP_ATOMIC)) { kfree_skb(skb); iface->stats.tx_dropped++; - return 0; + return NETDEV_TX_OK; } } else if (skb_headroom(skb) < need_headroom) { struct sk_buff *tmp = skb; @@ -224,13 +225,13 @@ int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev) kfree_skb(tmp); if (skb == NULL) { iface->stats.tx_dropped++; - return 0; + return NETDEV_TX_OK; } } else { skb = skb_unshare(skb, GFP_ATOMIC); if (skb == NULL) { iface->stats.tx_dropped++; - return 0; + return NETDEV_TX_OK; } } @@ -256,12 +257,13 @@ int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Send IEEE 802.11 encapsulated frame using the master radio device */ skb->dev = local->dev; dev_queue_xmit(skb); - return 0; + return NETDEV_TX_OK; } /* hard_start_xmit function for hostapd wlan#ap interfaces */ -int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev) +netdev_tx_t hostap_mgmt_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct hostap_interface *iface; local_info_t *local; @@ -276,7 +278,7 @@ int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev) printk(KERN_DEBUG "%s: hostap_mgmt_start_xmit: short skb " "(len=%d)\n", dev->name, skb->len); kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } iface->stats.tx_packets++; @@ -301,7 +303,7 @@ int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Send IEEE 802.11 encapsulated frame using the master radio device */ skb->dev = local->dev; dev_queue_xmit(skb); - return 0; + return NETDEV_TX_OK; } @@ -373,11 +375,12 @@ static struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb, /* hard_start_xmit function for master radio interface wifi#. * AP processing (TX rate control, power save buffering, etc.). * Use hardware TX function to send the frame. */ -int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev) +netdev_tx_t hostap_master_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct hostap_interface *iface; local_info_t *local; - int ret = NETDEV_TX_BUSY; + netdev_tx_t ret = NETDEV_TX_BUSY; u16 fc; struct hostap_tx_data tx; ap_tx_ret tx_ret; @@ -396,7 +399,7 @@ int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev) printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, " "expected 0x%08x)\n", dev->name, meta->magic, HOSTAP_SKB_TX_DATA_MAGIC); - ret = 0; + ret = NETDEV_TX_OK; iface->stats.tx_dropped++; goto fail; } @@ -414,7 +417,7 @@ int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev) if (skb->len < 24) { printk(KERN_DEBUG "%s: hostap_master_start_xmit: short skb " "(len=%d)\n", dev->name, skb->len); - ret = 0; + ret = NETDEV_TX_OK; iface->stats.tx_dropped++; goto fail; } @@ -441,13 +444,13 @@ int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev) dev->name, meta->ethertype); hostap_dump_tx_80211(dev->name, skb); - ret = 0; /* drop packet */ + ret = NETDEV_TX_OK; /* drop packet */ iface->stats.tx_dropped++; goto fail; } break; case AP_TX_DROP: - ret = 0; /* drop packet */ + ret = NETDEV_TX_OK; /* drop packet */ iface->stats.tx_dropped++; goto fail; case AP_TX_RETRY: @@ -455,7 +458,7 @@ int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev) case AP_TX_BUFFERED: /* do not free skb here, it will be freed when the * buffered frame is sent/timed out */ - ret = 0; + ret = NETDEV_TX_OK; goto tx_exit; } @@ -501,7 +504,7 @@ int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev) "frame (drop_unencrypted=1)\n", dev->name); } iface->stats.tx_dropped++; - ret = 0; + ret = NETDEV_TX_OK; goto fail; } @@ -510,7 +513,7 @@ int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev) if (skb == NULL) { printk(KERN_DEBUG "%s: TX - encryption failed\n", dev->name); - ret = 0; + ret = NETDEV_TX_OK; goto fail; } meta = (struct hostap_skb_tx_data *) skb->cb; @@ -519,23 +522,23 @@ int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev) "expected 0x%08x) after hostap_tx_encrypt\n", dev->name, meta->magic, HOSTAP_SKB_TX_DATA_MAGIC); - ret = 0; + ret = NETDEV_TX_OK; iface->stats.tx_dropped++; goto fail; } } if (local->func->tx == NULL || local->func->tx(skb, dev)) { - ret = 0; + ret = NETDEV_TX_OK; iface->stats.tx_dropped++; } else { - ret = 0; + ret = NETDEV_TX_OK; iface->stats.tx_packets++; iface->stats.tx_bytes += skb->len; } fail: - if (!ret && skb) + if (ret == NETDEV_TX_OK && skb) dev_kfree_skb(skb); tx_exit: if (tx.sta_ptr) diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index 633740277352..ad8eab4a639b 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -666,7 +666,8 @@ static int prism2_config(struct pcmcia_device *link) * irq structure is initialized. */ if (link->conf.Attributes & CONF_ENABLE_IRQ) { - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | + IRQ_HANDLE_PRESENT; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = prism2_interrupt; link->irq.Instance = dev; diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c index 742432388ca3..240cff1e6979 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -19,7 +19,7 @@ file called LICENSE. Contact Information: - James P. Ketrenos <ipw2100-admin@linux.intel.com> + Intel Linux Wireless <ilw@linux.intel.com> Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 Portions of this file are based on the sample_* files provided by Wireless @@ -185,7 +185,7 @@ MODULE_AUTHOR(DRV_COPYRIGHT); MODULE_LICENSE("GPL"); static int debug = 0; -static int mode = 0; +static int network_mode = 0; static int channel = 0; static int associate = 0; static int disable = 0; @@ -195,7 +195,7 @@ static struct ipw2100_fw ipw2100_firmware; #include <linux/moduleparam.h> module_param(debug, int, 0444); -module_param(mode, int, 0444); +module_param_named(mode, network_mode, int, 0444); module_param(channel, int, 0444); module_param(associate, int, 0444); module_param(disable, int, 0444); @@ -1673,7 +1673,7 @@ static int ipw2100_start_scan(struct ipw2100_priv *priv) return err; } -static const struct ieee80211_geo ipw_geos[] = { +static const struct libipw_geo ipw_geos[] = { { /* Restricted */ "---", .bg_channels = 14, @@ -1694,7 +1694,7 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) /* Age scan list entries found before suspend */ if (priv->suspend_time) { - ieee80211_networks_age(priv->ieee, priv->suspend_time); + libipw_networks_age(priv->ieee, priv->suspend_time); priv->suspend_time = 0; } @@ -1752,11 +1752,11 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) } /* Initialize the geo */ - if (ieee80211_set_geo(priv->ieee, &ipw_geos[0])) { + if (libipw_set_geo(priv->ieee, &ipw_geos[0])) { printk(KERN_WARNING DRV_NAME "Could not set geo\n"); return 0; } - priv->ieee->freq_band = IEEE80211_24GHZ_BAND; + priv->ieee->freq_band = LIBIPW_24GHZ_BAND; lock = LOCK_NONE; if (ipw2100_set_ordinal(priv, IPW_ORD_PERS_DB_LOCK, &lock, &ord_len)) { @@ -1817,7 +1817,7 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) /* Called by register_netdev() */ static int ipw2100_net_init(struct net_device *dev) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); return ipw2100_up(priv, 1); } @@ -2340,8 +2340,8 @@ static u32 ipw2100_match_buf(struct ipw2100_priv *priv, u8 * in_buf, * * When packet is provided by the firmware, it contains the following: * - * . ieee80211_hdr - * . ieee80211_snap_hdr + * . libipw_hdr + * . libipw_snap_hdr * * The size of the constructed ethernet * @@ -2396,7 +2396,7 @@ static void ipw2100_corruption_detected(struct ipw2100_priv *priv, int i) } static void isr_rx(struct ipw2100_priv *priv, int i, - struct ieee80211_rx_stats *stats) + struct libipw_rx_stats *stats) { struct net_device *dev = priv->net_dev; struct ipw2100_status *status = &priv->status_queue.drv[i]; @@ -2435,13 +2435,13 @@ static void isr_rx(struct ipw2100_priv *priv, int i, #ifdef IPW2100_RX_DEBUG /* Make a copy of the frame so we can dump it to the logs if - * ieee80211_rx fails */ + * libipw_rx fails */ skb_copy_from_linear_data(packet->skb, packet_data, min_t(u32, status->frame_size, IPW_RX_NIC_BUFFER_LENGTH)); #endif - if (!ieee80211_rx(priv->ieee, packet->skb, stats)) { + if (!libipw_rx(priv->ieee, packet->skb, stats)) { #ifdef IPW2100_RX_DEBUG IPW_DEBUG_DROP("%s: Non consumed packet:\n", dev->name); @@ -2449,7 +2449,7 @@ static void isr_rx(struct ipw2100_priv *priv, int i, #endif dev->stats.rx_errors++; - /* ieee80211_rx failed, so it didn't free the SKB */ + /* libipw_rx failed, so it didn't free the SKB */ dev_kfree_skb_any(packet->skb); packet->skb = NULL; } @@ -2470,7 +2470,7 @@ static void isr_rx(struct ipw2100_priv *priv, int i, #ifdef CONFIG_IPW2100_MONITOR static void isr_rx_monitor(struct ipw2100_priv *priv, int i, - struct ieee80211_rx_stats *stats) + struct libipw_rx_stats *stats) { struct net_device *dev = priv->net_dev; struct ipw2100_status *status = &priv->status_queue.drv[i]; @@ -2528,10 +2528,10 @@ static void isr_rx_monitor(struct ipw2100_priv *priv, int i, skb_put(packet->skb, status->frame_size + sizeof(struct ipw_rt_hdr)); - if (!ieee80211_rx(priv->ieee, packet->skb, stats)) { + if (!libipw_rx(priv->ieee, packet->skb, stats)) { dev->stats.rx_errors++; - /* ieee80211_rx failed, so it didn't free the SKB */ + /* libipw_rx failed, so it didn't free the SKB */ dev_kfree_skb_any(packet->skb); packet->skb = NULL; } @@ -2615,7 +2615,7 @@ static void __ipw2100_rx_process(struct ipw2100_priv *priv) u16 frame_type; u32 r, w, i, s; struct ipw2100_rx *u; - struct ieee80211_rx_stats stats = { + struct libipw_rx_stats stats = { .mac_time = jiffies, }; @@ -2661,8 +2661,8 @@ static void __ipw2100_rx_process(struct ipw2100_priv *priv) stats.mask = 0; if (stats.rssi != 0) - stats.mask |= IEEE80211_STATMASK_RSSI; - stats.freq = IEEE80211_24GHZ_BAND; + stats.mask |= LIBIPW_STATMASK_RSSI; + stats.freq = LIBIPW_24GHZ_BAND; IPW_DEBUG_RX("%s: '%s' frame type received (%d).\n", priv->net_dev->name, frame_types[frame_type], @@ -2686,11 +2686,11 @@ static void __ipw2100_rx_process(struct ipw2100_priv *priv) break; } #endif - if (stats.len < sizeof(struct ieee80211_hdr_3addr)) + if (stats.len < sizeof(struct libipw_hdr_3addr)) break; switch (WLAN_FC_GET_TYPE(le16_to_cpu(u->rx_data.header.frame_ctl))) { case IEEE80211_FTYPE_MGMT: - ieee80211_rx_mgt(priv->ieee, + libipw_rx_mgt(priv->ieee, &u->rx_data.header, &stats); break; @@ -2844,7 +2844,7 @@ static int __ipw2100_tx_process(struct ipw2100_priv *priv) #ifdef CONFIG_IPW2100_DEBUG { - int i = txq->oldest; + i = txq->oldest; IPW_DEBUG_TX("TX%d V=%p P=%04X T=%04X L=%d\n", i, &txq->drv[i], (u32) (txq->nic + i * sizeof(struct ipw2100_bd)), @@ -2884,7 +2884,7 @@ static int __ipw2100_tx_process(struct ipw2100_priv *priv) tbd->buf_length, PCI_DMA_TODEVICE); } - ieee80211_txb_free(packet->info.d_struct.txb); + libipw_txb_free(packet->info.d_struct.txb); packet->info.d_struct.txb = NULL; list_add_tail(element, &priv->tx_free_list); @@ -3028,7 +3028,7 @@ static void ipw2100_tx_send_data(struct ipw2100_priv *priv) int next = txq->next; int i = 0; struct ipw2100_data_header *ipw_hdr; - struct ieee80211_hdr_3addr *hdr; + struct libipw_hdr_3addr *hdr; while (!list_empty(&priv->tx_pend_list)) { /* if there isn't enough space in TBD queue, then @@ -3062,7 +3062,7 @@ static void ipw2100_tx_send_data(struct ipw2100_priv *priv) packet->index = txq->next; ipw_hdr = packet->info.d_struct.data; - hdr = (struct ieee80211_hdr_3addr *)packet->info.d_struct.txb-> + hdr = (struct libipw_hdr_3addr *)packet->info.d_struct.txb-> fragments[0]->data; if (priv->ieee->iw_mode == IW_MODE_INFRA) { @@ -3086,7 +3086,7 @@ static void ipw2100_tx_send_data(struct ipw2100_priv *priv) if (packet->info.d_struct.txb->nr_frags > 1) ipw_hdr->fragment_size = packet->info.d_struct.txb->frag_size - - IEEE80211_3ADDR_LEN; + LIBIPW_3ADDR_LEN; else ipw_hdr->fragment_size = 0; @@ -3119,13 +3119,13 @@ static void ipw2100_tx_send_data(struct ipw2100_priv *priv) IPW_BD_STATUS_TX_FRAME_NOT_LAST_FRAGMENT; tbd->buf_length = packet->info.d_struct.txb-> - fragments[i]->len - IEEE80211_3ADDR_LEN; + fragments[i]->len - LIBIPW_3ADDR_LEN; tbd->host_addr = pci_map_single(priv->pci_dev, packet->info.d_struct. txb->fragments[i]-> data + - IEEE80211_3ADDR_LEN, + LIBIPW_3ADDR_LEN, tbd->buf_length, PCI_DMA_TODEVICE); @@ -3330,10 +3330,10 @@ static irqreturn_t ipw2100_interrupt(int irq, void *data) return IRQ_NONE; } -static int ipw2100_tx(struct ieee80211_txb *txb, struct net_device *dev, - int pri) +static netdev_tx_t ipw2100_tx(struct libipw_txb *txb, + struct net_device *dev, int pri) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); struct list_head *element; struct ipw2100_tx_packet *packet; unsigned long flags; @@ -3369,12 +3369,12 @@ static int ipw2100_tx(struct ieee80211_txb *txb, struct net_device *dev, ipw2100_tx_send_data(priv); spin_unlock_irqrestore(&priv->low_lock, flags); - return 0; + return NETDEV_TX_OK; - fail_unlock: +fail_unlock: netif_stop_queue(dev); spin_unlock_irqrestore(&priv->low_lock, flags); - return 1; + return NETDEV_TX_BUSY; } static int ipw2100_msg_allocate(struct ipw2100_priv *priv) @@ -4488,7 +4488,7 @@ static void ipw2100_tx_initialize(struct ipw2100_priv *priv) /* We simply drop any SKBs that have been queued for * transmit */ if (priv->tx_buffers[i].info.d_struct.txb) { - ieee80211_txb_free(priv->tx_buffers[i].info.d_struct. + libipw_txb_free(priv->tx_buffers[i].info.d_struct. txb); priv->tx_buffers[i].info.d_struct.txb = NULL; } @@ -4527,7 +4527,7 @@ static void ipw2100_tx_free(struct ipw2100_priv *priv) for (i = 0; i < TX_PENDED_QUEUE_LENGTH; i++) { if (priv->tx_buffers[i].info.d_struct.txb) { - ieee80211_txb_free(priv->tx_buffers[i].info.d_struct. + libipw_txb_free(priv->tx_buffers[i].info.d_struct. txb); priv->tx_buffers[i].info.d_struct.txb = NULL; } @@ -5558,9 +5558,9 @@ static void ipw2100_security_work(struct work_struct *work) } static void shim__set_security(struct net_device *dev, - struct ieee80211_security *sec) + struct libipw_security *sec) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int i, force_update = 0; mutex_lock(&priv->action_mutex); @@ -5753,7 +5753,7 @@ static int ipw2100_adapter_setup(struct ipw2100_priv *priv) * method as well) to talk to the firmware */ static int ipw2100_set_address(struct net_device *dev, void *p) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); struct sockaddr *addr = p; int err = 0; @@ -5781,7 +5781,7 @@ static int ipw2100_set_address(struct net_device *dev, void *p) static int ipw2100_open(struct net_device *dev) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); unsigned long flags; IPW_DEBUG_INFO("dev->open\n"); @@ -5797,7 +5797,7 @@ static int ipw2100_open(struct net_device *dev) static int ipw2100_close(struct net_device *dev) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); unsigned long flags; struct list_head *element; struct ipw2100_tx_packet *packet; @@ -5818,7 +5818,7 @@ static int ipw2100_close(struct net_device *dev) list_del(element); DEC_STAT(&priv->tx_pend_stat); - ieee80211_txb_free(packet->info.d_struct.txb); + libipw_txb_free(packet->info.d_struct.txb); packet->info.d_struct.txb = NULL; list_add_tail(element, &priv->tx_free_list); @@ -5836,7 +5836,7 @@ static int ipw2100_close(struct net_device *dev) */ static void ipw2100_tx_timeout(struct net_device *dev) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); dev->stats.tx_errors++; @@ -5861,8 +5861,8 @@ static int ipw2100_wpa_enable(struct ipw2100_priv *priv, int value) static int ipw2100_wpa_set_auth_algs(struct ipw2100_priv *priv, int value) { - struct ieee80211_device *ieee = priv->ieee; - struct ieee80211_security sec = { + struct libipw_device *ieee = priv->ieee; + struct libipw_security sec = { .flags = SEC_AUTH_MODE, }; int ret = 0; @@ -5907,7 +5907,7 @@ static void ipw2100_wpa_assoc_frame(struct ipw2100_priv *priv, static void ipw_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); char fw_ver[64], ucode_ver[64]; strcpy(info->driver, DRV_NAME); @@ -5924,7 +5924,7 @@ static void ipw_ethtool_get_drvinfo(struct net_device *dev, static u32 ipw2100_ethtool_get_link(struct net_device *dev) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); return (priv->status & STATUS_ASSOCIATED) ? 1 : 0; } @@ -6011,8 +6011,8 @@ static void ipw2100_irq_tasklet(struct ipw2100_priv *priv); static const struct net_device_ops ipw2100_netdev_ops = { .ndo_open = ipw2100_open, .ndo_stop = ipw2100_close, - .ndo_start_xmit = ieee80211_xmit, - .ndo_change_mtu = ieee80211_change_mtu, + .ndo_start_xmit = libipw_xmit, + .ndo_change_mtu = libipw_change_mtu, .ndo_init = ipw2100_net_init, .ndo_tx_timeout = ipw2100_tx_timeout, .ndo_set_mac_address = ipw2100_set_address, @@ -6029,10 +6029,10 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev, struct ipw2100_priv *priv; struct net_device *dev; - dev = alloc_ieee80211(sizeof(struct ipw2100_priv)); + dev = alloc_ieee80211(sizeof(struct ipw2100_priv), 0); if (!dev) return NULL; - priv = ieee80211_priv(dev); + priv = libipw_priv(dev); priv->ieee = netdev_priv(dev); priv->pci_dev = pci_dev; priv->net_dev = dev; @@ -6046,7 +6046,7 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev, dev->netdev_ops = &ipw2100_netdev_ops; dev->ethtool_ops = &ipw2100_ethtool_ops; dev->wireless_handlers = &ipw2100_wx_handler_def; - priv->wireless_data.ieee80211 = priv->ieee; + priv->wireless_data.libipw = priv->ieee; dev->wireless_data = &priv->wireless_data; dev->watchdog_timeo = 3 * HZ; dev->irq = 0; @@ -6076,7 +6076,7 @@ static struct net_device *ipw2100_alloc_device(struct pci_dev *pci_dev, priv->ieee->ieee802_1x = 1; /* Set module parameters */ - switch (mode) { + switch (network_mode) { case 1: priv->ieee->iw_mode = IW_MODE_ADHOC; break; @@ -6202,7 +6202,7 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev, return err; } - priv = ieee80211_priv(dev); + priv = libipw_priv(dev); pci_set_master(pci_dev); pci_set_drvdata(pci_dev, priv); @@ -6342,7 +6342,7 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev, sysfs_remove_group(&pci_dev->dev.kobj, &ipw2100_attribute_group); - free_ieee80211(dev); + free_ieee80211(dev, 0); pci_set_drvdata(pci_dev, NULL); } @@ -6400,7 +6400,7 @@ static void __devexit ipw2100_pci_remove_one(struct pci_dev *pci_dev) if (dev->base_addr) iounmap((void __iomem *)dev->base_addr); - free_ieee80211(dev); + free_ieee80211(dev, 0); } pci_release_regions(pci_dev); @@ -6629,7 +6629,7 @@ static int ipw2100_wx_get_name(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); if (!(priv->status & STATUS_ASSOCIATED)) strcpy(wrqu->name, "unassociated"); else @@ -6643,7 +6643,7 @@ static int ipw2100_wx_set_freq(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); struct iw_freq *fwrq = &wrqu->freq; int err = 0; @@ -6693,7 +6693,7 @@ static int ipw2100_wx_get_freq(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); wrqu->freq.e = 0; @@ -6714,7 +6714,7 @@ static int ipw2100_wx_set_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int err = 0; IPW_DEBUG_WX("SET Mode -> %d \n", wrqu->mode); @@ -6757,7 +6757,7 @@ static int ipw2100_wx_get_mode(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); wrqu->mode = priv->ieee->iw_mode; IPW_DEBUG_WX("GET Mode -> %d\n", wrqu->mode); @@ -6792,7 +6792,7 @@ static int ipw2100_wx_get_range(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); struct iw_range *range = (struct iw_range *)extra; u16 val; int i, level; @@ -6913,7 +6913,7 @@ static int ipw2100_wx_set_wap(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int err = 0; static const unsigned char any[] = { @@ -6962,7 +6962,7 @@ static int ipw2100_wx_get_wap(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); /* If we are associated, trying to associate, or have a statically * configured BSSID then return that; otherwise return ANY */ @@ -6980,7 +6980,7 @@ static int ipw2100_wx_set_essid(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); char *essid = ""; /* ANY */ int length = 0; int err = 0; @@ -7035,7 +7035,7 @@ static int ipw2100_wx_get_essid(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); DECLARE_SSID_BUF(ssid); /* If we are associated, trying to associate, or have a statically @@ -7063,7 +7063,7 @@ static int ipw2100_wx_set_nick(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); if (wrqu->data.length > IW_ESSID_MAX_SIZE) return -E2BIG; @@ -7085,7 +7085,7 @@ static int ipw2100_wx_get_nick(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); wrqu->data.length = strlen(priv->nick); memcpy(extra, priv->nick, wrqu->data.length); @@ -7100,7 +7100,7 @@ static int ipw2100_wx_set_rate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); u32 target_rate = wrqu->bitrate.value; u32 rate; int err = 0; @@ -7140,7 +7140,7 @@ static int ipw2100_wx_get_rate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int val; unsigned int len = sizeof(val); int err = 0; @@ -7192,7 +7192,7 @@ static int ipw2100_wx_set_rts(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int value, err; /* Auto RTS not yet supported */ @@ -7231,7 +7231,7 @@ static int ipw2100_wx_get_rts(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); wrqu->rts.value = priv->rts_threshold & ~RTS_DISABLED; wrqu->rts.fixed = 1; /* no auto select */ @@ -7248,7 +7248,7 @@ static int ipw2100_wx_set_txpow(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int err = 0, value; if (ipw_radio_kill_sw(priv, wrqu->txpower.disabled)) @@ -7293,7 +7293,7 @@ static int ipw2100_wx_get_txpow(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); wrqu->txpower.disabled = (priv->status & STATUS_RF_KILL_MASK) ? 1 : 0; @@ -7320,7 +7320,7 @@ static int ipw2100_wx_set_frag(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); if (!wrqu->frag.fixed) return -EINVAL; @@ -7350,7 +7350,7 @@ static int ipw2100_wx_get_frag(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); wrqu->frag.value = priv->frag_threshold & ~FRAG_DISABLED; wrqu->frag.fixed = 0; /* no auto select */ wrqu->frag.disabled = (priv->frag_threshold & FRAG_DISABLED) ? 1 : 0; @@ -7364,7 +7364,7 @@ static int ipw2100_wx_set_retry(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int err = 0; if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled) @@ -7412,7 +7412,7 @@ static int ipw2100_wx_get_retry(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); wrqu->retry.disabled = 0; /* can't be disabled */ @@ -7440,7 +7440,7 @@ static int ipw2100_wx_set_scan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int err = 0; mutex_lock(&priv->action_mutex); @@ -7472,8 +7472,8 @@ static int ipw2100_wx_get_scan(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); - return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra); + struct ipw2100_priv *priv = libipw_priv(dev); + return libipw_wx_get_scan(priv->ieee, info, wrqu, extra); } /* @@ -7487,8 +7487,8 @@ static int ipw2100_wx_set_encode(struct net_device *dev, * No check of STATUS_INITIALIZED required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); - return ieee80211_wx_set_encode(priv->ieee, info, wrqu, key); + struct ipw2100_priv *priv = libipw_priv(dev); + return libipw_wx_set_encode(priv->ieee, info, wrqu, key); } static int ipw2100_wx_get_encode(struct net_device *dev, @@ -7499,15 +7499,15 @@ static int ipw2100_wx_get_encode(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); - return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key); + struct ipw2100_priv *priv = libipw_priv(dev); + return libipw_wx_get_encode(priv->ieee, info, wrqu, key); } static int ipw2100_wx_set_power(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int err = 0; mutex_lock(&priv->action_mutex); @@ -7556,7 +7556,7 @@ static int ipw2100_wx_get_power(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); if (!(priv->power_mode & IPW_POWER_ENABLED)) wrqu->power.disabled = 1; @@ -7580,8 +7580,8 @@ static int ipw2100_wx_set_genie(struct net_device *dev, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); - struct ieee80211_device *ieee = priv->ieee; + struct ipw2100_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; u8 *buf; if (!ieee->wpa_enabled) @@ -7615,8 +7615,8 @@ static int ipw2100_wx_get_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); - struct ieee80211_device *ieee = priv->ieee; + struct ipw2100_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) { wrqu->data.length = 0; @@ -7637,8 +7637,8 @@ static int ipw2100_wx_set_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); - struct ieee80211_device *ieee = priv->ieee; + struct ipw2100_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; struct iw_param *param = &wrqu->param; struct lib80211_crypt_data *crypt; unsigned long flags; @@ -7682,7 +7682,7 @@ static int ipw2100_wx_set_auth(struct net_device *dev, * can use this to determine if the CAP_PRIVACY_ON bit should * be set. */ - struct ieee80211_security sec = { + struct libipw_security sec = { .flags = SEC_ENABLED, .enabled = param->value, }; @@ -7730,8 +7730,8 @@ static int ipw2100_wx_get_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); - struct ieee80211_device *ieee = priv->ieee; + struct ipw2100_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; struct lib80211_crypt_data *crypt; struct iw_param *param = &wrqu->param; int ret = 0; @@ -7792,8 +7792,8 @@ static int ipw2100_wx_set_encodeext(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); - return ieee80211_wx_set_encodeext(priv->ieee, info, wrqu, extra); + struct ipw2100_priv *priv = libipw_priv(dev); + return libipw_wx_set_encodeext(priv->ieee, info, wrqu, extra); } /* SIOCGIWENCODEEXT */ @@ -7801,8 +7801,8 @@ static int ipw2100_wx_get_encodeext(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); - return ieee80211_wx_get_encodeext(priv->ieee, info, wrqu, extra); + struct ipw2100_priv *priv = libipw_priv(dev); + return libipw_wx_get_encodeext(priv->ieee, info, wrqu, extra); } /* SIOCSIWMLME */ @@ -7810,7 +7810,7 @@ static int ipw2100_wx_set_mlme(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); struct iw_mlme *mlme = (struct iw_mlme *)extra; __le16 reason; @@ -7841,7 +7841,7 @@ static int ipw2100_wx_set_promisc(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int *parms = (int *)extra; int enable = (parms[0] > 0); int err = 0; @@ -7872,7 +7872,7 @@ static int ipw2100_wx_reset(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); if (priv->status & STATUS_INITIALIZED) schedule_reset(priv); return 0; @@ -7884,7 +7884,7 @@ static int ipw2100_wx_set_powermode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int err = 0, mode = *(int *)extra; mutex_lock(&priv->action_mutex); @@ -7912,7 +7912,7 @@ static int ipw2100_wx_get_powermode(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int level = IPW_POWER_LEVEL(priv->power_mode); s32 timeout, period; @@ -7948,7 +7948,7 @@ static int ipw2100_wx_set_preamble(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int err, mode = *(int *)extra; mutex_lock(&priv->action_mutex); @@ -7981,7 +7981,7 @@ static int ipw2100_wx_get_preamble(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); if (priv->config & CFG_LONG_PREAMBLE) snprintf(wrqu->name, IFNAMSIZ, "long (1)"); @@ -7996,7 +7996,7 @@ static int ipw2100_wx_set_crc_check(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); int err, mode = *(int *)extra; mutex_lock(&priv->action_mutex); @@ -8028,7 +8028,7 @@ static int ipw2100_wx_get_crc_check(struct net_device *dev, * This can be called at any time. No action lock required */ - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); if (priv->config & CFG_CRC_CHECK) snprintf(wrqu->name, IFNAMSIZ, "CRC checked (1)"); @@ -8179,10 +8179,11 @@ static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev) int rssi_qual; int tx_qual; int beacon_qual; + int quality; - struct ipw2100_priv *priv = ieee80211_priv(dev); + struct ipw2100_priv *priv = libipw_priv(dev); struct iw_statistics *wstats; - u32 rssi, quality, tx_retries, missed_beacons, tx_failures; + u32 rssi, tx_retries, missed_beacons, tx_failures; u32 ord_len = sizeof(u32); if (!priv) @@ -8265,7 +8266,8 @@ static struct iw_statistics *ipw2100_wx_wireless_stats(struct net_device *dev) beacon_qual = (20 - missed_beacons) * (PERFECT - VERY_GOOD) / 20 + VERY_GOOD; - quality = min(beacon_qual, min(tx_qual, rssi_qual)); + quality = min(tx_qual, rssi_qual); + quality = min(beacon_qual, quality); #ifdef CONFIG_IPW2100_DEBUG if (beacon_qual == quality) diff --git a/drivers/net/wireless/ipw2x00/ipw2100.h b/drivers/net/wireless/ipw2x00/ipw2100.h index f183d951cd32..1eab0d698f4d 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.h +++ b/drivers/net/wireless/ipw2x00/ipw2100.h @@ -19,7 +19,7 @@ file called LICENSE. Contact Information: - James P. Ketrenos <ipw2100-admin@linux.intel.com> + Intel Linux Wireless <ilw@linux.intel.com> Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ******************************************************************************/ @@ -46,7 +46,7 @@ #include <linux/workqueue.h> #include <linux/mutex.h> -#include "ieee80211.h" +#include "libipw.h" struct ipw2100_priv; struct ipw2100_tx_packet; @@ -343,7 +343,7 @@ struct ipw2100_tx_packet { struct { /* DATA */ struct ipw2100_data_header *data; dma_addr_t data_phys; - struct ieee80211_txb *txb; + struct libipw_txb *txb; } d_struct; } info; int jiffy_start; @@ -492,7 +492,7 @@ struct ipw2100_priv { int stop_hang_check; /* Set 1 when shutting down to kill hang_check */ int stop_rf_kill; /* Set 1 when shutting down to kill rf_kill */ - struct ieee80211_device *ieee; + struct libipw_device *ieee; unsigned long status; unsigned long config; unsigned long capability; @@ -788,7 +788,7 @@ struct ipw2100_priv { #define IPW_CARD_DISABLE_PHY_OFF_COMPLETE_WAIT 100 // 100 milli #define IPW_PREPARE_POWER_DOWN_COMPLETE_WAIT 100 // 100 milli -#define IPW_HEADER_802_11_SIZE sizeof(struct ieee80211_hdr_3addr) +#define IPW_HEADER_802_11_SIZE sizeof(struct libipw_hdr_3addr) #define IPW_MAX_80211_PAYLOAD_SIZE 2304U #define IPW_MAX_802_11_PAYLOAD_LENGTH 2312 #define IPW_MAX_ACCEPTABLE_TX_FRAME_LENGTH 1536 @@ -803,13 +803,13 @@ struct ipw2100_priv { IPW_802_11_FCS_LENGTH) #define IPW_802_11_PAYLOAD_OFFSET \ - (sizeof(struct ieee80211_hdr_3addr) + \ - sizeof(struct ieee80211_snap_hdr)) + (sizeof(struct libipw_hdr_3addr) + \ + sizeof(struct libipw_snap_hdr)) struct ipw2100_rx { union { unsigned char payload[IPW_RX_NIC_BUFFER_LENGTH]; - struct ieee80211_hdr_4addr header; + struct libipw_hdr_4addr header; u32 status; struct ipw2100_notification notification; struct ipw2100_cmd_header command; diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index f593fbbb4e52..8d58e6ed4e7d 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -25,7 +25,7 @@ file called LICENSE. Contact Information: - James P. Ketrenos <ipw2100-admin@linux.intel.com> + Intel Linux Wireless <ilw@linux.intel.com> Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ******************************************************************************/ @@ -83,13 +83,13 @@ MODULE_LICENSE("GPL"); static int cmdlog = 0; static int debug = 0; -static int channel = 0; -static int mode = 0; +static int default_channel = 0; +static int network_mode = 0; static u32 ipw_debug_level; static int associate; static int auto_create = 1; -static int led = 0; +static int led_support = 0; static int disable = 0; static int bt_coexist = 0; static int hwcrypto = 0; @@ -103,6 +103,25 @@ static int antenna = CFG_SYS_ANTENNA_BOTH; static int rtap_iface = 0; /* def: 0 -- do not create rtap interface */ #endif +static struct ieee80211_rate ipw2200_rates[] = { + { .bitrate = 10 }, + { .bitrate = 20, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60 }, + { .bitrate = 90 }, + { .bitrate = 120 }, + { .bitrate = 180 }, + { .bitrate = 240 }, + { .bitrate = 360 }, + { .bitrate = 480 }, + { .bitrate = 540 } +}; + +#define ipw2200_a_rates (ipw2200_rates + 4) +#define ipw2200_num_a_rates 8 +#define ipw2200_bg_rates (ipw2200_rates + 0) +#define ipw2200_num_bg_rates 12 #ifdef CONFIG_IPW2200_QOS static int qos_enable = 0; @@ -111,7 +130,7 @@ static int qos_no_ack_mask = 0; static int burst_duration_CCK = 0; static int burst_duration_OFDM = 0; -static struct ieee80211_qos_parameters def_qos_parameters_OFDM = { +static struct libipw_qos_parameters def_qos_parameters_OFDM = { {QOS_TX0_CW_MIN_OFDM, QOS_TX1_CW_MIN_OFDM, QOS_TX2_CW_MIN_OFDM, QOS_TX3_CW_MIN_OFDM}, {QOS_TX0_CW_MAX_OFDM, QOS_TX1_CW_MAX_OFDM, QOS_TX2_CW_MAX_OFDM, @@ -122,7 +141,7 @@ static struct ieee80211_qos_parameters def_qos_parameters_OFDM = { QOS_TX2_TXOP_LIMIT_OFDM, QOS_TX3_TXOP_LIMIT_OFDM} }; -static struct ieee80211_qos_parameters def_qos_parameters_CCK = { +static struct libipw_qos_parameters def_qos_parameters_CCK = { {QOS_TX0_CW_MIN_CCK, QOS_TX1_CW_MIN_CCK, QOS_TX2_CW_MIN_CCK, QOS_TX3_CW_MIN_CCK}, {QOS_TX0_CW_MAX_CCK, QOS_TX1_CW_MAX_CCK, QOS_TX2_CW_MAX_CCK, @@ -133,7 +152,7 @@ static struct ieee80211_qos_parameters def_qos_parameters_CCK = { QOS_TX3_TXOP_LIMIT_CCK} }; -static struct ieee80211_qos_parameters def_parameters_OFDM = { +static struct libipw_qos_parameters def_parameters_OFDM = { {DEF_TX0_CW_MIN_OFDM, DEF_TX1_CW_MIN_OFDM, DEF_TX2_CW_MIN_OFDM, DEF_TX3_CW_MIN_OFDM}, {DEF_TX0_CW_MAX_OFDM, DEF_TX1_CW_MAX_OFDM, DEF_TX2_CW_MAX_OFDM, @@ -144,7 +163,7 @@ static struct ieee80211_qos_parameters def_parameters_OFDM = { DEF_TX2_TXOP_LIMIT_OFDM, DEF_TX3_TXOP_LIMIT_OFDM} }; -static struct ieee80211_qos_parameters def_parameters_CCK = { +static struct libipw_qos_parameters def_parameters_CCK = { {DEF_TX0_CW_MIN_CCK, DEF_TX1_CW_MIN_CCK, DEF_TX2_CW_MIN_CCK, DEF_TX3_CW_MIN_CCK}, {DEF_TX0_CW_MAX_CCK, DEF_TX1_CW_MAX_CCK, DEF_TX2_CW_MAX_CCK, @@ -164,9 +183,9 @@ static int from_priority_to_tx_queue[] = { static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv); -static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters +static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters *qos_param); -static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element +static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element *qos_param); #endif /* CONFIG_IPW2200_QOS */ @@ -1830,7 +1849,7 @@ static ssize_t store_speed_scan(struct device *d, struct device_attribute *attr, break; } - if (ieee80211_is_valid_channel(priv->ieee, channel)) + if (libipw_is_valid_channel(priv->ieee, channel)) priv->speed_scan[pos++] = channel; else IPW_WARNING("Skipping invalid channel request: %d\n", @@ -1882,7 +1901,7 @@ static ssize_t show_channels(struct device *d, char *buf) { struct ipw_priv *priv = dev_get_drvdata(d); - const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee); + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); int len = 0, i; len = sprintf(&buf[len], @@ -1892,14 +1911,14 @@ static ssize_t show_channels(struct device *d, for (i = 0; i < geo->bg_channels; i++) { len += sprintf(&buf[len], "%d: BSS%s%s, %s, Band %s.\n", geo->bg[i].channel, - geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT ? + geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT ? " (radar spectrum)" : "", - ((geo->bg[i].flags & IEEE80211_CH_NO_IBSS) || - (geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT)) + ((geo->bg[i].flags & LIBIPW_CH_NO_IBSS) || + (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT)) ? "" : ", IBSS", - geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY ? + geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY ? "passive only" : "active/passive", - geo->bg[i].flags & IEEE80211_CH_B_ONLY ? + geo->bg[i].flags & LIBIPW_CH_B_ONLY ? "B" : "B/G"); } @@ -1909,12 +1928,12 @@ static ssize_t show_channels(struct device *d, for (i = 0; i < geo->a_channels; i++) { len += sprintf(&buf[len], "%d: BSS%s%s, %s.\n", geo->a[i].channel, - geo->a[i].flags & IEEE80211_CH_RADAR_DETECT ? + geo->a[i].flags & LIBIPW_CH_RADAR_DETECT ? " (radar spectrum)" : "", - ((geo->a[i].flags & IEEE80211_CH_NO_IBSS) || - (geo->a[i].flags & IEEE80211_CH_RADAR_DETECT)) + ((geo->a[i].flags & LIBIPW_CH_NO_IBSS) || + (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT)) ? "" : ", IBSS", - geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY ? + geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY ? "passive only" : "active/passive"); } @@ -2429,7 +2448,7 @@ static int ipw_send_tx_power(struct ipw_priv *priv, struct ipw_tx_power *power) static int ipw_set_tx_power(struct ipw_priv *priv) { - const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee); + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); struct ipw_tx_power tx_power; s8 max_power; int i; @@ -2942,12 +2961,12 @@ static int ipw_fw_dma_wait(struct ipw_priv *priv) static void ipw_remove_current_network(struct ipw_priv *priv) { struct list_head *element, *safe; - struct ieee80211_network *network = NULL; + struct libipw_network *network = NULL; unsigned long flags; spin_lock_irqsave(&priv->ieee->lock, flags); list_for_each_safe(element, safe, &priv->ieee->network_list) { - network = list_entry(element, struct ieee80211_network, list); + network = list_entry(element, struct libipw_network, list); if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) { list_del(element); list_add_tail(&network->list, @@ -3765,7 +3784,7 @@ static void ipw_queue_tx_free_tfd(struct ipw_priv *priv, le16_to_cpu(bd->u.data.chunk_len[i]), PCI_DMA_TODEVICE); if (txq->txb[txq->q.last_used]) { - ieee80211_txb_free(txq->txb[txq->q.last_used]); + libipw_txb_free(txq->txb[txq->q.last_used]); txq->txb[txq->q.last_used] = NULL; } } @@ -4084,7 +4103,7 @@ static u32 ipw_get_max_rate(struct ipw_priv *priv) /* If currently associated in B mode, restrict the maximum * rate match to B rates */ if (priv->assoc_request.ieee_mode == IPW_B_MODE) - mask &= IEEE80211_CCK_RATES_MASK; + mask &= LIBIPW_CCK_RATES_MASK; /* TODO: Verify that the rate is supported by the current rates * list. */ @@ -4092,29 +4111,29 @@ static u32 ipw_get_max_rate(struct ipw_priv *priv) while (i && !(mask & i)) i >>= 1; switch (i) { - case IEEE80211_CCK_RATE_1MB_MASK: + case LIBIPW_CCK_RATE_1MB_MASK: return 1000000; - case IEEE80211_CCK_RATE_2MB_MASK: + case LIBIPW_CCK_RATE_2MB_MASK: return 2000000; - case IEEE80211_CCK_RATE_5MB_MASK: + case LIBIPW_CCK_RATE_5MB_MASK: return 5500000; - case IEEE80211_OFDM_RATE_6MB_MASK: + case LIBIPW_OFDM_RATE_6MB_MASK: return 6000000; - case IEEE80211_OFDM_RATE_9MB_MASK: + case LIBIPW_OFDM_RATE_9MB_MASK: return 9000000; - case IEEE80211_CCK_RATE_11MB_MASK: + case LIBIPW_CCK_RATE_11MB_MASK: return 11000000; - case IEEE80211_OFDM_RATE_12MB_MASK: + case LIBIPW_OFDM_RATE_12MB_MASK: return 12000000; - case IEEE80211_OFDM_RATE_18MB_MASK: + case LIBIPW_OFDM_RATE_18MB_MASK: return 18000000; - case IEEE80211_OFDM_RATE_24MB_MASK: + case LIBIPW_OFDM_RATE_24MB_MASK: return 24000000; - case IEEE80211_OFDM_RATE_36MB_MASK: + case LIBIPW_OFDM_RATE_36MB_MASK: return 36000000; - case IEEE80211_OFDM_RATE_48MB_MASK: + case LIBIPW_OFDM_RATE_48MB_MASK: return 48000000; - case IEEE80211_OFDM_RATE_54MB_MASK: + case LIBIPW_OFDM_RATE_54MB_MASK: return 54000000; } @@ -4279,9 +4298,10 @@ static void ipw_gather_stats(struct ipw_priv *priv) IPW_DEBUG_STATS("Signal level : %3d%% (%d dBm)\n", signal_quality, rssi); - quality = min(beacon_quality, - min(rate_quality, - min(tx_quality, min(rx_quality, signal_quality)))); + quality = min(rx_quality, signal_quality); + quality = min(tx_quality, quality); + quality = min(rate_quality, quality); + quality = min(beacon_quality, quality); if (quality == beacon_quality) IPW_DEBUG_STATS("Quality (%d%%): Clamped to missed beacons.\n", quality); @@ -4425,7 +4445,6 @@ static void ipw_rx_notification(struct ipw_priv *priv, { DECLARE_SSID_BUF(ssid); u16 size = le16_to_cpu(notif->size); - notif->size = le16_to_cpu(notif->size); IPW_DEBUG_NOTIF("type = %i (%d bytes)\n", notif->subtype, size); @@ -4480,11 +4499,11 @@ static void ipw_rx_notification(struct ipw_priv *priv, == IEEE80211_STYPE_ASSOC_RESP)) { if ((sizeof (struct - ieee80211_assoc_response) + libipw_assoc_response) <= size) && (size <= 2314)) { struct - ieee80211_rx_stats + libipw_rx_stats stats = { .len = size - 1, }; @@ -4492,10 +4511,10 @@ static void ipw_rx_notification(struct ipw_priv *priv, IPW_DEBUG_QOS ("QoS Associate " "size %d\n", size); - ieee80211_rx_mgt(priv-> + libipw_rx_mgt(priv-> ieee, (struct - ieee80211_hdr_4addr + libipw_hdr_4addr *) ¬if->u.raw, &stats); } @@ -4551,11 +4570,11 @@ static void ipw_rx_notification(struct ipw_priv *priv, case CMAS_INIT:{ if (priv->status & STATUS_AUTH) { struct - ieee80211_assoc_response + libipw_assoc_response *resp; resp = (struct - ieee80211_assoc_response + libipw_assoc_response *)¬if->u.raw; IPW_DEBUG(IPW_DL_NOTIF | IPW_DL_STATE | @@ -5241,33 +5260,33 @@ static struct ipw_rx_queue *ipw_rx_queue_alloc(struct ipw_priv *priv) static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate) { - rate &= ~IEEE80211_BASIC_RATE_MASK; + rate &= ~LIBIPW_BASIC_RATE_MASK; if (ieee_mode == IEEE_A) { switch (rate) { - case IEEE80211_OFDM_RATE_6MB: - return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ? + case LIBIPW_OFDM_RATE_6MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_6MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_9MB: - return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ? + case LIBIPW_OFDM_RATE_9MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_9MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_12MB: + case LIBIPW_OFDM_RATE_12MB: return priv-> - rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_18MB: + rates_mask & LIBIPW_OFDM_RATE_12MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_18MB: return priv-> - rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_24MB: + rates_mask & LIBIPW_OFDM_RATE_18MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_24MB: return priv-> - rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_36MB: + rates_mask & LIBIPW_OFDM_RATE_24MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_36MB: return priv-> - rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_48MB: + rates_mask & LIBIPW_OFDM_RATE_36MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_48MB: return priv-> - rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_54MB: + rates_mask & LIBIPW_OFDM_RATE_48MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_54MB: return priv-> - rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0; + rates_mask & LIBIPW_OFDM_RATE_54MB_MASK ? 1 : 0; default: return 0; } @@ -5275,14 +5294,14 @@ static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate) /* B and G mixed */ switch (rate) { - case IEEE80211_CCK_RATE_1MB: - return priv->rates_mask & IEEE80211_CCK_RATE_1MB_MASK ? 1 : 0; - case IEEE80211_CCK_RATE_2MB: - return priv->rates_mask & IEEE80211_CCK_RATE_2MB_MASK ? 1 : 0; - case IEEE80211_CCK_RATE_5MB: - return priv->rates_mask & IEEE80211_CCK_RATE_5MB_MASK ? 1 : 0; - case IEEE80211_CCK_RATE_11MB: - return priv->rates_mask & IEEE80211_CCK_RATE_11MB_MASK ? 1 : 0; + case LIBIPW_CCK_RATE_1MB: + return priv->rates_mask & LIBIPW_CCK_RATE_1MB_MASK ? 1 : 0; + case LIBIPW_CCK_RATE_2MB: + return priv->rates_mask & LIBIPW_CCK_RATE_2MB_MASK ? 1 : 0; + case LIBIPW_CCK_RATE_5MB: + return priv->rates_mask & LIBIPW_CCK_RATE_5MB_MASK ? 1 : 0; + case LIBIPW_CCK_RATE_11MB: + return priv->rates_mask & LIBIPW_CCK_RATE_11MB_MASK ? 1 : 0; } /* If we are limited to B modulations, bail at this point */ @@ -5291,29 +5310,29 @@ static int ipw_is_rate_in_mask(struct ipw_priv *priv, int ieee_mode, u8 rate) /* G */ switch (rate) { - case IEEE80211_OFDM_RATE_6MB: - return priv->rates_mask & IEEE80211_OFDM_RATE_6MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_9MB: - return priv->rates_mask & IEEE80211_OFDM_RATE_9MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_12MB: - return priv->rates_mask & IEEE80211_OFDM_RATE_12MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_18MB: - return priv->rates_mask & IEEE80211_OFDM_RATE_18MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_24MB: - return priv->rates_mask & IEEE80211_OFDM_RATE_24MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_36MB: - return priv->rates_mask & IEEE80211_OFDM_RATE_36MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_48MB: - return priv->rates_mask & IEEE80211_OFDM_RATE_48MB_MASK ? 1 : 0; - case IEEE80211_OFDM_RATE_54MB: - return priv->rates_mask & IEEE80211_OFDM_RATE_54MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_6MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_6MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_9MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_9MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_12MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_12MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_18MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_18MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_24MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_24MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_36MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_36MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_48MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_48MB_MASK ? 1 : 0; + case LIBIPW_OFDM_RATE_54MB: + return priv->rates_mask & LIBIPW_OFDM_RATE_54MB_MASK ? 1 : 0; } return 0; } static int ipw_compatible_rates(struct ipw_priv *priv, - const struct ieee80211_network *network, + const struct libipw_network *network, struct ipw_supported_rates *rates) { int num_rates, i; @@ -5325,7 +5344,7 @@ static int ipw_compatible_rates(struct ipw_priv *priv, if (!ipw_is_rate_in_mask(priv, network->mode, network->rates[i])) { - if (network->rates[i] & IEEE80211_BASIC_RATE_MASK) { + if (network->rates[i] & LIBIPW_BASIC_RATE_MASK) { IPW_DEBUG_SCAN("Adding masked mandatory " "rate %02X\n", network->rates[i]); @@ -5347,7 +5366,7 @@ static int ipw_compatible_rates(struct ipw_priv *priv, for (i = 0; i < num_rates; i++) { if (!ipw_is_rate_in_mask(priv, network->mode, network->rates_ex[i])) { - if (network->rates_ex[i] & IEEE80211_BASIC_RATE_MASK) { + if (network->rates_ex[i] & LIBIPW_BASIC_RATE_MASK) { IPW_DEBUG_SCAN("Adding masked mandatory " "rate %02X\n", network->rates_ex[i]); @@ -5383,73 +5402,73 @@ static void ipw_copy_rates(struct ipw_supported_rates *dest, static void ipw_add_cck_scan_rates(struct ipw_supported_rates *rates, u8 modulation, u32 rate_mask) { - u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ? - IEEE80211_BASIC_RATE_MASK : 0; + u8 basic_mask = (LIBIPW_OFDM_MODULATION == modulation) ? + LIBIPW_BASIC_RATE_MASK : 0; - if (rate_mask & IEEE80211_CCK_RATE_1MB_MASK) + if (rate_mask & LIBIPW_CCK_RATE_1MB_MASK) rates->supported_rates[rates->num_rates++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; + LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_1MB; - if (rate_mask & IEEE80211_CCK_RATE_2MB_MASK) + if (rate_mask & LIBIPW_CCK_RATE_2MB_MASK) rates->supported_rates[rates->num_rates++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; + LIBIPW_BASIC_RATE_MASK | LIBIPW_CCK_RATE_2MB; - if (rate_mask & IEEE80211_CCK_RATE_5MB_MASK) + if (rate_mask & LIBIPW_CCK_RATE_5MB_MASK) rates->supported_rates[rates->num_rates++] = basic_mask | - IEEE80211_CCK_RATE_5MB; + LIBIPW_CCK_RATE_5MB; - if (rate_mask & IEEE80211_CCK_RATE_11MB_MASK) + if (rate_mask & LIBIPW_CCK_RATE_11MB_MASK) rates->supported_rates[rates->num_rates++] = basic_mask | - IEEE80211_CCK_RATE_11MB; + LIBIPW_CCK_RATE_11MB; } static void ipw_add_ofdm_scan_rates(struct ipw_supported_rates *rates, u8 modulation, u32 rate_mask) { - u8 basic_mask = (IEEE80211_OFDM_MODULATION == modulation) ? - IEEE80211_BASIC_RATE_MASK : 0; + u8 basic_mask = (LIBIPW_OFDM_MODULATION == modulation) ? + LIBIPW_BASIC_RATE_MASK : 0; - if (rate_mask & IEEE80211_OFDM_RATE_6MB_MASK) + if (rate_mask & LIBIPW_OFDM_RATE_6MB_MASK) rates->supported_rates[rates->num_rates++] = basic_mask | - IEEE80211_OFDM_RATE_6MB; + LIBIPW_OFDM_RATE_6MB; - if (rate_mask & IEEE80211_OFDM_RATE_9MB_MASK) + if (rate_mask & LIBIPW_OFDM_RATE_9MB_MASK) rates->supported_rates[rates->num_rates++] = - IEEE80211_OFDM_RATE_9MB; + LIBIPW_OFDM_RATE_9MB; - if (rate_mask & IEEE80211_OFDM_RATE_12MB_MASK) + if (rate_mask & LIBIPW_OFDM_RATE_12MB_MASK) rates->supported_rates[rates->num_rates++] = basic_mask | - IEEE80211_OFDM_RATE_12MB; + LIBIPW_OFDM_RATE_12MB; - if (rate_mask & IEEE80211_OFDM_RATE_18MB_MASK) + if (rate_mask & LIBIPW_OFDM_RATE_18MB_MASK) rates->supported_rates[rates->num_rates++] = - IEEE80211_OFDM_RATE_18MB; + LIBIPW_OFDM_RATE_18MB; - if (rate_mask & IEEE80211_OFDM_RATE_24MB_MASK) + if (rate_mask & LIBIPW_OFDM_RATE_24MB_MASK) rates->supported_rates[rates->num_rates++] = basic_mask | - IEEE80211_OFDM_RATE_24MB; + LIBIPW_OFDM_RATE_24MB; - if (rate_mask & IEEE80211_OFDM_RATE_36MB_MASK) + if (rate_mask & LIBIPW_OFDM_RATE_36MB_MASK) rates->supported_rates[rates->num_rates++] = - IEEE80211_OFDM_RATE_36MB; + LIBIPW_OFDM_RATE_36MB; - if (rate_mask & IEEE80211_OFDM_RATE_48MB_MASK) + if (rate_mask & LIBIPW_OFDM_RATE_48MB_MASK) rates->supported_rates[rates->num_rates++] = - IEEE80211_OFDM_RATE_48MB; + LIBIPW_OFDM_RATE_48MB; - if (rate_mask & IEEE80211_OFDM_RATE_54MB_MASK) + if (rate_mask & LIBIPW_OFDM_RATE_54MB_MASK) rates->supported_rates[rates->num_rates++] = - IEEE80211_OFDM_RATE_54MB; + LIBIPW_OFDM_RATE_54MB; } struct ipw_network_match { - struct ieee80211_network *network; + struct libipw_network *network; struct ipw_supported_rates rates; }; static int ipw_find_adhoc_network(struct ipw_priv *priv, struct ipw_network_match *match, - struct ieee80211_network *network, + struct libipw_network *network, int roaming) { struct ipw_supported_rates rates; @@ -5570,7 +5589,7 @@ static int ipw_find_adhoc_network(struct ipw_priv *priv, } /* Filter out any incompatible freq / mode combinations */ - if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) { + if (!libipw_is_valid_mode(priv->ieee, network->mode)) { IPW_DEBUG_MERGE("Network '%s (%pM)' excluded " "because of invalid frequency/mode " "combination.\n", @@ -5620,7 +5639,7 @@ static void ipw_merge_adhoc_network(struct work_struct *work) DECLARE_SSID_BUF(ssid); struct ipw_priv *priv = container_of(work, struct ipw_priv, merge_networks); - struct ieee80211_network *network = NULL; + struct libipw_network *network = NULL; struct ipw_network_match match = { .network = priv->assoc_network }; @@ -5662,7 +5681,7 @@ static void ipw_merge_adhoc_network(struct work_struct *work) static int ipw_best_network(struct ipw_priv *priv, struct ipw_network_match *match, - struct ieee80211_network *network, int roaming) + struct libipw_network *network, int roaming) { struct ipw_supported_rates rates; DECLARE_SSID_BUF(ssid); @@ -5796,7 +5815,7 @@ static int ipw_best_network(struct ipw_priv *priv, } /* Filter out any incompatible freq / mode combinations */ - if (!ieee80211_is_valid_mode(priv->ieee, network->mode)) { + if (!libipw_is_valid_mode(priv->ieee, network->mode)) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because of invalid frequency/mode " "combination.\n", @@ -5807,7 +5826,7 @@ static int ipw_best_network(struct ipw_priv *priv, } /* Filter out invalid channel in current GEO */ - if (!ieee80211_is_valid_channel(priv->ieee, network->channel)) { + if (!libipw_is_valid_channel(priv->ieee, network->channel)) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because of invalid channel in current GEO\n", print_ssid(ssid, network->ssid, @@ -5853,9 +5872,9 @@ static int ipw_best_network(struct ipw_priv *priv, } static void ipw_adhoc_create(struct ipw_priv *priv, - struct ieee80211_network *network) + struct libipw_network *network) { - const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee); + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); int i; /* @@ -5870,25 +5889,25 @@ static void ipw_adhoc_create(struct ipw_priv *priv, * FW fatal error. * */ - switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) { - case IEEE80211_52GHZ_BAND: + switch (libipw_is_valid_channel(priv->ieee, priv->channel)) { + case LIBIPW_52GHZ_BAND: network->mode = IEEE_A; - i = ieee80211_channel_to_index(priv->ieee, priv->channel); + i = libipw_channel_to_index(priv->ieee, priv->channel); BUG_ON(i == -1); - if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) { + if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) { IPW_WARNING("Overriding invalid channel\n"); priv->channel = geo->a[0].channel; } break; - case IEEE80211_24GHZ_BAND: + case LIBIPW_24GHZ_BAND: if (priv->ieee->mode & IEEE_G) network->mode = IEEE_G; else network->mode = IEEE_B; - i = ieee80211_channel_to_index(priv->ieee, priv->channel); + i = libipw_channel_to_index(priv->ieee, priv->channel); BUG_ON(i == -1); - if (geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY) { + if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) { IPW_WARNING("Overriding invalid channel\n"); priv->channel = geo->bg[0].channel; } @@ -6115,70 +6134,71 @@ static void ipw_debug_config(struct ipw_priv *priv) static void ipw_set_fixed_rate(struct ipw_priv *priv, int mode) { /* TODO: Verify that this works... */ - struct ipw_fixed_rate fr = { - .tx_rates = priv->rates_mask - }; + struct ipw_fixed_rate fr; u32 reg; u16 mask = 0; + u16 new_tx_rates = priv->rates_mask; /* Identify 'current FW band' and match it with the fixed * Tx rates */ switch (priv->ieee->freq_band) { - case IEEE80211_52GHZ_BAND: /* A only */ + case LIBIPW_52GHZ_BAND: /* A only */ /* IEEE_A */ - if (priv->rates_mask & ~IEEE80211_OFDM_RATES_MASK) { + if (priv->rates_mask & ~LIBIPW_OFDM_RATES_MASK) { /* Invalid fixed rate mask */ IPW_DEBUG_WX ("invalid fixed rate mask in ipw_set_fixed_rate\n"); - fr.tx_rates = 0; + new_tx_rates = 0; break; } - fr.tx_rates >>= IEEE80211_OFDM_SHIFT_MASK_A; + new_tx_rates >>= LIBIPW_OFDM_SHIFT_MASK_A; break; default: /* 2.4Ghz or Mixed */ /* IEEE_B */ if (mode == IEEE_B) { - if (fr.tx_rates & ~IEEE80211_CCK_RATES_MASK) { + if (new_tx_rates & ~LIBIPW_CCK_RATES_MASK) { /* Invalid fixed rate mask */ IPW_DEBUG_WX ("invalid fixed rate mask in ipw_set_fixed_rate\n"); - fr.tx_rates = 0; + new_tx_rates = 0; } break; } /* IEEE_G */ - if (fr.tx_rates & ~(IEEE80211_CCK_RATES_MASK | - IEEE80211_OFDM_RATES_MASK)) { + if (new_tx_rates & ~(LIBIPW_CCK_RATES_MASK | + LIBIPW_OFDM_RATES_MASK)) { /* Invalid fixed rate mask */ IPW_DEBUG_WX ("invalid fixed rate mask in ipw_set_fixed_rate\n"); - fr.tx_rates = 0; + new_tx_rates = 0; break; } - if (IEEE80211_OFDM_RATE_6MB_MASK & fr.tx_rates) { - mask |= (IEEE80211_OFDM_RATE_6MB_MASK >> 1); - fr.tx_rates &= ~IEEE80211_OFDM_RATE_6MB_MASK; + if (LIBIPW_OFDM_RATE_6MB_MASK & new_tx_rates) { + mask |= (LIBIPW_OFDM_RATE_6MB_MASK >> 1); + new_tx_rates &= ~LIBIPW_OFDM_RATE_6MB_MASK; } - if (IEEE80211_OFDM_RATE_9MB_MASK & fr.tx_rates) { - mask |= (IEEE80211_OFDM_RATE_9MB_MASK >> 1); - fr.tx_rates &= ~IEEE80211_OFDM_RATE_9MB_MASK; + if (LIBIPW_OFDM_RATE_9MB_MASK & new_tx_rates) { + mask |= (LIBIPW_OFDM_RATE_9MB_MASK >> 1); + new_tx_rates &= ~LIBIPW_OFDM_RATE_9MB_MASK; } - if (IEEE80211_OFDM_RATE_12MB_MASK & fr.tx_rates) { - mask |= (IEEE80211_OFDM_RATE_12MB_MASK >> 1); - fr.tx_rates &= ~IEEE80211_OFDM_RATE_12MB_MASK; + if (LIBIPW_OFDM_RATE_12MB_MASK & new_tx_rates) { + mask |= (LIBIPW_OFDM_RATE_12MB_MASK >> 1); + new_tx_rates &= ~LIBIPW_OFDM_RATE_12MB_MASK; } - fr.tx_rates |= mask; + new_tx_rates |= mask; break; } + fr.tx_rates = cpu_to_le16(new_tx_rates); + reg = ipw_read32(priv, IPW_MEM_FIXED_OVERRIDE); ipw_write_reg32(priv, reg, *(u32 *) & fr); } @@ -6203,12 +6223,12 @@ static void ipw_add_scan_channels(struct ipw_priv *priv, int scan_type) { int channel_index = 0; - const struct ieee80211_geo *geo; + const struct libipw_geo *geo; int i; - geo = ieee80211_get_geo(priv->ieee); + geo = libipw_get_geo(priv->ieee); - if (priv->ieee->freq_band & IEEE80211_52GHZ_BAND) { + if (priv->ieee->freq_band & LIBIPW_52GHZ_BAND) { int start = channel_index; for (i = 0; i < geo->a_channels; i++) { if ((priv->status & STATUS_ASSOCIATED) && @@ -6218,7 +6238,7 @@ static void ipw_add_scan_channels(struct ipw_priv *priv, scan->channels_list[channel_index] = geo->a[i].channel; ipw_set_scan_type(scan, channel_index, geo->a[i]. - flags & IEEE80211_CH_PASSIVE_ONLY ? + flags & LIBIPW_CH_PASSIVE_ONLY ? IPW_SCAN_PASSIVE_FULL_DWELL_SCAN : scan_type); } @@ -6230,11 +6250,11 @@ static void ipw_add_scan_channels(struct ipw_priv *priv, } } - if (priv->ieee->freq_band & IEEE80211_24GHZ_BAND) { + if (priv->ieee->freq_band & LIBIPW_24GHZ_BAND) { int start = channel_index; if (priv->config & CFG_SPEED_SCAN) { int index; - u8 channels[IEEE80211_24GHZ_CHANNELS] = { + u8 channels[LIBIPW_24GHZ_CHANNELS] = { /* nop out the list */ [0] = 0 }; @@ -6266,11 +6286,11 @@ static void ipw_add_scan_channels(struct ipw_priv *priv, channel_index++; scan->channels_list[channel_index] = channel; index = - ieee80211_channel_to_index(priv->ieee, channel); + libipw_channel_to_index(priv->ieee, channel); ipw_set_scan_type(scan, channel_index, geo->bg[index]. flags & - IEEE80211_CH_PASSIVE_ONLY ? + LIBIPW_CH_PASSIVE_ONLY ? IPW_SCAN_PASSIVE_FULL_DWELL_SCAN : scan_type); } @@ -6285,7 +6305,7 @@ static void ipw_add_scan_channels(struct ipw_priv *priv, ipw_set_scan_type(scan, channel_index, geo->bg[i]. flags & - IEEE80211_CH_PASSIVE_ONLY ? + LIBIPW_CH_PASSIVE_ONLY ? IPW_SCAN_PASSIVE_FULL_DWELL_SCAN : scan_type); } @@ -6352,7 +6372,7 @@ static int ipw_request_scan_helper(struct ipw_priv *priv, int type, int direct) } memset(&scan, 0, sizeof(scan)); - scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee)); + scan.full_scan_index = cpu_to_le32(libipw_get_scans(priv->ieee)); if (type == IW_SCAN_TYPE_PASSIVE) { IPW_DEBUG_WX("use passive scanning\n"); @@ -6383,13 +6403,13 @@ static int ipw_request_scan_helper(struct ipw_priv *priv, int type, int direct) u8 channel; u8 band = 0; - switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) { - case IEEE80211_52GHZ_BAND: + switch (libipw_is_valid_channel(priv->ieee, priv->channel)) { + case LIBIPW_52GHZ_BAND: band = (u8) (IPW_A_MODE << 6) | 1; channel = priv->channel; break; - case IEEE80211_24GHZ_BAND: + case LIBIPW_24GHZ_BAND: band = (u8) (IPW_B_MODE << 6) | 1; channel = priv->channel; break; @@ -6510,8 +6530,8 @@ static int ipw_wpa_enable(struct ipw_priv *priv, int value) static int ipw_wpa_set_auth_algs(struct ipw_priv *priv, int value) { - struct ieee80211_device *ieee = priv->ieee; - struct ieee80211_security sec = { + struct libipw_device *ieee = priv->ieee; + struct libipw_security sec = { .flags = SEC_AUTH_MODE, }; int ret = 0; @@ -6561,8 +6581,8 @@ static int ipw_wx_set_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); - struct ieee80211_device *ieee = priv->ieee; + struct ipw_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; u8 *buf; int err = 0; @@ -6597,8 +6617,8 @@ static int ipw_wx_get_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); - struct ieee80211_device *ieee = priv->ieee; + struct ipw_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; int err = 0; if (ieee->wpa_ie_len == 0 || ieee->wpa_ie == NULL) { @@ -6640,8 +6660,8 @@ static int ipw_wx_set_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); - struct ieee80211_device *ieee = priv->ieee; + struct ipw_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; struct iw_param *param = &wrqu->param; struct lib80211_crypt_data *crypt; unsigned long flags; @@ -6692,7 +6712,7 @@ static int ipw_wx_set_auth(struct net_device *dev, * can use this to determine if the CAP_PRIVACY_ON bit should * be set. */ - struct ieee80211_security sec = { + struct libipw_security sec = { .flags = SEC_ENABLED, .enabled = param->value, }; @@ -6740,8 +6760,8 @@ static int ipw_wx_get_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); - struct ieee80211_device *ieee = priv->ieee; + struct ipw_priv *priv = libipw_priv(dev); + struct libipw_device *ieee = priv->ieee; struct lib80211_crypt_data *crypt; struct iw_param *param = &wrqu->param; int ret = 0; @@ -6799,7 +6819,7 @@ static int ipw_wx_set_encodeext(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; if (hwcrypto) { @@ -6821,7 +6841,7 @@ static int ipw_wx_set_encodeext(struct net_device *dev, } } - return ieee80211_wx_set_encodeext(priv->ieee, info, wrqu, extra); + return libipw_wx_set_encodeext(priv->ieee, info, wrqu, extra); } /* SIOCGIWENCODEEXT */ @@ -6829,8 +6849,8 @@ static int ipw_wx_get_encodeext(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); - return ieee80211_wx_get_encodeext(priv->ieee, info, wrqu, extra); + struct ipw_priv *priv = libipw_priv(dev); + return libipw_wx_get_encodeext(priv->ieee, info, wrqu, extra); } /* SIOCSIWMLME */ @@ -6838,7 +6858,7 @@ static int ipw_wx_set_mlme(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); struct iw_mlme *mlme = (struct iw_mlme *)extra; __le16 reason; @@ -6888,9 +6908,9 @@ static u8 ipw_qos_current_mode(struct ipw_priv * priv) */ static int ipw_qos_handle_probe_response(struct ipw_priv *priv, int active_network, - struct ieee80211_network *network) + struct libipw_network *network) { - u32 size = sizeof(struct ieee80211_qos_parameters); + u32 size = sizeof(struct libipw_qos_parameters); if (network->capability & WLAN_CAPABILITY_IBSS) network->qos_data.active = network->qos_data.supported; @@ -6948,12 +6968,12 @@ static int ipw_qos_handle_probe_response(struct ipw_priv *priv, * IPW_CMD_QOS_PARAMETERS and IPW_CMD_WME_INFO */ static int ipw_qos_activate(struct ipw_priv *priv, - struct ieee80211_qos_data *qos_network_data) + struct libipw_qos_data *qos_network_data) { int err; - struct ieee80211_qos_parameters qos_parameters[QOS_QOS_SETS]; - struct ieee80211_qos_parameters *active_one = NULL; - u32 size = sizeof(struct ieee80211_qos_parameters); + struct libipw_qos_parameters qos_parameters[QOS_QOS_SETS]; + struct libipw_qos_parameters *active_one = NULL; + u32 size = sizeof(struct libipw_qos_parameters); u32 burst_duration; int i; u8 type; @@ -7014,7 +7034,7 @@ static int ipw_qos_activate(struct ipw_priv *priv, IPW_DEBUG_QOS("QoS sending IPW_CMD_QOS_PARAMETERS\n"); err = ipw_send_qos_params_command(priv, - (struct ieee80211_qos_parameters *) + (struct libipw_qos_parameters *) &(qos_parameters[0])); if (err) IPW_DEBUG_QOS("QoS IPW_CMD_QOS_PARAMETERS failed\n"); @@ -7028,13 +7048,13 @@ static int ipw_qos_activate(struct ipw_priv *priv, static int ipw_qos_set_info_element(struct ipw_priv *priv) { int ret = 0; - struct ieee80211_qos_information_element qos_info; + struct libipw_qos_information_element qos_info; if (priv == NULL) return -1; qos_info.elementID = QOS_ELEMENT_ID; - qos_info.length = sizeof(struct ieee80211_qos_information_element) - 2; + qos_info.length = sizeof(struct libipw_qos_information_element) - 2; qos_info.version = QOS_VERSION_1; qos_info.ac_info = 0; @@ -7054,11 +7074,11 @@ static int ipw_qos_set_info_element(struct ipw_priv *priv) * Set the QoS parameter with the association request structure */ static int ipw_qos_association(struct ipw_priv *priv, - struct ieee80211_network *network) + struct libipw_network *network) { int err = 0; - struct ieee80211_qos_data *qos_data = NULL; - struct ieee80211_qos_data ibss_data = { + struct libipw_qos_data *qos_data = NULL; + struct libipw_qos_data ibss_data = { .supported = 1, .active = 1, }; @@ -7100,11 +7120,11 @@ static int ipw_qos_association(struct ipw_priv *priv, * setting */ static int ipw_qos_association_resp(struct ipw_priv *priv, - struct ieee80211_network *network) + struct libipw_network *network) { int ret = 0; unsigned long flags; - u32 size = sizeof(struct ieee80211_qos_parameters); + u32 size = sizeof(struct libipw_qos_parameters); int set_qos_param = 0; if ((priv == NULL) || (network == NULL) || @@ -7120,7 +7140,7 @@ static int ipw_qos_association_resp(struct ipw_priv *priv, spin_lock_irqsave(&priv->ieee->lock, flags); if (network->flags & NETWORK_HAS_QOS_PARAMETERS) { memcpy(&priv->assoc_network->qos_data, &network->qos_data, - sizeof(struct ieee80211_qos_data)); + sizeof(struct libipw_qos_data)); priv->assoc_network->qos_data.active = 1; if ((network->qos_data.old_param_count != network->qos_data.param_count)) { @@ -7156,7 +7176,7 @@ static u32 ipw_qos_get_burst_duration(struct ipw_priv *priv) if ((priv == NULL)) return 0; - if (!(priv->ieee->modulation & IEEE80211_OFDM_MODULATION)) + if (!(priv->ieee->modulation & LIBIPW_OFDM_MODULATION)) ret = priv->qos_data.burst_duration_CCK; else ret = priv->qos_data.burst_duration_OFDM; @@ -7208,8 +7228,8 @@ static int ipw_get_tx_queue_number(struct ipw_priv *priv, u16 priority) static int ipw_is_qos_active(struct net_device *dev, struct sk_buff *skb) { - struct ipw_priv *priv = ieee80211_priv(dev); - struct ieee80211_qos_data *qos_data = NULL; + struct ipw_priv *priv = libipw_priv(dev); + struct libipw_qos_data *qos_data = NULL; int active, supported; u8 *daddr = skb->data + ETH_ALEN; int unicast = !is_multicast_ether_addr(daddr); @@ -7264,9 +7284,6 @@ static void ipw_bg_qos_activate(struct work_struct *work) struct ipw_priv *priv = container_of(work, struct ipw_priv, qos_activate); - if (priv == NULL) - return; - mutex_lock(&priv->mutex); if (priv->status & STATUS_ASSOCIATED) @@ -7276,10 +7293,10 @@ static void ipw_bg_qos_activate(struct work_struct *work) } static int ipw_handle_probe_response(struct net_device *dev, - struct ieee80211_probe_response *resp, - struct ieee80211_network *network) + struct libipw_probe_response *resp, + struct libipw_network *network) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int active_network = ((priv->status & STATUS_ASSOCIATED) && (network == priv->assoc_network)); @@ -7289,10 +7306,10 @@ static int ipw_handle_probe_response(struct net_device *dev, } static int ipw_handle_beacon(struct net_device *dev, - struct ieee80211_beacon *resp, - struct ieee80211_network *network) + struct libipw_beacon *resp, + struct libipw_network *network) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int active_network = ((priv->status & STATUS_ASSOCIATED) && (network == priv->assoc_network)); @@ -7302,22 +7319,22 @@ static int ipw_handle_beacon(struct net_device *dev, } static int ipw_handle_assoc_response(struct net_device *dev, - struct ieee80211_assoc_response *resp, - struct ieee80211_network *network) + struct libipw_assoc_response *resp, + struct libipw_network *network) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); ipw_qos_association_resp(priv, network); return 0; } -static int ipw_send_qos_params_command(struct ipw_priv *priv, struct ieee80211_qos_parameters +static int ipw_send_qos_params_command(struct ipw_priv *priv, struct libipw_qos_parameters *qos_param) { return ipw_send_cmd_pdu(priv, IPW_CMD_QOS_PARAMETERS, sizeof(*qos_param) * 3, qos_param); } -static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos_information_element +static int ipw_send_qos_info_command(struct ipw_priv *priv, struct libipw_qos_information_element *qos_param) { return ipw_send_cmd_pdu(priv, IPW_CMD_WME_INFO, sizeof(*qos_param), @@ -7327,7 +7344,7 @@ static int ipw_send_qos_info_command(struct ipw_priv *priv, struct ieee80211_qos #endif /* CONFIG_IPW2200_QOS */ static int ipw_associate_network(struct ipw_priv *priv, - struct ieee80211_network *network, + struct libipw_network *network, struct ipw_supported_rates *rates, int roaming) { int err; @@ -7509,7 +7526,7 @@ static int ipw_associate_network(struct ipw_priv *priv, static void ipw_roam(void *data) { struct ipw_priv *priv = data; - struct ieee80211_network *network = NULL; + struct libipw_network *network = NULL; struct ipw_network_match match = { .network = priv->assoc_network }; @@ -7584,7 +7601,7 @@ static int ipw_associate(void *data) { struct ipw_priv *priv = data; - struct ieee80211_network *network = NULL; + struct libipw_network *network = NULL; struct ipw_network_match match = { .network = NULL }; @@ -7638,8 +7655,8 @@ static int ipw_associate(void *data) priv->config & CFG_STATIC_CHANNEL) { /* Use oldest network if the free list is empty */ if (list_empty(&priv->ieee->network_free_list)) { - struct ieee80211_network *oldest = NULL; - struct ieee80211_network *target; + struct libipw_network *oldest = NULL; + struct libipw_network *target; list_for_each_entry(target, &priv->ieee->network_list, list) { if ((oldest == NULL) || @@ -7660,7 +7677,7 @@ static int ipw_associate(void *data) } element = priv->ieee->network_free_list.next; - network = list_entry(element, struct ieee80211_network, list); + network = list_entry(element, struct libipw_network, list); ipw_adhoc_create(priv, network); rates = &priv->rates; list_del(element); @@ -7716,18 +7733,18 @@ static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv, switch (priv->ieee->sec.level) { case SEC_LEVEL_3: /* Remove CCMP HDR */ - memmove(skb->data + IEEE80211_3ADDR_LEN, - skb->data + IEEE80211_3ADDR_LEN + 8, - skb->len - IEEE80211_3ADDR_LEN - 8); + memmove(skb->data + LIBIPW_3ADDR_LEN, + skb->data + LIBIPW_3ADDR_LEN + 8, + skb->len - LIBIPW_3ADDR_LEN - 8); skb_trim(skb, skb->len - 16); /* CCMP_HDR_LEN + CCMP_MIC_LEN */ break; case SEC_LEVEL_2: break; case SEC_LEVEL_1: /* Remove IV */ - memmove(skb->data + IEEE80211_3ADDR_LEN, - skb->data + IEEE80211_3ADDR_LEN + 4, - skb->len - IEEE80211_3ADDR_LEN - 4); + memmove(skb->data + LIBIPW_3ADDR_LEN, + skb->data + LIBIPW_3ADDR_LEN + 4, + skb->len - LIBIPW_3ADDR_LEN - 4); skb_trim(skb, skb->len - 8); /* IV + ICV */ break; case SEC_LEVEL_0: @@ -7741,10 +7758,10 @@ static void ipw_rebuild_decrypted_skb(struct ipw_priv *priv, static void ipw_handle_data_packet(struct ipw_priv *priv, struct ipw_rx_mem_buffer *rxb, - struct ieee80211_rx_stats *stats) + struct libipw_rx_stats *stats) { struct net_device *dev = priv->net_dev; - struct ieee80211_hdr_4addr *hdr; + struct libipw_hdr_4addr *hdr; struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data; /* We received data from the HW, so stop the watchdog */ @@ -7774,15 +7791,15 @@ static void ipw_handle_data_packet(struct ipw_priv *priv, IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len); /* HW decrypt will not clear the WEP bit, MIC, PN, etc. */ - hdr = (struct ieee80211_hdr_4addr *)rxb->skb->data; + hdr = (struct libipw_hdr_4addr *)rxb->skb->data; if (priv->ieee->iw_mode != IW_MODE_MONITOR && (is_multicast_ether_addr(hdr->addr1) ? !priv->ieee->host_mc_decrypt : !priv->ieee->host_decrypt)) ipw_rebuild_decrypted_skb(priv, rxb->skb); - if (!ieee80211_rx(priv->ieee, rxb->skb, stats)) + if (!libipw_rx(priv->ieee, rxb->skb, stats)) dev->stats.rx_errors++; - else { /* ieee80211_rx succeeded, so it now owns the SKB */ + else { /* libipw_rx succeeded, so it now owns the SKB */ rxb->skb = NULL; __ipw_led_activity_on(priv); } @@ -7791,7 +7808,7 @@ static void ipw_handle_data_packet(struct ipw_priv *priv, #ifdef CONFIG_IPW2200_RADIOTAP static void ipw_handle_data_packet_monitor(struct ipw_priv *priv, struct ipw_rx_mem_buffer *rxb, - struct ieee80211_rx_stats *stats) + struct libipw_rx_stats *stats) { struct net_device *dev = priv->net_dev; struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data; @@ -7867,7 +7884,7 @@ static void ipw_handle_data_packet_monitor(struct ipw_priv *priv, /* Convert signal to DBM */ ipw_rt->rt_dbmsignal = antsignal; - ipw_rt->rt_dbmnoise = frame->noise; + ipw_rt->rt_dbmnoise = (s8) le16_to_cpu(frame->noise); /* Convert the channel data and set the flags */ ipw_rt->rt_channel = cpu_to_le16(ieee80211chan2mhz(received_channel)); @@ -7937,9 +7954,9 @@ static void ipw_handle_data_packet_monitor(struct ipw_priv *priv, IPW_DEBUG_RX("Rx packet of %d bytes.\n", rxb->skb->len); - if (!ieee80211_rx(priv->ieee, rxb->skb, stats)) + if (!libipw_rx(priv->ieee, rxb->skb, stats)) dev->stats.rx_errors++; - else { /* ieee80211_rx succeeded, so it now owns the SKB */ + else { /* libipw_rx succeeded, so it now owns the SKB */ rxb->skb = NULL; /* no LED during capture */ } @@ -7947,28 +7964,28 @@ static void ipw_handle_data_packet_monitor(struct ipw_priv *priv, #endif #ifdef CONFIG_IPW2200_PROMISCUOUS -#define ieee80211_is_probe_response(fc) \ +#define libipw_is_probe_response(fc) \ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && \ (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP ) -#define ieee80211_is_management(fc) \ +#define libipw_is_management(fc) \ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) -#define ieee80211_is_control(fc) \ +#define libipw_is_control(fc) \ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL) -#define ieee80211_is_data(fc) \ +#define libipw_is_data(fc) \ ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) -#define ieee80211_is_assoc_request(fc) \ +#define libipw_is_assoc_request(fc) \ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_ASSOC_REQ) -#define ieee80211_is_reassoc_request(fc) \ +#define libipw_is_reassoc_request(fc) \ ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_REASSOC_REQ) static void ipw_handle_promiscuous_rx(struct ipw_priv *priv, struct ipw_rx_mem_buffer *rxb, - struct ieee80211_rx_stats *stats) + struct libipw_rx_stats *stats) { struct net_device *dev = priv->prom_net_dev; struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)rxb->skb->data; @@ -7981,7 +7998,7 @@ static void ipw_handle_promiscuous_rx(struct ipw_priv *priv, u16 channel = frame->received_channel; u8 phy_flags = frame->antennaAndPhy; s8 signal = frame->rssi_dbm - IPW_RSSI_TO_DBM; - s8 noise = frame->noise; + s8 noise = (s8) le16_to_cpu(frame->noise); u8 rate = frame->rate; short len = le16_to_cpu(pkt->u.frame.length); struct sk_buff *skb; @@ -8018,17 +8035,17 @@ static void ipw_handle_promiscuous_rx(struct ipw_priv *priv, } hdr = (void *)rxb->skb->data + IPW_RX_FRAME_SIZE; - if (ieee80211_is_management(le16_to_cpu(hdr->frame_control))) { + if (libipw_is_management(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_MGMT) return; if (filter & IPW_PROM_MGMT_HEADER_ONLY) hdr_only = 1; - } else if (ieee80211_is_control(le16_to_cpu(hdr->frame_control))) { + } else if (libipw_is_control(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_CTL) return; if (filter & IPW_PROM_CTL_HEADER_ONLY) hdr_only = 1; - } else if (ieee80211_is_data(le16_to_cpu(hdr->frame_control))) { + } else if (libipw_is_data(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_DATA) return; if (filter & IPW_PROM_DATA_HEADER_ONLY) @@ -8046,7 +8063,7 @@ static void ipw_handle_promiscuous_rx(struct ipw_priv *priv, ipw_rt = (void *)skb->data; if (hdr_only) - len = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)); + len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_control)); memcpy(ipw_rt->payload, hdr, len); @@ -8143,7 +8160,7 @@ static void ipw_handle_promiscuous_rx(struct ipw_priv *priv, IPW_DEBUG_RX("Rx packet of %d bytes.\n", skb->len); - if (!ieee80211_rx(priv->prom_priv->ieee, skb, stats)) { + if (!libipw_rx(priv->prom_priv->ieee, skb, stats)) { dev->stats.rx_errors++; dev_kfree_skb_any(skb); } @@ -8151,7 +8168,7 @@ static void ipw_handle_promiscuous_rx(struct ipw_priv *priv, #endif static int is_network_packet(struct ipw_priv *priv, - struct ieee80211_hdr_4addr *header) + struct libipw_hdr_4addr *header) { /* Filter incoming packets to determine if they are targetted toward * this network, discarding packets coming from ourselves */ @@ -8189,7 +8206,7 @@ static int is_network_packet(struct ipw_priv *priv, #define IPW_PACKET_RETRY_TIME HZ static int is_duplicate_packet(struct ipw_priv *priv, - struct ieee80211_hdr_4addr *header) + struct libipw_hdr_4addr *header) { u16 sc = le16_to_cpu(header->seq_ctl); u16 seq = WLAN_GET_SEQ_SEQ(sc); @@ -8263,14 +8280,14 @@ static int is_duplicate_packet(struct ipw_priv *priv, static void ipw_handle_mgmt_packet(struct ipw_priv *priv, struct ipw_rx_mem_buffer *rxb, - struct ieee80211_rx_stats *stats) + struct libipw_rx_stats *stats) { struct sk_buff *skb = rxb->skb; struct ipw_rx_packet *pkt = (struct ipw_rx_packet *)skb->data; - struct ieee80211_hdr_4addr *header = (struct ieee80211_hdr_4addr *) + struct libipw_hdr_4addr *header = (struct libipw_hdr_4addr *) (skb->data + IPW_RX_FRAME_SIZE); - ieee80211_rx_mgt(priv->ieee, header, stats); + libipw_rx_mgt(priv->ieee, header, stats); if (priv->ieee->iw_mode == IW_MODE_ADHOC && ((WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) == @@ -8292,12 +8309,12 @@ static void ipw_handle_mgmt_packet(struct ipw_priv *priv, /* Advance past the ipw packet header to the 802.11 frame */ skb_pull(skb, IPW_RX_FRAME_SIZE); - /* Push the ieee80211_rx_stats before the 802.11 frame */ + /* Push the libipw_rx_stats before the 802.11 frame */ memcpy(skb_push(skb, sizeof(*stats)), stats, sizeof(*stats)); skb->dev = priv->ieee->dev; - /* Point raw at the ieee80211_stats */ + /* Point raw at the libipw_stats */ skb_reset_mac_header(skb); skb->pkt_type = PACKET_OTHERHOST; @@ -8317,7 +8334,7 @@ static void ipw_rx(struct ipw_priv *priv) { struct ipw_rx_mem_buffer *rxb; struct ipw_rx_packet *pkt; - struct ieee80211_hdr_4addr *header; + struct libipw_hdr_4addr *header; u32 r, w, i; u8 network_packet; u8 fill_rx = 0; @@ -8348,11 +8365,11 @@ static void ipw_rx(struct ipw_priv *priv) switch (pkt->header.message_type) { case RX_FRAME_TYPE: /* 802.11 frame */ { - struct ieee80211_rx_stats stats = { + struct libipw_rx_stats stats = { .rssi = pkt->u.frame.rssi_dbm - IPW_RSSI_TO_DBM, .signal = - le16_to_cpu(pkt->u.frame.rssi_dbm) - + pkt->u.frame.rssi_dbm - IPW_RSSI_TO_DBM + 0x100, .noise = le16_to_cpu(pkt->u.frame.noise), @@ -8363,19 +8380,19 @@ static void ipw_rx(struct ipw_priv *priv) .freq = (pkt->u.frame. control & (1 << 0)) ? - IEEE80211_24GHZ_BAND : - IEEE80211_52GHZ_BAND, + LIBIPW_24GHZ_BAND : + LIBIPW_52GHZ_BAND, .len = le16_to_cpu(pkt->u.frame.length), }; if (stats.rssi != 0) - stats.mask |= IEEE80211_STATMASK_RSSI; + stats.mask |= LIBIPW_STATMASK_RSSI; if (stats.signal != 0) - stats.mask |= IEEE80211_STATMASK_SIGNAL; + stats.mask |= LIBIPW_STATMASK_SIGNAL; if (stats.noise != 0) - stats.mask |= IEEE80211_STATMASK_NOISE; + stats.mask |= LIBIPW_STATMASK_NOISE; if (stats.rate != 0) - stats.mask |= IEEE80211_STATMASK_RATE; + stats.mask |= LIBIPW_STATMASK_RATE; priv->rx_packets++; @@ -8400,7 +8417,7 @@ static void ipw_rx(struct ipw_priv *priv) #endif header = - (struct ieee80211_hdr_4addr *)(rxb->skb-> + (struct libipw_hdr_4addr *)(rxb->skb-> data + IPW_RX_FRAME_SIZE); /* TODO: Check Ad-Hoc dest/source and make sure @@ -8423,7 +8440,7 @@ static void ipw_rx(struct ipw_priv *priv) le16_to_cpu(pkt->u.frame.length)); if (le16_to_cpu(pkt->u.frame.length) < - ieee80211_get_hdrlen(le16_to_cpu( + libipw_get_hdrlen(le16_to_cpu( header->frame_ctl))) { IPW_DEBUG_DROP ("Received packet is too small. " @@ -8534,7 +8551,7 @@ static int ipw_sw_reset(struct ipw_priv *priv, int option) /* We default to disabling the LED code as right now it causes * too many systems to lock up... */ - if (!led) + if (!led_support) priv->config |= CFG_NO_LED; if (associate) @@ -8556,10 +8573,10 @@ static int ipw_sw_reset(struct ipw_priv *priv, int option) IPW_DEBUG_INFO("Radio disabled.\n"); } - if (channel != 0) { + if (default_channel != 0) { priv->config |= CFG_STATIC_CHANNEL; - priv->channel = channel; - IPW_DEBUG_INFO("Bind to static channel %d\n", channel); + priv->channel = default_channel; + IPW_DEBUG_INFO("Bind to static channel %d\n", default_channel); /* TODO: Validate that provided channel is in range */ } #ifdef CONFIG_IPW2200_QOS @@ -8567,7 +8584,7 @@ static int ipw_sw_reset(struct ipw_priv *priv, int option) burst_duration_CCK, burst_duration_OFDM); #endif /* CONFIG_IPW2200_QOS */ - switch (mode) { + switch (network_mode) { case 1: priv->ieee->iw_mode = IW_MODE_ADHOC; priv->net_dev->type = ARPHRD_ETHER; @@ -8608,9 +8625,9 @@ static int ipw_sw_reset(struct ipw_priv *priv, int option) ": Detected Intel PRO/Wireless 2915ABG Network " "Connection\n"); priv->ieee->abg_true = 1; - band = IEEE80211_52GHZ_BAND | IEEE80211_24GHZ_BAND; - modulation = IEEE80211_OFDM_MODULATION | - IEEE80211_CCK_MODULATION; + band = LIBIPW_52GHZ_BAND | LIBIPW_24GHZ_BAND; + modulation = LIBIPW_OFDM_MODULATION | + LIBIPW_CCK_MODULATION; priv->adapter = IPW_2915ABG; priv->ieee->mode = IEEE_A | IEEE_G | IEEE_B; } else { @@ -8620,9 +8637,9 @@ static int ipw_sw_reset(struct ipw_priv *priv, int option) "Connection\n"); priv->ieee->abg_true = 0; - band = IEEE80211_24GHZ_BAND; - modulation = IEEE80211_OFDM_MODULATION | - IEEE80211_CCK_MODULATION; + band = LIBIPW_24GHZ_BAND; + modulation = LIBIPW_OFDM_MODULATION | + LIBIPW_CCK_MODULATION; priv->adapter = IPW_2200BG; priv->ieee->mode = IEEE_G | IEEE_B; } @@ -8630,7 +8647,7 @@ static int ipw_sw_reset(struct ipw_priv *priv, int option) priv->ieee->freq_band = band; priv->ieee->modulation = modulation; - priv->rates_mask = IEEE80211_DEFAULT_RATES_MASK; + priv->rates_mask = LIBIPW_DEFAULT_RATES_MASK; priv->disassociate_threshold = IPW_MB_DISASSOCIATE_THRESHOLD_DEFAULT; priv->roaming_threshold = IPW_MB_ROAMING_THRESHOLD_DEFAULT; @@ -8656,24 +8673,6 @@ static int ipw_sw_reset(struct ipw_priv *priv, int option) * */ -static int ipw_wx_get_name(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct ipw_priv *priv = ieee80211_priv(dev); - mutex_lock(&priv->mutex); - if (priv->status & STATUS_RF_KILL_MASK) - strcpy(wrqu->name, "radio off"); - else if (!(priv->status & STATUS_ASSOCIATED)) - strcpy(wrqu->name, "unassociated"); - else - snprintf(wrqu->name, IFNAMSIZ, "IEEE 802.11%c", - ipw_modes[priv->assoc_request.ieee_mode]); - IPW_DEBUG_WX("Name: %s\n", wrqu->name); - mutex_unlock(&priv->mutex); - return 0; -} - static int ipw_set_channel(struct ipw_priv *priv, u8 channel) { if (channel == 0) { @@ -8730,8 +8729,8 @@ static int ipw_wx_set_freq(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); - const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee); + struct ipw_priv *priv = libipw_priv(dev); + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); struct iw_freq *fwrq = &wrqu->freq; int ret = 0, i; u8 channel, flags; @@ -8746,23 +8745,23 @@ static int ipw_wx_set_freq(struct net_device *dev, } /* if setting by freq convert to channel */ if (fwrq->e == 1) { - channel = ieee80211_freq_to_channel(priv->ieee, fwrq->m); + channel = libipw_freq_to_channel(priv->ieee, fwrq->m); if (channel == 0) return -EINVAL; } else channel = fwrq->m; - if (!(band = ieee80211_is_valid_channel(priv->ieee, channel))) + if (!(band = libipw_is_valid_channel(priv->ieee, channel))) return -EINVAL; if (priv->ieee->iw_mode == IW_MODE_ADHOC) { - i = ieee80211_channel_to_index(priv->ieee, channel); + i = libipw_channel_to_index(priv->ieee, channel); if (i == -1) return -EINVAL; - flags = (band == IEEE80211_24GHZ_BAND) ? + flags = (band == LIBIPW_24GHZ_BAND) ? geo->bg[i].flags : geo->a[i].flags; - if (flags & IEEE80211_CH_PASSIVE_ONLY) { + if (flags & LIBIPW_CH_PASSIVE_ONLY) { IPW_DEBUG_WX("Invalid Ad-Hoc channel for 802.11a\n"); return -EINVAL; } @@ -8779,7 +8778,7 @@ static int ipw_wx_get_freq(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); wrqu->freq.e = 0; @@ -8790,16 +8789,16 @@ static int ipw_wx_get_freq(struct net_device *dev, priv->status & (STATUS_ASSOCIATING | STATUS_ASSOCIATED)) { int i; - i = ieee80211_channel_to_index(priv->ieee, priv->channel); + i = libipw_channel_to_index(priv->ieee, priv->channel); BUG_ON(i == -1); wrqu->freq.e = 1; - switch (ieee80211_is_valid_channel(priv->ieee, priv->channel)) { - case IEEE80211_52GHZ_BAND: + switch (libipw_is_valid_channel(priv->ieee, priv->channel)) { + case LIBIPW_52GHZ_BAND: wrqu->freq.m = priv->ieee->geo.a[i].freq * 100000; break; - case IEEE80211_24GHZ_BAND: + case LIBIPW_24GHZ_BAND: wrqu->freq.m = priv->ieee->geo.bg[i].freq * 100000; break; @@ -8818,7 +8817,7 @@ static int ipw_wx_set_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int err = 0; IPW_DEBUG_WX("Set MODE: %d\n", wrqu->mode); @@ -8870,7 +8869,7 @@ static int ipw_wx_get_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->mode = priv->ieee->iw_mode; IPW_DEBUG_WX("Get MODE -> %d\n", wrqu->mode); @@ -8899,9 +8898,9 @@ static int ipw_wx_get_range(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); struct iw_range *range = (struct iw_range *)extra; - const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee); + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); int i = 0, j; wrqu->data.length = sizeof(*range); @@ -8945,7 +8944,7 @@ static int ipw_wx_get_range(struct net_device *dev, if (priv->ieee->mode & (IEEE_B | IEEE_G)) { for (j = 0; j < geo->bg_channels && i < IW_MAX_FREQUENCIES; j++) { if ((priv->ieee->iw_mode == IW_MODE_ADHOC) && - (geo->bg[j].flags & IEEE80211_CH_PASSIVE_ONLY)) + (geo->bg[j].flags & LIBIPW_CH_PASSIVE_ONLY)) continue; range->freq[i].i = geo->bg[j].channel; @@ -8958,7 +8957,7 @@ static int ipw_wx_get_range(struct net_device *dev, if (priv->ieee->mode & IEEE_A) { for (j = 0; j < geo->a_channels && i < IW_MAX_FREQUENCIES; j++) { if ((priv->ieee->iw_mode == IW_MODE_ADHOC) && - (geo->a[j].flags & IEEE80211_CH_PASSIVE_ONLY)) + (geo->a[j].flags & LIBIPW_CH_PASSIVE_ONLY)) continue; range->freq[i].i = geo->a[j].channel; @@ -8993,7 +8992,7 @@ static int ipw_wx_set_wap(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); static const unsigned char any[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff @@ -9042,7 +9041,7 @@ static int ipw_wx_get_wap(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); /* If we are associated, trying to associate, or have a statically * configured BSSID then return that; otherwise return ANY */ @@ -9064,7 +9063,7 @@ static int ipw_wx_set_essid(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int length; DECLARE_SSID_BUF(ssid); @@ -9110,7 +9109,7 @@ static int ipw_wx_get_essid(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); DECLARE_SSID_BUF(ssid); /* If we are associated, trying to associate, or have a statically @@ -9136,7 +9135,7 @@ static int ipw_wx_set_nick(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); IPW_DEBUG_WX("Setting nick to '%s'\n", extra); if (wrqu->data.length > IW_ESSID_MAX_SIZE) @@ -9155,7 +9154,7 @@ static int ipw_wx_get_nick(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); IPW_DEBUG_WX("Getting nick\n"); mutex_lock(&priv->mutex); wrqu->data.length = strlen(priv->nick); @@ -9169,7 +9168,7 @@ static int ipw_wx_set_sens(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int err = 0; IPW_DEBUG_WX("Setting roaming threshold to %d\n", wrqu->sens.value); @@ -9199,7 +9198,7 @@ static int ipw_wx_get_sens(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->sens.fixed = 1; wrqu->sens.value = priv->roaming_threshold; @@ -9216,7 +9215,7 @@ static int ipw_wx_set_rate(struct net_device *dev, union iwreq_data *wrqu, char *extra) { /* TODO: We should use semaphores or locks for access to priv */ - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); u32 target_rate = wrqu->bitrate.value; u32 fixed, mask; @@ -9226,7 +9225,7 @@ static int ipw_wx_set_rate(struct net_device *dev, if (target_rate == -1) { fixed = 0; - mask = IEEE80211_DEFAULT_RATES_MASK; + mask = LIBIPW_DEFAULT_RATES_MASK; /* Now we should reassociate */ goto apply; } @@ -9235,62 +9234,62 @@ static int ipw_wx_set_rate(struct net_device *dev, fixed = wrqu->bitrate.fixed; if (target_rate == 1000000 || !fixed) - mask |= IEEE80211_CCK_RATE_1MB_MASK; + mask |= LIBIPW_CCK_RATE_1MB_MASK; if (target_rate == 1000000) goto apply; if (target_rate == 2000000 || !fixed) - mask |= IEEE80211_CCK_RATE_2MB_MASK; + mask |= LIBIPW_CCK_RATE_2MB_MASK; if (target_rate == 2000000) goto apply; if (target_rate == 5500000 || !fixed) - mask |= IEEE80211_CCK_RATE_5MB_MASK; + mask |= LIBIPW_CCK_RATE_5MB_MASK; if (target_rate == 5500000) goto apply; if (target_rate == 6000000 || !fixed) - mask |= IEEE80211_OFDM_RATE_6MB_MASK; + mask |= LIBIPW_OFDM_RATE_6MB_MASK; if (target_rate == 6000000) goto apply; if (target_rate == 9000000 || !fixed) - mask |= IEEE80211_OFDM_RATE_9MB_MASK; + mask |= LIBIPW_OFDM_RATE_9MB_MASK; if (target_rate == 9000000) goto apply; if (target_rate == 11000000 || !fixed) - mask |= IEEE80211_CCK_RATE_11MB_MASK; + mask |= LIBIPW_CCK_RATE_11MB_MASK; if (target_rate == 11000000) goto apply; if (target_rate == 12000000 || !fixed) - mask |= IEEE80211_OFDM_RATE_12MB_MASK; + mask |= LIBIPW_OFDM_RATE_12MB_MASK; if (target_rate == 12000000) goto apply; if (target_rate == 18000000 || !fixed) - mask |= IEEE80211_OFDM_RATE_18MB_MASK; + mask |= LIBIPW_OFDM_RATE_18MB_MASK; if (target_rate == 18000000) goto apply; if (target_rate == 24000000 || !fixed) - mask |= IEEE80211_OFDM_RATE_24MB_MASK; + mask |= LIBIPW_OFDM_RATE_24MB_MASK; if (target_rate == 24000000) goto apply; if (target_rate == 36000000 || !fixed) - mask |= IEEE80211_OFDM_RATE_36MB_MASK; + mask |= LIBIPW_OFDM_RATE_36MB_MASK; if (target_rate == 36000000) goto apply; if (target_rate == 48000000 || !fixed) - mask |= IEEE80211_OFDM_RATE_48MB_MASK; + mask |= LIBIPW_OFDM_RATE_48MB_MASK; if (target_rate == 48000000) goto apply; if (target_rate == 54000000 || !fixed) - mask |= IEEE80211_OFDM_RATE_54MB_MASK; + mask |= LIBIPW_OFDM_RATE_54MB_MASK; if (target_rate == 54000000) goto apply; @@ -9301,7 +9300,7 @@ static int ipw_wx_set_rate(struct net_device *dev, IPW_DEBUG_WX("Setting rate mask to 0x%08X [%s]\n", mask, fixed ? "fixed" : "sub-rates"); mutex_lock(&priv->mutex); - if (mask == IEEE80211_DEFAULT_RATES_MASK) { + if (mask == LIBIPW_DEFAULT_RATES_MASK) { priv->config &= ~CFG_FIXED_RATE; ipw_set_fixed_rate(priv, priv->ieee->mode); } else @@ -9328,7 +9327,7 @@ static int ipw_wx_get_rate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->bitrate.value = priv->last_rate; wrqu->bitrate.fixed = (priv->config & CFG_FIXED_RATE) ? 1 : 0; @@ -9341,7 +9340,7 @@ static int ipw_wx_set_rts(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); if (wrqu->rts.disabled || !wrqu->rts.fixed) priv->rts_threshold = DEFAULT_RTS_THRESHOLD; @@ -9364,7 +9363,7 @@ static int ipw_wx_get_rts(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->rts.value = priv->rts_threshold; wrqu->rts.fixed = 0; /* no auto select */ @@ -9378,7 +9377,7 @@ static int ipw_wx_set_txpow(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int err = 0; mutex_lock(&priv->mutex); @@ -9412,7 +9411,7 @@ static int ipw_wx_get_txpow(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->power.value = priv->tx_power; wrqu->power.fixed = 1; @@ -9430,7 +9429,7 @@ static int ipw_wx_set_frag(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); if (wrqu->frag.disabled || !wrqu->frag.fixed) priv->ieee->fts = DEFAULT_FTS; @@ -9454,7 +9453,7 @@ static int ipw_wx_get_frag(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->frag.value = priv->ieee->fts; wrqu->frag.fixed = 0; /* no auto select */ @@ -9469,7 +9468,7 @@ static int ipw_wx_set_retry(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); if (wrqu->retry.flags & IW_RETRY_LIFETIME || wrqu->retry.disabled) return -EINVAL; @@ -9502,7 +9501,7 @@ static int ipw_wx_get_retry(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); wrqu->retry.disabled = 0; @@ -9533,7 +9532,7 @@ static int ipw_wx_set_scan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); struct iw_scan_req *req = (struct iw_scan_req *)extra; struct delayed_work *work = NULL; @@ -9569,20 +9568,20 @@ static int ipw_wx_get_scan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); - return ieee80211_wx_get_scan(priv->ieee, info, wrqu, extra); + struct ipw_priv *priv = libipw_priv(dev); + return libipw_wx_get_scan(priv->ieee, info, wrqu, extra); } static int ipw_wx_set_encode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *key) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int ret; u32 cap = priv->capability; mutex_lock(&priv->mutex); - ret = ieee80211_wx_set_encode(priv->ieee, info, wrqu, key); + ret = libipw_wx_set_encode(priv->ieee, info, wrqu, key); /* In IBSS mode, we need to notify the firmware to update * the beacon info after we changed the capability. */ @@ -9599,15 +9598,15 @@ static int ipw_wx_get_encode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *key) { - struct ipw_priv *priv = ieee80211_priv(dev); - return ieee80211_wx_get_encode(priv->ieee, info, wrqu, key); + struct ipw_priv *priv = libipw_priv(dev); + return libipw_wx_get_encode(priv->ieee, info, wrqu, key); } static int ipw_wx_set_power(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int err; mutex_lock(&priv->mutex); if (wrqu->power.disabled) { @@ -9658,7 +9657,7 @@ static int ipw_wx_get_power(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); if (!(priv->power_mode & IPW_POWER_ENABLED)) wrqu->power.disabled = 1; @@ -9675,7 +9674,7 @@ static int ipw_wx_set_powermode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int mode = *(int *)extra; int err; @@ -9701,7 +9700,7 @@ static int ipw_wx_get_powermode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int level = IPW_POWER_LEVEL(priv->power_mode); char *p = extra; @@ -9733,7 +9732,7 @@ static int ipw_wx_set_wireless_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int mode = *(int *)extra; u8 band = 0, modulation = 0; @@ -9745,8 +9744,8 @@ static int ipw_wx_set_wireless_mode(struct net_device *dev, if (priv->adapter == IPW_2915ABG) { priv->ieee->abg_true = 1; if (mode & IEEE_A) { - band |= IEEE80211_52GHZ_BAND; - modulation |= IEEE80211_OFDM_MODULATION; + band |= LIBIPW_52GHZ_BAND; + modulation |= LIBIPW_OFDM_MODULATION; } else priv->ieee->abg_true = 0; } else { @@ -9761,14 +9760,14 @@ static int ipw_wx_set_wireless_mode(struct net_device *dev, } if (mode & IEEE_B) { - band |= IEEE80211_24GHZ_BAND; - modulation |= IEEE80211_CCK_MODULATION; + band |= LIBIPW_24GHZ_BAND; + modulation |= LIBIPW_CCK_MODULATION; } else priv->ieee->abg_true = 0; if (mode & IEEE_G) { - band |= IEEE80211_24GHZ_BAND; - modulation |= IEEE80211_OFDM_MODULATION; + band |= LIBIPW_24GHZ_BAND; + modulation |= LIBIPW_OFDM_MODULATION; } else priv->ieee->abg_true = 0; @@ -9798,7 +9797,7 @@ static int ipw_wx_get_wireless_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); switch (priv->ieee->mode) { case IEEE_A: @@ -9839,7 +9838,7 @@ static int ipw_wx_set_preamble(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int mode = *(int *)extra; mutex_lock(&priv->mutex); /* Switching from SHORT -> LONG requires a disassociation */ @@ -9872,7 +9871,7 @@ static int ipw_wx_get_preamble(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); mutex_lock(&priv->mutex); if (priv->config & CFG_PREAMBLE_LONG) snprintf(wrqu->name, IFNAMSIZ, "long (1)"); @@ -9887,7 +9886,7 @@ static int ipw_wx_set_monitor(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int *parms = (int *)extra; int enable = (parms[0] > 0); mutex_lock(&priv->mutex); @@ -9921,7 +9920,7 @@ static int ipw_wx_reset(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); IPW_DEBUG_WX("RESET\n"); queue_work(priv->workqueue, &priv->adapter_restart); return 0; @@ -9931,7 +9930,7 @@ static int ipw_wx_sw_reset(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); union iwreq_data wrqu_sec = { .encoding = { .flags = IW_ENCODE_DISABLED, @@ -9954,7 +9953,7 @@ static int ipw_wx_sw_reset(struct net_device *dev, ipw_radio_kill_sw(priv, priv->status & STATUS_RF_KILL_SW); mutex_unlock(&priv->mutex); - ieee80211_wx_set_encode(priv->ieee, info, &wrqu_sec, NULL); + libipw_wx_set_encode(priv->ieee, info, &wrqu_sec, NULL); mutex_lock(&priv->mutex); if (!(priv->status & STATUS_RF_KILL_MASK)) { @@ -9973,7 +9972,7 @@ static int ipw_wx_sw_reset(struct net_device *dev, /* Rebase the WE IOCTLs to zero for the handler array */ #define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT] static iw_handler ipw_wx_handlers[] = { - IW_IOCTL(SIOCGIWNAME) = ipw_wx_get_name, + IW_IOCTL(SIOCGIWNAME) = (iw_handler) cfg80211_wext_giwname, IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq, IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq, IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode, @@ -10099,7 +10098,7 @@ static struct iw_handler_def ipw_wx_handler_def = { */ static struct iw_statistics *ipw_get_wireless_stats(struct net_device *dev) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); struct iw_statistics *wstats; wstats = &priv->wstats; @@ -10180,13 +10179,13 @@ static int ipw_net_stop(struct net_device *dev) todo: modify to send one tfd per fragment instead of using chunking. otherwise -we need to heavily modify the ieee80211_skb_to_txb. +we need to heavily modify the libipw_skb_to_txb. */ -static int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb, +static int ipw_tx_skb(struct ipw_priv *priv, struct libipw_txb *txb, int pri) { - struct ieee80211_hdr_3addrqos *hdr = (struct ieee80211_hdr_3addrqos *) + struct libipw_hdr_3addrqos *hdr = (struct libipw_hdr_3addrqos *) txb->fragments[0]->data; int i = 0; struct tfd_frame *tfd; @@ -10198,13 +10197,12 @@ static int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb, #endif struct clx2_queue *q = &txq->q; u8 id, hdr_len, unicast; - u16 remaining_bytes; int fc; if (!(priv->status & STATUS_ASSOCIATED)) goto drop; - hdr_len = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); + hdr_len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); switch (priv->ieee->iw_mode) { case IW_MODE_ADHOC: unicast = !is_multicast_ether_addr(hdr->addr1); @@ -10237,7 +10235,6 @@ static int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb, tfd->u.data.cmd_id = DINO_CMD_TX; tfd->u.data.len = cpu_to_le16(txb->payload_size); - remaining_bytes = txb->payload_size; if (priv->assoc_request.ieee_mode == IPW_B_MODE) tfd->u.data.tx_flags_ext |= DCT_FLAG_EXT_MODE_CCK; @@ -10374,13 +10371,13 @@ static int ipw_tx_skb(struct ipw_priv *priv, struct ieee80211_txb *txb, drop: IPW_DEBUG_DROP("Silently dropping Tx packet.\n"); - ieee80211_txb_free(txb); + libipw_txb_free(txb); return NETDEV_TX_OK; } static int ipw_net_is_queue_full(struct net_device *dev, int pri) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); #ifdef CONFIG_IPW2200_QOS int tx_id = ipw_get_tx_queue_number(priv, pri); struct clx2_tx_queue *txq = &priv->txq[tx_id]; @@ -10396,9 +10393,9 @@ static int ipw_net_is_queue_full(struct net_device *dev, int pri) #ifdef CONFIG_IPW2200_PROMISCUOUS static void ipw_handle_promiscuous_tx(struct ipw_priv *priv, - struct ieee80211_txb *txb) + struct libipw_txb *txb) { - struct ieee80211_rx_stats dummystats; + struct libipw_rx_stats dummystats; struct ieee80211_hdr *hdr; u8 n; u16 filter = priv->prom_priv->filter; @@ -10411,17 +10408,17 @@ static void ipw_handle_promiscuous_tx(struct ipw_priv *priv, /* Filtering of fragment chains is done agains the first fragment */ hdr = (void *)txb->fragments[0]->data; - if (ieee80211_is_management(le16_to_cpu(hdr->frame_control))) { + if (libipw_is_management(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_MGMT) return; if (filter & IPW_PROM_MGMT_HEADER_ONLY) hdr_only = 1; - } else if (ieee80211_is_control(le16_to_cpu(hdr->frame_control))) { + } else if (libipw_is_control(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_CTL) return; if (filter & IPW_PROM_CTL_HEADER_ONLY) hdr_only = 1; - } else if (ieee80211_is_data(le16_to_cpu(hdr->frame_control))) { + } else if (libipw_is_data(le16_to_cpu(hdr->frame_control))) { if (filter & IPW_PROM_NO_DATA) return; if (filter & IPW_PROM_DATA_HEADER_ONLY) @@ -10436,7 +10433,7 @@ static void ipw_handle_promiscuous_tx(struct ipw_priv *priv, if (hdr_only) { hdr = (void *)src->data; - len = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control)); + len = libipw_get_hdrlen(le16_to_cpu(hdr->frame_control)); } else len = src->len; @@ -10470,18 +10467,18 @@ static void ipw_handle_promiscuous_tx(struct ipw_priv *priv, skb_copy_from_linear_data(src, skb_put(dst, len), len); - if (!ieee80211_rx(priv->prom_priv->ieee, dst, &dummystats)) + if (!libipw_rx(priv->prom_priv->ieee, dst, &dummystats)) dev_kfree_skb_any(dst); } } #endif -static int ipw_net_hard_start_xmit(struct ieee80211_txb *txb, - struct net_device *dev, int pri) +static netdev_tx_t ipw_net_hard_start_xmit(struct libipw_txb *txb, + struct net_device *dev, int pri) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); unsigned long flags; - int ret; + netdev_tx_t ret; IPW_DEBUG_TX("dev->xmit(%d bytes)\n", txb->payload_size); spin_lock_irqsave(&priv->lock, flags); @@ -10506,7 +10503,7 @@ static void ipw_net_set_multicast_list(struct net_device *dev) static int ipw_net_set_mac_address(struct net_device *dev, void *p) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); struct sockaddr *addr = p; if (!is_valid_ether_addr(addr->sa_data)) @@ -10524,7 +10521,7 @@ static int ipw_net_set_mac_address(struct net_device *dev, void *p) static void ipw_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct ipw_priv *p = ieee80211_priv(dev); + struct ipw_priv *p = libipw_priv(dev); char vers[64]; char date[32]; u32 len; @@ -10545,7 +10542,7 @@ static void ipw_ethtool_get_drvinfo(struct net_device *dev, static u32 ipw_ethtool_get_link(struct net_device *dev) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); return (priv->status & STATUS_ASSOCIATED) != 0; } @@ -10557,7 +10554,7 @@ static int ipw_ethtool_get_eeprom_len(struct net_device *dev) static int ipw_ethtool_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 * bytes) { - struct ipw_priv *p = ieee80211_priv(dev); + struct ipw_priv *p = libipw_priv(dev); if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE) return -EINVAL; @@ -10570,7 +10567,7 @@ static int ipw_ethtool_get_eeprom(struct net_device *dev, static int ipw_ethtool_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 * bytes) { - struct ipw_priv *p = ieee80211_priv(dev); + struct ipw_priv *p = libipw_priv(dev); int i; if (eeprom->offset + eeprom->len > IPW_EEPROM_IMAGE_SIZE) @@ -10786,9 +10783,9 @@ static int __devinit ipw_setup_deferred_work(struct ipw_priv *priv) } static void shim__set_security(struct net_device *dev, - struct ieee80211_security *sec) + struct libipw_security *sec) { - struct ipw_priv *priv = ieee80211_priv(dev); + struct ipw_priv *priv = libipw_priv(dev); int i; for (i = 0; i < 4; i++) { if (sec->flags & (1 << i)) { @@ -10873,21 +10870,21 @@ static int init_supported_rates(struct ipw_priv *priv, memset(rates, 0, sizeof(*rates)); /* configure supported rates */ switch (priv->ieee->freq_band) { - case IEEE80211_52GHZ_BAND: + case LIBIPW_52GHZ_BAND: rates->ieee_mode = IPW_A_MODE; rates->purpose = IPW_RATE_CAPABILITIES; - ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION, - IEEE80211_OFDM_DEFAULT_RATES_MASK); + ipw_add_ofdm_scan_rates(rates, LIBIPW_CCK_MODULATION, + LIBIPW_OFDM_DEFAULT_RATES_MASK); break; default: /* Mixed or 2.4Ghz */ rates->ieee_mode = IPW_G_MODE; rates->purpose = IPW_RATE_CAPABILITIES; - ipw_add_cck_scan_rates(rates, IEEE80211_CCK_MODULATION, - IEEE80211_CCK_DEFAULT_RATES_MASK); - if (priv->ieee->modulation & IEEE80211_OFDM_MODULATION) { - ipw_add_ofdm_scan_rates(rates, IEEE80211_CCK_MODULATION, - IEEE80211_OFDM_DEFAULT_RATES_MASK); + ipw_add_cck_scan_rates(rates, LIBIPW_CCK_MODULATION, + LIBIPW_CCK_DEFAULT_RATES_MASK); + if (priv->ieee->modulation & LIBIPW_OFDM_MODULATION) { + ipw_add_ofdm_scan_rates(rates, LIBIPW_CCK_MODULATION, + LIBIPW_OFDM_DEFAULT_RATES_MASK); } break; } @@ -10993,7 +10990,7 @@ static int ipw_config(struct ipw_priv *priv) * table. * */ -static const struct ieee80211_geo ipw_geos[] = { +static const struct libipw_geo ipw_geos[] = { { /* Restricted */ "---", .bg_channels = 11, @@ -11015,10 +11012,10 @@ static const struct ieee80211_geo ipw_geos[] = { {5200, 40}, {5220, 44}, {5240, 48}, - {5260, 52, IEEE80211_CH_PASSIVE_ONLY}, - {5280, 56, IEEE80211_CH_PASSIVE_ONLY}, - {5300, 60, IEEE80211_CH_PASSIVE_ONLY}, - {5320, 64, IEEE80211_CH_PASSIVE_ONLY}}, + {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, + {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, + {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, + {5320, 64, LIBIPW_CH_PASSIVE_ONLY}}, }, { /* Rest of World */ @@ -11043,10 +11040,10 @@ static const struct ieee80211_geo ipw_geos[] = { {5200, 40}, {5220, 44}, {5240, 48}, - {5260, 52, IEEE80211_CH_PASSIVE_ONLY}, - {5280, 56, IEEE80211_CH_PASSIVE_ONLY}, - {5300, 60, IEEE80211_CH_PASSIVE_ONLY}, - {5320, 64, IEEE80211_CH_PASSIVE_ONLY}, + {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, + {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, + {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, + {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, {5745, 149}, {5765, 153}, {5785, 157}, @@ -11066,15 +11063,15 @@ static const struct ieee80211_geo ipw_geos[] = { {5200, 40}, {5220, 44}, {5240, 48}, - {5260, 52, IEEE80211_CH_PASSIVE_ONLY}, - {5280, 56, IEEE80211_CH_PASSIVE_ONLY}, - {5300, 60, IEEE80211_CH_PASSIVE_ONLY}, - {5320, 64, IEEE80211_CH_PASSIVE_ONLY}, - {5745, 149, IEEE80211_CH_PASSIVE_ONLY}, - {5765, 153, IEEE80211_CH_PASSIVE_ONLY}, - {5785, 157, IEEE80211_CH_PASSIVE_ONLY}, - {5805, 161, IEEE80211_CH_PASSIVE_ONLY}, - {5825, 165, IEEE80211_CH_PASSIVE_ONLY}}, + {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, + {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, + {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, + {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, + {5745, 149, LIBIPW_CH_PASSIVE_ONLY}, + {5765, 153, LIBIPW_CH_PASSIVE_ONLY}, + {5785, 157, LIBIPW_CH_PASSIVE_ONLY}, + {5805, 161, LIBIPW_CH_PASSIVE_ONLY}, + {5825, 165, LIBIPW_CH_PASSIVE_ONLY}}, }, { /* Custom Japan */ @@ -11111,21 +11108,21 @@ static const struct ieee80211_geo ipw_geos[] = { {5200, 40}, {5220, 44}, {5240, 48}, - {5260, 52, IEEE80211_CH_PASSIVE_ONLY}, - {5280, 56, IEEE80211_CH_PASSIVE_ONLY}, - {5300, 60, IEEE80211_CH_PASSIVE_ONLY}, - {5320, 64, IEEE80211_CH_PASSIVE_ONLY}, - {5500, 100, IEEE80211_CH_PASSIVE_ONLY}, - {5520, 104, IEEE80211_CH_PASSIVE_ONLY}, - {5540, 108, IEEE80211_CH_PASSIVE_ONLY}, - {5560, 112, IEEE80211_CH_PASSIVE_ONLY}, - {5580, 116, IEEE80211_CH_PASSIVE_ONLY}, - {5600, 120, IEEE80211_CH_PASSIVE_ONLY}, - {5620, 124, IEEE80211_CH_PASSIVE_ONLY}, - {5640, 128, IEEE80211_CH_PASSIVE_ONLY}, - {5660, 132, IEEE80211_CH_PASSIVE_ONLY}, - {5680, 136, IEEE80211_CH_PASSIVE_ONLY}, - {5700, 140, IEEE80211_CH_PASSIVE_ONLY}}, + {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, + {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, + {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, + {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, + {5500, 100, LIBIPW_CH_PASSIVE_ONLY}, + {5520, 104, LIBIPW_CH_PASSIVE_ONLY}, + {5540, 108, LIBIPW_CH_PASSIVE_ONLY}, + {5560, 112, LIBIPW_CH_PASSIVE_ONLY}, + {5580, 116, LIBIPW_CH_PASSIVE_ONLY}, + {5600, 120, LIBIPW_CH_PASSIVE_ONLY}, + {5620, 124, LIBIPW_CH_PASSIVE_ONLY}, + {5640, 128, LIBIPW_CH_PASSIVE_ONLY}, + {5660, 132, LIBIPW_CH_PASSIVE_ONLY}, + {5680, 136, LIBIPW_CH_PASSIVE_ONLY}, + {5700, 140, LIBIPW_CH_PASSIVE_ONLY}}, }, { /* Custom Japan */ @@ -11135,7 +11132,7 @@ static const struct ieee80211_geo ipw_geos[] = { {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}, {2467, 12}, - {2472, 13}, {2484, 14, IEEE80211_CH_B_ONLY}}, + {2472, 13}, {2484, 14, LIBIPW_CH_B_ONLY}}, .a_channels = 4, .a = {{5170, 34}, {5190, 38}, {5210, 42}, {5230, 46}}, @@ -11148,8 +11145,8 @@ static const struct ieee80211_geo ipw_geos[] = { {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}, {2467, 12}, - {2472, 13}, {2484, 14, IEEE80211_CH_B_ONLY | - IEEE80211_CH_PASSIVE_ONLY}}, + {2472, 13}, {2484, 14, LIBIPW_CH_B_ONLY | + LIBIPW_CH_PASSIVE_ONLY}}, }, { /* High Band */ @@ -11159,8 +11156,8 @@ static const struct ieee80211_geo ipw_geos[] = { {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}, - {2467, 12, IEEE80211_CH_PASSIVE_ONLY}, - {2472, 13, IEEE80211_CH_PASSIVE_ONLY}}, + {2467, 12, LIBIPW_CH_PASSIVE_ONLY}, + {2472, 13, LIBIPW_CH_PASSIVE_ONLY}}, .a_channels = 4, .a = {{5745, 149}, {5765, 153}, {5785, 157}, {5805, 161}}, @@ -11186,33 +11183,33 @@ static const struct ieee80211_geo ipw_geos[] = { {2427, 4}, {2432, 5}, {2437, 6}, {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}, - {2467, 12, IEEE80211_CH_PASSIVE_ONLY}, - {2472, 13, IEEE80211_CH_PASSIVE_ONLY}}, + {2467, 12, LIBIPW_CH_PASSIVE_ONLY}, + {2472, 13, LIBIPW_CH_PASSIVE_ONLY}}, .a_channels = 24, - .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY}, - {5200, 40, IEEE80211_CH_PASSIVE_ONLY}, - {5220, 44, IEEE80211_CH_PASSIVE_ONLY}, - {5240, 48, IEEE80211_CH_PASSIVE_ONLY}, - {5260, 52, IEEE80211_CH_PASSIVE_ONLY}, - {5280, 56, IEEE80211_CH_PASSIVE_ONLY}, - {5300, 60, IEEE80211_CH_PASSIVE_ONLY}, - {5320, 64, IEEE80211_CH_PASSIVE_ONLY}, - {5500, 100, IEEE80211_CH_PASSIVE_ONLY}, - {5520, 104, IEEE80211_CH_PASSIVE_ONLY}, - {5540, 108, IEEE80211_CH_PASSIVE_ONLY}, - {5560, 112, IEEE80211_CH_PASSIVE_ONLY}, - {5580, 116, IEEE80211_CH_PASSIVE_ONLY}, - {5600, 120, IEEE80211_CH_PASSIVE_ONLY}, - {5620, 124, IEEE80211_CH_PASSIVE_ONLY}, - {5640, 128, IEEE80211_CH_PASSIVE_ONLY}, - {5660, 132, IEEE80211_CH_PASSIVE_ONLY}, - {5680, 136, IEEE80211_CH_PASSIVE_ONLY}, - {5700, 140, IEEE80211_CH_PASSIVE_ONLY}, - {5745, 149, IEEE80211_CH_PASSIVE_ONLY}, - {5765, 153, IEEE80211_CH_PASSIVE_ONLY}, - {5785, 157, IEEE80211_CH_PASSIVE_ONLY}, - {5805, 161, IEEE80211_CH_PASSIVE_ONLY}, - {5825, 165, IEEE80211_CH_PASSIVE_ONLY}}, + .a = {{5180, 36, LIBIPW_CH_PASSIVE_ONLY}, + {5200, 40, LIBIPW_CH_PASSIVE_ONLY}, + {5220, 44, LIBIPW_CH_PASSIVE_ONLY}, + {5240, 48, LIBIPW_CH_PASSIVE_ONLY}, + {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, + {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, + {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, + {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, + {5500, 100, LIBIPW_CH_PASSIVE_ONLY}, + {5520, 104, LIBIPW_CH_PASSIVE_ONLY}, + {5540, 108, LIBIPW_CH_PASSIVE_ONLY}, + {5560, 112, LIBIPW_CH_PASSIVE_ONLY}, + {5580, 116, LIBIPW_CH_PASSIVE_ONLY}, + {5600, 120, LIBIPW_CH_PASSIVE_ONLY}, + {5620, 124, LIBIPW_CH_PASSIVE_ONLY}, + {5640, 128, LIBIPW_CH_PASSIVE_ONLY}, + {5660, 132, LIBIPW_CH_PASSIVE_ONLY}, + {5680, 136, LIBIPW_CH_PASSIVE_ONLY}, + {5700, 140, LIBIPW_CH_PASSIVE_ONLY}, + {5745, 149, LIBIPW_CH_PASSIVE_ONLY}, + {5765, 153, LIBIPW_CH_PASSIVE_ONLY}, + {5785, 157, LIBIPW_CH_PASSIVE_ONLY}, + {5805, 161, LIBIPW_CH_PASSIVE_ONLY}, + {5825, 165, LIBIPW_CH_PASSIVE_ONLY}}, }, { /* Europe */ @@ -11223,19 +11220,19 @@ static const struct ieee80211_geo ipw_geos[] = { {2442, 7}, {2447, 8}, {2452, 9}, {2457, 10}, {2462, 11}}, .a_channels = 13, - .a = {{5180, 36, IEEE80211_CH_PASSIVE_ONLY}, - {5200, 40, IEEE80211_CH_PASSIVE_ONLY}, - {5220, 44, IEEE80211_CH_PASSIVE_ONLY}, - {5240, 48, IEEE80211_CH_PASSIVE_ONLY}, - {5260, 52, IEEE80211_CH_PASSIVE_ONLY}, - {5280, 56, IEEE80211_CH_PASSIVE_ONLY}, - {5300, 60, IEEE80211_CH_PASSIVE_ONLY}, - {5320, 64, IEEE80211_CH_PASSIVE_ONLY}, - {5745, 149, IEEE80211_CH_PASSIVE_ONLY}, - {5765, 153, IEEE80211_CH_PASSIVE_ONLY}, - {5785, 157, IEEE80211_CH_PASSIVE_ONLY}, - {5805, 161, IEEE80211_CH_PASSIVE_ONLY}, - {5825, 165, IEEE80211_CH_PASSIVE_ONLY}}, + .a = {{5180, 36, LIBIPW_CH_PASSIVE_ONLY}, + {5200, 40, LIBIPW_CH_PASSIVE_ONLY}, + {5220, 44, LIBIPW_CH_PASSIVE_ONLY}, + {5240, 48, LIBIPW_CH_PASSIVE_ONLY}, + {5260, 52, LIBIPW_CH_PASSIVE_ONLY}, + {5280, 56, LIBIPW_CH_PASSIVE_ONLY}, + {5300, 60, LIBIPW_CH_PASSIVE_ONLY}, + {5320, 64, LIBIPW_CH_PASSIVE_ONLY}, + {5745, 149, LIBIPW_CH_PASSIVE_ONLY}, + {5765, 153, LIBIPW_CH_PASSIVE_ONLY}, + {5785, 157, LIBIPW_CH_PASSIVE_ONLY}, + {5805, 161, LIBIPW_CH_PASSIVE_ONLY}, + {5825, 165, LIBIPW_CH_PASSIVE_ONLY}}, } }; @@ -11246,7 +11243,7 @@ static int ipw_up(struct ipw_priv *priv) /* Age scan list entries found before suspend */ if (priv->suspend_time) { - ieee80211_networks_age(priv->ieee, priv->suspend_time); + libipw_networks_age(priv->ieee, priv->suspend_time); priv->suspend_time = 0; } @@ -11291,7 +11288,7 @@ static int ipw_up(struct ipw_priv *priv) priv->eeprom[EEPROM_COUNTRY_CODE + 2]); j = 0; } - if (ieee80211_set_geo(priv->ieee, &ipw_geos[j])) { + if (libipw_set_geo(priv->ieee, &ipw_geos[j])) { IPW_WARNING("Could not set geography."); return 0; } @@ -11419,16 +11416,100 @@ static void ipw_bg_down(struct work_struct *work) /* Called by register_netdev() */ static int ipw_net_init(struct net_device *dev) { - struct ipw_priv *priv = ieee80211_priv(dev); + int i, rc = 0; + struct ipw_priv *priv = libipw_priv(dev); + const struct libipw_geo *geo = libipw_get_geo(priv->ieee); + struct wireless_dev *wdev = &priv->ieee->wdev; mutex_lock(&priv->mutex); if (ipw_up(priv)) { - mutex_unlock(&priv->mutex); - return -EIO; + rc = -EIO; + goto out; + } + + memcpy(wdev->wiphy->perm_addr, priv->mac_addr, ETH_ALEN); + + /* fill-out priv->ieee->bg_band */ + if (geo->bg_channels) { + struct ieee80211_supported_band *bg_band = &priv->ieee->bg_band; + + bg_band->band = IEEE80211_BAND_2GHZ; + bg_band->n_channels = geo->bg_channels; + bg_band->channels = + kzalloc(geo->bg_channels * + sizeof(struct ieee80211_channel), GFP_KERNEL); + /* translate geo->bg to bg_band.channels */ + for (i = 0; i < geo->bg_channels; i++) { + bg_band->channels[i].band = IEEE80211_BAND_2GHZ; + bg_band->channels[i].center_freq = geo->bg[i].freq; + bg_band->channels[i].hw_value = geo->bg[i].channel; + bg_band->channels[i].max_power = geo->bg[i].max_power; + if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) + bg_band->channels[i].flags |= + IEEE80211_CHAN_PASSIVE_SCAN; + if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) + bg_band->channels[i].flags |= + IEEE80211_CHAN_NO_IBSS; + if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) + bg_band->channels[i].flags |= + IEEE80211_CHAN_RADAR; + /* No equivalent for LIBIPW_CH_80211H_RULES, + LIBIPW_CH_UNIFORM_SPREADING, or + LIBIPW_CH_B_ONLY... */ + } + /* point at bitrate info */ + bg_band->bitrates = ipw2200_bg_rates; + bg_band->n_bitrates = ipw2200_num_bg_rates; + + wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = bg_band; + } + + /* fill-out priv->ieee->a_band */ + if (geo->a_channels) { + struct ieee80211_supported_band *a_band = &priv->ieee->a_band; + + a_band->band = IEEE80211_BAND_5GHZ; + a_band->n_channels = geo->a_channels; + a_band->channels = + kzalloc(geo->a_channels * + sizeof(struct ieee80211_channel), GFP_KERNEL); + /* translate geo->bg to a_band.channels */ + for (i = 0; i < geo->a_channels; i++) { + a_band->channels[i].band = IEEE80211_BAND_2GHZ; + a_band->channels[i].center_freq = geo->a[i].freq; + a_band->channels[i].hw_value = geo->a[i].channel; + a_band->channels[i].max_power = geo->a[i].max_power; + if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) + a_band->channels[i].flags |= + IEEE80211_CHAN_PASSIVE_SCAN; + if (geo->a[i].flags & LIBIPW_CH_NO_IBSS) + a_band->channels[i].flags |= + IEEE80211_CHAN_NO_IBSS; + if (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT) + a_band->channels[i].flags |= + IEEE80211_CHAN_RADAR; + /* No equivalent for LIBIPW_CH_80211H_RULES, + LIBIPW_CH_UNIFORM_SPREADING, or + LIBIPW_CH_B_ONLY... */ + } + /* point at bitrate info */ + a_band->bitrates = ipw2200_a_rates; + a_band->n_bitrates = ipw2200_num_a_rates; + + wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = a_band; + } + + set_wiphy_dev(wdev->wiphy, &priv->pci_dev->dev); + + /* With that information in place, we can now register the wiphy... */ + if (wiphy_register(wdev->wiphy)) { + rc = -EIO; + goto out; } +out: mutex_unlock(&priv->mutex); - return 0; + return rc; } /* PCI driver stuff */ @@ -11450,11 +11531,11 @@ static struct pci_device_id card_ids[] = { {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2754, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2761, 0, 0, 0}, {PCI_VENDOR_ID_INTEL, 0x1043, 0x8086, 0x2762, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x104f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - {PCI_VENDOR_ID_INTEL, 0x4220, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */ - {PCI_VENDOR_ID_INTEL, 0x4221, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* BG */ - {PCI_VENDOR_ID_INTEL, 0x4223, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */ - {PCI_VENDOR_ID_INTEL, 0x4224, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* ABG */ + {PCI_VDEVICE(INTEL, 0x104f), 0}, + {PCI_VDEVICE(INTEL, 0x4220), 0}, /* BG */ + {PCI_VDEVICE(INTEL, 0x4221), 0}, /* BG */ + {PCI_VDEVICE(INTEL, 0x4223), 0}, /* ABG */ + {PCI_VDEVICE(INTEL, 0x4224), 0}, /* ABG */ /* required last entry */ {0,} @@ -11498,7 +11579,7 @@ static struct attribute_group ipw_attribute_group = { #ifdef CONFIG_IPW2200_PROMISCUOUS static int ipw_prom_open(struct net_device *dev) { - struct ipw_prom_priv *prom_priv = ieee80211_priv(dev); + struct ipw_prom_priv *prom_priv = libipw_priv(dev); struct ipw_priv *priv = prom_priv->priv; IPW_DEBUG_INFO("prom dev->open\n"); @@ -11518,7 +11599,7 @@ static int ipw_prom_open(struct net_device *dev) static int ipw_prom_stop(struct net_device *dev) { - struct ipw_prom_priv *prom_priv = ieee80211_priv(dev); + struct ipw_prom_priv *prom_priv = libipw_priv(dev); struct ipw_priv *priv = prom_priv->priv; IPW_DEBUG_INFO("prom dev->stop\n"); @@ -11535,7 +11616,8 @@ static int ipw_prom_stop(struct net_device *dev) return 0; } -static int ipw_prom_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t ipw_prom_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) { IPW_DEBUG_INFO("prom dev->xmit\n"); dev_kfree_skb(skb); @@ -11546,7 +11628,7 @@ static const struct net_device_ops ipw_prom_netdev_ops = { .ndo_open = ipw_prom_open, .ndo_stop = ipw_prom_stop, .ndo_start_xmit = ipw_prom_hard_start_xmit, - .ndo_change_mtu = ieee80211_change_mtu, + .ndo_change_mtu = libipw_change_mtu, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, }; @@ -11558,11 +11640,11 @@ static int ipw_prom_alloc(struct ipw_priv *priv) if (priv->prom_net_dev) return -EPERM; - priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv)); + priv->prom_net_dev = alloc_ieee80211(sizeof(struct ipw_prom_priv), 1); if (priv->prom_net_dev == NULL) return -ENOMEM; - priv->prom_priv = ieee80211_priv(priv->prom_net_dev); + priv->prom_priv = libipw_priv(priv->prom_net_dev); priv->prom_priv->ieee = netdev_priv(priv->prom_net_dev); priv->prom_priv->priv = priv; @@ -11577,7 +11659,7 @@ static int ipw_prom_alloc(struct ipw_priv *priv) rc = register_netdev(priv->prom_net_dev); if (rc) { - free_ieee80211(priv->prom_net_dev); + free_ieee80211(priv->prom_net_dev, 1); priv->prom_net_dev = NULL; return rc; } @@ -11591,7 +11673,7 @@ static void ipw_prom_free(struct ipw_priv *priv) return; unregister_netdev(priv->prom_net_dev); - free_ieee80211(priv->prom_net_dev); + free_ieee80211(priv->prom_net_dev, 1); priv->prom_net_dev = NULL; } @@ -11604,8 +11686,8 @@ static const struct net_device_ops ipw_netdev_ops = { .ndo_stop = ipw_net_stop, .ndo_set_multicast_list = ipw_net_set_multicast_list, .ndo_set_mac_address = ipw_net_set_mac_address, - .ndo_start_xmit = ieee80211_xmit, - .ndo_change_mtu = ieee80211_change_mtu, + .ndo_start_xmit = libipw_xmit, + .ndo_change_mtu = libipw_change_mtu, .ndo_validate_addr = eth_validate_addr, }; @@ -11619,13 +11701,13 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev, struct ipw_priv *priv; int i; - net_dev = alloc_ieee80211(sizeof(struct ipw_priv)); + net_dev = alloc_ieee80211(sizeof(struct ipw_priv), 0); if (net_dev == NULL) { err = -ENOMEM; goto out; } - priv = ieee80211_priv(net_dev); + priv = libipw_priv(net_dev); priv->ieee = netdev_priv(net_dev); priv->net_dev = net_dev; @@ -11767,7 +11849,7 @@ static int __devinit ipw_pci_probe(struct pci_dev *pdev, pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); out_free_ieee80211: - free_ieee80211(priv->net_dev); + free_ieee80211(priv->net_dev, 0); out: return err; } @@ -11834,7 +11916,7 @@ static void __devexit ipw_pci_remove(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); - free_ieee80211(priv->net_dev); + free_ieee80211(priv->net_dev, 0); free_firmware(); } @@ -11963,13 +12045,13 @@ MODULE_PARM_DESC(associate, "auto associate when scanning (default off)"); module_param(auto_create, int, 0444); MODULE_PARM_DESC(auto_create, "auto create adhoc network (default on)"); -module_param(led, int, 0444); +module_param_named(led, led_support, int, 0444); 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"); -module_param(channel, int, 0444); +module_param_named(channel, default_channel, int, 0444); MODULE_PARM_DESC(channel, "channel to limit associate to (default 0 [ANY])"); #ifdef CONFIG_IPW2200_PROMISCUOUS @@ -11995,10 +12077,10 @@ MODULE_PARM_DESC(burst_duration_OFDM, "set OFDM burst value"); #endif /* CONFIG_IPW2200_QOS */ #ifdef CONFIG_IPW2200_MONITOR -module_param(mode, int, 0444); +module_param_named(mode, network_mode, int, 0444); MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS,2=Monitor)"); #else -module_param(mode, int, 0444); +module_param_named(mode, network_mode, int, 0444); MODULE_PARM_DESC(mode, "network mode (0=BSS,1=IBSS)"); #endif diff --git a/drivers/net/wireless/ipw2x00/ipw2200.h b/drivers/net/wireless/ipw2x00/ipw2200.h index 05e8ccf01c5f..bf0eeb2e873a 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.h +++ b/drivers/net/wireless/ipw2x00/ipw2200.h @@ -19,7 +19,7 @@ file called LICENSE. Contact Information: - James P. Ketrenos <ipw2100-admin@linux.intel.com> + Intel Linux Wireless <ilw@linux.intel.com> Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ******************************************************************************/ @@ -55,7 +55,7 @@ #include <linux/workqueue.h> -#include "ieee80211.h" +#include "libipw.h" /* Authentication and Association States */ enum connection_manager_assoc_states { @@ -365,8 +365,8 @@ enum connection_manager_assoc_states { /* QoS sturctures */ struct ipw_qos_info { int qos_enable; - struct ieee80211_qos_parameters *def_qos_parm_OFDM; - struct ieee80211_qos_parameters *def_qos_parm_CCK; + struct libipw_qos_parameters *def_qos_parm_OFDM; + struct libipw_qos_parameters *def_qos_parm_CCK; u32 burst_duration_CCK; u32 burst_duration_OFDM; u16 qos_no_ack_mask; @@ -534,7 +534,7 @@ typedef void destructor_func(const void *); struct clx2_tx_queue { struct clx2_queue q; struct tfd_frame *bd; - struct ieee80211_txb **txb; + struct libipw_txb **txb; }; /* @@ -1144,7 +1144,7 @@ enum ipw_prom_filter { struct ipw_priv; struct ipw_prom_priv { struct ipw_priv *priv; - struct ieee80211_device *ieee; + struct libipw_device *ieee; enum ipw_prom_filter filter; int tx_packets; int rx_packets; @@ -1175,7 +1175,7 @@ struct ipw_rt_hdr { struct ipw_priv { /* ieee device used by generic ieee processing code */ - struct ieee80211_device *ieee; + struct libipw_device *ieee; spinlock_t lock; spinlock_t irq_lock; @@ -1222,7 +1222,7 @@ struct ipw_priv { u32 roaming_threshold; struct ipw_associate assoc_request; - struct ieee80211_network *assoc_network; + struct libipw_network *assoc_network; unsigned long ts_scan_abort; struct ipw_supported_rates rates; diff --git a/drivers/net/wireless/ipw2x00/ieee80211.h b/drivers/net/wireless/ipw2x00/libipw.h index 70755c1336d5..bf45391172f3 100644 --- a/drivers/net/wireless/ipw2x00/ieee80211.h +++ b/drivers/net/wireless/ipw2x00/libipw.h @@ -20,21 +20,22 @@ * * API Version History * 1.0.x -- Initial version - * 1.1.x -- Added radiotap, QoS, TIM, ieee80211_geo APIs, + * 1.1.x -- Added radiotap, QoS, TIM, libipw_geo APIs, * various structure changes, and crypto API init method */ -#ifndef IEEE80211_H -#define IEEE80211_H +#ifndef LIBIPW_H +#define LIBIPW_H #include <linux/if_ether.h> /* ETH_ALEN */ #include <linux/kernel.h> /* ARRAY_SIZE */ #include <linux/wireless.h> #include <linux/ieee80211.h> #include <net/lib80211.h> +#include <net/cfg80211.h> -#define IEEE80211_VERSION "git-1.1.13" +#define LIBIPW_VERSION "git-1.1.13" -#define IEEE80211_DATA_LEN 2304 +#define LIBIPW_DATA_LEN 2304 /* Maximum size for the MA-UNITDATA primitive, 802.11 standard section 6.2.1.1.2. @@ -43,35 +44,35 @@ represents the 2304 bytes of real data, plus a possible 8 bytes of WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ -#define IEEE80211_1ADDR_LEN 10 -#define IEEE80211_2ADDR_LEN 16 -#define IEEE80211_3ADDR_LEN 24 -#define IEEE80211_4ADDR_LEN 30 -#define IEEE80211_FCS_LEN 4 -#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) -#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) +#define LIBIPW_1ADDR_LEN 10 +#define LIBIPW_2ADDR_LEN 16 +#define LIBIPW_3ADDR_LEN 24 +#define LIBIPW_4ADDR_LEN 30 +#define LIBIPW_FCS_LEN 4 +#define LIBIPW_HLEN (LIBIPW_4ADDR_LEN) +#define LIBIPW_FRAME_LEN (LIBIPW_DATA_LEN + LIBIPW_HLEN) #define MIN_FRAG_THRESHOLD 256U #define MAX_FRAG_THRESHOLD 2346U /* QOS control */ -#define IEEE80211_QCTL_TID 0x000F +#define LIBIPW_QCTL_TID 0x000F /* debug macros */ #ifdef CONFIG_LIBIPW_DEBUG -extern u32 ieee80211_debug_level; -#define IEEE80211_DEBUG(level, fmt, args...) \ -do { if (ieee80211_debug_level & (level)) \ +extern u32 libipw_debug_level; +#define LIBIPW_DEBUG(level, fmt, args...) \ +do { if (libipw_debug_level & (level)) \ printk(KERN_DEBUG "ieee80211: %c %s " fmt, \ in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) -static inline bool ieee80211_ratelimit_debug(u32 level) +static inline bool libipw_ratelimit_debug(u32 level) { - return (ieee80211_debug_level & level) && net_ratelimit(); + return (libipw_debug_level & level) && net_ratelimit(); } #else -#define IEEE80211_DEBUG(level, fmt, args...) do {} while (0) -static inline bool ieee80211_ratelimit_debug(u32 level) +#define LIBIPW_DEBUG(level, fmt, args...) do {} while (0) +static inline bool libipw_ratelimit_debug(u32 level) { return false; } @@ -83,51 +84,51 @@ static inline bool ieee80211_ratelimit_debug(u32 level) * If you are defining a new debug classification, simply add it to the #define * list here in the form of: * - * #define IEEE80211_DL_xxxx VALUE + * #define LIBIPW_DL_xxxx VALUE * * shifting value to the left one bit from the previous entry. xxxx should be * the name of the classification (for example, WEP) * - * You then need to either add a IEEE80211_xxxx_DEBUG() macro definition for your - * classification, or use IEEE80211_DEBUG(IEEE80211_DL_xxxx, ...) whenever you want + * You then need to either add a LIBIPW_xxxx_DEBUG() macro definition for your + * classification, or use LIBIPW_DEBUG(LIBIPW_DL_xxxx, ...) whenever you want * to send output to that classification. * * To add your debug level to the list of levels seen when you perform * * % cat /proc/net/ieee80211/debug_level * - * you simply need to add your entry to the ieee80211_debug_level array. + * you simply need to add your entry to the libipw_debug_level array. * * If you do not see debug_level in /proc/net/ieee80211 then you do not have * CONFIG_LIBIPW_DEBUG defined in your kernel configuration * */ -#define IEEE80211_DL_INFO (1<<0) -#define IEEE80211_DL_WX (1<<1) -#define IEEE80211_DL_SCAN (1<<2) -#define IEEE80211_DL_STATE (1<<3) -#define IEEE80211_DL_MGMT (1<<4) -#define IEEE80211_DL_FRAG (1<<5) -#define IEEE80211_DL_DROP (1<<7) - -#define IEEE80211_DL_TX (1<<8) -#define IEEE80211_DL_RX (1<<9) -#define IEEE80211_DL_QOS (1<<31) - -#define IEEE80211_ERROR(f, a...) printk(KERN_ERR "ieee80211: " f, ## a) -#define IEEE80211_WARNING(f, a...) printk(KERN_WARNING "ieee80211: " f, ## a) -#define IEEE80211_DEBUG_INFO(f, a...) IEEE80211_DEBUG(IEEE80211_DL_INFO, f, ## a) - -#define IEEE80211_DEBUG_WX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_WX, f, ## a) -#define IEEE80211_DEBUG_SCAN(f, a...) IEEE80211_DEBUG(IEEE80211_DL_SCAN, f, ## a) -#define IEEE80211_DEBUG_STATE(f, a...) IEEE80211_DEBUG(IEEE80211_DL_STATE, f, ## a) -#define IEEE80211_DEBUG_MGMT(f, a...) IEEE80211_DEBUG(IEEE80211_DL_MGMT, f, ## a) -#define IEEE80211_DEBUG_FRAG(f, a...) IEEE80211_DEBUG(IEEE80211_DL_FRAG, f, ## a) -#define IEEE80211_DEBUG_DROP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_DROP, f, ## a) -#define IEEE80211_DEBUG_TX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_TX, f, ## a) -#define IEEE80211_DEBUG_RX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_RX, f, ## a) -#define IEEE80211_DEBUG_QOS(f, a...) IEEE80211_DEBUG(IEEE80211_DL_QOS, f, ## a) +#define LIBIPW_DL_INFO (1<<0) +#define LIBIPW_DL_WX (1<<1) +#define LIBIPW_DL_SCAN (1<<2) +#define LIBIPW_DL_STATE (1<<3) +#define LIBIPW_DL_MGMT (1<<4) +#define LIBIPW_DL_FRAG (1<<5) +#define LIBIPW_DL_DROP (1<<7) + +#define LIBIPW_DL_TX (1<<8) +#define LIBIPW_DL_RX (1<<9) +#define LIBIPW_DL_QOS (1<<31) + +#define LIBIPW_ERROR(f, a...) printk(KERN_ERR "ieee80211: " f, ## a) +#define LIBIPW_WARNING(f, a...) printk(KERN_WARNING "ieee80211: " f, ## a) +#define LIBIPW_DEBUG_INFO(f, a...) LIBIPW_DEBUG(LIBIPW_DL_INFO, f, ## a) + +#define LIBIPW_DEBUG_WX(f, a...) LIBIPW_DEBUG(LIBIPW_DL_WX, f, ## a) +#define LIBIPW_DEBUG_SCAN(f, a...) LIBIPW_DEBUG(LIBIPW_DL_SCAN, f, ## a) +#define LIBIPW_DEBUG_STATE(f, a...) LIBIPW_DEBUG(LIBIPW_DL_STATE, f, ## a) +#define LIBIPW_DEBUG_MGMT(f, a...) LIBIPW_DEBUG(LIBIPW_DL_MGMT, f, ## a) +#define LIBIPW_DEBUG_FRAG(f, a...) LIBIPW_DEBUG(LIBIPW_DL_FRAG, f, ## a) +#define LIBIPW_DEBUG_DROP(f, a...) LIBIPW_DEBUG(LIBIPW_DL_DROP, f, ## a) +#define LIBIPW_DEBUG_TX(f, a...) LIBIPW_DEBUG(LIBIPW_DL_TX, f, ## a) +#define LIBIPW_DEBUG_RX(f, a...) LIBIPW_DEBUG(LIBIPW_DL_RX, f, ## a) +#define LIBIPW_DEBUG_QOS(f, a...) LIBIPW_DEBUG(LIBIPW_DL_QOS, f, ## a) #include <linux/netdevice.h> #include <linux/if_arp.h> /* ARPHRD_ETHER */ @@ -146,7 +147,7 @@ static inline bool ieee80211_ratelimit_debug(u32 level) #define P80211_OUI_LEN 3 -struct ieee80211_snap_hdr { +struct libipw_snap_hdr { u8 dsap; /* always 0xAA */ u8 ssap; /* always 0xAA */ @@ -155,7 +156,7 @@ struct ieee80211_snap_hdr { } __attribute__ ((packed)); -#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr) +#define SNAP_SIZE sizeof(struct libipw_snap_hdr) #define WLAN_FC_GET_VERS(fc) ((fc) & IEEE80211_FCTL_VERS) #define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE) @@ -164,74 +165,74 @@ struct ieee80211_snap_hdr { #define WLAN_GET_SEQ_FRAG(seq) ((seq) & IEEE80211_SCTL_FRAG) #define WLAN_GET_SEQ_SEQ(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) -#define IEEE80211_STATMASK_SIGNAL (1<<0) -#define IEEE80211_STATMASK_RSSI (1<<1) -#define IEEE80211_STATMASK_NOISE (1<<2) -#define IEEE80211_STATMASK_RATE (1<<3) -#define IEEE80211_STATMASK_WEMASK 0x7 - -#define IEEE80211_CCK_MODULATION (1<<0) -#define IEEE80211_OFDM_MODULATION (1<<1) - -#define IEEE80211_24GHZ_BAND (1<<0) -#define IEEE80211_52GHZ_BAND (1<<1) - -#define IEEE80211_CCK_RATE_1MB 0x02 -#define IEEE80211_CCK_RATE_2MB 0x04 -#define IEEE80211_CCK_RATE_5MB 0x0B -#define IEEE80211_CCK_RATE_11MB 0x16 -#define IEEE80211_OFDM_RATE_6MB 0x0C -#define IEEE80211_OFDM_RATE_9MB 0x12 -#define IEEE80211_OFDM_RATE_12MB 0x18 -#define IEEE80211_OFDM_RATE_18MB 0x24 -#define IEEE80211_OFDM_RATE_24MB 0x30 -#define IEEE80211_OFDM_RATE_36MB 0x48 -#define IEEE80211_OFDM_RATE_48MB 0x60 -#define IEEE80211_OFDM_RATE_54MB 0x6C -#define IEEE80211_BASIC_RATE_MASK 0x80 - -#define IEEE80211_CCK_RATE_1MB_MASK (1<<0) -#define IEEE80211_CCK_RATE_2MB_MASK (1<<1) -#define IEEE80211_CCK_RATE_5MB_MASK (1<<2) -#define IEEE80211_CCK_RATE_11MB_MASK (1<<3) -#define IEEE80211_OFDM_RATE_6MB_MASK (1<<4) -#define IEEE80211_OFDM_RATE_9MB_MASK (1<<5) -#define IEEE80211_OFDM_RATE_12MB_MASK (1<<6) -#define IEEE80211_OFDM_RATE_18MB_MASK (1<<7) -#define IEEE80211_OFDM_RATE_24MB_MASK (1<<8) -#define IEEE80211_OFDM_RATE_36MB_MASK (1<<9) -#define IEEE80211_OFDM_RATE_48MB_MASK (1<<10) -#define IEEE80211_OFDM_RATE_54MB_MASK (1<<11) - -#define IEEE80211_CCK_RATES_MASK 0x0000000F -#define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \ - IEEE80211_CCK_RATE_2MB_MASK) -#define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \ - IEEE80211_CCK_RATE_5MB_MASK | \ - IEEE80211_CCK_RATE_11MB_MASK) - -#define IEEE80211_OFDM_RATES_MASK 0x00000FF0 -#define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \ - IEEE80211_OFDM_RATE_12MB_MASK | \ - IEEE80211_OFDM_RATE_24MB_MASK) -#define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \ - IEEE80211_OFDM_RATE_9MB_MASK | \ - IEEE80211_OFDM_RATE_18MB_MASK | \ - IEEE80211_OFDM_RATE_36MB_MASK | \ - IEEE80211_OFDM_RATE_48MB_MASK | \ - IEEE80211_OFDM_RATE_54MB_MASK) -#define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \ - IEEE80211_CCK_DEFAULT_RATES_MASK) - -#define IEEE80211_NUM_OFDM_RATES 8 -#define IEEE80211_NUM_CCK_RATES 4 -#define IEEE80211_OFDM_SHIFT_MASK_A 4 +#define LIBIPW_STATMASK_SIGNAL (1<<0) +#define LIBIPW_STATMASK_RSSI (1<<1) +#define LIBIPW_STATMASK_NOISE (1<<2) +#define LIBIPW_STATMASK_RATE (1<<3) +#define LIBIPW_STATMASK_WEMASK 0x7 + +#define LIBIPW_CCK_MODULATION (1<<0) +#define LIBIPW_OFDM_MODULATION (1<<1) + +#define LIBIPW_24GHZ_BAND (1<<0) +#define LIBIPW_52GHZ_BAND (1<<1) + +#define LIBIPW_CCK_RATE_1MB 0x02 +#define LIBIPW_CCK_RATE_2MB 0x04 +#define LIBIPW_CCK_RATE_5MB 0x0B +#define LIBIPW_CCK_RATE_11MB 0x16 +#define LIBIPW_OFDM_RATE_6MB 0x0C +#define LIBIPW_OFDM_RATE_9MB 0x12 +#define LIBIPW_OFDM_RATE_12MB 0x18 +#define LIBIPW_OFDM_RATE_18MB 0x24 +#define LIBIPW_OFDM_RATE_24MB 0x30 +#define LIBIPW_OFDM_RATE_36MB 0x48 +#define LIBIPW_OFDM_RATE_48MB 0x60 +#define LIBIPW_OFDM_RATE_54MB 0x6C +#define LIBIPW_BASIC_RATE_MASK 0x80 + +#define LIBIPW_CCK_RATE_1MB_MASK (1<<0) +#define LIBIPW_CCK_RATE_2MB_MASK (1<<1) +#define LIBIPW_CCK_RATE_5MB_MASK (1<<2) +#define LIBIPW_CCK_RATE_11MB_MASK (1<<3) +#define LIBIPW_OFDM_RATE_6MB_MASK (1<<4) +#define LIBIPW_OFDM_RATE_9MB_MASK (1<<5) +#define LIBIPW_OFDM_RATE_12MB_MASK (1<<6) +#define LIBIPW_OFDM_RATE_18MB_MASK (1<<7) +#define LIBIPW_OFDM_RATE_24MB_MASK (1<<8) +#define LIBIPW_OFDM_RATE_36MB_MASK (1<<9) +#define LIBIPW_OFDM_RATE_48MB_MASK (1<<10) +#define LIBIPW_OFDM_RATE_54MB_MASK (1<<11) + +#define LIBIPW_CCK_RATES_MASK 0x0000000F +#define LIBIPW_CCK_BASIC_RATES_MASK (LIBIPW_CCK_RATE_1MB_MASK | \ + LIBIPW_CCK_RATE_2MB_MASK) +#define LIBIPW_CCK_DEFAULT_RATES_MASK (LIBIPW_CCK_BASIC_RATES_MASK | \ + LIBIPW_CCK_RATE_5MB_MASK | \ + LIBIPW_CCK_RATE_11MB_MASK) + +#define LIBIPW_OFDM_RATES_MASK 0x00000FF0 +#define LIBIPW_OFDM_BASIC_RATES_MASK (LIBIPW_OFDM_RATE_6MB_MASK | \ + LIBIPW_OFDM_RATE_12MB_MASK | \ + LIBIPW_OFDM_RATE_24MB_MASK) +#define LIBIPW_OFDM_DEFAULT_RATES_MASK (LIBIPW_OFDM_BASIC_RATES_MASK | \ + LIBIPW_OFDM_RATE_9MB_MASK | \ + LIBIPW_OFDM_RATE_18MB_MASK | \ + LIBIPW_OFDM_RATE_36MB_MASK | \ + LIBIPW_OFDM_RATE_48MB_MASK | \ + LIBIPW_OFDM_RATE_54MB_MASK) +#define LIBIPW_DEFAULT_RATES_MASK (LIBIPW_OFDM_DEFAULT_RATES_MASK | \ + LIBIPW_CCK_DEFAULT_RATES_MASK) + +#define LIBIPW_NUM_OFDM_RATES 8 +#define LIBIPW_NUM_CCK_RATES 4 +#define LIBIPW_OFDM_SHIFT_MASK_A 4 /* NOTE: This data is for statistical purposes; not all hardware provides this * information for frames received. - * For ieee80211_rx_mgt, you need to set at least the 'len' parameter. + * For libipw_rx_mgt, you need to set at least the 'len' parameter. */ -struct ieee80211_rx_stats { +struct libipw_rx_stats { u32 mac_time; s8 rssi; u8 signal; @@ -250,9 +251,9 @@ struct ieee80211_rx_stats { * three fragmented frames. This define can be increased to support more * concurrent frames, but it should be noted that each entry can consume about * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ -#define IEEE80211_FRAG_CACHE_LEN 4 +#define LIBIPW_FRAG_CACHE_LEN 4 -struct ieee80211_frag_entry { +struct libipw_frag_entry { unsigned long first_frag_time; unsigned int seq; unsigned int last_frag; @@ -261,7 +262,7 @@ struct ieee80211_frag_entry { u8 dst_addr[ETH_ALEN]; }; -struct ieee80211_stats { +struct libipw_stats { unsigned int tx_unicast_frames; unsigned int tx_multicast_frames; unsigned int tx_fragments; @@ -285,7 +286,7 @@ struct ieee80211_stats { unsigned int rx_message_in_bad_msg_fragments; }; -struct ieee80211_device; +struct libipw_device; #define SEC_KEY_1 (1<<0) #define SEC_KEY_2 (1<<1) @@ -314,7 +315,7 @@ struct ieee80211_device; #define SCM_KEY_LEN 32 #define SCM_TEMPORAL_KEY_LENGTH 16 -struct ieee80211_security { +struct libipw_security { u16 active_key:2, enabled:1, unicast_uses_group:1, encrypt:1; u8 auth_mode; u8 encode_alg[WEP_KEYS]; @@ -341,14 +342,14 @@ Total: 28-2340 bytes #define BEACON_PROBE_SSID_ID_POSITION 12 -struct ieee80211_hdr_1addr { +struct libipw_hdr_1addr { __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; u8 payload[0]; } __attribute__ ((packed)); -struct ieee80211_hdr_2addr { +struct libipw_hdr_2addr { __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; @@ -356,7 +357,7 @@ struct ieee80211_hdr_2addr { u8 payload[0]; } __attribute__ ((packed)); -struct ieee80211_hdr_3addr { +struct libipw_hdr_3addr { __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; @@ -366,7 +367,7 @@ struct ieee80211_hdr_3addr { u8 payload[0]; } __attribute__ ((packed)); -struct ieee80211_hdr_4addr { +struct libipw_hdr_4addr { __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; @@ -377,7 +378,7 @@ struct ieee80211_hdr_4addr { u8 payload[0]; } __attribute__ ((packed)); -struct ieee80211_hdr_3addrqos { +struct libipw_hdr_3addrqos { __le16 frame_ctl; __le16 duration_id; u8 addr1[ETH_ALEN]; @@ -388,7 +389,7 @@ struct ieee80211_hdr_3addrqos { __le16 qos_ctl; } __attribute__ ((packed)); -struct ieee80211_info_element { +struct libipw_info_element { u8 id; u8 len; u8 data[0]; @@ -411,16 +412,16 @@ struct ieee80211_info_element { u16 status; */ -struct ieee80211_auth { - struct ieee80211_hdr_3addr header; +struct libipw_auth { + struct libipw_hdr_3addr header; __le16 algorithm; __le16 transaction; __le16 status; /* challenge */ - struct ieee80211_info_element info_element[0]; + struct libipw_info_element info_element[0]; } __attribute__ ((packed)); -struct ieee80211_channel_switch { +struct libipw_channel_switch { u8 id; u8 len; u8 mode; @@ -428,73 +429,73 @@ struct ieee80211_channel_switch { u8 count; } __attribute__ ((packed)); -struct ieee80211_action { - struct ieee80211_hdr_3addr header; +struct libipw_action { + struct libipw_hdr_3addr header; u8 category; u8 action; union { - struct ieee80211_action_exchange { + struct libipw_action_exchange { u8 token; - struct ieee80211_info_element info_element[0]; + struct libipw_info_element info_element[0]; } exchange; - struct ieee80211_channel_switch channel_switch; + struct libipw_channel_switch channel_switch; } format; } __attribute__ ((packed)); -struct ieee80211_disassoc { - struct ieee80211_hdr_3addr header; +struct libipw_disassoc { + struct libipw_hdr_3addr header; __le16 reason; } __attribute__ ((packed)); /* Alias deauth for disassoc */ -#define ieee80211_deauth ieee80211_disassoc +#define libipw_deauth libipw_disassoc -struct ieee80211_probe_request { - struct ieee80211_hdr_3addr header; +struct libipw_probe_request { + struct libipw_hdr_3addr header; /* SSID, supported rates */ - struct ieee80211_info_element info_element[0]; + struct libipw_info_element info_element[0]; } __attribute__ ((packed)); -struct ieee80211_probe_response { - struct ieee80211_hdr_3addr header; +struct libipw_probe_response { + struct libipw_hdr_3addr header; __le32 time_stamp[2]; __le16 beacon_interval; __le16 capability; /* SSID, supported rates, FH params, DS params, * CF params, IBSS params, TIM (if beacon), RSN */ - struct ieee80211_info_element info_element[0]; + struct libipw_info_element info_element[0]; } __attribute__ ((packed)); /* Alias beacon for probe_response */ -#define ieee80211_beacon ieee80211_probe_response +#define libipw_beacon libipw_probe_response -struct ieee80211_assoc_request { - struct ieee80211_hdr_3addr header; +struct libipw_assoc_request { + struct libipw_hdr_3addr header; __le16 capability; __le16 listen_interval; /* SSID, supported rates, RSN */ - struct ieee80211_info_element info_element[0]; + struct libipw_info_element info_element[0]; } __attribute__ ((packed)); -struct ieee80211_reassoc_request { - struct ieee80211_hdr_3addr header; +struct libipw_reassoc_request { + struct libipw_hdr_3addr header; __le16 capability; __le16 listen_interval; u8 current_ap[ETH_ALEN]; - struct ieee80211_info_element info_element[0]; + struct libipw_info_element info_element[0]; } __attribute__ ((packed)); -struct ieee80211_assoc_response { - struct ieee80211_hdr_3addr header; +struct libipw_assoc_response { + struct libipw_hdr_3addr header; __le16 capability; __le16 status; __le16 aid; /* supported rates */ - struct ieee80211_info_element info_element[0]; + struct libipw_info_element info_element[0]; } __attribute__ ((packed)); -struct ieee80211_txb { +struct libipw_txb { u8 nr_frags; u8 encrypted; u8 rts_included; @@ -546,7 +547,7 @@ struct ieee80211_txb { #define QOS_VERSION_1 1 #define QOS_AIFSN_MIN_VALUE 2 -struct ieee80211_qos_information_element { +struct libipw_qos_information_element { u8 elementID; u8 length; u8 qui[QOS_OUI_LEN]; @@ -556,19 +557,19 @@ struct ieee80211_qos_information_element { u8 ac_info; } __attribute__ ((packed)); -struct ieee80211_qos_ac_parameter { +struct libipw_qos_ac_parameter { u8 aci_aifsn; u8 ecw_min_max; __le16 tx_op_limit; } __attribute__ ((packed)); -struct ieee80211_qos_parameter_info { - struct ieee80211_qos_information_element info_element; +struct libipw_qos_parameter_info { + struct libipw_qos_information_element info_element; u8 reserved; - struct ieee80211_qos_ac_parameter ac_params_record[QOS_QUEUE_NUM]; + struct libipw_qos_ac_parameter ac_params_record[QOS_QUEUE_NUM]; } __attribute__ ((packed)); -struct ieee80211_qos_parameters { +struct libipw_qos_parameters { __le16 cw_min[QOS_QUEUE_NUM]; __le16 cw_max[QOS_QUEUE_NUM]; u8 aifs[QOS_QUEUE_NUM]; @@ -576,107 +577,107 @@ struct ieee80211_qos_parameters { __le16 tx_op_limit[QOS_QUEUE_NUM]; } __attribute__ ((packed)); -struct ieee80211_qos_data { - struct ieee80211_qos_parameters parameters; +struct libipw_qos_data { + struct libipw_qos_parameters parameters; int active; int supported; u8 param_count; u8 old_param_count; }; -struct ieee80211_tim_parameters { +struct libipw_tim_parameters { u8 tim_count; u8 tim_period; } __attribute__ ((packed)); /*******************************************************/ -enum { /* ieee80211_basic_report.map */ - IEEE80211_BASIC_MAP_BSS = (1 << 0), - IEEE80211_BASIC_MAP_OFDM = (1 << 1), - IEEE80211_BASIC_MAP_UNIDENTIFIED = (1 << 2), - IEEE80211_BASIC_MAP_RADAR = (1 << 3), - IEEE80211_BASIC_MAP_UNMEASURED = (1 << 4), +enum { /* libipw_basic_report.map */ + LIBIPW_BASIC_MAP_BSS = (1 << 0), + LIBIPW_BASIC_MAP_OFDM = (1 << 1), + LIBIPW_BASIC_MAP_UNIDENTIFIED = (1 << 2), + LIBIPW_BASIC_MAP_RADAR = (1 << 3), + LIBIPW_BASIC_MAP_UNMEASURED = (1 << 4), /* Bits 5-7 are reserved */ }; -struct ieee80211_basic_report { +struct libipw_basic_report { u8 channel; __le64 start_time; __le16 duration; u8 map; } __attribute__ ((packed)); -enum { /* ieee80211_measurement_request.mode */ +enum { /* libipw_measurement_request.mode */ /* Bit 0 is reserved */ - IEEE80211_MEASUREMENT_ENABLE = (1 << 1), - IEEE80211_MEASUREMENT_REQUEST = (1 << 2), - IEEE80211_MEASUREMENT_REPORT = (1 << 3), + LIBIPW_MEASUREMENT_ENABLE = (1 << 1), + LIBIPW_MEASUREMENT_REQUEST = (1 << 2), + LIBIPW_MEASUREMENT_REPORT = (1 << 3), /* Bits 4-7 are reserved */ }; enum { - IEEE80211_REPORT_BASIC = 0, /* required */ - IEEE80211_REPORT_CCA = 1, /* optional */ - IEEE80211_REPORT_RPI = 2, /* optional */ + LIBIPW_REPORT_BASIC = 0, /* required */ + LIBIPW_REPORT_CCA = 1, /* optional */ + LIBIPW_REPORT_RPI = 2, /* optional */ /* 3-255 reserved */ }; -struct ieee80211_measurement_params { +struct libipw_measurement_params { u8 channel; __le64 start_time; __le16 duration; } __attribute__ ((packed)); -struct ieee80211_measurement_request { - struct ieee80211_info_element ie; +struct libipw_measurement_request { + struct libipw_info_element ie; u8 token; u8 mode; u8 type; - struct ieee80211_measurement_params params[0]; + struct libipw_measurement_params params[0]; } __attribute__ ((packed)); -struct ieee80211_measurement_report { - struct ieee80211_info_element ie; +struct libipw_measurement_report { + struct libipw_info_element ie; u8 token; u8 mode; u8 type; union { - struct ieee80211_basic_report basic[0]; + struct libipw_basic_report basic[0]; } u; } __attribute__ ((packed)); -struct ieee80211_tpc_report { +struct libipw_tpc_report { u8 transmit_power; u8 link_margin; } __attribute__ ((packed)); -struct ieee80211_channel_map { +struct libipw_channel_map { u8 channel; u8 map; } __attribute__ ((packed)); -struct ieee80211_ibss_dfs { - struct ieee80211_info_element ie; +struct libipw_ibss_dfs { + struct libipw_info_element ie; u8 owner[ETH_ALEN]; u8 recovery_interval; - struct ieee80211_channel_map channel_map[0]; + struct libipw_channel_map channel_map[0]; }; -struct ieee80211_csa { +struct libipw_csa { u8 mode; u8 channel; u8 count; } __attribute__ ((packed)); -struct ieee80211_quiet { +struct libipw_quiet { u8 count; u8 period; u8 duration; u8 offset; } __attribute__ ((packed)); -struct ieee80211_network { +struct libipw_network { /* These entries are used to identify a unique network */ u8 bssid[ETH_ALEN]; u8 channel; @@ -684,10 +685,10 @@ struct ieee80211_network { u8 ssid[IW_ESSID_MAX_SIZE + 1]; u8 ssid_len; - struct ieee80211_qos_data qos_data; + struct libipw_qos_data qos_data; /* These are network statistics */ - struct ieee80211_rx_stats stats; + struct libipw_rx_stats stats; u16 capability; u8 rates[MAX_RATES_LENGTH]; u8 rates_len; @@ -706,7 +707,7 @@ struct ieee80211_network { size_t wpa_ie_len; u8 rsn_ie[MAX_WPA_IE_LEN]; size_t rsn_ie_len; - struct ieee80211_tim_parameters tim; + struct libipw_tim_parameters tim; /* 802.11h info */ @@ -714,86 +715,89 @@ struct ieee80211_network { u8 power_constraint; /* TPC Report - mandatory if spctrm mgmt required */ - struct ieee80211_tpc_report tpc_report; + struct libipw_tpc_report tpc_report; /* IBSS DFS - mandatory if spctrm mgmt required and IBSS * NOTE: This is variable length and so must be allocated dynamically */ - struct ieee80211_ibss_dfs *ibss_dfs; + struct libipw_ibss_dfs *ibss_dfs; /* Channel Switch Announcement - optional if spctrm mgmt required */ - struct ieee80211_csa csa; + struct libipw_csa csa; /* Quiet - optional if spctrm mgmt required */ - struct ieee80211_quiet quiet; + struct libipw_quiet quiet; struct list_head list; }; -enum ieee80211_state { - IEEE80211_UNINITIALIZED = 0, - IEEE80211_INITIALIZED, - IEEE80211_ASSOCIATING, - IEEE80211_ASSOCIATED, - IEEE80211_AUTHENTICATING, - IEEE80211_AUTHENTICATED, - IEEE80211_SHUTDOWN +enum libipw_state { + LIBIPW_UNINITIALIZED = 0, + LIBIPW_INITIALIZED, + LIBIPW_ASSOCIATING, + LIBIPW_ASSOCIATED, + LIBIPW_AUTHENTICATING, + LIBIPW_AUTHENTICATED, + LIBIPW_SHUTDOWN }; #define DEFAULT_MAX_SCAN_AGE (15 * HZ) #define DEFAULT_FTS 2346 -#define CFG_IEEE80211_RESERVE_FCS (1<<0) -#define CFG_IEEE80211_COMPUTE_FCS (1<<1) -#define CFG_IEEE80211_RTS (1<<2) +#define CFG_LIBIPW_RESERVE_FCS (1<<0) +#define CFG_LIBIPW_COMPUTE_FCS (1<<1) +#define CFG_LIBIPW_RTS (1<<2) -#define IEEE80211_24GHZ_MIN_CHANNEL 1 -#define IEEE80211_24GHZ_MAX_CHANNEL 14 -#define IEEE80211_24GHZ_CHANNELS (IEEE80211_24GHZ_MAX_CHANNEL - \ - IEEE80211_24GHZ_MIN_CHANNEL + 1) +#define LIBIPW_24GHZ_MIN_CHANNEL 1 +#define LIBIPW_24GHZ_MAX_CHANNEL 14 +#define LIBIPW_24GHZ_CHANNELS (LIBIPW_24GHZ_MAX_CHANNEL - \ + LIBIPW_24GHZ_MIN_CHANNEL + 1) -#define IEEE80211_52GHZ_MIN_CHANNEL 34 -#define IEEE80211_52GHZ_MAX_CHANNEL 165 -#define IEEE80211_52GHZ_CHANNELS (IEEE80211_52GHZ_MAX_CHANNEL - \ - IEEE80211_52GHZ_MIN_CHANNEL + 1) +#define LIBIPW_52GHZ_MIN_CHANNEL 34 +#define LIBIPW_52GHZ_MAX_CHANNEL 165 +#define LIBIPW_52GHZ_CHANNELS (LIBIPW_52GHZ_MAX_CHANNEL - \ + LIBIPW_52GHZ_MIN_CHANNEL + 1) enum { - IEEE80211_CH_PASSIVE_ONLY = (1 << 0), - IEEE80211_CH_80211H_RULES = (1 << 1), - IEEE80211_CH_B_ONLY = (1 << 2), - IEEE80211_CH_NO_IBSS = (1 << 3), - IEEE80211_CH_UNIFORM_SPREADING = (1 << 4), - IEEE80211_CH_RADAR_DETECT = (1 << 5), - IEEE80211_CH_INVALID = (1 << 6), + LIBIPW_CH_PASSIVE_ONLY = (1 << 0), + LIBIPW_CH_80211H_RULES = (1 << 1), + LIBIPW_CH_B_ONLY = (1 << 2), + LIBIPW_CH_NO_IBSS = (1 << 3), + LIBIPW_CH_UNIFORM_SPREADING = (1 << 4), + LIBIPW_CH_RADAR_DETECT = (1 << 5), + LIBIPW_CH_INVALID = (1 << 6), }; -struct ieee80211_channel { +struct libipw_channel { u32 freq; /* in MHz */ u8 channel; u8 flags; u8 max_power; /* in dBm */ }; -struct ieee80211_geo { +struct libipw_geo { u8 name[4]; u8 bg_channels; u8 a_channels; - struct ieee80211_channel bg[IEEE80211_24GHZ_CHANNELS]; - struct ieee80211_channel a[IEEE80211_52GHZ_CHANNELS]; + struct libipw_channel bg[LIBIPW_24GHZ_CHANNELS]; + struct libipw_channel a[LIBIPW_52GHZ_CHANNELS]; }; -struct ieee80211_device { +struct libipw_device { struct net_device *dev; - struct ieee80211_security sec; + struct wireless_dev wdev; + struct libipw_security sec; /* Bookkeeping structures */ - struct ieee80211_stats ieee_stats; + struct libipw_stats ieee_stats; - struct ieee80211_geo geo; + struct libipw_geo geo; + struct ieee80211_supported_band bg_band; + struct ieee80211_supported_band a_band; /* Probe / Beacon management */ struct list_head network_free_list; struct list_head network_list; - struct ieee80211_network *networks; + struct libipw_network *networks; int scans; int scan_age; @@ -840,7 +844,7 @@ struct ieee80211_device { * with RX of broad/multicast frames */ /* Fragmentation structures */ - struct ieee80211_frag_entry frag_cache[IEEE80211_FRAG_CACHE_LEN]; + struct libipw_frag_entry frag_cache[LIBIPW_FRAG_CACHE_LEN]; unsigned int frag_next_idx; u16 fts; /* Fragmentation Threshold */ u16 rts; /* RTS threshold */ @@ -848,7 +852,7 @@ struct ieee80211_device { /* Association info */ u8 bssid[ETH_ALEN]; - enum ieee80211_state state; + enum libipw_state state; int mode; /* A, B, G */ int modulation; /* CCK, OFDM */ @@ -862,43 +866,43 @@ struct ieee80211_device { /* Callback functions */ void (*set_security) (struct net_device * dev, - struct ieee80211_security * sec); - int (*hard_start_xmit) (struct ieee80211_txb * txb, - struct net_device * dev, int pri); + struct libipw_security * sec); + netdev_tx_t (*hard_start_xmit) (struct libipw_txb * txb, + struct net_device * dev, int pri); int (*reset_port) (struct net_device * dev); int (*is_queue_full) (struct net_device * dev, int pri); int (*handle_management) (struct net_device * dev, - struct ieee80211_network * network, u16 type); + struct libipw_network * network, u16 type); int (*is_qos_active) (struct net_device *dev, struct sk_buff *skb); /* Typical STA methods */ int (*handle_auth) (struct net_device * dev, - struct ieee80211_auth * auth); + struct libipw_auth * auth); int (*handle_deauth) (struct net_device * dev, - struct ieee80211_deauth * auth); + struct libipw_deauth * auth); int (*handle_action) (struct net_device * dev, - struct ieee80211_action * action, - struct ieee80211_rx_stats * stats); + struct libipw_action * action, + struct libipw_rx_stats * stats); int (*handle_disassoc) (struct net_device * dev, - struct ieee80211_disassoc * assoc); + struct libipw_disassoc * assoc); int (*handle_beacon) (struct net_device * dev, - struct ieee80211_beacon * beacon, - struct ieee80211_network * network); + struct libipw_beacon * beacon, + struct libipw_network * network); int (*handle_probe_response) (struct net_device * dev, - struct ieee80211_probe_response * resp, - struct ieee80211_network * network); + struct libipw_probe_response * resp, + struct libipw_network * network); int (*handle_probe_request) (struct net_device * dev, - struct ieee80211_probe_request * req, - struct ieee80211_rx_stats * stats); + struct libipw_probe_request * req, + struct libipw_rx_stats * stats); int (*handle_assoc_response) (struct net_device * dev, - struct ieee80211_assoc_response * resp, - struct ieee80211_network * network); + struct libipw_assoc_response * resp, + struct libipw_network * network); /* Typical AP methods */ int (*handle_assoc_request) (struct net_device * dev); int (*handle_reassoc_request) (struct net_device * dev, - struct ieee80211_reassoc_request * req); + struct libipw_reassoc_request * req); /* This must be the last item so that it points to the data * allocated beyond this structure by alloc_ieee80211 */ @@ -910,12 +914,12 @@ struct ieee80211_device { #define IEEE_G (1<<2) #define IEEE_MODE_MASK (IEEE_A|IEEE_B|IEEE_G) -static inline void *ieee80211_priv(struct net_device *dev) +static inline void *libipw_priv(struct net_device *dev) { - return ((struct ieee80211_device *)netdev_priv(dev))->priv; + return ((struct libipw_device *)netdev_priv(dev))->priv; } -static inline int ieee80211_is_valid_mode(struct ieee80211_device *ieee, +static inline int libipw_is_valid_mode(struct libipw_device *ieee, int mode) { /* @@ -925,32 +929,32 @@ static inline int ieee80211_is_valid_mode(struct ieee80211_device *ieee, * */ if ((mode & IEEE_A) && - (ieee->modulation & IEEE80211_OFDM_MODULATION) && - (ieee->freq_band & IEEE80211_52GHZ_BAND)) + (ieee->modulation & LIBIPW_OFDM_MODULATION) && + (ieee->freq_band & LIBIPW_52GHZ_BAND)) return 1; if ((mode & IEEE_G) && - (ieee->modulation & IEEE80211_OFDM_MODULATION) && - (ieee->freq_band & IEEE80211_24GHZ_BAND)) + (ieee->modulation & LIBIPW_OFDM_MODULATION) && + (ieee->freq_band & LIBIPW_24GHZ_BAND)) return 1; if ((mode & IEEE_B) && - (ieee->modulation & IEEE80211_CCK_MODULATION) && - (ieee->freq_band & IEEE80211_24GHZ_BAND)) + (ieee->modulation & LIBIPW_CCK_MODULATION) && + (ieee->freq_band & LIBIPW_24GHZ_BAND)) return 1; return 0; } -static inline int ieee80211_get_hdrlen(u16 fc) +static inline int libipw_get_hdrlen(u16 fc) { - int hdrlen = IEEE80211_3ADDR_LEN; + int hdrlen = LIBIPW_3ADDR_LEN; u16 stype = WLAN_FC_GET_STYPE(fc); switch (WLAN_FC_GET_TYPE(fc)) { case IEEE80211_FTYPE_DATA: if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS)) - hdrlen = IEEE80211_4ADDR_LEN; + hdrlen = LIBIPW_4ADDR_LEN; if (stype & IEEE80211_STYPE_QOS_DATA) hdrlen += 2; break; @@ -958,10 +962,10 @@ static inline int ieee80211_get_hdrlen(u16 fc) switch (WLAN_FC_GET_STYPE(fc)) { case IEEE80211_STYPE_CTS: case IEEE80211_STYPE_ACK: - hdrlen = IEEE80211_1ADDR_LEN; + hdrlen = LIBIPW_1ADDR_LEN; break; default: - hdrlen = IEEE80211_2ADDR_LEN; + hdrlen = LIBIPW_2ADDR_LEN; break; } break; @@ -970,118 +974,119 @@ static inline int ieee80211_get_hdrlen(u16 fc) return hdrlen; } -static inline u8 *ieee80211_get_payload(struct ieee80211_hdr *hdr) +static inline u8 *libipw_get_payload(struct ieee80211_hdr *hdr) { - switch (ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_control))) { - case IEEE80211_1ADDR_LEN: - return ((struct ieee80211_hdr_1addr *)hdr)->payload; - case IEEE80211_2ADDR_LEN: - return ((struct ieee80211_hdr_2addr *)hdr)->payload; - case IEEE80211_3ADDR_LEN: - return ((struct ieee80211_hdr_3addr *)hdr)->payload; - case IEEE80211_4ADDR_LEN: - return ((struct ieee80211_hdr_4addr *)hdr)->payload; + switch (libipw_get_hdrlen(le16_to_cpu(hdr->frame_control))) { + case LIBIPW_1ADDR_LEN: + return ((struct libipw_hdr_1addr *)hdr)->payload; + case LIBIPW_2ADDR_LEN: + return ((struct libipw_hdr_2addr *)hdr)->payload; + case LIBIPW_3ADDR_LEN: + return ((struct libipw_hdr_3addr *)hdr)->payload; + case LIBIPW_4ADDR_LEN: + return ((struct libipw_hdr_4addr *)hdr)->payload; } return NULL; } -static inline int ieee80211_is_ofdm_rate(u8 rate) +static inline int libipw_is_ofdm_rate(u8 rate) { - switch (rate & ~IEEE80211_BASIC_RATE_MASK) { - case IEEE80211_OFDM_RATE_6MB: - case IEEE80211_OFDM_RATE_9MB: - case IEEE80211_OFDM_RATE_12MB: - case IEEE80211_OFDM_RATE_18MB: - case IEEE80211_OFDM_RATE_24MB: - case IEEE80211_OFDM_RATE_36MB: - case IEEE80211_OFDM_RATE_48MB: - case IEEE80211_OFDM_RATE_54MB: + switch (rate & ~LIBIPW_BASIC_RATE_MASK) { + case LIBIPW_OFDM_RATE_6MB: + case LIBIPW_OFDM_RATE_9MB: + case LIBIPW_OFDM_RATE_12MB: + case LIBIPW_OFDM_RATE_18MB: + case LIBIPW_OFDM_RATE_24MB: + case LIBIPW_OFDM_RATE_36MB: + case LIBIPW_OFDM_RATE_48MB: + case LIBIPW_OFDM_RATE_54MB: return 1; } return 0; } -static inline int ieee80211_is_cck_rate(u8 rate) +static inline int libipw_is_cck_rate(u8 rate) { - switch (rate & ~IEEE80211_BASIC_RATE_MASK) { - case IEEE80211_CCK_RATE_1MB: - case IEEE80211_CCK_RATE_2MB: - case IEEE80211_CCK_RATE_5MB: - case IEEE80211_CCK_RATE_11MB: + switch (rate & ~LIBIPW_BASIC_RATE_MASK) { + case LIBIPW_CCK_RATE_1MB: + case LIBIPW_CCK_RATE_2MB: + case LIBIPW_CCK_RATE_5MB: + case LIBIPW_CCK_RATE_11MB: return 1; } return 0; } /* ieee80211.c */ -extern void free_ieee80211(struct net_device *dev); -extern struct net_device *alloc_ieee80211(int sizeof_priv); -extern int ieee80211_change_mtu(struct net_device *dev, int new_mtu); +extern void free_ieee80211(struct net_device *dev, int monitor); +extern struct net_device *alloc_ieee80211(int sizeof_priv, int monitor); +extern int libipw_change_mtu(struct net_device *dev, int new_mtu); -extern void ieee80211_networks_age(struct ieee80211_device *ieee, +extern void libipw_networks_age(struct libipw_device *ieee, unsigned long age_secs); -extern int ieee80211_set_encryption(struct ieee80211_device *ieee); +extern int libipw_set_encryption(struct libipw_device *ieee); -/* ieee80211_tx.c */ -extern int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev); -extern void ieee80211_txb_free(struct ieee80211_txb *); +/* libipw_tx.c */ +extern netdev_tx_t libipw_xmit(struct sk_buff *skb, + struct net_device *dev); +extern void libipw_txb_free(struct libipw_txb *); -/* ieee80211_rx.c */ -extern void ieee80211_rx_any(struct ieee80211_device *ieee, - struct sk_buff *skb, struct ieee80211_rx_stats *stats); -extern int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, - struct ieee80211_rx_stats *rx_stats); +/* libipw_rx.c */ +extern void libipw_rx_any(struct libipw_device *ieee, + struct sk_buff *skb, struct libipw_rx_stats *stats); +extern int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, + struct libipw_rx_stats *rx_stats); /* make sure to set stats->len */ -extern void ieee80211_rx_mgt(struct ieee80211_device *ieee, - struct ieee80211_hdr_4addr *header, - struct ieee80211_rx_stats *stats); -extern void ieee80211_network_reset(struct ieee80211_network *network); +extern void libipw_rx_mgt(struct libipw_device *ieee, + struct libipw_hdr_4addr *header, + struct libipw_rx_stats *stats); +extern void libipw_network_reset(struct libipw_network *network); -/* ieee80211_geo.c */ -extern const struct ieee80211_geo *ieee80211_get_geo(struct ieee80211_device +/* libipw_geo.c */ +extern const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee); -extern int ieee80211_set_geo(struct ieee80211_device *ieee, - const struct ieee80211_geo *geo); +extern int libipw_set_geo(struct libipw_device *ieee, + const struct libipw_geo *geo); -extern int ieee80211_is_valid_channel(struct ieee80211_device *ieee, +extern int libipw_is_valid_channel(struct libipw_device *ieee, u8 channel); -extern int ieee80211_channel_to_index(struct ieee80211_device *ieee, +extern int libipw_channel_to_index(struct libipw_device *ieee, u8 channel); -extern u8 ieee80211_freq_to_channel(struct ieee80211_device *ieee, u32 freq); -extern u8 ieee80211_get_channel_flags(struct ieee80211_device *ieee, +extern u8 libipw_freq_to_channel(struct libipw_device *ieee, u32 freq); +extern u8 libipw_get_channel_flags(struct libipw_device *ieee, u8 channel); -extern const struct ieee80211_channel *ieee80211_get_channel(struct - ieee80211_device +extern const struct libipw_channel *libipw_get_channel(struct + libipw_device *ieee, u8 channel); -extern u32 ieee80211_channel_to_freq(struct ieee80211_device * ieee, +extern u32 libipw_channel_to_freq(struct libipw_device * ieee, u8 channel); -/* ieee80211_wx.c */ -extern int ieee80211_wx_get_scan(struct ieee80211_device *ieee, +/* libipw_wx.c */ +extern int libipw_wx_get_scan(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *key); -extern int ieee80211_wx_set_encode(struct ieee80211_device *ieee, +extern int libipw_wx_set_encode(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *key); -extern int ieee80211_wx_get_encode(struct ieee80211_device *ieee, +extern int libipw_wx_get_encode(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *key); -extern int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee, +extern int libipw_wx_set_encodeext(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); -extern int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee, +extern int libipw_wx_get_encodeext(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *extra); -static inline void ieee80211_increment_scans(struct ieee80211_device *ieee) +static inline void libipw_increment_scans(struct libipw_device *ieee) { ieee->scans++; } -static inline int ieee80211_get_scans(struct ieee80211_device *ieee) +static inline int libipw_get_scans(struct libipw_device *ieee) { return ieee->scans; } -#endif /* IEEE80211_H */ +#endif /* LIBIPW_H */ diff --git a/drivers/net/wireless/ipw2x00/libipw_geo.c b/drivers/net/wireless/ipw2x00/libipw_geo.c index 9dfbb8760f67..65e8c175a4a0 100644 --- a/drivers/net/wireless/ipw2x00/libipw_geo.c +++ b/drivers/net/wireless/ipw2x00/libipw_geo.c @@ -19,7 +19,7 @@ file called LICENSE. Contact Information: - James P. Ketrenos <ipw2100-admin@linux.intel.com> + Intel Linux Wireless <ilw@linux.intel.com> Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ******************************************************************************/ @@ -41,9 +41,9 @@ #include <linux/etherdevice.h> #include <asm/uaccess.h> -#include "ieee80211.h" +#include "libipw.h" -int ieee80211_is_valid_channel(struct ieee80211_device *ieee, u8 channel) +int libipw_is_valid_channel(struct libipw_device *ieee, u8 channel) { int i; @@ -52,27 +52,27 @@ int ieee80211_is_valid_channel(struct ieee80211_device *ieee, u8 channel) if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) return 0; - if (ieee->freq_band & IEEE80211_24GHZ_BAND) + if (ieee->freq_band & LIBIPW_24GHZ_BAND) for (i = 0; i < ieee->geo.bg_channels; i++) /* NOTE: If G mode is currently supported but * this is a B only channel, we don't see it * as valid. */ if ((ieee->geo.bg[i].channel == channel) && - !(ieee->geo.bg[i].flags & IEEE80211_CH_INVALID) && + !(ieee->geo.bg[i].flags & LIBIPW_CH_INVALID) && (!(ieee->mode & IEEE_G) || - !(ieee->geo.bg[i].flags & IEEE80211_CH_B_ONLY))) - return IEEE80211_24GHZ_BAND; + !(ieee->geo.bg[i].flags & LIBIPW_CH_B_ONLY))) + return LIBIPW_24GHZ_BAND; - if (ieee->freq_band & IEEE80211_52GHZ_BAND) + if (ieee->freq_band & LIBIPW_52GHZ_BAND) for (i = 0; i < ieee->geo.a_channels; i++) if ((ieee->geo.a[i].channel == channel) && - !(ieee->geo.a[i].flags & IEEE80211_CH_INVALID)) - return IEEE80211_52GHZ_BAND; + !(ieee->geo.a[i].flags & LIBIPW_CH_INVALID)) + return LIBIPW_52GHZ_BAND; return 0; } -int ieee80211_channel_to_index(struct ieee80211_device *ieee, u8 channel) +int libipw_channel_to_index(struct libipw_device *ieee, u8 channel) { int i; @@ -81,12 +81,12 @@ int ieee80211_channel_to_index(struct ieee80211_device *ieee, u8 channel) if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) return -1; - if (ieee->freq_band & IEEE80211_24GHZ_BAND) + if (ieee->freq_band & LIBIPW_24GHZ_BAND) for (i = 0; i < ieee->geo.bg_channels; i++) if (ieee->geo.bg[i].channel == channel) return i; - if (ieee->freq_band & IEEE80211_52GHZ_BAND) + if (ieee->freq_band & LIBIPW_52GHZ_BAND) for (i = 0; i < ieee->geo.a_channels; i++) if (ieee->geo.a[i].channel == channel) return i; @@ -94,22 +94,22 @@ int ieee80211_channel_to_index(struct ieee80211_device *ieee, u8 channel) return -1; } -u32 ieee80211_channel_to_freq(struct ieee80211_device * ieee, u8 channel) +u32 libipw_channel_to_freq(struct libipw_device * ieee, u8 channel) { - const struct ieee80211_channel * ch; + const struct libipw_channel * ch; /* Driver needs to initialize the geography map before using * these helper functions */ if (ieee->geo.bg_channels == 0 && ieee->geo.a_channels == 0) return 0; - ch = ieee80211_get_channel(ieee, channel); + ch = libipw_get_channel(ieee, channel); if (!ch->channel) return 0; return ch->freq; } -u8 ieee80211_freq_to_channel(struct ieee80211_device * ieee, u32 freq) +u8 libipw_freq_to_channel(struct libipw_device * ieee, u32 freq) { int i; @@ -120,12 +120,12 @@ u8 ieee80211_freq_to_channel(struct ieee80211_device * ieee, u32 freq) freq /= 100000; - if (ieee->freq_band & IEEE80211_24GHZ_BAND) + if (ieee->freq_band & LIBIPW_24GHZ_BAND) for (i = 0; i < ieee->geo.bg_channels; i++) if (ieee->geo.bg[i].freq == freq) return ieee->geo.bg[i].channel; - if (ieee->freq_band & IEEE80211_52GHZ_BAND) + if (ieee->freq_band & LIBIPW_52GHZ_BAND) for (i = 0; i < ieee->geo.a_channels; i++) if (ieee->geo.a[i].freq == freq) return ieee->geo.a[i].channel; @@ -133,63 +133,63 @@ u8 ieee80211_freq_to_channel(struct ieee80211_device * ieee, u32 freq) return 0; } -int ieee80211_set_geo(struct ieee80211_device *ieee, - const struct ieee80211_geo *geo) +int libipw_set_geo(struct libipw_device *ieee, + const struct libipw_geo *geo) { memcpy(ieee->geo.name, geo->name, 3); ieee->geo.name[3] = '\0'; ieee->geo.bg_channels = geo->bg_channels; ieee->geo.a_channels = geo->a_channels; memcpy(ieee->geo.bg, geo->bg, geo->bg_channels * - sizeof(struct ieee80211_channel)); + sizeof(struct libipw_channel)); memcpy(ieee->geo.a, geo->a, ieee->geo.a_channels * - sizeof(struct ieee80211_channel)); + sizeof(struct libipw_channel)); return 0; } -const struct ieee80211_geo *ieee80211_get_geo(struct ieee80211_device *ieee) +const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee) { return &ieee->geo; } -u8 ieee80211_get_channel_flags(struct ieee80211_device * ieee, u8 channel) +u8 libipw_get_channel_flags(struct libipw_device * ieee, u8 channel) { - int index = ieee80211_channel_to_index(ieee, channel); + int index = libipw_channel_to_index(ieee, channel); if (index == -1) - return IEEE80211_CH_INVALID; + return LIBIPW_CH_INVALID; - if (channel <= IEEE80211_24GHZ_CHANNELS) + if (channel <= LIBIPW_24GHZ_CHANNELS) return ieee->geo.bg[index].flags; return ieee->geo.a[index].flags; } -static const struct ieee80211_channel bad_channel = { +static const struct libipw_channel bad_channel = { .channel = 0, - .flags = IEEE80211_CH_INVALID, + .flags = LIBIPW_CH_INVALID, .max_power = 0, }; -const struct ieee80211_channel *ieee80211_get_channel(struct ieee80211_device +const struct libipw_channel *libipw_get_channel(struct libipw_device *ieee, u8 channel) { - int index = ieee80211_channel_to_index(ieee, channel); + int index = libipw_channel_to_index(ieee, channel); if (index == -1) return &bad_channel; - if (channel <= IEEE80211_24GHZ_CHANNELS) + if (channel <= LIBIPW_24GHZ_CHANNELS) return &ieee->geo.bg[index]; return &ieee->geo.a[index]; } -EXPORT_SYMBOL(ieee80211_get_channel); -EXPORT_SYMBOL(ieee80211_get_channel_flags); -EXPORT_SYMBOL(ieee80211_is_valid_channel); -EXPORT_SYMBOL(ieee80211_freq_to_channel); -EXPORT_SYMBOL(ieee80211_channel_to_freq); -EXPORT_SYMBOL(ieee80211_channel_to_index); -EXPORT_SYMBOL(ieee80211_set_geo); -EXPORT_SYMBOL(ieee80211_get_geo); +EXPORT_SYMBOL(libipw_get_channel); +EXPORT_SYMBOL(libipw_get_channel_flags); +EXPORT_SYMBOL(libipw_is_valid_channel); +EXPORT_SYMBOL(libipw_freq_to_channel); +EXPORT_SYMBOL(libipw_channel_to_freq); +EXPORT_SYMBOL(libipw_channel_to_index); +EXPORT_SYMBOL(libipw_set_geo); +EXPORT_SYMBOL(libipw_get_geo); diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/ipw2x00/libipw_module.c index 8ce6e961c5da..a0e9f6aed7da 100644 --- a/drivers/net/wireless/ipw2x00/libipw_module.c +++ b/drivers/net/wireless/ipw2x00/libipw_module.c @@ -25,7 +25,7 @@ file called LICENSE. Contact Information: - James P. Ketrenos <ipw2100-admin@linux.intel.com> + Intel Linux Wireless <ilw@linux.intel.com> Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 *******************************************************************************/ @@ -50,11 +50,11 @@ #include <net/net_namespace.h> #include <net/arp.h> -#include "ieee80211.h" +#include "libipw.h" #define DRV_DESCRIPTION "802.11 data/management/control stack" #define DRV_NAME "ieee80211" -#define DRV_VERSION IEEE80211_VERSION +#define DRV_VERSION LIBIPW_VERSION #define DRV_COPYRIGHT "Copyright (C) 2004-2005 Intel Corporation <jketreno@linux.intel.com>" MODULE_VERSION(DRV_VERSION); @@ -62,13 +62,16 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_AUTHOR(DRV_COPYRIGHT); MODULE_LICENSE("GPL"); -static int ieee80211_networks_allocate(struct ieee80211_device *ieee) +struct cfg80211_ops libipw_config_ops = { }; +void *libipw_wiphy_privid = &libipw_wiphy_privid; + +static int libipw_networks_allocate(struct libipw_device *ieee) { if (ieee->networks) return 0; ieee->networks = - kzalloc(MAX_NETWORK_COUNT * sizeof(struct ieee80211_network), + kzalloc(MAX_NETWORK_COUNT * sizeof(struct libipw_network), GFP_KERNEL); if (!ieee->networks) { printk(KERN_WARNING "%s: Out of memory allocating beacons\n", @@ -79,7 +82,7 @@ static int ieee80211_networks_allocate(struct ieee80211_device *ieee) return 0; } -void ieee80211_network_reset(struct ieee80211_network *network) +void libipw_network_reset(struct libipw_network *network) { if (!network) return; @@ -90,7 +93,7 @@ void ieee80211_network_reset(struct ieee80211_network *network) } } -static inline void ieee80211_networks_free(struct ieee80211_device *ieee) +static inline void libipw_networks_free(struct libipw_device *ieee) { int i; @@ -105,10 +108,10 @@ static inline void ieee80211_networks_free(struct ieee80211_device *ieee) ieee->networks = NULL; } -void ieee80211_networks_age(struct ieee80211_device *ieee, +void libipw_networks_age(struct libipw_device *ieee, unsigned long age_secs) { - struct ieee80211_network *network = NULL; + struct libipw_network *network = NULL; unsigned long flags; unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC); @@ -118,9 +121,9 @@ void ieee80211_networks_age(struct ieee80211_device *ieee, } spin_unlock_irqrestore(&ieee->lock, flags); } -EXPORT_SYMBOL(ieee80211_networks_age); +EXPORT_SYMBOL(libipw_networks_age); -static void ieee80211_networks_initialize(struct ieee80211_device *ieee) +static void libipw_networks_initialize(struct libipw_device *ieee) { int i; @@ -131,38 +134,59 @@ static void ieee80211_networks_initialize(struct ieee80211_device *ieee) &ieee->network_free_list); } -int ieee80211_change_mtu(struct net_device *dev, int new_mtu) +int libipw_change_mtu(struct net_device *dev, int new_mtu) { - if ((new_mtu < 68) || (new_mtu > IEEE80211_DATA_LEN)) + if ((new_mtu < 68) || (new_mtu > LIBIPW_DATA_LEN)) return -EINVAL; dev->mtu = new_mtu; return 0; } -EXPORT_SYMBOL(ieee80211_change_mtu); +EXPORT_SYMBOL(libipw_change_mtu); -struct net_device *alloc_ieee80211(int sizeof_priv) +struct net_device *alloc_ieee80211(int sizeof_priv, int monitor) { - struct ieee80211_device *ieee; + struct libipw_device *ieee; struct net_device *dev; int err; - IEEE80211_DEBUG_INFO("Initializing...\n"); + LIBIPW_DEBUG_INFO("Initializing...\n"); - dev = alloc_etherdev(sizeof(struct ieee80211_device) + sizeof_priv); + dev = alloc_etherdev(sizeof(struct libipw_device) + sizeof_priv); if (!dev) { - IEEE80211_ERROR("Unable to allocate network device.\n"); + LIBIPW_ERROR("Unable to allocate network device.\n"); goto failed; } ieee = netdev_priv(dev); ieee->dev = dev; - err = ieee80211_networks_allocate(ieee); + if (!monitor) { + ieee->wdev.wiphy = wiphy_new(&libipw_config_ops, 0); + if (!ieee->wdev.wiphy) { + LIBIPW_ERROR("Unable to allocate wiphy.\n"); + goto failed_free_netdev; + } + + ieee->dev->ieee80211_ptr = &ieee->wdev; + ieee->wdev.iftype = NL80211_IFTYPE_STATION; + + /* Fill-out wiphy structure bits we know... Not enough info + here to call set_wiphy_dev or set MAC address or channel info + -- have to do that in ->ndo_init... */ + ieee->wdev.wiphy->privid = libipw_wiphy_privid; + + ieee->wdev.wiphy->max_scan_ssids = 1; + ieee->wdev.wiphy->max_scan_ie_len = 0; + ieee->wdev.wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_ADHOC); + } + + err = libipw_networks_allocate(ieee); if (err) { - IEEE80211_ERROR("Unable to allocate beacon storage: %d\n", err); - goto failed_free_netdev; + LIBIPW_ERROR("Unable to allocate beacon storage: %d\n", err); + goto failed_free_wiphy; } - ieee80211_networks_initialize(ieee); + libipw_networks_initialize(ieee); /* Default fragmentation threshold is maximum payload size */ ieee->fts = DEFAULT_FTS; @@ -193,33 +217,45 @@ struct net_device *alloc_ieee80211(int sizeof_priv) return dev; +failed_free_wiphy: + if (!monitor) + wiphy_free(ieee->wdev.wiphy); failed_free_netdev: free_netdev(dev); failed: return NULL; } -void free_ieee80211(struct net_device *dev) +void free_ieee80211(struct net_device *dev, int monitor) { - struct ieee80211_device *ieee = netdev_priv(dev); + struct libipw_device *ieee = netdev_priv(dev); lib80211_crypt_info_free(&ieee->crypt_info); - ieee80211_networks_free(ieee); + libipw_networks_free(ieee); + + /* free cfg80211 resources */ + if (!monitor) { + wiphy_unregister(ieee->wdev.wiphy); + kfree(ieee->a_band.channels); + kfree(ieee->bg_band.channels); + wiphy_free(ieee->wdev.wiphy); + } + free_netdev(dev); } #ifdef CONFIG_LIBIPW_DEBUG static int debug = 0; -u32 ieee80211_debug_level = 0; -EXPORT_SYMBOL_GPL(ieee80211_debug_level); -static struct proc_dir_entry *ieee80211_proc = NULL; +u32 libipw_debug_level = 0; +EXPORT_SYMBOL_GPL(libipw_debug_level); +static struct proc_dir_entry *libipw_proc = NULL; static int show_debug_level(char *page, char **start, off_t offset, int count, int *eof, void *data) { - return snprintf(page, count, "0x%08X\n", ieee80211_debug_level); + return snprintf(page, count, "0x%08X\n", libipw_debug_level); } static int store_debug_level(struct file *file, const char __user * buffer, @@ -236,29 +272,29 @@ static int store_debug_level(struct file *file, const char __user * buffer, printk(KERN_INFO DRV_NAME ": %s is not in hex or decimal form.\n", buf); else - ieee80211_debug_level = val; + libipw_debug_level = val; return strnlen(buf, len); } #endif /* CONFIG_LIBIPW_DEBUG */ -static int __init ieee80211_init(void) +static int __init libipw_init(void) { #ifdef CONFIG_LIBIPW_DEBUG struct proc_dir_entry *e; - ieee80211_debug_level = debug; - ieee80211_proc = proc_mkdir(DRV_NAME, init_net.proc_net); - if (ieee80211_proc == NULL) { - IEEE80211_ERROR("Unable to create " DRV_NAME + libipw_debug_level = debug; + libipw_proc = proc_mkdir(DRV_NAME, init_net.proc_net); + if (libipw_proc == NULL) { + LIBIPW_ERROR("Unable to create " DRV_NAME " proc directory\n"); return -EIO; } e = create_proc_entry("debug_level", S_IFREG | S_IRUGO | S_IWUSR, - ieee80211_proc); + libipw_proc); if (!e) { remove_proc_entry(DRV_NAME, init_net.proc_net); - ieee80211_proc = NULL; + libipw_proc = NULL; return -EIO; } e->read_proc = show_debug_level; @@ -272,13 +308,13 @@ static int __init ieee80211_init(void) return 0; } -static void __exit ieee80211_exit(void) +static void __exit libipw_exit(void) { #ifdef CONFIG_LIBIPW_DEBUG - if (ieee80211_proc) { - remove_proc_entry("debug_level", ieee80211_proc); + if (libipw_proc) { + remove_proc_entry("debug_level", libipw_proc); remove_proc_entry(DRV_NAME, init_net.proc_net); - ieee80211_proc = NULL; + libipw_proc = NULL; } #endif /* CONFIG_LIBIPW_DEBUG */ } @@ -289,8 +325,8 @@ module_param(debug, int, 0444); MODULE_PARM_DESC(debug, "debug output mask"); #endif /* CONFIG_LIBIPW_DEBUG */ -module_exit(ieee80211_exit); -module_init(ieee80211_init); +module_exit(libipw_exit); +module_init(libipw_init); EXPORT_SYMBOL(alloc_ieee80211); EXPORT_SYMBOL(free_ieee80211); diff --git a/drivers/net/wireless/ipw2x00/libipw_rx.c b/drivers/net/wireless/ipw2x00/libipw_rx.c index dae4b8e4d8e9..282b1f7ff1e9 100644 --- a/drivers/net/wireless/ipw2x00/libipw_rx.c +++ b/drivers/net/wireless/ipw2x00/libipw_rx.c @@ -34,18 +34,18 @@ #include <net/lib80211.h> -#include "ieee80211.h" +#include "libipw.h" -static void ieee80211_monitor_rx(struct ieee80211_device *ieee, +static void libipw_monitor_rx(struct libipw_device *ieee, struct sk_buff *skb, - struct ieee80211_rx_stats *rx_stats) + struct libipw_rx_stats *rx_stats) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; u16 fc = le16_to_cpu(hdr->frame_control); skb->dev = ieee->dev; skb_reset_mac_header(skb); - skb_pull(skb, ieee80211_get_hdrlen(fc)); + skb_pull(skb, libipw_get_hdrlen(fc)); skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_80211_RAW); memset(skb->cb, 0, sizeof(skb->cb)); @@ -53,22 +53,22 @@ static void ieee80211_monitor_rx(struct ieee80211_device *ieee, } /* Called only as a tasklet (software IRQ) */ -static struct ieee80211_frag_entry *ieee80211_frag_cache_find(struct - ieee80211_device +static struct libipw_frag_entry *libipw_frag_cache_find(struct + libipw_device *ieee, unsigned int seq, unsigned int frag, u8 * src, u8 * dst) { - struct ieee80211_frag_entry *entry; + struct libipw_frag_entry *entry; int i; - for (i = 0; i < IEEE80211_FRAG_CACHE_LEN; i++) { + for (i = 0; i < LIBIPW_FRAG_CACHE_LEN; i++) { entry = &ieee->frag_cache[i]; if (entry->skb != NULL && time_after(jiffies, entry->first_frag_time + 2 * HZ)) { - IEEE80211_DEBUG_FRAG("expiring fragment cache entry " + LIBIPW_DEBUG_FRAG("expiring fragment cache entry " "seq=%u last_frag=%u\n", entry->seq, entry->last_frag); dev_kfree_skb_any(entry->skb); @@ -86,13 +86,13 @@ static struct ieee80211_frag_entry *ieee80211_frag_cache_find(struct } /* Called only as a tasklet (software IRQ) */ -static struct sk_buff *ieee80211_frag_cache_get(struct ieee80211_device *ieee, - struct ieee80211_hdr_4addr *hdr) +static struct sk_buff *libipw_frag_cache_get(struct libipw_device *ieee, + struct libipw_hdr_4addr *hdr) { struct sk_buff *skb = NULL; u16 sc; unsigned int frag, seq; - struct ieee80211_frag_entry *entry; + struct libipw_frag_entry *entry; sc = le16_to_cpu(hdr->seq_ctl); frag = WLAN_GET_SEQ_FRAG(sc); @@ -101,7 +101,7 @@ static struct sk_buff *ieee80211_frag_cache_get(struct ieee80211_device *ieee, if (frag == 0) { /* Reserve enough space to fit maximum frame length */ skb = dev_alloc_skb(ieee->dev->mtu + - sizeof(struct ieee80211_hdr_4addr) + + sizeof(struct libipw_hdr_4addr) + 8 /* LLC */ + 2 /* alignment */ + 8 /* WEP */ + ETH_ALEN /* WDS */ ); @@ -110,7 +110,7 @@ static struct sk_buff *ieee80211_frag_cache_get(struct ieee80211_device *ieee, entry = &ieee->frag_cache[ieee->frag_next_idx]; ieee->frag_next_idx++; - if (ieee->frag_next_idx >= IEEE80211_FRAG_CACHE_LEN) + if (ieee->frag_next_idx >= LIBIPW_FRAG_CACHE_LEN) ieee->frag_next_idx = 0; if (entry->skb != NULL) @@ -125,7 +125,7 @@ static struct sk_buff *ieee80211_frag_cache_get(struct ieee80211_device *ieee, } else { /* received a fragment of a frame for which the head fragment * should have already been received */ - entry = ieee80211_frag_cache_find(ieee, seq, frag, hdr->addr2, + entry = libipw_frag_cache_find(ieee, seq, frag, hdr->addr2, hdr->addr1); if (entry != NULL) { entry->last_frag = frag; @@ -137,21 +137,21 @@ static struct sk_buff *ieee80211_frag_cache_get(struct ieee80211_device *ieee, } /* Called only as a tasklet (software IRQ) */ -static int ieee80211_frag_cache_invalidate(struct ieee80211_device *ieee, - struct ieee80211_hdr_4addr *hdr) +static int libipw_frag_cache_invalidate(struct libipw_device *ieee, + struct libipw_hdr_4addr *hdr) { u16 sc; unsigned int seq; - struct ieee80211_frag_entry *entry; + struct libipw_frag_entry *entry; sc = le16_to_cpu(hdr->seq_ctl); seq = WLAN_GET_SEQ_SEQ(sc); - entry = ieee80211_frag_cache_find(ieee, seq, -1, hdr->addr2, + entry = libipw_frag_cache_find(ieee, seq, -1, hdr->addr2, hdr->addr1); if (entry == NULL) { - IEEE80211_DEBUG_FRAG("could not invalidate fragment cache " + LIBIPW_DEBUG_FRAG("could not invalidate fragment cache " "entry (seq=%u)\n", seq); return -1; } @@ -161,14 +161,14 @@ static int ieee80211_frag_cache_invalidate(struct ieee80211_device *ieee, } #ifdef NOT_YET -/* ieee80211_rx_frame_mgtmt +/* libipw_rx_frame_mgtmt * * Responsible for handling management control frames * - * Called by ieee80211_rx */ + * Called by libipw_rx */ static int -ieee80211_rx_frame_mgmt(struct ieee80211_device *ieee, struct sk_buff *skb, - struct ieee80211_rx_stats *rx_stats, u16 type, +libipw_rx_frame_mgmt(struct libipw_device *ieee, struct sk_buff *skb, + struct libipw_rx_stats *rx_stats, u16 type, u16 stype) { if (ieee->iw_mode == IW_MODE_MASTER) { @@ -176,7 +176,7 @@ ieee80211_rx_frame_mgmt(struct ieee80211_device *ieee, struct sk_buff *skb, ieee->dev->name); return 0; /* - hostap_update_sta_ps(ieee, (struct hostap_ieee80211_hdr_4addr *) + hostap_update_sta_ps(ieee, (struct hostap_libipw_hdr_4addr *) skb->data);*/ } @@ -219,26 +219,27 @@ ieee80211_rx_frame_mgmt(struct ieee80211_device *ieee, struct sk_buff *skb, /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ -static unsigned char rfc1042_header[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; +static unsigned char libipw_rfc1042_header[] = + { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; /* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ -static unsigned char bridge_tunnel_header[] = +static unsigned char libipw_bridge_tunnel_header[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; /* No encapsulation header if EtherType < 0x600 (=length) */ -/* Called by ieee80211_rx_frame_decrypt */ -static int ieee80211_is_eapol_frame(struct ieee80211_device *ieee, +/* Called by libipw_rx_frame_decrypt */ +static int libipw_is_eapol_frame(struct libipw_device *ieee, struct sk_buff *skb) { struct net_device *dev = ieee->dev; u16 fc, ethertype; - struct ieee80211_hdr_3addr *hdr; + struct libipw_hdr_3addr *hdr; u8 *pos; if (skb->len < 24) return 0; - hdr = (struct ieee80211_hdr_3addr *)skb->data; + hdr = (struct libipw_hdr_3addr *)skb->data; fc = le16_to_cpu(hdr->frame_ctl); /* check that the frame is unicast frame to us */ @@ -266,28 +267,28 @@ static int ieee80211_is_eapol_frame(struct ieee80211_device *ieee, return 0; } -/* Called only as a tasklet (software IRQ), by ieee80211_rx */ +/* Called only as a tasklet (software IRQ), by libipw_rx */ static int -ieee80211_rx_frame_decrypt(struct ieee80211_device *ieee, struct sk_buff *skb, +libipw_rx_frame_decrypt(struct libipw_device *ieee, struct sk_buff *skb, struct lib80211_crypt_data *crypt) { - struct ieee80211_hdr_3addr *hdr; + struct libipw_hdr_3addr *hdr; int res, hdrlen; if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) return 0; - hdr = (struct ieee80211_hdr_3addr *)skb->data; - hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); + hdr = (struct libipw_hdr_3addr *)skb->data; + hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); atomic_inc(&crypt->refcnt); res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); atomic_dec(&crypt->refcnt); if (res < 0) { - IEEE80211_DEBUG_DROP("decryption failed (SA=%pM) res=%d\n", + LIBIPW_DEBUG_DROP("decryption failed (SA=%pM) res=%d\n", hdr->addr2, res); if (res == -2) - IEEE80211_DEBUG_DROP("Decryption failed ICV " + LIBIPW_DEBUG_DROP("Decryption failed ICV " "mismatch (key %d)\n", skb->data[hdrlen + 3] >> 6); ieee->ieee_stats.rx_discards_undecryptable++; @@ -297,20 +298,20 @@ ieee80211_rx_frame_decrypt(struct ieee80211_device *ieee, struct sk_buff *skb, return res; } -/* Called only as a tasklet (software IRQ), by ieee80211_rx */ +/* Called only as a tasklet (software IRQ), by libipw_rx */ static int -ieee80211_rx_frame_decrypt_msdu(struct ieee80211_device *ieee, +libipw_rx_frame_decrypt_msdu(struct libipw_device *ieee, struct sk_buff *skb, int keyidx, struct lib80211_crypt_data *crypt) { - struct ieee80211_hdr_3addr *hdr; + struct libipw_hdr_3addr *hdr; int res, hdrlen; if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) return 0; - hdr = (struct ieee80211_hdr_3addr *)skb->data; - hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); + hdr = (struct libipw_hdr_3addr *)skb->data; + hdrlen = libipw_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); atomic_inc(&crypt->refcnt); res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); @@ -328,11 +329,11 @@ ieee80211_rx_frame_decrypt_msdu(struct ieee80211_device *ieee, /* All received frames are sent to this function. @skb contains the frame in * IEEE 802.11 format, i.e., in the format it was sent over air. * This function is called only as a tasklet (software IRQ). */ -int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, - struct ieee80211_rx_stats *rx_stats) +int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, + struct libipw_rx_stats *rx_stats) { struct net_device *dev = ieee->dev; - struct ieee80211_hdr_4addr *hdr; + struct libipw_hdr_4addr *hdr; size_t hdrlen; u16 fc, type, stype, sc; unsigned int frag; @@ -352,7 +353,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, int keyidx = 0; int can_be_decrypted = 0; - hdr = (struct ieee80211_hdr_4addr *)skb->data; + hdr = (struct libipw_hdr_4addr *)skb->data; if (skb->len < 10) { printk(KERN_INFO "%s: SKB length < 10\n", dev->name); goto rx_dropped; @@ -363,7 +364,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, stype = WLAN_FC_GET_STYPE(fc); sc = le16_to_cpu(hdr->seq_ctl); frag = WLAN_GET_SEQ_FRAG(sc); - hdrlen = ieee80211_get_hdrlen(fc); + hdrlen = libipw_get_hdrlen(fc); if (skb->len < hdrlen) { printk(KERN_INFO "%s: invalid SKB length %d\n", @@ -380,19 +381,19 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, struct iw_quality wstats; wstats.updated = 0; - if (rx_stats->mask & IEEE80211_STATMASK_RSSI) { + if (rx_stats->mask & LIBIPW_STATMASK_RSSI) { wstats.level = rx_stats->signal; wstats.updated |= IW_QUAL_LEVEL_UPDATED; } else wstats.updated |= IW_QUAL_LEVEL_INVALID; - if (rx_stats->mask & IEEE80211_STATMASK_NOISE) { + if (rx_stats->mask & LIBIPW_STATMASK_NOISE) { wstats.noise = rx_stats->noise; wstats.updated |= IW_QUAL_NOISE_UPDATED; } else wstats.updated |= IW_QUAL_NOISE_INVALID; - if (rx_stats->mask & IEEE80211_STATMASK_SIGNAL) { + if (rx_stats->mask & LIBIPW_STATMASK_SIGNAL) { wstats.qual = rx_stats->signal; wstats.updated |= IW_QUAL_QUAL_UPDATED; } else @@ -411,7 +412,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, if (ieee->iw_mode == IW_MODE_MONITOR) { dev->stats.rx_packets++; dev->stats.rx_bytes += skb->len; - ieee80211_monitor_rx(ieee, skb, rx_stats); + libipw_monitor_rx(ieee, skb, rx_stats); return 1; } @@ -457,7 +458,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, * frames from other than current BSS, so just drop the * frames silently instead of filling system log with * these reports. */ - IEEE80211_DEBUG_DROP("Decryption failed (not set)" + LIBIPW_DEBUG_DROP("Decryption failed (not set)" " (SA=%pM)\n", hdr->addr2); ieee->ieee_stats.rx_discards_undecryptable++; goto rx_dropped; @@ -475,7 +476,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, goto rx_dropped; } - if (ieee80211_rx_frame_mgmt(ieee, skb, rx_stats, type, stype)) + if (libipw_rx_frame_mgmt(ieee, skb, rx_stats, type, stype)) goto rx_dropped; else goto rx_exit; @@ -488,7 +489,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, ieee->prev_seq_ctl = sc; /* Data frame - extract src/dst addresses */ - if (skb->len < IEEE80211_3ADDR_LEN) + if (skb->len < LIBIPW_3ADDR_LEN) goto rx_dropped; switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { @@ -501,7 +502,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, memcpy(src, hdr->addr2, ETH_ALEN); break; case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: - if (skb->len < IEEE80211_4ADDR_LEN) + if (skb->len < LIBIPW_4ADDR_LEN) goto rx_dropped; memcpy(dst, hdr->addr3, ETH_ALEN); memcpy(src, hdr->addr4, ETH_ALEN); @@ -560,7 +561,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, stype != IEEE80211_STYPE_DATA_CFPOLL && stype != IEEE80211_STYPE_DATA_CFACKPOLL) { if (stype != IEEE80211_STYPE_NULLFUNC) - IEEE80211_DEBUG_DROP("RX: dropped data frame " + LIBIPW_DEBUG_DROP("RX: dropped data frame " "with no data (type=0x%02x, " "subtype=0x%02x, len=%d)\n", type, stype, skb->len); @@ -570,21 +571,21 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted && - (keyidx = ieee80211_rx_frame_decrypt(ieee, skb, crypt)) < 0) + (keyidx = libipw_rx_frame_decrypt(ieee, skb, crypt)) < 0) goto rx_dropped; - hdr = (struct ieee80211_hdr_4addr *)skb->data; + hdr = (struct libipw_hdr_4addr *)skb->data; /* skb: hdr + (possibly fragmented) plaintext payload */ // PR: FIXME: hostap has additional conditions in the "if" below: // ieee->host_decrypt && (fc & IEEE80211_FCTL_PROTECTED) && if ((frag != 0) || (fc & IEEE80211_FCTL_MOREFRAGS)) { int flen; - struct sk_buff *frag_skb = ieee80211_frag_cache_get(ieee, hdr); - IEEE80211_DEBUG_FRAG("Rx Fragment received (%u)\n", frag); + struct sk_buff *frag_skb = libipw_frag_cache_get(ieee, hdr); + LIBIPW_DEBUG_FRAG("Rx Fragment received (%u)\n", frag); if (!frag_skb) { - IEEE80211_DEBUG(IEEE80211_DL_RX | IEEE80211_DL_FRAG, + LIBIPW_DEBUG(LIBIPW_DL_RX | LIBIPW_DL_FRAG, "Rx cannot get skb from fragment " "cache (morefrag=%d seq=%u frag=%u)\n", (fc & IEEE80211_FCTL_MOREFRAGS) != 0, @@ -600,7 +601,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, printk(KERN_WARNING "%s: host decrypted and " "reassembled frame did not fit skb\n", dev->name); - ieee80211_frag_cache_invalidate(ieee, hdr); + libipw_frag_cache_invalidate(ieee, hdr); goto rx_dropped; } @@ -627,24 +628,24 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, /* this was the last fragment and the frame will be * delivered, so remove skb from fragment cache */ skb = frag_skb; - hdr = (struct ieee80211_hdr_4addr *)skb->data; - ieee80211_frag_cache_invalidate(ieee, hdr); + hdr = (struct libipw_hdr_4addr *)skb->data; + libipw_frag_cache_invalidate(ieee, hdr); } /* skb: hdr + (possible reassembled) full MSDU payload; possibly still * encrypted/authenticated */ if ((fc & IEEE80211_FCTL_PROTECTED) && can_be_decrypted && - ieee80211_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt)) + libipw_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt)) goto rx_dropped; - hdr = (struct ieee80211_hdr_4addr *)skb->data; + hdr = (struct libipw_hdr_4addr *)skb->data; if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep) { if ( /*ieee->ieee802_1x && */ - ieee80211_is_eapol_frame(ieee, skb)) { + libipw_is_eapol_frame(ieee, skb)) { /* pass unencrypted EAPOL frames even if encryption is * configured */ } else { - IEEE80211_DEBUG_DROP("encryption configured, but RX " + LIBIPW_DEBUG_DROP("encryption configured, but RX " "frame not encrypted (SA=%pM)\n", hdr->addr2); goto rx_dropped; @@ -652,8 +653,8 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, } if (crypt && !(fc & IEEE80211_FCTL_PROTECTED) && !ieee->open_wep && - !ieee80211_is_eapol_frame(ieee, skb)) { - IEEE80211_DEBUG_DROP("dropped unencrypted RX data " + !libipw_is_eapol_frame(ieee, skb)) { + LIBIPW_DEBUG_DROP("dropped unencrypted RX data " "frame from %pM (drop_unencrypted=1)\n", hdr->addr2); goto rx_dropped; @@ -736,9 +737,9 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, /* convert hdr + possible LLC headers into Ethernet header */ if (skb->len - hdrlen >= 8 && - ((memcmp(payload, rfc1042_header, SNAP_SIZE) == 0 && + ((memcmp(payload, libipw_rfc1042_header, SNAP_SIZE) == 0 && ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || - memcmp(payload, bridge_tunnel_header, SNAP_SIZE) == 0)) { + memcmp(payload, libipw_bridge_tunnel_header, SNAP_SIZE) == 0)) { /* remove RFC1042 or Bridge-Tunnel encapsulation and * replace EtherType */ skb_pull(skb, hdrlen + SNAP_SIZE); @@ -807,7 +808,7 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, /* netif_rx always succeeds, but it might drop * the packet. If it drops the packet, we log that * in our stats. */ - IEEE80211_DEBUG_DROP + LIBIPW_DEBUG_DROP ("RX: netif_rx dropped the packet\n"); dev->stats.rx_dropped++; } @@ -829,18 +830,18 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, return 0; } -/* Filter out unrelated packets, call ieee80211_rx[_mgt] +/* Filter out unrelated packets, call libipw_rx[_mgt] * This function takes over the skb, it should not be used again after calling * this function. */ -void ieee80211_rx_any(struct ieee80211_device *ieee, - struct sk_buff *skb, struct ieee80211_rx_stats *stats) +void libipw_rx_any(struct libipw_device *ieee, + struct sk_buff *skb, struct libipw_rx_stats *stats) { - struct ieee80211_hdr_4addr *hdr; + struct libipw_hdr_4addr *hdr; int is_packet_for_us; u16 fc; if (ieee->iw_mode == IW_MODE_MONITOR) { - if (!ieee80211_rx(ieee, skb, stats)) + if (!libipw_rx(ieee, skb, stats)) dev_kfree_skb_irq(skb); return; } @@ -848,7 +849,7 @@ void ieee80211_rx_any(struct ieee80211_device *ieee, if (skb->len < sizeof(struct ieee80211_hdr)) goto drop_free; - hdr = (struct ieee80211_hdr_4addr *)skb->data; + hdr = (struct libipw_hdr_4addr *)skb->data; fc = le16_to_cpu(hdr->frame_ctl); if ((fc & IEEE80211_FCTL_VERS) != 0) @@ -856,9 +857,9 @@ void ieee80211_rx_any(struct ieee80211_device *ieee, switch (fc & IEEE80211_FCTL_FTYPE) { case IEEE80211_FTYPE_MGMT: - if (skb->len < sizeof(struct ieee80211_hdr_3addr)) + if (skb->len < sizeof(struct libipw_hdr_3addr)) goto drop_free; - ieee80211_rx_mgt(ieee, hdr, stats); + libipw_rx_mgt(ieee, hdr, stats); dev_kfree_skb_irq(skb); return; case IEEE80211_FTYPE_DATA: @@ -910,7 +911,7 @@ void ieee80211_rx_any(struct ieee80211_device *ieee, } if (is_packet_for_us) - if (!ieee80211_rx(ieee, skb, stats)) + if (!libipw_rx(ieee, skb, stats)) dev_kfree_skb_irq(skb); return; @@ -928,7 +929,7 @@ static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 }; * Make ther structure we read from the beacon packet has * the right values */ -static int ieee80211_verify_qos_info(struct ieee80211_qos_information_element +static int libipw_verify_qos_info(struct libipw_qos_information_element *info_element, int sub_type) { @@ -947,12 +948,12 @@ static int ieee80211_verify_qos_info(struct ieee80211_qos_information_element /* * Parse a QoS parameter element */ -static int ieee80211_read_qos_param_element(struct ieee80211_qos_parameter_info - *element_param, struct ieee80211_info_element +static int libipw_read_qos_param_element(struct libipw_qos_parameter_info + *element_param, struct libipw_info_element *info_element) { int ret = 0; - u16 size = sizeof(struct ieee80211_qos_parameter_info) - 2; + u16 size = sizeof(struct libipw_qos_parameter_info) - 2; if ((info_element == NULL) || (element_param == NULL)) return -1; @@ -965,7 +966,7 @@ static int ieee80211_read_qos_param_element(struct ieee80211_qos_parameter_info } else ret = -1; if (ret == 0) - ret = ieee80211_verify_qos_info(&element_param->info_element, + ret = libipw_verify_qos_info(&element_param->info_element, QOS_OUI_PARAM_SUB_TYPE); return ret; } @@ -973,13 +974,13 @@ static int ieee80211_read_qos_param_element(struct ieee80211_qos_parameter_info /* * Parse a QoS information element */ -static int ieee80211_read_qos_info_element(struct - ieee80211_qos_information_element - *element_info, struct ieee80211_info_element +static int libipw_read_qos_info_element(struct + libipw_qos_information_element + *element_info, struct libipw_info_element *info_element) { int ret = 0; - u16 size = sizeof(struct ieee80211_qos_information_element) - 2; + u16 size = sizeof(struct libipw_qos_information_element) - 2; if (element_info == NULL) return -1; @@ -995,7 +996,7 @@ static int ieee80211_read_qos_info_element(struct ret = -1; if (ret == 0) - ret = ieee80211_verify_qos_info(element_info, + ret = libipw_verify_qos_info(element_info, QOS_OUI_INFO_SUB_TYPE); return ret; } @@ -1003,15 +1004,15 @@ static int ieee80211_read_qos_info_element(struct /* * Write QoS parameters from the ac parameters. */ -static int ieee80211_qos_convert_ac_to_parameters(struct - ieee80211_qos_parameter_info +static int libipw_qos_convert_ac_to_parameters(struct + libipw_qos_parameter_info *param_elm, struct - ieee80211_qos_parameters + libipw_qos_parameters *qos_param) { int rc = 0; int i; - struct ieee80211_qos_ac_parameter *ac_params; + struct libipw_qos_ac_parameter *ac_params; u32 txop; u8 cw_min; u8 cw_max; @@ -1042,27 +1043,27 @@ static int ieee80211_qos_convert_ac_to_parameters(struct * parameters element. check the information element length to decide * which type to read */ -static int ieee80211_parse_qos_info_param_IE(struct ieee80211_info_element +static int libipw_parse_qos_info_param_IE(struct libipw_info_element *info_element, - struct ieee80211_network *network) + struct libipw_network *network) { int rc = 0; - struct ieee80211_qos_parameters *qos_param = NULL; - struct ieee80211_qos_information_element qos_info_element; + struct libipw_qos_parameters *qos_param = NULL; + struct libipw_qos_information_element qos_info_element; - rc = ieee80211_read_qos_info_element(&qos_info_element, info_element); + rc = libipw_read_qos_info_element(&qos_info_element, info_element); if (rc == 0) { network->qos_data.param_count = qos_info_element.ac_info & 0x0F; network->flags |= NETWORK_HAS_QOS_INFORMATION; } else { - struct ieee80211_qos_parameter_info param_element; + struct libipw_qos_parameter_info param_element; - rc = ieee80211_read_qos_param_element(¶m_element, + rc = libipw_read_qos_param_element(¶m_element, info_element); if (rc == 0) { qos_param = &(network->qos_data.parameters); - ieee80211_qos_convert_ac_to_parameters(¶m_element, + libipw_qos_convert_ac_to_parameters(¶m_element, qos_param); network->flags |= NETWORK_HAS_QOS_PARAMETERS; network->qos_data.param_count = @@ -1071,7 +1072,7 @@ static int ieee80211_parse_qos_info_param_IE(struct ieee80211_info_element } if (rc == 0) { - IEEE80211_DEBUG_QOS("QoS is supported\n"); + LIBIPW_DEBUG_QOS("QoS is supported\n"); network->qos_data.supported = 1; } return rc; @@ -1116,9 +1117,9 @@ static const char *get_info_element_string(u16 id) } #endif -static int ieee80211_parse_info_param(struct ieee80211_info_element +static int libipw_parse_info_param(struct libipw_info_element *info_element, u16 length, - struct ieee80211_network *network) + struct libipw_network *network) { DECLARE_SSID_BUF(ssid); u8 i; @@ -1129,7 +1130,7 @@ static int ieee80211_parse_info_param(struct ieee80211_info_element while (length >= sizeof(*info_element)) { if (sizeof(*info_element) + info_element->len > length) { - IEEE80211_DEBUG_MGMT("Info elem: parse failed: " + LIBIPW_DEBUG_MGMT("Info elem: parse failed: " "info_element->len + 2 > left : " "info_element->len+2=%zd left=%d, id=%d.\n", info_element->len + @@ -1151,7 +1152,7 @@ static int ieee80211_parse_info_param(struct ieee80211_info_element memset(network->ssid + network->ssid_len, 0, IW_ESSID_MAX_SIZE - network->ssid_len); - IEEE80211_DEBUG_MGMT("WLAN_EID_SSID: '%s' len=%d.\n", + LIBIPW_DEBUG_MGMT("WLAN_EID_SSID: '%s' len=%d.\n", print_ssid(ssid, network->ssid, network->ssid_len), network->ssid_len); @@ -1170,17 +1171,17 @@ static int ieee80211_parse_info_param(struct ieee80211_info_element (p - rates_str), "%02X ", network->rates[i]); #endif - if (ieee80211_is_ofdm_rate + if (libipw_is_ofdm_rate (info_element->data[i])) { network->flags |= NETWORK_HAS_OFDM; if (info_element->data[i] & - IEEE80211_BASIC_RATE_MASK) + LIBIPW_BASIC_RATE_MASK) network->flags &= ~NETWORK_HAS_CCK; } } - IEEE80211_DEBUG_MGMT("WLAN_EID_SUPP_RATES: '%s' (%d)\n", + LIBIPW_DEBUG_MGMT("WLAN_EID_SUPP_RATES: '%s' (%d)\n", rates_str, network->rates_len); break; @@ -1197,61 +1198,61 @@ static int ieee80211_parse_info_param(struct ieee80211_info_element (p - rates_str), "%02X ", network->rates[i]); #endif - if (ieee80211_is_ofdm_rate + if (libipw_is_ofdm_rate (info_element->data[i])) { network->flags |= NETWORK_HAS_OFDM; if (info_element->data[i] & - IEEE80211_BASIC_RATE_MASK) + LIBIPW_BASIC_RATE_MASK) network->flags &= ~NETWORK_HAS_CCK; } } - IEEE80211_DEBUG_MGMT("WLAN_EID_EXT_SUPP_RATES: '%s' (%d)\n", + LIBIPW_DEBUG_MGMT("WLAN_EID_EXT_SUPP_RATES: '%s' (%d)\n", rates_str, network->rates_ex_len); break; case WLAN_EID_DS_PARAMS: - IEEE80211_DEBUG_MGMT("WLAN_EID_DS_PARAMS: %d\n", + LIBIPW_DEBUG_MGMT("WLAN_EID_DS_PARAMS: %d\n", info_element->data[0]); network->channel = info_element->data[0]; break; case WLAN_EID_FH_PARAMS: - IEEE80211_DEBUG_MGMT("WLAN_EID_FH_PARAMS: ignored\n"); + LIBIPW_DEBUG_MGMT("WLAN_EID_FH_PARAMS: ignored\n"); break; case WLAN_EID_CF_PARAMS: - IEEE80211_DEBUG_MGMT("WLAN_EID_CF_PARAMS: ignored\n"); + LIBIPW_DEBUG_MGMT("WLAN_EID_CF_PARAMS: ignored\n"); break; case WLAN_EID_TIM: network->tim.tim_count = info_element->data[0]; network->tim.tim_period = info_element->data[1]; - IEEE80211_DEBUG_MGMT("WLAN_EID_TIM: partially ignored\n"); + LIBIPW_DEBUG_MGMT("WLAN_EID_TIM: partially ignored\n"); break; case WLAN_EID_ERP_INFO: network->erp_value = info_element->data[0]; network->flags |= NETWORK_HAS_ERP_VALUE; - IEEE80211_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n", + LIBIPW_DEBUG_MGMT("MFIE_TYPE_ERP_SET: %d\n", network->erp_value); break; case WLAN_EID_IBSS_PARAMS: network->atim_window = info_element->data[0]; - IEEE80211_DEBUG_MGMT("WLAN_EID_IBSS_PARAMS: %d\n", + LIBIPW_DEBUG_MGMT("WLAN_EID_IBSS_PARAMS: %d\n", network->atim_window); break; case WLAN_EID_CHALLENGE: - IEEE80211_DEBUG_MGMT("WLAN_EID_CHALLENGE: ignored\n"); + LIBIPW_DEBUG_MGMT("WLAN_EID_CHALLENGE: ignored\n"); break; case WLAN_EID_GENERIC: - IEEE80211_DEBUG_MGMT("WLAN_EID_GENERIC: %d bytes\n", + LIBIPW_DEBUG_MGMT("WLAN_EID_GENERIC: %d bytes\n", info_element->len); - if (!ieee80211_parse_qos_info_param_IE(info_element, + if (!libipw_parse_qos_info_param_IE(info_element, network)) break; @@ -1268,7 +1269,7 @@ static int ieee80211_parse_info_param(struct ieee80211_info_element break; case WLAN_EID_RSN: - IEEE80211_DEBUG_MGMT("WLAN_EID_RSN: %d bytes\n", + LIBIPW_DEBUG_MGMT("WLAN_EID_RSN: %d bytes\n", info_element->len); network->rsn_ie_len = min(info_element->len + 2, MAX_WPA_IE_LEN); @@ -1318,7 +1319,7 @@ static int ieee80211_parse_info_param(struct ieee80211_info_element break; default: - IEEE80211_DEBUG_MGMT + LIBIPW_DEBUG_MGMT ("Unsupported info element: %s (%d)\n", get_info_element_string(info_element->id), info_element->id); @@ -1327,20 +1328,20 @@ static int ieee80211_parse_info_param(struct ieee80211_info_element length -= sizeof(*info_element) + info_element->len; info_element = - (struct ieee80211_info_element *)&info_element-> + (struct libipw_info_element *)&info_element-> data[info_element->len]; } return 0; } -static int ieee80211_handle_assoc_resp(struct ieee80211_device *ieee, struct ieee80211_assoc_response - *frame, struct ieee80211_rx_stats *stats) +static int libipw_handle_assoc_resp(struct libipw_device *ieee, struct libipw_assoc_response + *frame, struct libipw_rx_stats *stats) { - struct ieee80211_network network_resp = { + struct libipw_network network_resp = { .ibss_dfs = NULL, }; - struct ieee80211_network *network = &network_resp; + struct libipw_network *network = &network_resp; struct net_device *dev = ieee->dev; network->flags = 0; @@ -1361,7 +1362,7 @@ static int ieee80211_handle_assoc_resp(struct ieee80211_device *ieee, struct iee network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ? 0x3 : 0x0; - if (stats->freq == IEEE80211_52GHZ_BAND) { + if (stats->freq == LIBIPW_52GHZ_BAND) { /* for A band (No DS info) */ network->channel = stats->received_channel; } else @@ -1370,12 +1371,12 @@ static int ieee80211_handle_assoc_resp(struct ieee80211_device *ieee, struct iee network->wpa_ie_len = 0; network->rsn_ie_len = 0; - if (ieee80211_parse_info_param + if (libipw_parse_info_param (frame->info_element, stats->len - sizeof(*frame), network)) return 1; network->mode = 0; - if (stats->freq == IEEE80211_52GHZ_BAND) + if (stats->freq == LIBIPW_52GHZ_BAND) network->mode = IEEE_A; else { if (network->flags & NETWORK_HAS_OFDM) @@ -1394,10 +1395,10 @@ static int ieee80211_handle_assoc_resp(struct ieee80211_device *ieee, struct iee /***************************************************/ -static int ieee80211_network_init(struct ieee80211_device *ieee, struct ieee80211_probe_response +static int libipw_network_init(struct libipw_device *ieee, struct libipw_probe_response *beacon, - struct ieee80211_network *network, - struct ieee80211_rx_stats *stats) + struct libipw_network *network, + struct libipw_rx_stats *stats) { DECLARE_SSID_BUF(ssid); @@ -1423,7 +1424,7 @@ static int ieee80211_network_init(struct ieee80211_device *ieee, struct ieee8021 network->erp_value = (network->capability & WLAN_CAPABILITY_IBSS) ? 0x3 : 0x0; - if (stats->freq == IEEE80211_52GHZ_BAND) { + if (stats->freq == LIBIPW_52GHZ_BAND) { /* for A band (No DS info) */ network->channel = stats->received_channel; } else @@ -1432,12 +1433,12 @@ static int ieee80211_network_init(struct ieee80211_device *ieee, struct ieee8021 network->wpa_ie_len = 0; network->rsn_ie_len = 0; - if (ieee80211_parse_info_param + if (libipw_parse_info_param (beacon->info_element, stats->len - sizeof(*beacon), network)) return 1; network->mode = 0; - if (stats->freq == IEEE80211_52GHZ_BAND) + if (stats->freq == LIBIPW_52GHZ_BAND) network->mode = IEEE_A; else { if (network->flags & NETWORK_HAS_OFDM) @@ -1447,7 +1448,7 @@ static int ieee80211_network_init(struct ieee80211_device *ieee, struct ieee8021 } if (network->mode == 0) { - IEEE80211_DEBUG_SCAN("Filtered out '%s (%pM)' " + LIBIPW_DEBUG_SCAN("Filtered out '%s (%pM)' " "network.\n", print_ssid(ssid, network->ssid, network->ssid_len), @@ -1460,8 +1461,8 @@ static int ieee80211_network_init(struct ieee80211_device *ieee, struct ieee8021 return 0; } -static inline int is_same_network(struct ieee80211_network *src, - struct ieee80211_network *dst) +static inline int is_same_network(struct libipw_network *src, + struct libipw_network *dst) { /* A network is only a duplicate if the channel, BSSID, and ESSID * all match. We treat all <hidden> with the same BSSID and channel @@ -1472,13 +1473,13 @@ static inline int is_same_network(struct ieee80211_network *src, !memcmp(src->ssid, dst->ssid, src->ssid_len)); } -static void update_network(struct ieee80211_network *dst, - struct ieee80211_network *src) +static void update_network(struct libipw_network *dst, + struct libipw_network *src) { int qos_active; u8 old_param; - ieee80211_network_reset(dst); + libipw_network_reset(dst); dst->ibss_dfs = src->ibss_dfs; /* We only update the statistics if they were created by receiving @@ -1488,9 +1489,9 @@ static void update_network(struct ieee80211_network *dst, * down the signal level of an AP. */ if (dst->channel == src->stats.received_channel) memcpy(&dst->stats, &src->stats, - sizeof(struct ieee80211_rx_stats)); + sizeof(struct libipw_rx_stats)); else - IEEE80211_DEBUG_SCAN("Network %pM info received " + LIBIPW_DEBUG_SCAN("Network %pM info received " "off channel (%d vs. %d)\n", src->bssid, dst->channel, src->stats.received_channel); @@ -1521,7 +1522,7 @@ static void update_network(struct ieee80211_network *dst, old_param = dst->qos_data.old_param_count; if (dst->flags & NETWORK_HAS_QOS_MASK) memcpy(&dst->qos_data, &src->qos_data, - sizeof(struct ieee80211_qos_data)); + sizeof(struct libipw_qos_data)); else { dst->qos_data.supported = src->qos_data.supported; dst->qos_data.param_count = src->qos_data.param_count; @@ -1529,11 +1530,11 @@ static void update_network(struct ieee80211_network *dst, if (dst->qos_data.supported == 1) { if (dst->ssid_len) - IEEE80211_DEBUG_QOS + LIBIPW_DEBUG_QOS ("QoS the network %s is QoS supported\n", dst->ssid); else - IEEE80211_DEBUG_QOS + LIBIPW_DEBUG_QOS ("QoS the network is QoS supported\n"); } dst->qos_data.active = qos_active; @@ -1547,25 +1548,25 @@ static inline int is_beacon(__le16 fc) return (WLAN_FC_GET_STYPE(le16_to_cpu(fc)) == IEEE80211_STYPE_BEACON); } -static void ieee80211_process_probe_response(struct ieee80211_device +static void libipw_process_probe_response(struct libipw_device *ieee, struct - ieee80211_probe_response - *beacon, struct ieee80211_rx_stats + libipw_probe_response + *beacon, struct libipw_rx_stats *stats) { struct net_device *dev = ieee->dev; - struct ieee80211_network network = { + struct libipw_network network = { .ibss_dfs = NULL, }; - struct ieee80211_network *target; - struct ieee80211_network *oldest = NULL; + struct libipw_network *target; + struct libipw_network *oldest = NULL; #ifdef CONFIG_LIBIPW_DEBUG - struct ieee80211_info_element *info_element = beacon->info_element; + struct libipw_info_element *info_element = beacon->info_element; #endif unsigned long flags; DECLARE_SSID_BUF(ssid); - IEEE80211_DEBUG_SCAN("'%s' (%pM" + LIBIPW_DEBUG_SCAN("'%s' (%pM" "): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n", print_ssid(ssid, info_element->data, info_element->len), beacon->header.addr3, @@ -1586,8 +1587,8 @@ static void ieee80211_process_probe_response(struct ieee80211_device (beacon->capability & cpu_to_le16(1 << 0x1)) ? '1' : '0', (beacon->capability & cpu_to_le16(1 << 0x0)) ? '1' : '0'); - if (ieee80211_network_init(ieee, beacon, &network, stats)) { - IEEE80211_DEBUG_SCAN("Dropped '%s' (%pM) via %s.\n", + if (libipw_network_init(ieee, beacon, &network, stats)) { + LIBIPW_DEBUG_SCAN("Dropped '%s' (%pM) via %s.\n", print_ssid(ssid, info_element->data, info_element->len), beacon->header.addr3, @@ -1624,21 +1625,21 @@ static void ieee80211_process_probe_response(struct ieee80211_device /* If there are no more slots, expire the oldest */ list_del(&oldest->list); target = oldest; - IEEE80211_DEBUG_SCAN("Expired '%s' (%pM) from " + LIBIPW_DEBUG_SCAN("Expired '%s' (%pM) from " "network list.\n", print_ssid(ssid, target->ssid, target->ssid_len), target->bssid); - ieee80211_network_reset(target); + libipw_network_reset(target); } else { /* Otherwise just pull from the free list */ target = list_entry(ieee->network_free_list.next, - struct ieee80211_network, list); + struct libipw_network, list); list_del(ieee->network_free_list.next); } #ifdef CONFIG_LIBIPW_DEBUG - IEEE80211_DEBUG_SCAN("Adding '%s' (%pM) via %s.\n", + LIBIPW_DEBUG_SCAN("Adding '%s' (%pM) via %s.\n", print_ssid(ssid, network.ssid, network.ssid_len), network.bssid, @@ -1649,7 +1650,7 @@ static void ieee80211_process_probe_response(struct ieee80211_device network.ibss_dfs = NULL; list_add_tail(&target->list, &ieee->network_list); } else { - IEEE80211_DEBUG_SCAN("Updating '%s' (%pM) via %s.\n", + LIBIPW_DEBUG_SCAN("Updating '%s' (%pM) via %s.\n", print_ssid(ssid, target->ssid, target->ssid_len), target->bssid, @@ -1670,121 +1671,121 @@ static void ieee80211_process_probe_response(struct ieee80211_device } } -void ieee80211_rx_mgt(struct ieee80211_device *ieee, - struct ieee80211_hdr_4addr *header, - struct ieee80211_rx_stats *stats) +void libipw_rx_mgt(struct libipw_device *ieee, + struct libipw_hdr_4addr *header, + struct libipw_rx_stats *stats) { switch (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl))) { case IEEE80211_STYPE_ASSOC_RESP: - IEEE80211_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n", + LIBIPW_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); - ieee80211_handle_assoc_resp(ieee, - (struct ieee80211_assoc_response *) + libipw_handle_assoc_resp(ieee, + (struct libipw_assoc_response *) header, stats); break; case IEEE80211_STYPE_REASSOC_RESP: - IEEE80211_DEBUG_MGMT("received REASSOCIATION RESPONSE (%d)\n", + LIBIPW_DEBUG_MGMT("received REASSOCIATION RESPONSE (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); break; case IEEE80211_STYPE_PROBE_REQ: - IEEE80211_DEBUG_MGMT("received auth (%d)\n", + LIBIPW_DEBUG_MGMT("received auth (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); if (ieee->handle_probe_request != NULL) ieee->handle_probe_request(ieee->dev, (struct - ieee80211_probe_request *) + libipw_probe_request *) header, stats); break; case IEEE80211_STYPE_PROBE_RESP: - IEEE80211_DEBUG_MGMT("received PROBE RESPONSE (%d)\n", + LIBIPW_DEBUG_MGMT("received PROBE RESPONSE (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); - IEEE80211_DEBUG_SCAN("Probe response\n"); - ieee80211_process_probe_response(ieee, + LIBIPW_DEBUG_SCAN("Probe response\n"); + libipw_process_probe_response(ieee, (struct - ieee80211_probe_response *) + libipw_probe_response *) header, stats); break; case IEEE80211_STYPE_BEACON: - IEEE80211_DEBUG_MGMT("received BEACON (%d)\n", + LIBIPW_DEBUG_MGMT("received BEACON (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); - IEEE80211_DEBUG_SCAN("Beacon\n"); - ieee80211_process_probe_response(ieee, + LIBIPW_DEBUG_SCAN("Beacon\n"); + libipw_process_probe_response(ieee, (struct - ieee80211_probe_response *) + libipw_probe_response *) header, stats); break; case IEEE80211_STYPE_AUTH: - IEEE80211_DEBUG_MGMT("received auth (%d)\n", + LIBIPW_DEBUG_MGMT("received auth (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); if (ieee->handle_auth != NULL) ieee->handle_auth(ieee->dev, - (struct ieee80211_auth *)header); + (struct libipw_auth *)header); break; case IEEE80211_STYPE_DISASSOC: if (ieee->handle_disassoc != NULL) ieee->handle_disassoc(ieee->dev, - (struct ieee80211_disassoc *) + (struct libipw_disassoc *) header); break; case IEEE80211_STYPE_ACTION: - IEEE80211_DEBUG_MGMT("ACTION\n"); + LIBIPW_DEBUG_MGMT("ACTION\n"); if (ieee->handle_action) ieee->handle_action(ieee->dev, - (struct ieee80211_action *) + (struct libipw_action *) header, stats); break; case IEEE80211_STYPE_REASSOC_REQ: - IEEE80211_DEBUG_MGMT("received reassoc (%d)\n", + LIBIPW_DEBUG_MGMT("received reassoc (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); - IEEE80211_DEBUG_MGMT("%s: IEEE80211_REASSOC_REQ received\n", + LIBIPW_DEBUG_MGMT("%s: LIBIPW_REASSOC_REQ received\n", ieee->dev->name); if (ieee->handle_reassoc_request != NULL) ieee->handle_reassoc_request(ieee->dev, - (struct ieee80211_reassoc_request *) + (struct libipw_reassoc_request *) header); break; case IEEE80211_STYPE_ASSOC_REQ: - IEEE80211_DEBUG_MGMT("received assoc (%d)\n", + LIBIPW_DEBUG_MGMT("received assoc (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); - IEEE80211_DEBUG_MGMT("%s: IEEE80211_ASSOC_REQ received\n", + LIBIPW_DEBUG_MGMT("%s: LIBIPW_ASSOC_REQ received\n", ieee->dev->name); if (ieee->handle_assoc_request != NULL) ieee->handle_assoc_request(ieee->dev); break; case IEEE80211_STYPE_DEAUTH: - IEEE80211_DEBUG_MGMT("DEAUTH\n"); + LIBIPW_DEBUG_MGMT("DEAUTH\n"); if (ieee->handle_deauth != NULL) ieee->handle_deauth(ieee->dev, - (struct ieee80211_deauth *) + (struct libipw_deauth *) header); break; default: - IEEE80211_DEBUG_MGMT("received UNKNOWN (%d)\n", + LIBIPW_DEBUG_MGMT("received UNKNOWN (%d)\n", WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); - IEEE80211_DEBUG_MGMT("%s: Unknown management packet: %d\n", + LIBIPW_DEBUG_MGMT("%s: Unknown management packet: %d\n", ieee->dev->name, WLAN_FC_GET_STYPE(le16_to_cpu (header->frame_ctl))); @@ -1792,6 +1793,6 @@ void ieee80211_rx_mgt(struct ieee80211_device *ieee, } } -EXPORT_SYMBOL_GPL(ieee80211_rx_any); -EXPORT_SYMBOL(ieee80211_rx_mgt); -EXPORT_SYMBOL(ieee80211_rx); +EXPORT_SYMBOL_GPL(libipw_rx_any); +EXPORT_SYMBOL(libipw_rx_mgt); +EXPORT_SYMBOL(libipw_rx); diff --git a/drivers/net/wireless/ipw2x00/libipw_tx.c b/drivers/net/wireless/ipw2x00/libipw_tx.c index da2ad5437ce5..da8beac7fcf3 100644 --- a/drivers/net/wireless/ipw2x00/libipw_tx.c +++ b/drivers/net/wireless/ipw2x00/libipw_tx.c @@ -19,7 +19,7 @@ file called LICENSE. Contact Information: - James P. Ketrenos <ipw2100-admin@linux.intel.com> + Intel Linux Wireless <ilw@linux.intel.com> Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ******************************************************************************/ @@ -41,7 +41,7 @@ #include <linux/etherdevice.h> #include <asm/uaccess.h> -#include "ieee80211.h" +#include "libipw.h" /* @@ -126,12 +126,12 @@ payload of each frame is reduced to 492 bytes. static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 }; static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 }; -static int ieee80211_copy_snap(u8 * data, __be16 h_proto) +static int libipw_copy_snap(u8 * data, __be16 h_proto) { - struct ieee80211_snap_hdr *snap; + struct libipw_snap_hdr *snap; u8 *oui; - snap = (struct ieee80211_snap_hdr *)data; + snap = (struct libipw_snap_hdr *)data; snap->dsap = 0xaa; snap->ssap = 0xaa; snap->ctrl = 0x03; @@ -149,7 +149,7 @@ static int ieee80211_copy_snap(u8 * data, __be16 h_proto) return SNAP_SIZE + sizeof(u16); } -static int ieee80211_encrypt_fragment(struct ieee80211_device *ieee, +static int libipw_encrypt_fragment(struct libipw_device *ieee, struct sk_buff *frag, int hdr_len) { struct lib80211_crypt_data *crypt = @@ -177,7 +177,7 @@ static int ieee80211_encrypt_fragment(struct ieee80211_device *ieee, return 0; } -void ieee80211_txb_free(struct ieee80211_txb *txb) +void libipw_txb_free(struct libipw_txb *txb) { int i; if (unlikely(!txb)) @@ -188,17 +188,17 @@ void ieee80211_txb_free(struct ieee80211_txb *txb) kfree(txb); } -static struct ieee80211_txb *ieee80211_alloc_txb(int nr_frags, int txb_size, +static struct libipw_txb *libipw_alloc_txb(int nr_frags, int txb_size, int headroom, gfp_t gfp_mask) { - struct ieee80211_txb *txb; + struct libipw_txb *txb; int i; - txb = kmalloc(sizeof(struct ieee80211_txb) + (sizeof(u8 *) * nr_frags), + txb = kmalloc(sizeof(struct libipw_txb) + (sizeof(u8 *) * nr_frags), gfp_mask); if (!txb) return NULL; - memset(txb, 0, sizeof(struct ieee80211_txb)); + memset(txb, 0, sizeof(struct libipw_txb)); txb->nr_frags = nr_frags; txb->frag_size = txb_size; @@ -220,7 +220,7 @@ static struct ieee80211_txb *ieee80211_alloc_txb(int nr_frags, int txb_size, return txb; } -static int ieee80211_classify(struct sk_buff *skb) +static int libipw_classify(struct sk_buff *skb) { struct ethhdr *eth; struct iphdr *ip; @@ -252,11 +252,11 @@ static int ieee80211_classify(struct sk_buff *skb) /* Incoming skb is converted to a txb which consists of * a block of 802.11 fragment packets (stored as skbs) */ -int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) +netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev) { - struct ieee80211_device *ieee = netdev_priv(dev); - struct ieee80211_txb *txb = NULL; - struct ieee80211_hdr_3addrqos *frag_hdr; + struct libipw_device *ieee = netdev_priv(dev); + struct libipw_txb *txb = NULL; + struct libipw_hdr_3addrqos *frag_hdr; int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size, rts_required; unsigned long flags; @@ -264,7 +264,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) __be16 ether_type; int bytes, fc, hdr_len; struct sk_buff *skb_frag; - struct ieee80211_hdr_3addrqos header = {/* Ensure zero initialized */ + struct libipw_hdr_3addrqos header = {/* Ensure zero initialized */ .duration_id = 0, .seq_ctl = 0, .qos_ctl = 0 @@ -331,14 +331,14 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) memcpy(header.addr2, src, ETH_ALEN); memcpy(header.addr3, ieee->bssid, ETH_ALEN); } - hdr_len = IEEE80211_3ADDR_LEN; + hdr_len = LIBIPW_3ADDR_LEN; if (ieee->is_qos_active && ieee->is_qos_active(dev, skb)) { fc |= IEEE80211_STYPE_QOS_DATA; hdr_len += 2; - skb->priority = ieee80211_classify(skb); - header.qos_ctl |= cpu_to_le16(skb->priority & IEEE80211_QCTL_TID); + skb->priority = libipw_classify(skb); + header.qos_ctl |= cpu_to_le16(skb->priority & LIBIPW_QCTL_TID); } header.frame_ctl = cpu_to_le16(fc); @@ -362,12 +362,12 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) skb_reserve(skb_new, crypt->ops->extra_msdu_prefix_len); memcpy(skb_put(skb_new, hdr_len), &header, hdr_len); snapped = 1; - ieee80211_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)), + libipw_copy_snap(skb_put(skb_new, SNAP_SIZE + sizeof(u16)), ether_type); skb_copy_from_linear_data(skb, skb_put(skb_new, skb->len), skb->len); res = crypt->ops->encrypt_msdu(skb_new, hdr_len, crypt->priv); if (res < 0) { - IEEE80211_ERROR("msdu encryption failed\n"); + LIBIPW_ERROR("msdu encryption failed\n"); dev_kfree_skb_any(skb_new); goto failed; } @@ -393,8 +393,8 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) * for it when determining the amount of payload space. */ bytes_per_frag = frag_size - hdr_len; if (ieee->config & - (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS)) - bytes_per_frag -= IEEE80211_FCS_LEN; + (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) + bytes_per_frag -= LIBIPW_FCS_LEN; /* Each fragment may need to have room for encryptiong * pre/postfix */ @@ -417,14 +417,14 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) } rts_required = (frag_size > ieee->rts - && ieee->config & CFG_IEEE80211_RTS); + && ieee->config & CFG_LIBIPW_RTS); if (rts_required) nr_frags++; /* When we allocate the TXB we allocate enough space for the reserve * and full fragment bytes (bytes_per_frag doesn't include prefix, * postfix, header, FCS, etc.) */ - txb = ieee80211_alloc_txb(nr_frags, frag_size, + txb = libipw_alloc_txb(nr_frags, frag_size, ieee->tx_headroom, GFP_ATOMIC); if (unlikely(!txb)) { printk(KERN_WARNING "%s: Could not allocate TXB\n", @@ -441,7 +441,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) if (rts_required) { skb_frag = txb->fragments[0]; frag_hdr = - (struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len); + (struct libipw_hdr_3addrqos *)skb_put(skb_frag, hdr_len); /* * Set header frame_ctl to the RTS. @@ -456,7 +456,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) header.frame_ctl = cpu_to_le16(fc); if (ieee->config & - (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS)) + (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) skb_put(skb_frag, 4); txb->rts_included = 1; @@ -472,7 +472,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) crypt->ops->extra_mpdu_prefix_len); frag_hdr = - (struct ieee80211_hdr_3addrqos *)skb_put(skb_frag, hdr_len); + (struct libipw_hdr_3addrqos *)skb_put(skb_frag, hdr_len); memcpy(frag_hdr, &header, hdr_len); /* If this is not the last fragment, then add the MOREFRAGS @@ -487,7 +487,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) } if (i == 0 && !snapped) { - ieee80211_copy_snap(skb_put + libipw_copy_snap(skb_put (skb_frag, SNAP_SIZE + sizeof(u16)), ether_type); bytes -= SNAP_SIZE + sizeof(u16); @@ -501,7 +501,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) /* Encryption routine will move the header forward in order * to insert the IV between the header and the payload */ if (host_encrypt) - ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len); + libipw_encrypt_fragment(ieee, skb_frag, hdr_len); else if (host_build_iv) { atomic_inc(&crypt->refcnt); if (crypt->ops->build_iv) @@ -513,7 +513,7 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) } if (ieee->config & - (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS)) + (CFG_LIBIPW_COMPUTE_FCS | CFG_LIBIPW_RESERVE_FCS)) skb_put(skb_frag, 4); } @@ -523,17 +523,17 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) dev_kfree_skb_any(skb); if (txb) { - int ret = (*ieee->hard_start_xmit) (txb, dev, priority); - if (ret == 0) { + netdev_tx_t ret = (*ieee->hard_start_xmit)(txb, dev, priority); + if (ret == NETDEV_TX_OK) { dev->stats.tx_packets++; dev->stats.tx_bytes += txb->payload_size; - return 0; + return NETDEV_TX_OK; } - ieee80211_txb_free(txb); + libipw_txb_free(txb); } - return 0; + return NETDEV_TX_OK; failed: spin_unlock_irqrestore(&ieee->lock, flags); @@ -541,6 +541,6 @@ int ieee80211_xmit(struct sk_buff *skb, struct net_device *dev) dev->stats.tx_errors++; return NETDEV_TX_BUSY; } -EXPORT_SYMBOL(ieee80211_xmit); +EXPORT_SYMBOL(libipw_xmit); -EXPORT_SYMBOL(ieee80211_txb_free); +EXPORT_SYMBOL(libipw_txb_free); diff --git a/drivers/net/wireless/ipw2x00/libipw_wx.c b/drivers/net/wireless/ipw2x00/libipw_wx.c index 3c0812db030a..4d89f66f53b2 100644 --- a/drivers/net/wireless/ipw2x00/libipw_wx.c +++ b/drivers/net/wireless/ipw2x00/libipw_wx.c @@ -25,7 +25,7 @@ file called LICENSE. Contact Information: - James P. Ketrenos <ipw2100-admin@linux.intel.com> + Intel Linux Wireless <ilw@linux.intel.com> Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 ******************************************************************************/ @@ -37,9 +37,9 @@ #include <net/lib80211.h> #include <linux/wireless.h> -#include "ieee80211.h" +#include "libipw.h" -static const char *ieee80211_modes[] = { +static const char *libipw_modes[] = { "?", "a", "b", "ab", "g", "ag", "bg", "abg" }; @@ -54,9 +54,9 @@ static inline unsigned int elapsed_jiffies_msecs(unsigned long start) } #define MAX_CUSTOM_LEN 64 -static char *ieee80211_translate_scan(struct ieee80211_device *ieee, +static char *libipw_translate_scan(struct libipw_device *ieee, char *start, char *stop, - struct ieee80211_network *network, + struct libipw_network *network, struct iw_request_info *info) { char custom[MAX_CUSTOM_LEN]; @@ -84,7 +84,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, /* Add the protocol name */ iwe.cmd = SIOCGIWNAME; snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s", - ieee80211_modes[network->mode]); + libipw_modes[network->mode]); start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_CHAR_LEN); /* Add mode */ @@ -102,7 +102,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, /* Add channel and frequency */ /* Note : userspace automatically computes channel using iwrange */ iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = ieee80211_channel_to_freq(ieee, network->channel); + iwe.u.freq.m = libipw_channel_to_freq(ieee, network->channel); iwe.u.freq.e = 6; iwe.u.freq.i = 0; start = iwe_stream_add_event(info, start, stop, &iwe, IW_EV_FREQ_LEN); @@ -155,7 +155,7 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | IW_QUAL_NOISE_UPDATED; - if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) { + if (!(network->stats.mask & LIBIPW_STATMASK_RSSI)) { iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; iwe.u.qual.qual = 0; @@ -180,14 +180,14 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, iwe.u.qual.qual = 0; } - if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) { + if (!(network->stats.mask & LIBIPW_STATMASK_NOISE)) { iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID; iwe.u.qual.noise = 0; } else { iwe.u.qual.noise = network->stats.noise; } - if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) { + if (!(network->stats.mask & LIBIPW_STATMASK_SIGNAL)) { iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID; iwe.u.qual.level = 0; } else { @@ -237,14 +237,14 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, p = custom; p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Channel flags: "); - if (ieee80211_get_channel_flags(ieee, network->channel) & - IEEE80211_CH_INVALID) { + if (libipw_get_channel_flags(ieee, network->channel) & + LIBIPW_CH_INVALID) { iwe.cmd = IWEVCUSTOM; p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "INVALID "); } - if (ieee80211_get_channel_flags(ieee, network->channel) & - IEEE80211_CH_RADAR_DETECT) { + if (libipw_get_channel_flags(ieee, network->channel) & + LIBIPW_CH_RADAR_DETECT) { iwe.cmd = IWEVCUSTOM; p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), "DFS "); } @@ -259,11 +259,11 @@ static char *ieee80211_translate_scan(struct ieee80211_device *ieee, #define SCAN_ITEM_SIZE 128 -int ieee80211_wx_get_scan(struct ieee80211_device *ieee, +int libipw_wx_get_scan(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct ieee80211_network *network; + struct libipw_network *network; unsigned long flags; int err = 0; @@ -272,7 +272,7 @@ int ieee80211_wx_get_scan(struct ieee80211_device *ieee, int i = 0; DECLARE_SSID_BUF(ssid); - IEEE80211_DEBUG_WX("Getting scan\n"); + LIBIPW_DEBUG_WX("Getting scan\n"); spin_lock_irqsave(&ieee->lock, flags); @@ -285,10 +285,10 @@ int ieee80211_wx_get_scan(struct ieee80211_device *ieee, if (ieee->scan_age == 0 || time_after(network->last_scanned + ieee->scan_age, jiffies)) - ev = ieee80211_translate_scan(ieee, ev, stop, network, + ev = libipw_translate_scan(ieee, ev, stop, network, info); else { - IEEE80211_DEBUG_SCAN("Not showing network '%s (" + LIBIPW_DEBUG_SCAN("Not showing network '%s (" "%pM)' due to age (%ums).\n", print_ssid(ssid, network->ssid, network->ssid_len), @@ -303,18 +303,18 @@ int ieee80211_wx_get_scan(struct ieee80211_device *ieee, wrqu->data.length = ev - extra; wrqu->data.flags = 0; - IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i); + LIBIPW_DEBUG_WX("exit: %d networks returned.\n", i); return err; } -int ieee80211_wx_set_encode(struct ieee80211_device *ieee, +int libipw_wx_set_encode(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *keybuf) { struct iw_point *erq = &(wrqu->encoding); struct net_device *dev = ieee->dev; - struct ieee80211_security sec = { + struct libipw_security sec = { .flags = 0 }; int i, key, key_provided, len; @@ -322,7 +322,7 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, int host_crypto = ieee->host_encrypt || ieee->host_decrypt || ieee->host_build_iv; DECLARE_SSID_BUF(ssid); - IEEE80211_DEBUG_WX("SET_ENCODE\n"); + LIBIPW_DEBUG_WX("SET_ENCODE\n"); key = erq->flags & IW_ENCODE_INDEX; if (key) { @@ -335,18 +335,18 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, key = ieee->crypt_info.tx_keyidx; } - IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ? + LIBIPW_DEBUG_WX("Key: %d [%s]\n", key, key_provided ? "provided" : "default"); crypt = &ieee->crypt_info.crypt[key]; if (erq->flags & IW_ENCODE_DISABLED) { if (key_provided && *crypt) { - IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n", + LIBIPW_DEBUG_WX("Disabling encryption on key %d.\n", key); lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); } else - IEEE80211_DEBUG_WX("Disabling encryption.\n"); + LIBIPW_DEBUG_WX("Disabling encryption.\n"); /* Check all the keys to see if any are still configured, * and if no key index was provided, de-init them all */ @@ -410,7 +410,7 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, /* If a new key was provided, set it up */ if (erq->length > 0) { -#ifdef CONFIG_IEEE80211_DEBUG +#ifdef CONFIG_LIBIPW_DEBUG DECLARE_SSID_BUF(ssid); #endif @@ -419,7 +419,7 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, if (len > erq->length) memset(sec.keys[key] + erq->length, 0, len - erq->length); - IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n", + LIBIPW_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n", key, print_ssid(ssid, sec.keys[key], len), erq->length, len); sec.key_sizes[key] = len; @@ -438,7 +438,7 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, NULL, (*crypt)->priv); if (len == 0) { /* Set a default key of all 0 */ - IEEE80211_DEBUG_WX("Setting key %d to all " + LIBIPW_DEBUG_WX("Setting key %d to all " "zero.\n", key); memset(sec.keys[key], 0, 13); (*crypt)->ops->set_key(sec.keys[key], 13, NULL, @@ -449,7 +449,7 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, } /* No key data - just set the default TX key index */ if (key_provided) { - IEEE80211_DEBUG_WX("Setting key %d to default Tx " + LIBIPW_DEBUG_WX("Setting key %d to default Tx " "key.\n", key); ieee->crypt_info.tx_keyidx = key; sec.active_key = key; @@ -461,7 +461,7 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY; sec.flags |= SEC_AUTH_MODE; - IEEE80211_DEBUG_WX("Auth: %s\n", + LIBIPW_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ? "OPEN" : "SHARED KEY"); } @@ -490,16 +490,16 @@ int ieee80211_wx_set_encode(struct ieee80211_device *ieee, return 0; } -int ieee80211_wx_get_encode(struct ieee80211_device *ieee, +int libipw_wx_get_encode(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *keybuf) { struct iw_point *erq = &(wrqu->encoding); int len, key; struct lib80211_crypt_data *crypt; - struct ieee80211_security *sec = &ieee->sec; + struct libipw_security *sec = &ieee->sec; - IEEE80211_DEBUG_WX("GET_ENCODE\n"); + LIBIPW_DEBUG_WX("GET_ENCODE\n"); key = erq->flags & IW_ENCODE_INDEX; if (key) { @@ -532,7 +532,7 @@ int ieee80211_wx_get_encode(struct ieee80211_device *ieee, return 0; } -int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee, +int libipw_wx_set_encodeext(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { @@ -545,7 +545,7 @@ int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee, struct lib80211_crypto_ops *ops; struct lib80211_crypt_data **crypt; - struct ieee80211_security sec = { + struct libipw_security sec = { .flags = 0, }; @@ -611,7 +611,7 @@ int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee, module = "lib80211_crypt_ccmp"; break; default: - IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", + LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n", dev->name, ext->alg); ret = -EINVAL; goto done; @@ -623,7 +623,7 @@ int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee, ops = lib80211_get_crypto_ops(alg); } if (ops == NULL) { - IEEE80211_DEBUG_WX("%s: unknown crypto alg %d\n", + LIBIPW_DEBUG_WX("%s: unknown crypto alg %d\n", dev->name, ext->alg); ret = -EINVAL; goto done; @@ -653,7 +653,7 @@ int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee, if (ext->key_len > 0 && (*crypt)->ops->set_key && (*crypt)->ops->set_key(ext->key, ext->key_len, ext->rx_seq, (*crypt)->priv) < 0) { - IEEE80211_DEBUG_WX("%s: key setting failed\n", dev->name); + LIBIPW_DEBUG_WX("%s: key setting failed\n", dev->name); ret = -EINVAL; goto done; } @@ -700,20 +700,20 @@ int ieee80211_wx_set_encodeext(struct ieee80211_device *ieee, if (ieee->reset_on_keychange && ieee->iw_mode != IW_MODE_INFRA && ieee->reset_port && ieee->reset_port(dev)) { - IEEE80211_DEBUG_WX("%s: reset_port failed\n", dev->name); + LIBIPW_DEBUG_WX("%s: reset_port failed\n", dev->name); return -EINVAL; } return ret; } -int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee, +int libipw_wx_get_encodeext(struct libipw_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct iw_point *encoding = &wrqu->encoding; struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - struct ieee80211_security *sec = &ieee->sec; + struct libipw_security *sec = &ieee->sec; int idx, max_key_len; max_key_len = encoding->length - sizeof(*ext); @@ -763,9 +763,9 @@ int ieee80211_wx_get_encodeext(struct ieee80211_device *ieee, return 0; } -EXPORT_SYMBOL(ieee80211_wx_set_encodeext); -EXPORT_SYMBOL(ieee80211_wx_get_encodeext); +EXPORT_SYMBOL(libipw_wx_set_encodeext); +EXPORT_SYMBOL(libipw_wx_get_encodeext); -EXPORT_SYMBOL(ieee80211_wx_get_scan); -EXPORT_SYMBOL(ieee80211_wx_set_encode); -EXPORT_SYMBOL(ieee80211_wx_get_encode); +EXPORT_SYMBOL(libipw_wx_get_scan); +EXPORT_SYMBOL(libipw_wx_set_encode); +EXPORT_SYMBOL(libipw_wx_get_encode); diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index e092af09d6bf..99310c033253 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -9,6 +9,9 @@ config IWLWIFI config IWLWIFI_LEDS bool "Enable LED support in iwlagn and iwl3945 drivers" depends on IWLWIFI + default y + ---help--- + Select this if you want LED support. config IWLWIFI_SPECTRUM_MEASUREMENT bool "Enable Spectrum Measurement in iwlagn driver" diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index 7da52f1cc1d6..a95caa014143 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -46,7 +46,7 @@ #include "iwl-5000-hw.h" /* Highest firmware API version supported */ -#define IWL1000_UCODE_API_MAX 2 +#define IWL1000_UCODE_API_MAX 3 /* Lowest firmware API version supported */ #define IWL1000_UCODE_API_MIN 1 @@ -55,19 +55,109 @@ #define _IWL1000_MODULE_FIRMWARE(api) IWL1000_FW_PRE #api ".ucode" #define IWL1000_MODULE_FIRMWARE(api) _IWL1000_MODULE_FIRMWARE(api) + +/* + * For 1000, use advance thermal throttling critical temperature threshold, + * but legacy thermal management implementation for now. + * This is for the reason of 1000 uCode using advance thermal throttling API + * but not implement ct_kill_exit based on ct_kill exit temperature + * so the thermal throttling will still based on legacy thermal throttling + * management. + * The code here need to be modified once 1000 uCode has the advanced thermal + * throttling algorithm in place + */ +static void iwl1000_set_ct_threshold(struct iwl_priv *priv) +{ + /* want Celsius */ + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; + priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; +} + +/* NIC configuration for 1000 series */ +static void iwl1000_nic_config(struct iwl_priv *priv) +{ + iwl5000_nic_config(priv); + + /* Setting digital SVR for 1000 card to 1.32V */ + /* locking is acquired in iwl_set_bits_mask_prph() function */ + iwl_set_bits_mask_prph(priv, APMG_DIGITAL_SVR_REG, + APMG_SVR_DIGITAL_VOLTAGE_1_32, + ~APMG_SVR_VOLTAGE_CONFIG_BIT_MSK); +} + +static struct iwl_lib_ops iwl1000_lib = { + .set_hw_params = iwl5000_hw_set_hw_params, + .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, + .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, + .txq_set_sched = iwl5000_txq_set_sched, + .txq_agg_enable = iwl5000_txq_agg_enable, + .txq_agg_disable = iwl5000_txq_agg_disable, + .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd, + .txq_free_tfd = iwl_hw_txq_free_tfd, + .txq_init = iwl_hw_tx_queue_init, + .rx_handler_setup = iwl5000_rx_handler_setup, + .setup_deferred_work = iwl5000_setup_deferred_work, + .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr, + .load_ucode = iwl5000_load_ucode, + .init_alive_start = iwl5000_init_alive_start, + .alive_notify = iwl5000_alive_notify, + .send_tx_power = iwl5000_send_tx_power, + .update_chain_flags = iwl_update_chain_flags, + .apm_ops = { + .init = iwl5000_apm_init, + .reset = iwl5000_apm_reset, + .stop = iwl5000_apm_stop, + .config = iwl1000_nic_config, + .set_pwr_src = iwl_set_pwr_src, + }, + .eeprom_ops = { + .regulatory_bands = { + EEPROM_5000_REG_BAND_1_CHANNELS, + EEPROM_5000_REG_BAND_2_CHANNELS, + EEPROM_5000_REG_BAND_3_CHANNELS, + EEPROM_5000_REG_BAND_4_CHANNELS, + EEPROM_5000_REG_BAND_5_CHANNELS, + EEPROM_5000_REG_BAND_24_HT40_CHANNELS, + EEPROM_5000_REG_BAND_52_HT40_CHANNELS + }, + .verify_signature = iwlcore_eeprom_verify_signature, + .acquire_semaphore = iwlcore_eeprom_acquire_semaphore, + .release_semaphore = iwlcore_eeprom_release_semaphore, + .calib_version = iwl5000_eeprom_calib_version, + .query_addr = iwl5000_eeprom_query_addr, + }, + .post_associate = iwl_post_associate, + .isr = iwl_isr_ict, + .config_ap = iwl_config_ap, + .temp_ops = { + .temperature = iwl5000_temperature, + .set_ct_kill = iwl1000_set_ct_threshold, + }, +}; + +static struct iwl_ops iwl1000_ops = { + .ucode = &iwl5000_ucode, + .lib = &iwl1000_lib, + .hcmd = &iwl5000_hcmd, + .utils = &iwl5000_hcmd_utils, +}; + struct iwl_cfg iwl1000_bgn_cfg = { .name = "1000 Series BGN", .fw_name_pre = IWL1000_FW_PRE, .ucode_api_max = IWL1000_UCODE_API_MAX, .ucode_api_min = IWL1000_UCODE_API_MIN, .sku = IWL_SKU_G|IWL_SKU_N, - .ops = &iwl5000_ops, - .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, + .ops = &iwl1000_ops, + .eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_A, .valid_rx_ant = ANT_AB, .need_pll_cfg = true, + .max_ll_items = OTP_MAX_LL_ITEMS_1000, + .shadow_ram_support = false, + .ht_greenfield_support = true, }; diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h index 73f93a0ff2df..16772780c5b0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945-hw.h @@ -176,7 +176,7 @@ struct iwl3945_eeprom { * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory * txpower (MSB). * - * Entries immediately below are for 20 MHz channel width. FAT (40 MHz) + * Entries immediately below are for 20 MHz channel width. HT40 (40 MHz) * channels (only for 4965, not supported by 3945) appear later in the EEPROM. * * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 @@ -232,9 +232,8 @@ struct iwl3945_eeprom { #define PCI_CFG_REV_ID_BIT_BASIC_SKU (0x40) /* bit 6 */ #define PCI_CFG_REV_ID_BIT_RTP (0x80) /* bit 7 */ -#define TFD_QUEUE_MIN 0 -#define TFD_QUEUE_MAX 5 /* 4 DATA + 1 CMD */ - +/* 4 DATA + 1 CMD. There are 2 HCCA queues that are not used. */ +#define IWL39_NUM_QUEUES 5 #define IWL_NUM_SCAN_RATES (2) #define IWL_DEFAULT_TX_RETRY 15 @@ -280,8 +279,6 @@ struct iwl3945_eeprom { /* Size of uCode instruction memory in bootstrap state machine */ #define IWL39_MAX_BSM_SIZE IWL39_RTC_INST_SIZE -#define IWL39_MAX_NUM_QUEUES 8 - static inline int iwl3945_hw_valid_rtc_data_addr(u32 addr) { return (addr >= IWL39_RTC_DATA_LOWER_BOUND) && diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.c b/drivers/net/wireless/iwlwifi/iwl-3945-led.c index 225e5f889346..8c29ded7d02c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.c @@ -79,11 +79,10 @@ static const struct { #define IWL_MAX_BLINK_TBL (ARRAY_SIZE(blink_tbl) - 1) /*Exclude Solid on*/ #define IWL_SOLID_BLINK_IDX (ARRAY_SIZE(blink_tbl) - 1) -static int iwl3945_led_cmd_callback(struct iwl_priv *priv, - struct iwl_cmd *cmd, - struct sk_buff *skb) +static void iwl3945_led_cmd_callback(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb) { - return 1; } static inline int iwl3945_brightness_to_idx(enum led_brightness brightness) @@ -99,8 +98,8 @@ static int iwl_send_led_cmd(struct iwl_priv *priv, .id = REPLY_LEDS_CMD, .len = sizeof(struct iwl_led_cmd), .data = led_cmd, - .meta.flags = CMD_ASYNC, - .meta.u.callback = iwl3945_led_cmd_callback, + .flags = CMD_ASYNC, + .callback = iwl3945_led_cmd_callback, }; return iwl_send_cmd(priv, &cmd); diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c index 5eb538d18a80..a16bd4147eac 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c @@ -673,33 +673,17 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, s8 scale_action = 0; unsigned long flags; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - u16 fc; - u16 rate_mask = 0; + u16 rate_mask = sta ? sta->supp_rates[sband->band] : 0; s8 max_rate_idx = -1; struct iwl_priv *priv = (struct iwl_priv *)priv_r; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); IWL_DEBUG_RATE(priv, "enter\n"); - if (sta) - rate_mask = sta->supp_rates[sband->band]; - - /* Send management frames and NO_ACK data using lowest rate. */ - fc = le16_to_cpu(hdr->frame_control); - if ((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA || - info->flags & IEEE80211_TX_CTL_NO_ACK || - !sta || !priv_sta) { - IWL_DEBUG_RATE(priv, "leave: No STA priv data to update!\n"); - if (!rate_mask) - info->control.rates[0].idx = - rate_lowest_index(sband, NULL); - else - info->control.rates[0].idx = - rate_lowest_index(sband, sta); - if (info->flags & IEEE80211_TX_CTL_NO_ACK) - info->control.rates[0].count = 1; + if (rate_control_send_low(sta, priv_sta, txrc)) return; - } + + rate_mask = sta->supp_rates[sband->band]; /* get user max rate if set */ max_rate_idx = txrc->max_rate_idx; diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 46288e724889..e9a685d8e3a1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -349,12 +349,13 @@ static void iwl3945_rx_reply_tx(struct iwl_priv *priv, * *****************************************************************************/ -void iwl3945_hw_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) +void iwl3945_hw_rx_statistics(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) { struct iwl_rx_packet *pkt = (void *)rxb->skb->data; IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", (int)sizeof(struct iwl3945_notif_statistics), - le32_to_cpu(pkt->len)); + le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); memcpy(&priv->statistics_39, pkt->u.raw, sizeof(priv->statistics_39)); @@ -509,7 +510,7 @@ static void iwl3945_dbg_report_frame(struct iwl_priv *priv, struct iwl_rx_packet *pkt, struct ieee80211_hdr *header, int group100) { - if (priv->debug_level & IWL_DL_RX) + if (iwl_get_debug_level(priv) & IWL_DL_RX) _iwl3945_dbg_report_frame(priv, pkt, header, group100); } @@ -544,9 +545,7 @@ static void iwl3945_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; -#ifdef CONFIG_IWLWIFI_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); @@ -577,7 +576,10 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv, if (ieee80211_is_data(hdr->frame_control)) priv->rxtxpackets += len; #endif - ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats); + iwl_update_stats(priv, false, hdr->frame_control, len); + + memcpy(IEEE80211_SKB_RXCB(rxb->skb), stats, sizeof(*stats)); + ieee80211_rx_irqsafe(priv->hw, rxb->skb); rxb->skb = NULL; } @@ -678,6 +680,7 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, /* Set "1" to report good data frames in groups of 100 */ iwl3945_dbg_report_frame(priv, pkt, header, 1); + iwl_dbg_log_rx_data_frame(priv, le16_to_cpu(rx_hdr->len), header); if (network_packet) { priv->last_beacon_time = le32_to_cpu(rx_end->beacon_timestamp); @@ -748,8 +751,8 @@ void iwl3945_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) /* Unmap tx_cmd */ if (counter) pci_unmap_single(dev, - pci_unmap_addr(&txq->cmd[index]->meta, mapping), - pci_unmap_len(&txq->cmd[index]->meta, len), + pci_unmap_addr(&txq->meta[index], mapping), + pci_unmap_len(&txq->meta[index], len), PCI_DMA_TODEVICE); /* unmap chunks if any */ @@ -773,9 +776,11 @@ void iwl3945_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) * iwl3945_hw_build_tx_cmd_rate - Add rate portion to TX_CMD: * */ -void iwl3945_hw_build_tx_cmd_rate(struct iwl_priv *priv, struct iwl_cmd *cmd, - struct ieee80211_tx_info *info, - struct ieee80211_hdr *hdr, int sta_id, int tx_id) +void iwl3945_hw_build_tx_cmd_rate(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, + int sta_id, int tx_id) { u16 hw_value = ieee80211_get_tx_rate(priv->hw, info)->hw_value; u16 rate_index = min(hw_value & 0xffff, IWL_RATE_COUNT - 1); @@ -962,7 +967,7 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv) goto error; /* Tx queue(s) */ - for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) { + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) { slots_num = (txq_id == IWL_CMD_QUEUE_NUM) ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; rc = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num, @@ -1139,7 +1144,7 @@ void iwl3945_hw_txq_ctx_free(struct iwl_priv *priv) int txq_id; /* Tx queues */ - for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) if (txq_id == IWL_CMD_QUEUE_NUM) iwl_cmd_queue_free(priv); else @@ -1155,7 +1160,7 @@ void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv) iwl_write_prph(priv, ALM_SCD_MODE_REG, 0); /* reset TFD queues */ - for (txq_id = 0; txq_id <= priv->hw_params.max_txq_num; txq_id++) { + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) { iwl_write_direct32(priv, FH39_TCSR_CONFIG(txq_id), 0x0); iwl_poll_direct_bit(priv, FH39_TSSR_TX_STATUS, FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(txq_id), @@ -1857,7 +1862,7 @@ static int iwl3945_send_rxon_assoc(struct iwl_priv *priv) struct iwl_host_cmd cmd = { .id = REPLY_RXON_ASSOC, .len = sizeof(rxon_assoc), - .meta.flags = CMD_WANT_SKB, + .flags = CMD_WANT_SKB, .data = &rxon_assoc, }; const struct iwl_rxon_cmd *rxon1 = &priv->staging_rxon; @@ -1881,14 +1886,14 @@ static int iwl3945_send_rxon_assoc(struct iwl_priv *priv) if (rc) return rc; - res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; + res = (struct iwl_rx_packet *)cmd.reply_skb->data; if (res->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_RXON_ASSOC command\n"); rc = -EIO; } priv->alloc_rxb_skb--; - dev_kfree_skb_any(cmd.meta.u.skb); + dev_kfree_skb_any(cmd.reply_skb); return rc; } @@ -1986,7 +1991,7 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv) staging_rxon->reserved4 = 0; staging_rxon->reserved5 = 0; - iwl_set_rxon_hwcrypto(priv, !priv->hw_params.sw_crypto); + iwl_set_rxon_hwcrypto(priv, !iwl3945_mod_params.sw_crypto); /* Apply the new configuration */ rc = iwl_send_cmd_pdu(priv, REPLY_RXON, @@ -2551,7 +2556,7 @@ int iwl3945_hw_set_hw_params(struct iwl_priv *priv) } /* Assign number of Usable TX queues */ - priv->hw_params.max_txq_num = TFD_QUEUE_MAX; + priv->hw_params.max_txq_num = IWL39_NUM_QUEUES; priv->hw_params.tfd_size = sizeof(struct iwl3945_tfd); priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_3K; @@ -2562,6 +2567,7 @@ int iwl3945_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.bcast_sta_id = IWL3945_BROADCAST_ID; priv->hw_params.rx_wrt_ptr_reg = FH39_RSCSR_CHNL0_WPTR; + priv->hw_params.max_beacon_itrvl = IWL39_MAX_UCODE_BEACON_INTERVAL; return 0; } @@ -2784,11 +2790,50 @@ static int iwl3945_load_bsm(struct iwl_priv *priv) return 0; } +#define IWL3945_UCODE_GET(item) \ +static u32 iwl3945_ucode_get_##item(const struct iwl_ucode_header *ucode,\ + u32 api_ver) \ +{ \ + return le32_to_cpu(ucode->u.v1.item); \ +} + +static u32 iwl3945_ucode_get_header_size(u32 api_ver) +{ + return UCODE_HEADER_SIZE(1); +} +static u32 iwl3945_ucode_get_build(const struct iwl_ucode_header *ucode, + u32 api_ver) +{ + return 0; +} +static u8 *iwl3945_ucode_get_data(const struct iwl_ucode_header *ucode, + u32 api_ver) +{ + return (u8 *) ucode->u.v1.data; +} + +IWL3945_UCODE_GET(inst_size); +IWL3945_UCODE_GET(data_size); +IWL3945_UCODE_GET(init_size); +IWL3945_UCODE_GET(init_data_size); +IWL3945_UCODE_GET(boot_size); + static struct iwl_hcmd_ops iwl3945_hcmd = { .rxon_assoc = iwl3945_send_rxon_assoc, .commit_rxon = iwl3945_commit_rxon, }; +static struct iwl_ucode_ops iwl3945_ucode = { + .get_header_size = iwl3945_ucode_get_header_size, + .get_build = iwl3945_ucode_get_build, + .get_inst_size = iwl3945_ucode_get_inst_size, + .get_data_size = iwl3945_ucode_get_data_size, + .get_init_size = iwl3945_ucode_get_init_size, + .get_init_data_size = iwl3945_ucode_get_init_data_size, + .get_boot_size = iwl3945_ucode_get_boot_size, + .get_data = iwl3945_ucode_get_data, +}; + static struct iwl_lib_ops iwl3945_lib = { .txq_attach_buf_to_tfd = iwl3945_hw_txq_attach_buf_to_tfd, .txq_free_tfd = iwl3945_hw_txq_free_tfd, @@ -2808,8 +2853,8 @@ static struct iwl_lib_ops iwl3945_lib = { EEPROM_REGULATORY_BAND_3_CHANNELS, EEPROM_REGULATORY_BAND_4_CHANNELS, EEPROM_REGULATORY_BAND_5_CHANNELS, - EEPROM_REGULATORY_BAND_NO_FAT, - EEPROM_REGULATORY_BAND_NO_FAT, + EEPROM_REGULATORY_BAND_NO_HT40, + EEPROM_REGULATORY_BAND_NO_HT40, }, .verify_signature = iwlcore_eeprom_verify_signature, .acquire_semaphore = iwl3945_eeprom_acquire_semaphore, @@ -2829,6 +2874,7 @@ static struct iwl_hcmd_utils_ops iwl3945_hcmd_utils = { }; static struct iwl_ops iwl3945_ops = { + .ucode = &iwl3945_ucode, .lib = &iwl3945_lib, .hcmd = &iwl3945_hcmd, .utils = &iwl3945_hcmd_utils, @@ -2844,7 +2890,8 @@ static struct iwl_cfg iwl3945_bg_cfg = { .eeprom_ver = EEPROM_3945_EEPROM_VERSION, .ops = &iwl3945_ops, .mod_params = &iwl3945_mod_params, - .use_isr_legacy = true + .use_isr_legacy = true, + .ht_greenfield_support = false, }; static struct iwl_cfg iwl3945_abg_cfg = { @@ -2857,7 +2904,8 @@ static struct iwl_cfg iwl3945_abg_cfg = { .eeprom_ver = EEPROM_3945_EEPROM_VERSION, .ops = &iwl3945_ops, .mod_params = &iwl3945_mod_params, - .use_isr_legacy = true + .use_isr_legacy = true, + .ht_greenfield_support = false, }; struct pci_device_id iwl3945_hw_card_ids[] = { diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h index 2de6471d4be9..f24036909916 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945.h @@ -111,9 +111,6 @@ enum iwl3945_antenna { #define IWL_TX_FIFO_HCCA_2 6 #define IWL_TX_FIFO_NONE 7 -/* Minimum number of queues. MAX_NUM is defined in hw specific files */ -#define IWL39_MIN_NUM_QUEUES 4 - #define IEEE80211_DATA_LEN 2304 #define IEEE80211_4ADDR_LEN 30 #define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) @@ -257,10 +254,11 @@ extern int iwl3945_hw_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq); extern unsigned int iwl3945_hw_get_beacon_cmd(struct iwl_priv *priv, struct iwl3945_frame *frame, u8 rate); -void iwl3945_hw_build_tx_cmd_rate(struct iwl_priv *priv, struct iwl_cmd *cmd, - struct ieee80211_tx_info *info, - struct ieee80211_hdr *hdr, - int sta_id, int tx_id); +void iwl3945_hw_build_tx_cmd_rate(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, + int sta_id, int tx_id); extern int iwl3945_hw_reg_send_txpower(struct iwl_priv *priv); extern int iwl3945_hw_reg_set_txpower(struct iwl_priv *priv, s8 power); extern void iwl3945_hw_rx_statistics(struct iwl_priv *priv, diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h index a71a489096ff..b34322a32458 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-4965-hw.h @@ -188,7 +188,7 @@ static inline int iwl4965_hw_valid_rtc_data_addr(u32 addr) * * 1) Regulatory information (max txpower and channel usage flags) is provided * separately for each channel that can possibly supported by 4965. - * 40 MHz wide (.11n fat) channels are listed separately from 20 MHz + * 40 MHz wide (.11n HT40) channels are listed separately from 20 MHz * (legacy) channels. * * See struct iwl4965_eeprom_channel for format, and struct iwl4965_eeprom @@ -251,8 +251,8 @@ static inline int iwl4965_hw_valid_rtc_data_addr(u32 addr) * no reduction (such as with regulatory txpower limits) is required. * * Saturation and Backoff values apply equally to 20 Mhz (legacy) channel - * widths and 40 Mhz (.11n fat) channel widths; there is no separate - * factory measurement for fat channels. + * widths and 40 Mhz (.11n HT40) channel widths; there is no separate + * factory measurement for ht40 channels. * * The result of this step is the final target txpower. The rest of * the steps figure out the proper settings for the device to achieve diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 8f3d4bc6a03f..6a13bfbc9d98 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -46,7 +46,7 @@ #include "iwl-sta.h" static int iwl4965_send_tx_power(struct iwl_priv *priv); -static int iwl4965_hw_get_temperature(const struct iwl_priv *priv); +static int iwl4965_hw_get_temperature(struct iwl_priv *priv); /* Highest firmware API version supported */ #define IWL4965_UCODE_API_MAX 2 @@ -293,7 +293,7 @@ restart: queue_work(priv->workqueue, &priv->restart); } -static bool is_fat_channel(__le32 rxon_flags) +static bool is_ht40_channel(__le32 rxon_flags) { int chan_mod = le32_to_cpu(rxon_flags & RXON_FLG_CHANNEL_MODE_MSK) >> RXON_FLG_CHANNEL_MODE_POS; @@ -728,7 +728,7 @@ static int iwl4965_alive_notify(struct iwl_priv *priv) static struct iwl_sensitivity_ranges iwl4965_sensitivity = { .min_nrg_cck = 97, - .max_nrg_cck = 0, + .max_nrg_cck = 0, /* not used, set to 0 */ .auto_corr_min_ofdm = 85, .auto_corr_min_ofdm_mrc = 170, @@ -752,7 +752,8 @@ static struct iwl_sensitivity_ranges iwl4965_sensitivity = { static void iwl4965_set_ct_threshold(struct iwl_priv *priv) { /* want Kelvin */ - priv->hw_params.ct_kill_threshold = CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD); + priv->hw_params.ct_kill_threshold = + CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY); } /** @@ -781,7 +782,7 @@ static int iwl4965_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.max_data_size = IWL49_RTC_DATA_SIZE; priv->hw_params.max_inst_size = IWL49_RTC_INST_SIZE; priv->hw_params.max_bsm_size = BSM_SRAM_SIZE; - priv->hw_params.fat_channel = BIT(IEEE80211_BAND_5GHZ); + priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_5GHZ); priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR; @@ -1241,7 +1242,7 @@ static const struct gain_entry gain_table[2][108] = { }; static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel, - u8 is_fat, u8 ctrl_chan_high, + u8 is_ht40, u8 ctrl_chan_high, struct iwl4965_tx_power_db *tx_power_tbl) { u8 saturation_power; @@ -1273,8 +1274,8 @@ static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel, user_target_power = 2 * priv->tx_power_user_lmt; /* Get current (RXON) channel, band, width */ - IWL_DEBUG_TXPOWER(priv, "chan %d band %d is_fat %d\n", channel, band, - is_fat); + IWL_DEBUG_TXPOWER(priv, "chan %d band %d is_ht40 %d\n", channel, band, + is_ht40); ch_info = iwl_get_channel_info(priv, priv->band, channel); @@ -1293,7 +1294,7 @@ static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel, IWL_DEBUG_TXPOWER(priv, "channel %d belongs to txatten group %d\n", channel, txatten_grp); - if (is_fat) { + if (is_ht40) { if (ctrl_chan_high) channel -= 2; else @@ -1317,8 +1318,8 @@ static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel, /* regulatory txpower limits ... reg_limit values are in half-dBm, * max_power_avg values are in dBm, convert * 2 */ - if (is_fat) - reg_limit = ch_info->fat_max_power_avg * 2; + if (is_ht40) + reg_limit = ch_info->ht40_max_power_avg * 2; else reg_limit = ch_info->max_power_avg * 2; @@ -1484,7 +1485,7 @@ static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel, /** * iwl4965_send_tx_power - Configure the TXPOWER level user limit * - * Uses the active RXON for channel, band, and characteristics (fat, high) + * Uses the active RXON for channel, band, and characteristics (ht40, high) * The power limit is taken from priv->tx_power_user_lmt. */ static int iwl4965_send_tx_power(struct iwl_priv *priv) @@ -1492,7 +1493,7 @@ static int iwl4965_send_tx_power(struct iwl_priv *priv) struct iwl4965_txpowertable_cmd cmd = { 0 }; int ret; u8 band = 0; - bool is_fat = false; + bool is_ht40 = false; u8 ctrl_chan_high = 0; if (test_bit(STATUS_SCANNING, &priv->status)) { @@ -1505,9 +1506,9 @@ static int iwl4965_send_tx_power(struct iwl_priv *priv) band = priv->band == IEEE80211_BAND_2GHZ; - is_fat = is_fat_channel(priv->active_rxon.flags); + is_ht40 = is_ht40_channel(priv->active_rxon.flags); - if (is_fat && + if (is_ht40 && (priv->active_rxon.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) ctrl_chan_high = 1; @@ -1516,7 +1517,7 @@ static int iwl4965_send_tx_power(struct iwl_priv *priv) ret = iwl4965_fill_txpower_tbl(priv, band, le16_to_cpu(priv->active_rxon.channel), - is_fat, ctrl_chan_high, &cmd.tx_power); + is_ht40, ctrl_chan_high, &cmd.tx_power); if (ret) goto out; @@ -1570,7 +1571,7 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel) { int rc; u8 band = 0; - bool is_fat = false; + bool is_ht40 = false; u8 ctrl_chan_high = 0; struct iwl4965_channel_switch_cmd cmd = { 0 }; const struct iwl_channel_info *ch_info; @@ -1579,9 +1580,9 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel) ch_info = iwl_get_channel_info(priv, priv->band, channel); - is_fat = is_fat_channel(priv->staging_rxon.flags); + is_ht40 = is_ht40_channel(priv->staging_rxon.flags); - if (is_fat && + if (is_ht40 && (priv->active_rxon.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) ctrl_chan_high = 1; @@ -1596,7 +1597,7 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, u16 channel) else cmd.expect_beacon = 1; - rc = iwl4965_fill_txpower_tbl(priv, band, channel, is_fat, + rc = iwl4965_fill_txpower_tbl(priv, band, channel, is_ht40, ctrl_chan_high, &cmd.tx_power); if (rc) { IWL_DEBUG_11H(priv, "error:%d fill txpower_tbl\n", rc); @@ -1655,7 +1656,7 @@ static s32 sign_extend(u32 oper, int index) * * A return of <0 indicates bogus data in the statistics */ -static int iwl4965_hw_get_temperature(const struct iwl_priv *priv) +static int iwl4965_hw_get_temperature(struct iwl_priv *priv) { s32 temperature; s32 vt; @@ -1663,8 +1664,8 @@ static int iwl4965_hw_get_temperature(const struct iwl_priv *priv) u32 R4; if (test_bit(STATUS_TEMPERATURE, &priv->status) && - (priv->statistics.flag & STATISTICS_REPLY_FLG_FAT_MODE_MSK)) { - IWL_DEBUG_TEMP(priv, "Running FAT temperature calibration\n"); + (priv->statistics.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK)) { + IWL_DEBUG_TEMP(priv, "Running HT40 temperature calibration\n"); R1 = (s32)le32_to_cpu(priv->card_alive_init.therm_r1[1]); R2 = (s32)le32_to_cpu(priv->card_alive_init.therm_r2[1]); R3 = (s32)le32_to_cpu(priv->card_alive_init.therm_r3[1]); @@ -1772,6 +1773,7 @@ static void iwl4965_temperature_calib(struct iwl_priv *priv) } priv->temperature = temp; + iwl_tt_handler(priv); set_bit(STATUS_TEMPERATURE, &priv->status); if (!priv->disable_tx_power_cal && @@ -2221,12 +2223,50 @@ static void iwl4965_cancel_deferred_work(struct iwl_priv *priv) cancel_work_sync(&priv->txpower_work); } +#define IWL4965_UCODE_GET(item) \ +static u32 iwl4965_ucode_get_##item(const struct iwl_ucode_header *ucode,\ + u32 api_ver) \ +{ \ + return le32_to_cpu(ucode->u.v1.item); \ +} + +static u32 iwl4965_ucode_get_header_size(u32 api_ver) +{ + return UCODE_HEADER_SIZE(1); +} +static u32 iwl4965_ucode_get_build(const struct iwl_ucode_header *ucode, + u32 api_ver) +{ + return 0; +} +static u8 *iwl4965_ucode_get_data(const struct iwl_ucode_header *ucode, + u32 api_ver) +{ + return (u8 *) ucode->u.v1.data; +} + +IWL4965_UCODE_GET(inst_size); +IWL4965_UCODE_GET(data_size); +IWL4965_UCODE_GET(init_size); +IWL4965_UCODE_GET(init_data_size); +IWL4965_UCODE_GET(boot_size); + static struct iwl_hcmd_ops iwl4965_hcmd = { .rxon_assoc = iwl4965_send_rxon_assoc, .commit_rxon = iwl_commit_rxon, .set_rxon_chain = iwl_set_rxon_chain, }; +static struct iwl_ucode_ops iwl4965_ucode = { + .get_header_size = iwl4965_ucode_get_header_size, + .get_build = iwl4965_ucode_get_build, + .get_inst_size = iwl4965_ucode_get_inst_size, + .get_data_size = iwl4965_ucode_get_data_size, + .get_init_size = iwl4965_ucode_get_init_size, + .get_init_data_size = iwl4965_ucode_get_init_data_size, + .get_boot_size = iwl4965_ucode_get_boot_size, + .get_data = iwl4965_ucode_get_data, +}; static struct iwl_hcmd_utils_ops iwl4965_hcmd_utils = { .get_hcmd_size = iwl4965_get_hcmd_size, .build_addsta_hcmd = iwl4965_build_addsta_hcmd, @@ -2266,8 +2306,8 @@ static struct iwl_lib_ops iwl4965_lib = { EEPROM_REGULATORY_BAND_3_CHANNELS, EEPROM_REGULATORY_BAND_4_CHANNELS, EEPROM_REGULATORY_BAND_5_CHANNELS, - EEPROM_4965_REGULATORY_BAND_24_FAT_CHANNELS, - EEPROM_4965_REGULATORY_BAND_52_FAT_CHANNELS + EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS, + EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS }, .verify_signature = iwlcore_eeprom_verify_signature, .acquire_semaphore = iwlcore_eeprom_acquire_semaphore, @@ -2287,6 +2327,7 @@ static struct iwl_lib_ops iwl4965_lib = { }; static struct iwl_ops iwl4965_ops = { + .ucode = &iwl4965_ucode, .lib = &iwl4965_lib, .hcmd = &iwl4965_hcmd, .utils = &iwl4965_hcmd_utils, @@ -2303,7 +2344,8 @@ struct iwl_cfg iwl4965_agn_cfg = { .eeprom_calib_ver = EEPROM_4965_TX_POWER_VERSION, .ops = &iwl4965_ops, .mod_params = &iwl4965_mod_params, - .use_isr_legacy = true + .use_isr_legacy = true, + .ht_greenfield_support = false, }; /* Module firmware */ @@ -2313,8 +2355,6 @@ module_param_named(antenna, iwl4965_mod_params.antenna, int, 0444); MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); module_param_named(swcrypto, iwl4965_mod_params.sw_crypto, int, 0444); MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); -module_param_named(debug, iwl4965_mod_params.debug, uint, 0444); -MODULE_PARM_DESC(debug, "debug output mask"); module_param_named( disable_hw_scan, iwl4965_mod_params.disable_hw_scan, int, 0444); MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index b3c648ce8c7b..1d539e3b8db1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -91,7 +91,7 @@ static int iwl5000_apm_stop_master(struct iwl_priv *priv) } -static int iwl5000_apm_init(struct iwl_priv *priv) +int iwl5000_apm_init(struct iwl_priv *priv) { int ret = 0; @@ -137,7 +137,7 @@ static int iwl5000_apm_init(struct iwl_priv *priv) } /* FIXME: this is identical to 4965 */ -static void iwl5000_apm_stop(struct iwl_priv *priv) +void iwl5000_apm_stop(struct iwl_priv *priv) { unsigned long flags; @@ -156,7 +156,7 @@ static void iwl5000_apm_stop(struct iwl_priv *priv) } -static int iwl5000_apm_reset(struct iwl_priv *priv) +int iwl5000_apm_reset(struct iwl_priv *priv) { int ret = 0; @@ -198,7 +198,8 @@ out: } -static void iwl5000_nic_config(struct iwl_priv *priv) +/* NIC configuration for 5000 series and up */ +void iwl5000_nic_config(struct iwl_priv *priv) { unsigned long flags; u16 radio_cfg; @@ -239,11 +240,11 @@ static void iwl5000_nic_config(struct iwl_priv *priv) APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); + spin_unlock_irqrestore(&priv->lock, flags); } - /* * EEPROM */ @@ -283,7 +284,7 @@ static u32 eeprom_indirect_address(const struct iwl_priv *priv, u32 address) return (address & ADDRESS_MSK) + (offset << 1); } -static u16 iwl5000_eeprom_calib_version(struct iwl_priv *priv) +u16 iwl5000_eeprom_calib_version(struct iwl_priv *priv) { struct iwl_eeprom_calib_hdr { u8 version; @@ -388,7 +389,7 @@ void iwl5000_rts_tx_cmd_flag(struct ieee80211_tx_info *info, static struct iwl_sensitivity_ranges iwl5000_sensitivity = { .min_nrg_cck = 95, - .max_nrg_cck = 0, + .max_nrg_cck = 0, /* not used, set to 0 */ .auto_corr_min_ofdm = 90, .auto_corr_min_ofdm_mrc = 170, .auto_corr_min_ofdm_x1 = 120, @@ -407,7 +408,29 @@ static struct iwl_sensitivity_ranges iwl5000_sensitivity = { .nrg_th_ofdm = 95, }; -static const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv, +static struct iwl_sensitivity_ranges iwl5150_sensitivity = { + .min_nrg_cck = 95, + .max_nrg_cck = 0, /* not used, set to 0 */ + .auto_corr_min_ofdm = 90, + .auto_corr_min_ofdm_mrc = 170, + .auto_corr_min_ofdm_x1 = 105, + .auto_corr_min_ofdm_mrc_x1 = 220, + + .auto_corr_max_ofdm = 120, + .auto_corr_max_ofdm_mrc = 210, + /* max = min for performance bug in 5150 DSP */ + .auto_corr_max_ofdm_x1 = 105, + .auto_corr_max_ofdm_mrc_x1 = 220, + + .auto_corr_min_cck = 125, + .auto_corr_max_cck = 200, + .auto_corr_min_cck_mrc = 170, + .auto_corr_max_cck_mrc = 400, + .nrg_th_cck = 95, + .nrg_th_ofdm = 95, +}; + +const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv, size_t offset) { u32 address = eeprom_indirect_address(priv, offset); @@ -418,7 +441,7 @@ static const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv, static void iwl5150_set_ct_threshold(struct iwl_priv *priv) { const s32 volt2temp_coef = IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF; - s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD) - + s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY) - iwl_temp_calib_to_offset(priv); priv->hw_params.ct_kill_threshold = threshold * volt2temp_coef; @@ -427,7 +450,7 @@ static void iwl5150_set_ct_threshold(struct iwl_priv *priv) static void iwl5000_set_ct_threshold(struct iwl_priv *priv) { /* want Celsius */ - priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD_LEGACY; } /* @@ -471,7 +494,7 @@ static void iwl5000_rx_calib_result(struct iwl_priv *priv, { struct iwl_rx_packet *pkt = (void *)rxb->skb->data; struct iwl_calib_hdr *hdr = (struct iwl_calib_hdr *)pkt->u.raw; - int len = le32_to_cpu(pkt->len) & FH_RSCSR_FRAME_SIZE_MSK; + int len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; int index; /* reduce the size of the length field itself */ @@ -602,7 +625,7 @@ static int iwl5000_load_given_ucode(struct iwl_priv *priv, return ret; } -static int iwl5000_load_ucode(struct iwl_priv *priv) +int iwl5000_load_ucode(struct iwl_priv *priv) { int ret = 0; @@ -629,7 +652,7 @@ static int iwl5000_load_ucode(struct iwl_priv *priv) return ret; } -static void iwl5000_init_alive_start(struct iwl_priv *priv) +void iwl5000_init_alive_start(struct iwl_priv *priv) { int ret = 0; @@ -705,7 +728,7 @@ static int iwl5000_send_wimax_coex(struct iwl_priv *priv) sizeof(coex_cmd), &coex_cmd); } -static int iwl5000_alive_notify(struct iwl_priv *priv) +int iwl5000_alive_notify(struct iwl_priv *priv) { u32 a; unsigned long flags; @@ -792,7 +815,7 @@ static int iwl5000_alive_notify(struct iwl_priv *priv) return 0; } -static int iwl5000_hw_set_hw_params(struct iwl_priv *priv) +int iwl5000_hw_set_hw_params(struct iwl_priv *priv) { if ((priv->cfg->mod_params->num_of_queues > IWL50_NUM_QUEUES) || (priv->cfg->mod_params->num_of_queues < IWL_MIN_NUM_QUEUES)) { @@ -822,12 +845,10 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv) } priv->hw_params.max_bsm_size = 0; - priv->hw_params.fat_channel = BIT(IEEE80211_BAND_2GHZ) | + priv->hw_params.ht40_channel = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ); priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR; - priv->hw_params.sens = &iwl5000_sensitivity; - priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant); priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant); priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant; @@ -836,9 +857,11 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv) if (priv->cfg->ops->lib->temp_ops.set_ct_kill) priv->cfg->ops->lib->temp_ops.set_ct_kill(priv); + /* Set initial sensitivity parameters */ /* Set initial calibration set */ switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { case CSR_HW_REV_TYPE_5150: + priv->hw_params.sens = &iwl5150_sensitivity; priv->hw_params.calib_init_cfg = BIT(IWL_CALIB_DC) | BIT(IWL_CALIB_LO) | @@ -847,6 +870,7 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv) break; default: + priv->hw_params.sens = &iwl5000_sensitivity; priv->hw_params.calib_init_cfg = BIT(IWL_CALIB_XTAL) | BIT(IWL_CALIB_LO) | @@ -862,7 +886,7 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv) /** * iwl5000_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array */ -static void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv, +void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv, struct iwl_tx_queue *txq, u16 byte_cnt) { @@ -902,7 +926,7 @@ static void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv, tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent; } -static void iwl5000_txq_inval_byte_cnt_tbl(struct iwl_priv *priv, +void iwl5000_txq_inval_byte_cnt_tbl(struct iwl_priv *priv, struct iwl_tx_queue *txq) { struct iwl5000_scd_bc_tbl *scd_bc_tbl = priv->scd_bc_tbls.addr; @@ -957,7 +981,7 @@ static void iwl5000_tx_queue_stop_scheduler(struct iwl_priv *priv, u16 txq_id) (1 << IWL50_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); } -static int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id, +int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id, int tx_fifo, int sta_id, int tid, u16 ssn_idx) { unsigned long flags; @@ -1018,7 +1042,7 @@ static int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id, return 0; } -static int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, +int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, u16 ssn_idx, u8 tx_fifo) { if ((IWL50_FIRST_AMPDU_QUEUE > txq_id) || @@ -1061,7 +1085,7 @@ u16 iwl5000_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data) * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask * must be called under priv->lock and mac access */ -static void iwl5000_txq_set_sched(struct iwl_priv *priv, u32 mask) +void iwl5000_txq_set_sched(struct iwl_priv *priv, u32 mask) { iwl_write_prph(priv, IWL50_SCD_TXFACT, mask); } @@ -1282,13 +1306,13 @@ u16 iwl5000_get_hcmd_size(u8 cmd_id, u16 len) return len; } -static void iwl5000_setup_deferred_work(struct iwl_priv *priv) +void iwl5000_setup_deferred_work(struct iwl_priv *priv) { /* in 5000 the tx power calibration is done in uCode */ priv->disable_tx_power_cal = 1; } -static void iwl5000_rx_handler_setup(struct iwl_priv *priv) +void iwl5000_rx_handler_setup(struct iwl_priv *priv) { /* init calibration handlers */ priv->rx_handlers[CALIBRATION_RES_NOTIFICATION] = @@ -1299,7 +1323,7 @@ static void iwl5000_rx_handler_setup(struct iwl_priv *priv) } -static int iwl5000_hw_valid_rtc_data_addr(u32 addr) +int iwl5000_hw_valid_rtc_data_addr(u32 addr) { return (addr >= IWL50_RTC_DATA_LOWER_BOUND) && (addr < IWL50_RTC_DATA_UPPER_BOUND); @@ -1351,7 +1375,7 @@ static int iwl5000_send_rxon_assoc(struct iwl_priv *priv) return ret; } -static int iwl5000_send_tx_power(struct iwl_priv *priv) +int iwl5000_send_tx_power(struct iwl_priv *priv) { struct iwl5000_tx_power_dbm_cmd tx_power_cmd; u8 tx_ant_cfg_cmd; @@ -1371,10 +1395,11 @@ static int iwl5000_send_tx_power(struct iwl_priv *priv) NULL); } -static void iwl5000_temperature(struct iwl_priv *priv) +void iwl5000_temperature(struct iwl_priv *priv) { /* store temperature from statistics (in Celsius) */ priv->temperature = le32_to_cpu(priv->statistics.general.temperature); + iwl_tt_handler(priv); } static void iwl5150_temperature(struct iwl_priv *priv) @@ -1386,6 +1411,7 @@ static void iwl5150_temperature(struct iwl_priv *priv) vt = vt / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF + offset; /* now vt hold the temperature in Kelvin */ priv->temperature = KELVIN_TO_CELSIUS(vt); + iwl_tt_handler(priv); } /* Calc max signal level (dBm) among 3 possible receivers */ @@ -1426,6 +1452,44 @@ int iwl5000_calc_rssi(struct iwl_priv *priv, return max_rssi - agc - IWL49_RSSI_OFFSET; } +#define IWL5000_UCODE_GET(item) \ +static u32 iwl5000_ucode_get_##item(const struct iwl_ucode_header *ucode,\ + u32 api_ver) \ +{ \ + if (api_ver <= 2) \ + return le32_to_cpu(ucode->u.v1.item); \ + return le32_to_cpu(ucode->u.v2.item); \ +} + +static u32 iwl5000_ucode_get_header_size(u32 api_ver) +{ + if (api_ver <= 2) + return UCODE_HEADER_SIZE(1); + return UCODE_HEADER_SIZE(2); +} + +static u32 iwl5000_ucode_get_build(const struct iwl_ucode_header *ucode, + u32 api_ver) +{ + if (api_ver <= 2) + return 0; + return le32_to_cpu(ucode->u.v2.build); +} + +static u8 *iwl5000_ucode_get_data(const struct iwl_ucode_header *ucode, + u32 api_ver) +{ + if (api_ver <= 2) + return (u8 *) ucode->u.v1.data; + return (u8 *) ucode->u.v2.data; +} + +IWL5000_UCODE_GET(inst_size); +IWL5000_UCODE_GET(data_size); +IWL5000_UCODE_GET(init_size); +IWL5000_UCODE_GET(init_data_size); +IWL5000_UCODE_GET(boot_size); + struct iwl_hcmd_ops iwl5000_hcmd = { .rxon_assoc = iwl5000_send_rxon_assoc, .commit_rxon = iwl_commit_rxon, @@ -1441,6 +1505,17 @@ struct iwl_hcmd_utils_ops iwl5000_hcmd_utils = { .calc_rssi = iwl5000_calc_rssi, }; +struct iwl_ucode_ops iwl5000_ucode = { + .get_header_size = iwl5000_ucode_get_header_size, + .get_build = iwl5000_ucode_get_build, + .get_inst_size = iwl5000_ucode_get_inst_size, + .get_data_size = iwl5000_ucode_get_data_size, + .get_init_size = iwl5000_ucode_get_init_size, + .get_init_data_size = iwl5000_ucode_get_init_data_size, + .get_boot_size = iwl5000_ucode_get_boot_size, + .get_data = iwl5000_ucode_get_data, +}; + struct iwl_lib_ops iwl5000_lib = { .set_hw_params = iwl5000_hw_set_hw_params, .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, @@ -1473,8 +1548,8 @@ struct iwl_lib_ops iwl5000_lib = { EEPROM_5000_REG_BAND_3_CHANNELS, EEPROM_5000_REG_BAND_4_CHANNELS, EEPROM_5000_REG_BAND_5_CHANNELS, - EEPROM_5000_REG_BAND_24_FAT_CHANNELS, - EEPROM_5000_REG_BAND_52_FAT_CHANNELS + EEPROM_5000_REG_BAND_24_HT40_CHANNELS, + EEPROM_5000_REG_BAND_52_HT40_CHANNELS }, .verify_signature = iwlcore_eeprom_verify_signature, .acquire_semaphore = iwlcore_eeprom_acquire_semaphore, @@ -1523,8 +1598,8 @@ static struct iwl_lib_ops iwl5150_lib = { EEPROM_5000_REG_BAND_3_CHANNELS, EEPROM_5000_REG_BAND_4_CHANNELS, EEPROM_5000_REG_BAND_5_CHANNELS, - EEPROM_5000_REG_BAND_24_FAT_CHANNELS, - EEPROM_5000_REG_BAND_52_FAT_CHANNELS + EEPROM_5000_REG_BAND_24_HT40_CHANNELS, + EEPROM_5000_REG_BAND_52_HT40_CHANNELS }, .verify_signature = iwlcore_eeprom_verify_signature, .acquire_semaphore = iwlcore_eeprom_acquire_semaphore, @@ -1542,12 +1617,14 @@ static struct iwl_lib_ops iwl5150_lib = { }; struct iwl_ops iwl5000_ops = { + .ucode = &iwl5000_ucode, .lib = &iwl5000_lib, .hcmd = &iwl5000_hcmd, .utils = &iwl5000_hcmd_utils, }; static struct iwl_ops iwl5150_ops = { + .ucode = &iwl5000_ucode, .lib = &iwl5150_lib, .hcmd = &iwl5000_hcmd, .utils = &iwl5000_hcmd_utils, @@ -1576,6 +1653,7 @@ struct iwl_cfg iwl5300_agn_cfg = { .valid_tx_ant = ANT_ABC, .valid_rx_ant = ANT_ABC, .need_pll_cfg = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl5100_bg_cfg = { @@ -1592,6 +1670,7 @@ struct iwl_cfg iwl5100_bg_cfg = { .valid_tx_ant = ANT_B, .valid_rx_ant = ANT_AB, .need_pll_cfg = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl5100_abg_cfg = { @@ -1608,6 +1687,7 @@ struct iwl_cfg iwl5100_abg_cfg = { .valid_tx_ant = ANT_B, .valid_rx_ant = ANT_AB, .need_pll_cfg = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl5100_agn_cfg = { @@ -1624,6 +1704,7 @@ struct iwl_cfg iwl5100_agn_cfg = { .valid_tx_ant = ANT_B, .valid_rx_ant = ANT_AB, .need_pll_cfg = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl5350_agn_cfg = { @@ -1640,6 +1721,7 @@ struct iwl_cfg iwl5350_agn_cfg = { .valid_tx_ant = ANT_ABC, .valid_rx_ant = ANT_ABC, .need_pll_cfg = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl5150_agn_cfg = { @@ -1656,6 +1738,7 @@ struct iwl_cfg iwl5150_agn_cfg = { .valid_tx_ant = ANT_A, .valid_rx_ant = ANT_AB, .need_pll_cfg = true, + .ht_greenfield_support = true, }; MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX)); @@ -1664,8 +1747,6 @@ MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_MAX)); module_param_named(swcrypto50, iwl50_mod_params.sw_crypto, bool, 0444); MODULE_PARM_DESC(swcrypto50, "using software crypto engine (default 0 [hardware])\n"); -module_param_named(debug50, iwl50_mod_params.debug, uint, 0444); -MODULE_PARM_DESC(debug50, "50XX debug output mask"); module_param_named(queues_num50, iwl50_mod_params.num_of_queues, int, 0444); MODULE_PARM_DESC(queues_num50, "number of hw queues in 50xx series"); module_param_named(11n_disable50, iwl50_mod_params.disable_11n, int, 0444); diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index bd438d8acf55..82b9c93dff54 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -46,8 +46,8 @@ #include "iwl-5000-hw.h" /* Highest firmware API version supported */ -#define IWL6000_UCODE_API_MAX 2 -#define IWL6050_UCODE_API_MAX 2 +#define IWL6000_UCODE_API_MAX 4 +#define IWL6050_UCODE_API_MAX 4 /* Lowest firmware API version supported */ #define IWL6000_UCODE_API_MIN 1 @@ -61,6 +61,82 @@ #define _IWL6050_MODULE_FIRMWARE(api) IWL6050_FW_PRE #api ".ucode" #define IWL6050_MODULE_FIRMWARE(api) _IWL6050_MODULE_FIRMWARE(api) +static void iwl6000_set_ct_threshold(struct iwl_priv *priv) +{ + /* want Celsius */ + priv->hw_params.ct_kill_threshold = CT_KILL_THRESHOLD; + priv->hw_params.ct_kill_exit_threshold = CT_KILL_EXIT_THRESHOLD; +} + +/* NIC configuration for 6000 series */ +static void iwl6000_nic_config(struct iwl_priv *priv) +{ + iwl5000_nic_config(priv); + + /* no locking required for register write */ + if (priv->cfg->pa_type == IWL_PA_HYBRID) { + /* 2x2 hybrid phy type */ + iwl_write32(priv, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_HYB); + } else if (priv->cfg->pa_type == IWL_PA_INTERNAL) { + /* 2x2 IPA phy type */ + iwl_write32(priv, CSR_GP_DRIVER_REG, + CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA); + } + /* else do nothing, uCode configured */ +} + +static struct iwl_lib_ops iwl6000_lib = { + .set_hw_params = iwl5000_hw_set_hw_params, + .txq_update_byte_cnt_tbl = iwl5000_txq_update_byte_cnt_tbl, + .txq_inval_byte_cnt_tbl = iwl5000_txq_inval_byte_cnt_tbl, + .txq_set_sched = iwl5000_txq_set_sched, + .txq_agg_enable = iwl5000_txq_agg_enable, + .txq_agg_disable = iwl5000_txq_agg_disable, + .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd, + .txq_free_tfd = iwl_hw_txq_free_tfd, + .txq_init = iwl_hw_tx_queue_init, + .rx_handler_setup = iwl5000_rx_handler_setup, + .setup_deferred_work = iwl5000_setup_deferred_work, + .is_valid_rtc_data_addr = iwl5000_hw_valid_rtc_data_addr, + .load_ucode = iwl5000_load_ucode, + .init_alive_start = iwl5000_init_alive_start, + .alive_notify = iwl5000_alive_notify, + .send_tx_power = iwl5000_send_tx_power, + .update_chain_flags = iwl_update_chain_flags, + .apm_ops = { + .init = iwl5000_apm_init, + .reset = iwl5000_apm_reset, + .stop = iwl5000_apm_stop, + .config = iwl6000_nic_config, + .set_pwr_src = iwl_set_pwr_src, + }, + .eeprom_ops = { + .regulatory_bands = { + EEPROM_5000_REG_BAND_1_CHANNELS, + EEPROM_5000_REG_BAND_2_CHANNELS, + EEPROM_5000_REG_BAND_3_CHANNELS, + EEPROM_5000_REG_BAND_4_CHANNELS, + EEPROM_5000_REG_BAND_5_CHANNELS, + EEPROM_5000_REG_BAND_24_HT40_CHANNELS, + EEPROM_5000_REG_BAND_52_HT40_CHANNELS + }, + .verify_signature = iwlcore_eeprom_verify_signature, + .acquire_semaphore = iwlcore_eeprom_acquire_semaphore, + .release_semaphore = iwlcore_eeprom_release_semaphore, + .calib_version = iwl5000_eeprom_calib_version, + .query_addr = iwl5000_eeprom_query_addr, + .update_enhanced_txpower = iwlcore_eeprom_enhanced_txpower, + }, + .post_associate = iwl_post_associate, + .isr = iwl_isr_ict, + .config_ap = iwl_config_ap, + .temp_ops = { + .temperature = iwl5000_temperature, + .set_ct_kill = iwl6000_set_ct_threshold, + }, +}; + static struct iwl_hcmd_utils_ops iwl6000_hcmd_utils = { .get_hcmd_size = iwl5000_get_hcmd_size, .build_addsta_hcmd = iwl5000_build_addsta_hcmd, @@ -69,41 +145,57 @@ static struct iwl_hcmd_utils_ops iwl6000_hcmd_utils = { }; static struct iwl_ops iwl6000_ops = { - .lib = &iwl5000_lib, + .ucode = &iwl5000_ucode, + .lib = &iwl6000_lib, .hcmd = &iwl5000_hcmd, .utils = &iwl6000_hcmd_utils, }; -struct iwl_cfg iwl6000_2ag_cfg = { - .name = "6000 Series 2x2 AG", + +/* + * "h": Hybrid configuration, use both internal and external Power Amplifier + */ +struct iwl_cfg iwl6000h_2agn_cfg = { + .name = "6000 Series 2x2 AGN", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, - .sku = IWL_SKU_A|IWL_SKU_G, + .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, .ops = &iwl6000_ops, - .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, + .eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, .mod_params = &iwl50_mod_params, - .valid_tx_ant = ANT_BC, - .valid_rx_ant = ANT_BC, + .valid_tx_ant = ANT_AB, + .valid_rx_ant = ANT_AB, .need_pll_cfg = false, + .pa_type = IWL_PA_HYBRID, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .ht_greenfield_support = true, }; -struct iwl_cfg iwl6000_2agn_cfg = { +/* + * "i": Internal configuration, use internal Power Amplifier + */ +struct iwl_cfg iwl6000i_2agn_cfg = { .name = "6000 Series 2x2 AGN", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, .ops = &iwl6000_ops, - .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, + .eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, .mod_params = &iwl50_mod_params, - .valid_tx_ant = ANT_AB, - .valid_rx_ant = ANT_AB, + .valid_tx_ant = ANT_BC, + .valid_rx_ant = ANT_BC, .need_pll_cfg = false, + .pa_type = IWL_PA_INTERNAL, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl6050_2agn_cfg = { @@ -113,13 +205,17 @@ struct iwl_cfg iwl6050_2agn_cfg = { .ucode_api_min = IWL6050_UCODE_API_MIN, .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, .ops = &iwl6000_ops, - .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, + .eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_AB, .valid_rx_ant = ANT_AB, .need_pll_cfg = false, + .pa_type = IWL_PA_SYSTEM, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl6000_3agn_cfg = { @@ -129,13 +225,17 @@ struct iwl_cfg iwl6000_3agn_cfg = { .ucode_api_min = IWL6000_UCODE_API_MIN, .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, .ops = &iwl6000_ops, - .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, + .eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_ABC, .valid_rx_ant = ANT_ABC, .need_pll_cfg = false, + .pa_type = IWL_PA_SYSTEM, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .ht_greenfield_support = true, }; struct iwl_cfg iwl6050_3agn_cfg = { @@ -145,13 +245,17 @@ struct iwl_cfg iwl6050_3agn_cfg = { .ucode_api_min = IWL6050_UCODE_API_MIN, .sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N, .ops = &iwl6000_ops, - .eeprom_size = IWL_5000_EEPROM_IMG_SIZE, + .eeprom_size = OTP_LOW_IMAGE_SIZE, .eeprom_ver = EEPROM_5000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_5000_TX_POWER_VERSION, .mod_params = &iwl50_mod_params, .valid_tx_ant = ANT_ABC, .valid_rx_ant = ANT_ABC, .need_pll_cfg = false, + .pa_type = IWL_PA_SYSTEM, + .max_ll_items = OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .ht_greenfield_support = true, }; MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index ff20e5048a55..26ec969e265d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -97,7 +97,7 @@ 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_ht40; /* 1 = 40 MHz channel width */ u8 is_dup; /* 1 = duplicated data streams */ u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ u8 max_search; /* maximun number of tables we can search */ @@ -177,7 +177,7 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, struct sk_buff *skb, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta); -static void rs_fill_link_cmd(const struct iwl_priv *priv, +static void rs_fill_link_cmd(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, u32 rate_n_flags); @@ -332,6 +332,9 @@ static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data, } else return MAX_TID_COUNT; + if (unlikely(tid >= TID_MAX_LOAD_COUNT)) + return MAX_TID_COUNT; + tl = &lq_data->load[tid]; curr_time -= curr_time % TID_ROUND_VALUE; @@ -539,11 +542,11 @@ static u32 rate_n_flags_from_tbl(struct iwl_priv *priv, RATE_MCS_ANT_ABC_MSK); if (is_Ht(tbl->lq_type)) { - if (tbl->is_fat) { + if (tbl->is_ht40) { if (tbl->is_dup) rate_n_flags |= RATE_MCS_DUP_MSK; else - rate_n_flags |= RATE_MCS_FAT_MSK; + rate_n_flags |= RATE_MCS_HT40_MSK; } if (tbl->is_SGI) rate_n_flags |= RATE_MCS_SGI_MSK; @@ -579,7 +582,7 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, return -EINVAL; } tbl->is_SGI = 0; /* default legacy setup */ - tbl->is_fat = 0; + tbl->is_ht40 = 0; tbl->is_dup = 0; tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); tbl->lq_type = LQ_NONE; @@ -598,9 +601,9 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, if (rate_n_flags & RATE_MCS_SGI_MSK) tbl->is_SGI = 1; - if ((rate_n_flags & RATE_MCS_FAT_MSK) || + if ((rate_n_flags & RATE_MCS_HT40_MSK) || (rate_n_flags & RATE_MCS_DUP_MSK)) - tbl->is_fat = 1; + tbl->is_ht40 = 1; if (rate_n_flags & RATE_MCS_DUP_MSK) tbl->is_dup = 1; @@ -654,19 +657,15 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, return 1; } -/* in 4965 we don't use greenfield at all */ -static inline u8 rs_use_green(struct iwl_priv *priv, - struct ieee80211_conf *conf) +/** + * Green-field mode is valid if the station supports it and + * there are no non-GF stations present in the BSS. + */ +static inline u8 rs_use_green(struct ieee80211_sta *sta, + struct iwl_ht_info *ht_conf) { - u8 is_green; - - if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965) - is_green = 0; - else - is_green = (conf_is_ht(conf) && - priv->current_ht_config.is_green_field && - !priv->current_ht_config.non_GF_STA_present); - return is_green; + return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !(ht_conf->non_GF_STA_present); } /** @@ -776,7 +775,7 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, if (num_of_ant(tbl->ant_type) > 1) tbl->ant_type = ANT_A;/*FIXME:RS*/ - tbl->is_fat = 0; + tbl->is_ht40 = 0; tbl->is_SGI = 0; tbl->max_search = IWL_MAX_SEARCH; } @@ -880,7 +879,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, if ((info->status.rates[0].idx < 0) || (tbl_type.is_SGI != !!(info->status.rates[0].flags & IEEE80211_TX_RC_SHORT_GI)) || - (tbl_type.is_fat != !!(info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || + (tbl_type.is_ht40 != !!(info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || (tbl_type.is_dup != !!(info->status.rates[0].flags & IEEE80211_TX_RC_DUP_DATA)) || (tbl_type.ant_type != info->antenna_sel_tx) || (!!(tx_rate & RATE_MCS_HT_MSK) != !!(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) || @@ -1049,7 +1048,7 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, 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_ht40 && !lq_sta->is_dup) if (tbl->is_SGI) tbl->expected_tpt = expected_tpt_siso40MHzSGI; else @@ -1059,7 +1058,7 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, else tbl->expected_tpt = expected_tpt_siso20MHz; } else if (is_mimo2(tbl->lq_type)) { - if (tbl->is_fat && !lq_sta->is_dup) + if (tbl->is_ht40 && !lq_sta->is_dup) if (tbl->is_SGI) tbl->expected_tpt = expected_tpt_mimo2_40MHzSGI; else @@ -1069,7 +1068,7 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, else tbl->expected_tpt = expected_tpt_mimo2_20MHz; } else if (is_mimo3(tbl->lq_type)) { - if (tbl->is_fat && !lq_sta->is_dup) + if (tbl->is_ht40 && !lq_sta->is_dup) if (tbl->is_SGI) tbl->expected_tpt = expected_tpt_mimo3_40MHzSGI; else @@ -1217,22 +1216,10 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv, tbl->max_search = IWL_MAX_SEARCH; rate_mask = lq_sta->active_mimo2_rate; - if (iwl_is_fat_tx_allowed(priv, &sta->ht_cap)) - tbl->is_fat = 1; + if (iwl_is_ht40_tx_allowed(priv, &sta->ht_cap)) + tbl->is_ht40 = 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; - */ + tbl->is_ht40 = 0; rs_set_expected_tpt_table(lq_sta, tbl); @@ -1283,22 +1270,10 @@ static int rs_switch_to_mimo3(struct iwl_priv *priv, tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH; rate_mask = lq_sta->active_mimo3_rate; - if (iwl_is_fat_tx_allowed(priv, &sta->ht_cap)) - 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; + if (iwl_is_ht40_tx_allowed(priv, &sta->ht_cap)) + tbl->is_ht40 = 1; else - tbl->is_SGI = 0; - */ + tbl->is_ht40 = 0; rs_set_expected_tpt_table(lq_sta, tbl); @@ -1342,22 +1317,10 @@ static int rs_switch_to_siso(struct iwl_priv *priv, tbl->max_search = IWL_MAX_SEARCH; rate_mask = lq_sta->active_siso_rate; - if (iwl_is_fat_tx_allowed(priv, &sta->ht_cap)) - 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; + if (iwl_is_ht40_tx_allowed(priv, &sta->ht_cap)) + tbl->is_ht40 = 1; else - tbl->is_SGI = 0; - */ + tbl->is_ht40 = 0; if (is_green) tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/ @@ -1398,6 +1361,12 @@ static int rs_move_legacy_other(struct iwl_priv *priv, int ret = 0; u8 update_search_tbl_counter = 0; + if (!iwl_ht_enabled(priv)) + /* stay in Legacy */ + tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + else if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && + tbl->action > IWL_LEGACY_SWITCH_SISO) + tbl->action = IWL_LEGACY_SWITCH_SISO; for (; ;) { lq_sta->action_counter++; switch (tbl->action) { @@ -1521,6 +1490,7 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, 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]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action = tbl->action; @@ -1529,6 +1499,11 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, u8 update_search_tbl_counter = 0; int ret; + if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE && + tbl->action > IWL_SISO_SWITCH_ANTENNA2) { + /* stay in SISO */ + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + } for (;;) { lq_sta->action_counter++; switch (tbl->action) { @@ -1575,13 +1550,11 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, goto out; break; case IWL_SISO_SWITCH_GI: - if (!tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_20MHZ)) + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) break; - if (tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_40MHZ)) + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) break; IWL_DEBUG_RATE(priv, "LQ: SISO toggle SGI/NGI\n"); @@ -1655,6 +1628,7 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv, 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]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action = tbl->action; @@ -1663,6 +1637,12 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv, u8 update_search_tbl_counter = 0; int ret; + if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && + (tbl->action < IWL_MIMO2_SWITCH_SISO_A || + tbl->action > IWL_MIMO2_SWITCH_SISO_C)) { + /* switch in SISO */ + tbl->action = IWL_MIMO2_SWITCH_SISO_A; + } for (;;) { lq_sta->action_counter++; switch (tbl->action) { @@ -1709,13 +1689,11 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv, break; case IWL_MIMO2_SWITCH_GI: - if (!tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_20MHZ)) + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) break; - if (tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_40MHZ)) + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) break; IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle SGI/NGI\n"); @@ -1791,6 +1769,7 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv, 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]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action = tbl->action; @@ -1799,6 +1778,12 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv, int ret; u8 update_search_tbl_counter = 0; + if ((iwl_tx_ant_restriction(priv) == IWL_ANT_OK_SINGLE) && + (tbl->action < IWL_MIMO3_SWITCH_SISO_A || + tbl->action > IWL_MIMO3_SWITCH_SISO_C)) { + /* switch in SISO */ + tbl->action = IWL_MIMO3_SWITCH_SISO_A; + } for (;;) { lq_sta->action_counter++; switch (tbl->action) { @@ -1867,13 +1852,11 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv, break; case IWL_MIMO3_SWITCH_GI: - if (!tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_20MHZ)) + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) break; - if (tbl->is_fat && - !(priv->current_ht_config.sgf & - HT_SHORT_GI_40MHZ)) + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) break; IWL_DEBUG_RATE(priv, "LQ: MIMO3 toggle SGI/NGI\n"); @@ -2003,6 +1986,25 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta) } /* + * setup rate table in uCode + * return rate_n_flags as used in the table + */ +static u32 rs_update_rate_tbl(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + int index, u8 is_green) +{ + u32 rate; + + /* Update uCode's rate table. */ + rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); + rs_fill_link_cmd(priv, lq_sta, rate); + iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); + + return rate; +} + +/* * Do rate scaling and search for new modulation mode. */ static void rs_rate_scale_perform(struct iwl_priv *priv, @@ -2066,7 +2068,7 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, if (is_legacy(tbl->lq_type)) lq_sta->is_green = 0; else - lq_sta->is_green = rs_use_green(priv, conf); + lq_sta->is_green = rs_use_green(sta, &priv->current_ht_config); is_green = lq_sta->is_green; /* current tx rate */ @@ -2098,6 +2100,16 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, if (!((1 << index) & rate_scale_index_msk)) { IWL_ERR(priv, "Current Rate is not valid\n"); + if (lq_sta->search_better_tbl) { + /* revert to active table if search table is not valid*/ + tbl->lq_type = LQ_NONE; + lq_sta->search_better_tbl = 0; + tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + /* get "active" rate info */ + index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + rate = rs_update_rate_tbl(priv, lq_sta, + tbl, index, is_green); + } return; } @@ -2149,8 +2161,8 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, tbl->expected_tpt[index] + 64) / 128)); /* If we are searching for better modulation mode, check success. */ - if (lq_sta->search_better_tbl) { - + if (lq_sta->search_better_tbl && + (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI)) { /* 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. */ @@ -2278,7 +2290,11 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, ((sr > IWL_RATE_HIGH_TH) || (current_tpt > (100 * tbl->expected_tpt[low])))) scale_action = 0; - + if (!iwl_ht_enabled(priv) && !is_legacy(tbl->lq_type)) + scale_action = -1; + if (iwl_tx_ant_restriction(priv) != IWL_ANT_OK_MULTI && + (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) + scale_action = -1; switch (scale_action) { case -1: /* Decrease starting rate, update uCode's rate table */ @@ -2308,15 +2324,15 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, lq_update: /* Replace uCode's rate table for the destination station. */ - if (update_lq) { - rate = rate_n_flags_from_tbl(priv, 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); + if (update_lq) + rate = rs_update_rate_tbl(priv, lq_sta, + tbl, index, is_green); + if (iwl_tx_ant_restriction(priv) == IWL_ANT_OK_MULTI) { + /* 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 @@ -2373,7 +2389,8 @@ lq_update: * 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 >= tbl1->max_search)) { + (lq_sta->action_counter >= tbl1->max_search) && + iwl_ht_enabled(priv)) { if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && (lq_sta->tx_agg_tid_en & (1 << tid)) && (tid != MAX_TID_COUNT)) { @@ -2409,7 +2426,7 @@ static void rs_initialize_lq(struct iwl_priv *priv, int rate_idx; int i; u32 rate; - u8 use_green = rs_use_green(priv, conf); + u8 use_green = rs_use_green(sta, &priv->current_ht_config); u8 active_tbl = 0; u8 valid_tx_ant; @@ -2462,11 +2479,11 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_supported_band *sband = txrc->sband; struct iwl_priv *priv = (struct iwl_priv *)priv_r; struct ieee80211_conf *conf = &priv->hw->conf; + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_lq_sta *lq_sta = priv_sta; int rate_idx; - u64 mask_bit = 0; IWL_DEBUG_RATE_LIMIT(priv, "rate scale calculate new rate for skb\n"); @@ -2481,22 +2498,9 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, lq_sta->max_rate_idx = -1; } - if (sta) - mask_bit = sta->supp_rates[sband->band]; - /* Send management frames and NO_ACK data using lowest rate. */ - if (!ieee80211_is_data(hdr->frame_control) || - info->flags & IEEE80211_TX_CTL_NO_ACK || !sta || !lq_sta) { - if (!mask_bit) - info->control.rates[0].idx = - rate_lowest_index(sband, NULL); - else - info->control.rates[0].idx = - rate_lowest_index(sband, sta); - if (info->flags & IEEE80211_TX_CTL_NO_ACK) - info->control.rates[0].count = 1; + if (rate_control_send_low(sta, priv_sta, txrc)) return; - } rate_idx = lq_sta->last_txrate_idx; @@ -2508,7 +2512,7 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", hdr->addr1); sta_id = iwl_add_station(priv, hdr->addr1, - false, CMD_ASYNC, NULL); + false, CMD_ASYNC, ht_cap); } if ((sta_id != IWL_INVALID_STATION)) { lq_sta->lq.sta_id = sta_id; @@ -2533,7 +2537,7 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI; if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_DUP_DATA; - if (lq_sta->last_rate_n_flags & RATE_MCS_FAT_MSK) + if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK) info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD; @@ -2542,6 +2546,7 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, rate_idx = rate_lowest_index(sband, sta); else if (sband->band == IEEE80211_BAND_5GHZ) rate_idx -= IWL_FIRST_OFDM_RATE; + info->control.rates[0].flags = 0; } info->control.rates[0].idx = rate_idx; @@ -2577,6 +2582,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, int i, j; struct iwl_priv *priv = (struct iwl_priv *)priv_r; struct ieee80211_conf *conf = &priv->hw->conf; + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; struct iwl_lq_sta *lq_sta = priv_sta; u16 mask_bit = 0; int count; @@ -2605,7 +2611,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, if (sta_id == IWL_INVALID_STATION) { IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", sta->addr); sta_id = iwl_add_station(priv, sta->addr, false, - CMD_ASYNC, NULL); + CMD_ASYNC, ht_cap); } if ((sta_id != IWL_INVALID_STATION)) { lq_sta->lq.sta_id = sta_id; @@ -2618,7 +2624,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, lq_sta->is_dup = 0; lq_sta->max_rate_idx = -1; lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; - lq_sta->is_green = rs_use_green(priv, conf); + lq_sta->is_green = rs_use_green(sta, &priv->current_ht_config); lq_sta->active_legacy_rate = priv->active_rate & ~(0x1000); lq_sta->active_rate_basic = priv->active_rate_basic; lq_sta->band = priv->band; @@ -2626,19 +2632,19 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, * 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 = sta->ht_cap.mcs.rx_mask[0] << 1; - lq_sta->active_siso_rate |= sta->ht_cap.mcs.rx_mask[0] & 0x1; + lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; + lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[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 = sta->ht_cap.mcs.rx_mask[1] << 1; - lq_sta->active_mimo2_rate |= sta->ht_cap.mcs.rx_mask[1] & 0x1; + lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; + lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; lq_sta->active_mimo2_rate &= ~((u16)0x2); lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; - lq_sta->active_mimo3_rate = sta->ht_cap.mcs.rx_mask[2] << 1; - lq_sta->active_mimo3_rate |= sta->ht_cap.mcs.rx_mask[2] & 0x1; + lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1; + lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1; lq_sta->active_mimo3_rate &= ~((u16)0x2); lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE; @@ -2673,7 +2679,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, rs_initialize_lq(priv, conf, sta, lq_sta); } -static void rs_fill_link_cmd(const struct iwl_priv *priv, +static void rs_fill_link_cmd(struct iwl_priv *priv, struct iwl_lq_sta *lq_sta, u32 new_rate) { struct iwl_scale_tbl_info tbl_type; @@ -2920,7 +2926,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, (is_siso(tbl->lq_type)) ? "SISO" : ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3")); desc += sprintf(buff+desc, " %s", - (tbl->is_fat) ? "40MHz" : "20MHz"); + (tbl->is_ht40) ? "40MHz" : "20MHz"); desc += sprintf(buff+desc, " %s %s\n", (tbl->is_SGI) ? "SGI" : "", (lq_sta->is_green) ? "GF enabled" : ""); } @@ -2985,12 +2991,13 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, return -ENOMEM; for (i = 0; i < LQ_SIZE; i++) { - desc += sprintf(buff+desc, "%s type=%d SGI=%d FAT=%d DUP=%d GF=%d\n" + desc += sprintf(buff+desc, + "%s type=%d SGI=%d HT40=%d DUP=%d GF=%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_ht40, lq_sta->lq_info[i].is_dup, lq_sta->is_green, lq_sta->lq_info[i].current_rate); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 355f50ea7fef..00457bff1ed1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -171,7 +171,7 @@ int iwl_commit_rxon(struct iwl_priv *priv) le16_to_cpu(priv->staging_rxon.channel), priv->staging_rxon.bssid_addr); - iwl_set_rxon_hwcrypto(priv, !priv->hw_params.sw_crypto); + iwl_set_rxon_hwcrypto(priv, !priv->cfg->mod_params->sw_crypto); /* Apply the new configuration * RXON unassoc clears the station table in uCode, send it before @@ -442,8 +442,8 @@ void iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) /* Unmap tx_cmd */ if (num_tbs) pci_unmap_single(dev, - pci_unmap_addr(&txq->cmd[index]->meta, mapping), - pci_unmap_len(&txq->cmd[index]->meta, len), + pci_unmap_addr(&txq->meta[index], mapping), + pci_unmap_len(&txq->meta[index], len), PCI_DMA_BIDIRECTIONAL); /* Unmap chunks, if any. */ @@ -512,70 +512,6 @@ int iwl_hw_tx_queue_init(struct iwl_priv *priv, return 0; } - -/****************************************************************************** - * - * Misc. internal state and helper functions - * - ******************************************************************************/ - -#define MAX_UCODE_BEACON_INTERVAL 4096 - -static u16 iwl_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; - - if (!new_val) - new_val = MAX_UCODE_BEACON_INTERVAL; - - return new_val; -} - -static void iwl_setup_rxon_timing(struct iwl_priv *priv) -{ - u64 tsf; - s32 interval_tm, rem; - 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 = cpu_to_le64(priv->timestamp); - priv->rxon_timing.listen_interval = cpu_to_le16(conf->listen_interval); - - if (priv->iw_mode == NL80211_IFTYPE_STATION) { - beacon_int = iwl_adjust_beacon_interval(priv->beacon_int); - priv->rxon_timing.atim_window = 0; - } else { - beacon_int = iwl_adjust_beacon_interval( - priv->vif->bss_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; - } - - priv->rxon_timing.beacon_interval = cpu_to_le16(beacon_int); - - tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */ - interval_tm = beacon_int * 1024; - rem = do_div(tsf, interval_tm); - priv->rxon_timing.beacon_init_val = cpu_to_le32(interval_tm - rem); - - spin_unlock_irqrestore(&priv->lock, flags); - IWL_DEBUG_ASSOC(priv, "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)); -} - /****************************************************************************** * * Generic RX handler implementations @@ -697,7 +633,6 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv, 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; - unsigned long reg_flags; IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s\n", (flags & HW_CARD_DISABLED) ? "Kill" : "On", @@ -717,19 +652,12 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv, CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); iwl_write_direct32(priv, HBUS_TARG_MBX_C, HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); - - } - - 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); - spin_lock_irqsave(&priv->reg_lock, reg_flags); - if (!iwl_grab_nic_access(priv)) - iwl_release_nic_access(priv); - spin_unlock_irqrestore(&priv->reg_lock, reg_flags); } + if (flags & RF_CARD_DISABLED) + iwl_tt_enter_ct_kill(priv); } + if (!(flags & RF_CARD_DISABLED)) + iwl_tt_exit_ct_kill(priv); if (flags & HW_CARD_DISABLED) set_bit(STATUS_RF_KILL_HW, &priv->status); @@ -964,7 +892,7 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) iwl_write32(priv, CSR_FH_INT_STATUS, inta_fh); #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & IWL_DL_ISR) { + if (iwl_get_debug_level(priv) & IWL_DL_ISR) { /* just for debug */ inta_mask = iwl_read32(priv, CSR_INT_MASK); IWL_DEBUG_ISR(priv, "inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", @@ -983,7 +911,7 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) /* Now service all interrupt bits discovered above. */ if (inta & CSR_INT_BIT_HW_ERR) { - IWL_ERR(priv, "Microcode HW error detected. Restarting.\n"); + IWL_ERR(priv, "Hardware error detected. Restarting.\n"); /* Tell the device to stop sending interrupts */ iwl_disable_interrupts(priv); @@ -999,7 +927,7 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) } #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { + if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { /* NIC fires this, but we don't use it, redundant with WAKEUP */ if (inta & CSR_INT_BIT_SCD) { IWL_DEBUG_ISR(priv, "Scheduler finished to transmit " @@ -1024,7 +952,7 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) hw_rf_kill = 1; - IWL_DEBUG_RF_KILL(priv, "RF_KILL bit toggled to %s.\n", + IWL_WARN(priv, "RF_KILL bit toggled to %s.\n", hw_rf_kill ? "disable radio" : "enable radio"); priv->isr_stats.rfkill++; @@ -1113,7 +1041,7 @@ static void iwl_irq_tasklet_legacy(struct iwl_priv *priv) iwl_enable_interrupts(priv); #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { + if (iwl_get_debug_level(priv) & (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); @@ -1144,7 +1072,7 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) inta = priv->inta; #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & IWL_DL_ISR) { + if (iwl_get_debug_level(priv) & IWL_DL_ISR) { /* just for debug */ inta_mask = iwl_read32(priv, CSR_INT_MASK); IWL_DEBUG_ISR(priv, "inta 0x%08x, enabled 0x%08x\n ", @@ -1156,7 +1084,7 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) /* Now service all interrupt bits discovered above. */ if (inta & CSR_INT_BIT_HW_ERR) { - IWL_ERR(priv, "Microcode HW error detected. Restarting.\n"); + IWL_ERR(priv, "Hardware error detected. Restarting.\n"); /* Tell the device to stop sending interrupts */ iwl_disable_interrupts(priv); @@ -1172,7 +1100,7 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) } #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { + if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { /* NIC fires this, but we don't use it, redundant with WAKEUP */ if (inta & CSR_INT_BIT_SCD) { IWL_DEBUG_ISR(priv, "Scheduler finished to transmit " @@ -1197,7 +1125,7 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) hw_rf_kill = 1; - IWL_DEBUG_RF_KILL(priv, "RF_KILL bit toggled to %s.\n", + IWL_WARN(priv, "RF_KILL bit toggled to %s.\n", hw_rf_kill ? "disable radio" : "enable radio"); priv->isr_stats.rfkill++; @@ -1348,7 +1276,7 @@ static void iwl_nic_start(struct iwl_priv *priv) */ static int iwl_read_ucode(struct iwl_priv *priv) { - struct iwl_ucode *ucode; + struct iwl_ucode_header *ucode; int ret = -EINVAL, index; const struct firmware *ucode_raw; const char *name_pre = priv->cfg->fw_name_pre; @@ -1357,7 +1285,9 @@ static int iwl_read_ucode(struct iwl_priv *priv) char buf[25]; u8 *src; size_t len; - u32 api_ver, inst_size, data_size, init_size, init_data_size, boot_size; + u32 api_ver, build; + u32 inst_size, data_size, init_size, init_data_size, boot_size; + u16 eeprom_ver; /* Ask kernel firmware_class module to get the boot firmware off disk. * request_firmware() is synchronous, file is in memory on return. */ @@ -1387,23 +1317,26 @@ static int iwl_read_ucode(struct iwl_priv *priv) if (ret < 0) goto error; - /* Make sure that we got at least our header! */ - if (ucode_raw->size < sizeof(*ucode)) { + /* Make sure that we got at least the v1 header! */ + if (ucode_raw->size < priv->cfg->ops->ucode->get_header_size(1)) { IWL_ERR(priv, "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; + ucode = (struct iwl_ucode_header *)ucode_raw->data; priv->ucode_ver = le32_to_cpu(ucode->ver); api_ver = IWL_UCODE_API(priv->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); + build = priv->cfg->ops->ucode->get_build(ucode, api_ver); + inst_size = priv->cfg->ops->ucode->get_inst_size(ucode, api_ver); + data_size = priv->cfg->ops->ucode->get_data_size(ucode, api_ver); + init_size = priv->cfg->ops->ucode->get_init_size(ucode, api_ver); + init_data_size = + priv->cfg->ops->ucode->get_init_data_size(ucode, api_ver); + boot_size = priv->cfg->ops->ucode->get_boot_size(ucode, api_ver); + src = priv->cfg->ops->ucode->get_data(ucode, api_ver); /* api_ver should match the api version forming part of the * firmware filename ... but we don't check for that and only rely @@ -1429,6 +1362,14 @@ static int iwl_read_ucode(struct iwl_priv *priv) IWL_UCODE_API(priv->ucode_ver), IWL_UCODE_SERIAL(priv->ucode_ver)); + if (build) + IWL_DEBUG_INFO(priv, "Build %u\n", build); + + eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); + IWL_DEBUG_INFO(priv, "NVM Type: %s, version: 0x%x\n", + (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) + ? "OTP" : "EEPROM", eeprom_ver); + IWL_DEBUG_INFO(priv, "f/w package hdr ucode version raw = 0x%x\n", priv->ucode_ver); IWL_DEBUG_INFO(priv, "f/w package hdr runtime inst size = %u\n", @@ -1443,12 +1384,14 @@ static int iwl_read_ucode(struct iwl_priv *priv) boot_size); /* Verify size of file vs. image size info in file's header */ - if (ucode_raw->size < sizeof(*ucode) + + if (ucode_raw->size != + priv->cfg->ops->ucode->get_header_size(api_ver) + inst_size + data_size + init_size + init_data_size + boot_size) { - IWL_DEBUG_INFO(priv, "uCode file size %d too small\n", - (int)ucode_raw->size); + IWL_DEBUG_INFO(priv, + "uCode file size %d does not match expected size\n", + (int)ucode_raw->size); ret = -EINVAL; goto err_release; } @@ -1528,42 +1471,42 @@ static int iwl_read_ucode(struct iwl_priv *priv) /* 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; + len = inst_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) uCode instr len %Zd\n", len); memcpy(priv->ucode_code.v_addr, src, len); + src += len; + IWL_DEBUG_INFO(priv, "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 iwl_up() */ - src = &ucode->data[inst_size]; - len = priv->ucode_data.len; + len = data_size; IWL_DEBUG_INFO(priv, "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); + src += len; /* Initialization instructions (3rd block) */ if (init_size) { - src = &ucode->data[inst_size + data_size]; - len = priv->ucode_init.len; + len = init_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) init instr len %Zd\n", len); memcpy(priv->ucode_init.v_addr, src, len); + 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; + len = init_data_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) init data len %Zd\n", len); memcpy(priv->ucode_init_data.v_addr, src, len); + src += len; } /* Bootstrap instructions (5th block) */ - src = &ucode->data[inst_size + data_size + init_size + init_data_size]; - len = priv->ucode_boot.len; + len = boot_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) boot instr len %Zd\n", len); memcpy(priv->ucode_boot.v_addr, src, len); @@ -1663,7 +1606,7 @@ static void iwl_alive_start(struct iwl_priv *priv) set_bit(STATUS_READY, &priv->status); wake_up_interruptible(&priv->wait_command_queue); - iwl_power_update_mode(priv, 1); + iwl_power_update_mode(priv, true); /* reassociate for ADHOC mode */ if (priv->vif && (priv->iw_mode == NL80211_IFTYPE_ADHOC)) { @@ -1812,6 +1755,11 @@ static int iwl_prepare_card_hw(struct iwl_priv *priv) IWL_DEBUG_INFO(priv, "iwl_prepare_card_hw enter \n"); + ret = iwl_set_hw_ready(priv); + if (priv->hw_ready) + return ret; + + /* If HW is not ready, prepare the conditions to check again */ iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PREPARE); @@ -1819,6 +1767,7 @@ static int iwl_prepare_card_hw(struct iwl_priv *priv) ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); + /* HW should be ready by now, check again. */ if (ret != -ETIMEDOUT) iwl_set_hw_ready(priv); @@ -2126,7 +2075,7 @@ void iwl_post_associate(struct iwl_priv *priv) * If chain noise has already been run, then we need to enable * power management here */ if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE) - iwl_power_update_mode(priv, 0); + iwl_power_update_mode(priv, false); /* Enable Rx differential gain and sensitivity calibrations */ iwl_chain_noise_reset(priv); @@ -2206,7 +2155,7 @@ static void iwl_mac_stop(struct ieee80211_hw *hw) priv->is_open = 0; - if (iwl_is_ready_rf(priv)) { + if (iwl_is_ready_rf(priv) || test_bit(STATUS_SCAN_HW, &priv->status)) { /* stop mac, cancel any scan request and clear * RXON_FILTER_ASSOC_MSK BIT */ @@ -2331,7 +2280,7 @@ static int iwl_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, IWL_DEBUG_MAC80211(priv, "enter\n"); - if (priv->hw_params.sw_crypto) { + if (priv->cfg->mod_params->sw_crypto) { IWL_DEBUG_MAC80211(priv, "leave - hwcrypto disabled\n"); return -EOPNOTSUPP; } @@ -2455,14 +2404,16 @@ static int iwl_mac_get_stats(struct ieee80211_hw *hw, * used for controlling the debug level. * * See the level definitions in iwl for details. + * + * The debug_level being managed using sysfs below is a per device debug + * level that is used instead of the global debug level if it (the per + * device debug level) is set. */ - static ssize_t show_debug_level(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); - - return sprintf(buf, "0x%08X\n", priv->debug_level); + return sprintf(buf, "0x%08X\n", iwl_get_debug_level(priv)); } static ssize_t store_debug_level(struct device *d, struct device_attribute *attr, @@ -2475,9 +2426,12 @@ static ssize_t store_debug_level(struct device *d, ret = strict_strtoul(buf, 0, &val); if (ret) IWL_ERR(priv, "%s is not in hex or decimal form.\n", buf); - else + else { priv->debug_level = val; - + if (iwl_alloc_traffic_mem(priv)) + IWL_ERR(priv, + "Not enough memory to generate traffic log\n"); + } return strnlen(buf, count); } @@ -2488,39 +2442,6 @@ static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, #endif /* CONFIG_IWLWIFI_DEBUG */ -static ssize_t show_version(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - 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, "NVM Type: %s, version: 0x%x\n", - (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) - ? "OTP" : "EEPROM", 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) { @@ -2556,10 +2477,15 @@ static ssize_t store_tx_power(struct device *d, ret = strict_strtoul(buf, 10, &val); if (ret) IWL_INFO(priv, "%s is not in decimal form.\n", buf); - else - iwl_set_tx_power(priv, val, false); - - return count; + else { + ret = iwl_set_tx_power(priv, val, false); + if (ret) + IWL_ERR(priv, "failed setting tx power (0x%d).\n", + ret); + else + ret = count; + } + return ret; } static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, show_tx_power, store_tx_power); @@ -2644,67 +2570,6 @@ static ssize_t store_filter_flags(struct device *d, static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, show_filter_flags, store_filter_flags); -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; - unsigned long mode; - - - mutex_lock(&priv->mutex); - - ret = strict_strtoul(buf, 10, &mode); - if (ret) - goto out; - - ret = iwl_power_set_user_mode(priv, mode); - if (ret) { - IWL_DEBUG_MAC80211(priv, "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 level = priv->power_data.power_mode; - char *p = buf; - - p += sprintf(p, "%d\n", level); - return p - buf + 1; -} - -static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, show_power_level, - store_power_level); - -static ssize_t show_qos(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - char *p = buf; - int q; - - for (q = 0; q < AC_NUM; q++) { - p += sprintf(p, "\tcw_min\tcw_max\taifsn\ttxop\n"); - p += sprintf(p, "AC[%d]\t%u\t%u\t%u\t%u\n", q, - priv->qos_data.def_qos_parm.ac[q].cw_min, - priv->qos_data.def_qos_parm.ac[q].cw_max, - priv->qos_data.def_qos_parm.ac[q].aifsn, - priv->qos_data.def_qos_parm.ac[q].edca_txop); - } - - return p - buf + 1; -} - -static DEVICE_ATTR(qos, S_IRUGO, show_qos, NULL); static ssize_t show_statistics(struct device *d, struct device_attribute *attr, char *buf) @@ -2797,15 +2662,12 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv) static struct attribute *iwl_sysfs_entries[] = { &dev_attr_flags.attr, &dev_attr_filter_flags.attr, - &dev_attr_power_level.attr, &dev_attr_statistics.attr, &dev_attr_temperature.attr, &dev_attr_tx_power.attr, #ifdef CONFIG_IWLWIFI_DEBUG &dev_attr_debug_level.attr, #endif - &dev_attr_version.attr, - &dev_attr_qos.attr, NULL }; @@ -2849,7 +2711,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* 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) + if (iwl_debug_level & IWL_DL_INFO) dev_printk(KERN_DEBUG, &(pdev->dev), "Disabling hw_scan\n"); iwl_hw_ops.hw_scan = NULL; @@ -2871,9 +2733,10 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) priv->inta_mask = CSR_INI_SET_MASK; #ifdef CONFIG_IWLWIFI_DEBUG - priv->debug_level = priv->cfg->mod_params->debug; atomic_set(&priv->restrict_refcnt, 0); #endif + if (iwl_alloc_traffic_mem(priv)) + IWL_ERR(priv, "Not enough memory to generate traffic log\n"); /************************** * 2. Initializing PCI bus @@ -3034,6 +2897,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) test_bit(STATUS_RF_KILL_HW, &priv->status)); iwl_power_initialize(priv); + iwl_tt_initialize(priv); return 0; out_remove_sysfs: @@ -3057,6 +2921,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_disable_device(pdev); out_ieee80211_free_hw: ieee80211_free_hw(priv->hw); + iwl_free_traffic_mem(priv); out: return err; } @@ -3086,6 +2951,8 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev) iwl_down(priv); } + iwl_tt_exit(priv); + /* make sure we flush any pending irq or * tasklet for the driver */ @@ -3113,6 +2980,7 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev) * until now... */ destroy_workqueue(priv->workqueue); priv->workqueue = NULL; + iwl_free_traffic_mem(priv); free_irq(priv->pci_dev->irq, priv); pci_disable_msi(priv->pci_dev); @@ -3163,15 +3031,12 @@ static struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x423C, PCI_ANY_ID, iwl5150_agn_cfg)}, {IWL_PCI_DEVICE(0x423D, PCI_ANY_ID, iwl5150_agn_cfg)}, /* 6000/6050 Series */ - {IWL_PCI_DEVICE(0x0082, 0x1102, iwl6000_2ag_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0x1112, iwl6000_2ag_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1122, iwl6000_2ag_cfg)}, + {IWL_PCI_DEVICE(0x008D, PCI_ANY_ID, iwl6000h_2agn_cfg)}, + {IWL_PCI_DEVICE(0x008E, PCI_ANY_ID, iwl6000h_2agn_cfg)}, {IWL_PCI_DEVICE(0x422B, PCI_ANY_ID, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x422C, PCI_ANY_ID, iwl6000_2agn_cfg)}, + {IWL_PCI_DEVICE(0x422C, PCI_ANY_ID, iwl6000i_2agn_cfg)}, {IWL_PCI_DEVICE(0x4238, PCI_ANY_ID, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x4239, PCI_ANY_ID, iwl6000_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0082, PCI_ANY_ID, iwl6000_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0085, PCI_ANY_ID, iwl6000_3agn_cfg)}, + {IWL_PCI_DEVICE(0x4239, PCI_ANY_ID, iwl6000i_2agn_cfg)}, {IWL_PCI_DEVICE(0x0086, PCI_ANY_ID, iwl6050_3agn_cfg)}, {IWL_PCI_DEVICE(0x0087, PCI_ANY_ID, iwl6050_2agn_cfg)}, {IWL_PCI_DEVICE(0x0088, PCI_ANY_ID, iwl6050_3agn_cfg)}, @@ -3231,3 +3096,11 @@ static void __exit iwl_exit(void) module_exit(iwl_exit); module_init(iwl_init); + +#ifdef CONFIG_IWLWIFI_DEBUG +module_param_named(debug50, iwl_debug_level, uint, 0444); +MODULE_PARM_DESC(debug50, "50XX debug output mask (deprecated)"); +module_param_named(debug, iwl_debug_level, uint, 0644); +MODULE_PARM_DESC(debug, "debug output mask"); +#endif + diff --git a/drivers/net/wireless/iwlwifi/iwl-calib.c b/drivers/net/wireless/iwlwifi/iwl-calib.c index a5d63672ad39..c4b565a2de94 100644 --- a/drivers/net/wireless/iwlwifi/iwl-calib.c +++ b/drivers/net/wireless/iwlwifi/iwl-calib.c @@ -86,7 +86,7 @@ int iwl_send_calib_results(struct iwl_priv *priv) struct iwl_host_cmd hcmd = { .id = REPLY_PHY_CALIBRATION_CMD, - .meta.flags = CMD_SIZE_HUGE, + .flags = CMD_SIZE_HUGE, }; for (i = 0; i < IWL_CALIB_MAX; i++) { @@ -251,12 +251,7 @@ static int iwl_sens_energy_cck(struct iwl_priv *priv, /* increase energy threshold (reduce nrg value) * to decrease sensitivity */ - if (data->nrg_th_cck > - (ranges->max_nrg_cck + NRG_STEP_CCK)) - data->nrg_th_cck = data->nrg_th_cck - - NRG_STEP_CCK; - else - data->nrg_th_cck = ranges->max_nrg_cck; + data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; /* Else if we got fewer than desired, increase sensitivity */ } else if (false_alarms < min_false_alarms) { data->nrg_curr_state = IWL_FA_TOO_FEW; @@ -424,7 +419,7 @@ static int iwl_sensitivity_write(struct iwl_priv *priv) struct iwl_host_cmd cmd_out = { .id = SENSITIVITY_CMD, .len = sizeof(struct iwl_sensitivity_cmd), - .meta.flags = CMD_ASYNC, + .flags = CMD_ASYNC, .data = &cmd, }; @@ -857,7 +852,7 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv, priv->cfg->ops->lib->update_chain_flags(priv); data->state = IWL_CHAIN_NOISE_DONE; - iwl_power_update_mode(priv, 0); + iwl_power_update_mode(priv, false); } EXPORT_SYMBOL(iwl_chain_noise_calibration); diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index c87033bf3ad2..2c5c88fc38f5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -283,7 +283,7 @@ struct iwl3945_power_per_rate { * 1) Dual stream (MIMO) * 2) Triple stream (MIMO) * - * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps FAT duplicate data + * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data * * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"): * 3-0: 0xD) 6 Mbps @@ -320,11 +320,11 @@ struct iwl3945_power_per_rate { #define RATE_MCS_GF_POS 10 #define RATE_MCS_GF_MSK 0x400 -/* Bit 11: (1) Use 40Mhz FAT chnl width, (0) use 20 MHz legacy chnl width */ -#define RATE_MCS_FAT_POS 11 -#define RATE_MCS_FAT_MSK 0x800 +/* Bit 11: (1) Use 40Mhz HT40 chnl width, (0) use 20 MHz legacy chnl width */ +#define RATE_MCS_HT40_POS 11 +#define RATE_MCS_HT40_MSK 0x800 -/* Bit 12: (1) Duplicate data on both 20MHz chnls. FAT (bit 11) must be set. */ +/* Bit 12: (1) Duplicate data on both 20MHz chnls. HT40 (bit 11) must be set. */ #define RATE_MCS_DUP_POS 12 #define RATE_MCS_DUP_MSK 0x1000 @@ -459,7 +459,7 @@ struct iwl_init_alive_resp { /* calibration values from "initialize" uCode */ __le32 voltage; /* signed, higher value is lower voltage */ - __le32 therm_r1[2]; /* signed, 1st for normal, 2nd for FAT channel*/ + __le32 therm_r1[2]; /* signed, 1st for normal, 2nd for HT40 */ __le32 therm_r2[2]; /* signed */ __le32 therm_r3[2]; /* signed */ __le32 therm_r4[2]; /* signed */ @@ -610,7 +610,7 @@ enum { #define RXON_FLG_HT_OPERATING_MODE_POS (23) #define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23) -#define RXON_FLG_FAT_PROT_MSK cpu_to_le32(0x2 << 23) +#define RXON_FLG_HT40_PROT_MSK cpu_to_le32(0x2 << 23) #define RXON_FLG_CHANNEL_MODE_POS (25) #define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25) @@ -765,6 +765,8 @@ struct iwl5000_rxon_assoc_cmd { } __attribute__ ((packed)); #define IWL_CONN_MAX_LISTEN_INTERVAL 10 +#define IWL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */ +#define IWL39_MAX_UCODE_BEACON_INTERVAL 1 /* 1024 */ /* * REPLY_RXON_TIMING = 0x14 (command, has simple generic response) @@ -884,12 +886,11 @@ struct iwl_qosparam_cmd { #define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2); #define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8); -#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8); #define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17) #define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18) #define STA_FLG_MAX_AGG_SIZE_POS (19) #define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19) -#define STA_FLG_FAT_EN_MSK cpu_to_le32(1 << 21) +#define STA_FLG_HT40_EN_MSK cpu_to_le32(1 << 21) #define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22) #define STA_FLG_AGG_MPDU_DENSITY_POS (23) #define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23) @@ -1154,6 +1155,7 @@ struct iwl_wep_cmd { #define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) #define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) #define RX_RES_PHY_FLAGS_ANTENNA_MSK cpu_to_le16(0xf0) +#define RX_RES_PHY_FLAGS_ANTENNA_POS 4 #define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) #define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8) @@ -1922,7 +1924,7 @@ struct iwl_link_qual_general_params { #define LINK_QUAL_AGG_DISABLE_START_MIN (0) #define LINK_QUAL_AGG_FRAME_LIMIT_DEF (31) -#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (64) +#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) #define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) /** @@ -1982,10 +1984,10 @@ struct iwl_link_qual_agg_params { * a) Use this same initial rate for first 3 entries. * b) Find next lower available rate using same mode (SISO or MIMO), * use for next 3 entries. If no lower rate available, switch to - * legacy mode (no FAT channel, no MIMO, no short guard interval). + * legacy mode (no HT40 channel, no MIMO, no short guard interval). * c) If using MIMO, set command's mimo_delimiter to number of entries * using MIMO (3 or 6). - * d) After trying 2 HT rates, switch to legacy mode (no FAT channel, + * d) After trying 2 HT rates, switch to legacy mode (no HT40 channel, * no MIMO, no short guard interval), at the next lower bit rate * (e.g. if second HT bit rate was 54, try 48 legacy), and follow * legacy procedure for remaining table entries. @@ -2311,15 +2313,22 @@ struct iwl_spectrum_notification { * PM allow: * bit 0 - '0' Driver not allow power management * '1' Driver allow PM (use rest of parameters) + * * uCode send sleep notifications: * bit 1 - '0' Don't send sleep notification * '1' send sleep notification (SEND_PM_NOTIFICATION) + * * Sleep over DTIM * bit 2 - '0' PM have to walk up every DTIM * '1' PM could sleep over DTIM till listen Interval. + * * PCI power managed * bit 3 - '0' (PCI_CFG_LINK_CTRL & 0x1) * '1' !(PCI_CFG_LINK_CTRL & 0x1) + * + * Fast PD + * bit 4 - '1' Put radio to sleep when receiving frame for others + * * Force sleep Modes * bit 31/30- '00' use both mac/xtal sleeps * '01' force Mac sleep @@ -2411,6 +2420,13 @@ struct iwl_ct_kill_config { __le32 critical_temperature_R; } __attribute__ ((packed)); +/* 1000, and 6x00 */ +struct iwl_ct_kill_throttling_config { + __le32 critical_temperature_exit; + __le32 reserved; + __le32 critical_temperature_enter; +} __attribute__ ((packed)); + /****************************************************************************** * (8) * Scan Commands, Responses, Notifications: @@ -2913,6 +2929,20 @@ struct statistics_rx { struct statistics_rx_ht_phy ofdm_ht; } __attribute__ ((packed)); +/** + * struct statistics_tx_power - current tx power + * + * @ant_a: current tx power on chain a in 1/2 dB step + * @ant_b: current tx power on chain b in 1/2 dB step + * @ant_c: current tx power on chain c in 1/2 dB step + */ +struct statistics_tx_power { + u8 ant_a; + u8 ant_b; + u8 ant_c; + u8 reserved; +} __attribute__ ((packed)); + struct statistics_tx_non_phy_agg { __le32 ba_timeout; __le32 ba_reschedule_frames; @@ -2924,8 +2954,6 @@ struct statistics_tx_non_phy_agg { __le32 underrun; __le32 bt_prio_kill; __le32 rx_ba_rsp_cnt; - __le32 reserved2; - __le32 reserved3; } __attribute__ ((packed)); struct statistics_tx { @@ -2944,6 +2972,8 @@ struct statistics_tx { __le32 cts_timeout_collision; __le32 ack_or_ba_timeout_collision; struct statistics_tx_non_phy_agg agg; + struct statistics_tx_power tx_power; + __le32 reserved1; } __attribute__ ((packed)); @@ -3008,7 +3038,7 @@ struct iwl_statistics_cmd { * one channel that has just been scanned. */ #define STATISTICS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2) -#define STATISTICS_REPLY_FLG_FAT_MODE_MSK cpu_to_le32(0x8) +#define STATISTICS_REPLY_FLG_HT40_MODE_MSK cpu_to_le32(0x8) struct iwl3945_notif_statistics { __le32 flag; @@ -3465,7 +3495,7 @@ struct iwl_wimax_coex_cmd { *****************************************************************************/ struct iwl_rx_packet { - __le32 len; + __le32 len_n_flags; struct iwl_cmd_header hdr; union { struct iwl3945_rx_frame rx_frame; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 18b135f510e5..0bfd4e918139 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -59,6 +59,9 @@ MODULE_LICENSE("GPL"); IWL_RATE_##pp##M_INDEX, \ IWL_RATE_##np##M_INDEX } +u32 iwl_debug_level; +EXPORT_SYMBOL(iwl_debug_level); + static irqreturn_t iwl_isr(int irq, void *data); /* @@ -102,7 +105,7 @@ void iwl_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags, r->flags |= IEEE80211_TX_RC_MCS; if (rate_n_flags & RATE_MCS_GF_MSK) r->flags |= IEEE80211_TX_RC_GREEN_FIELD; - if (rate_n_flags & RATE_MCS_FAT_MSK) + if (rate_n_flags & RATE_MCS_HT40_MSK) r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; if (rate_n_flags & RATE_MCS_DUP_MSK) r->flags |= IEEE80211_TX_RC_DUP_DATA; @@ -391,13 +394,14 @@ static void iwlcore_init_ht_hw_capab(const struct iwl_priv *priv, ht_info->ht_supported = true; - ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; + if (priv->cfg->ht_greenfield_support) + ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; ht_info->cap |= IEEE80211_HT_CAP_SGI_20; ht_info->cap |= (IEEE80211_HT_CAP_SM_PS & (WLAN_HT_CAP_SM_PS_DISABLED << 2)); max_bit_rate = MAX_BIT_RATE_20_MHZ; - if (priv->hw_params.fat_channel & BIT(band)) { + if (priv->hw_params.ht40_channel & BIT(band)) { ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; ht_info->cap |= IEEE80211_HT_CAP_SGI_40; ht_info->mcs.rx_mask[4] = 0x01; @@ -537,17 +541,14 @@ int iwlcore_init_geos(struct iwl_priv *priv) if (ch->flags & EEPROM_CHANNEL_RADAR) geo_ch->flags |= IEEE80211_CHAN_RADAR; - geo_ch->flags |= ch->fat_extension_channel; + geo_ch->flags |= ch->ht40_extension_channel; - if (ch->max_power_avg > priv->tx_power_channel_lmt) - priv->tx_power_channel_lmt = ch->max_power_avg; + if (ch->max_power_avg > priv->tx_power_device_lmt) + priv->tx_power_device_lmt = ch->max_power_avg; } else { geo_ch->flags |= IEEE80211_CHAN_DISABLED; } - /* Save flags for reg domain usage */ - geo_ch->orig_flags = geo_ch->flags; - IWL_DEBUG_INFO(priv, "Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", ch->channel, geo_ch->center_freq, is_channel_a_band(ch) ? "5.2" : "2.4", @@ -604,16 +605,16 @@ static u8 iwl_is_channel_extension(struct iwl_priv *priv, return 0; if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) - return !(ch_info->fat_extension_channel & + return !(ch_info->ht40_extension_channel & IEEE80211_CHAN_NO_HT40PLUS); else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW) - return !(ch_info->fat_extension_channel & + return !(ch_info->ht40_extension_channel & IEEE80211_CHAN_NO_HT40MINUS); return 0; } -u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv, +u8 iwl_is_ht40_tx_allowed(struct iwl_priv *priv, struct ieee80211_sta_ht_cap *sta_ht_inf) { struct iwl_ht_info *iwl_ht_conf = &priv->current_ht_config; @@ -629,11 +630,72 @@ u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv, if (!sta_ht_inf->ht_supported) return 0; } +#ifdef CONFIG_IWLWIFI_DEBUG + if (priv->disable_ht40) + return 0; +#endif return iwl_is_channel_extension(priv, priv->band, le16_to_cpu(priv->staging_rxon.channel), iwl_ht_conf->extension_chan_offset); } -EXPORT_SYMBOL(iwl_is_fat_tx_allowed); +EXPORT_SYMBOL(iwl_is_ht40_tx_allowed); + +static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) +{ + u16 new_val = 0; + u16 beacon_factor = 0; + + beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val; + new_val = beacon_val / beacon_factor; + + if (!new_val) + new_val = max_beacon_val; + + return new_val; +} + +void iwl_setup_rxon_timing(struct iwl_priv *priv) +{ + u64 tsf; + s32 interval_tm, rem; + unsigned long flags; + struct ieee80211_conf *conf = NULL; + u16 beacon_int; + + conf = ieee80211_get_hw_conf(priv->hw); + + spin_lock_irqsave(&priv->lock, flags); + priv->rxon_timing.timestamp = cpu_to_le64(priv->timestamp); + priv->rxon_timing.listen_interval = cpu_to_le16(conf->listen_interval); + + if (priv->iw_mode == NL80211_IFTYPE_STATION) { + beacon_int = priv->beacon_int; + priv->rxon_timing.atim_window = 0; + } else { + beacon_int = priv->vif->bss_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; + } + + beacon_int = iwl_adjust_beacon_interval(beacon_int, + priv->hw_params.max_beacon_itrvl * 1024); + priv->rxon_timing.beacon_interval = cpu_to_le16(beacon_int); + + tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */ + interval_tm = beacon_int * 1024; + rem = do_div(tsf, interval_tm); + priv->rxon_timing.beacon_init_val = cpu_to_le32(interval_tm - rem); + + spin_unlock_irqrestore(&priv->lock, flags); + IWL_DEBUG_ASSOC(priv, + "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)); +} +EXPORT_SYMBOL(iwl_setup_rxon_timing); void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt) { @@ -805,7 +867,7 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) if (!ht_info->is_ht) { rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | - RXON_FLG_FAT_PROT_MSK | + RXON_FLG_HT40_PROT_MSK | RXON_FLG_HT_PROT_MSK); return; } @@ -816,12 +878,12 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info) rxon->flags |= cpu_to_le32(ht_info->ht_protection << RXON_FLG_HT_OPERATING_MODE_POS); /* Set up channel bandwidth: - * 20 MHz only, 20/40 mixed or pure 40 if fat ok */ + * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ /* clear the HT channel mode before set the mode */ rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); - if (iwl_is_fat_tx_allowed(priv, NULL)) { - /* pure 40 fat */ + if (iwl_is_ht40_tx_allowed(priv, NULL)) { + /* pure ht40 */ if (ht_info->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; /* Note: control channel is opposite of extension channel */ @@ -1076,7 +1138,6 @@ void iwl_set_flags_for_band(struct iwl_priv *priv, priv->staging_rxon.flags &= ~RXON_FLG_CCK_MSK; } } -EXPORT_SYMBOL(iwl_set_flags_for_band); /* * initialize rxon structure with default values from eeprom @@ -1231,9 +1292,191 @@ static void iwl_print_rx_config_cmd(struct iwl_priv *priv) IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr); IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); } -#endif + +static const char *desc_lookup_text[] = { + "OK", + "FAIL", + "BAD_PARAM", + "BAD_CHECKSUM", + "NMI_INTERRUPT_WDG", + "SYSASSERT", + "FATAL_ERROR", + "BAD_COMMAND", + "HW_ERROR_TUNE_LOCK", + "HW_ERROR_TEMPERATURE", + "ILLEGAL_CHAN_FREQ", + "VCC_NOT_STABLE", + "FH_ERROR", + "NMI_INTERRUPT_HOST", + "NMI_INTERRUPT_ACTION_PT", + "NMI_INTERRUPT_UNKNOWN", + "UCODE_VERSION_MISMATCH", + "HW_ERROR_ABS_LOCK", + "HW_ERROR_CAL_LOCK_FAIL", + "NMI_INTERRUPT_INST_ACTION_PT", + "NMI_INTERRUPT_DATA_ACTION_PT", + "NMI_TRM_HW_ER", + "NMI_INTERRUPT_TRM", + "NMI_INTERRUPT_BREAK_POINT" + "DEBUG_0", + "DEBUG_1", + "DEBUG_2", + "DEBUG_3", + "UNKNOWN" +}; + +static const char *desc_lookup(int i) +{ + int max = ARRAY_SIZE(desc_lookup_text) - 1; + + if (i < 0 || i > max) + i = max; + + return desc_lookup_text[i]; +} + +#define ERROR_START_OFFSET (1 * sizeof(u32)) +#define ERROR_ELEM_SIZE (7 * sizeof(u32)) + +static void iwl_dump_nic_error_log(struct iwl_priv *priv) +{ + u32 data2, line; + u32 desc, time, count, base, data1; + u32 blink1, blink2, ilink1, ilink2; + + if (priv->ucode_type == UCODE_INIT) + base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr); + else + base = le32_to_cpu(priv->card_alive.error_event_table_ptr); + + if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { + IWL_ERR(priv, "Not valid error log pointer 0x%08X\n", base); + return; + } + + count = iwl_read_targ_mem(priv, base); + + if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { + IWL_ERR(priv, "Start IWL Error Log Dump:\n"); + IWL_ERR(priv, "Status: 0x%08lX, count: %d\n", + priv->status, count); + } + + desc = iwl_read_targ_mem(priv, base + 1 * sizeof(u32)); + blink1 = iwl_read_targ_mem(priv, base + 3 * sizeof(u32)); + blink2 = iwl_read_targ_mem(priv, base + 4 * sizeof(u32)); + ilink1 = iwl_read_targ_mem(priv, base + 5 * sizeof(u32)); + ilink2 = iwl_read_targ_mem(priv, base + 6 * sizeof(u32)); + data1 = iwl_read_targ_mem(priv, base + 7 * sizeof(u32)); + data2 = iwl_read_targ_mem(priv, base + 8 * sizeof(u32)); + line = iwl_read_targ_mem(priv, base + 9 * sizeof(u32)); + time = iwl_read_targ_mem(priv, base + 11 * sizeof(u32)); + + IWL_ERR(priv, "Desc Time " + "data1 data2 line\n"); + IWL_ERR(priv, "%-28s (#%02d) %010u 0x%08X 0x%08X %u\n", + desc_lookup(desc), desc, time, data1, data2, line); + IWL_ERR(priv, "blink1 blink2 ilink1 ilink2\n"); + IWL_ERR(priv, "0x%05X 0x%05X 0x%05X 0x%05X\n", blink1, blink2, + ilink1, ilink2); + +} + +#define EVENT_START_OFFSET (4 * sizeof(u32)) /** + * iwl_print_event_log - Dump error event log to syslog + * + */ +static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, + u32 num_events, u32 mode) +{ + u32 i; + u32 base; /* SRAM byte address of event log header */ + u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ + u32 ptr; /* SRAM byte address of log data */ + u32 ev, time, data; /* event log data */ + + if (num_events == 0) + return; + if (priv->ucode_type == UCODE_INIT) + base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); + else + base = le32_to_cpu(priv->card_alive.log_event_table_ptr); + + if (mode == 0) + event_size = 2 * sizeof(u32); + else + event_size = 3 * sizeof(u32); + + ptr = base + EVENT_START_OFFSET + (start_idx * event_size); + + /* "time" is actually "data" for mode 0 (no timestamp). + * place event id # at far right for easier visual parsing. */ + for (i = 0; i < num_events; i++) { + ev = iwl_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + time = iwl_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + if (mode == 0) { + /* data, ev */ + IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev); + } else { + data = iwl_read_targ_mem(priv, ptr); + ptr += sizeof(u32); + IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", + time, data, ev); + } + } +} + +void iwl_dump_nic_event_log(struct iwl_priv *priv) +{ + u32 base; /* SRAM byte address of event log header */ + u32 capacity; /* event log capacity in # entries */ + u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ + u32 num_wraps; /* # times uCode wrapped to top of log */ + u32 next_entry; /* index of next entry to be written by uCode */ + u32 size; /* # entries that we'll print */ + + if (priv->ucode_type == UCODE_INIT) + base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); + else + base = le32_to_cpu(priv->card_alive.log_event_table_ptr); + + if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { + IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base); + return; + } + + /* event log header */ + capacity = iwl_read_targ_mem(priv, base); + mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32))); + num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32))); + next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32))); + + size = num_wraps ? capacity : next_entry; + + /* bail out if nothing in log */ + if (size == 0) { + IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n"); + return; + } + + IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n", + size, num_wraps); + + /* if uCode has wrapped back to top of log, start at the oldest entry, + * i.e the next one that uCode would fill. */ + if (num_wraps) + iwl_print_event_log(priv, next_entry, + capacity - next_entry, mode); + /* (then/else) start at top of log */ + iwl_print_event_log(priv, 0, next_entry, mode); + +} +#endif +/** * iwl_irq_handle_error - called for HW or SW error interrupt from card */ void iwl_irq_handle_error(struct iwl_priv *priv) @@ -1245,7 +1488,7 @@ void iwl_irq_handle_error(struct iwl_priv *priv) clear_bit(STATUS_HCMD_ACTIVE, &priv->status); #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & IWL_DL_FW_ERRORS) { + if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) { iwl_dump_nic_error_log(priv); iwl_dump_nic_event_log(priv); iwl_print_rx_config_cmd(priv); @@ -1271,7 +1514,7 @@ EXPORT_SYMBOL(iwl_irq_handle_error); void iwl_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list) + u64 multicast) { struct iwl_priv *priv = hw->priv; __le32 *filter_flags = &priv->staging_rxon.filter_flags; @@ -1325,7 +1568,9 @@ int iwl_setup_mac(struct iwl_priv *priv) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM | IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_SPECTRUM_MGMT; + IEEE80211_HW_SPECTRUM_MGMT | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); @@ -1335,6 +1580,12 @@ int iwl_setup_mac(struct iwl_priv *priv) /* Firmware does not support this */ hw->wiphy->disable_beacon_hints = true; + /* + * For now, disable PS by default because it affects + * RX performance significantly. + */ + hw->wiphy->ps_default = false; + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; /* we create the 802.11 header and a zero-length SSID element */ hw->wiphy->max_scan_ie_len = IWL_MAX_PROBE_REQUEST - 24 - 2; @@ -1364,7 +1615,6 @@ EXPORT_SYMBOL(iwl_setup_mac); int iwl_set_hw_params(struct iwl_priv *priv) { - priv->hw_params.sw_crypto = priv->cfg->mod_params->sw_crypto; priv->hw_params.max_rxq_size = RX_QUEUE_SIZE; priv->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG; if (priv->cfg->mod_params->amsdu_size_8K) @@ -1373,6 +1623,8 @@ int iwl_set_hw_params(struct iwl_priv *priv) priv->hw_params.rx_buf_size = IWL_RX_BUF_SIZE_4K; priv->hw_params.max_pkt_size = priv->hw_params.rx_buf_size - 256; + priv->hw_params.max_beacon_itrvl = IWL_MAX_UCODE_BEACON_INTERVAL; + if (priv->cfg->mod_params->disable_11n) priv->cfg->sku &= ~IWL_SKU_N; @@ -1419,9 +1671,10 @@ int iwl_init_drv(struct iwl_priv *priv) priv->qos_data.qos_cap.val = 0; priv->rates_mask = IWL_RATES_MASK; - /* If power management is turned on, default to CAM mode */ - priv->power_mode = IWL_POWER_MODE_CAM; - priv->tx_power_user_lmt = IWL_TX_POWER_TARGET_POWER_MAX; + /* Set the tx_power_user_lmt to the lowest power level + * this value will get overwritten by channel max power avg + * from eeprom */ + priv->tx_power_user_lmt = IWL_TX_POWER_TARGET_POWER_MIN; ret = iwl_init_channel_map(priv); if (ret) { @@ -1448,6 +1701,8 @@ EXPORT_SYMBOL(iwl_init_drv); int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) { int ret = 0; + s8 prev_tx_power = priv->tx_power_user_lmt; + if (tx_power < IWL_TX_POWER_TARGET_POWER_MIN) { IWL_WARN(priv, "Requested user TXPOWER %d below lower limit %d.\n", tx_power, @@ -1455,25 +1710,37 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) return -EINVAL; } - if (tx_power > IWL_TX_POWER_TARGET_POWER_MAX) { - IWL_WARN(priv, "Requested user TXPOWER %d above upper limit %d.\n", - tx_power, - IWL_TX_POWER_TARGET_POWER_MAX); + if (tx_power > priv->tx_power_device_lmt) { + IWL_WARN(priv, + "Requested user TXPOWER %d above upper limit %d.\n", + tx_power, priv->tx_power_device_lmt); return -EINVAL; } if (priv->tx_power_user_lmt != tx_power) force = true; - priv->tx_power_user_lmt = tx_power; - /* if nic is not up don't send command */ - if (!iwl_is_ready_rf(priv)) - return ret; - - if (force && priv->cfg->ops->lib->send_tx_power) - ret = priv->cfg->ops->lib->send_tx_power(priv); + if (iwl_is_ready_rf(priv)) { + priv->tx_power_user_lmt = tx_power; + if (force && priv->cfg->ops->lib->send_tx_power) + ret = priv->cfg->ops->lib->send_tx_power(priv); + else if (!priv->cfg->ops->lib->send_tx_power) + ret = -EOPNOTSUPP; + /* + * if fail to set tx_power, restore the orig. tx power + */ + if (ret) + priv->tx_power_user_lmt = prev_tx_power; + } + /* + * Even this is an async host command, the command + * will always report success from uCode + * So once driver can placing the command into the queue + * successfully, driver can use priv->tx_power_user_lmt + * to reflect the current tx power + */ return ret; } EXPORT_SYMBOL(iwl_set_tx_power); @@ -1487,31 +1754,6 @@ void iwl_uninit_drv(struct iwl_priv *priv) } EXPORT_SYMBOL(iwl_uninit_drv); - -void iwl_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(priv, "Disabled interrupts\n"); -} -EXPORT_SYMBOL(iwl_disable_interrupts); - -void iwl_enable_interrupts(struct iwl_priv *priv) -{ - IWL_DEBUG_ISR(priv, "Enabling interrupts\n"); - set_bit(STATUS_INT_ENABLED, &priv->status); - iwl_write32(priv, CSR_INT_MASK, priv->inta_mask); -} -EXPORT_SYMBOL(iwl_enable_interrupts); - - #define ICT_COUNT (PAGE_SIZE/sizeof(u32)) /* Free dram table */ @@ -1581,7 +1823,7 @@ int iwl_reset_ict(struct iwl_priv *priv) spin_lock_irqsave(&priv->lock, flags); iwl_disable_interrupts(priv); - memset(&priv->ict_tbl[0],0, sizeof(u32) * ICT_COUNT); + memset(&priv->ict_tbl[0], 0, sizeof(u32) * ICT_COUNT); val = priv->aligned_ict_tbl_dma >> PAGE_SHIFT; @@ -1659,13 +1901,13 @@ irqreturn_t iwl_isr_ict(int irq, void *data) /* read all entries that not 0 start with ict_index */ while (priv->ict_tbl[priv->ict_index]) { - val |= priv->ict_tbl[priv->ict_index]; + val |= le32_to_cpu(priv->ict_tbl[priv->ict_index]); IWL_DEBUG_ISR(priv, "ICT index %d value 0x%08X\n", - priv->ict_index, - priv->ict_tbl[priv->ict_index]); + priv->ict_index, + le32_to_cpu(priv->ict_tbl[priv->ict_index])); priv->ict_tbl[priv->ict_index] = 0; priv->ict_index = iwl_queue_inc_wrap(priv->ict_index, - ICT_COUNT); + ICT_COUNT); } @@ -1745,7 +1987,7 @@ static irqreturn_t iwl_isr(int irq, void *data) } #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { + if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, " "fh 0x%08x\n", inta, inta_mask, inta_fh); @@ -1852,7 +2094,7 @@ int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags) u32 stat_flags = 0; struct iwl_host_cmd cmd = { .id = REPLY_STATISTICS_CMD, - .meta.flags = flags, + .flags = flags, .len = sizeof(stat_flags), .data = (u8 *) &stat_flags, }; @@ -1984,194 +2226,10 @@ int iwl_verify_ucode(struct iwl_priv *priv) EXPORT_SYMBOL(iwl_verify_ucode); -static const char *desc_lookup_text[] = { - "OK", - "FAIL", - "BAD_PARAM", - "BAD_CHECKSUM", - "NMI_INTERRUPT_WDG", - "SYSASSERT", - "FATAL_ERROR", - "BAD_COMMAND", - "HW_ERROR_TUNE_LOCK", - "HW_ERROR_TEMPERATURE", - "ILLEGAL_CHAN_FREQ", - "VCC_NOT_STABLE", - "FH_ERROR", - "NMI_INTERRUPT_HOST", - "NMI_INTERRUPT_ACTION_PT", - "NMI_INTERRUPT_UNKNOWN", - "UCODE_VERSION_MISMATCH", - "HW_ERROR_ABS_LOCK", - "HW_ERROR_CAL_LOCK_FAIL", - "NMI_INTERRUPT_INST_ACTION_PT", - "NMI_INTERRUPT_DATA_ACTION_PT", - "NMI_TRM_HW_ER", - "NMI_INTERRUPT_TRM", - "NMI_INTERRUPT_BREAK_POINT" - "DEBUG_0", - "DEBUG_1", - "DEBUG_2", - "DEBUG_3", - "UNKNOWN" -}; - -static const char *desc_lookup(int i) -{ - int max = ARRAY_SIZE(desc_lookup_text) - 1; - - if (i < 0 || i > max) - i = max; - - return desc_lookup_text[i]; -} - -#define ERROR_START_OFFSET (1 * sizeof(u32)) -#define ERROR_ELEM_SIZE (7 * sizeof(u32)) - -void iwl_dump_nic_error_log(struct iwl_priv *priv) -{ - u32 data2, line; - u32 desc, time, count, base, data1; - u32 blink1, blink2, ilink1, ilink2; - - if (priv->ucode_type == UCODE_INIT) - base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr); - else - base = le32_to_cpu(priv->card_alive.error_event_table_ptr); - - if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { - IWL_ERR(priv, "Not valid error log pointer 0x%08X\n", base); - return; - } - - count = iwl_read_targ_mem(priv, base); - - if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { - IWL_ERR(priv, "Start IWL Error Log Dump:\n"); - IWL_ERR(priv, "Status: 0x%08lX, count: %d\n", - priv->status, count); - } - - desc = iwl_read_targ_mem(priv, base + 1 * sizeof(u32)); - blink1 = iwl_read_targ_mem(priv, base + 3 * sizeof(u32)); - blink2 = iwl_read_targ_mem(priv, base + 4 * sizeof(u32)); - ilink1 = iwl_read_targ_mem(priv, base + 5 * sizeof(u32)); - ilink2 = iwl_read_targ_mem(priv, base + 6 * sizeof(u32)); - data1 = iwl_read_targ_mem(priv, base + 7 * sizeof(u32)); - data2 = iwl_read_targ_mem(priv, base + 8 * sizeof(u32)); - line = iwl_read_targ_mem(priv, base + 9 * sizeof(u32)); - time = iwl_read_targ_mem(priv, base + 11 * sizeof(u32)); - - IWL_ERR(priv, "Desc Time " - "data1 data2 line\n"); - IWL_ERR(priv, "%-28s (#%02d) %010u 0x%08X 0x%08X %u\n", - desc_lookup(desc), desc, time, data1, data2, line); - IWL_ERR(priv, "blink1 blink2 ilink1 ilink2\n"); - IWL_ERR(priv, "0x%05X 0x%05X 0x%05X 0x%05X\n", blink1, blink2, - ilink1, ilink2); - -} -EXPORT_SYMBOL(iwl_dump_nic_error_log); - -#define EVENT_START_OFFSET (4 * sizeof(u32)) - -/** - * iwl_print_event_log - Dump error event log to syslog - * - */ -static void iwl_print_event_log(struct iwl_priv *priv, u32 start_idx, - u32 num_events, u32 mode) -{ - u32 i; - u32 base; /* SRAM byte address of event log header */ - u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ - u32 ptr; /* SRAM byte address of log data */ - u32 ev, time, data; /* event log data */ - - if (num_events == 0) - return; - if (priv->ucode_type == UCODE_INIT) - base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); - else - base = le32_to_cpu(priv->card_alive.log_event_table_ptr); - - if (mode == 0) - event_size = 2 * sizeof(u32); - else - event_size = 3 * sizeof(u32); - - ptr = base + EVENT_START_OFFSET + (start_idx * event_size); - - /* "time" is actually "data" for mode 0 (no timestamp). - * place event id # at far right for easier visual parsing. */ - for (i = 0; i < num_events; i++) { - ev = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); - time = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); - if (mode == 0) { - /* data, ev */ - IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", time, ev); - } else { - data = iwl_read_targ_mem(priv, ptr); - ptr += sizeof(u32); - IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", - time, data, ev); - } - } -} - -void iwl_dump_nic_event_log(struct iwl_priv *priv) -{ - u32 base; /* SRAM byte address of event log header */ - u32 capacity; /* event log capacity in # entries */ - u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ - u32 num_wraps; /* # times uCode wrapped to top of log */ - u32 next_entry; /* index of next entry to be written by uCode */ - u32 size; /* # entries that we'll print */ - - if (priv->ucode_type == UCODE_INIT) - base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); - else - base = le32_to_cpu(priv->card_alive.log_event_table_ptr); - - if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { - IWL_ERR(priv, "Invalid event log pointer 0x%08X\n", base); - return; - } - - /* event log header */ - capacity = iwl_read_targ_mem(priv, base); - mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32))); - num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32))); - next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32))); - - size = num_wraps ? capacity : next_entry; - - /* bail out if nothing in log */ - if (size == 0) { - IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n"); - return; - } - - IWL_ERR(priv, "Start IWL Event Log Dump: display count %d, wraps %d\n", - size, num_wraps); - - /* if uCode has wrapped back to top of log, start at the oldest entry, - * i.e the next one that uCode would fill. */ - if (num_wraps) - iwl_print_event_log(priv, next_entry, - capacity - next_entry, mode); - /* (then/else) start at top of log */ - iwl_print_event_log(priv, 0, next_entry, mode); - -} -EXPORT_SYMBOL(iwl_dump_nic_event_log); - void iwl_rf_kill_ct_config(struct iwl_priv *priv) { struct iwl_ct_kill_config cmd; + struct iwl_ct_kill_throttling_config adv_cmd; unsigned long flags; int ret = 0; @@ -2179,18 +2237,44 @@ void iwl_rf_kill_ct_config(struct iwl_priv *priv) iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); spin_unlock_irqrestore(&priv->lock, flags); + priv->thermal_throttle.ct_kill_toggle = false; + + switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { + case CSR_HW_REV_TYPE_1000: + case CSR_HW_REV_TYPE_6x00: + case CSR_HW_REV_TYPE_6x50: + adv_cmd.critical_temperature_enter = + cpu_to_le32(priv->hw_params.ct_kill_threshold); + adv_cmd.critical_temperature_exit = + cpu_to_le32(priv->hw_params.ct_kill_exit_threshold); + + ret = iwl_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD, + sizeof(adv_cmd), &adv_cmd); + if (ret) + IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); + else + IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " + "succeeded, " + "critical temperature enter is %d," + "exit is %d\n", + priv->hw_params.ct_kill_threshold, + priv->hw_params.ct_kill_exit_threshold); + break; + default: + cmd.critical_temperature_R = + cpu_to_le32(priv->hw_params.ct_kill_threshold); - cmd.critical_temperature_R = - cpu_to_le32(priv->hw_params.ct_kill_threshold); - - ret = iwl_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD, - sizeof(cmd), &cmd); - if (ret) - IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); - else - IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD succeeded, " - "critical temperature is %d\n", - cmd.critical_temperature_R); + ret = iwl_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD, + sizeof(cmd), &cmd); + if (ret) + IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); + else + IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " + "succeeded, " + "critical temperature is %d\n", + priv->hw_params.ct_kill_threshold); + break; + } } EXPORT_SYMBOL(iwl_rf_kill_ct_config); @@ -2211,12 +2295,11 @@ int iwl_send_card_state(struct iwl_priv *priv, u32 flags, u8 meta_flag) .id = REPLY_CARD_STATE_CMD, .len = sizeof(u32), .data = &flags, - .meta.flags = meta_flag, + .flags = meta_flag, }; return iwl_send_cmd(priv, &cmd); } -EXPORT_SYMBOL(iwl_send_card_state); void iwl_rx_pm_sleep_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) @@ -2234,10 +2317,11 @@ void iwl_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; + u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; IWL_DEBUG_RADIO(priv, "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)); + "notification for %s:\n", len, + get_cmd_string(pkt->hdr.cmd)); + iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->u.raw, len); } EXPORT_SYMBOL(iwl_rx_pm_debug_statistics_notif); @@ -2260,7 +2344,6 @@ void iwl_clear_isr_stats(struct iwl_priv *priv) { memset(&priv->isr_stats, 0, sizeof(priv->isr_stats)); } -EXPORT_SYMBOL(iwl_clear_isr_stats); int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params) @@ -2333,39 +2416,10 @@ static void iwl_ht_conf(struct iwl_priv *priv, } ht_conf = &sta->ht_cap; - 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_20_40); - - /* - * XXX: The HT configuration needs to be moved into iwl_mac_config() - * to be done there correctly. - */ - - iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; - if (conf_is_ht40_minus(&priv->hw->conf)) - iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; - else if (conf_is_ht40_plus(&priv->hw->conf)) - iwl_conf->extension_chan_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - - /* If no above or below channel supplied disable FAT channel */ - if (iwl_conf->extension_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_ABOVE && - iwl_conf->extension_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_BELOW) - iwl_conf->supported_chan_width = 0; - iwl_conf->sm_ps = (u8)((ht_conf->cap & IEEE80211_HT_CAP_SM_PS) >> 2); memcpy(&iwl_conf->mcs, &ht_conf->mcs, 16); - iwl_conf->tx_chan_width = iwl_conf->supported_chan_width != 0; iwl_conf->ht_protection = bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; iwl_conf->non_GF_STA_present = @@ -2492,7 +2546,6 @@ void iwl_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; @@ -2679,6 +2732,7 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) struct iwl_priv *priv = hw->priv; const struct iwl_channel_info *ch_info; struct ieee80211_conf *conf = &hw->conf; + struct iwl_ht_info *ht_conf = &priv->current_ht_config; unsigned long flags = 0; int ret = 0; u16 ch; @@ -2720,10 +2774,32 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) goto set_ch_out; } - priv->current_ht_config.is_ht = conf_is_ht(conf); - spin_lock_irqsave(&priv->lock, flags); + /* Configure HT40 channels */ + ht_conf->is_ht = conf_is_ht(conf); + if (ht_conf->is_ht) { + if (conf_is_ht40_minus(conf)) { + ht_conf->extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_BELOW; + ht_conf->supported_chan_width = + IWL_CHANNEL_WIDTH_40MHZ; + } else if (conf_is_ht40_plus(conf)) { + ht_conf->extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + ht_conf->supported_chan_width = + IWL_CHANNEL_WIDTH_40MHZ; + } else { + ht_conf->extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_NONE; + ht_conf->supported_chan_width = + IWL_CHANNEL_WIDTH_20MHZ; + } + } else + ht_conf->supported_chan_width = IWL_CHANNEL_WIDTH_20MHZ; + /* Default to no protection. Protection mode will later be set + * from BSS config in iwl_ht_conf */ + ht_conf->ht_protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; /* if we are switching from ht to 2.4 clear flags * from any ht related info since 2.4 does not @@ -2742,13 +2818,10 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) iwl_set_rate(priv); } - if (changed & IEEE80211_CONF_CHANGE_PS && - priv->iw_mode == NL80211_IFTYPE_STATION) { - priv->power_data.power_disabled = - !(conf->flags & IEEE80211_CONF_PS); - ret = iwl_power_update_mode(priv, 0); + if (changed & IEEE80211_CONF_CHANGE_PS) { + ret = iwl_power_update_mode(priv, false); if (ret) - IWL_DEBUG_MAC80211(priv, "Error setting power level\n"); + IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n"); } if (changed & IEEE80211_CONF_CHANGE_POWER) { @@ -2881,6 +2954,248 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw) } EXPORT_SYMBOL(iwl_mac_reset_tsf); +#ifdef CONFIG_IWLWIFI_DEBUGFS + +#define IWL_TRAFFIC_DUMP_SIZE (IWL_TRAFFIC_ENTRY_SIZE * IWL_TRAFFIC_ENTRIES) + +void iwl_reset_traffic_log(struct iwl_priv *priv) +{ + priv->tx_traffic_idx = 0; + priv->rx_traffic_idx = 0; + if (priv->tx_traffic) + memset(priv->tx_traffic, 0, IWL_TRAFFIC_DUMP_SIZE); + if (priv->rx_traffic) + memset(priv->rx_traffic, 0, IWL_TRAFFIC_DUMP_SIZE); +} + +int iwl_alloc_traffic_mem(struct iwl_priv *priv) +{ + u32 traffic_size = IWL_TRAFFIC_DUMP_SIZE; + + if (iwl_debug_level & IWL_DL_TX) { + if (!priv->tx_traffic) { + priv->tx_traffic = + kzalloc(traffic_size, GFP_KERNEL); + if (!priv->tx_traffic) + return -ENOMEM; + } + } + if (iwl_debug_level & IWL_DL_RX) { + if (!priv->rx_traffic) { + priv->rx_traffic = + kzalloc(traffic_size, GFP_KERNEL); + if (!priv->rx_traffic) + return -ENOMEM; + } + } + iwl_reset_traffic_log(priv); + return 0; +} +EXPORT_SYMBOL(iwl_alloc_traffic_mem); + +void iwl_free_traffic_mem(struct iwl_priv *priv) +{ + kfree(priv->tx_traffic); + priv->tx_traffic = NULL; + + kfree(priv->rx_traffic); + priv->rx_traffic = NULL; +} +EXPORT_SYMBOL(iwl_free_traffic_mem); + +void iwl_dbg_log_tx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header) +{ + __le16 fc; + u16 len; + + if (likely(!(iwl_debug_level & IWL_DL_TX))) + return; + + if (!priv->tx_traffic) + return; + + fc = header->frame_control; + if (ieee80211_is_data(fc)) { + len = (length > IWL_TRAFFIC_ENTRY_SIZE) + ? IWL_TRAFFIC_ENTRY_SIZE : length; + memcpy((priv->tx_traffic + + (priv->tx_traffic_idx * IWL_TRAFFIC_ENTRY_SIZE)), + header, len); + priv->tx_traffic_idx = + (priv->tx_traffic_idx + 1) % IWL_TRAFFIC_ENTRIES; + } +} +EXPORT_SYMBOL(iwl_dbg_log_tx_data_frame); + +void iwl_dbg_log_rx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header) +{ + __le16 fc; + u16 len; + + if (likely(!(iwl_debug_level & IWL_DL_RX))) + return; + + if (!priv->rx_traffic) + return; + + fc = header->frame_control; + if (ieee80211_is_data(fc)) { + len = (length > IWL_TRAFFIC_ENTRY_SIZE) + ? IWL_TRAFFIC_ENTRY_SIZE : length; + memcpy((priv->rx_traffic + + (priv->rx_traffic_idx * IWL_TRAFFIC_ENTRY_SIZE)), + header, len); + priv->rx_traffic_idx = + (priv->rx_traffic_idx + 1) % IWL_TRAFFIC_ENTRIES; + } +} +EXPORT_SYMBOL(iwl_dbg_log_rx_data_frame); + +const char *get_mgmt_string(int cmd) +{ + switch (cmd) { + IWL_CMD(MANAGEMENT_ASSOC_REQ); + IWL_CMD(MANAGEMENT_ASSOC_RESP); + IWL_CMD(MANAGEMENT_REASSOC_REQ); + IWL_CMD(MANAGEMENT_REASSOC_RESP); + IWL_CMD(MANAGEMENT_PROBE_REQ); + IWL_CMD(MANAGEMENT_PROBE_RESP); + IWL_CMD(MANAGEMENT_BEACON); + IWL_CMD(MANAGEMENT_ATIM); + IWL_CMD(MANAGEMENT_DISASSOC); + IWL_CMD(MANAGEMENT_AUTH); + IWL_CMD(MANAGEMENT_DEAUTH); + IWL_CMD(MANAGEMENT_ACTION); + default: + return "UNKNOWN"; + + } +} + +const char *get_ctrl_string(int cmd) +{ + switch (cmd) { + IWL_CMD(CONTROL_BACK_REQ); + IWL_CMD(CONTROL_BACK); + IWL_CMD(CONTROL_PSPOLL); + IWL_CMD(CONTROL_RTS); + IWL_CMD(CONTROL_CTS); + IWL_CMD(CONTROL_ACK); + IWL_CMD(CONTROL_CFEND); + IWL_CMD(CONTROL_CFENDACK); + default: + return "UNKNOWN"; + + } +} + +void iwl_clear_tx_stats(struct iwl_priv *priv) +{ + memset(&priv->tx_stats, 0, sizeof(struct traffic_stats)); + +} + +void iwl_clear_rx_stats(struct iwl_priv *priv) +{ + memset(&priv->rx_stats, 0, sizeof(struct traffic_stats)); +} + +/* + * if CONFIG_IWLWIFI_DEBUGFS defined, iwl_update_stats function will + * record all the MGMT, CTRL and DATA pkt for both TX and Rx pass. + * Use debugFs to display the rx/rx_statistics + * if CONFIG_IWLWIFI_DEBUGFS not being defined, then no MGMT and CTRL + * information will be recorded, but DATA pkt still will be recorded + * for the reason of iwl_led.c need to control the led blinking based on + * number of tx and rx data. + * + */ +void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len) +{ + struct traffic_stats *stats; + + if (is_tx) + stats = &priv->tx_stats; + else + stats = &priv->rx_stats; + + if (ieee80211_is_mgmt(fc)) { + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): + stats->mgmt[MANAGEMENT_ASSOC_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): + stats->mgmt[MANAGEMENT_ASSOC_RESP]++; + break; + case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): + stats->mgmt[MANAGEMENT_REASSOC_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP): + stats->mgmt[MANAGEMENT_REASSOC_RESP]++; + break; + case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): + stats->mgmt[MANAGEMENT_PROBE_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP): + stats->mgmt[MANAGEMENT_PROBE_RESP]++; + break; + case cpu_to_le16(IEEE80211_STYPE_BEACON): + stats->mgmt[MANAGEMENT_BEACON]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ATIM): + stats->mgmt[MANAGEMENT_ATIM]++; + break; + case cpu_to_le16(IEEE80211_STYPE_DISASSOC): + stats->mgmt[MANAGEMENT_DISASSOC]++; + break; + case cpu_to_le16(IEEE80211_STYPE_AUTH): + stats->mgmt[MANAGEMENT_AUTH]++; + break; + case cpu_to_le16(IEEE80211_STYPE_DEAUTH): + stats->mgmt[MANAGEMENT_DEAUTH]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ACTION): + stats->mgmt[MANAGEMENT_ACTION]++; + break; + } + } else if (ieee80211_is_ctl(fc)) { + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_BACK_REQ): + stats->ctrl[CONTROL_BACK_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_BACK): + stats->ctrl[CONTROL_BACK]++; + break; + case cpu_to_le16(IEEE80211_STYPE_PSPOLL): + stats->ctrl[CONTROL_PSPOLL]++; + break; + case cpu_to_le16(IEEE80211_STYPE_RTS): + stats->ctrl[CONTROL_RTS]++; + break; + case cpu_to_le16(IEEE80211_STYPE_CTS): + stats->ctrl[CONTROL_CTS]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ACK): + stats->ctrl[CONTROL_ACK]++; + break; + case cpu_to_le16(IEEE80211_STYPE_CFEND): + stats->ctrl[CONTROL_CFEND]++; + break; + case cpu_to_le16(IEEE80211_STYPE_CFENDACK): + stats->ctrl[CONTROL_CFENDACK]++; + break; + } + } else { + /* data */ + stats->data_cnt++; + stats->data_bytes += len; + } +} +EXPORT_SYMBOL(iwl_update_stats); +#endif + #ifdef CONFIG_PM int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index dabf663e36e5..62d90364b61d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -83,6 +83,8 @@ struct iwl_cmd; #define IWL_SKU_A 0x2 #define IWL_SKU_N 0x8 +#define IWL_CMD(x) case x: return #x + struct iwl_hcmd_ops { int (*rxon_assoc)(struct iwl_priv *priv); int (*commit_rxon)(struct iwl_priv *priv); @@ -116,6 +118,17 @@ struct iwl_temp_ops { void (*set_ct_kill)(struct iwl_priv *priv); }; +struct iwl_ucode_ops { + u32 (*get_header_size)(u32); + u32 (*get_build)(const struct iwl_ucode_header *, u32); + u32 (*get_inst_size)(const struct iwl_ucode_header *, u32); + u32 (*get_data_size)(const struct iwl_ucode_header *, u32); + u32 (*get_init_size)(const struct iwl_ucode_header *, u32); + u32 (*get_init_data_size)(const struct iwl_ucode_header *, u32); + u32 (*get_boot_size)(const struct iwl_ucode_header *, u32); + u8 * (*get_data)(const struct iwl_ucode_header *, u32); +}; + struct iwl_lib_ops { /* set hw dependent parameters */ int (*set_hw_params)(struct iwl_priv *priv); @@ -171,6 +184,7 @@ struct iwl_lib_ops { }; struct iwl_ops { + const struct iwl_ucode_ops *ucode; const struct iwl_lib_ops *lib; const struct iwl_hcmd_ops *hcmd; const struct iwl_hcmd_utils_ops *utils; @@ -178,7 +192,6 @@ struct iwl_ops { struct iwl_mod_params { int sw_crypto; /* def: 0 = using hardware encryption */ - u32 debug; /* def: 0 = minimal debug log messages */ int disable_hw_scan; /* def: 0 = use h/w scan */ int num_of_queues; /* def: HW dependent */ int num_of_ampdu_queues;/* def: HW dependent */ @@ -195,6 +208,9 @@ struct iwl_mod_params { * filename is constructed as fw_name_pre<api>.ucode. * @ucode_api_max: Highest version of uCode API supported by driver. * @ucode_api_min: Lowest version of uCode API supported by driver. + * @pa_type: used by 6000 series only to identify the type of Power Amplifier + * @max_ll_items: max number of OTP blocks + * @shadow_ram_support: shadow support for OTP memory * * We enable the driver to be backward compatible wrt API version. The * driver specifies which APIs it supports (with @ucode_api_max being the @@ -215,6 +231,7 @@ struct iwl_mod_params { * iwl_hcmd_utils_ops etc. we accommodate different command structures * and flows between hardware versions (4965/5000) as well as their API * versions. + * */ struct iwl_cfg { const char *name; @@ -231,6 +248,10 @@ struct iwl_cfg { u8 valid_rx_ant; bool need_pll_cfg; bool use_isr_legacy; + enum iwl_pa_type pa_type; + const u16 max_ll_items; + const bool shadow_ram_support; + const bool ht_greenfield_support; }; /*************************** @@ -250,7 +271,7 @@ int iwl_full_rxon_required(struct iwl_priv *priv); void iwl_set_rxon_chain(struct iwl_priv *priv); int iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch); void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_info *ht_info); -u8 iwl_is_fat_tx_allowed(struct iwl_priv *priv, +u8 iwl_is_ht40_tx_allowed(struct iwl_priv *priv, struct ieee80211_sta_ht_cap *sta_ht_inf); void iwl_set_flags_for_band(struct iwl_priv *priv, enum ieee80211_band band); void iwl_connection_init_rx_config(struct iwl_priv *priv, int mode); @@ -261,8 +282,7 @@ int iwl_set_decrypted_flag(struct iwl_priv *priv, void iwl_irq_handle_error(struct iwl_priv *priv); void iwl_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list); + unsigned int *total_flags, u64 multicast); int iwl_hw_nic_init(struct iwl_priv *priv); int iwl_setup_mac(struct iwl_priv *priv); int iwl_set_hw_params(struct iwl_priv *priv); @@ -286,7 +306,55 @@ void iwl_config_ap(struct iwl_priv *priv); int iwl_mac_get_tx_stats(struct ieee80211_hw *hw, struct ieee80211_tx_queue_stats *stats); void iwl_mac_reset_tsf(struct ieee80211_hw *hw); +#ifdef CONFIG_IWLWIFI_DEBUGFS +int iwl_alloc_traffic_mem(struct iwl_priv *priv); +void iwl_free_traffic_mem(struct iwl_priv *priv); +void iwl_reset_traffic_log(struct iwl_priv *priv); +void iwl_dbg_log_tx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header); +void iwl_dbg_log_rx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header); +const char *get_mgmt_string(int cmd); +const char *get_ctrl_string(int cmd); +void iwl_clear_tx_stats(struct iwl_priv *priv); +void iwl_clear_rx_stats(struct iwl_priv *priv); +void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, + u16 len); +#else +static inline int iwl_alloc_traffic_mem(struct iwl_priv *priv) +{ + return 0; +} +static inline void iwl_free_traffic_mem(struct iwl_priv *priv) +{ +} +static inline void iwl_reset_traffic_log(struct iwl_priv *priv) +{ +} +static inline void iwl_dbg_log_tx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header) +{ +} +static inline void iwl_dbg_log_rx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header) +{ +} +static inline void iwl_update_stats(struct iwl_priv *priv, bool is_tx, + __le16 fc, u16 len) +{ + struct traffic_stats *stats; + + if (is_tx) + stats = &priv->tx_stats; + else + stats = &priv->rx_stats; + if (ieee80211_is_data(fc)) { + /* data */ + stats->data_bytes += len; + } +} +#endif /***************************************************** * RX handlers. * **************************************************/ @@ -384,7 +452,6 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags) void iwl_init_scan_params(struct iwl_priv *priv); int iwl_scan_cancel(struct iwl_priv *priv); int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); -int iwl_scan_initiate(struct iwl_priv *priv); int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req); u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame, const u8 *ie, int ie_len, int left); @@ -398,7 +465,6 @@ void iwl_bg_scan_check(struct work_struct *data); void iwl_bg_abort_scan(struct work_struct *work); void iwl_bg_scan_completed(struct work_struct *work); void iwl_setup_scan_deferred_work(struct iwl_priv *priv); -int iwl_send_scan_abort(struct iwl_priv *priv); /* For faster active scanning, scan will move to the next channel if fewer than * PLCP_QUIET_THRESH packets are heard on this channel within @@ -437,9 +503,9 @@ int __must_check iwl_send_cmd_pdu(struct iwl_priv *priv, u8 id, u16 len, const void *data); int iwl_send_cmd_pdu_async(struct iwl_priv *priv, u8 id, u16 len, const void *data, - int (*callback)(struct iwl_priv *priv, - struct iwl_cmd *cmd, - struct sk_buff *skb)); + void (*callback)(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb)); int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd); @@ -449,8 +515,6 @@ int iwl_send_card_state(struct iwl_priv *priv, u32 flags, /***************************************************** * PCI * *****************************************************/ -void iwl_disable_interrupts(struct iwl_priv *priv); -void iwl_enable_interrupts(struct iwl_priv *priv); irqreturn_t iwl_isr_legacy(int irq, void *data); int iwl_reset_ict(struct iwl_priv *priv); void iwl_disable_ict(struct iwl_priv *priv); @@ -474,7 +538,6 @@ int iwl_pci_resume(struct pci_dev *pdev); /***************************************************** * Error Handling Debugging ******************************************************/ -void iwl_dump_nic_error_log(struct iwl_priv *priv); void iwl_dump_nic_event_log(struct iwl_priv *priv); void iwl_clear_isr_stats(struct iwl_priv *priv); @@ -556,6 +619,7 @@ extern void iwl_rx_reply_rx_phy(struct iwl_priv *priv, void iwl_rx_reply_compressed_ba(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); +void iwl_setup_rxon_timing(struct iwl_priv *priv); static inline int iwl_send_rxon_assoc(struct iwl_priv *priv) { return priv->cfg->ops->hcmd->rxon_assoc(priv); diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index f03dae1b2f36..06437d13e73e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -91,7 +91,8 @@ #define CSR_EEPROM_GP (CSR_BASE+0x030) #define CSR_OTP_GP_REG (CSR_BASE+0x034) #define CSR_GIO_REG (CSR_BASE+0x03C) -#define CSR_GP_UCODE (CSR_BASE+0x044) +#define CSR_GP_UCODE_REG (CSR_BASE+0x048) +#define CSR_GP_DRIVER_REG (CSR_BASE+0x050) #define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054) #define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058) #define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c) @@ -245,6 +246,13 @@ #define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004) #define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008) +/* GP Driver */ +#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_MSK (0x00000003) +#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_3x3_HYB (0x00000000) +#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_HYB (0x00000001) +#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA (0x00000002) + + /* GI Chicken Bits */ #define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) #define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index 2cf014f523be..cbc62904655d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -30,16 +30,23 @@ #define __iwl_debug_h__ struct iwl_priv; +extern u32 iwl_debug_level; #define IWL_ERR(p, f, a...) dev_err(&((p)->pci_dev->dev), f, ## a) #define IWL_WARN(p, f, a...) dev_warn(&((p)->pci_dev->dev), f, ## a) #define IWL_INFO(p, f, a...) dev_info(&((p)->pci_dev->dev), f, ## a) #define IWL_CRIT(p, f, a...) dev_crit(&((p)->pci_dev->dev), f, ## a) +#define iwl_print_hex_error(priv, p, len) \ +do { \ + print_hex_dump(KERN_ERR, "iwl data: ", \ + DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ +} while (0) + #ifdef CONFIG_IWLWIFI_DEBUG #define IWL_DEBUG(__priv, level, fmt, args...) \ do { \ - if (__priv->debug_level & (level)) \ + if (iwl_get_debug_level(__priv) & (level)) \ dev_printk(KERN_ERR, &(__priv->hw->wiphy->dev), \ "%c %s " fmt, in_interrupt() ? 'I' : 'U', \ __func__ , ## args); \ @@ -47,7 +54,7 @@ do { \ #define IWL_DEBUG_LIMIT(__priv, level, fmt, args...) \ do { \ - if ((__priv->debug_level & (level)) && net_ratelimit()) \ + if ((iwl_get_debug_level(__priv) & (level)) && net_ratelimit()) \ dev_printk(KERN_ERR, &(__priv->hw->wiphy->dev), \ "%c %s " fmt, in_interrupt() ? 'I' : 'U', \ __func__ , ## args); \ @@ -55,7 +62,7 @@ do { \ #define iwl_print_hex_dump(priv, level, p, len) \ do { \ - if (priv->debug_level & level) \ + if (iwl_get_debug_level(priv) & level) \ print_hex_dump(KERN_DEBUG, "iwl data: ", \ DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ } while (0) @@ -65,23 +72,43 @@ struct iwl_debugfs { const char *name; struct dentry *dir_drv; struct dentry *dir_data; + struct dentry *dir_debug; struct dentry *dir_rf; struct dir_data_files { struct dentry *file_sram; struct dentry *file_nvm; struct dentry *file_stations; - struct dentry *file_rx_statistics; - struct dentry *file_tx_statistics; struct dentry *file_log_event; struct dentry *file_channels; struct dentry *file_status; struct dentry *file_interrupt; + struct dentry *file_qos; + struct dentry *file_thermal_throttling; +#ifdef CONFIG_IWLWIFI_LEDS + struct dentry *file_led; +#endif + struct dentry *file_disable_ht40; + struct dentry *file_sleep_level_override; + struct dentry *file_current_sleep_command; } dbgfs_data_files; struct dir_rf_files { struct dentry *file_disable_sensitivity; struct dentry *file_disable_chain_noise; struct dentry *file_disable_tx_power; } dbgfs_rf_files; + struct dir_debug_files { + struct dentry *file_rx_statistics; + struct dentry *file_tx_statistics; + struct dentry *file_traffic_log; + struct dentry *file_rx_queue; + struct dentry *file_tx_queue; + struct dentry *file_ucode_rx_stats; + struct dentry *file_ucode_tx_stats; + struct dentry *file_ucode_general_stats; + struct dentry *file_sensitivity; + struct dentry *file_chain_noise; + struct dentry *file_tx_power; + } dbgfs_debug_files; u32 sram_offset; u32 sram_len; }; diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c index ca00cc8ad4c7..fb844859a443 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c @@ -38,7 +38,7 @@ #include "iwl-debug.h" #include "iwl-core.h" #include "iwl-io.h" - +#include "iwl-calib.h" /* create and remove of files */ #define DEBUGFS_ADD_DIR(name, parent) do { \ @@ -49,7 +49,8 @@ #define DEBUGFS_ADD_FILE(name, parent) do { \ dbgfs->dbgfs_##parent##_files.file_##name = \ - debugfs_create_file(#name, 0644, dbgfs->dir_##parent, priv, \ + debugfs_create_file(#name, S_IWUSR | S_IRUSR, \ + dbgfs->dir_##parent, priv, \ &iwl_dbgfs_##name##_ops); \ if (!(dbgfs->dbgfs_##parent##_files.file_##name)) \ goto err; \ @@ -57,7 +58,8 @@ #define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \ dbgfs->dbgfs_##parent##_files.file_##name = \ - debugfs_create_bool(#name, 0644, dbgfs->dir_##parent, ptr); \ + debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \ + dbgfs->dir_##parent, ptr); \ if (IS_ERR(dbgfs->dbgfs_##parent##_files.file_##name) \ || !dbgfs->dbgfs_##parent##_files.file_##name) \ goto err; \ @@ -65,7 +67,7 @@ #define DEBUGFS_ADD_X32(name, parent, ptr) do { \ dbgfs->dbgfs_##parent##_files.file_##name = \ - debugfs_create_x32(#name, 0444, dbgfs->dir_##parent, ptr); \ + debugfs_create_x32(#name, S_IRUSR, dbgfs->dir_##parent, ptr); \ if (IS_ERR(dbgfs->dbgfs_##parent##_files.file_##name) \ || !dbgfs->dbgfs_##parent##_files.file_##name) \ goto err; \ @@ -124,18 +126,58 @@ static ssize_t iwl_dbgfs_tx_statistics_read(struct file *file, size_t count, loff_t *ppos) { struct iwl_priv *priv = (struct iwl_priv *)file->private_data; - char buf[256]; + char *buf; int pos = 0; - const size_t bufsz = sizeof(buf); - pos += scnprintf(buf + pos, bufsz - pos, "mgmt: %u\n", - priv->tx_stats[0].cnt); - pos += scnprintf(buf + pos, bufsz - pos, "ctrl: %u\n", - priv->tx_stats[1].cnt); - pos += scnprintf(buf + pos, bufsz - pos, "data: %u\n", - priv->tx_stats[2].cnt); + int cnt; + ssize_t ret; + const size_t bufsz = 100 + sizeof(char) * 24 * (MANAGEMENT_MAX + CONTROL_MAX); + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); + for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\t%s\t\t: %u\n", + get_mgmt_string(cnt), + priv->tx_stats.mgmt[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Control\n"); + for (cnt = 0; cnt < CONTROL_MAX; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\t%s\t\t: %u\n", + get_ctrl_string(cnt), + priv->tx_stats.ctrl[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Data:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "\tcnt: %u\n", + priv->tx_stats.data_cnt); + pos += scnprintf(buf + pos, bufsz - pos, "\tbytes: %llu\n", + priv->tx_stats.data_bytes); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +static ssize_t iwl_dbgfs_tx_statistics_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + u32 clear_flag; + char buf[8]; + int buf_size; + + 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", &clear_flag) != 1) + return -EFAULT; + if (clear_flag == 1) + iwl_clear_tx_stats(priv); + + return count; } static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file, @@ -143,18 +185,59 @@ static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file, size_t count, loff_t *ppos) { struct iwl_priv *priv = (struct iwl_priv *)file->private_data; - char buf[256]; + char *buf; int pos = 0; - const size_t bufsz = sizeof(buf); + int cnt; + ssize_t ret; + const size_t bufsz = 100 + + sizeof(char) * 24 * (MANAGEMENT_MAX + CONTROL_MAX); + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; - pos += scnprintf(buf + pos, bufsz - pos, "mgmt: %u\n", - priv->rx_stats[0].cnt); - pos += scnprintf(buf + pos, bufsz - pos, "ctrl: %u\n", - priv->rx_stats[1].cnt); - pos += scnprintf(buf + pos, bufsz - pos, "data: %u\n", - priv->rx_stats[2].cnt); + pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); + for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\t%s\t\t: %u\n", + get_mgmt_string(cnt), + priv->rx_stats.mgmt[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Control:\n"); + for (cnt = 0; cnt < CONTROL_MAX; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\t%s\t\t: %u\n", + get_ctrl_string(cnt), + priv->rx_stats.ctrl[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Data:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "\tcnt: %u\n", + priv->rx_stats.data_cnt); + pos += scnprintf(buf + pos, bufsz - pos, "\tbytes: %llu\n", + priv->rx_stats.data_bytes); - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_rx_statistics_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + u32 clear_flag; + char buf[8]; + int buf_size; + + 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", &clear_flag) != 1) + return -EFAULT; + if (clear_flag == 1) + iwl_clear_rx_stats(priv); + return count; } #define BYTE1_MASK 0x000000ff; @@ -566,16 +649,982 @@ static ssize_t iwl_dbgfs_interrupt_write(struct file *file, return count; } +static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0, i; + char buf[256]; + const size_t bufsz = sizeof(buf); + ssize_t ret; + + for (i = 0; i < AC_NUM; i++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\tcw_min\tcw_max\taifsn\ttxop\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "AC[%d]\t%u\t%u\t%u\t%u\n", i, + priv->qos_data.def_qos_parm.ac[i].cw_min, + priv->qos_data.def_qos_parm.ac[i].cw_max, + priv->qos_data.def_qos_parm.ac[i].aifsn, + priv->qos_data.def_qos_parm.ac[i].edca_txop); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + return ret; +} + +#ifdef CONFIG_IWLWIFI_LEDS +static ssize_t iwl_dbgfs_led_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char buf[256]; + const size_t bufsz = sizeof(buf); + ssize_t ret; + + pos += scnprintf(buf + pos, bufsz - pos, + "allow blinking: %s\n", + (priv->allow_blinking) ? "True" : "False"); + if (priv->allow_blinking) { + pos += scnprintf(buf + pos, bufsz - pos, + "Led blinking rate: %u\n", + priv->last_blink_rate); + pos += scnprintf(buf + pos, bufsz - pos, + "Last blink time: %lu\n", + priv->last_blink_time); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + return ret; +} +#endif + +static ssize_t iwl_dbgfs_thermal_throttling_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; + char buf[100]; + int pos = 0; + const size_t bufsz = sizeof(buf); + ssize_t ret; + + pos += scnprintf(buf + pos, bufsz - pos, + "Thermal Throttling Mode: %s\n", + tt->advanced_tt ? "Advance" : "Legacy"); + pos += scnprintf(buf + pos, bufsz - pos, + "Thermal Throttling State: %d\n", + tt->state); + if (tt->advanced_tt) { + restriction = tt->restriction + tt->state; + pos += scnprintf(buf + pos, bufsz - pos, + "Tx mode: %d\n", + restriction->tx_stream); + pos += scnprintf(buf + pos, bufsz - pos, + "Rx mode: %d\n", + restriction->rx_stream); + pos += scnprintf(buf + pos, bufsz - pos, + "HT mode: %d\n", + restriction->is_ht); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + return ret; +} + +static ssize_t iwl_dbgfs_disable_ht40_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int ht40; + + 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, "%d", &ht40) != 1) + return -EFAULT; + if (!iwl_is_associated(priv)) + priv->disable_ht40 = ht40 ? true : false; + else { + IWL_ERR(priv, "Sta associated with AP - " + "Change to 40MHz channel support is not allowed\n"); + return -EINVAL; + } + + return count; +} + +static ssize_t iwl_dbgfs_disable_ht40_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + char buf[100]; + int pos = 0; + const size_t bufsz = sizeof(buf); + ssize_t ret; + + pos += scnprintf(buf + pos, bufsz - pos, + "11n 40MHz Mode: %s\n", + priv->disable_ht40 ? "Disabled" : "Enabled"); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + return ret; +} + +static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int value; + + 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, "%d", &value) != 1) + return -EINVAL; + + /* + * Our users expect 0 to be "CAM", but 0 isn't actually + * valid here. However, let's not confuse them and present + * IWL_POWER_INDEX_1 as "1", not "0". + */ + if (value > 0) + value -= 1; + + if (value != -1 && (value < 0 || value >= IWL_POWER_NUM)) + return -EINVAL; + + priv->power_data.debug_sleep_level_override = value; + + iwl_power_update_mode(priv, false); + + return count; +} + +static ssize_t iwl_dbgfs_sleep_level_override_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + char buf[10]; + int pos, value; + const size_t bufsz = sizeof(buf); + + /* see the write function */ + value = priv->power_data.debug_sleep_level_override; + if (value >= 0) + value += 1; + + pos = scnprintf(buf, bufsz, "%d\n", value); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + char buf[200]; + int pos = 0, i; + const size_t bufsz = sizeof(buf); + struct iwl_powertable_cmd *cmd = &priv->power_data.sleep_cmd; + + pos += scnprintf(buf + pos, bufsz - pos, + "flags: %#.2x\n", le16_to_cpu(cmd->flags)); + pos += scnprintf(buf + pos, bufsz - pos, + "RX/TX timeout: %d/%d usec\n", + le32_to_cpu(cmd->rx_data_timeout), + le32_to_cpu(cmd->tx_data_timeout)); + for (i = 0; i < IWL_POWER_VEC_SIZE; i++) + pos += scnprintf(buf + pos, bufsz - pos, + "sleep_interval[%d]: %d\n", i, + le32_to_cpu(cmd->sleep_interval[i])); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} DEBUGFS_READ_WRITE_FILE_OPS(sram); DEBUGFS_WRITE_FILE_OPS(log_event); DEBUGFS_READ_FILE_OPS(nvm); DEBUGFS_READ_FILE_OPS(stations); -DEBUGFS_READ_FILE_OPS(rx_statistics); -DEBUGFS_READ_FILE_OPS(tx_statistics); DEBUGFS_READ_FILE_OPS(channels); DEBUGFS_READ_FILE_OPS(status); DEBUGFS_READ_WRITE_FILE_OPS(interrupt); +DEBUGFS_READ_FILE_OPS(qos); +#ifdef CONFIG_IWLWIFI_LEDS +DEBUGFS_READ_FILE_OPS(led); +#endif +DEBUGFS_READ_FILE_OPS(thermal_throttling); +DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40); +DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override); +DEBUGFS_READ_FILE_OPS(current_sleep_command); + +static ssize_t iwl_dbgfs_traffic_log_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0, ofs = 0; + int cnt = 0, entry; + struct iwl_tx_queue *txq; + struct iwl_queue *q; + struct iwl_rx_queue *rxq = &priv->rxq; + char *buf; + int bufsz = ((IWL_TRAFFIC_ENTRIES * IWL_TRAFFIC_ENTRY_SIZE * 64) * 2) + + (IWL_MAX_NUM_QUEUES * 32 * 8) + 400; + const u8 *ptr; + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate buffer\n"); + return -ENOMEM; + } + pos += scnprintf(buf + pos, bufsz - pos, "Tx Queue\n"); + for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) { + txq = &priv->txq[cnt]; + q = &txq->q; + pos += scnprintf(buf + pos, bufsz - pos, + "q[%d]: read_ptr: %u, write_ptr: %u\n", + cnt, q->read_ptr, q->write_ptr); + } + if (priv->tx_traffic && (iwl_debug_level & IWL_DL_TX)) { + ptr = priv->tx_traffic; + pos += scnprintf(buf + pos, bufsz - pos, + "Tx Traffic idx: %u\n", priv->tx_traffic_idx); + for (cnt = 0, ofs = 0; cnt < IWL_TRAFFIC_ENTRIES; cnt++) { + for (entry = 0; entry < IWL_TRAFFIC_ENTRY_SIZE / 16; + entry++, ofs += 16) { + pos += scnprintf(buf + pos, bufsz - pos, + "0x%.4x ", ofs); + hex_dump_to_buffer(ptr + ofs, 16, 16, 2, + buf + pos, bufsz - pos, 0); + pos += strlen(buf); + if (bufsz - pos > 0) + buf[pos++] = '\n'; + } + } + } + + pos += scnprintf(buf + pos, bufsz - pos, "Rx Queue\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "read: %u, write: %u\n", + rxq->read, rxq->write); + + if (priv->rx_traffic && (iwl_debug_level & IWL_DL_RX)) { + ptr = priv->rx_traffic; + pos += scnprintf(buf + pos, bufsz - pos, + "Rx Traffic idx: %u\n", priv->rx_traffic_idx); + for (cnt = 0, ofs = 0; cnt < IWL_TRAFFIC_ENTRIES; cnt++) { + for (entry = 0; entry < IWL_TRAFFIC_ENTRY_SIZE / 16; + entry++, ofs += 16) { + pos += scnprintf(buf + pos, bufsz - pos, + "0x%.4x ", ofs); + hex_dump_to_buffer(ptr + ofs, 16, 16, 2, + buf + pos, bufsz - pos, 0); + pos += strlen(buf); + if (bufsz - pos > 0) + buf[pos++] = '\n'; + } + } + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_traffic_log_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int traffic_log; + + 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, "%d", &traffic_log) != 1) + return -EFAULT; + if (traffic_log == 0) + iwl_reset_traffic_log(priv); + + return count; +} + +static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_tx_queue *txq; + struct iwl_queue *q; + char *buf; + int pos = 0; + int cnt; + int ret; + const size_t bufsz = sizeof(char) * 60 * IWL_MAX_NUM_QUEUES; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) { + txq = &priv->txq[cnt]; + q = &txq->q; + pos += scnprintf(buf + pos, bufsz - pos, + "hwq %.2d: read=%u write=%u stop=%d" + " swq_id=%#.2x (ac %d/hwq %d)\n", + cnt, q->read_ptr, q->write_ptr, + !!test_bit(cnt, priv->queue_stopped), + txq->swq_id, + txq->swq_id & 0x80 ? txq->swq_id & 3 : + txq->swq_id, + txq->swq_id & 0x80 ? (txq->swq_id >> 2) & + 0x1f : txq->swq_id); + if (cnt >= 4) + continue; + /* for the ACs, display the stop count too */ + pos += scnprintf(buf + pos, bufsz - pos, + " stop-count: %d\n", + atomic_read(&priv->queue_stop_count[cnt])); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_rx_queue *rxq = &priv->rxq; + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", + rxq->read); + pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", + rxq->write); + pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", + rxq->free_count); + pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", + le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +#define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0) +#define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1) +#define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2) + +static int iwl_dbgfs_statistics_flag(struct iwl_priv *priv, char *buf, + int bufsz) +{ + int p = 0; + + p += scnprintf(buf + p, bufsz - p, + "Statistics Flag(0x%X):\n", + le32_to_cpu(priv->statistics.flag)); + if (le32_to_cpu(priv->statistics.flag) & UCODE_STATISTICS_CLEAR_MSK) + p += scnprintf(buf + p, bufsz - p, + "\tStatistics have been cleared\n"); + p += scnprintf(buf + p, bufsz - p, + "\tOperational Frequency: %s\n", + (le32_to_cpu(priv->statistics.flag) & + UCODE_STATISTICS_FREQUENCY_MSK) + ? "2.4 GHz" : "5.2 GHz"); + p += scnprintf(buf + p, bufsz - p, + "\tTGj Narrow Band: %s\n", + (le32_to_cpu(priv->statistics.flag) & + UCODE_STATISTICS_NARROW_BAND_MSK) + ? "enabled" : "disabled"); + return p; +} + + +static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char *buf; + int bufsz = sizeof(struct statistics_rx_phy) * 20 + + sizeof(struct statistics_rx_non_phy) * 20 + + sizeof(struct statistics_rx_ht_phy) * 20 + 400; + ssize_t ret; + struct statistics_rx_phy *ofdm; + struct statistics_rx_phy *cck; + struct statistics_rx_non_phy *general; + struct statistics_rx_ht_phy *ht; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + ret = iwl_send_statistics_request(priv, 0); + mutex_unlock(&priv->mutex); + + if (ret) { + IWL_ERR(priv, + "Error sending statistics request: %zd\n", ret); + return -EAGAIN; + } + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + ofdm = &priv->statistics.rx.ofdm; + cck = &priv->statistics.rx.cck; + general = &priv->statistics.rx.general; + ht = &priv->statistics.rx.ofdm_ht; + pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - OFDM:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt: %u\n", + le32_to_cpu(ofdm->ina_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt: %u\n", + le32_to_cpu(ofdm->fina_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "plcp_err: %u\n", + le32_to_cpu(ofdm->plcp_err)); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_err: %u\n", + le32_to_cpu(ofdm->crc32_err)); + pos += scnprintf(buf + pos, bufsz - pos, "overrun_err: %u\n", + le32_to_cpu(ofdm->overrun_err)); + pos += scnprintf(buf + pos, bufsz - pos, "early_overrun_err: %u\n", + le32_to_cpu(ofdm->early_overrun_err)); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_good: %u\n", + le32_to_cpu(ofdm->crc32_good)); + pos += scnprintf(buf + pos, bufsz - pos, "false_alarm_cnt: %u\n", + le32_to_cpu(ofdm->false_alarm_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "fina_sync_err_cnt: %u\n", + le32_to_cpu(ofdm->fina_sync_err_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "sfd_timeout: %u\n", + le32_to_cpu(ofdm->sfd_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, "fina_timeout: %u\n", + le32_to_cpu(ofdm->fina_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, "unresponded_rts: %u\n", + le32_to_cpu(ofdm->unresponded_rts)); + pos += scnprintf(buf + pos, bufsz - pos, + "rxe_frame_limit_overrun: %u\n", + le32_to_cpu(ofdm->rxe_frame_limit_overrun)); + pos += scnprintf(buf + pos, bufsz - pos, "sent_ack_cnt: %u\n", + le32_to_cpu(ofdm->sent_ack_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "sent_cts_cnt: %u\n", + le32_to_cpu(ofdm->sent_cts_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "sent_ba_rsp_cnt: %u\n", + le32_to_cpu(ofdm->sent_ba_rsp_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "dsp_self_kill: %u\n", + le32_to_cpu(ofdm->dsp_self_kill)); + pos += scnprintf(buf + pos, bufsz - pos, "mh_format_err: %u\n", + le32_to_cpu(ofdm->mh_format_err)); + pos += scnprintf(buf + pos, bufsz - pos, "re_acq_main_rssi_sum: %u\n", + le32_to_cpu(ofdm->re_acq_main_rssi_sum)); + + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - CCK:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "ina_cnt: %u\n", + le32_to_cpu(cck->ina_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "fina_cnt: %u\n", + le32_to_cpu(cck->fina_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "plcp_err: %u\n", + le32_to_cpu(cck->plcp_err)); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_err: %u\n", + le32_to_cpu(cck->crc32_err)); + pos += scnprintf(buf + pos, bufsz - pos, "overrun_err: %u\n", + le32_to_cpu(cck->overrun_err)); + pos += scnprintf(buf + pos, bufsz - pos, "early_overrun_err: %u\n", + le32_to_cpu(cck->early_overrun_err)); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_good: %u\n", + le32_to_cpu(cck->crc32_good)); + pos += scnprintf(buf + pos, bufsz - pos, "false_alarm_cnt: %u\n", + le32_to_cpu(cck->false_alarm_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "fina_sync_err_cnt: %u\n", + le32_to_cpu(cck->fina_sync_err_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "sfd_timeout: %u\n", + le32_to_cpu(cck->sfd_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, "fina_timeout: %u\n", + le32_to_cpu(cck->fina_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, "unresponded_rts: %u\n", + le32_to_cpu(cck->unresponded_rts)); + pos += scnprintf(buf + pos, bufsz - pos, + "rxe_frame_limit_overrun: %u\n", + le32_to_cpu(cck->rxe_frame_limit_overrun)); + pos += scnprintf(buf + pos, bufsz - pos, "sent_ack_cnt: %u\n", + le32_to_cpu(cck->sent_ack_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "sent_cts_cnt: %u\n", + le32_to_cpu(cck->sent_cts_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "sent_ba_rsp_cnt: %u\n", + le32_to_cpu(cck->sent_ba_rsp_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "dsp_self_kill: %u\n", + le32_to_cpu(cck->dsp_self_kill)); + pos += scnprintf(buf + pos, bufsz - pos, "mh_format_err: %u\n", + le32_to_cpu(cck->mh_format_err)); + pos += scnprintf(buf + pos, bufsz - pos, "re_acq_main_rssi_sum: %u\n", + le32_to_cpu(cck->re_acq_main_rssi_sum)); + + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - GENERAL:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "bogus_cts: %u\n", + le32_to_cpu(general->bogus_cts)); + pos += scnprintf(buf + pos, bufsz - pos, "bogus_ack: %u\n", + le32_to_cpu(general->bogus_ack)); + pos += scnprintf(buf + pos, bufsz - pos, "non_bssid_frames: %u\n", + le32_to_cpu(general->non_bssid_frames)); + pos += scnprintf(buf + pos, bufsz - pos, "filtered_frames: %u\n", + le32_to_cpu(general->filtered_frames)); + pos += scnprintf(buf + pos, bufsz - pos, "non_channel_beacons: %u\n", + le32_to_cpu(general->non_channel_beacons)); + pos += scnprintf(buf + pos, bufsz - pos, "channel_beacons: %u\n", + le32_to_cpu(general->channel_beacons)); + pos += scnprintf(buf + pos, bufsz - pos, "num_missed_bcon: %u\n", + le32_to_cpu(general->num_missed_bcon)); + pos += scnprintf(buf + pos, bufsz - pos, + "adc_rx_saturation_time: %u\n", + le32_to_cpu(general->adc_rx_saturation_time)); + pos += scnprintf(buf + pos, bufsz - pos, + "ina_detection_search_time: %u\n", + le32_to_cpu(general->ina_detection_search_time)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_silence_rssi_a: %u\n", + le32_to_cpu(general->beacon_silence_rssi_a)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_silence_rssi_b: %u\n", + le32_to_cpu(general->beacon_silence_rssi_b)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_silence_rssi_c: %u\n", + le32_to_cpu(general->beacon_silence_rssi_c)); + pos += scnprintf(buf + pos, bufsz - pos, + "interference_data_flag: %u\n", + le32_to_cpu(general->interference_data_flag)); + pos += scnprintf(buf + pos, bufsz - pos, "channel_load: %u\n", + le32_to_cpu(general->channel_load)); + pos += scnprintf(buf + pos, bufsz - pos, "dsp_false_alarms: %u\n", + le32_to_cpu(general->dsp_false_alarms)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_rssi_a: %u\n", + le32_to_cpu(general->beacon_rssi_a)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_rssi_b: %u\n", + le32_to_cpu(general->beacon_rssi_b)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_rssi_c: %u\n", + le32_to_cpu(general->beacon_rssi_c)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_energy_a: %u\n", + le32_to_cpu(general->beacon_energy_a)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_energy_b: %u\n", + le32_to_cpu(general->beacon_energy_b)); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_energy_c: %u\n", + le32_to_cpu(general->beacon_energy_c)); + + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Rx - OFDM_HT:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "plcp_err: %u\n", + le32_to_cpu(ht->plcp_err)); + pos += scnprintf(buf + pos, bufsz - pos, "overrun_err: %u\n", + le32_to_cpu(ht->overrun_err)); + pos += scnprintf(buf + pos, bufsz - pos, "early_overrun_err: %u\n", + le32_to_cpu(ht->early_overrun_err)); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_good: %u\n", + le32_to_cpu(ht->crc32_good)); + pos += scnprintf(buf + pos, bufsz - pos, "crc32_err: %u\n", + le32_to_cpu(ht->crc32_err)); + pos += scnprintf(buf + pos, bufsz - pos, "mh_format_err: %u\n", + le32_to_cpu(ht->mh_format_err)); + pos += scnprintf(buf + pos, bufsz - pos, "agg_crc32_good: %u\n", + le32_to_cpu(ht->agg_crc32_good)); + pos += scnprintf(buf + pos, bufsz - pos, "agg_mpdu_cnt: %u\n", + le32_to_cpu(ht->agg_mpdu_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "agg_cnt: %u\n", + le32_to_cpu(ht->agg_cnt)); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char *buf; + int bufsz = (sizeof(struct statistics_tx) * 24) + 250; + ssize_t ret; + struct statistics_tx *tx; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + ret = iwl_send_statistics_request(priv, 0); + mutex_unlock(&priv->mutex); + + if (ret) { + IWL_ERR(priv, + "Error sending statistics request: %zd\n", ret); + return -EAGAIN; + } + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + tx = &priv->statistics.tx; + pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_Tx:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "preamble: %u\n", + le32_to_cpu(tx->preamble_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "rx_detected_cnt: %u\n", + le32_to_cpu(tx->rx_detected_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "bt_prio_defer_cnt: %u\n", + le32_to_cpu(tx->bt_prio_defer_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "bt_prio_kill_cnt: %u\n", + le32_to_cpu(tx->bt_prio_kill_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "few_bytes_cnt: %u\n", + le32_to_cpu(tx->few_bytes_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "cts_timeout: %u\n", + le32_to_cpu(tx->cts_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, "ack_timeout: %u\n", + le32_to_cpu(tx->ack_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, "expected_ack_cnt: %u\n", + le32_to_cpu(tx->expected_ack_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "actual_ack_cnt: %u\n", + le32_to_cpu(tx->actual_ack_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "dump_msdu_cnt: %u\n", + le32_to_cpu(tx->dump_msdu_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, + "burst_abort_next_frame_mismatch_cnt: %u\n", + le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, + "burst_abort_missing_next_frame_cnt: %u\n", + le32_to_cpu(tx->burst_abort_missing_next_frame_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "cts_timeout_collision: %u\n", + le32_to_cpu(tx->cts_timeout_collision)); + pos += scnprintf(buf + pos, bufsz - pos, + "ack_or_ba_timeout_collision: %u\n", + le32_to_cpu(tx->ack_or_ba_timeout_collision)); + pos += scnprintf(buf + pos, bufsz - pos, "agg ba_timeout: %u\n", + le32_to_cpu(tx->agg.ba_timeout)); + pos += scnprintf(buf + pos, bufsz - pos, + "agg ba_reschedule_frames: %u\n", + le32_to_cpu(tx->agg.ba_reschedule_frames)); + pos += scnprintf(buf + pos, bufsz - pos, + "agg scd_query_agg_frame_cnt: %u\n", + le32_to_cpu(tx->agg.scd_query_agg_frame_cnt)); + pos += scnprintf(buf + pos, bufsz - pos, "agg scd_query_no_agg: %u\n", + le32_to_cpu(tx->agg.scd_query_no_agg)); + pos += scnprintf(buf + pos, bufsz - pos, "agg scd_query_agg: %u\n", + le32_to_cpu(tx->agg.scd_query_agg)); + pos += scnprintf(buf + pos, bufsz - pos, + "agg scd_query_mismatch: %u\n", + le32_to_cpu(tx->agg.scd_query_mismatch)); + pos += scnprintf(buf + pos, bufsz - pos, "agg frame_not_ready: %u\n", + le32_to_cpu(tx->agg.frame_not_ready)); + pos += scnprintf(buf + pos, bufsz - pos, "agg underrun: %u\n", + le32_to_cpu(tx->agg.underrun)); + pos += scnprintf(buf + pos, bufsz - pos, "agg bt_prio_kill: %u\n", + le32_to_cpu(tx->agg.bt_prio_kill)); + pos += scnprintf(buf + pos, bufsz - pos, "agg rx_ba_rsp_cnt: %u\n", + le32_to_cpu(tx->agg.rx_ba_rsp_cnt)); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + char *buf; + int bufsz = sizeof(struct statistics_general) * 4 + 250; + ssize_t ret; + struct statistics_general *general; + struct statistics_dbg *dbg; + struct statistics_div *div; + + if (!iwl_is_alive(priv)) + return -EAGAIN; + + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + ret = iwl_send_statistics_request(priv, 0); + mutex_unlock(&priv->mutex); + + if (ret) { + IWL_ERR(priv, + "Error sending statistics request: %zd\n", ret); + return -EAGAIN; + } + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + general = &priv->statistics.general; + dbg = &priv->statistics.general.dbg; + div = &priv->statistics.general.div; + pos += iwl_dbgfs_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, "Statistics_General:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "temperature: %u\n", + le32_to_cpu(general->temperature)); + pos += scnprintf(buf + pos, bufsz - pos, "temperature_m: %u\n", + le32_to_cpu(general->temperature_m)); + pos += scnprintf(buf + pos, bufsz - pos, "burst_check: %u\n", + le32_to_cpu(dbg->burst_check)); + pos += scnprintf(buf + pos, bufsz - pos, "burst_count: %u\n", + le32_to_cpu(dbg->burst_count)); + pos += scnprintf(buf + pos, bufsz - pos, "sleep_time: %u\n", + le32_to_cpu(general->sleep_time)); + pos += scnprintf(buf + pos, bufsz - pos, "slots_out: %u\n", + le32_to_cpu(general->slots_out)); + pos += scnprintf(buf + pos, bufsz - pos, "slots_idle: %u\n", + le32_to_cpu(general->slots_idle)); + pos += scnprintf(buf + pos, bufsz - pos, "ttl_timestamp: %u\n", + le32_to_cpu(general->ttl_timestamp)); + pos += scnprintf(buf + pos, bufsz - pos, "tx_on_a: %u\n", + le32_to_cpu(div->tx_on_a)); + pos += scnprintf(buf + pos, bufsz - pos, "tx_on_b: %u\n", + le32_to_cpu(div->tx_on_b)); + pos += scnprintf(buf + pos, bufsz - pos, "exec_time: %u\n", + le32_to_cpu(div->exec_time)); + pos += scnprintf(buf + pos, bufsz - pos, "probe_time: %u\n", + le32_to_cpu(div->probe_time)); + pos += scnprintf(buf + pos, bufsz - pos, "rx_enable_counter: %u\n", + le32_to_cpu(general->rx_enable_counter)); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_sensitivity_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = sizeof(struct iwl_sensitivity_data) * 4 + 100; + ssize_t ret; + struct iwl_sensitivity_data *data; + + data = &priv->sensitivity_data; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm:\t\t\t %u\n", + data->auto_corr_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, + "auto_corr_ofdm_mrc:\t\t %u\n", + data->auto_corr_ofdm_mrc); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_x1:\t\t %u\n", + data->auto_corr_ofdm_x1); + pos += scnprintf(buf + pos, bufsz - pos, + "auto_corr_ofdm_mrc_x1:\t\t %u\n", + data->auto_corr_ofdm_mrc_x1); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck:\t\t\t %u\n", + data->auto_corr_cck); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck_mrc:\t\t %u\n", + data->auto_corr_cck_mrc); + pos += scnprintf(buf + pos, bufsz - pos, + "last_bad_plcp_cnt_ofdm:\t\t %u\n", + data->last_bad_plcp_cnt_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_ofdm:\t\t %u\n", + data->last_fa_cnt_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, + "last_bad_plcp_cnt_cck:\t\t %u\n", + data->last_bad_plcp_cnt_cck); + pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_cck:\t\t %u\n", + data->last_fa_cnt_cck); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_curr_state:\t\t\t %u\n", + data->nrg_curr_state); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_prev_state:\t\t\t %u\n", + data->nrg_prev_state); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_value:\t\t\t"); + for (cnt = 0; cnt < 10; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->nrg_value[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_rssi:\t\t"); + for (cnt = 0; cnt < NRG_NUM_PREV_STAT_L; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->nrg_silence_rssi[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_ref:\t\t %u\n", + data->nrg_silence_ref); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_energy_idx:\t\t\t %u\n", + data->nrg_energy_idx); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_idx:\t\t %u\n", + data->nrg_silence_idx); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_cck:\t\t\t %u\n", + data->nrg_th_cck); + pos += scnprintf(buf + pos, bufsz - pos, + "nrg_auto_corr_silence_diff:\t %u\n", + data->nrg_auto_corr_silence_diff); + pos += scnprintf(buf + pos, bufsz - pos, "num_in_cck_no_fa:\t\t %u\n", + data->num_in_cck_no_fa); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_ofdm:\t\t\t %u\n", + data->nrg_th_ofdm); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + + +static ssize_t iwl_dbgfs_chain_noise_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = sizeof(struct iwl_chain_noise_data) * 4 + 100; + ssize_t ret; + struct iwl_chain_noise_data *data; + + data = &priv->chain_noise_data; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + pos += scnprintf(buf + pos, bufsz - pos, "active_chains:\t\t\t %u\n", + data->active_chains); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_a:\t\t\t %u\n", + data->chain_noise_a); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_b:\t\t\t %u\n", + data->chain_noise_b); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_c:\t\t\t %u\n", + data->chain_noise_c); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_a:\t\t\t %u\n", + data->chain_signal_a); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_b:\t\t\t %u\n", + data->chain_signal_b); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_c:\t\t\t %u\n", + data->chain_signal_c); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_count:\t\t\t %u\n", + data->beacon_count); + + pos += scnprintf(buf + pos, bufsz - pos, "disconn_array:\t\t\t"); + for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->disconn_array[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "delta_gain_code:\t\t"); + for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->delta_gain_code[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "radio_write:\t\t\t %u\n", + data->radio_write); + pos += scnprintf(buf + pos, bufsz - pos, "state:\t\t\t\t %u\n", + data->state); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_tx_power_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + char buf[128]; + int pos = 0; + ssize_t ret; + const size_t bufsz = sizeof(buf); + struct statistics_tx *tx; + + if (!iwl_is_alive(priv)) + pos += scnprintf(buf + pos, bufsz - pos, "N/A\n"); + else { + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + ret = iwl_send_statistics_request(priv, 0); + mutex_unlock(&priv->mutex); + + if (ret) { + IWL_ERR(priv, "Error sending statistics request: %zd\n", + ret); + return -EAGAIN; + } + tx = &priv->statistics.tx; + if (tx->tx_power.ant_a || + tx->tx_power.ant_b || + tx->tx_power.ant_c) { + pos += scnprintf(buf + pos, bufsz - pos, + "tx power: (1/2 dB step)\n"); + if ((priv->cfg->valid_tx_ant & ANT_A) && + tx->tx_power.ant_a) + pos += scnprintf(buf + pos, bufsz - pos, + "\tantenna A: 0x%X\n", + tx->tx_power.ant_a); + if ((priv->cfg->valid_tx_ant & ANT_B) && + tx->tx_power.ant_b) + pos += scnprintf(buf + pos, bufsz - pos, + "\tantenna B: 0x%X\n", + tx->tx_power.ant_b); + if ((priv->cfg->valid_tx_ant & ANT_C) && + tx->tx_power.ant_c) + pos += scnprintf(buf + pos, bufsz - pos, + "\tantenna C: 0x%X\n", + tx->tx_power.ant_c); + } else + pos += scnprintf(buf + pos, bufsz - pos, "N/A\n"); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +DEBUGFS_READ_WRITE_FILE_OPS(rx_statistics); +DEBUGFS_READ_WRITE_FILE_OPS(tx_statistics); +DEBUGFS_READ_WRITE_FILE_OPS(traffic_log); +DEBUGFS_READ_FILE_OPS(rx_queue); +DEBUGFS_READ_FILE_OPS(tx_queue); +DEBUGFS_READ_FILE_OPS(ucode_rx_stats); +DEBUGFS_READ_FILE_OPS(ucode_tx_stats); +DEBUGFS_READ_FILE_OPS(ucode_general_stats); +DEBUGFS_READ_FILE_OPS(sensitivity); +DEBUGFS_READ_FILE_OPS(chain_noise); +DEBUGFS_READ_FILE_OPS(tx_power); /* * Create the debugfs files and directories @@ -603,19 +1652,42 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) DEBUGFS_ADD_DIR(data, dbgfs->dir_drv); DEBUGFS_ADD_DIR(rf, dbgfs->dir_drv); + DEBUGFS_ADD_DIR(debug, dbgfs->dir_drv); DEBUGFS_ADD_FILE(nvm, data); DEBUGFS_ADD_FILE(sram, data); DEBUGFS_ADD_FILE(log_event, data); DEBUGFS_ADD_FILE(stations, data); - DEBUGFS_ADD_FILE(rx_statistics, data); - DEBUGFS_ADD_FILE(tx_statistics, data); DEBUGFS_ADD_FILE(channels, data); DEBUGFS_ADD_FILE(status, data); DEBUGFS_ADD_FILE(interrupt, data); + DEBUGFS_ADD_FILE(qos, data); +#ifdef CONFIG_IWLWIFI_LEDS + DEBUGFS_ADD_FILE(led, data); +#endif + DEBUGFS_ADD_FILE(sleep_level_override, data); + DEBUGFS_ADD_FILE(current_sleep_command, data); + DEBUGFS_ADD_FILE(thermal_throttling, data); + DEBUGFS_ADD_FILE(disable_ht40, data); + DEBUGFS_ADD_FILE(rx_statistics, debug); + DEBUGFS_ADD_FILE(tx_statistics, debug); + DEBUGFS_ADD_FILE(traffic_log, debug); + DEBUGFS_ADD_FILE(rx_queue, debug); + DEBUGFS_ADD_FILE(tx_queue, debug); + DEBUGFS_ADD_FILE(tx_power, debug); + if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) { + DEBUGFS_ADD_FILE(ucode_rx_stats, debug); + DEBUGFS_ADD_FILE(ucode_tx_stats, debug); + DEBUGFS_ADD_FILE(ucode_general_stats, debug); + DEBUGFS_ADD_FILE(sensitivity, debug); + DEBUGFS_ADD_FILE(chain_noise, debug); + } DEBUGFS_ADD_BOOL(disable_sensitivity, rf, &priv->disable_sens_cal); DEBUGFS_ADD_BOOL(disable_chain_noise, rf, &priv->disable_chain_noise_cal); - DEBUGFS_ADD_BOOL(disable_tx_power, rf, &priv->disable_tx_power_cal); + if (((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965) || + ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_3945)) + DEBUGFS_ADD_BOOL(disable_tx_power, rf, + &priv->disable_tx_power_cal); return 0; err: @@ -634,19 +1706,46 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv) if (!priv->dbgfs) return; + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_sleep_level_override); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_current_sleep_command); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_nvm); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_rx_statistics); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_tx_statistics); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_sram); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_log_event); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_stations); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_channels); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_status); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_interrupt); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_qos); +#ifdef CONFIG_IWLWIFI_LEDS + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_led); +#endif + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_thermal_throttling); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_data_files.file_disable_ht40); DEBUGFS_REMOVE(priv->dbgfs->dir_data); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_rx_statistics); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_statistics); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_traffic_log); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_rx_queue); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_queue); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_power); + if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) { + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. + file_ucode_rx_stats); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. + file_ucode_tx_stats); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. + file_ucode_general_stats); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. + file_sensitivity); + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files. + file_chain_noise); + } + DEBUGFS_REMOVE(priv->dbgfs->dir_debug); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_sensitivity); DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_chain_noise); - DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_tx_power); + if (((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965) || + ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_3945)) + DEBUGFS_REMOVE(priv->dbgfs->dbgfs_rf_files.file_disable_tx_power); DEBUGFS_REMOVE(priv->dbgfs->dir_rf); DEBUGFS_REMOVE(priv->dbgfs->dir_drv); kfree(priv->dbgfs); diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 650e20af20fa..028d50599550 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -56,16 +56,19 @@ extern struct iwl_cfg iwl5350_agn_cfg; extern struct iwl_cfg iwl5100_bg_cfg; extern struct iwl_cfg iwl5100_abg_cfg; extern struct iwl_cfg iwl5150_agn_cfg; -extern struct iwl_cfg iwl6000_2ag_cfg; -extern struct iwl_cfg iwl6000_2agn_cfg; +extern struct iwl_cfg iwl6000h_2agn_cfg; +extern struct iwl_cfg iwl6000i_2agn_cfg; extern struct iwl_cfg iwl6000_3agn_cfg; extern struct iwl_cfg iwl6050_2agn_cfg; extern struct iwl_cfg iwl6050_3agn_cfg; extern struct iwl_cfg iwl1000_bgn_cfg; +struct iwl_tx_queue; + /* shared structures from iwl-5000.c */ extern struct iwl_mod_params iwl50_mod_params; extern struct iwl_ops iwl5000_ops; +extern struct iwl_ucode_ops iwl5000_ucode; extern struct iwl_lib_ops iwl5000_lib; extern struct iwl_hcmd_ops iwl5000_hcmd; extern struct iwl_hcmd_utils_ops iwl5000_hcmd_utils; @@ -78,9 +81,37 @@ extern void iwl5000_rts_tx_cmd_flag(struct ieee80211_tx_info *info, __le32 *tx_flags); extern int iwl5000_calc_rssi(struct iwl_priv *priv, struct iwl_rx_phy_res *rx_resp); +extern int iwl5000_apm_init(struct iwl_priv *priv); +extern void iwl5000_apm_stop(struct iwl_priv *priv); +extern int iwl5000_apm_reset(struct iwl_priv *priv); +extern void iwl5000_nic_config(struct iwl_priv *priv); +extern u16 iwl5000_eeprom_calib_version(struct iwl_priv *priv); +extern const u8 *iwl5000_eeprom_query_addr(const struct iwl_priv *priv, + size_t offset); +extern void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv, + struct iwl_tx_queue *txq, + u16 byte_cnt); +extern void iwl5000_txq_inval_byte_cnt_tbl(struct iwl_priv *priv, + struct iwl_tx_queue *txq); +extern int iwl5000_load_ucode(struct iwl_priv *priv); +extern void iwl5000_init_alive_start(struct iwl_priv *priv); +extern int iwl5000_alive_notify(struct iwl_priv *priv); +extern int iwl5000_hw_set_hw_params(struct iwl_priv *priv); +extern int iwl5000_txq_agg_enable(struct iwl_priv *priv, int txq_id, + int tx_fifo, int sta_id, int tid, u16 ssn_idx); +extern int iwl5000_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, + u16 ssn_idx, u8 tx_fifo); +extern void iwl5000_txq_set_sched(struct iwl_priv *priv, u32 mask); +extern void iwl5000_setup_deferred_work(struct iwl_priv *priv); +extern void iwl5000_rx_handler_setup(struct iwl_priv *priv); +extern int iwl5000_hw_valid_rtc_data_addr(u32 addr); +extern int iwl5000_send_tx_power(struct iwl_priv *priv); +extern void iwl5000_temperature(struct iwl_priv *priv); /* CT-KILL constants */ -#define CT_KILL_THRESHOLD 110 /* in Celsius */ +#define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */ +#define CT_KILL_THRESHOLD 114 /* in Celsius */ +#define CT_KILL_EXIT_THRESHOLD 95 /* in Celsius */ /* Default noise level to report when noise measurement is not available. * This may be because we're: @@ -119,6 +150,31 @@ struct iwl_rx_mem_buffer { struct list_head list; }; +/* defined below */ +struct iwl_device_cmd; + +struct iwl_cmd_meta { + /* only for SYNC commands, iff the reply skb is wanted */ + struct iwl_host_cmd *source; + /* + * only for ASYNC commands + * (which is somewhat stupid -- look at iwl-sta.c for instance + * which duplicates a bunch of code because the callback isn't + * invoked for SYNC commands, if it were and its result passed + * through it would be simpler...) + */ + void (*callback)(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb); + + /* The CMD_SIZE_HUGE flag bit indicates that the command + * structure is stored at the end of the shared queue memory. */ + u32 flags; + + DECLARE_PCI_UNMAP_ADDR(mapping) + DECLARE_PCI_UNMAP_LEN(len) +}; + /* * Generic queue structure * @@ -146,7 +202,8 @@ struct iwl_tx_info { * struct iwl_tx_queue - Tx Queue for DMA * @q: generic Rx/Tx queue descriptor * @bd: base of circular buffer of TFDs - * @cmd: array of command/Tx buffers + * @cmd: array of command/TX buffer pointers + * @meta: array of meta data for each command/tx buffer * @dma_addr_cmd: physical address of cmd/tx buffer array * @txb: array of per-TFD driver data * @need_update: indicates need to update read/write index @@ -161,7 +218,8 @@ struct iwl_tx_info { struct iwl_tx_queue { struct iwl_queue q; void *tfds; - struct iwl_cmd *cmd[TFD_TX_CMD_SLOTS]; + struct iwl_device_cmd **cmd; + struct iwl_cmd_meta *meta; struct iwl_tx_info *txb; u8 need_update; u8 sched_retry; @@ -219,8 +277,8 @@ struct iwl_channel_info { struct iwl4965_channel_tgd_info tgd; struct iwl4965_channel_tgh_info tgh; struct iwl_eeprom_channel eeprom; /* EEPROM regulatory limit */ - struct iwl_eeprom_channel fat_eeprom; /* EEPROM regulatory limit for - * FAT channel */ + struct iwl_eeprom_channel ht40_eeprom; /* EEPROM regulatory limit for + * HT40 channel */ u8 channel; /* channel number */ u8 flags; /* flags copied from EEPROM */ @@ -233,13 +291,13 @@ struct iwl_channel_info { u8 band_index; /* 0-4, maps channel to band1/2/3/4/5 */ enum ieee80211_band band; - /* FAT channel info */ - s8 fat_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ - s8 fat_curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) */ - s8 fat_min_power; /* always 0 */ - s8 fat_scan_power; /* (dBm) eeprom, direct scans, any rate */ - u8 fat_flags; /* flags copied from EEPROM */ - u8 fat_extension_channel; /* HT_IE_EXT_CHANNEL_* */ + /* HT40 channel info */ + s8 ht40_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ + s8 ht40_curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) */ + s8 ht40_min_power; /* always 0 */ + s8 ht40_scan_power; /* (dBm) eeprom, direct scans, any rate */ + u8 ht40_flags; /* flags copied from EEPROM */ + u8 ht40_extension_channel; /* HT_IE_EXT_CHANNEL_* */ /* Radio/DSP gain settings for each "normal" data Tx rate. * These include, in addition to RF and DSP gain, a few fields for @@ -298,35 +356,16 @@ enum { CMD_WANT_SKB = (1 << 2), }; -struct iwl_cmd; -struct iwl_priv; - -struct iwl_cmd_meta { - struct iwl_cmd_meta *source; - union { - struct sk_buff *skb; - int (*callback)(struct iwl_priv *priv, - struct iwl_cmd *cmd, struct sk_buff *skb); - } __attribute__ ((packed)) u; - - /* The CMD_SIZE_HUGE flag bit indicates that the command - * structure is stored at the end of the shared queue memory. */ - u32 flags; - DECLARE_PCI_UNMAP_ADDR(mapping) - DECLARE_PCI_UNMAP_LEN(len) -} __attribute__ ((packed)); - #define IWL_CMD_MAX_PAYLOAD 320 /** - * struct iwl_cmd + * struct iwl_device_cmd * * For allocation of the command and tx queues, this establishes the overall * size of the largest command we send to uCode, except for a scan command * (which is relatively huge; space is allocated separately). */ -struct iwl_cmd { - struct iwl_cmd_meta meta; /* driver data */ +struct iwl_device_cmd { struct iwl_cmd_header hdr; /* uCode API */ union { u32 flags; @@ -338,17 +377,20 @@ struct iwl_cmd { } __attribute__ ((packed)) cmd; } __attribute__ ((packed)); +#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd)) + struct iwl_host_cmd { - u8 id; - u16 len; - struct iwl_cmd_meta meta; const void *data; + struct sk_buff *reply_skb; + void (*callback)(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb); + u32 flags; + u16 len; + u8 id; }; -#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_cmd) - \ - sizeof(struct iwl_cmd_meta)) - /* * RX related structures and functions */ @@ -449,23 +491,25 @@ union iwl_ht_rate_supp { }; #define CFG_HT_RX_AMPDU_FACTOR_DEF (0x3) -#define CFG_HT_MPDU_DENSITY_2USEC (0x5) -#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_2USEC + +/* + * Maximal MPDU density for TX aggregation + * 4 - 2us density + * 5 - 4us density + * 6 - 8us density + * 7 - 16us density + */ +#define CFG_HT_MPDU_DENSITY_4USEC (0x5) +#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_4USEC struct iwl_ht_info { /* self configuration data */ u8 is_ht; u8 supported_chan_width; u8 sm_ps; - u8 is_green_field; - u8 sgf; /* HT_SHORT_GI_* short guard interval */ - u8 max_amsdu_size; - u8 ampdu_factor; - u8 mpdu_density; struct ieee80211_mcs_info mcs; /* BSS related data */ u8 extension_chan_offset; - u8 tx_chan_width; u8 ht_protection; u8 non_GF_STA_present; }; @@ -525,15 +569,29 @@ struct fw_desc { }; /* uCode file layout */ -struct iwl_ucode { - __le32 ver; /* major/minor/API/serial */ - __le32 inst_size; /* bytes of runtime instructions */ - __le32 data_size; /* bytes of runtime data */ - __le32 init_size; /* bytes of initialization instructions */ - __le32 init_data_size; /* bytes of initialization data */ - __le32 boot_size; /* bytes of bootstrap instructions */ - u8 data[0]; /* data in same order as "size" elements */ +struct iwl_ucode_header { + __le32 ver; /* major/minor/API/serial */ + union { + struct { + __le32 inst_size; /* bytes of runtime code */ + __le32 data_size; /* bytes of runtime data */ + __le32 init_size; /* bytes of init code */ + __le32 init_data_size; /* bytes of init data */ + __le32 boot_size; /* bytes of bootstrap code */ + u8 data[0]; /* in same order as sizes */ + } v1; + struct { + __le32 build; /* build number */ + __le32 inst_size; /* bytes of runtime code */ + __le32 data_size; /* bytes of runtime data */ + __le32 init_size; /* bytes of init code */ + __le32 init_data_size; /* bytes of init data */ + __le32 boot_size; /* bytes of bootstrap code */ + u8 data[0]; /* in same order as sizes */ + } v2; + } u; }; +#define UCODE_HEADER_SIZE(ver) ((ver) == 1 ? 24 : 28) struct iwl4965_ibss_seq { u8 mac[ETH_ALEN]; @@ -585,7 +643,7 @@ struct iwl_sensitivity_ranges { * @rx_wrt_ptr_reg: FH{39}_RSCSR_CHNL0_WPTR * @max_stations: * @bcast_sta_id: - * @fat_channel: is 40MHz width possible in band 2.4 + * @ht40_channel: is 40MHz width possible in band 2.4 * BIT(IEEE80211_BAND_5GHZ) BIT(IEEE80211_BAND_5GHZ) * @sw_crypto: 0 for hw, 1 for sw * @max_xxx_size: for ucode uses @@ -609,12 +667,14 @@ struct iwl_hw_params { u32 max_pkt_size; u8 max_stations; u8 bcast_sta_id; - u8 fat_channel; - u8 sw_crypto; + u8 ht40_channel; + u8 max_beacon_itrvl; /* in 1024 ms */ u32 max_inst_size; u32 max_data_size; u32 max_bsm_size; u32 ct_kill_threshold; /* value in hw-dependent units */ + u32 ct_kill_exit_threshold; /* value in hw-dependent units */ + /* for 1000, 6000 series and up */ u32 calib_init_cfg; const struct iwl_sensitivity_ranges *sens; }; @@ -666,9 +726,6 @@ struct iwl_dma_ptr { size_t size; }; -#define HT_SHORT_GI_20MHZ (1 << 0) -#define HT_SHORT_GI_40MHZ (1 << 1) - #define IWL_CHANNEL_WIDTH_20MHZ 0 #define IWL_CHANNEL_WIDTH_40MHZ 1 @@ -809,6 +866,8 @@ struct iwl_chain_noise_data { #define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ #define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ +#define IWL_TRAFFIC_ENTRIES (256) +#define IWL_TRAFFIC_ENTRY_SIZE (64) enum { MEASUREMENT_READY = (1 << 0), @@ -820,6 +879,30 @@ enum iwl_nvm_type { NVM_DEVICE_TYPE_OTP, }; +/* + * Two types of OTP memory access modes + * IWL_OTP_ACCESS_ABSOLUTE - absolute address mode, + * based on physical memory addressing + * IWL_OTP_ACCESS_RELATIVE - relative address mode, + * based on logical memory addressing + */ +enum iwl_access_mode { + IWL_OTP_ACCESS_ABSOLUTE, + IWL_OTP_ACCESS_RELATIVE, +}; + +/** + * enum iwl_pa_type - Power Amplifier type + * @IWL_PA_SYSTEM: based on uCode configuration + * @IWL_PA_HYBRID: use both Internal and external PA + * @IWL_PA_INTERNAL: use Internal only + */ +enum iwl_pa_type { + IWL_PA_SYSTEM = 0, + IWL_PA_HYBRID = 1, + IWL_PA_INTERNAL = 2, +}; + /* interrupt statistics */ struct isr_statistics { u32 hw; @@ -836,6 +919,48 @@ struct isr_statistics { u32 unhandled; }; +#ifdef CONFIG_IWLWIFI_DEBUGFS +/* management statistics */ +enum iwl_mgmt_stats { + MANAGEMENT_ASSOC_REQ = 0, + MANAGEMENT_ASSOC_RESP, + MANAGEMENT_REASSOC_REQ, + MANAGEMENT_REASSOC_RESP, + MANAGEMENT_PROBE_REQ, + MANAGEMENT_PROBE_RESP, + MANAGEMENT_BEACON, + MANAGEMENT_ATIM, + MANAGEMENT_DISASSOC, + MANAGEMENT_AUTH, + MANAGEMENT_DEAUTH, + MANAGEMENT_ACTION, + MANAGEMENT_MAX, +}; +/* control statistics */ +enum iwl_ctrl_stats { + CONTROL_BACK_REQ = 0, + CONTROL_BACK, + CONTROL_PSPOLL, + CONTROL_RTS, + CONTROL_CTS, + CONTROL_ACK, + CONTROL_CFEND, + CONTROL_CFENDACK, + CONTROL_MAX, +}; + +struct traffic_stats { + u32 mgmt[MANAGEMENT_MAX]; + u32 ctrl[CONTROL_MAX]; + u32 data_cnt; + u64 data_bytes; +}; +#else +struct traffic_stats { + u64 data_bytes; +}; +#endif + #define IWL_MAX_NUM_QUEUES 20 /* FIXME: do dynamic allocation */ struct iwl_priv { @@ -981,15 +1106,14 @@ struct iwl_priv { int last_rx_noise; /* From beacon statistics */ /* counts mgmt, ctl, and data packets */ - struct traffic_stats { - u32 cnt; - u64 bytes; - } tx_stats[3], rx_stats[3]; + struct traffic_stats tx_stats; + struct traffic_stats rx_stats; /* counts interrupts */ struct isr_statistics isr_stats; struct iwl_power_mgr power_data; + struct iwl_tt_mgmt thermal_throttle; struct iwl_notif_statistics statistics; unsigned long last_statistics_time; @@ -997,7 +1121,6 @@ struct iwl_priv { /* context information */ u16 rates_mask; - u32 power_mode; u8 bssid[ETH_ALEN]; u16 rts_threshold; u8 mac_addr[ETH_ALEN]; @@ -1047,7 +1170,7 @@ struct iwl_priv { struct iwl_hw_params hw_params; /* INT ICT Table */ - u32 *ict_tbl; + __le32 *ict_tbl; dma_addr_t ict_tbl_dma; dma_addr_t aligned_ict_tbl_dma; int ict_index; @@ -1076,6 +1199,9 @@ struct iwl_priv { struct work_struct report_work; struct work_struct request_scan; struct work_struct beacon_update; + struct work_struct tt_work; + struct work_struct ct_enter; + struct work_struct ct_exit; struct tasklet_struct irq_tasklet; @@ -1089,16 +1215,22 @@ struct iwl_priv { /* TX Power */ s8 tx_power_user_lmt; - s8 tx_power_channel_lmt; + s8 tx_power_device_lmt; #ifdef CONFIG_IWLWIFI_DEBUG /* debugging info */ - u32 debug_level; + u32 debug_level; /* per device debugging will override global + iwl_debug_level if set */ u32 framecnt_to_us; atomic_t restrict_refcnt; + bool disable_ht40; #ifdef CONFIG_IWLWIFI_DEBUGFS /* debugfs */ + u16 tx_traffic_idx; + u16 rx_traffic_idx; + u8 *tx_traffic; + u8 *rx_traffic; struct iwl_debugfs *dbgfs; #endif /* CONFIG_IWLWIFI_DEBUGFS */ #endif /* CONFIG_IWLWIFI_DEBUG */ @@ -1130,8 +1262,27 @@ static inline void iwl_txq_ctx_deactivate(struct iwl_priv *priv, int txq_id) #ifdef CONFIG_IWLWIFI_DEBUG const char *iwl_get_tx_fail_reason(u32 status); +/* + * iwl_get_debug_level: Return active debug level for device + * + * Using sysfs it is possible to set per device debug level. This debug + * level will be used if set, otherwise the global debug level which can be + * set via module parameter is used. + */ +static inline u32 iwl_get_debug_level(struct iwl_priv *priv) +{ + if (priv->debug_level) + return priv->debug_level; + else + return iwl_debug_level; +} #else static inline const char *iwl_get_tx_fail_reason(u32 status) { return ""; } + +static inline u32 iwl_get_debug_level(struct iwl_priv *priv) +{ + return iwl_debug_level; +} #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index 7d7554a2f341..3d2b93a61e62 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -127,14 +127,86 @@ static const u8 iwl_eeprom_band_5[] = { /* 5725-5825MHz */ 145, 149, 153, 157, 161, 165 }; -static const u8 iwl_eeprom_band_6[] = { /* 2.4 FAT channel */ +static const u8 iwl_eeprom_band_6[] = { /* 2.4 ht40 channel */ 1, 2, 3, 4, 5, 6, 7 }; -static const u8 iwl_eeprom_band_7[] = { /* 5.2 FAT channel */ +static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */ 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 }; +/** + * struct iwl_txpwr_section: eeprom section information + * @offset: indirect address into eeprom image + * @count: number of "struct iwl_eeprom_enhanced_txpwr" in this section + * @band: band type for the section + * @is_common - true: common section, false: channel section + * @is_cck - true: cck section, false: not cck section + * @is_ht_40 - true: all channel in the section are HT40 channel, + * false: legacy or HT 20 MHz + * ignore if it is common section + * @iwl_eeprom_section_channel: channel array in the section, + * ignore if common section + */ +struct iwl_txpwr_section { + u32 offset; + u8 count; + enum ieee80211_band band; + bool is_common; + bool is_cck; + bool is_ht40; + u8 iwl_eeprom_section_channel[EEPROM_MAX_TXPOWER_SECTION_ELEMENTS]; +}; + +/** + * section 1 - 3 are regulatory tx power apply to all channels based on + * modulation: CCK, OFDM + * Band: 2.4GHz, 5.2GHz + * section 4 - 10 are regulatory tx power apply to specified channels + * For example: + * 1L - Channel 1 Legacy + * 1HT - Channel 1 HT + * (1,+1) - Channel 1 HT40 "_above_" + * + * Section 1: all CCK channels + * Section 2: all 2.4 GHz OFDM (Legacy, HT and HT40) channels + * Section 3: all 5.2 GHz OFDM (Legacy, HT and HT40) channels + * Section 4: 2.4 GHz 20MHz channels: 1L, 1HT, 2L, 2HT, 10L, 10HT, 11L, 11HT + * Section 5: 2.4 GHz 40MHz channels: (1,+1) (2,+1) (6,+1) (7,+1) (9,+1) + * Section 6: 5.2 GHz 20MHz channels: 36L, 64L, 100L, 36HT, 64HT, 100HT + * Section 7: 5.2 GHz 40MHz channels: (36,+1) (60,+1) (100,+1) + * Section 8: 2.4 GHz channel: 13L, 13HT + * Section 9: 2.4 GHz channel: 140L, 140HT + * Section 10: 2.4 GHz 40MHz channels: (132,+1) (44,+1) + * + */ +static const struct iwl_txpwr_section enhinfo[] = { + { EEPROM_LB_CCK_20_COMMON, 1, IEEE80211_BAND_2GHZ, true, true, false }, + { EEPROM_LB_OFDM_COMMON, 3, IEEE80211_BAND_2GHZ, true, false, false }, + { EEPROM_HB_OFDM_COMMON, 3, IEEE80211_BAND_5GHZ, true, false, false }, + { EEPROM_LB_OFDM_20_BAND, 8, IEEE80211_BAND_2GHZ, + false, false, false, + {1, 1, 2, 2, 10, 10, 11, 11 } }, + { EEPROM_LB_OFDM_HT40_BAND, 5, IEEE80211_BAND_2GHZ, + false, false, true, + { 1, 2, 6, 7, 9 } }, + { EEPROM_HB_OFDM_20_BAND, 6, IEEE80211_BAND_5GHZ, + false, false, false, + { 36, 64, 100, 36, 64, 100 } }, + { EEPROM_HB_OFDM_HT40_BAND, 3, IEEE80211_BAND_5GHZ, + false, false, true, + { 36, 60, 100 } }, + { EEPROM_LB_OFDM_20_CHANNEL_13, 2, IEEE80211_BAND_2GHZ, + false, false, false, + { 13, 13 } }, + { EEPROM_HB_OFDM_20_CHANNEL_140, 2, IEEE80211_BAND_5GHZ, + false, false, false, + { 140, 140 } }, + { EEPROM_HB_OFDM_HT40_BAND_1, 2, IEEE80211_BAND_5GHZ, + false, false, true, + { 132, 44 } }, +}; + /****************************************************************************** * * EEPROM related functions @@ -152,6 +224,19 @@ int iwlcore_eeprom_verify_signature(struct iwl_priv *priv) } EXPORT_SYMBOL(iwlcore_eeprom_verify_signature); +static void iwl_set_otp_access(struct iwl_priv *priv, enum iwl_access_mode mode) +{ + u32 otpgp; + + otpgp = iwl_read32(priv, CSR_OTP_GP_REG); + if (mode == IWL_OTP_ACCESS_ABSOLUTE) + iwl_clear_bit(priv, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_OTP_ACCESS_MODE); + else + iwl_set_bit(priv, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_OTP_ACCESS_MODE); +} + static int iwlcore_get_nvm_type(struct iwl_priv *priv) { u32 otpgp; @@ -159,6 +244,9 @@ static int iwlcore_get_nvm_type(struct iwl_priv *priv) /* OTP only valid for CP/PP and after */ switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { + case CSR_HW_REV_TYPE_NONE: + IWL_ERR(priv, "Unknown hardware type\n"); + return -ENOENT; case CSR_HW_REV_TYPE_3945: case CSR_HW_REV_TYPE_4965: case CSR_HW_REV_TYPE_5300: @@ -249,6 +337,124 @@ static int iwl_init_otp_access(struct iwl_priv *priv) return ret; } +static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, u16 *eeprom_data) +{ + int ret = 0; + u32 r; + u32 otpgp; + + _iwl_write32(priv, CSR_EEPROM_REG, + CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); + ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG, + CSR_EEPROM_REG_READ_VALID_MSK, + IWL_EEPROM_ACCESS_TIMEOUT); + if (ret < 0) { + IWL_ERR(priv, "Time out reading OTP[%d]\n", addr); + return ret; + } + r = _iwl_read_direct32(priv, CSR_EEPROM_REG); + /* check for ECC errors: */ + otpgp = iwl_read32(priv, CSR_OTP_GP_REG); + if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) { + /* stop in this case */ + /* set the uncorrectable OTP ECC bit for acknowledgement */ + iwl_set_bit(priv, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); + IWL_ERR(priv, "Uncorrectable OTP ECC error, abort OTP read\n"); + return -EINVAL; + } + if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) { + /* continue in this case */ + /* set the correctable OTP ECC bit for acknowledgement */ + iwl_set_bit(priv, CSR_OTP_GP_REG, + CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK); + IWL_ERR(priv, "Correctable OTP ECC error, continue read\n"); + } + *eeprom_data = le16_to_cpu((__force __le16)(r >> 16)); + return 0; +} + +/* + * iwl_is_otp_empty: check for empty OTP + */ +static bool iwl_is_otp_empty(struct iwl_priv *priv) +{ + u16 next_link_addr = 0, link_value; + bool is_empty = false; + + /* locate the beginning of OTP link list */ + if (!iwl_read_otp_word(priv, next_link_addr, &link_value)) { + if (!link_value) { + IWL_ERR(priv, "OTP is empty\n"); + is_empty = true; + } + } else { + IWL_ERR(priv, "Unable to read first block of OTP list.\n"); + is_empty = true; + } + + return is_empty; +} + + +/* + * iwl_find_otp_image: find EEPROM image in OTP + * finding the OTP block that contains the EEPROM image. + * the last valid block on the link list (the block _before_ the last block) + * is the block we should read and used to configure the device. + * If all the available OTP blocks are full, the last block will be the block + * we should read and used to configure the device. + * only perform this operation if shadow RAM is disabled + */ +static int iwl_find_otp_image(struct iwl_priv *priv, + u16 *validblockaddr) +{ + u16 next_link_addr = 0, link_value = 0, valid_addr; + int ret = 0; + int usedblocks = 0; + + /* set addressing mode to absolute to traverse the link list */ + iwl_set_otp_access(priv, IWL_OTP_ACCESS_ABSOLUTE); + + /* checking for empty OTP or error */ + if (iwl_is_otp_empty(priv)) + return -EINVAL; + + /* + * start traverse link list + * until reach the max number of OTP blocks + * different devices have different number of OTP blocks + */ + do { + /* save current valid block address + * check for more block on the link list + */ + valid_addr = next_link_addr; + next_link_addr = link_value; + IWL_DEBUG_INFO(priv, "OTP blocks %d addr 0x%x\n", + usedblocks, next_link_addr); + if (iwl_read_otp_word(priv, next_link_addr, &link_value)) + return -EINVAL; + if (!link_value) { + /* + * reach the end of link list, + * set address point to the starting address + * of the image + */ + goto done; + } + /* more in the link list, continue */ + usedblocks++; + } while (usedblocks < priv->cfg->max_ll_items); + /* OTP full, use last block */ + IWL_DEBUG_INFO(priv, "OTP is full, use last block\n"); +done: + *validblockaddr = valid_addr; + /* skip first 2 bytes (link list pointer) */ + *validblockaddr += 2; + return ret; +} + /** * iwl_eeprom_init - read EEPROM contents * @@ -263,14 +469,14 @@ int iwl_eeprom_init(struct iwl_priv *priv) int sz; int ret; u16 addr; - u32 otpgp; + u16 validblockaddr = 0; + u16 cache_addr = 0; priv->nvm_device_type = iwlcore_get_nvm_type(priv); - + if (priv->nvm_device_type == -ENOENT) + return -ENOENT; /* allocate eeprom */ - if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) - priv->cfg->eeprom_size = - OTP_BLOCK_SIZE * OTP_LOWER_BLOCKS_TOTAL; + IWL_DEBUG_INFO(priv, "NVM size = %d\n", priv->cfg->eeprom_size); sz = priv->cfg->eeprom_size; priv->eeprom = kzalloc(sz, GFP_KERNEL); if (!priv->eeprom) { @@ -298,46 +504,31 @@ int iwl_eeprom_init(struct iwl_priv *priv) if (ret) { IWL_ERR(priv, "Failed to initialize OTP access.\n"); ret = -ENOENT; - goto err; + goto done; } _iwl_write32(priv, CSR_EEPROM_GP, iwl_read32(priv, CSR_EEPROM_GP) & ~CSR_EEPROM_GP_IF_OWNER_MSK); - /* clear */ - _iwl_write32(priv, CSR_OTP_GP_REG, - iwl_read32(priv, CSR_OTP_GP_REG) | + + iwl_set_bit(priv, CSR_OTP_GP_REG, CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK | CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); - - for (addr = 0; addr < sz; addr += sizeof(u16)) { - u32 r; - - _iwl_write32(priv, CSR_EEPROM_REG, - CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); - - ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG, - CSR_EEPROM_REG_READ_VALID_MSK, - IWL_EEPROM_ACCESS_TIMEOUT); - if (ret < 0) { - IWL_ERR(priv, "Time out reading OTP[%d]\n", addr); + /* traversing the linked list if no shadow ram supported */ + if (!priv->cfg->shadow_ram_support) { + if (iwl_find_otp_image(priv, &validblockaddr)) { + ret = -ENOENT; goto done; } - r = _iwl_read_direct32(priv, CSR_EEPROM_REG); - /* check for ECC errors: */ - otpgp = iwl_read32(priv, CSR_OTP_GP_REG); - if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) { - /* stop in this case */ - IWL_ERR(priv, "Uncorrectable OTP ECC error, Abort OTP read\n"); + } + for (addr = validblockaddr; addr < validblockaddr + sz; + addr += sizeof(u16)) { + u16 eeprom_data; + + ret = iwl_read_otp_word(priv, addr, &eeprom_data); + if (ret) goto done; - } - if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) { - /* continue in this case */ - _iwl_write32(priv, CSR_OTP_GP_REG, - iwl_read32(priv, CSR_OTP_GP_REG) | - CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK); - IWL_ERR(priv, "Correctable OTP ECC error, continue read\n"); - } - e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16)); + e[cache_addr / 2] = eeprom_data; + cache_addr += sizeof(u16); } } else { /* eeprom is an array of 16bit values */ @@ -458,13 +649,13 @@ static void iwl_init_band_reference(const struct iwl_priv *priv, iwl_eeprom_query_addr(priv, offset); *eeprom_ch_index = iwl_eeprom_band_5; break; - case 6: /* 2.4GHz FAT channels */ + case 6: /* 2.4GHz ht40 channels */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6); *eeprom_ch_info = (struct iwl_eeprom_channel *) iwl_eeprom_query_addr(priv, offset); *eeprom_ch_index = iwl_eeprom_band_6; break; - case 7: /* 5 GHz FAT channels */ + case 7: /* 5 GHz ht40 channels */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7); *eeprom_ch_info = (struct iwl_eeprom_channel *) iwl_eeprom_query_addr(priv, offset); @@ -480,14 +671,14 @@ static void iwl_init_band_reference(const struct iwl_priv *priv, ? # x " " : "") /** - * iwl_set_fat_chan_info - Copy fat channel info into driver's priv. + * iwl_mod_ht40_chan_info - Copy ht40 channel info into driver's priv. * * Does not set up a command, or touch hardware. */ -static int iwl_set_fat_chan_info(struct iwl_priv *priv, +static int iwl_mod_ht40_chan_info(struct iwl_priv *priv, enum ieee80211_band band, u16 channel, const struct iwl_eeprom_channel *eeprom_ch, - u8 fat_extension_channel) + u8 clear_ht40_extension_channel) { struct iwl_channel_info *ch_info; @@ -497,7 +688,7 @@ static int iwl_set_fat_chan_info(struct iwl_priv *priv, if (!is_channel_valid(ch_info)) return -1; - IWL_DEBUG_INFO(priv, "FAT Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):" + IWL_DEBUG_INFO(priv, "HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):" " Ad-Hoc %ssupported\n", ch_info->channel, is_channel_a_band(ch_info) ? @@ -513,17 +704,189 @@ static int iwl_set_fat_chan_info(struct iwl_priv *priv, && !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? "" : "not "); - ch_info->fat_eeprom = *eeprom_ch; - ch_info->fat_max_power_avg = eeprom_ch->max_power_avg; - ch_info->fat_curr_txpow = eeprom_ch->max_power_avg; - ch_info->fat_min_power = 0; - ch_info->fat_scan_power = eeprom_ch->max_power_avg; - ch_info->fat_flags = eeprom_ch->flags; - ch_info->fat_extension_channel = fat_extension_channel; + ch_info->ht40_eeprom = *eeprom_ch; + ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg; + ch_info->ht40_curr_txpow = eeprom_ch->max_power_avg; + ch_info->ht40_min_power = 0; + ch_info->ht40_scan_power = eeprom_ch->max_power_avg; + ch_info->ht40_flags = eeprom_ch->flags; + ch_info->ht40_extension_channel &= ~clear_ht40_extension_channel; return 0; } +/** + * iwl_get_max_txpower_avg - get the highest tx power from all chains. + * find the highest tx power from all chains for the channel + */ +static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv, + struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, int element) +{ + s8 max_txpower_avg = 0; /* (dBm) */ + + IWL_DEBUG_INFO(priv, "%d - " + "chain_a: %d dB chain_b: %d dB " + "chain_c: %d dB mimo2: %d dB mimo3: %d dB\n", + element, + enhanced_txpower[element].chain_a_max >> 1, + enhanced_txpower[element].chain_b_max >> 1, + enhanced_txpower[element].chain_c_max >> 1, + enhanced_txpower[element].mimo2_max >> 1, + enhanced_txpower[element].mimo3_max >> 1); + /* Take the highest tx power from any valid chains */ + if ((priv->cfg->valid_tx_ant & ANT_A) && + (enhanced_txpower[element].chain_a_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].chain_a_max; + if ((priv->cfg->valid_tx_ant & ANT_B) && + (enhanced_txpower[element].chain_b_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].chain_b_max; + if ((priv->cfg->valid_tx_ant & ANT_C) && + (enhanced_txpower[element].chain_c_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].chain_c_max; + if (((priv->cfg->valid_tx_ant == ANT_AB) | + (priv->cfg->valid_tx_ant == ANT_BC) | + (priv->cfg->valid_tx_ant == ANT_AC)) && + (enhanced_txpower[element].mimo2_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].mimo2_max; + if ((priv->cfg->valid_tx_ant == ANT_ABC) && + (enhanced_txpower[element].mimo3_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].mimo3_max; + + /* max. tx power in EEPROM is in 1/2 dBm format + * convert from 1/2 dBm to dBm + */ + return max_txpower_avg >> 1; +} + +/** + * iwl_update_common_txpower: update channel tx power + * update tx power per band based on EEPROM enhanced tx power info. + */ +static s8 iwl_update_common_txpower(struct iwl_priv *priv, + struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, + int section, int element) +{ + struct iwl_channel_info *ch_info; + int ch; + bool is_ht40 = false; + s8 max_txpower_avg; /* (dBm) */ + + /* it is common section, contain all type (Legacy, HT and HT40) + * based on the element in the section to determine + * is it HT 40 or not + */ + if (element == EEPROM_TXPOWER_COMMON_HT40_INDEX) + is_ht40 = true; + max_txpower_avg = + iwl_get_max_txpower_avg(priv, enhanced_txpower, element); + ch_info = priv->channel_info; + + for (ch = 0; ch < priv->channel_count; ch++) { + /* find matching band and update tx power if needed */ + if ((ch_info->band == enhinfo[section].band) && + (ch_info->max_power_avg < max_txpower_avg) && (!is_ht40)) { + /* Update regulatory-based run-time data */ + ch_info->max_power_avg = ch_info->curr_txpow = + max_txpower_avg; + ch_info->scan_power = max_txpower_avg; + } + if ((ch_info->band == enhinfo[section].band) && is_ht40 && + ch_info->ht40_max_power_avg && + (ch_info->ht40_max_power_avg < max_txpower_avg)) { + /* Update regulatory-based run-time data */ + ch_info->ht40_max_power_avg = max_txpower_avg; + ch_info->ht40_curr_txpow = max_txpower_avg; + ch_info->ht40_scan_power = max_txpower_avg; + } + ch_info++; + } + return max_txpower_avg; +} + +/** + * iwl_update_channel_txpower: update channel tx power + * update channel tx power based on EEPROM enhanced tx power info. + */ +static s8 iwl_update_channel_txpower(struct iwl_priv *priv, + struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, + int section, int element) +{ + struct iwl_channel_info *ch_info; + int ch; + u8 channel; + s8 max_txpower_avg; /* (dBm) */ + + channel = enhinfo[section].iwl_eeprom_section_channel[element]; + max_txpower_avg = + iwl_get_max_txpower_avg(priv, enhanced_txpower, element); + + ch_info = priv->channel_info; + for (ch = 0; ch < priv->channel_count; ch++) { + /* find matching channel and update tx power if needed */ + if (ch_info->channel == channel) { + if ((ch_info->max_power_avg < max_txpower_avg) && + (!enhinfo[section].is_ht40)) { + /* Update regulatory-based run-time data */ + ch_info->max_power_avg = max_txpower_avg; + ch_info->curr_txpow = max_txpower_avg; + ch_info->scan_power = max_txpower_avg; + } + if ((enhinfo[section].is_ht40) && + (ch_info->ht40_max_power_avg) && + (ch_info->ht40_max_power_avg < max_txpower_avg)) { + /* Update regulatory-based run-time data */ + ch_info->ht40_max_power_avg = max_txpower_avg; + ch_info->ht40_curr_txpow = max_txpower_avg; + ch_info->ht40_scan_power = max_txpower_avg; + } + break; + } + ch_info++; + } + return max_txpower_avg; +} + +/** + * iwlcore_eeprom_enhanced_txpower: process enhanced tx power info + */ +void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv) +{ + int eeprom_section_count = 0; + int section, element; + struct iwl_eeprom_enhanced_txpwr *enhanced_txpower; + u32 offset; + s8 max_txpower_avg; /* (dBm) */ + + /* Loop through all the sections + * adjust bands and channel's max tx power + * Set the tx_power_user_lmt to the highest power + * supported by any channels and chains + */ + for (section = 0; section < ARRAY_SIZE(enhinfo); section++) { + eeprom_section_count = enhinfo[section].count; + offset = enhinfo[section].offset; + enhanced_txpower = (struct iwl_eeprom_enhanced_txpwr *) + iwl_eeprom_query_addr(priv, offset); + + for (element = 0; element < eeprom_section_count; element++) { + if (enhinfo[section].is_common) + max_txpower_avg = + iwl_update_common_txpower(priv, + enhanced_txpower, section, element); + else + max_txpower_avg = + iwl_update_channel_txpower(priv, + enhanced_txpower, section, element); + + /* Update the tx_power_user_lmt to the highest power + * supported by any channel */ + if (max_txpower_avg > priv->tx_power_user_lmt) + priv->tx_power_user_lmt = max_txpower_avg; + } + } +} +EXPORT_SYMBOL(iwlcore_eeprom_enhanced_txpower); + #define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \ ? # x " " : "") @@ -585,11 +948,10 @@ int iwl_init_channel_map(struct iwl_priv *priv) /* Copy the run-time flags so they are there even on * invalid channels */ ch_info->flags = eeprom_ch_info[ch].flags; - /* First write that fat is not enabled, and then enable + /* First write that ht40 is not enabled, and then enable * one by one */ - ch_info->fat_extension_channel = - (IEEE80211_CHAN_NO_HT40PLUS | - IEEE80211_CHAN_NO_HT40MINUS); + ch_info->ht40_extension_channel = + IEEE80211_CHAN_NO_HT40; if (!(is_channel_valid(ch_info))) { IWL_DEBUG_INFO(priv, "Ch. %d Flags %x [%sGHz] - " @@ -638,17 +1000,16 @@ int iwl_init_channel_map(struct iwl_priv *priv) } } - /* Check if we do have FAT channels */ + /* Check if we do have HT40 channels */ if (priv->cfg->ops->lib->eeprom_ops.regulatory_bands[5] == - EEPROM_REGULATORY_BAND_NO_FAT && + EEPROM_REGULATORY_BAND_NO_HT40 && priv->cfg->ops->lib->eeprom_ops.regulatory_bands[6] == - EEPROM_REGULATORY_BAND_NO_FAT) + EEPROM_REGULATORY_BAND_NO_HT40) return 0; - /* Two additional EEPROM bands for 2.4 and 5 GHz FAT channels */ + /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ for (band = 6; band <= 7; band++) { enum ieee80211_band ieeeband; - u8 fat_extension_chan; iwl_init_band_reference(priv, band, &eeprom_ch_count, &eeprom_ch_info, &eeprom_ch_index); @@ -659,31 +1020,28 @@ int iwl_init_channel_map(struct iwl_priv *priv) /* Loop through each band adding each of the channels */ for (ch = 0; ch < eeprom_ch_count; ch++) { - - if ((band == 6) && - ((eeprom_ch_index[ch] == 5) || - (eeprom_ch_index[ch] == 6) || - (eeprom_ch_index[ch] == 7))) - /* both are allowed: above and below */ - fat_extension_chan = 0; - else - fat_extension_chan = - IEEE80211_CHAN_NO_HT40MINUS; - /* Set up driver's info for lower half */ - iwl_set_fat_chan_info(priv, ieeeband, + iwl_mod_ht40_chan_info(priv, ieeeband, eeprom_ch_index[ch], - &(eeprom_ch_info[ch]), - fat_extension_chan); + &eeprom_ch_info[ch], + IEEE80211_CHAN_NO_HT40PLUS); /* Set up driver's info for upper half */ - iwl_set_fat_chan_info(priv, ieeeband, - (eeprom_ch_index[ch] + 4), - &(eeprom_ch_info[ch]), - IEEE80211_CHAN_NO_HT40PLUS); + iwl_mod_ht40_chan_info(priv, ieeeband, + eeprom_ch_index[ch] + 4, + &eeprom_ch_info[ch], + IEEE80211_CHAN_NO_HT40MINUS); } } + /* for newer device (6000 series and up) + * EEPROM contain enhanced tx power information + * driver need to process addition information + * to determine the max channel tx power limits + */ + if (priv->cfg->ops->lib->eeprom_ops.update_enhanced_txpower) + priv->cfg->ops->lib->eeprom_ops.update_enhanced_txpower(priv); + return 0; } EXPORT_SYMBOL(iwl_init_channel_map); diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h index 195b4ef12c27..6b68db7b1b81 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h @@ -88,10 +88,10 @@ struct iwl_priv; * requirement for establishing a new network for legal operation on channels * requiring RADAR detection or restricting ACTIVE scanning. * - * NOTE: "WIDE" flag does not indicate anything about "FAT" 40 MHz channels. - * It only indicates that 20 MHz channel use is supported; FAT channel + * NOTE: "WIDE" flag does not indicate anything about "HT40" 40 MHz channels. + * It only indicates that 20 MHz channel use is supported; HT40 channel * usage is indicated by a separate set of regulatory flags for each - * FAT channel pair. + * HT40 channel pair. * * NOTE: Using a channel inappropriately will result in a uCode error! */ @@ -112,12 +112,36 @@ enum { #define EEPROM_SKU_CAP_HW_RF_KILL_ENABLE (1 << 1) /* *regulatory* channel data format in eeprom, one for each channel. - * There are separate entries for FAT (40 MHz) vs. normal (20 MHz) channels. */ + * There are separate entries for HT40 (40 MHz) vs. normal (20 MHz) channels. */ struct iwl_eeprom_channel { u8 flags; /* EEPROM_CHANNEL_* flags copied from EEPROM */ s8 max_power_avg; /* max power (dBm) on this chnl, limit 31 */ } __attribute__ ((packed)); +/** + * iwl_eeprom_enhanced_txpwr structure + * This structure presents the enhanced regulatory tx power limit layout + * in eeprom image + * Enhanced regulatory tx power portion of eeprom image can be broken down + * into individual structures; each one is 8 bytes in size and contain the + * following information + * @chain_a_max_pwr: chain a max power in 1/2 dBm + * @chain_b_max_pwr: chain b max power in 1/2 dBm + * @chain_c_max_pwr: chain c max power in 1/2 dBm + * @mimo2_max_pwr: mimo2 max power in 1/2 dBm + * @mimo3_max_pwr: mimo3 max power in 1/2 dBm + * + */ +struct iwl_eeprom_enhanced_txpwr { + u16 reserved; + s8 chain_a_max; + s8 chain_b_max; + s8 chain_c_max; + s8 reserved1; + s8 mimo2_max; + s8 mimo3_max; +} __attribute__ ((packed)); + /* 3945 Specific */ #define EEPROM_3945_EEPROM_VERSION (0x2f) @@ -170,18 +194,77 @@ struct iwl_eeprom_channel { | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 22 bytes */ #define EEPROM_5000_REG_BAND_5_CHANNELS ((0x74)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 12 bytes */ -#define EEPROM_5000_REG_BAND_24_FAT_CHANNELS ((0x82)\ +#define EEPROM_5000_REG_BAND_24_HT40_CHANNELS ((0x82)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 14 bytes */ -#define EEPROM_5000_REG_BAND_52_FAT_CHANNELS ((0x92)\ +#define EEPROM_5000_REG_BAND_52_HT40_CHANNELS ((0x92)\ | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 22 bytes */ +/* 6000 and up regulatory tx power - indirect access */ +/* max. elements per section */ +#define EEPROM_MAX_TXPOWER_SECTION_ELEMENTS (8) +#define EEPROM_TXPOWER_COMMON_HT40_INDEX (2) + +/** + * Partition the enhanced tx power portion of eeprom image into + * 10 sections based on band, modulation, frequency and channel + * + * Section 1: all CCK channels + * Section 2: all 2.4 GHz OFDM (Legacy, HT and HT40 ) channels + * Section 3: all 5.2 GHz OFDM (Legacy, HT and HT40) channels + * Section 4: 2.4 GHz 20MHz channels: 1, 2, 10, 11. Both Legacy and HT + * Section 5: 2.4 GHz 40MHz channels: 1, 2, 6, 7, 9, (_above_) + * Section 6: 5.2 GHz 20MHz channels: 36, 64, 100, both Legacy and HT + * Section 7: 5.2 GHz 40MHz channels: 36, 60, 100 (_above_) + * Section 8: 2.4 GHz channel 13, Both Legacy and HT + * Section 9: 2.4 GHz channel 140, Both Legacy and HT + * Section 10: 2.4 GHz 40MHz channels: 132, 44 (_above_) + */ +/* 2.4 GHz band: CCK */ +#define EEPROM_LB_CCK_20_COMMON ((0xAA)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 8 bytes */ +/* 2.4 GHz band: 20MHz-Legacy, 20MHz-HT, 40MHz-HT */ +#define EEPROM_LB_OFDM_COMMON ((0xB2)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 24 bytes */ +/* 5.2 GHz band: 20MHz-Legacy, 20MHz-HT, 40MHz-HT */ +#define EEPROM_HB_OFDM_COMMON ((0xCA)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 24 bytes */ +/* 2.4GHz band channels: + * 1Legacy, 1HT, 2Legacy, 2HT, 10Legacy, 10HT, 11Legacy, 11HT */ +#define EEPROM_LB_OFDM_20_BAND ((0xE2)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 64 bytes */ +/* 2.4 GHz band HT40 channels: (1,+1) (2,+1) (6,+1) (7,+1) (9,+1) */ +#define EEPROM_LB_OFDM_HT40_BAND ((0x122)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 40 bytes */ +/* 5.2GHz band channels: 36Legacy, 36HT, 64Legacy, 64HT, 100Legacy, 100HT */ +#define EEPROM_HB_OFDM_20_BAND ((0x14A)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 48 bytes */ +/* 5.2 GHz band HT40 channels: (36,+1) (60,+1) (100,+1) */ +#define EEPROM_HB_OFDM_HT40_BAND ((0x17A)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 24 bytes */ +/* 2.4 GHz band, channnel 13: Legacy, HT */ +#define EEPROM_LB_OFDM_20_CHANNEL_13 ((0x192)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 16 bytes */ +/* 5.2 GHz band, channnel 140: Legacy, HT */ +#define EEPROM_HB_OFDM_20_CHANNEL_140 ((0x1A2)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 16 bytes */ +/* 5.2 GHz band, HT40 channnels (132,+1) (44,+1) */ +#define EEPROM_HB_OFDM_HT40_BAND_1 ((0x1B2)\ + | INDIRECT_ADDRESS | INDIRECT_REGULATORY) /* 16 bytes */ + + /* 5050 Specific */ #define EEPROM_5050_TX_POWER_VERSION (4) #define EEPROM_5050_EEPROM_VERSION (0x21E) /* OTP */ -#define OTP_LOWER_BLOCKS_TOTAL (3) -#define OTP_BLOCK_SIZE (0x400) +/* lower blocks contain EEPROM image and calibration data */ +#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */ +/* high blocks contain PAPD data */ +#define OTP_HIGH_IMAGE_SIZE_6x00 (6 * 512 * sizeof(u16)) /* 6 KB */ +#define OTP_HIGH_IMAGE_SIZE_1000 (0x200 * sizeof(u16)) /* 1024 bytes */ +#define OTP_MAX_LL_ITEMS_1000 (3) /* OTP blocks for 1000 */ +#define OTP_MAX_LL_ITEMS_6x00 (4) /* OTP blocks for 6x00 */ +#define OTP_MAX_LL_ITEMS_6x50 (7) /* OTP blocks for 6x50 */ /* 2.4 GHz */ extern const u8 iwl_eeprom_band_1[14]; @@ -313,7 +396,7 @@ struct iwl_eeprom_calib_info { * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory * txpower (MSB). * - * Entries immediately below are for 20 MHz channel width. FAT (40 MHz) + * Entries immediately below are for 20 MHz channel width. HT40 (40 MHz) * channels (only for 4965, not supported by 3945) appear later in the EEPROM. * * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 @@ -352,29 +435,29 @@ struct iwl_eeprom_calib_info { #define EEPROM_REGULATORY_BAND_5_CHANNELS (2*0x99) /* 12 bytes */ /* - * 2.4 GHz FAT channels 1 (5), 2 (6), 3 (7), 4 (8), 5 (9), 6 (10), 7 (11) + * 2.4 GHz HT40 channels 1 (5), 2 (6), 3 (7), 4 (8), 5 (9), 6 (10), 7 (11) * * The channel listed is the center of the lower 20 MHz half of the channel. * The overall center frequency is actually 2 channels (10 MHz) above that, - * and the upper half of each FAT channel is centered 4 channels (20 MHz) away - * from the lower half; e.g. the upper half of FAT channel 1 is channel 5, - * and the overall FAT channel width centers on channel 3. + * and the upper half of each HT40 channel is centered 4 channels (20 MHz) away + * from the lower half; e.g. the upper half of HT40 channel 1 is channel 5, + * and the overall HT40 channel width centers on channel 3. * * NOTE: The RXON command uses 20 MHz channel numbers to specify the * control channel to which to tune. RXON also specifies whether the - * control channel is the upper or lower half of a FAT channel. + * control channel is the upper or lower half of a HT40 channel. * - * NOTE: 4965 does not support FAT channels on 2.4 GHz. + * NOTE: 4965 does not support HT40 channels on 2.4 GHz. */ -#define EEPROM_4965_REGULATORY_BAND_24_FAT_CHANNELS (2*0xA0) /* 14 bytes */ +#define EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS (2*0xA0) /* 14 bytes */ /* - * 5.2 GHz FAT channels 36 (40), 44 (48), 52 (56), 60 (64), + * 5.2 GHz HT40 channels 36 (40), 44 (48), 52 (56), 60 (64), * 100 (104), 108 (112), 116 (120), 124 (128), 132 (136), 149 (153), 157 (161) */ -#define EEPROM_4965_REGULATORY_BAND_52_FAT_CHANNELS (2*0xA8) /* 22 bytes */ +#define EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS (2*0xA8) /* 22 bytes */ -#define EEPROM_REGULATORY_BAND_NO_FAT (0) +#define EEPROM_REGULATORY_BAND_NO_HT40 (0) struct iwl_eeprom_ops { const u32 regulatory_bands[7]; @@ -383,6 +466,7 @@ struct iwl_eeprom_ops { void (*release_semaphore) (struct iwl_priv *priv); u16 (*calib_version) (struct iwl_priv *priv); const u8* (*query_addr) (const struct iwl_priv *priv, size_t offset); + void (*update_enhanced_txpower) (struct iwl_priv *priv); }; @@ -397,7 +481,7 @@ int iwlcore_eeprom_verify_signature(struct iwl_priv *priv); int iwlcore_eeprom_acquire_semaphore(struct iwl_priv *priv); void iwlcore_eeprom_release_semaphore(struct iwl_priv *priv); const u8 *iwlcore_eeprom_query_addr(const struct iwl_priv *priv, size_t offset); - +void iwlcore_eeprom_enhanced_txpower(struct iwl_priv *priv); int iwl_init_channel_map(struct iwl_priv *priv); void iwl_free_channel_map(struct iwl_priv *priv); const struct iwl_channel_info *iwl_get_channel_info( diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c index 17d61ac8ed61..532c8d6cd8da 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c +++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c @@ -36,8 +36,6 @@ #include "iwl-core.h" -#define IWL_CMD(x) case x: return #x - const char *get_cmd_string(u8 cmd) { switch (cmd) { @@ -103,22 +101,23 @@ EXPORT_SYMBOL(get_cmd_string); #define HOST_COMPLETE_TIMEOUT (HZ / 2) -static int iwl_generic_cmd_callback(struct iwl_priv *priv, - struct iwl_cmd *cmd, struct sk_buff *skb) +static void iwl_generic_cmd_callback(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb) { struct iwl_rx_packet *pkt = NULL; if (!skb) { IWL_ERR(priv, "Error: Response NULL in %s.\n", get_cmd_string(cmd->hdr.cmd)); - return 1; + return; } pkt = (struct iwl_rx_packet *)skb->data; if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); - return 1; + return; } #ifdef CONFIG_IWLWIFI_DEBUG @@ -127,29 +126,26 @@ static int iwl_generic_cmd_callback(struct iwl_priv *priv, case SENSITIVITY_CMD: IWL_DEBUG_HC_DUMP(priv, "back from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); - break; + break; default: IWL_DEBUG_HC(priv, "back from %s (0x%08X)\n", get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); } #endif - - /* Let iwl_tx_complete free the response skb */ - return 1; } static int iwl_send_cmd_async(struct iwl_priv *priv, struct iwl_host_cmd *cmd) { int ret; - BUG_ON(!(cmd->meta.flags & CMD_ASYNC)); + BUG_ON(!(cmd->flags & CMD_ASYNC)); /* An asynchronous command can not expect an SKB to be set. */ - BUG_ON(cmd->meta.flags & CMD_WANT_SKB); + BUG_ON(cmd->flags & CMD_WANT_SKB); /* Assign a generic callback if one is not provided */ - if (!cmd->meta.u.callback) - cmd->meta.u.callback = iwl_generic_cmd_callback; + if (!cmd->callback) + cmd->callback = iwl_generic_cmd_callback; if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return -EBUSY; @@ -168,10 +164,10 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) int cmd_idx; int ret; - BUG_ON(cmd->meta.flags & CMD_ASYNC); + BUG_ON(cmd->flags & CMD_ASYNC); /* A synchronous command can not have a callback set. */ - BUG_ON(cmd->meta.u.callback != NULL); + BUG_ON(cmd->callback); if (test_and_set_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status)) { IWL_ERR(priv, @@ -183,9 +179,6 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) set_bit(STATUS_HCMD_ACTIVE, &priv->status); - if (cmd->meta.flags & CMD_WANT_SKB) - cmd->meta.source = &cmd->meta; - cmd_idx = iwl_enqueue_hcmd(priv, cmd); if (cmd_idx < 0) { ret = cmd_idx; @@ -222,7 +215,7 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) ret = -EIO; goto fail; } - if ((cmd->meta.flags & CMD_WANT_SKB) && !cmd->meta.u.skb) { + if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_skb) { IWL_ERR(priv, "Error: Response NULL in '%s'\n", get_cmd_string(cmd->id)); ret = -EIO; @@ -233,20 +226,20 @@ int iwl_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) goto out; cancel: - if (cmd->meta.flags & CMD_WANT_SKB) { - struct iwl_cmd *qcmd; - - /* Cancel the CMD_WANT_SKB flag for the cmd in the + if (cmd->flags & CMD_WANT_SKB) { + /* + * Cancel the CMD_WANT_SKB flag for the cmd in the * 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->meta.flags &= ~CMD_WANT_SKB; + * address (cmd->meta.source). + */ + priv->txq[IWL_CMD_QUEUE_NUM].meta[cmd_idx].flags &= + ~CMD_WANT_SKB; } fail: - if (cmd->meta.u.skb) { - dev_kfree_skb_any(cmd->meta.u.skb); - cmd->meta.u.skb = NULL; + if (cmd->reply_skb) { + dev_kfree_skb_any(cmd->reply_skb); + cmd->reply_skb = NULL; } out: clear_bit(STATUS_HCMD_SYNC_ACTIVE, &priv->status); @@ -256,7 +249,7 @@ EXPORT_SYMBOL(iwl_send_cmd_sync); int iwl_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) { - if (cmd->meta.flags & CMD_ASYNC) + if (cmd->flags & CMD_ASYNC) return iwl_send_cmd_async(priv, cmd); return iwl_send_cmd_sync(priv, cmd); @@ -277,9 +270,9 @@ EXPORT_SYMBOL(iwl_send_cmd_pdu); int iwl_send_cmd_pdu_async(struct iwl_priv *priv, u8 id, u16 len, const void *data, - int (*callback)(struct iwl_priv *priv, - struct iwl_cmd *cmd, - struct sk_buff *skb)) + void (*callback)(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb)) { struct iwl_host_cmd cmd = { .id = id, @@ -287,8 +280,8 @@ int iwl_send_cmd_pdu_async(struct iwl_priv *priv, .data = data, }; - cmd.meta.flags |= CMD_ASYNC; - cmd.meta.u.callback = callback; + cmd.flags |= CMD_ASYNC; + cmd.callback = callback; return iwl_send_cmd_async(priv, &cmd); } diff --git a/drivers/net/wireless/iwlwifi/iwl-helpers.h b/drivers/net/wireless/iwlwifi/iwl-helpers.h index a1328c3c81ae..bd0b12efb5c7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-helpers.h +++ b/drivers/net/wireless/iwlwifi/iwl-helpers.h @@ -145,4 +145,25 @@ static inline void iwl_stop_queue(struct iwl_priv *priv, u8 queue) #define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue #define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue +static inline void iwl_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(priv, "Disabled interrupts\n"); +} + +static inline void iwl_enable_interrupts(struct iwl_priv *priv) +{ + IWL_DEBUG_ISR(priv, "Enabling interrupts\n"); + set_bit(STATUS_INT_ENABLED, &priv->status); + iwl_write32(priv, CSR_INT_MASK, priv->inta_mask); +} + #endif /* __iwl_helpers_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c index 5e64252f80f6..f420c99e7240 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-led.c @@ -54,7 +54,7 @@ static const char *led_type_str[] = { static const struct { - u16 tpt; + u16 tpt; /* Mb/s */ u8 on_time; u8 off_time; } blink_tbl[] = @@ -91,8 +91,8 @@ static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) .id = REPLY_LEDS_CMD, .len = sizeof(struct iwl_led_cmd), .data = led_cmd, - .meta.flags = CMD_ASYNC, - .meta.u.callback = NULL, + .flags = CMD_ASYNC, + .callback = NULL, }; u32 reg; @@ -104,7 +104,7 @@ static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) } /* Set led pattern command */ -static int iwl4965_led_pattern(struct iwl_priv *priv, int led_id, +static int iwl_led_pattern(struct iwl_priv *priv, int led_id, unsigned int idx) { struct iwl_led_cmd led_cmd = { @@ -121,7 +121,7 @@ static int iwl4965_led_pattern(struct iwl_priv *priv, int led_id, } /* Set led register off */ -static int iwl4965_led_on_reg(struct iwl_priv *priv, int led_id) +static int iwl_led_on_reg(struct iwl_priv *priv, int led_id) { IWL_DEBUG_LED(priv, "led on %d\n", led_id); iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_ON); @@ -130,7 +130,7 @@ static int iwl4965_led_on_reg(struct iwl_priv *priv, int led_id) #if 0 /* Set led on command */ -static int iwl4965_led_on(struct iwl_priv *priv, int led_id) +static int iwl_led_on(struct iwl_priv *priv, int led_id) { struct iwl_led_cmd led_cmd = { .id = led_id, @@ -142,7 +142,7 @@ static int iwl4965_led_on(struct iwl_priv *priv, int led_id) } /* Set led off command */ -int iwl4965_led_off(struct iwl_priv *priv, int led_id) +int iwl_led_off(struct iwl_priv *priv, int led_id) { struct iwl_led_cmd led_cmd = { .id = led_id, @@ -157,7 +157,7 @@ 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) +static int iwl_led_off_reg(struct iwl_priv *priv, int led_id) { IWL_DEBUG_LED(priv, "LED Reg off\n"); iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_OFF); @@ -171,7 +171,7 @@ static int iwl_led_associate(struct iwl_priv *priv, int led_id) { IWL_DEBUG_LED(priv, "Associated\n"); priv->allow_blinking = 1; - return iwl4965_led_on_reg(priv, led_id); + return iwl_led_on_reg(priv, led_id); } static int iwl_led_disassociate(struct iwl_priv *priv, int led_id) { @@ -264,13 +264,16 @@ static int iwl_leds_register_led(struct iwl_priv *priv, struct iwl_led *led, /* - * calculate blink rate according to last 2 sec Tx/Rx activities + * calculate blink rate according to last second Tx/Rx activities */ static int iwl_get_blink_rate(struct iwl_priv *priv) { int i; - u64 current_tpt = priv->tx_stats[2].bytes; - /* FIXME: + priv->rx_stats[2].bytes; */ + /* count both tx and rx traffic to be able to + * handle traffic in either direction + */ + u64 current_tpt = priv->tx_stats.data_bytes + + priv->rx_stats.data_bytes; s64 tpt = current_tpt - priv->led_tpt; if (tpt < 0) /* wraparound */ @@ -314,7 +317,7 @@ void iwl_leds_background(struct iwl_priv *priv) priv->last_blink_time = 0; if (priv->last_blink_rate != IWL_SOLID_BLINK_IDX) { priv->last_blink_rate = IWL_SOLID_BLINK_IDX; - iwl4965_led_pattern(priv, IWL_LED_LINK, + iwl_led_pattern(priv, IWL_LED_LINK, IWL_SOLID_BLINK_IDX); } return; @@ -328,12 +331,11 @@ void iwl_leds_background(struct iwl_priv *priv) /* call only if blink rate change */ if (blink_idx != priv->last_blink_rate) - iwl4965_led_pattern(priv, IWL_LED_LINK, blink_idx); + iwl_led_pattern(priv, IWL_LED_LINK, blink_idx); priv->last_blink_time = jiffies; priv->last_blink_rate = blink_idx; } -EXPORT_SYMBOL(iwl_leds_background); /* Register all led handler */ int iwl_leds_register(struct iwl_priv *priv) @@ -351,8 +353,8 @@ int iwl_leds_register(struct iwl_priv *priv) 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; - priv->led[IWL_LED_TRG_RADIO].led_off = iwl4965_led_off_reg; + priv->led[IWL_LED_TRG_RADIO].led_on = iwl_led_on_reg; + priv->led[IWL_LED_TRG_RADIO].led_off = iwl_led_off_reg; priv->led[IWL_LED_TRG_RADIO].led_pattern = NULL; ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_RADIO], @@ -386,7 +388,7 @@ int iwl_leds_register(struct iwl_priv *priv) priv->led[IWL_LED_TRG_RX].led_on = iwl_led_associated; priv->led[IWL_LED_TRG_RX].led_off = iwl_led_associated; - priv->led[IWL_LED_TRG_RX].led_pattern = iwl4965_led_pattern; + priv->led[IWL_LED_TRG_RX].led_pattern = iwl_led_pattern; if (ret) goto exit_fail; @@ -401,7 +403,7 @@ int iwl_leds_register(struct iwl_priv *priv) priv->led[IWL_LED_TRG_TX].led_on = iwl_led_associated; priv->led[IWL_LED_TRG_TX].led_off = iwl_led_associated; - priv->led[IWL_LED_TRG_TX].led_pattern = iwl4965_led_pattern; + priv->led[IWL_LED_TRG_TX].led_pattern = iwl_led_pattern; if (ret) goto exit_fail; diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c index f2ea3f05f6e1..0b16841f45f4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.c +++ b/drivers/net/wireless/iwlwifi/iwl-power.c @@ -36,25 +36,41 @@ #include "iwl-eeprom.h" #include "iwl-dev.h" #include "iwl-core.h" +#include "iwl-io.h" #include "iwl-commands.h" #include "iwl-debug.h" #include "iwl-power.h" /* - * Setting power level allow the card to go to sleep when not busy. + * Setting power level allows the card to go to sleep when not busy. * - * The power level is set to INDEX_1 (the least deep state) by - * default, and will, in the future, be the deepest state unless - * otherwise required by pm_qos network latency requirements. - * - * Using INDEX_1 without pm_qos is ok because mac80211 will disable - * PS when even checking every beacon for the TIM bit would exceed - * the required latency. + * We calculate a sleep command based on the required latency, which + * we get from mac80211. In order to handle thermal throttling, we can + * also use pre-defined power levels. */ -#define IWL_POWER_RANGE_0_MAX (2) -#define IWL_POWER_RANGE_1_MAX (10) +/* + * For now, keep using power level 1 instead of automatically + * adjusting ... + */ +bool no_sleep_autoadjust = true; +module_param(no_sleep_autoadjust, bool, S_IRUGO); +MODULE_PARM_DESC(no_sleep_autoadjust, + "don't automatically adjust sleep level " + "according to maximum network latency"); +/* + * This defines the old power levels. They are still used by default + * (level 1) and for thermal throttle (levels 3 through 5) + */ + +struct iwl_power_vec_entry { + struct iwl_powertable_cmd cmd; + u8 no_dtim; +}; + +#define IWL_DTIM_RANGE_0_MAX 2 +#define IWL_DTIM_RANGE_1_MAX 10 #define NOSLP cpu_to_le16(0), 0, 0 #define SLP IWL_POWER_DRIVER_ALLOW_SLEEP_MSK, 0, 0 @@ -66,9 +82,8 @@ cpu_to_le32(X3), \ cpu_to_le32(X4)} /* default power management (not Tx power) table values */ -/* for DTIM period 0 through IWL_POWER_RANGE_0_MAX */ +/* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */ static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { - {{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}, {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0}, @@ -77,9 +92,8 @@ static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { }; -/* for DTIM period IWL_POWER_RANGE_0_MAX + 1 through IWL_POWER_RANGE_1_MAX */ +/* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = { - {{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}, {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0}, @@ -87,9 +101,8 @@ static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = { {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 7, 10, 10)}, 2} }; -/* for DTIM period > IWL_POWER_RANGE_1_MAX */ +/* for DTIM period > IWL_DTIM_RANGE_1_MAX */ static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { - {{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}, {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, @@ -97,80 +110,29 @@ static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { {{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(priv, POWER_TABLE_CMD, - sizeof(struct iwl_powertable_cmd), cmd); -} - -/* initialize to default */ -static void iwl_power_init_handle(struct iwl_priv *priv) -{ - struct iwl_power_mgr *pow_data; - int size = sizeof(struct iwl_power_vec_entry) * IWL_POWER_NUM; - struct iwl_powertable_cmd *cmd; - int i; - u16 lctl; - - IWL_DEBUG_POWER(priv, "Initialize power \n"); - - pow_data = &priv->power_data; - - memset(pow_data, 0, sizeof(*pow_data)); - - memcpy(&pow_data->pwr_range_0[0], &range_0[0], size); - memcpy(&pow_data->pwr_range_1[0], &range_1[0], size); - memcpy(&pow_data->pwr_range_2[0], &range_2[0], size); - - lctl = iwl_pcie_link_ctl(priv); - - IWL_DEBUG_POWER(priv, "adjust power command flags\n"); - - for (i = 0; i < IWL_POWER_NUM; i++) { - cmd = &pow_data->pwr_range_0[i].cmd; - - if (lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN) - cmd->flags &= ~IWL_POWER_PCI_PM_MSK; - else - cmd->flags |= IWL_POWER_PCI_PM_MSK; - } -} - -/* adjust power command according to DTIM period and power level*/ -static int iwl_update_power_cmd(struct iwl_priv *priv, - struct iwl_powertable_cmd *cmd, u16 mode) +static void iwl_static_sleep_cmd(struct iwl_priv *priv, + struct iwl_powertable_cmd *cmd, + enum iwl_power_level lvl, int period) { - struct iwl_power_vec_entry *range; - struct iwl_power_mgr *pow_data; - int i; - u32 max_sleep = 0; - u8 period; + const struct iwl_power_vec_entry *table; + int max_sleep, i; bool skip; - if (mode > IWL_POWER_INDEX_5) { - IWL_DEBUG_POWER(priv, "Error invalid power mode \n"); - return -EINVAL; - } + table = range_2; + if (period < IWL_DTIM_RANGE_1_MAX) + table = range_1; + if (period < IWL_DTIM_RANGE_0_MAX) + table = range_0; - pow_data = &priv->power_data; + BUG_ON(lvl < 0 || lvl >= IWL_POWER_NUM); - if (pow_data->dtim_period <= IWL_POWER_RANGE_0_MAX) - range = &pow_data->pwr_range_0[0]; - else if (pow_data->dtim_period <= IWL_POWER_RANGE_1_MAX) - range = &pow_data->pwr_range_1[0]; - else - range = &pow_data->pwr_range_2[0]; - - period = pow_data->dtim_period; - memcpy(cmd, &range[mode].cmd, sizeof(struct iwl_powertable_cmd)); + *cmd = table[lvl].cmd; if (period == 0) { - period = 1; skip = false; + period = 1; } else { - skip = !!range[mode].no_dtim; + skip = !!table[lvl].no_dtim; } if (skip) { @@ -178,7 +140,7 @@ static int iwl_update_power_cmd(struct iwl_priv *priv, max_sleep = le32_to_cpu(slp_itrvl); if (max_sleep == 0xFF) max_sleep = period * (skip + 1); - else if (max_sleep > period) + else if (max_sleep > period) max_sleep = (le32_to_cpu(slp_itrvl) / period) * period; cmd->flags |= IWL_POWER_SLEEP_OVER_DTIM_MSK; } else { @@ -190,6 +152,92 @@ static int iwl_update_power_cmd(struct iwl_priv *priv, if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep) cmd->sleep_interval[i] = cpu_to_le32(max_sleep); + if (priv->power_data.pci_pm) + cmd->flags |= IWL_POWER_PCI_PM_MSK; + else + cmd->flags &= ~IWL_POWER_PCI_PM_MSK; + + IWL_DEBUG_POWER(priv, "Sleep command for index %d\n", lvl + 1); +} + +/* default Thermal Throttling transaction table + * Current state | Throttling Down | Throttling Up + *============================================================================= + * Condition Nxt State Condition Nxt State Condition Nxt State + *----------------------------------------------------------------------------- + * IWL_TI_0 T >= 115 CT_KILL 115>T>=105 TI_1 N/A N/A + * IWL_TI_1 T >= 115 CT_KILL 115>T>=110 TI_2 T<=95 TI_0 + * IWL_TI_2 T >= 115 CT_KILL T<=100 TI_1 + * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 + *============================================================================= + */ +static const struct iwl_tt_trans tt_range_0[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, 104}, + {IWL_TI_1, 105, CT_KILL_THRESHOLD}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_1[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, 95}, + {IWL_TI_2, 110, CT_KILL_THRESHOLD}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_2[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_1, IWL_ABSOLUTE_ZERO, 100}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, + {IWL_TI_CT_KILL, CT_KILL_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; +static const struct iwl_tt_trans tt_range_3[IWL_TI_STATE_MAX - 1] = { + {IWL_TI_0, IWL_ABSOLUTE_ZERO, CT_KILL_EXIT_THRESHOLD}, + {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX}, + {IWL_TI_CT_KILL, CT_KILL_EXIT_THRESHOLD + 1, IWL_ABSOLUTE_MAX} +}; + +/* Advance Thermal Throttling default restriction table */ +static const struct iwl_tt_restriction restriction_range[IWL_TI_STATE_MAX] = { + {IWL_ANT_OK_MULTI, IWL_ANT_OK_MULTI, true }, + {IWL_ANT_OK_SINGLE, IWL_ANT_OK_MULTI, true }, + {IWL_ANT_OK_SINGLE, IWL_ANT_OK_SINGLE, false }, + {IWL_ANT_OK_NONE, IWL_ANT_OK_NONE, false } +}; + + +static void iwl_power_sleep_cam_cmd(struct iwl_priv *priv, + struct iwl_powertable_cmd *cmd) +{ + memset(cmd, 0, sizeof(*cmd)); + + if (priv->power_data.pci_pm) + cmd->flags |= IWL_POWER_PCI_PM_MSK; + + IWL_DEBUG_POWER(priv, "Sleep command for CAM\n"); +} + +static void iwl_power_fill_sleep_cmd(struct iwl_priv *priv, + struct iwl_powertable_cmd *cmd, + int dynps_ms, int wakeup_period) +{ + int i; + + memset(cmd, 0, sizeof(*cmd)); + + cmd->flags = IWL_POWER_DRIVER_ALLOW_SLEEP_MSK | + IWL_POWER_FAST_PD; /* no use seeing frames for others */ + + if (priv->power_data.pci_pm) + cmd->flags |= IWL_POWER_PCI_PM_MSK; + + cmd->rx_data_timeout = cpu_to_le32(1000 * dynps_ms); + cmd->tx_data_timeout = cpu_to_le32(1000 * dynps_ms); + + for (i = 0; i < IWL_POWER_VEC_SIZE; i++) + cmd->sleep_interval[i] = cpu_to_le32(wakeup_period); + + IWL_DEBUG_POWER(priv, "Automatic sleep command\n"); +} + +static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) +{ + IWL_DEBUG_POWER(priv, "Sending power/sleep command\n"); IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags); IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", le32_to_cpu(cmd->tx_data_timeout)); IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", le32_to_cpu(cmd->rx_data_timeout)); @@ -200,79 +248,587 @@ static int iwl_update_power_cmd(struct iwl_priv *priv, le32_to_cpu(cmd->sleep_interval[3]), le32_to_cpu(cmd->sleep_interval[4])); - return 0; + return iwl_send_cmd_pdu(priv, POWER_TABLE_CMD, + sizeof(struct iwl_powertable_cmd), cmd); } -/* - * compute the final power mode index - */ int iwl_power_update_mode(struct iwl_priv *priv, bool force) { - struct iwl_power_mgr *setting = &(priv->power_data); int ret = 0; - u16 uninitialized_var(final_mode); + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + bool enabled = (priv->iw_mode == NL80211_IFTYPE_STATION) && + (priv->hw->conf.flags & IEEE80211_CONF_PS); bool update_chains; + struct iwl_powertable_cmd cmd; + int dtimper; /* Don't update the RX chain when chain noise calibration is running */ update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE || priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE; - final_mode = priv->power_data.user_power_setting; - - if (setting->power_disabled) - final_mode = IWL_POWER_MODE_CAM; + if (priv->vif) + dtimper = priv->vif->bss_conf.dtim_period; + else + dtimper = 1; + + /* TT power setting overwrites everything */ + if (tt->state >= IWL_TI_1) + iwl_static_sleep_cmd(priv, &cmd, tt->tt_power_mode, dtimper); + else if (!enabled) + iwl_power_sleep_cam_cmd(priv, &cmd); + else if (priv->power_data.debug_sleep_level_override >= 0) + iwl_static_sleep_cmd(priv, &cmd, + priv->power_data.debug_sleep_level_override, + dtimper); + else if (no_sleep_autoadjust) + iwl_static_sleep_cmd(priv, &cmd, IWL_POWER_INDEX_1, dtimper); + else + iwl_power_fill_sleep_cmd(priv, &cmd, + priv->hw->conf.dynamic_ps_timeout, + priv->hw->conf.max_sleep_period); if (iwl_is_ready_rf(priv) && - ((setting->power_mode != final_mode) || force)) { - struct iwl_powertable_cmd cmd; - - if (final_mode != IWL_POWER_MODE_CAM) + (memcmp(&priv->power_data.sleep_cmd, &cmd, sizeof(cmd)) || force)) { + if (cmd.flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK) set_bit(STATUS_POWER_PMI, &priv->status); - iwl_update_power_cmd(priv, &cmd, final_mode); - cmd.keep_alive_beacons = 0; - - if (final_mode == IWL_POWER_INDEX_5) - cmd.flags |= IWL_POWER_FAST_PD; - ret = iwl_set_power(priv, &cmd); - - if (final_mode == IWL_POWER_MODE_CAM) - clear_bit(STATUS_POWER_PMI, &priv->status); - - if (priv->cfg->ops->lib->update_chain_flags && update_chains) - priv->cfg->ops->lib->update_chain_flags(priv); - else - IWL_DEBUG_POWER(priv, "Cannot update the power, chain noise " + if (!ret) { + if (!(cmd.flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)) + clear_bit(STATUS_POWER_PMI, &priv->status); + + if (priv->cfg->ops->lib->update_chain_flags && + update_chains) + priv->cfg->ops->lib->update_chain_flags(priv); + else + IWL_DEBUG_POWER(priv, + "Cannot update the power, chain noise " "calibration running: %d\n", priv->chain_noise_data.state); - if (!ret) - setting->power_mode = final_mode; + memcpy(&priv->power_data.sleep_cmd, &cmd, sizeof(cmd)); + } else + IWL_ERR(priv, "set power fail, ret = %d", ret); } return ret; } EXPORT_SYMBOL(iwl_power_update_mode); -/* set user_power_setting */ -int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode) +bool iwl_ht_enabled(struct iwl_priv *priv) { - if (mode >= IWL_POWER_NUM) - return -EINVAL; + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; - priv->power_data.user_power_setting = mode; + if (!priv->thermal_throttle.advanced_tt) + return true; + restriction = tt->restriction + tt->state; + return restriction->is_ht; +} +EXPORT_SYMBOL(iwl_ht_enabled); + +enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; - return iwl_power_update_mode(priv, 0); + if (!priv->thermal_throttle.advanced_tt) + return IWL_ANT_OK_MULTI; + restriction = tt->restriction + tt->state; + return restriction->tx_stream; } -EXPORT_SYMBOL(iwl_power_set_user_mode); +EXPORT_SYMBOL(iwl_tx_ant_restriction); + +enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + struct iwl_tt_restriction *restriction; + + if (!priv->thermal_throttle.advanced_tt) + return IWL_ANT_OK_MULTI; + restriction = tt->restriction + tt->state; + return restriction->rx_stream; +} + +#define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ + +/* + * toggle the bit to wake up uCode and check the temperature + * if the temperature is below CT, uCode will stay awake and send card + * state notification with CT_KILL bit clear to inform Thermal Throttling + * Management to change state. Otherwise, uCode will go back to sleep + * without doing anything, driver should continue the 5 seconds timer + * to wake up uCode for temperature check until temperature drop below CT + */ +static void iwl_tt_check_exit_ct_kill(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + unsigned long flags; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (tt->state == IWL_TI_CT_KILL) { + if (priv->thermal_throttle.ct_kill_toggle) { + iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + priv->thermal_throttle.ct_kill_toggle = false; + } else { + iwl_write32(priv, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + priv->thermal_throttle.ct_kill_toggle = true; + } + iwl_read32(priv, CSR_UCODE_DRV_GP1); + spin_lock_irqsave(&priv->reg_lock, flags); + if (!iwl_grab_nic_access(priv)) + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, flags); + + /* Reschedule the ct_kill timer to occur in + * CT_KILL_EXIT_DURATION seconds to ensure we get a + * thermal update */ + mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, jiffies + + CT_KILL_EXIT_DURATION * HZ); + } +} + +static void iwl_perform_ct_kill_task(struct iwl_priv *priv, + bool stop) +{ + if (stop) { + IWL_DEBUG_POWER(priv, "Stop all queues\n"); + if (priv->mac80211_registered) + ieee80211_stop_queues(priv->hw); + IWL_DEBUG_POWER(priv, + "Schedule 5 seconds CT_KILL Timer\n"); + mod_timer(&priv->thermal_throttle.ct_kill_exit_tm, jiffies + + CT_KILL_EXIT_DURATION * HZ); + } else { + IWL_DEBUG_POWER(priv, "Wake all queues\n"); + if (priv->mac80211_registered) + ieee80211_wake_queues(priv->hw); + } +} + +#define IWL_MINIMAL_POWER_THRESHOLD (CT_KILL_THRESHOLD_LEGACY) +#define IWL_REDUCED_PERFORMANCE_THRESHOLD_2 (100) +#define IWL_REDUCED_PERFORMANCE_THRESHOLD_1 (90) + +/* + * Legacy thermal throttling + * 1) Avoid NIC destruction due to high temperatures + * Chip will identify dangerously high temperatures that can + * harm the device and will power down + * 2) Avoid the NIC power down due to high temperature + * Throttle early enough to lower the power consumption before + * drastic steps are needed + */ +static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + enum iwl_tt_state old_state; + +#ifdef CONFIG_IWLWIFI_DEBUG + if ((tt->tt_previous_temp) && + (temp > tt->tt_previous_temp) && + ((temp - tt->tt_previous_temp) > + IWL_TT_INCREASE_MARGIN)) { + IWL_DEBUG_POWER(priv, + "Temperature increase %d degree Celsius\n", + (temp - tt->tt_previous_temp)); + } +#endif + old_state = tt->state; + /* in Celsius */ + if (temp >= IWL_MINIMAL_POWER_THRESHOLD) + tt->state = IWL_TI_CT_KILL; + else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_2) + tt->state = IWL_TI_2; + else if (temp >= IWL_REDUCED_PERFORMANCE_THRESHOLD_1) + tt->state = IWL_TI_1; + else + tt->state = IWL_TI_0; + +#ifdef CONFIG_IWLWIFI_DEBUG + tt->tt_previous_temp = temp; +#endif + if (tt->state != old_state) { + switch (tt->state) { + case IWL_TI_0: + /* + * When the system is ready to go back to IWL_TI_0 + * we only have to call iwl_power_update_mode() to + * do so. + */ + break; + case IWL_TI_1: + tt->tt_power_mode = IWL_POWER_INDEX_3; + break; + case IWL_TI_2: + tt->tt_power_mode = IWL_POWER_INDEX_4; + break; + default: + tt->tt_power_mode = IWL_POWER_INDEX_5; + break; + } + mutex_lock(&priv->mutex); + if (iwl_power_update_mode(priv, true)) { + /* TT state not updated + * try again during next temperature read + */ + tt->state = old_state; + IWL_ERR(priv, "Cannot update power mode, " + "TT state not updated\n"); + } else { + if (tt->state == IWL_TI_CT_KILL) + iwl_perform_ct_kill_task(priv, true); + else if (old_state == IWL_TI_CT_KILL && + tt->state != IWL_TI_CT_KILL) + iwl_perform_ct_kill_task(priv, false); + IWL_DEBUG_POWER(priv, "Temperature state changed %u\n", + tt->state); + IWL_DEBUG_POWER(priv, "Power Index change to %u\n", + tt->tt_power_mode); + } + mutex_unlock(&priv->mutex); + } +} + +/* + * Advance thermal throttling + * 1) Avoid NIC destruction due to high temperatures + * Chip will identify dangerously high temperatures that can + * harm the device and will power down + * 2) Avoid the NIC power down due to high temperature + * Throttle early enough to lower the power consumption before + * drastic steps are needed + * Actions include relaxing the power down sleep thresholds and + * decreasing the number of TX streams + * 3) Avoid throughput performance impact as much as possible + * + *============================================================================= + * Condition Nxt State Condition Nxt State Condition Nxt State + *----------------------------------------------------------------------------- + * IWL_TI_0 T >= 115 CT_KILL 115>T>=105 TI_1 N/A N/A + * IWL_TI_1 T >= 115 CT_KILL 115>T>=110 TI_2 T<=95 TI_0 + * IWL_TI_2 T >= 115 CT_KILL T<=100 TI_1 + * IWL_CT_KILL N/A N/A N/A N/A T<=95 TI_0 + *============================================================================= + */ +static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + int i; + bool changed = false; + enum iwl_tt_state old_state; + struct iwl_tt_trans *transaction; + + old_state = tt->state; + for (i = 0; i < IWL_TI_STATE_MAX - 1; i++) { + /* based on the current TT state, + * find the curresponding transaction table + * each table has (IWL_TI_STATE_MAX - 1) entries + * tt->transaction + ((old_state * (IWL_TI_STATE_MAX - 1)) + * will advance to the correct table. + * then based on the current temperature + * find the next state need to transaction to + * go through all the possible (IWL_TI_STATE_MAX - 1) entries + * in the current table to see if transaction is needed + */ + transaction = tt->transaction + + ((old_state * (IWL_TI_STATE_MAX - 1)) + i); + if (temp >= transaction->tt_low && + temp <= transaction->tt_high) { +#ifdef CONFIG_IWLWIFI_DEBUG + if ((tt->tt_previous_temp) && + (temp > tt->tt_previous_temp) && + ((temp - tt->tt_previous_temp) > + IWL_TT_INCREASE_MARGIN)) { + IWL_DEBUG_POWER(priv, + "Temperature increase %d " + "degree Celsius\n", + (temp - tt->tt_previous_temp)); + } + tt->tt_previous_temp = temp; +#endif + if (old_state != + transaction->next_state) { + changed = true; + tt->state = + transaction->next_state; + } + break; + } + } + if (changed) { + struct iwl_rxon_cmd *rxon = &priv->staging_rxon; + + if (tt->state >= IWL_TI_1) { + /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */ + tt->tt_power_mode = IWL_POWER_INDEX_5; + if (!iwl_ht_enabled(priv)) + /* disable HT */ + rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | + RXON_FLG_HT40_PROT_MSK | + RXON_FLG_HT_PROT_MSK); + else { + /* check HT capability and set + * according to the system HT capability + * in case get disabled before */ + iwl_set_rxon_ht(priv, &priv->current_ht_config); + } + + } else { + /* + * restore system power setting -- it will be + * recalculated automatically. + */ + + /* check HT capability and set + * according to the system HT capability + * in case get disabled before */ + iwl_set_rxon_ht(priv, &priv->current_ht_config); + } + mutex_lock(&priv->mutex); + if (iwl_power_update_mode(priv, true)) { + /* TT state not updated + * try again during next temperature read + */ + IWL_ERR(priv, "Cannot update power mode, " + "TT state not updated\n"); + tt->state = old_state; + } else { + IWL_DEBUG_POWER(priv, + "Thermal Throttling to new state: %u\n", + tt->state); + if (old_state != IWL_TI_CT_KILL && + tt->state == IWL_TI_CT_KILL) { + IWL_DEBUG_POWER(priv, "Enter IWL_TI_CT_KILL\n"); + iwl_perform_ct_kill_task(priv, true); + + } else if (old_state == IWL_TI_CT_KILL && + tt->state != IWL_TI_CT_KILL) { + IWL_DEBUG_POWER(priv, "Exit IWL_TI_CT_KILL\n"); + iwl_perform_ct_kill_task(priv, false); + } + } + mutex_unlock(&priv->mutex); + } +} + +/* Card State Notification indicated reach critical temperature + * if PSP not enable, no Thermal Throttling function will be performed + * just set the GP1 bit to acknowledge the event + * otherwise, go into IWL_TI_CT_KILL state + * since Card State Notification will not provide any temperature reading + * for Legacy mode + * so just pass the CT_KILL temperature to iwl_legacy_tt_handler() + * for advance mode + * pass CT_KILL_THRESHOLD+1 to make sure move into IWL_TI_CT_KILL state + */ +static void iwl_bg_ct_enter(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_enter); + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!iwl_is_ready(priv)) + return; + + if (tt->state != IWL_TI_CT_KILL) { + IWL_ERR(priv, "Device reached critical temperature " + "- ucode going to sleep!\n"); + if (!priv->thermal_throttle.advanced_tt) + iwl_legacy_tt_handler(priv, + IWL_MINIMAL_POWER_THRESHOLD); + else + iwl_advance_tt_handler(priv, + CT_KILL_THRESHOLD + 1); + } +} + +/* Card State Notification indicated out of critical temperature + * since Card State Notification will not provide any temperature reading + * so pass the IWL_REDUCED_PERFORMANCE_THRESHOLD_2 temperature + * to iwl_legacy_tt_handler() to get out of IWL_CT_KILL state + */ +static void iwl_bg_ct_exit(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, ct_exit); + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!iwl_is_ready(priv)) + return; + + /* stop ct_kill_exit_tm timer */ + del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); + + if (tt->state == IWL_TI_CT_KILL) { + IWL_ERR(priv, + "Device temperature below critical" + "- ucode awake!\n"); + if (!priv->thermal_throttle.advanced_tt) + iwl_legacy_tt_handler(priv, + IWL_REDUCED_PERFORMANCE_THRESHOLD_2); + else + iwl_advance_tt_handler(priv, CT_KILL_EXIT_THRESHOLD); + } +} + +void iwl_tt_enter_ct_kill(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + IWL_DEBUG_POWER(priv, "Queueing critical temperature enter.\n"); + queue_work(priv->workqueue, &priv->ct_enter); +} +EXPORT_SYMBOL(iwl_tt_enter_ct_kill); + +void iwl_tt_exit_ct_kill(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + IWL_DEBUG_POWER(priv, "Queueing critical temperature exit.\n"); + queue_work(priv->workqueue, &priv->ct_exit); +} +EXPORT_SYMBOL(iwl_tt_exit_ct_kill); + +static void iwl_bg_tt_work(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, tt_work); + s32 temp = priv->temperature; /* degrees CELSIUS except 4965 */ + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_4965) + temp = KELVIN_TO_CELSIUS(priv->temperature); + + if (!priv->thermal_throttle.advanced_tt) + iwl_legacy_tt_handler(priv, temp); + else + iwl_advance_tt_handler(priv, temp); +} + +void iwl_tt_handler(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + IWL_DEBUG_POWER(priv, "Queueing thermal throttling work.\n"); + queue_work(priv->workqueue, &priv->tt_work); +} +EXPORT_SYMBOL(iwl_tt_handler); + +/* Thermal throttling initialization + * For advance thermal throttling: + * Initialize Thermal Index and temperature threshold table + * Initialize thermal throttling restriction table + */ +void iwl_tt_initialize(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + int size = sizeof(struct iwl_tt_trans) * (IWL_TI_STATE_MAX - 1); + struct iwl_tt_trans *transaction; + + IWL_DEBUG_POWER(priv, "Initialize Thermal Throttling \n"); + + memset(tt, 0, sizeof(struct iwl_tt_mgmt)); + + tt->state = IWL_TI_0; + init_timer(&priv->thermal_throttle.ct_kill_exit_tm); + priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv; + priv->thermal_throttle.ct_kill_exit_tm.function = iwl_tt_check_exit_ct_kill; + + /* setup deferred ct kill work */ + INIT_WORK(&priv->tt_work, iwl_bg_tt_work); + INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); + INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); + + switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { + case CSR_HW_REV_TYPE_6x00: + case CSR_HW_REV_TYPE_6x50: + IWL_DEBUG_POWER(priv, "Advanced Thermal Throttling\n"); + tt->restriction = kzalloc(sizeof(struct iwl_tt_restriction) * + IWL_TI_STATE_MAX, GFP_KERNEL); + tt->transaction = kzalloc(sizeof(struct iwl_tt_trans) * + IWL_TI_STATE_MAX * (IWL_TI_STATE_MAX - 1), + GFP_KERNEL); + if (!tt->restriction || !tt->transaction) { + IWL_ERR(priv, "Fallback to Legacy Throttling\n"); + priv->thermal_throttle.advanced_tt = false; + kfree(tt->restriction); + tt->restriction = NULL; + kfree(tt->transaction); + tt->transaction = NULL; + } else { + transaction = tt->transaction + + (IWL_TI_0 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_0[0], size); + transaction = tt->transaction + + (IWL_TI_1 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_1[0], size); + transaction = tt->transaction + + (IWL_TI_2 * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_2[0], size); + transaction = tt->transaction + + (IWL_TI_CT_KILL * (IWL_TI_STATE_MAX - 1)); + memcpy(transaction, &tt_range_3[0], size); + size = sizeof(struct iwl_tt_restriction) * + IWL_TI_STATE_MAX; + memcpy(tt->restriction, + &restriction_range[0], size); + priv->thermal_throttle.advanced_tt = true; + } + break; + default: + IWL_DEBUG_POWER(priv, "Legacy Thermal Throttling\n"); + priv->thermal_throttle.advanced_tt = false; + break; + } +} +EXPORT_SYMBOL(iwl_tt_initialize); + +/* cleanup thermal throttling management related memory and timer */ +void iwl_tt_exit(struct iwl_priv *priv) +{ + struct iwl_tt_mgmt *tt = &priv->thermal_throttle; + + /* stop ct_kill_exit_tm timer if activated */ + del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); + cancel_work_sync(&priv->tt_work); + cancel_work_sync(&priv->ct_enter); + cancel_work_sync(&priv->ct_exit); + + if (priv->thermal_throttle.advanced_tt) { + /* free advance thermal throttling memory */ + kfree(tt->restriction); + tt->restriction = NULL; + kfree(tt->transaction); + tt->transaction = NULL; + } +} +EXPORT_SYMBOL(iwl_tt_exit); /* initialize to default */ void iwl_power_initialize(struct iwl_priv *priv) { - iwl_power_init_handle(priv); - priv->power_data.user_power_setting = IWL_POWER_INDEX_1; - /* default to disabled until mac80211 says otherwise */ - priv->power_data.power_disabled = 1; + u16 lctl = iwl_pcie_link_ctl(priv); + + priv->power_data.pci_pm = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN); + + priv->power_data.debug_sleep_level_override = -1; + + memset(&priv->power_data.sleep_cmd, 0, + sizeof(priv->power_data.sleep_cmd)); } EXPORT_SYMBOL(iwl_power_initialize); diff --git a/drivers/net/wireless/iwlwifi/iwl-power.h b/drivers/net/wireless/iwlwifi/iwl-power.h index 37ba3bb7a25a..df6f6a49712b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.h +++ b/drivers/net/wireless/iwlwifi/iwl-power.h @@ -28,13 +28,91 @@ #ifndef __iwl_power_setting_h__ #define __iwl_power_setting_h__ -#include <net/mac80211.h> #include "iwl-commands.h" -struct iwl_priv; +#define IWL_ABSOLUTE_ZERO 0 +#define IWL_ABSOLUTE_MAX 0xFFFFFFFF +#define IWL_TT_INCREASE_MARGIN 5 -enum { - IWL_POWER_MODE_CAM, /* Continuously Aware Mode, always on */ +enum iwl_antenna_ok { + IWL_ANT_OK_NONE, + IWL_ANT_OK_SINGLE, + IWL_ANT_OK_MULTI, +}; + +/* Thermal Throttling State Machine states */ +enum iwl_tt_state { + IWL_TI_0, /* normal temperature, system power state */ + IWL_TI_1, /* high temperature detect, low power state */ + IWL_TI_2, /* higher temperature detected, lower power state */ + IWL_TI_CT_KILL, /* critical temperature detected, lowest power state */ + IWL_TI_STATE_MAX +}; + +/** + * struct iwl_tt_restriction - Thermal Throttling restriction table + * @tx_stream: number of tx stream allowed + * @is_ht: ht enable/disable + * @rx_stream: number of rx stream allowed + * + * This table is used by advance thermal throttling management + * based on the current thermal throttling state, and determines + * the number of tx/rx streams and the status of HT operation. + */ +struct iwl_tt_restriction { + enum iwl_antenna_ok tx_stream; + enum iwl_antenna_ok rx_stream; + bool is_ht; +}; + +/** + * struct iwl_tt_trans - Thermal Throttling transaction table + * @next_state: next thermal throttling mode + * @tt_low: low temperature threshold to change state + * @tt_high: high temperature threshold to change state + * + * This is used by the advanced thermal throttling algorithm + * to determine the next thermal state to go based on the + * current temperature. + */ +struct iwl_tt_trans { + enum iwl_tt_state next_state; + u32 tt_low; + u32 tt_high; +}; + +/** + * struct iwl_tt_mgnt - Thermal Throttling Management structure + * @advanced_tt: advanced thermal throttle required + * @state: current Thermal Throttling state + * @tt_power_mode: Thermal Throttling power mode index + * being used to set power level when + * when thermal throttling state != IWL_TI_0 + * the tt_power_mode should set to different + * power mode based on the current tt state + * @tt_previous_temperature: last measured temperature + * @iwl_tt_restriction: ptr to restriction tbl, used by advance + * thermal throttling to determine how many tx/rx streams + * should be used in tt state; and can HT be enabled or not + * @iwl_tt_trans: ptr to adv trans table, used by advance thermal throttling + * state transaction + * @ct_kill_toggle: used to toggle the CSR bit when checking uCode temperature + * @ct_kill_exit_tm: timer to exit thermal kill + */ +struct iwl_tt_mgmt { + enum iwl_tt_state state; + bool advanced_tt; + u8 tt_power_mode; + bool ct_kill_toggle; +#ifdef CONFIG_IWLWIFI_DEBUG + s32 tt_previous_temp; +#endif + struct iwl_tt_restriction *restriction; + struct iwl_tt_trans *transaction; + struct timer_list ct_kill_exit_tm; +}; + +enum iwl_power_level { IWL_POWER_INDEX_1, IWL_POWER_INDEX_2, IWL_POWER_INDEX_3, @@ -43,26 +121,23 @@ enum { IWL_POWER_NUM }; -/* Power management (not Tx power) structures */ - -struct iwl_power_vec_entry { - struct iwl_powertable_cmd cmd; - u8 no_dtim; -}; - struct iwl_power_mgr { - struct iwl_power_vec_entry pwr_range_0[IWL_POWER_NUM]; - struct iwl_power_vec_entry pwr_range_1[IWL_POWER_NUM]; - struct iwl_power_vec_entry pwr_range_2[IWL_POWER_NUM]; - u32 dtim_period; - /* final power level that used to calculate final power command */ - u8 power_mode; - u8 user_power_setting; /* set by user through sysfs */ - u8 power_disabled; /* set by mac80211's CONF_PS */ + struct iwl_powertable_cmd sleep_cmd; + int debug_sleep_level_override; + bool pci_pm; }; int iwl_power_update_mode(struct iwl_priv *priv, bool force); -int iwl_power_set_user_mode(struct iwl_priv *priv, u16 mode); +bool iwl_ht_enabled(struct iwl_priv *priv); +enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv); +enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv); +void iwl_tt_enter_ct_kill(struct iwl_priv *priv); +void iwl_tt_exit_ct_kill(struct iwl_priv *priv); +void iwl_tt_handler(struct iwl_priv *priv); +void iwl_tt_initialize(struct iwl_priv *priv); +void iwl_tt_exit(struct iwl_priv *priv); void iwl_power_initialize(struct iwl_priv *priv); +extern bool no_sleep_autoadjust; + #endif /* __iwl_power_setting_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 3b9cac3fd216..d393e8f02102 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -80,6 +80,8 @@ #define APMG_RFKILL_REG (APMG_BASE + 0x0014) #define APMG_RTC_INT_STT_REG (APMG_BASE + 0x001c) #define APMG_RTC_INT_MSK_REG (APMG_BASE + 0x0020) +#define APMG_DIGITAL_SVR_REG (APMG_BASE + 0x0058) +#define APMG_ANALOG_SVR_REG (APMG_BASE + 0x006C) #define APMG_CLK_VAL_DMA_CLK_RQT (0x00000200) #define APMG_CLK_VAL_BSM_CLK_RQT (0x00000800) @@ -91,7 +93,8 @@ #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_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */ +#define APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060) #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index 2b8d40b37a1c..e34d3fcb6c3d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -406,7 +406,6 @@ void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq) rxq->free_count = 0; spin_unlock_irqrestore(&rxq->lock, flags); } -EXPORT_SYMBOL(iwl_rx_queue_reset); int iwl_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq) { @@ -540,13 +539,14 @@ void iwl_rx_statistics(struct iwl_priv *priv, struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", - (int)sizeof(priv->statistics), pkt->len); + (int)sizeof(priv->statistics), + le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); change = ((priv->statistics.general.temperature != pkt->u.stats.general.temperature) || ((priv->statistics.flag & - STATISTICS_REPLY_FLG_FAT_MODE_MSK) != - (pkt->u.stats.flag & STATISTICS_REPLY_FLG_FAT_MODE_MSK))); + STATISTICS_REPLY_FLG_HT40_MODE_MSK) != + (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK))); memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics)); @@ -646,7 +646,7 @@ static void iwl_dbg_report_frame(struct iwl_priv *priv, u32 tsf_low; int rssi; - if (likely(!(priv->debug_level & IWL_DL_RX))) + if (likely(!(iwl_get_debug_level(priv) & IWL_DL_RX))) return; /* MAC header */ @@ -746,14 +746,6 @@ static void iwl_dbg_report_frame(struct iwl_priv *priv, } #endif -static void iwl_update_rx_stats(struct iwl_priv *priv, u16 fc, u16 len) -{ - /* 0 - mgmt, 1 - cnt, 2 - data */ - int idx = (fc & IEEE80211_FCTL_FTYPE) >> 2; - priv->rx_stats[idx].cnt++; - priv->rx_stats[idx].bytes += len; -} - /* * returns non-zero if packet should be dropped */ @@ -862,61 +854,12 @@ static u32 iwl_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in) } static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv, - int include_phy, - struct iwl_rx_mem_buffer *rxb, - struct ieee80211_rx_status *stats) + struct ieee80211_hdr *hdr, + u16 len, + u32 ampdu_status, + struct iwl_rx_mem_buffer *rxb, + struct ieee80211_rx_status *stats) { - struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; - 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; - unsigned int skblen; - u32 ampdu_status; - u32 ampdu_status_legacy; - - if (!include_phy && priv->last_phy_res[0]) - rx_start = (struct iwl_rx_phy_res *)&priv->last_phy_res[1]; - - if (!rx_start) { - IWL_ERR(priv, "MPDU frame without a PHY data\n"); - return; - } - if (include_phy) { - hdr = (struct ieee80211_hdr *)((u8 *) &rx_start[1] + - rx_start->cfg_phy_cnt); - - len = le16_to_cpu(rx_start->byte_count); - - rx_end = (__le32 *)((u8 *) &pkt->u.raw[0] + - sizeof(struct iwl_rx_phy_res) + - rx_start->cfg_phy_cnt + len); - - } else { - struct iwl4965_rx_mpdu_res_start *amsdu = - (struct iwl4965_rx_mpdu_res_start *)pkt->u.raw; - - hdr = (struct ieee80211_hdr *)(pkt->u.raw + - sizeof(struct iwl4965_rx_mpdu_res_start)); - len = le16_to_cpu(amsdu->byte_count); - rx_start->byte_count = amsdu->byte_count; - rx_end = (__le32 *) (((u8 *) hdr) + len); - } - - ampdu_status = le32_to_cpu(*rx_end); - skblen = ((u8 *) rx_end - (u8 *) &pkt->u.raw[0]) + sizeof(u32); - - if (!include_phy) { - /* New status scheme, need to translate */ - ampdu_status_legacy = ampdu_status; - ampdu_status = iwl_translate_rx_status(priv, ampdu_status); - } - - /* start from MAC */ - skb_reserve(rxb->skb, (void *)hdr - (void *)pkt); - skb_put(rxb->skb, len); /* end where data ends */ - /* We only process data packets if the interface is open */ if (unlikely(!priv->is_open)) { IWL_DEBUG_DROP_LIMIT(priv, @@ -924,15 +867,18 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv, return; } - hdr = (struct ieee80211_hdr *)rxb->skb->data; - - /* in case of HW accelerated crypto and bad decryption, drop */ - if (!priv->hw_params.sw_crypto && + /* In case of HW accelerated crypto and bad decryption, drop */ + if (!priv->cfg->mod_params->sw_crypto && iwl_set_decrypted_flag(priv, hdr, ampdu_status, stats)) return; - iwl_update_rx_stats(priv, le16_to_cpu(hdr->frame_control), len); - ieee80211_rx_irqsafe(priv->hw, rxb->skb, stats); + /* Resize SKB from mac header to end of packet */ + skb_reserve(rxb->skb, (void *)hdr - (void *)rxb->skb->data); + skb_put(rxb->skb, len); + + iwl_update_stats(priv, false, hdr->frame_control, len); + memcpy(IEEE80211_SKB_RXCB(rxb->skb), stats, sizeof(*stats)); + ieee80211_rx_irqsafe(priv->hw, rxb->skb); priv->alloc_rxb_skb--; rxb->skb = NULL; } @@ -963,25 +909,67 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, struct ieee80211_hdr *header; struct ieee80211_rx_status rx_status; struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data; - /* Use phy data (Rx signal strength, etc.) contained within - * 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 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; + struct iwl_rx_phy_res *phy_res; + __le32 rx_pkt_status; + struct iwl4965_rx_mpdu_res_start *amsdu; + u32 len; + u32 ampdu_status; u16 fc; - u8 network_packet; + u32 rate_n_flags; + + /** + * REPLY_RX and REPLY_RX_MPDU_CMD are handled differently. + * REPLY_RX: physical layer info is in this buffer + * REPLY_RX_MPDU_CMD: physical layer info was sent in separate + * command and cached in priv->last_phy_res + * + * Here we set up local variables depending on which command is + * received. + */ + if (pkt->hdr.cmd == REPLY_RX) { + phy_res = (struct iwl_rx_phy_res *)pkt->u.raw; + header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*phy_res) + + phy_res->cfg_phy_cnt); + + len = le16_to_cpu(phy_res->byte_count); + rx_pkt_status = *(__le32 *)(pkt->u.raw + sizeof(*phy_res) + + phy_res->cfg_phy_cnt + len); + ampdu_status = le32_to_cpu(rx_pkt_status); + } else { + if (!priv->last_phy_res[0]) { + IWL_ERR(priv, "MPDU frame without cached PHY data\n"); + return; + } + phy_res = (struct iwl_rx_phy_res *)&priv->last_phy_res[1]; + amsdu = (struct iwl4965_rx_mpdu_res_start *)pkt->u.raw; + header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*amsdu)); + len = le16_to_cpu(amsdu->byte_count); + rx_pkt_status = *(__le32 *)(pkt->u.raw + sizeof(*amsdu) + len); + ampdu_status = iwl_translate_rx_status(priv, + le32_to_cpu(rx_pkt_status)); + } - rx_status.mactime = le64_to_cpu(rx_start->timestamp); + if ((unlikely(phy_res->cfg_phy_cnt > 20))) { + IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d/n", + phy_res->cfg_phy_cnt); + return; + } + + if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) || + !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { + IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n", + le32_to_cpu(rx_pkt_status)); + return; + } + + /* rx_status carries information about the packet to mac80211 */ + rx_status.mactime = le64_to_cpu(phy_res->timestamp); rx_status.freq = - ieee80211_channel_to_frequency(le16_to_cpu(rx_start->channel)); - rx_status.band = (rx_start->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? + ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel)); + rx_status.band = (phy_res->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; rx_status.rate_idx = - iwl_hwrate_to_plcp_idx(le32_to_cpu(rx_start->rate_n_flags)); + iwl_hwrate_to_plcp_idx(le32_to_cpu(phy_res->rate_n_flags)); if (rx_status.band == IEEE80211_BAND_5GHZ) rx_status.rate_idx -= IWL_FIRST_OFDM_RATE; @@ -991,54 +979,10 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, * 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(priv, "dsp size out of range [0,20]: %d/n", - rx_start->cfg_phy_cnt); - return; - } - - if (!include_phy) { - if (priv->last_phy_res[0]) - rx_start = (struct iwl_rx_phy_res *) - &priv->last_phy_res[1]; - else - rx_start = NULL; - } - - if (!rx_start) { - IWL_ERR(priv, "MPDU frame without a PHY data\n"); - return; - } - - if (include_phy) { - header = (struct ieee80211_hdr *)((u8 *) &rx_start[1] - + rx_start->cfg_phy_cnt); - - len = le16_to_cpu(rx_start->byte_count); - rx_end = (__le32 *)(pkt->u.raw + rx_start->cfg_phy_cnt + - sizeof(struct iwl_rx_phy_res) + len); - } else { - struct iwl4965_rx_mpdu_res_start *amsdu = - (struct iwl4965_rx_mpdu_res_start *)pkt->u.raw; - - header = (void *)(pkt->u.raw + - sizeof(struct iwl4965_rx_mpdu_res_start)); - len = le16_to_cpu(amsdu->byte_count); - rx_end = (__le32 *) (pkt->u.raw + - sizeof(struct iwl4965_rx_mpdu_res_start) + len); - } - - if (!(*rx_end & RX_RES_STATUS_NO_CRC32_ERROR) || - !(*rx_end & RX_RES_STATUS_NO_RXE_OVERFLOW)) { - IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n", - le32_to_cpu(*rx_end)); - return; - } - - priv->ucode_beacon_time = le32_to_cpu(rx_start->beacon_time_stamp); + priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); /* Find max signal strength (dBm) among 3 antenna/receiver chains */ - rx_status.signal = iwl_calc_rssi(priv, rx_start); + rx_status.signal = iwl_calc_rssi(priv, phy_res); /* Meaningful noise values are available only from beacon statistics, * which are gathered only when associated, and indicate noise @@ -1058,13 +1002,14 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, if (!iwl_is_associated(priv)) priv->last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE; - /* Set "1" to report good data frames in groups of 100 */ #ifdef CONFIG_IWLWIFI_DEBUG - if (unlikely(priv->debug_level & IWL_DL_RX)) - iwl_dbg_report_frame(priv, rx_start, len, header, 1); + /* Set "1" to report good data frames in groups of 100 */ + if (unlikely(iwl_get_debug_level(priv) & IWL_DL_RX)) + iwl_dbg_report_frame(priv, phy_res, len, header, 1); #endif + iwl_dbg_log_rx_data_frame(priv, len, header); IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, noise %d, qual %d, TSF %llu\n", - rx_status.signal, rx_status.noise, rx_status.signal, + rx_status.signal, rx_status.noise, rx_status.qual, (unsigned long long)rx_status.mactime); /* @@ -1080,18 +1025,27 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, * new 802.11n radiotap field "RX chains" that is defined * as a bitmask. */ - rx_status.antenna = le16_to_cpu(rx_start->phy_flags & - RX_RES_PHY_FLAGS_ANTENNA_MSK) >> 4; + rx_status.antenna = + le16_to_cpu(phy_res->phy_flags & RX_RES_PHY_FLAGS_ANTENNA_MSK) + >> RX_RES_PHY_FLAGS_ANTENNA_POS; /* set the preamble flag if appropriate */ - if (rx_start->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) + if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) rx_status.flag |= RX_FLAG_SHORTPRE; - network_packet = iwl_is_network_packet(priv, header); - if (network_packet) { + /* Set up the HT phy flags */ + rate_n_flags = le32_to_cpu(phy_res->rate_n_flags); + if (rate_n_flags & RATE_MCS_HT_MSK) + rx_status.flag |= RX_FLAG_HT; + if (rate_n_flags & RATE_MCS_HT40_MSK) + rx_status.flag |= RX_FLAG_40MHZ; + if (rate_n_flags & RATE_MCS_SGI_MSK) + rx_status.flag |= RX_FLAG_SHORT_GI; + + if (iwl_is_network_packet(priv, header)) { priv->last_rx_rssi = rx_status.signal; priv->last_beacon_time = priv->ucode_beacon_time; - priv->last_tsf = le64_to_cpu(rx_start->timestamp); + priv->last_tsf = le64_to_cpu(phy_res->timestamp); } fc = le16_to_cpu(header->frame_control); @@ -1103,8 +1057,8 @@ void iwl_rx_reply_rx(struct iwl_priv *priv, header->addr2); /* fall through */ default: - iwl_pass_packet_to_mac80211(priv, include_phy, rxb, - &rx_status); + iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status, + rxb, &rx_status); break; } diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index e26875dbe859..4f3a108fa990 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -109,13 +109,13 @@ int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) } EXPORT_SYMBOL(iwl_scan_cancel_timeout); -int iwl_send_scan_abort(struct iwl_priv *priv) +static int iwl_send_scan_abort(struct iwl_priv *priv) { int ret = 0; struct iwl_rx_packet *res; struct iwl_host_cmd cmd = { .id = REPLY_SCAN_ABORT_CMD, - .meta.flags = CMD_WANT_SKB, + .flags = CMD_WANT_SKB, }; /* If there isn't a scan actively going on in the hardware @@ -132,7 +132,7 @@ int iwl_send_scan_abort(struct iwl_priv *priv) return ret; } - res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; + res = (struct iwl_rx_packet *)cmd.reply_skb->data; if (res->u.status != CAN_ABORT_STATUS) { /* The scan abort will return 1 for success or * 2 for "failure". A failure condition can be @@ -146,11 +146,10 @@ int iwl_send_scan_abort(struct iwl_priv *priv) } priv->alloc_rxb_skb--; - dev_kfree_skb_any(cmd.meta.u.skb); + dev_kfree_skb_any(cmd.reply_skb); return ret; } -EXPORT_SYMBOL(iwl_send_scan_abort); /* Service response to REPLY_SCAN_CMD (0x80) */ static void iwl_rx_reply_scan(struct iwl_priv *priv, @@ -322,7 +321,7 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv, u8 is_active, u8 n_probes, struct iwl_scan_channel *scan_ch) { - const struct ieee80211_channel *channels = NULL; + struct ieee80211_channel *chan; const struct ieee80211_supported_band *sband; const struct iwl_channel_info *ch_info; u16 passive_dwell = 0; @@ -334,20 +333,19 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv, if (!sband) return 0; - channels = sband->channels; - active_dwell = iwl_get_active_dwell_time(priv, band, n_probes); passive_dwell = iwl_get_passive_dwell_time(priv, band); if (passive_dwell <= active_dwell) passive_dwell = active_dwell + 1; - for (i = 0, added = 0; i < sband->n_channels; i++) { - if (channels[i].flags & IEEE80211_CHAN_DISABLED) + for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) { + chan = priv->scan_request->channels[i]; + + if (chan->band != band) continue; - channel = - ieee80211_frequency_to_channel(channels[i].center_freq); + channel = ieee80211_frequency_to_channel(chan->center_freq); scan_ch->channel = cpu_to_le16(channel); ch_info = iwl_get_channel_info(priv, band, channel); @@ -358,7 +356,7 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv, } if (!is_active || is_channel_passive(ch_info) || - (channels[i].flags & IEEE80211_CHAN_PASSIVE_SCAN)) + (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; else scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; @@ -405,7 +403,7 @@ void iwl_init_scan_params(struct iwl_priv *priv) priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; } -int iwl_scan_initiate(struct iwl_priv *priv) +static int iwl_scan_initiate(struct iwl_priv *priv) { if (!iwl_is_ready_rf(priv)) { IWL_DEBUG_SCAN(priv, "Aborting scan due to not ready.\n"); @@ -423,10 +421,6 @@ int iwl_scan_initiate(struct iwl_priv *priv) } IWL_DEBUG_INFO(priv, "Starting scan...\n"); - if (priv->cfg->sku & IWL_SKU_G) - priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ); - if (priv->cfg->sku & IWL_SKU_A) - priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ); set_bit(STATUS_SCANNING, &priv->status); priv->scan_start = jiffies; priv->scan_pass_start = priv->scan_start; @@ -435,7 +429,6 @@ int iwl_scan_initiate(struct iwl_priv *priv) return 0; } -EXPORT_SYMBOL(iwl_scan_initiate); #define IWL_DELAY_NEXT_SCAN (HZ*2) @@ -444,7 +437,7 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw, { unsigned long flags; struct iwl_priv *priv = hw->priv; - int ret; + int ret, i; IWL_DEBUG_MAC80211(priv, "enter\n"); @@ -478,6 +471,10 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw, goto out_unlock; } + priv->scan_bands = 0; + for (i = 0; i < req->n_channels; i++) + priv->scan_bands |= BIT(req->channels[i]->band); + priv->scan_request = req; ret = iwl_scan_initiate(priv); @@ -570,7 +567,7 @@ static void iwl_bg_request_scan(struct work_struct *data) struct iwl_host_cmd cmd = { .id = REPLY_SCAN_CMD, .len = sizeof(struct iwl_scan_cmd), - .meta.flags = CMD_SIZE_HUGE, + .flags = CMD_SIZE_HUGE, }; struct iwl_scan_cmd *scan; struct ieee80211_conf *conf = NULL; @@ -799,7 +796,8 @@ void iwl_bg_abort_scan(struct work_struct *work) { struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan); - if (!iwl_is_ready(priv)) + if (!test_bit(STATUS_READY, &priv->status) || + !test_bit(STATUS_GEO_CONFIGURED, &priv->status)) return; mutex_lock(&priv->mutex); diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index ffd5c61a7553..a2b9ec82b965 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -97,8 +97,9 @@ static void iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) spin_unlock_irqrestore(&priv->sta_lock, flags); } -static int iwl_add_sta_callback(struct iwl_priv *priv, - struct iwl_cmd *cmd, struct sk_buff *skb) +static void iwl_add_sta_callback(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb) { struct iwl_rx_packet *res = NULL; struct iwl_addsta_cmd *addsta = @@ -107,14 +108,14 @@ static int iwl_add_sta_callback(struct iwl_priv *priv, if (!skb) { IWL_ERR(priv, "Error: Response NULL in REPLY_ADD_STA.\n"); - return 1; + return; } res = (struct iwl_rx_packet *)skb->data; if (res->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", res->hdr.flags); - return 1; + return; } switch (res->u.add_sta.status) { @@ -126,9 +127,6 @@ static int iwl_add_sta_callback(struct iwl_priv *priv, res->u.add_sta.status); break; } - - /* We didn't cache the SKB; let the caller free it */ - return 1; } int iwl_send_add_sta(struct iwl_priv *priv, @@ -139,14 +137,14 @@ int iwl_send_add_sta(struct iwl_priv *priv, u8 data[sizeof(*sta)]; struct iwl_host_cmd cmd = { .id = REPLY_ADD_STA, - .meta.flags = flags, + .flags = flags, .data = data, }; if (flags & CMD_ASYNC) - cmd.meta.u.callback = iwl_add_sta_callback; + cmd.callback = iwl_add_sta_callback; else - cmd.meta.flags |= CMD_WANT_SKB; + cmd.flags |= CMD_WANT_SKB; cmd.len = priv->cfg->ops->utils->build_addsta_hcmd(sta, data); ret = iwl_send_cmd(priv, &cmd); @@ -154,7 +152,7 @@ int iwl_send_add_sta(struct iwl_priv *priv, if (ret || (flags & CMD_ASYNC)) return ret; - res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; + res = (struct iwl_rx_packet *)cmd.reply_skb->data; if (res->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", res->hdr.flags); @@ -175,7 +173,7 @@ int iwl_send_add_sta(struct iwl_priv *priv, } priv->alloc_rxb_skb--; - dev_kfree_skb_any(cmd.meta.u.skb); + dev_kfree_skb_any(cmd.reply_skb); return ret; } @@ -216,10 +214,10 @@ static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, sta_flags |= cpu_to_le32( (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); - if (iwl_is_fat_tx_allowed(priv, sta_ht_inf)) - sta_flags |= STA_FLG_FAT_EN_MSK; + if (iwl_is_ht40_tx_allowed(priv, sta_ht_inf)) + sta_flags |= STA_FLG_HT40_EN_MSK; else - sta_flags &= ~STA_FLG_FAT_EN_MSK; + sta_flags &= ~STA_FLG_HT40_EN_MSK; priv->stations[index].sta.station_flags = sta_flags; done: @@ -324,8 +322,9 @@ static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, const char *addr) spin_unlock_irqrestore(&priv->sta_lock, flags); } -static int iwl_remove_sta_callback(struct iwl_priv *priv, - struct iwl_cmd *cmd, struct sk_buff *skb) +static void iwl_remove_sta_callback(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct sk_buff *skb) { struct iwl_rx_packet *res = NULL; struct iwl_rem_sta_cmd *rm_sta = @@ -334,14 +333,14 @@ static int iwl_remove_sta_callback(struct iwl_priv *priv, if (!skb) { IWL_ERR(priv, "Error: Response NULL in REPLY_REMOVE_STA.\n"); - return 1; + return; } res = (struct iwl_rx_packet *)skb->data; if (res->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", res->hdr.flags); - return 1; + return; } switch (res->u.rem_sta.status) { @@ -352,9 +351,6 @@ static int iwl_remove_sta_callback(struct iwl_priv *priv, IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); break; } - - /* We didn't cache the SKB; let the caller free it */ - return 1; } static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, @@ -368,7 +364,7 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, struct iwl_host_cmd cmd = { .id = REPLY_REMOVE_STA, .len = sizeof(struct iwl_rem_sta_cmd), - .meta.flags = flags, + .flags = flags, .data = &rm_sta_cmd, }; @@ -377,15 +373,15 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, memcpy(&rm_sta_cmd.addr, addr , ETH_ALEN); if (flags & CMD_ASYNC) - cmd.meta.u.callback = iwl_remove_sta_callback; + cmd.callback = iwl_remove_sta_callback; else - cmd.meta.flags |= CMD_WANT_SKB; + cmd.flags |= CMD_WANT_SKB; ret = iwl_send_cmd(priv, &cmd); if (ret || (flags & CMD_ASYNC)) return ret; - res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; + res = (struct iwl_rx_packet *)cmd.reply_skb->data; if (res->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", res->hdr.flags); @@ -406,7 +402,7 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, } priv->alloc_rxb_skb--; - dev_kfree_skb_any(cmd.meta.u.skb); + dev_kfree_skb_any(cmd.reply_skb); return ret; } @@ -468,7 +464,6 @@ out: spin_unlock_irqrestore(&priv->sta_lock, flags); return ret; } -EXPORT_SYMBOL(iwl_remove_station); /** * iwl_clear_stations_table - Clear the driver's station table @@ -525,7 +520,7 @@ int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, u8 send_if_empty) struct iwl_host_cmd cmd = { .id = REPLY_WEPKEY, .data = wep_cmd, - .meta.flags = CMD_ASYNC, + .flags = CMD_SYNC, }; memset(wep_cmd, 0, cmd_size + @@ -930,7 +925,7 @@ int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_host_cmd cmd = { .id = REPLY_TX_LINK_QUALITY_CMD, .len = sizeof(struct iwl_link_quality_cmd), - .meta.flags = flags, + .flags = flags, .data = lq, }; @@ -1056,11 +1051,10 @@ EXPORT_SYMBOL(iwl_rxon_add_station); int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr) { int sta_id; - u16 fc = le16_to_cpu(hdr->frame_control); + __le16 fc = hdr->frame_control; /* If this frame is broadcast or management, use broadcast station id */ - if (((fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA) || - is_multicast_ether_addr(hdr->addr1)) + if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1)) return priv->hw_params.bcast_sta_id; switch (priv->iw_mode) { diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 2e89040e63be..7bc9c0039f79 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -141,7 +141,7 @@ void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id) q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) priv->cfg->ops->lib->txq_free_tfd(priv, txq); - len = sizeof(struct iwl_cmd) * q->n_window; + len = sizeof(struct iwl_device_cmd) * q->n_window; /* De-alloc array of command/tx buffers */ for (i = 0; i < TFD_TX_CMD_SLOTS; i++) @@ -156,6 +156,12 @@ void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id) kfree(txq->txb); txq->txb = NULL; + /* deallocate arrays */ + kfree(txq->cmd); + kfree(txq->meta); + txq->cmd = NULL; + txq->meta = NULL; + /* 0-fill queue descriptor structure */ memset(txq, 0, sizeof(*txq)); } @@ -179,7 +185,7 @@ void iwl_cmd_queue_free(struct iwl_priv *priv) if (q->n_bd == 0) return; - len = sizeof(struct iwl_cmd) * q->n_window; + len = sizeof(struct iwl_device_cmd) * q->n_window; len += IWL_MAX_SCAN_SIZE; /* De-alloc array of command/tx buffers */ @@ -318,6 +324,7 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, { int i, len; int ret; + int actual_slots = slots_num; /* * Alloc buffer array for commands (Tx or other types of commands). @@ -327,14 +334,22 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, * For normal Tx queues (all other queues), no super-size command * space is needed. */ - 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; - } + if (txq_id == IWL_CMD_QUEUE_NUM) + actual_slots++; + + txq->meta = kzalloc(sizeof(struct iwl_cmd_meta) * actual_slots, + GFP_KERNEL); + txq->cmd = kzalloc(sizeof(struct iwl_device_cmd *) * actual_slots, + GFP_KERNEL); + + if (!txq->meta || !txq->cmd) + goto out_free_arrays; + + len = sizeof(struct iwl_device_cmd); + for (i = 0; i < actual_slots; i++) { + /* only happens for cmd queue */ + if (i == slots_num) + len += IWL_MAX_SCAN_SIZE; txq->cmd[i] = kmalloc(len, GFP_KERNEL); if (!txq->cmd[i]) @@ -348,6 +363,10 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, txq->need_update = 0; + /* aggregation TX queues will get their ID when aggregation begins */ + if (txq_id <= IWL_TX_FIFO_AC3) + txq->swq_id = txq_id; + /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */ BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); @@ -360,15 +379,12 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, return 0; err: - for (i = 0; i < slots_num; i++) { + for (i = 0; i < actual_slots; i++) kfree(txq->cmd[i]); - txq->cmd[i] = NULL; - } +out_free_arrays: + kfree(txq->meta); + kfree(txq->cmd); - if (txq_id == IWL_CMD_QUEUE_NUM) { - kfree(txq->cmd[slots_num]); - txq->cmd[slots_num] = NULL; - } return -ENOMEM; } EXPORT_SYMBOL(iwl_tx_queue_init); @@ -652,14 +668,6 @@ static void iwl_tx_cmd_build_hwcrypto(struct iwl_priv *priv, } } -static void iwl_update_tx_stats(struct iwl_priv *priv, u16 fc, u16 len) -{ - /* 0 - mgmt, 1 - cnt, 2 - data */ - int idx = (fc & IEEE80211_FCTL_FTYPE) >> 2; - priv->tx_stats[idx].cnt++; - priv->tx_stats[idx].bytes += len; -} - /* * start REPLY_TX command process */ @@ -669,7 +677,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_tx_queue *txq; struct iwl_queue *q; - struct iwl_cmd *out_cmd; + struct iwl_device_cmd *out_cmd; + struct iwl_cmd_meta *out_meta; struct iwl_tx_cmd *tx_cmd; int swq_id, txq_id; dma_addr_t phys_addr; @@ -709,10 +718,9 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX(priv, "Sending REASSOC frame\n"); #endif - /* drop all data frame if we are not associated */ + /* drop all non-injected data frame if we are not associated */ if (ieee80211_is_data(fc) && - (!iwl_is_monitor_mode(priv) || - !(info->flags & IEEE80211_TX_CTL_INJECTED)) && /* packet injection */ + !(info->flags & IEEE80211_TX_CTL_INJECTED) && (!iwl_is_associated(priv) || ((priv->iw_mode == NL80211_IFTYPE_STATION) && !priv->assoc_id) || !priv->assoc_station_added)) { @@ -723,7 +731,10 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) hdr_len = ieee80211_hdrlen(fc); /* Find (or create) index into station table for destination station */ - sta_id = iwl_get_sta_id(priv, hdr); + if (info->flags & IEEE80211_TX_CTL_INJECTED) + sta_id = priv->hw_params.bcast_sta_id; + else + sta_id = iwl_get_sta_id(priv, hdr); if (sta_id == IWL_INVALID_STATION) { IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", hdr->addr1); @@ -732,11 +743,12 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); - swq_id = skb_get_queue_mapping(skb); - txq_id = swq_id; + txq_id = skb_get_queue_mapping(skb); if (ieee80211_is_data_qos(fc)) { qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + if (unlikely(tid >= MAX_TID_COUNT)) + goto drop_unlock; seq_number = priv->stations[sta_id].tid[tid].seq_number; seq_number &= IEEE80211_SCTL_SEQ; hdr->seq_ctrl = hdr->seq_ctrl & @@ -744,15 +756,13 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) hdr->seq_ctrl |= cpu_to_le16(seq_number); seq_number += 0x10; /* aggregation is on for this <sta,tid> */ - if (info->flags & IEEE80211_TX_CTL_AMPDU) { + if (info->flags & IEEE80211_TX_CTL_AMPDU) txq_id = priv->stations[sta_id].tid[tid].agg.txq_id; - swq_id = iwl_virtual_agg_queue_num(swq_id, txq_id); - } } txq = &priv->txq[txq_id]; + swq_id = txq->swq_id; q = &txq->q; - txq->swq_id = swq_id; if (unlikely(iwl_queue_space(q) < q->high_mark)) goto drop_unlock; @@ -766,6 +776,7 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* Set up first empty entry in queue's array of Tx/cmd buffers */ out_cmd = txq->cmd[q->write_ptr]; + out_meta = &txq->meta[q->write_ptr]; tx_cmd = &out_cmd->cmd.tx; memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); memset(tx_cmd, 0, sizeof(struct iwl_tx_cmd)); @@ -793,12 +804,12 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* TODO need this for burst mode later on */ iwl_tx_cmd_build_basic(priv, tx_cmd, info, hdr, sta_id); + iwl_dbg_log_tx_data_frame(priv, len, hdr); /* set is_hcca to 0; it probably will never be implemented */ iwl_tx_cmd_build_rate(priv, tx_cmd, info, fc, sta_id, 0); - iwl_update_tx_stats(priv, le16_to_cpu(fc), len); - + iwl_update_stats(priv, true, fc, len); /* * Use the first empty entry in this queue's command buffer array * to contain the Tx command and MAC header concatenated together @@ -828,8 +839,8 @@ int iwl_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) txcmd_phys = pci_map_single(priv->pci_dev, &out_cmd->hdr, len, PCI_DMA_BIDIRECTIONAL); - pci_unmap_addr_set(&out_cmd->meta, mapping, txcmd_phys); - pci_unmap_len_set(&out_cmd->meta, len, len); + pci_unmap_addr_set(out_meta, mapping, txcmd_phys); + pci_unmap_len_set(out_meta, len, len); /* Add buffer containing Tx command and MAC(!) header to TFD's * first entry */ priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq, @@ -922,7 +933,8 @@ 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_cmd *out_cmd; + struct iwl_device_cmd *out_cmd; + struct iwl_cmd_meta *out_meta; dma_addr_t phys_addr; unsigned long flags; int len, ret; @@ -936,25 +948,32 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then * we will need to increase the size of the TFD entries */ BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) && - !(cmd->meta.flags & CMD_SIZE_HUGE)); + !(cmd->flags & CMD_SIZE_HUGE)); if (iwl_is_rfkill(priv)) { - IWL_DEBUG_INFO(priv, "Not sending command - RF KILL"); + IWL_DEBUG_INFO(priv, "Not sending command - RF KILL\n"); return -EIO; } - if (iwl_queue_space(q) < ((cmd->meta.flags & CMD_ASYNC) ? 2 : 1)) { + if (iwl_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { IWL_ERR(priv, "No space for Tx\n"); return -ENOSPC; } spin_lock_irqsave(&priv->hcmd_lock, flags); - idx = get_cmd_index(q, q->write_ptr, cmd->meta.flags & CMD_SIZE_HUGE); + idx = get_cmd_index(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE); out_cmd = txq->cmd[idx]; + out_meta = &txq->meta[idx]; + + memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ + out_meta->flags = cmd->flags; + if (cmd->flags & CMD_WANT_SKB) + out_meta->source = cmd; + if (cmd->flags & CMD_ASYNC) + out_meta->callback = cmd->callback; out_cmd->hdr.cmd = cmd->id; - memcpy(&out_cmd->meta, &cmd->meta, sizeof(cmd->meta)); memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len); /* At this point, the out_cmd now has all of the incoming cmd @@ -963,9 +982,9 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) out_cmd->hdr.flags = 0; out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(IWL_CMD_QUEUE_NUM) | INDEX_TO_SEQ(q->write_ptr)); - if (out_cmd->meta.flags & CMD_SIZE_HUGE) + if (cmd->flags & CMD_SIZE_HUGE) out_cmd->hdr.sequence |= SEQ_HUGE_FRAME; - len = sizeof(struct iwl_cmd) - sizeof(struct iwl_cmd_meta); + len = sizeof(struct iwl_device_cmd); len += (idx == TFD_CMD_SLOTS) ? IWL_MAX_SCAN_SIZE : 0; @@ -997,8 +1016,8 @@ int iwl_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) phys_addr = pci_map_single(priv->pci_dev, &out_cmd->hdr, fix_size, PCI_DMA_BIDIRECTIONAL); - pci_unmap_addr_set(&out_cmd->meta, mapping, phys_addr); - pci_unmap_len_set(&out_cmd->meta, len, fix_size); + pci_unmap_addr_set(out_meta, mapping, phys_addr); + pci_unmap_len_set(out_meta, len, fix_size); priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq, phys_addr, fix_size, 1, @@ -1067,8 +1086,8 @@ static void iwl_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id, } pci_unmap_single(priv->pci_dev, - pci_unmap_addr(&txq->cmd[cmd_idx]->meta, mapping), - pci_unmap_len(&txq->cmd[cmd_idx]->meta, len), + pci_unmap_addr(&txq->meta[cmd_idx], mapping), + pci_unmap_len(&txq->meta[cmd_idx], len), PCI_DMA_BIDIRECTIONAL); for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; @@ -1099,7 +1118,8 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) int index = SEQ_TO_INDEX(sequence); int cmd_index; bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME); - struct iwl_cmd *cmd; + struct iwl_device_cmd *cmd; + struct iwl_cmd_meta *meta; /* If a Tx command is being handled and it isn't in the actual * command queue then there a command routing bug has been introduced @@ -1109,24 +1129,24 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) txq_id, sequence, priv->txq[IWL_CMD_QUEUE_NUM].q.read_ptr, priv->txq[IWL_CMD_QUEUE_NUM].q.write_ptr)) { - iwl_print_hex_dump(priv, IWL_DL_INFO , rxb, 32); + iwl_print_hex_error(priv, pkt, 32); return; } cmd_index = get_cmd_index(&priv->txq[IWL_CMD_QUEUE_NUM].q, index, huge); cmd = priv->txq[IWL_CMD_QUEUE_NUM].cmd[cmd_index]; + meta = &priv->txq[IWL_CMD_QUEUE_NUM].meta[cmd_index]; /* Input error checking is done when commands are added to queue. */ - if (cmd->meta.flags & CMD_WANT_SKB) { - cmd->meta.source->u.skb = rxb->skb; - rxb->skb = NULL; - } else if (cmd->meta.u.callback && - !cmd->meta.u.callback(priv, cmd, rxb->skb)) + if (meta->flags & CMD_WANT_SKB) { + meta->source->reply_skb = rxb->skb; rxb->skb = NULL; + } else if (meta->callback) + meta->callback(priv, cmd, rxb->skb); iwl_hcmd_queue_reclaim(priv, txq_id, index, cmd_index); - if (!(cmd->meta.flags & CMD_ASYNC)) { + if (!(meta->flags & CMD_ASYNC)) { clear_bit(STATUS_HCMD_ACTIVE, &priv->status); wake_up_interruptible(&priv->wait_command_queue); } @@ -1189,6 +1209,7 @@ int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn) tid_data = &priv->stations[sta_id].tid[tid]; *ssn = SEQ_TO_SN(tid_data->seq_number); tid_data->agg.txq_id = txq_id; + priv->txq[txq_id].swq_id = iwl_virtual_agg_queue_num(tx_fifo, txq_id); spin_unlock_irqrestore(&priv->sta_lock, flags); ret = priv->cfg->ops->lib->txq_agg_enable(priv, txq_id, tx_fifo, @@ -1221,6 +1242,9 @@ int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid) return -EINVAL; } + if (unlikely(tid >= MAX_TID_COUNT)) + return -EINVAL; + if (likely(tid < ARRAY_SIZE(default_tid_to_tx_fifo))) tx_fifo_id = default_tid_to_tx_fifo[tid]; else diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 523843369ca2..34790413eadd 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -89,7 +89,7 @@ MODULE_LICENSE("GPL"); /* module parameters */ struct iwl_mod_params iwl3945_mod_params = { - .num_of_queues = IWL39_MAX_NUM_QUEUES, + .num_of_queues = IWL39_NUM_QUEUES, /* Not used */ .sw_crypto = 1, .restart_fw = 1, /* the rest are 0 by default */ @@ -361,79 +361,9 @@ static void iwl3945_unset_hw_params(struct iwl_priv *priv) priv->shared_phys); } -#define MAX_UCODE_BEACON_INTERVAL 1024 -#define INTEL_CONN_LISTEN_INTERVAL cpu_to_le16(0xA) - -static __le16 iwl3945_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 iwl3945_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 = cpu_to_le64(priv->timestamp); - priv->rxon_timing.listen_interval = INTEL_CONN_LISTEN_INTERVAL; - - tsf = priv->timestamp; - - beacon_int = priv->beacon_int; - spin_unlock_irqrestore(&priv->lock, flags); - - if (priv->iw_mode == NL80211_IFTYPE_STATION) { - 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 = - iwl3945_adjust_beacon_interval( - le16_to_cpu(priv->rxon_timing.beacon_interval)); - } - - priv->rxon_timing.atim_window = 0; - } else { - priv->rxon_timing.beacon_interval = - iwl3945_adjust_beacon_interval( - priv->vif->bss_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(priv, - "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 iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv, struct ieee80211_tx_info *info, - struct iwl_cmd *cmd, + struct iwl_device_cmd *cmd, struct sk_buff *skb_frag, int sta_id) { @@ -473,7 +403,7 @@ static void iwl3945_build_tx_cmd_hwcrypto(struct iwl_priv *priv, * handle build REPLY_TX command notification. */ static void iwl3945_build_tx_cmd_basic(struct iwl_priv *priv, - struct iwl_cmd *cmd, + struct iwl_device_cmd *cmd, struct ieee80211_tx_info *info, struct ieee80211_hdr *hdr, u8 std_id) { @@ -546,7 +476,8 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) struct iwl3945_tx_cmd *tx; struct iwl_tx_queue *txq = NULL; struct iwl_queue *q = NULL; - struct iwl_cmd *out_cmd = NULL; + struct iwl_device_cmd *out_cmd; + struct iwl_cmd_meta *out_meta; dma_addr_t phys_addr; dma_addr_t txcmd_phys; int txq_id = skb_get_queue_mapping(skb); @@ -587,9 +518,9 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX(priv, "Sending REASSOC frame\n"); #endif - /* drop all data frame if we are not associated */ + /* drop all non-injected data frame if we are not associated */ if (ieee80211_is_data(fc) && - (!iwl_is_monitor_mode(priv)) && /* packet injection */ + !(info->flags & IEEE80211_TX_CTL_INJECTED) && (!iwl_is_associated(priv) || ((priv->iw_mode == NL80211_IFTYPE_STATION) && !priv->assoc_id))) { IWL_DEBUG_DROP(priv, "Dropping - !iwl_is_associated\n"); @@ -601,7 +532,10 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) hdr_len = ieee80211_hdrlen(fc); /* Find (or create) index into station table for destination station */ - sta_id = iwl_get_sta_id(priv, hdr); + if (info->flags & IEEE80211_TX_CTL_INJECTED) + sta_id = priv->hw_params.bcast_sta_id; + else + sta_id = iwl_get_sta_id(priv, hdr); if (sta_id == IWL_INVALID_STATION) { IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", hdr->addr1); @@ -613,6 +547,8 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) if (ieee80211_is_data_qos(fc)) { qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + if (unlikely(tid >= MAX_TID_COUNT)) + goto drop; seq_number = priv->stations[sta_id].tid[tid].seq_number & IEEE80211_SCTL_SEQ; hdr->seq_ctrl = cpu_to_le16(seq_number) | @@ -635,6 +571,7 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* Init first empty entry in queue's array of Tx/cmd buffers */ out_cmd = txq->cmd[idx]; + out_meta = &txq->meta[idx]; tx = (struct iwl3945_tx_cmd *)out_cmd->cmd.payload; memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); memset(tx, 0, sizeof(*tx)); @@ -666,7 +603,8 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) len = (u16)skb->len; tx->len = cpu_to_le16(len); - + iwl_dbg_log_tx_data_frame(priv, len, hdr); + iwl_update_stats(priv, true, fc, len); tx->tx_flags &= ~TX_CMD_FLG_ANT_A_MSK; tx->tx_flags &= ~TX_CMD_FLG_ANT_B_MSK; @@ -712,8 +650,8 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) len, PCI_DMA_TODEVICE); /* we do not map meta data ... so we can safely access address to * provide to unmap command*/ - pci_unmap_addr_set(&out_cmd->meta, mapping, txcmd_phys); - pci_unmap_len_set(&out_cmd->meta, len, len); + pci_unmap_addr_set(out_meta, mapping, txcmd_phys); + pci_unmap_len_set(out_meta, len, len); /* Add buffer containing Tx command and MAC(!) header to TFD's * first entry */ @@ -823,7 +761,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, struct iwl_host_cmd cmd = { .id = REPLY_SPECTRUM_MEASUREMENT_CMD, .data = (void *)&spectrum, - .meta.flags = CMD_WANT_SKB, + .flags = CMD_WANT_SKB, }; u32 add_time = le64_to_cpu(params->start_time); int rc; @@ -864,7 +802,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, if (rc) return rc; - res = (struct iwl_rx_packet *)cmd.meta.u.skb->data; + res = (struct iwl_rx_packet *)cmd.reply_skb->data; if (res->hdr.flags & IWL_CMD_FAILED_MSK) { IWL_ERR(priv, "Bad return from REPLY_RX_ON_ASSOC command\n"); rc = -EIO; @@ -887,7 +825,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, break; } - dev_kfree_skb_any(cmd.meta.u.skb); + dev_kfree_skb_any(cmd.reply_skb); return rc; } @@ -996,7 +934,7 @@ static void iwl3945_rx_card_state_notif(struct iwl_priv *priv, u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); unsigned long status = priv->status; - IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s\n", + IWL_WARN(priv, "Card state received: HW:%s SW:%s\n", (flags & HW_CARD_DISABLED) ? "Kill" : "On", (flags & SW_CARD_DISABLED) ? "Kill" : "On"); @@ -1714,7 +1652,7 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) iwl_write32(priv, CSR_FH_INT_STATUS, inta_fh); #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & IWL_DL_ISR) { + if (iwl_get_debug_level(priv) & IWL_DL_ISR) { /* just for debug */ inta_mask = iwl_read32(priv, CSR_INT_MASK); IWL_DEBUG_ISR(priv, "inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", @@ -1733,7 +1671,7 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) /* Now service all interrupt bits discovered above. */ if (inta & CSR_INT_BIT_HW_ERR) { - IWL_ERR(priv, "Microcode HW error detected. Restarting.\n"); + IWL_ERR(priv, "Hardware error detected. Restarting.\n"); /* Tell the device to stop sending interrupts */ iwl_disable_interrupts(priv); @@ -1749,7 +1687,7 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) } #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { + if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { /* NIC fires this, but we don't use it, redundant with WAKEUP */ if (inta & CSR_INT_BIT_SCD) { IWL_DEBUG_ISR(priv, "Scheduler finished to transmit " @@ -1828,7 +1766,7 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) iwl_enable_interrupts(priv); #ifdef CONFIG_IWLWIFI_DEBUG - if (priv->debug_level & (IWL_DL_ISR)) { + if (iwl_get_debug_level(priv) & (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); @@ -1844,7 +1782,7 @@ static int iwl3945_get_channels_for_scan(struct iwl_priv *priv, u8 is_active, u8 n_probes, struct iwl3945_scan_channel *scan_ch) { - const struct ieee80211_channel *channels = NULL; + struct ieee80211_channel *chan; const struct ieee80211_supported_band *sband; const struct iwl_channel_info *ch_info; u16 passive_dwell = 0; @@ -1855,19 +1793,19 @@ static int iwl3945_get_channels_for_scan(struct iwl_priv *priv, if (!sband) return 0; - channels = sband->channels; - active_dwell = iwl_get_active_dwell_time(priv, band, n_probes); passive_dwell = iwl_get_passive_dwell_time(priv, band); if (passive_dwell <= active_dwell) passive_dwell = active_dwell + 1; - for (i = 0, added = 0; i < sband->n_channels; i++) { - if (channels[i].flags & IEEE80211_CHAN_DISABLED) + for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) { + chan = priv->scan_request->channels[i]; + + if (chan->band != band) continue; - scan_ch->channel = channels[i].hw_value; + scan_ch->channel = chan->hw_value; ch_info = iwl_get_channel_info(priv, band, scan_ch->channel); if (!is_channel_valid(ch_info)) { @@ -1882,7 +1820,7 @@ static int iwl3945_get_channels_for_scan(struct iwl_priv *priv, * and use long active_dwell time. */ if (!is_active || is_channel_passive(ch_info) || - (channels[i].flags & IEEE80211_CHAN_PASSIVE_SCAN)) { + (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) { scan_ch->type = 0; /* passive */ if (IWL_UCODE_API(priv->ucode_ver) == 1) scan_ch->active_dwell = cpu_to_le16(passive_dwell - 1); @@ -2111,7 +2049,7 @@ static void iwl3945_nic_start(struct iwl_priv *priv) */ static int iwl3945_read_ucode(struct iwl_priv *priv) { - struct iwl_ucode *ucode; + const struct iwl_ucode_header *ucode; int ret = -EINVAL, index; const struct firmware *ucode_raw; /* firmware file name contains uCode/driver compatibility version */ @@ -2152,22 +2090,24 @@ static int iwl3945_read_ucode(struct iwl_priv *priv) goto error; /* Make sure that we got at least our header! */ - if (ucode_raw->size < sizeof(*ucode)) { + if (ucode_raw->size < priv->cfg->ops->ucode->get_header_size(1)) { IWL_ERR(priv, "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; + ucode = (struct iwl_ucode_header *)ucode_raw->data; priv->ucode_ver = le32_to_cpu(ucode->ver); api_ver = IWL_UCODE_API(priv->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); + inst_size = priv->cfg->ops->ucode->get_inst_size(ucode, api_ver); + data_size = priv->cfg->ops->ucode->get_data_size(ucode, api_ver); + init_size = priv->cfg->ops->ucode->get_init_size(ucode, api_ver); + init_data_size = + priv->cfg->ops->ucode->get_init_data_size(ucode, api_ver); + boot_size = priv->cfg->ops->ucode->get_boot_size(ucode, api_ver); + src = priv->cfg->ops->ucode->get_data(ucode, api_ver); /* api_ver should match the api version forming part of the * firmware filename ... but we don't check for that and only rely @@ -2208,12 +2148,13 @@ static int iwl3945_read_ucode(struct iwl_priv *priv) /* Verify size of file vs. image size info in file's header */ - if (ucode_raw->size < sizeof(*ucode) + + if (ucode_raw->size != priv->cfg->ops->ucode->get_header_size(api_ver) + inst_size + data_size + init_size + init_data_size + boot_size) { - IWL_DEBUG_INFO(priv, "uCode file size %zd too small\n", - ucode_raw->size); + IWL_DEBUG_INFO(priv, + "uCode file size %zd does not match expected size\n", + ucode_raw->size); ret = -EINVAL; goto err_release; } @@ -2296,44 +2237,44 @@ static int iwl3945_read_ucode(struct iwl_priv *priv) /* 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; + len = inst_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) uCode instr len %zd\n", len); memcpy(priv->ucode_code.v_addr, src, len); + src += len; + IWL_DEBUG_INFO(priv, "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 iwl3945_up() */ - src = &ucode->data[inst_size]; - len = priv->ucode_data.len; + len = data_size; IWL_DEBUG_INFO(priv, "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); + src += len; /* Initialization instructions (3rd block) */ if (init_size) { - src = &ucode->data[inst_size + data_size]; - len = priv->ucode_init.len; + len = init_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) init instr len %zd\n", len); memcpy(priv->ucode_init.v_addr, src, len); + 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; + len = init_data_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) init data len %zd\n", len); memcpy(priv->ucode_init_data.v_addr, src, len); + src += len; } /* Bootstrap instructions (5th block) */ - src = &ucode->data[inst_size + data_size + init_size + init_data_size]; - len = priv->ucode_boot.len; + len = boot_size; IWL_DEBUG_INFO(priv, "Copying (but not loading) boot instr len %zd\n", len); memcpy(priv->ucode_boot.v_addr, src, len); @@ -2784,7 +2725,7 @@ static void iwl3945_bg_request_scan(struct work_struct *data) struct iwl_host_cmd cmd = { .id = REPLY_SCAN_CMD, .len = sizeof(struct iwl3945_scan_cmd), - .meta.flags = CMD_SIZE_HUGE, + .flags = CMD_SIZE_HUGE, }; int rc = 0; struct iwl3945_scan_cmd *scan; @@ -3066,7 +3007,7 @@ void iwl3945_post_associate(struct iwl_priv *priv) iwlcore_commit_rxon(priv); memset(&priv->rxon_timing, 0, sizeof(struct iwl_rxon_time_cmd)); - iwl3945_setup_rxon_timing(priv); + iwl_setup_rxon_timing(priv); rc = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING, sizeof(priv->rxon_timing), &priv->rxon_timing); if (rc) @@ -3261,7 +3202,7 @@ void iwl3945_config_ap(struct iwl_priv *priv) /* RXON Timing */ memset(&priv->rxon_timing, 0, sizeof(struct iwl_rxon_time_cmd)); - iwl3945_setup_rxon_timing(priv); + iwl_setup_rxon_timing(priv); rc = iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING, sizeof(priv->rxon_timing), &priv->rxon_timing); @@ -3375,13 +3316,16 @@ static int iwl3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, * used for controlling the debug level. * * See the level definitions in iwl for details. + * + * The debug_level being managed using sysfs below is a per device debug + * level that is used instead of the global debug level if it (the per + * device debug level) is set. */ static ssize_t show_debug_level(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); - - return sprintf(buf, "0x%08X\n", priv->debug_level); + return sprintf(buf, "0x%08X\n", iwl_get_debug_level(priv)); } static ssize_t store_debug_level(struct device *d, struct device_attribute *attr, @@ -3394,9 +3338,12 @@ static ssize_t store_debug_level(struct device *d, ret = strict_strtoul(buf, 0, &val); if (ret) IWL_INFO(priv, "%s is not in hex or decimal form.\n", buf); - else + else { priv->debug_level = val; - + if (iwl_alloc_traffic_mem(priv)) + IWL_ERR(priv, + "Not enough memory to generate traffic log\n"); + } return strnlen(buf, count); } @@ -3612,65 +3559,6 @@ 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; - unsigned long mode; - - - mutex_lock(&priv->mutex); - - ret = strict_strtoul(buf, 10, &mode); - if (ret) - goto out; - - ret = iwl_power_set_user_mode(priv, mode); - if (ret) { - IWL_DEBUG_MAC80211(priv, "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 level = priv->power_data.power_mode; - char *p = buf; - - p += sprintf(p, "%d\n", level); - return p - buf + 1; -} - -static DEVICE_ATTR(power_level, S_IWUSR | S_IRUSR, - show_power_level, store_power_level); - -#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_channels(struct device *d, struct device_attribute *attr, char *buf) { @@ -3847,7 +3735,6 @@ static struct attribute *iwl3945_sysfs_entries[] = { #ifdef CONFIG_IWL3945_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, @@ -3912,8 +3799,6 @@ static int iwl3945_init_drv(struct iwl_priv *priv) priv->qos_data.qos_cap.val = 0; priv->rates_mask = IWL_RATES_MASK; - /* If power management is turned on, default to CAM mode */ - priv->power_mode = IWL_POWER_MODE_CAM; priv->tx_power_user_lmt = IWL_DEFAULT_TX_POWER; if (eeprom->version < EEPROM_3945_EEPROM_VERSION) { @@ -3960,7 +3845,9 @@ static int iwl3945_setup_mac(struct iwl_priv *priv) /* Tell mac80211 our characteristics */ hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM | - IEEE80211_HW_SPECTRUM_MGMT; + IEEE80211_HW_SPECTRUM_MGMT | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | @@ -4020,15 +3907,6 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e priv = hw->priv; SET_IEEE80211_DEV(hw, &pdev->dev); - if ((iwl3945_mod_params.num_of_queues > IWL39_MAX_NUM_QUEUES) || - (iwl3945_mod_params.num_of_queues < IWL39_MIN_NUM_QUEUES)) { - IWL_ERR(priv, - "invalid queues_num, should be between %d and %d\n", - IWL39_MIN_NUM_QUEUES, IWL39_MAX_NUM_QUEUES); - err = -EINVAL; - goto out_ieee80211_free_hw; - } - /* * Disabling hardware scan means that mac80211 will perform scans * "the hard way", rather than using device's scan. @@ -4045,9 +3923,10 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e priv->inta_mask = CSR_INI_SET_MASK; #ifdef CONFIG_IWLWIFI_DEBUG - priv->debug_level = iwl3945_mod_params.debug; atomic_set(&priv->restrict_refcnt, 0); #endif + if (iwl_alloc_traffic_mem(priv)) + IWL_ERR(priv, "Not enough memory to generate traffic log\n"); /*************************** * 2. Initializing PCI bus @@ -4210,6 +4089,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e pci_disable_device(pdev); out_ieee80211_free_hw: ieee80211_free_hw(priv->hw); + iwl_free_traffic_mem(priv); out: return err; } @@ -4265,6 +4145,7 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev) * until now... */ destroy_workqueue(priv->workqueue); priv->workqueue = NULL; + iwl_free_traffic_mem(priv); free_irq(pdev->irq, priv); pci_disable_msi(pdev); @@ -4341,14 +4222,12 @@ MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); module_param_named(swcrypto, iwl3945_mod_params.sw_crypto, int, 0444); MODULE_PARM_DESC(swcrypto, "using software crypto (default 1 [software])\n"); -module_param_named(debug, iwl3945_mod_params.debug, uint, 0444); +#ifdef CONFIG_IWLWIFI_DEBUG +module_param_named(debug, iwl_debug_level, uint, 0644); MODULE_PARM_DESC(debug, "debug output mask"); +#endif module_param_named(disable_hw_scan, iwl3945_mod_params.disable_hw_scan, int, 0444); MODULE_PARM_DESC(disable_hw_scan, "disable hardware scanning (default 0)"); - -module_param_named(queues_num, iwl3945_mod_params.num_of_queues, int, 0444); -MODULE_PARM_DESC(queues_num, "number of hw queues."); - module_param_named(fw_restart3945, iwl3945_mod_params.restart_fw, int, 0444); MODULE_PARM_DESC(fw_restart3945, "restart firmware in case of error"); diff --git a/drivers/net/wireless/iwmc3200wifi/Kconfig b/drivers/net/wireless/iwmc3200wifi/Kconfig index 030401d367d3..c62da435285a 100644 --- a/drivers/net/wireless/iwmc3200wifi/Kconfig +++ b/drivers/net/wireless/iwmc3200wifi/Kconfig @@ -2,7 +2,6 @@ config IWM tristate "Intel Wireless Multicomm 3200 WiFi driver" depends on MMC && WLAN_80211 && EXPERIMENTAL depends on CFG80211 - select WIRELESS_EXT select FW_LOADER help The Intel Wireless Multicomm 3200 hardware is a combo diff --git a/drivers/net/wireless/iwmc3200wifi/Makefile b/drivers/net/wireless/iwmc3200wifi/Makefile index 927f022545c1..d34291b652d3 100644 --- a/drivers/net/wireless/iwmc3200wifi/Makefile +++ b/drivers/net/wireless/iwmc3200wifi/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_IWM) := iwmc3200wifi.o iwmc3200wifi-objs += main.o netdev.o rx.o tx.o sdio.o hal.o fw.o -iwmc3200wifi-objs += commands.o wext.o cfg80211.o eeprom.o +iwmc3200wifi-objs += commands.o cfg80211.o eeprom.o iwmc3200wifi-$(CONFIG_IWM_DEBUG) += debugfs.o diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c index 96f714e6e12b..a6e852f4f92c 100644 --- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c +++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/netdevice.h> +#include <linux/etherdevice.h> #include <linux/wireless.h> #include <linux/ieee80211.h> #include <net/cfg80211.h> @@ -130,6 +131,133 @@ static struct ieee80211_supported_band iwm_band_5ghz = { .n_bitrates = iwm_a_rates_size, }; +static int iwm_key_init(struct iwm_key *key, u8 key_index, + const u8 *mac_addr, struct key_params *params) +{ + key->hdr.key_idx = key_index; + if (!mac_addr || is_broadcast_ether_addr(mac_addr)) { + key->hdr.multicast = 1; + memset(key->hdr.mac, 0xff, ETH_ALEN); + } else { + key->hdr.multicast = 0; + memcpy(key->hdr.mac, mac_addr, ETH_ALEN); + } + + if (params) { + if (params->key_len > WLAN_MAX_KEY_LEN || + params->seq_len > IW_ENCODE_SEQ_MAX_SIZE) + return -EINVAL; + + key->cipher = params->cipher; + key->key_len = params->key_len; + key->seq_len = params->seq_len; + memcpy(key->key, params->key, key->key_len); + memcpy(key->seq, params->seq, key->seq_len); + } + + return 0; +} + +static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_index, const u8 *mac_addr, + struct key_params *params) +{ + struct iwm_priv *iwm = ndev_to_iwm(ndev); + struct iwm_key *key = &iwm->keys[key_index]; + int ret; + + IWM_DBG_WEXT(iwm, DBG, "Adding key for %pM\n", mac_addr); + + memset(key, 0, sizeof(struct iwm_key)); + ret = iwm_key_init(key, key_index, mac_addr, params); + if (ret < 0) { + IWM_ERR(iwm, "Invalid key_params\n"); + return ret; + } + + return iwm_set_key(iwm, 0, key); +} + +static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_index, const u8 *mac_addr, void *cookie, + void (*callback)(void *cookie, + struct key_params*)) +{ + struct iwm_priv *iwm = ndev_to_iwm(ndev); + struct iwm_key *key = &iwm->keys[key_index]; + struct key_params params; + + IWM_DBG_WEXT(iwm, DBG, "Getting key %d\n", key_index); + + memset(¶ms, 0, sizeof(params)); + + params.cipher = key->cipher; + params.key_len = key->key_len; + params.seq_len = key->seq_len; + params.seq = key->seq; + params.key = key->key; + + callback(cookie, ¶ms); + + return key->key_len ? 0 : -ENOENT; +} + + +static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_index, const u8 *mac_addr) +{ + struct iwm_priv *iwm = ndev_to_iwm(ndev); + struct iwm_key *key = &iwm->keys[key_index]; + + if (!iwm->keys[key_index].key_len) { + IWM_DBG_WEXT(iwm, DBG, "Key %d not used\n", key_index); + return 0; + } + + if (key_index == iwm->default_key) + iwm->default_key = -1; + + return iwm_set_key(iwm, 1, key); +} + +static int iwm_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *ndev, + u8 key_index) +{ + struct iwm_priv *iwm = ndev_to_iwm(ndev); + + IWM_DBG_WEXT(iwm, DBG, "Default key index is: %d\n", key_index); + + if (!iwm->keys[key_index].key_len) { + IWM_ERR(iwm, "Key %d not used\n", key_index); + return -EINVAL; + } + + iwm->default_key = key_index; + + return iwm_set_tx_key(iwm, key_index); +} + +int iwm_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, + u8 *mac, struct station_info *sinfo) +{ + struct iwm_priv *iwm = ndev_to_iwm(ndev); + + if (memcmp(mac, iwm->bssid, ETH_ALEN)) + return -ENOENT; + + sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->txrate.legacy = iwm->rate * 10; + + if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { + sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->signal = iwm->wstats.qual.level; + } + + return 0; +} + + int iwm_cfg80211_inform_bss(struct iwm_priv *iwm) { struct wiphy *wiphy = iwm_to_wiphy(iwm); @@ -167,20 +295,15 @@ int iwm_cfg80211_inform_bss(struct iwm_priv *iwm) return 0; } -static int iwm_cfg80211_change_iface(struct wiphy *wiphy, int ifindex, +static int iwm_cfg80211_change_iface(struct wiphy *wiphy, + struct net_device *ndev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { - struct net_device *ndev; struct wireless_dev *wdev; struct iwm_priv *iwm; u32 old_mode; - /* we're under RTNL */ - ndev = __dev_get_by_index(&init_net, ifindex); - if (!ndev) - return -ENODEV; - wdev = ndev->ieee80211_ptr; iwm = ndev_to_iwm(ndev); old_mode = iwm->conf.mode; @@ -329,12 +452,250 @@ static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) return 0; } +static int iwm_set_auth_type(struct iwm_priv *iwm, + enum nl80211_auth_type sme_auth_type) +{ + u8 *auth_type = &iwm->umac_profile->sec.auth_type; + + switch (sme_auth_type) { + case NL80211_AUTHTYPE_AUTOMATIC: + case NL80211_AUTHTYPE_OPEN_SYSTEM: + IWM_DBG_WEXT(iwm, DBG, "OPEN auth\n"); + *auth_type = UMAC_AUTH_TYPE_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + if (iwm->umac_profile->sec.flags & + (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) { + IWM_DBG_WEXT(iwm, DBG, "WPA auth alg\n"); + *auth_type = UMAC_AUTH_TYPE_RSNA_PSK; + } else { + IWM_DBG_WEXT(iwm, DBG, "WEP shared key auth alg\n"); + *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; + } + + break; + default: + IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", sme_auth_type); + return -ENOTSUPP; + } + + return 0; +} + +static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version) +{ + IWM_DBG_WEXT(iwm, DBG, "wpa_version: %d\n", wpa_version); + + if (!wpa_version) { + iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE; + return 0; + } + + if (wpa_version & NL80211_WPA_VERSION_2) + iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK; + + if (wpa_version & NL80211_WPA_VERSION_1) + iwm->umac_profile->sec.flags |= UMAC_SEC_FLG_WPA_ON_MSK; + + return 0; +} + +static int iwm_set_cipher(struct iwm_priv *iwm, u32 cipher, bool ucast) +{ + u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher : + &iwm->umac_profile->sec.mcast_cipher; + + if (!cipher) { + *profile_cipher = UMAC_CIPHER_TYPE_NONE; + return 0; + } + + IWM_DBG_WEXT(iwm, DBG, "%ccast cipher is 0x%x\n", ucast ? 'u' : 'm', + cipher); + + switch (cipher) { + case IW_AUTH_CIPHER_NONE: + *profile_cipher = UMAC_CIPHER_TYPE_NONE; + break; + case WLAN_CIPHER_SUITE_WEP40: + *profile_cipher = UMAC_CIPHER_TYPE_WEP_40; + break; + case WLAN_CIPHER_SUITE_WEP104: + *profile_cipher = UMAC_CIPHER_TYPE_WEP_104; + break; + case WLAN_CIPHER_SUITE_TKIP: + *profile_cipher = UMAC_CIPHER_TYPE_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + *profile_cipher = UMAC_CIPHER_TYPE_CCMP; + break; + default: + IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher); + return -ENOTSUPP; + } + + return 0; +} + +static int iwm_set_key_mgt(struct iwm_priv *iwm, u32 key_mgt) +{ + u8 *auth_type = &iwm->umac_profile->sec.auth_type; + + IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt); + + if (key_mgt == WLAN_AKM_SUITE_8021X) + *auth_type = UMAC_AUTH_TYPE_8021X; + else if (key_mgt == WLAN_AKM_SUITE_PSK) { + if (iwm->umac_profile->sec.flags & + (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) + *auth_type = UMAC_AUTH_TYPE_RSNA_PSK; + else + *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; + } else { + IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt); + return -EINVAL; + } + + return 0; +} + + +static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct iwm_priv *iwm = wiphy_to_iwm(wiphy); + struct ieee80211_channel *chan = sme->channel; + int ret; + + if (!test_bit(IWM_STATUS_READY, &iwm->status)) + return -EIO; + + if (!sme->ssid) + return -EINVAL; + + if (chan) + iwm->channel = + ieee80211_frequency_to_channel(chan->center_freq); + + iwm->umac_profile->ssid.ssid_len = sme->ssid_len; + memcpy(iwm->umac_profile->ssid.ssid, sme->ssid, sme->ssid_len); + + if (sme->bssid) { + IWM_DBG_WEXT(iwm, DBG, "BSSID: %pM\n", sme->bssid); + memcpy(&iwm->umac_profile->bssid[0], sme->bssid, ETH_ALEN); + iwm->umac_profile->bss_num = 1; + } else { + memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN); + iwm->umac_profile->bss_num = 0; + } + + ret = iwm_set_wpa_version(iwm, sme->crypto.wpa_versions); + if (ret < 0) + return ret; + + ret = iwm_set_auth_type(iwm, sme->auth_type); + if (ret < 0) + return ret; + + if (sme->crypto.n_ciphers_pairwise) { + ret = iwm_set_cipher(iwm, sme->crypto.ciphers_pairwise[0], + true); + if (ret < 0) + return ret; + } + + ret = iwm_set_cipher(iwm, sme->crypto.cipher_group, false); + if (ret < 0) + return ret; + + if (sme->crypto.n_akm_suites) { + ret = iwm_set_key_mgt(iwm, sme->crypto.akm_suites[0]); + if (ret < 0) + return ret; + } + + return iwm_send_mlme_profile(iwm); +} + +static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code) +{ + struct iwm_priv *iwm = wiphy_to_iwm(wiphy); + + IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active); + + if (iwm->umac_profile_active) + return iwm_invalidate_mlme_profile(iwm); + + return 0; +} + +static int iwm_cfg80211_set_txpower(struct wiphy *wiphy, + enum tx_power_setting type, int dbm) +{ + switch (type) { + case TX_POWER_AUTOMATIC: + return 0; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm) +{ + struct iwm_priv *iwm = wiphy_to_iwm(wiphy); + + *dbm = iwm->txpower; + + return 0; +} + +static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, + bool enabled, int timeout) +{ + struct iwm_priv *iwm = wiphy_to_iwm(wiphy); + u32 power_index; + + if (enabled) + power_index = IWM_POWER_INDEX_DEFAULT; + else + power_index = IWM_POWER_INDEX_MIN; + + if (power_index == iwm->conf.power_index) + return 0; + + iwm->conf.power_index = power_index; + + return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, + CFG_POWER_INDEX, iwm->conf.power_index); +} + static struct cfg80211_ops iwm_cfg80211_ops = { .change_virtual_intf = iwm_cfg80211_change_iface, + .add_key = iwm_cfg80211_add_key, + .get_key = iwm_cfg80211_get_key, + .del_key = iwm_cfg80211_del_key, + .set_default_key = iwm_cfg80211_set_default_key, + .get_station = iwm_cfg80211_get_station, .scan = iwm_cfg80211_scan, .set_wiphy_params = iwm_cfg80211_set_wiphy_params, + .connect = iwm_cfg80211_connect, + .disconnect = iwm_cfg80211_disconnect, .join_ibss = iwm_cfg80211_join_ibss, .leave_ibss = iwm_cfg80211_leave_ibss, + .set_tx_power = iwm_cfg80211_set_txpower, + .get_tx_power = iwm_cfg80211_get_txpower, + .set_power_mgmt = iwm_cfg80211_set_power_mgmt, +}; + +static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, }; struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev) @@ -379,6 +740,9 @@ struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev) wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz; wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wdev->wiphy->cipher_suites = cipher_suites; + wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + ret = wiphy_register(wdev->wiphy); if (ret < 0) { dev_err(dev, "Couldn't register wiphy device\n"); diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c index e2334d123599..a68a2aff3c1e 100644 --- a/drivers/net/wireless/iwmc3200wifi/commands.c +++ b/drivers/net/wireless/iwmc3200wifi/commands.c @@ -70,14 +70,27 @@ static int iwm_send_lmac_ptrough_cmd(struct iwm_priv *iwm, int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size, bool resp) { + struct iwm_umac_wifi_if *hdr = (struct iwm_umac_wifi_if *)payload; struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; struct iwm_umac_cmd umac_cmd; + int ret; + u8 oid = hdr->oid; umac_cmd.id = UMAC_CMD_OPCODE_WIFI_IF_WRAPPER; umac_cmd.resp = resp; - return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, - payload, payload_size); + ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, + payload, payload_size); + + if (resp) { + ret = wait_event_interruptible_timeout(iwm->wifi_ntfy_queue, + test_and_clear_bit(oid, &iwm->wifi_ntfy[0]), + 3 * HZ); + + return ret ? 0 : -EBUSY; + } + + return ret; } static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] = @@ -106,7 +119,7 @@ static struct coex_event iwm_sta_cm_prio_tbl[COEX_EVENTS_NUM] = {4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS}, {3, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS}, {5, 5, 0, COEX_CALIBRATION_FLAGS}, - {4, 4, 0, COEX_PERIODIC_CALIBRATION_FLAGS}, + {3, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS}, {5, 4, 0, COEX_CONNECTION_ESTAB_FLAGS}, {4, 4, 0, COEX_ASSOCIATED_IDLE_FLAGS}, {4, 4, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS}, @@ -332,8 +345,7 @@ int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key, return ret; } -int iwm_send_umac_config(struct iwm_priv *iwm, - __le32 reset_flags) +int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags) { int ret; @@ -361,6 +373,12 @@ int iwm_send_umac_config(struct iwm_priv *iwm, return ret; ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, + CFG_WIRELESS_MODE, + iwm->conf.wireless_mode); + if (ret < 0) + return ret; + + ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, CFG_COEX_MODE, iwm->conf.coexist_mode); if (ret < 0) return ret; @@ -402,7 +420,7 @@ int iwm_send_umac_config(struct iwm_priv *iwm, return ret; ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, - CFG_PM_CTRL_FLAGS, 0x30001); + CFG_PM_CTRL_FLAGS, 0x1); if (ret < 0) return ret; @@ -462,8 +480,10 @@ static int iwm_target_read(struct iwm_priv *iwm, __le32 address, target_cmd.eop = 1; ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL); - if (ret < 0) + if (ret < 0) { IWM_ERR(iwm, "Couldn't send READ command\n"); + return ret; + } /* When succeding, the send_target routine returns the seq number */ seq_num = ret; @@ -483,7 +503,7 @@ static int iwm_target_read(struct iwm_priv *iwm, __le32 address, kfree(cmd); - return ret; + return 0; } int iwm_read_mac(struct iwm_priv *iwm, u8 *mac) @@ -493,7 +513,7 @@ int iwm_read_mac(struct iwm_priv *iwm, u8 *mac) ret = iwm_target_read(iwm, cpu_to_le32(WICO_MAC_ADDRESS_ADDR), mac_align, sizeof(mac_align)); - if (ret < 0) + if (ret) return ret; if (is_valid_ether_addr(mac_align)) @@ -507,22 +527,6 @@ int iwm_read_mac(struct iwm_priv *iwm, u8 *mac) return 0; } -int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx) -{ - struct iwm_umac_tx_key_id tx_key_id; - - if (!iwm->default_key || !iwm->default_key->in_use) - return -EINVAL; - - tx_key_id.hdr.oid = UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID; - tx_key_id.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_tx_key_id) - - sizeof(struct iwm_umac_wifi_if)); - - tx_key_id.key_idx = key_idx; - - return iwm_send_wifi_if_cmd(iwm, &tx_key_id, sizeof(tx_key_id), 1); -} - static int iwm_check_profile(struct iwm_priv *iwm) { if (!iwm->umac_profile_active) @@ -556,10 +560,35 @@ static int iwm_check_profile(struct iwm_priv *iwm) return 0; } -int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key, - struct iwm_key *key) +int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx) { + struct iwm_umac_tx_key_id tx_key_id; int ret; + + ret = iwm_check_profile(iwm); + if (ret < 0) + return ret; + + /* UMAC only allows to set default key for WEP and auth type is + * NOT 802.1X or RSNA. */ + if ((iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_40 && + iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_104) || + iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_8021X || + iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_RSNA_PSK) + return 0; + + tx_key_id.hdr.oid = UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID; + tx_key_id.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_tx_key_id) - + sizeof(struct iwm_umac_wifi_if)); + + tx_key_id.key_idx = key_idx; + + return iwm_send_wifi_if_cmd(iwm, &tx_key_id, sizeof(tx_key_id), 1); +} + +int iwm_set_key(struct iwm_priv *iwm, bool remove, struct iwm_key *key) +{ + int ret = 0; u8 cmd[64], *sta_addr, *key_data, key_len; s8 key_idx; u16 cmd_size = 0; @@ -569,15 +598,6 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key, struct iwm_umac_key_tkip *tkip = (struct iwm_umac_key_tkip *)cmd; struct iwm_umac_key_ccmp *ccmp = (struct iwm_umac_key_ccmp *)cmd; - if (set_tx_key) - iwm->default_key = key; - - /* - * We check if our current profile is valid. - * If not, we dont push the key, we just cache them, - * so that with the next siwsessid call, the keys - * will be actually pushed. - */ if (!remove) { ret = iwm_check_profile(iwm); if (ret < 0) @@ -590,8 +610,9 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key, key_idx = key->hdr.key_idx; if (!remove) { - IWM_DBG_WEXT(iwm, DBG, "key_idx:%d set tx key:%d\n", - key_idx, set_tx_key); + u8 auth_type = iwm->umac_profile->sec.auth_type; + + IWM_DBG_WEXT(iwm, DBG, "key_idx:%d\n", key_idx); IWM_DBG_WEXT(iwm, DBG, "key_len:%d\n", key_len); IWM_DBG_WEXT(iwm, DBG, "MAC:%pM, idx:%d, multicast:%d\n", key_hdr->mac, key_hdr->key_idx, key_hdr->multicast); @@ -603,8 +624,8 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key, iwm->umac_profile->sec.auth_type, iwm->umac_profile->sec.flags); - switch (key->alg) { - case UMAC_CIPHER_TYPE_WEP_40: + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: wep40->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP40_KEY; wep40->hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_key_wep40) - @@ -613,12 +634,14 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key, memcpy(&wep40->key_hdr, key_hdr, sizeof(struct iwm_umac_key_hdr)); memcpy(wep40->key, key_data, key_len); - wep40->static_key = 1; + wep40->static_key = + !!((auth_type != UMAC_AUTH_TYPE_8021X) && + (auth_type != UMAC_AUTH_TYPE_RSNA_PSK)); cmd_size = sizeof(struct iwm_umac_key_wep40); break; - case UMAC_CIPHER_TYPE_WEP_104: + case WLAN_CIPHER_SUITE_WEP104: wep104->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP104_KEY; wep104->hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_key_wep104) - @@ -627,12 +650,14 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key, memcpy(&wep104->key_hdr, key_hdr, sizeof(struct iwm_umac_key_hdr)); memcpy(wep104->key, key_data, key_len); - wep104->static_key = 1; + wep104->static_key = + !!((auth_type != UMAC_AUTH_TYPE_8021X) && + (auth_type != UMAC_AUTH_TYPE_RSNA_PSK)); cmd_size = sizeof(struct iwm_umac_key_wep104); break; - case UMAC_CIPHER_TYPE_CCMP: + case WLAN_CIPHER_SUITE_CCMP: key_hdr->key_idx++; ccmp->hdr.oid = UMAC_WIFI_IF_CMD_ADD_CCMP_KEY; ccmp->hdr.buf_size = @@ -644,13 +669,13 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key, memcpy(ccmp->key, key_data, key_len); - if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID) - memcpy(ccmp->iv_count, key->rx_seq, 6); + if (key->seq_len) + memcpy(ccmp->iv_count, key->seq, key->seq_len); cmd_size = sizeof(struct iwm_umac_key_ccmp); break; - case UMAC_CIPHER_TYPE_TKIP: + case WLAN_CIPHER_SUITE_TKIP: key_hdr->key_idx++; tkip->hdr.oid = UMAC_WIFI_IF_CMD_ADD_TKIP_KEY; tkip->hdr.buf_size = @@ -667,8 +692,8 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key, key_data + IWM_TKIP_KEY_SIZE + IWM_TKIP_MIC_SIZE, IWM_TKIP_MIC_SIZE); - if (key->flags & IW_ENCODE_EXT_RX_SEQ_VALID) - memcpy(ccmp->iv_count, key->rx_seq, 6); + if (key->seq_len) + memcpy(ccmp->iv_count, key->seq, key->seq_len); cmd_size = sizeof(struct iwm_umac_key_tkip); break; @@ -677,8 +702,8 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key, return -ENOTSUPP; } - if ((key->alg == UMAC_CIPHER_TYPE_CCMP) || - (key->alg == UMAC_CIPHER_TYPE_TKIP)) + if ((key->cipher == WLAN_CIPHER_SUITE_TKIP) || + (key->cipher == WLAN_CIPHER_SUITE_CCMP)) /* * UGLY_UGLY_UGLY * Copied HACK from the MWG driver. @@ -689,23 +714,11 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key, schedule_timeout_interruptible(usecs_to_jiffies(300)); ret = iwm_send_wifi_if_cmd(iwm, cmd, cmd_size, 1); - if (ret < 0) - goto err; - - /* - * We need a default key only if it is set and - * if we're doing WEP. - */ - if (iwm->default_key == key && - ((key->alg == UMAC_CIPHER_TYPE_WEP_40) || - (key->alg == UMAC_CIPHER_TYPE_WEP_104))) { - ret = iwm_set_tx_key(iwm, key_idx); - if (ret < 0) - goto err; - } } else { struct iwm_umac_key_remove key_remove; + IWM_DBG_WEXT(iwm, ERR, "Removing key_idx:%d\n", key_idx); + key_remove.hdr.oid = UMAC_WIFI_IF_CMD_REMOVE_KEY; key_remove.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_key_remove) - @@ -716,23 +729,19 @@ int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key, ret = iwm_send_wifi_if_cmd(iwm, &key_remove, sizeof(struct iwm_umac_key_remove), 1); - if (ret < 0) + if (ret) return ret; - iwm->keys[key_idx].in_use = 0; + iwm->keys[key_idx].key_len = 0; } - return 0; - - err: - kfree(key); return ret; } int iwm_send_mlme_profile(struct iwm_priv *iwm) { - int ret, i; + int ret; struct iwm_umac_profile profile; memcpy(&profile, iwm->umac_profile, sizeof(profile)); @@ -742,45 +751,18 @@ int iwm_send_mlme_profile(struct iwm_priv *iwm) sizeof(struct iwm_umac_wifi_if)); ret = iwm_send_wifi_if_cmd(iwm, &profile, sizeof(profile), 1); - if (ret < 0) { + if (ret) { IWM_ERR(iwm, "Send profile command failed\n"); return ret; } - /* Wait for the profile to be active */ - ret = wait_event_interruptible_timeout(iwm->mlme_queue, - iwm->umac_profile_active == 1, - 3 * HZ); - if (!ret) - return -EBUSY; - - - for (i = 0; i < IWM_NUM_KEYS; i++) - if (iwm->keys[i].in_use) { - int default_key = 0; - struct iwm_key *key = &iwm->keys[i]; - - if (key == iwm->default_key) - default_key = 1; - - /* Wait for the profile before sending the keys */ - wait_event_interruptible_timeout(iwm->mlme_queue, - (test_bit(IWM_STATUS_ASSOCIATING, &iwm->status) || - test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)), - 3 * HZ); - - ret = iwm_set_key(iwm, 0, default_key, key); - if (ret < 0) - return ret; - } - return 0; } int iwm_invalidate_mlme_profile(struct iwm_priv *iwm) { - int ret; struct iwm_umac_invalidate_profile invalid; + int ret; invalid.hdr.oid = UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE; invalid.hdr.buf_size = @@ -790,16 +772,13 @@ int iwm_invalidate_mlme_profile(struct iwm_priv *iwm) invalid.reason = WLAN_REASON_UNSPECIFIED; ret = iwm_send_wifi_if_cmd(iwm, &invalid, sizeof(invalid), 1); - if (ret < 0) + if (ret) return ret; ret = wait_event_interruptible_timeout(iwm->mlme_queue, - (iwm->umac_profile_active == 0), - 2 * HZ); - if (!ret) - return -EBUSY; + (iwm->umac_profile_active == 0), 2 * HZ); - return 0; + return ret ? 0 : -EBUSY; } int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags) @@ -882,7 +861,7 @@ int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, } ret = iwm_send_wifi_if_cmd(iwm, &req, sizeof(req), 0); - if (ret < 0) { + if (ret) { IWM_ERR(iwm, "Couldn't send scan request\n"); return ret; } diff --git a/drivers/net/wireless/iwmc3200wifi/commands.h b/drivers/net/wireless/iwmc3200wifi/commands.h index 36b13a130595..e24d5b633997 100644 --- a/drivers/net/wireless/iwmc3200wifi/commands.h +++ b/drivers/net/wireless/iwmc3200wifi/commands.h @@ -106,8 +106,7 @@ enum { CFG_TLC_SPATIAL_STREAM_SUPPORTED, CFG_TLC_RETRY_PER_RATE, CFG_TLC_RETRY_PER_HT_RATE, - CFG_TLC_FIXED_RATE, - CFG_TLC_FIXED_RATE_FLAGS, + CFG_TLC_FIXED_MCS, CFG_TLC_CONTROL_FLAGS, CFG_TLC_SR_MIN_FAIL, CFG_TLC_SR_MIN_PASS, @@ -232,6 +231,7 @@ struct iwm_umac_cmd_get_channel_list { /* Wireless mode */ #define WIRELESS_MODE_11A 0x1 #define WIRELESS_MODE_11G 0x2 +#define WIRELESS_MODE_11N 0x4 #define UMAC_PROFILE_EX_IE_REQUIRED 0x1 #define UMAC_PROFILE_QOS_ALLOWED 0x2 @@ -406,8 +406,7 @@ int iwm_send_mlme_profile(struct iwm_priv *iwm); int iwm_invalidate_mlme_profile(struct iwm_priv *iwm); int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id); int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx); -int iwm_set_key(struct iwm_priv *iwm, bool remove, bool set_tx_key, - struct iwm_key *key); +int iwm_set_key(struct iwm_priv *iwm, bool remove, struct iwm_key *key); int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags); int iwm_send_umac_channel_list(struct iwm_priv *iwm); int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, diff --git a/drivers/net/wireless/iwmc3200wifi/eeprom.c b/drivers/net/wireless/iwmc3200wifi/eeprom.c index 0f34b84fd2eb..365910fbe01e 100644 --- a/drivers/net/wireless/iwmc3200wifi/eeprom.c +++ b/drivers/net/wireless/iwmc3200wifi/eeprom.c @@ -156,10 +156,6 @@ int iwm_eeprom_init(struct iwm_priv *iwm) return -ENOMEM; for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) { -#ifdef CONFIG_IWM_B0_HW_SUPPORT - if (iwm->conf.hw_b0 && (i >= IWM_EEPROM_INDIRECT_OFFSET)) - break; -#endif ret = iwm_eeprom_read(iwm, i); if (ret < 0) { IWM_ERR(iwm, "Couldn't read eeprom entry #%d: %s\n", diff --git a/drivers/net/wireless/iwmc3200wifi/fw.c b/drivers/net/wireless/iwmc3200wifi/fw.c index ec1a15a5a0e4..0f32cab9ced4 100644 --- a/drivers/net/wireless/iwmc3200wifi/fw.c +++ b/drivers/net/wireless/iwmc3200wifi/fw.c @@ -275,6 +275,7 @@ static int iwm_load_lmac(struct iwm_priv *iwm, const char *img_name) */ int iwm_load_fw(struct iwm_priv *iwm) { + unsigned long init_calib_map, periodic_calib_map; int ret; /* We first start downloading the UMAC */ @@ -315,23 +316,19 @@ int iwm_load_fw(struct iwm_priv *iwm) return ret; } -#ifdef CONFIG_IWM_B0_HW_SUPPORT - if (iwm->conf.hw_b0) { - clear_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->conf.init_calib_map); - clear_bit(PHY_CALIBRATE_RX_IQ_CMD, - &iwm->conf.periodic_calib_map); - } -#endif + init_calib_map = iwm->conf.calib_map & IWM_CALIB_MAP_INIT_MSK; + periodic_calib_map = IWM_CALIB_MAP_PER_LMAC(iwm->conf.calib_map); + /* Read RX IQ calibration result from EEPROM */ - if (test_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->conf.init_calib_map)) { + if (test_bit(PHY_CALIBRATE_RX_IQ_CMD, &init_calib_map)) { iwm_store_rxiq_calib_result(iwm); set_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->calib_done_map); } iwm_send_prio_table(iwm); - iwm_send_init_calib_cfg(iwm, iwm->conf.init_calib_map); + iwm_send_init_calib_cfg(iwm, init_calib_map); - while (iwm->calib_done_map != iwm->conf.init_calib_map) { + while (iwm->calib_done_map != init_calib_map) { ret = iwm_notif_handle(iwm, CALIBRATION_RES_NOTIFICATION, IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT); if (ret) { @@ -340,7 +337,7 @@ int iwm_load_fw(struct iwm_priv *iwm) } IWM_DBG_FW(iwm, DBG, "Got calibration result. calib_done_map: " "0x%lx, requested calibrations: 0x%lx\n", - iwm->calib_done_map, iwm->conf.init_calib_map); + iwm->calib_done_map, init_calib_map); } /* Handle LMAC CALIBRATION_COMPLETE notification */ @@ -378,7 +375,7 @@ int iwm_load_fw(struct iwm_priv *iwm) iwm_send_prio_table(iwm); iwm_send_calib_results(iwm); - iwm_send_periodic_calib_cfg(iwm, iwm->conf.periodic_calib_map); + iwm_send_periodic_calib_cfg(iwm, periodic_calib_map); return 0; diff --git a/drivers/net/wireless/iwmc3200wifi/hal.c b/drivers/net/wireless/iwmc3200wifi/hal.c index ee127fe4f43f..c430418248b4 100644 --- a/drivers/net/wireless/iwmc3200wifi/hal.c +++ b/drivers/net/wireless/iwmc3200wifi/hal.c @@ -105,9 +105,9 @@ #include "umac.h" #include "debug.h" -static void iwm_nonwifi_cmd_init(struct iwm_priv *iwm, - struct iwm_nonwifi_cmd *cmd, - struct iwm_udma_nonwifi_cmd *udma_cmd) +static int iwm_nonwifi_cmd_init(struct iwm_priv *iwm, + struct iwm_nonwifi_cmd *cmd, + struct iwm_udma_nonwifi_cmd *udma_cmd) { INIT_LIST_HEAD(&cmd->pending); @@ -118,7 +118,7 @@ static void iwm_nonwifi_cmd_init(struct iwm_priv *iwm, cmd->seq_num = iwm->nonwifi_seq_num; udma_cmd->seq_num = cpu_to_le16(cmd->seq_num); - cmd->seq_num = iwm->nonwifi_seq_num++; + iwm->nonwifi_seq_num++; iwm->nonwifi_seq_num %= UMAC_NONWIFI_SEQ_NUM_MAX; if (udma_cmd->resp) @@ -130,6 +130,8 @@ static void iwm_nonwifi_cmd_init(struct iwm_priv *iwm, cmd->buf.len = 0; memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd)); + + return cmd->seq_num; } u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm) @@ -369,7 +371,7 @@ int iwm_hal_send_target_cmd(struct iwm_priv *iwm, const void *payload) { struct iwm_nonwifi_cmd *cmd; - int ret; + int ret, seq_num; cmd = kzalloc(sizeof(struct iwm_nonwifi_cmd), GFP_KERNEL); if (!cmd) { @@ -377,7 +379,7 @@ int iwm_hal_send_target_cmd(struct iwm_priv *iwm, return -ENOMEM; } - iwm_nonwifi_cmd_init(iwm, cmd, udma_cmd); + seq_num = iwm_nonwifi_cmd_init(iwm, cmd, udma_cmd); if (cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE || cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT) { @@ -393,7 +395,7 @@ int iwm_hal_send_target_cmd(struct iwm_priv *iwm, if (ret < 0) return ret; - return cmd->seq_num; + return seq_num; } static void iwm_build_lmac_hdr(struct iwm_priv *iwm, struct iwm_lmac_hdr *hdr, diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h index 77c339f8516c..7a51bc340fda 100644 --- a/drivers/net/wireless/iwmc3200wifi/iwm.h +++ b/drivers/net/wireless/iwmc3200wifi/iwm.h @@ -52,8 +52,6 @@ #define IWM_COPYRIGHT "Copyright(c) 2009 Intel Corporation" #define IWM_AUTHOR "<ilw@linux.intel.com>" -#define CONFIG_IWM_B0_HW_SUPPORT 1 - #define IWM_SRC_LMAC UMAC_HDI_IN_SOURCE_FHRX #define IWM_SRC_UDMA UMAC_HDI_IN_SOURCE_UDMA #define IWM_SRC_UMAC UMAC_HDI_IN_SOURCE_FW @@ -65,8 +63,7 @@ struct iwm_conf { u32 sdio_ior_timeout; - unsigned long init_calib_map; - unsigned long periodic_calib_map; + unsigned long calib_map; bool reset_on_fatal_err; bool auto_connect; bool wimax_not_present; @@ -87,9 +84,6 @@ struct iwm_conf { u8 ibss_channel; u8 mac_addr[ETH_ALEN]; -#ifdef CONFIG_IWM_B0_HW_SUPPORT - bool hw_b0; -#endif }; enum { @@ -162,13 +156,11 @@ struct iwm_umac_key_hdr { struct iwm_key { struct iwm_umac_key_hdr hdr; - u8 in_use; - u8 alg; - u32 flags; - u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; - u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; - u8 key_len; - u8 key[32]; + u32 cipher; + u8 key[WLAN_MAX_KEY_LEN]; + u8 seq[IW_ENCODE_SEQ_MAX_SIZE]; + int key_len; + int seq_len; }; #define IWM_RX_ID_HASH 0xff @@ -186,10 +178,6 @@ struct iwm_key { #define IWM_STATUS_ASSOCIATING 3 #define IWM_STATUS_ASSOCIATED 4 -#define IWM_RADIO_RFKILL_OFF 0 -#define IWM_RADIO_RFKILL_HW 1 -#define IWM_RADIO_RFKILL_SW 2 - struct iwm_tx_queue { int id; struct sk_buff_head queue; @@ -223,7 +211,6 @@ struct iwm_priv { struct iwm_conf conf; unsigned long status; - unsigned long radio; struct list_head pending_notif; wait_queue_head_t notif_queue; @@ -242,6 +229,7 @@ struct iwm_priv { u8 bssid[ETH_ALEN]; u8 channel; u16 rate; + u32 txpower; struct iwm_sta_info sta_table[IWM_STA_TABLE_NUM]; struct list_head bss_list; @@ -276,7 +264,10 @@ struct iwm_priv { struct iwm_tx_queue txq[IWM_TX_QUEUES]; struct iwm_key keys[IWM_NUM_KEYS]; - struct iwm_key *default_key; + s8 default_key; + + DECLARE_BITMAP(wifi_ntfy, WIFI_IF_NTFY_MAX); + wait_queue_head_t wifi_ntfy_queue; wait_queue_head_t mlme_queue; @@ -289,7 +280,11 @@ struct iwm_priv { struct timer_list watchdog; struct work_struct reset_worker; struct mutex mutex; - struct rfkill *rfkill; + + u8 *req_ie; + int req_ie_len; + u8 *resp_ie; + int resp_ie_len; char private[0] __attribute__((__aligned__(NETDEV_ALIGN))); }; @@ -311,8 +306,6 @@ static inline void *iwm_private(struct iwm_priv *iwm) #define skb_to_rx_info(s) ((struct iwm_rx_info *)(s->cb)) #define skb_to_tx_info(s) ((struct iwm_tx_info *)s->cb) -extern const struct iw_handler_def iwm_iw_handler_def; - void *iwm_if_alloc(int sizeof_bus, struct device *dev, struct iwm_if_ops *if_ops); void iwm_if_free(struct iwm_priv *iwm); diff --git a/drivers/net/wireless/iwmc3200wifi/lmac.h b/drivers/net/wireless/iwmc3200wifi/lmac.h index db2e5eea1895..19213e165f5f 100644 --- a/drivers/net/wireless/iwmc3200wifi/lmac.h +++ b/drivers/net/wireless/iwmc3200wifi/lmac.h @@ -396,6 +396,10 @@ enum { CALIBRATION_CMD_NUM, }; +#define IWM_CALIB_MAP_INIT_MSK 0xFFFF +#define IWM_CALIB_MAP_PER_LMAC(m) ((m & 0xFF0000) >> 16) +#define IWM_CALIB_MAP_PER_UMAC(m) ((m & 0xFF000000) >> 24) + struct iwm_lmac_calib_hdr { u8 opcode; u8 first_grp; diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c index 8be206d58222..cf2574442b57 100644 --- a/drivers/net/wireless/iwmc3200wifi/main.c +++ b/drivers/net/wireless/iwmc3200wifi/main.c @@ -53,11 +53,7 @@ static struct iwm_conf def_iwm_conf = { .sdio_ior_timeout = 5000, - .init_calib_map = BIT(PHY_CALIBRATE_DC_CMD) | - BIT(PHY_CALIBRATE_LO_CMD) | - BIT(PHY_CALIBRATE_TX_IQ_CMD) | - BIT(PHY_CALIBRATE_RX_IQ_CMD), - .periodic_calib_map = BIT(PHY_CALIBRATE_DC_CMD) | + .calib_map = BIT(PHY_CALIBRATE_DC_CMD) | BIT(PHY_CALIBRATE_LO_CMD) | BIT(PHY_CALIBRATE_TX_IQ_CMD) | BIT(PHY_CALIBRATE_RX_IQ_CMD) | @@ -191,6 +187,7 @@ int iwm_priv_init(struct iwm_priv *iwm) INIT_LIST_HEAD(&iwm->pending_notif); init_waitqueue_head(&iwm->notif_queue); init_waitqueue_head(&iwm->nonwifi_queue); + init_waitqueue_head(&iwm->wifi_ntfy_queue); init_waitqueue_head(&iwm->mlme_queue); memcpy(&iwm->conf, &def_iwm_conf, sizeof(struct iwm_conf)); spin_lock_init(&iwm->tx_credit.lock); @@ -229,7 +226,7 @@ int iwm_priv_init(struct iwm_priv *iwm) for (i = 0; i < IWM_NUM_KEYS; i++) memset(&iwm->keys[i], 0, sizeof(struct iwm_key)); - iwm->default_key = NULL; + iwm->default_key = -1; init_timer(&iwm->watchdog); iwm->watchdog.function = iwm_watchdog; @@ -500,6 +497,13 @@ void iwm_link_off(struct iwm_priv *iwm) memset(wstats, 0, sizeof(struct iw_statistics)); wstats->qual.updated = IW_QUAL_ALL_INVALID; + kfree(iwm->req_ie); + iwm->req_ie = NULL; + iwm->req_ie_len = 0; + kfree(iwm->resp_ie); + iwm->resp_ie = NULL; + iwm->resp_ie_len = 0; + del_timer_sync(&iwm->watchdog); } @@ -518,13 +522,6 @@ static int iwm_channels_init(struct iwm_priv *iwm) { int ret; -#ifdef CONFIG_IWM_B0_HW_SUPPORT - if (iwm->conf.hw_b0) { - IWM_INFO(iwm, "Workaround EEPROM channels for B0 hardware\n"); - return 0; - } -#endif - ret = iwm_send_umac_channel_list(iwm); if (ret) { IWM_ERR(iwm, "Send channel list failed\n"); @@ -642,19 +639,10 @@ int __iwm_up(struct iwm_priv *iwm) } } - iwm->umac_profile = kmalloc(sizeof(struct iwm_umac_profile), - GFP_KERNEL); - if (!iwm->umac_profile) { - IWM_ERR(iwm, "Couldn't alloc memory for profile\n"); - goto err_fw; - } - - iwm_init_default_profile(iwm, iwm->umac_profile); - ret = iwm_channels_init(iwm); if (ret < 0) { IWM_ERR(iwm, "Couldn't init channels\n"); - goto err_profile; + goto err_fw; } /* Set the READY bit to indicate interface is brought up successfully */ @@ -662,10 +650,6 @@ int __iwm_up(struct iwm_priv *iwm) return 0; - err_profile: - kfree(iwm->umac_profile); - iwm->umac_profile = NULL; - err_fw: iwm_eeprom_exit(iwm); @@ -704,11 +688,10 @@ int __iwm_down(struct iwm_priv *iwm) clear_bit(IWM_STATUS_READY, &iwm->status); iwm_eeprom_exit(iwm); - kfree(iwm->umac_profile); - iwm->umac_profile = NULL; iwm_bss_list_clean(iwm); - - iwm->default_key = NULL; + iwm_init_default_profile(iwm, iwm->umac_profile); + iwm->umac_profile_active = false; + iwm->default_key = -1; iwm->core_enabled = 0; ret = iwm_bus_disable(iwm); diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c index bf294e41753b..35ec006c2d2c 100644 --- a/drivers/net/wireless/iwmc3200wifi/netdev.c +++ b/drivers/net/wireless/iwmc3200wifi/netdev.c @@ -48,29 +48,22 @@ #include <linux/netdevice.h> #include "iwm.h" +#include "commands.h" #include "cfg80211.h" #include "debug.h" static int iwm_open(struct net_device *ndev) { struct iwm_priv *iwm = ndev_to_iwm(ndev); - int ret = 0; - - if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio)) - ret = iwm_up(iwm); - return ret; + return iwm_up(iwm); } static int iwm_stop(struct net_device *ndev) { struct iwm_priv *iwm = ndev_to_iwm(ndev); - int ret = 0; - - if (!test_bit(IWM_RADIO_RFKILL_SW, &iwm->radio)) - ret = iwm_down(iwm); - return ret; + return iwm_down(iwm); } /* @@ -128,13 +121,24 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev, } ndev->netdev_ops = &iwm_netdev_ops; - ndev->wireless_handlers = &iwm_iw_handler_def; ndev->ieee80211_ptr = wdev; SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); wdev->netdev = ndev; + iwm->umac_profile = kmalloc(sizeof(struct iwm_umac_profile), + GFP_KERNEL); + if (!iwm->umac_profile) { + dev_err(dev, "Couldn't alloc memory for profile\n"); + goto out_profile; + } + + iwm_init_default_profile(iwm, iwm->umac_profile); + return iwm; + out_profile: + free_netdev(ndev); + out_priv: iwm_priv_deinit(iwm); @@ -150,6 +154,8 @@ void iwm_if_free(struct iwm_priv *iwm) free_netdev(iwm_to_ndev(iwm)); iwm_priv_deinit(iwm); + kfree(iwm->umac_profile); + iwm->umac_profile = NULL; iwm_wdev_free(iwm); } diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c index d73cf96c6dc6..86079a187eef 100644 --- a/drivers/net/wireless/iwmc3200wifi/rx.c +++ b/drivers/net/wireless/iwmc3200wifi/rx.c @@ -102,7 +102,6 @@ static int iwm_ntf_error(struct iwm_priv *iwm, u8 *buf, error = (struct iwm_umac_notif_error *)buf; fw_err = &error->err; - IWM_ERR(iwm, "%cMAC FW ERROR:\n", (le32_to_cpu(fw_err->category) == UMAC_SYS_ERR_CAT_LMAC) ? 'L' : 'U'); IWM_ERR(iwm, "\tCategory: %d\n", le32_to_cpu(fw_err->category)); @@ -143,17 +142,18 @@ static int iwm_ntf_init_complete(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size, struct iwm_wifi_cmd *cmd) { + struct wiphy *wiphy = iwm_to_wiphy(iwm); struct iwm_umac_notif_init_complete *init_complete = (struct iwm_umac_notif_init_complete *)(buf); u16 status = le16_to_cpu(init_complete->status); + bool blocked = (status == UMAC_NTFY_INIT_COMPLETE_STATUS_ERR); - if (status == UMAC_NTFY_INIT_COMPLETE_STATUS_ERR) { + if (blocked) IWM_DBG_NTF(iwm, DBG, "Hardware rf kill is on (radio off)\n"); - set_bit(IWM_RADIO_RFKILL_HW, &iwm->radio); - } else { + else IWM_DBG_NTF(iwm, DBG, "Hardware rf kill is off (radio on)\n"); - clear_bit(IWM_RADIO_RFKILL_HW, &iwm->radio); - } + + wiphy_rfkill_set_hw_state(wiphy, blocked); return 0; } @@ -218,17 +218,17 @@ static int iwm_ntf_tx(struct iwm_priv *iwm, u8 *buf, (buf + sizeof(struct iwm_umac_wifi_in_hdr)); hdr = (struct iwm_umac_wifi_in_hdr *)buf; - IWM_DBG_NTF(iwm, DBG, "REPLY_TX, buf size: %lu\n", buf_size); + IWM_DBG_TX(iwm, DBG, "REPLY_TX, buf size: %lu\n", buf_size); - IWM_DBG_NTF(iwm, DBG, "Seqnum: %d\n", - le16_to_cpu(hdr->sw_hdr.cmd.seq_num)); - IWM_DBG_NTF(iwm, DBG, "\tFrame cnt: %d\n", tx_resp->frame_cnt); - IWM_DBG_NTF(iwm, DBG, "\tRetry cnt: %d\n", - le16_to_cpu(tx_resp->retry_cnt)); - IWM_DBG_NTF(iwm, DBG, "\tSeq ctl: %d\n", le16_to_cpu(tx_resp->seq_ctl)); - IWM_DBG_NTF(iwm, DBG, "\tByte cnt: %d\n", - le16_to_cpu(tx_resp->byte_cnt)); - IWM_DBG_NTF(iwm, DBG, "\tStatus: 0x%x\n", le32_to_cpu(tx_resp->status)); + IWM_DBG_TX(iwm, DBG, "Seqnum: %d\n", + le16_to_cpu(hdr->sw_hdr.cmd.seq_num)); + IWM_DBG_TX(iwm, DBG, "\tFrame cnt: %d\n", tx_resp->frame_cnt); + IWM_DBG_TX(iwm, DBG, "\tRetry cnt: %d\n", + le16_to_cpu(tx_resp->retry_cnt)); + IWM_DBG_TX(iwm, DBG, "\tSeq ctl: %d\n", le16_to_cpu(tx_resp->seq_ctl)); + IWM_DBG_TX(iwm, DBG, "\tByte cnt: %d\n", + le16_to_cpu(tx_resp->byte_cnt)); + IWM_DBG_TX(iwm, DBG, "\tStatus: 0x%x\n", le32_to_cpu(tx_resp->status)); return 0; } @@ -418,8 +418,8 @@ static int iwm_ntf_rx_ticket(struct iwm_priv *iwm, u8 *buf, if (IS_ERR(ticket_node)) return PTR_ERR(ticket_node); - IWM_DBG_NTF(iwm, DBG, "TICKET RELEASE(%d)\n", - ticket->id); + IWM_DBG_RX(iwm, DBG, "TICKET RELEASE(%d)\n", + ticket->id); list_add_tail(&ticket_node->node, &iwm->rx_tickets); /* @@ -454,15 +454,15 @@ static int iwm_ntf_rx_packet(struct iwm_priv *iwm, u8 *buf, u16 id, buf_offset; u32 packet_size; - IWM_DBG_NTF(iwm, DBG, "\n"); + IWM_DBG_RX(iwm, DBG, "\n"); wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf; id = le16_to_cpu(wifi_hdr->sw_hdr.cmd.seq_num); buf_offset = sizeof(struct iwm_umac_wifi_in_hdr); packet_size = buf_size - sizeof(struct iwm_umac_wifi_in_hdr); - IWM_DBG_NTF(iwm, DBG, "CMD:0x%x, seqnum: %d, packet size: %d\n", - wifi_hdr->sw_hdr.cmd.cmd, id, packet_size); + IWM_DBG_RX(iwm, DBG, "CMD:0x%x, seqnum: %d, packet size: %d\n", + wifi_hdr->sw_hdr.cmd.cmd, id, packet_size); IWM_DBG_RX(iwm, DBG, "Packet id: %d\n", id); IWM_HEXDUMP(iwm, DBG, RX, "PACKET: ", buf + buf_offset, packet_size); @@ -503,13 +503,10 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf, { struct iwm_umac_notif_assoc_complete *complete = (struct iwm_umac_notif_assoc_complete *)buf; - union iwreq_data wrqu; IWM_DBG_MLME(iwm, INFO, "Association with %pM completed, status: %d\n", complete->bssid, complete->status); - memset(&wrqu, 0, sizeof(wrqu)); - clear_bit(IWM_STATUS_ASSOCIATING, &iwm->status); switch (le32_to_cpu(complete->status)) { @@ -520,7 +517,14 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf, iwm_link_on(iwm); - memcpy(wrqu.ap_addr.sa_data, complete->bssid, ETH_ALEN); + if (iwm->conf.mode == UMAC_MODE_IBSS) + goto ibss; + + cfg80211_connect_result(iwm_to_ndev(iwm), + complete->bssid, + iwm->req_ie, iwm->req_ie_len, + iwm->resp_ie, iwm->resp_ie_len, + WLAN_STATUS_SUCCESS, GFP_KERNEL); break; case UMAC_ASSOC_COMPLETE_FAILURE: clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status); @@ -528,18 +532,22 @@ static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf, iwm->channel = 0; iwm_link_off(iwm); + + if (iwm->conf.mode == UMAC_MODE_IBSS) + goto ibss; + + cfg80211_connect_result(iwm_to_ndev(iwm), complete->bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); default: break; } - if (iwm->conf.mode == UMAC_MODE_IBSS) { - cfg80211_ibss_joined(iwm_to_ndev(iwm), iwm->bssid, GFP_KERNEL); - return 0; - } - - wrqu.ap_addr.sa_family = ARPHRD_ETHER; - wireless_send_event(iwm_to_ndev(iwm), SIOCGIWAP, &wrqu, NULL); + return 0; + ibss: + cfg80211_ibss_joined(iwm_to_ndev(iwm), iwm->bssid, GFP_KERNEL); return 0; } @@ -769,37 +777,46 @@ static int iwm_mlme_mgt_frame(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size, struct iwm_wifi_cmd *cmd) { struct iwm_umac_notif_mgt_frame *mgt_frame = - (struct iwm_umac_notif_mgt_frame *)buf; + (struct iwm_umac_notif_mgt_frame *)buf; struct ieee80211_mgmt *mgt = (struct ieee80211_mgmt *)mgt_frame->frame; u8 *ie; - unsigned int event; - union iwreq_data wrqu; IWM_HEXDUMP(iwm, DBG, MLME, "MGT: ", mgt_frame->frame, le16_to_cpu(mgt_frame->len)); if (ieee80211_is_assoc_req(mgt->frame_control)) { ie = mgt->u.assoc_req.variable;; - event = IWEVASSOCREQIE; + iwm->req_ie_len = + le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt); + kfree(iwm->req_ie); + iwm->req_ie = kmemdup(mgt->u.assoc_req.variable, + iwm->req_ie_len, GFP_KERNEL); } else if (ieee80211_is_reassoc_req(mgt->frame_control)) { ie = mgt->u.reassoc_req.variable;; - event = IWEVASSOCREQIE; + iwm->req_ie_len = + le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt); + kfree(iwm->req_ie); + iwm->req_ie = kmemdup(mgt->u.reassoc_req.variable, + iwm->req_ie_len, GFP_KERNEL); } else if (ieee80211_is_assoc_resp(mgt->frame_control)) { ie = mgt->u.assoc_resp.variable;; - event = IWEVASSOCRESPIE; + iwm->resp_ie_len = + le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt); + kfree(iwm->resp_ie); + iwm->resp_ie = kmemdup(mgt->u.assoc_resp.variable, + iwm->resp_ie_len, GFP_KERNEL); } else if (ieee80211_is_reassoc_resp(mgt->frame_control)) { ie = mgt->u.reassoc_resp.variable;; - event = IWEVASSOCRESPIE; + iwm->resp_ie_len = + le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt); + kfree(iwm->resp_ie); + iwm->resp_ie = kmemdup(mgt->u.reassoc_resp.variable, + iwm->resp_ie_len, GFP_KERNEL); } else { IWM_ERR(iwm, "Unsupported management frame"); return 0; } - wrqu.data.length = le16_to_cpu(mgt_frame->len) - (ie - (u8 *)mgt); - - IWM_HEXDUMP(iwm, DBG, MLME, "EVT: ", ie, wrqu.data.length); - wireless_send_event(iwm_to_ndev(iwm), event, &wrqu, ie); - return 0; } @@ -875,6 +892,7 @@ static int iwm_ntf_statistics(struct iwm_priv *iwm, u8 *buf, /* UMAC passes rate info multiplies by 2 */ iwm->rate = max_rate >> 1; } + iwm->txpower = le32_to_cpu(stats->tx_power); wstats->status = 0; @@ -922,13 +940,6 @@ static int iwm_ntf_eeprom_proxy(struct iwm_priv *iwm, u8 *buf, if ((hdr_offset + hdr_len) > IWM_EEPROM_LEN) return -EINVAL; -#ifdef CONFIG_IWM_B0_HW_SUPPORT - if (hdr_offset == IWM_EEPROM_SKU_CAP_OFF) { - if (eeprom_proxy->buf[0] == 0xff) - iwm->conf.hw_b0 = 1; - } -#endif - switch (hdr_type) { case IWM_UMAC_CMD_EEPROM_TYPE_READ: memcpy(iwm->eeprom + hdr_offset, eeprom_proxy->buf, hdr_len); @@ -993,12 +1004,17 @@ static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf, (struct iwm_umac_wifi_if *)cmd->buf.payload; IWM_DBG_NTF(iwm, DBG, "WIFI_IF_WRAPPER cmd is delivered to UMAC: " - "oid is %d\n", hdr->oid); + "oid is 0x%x\n", hdr->oid); + + if (hdr->oid <= WIFI_IF_NTFY_MAX) { + set_bit(hdr->oid, &iwm->wifi_ntfy[0]); + wake_up_interruptible(&iwm->wifi_ntfy_queue); + } else + return -EINVAL; switch (hdr->oid) { case UMAC_WIFI_IF_CMD_SET_PROFILE: iwm->umac_profile_active = 1; - wake_up_interruptible(&iwm->mlme_queue); break; default: break; @@ -1010,6 +1026,7 @@ static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf, static int iwm_ntf_card_state(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size, struct iwm_wifi_cmd *cmd) { + struct wiphy *wiphy = iwm_to_wiphy(iwm); struct iwm_lmac_card_state *state = (struct iwm_lmac_card_state *) (buf + sizeof(struct iwm_umac_wifi_in_hdr)); u32 flags = le32_to_cpu(state->flags); @@ -1018,10 +1035,7 @@ static int iwm_ntf_card_state(struct iwm_priv *iwm, u8 *buf, flags & IWM_CARD_STATE_HW_DISABLED ? "ON" : "OFF", flags & IWM_CARD_STATE_CTKILL_DISABLED ? "ON" : "OFF"); - if (flags & IWM_CARD_STATE_HW_DISABLED) - set_bit(IWM_RADIO_RFKILL_HW, &iwm->radio); - else - clear_bit(IWM_RADIO_RFKILL_HW, &iwm->radio); + wiphy_rfkill_set_hw_state(wiphy, flags & IWM_CARD_STATE_HW_DISABLED); return 0; } @@ -1362,13 +1376,13 @@ static void iwm_rx_process_packet(struct iwm_priv *iwm, skb->dev = iwm_to_ndev(iwm); skb->protocol = eth_type_trans(skb, ndev); - skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->ip_summed = CHECKSUM_NONE; memset(skb->cb, 0, sizeof(skb->cb)); ndev->stats.rx_packets++; ndev->stats.rx_bytes += skb->len; - if (netif_rx(skb) == NET_RX_DROP) { + if (netif_rx_ni(skb) == NET_RX_DROP) { IWM_ERR(iwm, "Packet dropped\n"); ndev->stats.rx_dropped++; } diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c index 916681837fd2..8b1de84003ca 100644 --- a/drivers/net/wireless/iwmc3200wifi/sdio.c +++ b/drivers/net/wireless/iwmc3200wifi/sdio.c @@ -65,6 +65,7 @@ #include <linux/kernel.h> #include <linux/netdevice.h> #include <linux/debugfs.h> +#include <linux/mmc/sdio_ids.h> #include <linux/mmc/sdio.h> #include <linux/mmc/sdio_func.h> @@ -492,7 +493,8 @@ static void iwm_sdio_remove(struct sdio_func *func) } static const struct sdio_device_id iwm_sdio_ids[] = { - { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, SDIO_DEVICE_ID_IWM) }, + { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, + SDIO_DEVICE_ID_INTEL_IWMC3200WIFI) }, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, iwm_sdio_ids); @@ -506,11 +508,7 @@ static struct sdio_driver iwm_sdio_driver = { static int __init iwm_sdio_init_module(void) { - int ret; - - ret = sdio_register_driver(&iwm_sdio_driver); - - return ret; + return sdio_register_driver(&iwm_sdio_driver); } static void __exit iwm_sdio_exit_module(void) diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.h b/drivers/net/wireless/iwmc3200wifi/sdio.h index b3c156b08dda..aab6b6892e45 100644 --- a/drivers/net/wireless/iwmc3200wifi/sdio.h +++ b/drivers/net/wireless/iwmc3200wifi/sdio.h @@ -39,9 +39,6 @@ #ifndef __IWM_SDIO_H__ #define __IWM_SDIO_H__ -#define SDIO_VENDOR_ID_INTEL 0x89 -#define SDIO_DEVICE_ID_IWM 0x1403 - #define IWM_SDIO_DATA_ADDR 0x0 #define IWM_SDIO_INTR_ENABLE_ADDR 0x14 #define IWM_SDIO_INTR_STATUS_ADDR 0x13 diff --git a/drivers/net/wireless/iwmc3200wifi/umac.h b/drivers/net/wireless/iwmc3200wifi/umac.h index 4a95cce1f0a6..c5a14ae3160a 100644 --- a/drivers/net/wireless/iwmc3200wifi/umac.h +++ b/drivers/net/wireless/iwmc3200wifi/umac.h @@ -495,6 +495,8 @@ struct iwm_fw_alive_hdr { #define WIFI_DBG_IF_NTFY_COEX_HANDLE_ENVELOP 0xE8 #define WIFI_DBG_IF_NTFY_COEX_HANDLE_RELEASE_ENVELOP 0xE9 +#define WIFI_IF_NTFY_MAX 0xff + /* Notification structures */ struct iwm_umac_notif_wifi_if { struct iwm_umac_wifi_in_hdr hdr; @@ -613,6 +615,7 @@ struct iwm_umac_notif_alive { } __attribute__ ((packed)); struct iwm_umac_notif_init_complete { + struct iwm_umac_wifi_in_hdr hdr; __le16 status; __le16 reserved; } __attribute__ ((packed)); @@ -641,6 +644,11 @@ struct iwm_fw_error_hdr { __le32 umac_status; __le32 lmac_status; __le32 sdio_status; + __le32 dbm_sample_ctrl; + __le32 dbm_buf_base; + __le32 dbm_buf_end; + __le32 dbm_buf_write_ptr; + __le32 dbm_buf_cycle_cnt; } __attribute__ ((packed)); struct iwm_umac_notif_error { diff --git a/drivers/net/wireless/iwmc3200wifi/wext.c b/drivers/net/wireless/iwmc3200wifi/wext.c deleted file mode 100644 index 584c94d0f399..000000000000 --- a/drivers/net/wireless/iwmc3200wifi/wext.c +++ /dev/null @@ -1,723 +0,0 @@ -/* - * Intel Wireless Multicomm 3200 WiFi driver - * - * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com> - * Samuel Ortiz <samuel.ortiz@intel.com> - * Zhu Yi <yi.zhu@intel.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 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-1301, USA. - * - */ - -#include <linux/kernel.h> -#include <linux/netdevice.h> -#include <linux/wireless.h> -#include <linux/if_arp.h> -#include <linux/etherdevice.h> -#include <net/cfg80211.h> -#include <net/iw_handler.h> - -#include "iwm.h" -#include "umac.h" -#include "commands.h" -#include "debug.h" - -static struct iw_statistics *iwm_get_wireless_stats(struct net_device *dev) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - struct iw_statistics *wstats = &iwm->wstats; - - if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { - memset(wstats, 0, sizeof(struct iw_statistics)); - wstats->qual.updated = IW_QUAL_ALL_INVALID; - } - - return wstats; -} - -static int iwm_wext_siwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - - if (freq->flags == IW_FREQ_AUTO) - return 0; - - /* frequency/channel can only be set in IBSS mode */ - if (iwm->conf.mode != UMAC_MODE_IBSS) - return -EOPNOTSUPP; - - return cfg80211_ibss_wext_siwfreq(dev, info, freq, extra); -} - -static int iwm_wext_giwfreq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *freq, char *extra) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - - if (iwm->conf.mode == UMAC_MODE_IBSS) - return cfg80211_ibss_wext_giwfreq(dev, info, freq, extra); - - freq->e = 0; - freq->m = iwm->channel; - - return 0; -} - -static int iwm_wext_siwap(struct net_device *dev, struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - - if (iwm->conf.mode == UMAC_MODE_IBSS) - return cfg80211_ibss_wext_siwap(dev, info, ap_addr, extra); - - if (!test_bit(IWM_STATUS_READY, &iwm->status)) - return -EIO; - - if (is_zero_ether_addr(ap_addr->sa_data) || - is_broadcast_ether_addr(ap_addr->sa_data)) { - IWM_DBG_WEXT(iwm, DBG, "clear mandatory bssid %pM\n", - iwm->umac_profile->bssid[0]); - memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN); - iwm->umac_profile->bss_num = 0; - } else { - IWM_DBG_WEXT(iwm, DBG, "add mandatory bssid %pM\n", - ap_addr->sa_data); - memcpy(&iwm->umac_profile->bssid[0], ap_addr->sa_data, - ETH_ALEN); - iwm->umac_profile->bss_num = 1; - } - - if (iwm->umac_profile_active) { - if (!memcmp(&iwm->umac_profile->bssid[0], iwm->bssid, ETH_ALEN)) - return 0; - - iwm_invalidate_mlme_profile(iwm); - } - - if (iwm->umac_profile->ssid.ssid_len) - return iwm_send_mlme_profile(iwm); - - return 0; -} - -static int iwm_wext_giwap(struct net_device *dev, struct iw_request_info *info, - struct sockaddr *ap_addr, char *extra) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - - switch (iwm->conf.mode) { - case UMAC_MODE_IBSS: - return cfg80211_ibss_wext_giwap(dev, info, ap_addr, extra); - case UMAC_MODE_BSS: - if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { - ap_addr->sa_family = ARPHRD_ETHER; - memcpy(&ap_addr->sa_data, iwm->bssid, ETH_ALEN); - } else - memset(&ap_addr->sa_data, 0, ETH_ALEN); - break; - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static int iwm_wext_siwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - size_t len = data->length; - int ret; - - if (iwm->conf.mode == UMAC_MODE_IBSS) - return cfg80211_ibss_wext_siwessid(dev, info, data, ssid); - - if (!test_bit(IWM_STATUS_READY, &iwm->status)) - return -EIO; - - if (len > 0 && ssid[len - 1] == '\0') - len--; - - if (iwm->umac_profile_active) { - if (iwm->umac_profile->ssid.ssid_len == len && - !memcmp(iwm->umac_profile->ssid.ssid, ssid, len)) - return 0; - - ret = iwm_invalidate_mlme_profile(iwm); - if (ret < 0) { - IWM_ERR(iwm, "Couldn't invalidate profile\n"); - return ret; - } - } - - iwm->umac_profile->ssid.ssid_len = len; - memcpy(iwm->umac_profile->ssid.ssid, ssid, len); - - return iwm_send_mlme_profile(iwm); -} - -static int iwm_wext_giwessid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *data, char *ssid) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - - if (iwm->conf.mode == UMAC_MODE_IBSS) - return cfg80211_ibss_wext_giwessid(dev, info, data, ssid); - - if (!test_bit(IWM_STATUS_READY, &iwm->status)) - return -EIO; - - data->length = iwm->umac_profile->ssid.ssid_len; - if (data->length) { - memcpy(ssid, iwm->umac_profile->ssid.ssid, data->length); - data->flags = 1; - } else - data->flags = 0; - - return 0; -} - -static struct iwm_key * -iwm_key_init(struct iwm_priv *iwm, u8 key_idx, bool in_use, - struct iw_encode_ext *ext, u8 alg) -{ - struct iwm_key *key = &iwm->keys[key_idx]; - - memset(key, 0, sizeof(struct iwm_key)); - memcpy(key->hdr.mac, ext->addr.sa_data, ETH_ALEN); - key->hdr.key_idx = key_idx; - if (is_broadcast_ether_addr(ext->addr.sa_data)) - key->hdr.multicast = 1; - - key->in_use = in_use; - key->flags = ext->ext_flags; - key->alg = alg; - key->key_len = ext->key_len; - memcpy(key->key, ext->key, ext->key_len); - - return key; -} - -static int iwm_wext_giwrate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *rate, char *extra) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - - rate->value = iwm->rate * 1000000; - - return 0; -} - -static int iwm_wext_siwencode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, char *key_buf) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - struct iwm_key *uninitialized_var(key); - int idx, i, uninitialized_var(alg), remove = 0, ret; - - IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", erq->length); - IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags); - - if (!iwm->umac_profile) { - IWM_ERR(iwm, "UMAC profile not allocated yet\n"); - return -ENODEV; - } - - if (erq->length == WLAN_KEY_LEN_WEP40) { - alg = UMAC_CIPHER_TYPE_WEP_40; - iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_40; - iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_40; - } else if (erq->length == WLAN_KEY_LEN_WEP104) { - alg = UMAC_CIPHER_TYPE_WEP_104; - iwm->umac_profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_WEP_104; - iwm->umac_profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_WEP_104; - } - - if (erq->flags & IW_ENCODE_RESTRICTED) - iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; - else - iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN; - - idx = erq->flags & IW_ENCODE_INDEX; - if (idx == 0) { - if (iwm->default_key) - for (i = 0; i < IWM_NUM_KEYS; i++) { - if (iwm->default_key == &iwm->keys[i]) { - idx = i; - break; - } - } - else - iwm->default_key = &iwm->keys[idx]; - } else if (idx < 1 || idx > 4) { - return -EINVAL; - } else - idx--; - - if (erq->flags & IW_ENCODE_DISABLED) - remove = 1; - else if (erq->length == 0) { - if (!iwm->keys[idx].in_use) - return -EINVAL; - iwm->default_key = &iwm->keys[idx]; - } - - if (erq->length) { - key = &iwm->keys[idx]; - memset(key, 0, sizeof(struct iwm_key)); - memset(key->hdr.mac, 0xff, ETH_ALEN); - key->hdr.key_idx = idx; - key->hdr.multicast = 1; - key->in_use = !remove; - key->alg = alg; - key->key_len = erq->length; - memcpy(key->key, key_buf, erq->length); - - IWM_DBG_WEXT(iwm, DBG, "Setting key %d, default: %d\n", - idx, !!iwm->default_key); - } - - if (remove) { - if ((erq->flags & IW_ENCODE_NOKEY) || (erq->length == 0)) { - int j; - for (j = 0; j < IWM_NUM_KEYS; j++) - if (iwm->keys[j].in_use) { - struct iwm_key *k = &iwm->keys[j]; - - k->in_use = 0; - ret = iwm_set_key(iwm, remove, 0, k); - if (ret < 0) - return ret; - } - - iwm->umac_profile->sec.ucast_cipher = - UMAC_CIPHER_TYPE_NONE; - iwm->umac_profile->sec.mcast_cipher = - UMAC_CIPHER_TYPE_NONE; - iwm->umac_profile->sec.auth_type = - UMAC_AUTH_TYPE_OPEN; - - return 0; - } else { - key->in_use = 0; - return iwm_set_key(iwm, remove, 0, key); - } - } - - /* - * If we havent set a profile yet, we cant set keys. - * Keys will be pushed after we're associated. - */ - if (!iwm->umac_profile_active) - return 0; - - /* - * If there is a current active profile, but no - * default key, it's not worth trying to associate again. - */ - if (!iwm->default_key) - return 0; - - /* - * Here we have an active profile, but a key setting changed. - * We thus have to invalidate the current profile, and push the - * new one. Keys will be pushed when association takes place. - */ - ret = iwm_invalidate_mlme_profile(iwm); - if (ret < 0) { - IWM_ERR(iwm, "Couldn't invalidate profile\n"); - return ret; - } - - return iwm_send_mlme_profile(iwm); -} - -static int iwm_wext_giwencode(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, char *key) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - int idx, i; - - idx = erq->flags & IW_ENCODE_INDEX; - if (idx < 1 || idx > 4) { - idx = -1; - if (!iwm->default_key) { - erq->length = 0; - erq->flags |= IW_ENCODE_NOKEY; - return 0; - } else - for (i = 0; i < IWM_NUM_KEYS; i++) { - if (iwm->default_key == &iwm->keys[i]) { - idx = i; - break; - } - } - if (idx < 0) - return -EINVAL; - } else - idx--; - - erq->flags = idx + 1; - - if (!iwm->keys[idx].in_use) { - erq->length = 0; - erq->flags |= IW_ENCODE_DISABLED; - return 0; - } - - memcpy(key, iwm->keys[idx].key, - min_t(int, erq->length, iwm->keys[idx].key_len)); - erq->length = iwm->keys[idx].key_len; - erq->flags |= IW_ENCODE_ENABLED; - - if (iwm->umac_profile->mode == UMAC_MODE_BSS) { - switch (iwm->umac_profile->sec.auth_type) { - case UMAC_AUTH_TYPE_OPEN: - erq->flags |= IW_ENCODE_OPEN; - break; - default: - erq->flags |= IW_ENCODE_RESTRICTED; - break; - } - } - - return 0; -} - -static int iwm_set_wpa_version(struct iwm_priv *iwm, u8 wpa_version) -{ - if (wpa_version & IW_AUTH_WPA_VERSION_WPA2) - iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK; - else if (wpa_version & IW_AUTH_WPA_VERSION_WPA) - iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK; - else - iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE; - - return 0; -} - -static int iwm_wext_siwpower(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *wrq, char *extra) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - u32 power_index; - - if (wrq->disabled) { - power_index = IWM_POWER_INDEX_MIN; - goto set; - } else - power_index = IWM_POWER_INDEX_DEFAULT; - - switch (wrq->flags & IW_POWER_MODE) { - case IW_POWER_ON: - case IW_POWER_MODE: - case IW_POWER_ALL_R: - break; - default: - return -EINVAL; - } - - set: - if (power_index == iwm->conf.power_index) - return 0; - - iwm->conf.power_index = power_index; - - return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, - CFG_POWER_INDEX, iwm->conf.power_index); -} - -static int iwm_wext_giwpower(struct net_device *dev, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - - wrqu->power.disabled = (iwm->conf.power_index == IWM_POWER_INDEX_MIN); - - return 0; -} - -static int iwm_set_key_mgt(struct iwm_priv *iwm, u8 key_mgt) -{ - u8 *auth_type = &iwm->umac_profile->sec.auth_type; - - if (key_mgt == IW_AUTH_KEY_MGMT_802_1X) - *auth_type = UMAC_AUTH_TYPE_8021X; - else if (key_mgt == IW_AUTH_KEY_MGMT_PSK) { - if (iwm->umac_profile->sec.flags & - (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) - *auth_type = UMAC_AUTH_TYPE_RSNA_PSK; - else - *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; - } else { - IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt); - return -EINVAL; - } - - return 0; -} - -static int iwm_set_cipher(struct iwm_priv *iwm, u8 cipher, u8 ucast) -{ - u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher : - &iwm->umac_profile->sec.mcast_cipher; - - switch (cipher) { - case IW_AUTH_CIPHER_NONE: - *profile_cipher = UMAC_CIPHER_TYPE_NONE; - break; - case IW_AUTH_CIPHER_WEP40: - *profile_cipher = UMAC_CIPHER_TYPE_WEP_40; - break; - case IW_AUTH_CIPHER_TKIP: - *profile_cipher = UMAC_CIPHER_TYPE_TKIP; - break; - case IW_AUTH_CIPHER_CCMP: - *profile_cipher = UMAC_CIPHER_TYPE_CCMP; - break; - case IW_AUTH_CIPHER_WEP104: - *profile_cipher = UMAC_CIPHER_TYPE_WEP_104; - break; - default: - IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher); - return -ENOTSUPP; - } - - return 0; -} - -static int iwm_set_auth_alg(struct iwm_priv *iwm, u8 auth_alg) -{ - u8 *auth_type = &iwm->umac_profile->sec.auth_type; - - switch (auth_alg) { - case IW_AUTH_ALG_OPEN_SYSTEM: - *auth_type = UMAC_AUTH_TYPE_OPEN; - break; - case IW_AUTH_ALG_SHARED_KEY: - if (iwm->umac_profile->sec.flags & - (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) { - if (*auth_type == UMAC_AUTH_TYPE_8021X) - return -EINVAL; - *auth_type = UMAC_AUTH_TYPE_RSNA_PSK; - } else { - *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; - } - break; - case IW_AUTH_ALG_LEAP: - default: - IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", auth_alg); - return -ENOTSUPP; - } - - return 0; -} - -static int iwm_wext_siwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - int ret; - - if ((data->flags) & - (IW_AUTH_WPA_VERSION | IW_AUTH_KEY_MGMT | - IW_AUTH_WPA_ENABLED | IW_AUTH_80211_AUTH_ALG)) { - /* We need to invalidate the current profile */ - if (iwm->umac_profile_active) { - ret = iwm_invalidate_mlme_profile(iwm); - if (ret < 0) { - IWM_ERR(iwm, "Couldn't invalidate profile\n"); - return ret; - } - } - } - - switch (data->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - return iwm_set_wpa_version(iwm, data->value); - break; - case IW_AUTH_CIPHER_PAIRWISE: - return iwm_set_cipher(iwm, data->value, 1); - break; - case IW_AUTH_CIPHER_GROUP: - return iwm_set_cipher(iwm, data->value, 0); - break; - case IW_AUTH_KEY_MGMT: - return iwm_set_key_mgt(iwm, data->value); - break; - case IW_AUTH_80211_AUTH_ALG: - return iwm_set_auth_alg(iwm, data->value); - break; - default: - return -ENOTSUPP; - } - - return 0; -} - -static int iwm_wext_giwauth(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *data, char *extra) -{ - return 0; -} - -static int iwm_wext_siwencodeext(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *erq, char *extra) -{ - struct iwm_priv *iwm = ndev_to_iwm(dev); - struct iwm_key *key; - struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; - int uninitialized_var(alg), idx, i, remove = 0; - - IWM_DBG_WEXT(iwm, DBG, "alg: 0x%x\n", ext->alg); - IWM_DBG_WEXT(iwm, DBG, "key len: %d\n", ext->key_len); - IWM_DBG_WEXT(iwm, DBG, "ext_flags: 0x%x\n", ext->ext_flags); - IWM_DBG_WEXT(iwm, DBG, "flags: 0x%x\n", erq->flags); - IWM_DBG_WEXT(iwm, DBG, "length: 0x%x\n", erq->length); - - switch (ext->alg) { - case IW_ENCODE_ALG_NONE: - remove = 1; - break; - case IW_ENCODE_ALG_WEP: - if (ext->key_len == WLAN_KEY_LEN_WEP40) - alg = UMAC_CIPHER_TYPE_WEP_40; - else if (ext->key_len == WLAN_KEY_LEN_WEP104) - alg = UMAC_CIPHER_TYPE_WEP_104; - else { - IWM_ERR(iwm, "Invalid key length: %d\n", ext->key_len); - return -EINVAL; - } - - break; - case IW_ENCODE_ALG_TKIP: - alg = UMAC_CIPHER_TYPE_TKIP; - break; - case IW_ENCODE_ALG_CCMP: - alg = UMAC_CIPHER_TYPE_CCMP; - break; - default: - return -EOPNOTSUPP; - } - - idx = erq->flags & IW_ENCODE_INDEX; - - if (idx == 0) { - if (iwm->default_key) - for (i = 0; i < IWM_NUM_KEYS; i++) { - if (iwm->default_key == &iwm->keys[i]) { - idx = i; - break; - } - } - } else if (idx < 1 || idx > 4) { - return -EINVAL; - } else - idx--; - - if (erq->flags & IW_ENCODE_DISABLED) - remove = 1; - else if ((erq->length == 0) || - (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) { - iwm->default_key = &iwm->keys[idx]; - if (iwm->umac_profile_active && ext->alg == IW_ENCODE_ALG_WEP) - return iwm_set_tx_key(iwm, idx); - } - - key = iwm_key_init(iwm, idx, !remove, ext, alg); - - return iwm_set_key(iwm, remove, !iwm->default_key, key); -} - -static const iw_handler iwm_handlers[] = -{ - (iw_handler) NULL, /* SIOCSIWCOMMIT */ - (iw_handler) cfg80211_wext_giwname, /* SIOCGIWNAME */ - (iw_handler) NULL, /* SIOCSIWNWID */ - (iw_handler) NULL, /* SIOCGIWNWID */ - (iw_handler) iwm_wext_siwfreq, /* SIOCSIWFREQ */ - (iw_handler) iwm_wext_giwfreq, /* SIOCGIWFREQ */ - (iw_handler) cfg80211_wext_siwmode, /* SIOCSIWMODE */ - (iw_handler) cfg80211_wext_giwmode, /* SIOCGIWMODE */ - (iw_handler) NULL, /* SIOCSIWSENS */ - (iw_handler) NULL, /* SIOCGIWSENS */ - (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ - (iw_handler) cfg80211_wext_giwrange, /* SIOCGIWRANGE */ - (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ - (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ - (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ - (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ - (iw_handler) NULL, /* SIOCSIWSPY */ - (iw_handler) NULL, /* SIOCGIWSPY */ - (iw_handler) NULL, /* SIOCSIWTHRSPY */ - (iw_handler) NULL, /* SIOCGIWTHRSPY */ - (iw_handler) iwm_wext_siwap, /* SIOCSIWAP */ - (iw_handler) iwm_wext_giwap, /* SIOCGIWAP */ - (iw_handler) NULL, /* SIOCSIWMLME */ - (iw_handler) NULL, /* SIOCGIWAPLIST */ - (iw_handler) cfg80211_wext_siwscan, /* SIOCSIWSCAN */ - (iw_handler) cfg80211_wext_giwscan, /* SIOCGIWSCAN */ - (iw_handler) iwm_wext_siwessid, /* SIOCSIWESSID */ - (iw_handler) iwm_wext_giwessid, /* SIOCGIWESSID */ - (iw_handler) NULL, /* SIOCSIWNICKN */ - (iw_handler) NULL, /* SIOCGIWNICKN */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCSIWRATE */ - (iw_handler) iwm_wext_giwrate, /* SIOCGIWRATE */ - (iw_handler) cfg80211_wext_siwrts, /* SIOCSIWRTS */ - (iw_handler) cfg80211_wext_giwrts, /* SIOCGIWRTS */ - (iw_handler) cfg80211_wext_siwfrag, /* SIOCSIWFRAG */ - (iw_handler) cfg80211_wext_giwfrag, /* SIOCGIWFRAG */ - (iw_handler) NULL, /* SIOCSIWTXPOW */ - (iw_handler) NULL, /* SIOCGIWTXPOW */ - (iw_handler) NULL, /* SIOCSIWRETRY */ - (iw_handler) NULL, /* SIOCGIWRETRY */ - (iw_handler) iwm_wext_siwencode, /* SIOCSIWENCODE */ - (iw_handler) iwm_wext_giwencode, /* SIOCGIWENCODE */ - (iw_handler) iwm_wext_siwpower, /* SIOCSIWPOWER */ - (iw_handler) iwm_wext_giwpower, /* SIOCGIWPOWER */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* -- hole -- */ - (iw_handler) NULL, /* SIOCSIWGENIE */ - (iw_handler) NULL, /* SIOCGIWGENIE */ - (iw_handler) iwm_wext_siwauth, /* SIOCSIWAUTH */ - (iw_handler) iwm_wext_giwauth, /* SIOCGIWAUTH */ - (iw_handler) iwm_wext_siwencodeext, /* SIOCSIWENCODEEXT */ - (iw_handler) NULL, /* SIOCGIWENCODEEXT */ - (iw_handler) NULL, /* SIOCSIWPMKSA */ - (iw_handler) NULL, /* -- hole -- */ -}; - -const struct iw_handler_def iwm_iw_handler_def = { - .num_standard = ARRAY_SIZE(iwm_handlers), - .standard = (iw_handler *) iwm_handlers, - .get_wireless_stats = iwm_get_wireless_stats, -}; - diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c index b9b374119033..dd8732611ba9 100644 --- a/drivers/net/wireless/libertas/assoc.c +++ b/drivers/net/wireless/libertas/assoc.c @@ -34,7 +34,8 @@ static const u8 bssid_off[ETH_ALEN] __attribute__ ((aligned (2))) = * * @param priv A pointer to struct lbs_private structure * @param rates the buffer which keeps input and output - * @param rates_size the size of rate1 buffer; new size of buffer on return + * @param rates_size the size of rates buffer; new size of buffer on return, + * which will be less than or equal to original rates_size * * @return 0 on success, or -1 on error */ @@ -42,42 +43,42 @@ static int get_common_rates(struct lbs_private *priv, u8 *rates, u16 *rates_size) { - u8 *card_rates = lbs_bg_rates; - size_t num_card_rates = sizeof(lbs_bg_rates); - int ret = 0, i, j; - u8 tmp[30]; - size_t tmp_size = 0; - - /* For each rate in card_rates that exists in rate1, copy to tmp */ - for (i = 0; card_rates[i] && (i < num_card_rates); i++) { - for (j = 0; rates[j] && (j < *rates_size); j++) { - if (rates[j] == card_rates[i]) - tmp[tmp_size++] = card_rates[i]; + int i, j; + u8 intersection[MAX_RATES]; + u16 intersection_size; + u16 num_rates = 0; + + intersection_size = min_t(u16, *rates_size, ARRAY_SIZE(intersection)); + + /* Allow each rate from 'rates' that is supported by the hardware */ + for (i = 0; i < ARRAY_SIZE(lbs_bg_rates) && lbs_bg_rates[i]; i++) { + for (j = 0; j < intersection_size && rates[j]; j++) { + if (rates[j] == lbs_bg_rates[i]) + intersection[num_rates++] = rates[j]; } } lbs_deb_hex(LBS_DEB_JOIN, "AP rates ", rates, *rates_size); - lbs_deb_hex(LBS_DEB_JOIN, "card rates ", card_rates, num_card_rates); - lbs_deb_hex(LBS_DEB_JOIN, "common rates", tmp, tmp_size); + lbs_deb_hex(LBS_DEB_JOIN, "card rates ", lbs_bg_rates, + ARRAY_SIZE(lbs_bg_rates)); + lbs_deb_hex(LBS_DEB_JOIN, "common rates", intersection, num_rates); lbs_deb_join("TX data rate 0x%02x\n", priv->cur_rate); if (!priv->enablehwauto) { - for (i = 0; i < tmp_size; i++) { - if (tmp[i] == priv->cur_rate) + for (i = 0; i < num_rates; i++) { + if (intersection[i] == priv->cur_rate) goto done; } lbs_pr_alert("Previously set fixed data rate %#x isn't " "compatible with the network.\n", priv->cur_rate); - ret = -1; - goto done; + return -1; } - ret = 0; done: memset(rates, 0, *rates_size); - *rates_size = min_t(int, tmp_size, *rates_size); - memcpy(rates, tmp, *rates_size); - return ret; + *rates_size = num_rates; + memcpy(rates, intersection, num_rates); + return 0; } @@ -129,7 +130,6 @@ static int lbs_set_authentication(struct lbs_private *priv, u8 bssid[6], u8 auth { struct cmd_ds_802_11_authenticate cmd; int ret = -1; - DECLARE_MAC_BUF(mac); lbs_deb_enter(LBS_DEB_JOIN); @@ -138,8 +138,7 @@ static int lbs_set_authentication(struct lbs_private *priv, u8 bssid[6], u8 auth cmd.authtype = iw_auth_to_ieee_auth(auth); - lbs_deb_join("AUTH_CMD: BSSID %s, auth 0x%x\n", - print_mac(mac, bssid), cmd.authtype); + lbs_deb_join("AUTH_CMD: BSSID %pM, auth 0x%x\n", bssid, cmd.authtype); ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd); @@ -321,8 +320,8 @@ static int lbs_associate(struct lbs_private *priv, rates = (struct mrvl_ie_rates_param_set *) pos; rates->header.type = cpu_to_le16(TLV_TYPE_RATES); - memcpy(&rates->rates, &bss->rates, MAX_RATES); - tmplen = MAX_RATES; + tmplen = min_t(u16, ARRAY_SIZE(bss->rates), MAX_RATES); + memcpy(&rates->rates, &bss->rates, tmplen); if (get_common_rates(priv, rates->rates, &tmplen)) { ret = -1; goto done; @@ -342,8 +341,6 @@ static int lbs_associate(struct lbs_private *priv, /* Firmware v9+ indicate authentication suites as a TLV */ if (priv->fwrelease >= 0x09000000) { - DECLARE_MAC_BUF(mac); - auth = (struct mrvl_ie_auth_type *) pos; auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); auth->header.len = cpu_to_le16(2); @@ -351,8 +348,8 @@ static int lbs_associate(struct lbs_private *priv, auth->auth = cpu_to_le16(tmpauth); pos += sizeof(auth->header) + 2; - lbs_deb_join("AUTH_CMD: BSSID %s, auth 0x%x\n", - print_mac(mac, bss->bssid), priv->secinfo.auth_mode); + lbs_deb_join("AUTH_CMD: BSSID %pM, auth 0x%x\n", + bss->bssid, priv->secinfo.auth_mode); } /* WPA/WPA2 IEs */ @@ -598,7 +595,7 @@ static int lbs_adhoc_join(struct lbs_private *priv, /* Copy Data rates from the rates recorded in scan response */ memset(cmd.bss.rates, 0, sizeof(cmd.bss.rates)); - ratesize = min_t(u16, sizeof(cmd.bss.rates), MAX_RATES); + ratesize = min_t(u16, ARRAY_SIZE(cmd.bss.rates), ARRAY_SIZE (bss->rates)); memcpy(cmd.bss.rates, bss->rates, ratesize); if (get_common_rates(priv, cmd.bss.rates, &ratesize)) { lbs_deb_join("ADHOC_JOIN: get_common_rates returned error.\n"); @@ -1368,11 +1365,17 @@ static int assoc_helper_wpa_keys(struct lbs_private *priv, if (ret) goto out; + memcpy(&priv->wpa_unicast_key, &assoc_req->wpa_unicast_key, + sizeof(struct enc_key)); + if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) { clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags); ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req); assoc_req->flags = flags; + + memcpy(&priv->wpa_mcast_key, &assoc_req->wpa_mcast_key, + sizeof(struct enc_key)); } out: diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c index 811ffc3ef414..893a55ca344a 100644 --- a/drivers/net/wireless/libertas/debugfs.c +++ b/drivers/net/wireless/libertas/debugfs.c @@ -45,6 +45,8 @@ static ssize_t lbs_dev_info(struct file *file, char __user *userbuf, unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; ssize_t res; + if (!buf) + return -ENOMEM; pos += snprintf(buf+pos, len-pos, "state = %s\n", szStates[priv->connect_status]); @@ -68,6 +70,8 @@ static ssize_t lbs_getscantable(struct file *file, char __user *userbuf, char *buf = (char *)addr; DECLARE_SSID_BUF(ssid); struct bss_descriptor * iter_bss; + if (!buf) + return -ENOMEM; pos += snprintf(buf+pos, len-pos, "# | ch | rssi | bssid | cap | Qual | SSID \n"); @@ -110,6 +114,8 @@ static ssize_t lbs_sleepparams_write(struct file *file, int p1, p2, p3, p4, p5, p6; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; + if (!buf) + return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, user_buf, buf_size)) { @@ -148,6 +154,8 @@ static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf, struct sleep_params sp; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; + if (!buf) + return -ENOMEM; ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp); if (ret) @@ -433,6 +441,8 @@ static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf, int ret; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; + if (!buf) + return -ENOMEM; offval.offset = priv->mac_offset; offval.value = 0; @@ -457,6 +467,8 @@ static ssize_t lbs_rdmac_write(struct file *file, ssize_t res, buf_size; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; + if (!buf) + return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, userbuf, buf_size)) { @@ -481,6 +493,8 @@ static ssize_t lbs_wrmac_write(struct file *file, struct lbs_offset_value offval; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; + if (!buf) + return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, userbuf, buf_size)) { @@ -515,6 +529,8 @@ static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf, int ret; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; + if (!buf) + return -ENOMEM; offval.offset = priv->bbp_offset; offval.value = 0; @@ -540,6 +556,8 @@ static ssize_t lbs_rdbbp_write(struct file *file, ssize_t res, buf_size; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; + if (!buf) + return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, userbuf, buf_size)) { @@ -564,6 +582,8 @@ static ssize_t lbs_wrbbp_write(struct file *file, struct lbs_offset_value offval; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; + if (!buf) + return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, userbuf, buf_size)) { @@ -598,6 +618,8 @@ static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf, int ret; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; + if (!buf) + return -ENOMEM; offval.offset = priv->rf_offset; offval.value = 0; @@ -623,6 +645,8 @@ static ssize_t lbs_rdrf_write(struct file *file, ssize_t res, buf_size; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; + if (!buf) + return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, userbuf, buf_size)) { @@ -647,6 +671,8 @@ static ssize_t lbs_wrrf_write(struct file *file, struct lbs_offset_value offval; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; + if (!buf) + return -ENOMEM; buf_size = min(count, len - 1); if (copy_from_user(buf, userbuf, buf_size)) { @@ -853,6 +879,8 @@ static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf, struct debug_data *d; unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *)addr; + if (!buf) + return -ENOMEM; p = buf; diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h index 0b84bdca0726..8b15380ae6e1 100644 --- a/drivers/net/wireless/libertas/decl.h +++ b/drivers/net/wireless/libertas/decl.h @@ -6,7 +6,7 @@ #ifndef _LBS_DECL_H_ #define _LBS_DECL_H_ -#include <linux/device.h> +#include <linux/netdevice.h> #include "defs.h" @@ -41,7 +41,8 @@ u8 lbs_data_rate_to_fw_index(u32 rate); int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len); void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, int result); -int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev); +netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev); int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band); int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *); diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index f9ec69e04734..578c69783589 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -260,7 +260,6 @@ struct lbs_private { u16 psmode; /* Wlan802_11PowermodeCAM=disable Wlan802_11PowermodeMAX_PSP=enable */ u32 psstate; - char ps_supported; u8 needtowakeup; struct assoc_request * pending_assoc_req; diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index 2a5b083bf9bd..62381768f2d5 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -59,6 +59,7 @@ struct if_cs_card { struct pcmcia_device *p_dev; struct lbs_private *priv; void __iomem *iobase; + bool align_regs; }; @@ -274,16 +275,25 @@ static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 r #define IF_CS_PRODUCT_ID 0x0000001C #define IF_CS_CF8385_B1_REV 0x12 #define IF_CS_CF8381_B3_REV 0x04 +#define IF_CS_CF8305_B1_REV 0x03 /* * Used to detect other cards than CF8385 since their revisions of silicon * doesn't match those from CF8385, eg. CF8381 B3 works with this driver. */ +#define CF8305_MANFID 0x02db +#define CF8305_CARDID 0x8103 #define CF8381_MANFID 0x02db #define CF8381_CARDID 0x6064 #define CF8385_MANFID 0x02df #define CF8385_CARDID 0x8103 +static inline int if_cs_hw_is_cf8305(struct pcmcia_device *p_dev) +{ + return (p_dev->manf_id == CF8305_MANFID && + p_dev->card_id == CF8305_CARDID); +} + static inline int if_cs_hw_is_cf8381(struct pcmcia_device *p_dev) { return (p_dev->manf_id == CF8381_MANFID && @@ -556,7 +566,15 @@ static int if_cs_prog_helper(struct if_cs_card *card) lbs_deb_enter(LBS_DEB_CS); - scratch = if_cs_read8(card, IF_CS_SCRATCH); + /* + * This is the only place where an unaligned register access happens on + * the CF8305 card, therefore for the sake of speed of the driver, we do + * the alignment correction here. + */ + if (card->align_regs) + scratch = if_cs_read16(card, IF_CS_SCRATCH) >> 8; + else + scratch = if_cs_read8(card, IF_CS_SCRATCH); /* "If the value is 0x5a, the firmware is already * downloaded successfully" @@ -880,8 +898,24 @@ static int if_cs_probe(struct pcmcia_device *p_dev) p_dev->irq.AssignedIRQ, p_dev->io.BasePort1, p_dev->io.BasePort1 + p_dev->io.NumPorts1 - 1); + /* + * Most of the libertas cards can do unaligned register access, but some + * weird ones can not. That's especially true for the CF8305 card. + */ + card->align_regs = 0; + /* Check if we have a current silicon */ prod_id = if_cs_read8(card, IF_CS_PRODUCT_ID); + if (if_cs_hw_is_cf8305(p_dev)) { + card->align_regs = 1; + if (prod_id < IF_CS_CF8305_B1_REV) { + lbs_pr_err("old chips like 8305 rev B3 " + "aren't supported\n"); + ret = -ENODEV; + goto out2; + } + } + if (if_cs_hw_is_cf8381(p_dev) && prod_id < IF_CS_CF8381_B3_REV) { lbs_pr_err("old chips like 8381 rev B3 aren't supported\n"); ret = -ENODEV; @@ -896,7 +930,7 @@ static int if_cs_probe(struct pcmcia_device *p_dev) /* Load the firmware early, before calling into libertas.ko */ ret = if_cs_prog_helper(card); - if (ret == 0) + if (ret == 0 && !if_cs_hw_is_cf8305(p_dev)) ret = if_cs_prog_real(card); if (ret) goto out2; @@ -933,9 +967,6 @@ static int if_cs_probe(struct pcmcia_device *p_dev) goto out3; } - /* The firmware for the CF card supports powersave */ - priv->ps_supported = 1; - ret = 0; goto out; @@ -979,6 +1010,7 @@ static void if_cs_detach(struct pcmcia_device *p_dev) /********************************************************************/ static struct pcmcia_device_id if_cs_ids[] = { + PCMCIA_DEVICE_MANF_CARD(CF8305_MANFID, CF8305_CARDID), PCMCIA_DEVICE_MANF_CARD(CF8381_MANFID, CF8381_CARDID), PCMCIA_DEVICE_MANF_CARD(CF8385_MANFID, CF8385_CARDID), PCMCIA_DEVICE_NULL, diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 8cdb88c6ca28..485a8d406525 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -1039,9 +1039,6 @@ static int if_sdio_probe(struct sdio_func *func, if (ret) goto err_activate_card; - if (priv->fwcapinfo & FW_CAPINFO_PS) - priv->ps_supported = 1; - out: lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); @@ -1096,11 +1093,11 @@ static void if_sdio_remove(struct sdio_func *func) lbs_pr_alert("CMD_FUNC_SHUTDOWN cmd failed\n"); } - card->priv->surpriseremoved = 1; lbs_deb_sdio("call remove card\n"); lbs_stop_card(card->priv); lbs_remove_card(card->priv); + card->priv->surpriseremoved = 1; flush_workqueue(card->workqueue); destroy_workqueue(card->workqueue); diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index 6564282ce476..446e327180f8 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -376,7 +376,7 @@ static int spu_set_bus_mode(struct if_spi_card *card, u16 mode) err = spu_read_u16(card, IF_SPI_SPU_BUS_MODE_REG, &rval); if (err) return err; - if (rval != mode) { + if ((rval & 0xF) != mode) { lbs_pr_err("Can't read bus mode register.\n"); return -EIO; } @@ -737,7 +737,7 @@ static int if_spi_c2h_data(struct if_spi_card *card) goto out; } else if (len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) { lbs_pr_err("%s: error: card has %d bytes of data, but " - "our maximum skb size is %lu\n", + "our maximum skb size is %zu\n", __func__, len, MRVDRV_ETH_RX_PACKET_BUFFER_SIZE); err = -EINVAL; goto out; @@ -1118,7 +1118,6 @@ static int __devinit if_spi_probe(struct spi_device *spi) priv->card = card; priv->hw_host_to_card = if_spi_host_to_card; priv->fw_ready = 1; - priv->ps_supported = 1; /* Initialize interrupt handling stuff. */ card->run_thread = 1; @@ -1171,12 +1170,13 @@ static int __devexit libertas_spi_remove(struct spi_device *spi) lbs_deb_spi("libertas_spi_remove\n"); lbs_deb_enter(LBS_DEB_SPI); - priv->surpriseremoved = 1; lbs_stop_card(priv); + lbs_remove_card(priv); /* will call free_netdev */ + + priv->surpriseremoved = 1; free_irq(spi->irq, card); if_spi_terminate_spi_thread(card); - lbs_remove_card(priv); /* will call free_netdev */ if (card->pdata->teardown) card->pdata->teardown(spi); free_if_spi_card(card); diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c index 1844c5adf6e9..92bc8c5f1ca2 100644 --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -181,13 +181,14 @@ static void if_usb_setup_firmware(struct lbs_private *priv) wake_method.action = cpu_to_le16(CMD_ACT_GET); if (lbs_cmd_with_response(priv, CMD_802_11_FW_WAKE_METHOD, &wake_method)) { lbs_pr_info("Firmware does not seem to support PS mode\n"); + priv->fwcapinfo &= ~FW_CAPINFO_PS; } else { if (le16_to_cpu(wake_method.method) == CMD_WAKE_METHOD_COMMAND_INT) { lbs_deb_usb("Firmware seems to support PS with wake-via-command\n"); - priv->ps_supported = 1; } else { /* The versions which boot up this way don't seem to work even if we set it to the command interrupt */ + priv->fwcapinfo &= ~FW_CAPINFO_PS; lbs_pr_info("Firmware doesn't wake via command interrupt; disabling PS mode\n"); } } diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 89575e448015..87b4e497faa2 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -1176,7 +1176,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) /* Allocate an Ethernet device and register it */ dev = alloc_etherdev(sizeof(struct lbs_private)); if (!dev) { - lbs_pr_err("init ethX device failed\n"); + lbs_pr_err("init wlanX device failed\n"); goto done; } priv = netdev_priv(dev); @@ -1204,6 +1204,7 @@ struct lbs_private *lbs_add_card(void *card, struct device *dmdev) SET_NETDEV_DEV(dev, dmdev); priv->rtap_net_dev = NULL; + strcpy(dev->name, "wlan%d"); lbs_deb_thread("Starting main thread...\n"); init_waitqueue_head(&priv->waitq); @@ -1646,7 +1647,8 @@ static int lbs_rtap_stop(struct net_device *dev) return 0; } -static int lbs_rtap_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t lbs_rtap_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) { netif_stop_queue(dev); return NETDEV_TX_BUSY; diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c index 160cfd8311c0..4c018f7a0a8d 100644 --- a/drivers/net/wireless/libertas/tx.c +++ b/drivers/net/wireless/libertas/tx.c @@ -57,19 +57,17 @@ static u32 convert_radiotap_rate_to_mv(u8 rate) * @param skb A pointer to skb which includes TX packet * @return 0 or -1 */ -int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +netdev_tx_t lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned long flags; struct lbs_private *priv = dev->ml_priv; struct txpd *txpd; char *p802x_hdr; uint16_t pkt_len; - int ret; + netdev_tx_t ret = NETDEV_TX_OK; lbs_deb_enter(LBS_DEB_TX); - ret = NETDEV_TX_OK; - /* We need to protect against the queues being restarted before we get round to stopping them */ spin_lock_irqsave(&priv->driver_lock, flags); diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c index 8bc1907458b1..be837a0d2517 100644 --- a/drivers/net/wireless/libertas/wext.c +++ b/drivers/net/wireless/libertas/wext.c @@ -712,7 +712,7 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info, lbs_deb_enter(LBS_DEB_WEXT); - if (!priv->ps_supported) { + if (!(priv->fwcapinfo & FW_CAPINFO_PS)) { if (vwrq->disabled) return 0; else @@ -1728,6 +1728,8 @@ static int lbs_set_auth(struct net_device *dev, } switch (dwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_PRIVACY_INVOKED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: case IW_AUTH_TKIP_COUNTERMEASURES: case IW_AUTH_CIPHER_PAIRWISE: case IW_AUTH_CIPHER_GROUP: diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c index 10a99e26d392..019431d2f8a9 100644 --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -366,15 +366,35 @@ static int lbtf_op_config(struct ieee80211_hw *hw, u32 changed) return 0; } +static u64 lbtf_op_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + struct lbtf_private *priv = hw->priv; + int i; + + if (!mc_count || mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) + return mc_count; + + priv->nr_of_multicastmacaddr = mc_count; + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + memcpy(&priv->multicastlist[i], mclist->da_addr, + ETH_ALEN); + mclist = mclist->next; + } + + return mc_count; +} + #define SUPPORTED_FIF_FLAGS (FIF_PROMISC_IN_BSS | FIF_ALLMULTI) static void lbtf_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { struct lbtf_private *priv = hw->priv; int old_mac_control = priv->mac_control; - int i; changed_flags &= SUPPORTED_FIF_FLAGS; *new_flags &= SUPPORTED_FIF_FLAGS; @@ -386,20 +406,12 @@ static void lbtf_op_configure_filter(struct ieee80211_hw *hw, else priv->mac_control &= ~CMD_ACT_MAC_PROMISCUOUS_ENABLE; if (*new_flags & (FIF_ALLMULTI) || - mc_count > MRVDRV_MAX_MULTICAST_LIST_SIZE) { + multicast > MRVDRV_MAX_MULTICAST_LIST_SIZE) { priv->mac_control |= CMD_ACT_MAC_ALL_MULTICAST_ENABLE; priv->mac_control &= ~CMD_ACT_MAC_MULTICAST_ENABLE; - } else if (mc_count) { + } else if (multicast) { priv->mac_control |= CMD_ACT_MAC_MULTICAST_ENABLE; priv->mac_control &= ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE; - priv->nr_of_multicastmacaddr = mc_count; - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - memcpy(&priv->multicastlist[i], mclist->da_addr, - ETH_ALEN); - mclist = mclist->next; - } lbtf_cmd_set_mac_multicast_addr(priv); } else { priv->mac_control &= ~(CMD_ACT_MAC_MULTICAST_ENABLE | @@ -461,6 +473,7 @@ static const struct ieee80211_ops lbtf_ops = { .add_interface = lbtf_op_add_interface, .remove_interface = lbtf_op_remove_interface, .config = lbtf_op_config, + .prepare_multicast = lbtf_op_prepare_multicast, .configure_filter = lbtf_op_configure_filter, .bss_info_changed = lbtf_op_bss_info_changed, }; @@ -503,7 +516,8 @@ int lbtf_rx(struct lbtf_private *priv, struct sk_buff *skb) skb_reserve(skb, 2); } - ieee80211_rx_irqsafe(priv->hw, skb, &stats); + memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats)); + ieee80211_rx_irqsafe(priv->hw, skb); return 0; } EXPORT_SYMBOL_GPL(lbtf_rx); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 7916ca3f84c8..896f532182f0 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -15,6 +15,8 @@ #include <linux/list.h> #include <linux/spinlock.h> +#include <net/dst.h> +#include <net/xfrm.h> #include <net/mac80211.h> #include <net/ieee80211_radiotap.h> #include <linux/if_arp.h> @@ -310,11 +312,12 @@ struct hwsim_radiotap_hdr { } __attribute__ ((packed)); -static int hwsim_mon_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb, + struct net_device *dev) { /* TODO: allow packet injection */ dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } @@ -404,11 +407,19 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, rx_status.freq = data->channel->center_freq; rx_status.band = data->channel->band; rx_status.rate_idx = info->control.rates[0].idx; - /* TODO: simulate signal strength (and optional packet drop) */ + /* TODO: simulate real signal strength (and optional packet loss) */ + rx_status.signal = -50; if (data->ps != PS_DISABLED) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); + /* release the skb's source info */ + skb_orphan(skb); + skb_dst_drop(skb); + skb->mark = 0; + secpath_reset(skb); + nf_reset(skb); + /* Copy skb to all enabled radios that are on the current frequency */ spin_lock(&hwsim_radio_lock); list_for_each_entry(data2, &hwsim_radios, list) { @@ -430,7 +441,8 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, if (memcmp(hdr->addr1, data2->hw->wiphy->perm_addr, ETH_ALEN) == 0) ack = true; - ieee80211_rx_irqsafe(data2->hw, nskb, &rx_status); + memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(data2->hw, nskb); } spin_unlock(&hwsim_radio_lock); @@ -571,9 +583,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mc_list) + unsigned int *total_flags,u64 multicast) { struct mac80211_hwsim_data *data = hw->priv; @@ -690,6 +700,74 @@ static int mac80211_hwsim_conf_tx( return 0; } +#ifdef CONFIG_NL80211_TESTMODE +/* + * This section contains example code for using netlink + * attributes with the testmode command in nl80211. + */ + +/* These enums need to be kept in sync with userspace */ +enum hwsim_testmode_attr { + __HWSIM_TM_ATTR_INVALID = 0, + HWSIM_TM_ATTR_CMD = 1, + HWSIM_TM_ATTR_PS = 2, + + /* keep last */ + __HWSIM_TM_ATTR_AFTER_LAST, + HWSIM_TM_ATTR_MAX = __HWSIM_TM_ATTR_AFTER_LAST - 1 +}; + +enum hwsim_testmode_cmd { + HWSIM_TM_CMD_SET_PS = 0, + HWSIM_TM_CMD_GET_PS = 1, +}; + +static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = { + [HWSIM_TM_ATTR_CMD] = { .type = NLA_U32 }, + [HWSIM_TM_ATTR_PS] = { .type = NLA_U32 }, +}; + +static int hwsim_fops_ps_write(void *dat, u64 val); + +static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, + void *data, int len) +{ + struct mac80211_hwsim_data *hwsim = hw->priv; + struct nlattr *tb[HWSIM_TM_ATTR_MAX + 1]; + struct sk_buff *skb; + int err, ps; + + err = nla_parse(tb, HWSIM_TM_ATTR_MAX, data, len, + hwsim_testmode_policy); + if (err) + return err; + + if (!tb[HWSIM_TM_ATTR_CMD]) + return -EINVAL; + + switch (nla_get_u32(tb[HWSIM_TM_ATTR_CMD])) { + case HWSIM_TM_CMD_SET_PS: + if (!tb[HWSIM_TM_ATTR_PS]) + return -EINVAL; + ps = nla_get_u32(tb[HWSIM_TM_ATTR_PS]); + return hwsim_fops_ps_write(hwsim, ps); + case HWSIM_TM_CMD_GET_PS: + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, + nla_total_size(sizeof(u32))); + if (!skb) + return -ENOMEM; + NLA_PUT_U32(skb, HWSIM_TM_ATTR_PS, hwsim->ps); + return cfg80211_testmode_reply(skb); + default: + return -EOPNOTSUPP; + } + + nla_put_failure: + kfree_skb(skb); + return -ENOBUFS; +} +#endif + static const struct ieee80211_ops mac80211_hwsim_ops = { .tx = mac80211_hwsim_tx, @@ -703,6 +781,7 @@ static const struct ieee80211_ops mac80211_hwsim_ops = .sta_notify = mac80211_hwsim_sta_notify, .set_tim = mac80211_hwsim_set_tim, .conf_tx = mac80211_hwsim_conf_tx, + CFG80211_TESTMODE_CMD(mac80211_hwsim_testmode_cmd) }; @@ -757,7 +836,6 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) { struct mac80211_hwsim_data *data = dat; struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - DECLARE_MAC_BUF(buf); struct sk_buff *skb; struct ieee80211_pspoll *pspoll; @@ -787,7 +865,6 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, struct ieee80211_vif *vif, int ps) { struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - DECLARE_MAC_BUF(buf); struct sk_buff *skb; struct ieee80211_hdr *hdr; @@ -945,7 +1022,8 @@ static int __init init_mac80211_hwsim(void) BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT); - hw->flags = IEEE80211_HW_MFP_CAPABLE; + hw->flags = IEEE80211_HW_MFP_CAPABLE | + IEEE80211_HW_SIGNAL_DBM; /* ask mac80211 to reserve space for magic */ hw->vif_data_size = sizeof(struct hwsim_vif_priv); diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 83967afe0821..746532ebe5a8 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1,7 +1,8 @@ /* - * drivers/net/wireless/mwl8k.c driver for Marvell TOPDOG 802.11 Wireless cards + * drivers/net/wireless/mwl8k.c + * Driver for Marvell TOPDOG 802.11 Wireless cards * - * Copyright (C) 2008 Marvell Semiconductor Inc. + * Copyright (C) 2008-2009 Marvell Semiconductor Inc. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any @@ -24,7 +25,7 @@ #define MWL8K_DESC "Marvell TOPDOG(R) 802.11 Wireless Network Driver" #define MWL8K_NAME KBUILD_MODNAME -#define MWL8K_VERSION "0.9.1" +#define MWL8K_VERSION "0.10" MODULE_DESCRIPTION(MWL8K_DESC); MODULE_VERSION(MWL8K_VERSION); @@ -38,16 +39,14 @@ static DEFINE_PCI_DEVICE_TABLE(mwl8k_table) = { }; MODULE_DEVICE_TABLE(pci, mwl8k_table); -#define IEEE80211_ADDR_LEN ETH_ALEN - /* Register definitions */ #define MWL8K_HIU_GEN_PTR 0x00000c10 -#define MWL8K_MODE_STA 0x0000005a -#define MWL8K_MODE_AP 0x000000a5 +#define MWL8K_MODE_STA 0x0000005a +#define MWL8K_MODE_AP 0x000000a5 #define MWL8K_HIU_INT_CODE 0x00000c14 -#define MWL8K_FWSTA_READY 0xf0f1f2f4 -#define MWL8K_FWAP_READY 0xf1f2f4a5 -#define MWL8K_INT_CODE_CMD_FINISHED 0x00000005 +#define MWL8K_FWSTA_READY 0xf0f1f2f4 +#define MWL8K_FWAP_READY 0xf1f2f4a5 +#define MWL8K_INT_CODE_CMD_FINISHED 0x00000005 #define MWL8K_HIU_SCRATCH 0x00000c40 /* Host->device communications */ @@ -56,11 +55,10 @@ MODULE_DEVICE_TABLE(pci, mwl8k_table); #define MWL8K_HIU_H2A_INTERRUPT_MASK 0x00000c20 #define MWL8K_HIU_H2A_INTERRUPT_CLEAR_SEL 0x00000c24 #define MWL8K_HIU_H2A_INTERRUPT_STATUS_MASK 0x00000c28 -#define MWL8K_H2A_INT_DUMMY (1 << 20) -#define MWL8K_H2A_INT_RESET (1 << 15) -#define MWL8K_H2A_INT_PS (1 << 2) -#define MWL8K_H2A_INT_DOORBELL (1 << 1) -#define MWL8K_H2A_INT_PPA_READY (1 << 0) +#define MWL8K_H2A_INT_DUMMY (1 << 20) +#define MWL8K_H2A_INT_RESET (1 << 15) +#define MWL8K_H2A_INT_DOORBELL (1 << 1) +#define MWL8K_H2A_INT_PPA_READY (1 << 0) /* Device->host communications */ #define MWL8K_HIU_A2H_INTERRUPT_EVENTS 0x00000c2c @@ -68,16 +66,16 @@ MODULE_DEVICE_TABLE(pci, mwl8k_table); #define MWL8K_HIU_A2H_INTERRUPT_MASK 0x00000c34 #define MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL 0x00000c38 #define MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK 0x00000c3c -#define MWL8K_A2H_INT_DUMMY (1 << 20) -#define MWL8K_A2H_INT_CHNL_SWITCHED (1 << 11) -#define MWL8K_A2H_INT_QUEUE_EMPTY (1 << 10) -#define MWL8K_A2H_INT_RADAR_DETECT (1 << 7) -#define MWL8K_A2H_INT_RADIO_ON (1 << 6) -#define MWL8K_A2H_INT_RADIO_OFF (1 << 5) -#define MWL8K_A2H_INT_MAC_EVENT (1 << 3) -#define MWL8K_A2H_INT_OPC_DONE (1 << 2) -#define MWL8K_A2H_INT_RX_READY (1 << 1) -#define MWL8K_A2H_INT_TX_DONE (1 << 0) +#define MWL8K_A2H_INT_DUMMY (1 << 20) +#define MWL8K_A2H_INT_CHNL_SWITCHED (1 << 11) +#define MWL8K_A2H_INT_QUEUE_EMPTY (1 << 10) +#define MWL8K_A2H_INT_RADAR_DETECT (1 << 7) +#define MWL8K_A2H_INT_RADIO_ON (1 << 6) +#define MWL8K_A2H_INT_RADIO_OFF (1 << 5) +#define MWL8K_A2H_INT_MAC_EVENT (1 << 3) +#define MWL8K_A2H_INT_OPC_DONE (1 << 2) +#define MWL8K_A2H_INT_RX_READY (1 << 1) +#define MWL8K_A2H_INT_TX_DONE (1 << 0) #define MWL8K_A2H_EVENTS (MWL8K_A2H_INT_DUMMY | \ MWL8K_A2H_INT_CHNL_SWITCHED | \ @@ -113,17 +111,6 @@ struct mwl8k_rx_queue { struct sk_buff **rx_skb; }; -struct mwl8k_skb { - /* - * The DMA engine requires a modification to the payload. - * If the skbuff is shared/cloned, it needs to be unshared. - * This method is used to ensure the stack always gets back - * the skbuff it sent for transmission. - */ - struct sk_buff *clone; - struct sk_buff *skb; -}; - struct mwl8k_tx_queue { /* hw transmits here */ int tx_head; @@ -134,7 +121,7 @@ struct mwl8k_tx_queue { struct ieee80211_tx_queue_stats tx_stats; struct mwl8k_tx_desc *tx_desc_area; dma_addr_t tx_desc_dma; - struct mwl8k_skb *tx_skb; + struct sk_buff **tx_skb; }; /* Pointers to the firmware data and meta information about it. */ @@ -152,19 +139,22 @@ struct mwl8k_priv { struct pci_dev *pdev; u8 name[16]; - /* firmware access lock */ - spinlock_t fw_lock; /* firmware files and meta data */ struct mwl8k_firmware fw; u32 part_num; + /* firmware access */ + struct mutex fw_mutex; + struct task_struct *fw_mutex_owner; + int fw_mutex_depth; + struct completion *tx_wait; + struct completion *hostcmd_wait; + /* lock held over TX and TX reap */ spinlock_t tx_lock; - u32 int_mask; struct ieee80211_vif *vif; - struct list_head vif_list; struct ieee80211_channel *current_channel; @@ -173,10 +163,8 @@ struct mwl8k_priv { dma_addr_t cookie_dma; u16 num_mcaddrs; - u16 region_code; u8 hw_rev; - __le32 fw_rev; - u32 wep_enabled; + u32 fw_rev; /* * Running count of TX packets in flight, to avoid @@ -192,19 +180,13 @@ struct mwl8k_priv { struct ieee80211_channel channels[14]; struct ieee80211_rate rates[12]; - /* RF preamble: Short, Long or Auto */ - u8 radio_preamble; - u8 radio_state; - - /* WMM MODE 1 for enabled; 0 for disabled */ - bool wmm_mode; - - /* Set if PHY config is in progress */ - bool inconfig; + bool radio_on; + bool radio_short_preamble; + bool wmm_enabled; /* XXX need to convert this to handle multiple interfaces */ bool capture_beacon; - u8 capture_bssid[IEEE80211_ADDR_LEN]; + u8 capture_bssid[ETH_ALEN]; struct sk_buff *beacon_skb; /* @@ -220,14 +202,10 @@ struct mwl8k_priv { /* Work thread to serialize configuration requests */ struct workqueue_struct *config_wq; - struct completion *hostcmd_wait; - struct completion *tx_wait; }; /* Per interface specific private data */ struct mwl8k_vif { - struct list_head node; - /* backpointer to parent config block */ struct mwl8k_priv *priv; @@ -235,8 +213,8 @@ struct mwl8k_vif { struct ieee80211_bss_conf bss_info; /* BSSID of AP or IBSS */ - u8 bssid[IEEE80211_ADDR_LEN]; - u8 mac_addr[IEEE80211_ADDR_LEN]; + u8 bssid[ETH_ALEN]; + u8 mac_addr[ETH_ALEN]; /* * Subset of supported legacy rates. @@ -247,18 +225,11 @@ struct mwl8k_vif { /* number of supported legacy rates */ u8 legacy_nrates; - /* Number of supported MCS rates. Work in progress */ - u8 mcs_nrates; - /* Index into station database.Returned by update_sta_db call */ u8 peer_id; /* Non AMPDU sequence number assigned by driver */ u16 seqno; - - /* Note:There is no channel info, - * refer to the master channel info in priv - */ }; #define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) @@ -292,28 +263,6 @@ static const struct ieee80211_rate mwl8k_rates[] = { { .bitrate = 540, .hw_value = 108, }, }; -/* Radio settings */ -#define MWL8K_RADIO_FORCE 0x2 -#define MWL8K_RADIO_ENABLE 0x1 -#define MWL8K_RADIO_DISABLE 0x0 -#define MWL8K_RADIO_AUTO_PREAMBLE 0x0005 -#define MWL8K_RADIO_SHORT_PREAMBLE 0x0003 -#define MWL8K_RADIO_LONG_PREAMBLE 0x0001 - -/* WMM */ -#define MWL8K_WMM_ENABLE 1 -#define MWL8K_WMM_DISABLE 0 - -#define MWL8K_RADIO_DEFAULT_PREAMBLE MWL8K_RADIO_LONG_PREAMBLE - -/* Slot time */ - -/* Short Slot: 9us slot time */ -#define MWL8K_SHORT_SLOTTIME 1 - -/* Long slot: 20us slot time */ -#define MWL8K_LONG_SLOTTIME 0 - /* Set or get info from Firmware */ #define MWL8K_CMD_SET 0x0001 #define MWL8K_CMD_GET 0x0000 @@ -323,25 +272,23 @@ static const struct ieee80211_rate mwl8k_rates[] = { #define MWL8K_CMD_GET_HW_SPEC 0x0003 #define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010 #define MWL8K_CMD_GET_STAT 0x0014 -#define MWL8K_CMD_RADIO_CONTROL 0x001C -#define MWL8K_CMD_RF_TX_POWER 0x001E +#define MWL8K_CMD_RADIO_CONTROL 0x001c +#define MWL8K_CMD_RF_TX_POWER 0x001e #define MWL8K_CMD_SET_PRE_SCAN 0x0107 #define MWL8K_CMD_SET_POST_SCAN 0x0108 -#define MWL8K_CMD_SET_RF_CHANNEL 0x010A +#define MWL8K_CMD_SET_RF_CHANNEL 0x010a +#define MWL8K_CMD_SET_AID 0x010d +#define MWL8K_CMD_SET_RATE 0x0110 +#define MWL8K_CMD_SET_FINALIZE_JOIN 0x0111 +#define MWL8K_CMD_RTS_THRESHOLD 0x0113 #define MWL8K_CMD_SET_SLOT 0x0114 +#define MWL8K_CMD_SET_EDCA_PARAMS 0x0115 +#define MWL8K_CMD_SET_WMM_MODE 0x0123 #define MWL8K_CMD_MIMO_CONFIG 0x0125 +#define MWL8K_CMD_USE_FIXED_RATE 0x0126 #define MWL8K_CMD_ENABLE_SNIFFER 0x0150 -#define MWL8K_CMD_SET_WMM_MODE 0x0123 -#define MWL8K_CMD_SET_EDCA_PARAMS 0x0115 -#define MWL8K_CMD_SET_FINALIZE_JOIN 0x0111 -#define MWL8K_CMD_UPDATE_STADB 0x1123 #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 -#define MWL8K_CMD_SET_LINKADAPT_MODE 0x0129 -#define MWL8K_CMD_SET_AID 0x010d -#define MWL8K_CMD_SET_RATE 0x0110 -#define MWL8K_CMD_USE_FIXED_RATE 0x0126 -#define MWL8K_CMD_RTS_THRESHOLD 0x0113 -#define MWL8K_CMD_ENCRYPTION 0x1122 +#define MWL8K_CMD_UPDATE_STADB 0x1123 static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) { @@ -349,7 +296,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) snprintf(buf, bufsize, "%s", #x);\ return buf;\ } while (0) - switch (cmd & (~0x8000)) { + switch (cmd & ~0x8000) { MWL8K_CMDNAME(CODE_DNLD); MWL8K_CMDNAME(GET_HW_SPEC); MWL8K_CMDNAME(MAC_MULTICAST_ADR); @@ -359,20 +306,18 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) MWL8K_CMDNAME(SET_PRE_SCAN); MWL8K_CMDNAME(SET_POST_SCAN); MWL8K_CMDNAME(SET_RF_CHANNEL); + MWL8K_CMDNAME(SET_AID); + MWL8K_CMDNAME(SET_RATE); + MWL8K_CMDNAME(SET_FINALIZE_JOIN); + MWL8K_CMDNAME(RTS_THRESHOLD); MWL8K_CMDNAME(SET_SLOT); + MWL8K_CMDNAME(SET_EDCA_PARAMS); + MWL8K_CMDNAME(SET_WMM_MODE); MWL8K_CMDNAME(MIMO_CONFIG); + MWL8K_CMDNAME(USE_FIXED_RATE); MWL8K_CMDNAME(ENABLE_SNIFFER); - MWL8K_CMDNAME(SET_WMM_MODE); - MWL8K_CMDNAME(SET_EDCA_PARAMS); - MWL8K_CMDNAME(SET_FINALIZE_JOIN); - MWL8K_CMDNAME(UPDATE_STADB); MWL8K_CMDNAME(SET_RATEADAPT_MODE); - MWL8K_CMDNAME(SET_LINKADAPT_MODE); - MWL8K_CMDNAME(SET_AID); - MWL8K_CMDNAME(SET_RATE); - MWL8K_CMDNAME(USE_FIXED_RATE); - MWL8K_CMDNAME(RTS_THRESHOLD); - MWL8K_CMDNAME(ENCRYPTION); + MWL8K_CMDNAME(UPDATE_STADB); default: snprintf(buf, bufsize, "0x%x", cmd); } @@ -466,7 +411,6 @@ mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length) { void __iomem *regs = priv->regs; dma_addr_t dma_addr; - int rc; int loops; dma_addr = pci_map_single(priv->pdev, data, length, PCI_DMA_TODEVICE); @@ -480,7 +424,6 @@ mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length) iowrite32(MWL8K_H2A_INT_DUMMY, regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); - rc = -ETIMEDOUT; loops = 1000; do { u32 int_code; @@ -488,7 +431,6 @@ mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length) int_code = ioread32(regs + MWL8K_HIU_INT_CODE); if (int_code == MWL8K_INT_CODE_CMD_FINISHED) { iowrite32(0, regs + MWL8K_HIU_INT_CODE); - rc = 0; break; } @@ -497,26 +439,7 @@ mwl8k_send_fw_load_cmd(struct mwl8k_priv *priv, void *data, int length) pci_unmap_single(priv->pdev, dma_addr, length, PCI_DMA_TODEVICE); - /* - * Clear 'command done' interrupt bit. - */ - loops = 1000; - do { - u32 status; - - status = ioread32(priv->regs + - MWL8K_HIU_A2H_INTERRUPT_STATUS); - if (status & MWL8K_A2H_INT_OPC_DONE) { - iowrite32(~MWL8K_A2H_INT_OPC_DONE, - priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - break; - } - - udelay(1); - } while (--loops); - - return rc; + return loops ? 0 : -ETIMEDOUT; } static int mwl8k_load_fw_image(struct mwl8k_priv *priv, @@ -681,11 +604,9 @@ struct ewc_ht_info { /* Peer Entry flags - used to define the type of the peer node */ #define MWL8K_PEER_TYPE_ACCESSPOINT 2 -#define MWL8K_PEER_TYPE_ADHOC_STATION 4 #define MWL8K_IEEE_LEGACY_DATA_RATES 12 #define MWL8K_MCS_BITMAP_SIZE 16 -#define pad_size 16 struct peer_capability_info { /* Peer type - AP vs. STA. */ @@ -707,7 +628,7 @@ struct peer_capability_info { /* HT rate table. Intersection of our rates and peer rates. */ __u8 ht_rates[MWL8K_MCS_BITMAP_SIZE]; - __u8 pad[pad_size]; + __u8 pad[16]; /* If set, interoperability mode, no proprietary extensions. */ __u8 interop; @@ -717,15 +638,6 @@ struct peer_capability_info { } __attribute__((packed)); /* Inline functions to manipulate QoS field in data descriptor. */ -static inline u16 mwl8k_qos_setbit_tid(u16 qos, u8 tid) -{ - u16 val_mask = 0x000f; - u16 qos_mask = ~val_mask; - - /* TID bits 0-3 */ - return (qos & qos_mask) | (tid & val_mask); -} - static inline u16 mwl8k_qos_setbit_eosp(u16 qos) { u16 val_mask = 1 << 4; @@ -769,12 +681,11 @@ struct mwl8k_dma_data { } __attribute__((packed)); /* Routines to add/remove DMA header from skb. */ -static inline int mwl8k_remove_dma_header(struct sk_buff *skb) +static inline void mwl8k_remove_dma_header(struct sk_buff *skb) { - struct mwl8k_dma_data *tr = (struct mwl8k_dma_data *)(skb->data); + struct mwl8k_dma_data *tr = (struct mwl8k_dma_data *)skb->data; void *dst, *src = &tr->wh; - __le16 fc = tr->wh.frame_control; - int hdrlen = ieee80211_hdrlen(fc); + int hdrlen = ieee80211_hdrlen(tr->wh.frame_control); u16 space = sizeof(struct mwl8k_dma_data) - hdrlen; dst = (void *)tr + space; @@ -782,11 +693,9 @@ static inline int mwl8k_remove_dma_header(struct sk_buff *skb) memmove(dst, src, hdrlen); skb_pull(skb, space); } - - return 0; } -static inline struct sk_buff *mwl8k_add_dma_header(struct sk_buff *skb) +static inline void mwl8k_add_dma_header(struct sk_buff *skb) { struct ieee80211_hdr *wh; u32 hdrlen, pktlen; @@ -810,7 +719,7 @@ static inline struct sk_buff *mwl8k_add_dma_header(struct sk_buff *skb) memmove(&tr->wh, wh, hdrlen); /* Clear addr4 */ - memset(tr->wh.addr4, 0, IEEE80211_ADDR_LEN); + memset(tr->wh.addr4, 0, ETH_ALEN); /* * Firmware length is the length of the fully formed "802.11 @@ -818,17 +727,13 @@ static inline struct sk_buff *mwl8k_add_dma_header(struct sk_buff *skb) * This includes all crypto material including the MIC. */ tr->fwlen = cpu_to_le16(pktlen - hdrlen); - - return skb; } /* * Packet reception. */ -#define MWL8K_RX_CTRL_KEY_INDEX_MASK 0x30 #define MWL8K_RX_CTRL_OWNED_BY_HOST 0x02 -#define MWL8K_RX_CTRL_AMPDU 0x01 struct mwl8k_rx_desc { __le16 pkt_len; @@ -979,7 +884,7 @@ static inline void mwl8k_save_beacon(struct mwl8k_priv *priv, struct sk_buff *skb) { priv->capture_beacon = false; - memset(priv->capture_bssid, 0, IEEE80211_ADDR_LEN); + memset(priv->capture_bssid, 0, ETH_ALEN); /* * Use GFP_ATOMIC as rxq_process is called from @@ -1024,10 +929,7 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit) MWL8K_RX_MAXSZ, PCI_DMA_FROMDEVICE); skb_put(skb, le16_to_cpu(rx_desc->pkt_len)); - if (mwl8k_remove_dma_header(skb)) { - dev_kfree_skb(skb); - continue; - } + mwl8k_remove_dma_header(skb); wh = (struct ieee80211_hdr *)skb->data; @@ -1049,7 +951,8 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit) status.flag = 0; status.band = IEEE80211_BAND_2GHZ; status.freq = ieee80211_channel_to_frequency(rx_desc->channel); - ieee80211_rx_irqsafe(hw, skb, &status); + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + ieee80211_rx_irqsafe(hw, skb); processed++; } @@ -1072,8 +975,6 @@ enum { /* Transmit packet ACK policy */ #define MWL8K_TXD_ACK_POLICY_NORMAL 0 -#define MWL8K_TXD_ACK_POLICY_NONE 1 -#define MWL8K_TXD_ACK_POLICY_NO_EXPLICIT 2 #define MWL8K_TXD_ACK_POLICY_BLOCKACK 3 #define GET_TXQ(_ac) (\ @@ -1082,20 +983,11 @@ enum { ((_ac) == WME_AC_BK) ? MWL8K_WME_AC_BK : \ MWL8K_WME_AC_BE) -#define MWL8K_TXD_STATUS_IDLE 0x00000000 -#define MWL8K_TXD_STATUS_USED 0x00000001 #define MWL8K_TXD_STATUS_OK 0x00000001 #define MWL8K_TXD_STATUS_OK_RETRY 0x00000002 #define MWL8K_TXD_STATUS_OK_MORE_RETRY 0x00000004 #define MWL8K_TXD_STATUS_MULTICAST_TX 0x00000008 -#define MWL8K_TXD_STATUS_BROADCAST_TX 0x00000010 -#define MWL8K_TXD_STATUS_FAILED_LINK_ERROR 0x00000020 -#define MWL8K_TXD_STATUS_FAILED_EXCEED_LIMIT 0x00000040 -#define MWL8K_TXD_STATUS_FAILED_AGING 0x00000080 -#define MWL8K_TXD_STATUS_HOST_CMD 0x40000000 #define MWL8K_TXD_STATUS_FW_OWNED 0x80000000 -#define MWL8K_TXD_SOFTSTALE 0x80 -#define MWL8K_TXD_SOFTSTALE_MGMT_RETRY 0x01 struct mwl8k_tx_desc { __le32 status; @@ -1104,7 +996,7 @@ struct mwl8k_tx_desc { __le16 qos_control; __le32 pkt_phys_addr; __le16 pkt_len; - __u8 dest_MAC_addr[IEEE80211_ADDR_LEN]; + __u8 dest_MAC_addr[ETH_ALEN]; __le32 next_tx_desc_phys_addr; __le32 reserved; __le16 rate_info; @@ -1121,8 +1013,7 @@ static int mwl8k_txq_init(struct ieee80211_hw *hw, int index) int size; int i; - memset(&txq->tx_stats, 0, - sizeof(struct ieee80211_tx_queue_stats)); + memset(&txq->tx_stats, 0, sizeof(struct ieee80211_tx_queue_stats)); txq->tx_stats.limit = MWL8K_TX_DESCS; txq->tx_head = 0; txq->tx_tail = 0; @@ -1189,17 +1080,17 @@ struct mwl8k_txq_info { }; static int mwl8k_scan_tx_ring(struct mwl8k_priv *priv, - struct mwl8k_txq_info txinfo[], - u32 num_queues) + struct mwl8k_txq_info *txinfo) { int count, desc, status; struct mwl8k_tx_queue *txq; struct mwl8k_tx_desc *tx_desc; int ndescs = 0; - memset(txinfo, 0, num_queues * sizeof(struct mwl8k_txq_info)); + memset(txinfo, 0, MWL8K_TX_QUEUES * sizeof(struct mwl8k_txq_info)); + spin_lock_bh(&priv->tx_lock); - for (count = 0; count < num_queues; count++) { + for (count = 0; count < MWL8K_TX_QUEUES; count++) { txq = priv->txq + count; txinfo[count].len = txq->tx_stats.len; txinfo[count].head = txq->tx_head; @@ -1222,34 +1113,34 @@ static int mwl8k_scan_tx_ring(struct mwl8k_priv *priv, return ndescs; } -static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw, u32 delay_ms) +/* + * Must be called with hw->fw_mutex held and tx queues stopped. + */ +static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) { - u32 count = 0; - unsigned long timeout = 0; struct mwl8k_priv *priv = hw->priv; DECLARE_COMPLETION_ONSTACK(cmd_wait); + u32 count; + unsigned long timeout; might_sleep(); - if (priv->tx_wait != NULL) - printk(KERN_ERR "WARNING Previous TXWaitEmpty instance\n"); - spin_lock_bh(&priv->tx_lock); count = mwl8k_txq_busy(priv); if (count) { priv->tx_wait = &cmd_wait; - if (priv->radio_state) + if (priv->radio_on) mwl8k_tx_start(priv); } spin_unlock_bh(&priv->tx_lock); if (count) { - struct mwl8k_txq_info txinfo[4]; + struct mwl8k_txq_info txinfo[MWL8K_TX_QUEUES]; int index; int newcount; timeout = wait_for_completion_timeout(&cmd_wait, - msecs_to_jiffies(delay_ms)); + msecs_to_jiffies(5000)); if (timeout) return 0; @@ -1258,11 +1149,11 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw, u32 delay_ms) newcount = mwl8k_txq_busy(priv); spin_unlock_bh(&priv->tx_lock); - printk(KERN_ERR "%s(%u) TIMEDOUT:%ums Pend:%u-->%u\n", - __func__, __LINE__, delay_ms, count, newcount); + printk(KERN_ERR "%s(%u) TIMEDOUT:5000ms Pend:%u-->%u\n", + __func__, __LINE__, count, newcount); - mwl8k_scan_tx_ring(priv, txinfo, 4); - for (index = 0 ; index < 4; index++) + mwl8k_scan_tx_ring(priv, txinfo); + for (index = 0; index < MWL8K_TX_QUEUES; index++) printk(KERN_ERR "TXQ:%u L:%u H:%u T:%u FW:%u DRV:%u U:%u\n", index, @@ -1272,18 +1163,17 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw, u32 delay_ms) txinfo[index].fw_owned, txinfo[index].drv_owned, txinfo[index].unused); + return -ETIMEDOUT; } return 0; } -#define MWL8K_TXD_OK (MWL8K_TXD_STATUS_OK | \ - MWL8K_TXD_STATUS_OK_RETRY | \ - MWL8K_TXD_STATUS_OK_MORE_RETRY) -#define MWL8K_TXD_SUCCESS(stat) ((stat) & MWL8K_TXD_OK) -#define MWL8K_TXD_FAIL_RETRY(stat) \ - ((stat) & (MWL8K_TXD_STATUS_FAILED_EXCEED_LIMIT)) +#define MWL8K_TXD_SUCCESS(status) \ + ((status) & (MWL8K_TXD_STATUS_OK | \ + MWL8K_TXD_STATUS_OK_RETRY | \ + MWL8K_TXD_STATUS_OK_MORE_RETRY)) static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) { @@ -1293,15 +1183,13 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) while (txq->tx_stats.len > 0) { int tx; - int rc; struct mwl8k_tx_desc *tx_desc; unsigned long addr; - size_t size; + int size; struct sk_buff *skb; struct ieee80211_tx_info *info; u32 status; - rc = 0; tx = txq->tx_head; tx_desc = txq->tx_desc_area + tx; @@ -1320,56 +1208,30 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) priv->pending_tx_pkts--; addr = le32_to_cpu(tx_desc->pkt_phys_addr); - size = (u32)(le16_to_cpu(tx_desc->pkt_len)); - skb = txq->tx_skb[tx].skb; - txq->tx_skb[tx].skb = NULL; + size = le16_to_cpu(tx_desc->pkt_len); + skb = txq->tx_skb[tx]; + txq->tx_skb[tx] = NULL; BUG_ON(skb == NULL); pci_unmap_single(priv->pdev, addr, size, PCI_DMA_TODEVICE); - rc = mwl8k_remove_dma_header(skb); + mwl8k_remove_dma_header(skb); /* Mark descriptor as unused */ tx_desc->pkt_phys_addr = 0; tx_desc->pkt_len = 0; - if (txq->tx_skb[tx].clone) { - /* Replace with original skb - * before returning to stack - * as buffer has been cloned - */ - dev_kfree_skb(skb); - skb = txq->tx_skb[tx].clone; - txq->tx_skb[tx].clone = NULL; - } - - if (rc) { - /* Something has gone wrong here. - * Failed to remove DMA header. - * Print error message and drop packet. - */ - printk(KERN_ERR "%s: Error removing DMA header from " - "tx skb 0x%p.\n", priv->name, skb); - - dev_kfree_skb(skb); - continue; - } - info = IEEE80211_SKB_CB(skb); ieee80211_tx_info_clear_status(info); - - /* Convert firmware status stuff into tx_status */ - if (MWL8K_TXD_SUCCESS(status)) { - /* Transmit OK */ + if (MWL8K_TXD_SUCCESS(status)) info->flags |= IEEE80211_TX_STAT_ACK; - } ieee80211_tx_status_irqsafe(hw, skb); - wake = !priv->inconfig && priv->radio_state; + wake = 1; } - if (wake) + if (wake && priv->radio_on && !mutex_is_locked(&priv->fw_mutex)) ieee80211_wake_queue(hw, index); } @@ -1395,56 +1257,60 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) { struct mwl8k_priv *priv = hw->priv; struct ieee80211_tx_info *tx_info; + struct mwl8k_vif *mwl8k_vif; struct ieee80211_hdr *wh; struct mwl8k_tx_queue *txq; struct mwl8k_tx_desc *tx; - struct mwl8k_dma_data *tr; - struct mwl8k_vif *mwl8k_vif; - struct sk_buff *org_skb = skb; dma_addr_t dma; - u16 qos = 0; - bool qosframe = false, ampduframe = false; - bool mcframe = false, eapolframe = false; - bool amsduframe = false; - __le16 fc; - - txq = priv->txq + index; - tx = txq->tx_desc_area + txq->tx_tail; + u32 txstatus; + u8 txdatarate; + u16 qos; - BUG_ON(txq->tx_skb[txq->tx_tail].skb != NULL); + wh = (struct ieee80211_hdr *)skb->data; + if (ieee80211_is_data_qos(wh->frame_control)) + qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh))); + else + qos = 0; - /* - * Append HW DMA header to start of packet. Drop packet if - * there is not enough space or a failure to unshare/unclone - * the skb. - */ - skb = mwl8k_add_dma_header(skb); - - if (skb == NULL) { - printk(KERN_DEBUG "%s: failed to prepend HW DMA " - "header, dropping TX frame.\n", priv->name); - dev_kfree_skb(org_skb); - return NETDEV_TX_OK; - } + mwl8k_add_dma_header(skb); + wh = &((struct mwl8k_dma_data *)skb->data)->wh; tx_info = IEEE80211_SKB_CB(skb); mwl8k_vif = MWL8K_VIF(tx_info->control.vif); - tr = (struct mwl8k_dma_data *)skb->data; - wh = &tr->wh; - fc = wh->frame_control; - qosframe = ieee80211_is_data_qos(fc); - mcframe = is_multicast_ether_addr(wh->addr1); - ampduframe = !!(tx_info->flags & IEEE80211_TX_CTL_AMPDU); if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { u16 seqno = mwl8k_vif->seqno; + wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); wh->seq_ctrl |= cpu_to_le16(seqno << 4); mwl8k_vif->seqno = seqno++ % 4096; } - if (qosframe) - qos = le16_to_cpu(*((__le16 *)ieee80211_get_qos_ctl(wh))); + /* Setup firmware control bit fields for each frame type. */ + txstatus = 0; + txdatarate = 0; + if (ieee80211_is_mgmt(wh->frame_control) || + ieee80211_is_ctl(wh->frame_control)) { + txdatarate = 0; + qos = mwl8k_qos_setbit_eosp(qos); + /* Set Queue size to unspecified */ + qos = mwl8k_qos_setbit_qlen(qos, 0xff); + } else if (ieee80211_is_data(wh->frame_control)) { + txdatarate = 1; + if (is_multicast_ether_addr(wh->addr1)) + txstatus |= MWL8K_TXD_STATUS_MULTICAST_TX; + + /* Send pkt in an aggregate if AMPDU frame. */ + if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) + qos = mwl8k_qos_setbit_ack(qos, + MWL8K_TXD_ACK_POLICY_BLOCKACK); + else + qos = mwl8k_qos_setbit_ack(qos, + MWL8K_TXD_ACK_POLICY_NORMAL); + + if (qos & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT) + qos = mwl8k_qos_setbit_amsdu(qos); + } dma = pci_map_single(priv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); @@ -1452,99 +1318,40 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) if (pci_dma_mapping_error(priv->pdev, dma)) { printk(KERN_DEBUG "%s: failed to dma map skb, " "dropping TX frame.\n", priv->name); - - if (org_skb != NULL) - dev_kfree_skb(org_skb); - if (skb != NULL) - dev_kfree_skb(skb); + dev_kfree_skb(skb); return NETDEV_TX_OK; } - /* Set desc header, cpu bit order. */ - tx->status = 0; - tx->data_rate = 0; - tx->tx_priority = index; - tx->qos_control = 0; - tx->rate_info = 0; - tx->peer_id = mwl8k_vif->peer_id; - - amsduframe = !!(qos & IEEE80211_QOS_CONTROL_A_MSDU_PRESENT); - - /* Setup firmware control bit fields for each frame type. */ - if (ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc)) { - tx->data_rate = 0; - qos = mwl8k_qos_setbit_eosp(qos); - /* Set Queue size to unspecified */ - qos = mwl8k_qos_setbit_qlen(qos, 0xff); - } else if (ieee80211_is_data(fc)) { - tx->data_rate = 1; - if (mcframe) - tx->status |= MWL8K_TXD_STATUS_MULTICAST_TX; + spin_lock_bh(&priv->tx_lock); - /* - * Tell firmware to not send EAPOL pkts in an - * aggregate. Verify against mac80211 tx path. If - * stack turns off AMPDU for an EAPOL frame this - * check will be removed. - */ - if (eapolframe) { - qos = mwl8k_qos_setbit_ack(qos, - MWL8K_TXD_ACK_POLICY_NORMAL); - } else { - /* Send pkt in an aggregate if AMPDU frame. */ - if (ampduframe) - qos = mwl8k_qos_setbit_ack(qos, - MWL8K_TXD_ACK_POLICY_BLOCKACK); - else - qos = mwl8k_qos_setbit_ack(qos, - MWL8K_TXD_ACK_POLICY_NORMAL); + txq = priv->txq + index; - if (amsduframe) - qos = mwl8k_qos_setbit_amsdu(qos); - } - } + BUG_ON(txq->tx_skb[txq->tx_tail] != NULL); + txq->tx_skb[txq->tx_tail] = skb; - /* Convert to little endian */ + tx = txq->tx_desc_area + txq->tx_tail; + tx->data_rate = txdatarate; + tx->tx_priority = index; tx->qos_control = cpu_to_le16(qos); - tx->status = cpu_to_le32(tx->status); tx->pkt_phys_addr = cpu_to_le32(dma); tx->pkt_len = cpu_to_le16(skb->len); - - txq->tx_skb[txq->tx_tail].skb = skb; - txq->tx_skb[txq->tx_tail].clone = - skb == org_skb ? NULL : org_skb; - - spin_lock_bh(&priv->tx_lock); - - tx->status = cpu_to_le32(MWL8K_TXD_STATUS_OK | - MWL8K_TXD_STATUS_FW_OWNED); + tx->rate_info = 0; + tx->peer_id = mwl8k_vif->peer_id; wmb(); + tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus); + + txq->tx_stats.count++; txq->tx_stats.len++; priv->pending_tx_pkts++; - txq->tx_stats.count++; - txq->tx_tail++; + txq->tx_tail++; if (txq->tx_tail == MWL8K_TX_DESCS) txq->tx_tail = 0; + if (txq->tx_head == txq->tx_tail) ieee80211_stop_queue(hw, index); - if (priv->inconfig) { - /* - * Silently queue packet when we are in the middle of - * a config cycle. Notify firmware only if we are - * waiting for TXQs to empty. If a packet is sent - * before .config() is complete, perhaps it is better - * to drop the packet, as the channel is being changed - * and the packet will end up on the wrong channel. - */ - printk(KERN_ERR "%s(): WARNING TX activity while " - "in config\n", __func__); - - if (priv->tx_wait != NULL) - mwl8k_tx_start(priv); - } else - mwl8k_tx_start(priv); + mwl8k_tx_start(priv); spin_unlock_bh(&priv->tx_lock); @@ -1553,6 +1360,60 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) /* + * Firmware access. + * + * We have the following requirements for issuing firmware commands: + * - Some commands require that the packet transmit path is idle when + * the command is issued. (For simplicity, we'll just quiesce the + * transmit path for every command.) + * - There are certain sequences of commands that need to be issued to + * the hardware sequentially, with no other intervening commands. + * + * This leads to an implementation of a "firmware lock" as a mutex that + * can be taken recursively, and which is taken by both the low-level + * command submission function (mwl8k_post_cmd) as well as any users of + * that function that require issuing of an atomic sequence of commands, + * and quiesces the transmit path whenever it's taken. + */ +static int mwl8k_fw_lock(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + + if (priv->fw_mutex_owner != current) { + int rc; + + mutex_lock(&priv->fw_mutex); + ieee80211_stop_queues(hw); + + rc = mwl8k_tx_wait_empty(hw); + if (rc) { + ieee80211_wake_queues(hw); + mutex_unlock(&priv->fw_mutex); + + return rc; + } + + priv->fw_mutex_owner = current; + } + + priv->fw_mutex_depth++; + + return 0; +} + +static void mwl8k_fw_unlock(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + + if (!--priv->fw_mutex_depth) { + ieee80211_wake_queues(hw); + priv->fw_mutex_owner = NULL; + mutex_unlock(&priv->fw_mutex); + } +} + + +/* * Command processing. */ @@ -1567,7 +1428,6 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) dma_addr_t dma_addr; unsigned int dma_size; int rc; - u16 __iomem *result; unsigned long timeout = 0; u8 buf[32]; @@ -1578,41 +1438,43 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) if (pci_dma_mapping_error(priv->pdev, dma_addr)) return -ENOMEM; - if (priv->hostcmd_wait != NULL) - printk(KERN_ERR "WARNING host command in progress\n"); + rc = mwl8k_fw_lock(hw); + if (rc) { + pci_unmap_single(priv->pdev, dma_addr, dma_size, + PCI_DMA_BIDIRECTIONAL); + return rc; + } - spin_lock_irq(&priv->fw_lock); priv->hostcmd_wait = &cmd_wait; iowrite32(dma_addr, regs + MWL8K_HIU_GEN_PTR); iowrite32(MWL8K_H2A_INT_DOORBELL, regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); iowrite32(MWL8K_H2A_INT_DUMMY, regs + MWL8K_HIU_H2A_INTERRUPT_EVENTS); - spin_unlock_irq(&priv->fw_lock); timeout = wait_for_completion_timeout(&cmd_wait, msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS)); + priv->hostcmd_wait = NULL; + + mwl8k_fw_unlock(hw); + pci_unmap_single(priv->pdev, dma_addr, dma_size, PCI_DMA_BIDIRECTIONAL); - result = &cmd->result; if (!timeout) { - spin_lock_irq(&priv->fw_lock); - priv->hostcmd_wait = NULL; - spin_unlock_irq(&priv->fw_lock); printk(KERN_ERR "%s: Command %s timeout after %u ms\n", priv->name, mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), MWL8K_CMD_TIMEOUT_MS); rc = -ETIMEDOUT; } else { - rc = *result ? -EINVAL : 0; + rc = cmd->result ? -EINVAL : 0; if (rc) printk(KERN_ERR "%s: Command %s error 0x%x\n", priv->name, mwl8k_cmd_name(cmd->code, buf, sizeof(buf)), - *result); + le16_to_cpu(cmd->result)); } return rc; @@ -1626,7 +1488,7 @@ struct mwl8k_cmd_get_hw_spec { __u8 hw_rev; __u8 host_interface; __le16 num_mcaddrs; - __u8 perm_addr[IEEE80211_ADDR_LEN]; + __u8 perm_addr[ETH_ALEN]; __le16 region_code; __le32 fw_rev; __le32 ps_cookie; @@ -1670,7 +1532,6 @@ static int mwl8k_cmd_get_hw_spec(struct ieee80211_hw *hw) priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; - priv->region_code = le16_to_cpu(cmd->region_code); } kfree(cmd); @@ -1684,41 +1545,44 @@ struct mwl8k_cmd_mac_multicast_adr { struct mwl8k_cmd_pkt header; __le16 action; __le16 numaddr; - __u8 addr[1][IEEE80211_ADDR_LEN]; + __u8 addr[0][ETH_ALEN]; }; #define MWL8K_ENABLE_RX_MULTICAST 0x000F -static int mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, - int mc_count, - struct dev_addr_list *mclist) + +static struct mwl8k_cmd_pkt * +__mwl8k_cmd_mac_multicast_adr(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) { + struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_mac_multicast_adr *cmd; - int index = 0; - int rc; - int size = sizeof(*cmd) + ((mc_count - 1) * IEEE80211_ADDR_LEN); - cmd = kzalloc(size, GFP_KERNEL); + int size; + int i; + + if (mc_count > priv->num_mcaddrs) + mc_count = priv->num_mcaddrs; + + size = sizeof(*cmd) + mc_count * ETH_ALEN; + + cmd = kzalloc(size, GFP_ATOMIC); if (cmd == NULL) - return -ENOMEM; + return NULL; cmd->header.code = cpu_to_le16(MWL8K_CMD_MAC_MULTICAST_ADR); cmd->header.length = cpu_to_le16(size); cmd->action = cpu_to_le16(MWL8K_ENABLE_RX_MULTICAST); cmd->numaddr = cpu_to_le16(mc_count); - while ((index < mc_count) && mclist) { - if (mclist->da_addrlen != IEEE80211_ADDR_LEN) { - rc = -EINVAL; - goto mwl8k_cmd_mac_multicast_adr_exit; + + for (i = 0; i < mc_count && mclist; i++) { + if (mclist->da_addrlen != ETH_ALEN) { + kfree(cmd); + return NULL; } - memcpy(cmd->addr[index], mclist->da_addr, IEEE80211_ADDR_LEN); - index++; + memcpy(cmd->addr[i], mclist->da_addr, ETH_ALEN); mclist = mclist->next; } - rc = mwl8k_post_cmd(hw, &cmd->header); - -mwl8k_cmd_mac_multicast_adr_exit: - kfree(cmd); - return rc; + return &cmd->header; } /* @@ -1775,18 +1639,16 @@ struct mwl8k_cmd_802_11_radio_control { __le16 radio_on; } __attribute__((packed)); -static int mwl8k_cmd_802_11_radio_control(struct ieee80211_hw *hw, int enable) +static int +mwl8k_cmd_802_11_radio_control(struct ieee80211_hw *hw, bool enable, bool force) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_802_11_radio_control *cmd; int rc; - if (((enable & MWL8K_RADIO_ENABLE) == priv->radio_state) && - !(enable & MWL8K_RADIO_FORCE)) + if (enable == priv->radio_on && !force) return 0; - enable &= MWL8K_RADIO_ENABLE; - cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; @@ -1794,18 +1656,28 @@ static int mwl8k_cmd_802_11_radio_control(struct ieee80211_hw *hw, int enable) cmd->header.code = cpu_to_le16(MWL8K_CMD_RADIO_CONTROL); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET); - cmd->control = cpu_to_le16(priv->radio_preamble); + cmd->control = cpu_to_le16(priv->radio_short_preamble ? 3 : 1); cmd->radio_on = cpu_to_le16(enable ? 0x0001 : 0x0000); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); if (!rc) - priv->radio_state = enable; + priv->radio_on = enable; return rc; } +static int mwl8k_cmd_802_11_radio_disable(struct ieee80211_hw *hw) +{ + return mwl8k_cmd_802_11_radio_control(hw, 0, 0); +} + +static int mwl8k_cmd_802_11_radio_enable(struct ieee80211_hw *hw) +{ + return mwl8k_cmd_802_11_radio_control(hw, 1, 0); +} + static int mwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble) { @@ -1815,12 +1687,9 @@ mwl8k_set_radio_preamble(struct ieee80211_hw *hw, bool short_preamble) return -EINVAL; priv = hw->priv; - priv->radio_preamble = (short_preamble ? - MWL8K_RADIO_SHORT_PREAMBLE : - MWL8K_RADIO_LONG_PREAMBLE); + priv->radio_short_preamble = short_preamble; - return mwl8k_cmd_802_11_radio_control(hw, - MWL8K_RADIO_ENABLE | MWL8K_RADIO_FORCE); + return mwl8k_cmd_802_11_radio_control(hw, 1, 1); } /* @@ -1888,11 +1757,11 @@ static int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw) struct mwl8k_cmd_set_post_scan { struct mwl8k_cmd_pkt header; __le32 isibss; - __u8 bssid[IEEE80211_ADDR_LEN]; + __u8 bssid[ETH_ALEN]; } __attribute__((packed)); static int -mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, __u8 mac[IEEE80211_ADDR_LEN]) +mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, __u8 *mac) { struct mwl8k_cmd_set_post_scan *cmd; int rc; @@ -1904,7 +1773,7 @@ mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, __u8 mac[IEEE80211_ADDR_LEN]) cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_POST_SCAN); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->isibss = 0; - memcpy(cmd->bssid, mac, IEEE80211_ADDR_LEN); + memcpy(cmd->bssid, mac, ETH_ALEN); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -1956,7 +1825,7 @@ struct mwl8k_cmd_set_slot { __u8 short_slot; } __attribute__((packed)); -static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, int slot_time) +static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, bool short_slot_time) { struct mwl8k_cmd_set_slot *cmd; int rc; @@ -1968,7 +1837,7 @@ static int mwl8k_cmd_set_slot(struct ieee80211_hw *hw, int slot_time) cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_SLOT); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET); - cmd->short_slot = slot_time == MWL8K_SHORT_SLOTTIME ? 1 : 0; + cmd->short_slot = short_slot_time; rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2026,7 +1895,7 @@ static int mwl8k_enable_sniffer(struct ieee80211_hw *hw, bool enable) cmd->header.code = cpu_to_le16(MWL8K_CMD_ENABLE_SNIFFER); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = enable ? cpu_to_le32((u32)MWL8K_CMD_SET) : 0; + cmd->action = cpu_to_le32(!!enable); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2035,7 +1904,7 @@ static int mwl8k_enable_sniffer(struct ieee80211_hw *hw, bool enable) } /* - * CMD_SET_RATE_ADAPT_MODE. + * CMD_SET_RATEADAPT_MODE. */ struct mwl8k_cmd_set_rate_adapt_mode { struct mwl8k_cmd_pkt header; @@ -2083,13 +1952,13 @@ static int mwl8k_set_wmm(struct ieee80211_hw *hw, bool enable) cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_WMM_MODE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = enable ? cpu_to_le16(MWL8K_CMD_SET) : 0; + cmd->action = cpu_to_le16(!!enable); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); if (!rc) - priv->wmm_mode = enable; + priv->wmm_enabled = enable; return rc; } @@ -2104,7 +1973,7 @@ struct mwl8k_cmd_rts_threshold { } __attribute__((packed)); static int mwl8k_rts_threshold(struct ieee80211_hw *hw, - u16 action, u16 *threshold) + u16 action, u16 threshold) { struct mwl8k_cmd_rts_threshold *cmd; int rc; @@ -2116,7 +1985,7 @@ static int mwl8k_rts_threshold(struct ieee80211_hw *hw, cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(action); - cmd->threshold = cpu_to_le16(*threshold); + cmd->threshold = cpu_to_le16(threshold); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2149,7 +2018,6 @@ struct mwl8k_cmd_set_edca_params { __u8 txq; } __attribute__((packed)); -#define MWL8K_GET_EDCA_ALL 0 #define MWL8K_SET_EDCA_CW 0x01 #define MWL8K_SET_EDCA_TXOP 0x02 #define MWL8K_SET_EDCA_AIFS 0x04 @@ -2164,22 +2032,18 @@ mwl8k_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, __u8 aifs, __u16 txop) { struct mwl8k_cmd_set_edca_params *cmd; - u32 log_cw_min, log_cw_max; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; - log_cw_min = ilog2(cw_min+1); - log_cw_max = ilog2(cw_max+1); cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL); cmd->txop = cpu_to_le16(txop); - cmd->log_cw_max = (u8)log_cw_max; - cmd->log_cw_min = (u8)log_cw_min; + cmd->log_cw_max = (u8)ilog2(cw_max + 1); + cmd->log_cw_min = (u8)ilog2(cw_min + 1); cmd->aifs = aifs; cmd->txq = qnum; @@ -2220,11 +2084,7 @@ static int mwl8k_finalize_join(struct ieee80211_hw *hw, void *frame, cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_FINALIZE_JOIN); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - - if (dtim) - cmd->sleep_interval = cpu_to_le32(dtim); - else - cmd->sleep_interval = cpu_to_le32(1); + cmd->sleep_interval = cpu_to_le32(dtim ? dtim : 1); hdrlen = ieee80211_hdrlen(payload->frame_control); @@ -2236,8 +2096,8 @@ static int mwl8k_finalize_join(struct ieee80211_hw *hw, void *frame, "sent to firmware. Sz=%u MAX=%u\n", __func__, payload_len, MWL8K_FJ_BEACON_MAXLEN); - payload_len = payload_len > MWL8K_FJ_BEACON_MAXLEN ? - MWL8K_FJ_BEACON_MAXLEN : payload_len; + if (payload_len > MWL8K_FJ_BEACON_MAXLEN) + payload_len = MWL8K_FJ_BEACON_MAXLEN; if (payload && payload_len) memcpy(cmd->beacon_data, &payload->u.beacon, payload_len); @@ -2257,7 +2117,7 @@ struct mwl8k_cmd_update_sta_db { __le32 action; /* Peer MAC address */ - __u8 peer_addr[IEEE80211_ADDR_LEN]; + __u8 peer_addr[ETH_ALEN]; __le32 reserved; @@ -2273,7 +2133,6 @@ static int mwl8k_cmd_update_sta_db(struct ieee80211_hw *hw, struct mwl8k_cmd_update_sta_db *cmd; struct peer_capability_info *peer_info; struct ieee80211_rate *bitrates = mv_vif->legacy_rates; - DECLARE_MAC_BUF(mac); int rc; __u8 count, *rates; @@ -2286,7 +2145,7 @@ static int mwl8k_cmd_update_sta_db(struct ieee80211_hw *hw, cmd->action = cpu_to_le32(action); peer_info = &cmd->peer_info; - memcpy(cmd->peer_addr, mv_vif->bssid, IEEE80211_ADDR_LEN); + memcpy(cmd->peer_addr, mv_vif->bssid, ETH_ALEN); switch (action) { case MWL8K_STA_DB_ADD_ENTRY: @@ -2298,7 +2157,7 @@ static int mwl8k_cmd_update_sta_db(struct ieee80211_hw *hw, peer_info->amsdu_enabled = 0; rates = peer_info->legacy_rates; - for (count = 0 ; count < mv_vif->legacy_nrates; count++) + for (count = 0; count < mv_vif->legacy_nrates; count++) rates[count] = bitrates[count].hw_value; rc = mwl8k_post_cmd(hw, &cmd->header); @@ -2323,25 +2182,19 @@ static int mwl8k_cmd_update_sta_db(struct ieee80211_hw *hw, /* * CMD_SET_AID. */ -#define IEEE80211_OPMODE_DISABLED 0x00 -#define IEEE80211_OPMODE_NON_MEMBER_PROT_MODE 0x01 -#define IEEE80211_OPMODE_ONE_20MHZ_STA_PROT_MODE 0x02 -#define IEEE80211_OPMODE_HTMIXED_PROT_MODE 0x03 - #define MWL8K_RATE_INDEX_MAX_ARRAY 14 #define MWL8K_FRAME_PROT_DISABLED 0x00 #define MWL8K_FRAME_PROT_11G 0x07 #define MWL8K_FRAME_PROT_11N_HT_40MHZ_ONLY 0x02 #define MWL8K_FRAME_PROT_11N_HT_ALL 0x06 -#define MWL8K_FRAME_PROT_MASK 0x07 struct mwl8k_cmd_update_set_aid { struct mwl8k_cmd_pkt header; __le16 aid; /* AP's MAC address (BSSID) */ - __u8 bssid[IEEE80211_ADDR_LEN]; + __u8 bssid[ETH_ALEN]; __le16 protection_mode; __u8 supp_rates[MWL8K_RATE_INDEX_MAX_ARRAY]; } __attribute__((packed)); @@ -2365,9 +2218,7 @@ static int mwl8k_cmd_set_aid(struct ieee80211_hw *hw, cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->aid = cpu_to_le16(info->aid); - memcpy(cmd->bssid, mv_vif->bssid, IEEE80211_ADDR_LEN); - - prot_mode = MWL8K_FRAME_PROT_DISABLED; + memcpy(cmd->bssid, mv_vif->bssid, ETH_ALEN); if (info->use_cts_prot) { prot_mode = MWL8K_FRAME_PROT_11G; @@ -2385,7 +2236,6 @@ static int mwl8k_cmd_set_aid(struct ieee80211_hw *hw, break; } } - cmd->protection_mode = cpu_to_le16(prot_mode); for (count = 0; count < mv_vif->legacy_nrates; count++) @@ -2439,10 +2289,6 @@ static int mwl8k_update_rateset(struct ieee80211_hw *hw, */ #define MWL8K_RATE_TABLE_SIZE 8 #define MWL8K_UCAST_RATE 0 -#define MWL8K_MCAST_RATE 1 -#define MWL8K_BCAST_RATE 2 - -#define MWL8K_USE_FIXED_RATE 0x0001 #define MWL8K_USE_AUTO_RATE 0x0002 struct mwl8k_rate_entry { @@ -2535,7 +2381,6 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - status &= priv->int_mask; if (!status) return IRQ_NONE; @@ -2548,17 +2393,14 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) } if (status & MWL8K_A2H_INT_OPC_DONE) { - if (priv->hostcmd_wait != NULL) { + if (priv->hostcmd_wait != NULL) complete(priv->hostcmd_wait); - priv->hostcmd_wait = NULL; - } } if (status & MWL8K_A2H_INT_QUEUE_EMPTY) { - if (!priv->inconfig && - priv->radio_state && - mwl8k_txq_busy(priv)) - mwl8k_tx_start(priv); + if (!mutex_is_locked(&priv->fw_mutex) && + priv->radio_on && mwl8k_txq_busy(priv)) + mwl8k_tx_start(priv); } return IRQ_HANDLED; @@ -2586,365 +2428,68 @@ static int mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) return rc; } -struct mwl8k_work_struct { - /* Initialized by mwl8k_queue_work(). */ - struct work_struct wt; - - /* Required field passed in to mwl8k_queue_work(). */ - struct ieee80211_hw *hw; - - /* Required field passed in to mwl8k_queue_work(). */ - int (*wfunc)(struct work_struct *w); - - /* Initialized by mwl8k_queue_work(). */ - struct completion *cmd_wait; - - /* Result code. */ - int rc; - - /* - * Optional field. Refer to explanation of MWL8K_WQ_XXX_XXX - * flags for explanation. Defaults to MWL8K_WQ_DEFAULT_OPTIONS. - */ - u32 options; - - /* Optional field. Defaults to MWL8K_CONFIG_TIMEOUT_MS. */ - unsigned long timeout_ms; - - /* Optional field. Defaults to MWL8K_WQ_TXWAIT_ATTEMPTS. */ - u32 txwait_attempts; - - /* Optional field. Defaults to MWL8K_TXWAIT_MS. */ - u32 tx_timeout_ms; - u32 step; -}; - -/* Flags controlling behavior of config queue requests */ - -/* Caller spins while waiting for completion. */ -#define MWL8K_WQ_SPIN 0x00000001 - -/* Wait for TX queues to empty before proceeding with configuration. */ -#define MWL8K_WQ_TX_WAIT_EMPTY 0x00000002 - -/* Queue request and return immediately. */ -#define MWL8K_WQ_POST_REQUEST 0x00000004 - -/* - * Caller sleeps and waits for task complete notification. - * Do not use in atomic context. - */ -#define MWL8K_WQ_SLEEP 0x00000008 - -/* Free work struct when task is done. */ -#define MWL8K_WQ_FREE_WORKSTRUCT 0x00000010 - -/* - * Config request is queued and returns to caller imediately. Use - * this in atomic context. Work struct is freed by mwl8k_queue_work() - * when this flag is set. - */ -#define MWL8K_WQ_QUEUE_ONLY (MWL8K_WQ_POST_REQUEST | \ - MWL8K_WQ_FREE_WORKSTRUCT) - -/* Default work queue behavior is to sleep and wait for tx completion. */ -#define MWL8K_WQ_DEFAULT_OPTIONS (MWL8K_WQ_SLEEP | MWL8K_WQ_TX_WAIT_EMPTY) - -/* - * Default config request timeout. Add adjustments to make sure the - * config thread waits long enough for both tx wait and cmd wait before - * timing out. - */ - -/* Time to wait for all TXQs to drain. TX Doorbell is pressed each time. */ -#define MWL8K_TXWAIT_TIMEOUT_MS 1000 - -/* Default number of TX wait attempts. */ -#define MWL8K_WQ_TXWAIT_ATTEMPTS 4 - -/* Total time to wait for TXQ to drain. */ -#define MWL8K_TXWAIT_MS (MWL8K_TXWAIT_TIMEOUT_MS * \ - MWL8K_WQ_TXWAIT_ATTEMPTS) - -/* Scheduling slop. */ -#define MWL8K_OS_SCHEDULE_OVERHEAD_MS 200 - -#define MWL8K_CONFIG_TIMEOUT_MS (MWL8K_CMD_TIMEOUT_MS + \ - MWL8K_TXWAIT_MS + \ - MWL8K_OS_SCHEDULE_OVERHEAD_MS) - -static void mwl8k_config_thread(struct work_struct *wt) -{ - struct mwl8k_work_struct *worker = (struct mwl8k_work_struct *)wt; - struct ieee80211_hw *hw = worker->hw; - struct mwl8k_priv *priv = hw->priv; - int rc = 0; - - spin_lock_irq(&priv->tx_lock); - priv->inconfig = true; - spin_unlock_irq(&priv->tx_lock); - - ieee80211_stop_queues(hw); - - /* - * Wait for host queues to drain before doing PHY - * reconfiguration. This avoids interrupting any in-flight - * DMA transfers to the hardware. - */ - if (worker->options & MWL8K_WQ_TX_WAIT_EMPTY) { - u32 timeout; - u32 time_remaining; - u32 iter; - u32 tx_wait_attempts = worker->txwait_attempts; - - time_remaining = worker->tx_timeout_ms; - if (!tx_wait_attempts) - tx_wait_attempts = 1; - - timeout = worker->tx_timeout_ms/tx_wait_attempts; - if (!timeout) - timeout = 1; - - iter = tx_wait_attempts; - do { - int wait_time; - - if (time_remaining > timeout) { - time_remaining -= timeout; - wait_time = timeout; - } else - wait_time = time_remaining; - - if (!wait_time) - wait_time = 1; - - rc = mwl8k_tx_wait_empty(hw, wait_time); - if (rc) - printk(KERN_ERR "%s() txwait timeout=%ums " - "Retry:%u/%u\n", __func__, timeout, - tx_wait_attempts - iter + 1, - tx_wait_attempts); - - } while (rc && --iter); - - rc = iter ? 0 : -ETIMEDOUT; - } - if (!rc) - rc = worker->wfunc(wt); - - spin_lock_irq(&priv->tx_lock); - priv->inconfig = false; - if (priv->pending_tx_pkts && priv->radio_state) - mwl8k_tx_start(priv); - spin_unlock_irq(&priv->tx_lock); - ieee80211_wake_queues(hw); - - worker->rc = rc; - if (worker->options & MWL8K_WQ_SLEEP) - complete(worker->cmd_wait); - - if (worker->options & MWL8K_WQ_FREE_WORKSTRUCT) - kfree(wt); -} - -static int mwl8k_queue_work(struct ieee80211_hw *hw, - struct mwl8k_work_struct *worker, - struct workqueue_struct *wqueue, - int (*wfunc)(struct work_struct *w)) -{ - unsigned long timeout = 0; - int rc = 0; - - DECLARE_COMPLETION_ONSTACK(cmd_wait); - - if (!worker->timeout_ms) - worker->timeout_ms = MWL8K_CONFIG_TIMEOUT_MS; - - if (!worker->options) - worker->options = MWL8K_WQ_DEFAULT_OPTIONS; - - if (!worker->txwait_attempts) - worker->txwait_attempts = MWL8K_WQ_TXWAIT_ATTEMPTS; - - if (!worker->tx_timeout_ms) - worker->tx_timeout_ms = MWL8K_TXWAIT_MS; - - worker->hw = hw; - worker->cmd_wait = &cmd_wait; - worker->rc = 1; - worker->wfunc = wfunc; - - INIT_WORK(&worker->wt, mwl8k_config_thread); - queue_work(wqueue, &worker->wt); - - if (worker->options & MWL8K_WQ_POST_REQUEST) { - rc = 0; - } else { - if (worker->options & MWL8K_WQ_SPIN) { - timeout = worker->timeout_ms; - while (timeout && (worker->rc > 0)) { - mdelay(1); - timeout--; - } - } else if (worker->options & MWL8K_WQ_SLEEP) - timeout = wait_for_completion_timeout(&cmd_wait, - msecs_to_jiffies(worker->timeout_ms)); - - if (timeout) - rc = worker->rc; - else { - cancel_work_sync(&worker->wt); - rc = -ETIMEDOUT; - } - } - - return rc; -} - -struct mwl8k_start_worker { - struct mwl8k_work_struct header; -}; - -static int mwl8k_start_wt(struct work_struct *wt) -{ - struct mwl8k_start_worker *worker = (struct mwl8k_start_worker *)wt; - struct ieee80211_hw *hw = worker->header.hw; - struct mwl8k_priv *priv = hw->priv; - int rc = 0; - - if (priv->vif != NULL) { - rc = -EIO; - goto mwl8k_start_exit; - } - - /* Turn on radio */ - if (mwl8k_cmd_802_11_radio_control(hw, MWL8K_RADIO_ENABLE)) { - rc = -EIO; - goto mwl8k_start_exit; - } - - /* Purge TX/RX HW queues */ - if (mwl8k_cmd_set_pre_scan(hw)) { - rc = -EIO; - goto mwl8k_start_exit; - } - - if (mwl8k_cmd_set_post_scan(hw, "\x00\x00\x00\x00\x00\x00")) { - rc = -EIO; - goto mwl8k_start_exit; - } - - /* Enable firmware rate adaptation */ - if (mwl8k_cmd_setrateadaptmode(hw, 0)) { - rc = -EIO; - goto mwl8k_start_exit; - } - - /* Disable WMM. WMM gets enabled when stack sends WMM parms */ - if (mwl8k_set_wmm(hw, MWL8K_WMM_DISABLE)) { - rc = -EIO; - goto mwl8k_start_exit; - } - - /* Disable sniffer mode */ - if (mwl8k_enable_sniffer(hw, 0)) - rc = -EIO; - -mwl8k_start_exit: - return rc; -} - static int mwl8k_start(struct ieee80211_hw *hw) { - struct mwl8k_start_worker *worker; struct mwl8k_priv *priv = hw->priv; int rc; - /* Enable tx reclaim tasklet */ - tasklet_enable(&priv->tx_reclaim_task); - rc = request_irq(priv->pdev->irq, &mwl8k_interrupt, IRQF_SHARED, MWL8K_NAME, hw); if (rc) { printk(KERN_ERR "%s: failed to register IRQ handler\n", priv->name); - rc = -EIO; - goto mwl8k_start_disable_tasklet; + return -EIO; } - /* Enable interrupts */ - iowrite32(priv->int_mask, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - - worker = kzalloc(sizeof(*worker), GFP_KERNEL); - if (worker == NULL) { - rc = -ENOMEM; - goto mwl8k_start_disable_irq; - } + /* Enable tx reclaim tasklet */ + tasklet_enable(&priv->tx_reclaim_task); - rc = mwl8k_queue_work(hw, &worker->header, - priv->config_wq, mwl8k_start_wt); - kfree(worker); - if (!rc) - return rc; + /* Enable interrupts */ + iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - if (rc == -ETIMEDOUT) - printk(KERN_ERR "%s() timed out\n", __func__); + rc = mwl8k_fw_lock(hw); + if (!rc) { + rc = mwl8k_cmd_802_11_radio_enable(hw); - rc = -EIO; + if (!rc) + rc = mwl8k_cmd_set_pre_scan(hw); -mwl8k_start_disable_irq: - spin_lock_irq(&priv->tx_lock); - iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - spin_unlock_irq(&priv->tx_lock); - free_irq(priv->pdev->irq, hw); + if (!rc) + rc = mwl8k_cmd_set_post_scan(hw, + "\x00\x00\x00\x00\x00\x00"); -mwl8k_start_disable_tasklet: - tasklet_disable(&priv->tx_reclaim_task); + if (!rc) + rc = mwl8k_cmd_setrateadaptmode(hw, 0); - return rc; -} + if (!rc) + rc = mwl8k_set_wmm(hw, 0); -struct mwl8k_stop_worker { - struct mwl8k_work_struct header; -}; + if (!rc) + rc = mwl8k_enable_sniffer(hw, 0); -static int mwl8k_stop_wt(struct work_struct *wt) -{ - struct mwl8k_stop_worker *worker = (struct mwl8k_stop_worker *)wt; - struct ieee80211_hw *hw = worker->header.hw; - int rc; + mwl8k_fw_unlock(hw); + } - rc = mwl8k_cmd_802_11_radio_control(hw, MWL8K_RADIO_DISABLE); + if (rc) { + iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); + free_irq(priv->pdev->irq, hw); + tasklet_disable(&priv->tx_reclaim_task); + } return rc; } static void mwl8k_stop(struct ieee80211_hw *hw) { - int rc; - struct mwl8k_stop_worker *worker; struct mwl8k_priv *priv = hw->priv; int i; - if (priv->vif != NULL) - return; + mwl8k_cmd_802_11_radio_disable(hw); ieee80211_stop_queues(hw); - worker = kzalloc(sizeof(*worker), GFP_KERNEL); - if (worker == NULL) - return; - - rc = mwl8k_queue_work(hw, &worker->header, - priv->config_wq, mwl8k_stop_wt); - kfree(worker); - if (rc == -ETIMEDOUT) - printk(KERN_ERR "%s() timed out\n", __func__); - /* Disable interrupts */ - spin_lock_irq(&priv->tx_lock); iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - spin_unlock_irq(&priv->tx_lock); free_irq(priv->pdev->irq, hw); /* Stop finalize join worker */ @@ -2978,8 +2523,7 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, /* * We only support managed interfaces for now. */ - if (conf->type != NL80211_IFTYPE_STATION && - conf->type != NL80211_IFTYPE_MONITOR) + if (conf->type != NL80211_IFTYPE_STATION) return -EINVAL; /* Clean out driver private area */ @@ -2987,13 +2531,13 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); /* Save the mac address */ - memcpy(mwl8k_vif->mac_addr, conf->mac_addr, IEEE80211_ADDR_LEN); + memcpy(mwl8k_vif->mac_addr, conf->mac_addr, ETH_ALEN); /* Back pointer to parent config block */ mwl8k_vif->priv = priv; /* Setup initial PHY parameters */ - memcpy(mwl8k_vif->legacy_rates , + memcpy(mwl8k_vif->legacy_rates, priv->rates, sizeof(mwl8k_vif->legacy_rates)); mwl8k_vif->legacy_nrates = ARRAY_SIZE(priv->rates); @@ -3017,213 +2561,148 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw, priv->vif = NULL; } -struct mwl8k_config_worker { - struct mwl8k_work_struct header; - u32 changed; -}; - -static int mwl8k_config_wt(struct work_struct *wt) +static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) { - struct mwl8k_config_worker *worker = - (struct mwl8k_config_worker *)wt; - struct ieee80211_hw *hw = worker->header.hw; struct ieee80211_conf *conf = &hw->conf; struct mwl8k_priv *priv = hw->priv; - int rc = 0; + int rc; - if (!conf->radio_enabled) { - mwl8k_cmd_802_11_radio_control(hw, MWL8K_RADIO_DISABLE); + if (conf->flags & IEEE80211_CONF_IDLE) { + mwl8k_cmd_802_11_radio_disable(hw); priv->current_channel = NULL; - rc = 0; - goto mwl8k_config_exit; + return 0; } - if (mwl8k_cmd_802_11_radio_control(hw, MWL8K_RADIO_ENABLE)) { - rc = -EINVAL; - goto mwl8k_config_exit; - } + rc = mwl8k_fw_lock(hw); + if (rc) + return rc; - priv->current_channel = conf->channel; + rc = mwl8k_cmd_802_11_radio_enable(hw); + if (rc) + goto out; - if (mwl8k_cmd_set_rf_channel(hw, conf->channel)) { - rc = -EINVAL; - goto mwl8k_config_exit; - } + rc = mwl8k_cmd_set_rf_channel(hw, conf->channel); + if (rc) + goto out; + + priv->current_channel = conf->channel; if (conf->power_level > 18) conf->power_level = 18; - if (mwl8k_cmd_802_11_rf_tx_power(hw, conf->power_level)) { - rc = -EINVAL; - goto mwl8k_config_exit; - } + rc = mwl8k_cmd_802_11_rf_tx_power(hw, conf->power_level); + if (rc) + goto out; if (mwl8k_cmd_mimo_config(hw, 0x7, 0x7)) rc = -EINVAL; -mwl8k_config_exit: +out: + mwl8k_fw_unlock(hw); + return rc; } -static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) +static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) { - int rc = 0; - struct mwl8k_config_worker *worker; struct mwl8k_priv *priv = hw->priv; - - worker = kzalloc(sizeof(*worker), GFP_KERNEL); - if (worker == NULL) - return -ENOMEM; - - worker->changed = changed; - rc = mwl8k_queue_work(hw, &worker->header, - priv->config_wq, mwl8k_config_wt); - if (rc == -ETIMEDOUT) { - printk(KERN_ERR "%s() timed out.\n", __func__); - rc = -EINVAL; - } - - kfree(worker); - - /* - * mac80211 will crash on anything other than -EINVAL on - * error. Looks like wireless extensions which calls mac80211 - * may be the actual culprit... - */ - return rc ? -EINVAL : 0; -} - -struct mwl8k_bss_info_changed_worker { - struct mwl8k_work_struct header; - struct ieee80211_vif *vif; - struct ieee80211_bss_conf *info; - u32 changed; -}; - -static int mwl8k_bss_info_changed_wt(struct work_struct *wt) -{ - struct mwl8k_bss_info_changed_worker *worker = - (struct mwl8k_bss_info_changed_worker *)wt; - struct ieee80211_hw *hw = worker->header.hw; - struct ieee80211_vif *vif = worker->vif; - struct ieee80211_bss_conf *info = worker->info; - u32 changed; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); int rc; - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + if (changed & BSS_CHANGED_BSSID) + memcpy(mwl8k_vif->bssid, info->bssid, ETH_ALEN); + + if ((changed & BSS_CHANGED_ASSOC) == 0) + return; - changed = worker->changed; priv->capture_beacon = false; + rc = mwl8k_fw_lock(hw); + if (rc) + return; + if (info->assoc) { memcpy(&mwl8k_vif->bss_info, info, sizeof(struct ieee80211_bss_conf)); /* Install rates */ - if (mwl8k_update_rateset(hw, vif)) - goto mwl8k_bss_info_changed_exit; + rc = mwl8k_update_rateset(hw, vif); + if (rc) + goto out; /* Turn on rate adaptation */ - if (mwl8k_cmd_use_fixed_rate(hw, MWL8K_USE_AUTO_RATE, - MWL8K_UCAST_RATE, NULL)) - goto mwl8k_bss_info_changed_exit; + rc = mwl8k_cmd_use_fixed_rate(hw, MWL8K_USE_AUTO_RATE, + MWL8K_UCAST_RATE, NULL); + if (rc) + goto out; /* Set radio preamble */ - if (mwl8k_set_radio_preamble(hw, - info->use_short_preamble)) - goto mwl8k_bss_info_changed_exit; + rc = mwl8k_set_radio_preamble(hw, info->use_short_preamble); + if (rc) + goto out; /* Set slot time */ - if (mwl8k_cmd_set_slot(hw, info->use_short_slot ? - MWL8K_SHORT_SLOTTIME : MWL8K_LONG_SLOTTIME)) - goto mwl8k_bss_info_changed_exit; + rc = mwl8k_cmd_set_slot(hw, info->use_short_slot); + if (rc) + goto out; /* Update peer rate info */ - if (mwl8k_cmd_update_sta_db(hw, vif, - MWL8K_STA_DB_MODIFY_ENTRY)) - goto mwl8k_bss_info_changed_exit; + rc = mwl8k_cmd_update_sta_db(hw, vif, + MWL8K_STA_DB_MODIFY_ENTRY); + if (rc) + goto out; /* Set AID */ - if (mwl8k_cmd_set_aid(hw, vif)) - goto mwl8k_bss_info_changed_exit; + rc = mwl8k_cmd_set_aid(hw, vif); + if (rc) + goto out; /* * Finalize the join. Tell rx handler to process * next beacon from our BSSID. */ - memcpy(priv->capture_bssid, - mwl8k_vif->bssid, IEEE80211_ADDR_LEN); + memcpy(priv->capture_bssid, mwl8k_vif->bssid, ETH_ALEN); priv->capture_beacon = true; } else { - mwl8k_cmd_update_sta_db(hw, vif, MWL8K_STA_DB_DEL_ENTRY); + rc = mwl8k_cmd_update_sta_db(hw, vif, MWL8K_STA_DB_DEL_ENTRY); memset(&mwl8k_vif->bss_info, 0, sizeof(struct ieee80211_bss_conf)); - memset(mwl8k_vif->bssid, 0, IEEE80211_ADDR_LEN); + memset(mwl8k_vif->bssid, 0, ETH_ALEN); } -mwl8k_bss_info_changed_exit: - rc = 0; - return rc; +out: + mwl8k_fw_unlock(hw); } -static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed) +static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) { - struct mwl8k_bss_info_changed_worker *worker; - struct mwl8k_priv *priv = hw->priv; - struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); - int rc; - - if (changed & BSS_CHANGED_BSSID) - memcpy(mv_vif->bssid, info->bssid, IEEE80211_ADDR_LEN); - - if ((changed & BSS_CHANGED_ASSOC) == 0) - return; - - worker = kzalloc(sizeof(*worker), GFP_KERNEL); - if (worker == NULL) - return; + struct mwl8k_cmd_pkt *cmd; - worker->vif = vif; - worker->info = info; - worker->changed = changed; - rc = mwl8k_queue_work(hw, &worker->header, - priv->config_wq, - mwl8k_bss_info_changed_wt); - kfree(worker); - if (rc == -ETIMEDOUT) - printk(KERN_ERR "%s() timed out\n", __func__); -} - -struct mwl8k_configure_filter_worker { - struct mwl8k_work_struct header; - unsigned int changed_flags; - unsigned int *total_flags; - int mc_count; - struct dev_addr_list *mclist; -}; + cmd = __mwl8k_cmd_mac_multicast_adr(hw, mc_count, mclist); -#define MWL8K_SUPPORTED_IF_FLAGS FIF_BCN_PRBRESP_PROMISC + return (unsigned long)cmd; +} -static int mwl8k_configure_filter_wt(struct work_struct *wt) +static void mwl8k_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) { - struct mwl8k_configure_filter_worker *worker = - (struct mwl8k_configure_filter_worker *)wt; + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_cmd_pkt *multicast_adr_cmd; - struct ieee80211_hw *hw = worker->header.hw; - unsigned int changed_flags = worker->changed_flags; - unsigned int *total_flags = worker->total_flags; - int mc_count = worker->mc_count; - struct dev_addr_list *mclist = worker->mclist; + /* Clear unsupported feature flags */ + *total_flags &= FIF_BCN_PRBRESP_PROMISC; - struct mwl8k_priv *priv = hw->priv; - int rc = 0; + if (mwl8k_fw_lock(hw)) + return; if (changed_flags & FIF_BCN_PRBRESP_PROMISC) { if (*total_flags & FIF_BCN_PRBRESP_PROMISC) - rc = mwl8k_cmd_set_pre_scan(hw); + mwl8k_cmd_set_pre_scan(hw); else { u8 *bssid; @@ -3231,151 +2710,45 @@ static int mwl8k_configure_filter_wt(struct work_struct *wt) if (priv->vif != NULL) bssid = MWL8K_VIF(priv->vif)->bssid; - rc = mwl8k_cmd_set_post_scan(hw, bssid); + mwl8k_cmd_set_post_scan(hw, bssid); } } - if (rc) - goto mwl8k_configure_filter_exit; - if (mc_count) { - mc_count = mc_count < priv->num_mcaddrs ? - mc_count : priv->num_mcaddrs; - rc = mwl8k_cmd_mac_multicast_adr(hw, mc_count, mclist); - if (rc) - printk(KERN_ERR - "%s()Error setting multicast addresses\n", - __func__); + multicast_adr_cmd = (void *)(unsigned long)multicast; + if (multicast_adr_cmd != NULL) { + mwl8k_post_cmd(hw, multicast_adr_cmd); + kfree(multicast_adr_cmd); } -mwl8k_configure_filter_exit: - return rc; -} - -static void mwl8k_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, - struct dev_addr_list *mclist) -{ - - struct mwl8k_configure_filter_worker *worker; - struct mwl8k_priv *priv = hw->priv; - - /* Clear unsupported feature flags */ - *total_flags &= MWL8K_SUPPORTED_IF_FLAGS; - - if (!(changed_flags & MWL8K_SUPPORTED_IF_FLAGS) && !mc_count) - return; - - worker = kzalloc(sizeof(*worker), GFP_ATOMIC); - if (worker == NULL) - return; - - worker->header.options = MWL8K_WQ_QUEUE_ONLY | MWL8K_WQ_TX_WAIT_EMPTY; - worker->changed_flags = changed_flags; - worker->total_flags = total_flags; - worker->mc_count = mc_count; - worker->mclist = mclist; - - mwl8k_queue_work(hw, &worker->header, priv->config_wq, - mwl8k_configure_filter_wt); -} - -struct mwl8k_set_rts_threshold_worker { - struct mwl8k_work_struct header; - u32 value; -}; - -static int mwl8k_set_rts_threshold_wt(struct work_struct *wt) -{ - struct mwl8k_set_rts_threshold_worker *worker = - (struct mwl8k_set_rts_threshold_worker *)wt; - - struct ieee80211_hw *hw = worker->header.hw; - u16 threshold = (u16)(worker->value); - int rc; - - rc = mwl8k_rts_threshold(hw, MWL8K_CMD_SET, &threshold); - - return rc; + mwl8k_fw_unlock(hw); } static int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { - int rc; - struct mwl8k_set_rts_threshold_worker *worker; - struct mwl8k_priv *priv = hw->priv; - - worker = kzalloc(sizeof(*worker), GFP_KERNEL); - if (worker == NULL) - return -ENOMEM; - - worker->value = value; - - rc = mwl8k_queue_work(hw, &worker->header, - priv->config_wq, - mwl8k_set_rts_threshold_wt); - kfree(worker); - - if (rc == -ETIMEDOUT) { - printk(KERN_ERR "%s() timed out\n", __func__); - rc = -EINVAL; - } - - return rc; -} - -struct mwl8k_conf_tx_worker { - struct mwl8k_work_struct header; - u16 queue; - const struct ieee80211_tx_queue_params *params; -}; - -static int mwl8k_conf_tx_wt(struct work_struct *wt) -{ - struct mwl8k_conf_tx_worker *worker = - (struct mwl8k_conf_tx_worker *)wt; - - struct ieee80211_hw *hw = worker->header.hw; - u16 queue = worker->queue; - const struct ieee80211_tx_queue_params *params = worker->params; - - struct mwl8k_priv *priv = hw->priv; - int rc = 0; - - if (priv->wmm_mode == MWL8K_WMM_DISABLE) - if (mwl8k_set_wmm(hw, MWL8K_WMM_ENABLE)) { - rc = -EINVAL; - goto mwl8k_conf_tx_exit; - } - - if (mwl8k_set_edca_params(hw, GET_TXQ(queue), params->cw_min, - params->cw_max, params->aifs, params->txop)) - rc = -EINVAL; -mwl8k_conf_tx_exit: - return rc; + return mwl8k_rts_threshold(hw, MWL8K_CMD_SET, value); } static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params) { - int rc; - struct mwl8k_conf_tx_worker *worker; struct mwl8k_priv *priv = hw->priv; + int rc; - worker = kzalloc(sizeof(*worker), GFP_KERNEL); - if (worker == NULL) - return -ENOMEM; + rc = mwl8k_fw_lock(hw); + if (!rc) { + if (!priv->wmm_enabled) + rc = mwl8k_set_wmm(hw, 1); - worker->queue = queue; - worker->params = params; - rc = mwl8k_queue_work(hw, &worker->header, - priv->config_wq, mwl8k_conf_tx_wt); - kfree(worker); - if (rc == -ETIMEDOUT) { - printk(KERN_ERR "%s() timed out\n", __func__); - rc = -EINVAL; + if (!rc) + rc = mwl8k_set_edca_params(hw, queue, + params->cw_min, + params->cw_max, + params->aifs, + params->txop); + + mwl8k_fw_unlock(hw); } + return rc; } @@ -3393,44 +2766,14 @@ static int mwl8k_get_tx_stats(struct ieee80211_hw *hw, sizeof(struct ieee80211_tx_queue_stats)); } spin_unlock_bh(&priv->tx_lock); - return 0; -} - -struct mwl8k_get_stats_worker { - struct mwl8k_work_struct header; - struct ieee80211_low_level_stats *stats; -}; - -static int mwl8k_get_stats_wt(struct work_struct *wt) -{ - struct mwl8k_get_stats_worker *worker = - (struct mwl8k_get_stats_worker *)wt; - return mwl8k_cmd_802_11_get_stat(worker->header.hw, worker->stats); + return 0; } static int mwl8k_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats) { - int rc; - struct mwl8k_get_stats_worker *worker; - struct mwl8k_priv *priv = hw->priv; - - worker = kzalloc(sizeof(*worker), GFP_KERNEL); - if (worker == NULL) - return -ENOMEM; - - worker->stats = stats; - rc = mwl8k_queue_work(hw, &worker->header, - priv->config_wq, mwl8k_get_stats_wt); - - kfree(worker); - if (rc == -ETIMEDOUT) { - printk(KERN_ERR "%s() timed out\n", __func__); - rc = -EINVAL; - } - - return rc; + return mwl8k_cmd_802_11_get_stat(hw, stats); } static const struct ieee80211_ops mwl8k_ops = { @@ -3441,6 +2784,7 @@ static const struct ieee80211_ops mwl8k_ops = { .remove_interface = mwl8k_remove_interface, .config = mwl8k_config, .bss_info_changed = mwl8k_bss_info_changed, + .prepare_multicast = mwl8k_prepare_multicast, .configure_filter = mwl8k_configure_filter, .set_rts_threshold = mwl8k_set_rts_threshold, .conf_tx = mwl8k_conf_tx, @@ -3458,12 +2802,9 @@ static void mwl8k_tx_reclaim_handler(unsigned long data) for (i = 0; i < MWL8K_TX_QUEUES; i++) mwl8k_txq_reclaim(hw, i, 0); - if (priv->tx_wait != NULL) { - int count = mwl8k_txq_busy(priv); - if (count == 0) { - complete(priv->tx_wait); - priv->tx_wait = NULL; - } + if (priv->tx_wait != NULL && mwl8k_txq_busy(priv) == 0) { + complete(priv->tx_wait); + priv->tx_wait = NULL; } spin_unlock_bh(&priv->tx_lock); } @@ -3473,7 +2814,7 @@ static void mwl8k_finalize_join_worker(struct work_struct *work) struct mwl8k_priv *priv = container_of(work, struct mwl8k_priv, finalize_join_worker); struct sk_buff *skb = priv->beacon_skb; - u8 dtim = (MWL8K_VIF(priv->vif))->bss_info.dtim_period; + u8 dtim = MWL8K_VIF(priv->vif)->bss_info.dtim_period; mwl8k_finalize_join(priv->hw, skb->data, skb->len, dtim); dev_kfree_skb(skb); @@ -3484,12 +2825,16 @@ static void mwl8k_finalize_join_worker(struct work_struct *work) static int __devinit mwl8k_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + static int printed_version = 0; struct ieee80211_hw *hw; struct mwl8k_priv *priv; - DECLARE_MAC_BUF(mac); int rc; int i; - u8 *fw; + + if (!printed_version) { + printk(KERN_INFO "%s version %s\n", MWL8K_DESC, MWL8K_VERSION); + printed_version = 1; + } rc = pci_enable_device(pdev); if (rc) { @@ -3517,16 +2862,10 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, priv = hw->priv; priv->hw = hw; priv->pdev = pdev; - priv->hostcmd_wait = NULL; - priv->tx_wait = NULL; - priv->inconfig = false; - priv->wep_enabled = 0; - priv->wmm_mode = false; + priv->wmm_enabled = false; priv->pending_tx_pkts = 0; strncpy(priv->name, MWL8K_NAME, sizeof(priv->name)); - spin_lock_init(&priv->fw_lock); - SET_IEEE80211_DEV(hw, &pdev->dev); pci_set_drvdata(pdev, hw); @@ -3558,17 +2897,16 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, hw->queues = MWL8K_TX_QUEUES; - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_MONITOR); + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); /* Set rssi and noise values to dBm */ - hw->flags |= (IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM); + hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; hw->vif_data_size = sizeof(struct mwl8k_vif); priv->vif = NULL; /* Set default radio state and preamble */ - priv->radio_preamble = MWL8K_RADIO_DEFAULT_PREAMBLE; - priv->radio_state = MWL8K_RADIO_DISABLE; + priv->radio_on = 0; + priv->radio_short_preamble = 0; /* Finalize join worker */ INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); @@ -3593,6 +2931,12 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, goto err_iounmap; rxq_refill(hw, 0, INT_MAX); + mutex_init(&priv->fw_mutex); + priv->fw_mutex_owner = NULL; + priv->fw_mutex_depth = 0; + priv->tx_wait = NULL; + priv->hostcmd_wait = NULL; + spin_lock_init(&priv->tx_lock); for (i = 0; i < MWL8K_TX_QUEUES; i++) { @@ -3602,8 +2946,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, } iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - priv->int_mask = 0; - iowrite32(priv->int_mask, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); + iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); @@ -3640,9 +2983,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, * commands use interrupts and avoids polling. Disable * interrupts when done. */ - priv->int_mask |= MWL8K_A2H_EVENTS; - - iowrite32(priv->int_mask, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); + iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); /* Get config data, mac addrs etc */ rc = mwl8k_cmd_get_hw_spec(hw); @@ -3652,16 +2993,14 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, } /* Turn radio off */ - rc = mwl8k_cmd_802_11_radio_control(hw, MWL8K_RADIO_DISABLE); + rc = mwl8k_cmd_802_11_radio_disable(hw); if (rc) { printk(KERN_ERR "%s: Cannot disable\n", priv->name); goto err_stop_firmware; } /* Disable interrupts */ - spin_lock_irq(&priv->tx_lock); iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - spin_unlock_irq(&priv->tx_lock); free_irq(priv->pdev->irq, hw); rc = ieee80211_register_hw(hw); @@ -3670,13 +3009,11 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, goto err_stop_firmware; } - fw = (u8 *)&priv->fw_rev; - printk(KERN_INFO "%s: 88W%u %s\n", priv->name, priv->part_num, - MWL8K_DESC); - printk(KERN_INFO "%s: Driver Ver:%s Firmware Ver:%u.%u.%u.%u\n", - priv->name, MWL8K_VERSION, fw[3], fw[2], fw[1], fw[0]); - printk(KERN_INFO "%s: MAC Address: %s\n", priv->name, - print_mac(mac, hw->wiphy->perm_addr)); + printk(KERN_INFO "%s: 88w%u v%d, %pM, firmware version %u.%u.%u.%u\n", + wiphy_name(hw->wiphy), priv->part_num, priv->hw_rev, + hw->wiphy->perm_addr, + (priv->fw_rev >> 24) & 0xff, (priv->fw_rev >> 16) & 0xff, + (priv->fw_rev >> 8) & 0xff, priv->fw_rev & 0xff); return 0; @@ -3685,9 +3022,7 @@ err_stop_firmware: mwl8k_release_firmware(priv); err_free_irq: - spin_lock_irq(&priv->tx_lock); iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - spin_unlock_irq(&priv->tx_lock); free_irq(priv->pdev->irq, hw); err_free_queues: diff --git a/drivers/net/wireless/netwave_cs.c b/drivers/net/wireless/netwave_cs.c index d63c8992f229..9498b46c99a4 100644 --- a/drivers/net/wireless/netwave_cs.c +++ b/drivers/net/wireless/netwave_cs.c @@ -203,7 +203,8 @@ static int netwave_open(struct net_device *dev); /* Open the device */ static int netwave_close(struct net_device *dev); /* Close the device */ /* Packet transmission and Packet reception */ -static int netwave_start_xmit( struct sk_buff *skb, struct net_device *dev); +static netdev_tx_t netwave_start_xmit( struct sk_buff *skb, + struct net_device *dev); static int netwave_rx( struct net_device *dev); /* Interrupt routines */ @@ -1026,7 +1027,8 @@ static int netwave_hw_xmit(unsigned char* data, int len, return 0; } -static int netwave_start_xmit(struct sk_buff *skb, struct net_device *dev) { +static netdev_tx_t netwave_start_xmit(struct sk_buff *skb, + struct net_device *dev) { /* This flag indicate that the hardware can't perform a transmission. * Theoritically, NET3 check it before sending a packet to the driver, * but in fact it never do that and pool continuously. @@ -1047,7 +1049,7 @@ static int netwave_start_xmit(struct sk_buff *skb, struct net_device *dev) { } dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } /* netwave_start_xmit */ /* diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig index 44411eb4e91b..83b635fd7784 100644 --- a/drivers/net/wireless/orinoco/Kconfig +++ b/drivers/net/wireless/orinoco/Kconfig @@ -1,6 +1,7 @@ config HERMES tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)" depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211 + depends on CFG80211 select WIRELESS_EXT select FW_LOADER select CRYPTO diff --git a/drivers/net/wireless/orinoco/Makefile b/drivers/net/wireless/orinoco/Makefile index 1fc7409d6699..9abd6329bcbd 100644 --- a/drivers/net/wireless/orinoco/Makefile +++ b/drivers/net/wireless/orinoco/Makefile @@ -1,7 +1,7 @@ # # Makefile for the orinoco wireless device drivers. # -orinoco-objs := main.o fw.o hw.o mic.o scan.o wext.o hermes_dld.o hermes.o +orinoco-objs := main.o fw.o hw.o mic.o scan.o wext.o hermes_dld.o hermes.o cfg.o obj-$(CONFIG_HERMES) += orinoco.o obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o diff --git a/drivers/net/wireless/orinoco/airport.c b/drivers/net/wireless/orinoco/airport.c index 8c4065f1b0d0..c60df2c1aca3 100644 --- a/drivers/net/wireless/orinoco/airport.c +++ b/drivers/net/wireless/orinoco/airport.c @@ -27,6 +27,7 @@ struct airport { struct macio_dev *mdev; void __iomem *vaddr; + unsigned int irq; int irq_requested; int ndev_registered; }; @@ -34,8 +35,9 @@ struct airport { static int airport_suspend(struct macio_dev *mdev, pm_message_t state) { - struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev); - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); + struct net_device *dev = priv->ndev; + struct airport *card = priv->card; unsigned long flags; int err; @@ -48,18 +50,10 @@ airport_suspend(struct macio_dev *mdev, pm_message_t state) return 0; } - err = __orinoco_down(dev); - if (err) - printk(KERN_WARNING "%s: PBOOK_SLEEP_NOW: Error %d downing interface\n", - dev->name, err); - - netif_device_detach(dev); - - priv->hw_unavailable++; - + orinoco_down(priv); orinoco_unlock(priv, &flags); - disable_irq(dev->irq); + disable_irq(card->irq); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(mdev), 0, 0); @@ -69,8 +63,9 @@ airport_suspend(struct macio_dev *mdev, pm_message_t state) static int airport_resume(struct macio_dev *mdev) { - struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev); - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); + struct net_device *dev = priv->ndev; + struct airport *card = priv->card; unsigned long flags; int err; @@ -80,47 +75,27 @@ airport_resume(struct macio_dev *mdev) macio_get_of_node(mdev), 0, 1); msleep(200); - enable_irq(dev->irq); - - err = orinoco_reinit_firmware(dev); - if (err) { - printk(KERN_ERR "%s: Error %d re-initializing firmware on PBOOK_WAKE\n", - dev->name, err); - return 0; - } + enable_irq(card->irq); spin_lock_irqsave(&priv->lock, flags); - - netif_device_attach(dev); - - priv->hw_unavailable--; - - if (priv->open && (!priv->hw_unavailable)) { - err = __orinoco_up(dev); - if (err) - printk(KERN_ERR "%s: Error %d restarting card on PBOOK_WAKE\n", - dev->name, err); - } - - + err = orinoco_up(priv); spin_unlock_irqrestore(&priv->lock, flags); - return 0; + return err; } static int airport_detach(struct macio_dev *mdev) { - struct net_device *dev = dev_get_drvdata(&mdev->ofdev.dev); - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev); struct airport *card = priv->card; if (card->ndev_registered) - unregister_netdev(dev); + orinoco_if_del(priv); card->ndev_registered = 0; if (card->irq_requested) - free_irq(dev->irq, dev); + free_irq(card->irq, priv); card->irq_requested = 0; if (card->vaddr) @@ -134,7 +109,7 @@ airport_detach(struct macio_dev *mdev) ssleep(1); macio_set_drvdata(mdev, NULL); - free_orinocodev(dev); + free_orinocodev(priv); return 0; } @@ -146,7 +121,6 @@ static int airport_hard_reset(struct orinoco_private *priv) * re-initialize properly, it falls in a screaming heap * shortly afterwards. */ #if 0 - struct net_device *dev = priv->ndev; struct airport *card = priv->card; /* Vitally important. If we don't do this it seems we get an @@ -154,7 +128,7 @@ static int airport_hard_reset(struct orinoco_private *priv) * hw_unavailable is already set it doesn't get ACKed, we get * into an interrupt loop and the PMU decides to turn us * off. */ - disable_irq(dev->irq); + disable_irq(card->irq); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, macio_get_of_node(card->mdev), 0, 0); @@ -163,7 +137,7 @@ static int airport_hard_reset(struct orinoco_private *priv) macio_get_of_node(card->mdev), 0, 1); ssleep(1); - enable_irq(dev->irq); + enable_irq(card->irq); ssleep(1); #endif @@ -174,7 +148,6 @@ static int airport_attach(struct macio_dev *mdev, const struct of_device_id *match) { struct orinoco_private *priv; - struct net_device *dev; struct airport *card; unsigned long phys_addr; hermes_t *hw; @@ -185,33 +158,29 @@ airport_attach(struct macio_dev *mdev, const struct of_device_id *match) } /* Allocate space for private device-specific data */ - dev = alloc_orinocodev(sizeof(*card), &mdev->ofdev.dev, - airport_hard_reset, NULL); - if (!dev) { + priv = alloc_orinocodev(sizeof(*card), &mdev->ofdev.dev, + airport_hard_reset, NULL); + if (!priv) { printk(KERN_ERR PFX "Cannot allocate network device\n"); return -ENODEV; } - priv = netdev_priv(dev); card = priv->card; hw = &priv->hw; card->mdev = mdev; - if (macio_request_resource(mdev, 0, "airport")) { + if (macio_request_resource(mdev, 0, DRIVER_NAME)) { printk(KERN_ERR PFX "can't request IO resource !\n"); - free_orinocodev(dev); + free_orinocodev(priv); return -EBUSY; } - SET_NETDEV_DEV(dev, &mdev->ofdev.dev); - - macio_set_drvdata(mdev, dev); + macio_set_drvdata(mdev, priv); /* Setup interrupts & base address */ - dev->irq = macio_irq(mdev, 0); + card->irq = macio_irq(mdev, 0); phys_addr = macio_resource_start(mdev, 0); /* Physical address */ printk(KERN_DEBUG PFX "Physical address %lx\n", phys_addr); - dev->base_addr = phys_addr; card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN); if (!card->vaddr) { printk(KERN_ERR PFX "ioremap() failed\n"); @@ -228,18 +197,23 @@ airport_attach(struct macio_dev *mdev, const struct of_device_id *match) /* Reset it before we get the interrupt */ hermes_init(hw); - if (request_irq(dev->irq, orinoco_interrupt, 0, dev->name, dev)) { - printk(KERN_ERR PFX "Couldn't get IRQ %d\n", dev->irq); + if (request_irq(card->irq, orinoco_interrupt, 0, DRIVER_NAME, priv)) { + printk(KERN_ERR PFX "Couldn't get IRQ %d\n", card->irq); goto failed; } card->irq_requested = 1; - /* Tell the stack we exist */ - if (register_netdev(dev) != 0) { - printk(KERN_ERR PFX "register_netdev() failed\n"); + /* Initialise the main driver */ + if (orinoco_init(priv) != 0) { + printk(KERN_ERR PFX "orinoco_init() failed\n"); + goto failed; + } + + /* Register an interface with the stack */ + if (orinoco_if_add(priv, phys_addr, card->irq) != 0) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto failed; } - printk(KERN_DEBUG PFX "Card registered for interface %s\n", dev->name); card->ndev_registered = 1; return 0; failed: diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c new file mode 100644 index 000000000000..27f2d3342645 --- /dev/null +++ b/drivers/net/wireless/orinoco/cfg.c @@ -0,0 +1,203 @@ +/* cfg80211 support + * + * See copyright notice in main.c + */ +#include <linux/ieee80211.h> +#include <net/cfg80211.h> +#include "hw.h" +#include "main.h" +#include "orinoco.h" + +#include "cfg.h" + +/* Supported bitrates. Must agree with hw.c */ +static struct ieee80211_rate orinoco_rates[] = { + { .bitrate = 10 }, + { .bitrate = 20 }, + { .bitrate = 55 }, + { .bitrate = 110 }, +}; + +static const void * const orinoco_wiphy_privid = &orinoco_wiphy_privid; + +/* Called after orinoco_private is allocated. */ +void orinoco_wiphy_init(struct wiphy *wiphy) +{ + struct orinoco_private *priv = wiphy_priv(wiphy); + + wiphy->privid = orinoco_wiphy_privid; + + set_wiphy_dev(wiphy, priv->dev); +} + +/* Called after firmware is initialised */ +int orinoco_wiphy_register(struct wiphy *wiphy) +{ + struct orinoco_private *priv = wiphy_priv(wiphy); + int i, channels = 0; + + if (priv->firmware_type == FIRMWARE_TYPE_AGERE) + wiphy->max_scan_ssids = 1; + else + wiphy->max_scan_ssids = 0; + + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + + /* TODO: should we set if we only have demo ad-hoc? + * (priv->has_port3) + */ + if (priv->has_ibss) + wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); + + if (!priv->broken_monitor || force_monitor) + wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); + + priv->band.bitrates = orinoco_rates; + priv->band.n_bitrates = ARRAY_SIZE(orinoco_rates); + + /* Only support channels allowed by the card EEPROM */ + for (i = 0; i < NUM_CHANNELS; i++) { + if (priv->channel_mask & (1 << i)) { + priv->channels[i].center_freq = + ieee80211_dsss_chan_to_freq(i+1); + channels++; + } + } + priv->band.channels = priv->channels; + priv->band.n_channels = channels; + + wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + i = 0; + if (priv->has_wep) { + priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP40; + i++; + + if (priv->has_big_wep) { + priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP104; + i++; + } + } + if (priv->has_wpa) { + priv->cipher_suites[i] = WLAN_CIPHER_SUITE_TKIP; + i++; + } + wiphy->cipher_suites = priv->cipher_suites; + wiphy->n_cipher_suites = i; + + wiphy->rts_threshold = priv->rts_thresh; + if (!priv->has_mwo) + wiphy->frag_threshold = priv->frag_thresh; + + return wiphy_register(wiphy); +} + +static int orinoco_change_vif(struct wiphy *wiphy, struct net_device *dev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct orinoco_private *priv = wiphy_priv(wiphy); + int err = 0; + unsigned long lock; + + if (orinoco_lock(priv, &lock) != 0) + return -EBUSY; + + switch (type) { + case NL80211_IFTYPE_ADHOC: + if (!priv->has_ibss && !priv->has_port3) + err = -EINVAL; + break; + + case NL80211_IFTYPE_STATION: + break; + + case NL80211_IFTYPE_MONITOR: + if (priv->broken_monitor && !force_monitor) { + printk(KERN_WARNING "%s: Monitor mode support is " + "buggy in this firmware, not enabling\n", + wiphy_name(wiphy)); + err = -EINVAL; + } + break; + + default: + err = -EINVAL; + } + + if (!err) { + priv->iw_mode = type; + set_port_type(priv); + err = orinoco_commit(priv); + } + + orinoco_unlock(priv, &lock); + + return err; +} + +static int orinoco_scan(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request) +{ + struct orinoco_private *priv = wiphy_priv(wiphy); + int err; + + if (!request) + return -EINVAL; + + if (priv->scan_request && priv->scan_request != request) + return -EBUSY; + + priv->scan_request = request; + + err = orinoco_hw_trigger_scan(priv, request->ssids); + + return err; +} + +static int orinoco_set_channel(struct wiphy *wiphy, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + struct orinoco_private *priv = wiphy_priv(wiphy); + int err = 0; + unsigned long flags; + int channel; + + if (!chan) + return -EINVAL; + + if (channel_type != NL80211_CHAN_NO_HT) + return -EINVAL; + + if (chan->band != IEEE80211_BAND_2GHZ) + return -EINVAL; + + channel = ieee80211_freq_to_dsss_chan(chan->center_freq); + + if ((channel < 1) || (channel > NUM_CHANNELS) || + !(priv->channel_mask & (1 << (channel-1)))) + return -EINVAL; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + priv->channel = channel; + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { + /* Fast channel change - no commit if successful */ + hermes_t *hw = &priv->hw; + err = hermes_docmd_wait(hw, HERMES_CMD_TEST | + HERMES_TEST_SET_CHANNEL, + channel, NULL); + } + orinoco_unlock(priv, &flags); + + return err; +} + +const struct cfg80211_ops orinoco_cfg_ops = { + .change_virtual_intf = orinoco_change_vif, + .set_channel = orinoco_set_channel, + .scan = orinoco_scan, +}; diff --git a/drivers/net/wireless/orinoco/cfg.h b/drivers/net/wireless/orinoco/cfg.h new file mode 100644 index 000000000000..3ddc96a06cd7 --- /dev/null +++ b/drivers/net/wireless/orinoco/cfg.h @@ -0,0 +1,15 @@ +/* cfg80211 support. + * + * See copyright notice in main.c + */ +#ifndef ORINOCO_CFG_H +#define ORINOCO_CFG_H + +#include <net/cfg80211.h> + +extern const struct cfg80211_ops orinoco_cfg_ops; + +void orinoco_wiphy_init(struct wiphy *wiphy); +int orinoco_wiphy_register(struct wiphy *wiphy); + +#endif /* ORINOCO_CFG_H */ diff --git a/drivers/net/wireless/orinoco/fw.c b/drivers/net/wireless/orinoco/fw.c index 1084b43e04bc..1257250a1e22 100644 --- a/drivers/net/wireless/orinoco/fw.c +++ b/drivers/net/wireless/orinoco/fw.c @@ -4,6 +4,7 @@ */ #include <linux/kernel.h> #include <linux/firmware.h> +#include <linux/device.h> #include "hermes.h" #include "hermes_dld.h" @@ -99,7 +100,7 @@ orinoco_dl_firmware(struct orinoco_private *priv, const void *end; const char *firmware; const char *fw_err; - struct net_device *dev = priv->ndev; + struct device *dev = priv->dev; int err = 0; pda = kzalloc(fw->pda_size, GFP_KERNEL); @@ -111,12 +112,11 @@ orinoco_dl_firmware(struct orinoco_private *priv, else firmware = fw->sta_fw; - printk(KERN_DEBUG "%s: Attempting to download firmware %s\n", - dev->name, firmware); + dev_dbg(dev, "Attempting to download firmware %s\n", firmware); /* Read current plug data */ err = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 0); - printk(KERN_DEBUG "%s: Read PDA returned %d\n", dev->name, err); + dev_dbg(dev, "Read PDA returned %d\n", err); if (err) goto free; @@ -124,8 +124,7 @@ orinoco_dl_firmware(struct orinoco_private *priv, err = request_firmware(&fw_entry, firmware, priv->dev); if (err) { - printk(KERN_ERR "%s: Cannot find firmware %s\n", - dev->name, firmware); + dev_err(dev, "Cannot find firmware %s\n", firmware); err = -ENOENT; goto free; } @@ -136,16 +135,15 @@ orinoco_dl_firmware(struct orinoco_private *priv, fw_err = validate_fw(hdr, fw_entry->size); if (fw_err) { - printk(KERN_WARNING "%s: Invalid firmware image detected (%s). " - "Aborting download\n", - dev->name, fw_err); + dev_warn(dev, "Invalid firmware image detected (%s). " + "Aborting download\n", fw_err); err = -EINVAL; goto abort; } /* Enable aux port to allow programming */ err = hermesi_program_init(hw, le32_to_cpu(hdr->entry_point)); - printk(KERN_DEBUG "%s: Program init returned %d\n", dev->name, err); + dev_dbg(dev, "Program init returned %d\n", err); if (err != 0) goto abort; @@ -156,7 +154,7 @@ orinoco_dl_firmware(struct orinoco_private *priv, end = fw_entry->data + fw_entry->size; err = hermes_program(hw, first_block, end); - printk(KERN_DEBUG "%s: Program returned %d\n", dev->name, err); + dev_dbg(dev, "Program returned %d\n", err); if (err != 0) goto abort; @@ -167,19 +165,18 @@ orinoco_dl_firmware(struct orinoco_private *priv, err = hermes_apply_pda_with_defaults(hw, first_block, end, pda, &pda[fw->pda_size / sizeof(*pda)]); - printk(KERN_DEBUG "%s: Apply PDA returned %d\n", dev->name, err); + dev_dbg(dev, "Apply PDA returned %d\n", err); if (err) goto abort; /* Tell card we've finished */ err = hermesi_program_end(hw); - printk(KERN_DEBUG "%s: Program end returned %d\n", dev->name, err); + dev_dbg(dev, "Program end returned %d\n", err); if (err != 0) goto abort; /* Check if we're running */ - printk(KERN_DEBUG "%s: hermes_present returned %d\n", - dev->name, hermes_present(hw)); + dev_dbg(dev, "hermes_present returned %d\n", hermes_present(hw)); abort: /* If we requested the firmware, release it. */ @@ -282,14 +279,13 @@ static int symbol_dl_firmware(struct orinoco_private *priv, const struct fw_info *fw) { - struct net_device *dev = priv->ndev; + struct device *dev = priv->dev; int ret; const struct firmware *fw_entry; if (!orinoco_cached_fw_get(priv, true)) { if (request_firmware(&fw_entry, fw->pri_fw, priv->dev) != 0) { - printk(KERN_ERR "%s: Cannot find firmware: %s\n", - dev->name, fw->pri_fw); + dev_err(dev, "Cannot find firmware: %s\n", fw->pri_fw); return -ENOENT; } } else @@ -302,15 +298,13 @@ symbol_dl_firmware(struct orinoco_private *priv, if (!orinoco_cached_fw_get(priv, true)) release_firmware(fw_entry); if (ret) { - printk(KERN_ERR "%s: Primary firmware download failed\n", - dev->name); + dev_err(dev, "Primary firmware download failed\n"); return ret; } if (!orinoco_cached_fw_get(priv, false)) { if (request_firmware(&fw_entry, fw->sta_fw, priv->dev) != 0) { - printk(KERN_ERR "%s: Cannot find firmware: %s\n", - dev->name, fw->sta_fw); + dev_err(dev, "Cannot find firmware: %s\n", fw->sta_fw); return -ENOENT; } } else @@ -322,8 +316,7 @@ symbol_dl_firmware(struct orinoco_private *priv, if (!orinoco_cached_fw_get(priv, false)) release_firmware(fw_entry); if (ret) { - printk(KERN_ERR "%s: Secondary firmware download failed\n", - dev->name); + dev_err(dev, "Secondary firmware download failed\n"); } return ret; diff --git a/drivers/net/wireless/orinoco/hermes.c b/drivers/net/wireless/orinoco/hermes.c index f2c918c2572d..1a2fca76fd3c 100644 --- a/drivers/net/wireless/orinoco/hermes.c +++ b/drivers/net/wireless/orinoco/hermes.c @@ -469,7 +469,7 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize, u16 rlength, rtype; unsigned nwords; - if ((bufsize < 0) || (bufsize % 2)) + if (bufsize % 2) return -EINVAL; err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL); diff --git a/drivers/net/wireless/orinoco/hermes.h b/drivers/net/wireless/orinoco/hermes.h index c78c442a02c8..2dddbb597c4d 100644 --- a/drivers/net/wireless/orinoco/hermes.h +++ b/drivers/net/wireless/orinoco/hermes.h @@ -342,7 +342,7 @@ struct agere_ext_scan_info { __le64 timestamp; __le16 beacon_interval; __le16 capabilities; - u8 data[316]; + u8 data[0]; } __attribute__ ((packed)); #define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000) diff --git a/drivers/net/wireless/orinoco/hermes_dld.c b/drivers/net/wireless/orinoco/hermes_dld.c index a9ba195cdada..a3eefe109df4 100644 --- a/drivers/net/wireless/orinoco/hermes_dld.c +++ b/drivers/net/wireless/orinoco/hermes_dld.c @@ -309,7 +309,7 @@ int hermes_read_pda(hermes_t *hw, /* Open auxiliary port */ ret = hermes_aux_control(hw, 1); - printk(KERN_DEBUG PFX "AUX enable returned %d\n", ret); + pr_debug(PFX "AUX enable returned %d\n", ret); if (ret) return ret; @@ -319,12 +319,12 @@ int hermes_read_pda(hermes_t *hw, /* Close aux port */ ret = hermes_aux_control(hw, 0); - printk(KERN_DEBUG PFX "AUX disable returned %d\n", ret); + pr_debug(PFX "AUX disable returned %d\n", ret); /* Check PDA length */ pda_size = le16_to_cpu(pda[0]); - printk(KERN_DEBUG PFX "Actual PDA length %d, Max allowed %d\n", - pda_size, pda_len); + pr_debug(PFX "Actual PDA length %d, Max allowed %d\n", + pda_size, pda_len); if (pda_size > pda_len) return -EINVAL; @@ -422,20 +422,19 @@ int hermesi_program_init(hermes_t *hw, u32 offset) return err; err = hermes_aux_control(hw, 1); - printk(KERN_DEBUG PFX "AUX enable returned %d\n", err); + pr_debug(PFX "AUX enable returned %d\n", err); if (err) return err; - printk(KERN_DEBUG PFX "Enabling volatile, EP 0x%08x\n", offset); + pr_debug(KERN_DEBUG PFX "Enabling volatile, EP 0x%08x\n", offset); err = hermes_doicmd_wait(hw, HERMES_PROGRAM_ENABLE_VOLATILE, offset & 0xFFFFu, offset >> 16, 0, NULL); - printk(KERN_DEBUG PFX "PROGRAM_ENABLE returned %d\n", - err); + pr_debug(PFX "PROGRAM_ENABLE returned %d\n", err); return err; } @@ -454,16 +453,16 @@ int hermesi_program_end(hermes_t *hw) rc = hermes_docmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp); - printk(KERN_DEBUG PFX "PROGRAM_DISABLE returned %d, " - "r0 0x%04x, r1 0x%04x, r2 0x%04x\n", - rc, resp.resp0, resp.resp1, resp.resp2); + pr_debug(PFX "PROGRAM_DISABLE returned %d, " + "r0 0x%04x, r1 0x%04x, r2 0x%04x\n", + rc, resp.resp0, resp.resp1, resp.resp2); if ((rc == 0) && ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD)) rc = -EIO; err = hermes_aux_control(hw, 0); - printk(KERN_DEBUG PFX "AUX disable returned %d\n", err); + pr_debug(PFX "AUX disable returned %d\n", err); /* Acknowledge any outstanding command */ hermes_write_regn(hw, EVACK, 0xFFFF); @@ -496,9 +495,8 @@ int hermes_program(hermes_t *hw, const char *first_block, const void *end) while ((blkaddr != BLOCK_END) && (((void *) blk + blklen) <= end)) { - printk(KERN_DEBUG PFX - "Programming block of length %d to address 0x%08x\n", - blklen, blkaddr); + pr_debug(PFX "Programming block of length %d " + "to address 0x%08x\n", blklen, blkaddr); #if !LIMIT_PROGRAM_SIZE /* wl_lkm driver splits this into writes of 2000 bytes */ @@ -510,10 +508,9 @@ int hermes_program(hermes_t *hw, const char *first_block, const void *end) addr = blkaddr; while (addr < (blkaddr + blklen)) { - printk(KERN_DEBUG PFX - "Programming subblock of length %d " - "to address 0x%08x. Data @ %p\n", - len, addr, &blk->data[addr - blkaddr]); + pr_debug(PFX "Programming subblock of length %d " + "to address 0x%08x. Data @ %p\n", + len, addr, &blk->data[addr - blkaddr]); hermes_aux_setaddr(hw, addr); hermes_write_bytes(hw, HERMES_AUXDATA, @@ -643,8 +640,8 @@ int hermes_apply_pda_with_defaults(hermes_t *hw, pdi = hermes_find_pdi(first_pdi, record_id, pda_end); if (pdi) - printk(KERN_DEBUG PFX "Found record 0x%04x at %p\n", - record_id, pdi); + pr_debug(PFX "Found record 0x%04x at %p\n", + record_id, pdi); switch (record_id) { case 0x110: /* Modem REFDAC values */ @@ -654,9 +651,9 @@ int hermes_apply_pda_with_defaults(hermes_t *hw, default_pdi = NULL; if (outdoor_pdi) { pdi = outdoor_pdi; - printk(KERN_DEBUG PFX - "Using outdoor record 0x%04x at %p\n", - record_id + 1, pdi); + pr_debug(PFX + "Using outdoor record 0x%04x at %p\n", + record_id + 1, pdi); } break; case 0x5: /* HWIF Compatiblity */ @@ -684,9 +681,8 @@ int hermes_apply_pda_with_defaults(hermes_t *hw, if (!pdi && default_pdi) { /* Use default */ pdi = default_pdi; - printk(KERN_DEBUG PFX - "Using default record 0x%04x at %p\n", - record_id, pdi); + pr_debug(PFX "Using default record 0x%04x at %p\n", + record_id, pdi); } if (pdi) { diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c index b3946272c72e..359652d35e63 100644 --- a/drivers/net/wireless/orinoco/hw.c +++ b/drivers/net/wireless/orinoco/hw.c @@ -3,16 +3,22 @@ * See copyright notice in main.c */ #include <linux/kernel.h> +#include <linux/device.h> #include <linux/if_arp.h> #include <linux/ieee80211.h> #include <linux/wireless.h> - +#include <net/cfg80211.h> #include "hermes.h" #include "hermes_rid.h" #include "orinoco.h" #include "hw.h" +#define SYMBOL_MAX_VER_LEN (14) + +/* Symbol firmware has a bug allocating buffers larger than this */ +#define TX_NICBUF_SIZE_BUG 1585 + /********************************************************************/ /* Data tables */ /********************************************************************/ @@ -36,6 +42,343 @@ static const struct { }; #define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table) +/* Firmware version encoding */ +struct comp_id { + u16 id, variant, major, minor; +} __attribute__ ((packed)); + +static inline fwtype_t determine_firmware_type(struct comp_id *nic_id) +{ + if (nic_id->id < 0x8000) + return FIRMWARE_TYPE_AGERE; + else if (nic_id->id == 0x8000 && nic_id->major == 0) + return FIRMWARE_TYPE_SYMBOL; + else + return FIRMWARE_TYPE_INTERSIL; +} + +/* Set priv->firmware type, determine firmware properties + * This function can be called before we have registerred with netdev, + * so all errors go out with dev_* rather than printk + */ +int determine_fw_capabilities(struct orinoco_private *priv) +{ + struct device *dev = priv->dev; + hermes_t *hw = &priv->hw; + int err; + struct comp_id nic_id, sta_id; + unsigned int firmver; + char tmp[SYMBOL_MAX_VER_LEN+1] __attribute__((aligned(2))); + + /* Get the hardware version */ + err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id); + if (err) { + dev_err(dev, "Cannot read hardware identity: error %d\n", + err); + return err; + } + + le16_to_cpus(&nic_id.id); + le16_to_cpus(&nic_id.variant); + le16_to_cpus(&nic_id.major); + le16_to_cpus(&nic_id.minor); + dev_info(dev, "Hardware identity %04x:%04x:%04x:%04x\n", + nic_id.id, nic_id.variant, nic_id.major, nic_id.minor); + + priv->firmware_type = determine_firmware_type(&nic_id); + + /* Get the firmware version */ + err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id); + if (err) { + dev_err(dev, "Cannot read station identity: error %d\n", + err); + return err; + } + + le16_to_cpus(&sta_id.id); + le16_to_cpus(&sta_id.variant); + le16_to_cpus(&sta_id.major); + le16_to_cpus(&sta_id.minor); + dev_info(dev, "Station identity %04x:%04x:%04x:%04x\n", + sta_id.id, sta_id.variant, sta_id.major, sta_id.minor); + + switch (sta_id.id) { + case 0x15: + dev_err(dev, "Primary firmware is active\n"); + return -ENODEV; + case 0x14b: + dev_err(dev, "Tertiary firmware is active\n"); + return -ENODEV; + case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */ + case 0x21: /* Symbol Spectrum24 Trilogy */ + break; + default: + dev_notice(dev, "Unknown station ID, please report\n"); + break; + } + + /* Default capabilities */ + priv->has_sensitivity = 1; + priv->has_mwo = 0; + priv->has_preamble = 0; + priv->has_port3 = 1; + priv->has_ibss = 1; + priv->has_wep = 0; + priv->has_big_wep = 0; + priv->has_alt_txcntl = 0; + priv->has_ext_scan = 0; + priv->has_wpa = 0; + priv->do_fw_download = 0; + + /* Determine capabilities from the firmware version */ + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: + /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout, + ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */ + snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, + "Lucent/Agere %d.%02d", sta_id.major, sta_id.minor); + + firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor; + + priv->has_ibss = (firmver >= 0x60006); + priv->has_wep = (firmver >= 0x40020); + priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell + Gold cards from the others? */ + priv->has_mwo = (firmver >= 0x60000); + priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ + priv->ibss_port = 1; + priv->has_hostscan = (firmver >= 0x8000a); + priv->do_fw_download = 1; + priv->broken_monitor = (firmver >= 0x80000); + priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */ + priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */ + priv->has_wpa = (firmver >= 0x9002a); + /* Tested with Agere firmware : + * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II + * Tested CableTron firmware : 4.32 => Anton */ + break; + case FIRMWARE_TYPE_SYMBOL: + /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */ + /* Intel MAC : 00:02:B3:* */ + /* 3Com MAC : 00:50:DA:* */ + memset(tmp, 0, sizeof(tmp)); + /* Get the Symbol firmware version */ + err = hermes_read_ltv(hw, USER_BAP, + HERMES_RID_SECONDARYVERSION_SYMBOL, + SYMBOL_MAX_VER_LEN, NULL, &tmp); + if (err) { + dev_warn(dev, "Error %d reading Symbol firmware info. " + "Wildly guessing capabilities...\n", err); + firmver = 0; + tmp[0] = '\0'; + } else { + /* The firmware revision is a string, the format is + * something like : "V2.20-01". + * Quick and dirty parsing... - Jean II + */ + firmver = ((tmp[1] - '0') << 16) + | ((tmp[3] - '0') << 12) + | ((tmp[4] - '0') << 8) + | ((tmp[6] - '0') << 4) + | (tmp[7] - '0'); + + tmp[SYMBOL_MAX_VER_LEN] = '\0'; + } + + snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, + "Symbol %s", tmp); + + priv->has_ibss = (firmver >= 0x20000); + priv->has_wep = (firmver >= 0x15012); + priv->has_big_wep = (firmver >= 0x20000); + priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) || + (firmver >= 0x29000 && firmver < 0x30000) || + firmver >= 0x31000; + priv->has_preamble = (firmver >= 0x20000); + priv->ibss_port = 4; + + /* Symbol firmware is found on various cards, but + * there has been no attempt to check firmware + * download on non-spectrum_cs based cards. + * + * Given that the Agere firmware download works + * differently, we should avoid doing a firmware + * download with the Symbol algorithm on non-spectrum + * cards. + * + * For now we can identify a spectrum_cs based card + * because it has a firmware reset function. + */ + priv->do_fw_download = (priv->stop_fw != NULL); + + priv->broken_disableport = (firmver == 0x25013) || + (firmver >= 0x30000 && firmver <= 0x31000); + priv->has_hostscan = (firmver >= 0x31001) || + (firmver >= 0x29057 && firmver < 0x30000); + /* Tested with Intel firmware : 0x20015 => Jean II */ + /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */ + break; + case FIRMWARE_TYPE_INTERSIL: + /* D-Link, Linksys, Adtron, ZoomAir, and many others... + * Samsung, Compaq 100/200 and Proxim are slightly + * different and less well tested */ + /* D-Link MAC : 00:40:05:* */ + /* Addtron MAC : 00:90:D1:* */ + snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, + "Intersil %d.%d.%d", sta_id.major, sta_id.minor, + sta_id.variant); + + firmver = ((unsigned long)sta_id.major << 16) | + ((unsigned long)sta_id.minor << 8) | sta_id.variant; + + priv->has_ibss = (firmver >= 0x000700); /* FIXME */ + priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); + priv->has_pm = (firmver >= 0x000700); + priv->has_hostscan = (firmver >= 0x010301); + + if (firmver >= 0x000800) + priv->ibss_port = 0; + else { + dev_notice(dev, "Intersil firmware earlier than v0.8.x" + " - several features not supported\n"); + priv->ibss_port = 1; + } + break; + } + dev_info(dev, "Firmware determined as %s\n", priv->fw_name); + + return 0; +} + +/* Read settings from EEPROM into our private structure. + * MAC address gets dropped into callers buffer + * Can be called before netdev registration. + */ +int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr) +{ + struct device *dev = priv->dev; + struct hermes_idstring nickbuf; + hermes_t *hw = &priv->hw; + int len; + int err; + u16 reclen; + + /* Get the MAC address */ + err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, + ETH_ALEN, NULL, dev_addr); + if (err) { + dev_warn(dev, "Failed to read MAC address!\n"); + goto out; + } + + dev_dbg(dev, "MAC address %pM\n", dev_addr); + + /* Get the station name */ + err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, + sizeof(nickbuf), &reclen, &nickbuf); + if (err) { + dev_err(dev, "failed to read station name\n"); + goto out; + } + if (nickbuf.len) + len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len)); + else + len = min(IW_ESSID_MAX_SIZE, 2 * reclen); + memcpy(priv->nick, &nickbuf.val, len); + priv->nick[len] = '\0'; + + dev_dbg(dev, "Station name \"%s\"\n", priv->nick); + + /* Get allowed channels */ + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST, + &priv->channel_mask); + if (err) { + dev_err(dev, "Failed to read channel list!\n"); + goto out; + } + + /* Get initial AP density */ + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, + &priv->ap_density); + if (err || priv->ap_density < 1 || priv->ap_density > 3) + priv->has_sensitivity = 0; + + /* Get initial RTS threshold */ + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, + &priv->rts_thresh); + if (err) { + dev_err(dev, "Failed to read RTS threshold!\n"); + goto out; + } + + /* Get initial fragmentation settings */ + if (priv->has_mwo) + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFMWOROBUST_AGERE, + &priv->mwo_robust); + else + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, + &priv->frag_thresh); + if (err) { + dev_err(dev, "Failed to read fragmentation settings!\n"); + goto out; + } + + /* Power management setup */ + if (priv->has_pm) { + priv->pm_on = 0; + priv->pm_mcast = 1; + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFMAXSLEEPDURATION, + &priv->pm_period); + if (err) { + dev_err(dev, "Failed to read power management " + "period!\n"); + goto out; + } + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFPMHOLDOVERDURATION, + &priv->pm_timeout); + if (err) { + dev_err(dev, "Failed to read power management " + "timeout!\n"); + goto out; + } + } + + /* Preamble setup */ + if (priv->has_preamble) { + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFPREAMBLE_SYMBOL, + &priv->preamble); + } + +out: + return err; +} + +/* Can be called before netdev registration */ +int orinoco_hw_allocate_fid(struct orinoco_private *priv) +{ + struct device *dev = priv->dev; + struct hermes *hw = &priv->hw; + int err; + + err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); + if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) { + /* Try workaround for old Symbol firmware bug */ + priv->nicbuf_size = TX_NICBUF_SIZE_BUG; + err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); + + dev_warn(dev, "Firmware ALLOC bug detected " + "(old Symbol firmware?). Work around %s\n", + err ? "failed!" : "ok."); + } + + return err; +} + int orinoco_get_bitratemode(int bitrate, int automatic) { int ratemode = -1; @@ -63,12 +406,243 @@ void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic) *automatic = bitrate_table[ratemode].automatic; } +int orinoco_hw_program_rids(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + struct wireless_dev *wdev = netdev_priv(dev); + hermes_t *hw = &priv->hw; + int err; + struct hermes_idstring idbuf; + + /* Set the MAC address */ + err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, + HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr); + if (err) { + printk(KERN_ERR "%s: Error %d setting MAC address\n", + dev->name, err); + return err; + } + + /* Set up the link mode */ + err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, + priv->port_type); + if (err) { + printk(KERN_ERR "%s: Error %d setting port type\n", + dev->name, err); + return err; + } + /* Set the channel/frequency */ + if (priv->channel != 0 && priv->iw_mode != NL80211_IFTYPE_STATION) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFOWNCHANNEL, + priv->channel); + if (err) { + printk(KERN_ERR "%s: Error %d setting channel %d\n", + dev->name, err, priv->channel); + return err; + } + } + + if (priv->has_ibss) { + u16 createibss; + + if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) { + printk(KERN_WARNING "%s: This firmware requires an " + "ESSID in IBSS-Ad-Hoc mode.\n", dev->name); + /* With wvlan_cs, in this case, we would crash. + * hopefully, this driver will behave better... + * Jean II */ + createibss = 0; + } else { + createibss = priv->createibss; + } + + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFCREATEIBSS, + createibss); + if (err) { + printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", + dev->name, err); + return err; + } + } + + /* Set the desired BSSID */ + err = __orinoco_hw_set_wap(priv); + if (err) { + printk(KERN_ERR "%s: Error %d setting AP address\n", + dev->name, err); + return err; + } + + /* Set the desired ESSID */ + idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); + memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); + /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */ + err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, + HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), + &idbuf); + if (err) { + printk(KERN_ERR "%s: Error %d setting OWNSSID\n", + dev->name, err); + return err; + } + err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, + HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), + &idbuf); + if (err) { + printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", + dev->name, err); + return err; + } + + /* Set the station name */ + idbuf.len = cpu_to_le16(strlen(priv->nick)); + memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val)); + err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, + HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2), + &idbuf); + if (err) { + printk(KERN_ERR "%s: Error %d setting nickname\n", + dev->name, err); + return err; + } + + /* Set AP density */ + if (priv->has_sensitivity) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFSYSTEMSCALE, + priv->ap_density); + if (err) { + printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. " + "Disabling sensitivity control\n", + dev->name, err); + + priv->has_sensitivity = 0; + } + } + + /* Set RTS threshold */ + err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, + priv->rts_thresh); + if (err) { + printk(KERN_ERR "%s: Error %d setting RTS threshold\n", + dev->name, err); + return err; + } + + /* Set fragmentation threshold or MWO robustness */ + if (priv->has_mwo) + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFMWOROBUST_AGERE, + priv->mwo_robust); + else + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, + priv->frag_thresh); + if (err) { + printk(KERN_ERR "%s: Error %d setting fragmentation\n", + dev->name, err); + return err; + } + + /* Set bitrate */ + err = __orinoco_hw_set_bitrate(priv); + if (err) { + printk(KERN_ERR "%s: Error %d setting bitrate\n", + dev->name, err); + return err; + } + + /* Set power management */ + if (priv->has_pm) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFPMENABLED, + priv->pm_on); + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } + + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFMULTICASTRECEIVE, + priv->pm_mcast); + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFMAXSLEEPDURATION, + priv->pm_period); + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFPMHOLDOVERDURATION, + priv->pm_timeout); + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } + } + + /* Set preamble - only for Symbol so far... */ + if (priv->has_preamble) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFPREAMBLE_SYMBOL, + priv->preamble); + if (err) { + printk(KERN_ERR "%s: Error %d setting preamble\n", + dev->name, err); + return err; + } + } + + /* Set up encryption */ + if (priv->has_wep || priv->has_wpa) { + err = __orinoco_hw_setup_enc(priv); + if (err) { + printk(KERN_ERR "%s: Error %d activating encryption\n", + dev->name, err); + return err; + } + } + + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { + /* Enable monitor mode */ + dev->type = ARPHRD_IEEE80211; + err = hermes_docmd_wait(hw, HERMES_CMD_TEST | + HERMES_TEST_MONITOR, 0, NULL); + } else { + /* Disable monitor mode */ + dev->type = ARPHRD_ETHER; + err = hermes_docmd_wait(hw, HERMES_CMD_TEST | + HERMES_TEST_STOP, 0, NULL); + } + if (err) + return err; + + /* Reset promiscuity / multicast*/ + priv->promiscuous = 0; + priv->mc_count = 0; + + /* Record mode change */ + wdev->iftype = priv->iw_mode; + + return 0; +} + /* Get tsc from the firmware */ int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc) { hermes_t *hw = &priv->hw; int err = 0; - u8 tsc_arr[4][IW_ENCODE_SEQ_MAX_SIZE]; + u8 tsc_arr[4][ORINOCO_SEQ_LEN]; if ((key < 0) || (key >= 4)) return -EINVAL; @@ -194,12 +768,29 @@ int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv) { hermes_t *hw = &priv->hw; int err = 0; + int i; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: + { + struct orinoco_key keys[ORINOCO_MAX_KEYS]; + + memset(&keys, 0, sizeof(keys)); + for (i = 0; i < ORINOCO_MAX_KEYS; i++) { + int len = min(priv->keys[i].key_len, + ORINOCO_MAX_KEY_SIZE); + memcpy(&keys[i].data, priv->keys[i].key, len); + if (len > SMALL_KEY_SIZE) + keys[i].len = cpu_to_le16(LARGE_KEY_SIZE); + else if (len > 0) + keys[i].len = cpu_to_le16(SMALL_KEY_SIZE); + else + keys[i].len = cpu_to_le16(0); + } + err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFWEPKEYS_AGERE, - &priv->keys); + &keys); if (err) return err; err = hermes_write_wordrec(hw, USER_BAP, @@ -208,28 +799,38 @@ int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv) if (err) return err; break; + } case FIRMWARE_TYPE_INTERSIL: case FIRMWARE_TYPE_SYMBOL: { int keylen; - int i; /* Force uniform key length to work around * firmware bugs */ - keylen = le16_to_cpu(priv->keys[priv->tx_key].len); + keylen = priv->keys[priv->tx_key].key_len; if (keylen > LARGE_KEY_SIZE) { printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n", priv->ndev->name, priv->tx_key, keylen); return -E2BIG; - } + } else if (keylen > SMALL_KEY_SIZE) + keylen = LARGE_KEY_SIZE; + else if (keylen > 0) + keylen = SMALL_KEY_SIZE; + else + keylen = 0; /* Write all 4 keys */ for (i = 0; i < ORINOCO_MAX_KEYS; i++) { + u8 key[LARGE_KEY_SIZE] = { 0 }; + + memcpy(key, priv->keys[i].key, + priv->keys[i].key_len); + err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDEFAULTKEY0 + i, HERMES_BYTES_TO_RECLEN(keylen), - priv->keys[i].data); + key); if (err) return err; } @@ -255,8 +856,8 @@ int __orinoco_hw_setup_enc(struct orinoco_private *priv) int auth_flag; int enc_flag; - /* Setup WEP keys for WEP and WPA */ - if (priv->encode_alg) + /* Setup WEP keys */ + if (priv->encode_alg == ORINOCO_ALG_WEP) __orinoco_hw_setup_wepkeys(priv); if (priv->wep_restrict) @@ -266,14 +867,14 @@ int __orinoco_hw_setup_enc(struct orinoco_private *priv) if (priv->wpa_enabled) enc_flag = 2; - else if (priv->encode_alg == IW_ENCODE_ALG_WEP) + else if (priv->encode_alg == ORINOCO_ALG_WEP) enc_flag = 1; else enc_flag = 0; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ - if (priv->encode_alg == IW_ENCODE_ALG_WEP) { + if (priv->encode_alg == ORINOCO_ALG_WEP) { /* Enable the shared-key authentication. */ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFAUTHENTICATION_AGERE, @@ -298,7 +899,7 @@ int __orinoco_hw_setup_enc(struct orinoco_private *priv) case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */ case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */ - if (priv->encode_alg == IW_ENCODE_ALG_WEP) { + if (priv->encode_alg == ORINOCO_ALG_WEP) { if (priv->wep_restrict || (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)) master_wep_flag = HERMES_WEP_PRIVACY_INVOKED | @@ -314,7 +915,7 @@ int __orinoco_hw_setup_enc(struct orinoco_private *priv) } else master_wep_flag = 0; - if (priv->iw_mode == IW_MODE_MONITOR) + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) master_wep_flag |= HERMES_WEP_HOST_DECRYPT; /* Master WEP setting : on/off */ @@ -331,20 +932,22 @@ int __orinoco_hw_setup_enc(struct orinoco_private *priv) } /* key must be 32 bytes, including the tx and rx MIC keys. - * rsc must be 8 bytes - * tsc must be 8 bytes or NULL + * rsc must be NULL or up to 8 bytes + * tsc must be NULL or up to 8 bytes */ -int __orinoco_hw_set_tkip_key(hermes_t *hw, int key_idx, int set_tx, - u8 *key, u8 *rsc, u8 *tsc) +int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, + int set_tx, u8 *key, u8 *rsc, size_t rsc_len, + u8 *tsc, size_t tsc_len) { struct { __le16 idx; - u8 rsc[IW_ENCODE_SEQ_MAX_SIZE]; + u8 rsc[ORINOCO_SEQ_LEN]; u8 key[TKIP_KEYLEN]; u8 tx_mic[MIC_KEYLEN]; u8 rx_mic[MIC_KEYLEN]; - u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; + u8 tsc[ORINOCO_SEQ_LEN]; } __attribute__ ((packed)) buf; + hermes_t *hw = &priv->hw; int ret; int err; int k; @@ -359,17 +962,22 @@ int __orinoco_hw_set_tkip_key(hermes_t *hw, int key_idx, int set_tx, memcpy(buf.key, key, sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic)); - if (rsc == NULL) - memset(buf.rsc, 0, sizeof(buf.rsc)); - else - memcpy(buf.rsc, rsc, sizeof(buf.rsc)); + if (rsc_len > sizeof(buf.rsc)) + rsc_len = sizeof(buf.rsc); + + if (tsc_len > sizeof(buf.tsc)) + tsc_len = sizeof(buf.tsc); - if (tsc == NULL) { - memset(buf.tsc, 0, sizeof(buf.tsc)); + memset(buf.rsc, 0, sizeof(buf.rsc)); + memset(buf.tsc, 0, sizeof(buf.tsc)); + + if (rsc != NULL) + memcpy(buf.rsc, rsc, rsc_len); + + if (tsc != NULL) + memcpy(buf.tsc, tsc, tsc_len); + else buf.tsc[4] = 0x10; - } else { - memcpy(buf.tsc, tsc, sizeof(buf.tsc)); - } /* Wait upto 100ms for tx queue to empty */ for (k = 100; k > 0; k--) { @@ -395,7 +1003,6 @@ int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx) hermes_t *hw = &priv->hw; int err; - memset(&priv->tkip_key[key_idx], 0, sizeof(priv->tkip_key[key_idx])); err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE, key_idx); @@ -582,3 +1189,124 @@ int orinoco_hw_get_bitratelist(struct orinoco_private *priv, return 0; } + +int orinoco_hw_trigger_scan(struct orinoco_private *priv, + const struct cfg80211_ssid *ssid) +{ + struct net_device *dev = priv->ndev; + hermes_t *hw = &priv->hw; + unsigned long flags; + int err = 0; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + /* Scanning with port 0 disabled would fail */ + if (!netif_running(dev)) { + err = -ENETDOWN; + goto out; + } + + /* In monitor mode, the scan results are always empty. + * Probe responses are passed to the driver as received + * frames and could be processed in software. */ + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { + err = -EOPNOTSUPP; + goto out; + } + + if (priv->has_hostscan) { + switch (priv->firmware_type) { + case FIRMWARE_TYPE_SYMBOL: + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFHOSTSCAN_SYMBOL, + HERMES_HOSTSCAN_SYMBOL_ONCE | + HERMES_HOSTSCAN_SYMBOL_BCAST); + break; + case FIRMWARE_TYPE_INTERSIL: { + __le16 req[3]; + + req[0] = cpu_to_le16(0x3fff); /* All channels */ + req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */ + req[2] = 0; /* Any ESSID */ + err = HERMES_WRITE_RECORD(hw, USER_BAP, + HERMES_RID_CNFHOSTSCAN, &req); + break; + } + case FIRMWARE_TYPE_AGERE: + if (ssid->ssid_len > 0) { + struct hermes_idstring idbuf; + size_t len = ssid->ssid_len; + + idbuf.len = cpu_to_le16(len); + memcpy(idbuf.val, ssid->ssid, len); + + err = hermes_write_ltv(hw, USER_BAP, + HERMES_RID_CNFSCANSSID_AGERE, + HERMES_BYTES_TO_RECLEN(len + 2), + &idbuf); + } else + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFSCANSSID_AGERE, + 0); /* Any ESSID */ + if (err) + break; + + if (priv->has_ext_scan) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFSCANCHANNELS2GHZ, + 0x7FFF); + if (err) + goto out; + + err = hermes_inquire(hw, + HERMES_INQ_CHANNELINFO); + } else + err = hermes_inquire(hw, HERMES_INQ_SCAN); + + break; + } + } else + err = hermes_inquire(hw, HERMES_INQ_SCAN); + + out: + orinoco_unlock(priv, &flags); + + return err; +} + +/* Disassociate from node with BSSID addr */ +int orinoco_hw_disassociate(struct orinoco_private *priv, + u8 *addr, u16 reason_code) +{ + hermes_t *hw = &priv->hw; + int err; + + struct { + u8 addr[ETH_ALEN]; + __le16 reason_code; + } __attribute__ ((packed)) buf; + + /* Currently only supported by WPA enabled Agere fw */ + if (!priv->has_wpa) + return -EOPNOTSUPP; + + memcpy(buf.addr, addr, ETH_ALEN); + buf.reason_code = cpu_to_le16(reason_code); + err = HERMES_WRITE_RECORD(hw, USER_BAP, + HERMES_RID_CNFDISASSOCIATE, + &buf); + return err; +} + +int orinoco_hw_get_current_bssid(struct orinoco_private *priv, + u8 *addr) +{ + hermes_t *hw = &priv->hw; + int err; + + err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, + ETH_ALEN, NULL, addr); + + return err; +} diff --git a/drivers/net/wireless/orinoco/hw.h b/drivers/net/wireless/orinoco/hw.h index dc3f23a9c1c7..8df6e8752be6 100644 --- a/drivers/net/wireless/orinoco/hw.h +++ b/drivers/net/wireless/orinoco/hw.h @@ -7,6 +7,7 @@ #include <linux/types.h> #include <linux/wireless.h> +#include <net/cfg80211.h> /* Hardware BAPs */ #define USER_BAP 0 @@ -23,17 +24,22 @@ struct orinoco_private; struct dev_addr_list; +int determine_fw_capabilities(struct orinoco_private *priv); +int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr); +int orinoco_hw_allocate_fid(struct orinoco_private *priv); int orinoco_get_bitratemode(int bitrate, int automatic); void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic); +int orinoco_hw_program_rids(struct orinoco_private *priv); int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc); int __orinoco_hw_set_bitrate(struct orinoco_private *priv); int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate); int __orinoco_hw_set_wap(struct orinoco_private *priv); int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv); int __orinoco_hw_setup_enc(struct orinoco_private *priv); -int __orinoco_hw_set_tkip_key(hermes_t *hw, int key_idx, int set_tx, - u8 *key, u8 *rsc, u8 *tsc); +int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, + int set_tx, u8 *key, u8 *rsc, size_t rsc_len, + u8 *tsc, size_t tsc_len); int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx); int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, struct dev_addr_list *mc_list, @@ -43,5 +49,11 @@ int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, int orinoco_hw_get_freq(struct orinoco_private *priv); int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates, s32 *rates, int max); +int orinoco_hw_trigger_scan(struct orinoco_private *priv, + const struct cfg80211_ssid *ssid); +int orinoco_hw_disassociate(struct orinoco_private *priv, + u8 *addr, u16 reason_code); +int orinoco_hw_get_current_bssid(struct orinoco_private *priv, + u8 *addr); #endif /* _ORINOCO_HW_H_ */ diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c index a370e510f19f..7a32bcb0c037 100644 --- a/drivers/net/wireless/orinoco/main.c +++ b/drivers/net/wireless/orinoco/main.c @@ -80,6 +80,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/device.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> @@ -88,6 +89,7 @@ #include <linux/wireless.h> #include <linux/ieee80211.h> #include <net/iw_handler.h> +#include <net/cfg80211.h> #include "hermes_rid.h" #include "hermes_dld.h" @@ -96,6 +98,7 @@ #include "mic.h" #include "fw.h" #include "wext.h" +#include "cfg.h" #include "main.h" #include "orinoco.h" @@ -142,13 +145,11 @@ static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; #define ORINOCO_MIN_MTU 256 #define ORINOCO_MAX_MTU (IEEE80211_MAX_DATA_LEN - ENCAPS_OVERHEAD) -#define SYMBOL_MAX_VER_LEN (14) #define MAX_IRQLOOPS_PER_IRQ 10 #define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of * how many events the * device could * legitimately generate */ -#define TX_NICBUF_SIZE_BUG 1585 /* Bug in Symbol firmware */ #define DUMMY_FID 0xFFFF @@ -205,11 +206,21 @@ struct orinoco_rx_data { struct list_head list; }; +struct orinoco_scan_data { + void *buf; + size_t len; + int type; + struct list_head list; +}; + /********************************************************************/ /* Function prototypes */ /********************************************************************/ -static void __orinoco_set_multicast_list(struct net_device *dev); +static int __orinoco_set_multicast_list(struct net_device *dev); +static int __orinoco_up(struct orinoco_private *priv); +static int __orinoco_down(struct orinoco_private *priv); +static int __orinoco_commit(struct orinoco_private *priv); /********************************************************************/ /* Internal helper functions */ @@ -218,11 +229,11 @@ static void __orinoco_set_multicast_list(struct net_device *dev); void set_port_type(struct orinoco_private *priv) { switch (priv->iw_mode) { - case IW_MODE_INFRA: + case NL80211_IFTYPE_STATION: priv->port_type = 1; priv->createibss = 0; break; - case IW_MODE_ADHOC: + case NL80211_IFTYPE_ADHOC: if (priv->prefer_port3) { priv->port_type = 3; priv->createibss = 0; @@ -231,7 +242,7 @@ void set_port_type(struct orinoco_private *priv) priv->createibss = 1; } break; - case IW_MODE_MONITOR: + case NL80211_IFTYPE_MONITOR: priv->port_type = 3; priv->createibss = 0; break; @@ -247,14 +258,14 @@ void set_port_type(struct orinoco_private *priv) static int orinoco_open(struct net_device *dev) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; int err; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; - err = __orinoco_up(dev); + err = __orinoco_up(priv); if (!err) priv->open = 1; @@ -266,7 +277,7 @@ static int orinoco_open(struct net_device *dev) static int orinoco_stop(struct net_device *dev) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int err = 0; /* We mustn't use orinoco_lock() here, because we need to be @@ -276,7 +287,7 @@ static int orinoco_stop(struct net_device *dev) priv->open = 0; - err = __orinoco_down(dev); + err = __orinoco_down(priv); spin_unlock_irq(&priv->lock); @@ -285,14 +296,14 @@ static int orinoco_stop(struct net_device *dev) static struct net_device_stats *orinoco_get_stats(struct net_device *dev) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); return &priv->stats; } static void orinoco_set_multicast_list(struct net_device *dev) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; if (orinoco_lock(priv, &flags) != 0) { @@ -307,7 +318,7 @@ static void orinoco_set_multicast_list(struct net_device *dev) static int orinoco_change_mtu(struct net_device *dev, int new_mtu) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); if ((new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU)) return -EINVAL; @@ -326,16 +337,18 @@ static int orinoco_change_mtu(struct net_device *dev, int new_mtu) /* Tx path */ /********************************************************************/ -static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t orinoco_xmit(struct sk_buff *skb, struct net_device *dev) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; + struct orinoco_tkip_key *key; hermes_t *hw = &priv->hw; int err = 0; u16 txfid = priv->txfid; struct ethhdr *eh; int tx_control; unsigned long flags; + int do_mic; if (!netif_running(dev)) { printk(KERN_ERR "%s: Tx on stopped device!\n", @@ -355,7 +368,8 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } - if (!netif_carrier_ok(dev) || (priv->iw_mode == IW_MODE_MONITOR)) { + if (!netif_carrier_ok(dev) || + (priv->iw_mode == NL80211_IFTYPE_MONITOR)) { /* Oops, the firmware hasn't established a connection, silently drop the packet (this seems to be the safest approach). */ @@ -366,9 +380,14 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) if (skb->len < ETH_HLEN) goto drop; + key = (struct orinoco_tkip_key *) priv->keys[priv->tx_key].key; + + do_mic = ((priv->encode_alg == ORINOCO_ALG_TKIP) && + (key != NULL)); + tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX; - if (priv->encode_alg == IW_ENCODE_ALG_TKIP) + if (do_mic) tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) | HERMES_TXCTRL_MIC; @@ -450,7 +469,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) } /* Calculate Michael MIC */ - if (priv->encode_alg == IW_ENCODE_ALG_TKIP) { + if (do_mic) { u8 mic_buf[MICHAEL_MIC_LEN + 1]; u8 *mic; size_t offset; @@ -468,8 +487,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) len = MICHAEL_MIC_LEN; } - orinoco_mic(priv->tx_tfm_mic, - priv->tkip_key[priv->tx_key].tx_mic, + orinoco_mic(priv->tx_tfm_mic, key->tx_mic, eh->h_dest, eh->h_source, 0 /* priority */, skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic); @@ -518,7 +536,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); u16 fid = hermes_read_regn(hw, ALLOCFID); if (fid != priv->txfid) { @@ -533,7 +551,7 @@ static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw) static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; stats->tx_packets++; @@ -545,7 +563,7 @@ static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw) static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; u16 fid = hermes_read_regn(hw, TXCOMPLFID); u16 status; @@ -601,7 +619,7 @@ static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) static void orinoco_tx_timeout(struct net_device *dev) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; struct hermes *hw = &priv->hw; @@ -650,7 +668,7 @@ static void orinoco_stat_gather(struct net_device *dev, struct sk_buff *skb, struct hermes_rx_descriptor *desc) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); /* Using spy support with lots of Rx packets, like in an * infrastructure (AP), will really slow down everything, because @@ -687,7 +705,7 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid, int err; int len; struct sk_buff *skb; - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; hermes_t *hw = &priv->hw; @@ -778,7 +796,7 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid, static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; struct iw_statistics *wstats = &priv->wstats; struct sk_buff *skb = NULL; @@ -816,7 +834,7 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) } /* Handle frames in monitor mode */ - if (priv->iw_mode == IW_MODE_MONITOR) { + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { orinoco_rx_monitor(dev, rxfid, desc); goto out; } @@ -902,7 +920,7 @@ static void orinoco_rx(struct net_device *dev, struct hermes_rx_descriptor *desc, struct sk_buff *skb) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); struct net_device_stats *stats = &priv->stats; u16 status, fc; int length; @@ -914,6 +932,7 @@ static void orinoco_rx(struct net_device *dev, /* Calculate and check MIC */ if (status & HERMES_RXSTAT_MIC) { + struct orinoco_tkip_key *key; int key_id = ((status & HERMES_RXSTAT_MIC_KEY_ID) >> HERMES_MIC_KEY_ID_SHIFT); u8 mic[MICHAEL_MIC_LEN]; @@ -927,14 +946,18 @@ static void orinoco_rx(struct net_device *dev, skb_trim(skb, skb->len - MICHAEL_MIC_LEN); length -= MICHAEL_MIC_LEN; - orinoco_mic(priv->rx_tfm_mic, - priv->tkip_key[key_id].rx_mic, - desc->addr1, - src, + key = (struct orinoco_tkip_key *) priv->keys[key_id].key; + + if (!key) { + printk(KERN_WARNING "%s: Received encrypted frame from " + "%pM using key %i, but key is not installed\n", + dev->name, src, key_id); + goto drop; + } + + orinoco_mic(priv->rx_tfm_mic, key->rx_mic, desc->addr1, src, 0, /* priority or QoS? */ - skb->data, - skb->len, - &mic[0]); + skb->data, skb->len, &mic[0]); if (memcmp(mic, rxmic, MICHAEL_MIC_LEN)) { @@ -1016,8 +1039,8 @@ static void orinoco_rx(struct net_device *dev, static void orinoco_rx_isr_tasklet(unsigned long data) { - struct net_device *dev = (struct net_device *) data; - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = (struct orinoco_private *) data; + struct net_device *dev = priv->ndev; struct orinoco_rx_data *rx_data, *temp; struct hermes_rx_descriptor *desc; struct sk_buff *skb; @@ -1260,9 +1283,81 @@ static void orinoco_send_wevents(struct work_struct *work) orinoco_unlock(priv, &flags); } +static void qbuf_scan(struct orinoco_private *priv, void *buf, + int len, int type) +{ + struct orinoco_scan_data *sd; + unsigned long flags; + + sd = kmalloc(sizeof(*sd), GFP_ATOMIC); + sd->buf = buf; + sd->len = len; + sd->type = type; + + spin_lock_irqsave(&priv->scan_lock, flags); + list_add_tail(&sd->list, &priv->scan_list); + spin_unlock_irqrestore(&priv->scan_lock, flags); + + schedule_work(&priv->process_scan); +} + +static void qabort_scan(struct orinoco_private *priv) +{ + struct orinoco_scan_data *sd; + unsigned long flags; + + sd = kmalloc(sizeof(*sd), GFP_ATOMIC); + sd->len = -1; /* Abort */ + + spin_lock_irqsave(&priv->scan_lock, flags); + list_add_tail(&sd->list, &priv->scan_list); + spin_unlock_irqrestore(&priv->scan_lock, flags); + + schedule_work(&priv->process_scan); +} + +static void orinoco_process_scan_results(struct work_struct *work) +{ + struct orinoco_private *priv = + container_of(work, struct orinoco_private, process_scan); + struct orinoco_scan_data *sd, *temp; + unsigned long flags; + void *buf; + int len; + int type; + + spin_lock_irqsave(&priv->scan_lock, flags); + list_for_each_entry_safe(sd, temp, &priv->scan_list, list) { + spin_unlock_irqrestore(&priv->scan_lock, flags); + + buf = sd->buf; + len = sd->len; + type = sd->type; + + list_del(&sd->list); + kfree(sd); + + if (len > 0) { + if (type == HERMES_INQ_CHANNELINFO) + orinoco_add_extscan_result(priv, buf, len); + else + orinoco_add_hostscan_results(priv, buf, len); + + kfree(buf); + } else if (priv->scan_request) { + /* Either abort or complete the scan */ + cfg80211_scan_done(priv->scan_request, (len < 0)); + priv->scan_request = NULL; + } + + spin_lock_irqsave(&priv->scan_lock, flags); + } + spin_unlock_irqrestore(&priv->scan_lock, flags); +} + static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); u16 infofid; struct { __le16 len; @@ -1327,7 +1422,7 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) u16 newstatus; int connected; - if (priv->iw_mode == IW_MODE_MONITOR) + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) break; if (len != sizeof(linkstatus)) { @@ -1346,7 +1441,7 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) * the hostscan frame can be requested. */ if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE && priv->firmware_type == FIRMWARE_TYPE_SYMBOL && - priv->has_hostscan && priv->scan_inprogress) { + priv->has_hostscan && priv->scan_request) { hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL); break; } @@ -1372,7 +1467,7 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) } break; case HERMES_INQ_SCAN: - if (!priv->scan_inprogress && priv->bssid_fixed && + if (!priv->scan_request && priv->bssid_fixed && priv->firmware_type == FIRMWARE_TYPE_INTERSIL) { schedule_work(&priv->join_work); break; @@ -1382,30 +1477,30 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) case HERMES_INQ_HOSTSCAN_SYMBOL: { /* Result of a scanning. Contains information about * cells in the vicinity - Jean II */ - union iwreq_data wrqu; unsigned char *buf; - /* Scan is no longer in progress */ - priv->scan_inprogress = 0; - /* Sanity check */ if (len > 4096) { printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n", dev->name, len); + qabort_scan(priv); break; } /* Allocate buffer for results */ buf = kmalloc(len, GFP_ATOMIC); - if (buf == NULL) + if (buf == NULL) { /* No memory, so can't printk()... */ + qabort_scan(priv); break; + } /* Read scan data */ err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len, infofid, sizeof(info)); if (err) { kfree(buf); + qabort_scan(priv); break; } @@ -1419,24 +1514,14 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) } #endif /* ORINOCO_DEBUG */ - if (orinoco_process_scan_results(priv, buf, len) == 0) { - /* Send an empty event to user space. - * We don't send the received data on the event because - * it would require us to do complex transcoding, and - * we want to minimise the work done in the irq handler - * Use a request to extract the data - Jean II */ - wrqu.data.length = 0; - wrqu.data.flags = 0; - wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); - } - kfree(buf); + qbuf_scan(priv, buf, len, type); } break; case HERMES_INQ_CHANNELINFO: { struct agere_ext_scan_info *bss; - if (!priv->scan_inprogress) { + if (!priv->scan_request) { printk(KERN_DEBUG "%s: Got chaninfo without scan, " "len=%d\n", dev->name, len); break; @@ -1444,25 +1529,12 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) /* An empty result indicates that the scan is complete */ if (len == 0) { - union iwreq_data wrqu; - - /* Scan is no longer in progress */ - priv->scan_inprogress = 0; - - wrqu.data.length = 0; - wrqu.data.flags = 0; - wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); + qbuf_scan(priv, NULL, len, type); break; } /* Sanity check */ - else if (len > sizeof(*bss)) { - printk(KERN_WARNING - "%s: Ext scan results too large (%d bytes). " - "Truncating results to %zd bytes.\n", - dev->name, len, sizeof(*bss)); - len = sizeof(*bss); - } else if (len < (offsetof(struct agere_ext_scan_info, + else if (len < (offsetof(struct agere_ext_scan_info, data) + 2)) { /* Drop this result now so we don't have to * keep checking later */ @@ -1472,21 +1544,18 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) break; } - bss = kmalloc(sizeof(*bss), GFP_ATOMIC); + bss = kmalloc(len, GFP_ATOMIC); if (bss == NULL) break; /* Read scan data */ err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len, infofid, sizeof(info)); - if (err) { + if (err) kfree(bss); - break; - } - - orinoco_add_ext_scan_result(priv, bss); + else + qbuf_scan(priv, bss, len, type); - kfree(bss); break; } case HERMES_INQ_SEC_STAT_AGERE: @@ -1501,6 +1570,8 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) /* We don't actually do anything about it */ break; } + + return; } static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw) @@ -1513,15 +1584,15 @@ static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw) /* Internal hardware control routines */ /********************************************************************/ -int __orinoco_up(struct net_device *dev) +static int __orinoco_up(struct orinoco_private *priv) { - struct orinoco_private *priv = netdev_priv(dev); + struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; int err; netif_carrier_off(dev); /* just to make sure */ - err = __orinoco_program_rids(dev); + err = __orinoco_commit(priv); if (err) { printk(KERN_ERR "%s: Error %d configuring card\n", dev->name, err); @@ -1541,11 +1612,10 @@ int __orinoco_up(struct net_device *dev) return 0; } -EXPORT_SYMBOL(__orinoco_up); -int __orinoco_down(struct net_device *dev) +static int __orinoco_down(struct orinoco_private *priv) { - struct orinoco_private *priv = netdev_priv(dev); + struct net_device *dev = priv->ndev; struct hermes *hw = &priv->hw; int err; @@ -1573,31 +1643,9 @@ int __orinoco_down(struct net_device *dev) return 0; } -EXPORT_SYMBOL(__orinoco_down); - -static int orinoco_allocate_fid(struct net_device *dev) -{ - struct orinoco_private *priv = netdev_priv(dev); - struct hermes *hw = &priv->hw; - int err; - - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); - if (err == -EIO && priv->nicbuf_size > TX_NICBUF_SIZE_BUG) { - /* Try workaround for old Symbol firmware bug */ - priv->nicbuf_size = TX_NICBUF_SIZE_BUG; - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); - - printk(KERN_WARNING "%s: firmware ALLOC bug detected " - "(old Symbol firmware?). Work around %s\n", - dev->name, err ? "failed!" : "ok."); - } - - return err; -} -int orinoco_reinit_firmware(struct net_device *dev) +static int orinoco_reinit_firmware(struct orinoco_private *priv) { - struct orinoco_private *priv = netdev_priv(dev); struct hermes *hw = &priv->hw; int err; @@ -1608,246 +1656,15 @@ int orinoco_reinit_firmware(struct net_device *dev) priv->do_fw_download = 0; } if (!err) - err = orinoco_allocate_fid(dev); + err = orinoco_hw_allocate_fid(priv); return err; } -EXPORT_SYMBOL(orinoco_reinit_firmware); - -int __orinoco_program_rids(struct net_device *dev) -{ - struct orinoco_private *priv = netdev_priv(dev); - hermes_t *hw = &priv->hw; - int err; - struct hermes_idstring idbuf; - - /* Set the MAC address */ - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, - HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr); - if (err) { - printk(KERN_ERR "%s: Error %d setting MAC address\n", - dev->name, err); - return err; - } - - /* Set up the link mode */ - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, - priv->port_type); - if (err) { - printk(KERN_ERR "%s: Error %d setting port type\n", - dev->name, err); - return err; - } - /* Set the channel/frequency */ - if (priv->channel != 0 && priv->iw_mode != IW_MODE_INFRA) { - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFOWNCHANNEL, - priv->channel); - if (err) { - printk(KERN_ERR "%s: Error %d setting channel %d\n", - dev->name, err, priv->channel); - return err; - } - } - - if (priv->has_ibss) { - u16 createibss; - - if ((strlen(priv->desired_essid) == 0) && (priv->createibss)) { - printk(KERN_WARNING "%s: This firmware requires an " - "ESSID in IBSS-Ad-Hoc mode.\n", dev->name); - /* With wvlan_cs, in this case, we would crash. - * hopefully, this driver will behave better... - * Jean II */ - createibss = 0; - } else { - createibss = priv->createibss; - } - - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFCREATEIBSS, - createibss); - if (err) { - printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", - dev->name, err); - return err; - } - } - /* Set the desired BSSID */ - err = __orinoco_hw_set_wap(priv); - if (err) { - printk(KERN_ERR "%s: Error %d setting AP address\n", - dev->name, err); - return err; - } - /* Set the desired ESSID */ - idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); - memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); - /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */ - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, - HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), - &idbuf); - if (err) { - printk(KERN_ERR "%s: Error %d setting OWNSSID\n", - dev->name, err); - return err; - } - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, - HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), - &idbuf); - if (err) { - printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", - dev->name, err); - return err; - } - - /* Set the station name */ - idbuf.len = cpu_to_le16(strlen(priv->nick)); - memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val)); - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, - HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2), - &idbuf); - if (err) { - printk(KERN_ERR "%s: Error %d setting nickname\n", - dev->name, err); - return err; - } - - /* Set AP density */ - if (priv->has_sensitivity) { - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFSYSTEMSCALE, - priv->ap_density); - if (err) { - printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. " - "Disabling sensitivity control\n", - dev->name, err); - - priv->has_sensitivity = 0; - } - } - - /* Set RTS threshold */ - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, - priv->rts_thresh); - if (err) { - printk(KERN_ERR "%s: Error %d setting RTS threshold\n", - dev->name, err); - return err; - } - - /* Set fragmentation threshold or MWO robustness */ - if (priv->has_mwo) - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFMWOROBUST_AGERE, - priv->mwo_robust); - else - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, - priv->frag_thresh); - if (err) { - printk(KERN_ERR "%s: Error %d setting fragmentation\n", - dev->name, err); - return err; - } - - /* Set bitrate */ - err = __orinoco_hw_set_bitrate(priv); - if (err) { - printk(KERN_ERR "%s: Error %d setting bitrate\n", - dev->name, err); - return err; - } - - /* Set power management */ - if (priv->has_pm) { - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFPMENABLED, - priv->pm_on); - if (err) { - printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); - return err; - } - - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFMULTICASTRECEIVE, - priv->pm_mcast); - if (err) { - printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); - return err; - } - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFMAXSLEEPDURATION, - priv->pm_period); - if (err) { - printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); - return err; - } - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFPMHOLDOVERDURATION, - priv->pm_timeout); - if (err) { - printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); - return err; - } - } - - /* Set preamble - only for Symbol so far... */ - if (priv->has_preamble) { - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFPREAMBLE_SYMBOL, - priv->preamble); - if (err) { - printk(KERN_ERR "%s: Error %d setting preamble\n", - dev->name, err); - return err; - } - } - - /* Set up encryption */ - if (priv->has_wep || priv->has_wpa) { - err = __orinoco_hw_setup_enc(priv); - if (err) { - printk(KERN_ERR "%s: Error %d activating encryption\n", - dev->name, err); - return err; - } - } - - if (priv->iw_mode == IW_MODE_MONITOR) { - /* Enable monitor mode */ - dev->type = ARPHRD_IEEE80211; - err = hermes_docmd_wait(hw, HERMES_CMD_TEST | - HERMES_TEST_MONITOR, 0, NULL); - } else { - /* Disable monitor mode */ - dev->type = ARPHRD_ETHER; - err = hermes_docmd_wait(hw, HERMES_CMD_TEST | - HERMES_TEST_STOP, 0, NULL); - } - if (err) - return err; - - /* Set promiscuity / multicast*/ - priv->promiscuous = 0; - priv->mc_count = 0; - - /* FIXME: what about netif_tx_lock */ - __orinoco_set_multicast_list(dev); - - return 0; -} - -/* FIXME: return int? */ -static void +static int __orinoco_set_multicast_list(struct net_device *dev) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int err = 0; int promisc, mc_count; @@ -1864,6 +1681,8 @@ __orinoco_set_multicast_list(struct net_device *dev) err = __orinoco_hw_set_multicast_list(priv, dev->mc_list, mc_count, promisc); + + return err; } /* This must be called from user context, without locks held - use @@ -1896,9 +1715,11 @@ void orinoco_reset(struct work_struct *work) orinoco_unlock(priv, &flags); - /* Scanning support: Cleanup of driver struct */ - orinoco_clear_scan_results(priv, 0); - priv->scan_inprogress = 0; + /* Scanning support: Notify scan cancellation */ + if (priv->scan_request) { + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + } if (priv->hard_reset) { err = (*priv->hard_reset)(priv); @@ -1909,7 +1730,7 @@ void orinoco_reset(struct work_struct *work) } } - err = orinoco_reinit_firmware(dev); + err = orinoco_reinit_firmware(priv); if (err) { printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n", dev->name, err); @@ -1924,7 +1745,7 @@ void orinoco_reset(struct work_struct *work) /* priv->open or priv->hw_unavailable might have changed while * we dropped the lock */ if (priv->open && (!priv->hw_unavailable)) { - err = __orinoco_up(dev); + err = __orinoco_up(priv); if (err) { printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", dev->name, err); @@ -1941,6 +1762,64 @@ void orinoco_reset(struct work_struct *work) printk(KERN_ERR "%s: Device has been disabled!\n", dev->name); } +static int __orinoco_commit(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + int err = 0; + + err = orinoco_hw_program_rids(priv); + + /* FIXME: what about netif_tx_lock */ + (void) __orinoco_set_multicast_list(dev); + + return err; +} + +/* Ensures configuration changes are applied. May result in a reset. + * The caller should hold priv->lock + */ +int orinoco_commit(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + hermes_t *hw = &priv->hw; + int err; + + if (priv->broken_disableport) { + schedule_work(&priv->reset_work); + return 0; + } + + err = hermes_disable_port(hw, 0); + if (err) { + printk(KERN_WARNING "%s: Unable to disable port " + "while reconfiguring card\n", dev->name); + priv->broken_disableport = 1; + goto out; + } + + err = __orinoco_commit(priv); + if (err) { + printk(KERN_WARNING "%s: Unable to reconfigure card\n", + dev->name); + goto out; + } + + err = hermes_enable_port(hw, 0); + if (err) { + printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", + dev->name); + goto out; + } + + out: + if (err) { + printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); + schedule_work(&priv->reset_work); + err = 0; + } + return err; +} + /********************************************************************/ /* Interrupt handler */ /********************************************************************/ @@ -1960,8 +1839,8 @@ static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw) irqreturn_t orinoco_interrupt(int irq, void *dev_id) { - struct net_device *dev = dev_id; - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = dev_id; + struct net_device *dev = priv->ndev; hermes_t *hw = &priv->hw; int count = MAX_IRQLOOPS_PER_IRQ; u16 evstat, events; @@ -2096,227 +1975,12 @@ static void orinoco_unregister_pm_notifier(struct orinoco_private *priv) /* Initialization */ /********************************************************************/ -struct comp_id { - u16 id, variant, major, minor; -} __attribute__ ((packed)); - -static inline fwtype_t determine_firmware_type(struct comp_id *nic_id) -{ - if (nic_id->id < 0x8000) - return FIRMWARE_TYPE_AGERE; - else if (nic_id->id == 0x8000 && nic_id->major == 0) - return FIRMWARE_TYPE_SYMBOL; - else - return FIRMWARE_TYPE_INTERSIL; -} - -/* Set priv->firmware type, determine firmware properties */ -static int determine_firmware(struct net_device *dev) +int orinoco_init(struct orinoco_private *priv) { - struct orinoco_private *priv = netdev_priv(dev); - hermes_t *hw = &priv->hw; - int err; - struct comp_id nic_id, sta_id; - unsigned int firmver; - char tmp[SYMBOL_MAX_VER_LEN+1] __attribute__((aligned(2))); - - /* Get the hardware version */ - err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_NICID, &nic_id); - if (err) { - printk(KERN_ERR "%s: Cannot read hardware identity: error %d\n", - dev->name, err); - return err; - } - - le16_to_cpus(&nic_id.id); - le16_to_cpus(&nic_id.variant); - le16_to_cpus(&nic_id.major); - le16_to_cpus(&nic_id.minor); - printk(KERN_DEBUG "%s: Hardware identity %04x:%04x:%04x:%04x\n", - dev->name, nic_id.id, nic_id.variant, - nic_id.major, nic_id.minor); - - priv->firmware_type = determine_firmware_type(&nic_id); - - /* Get the firmware version */ - err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id); - if (err) { - printk(KERN_ERR "%s: Cannot read station identity: error %d\n", - dev->name, err); - return err; - } - - le16_to_cpus(&sta_id.id); - le16_to_cpus(&sta_id.variant); - le16_to_cpus(&sta_id.major); - le16_to_cpus(&sta_id.minor); - printk(KERN_DEBUG "%s: Station identity %04x:%04x:%04x:%04x\n", - dev->name, sta_id.id, sta_id.variant, - sta_id.major, sta_id.minor); - - switch (sta_id.id) { - case 0x15: - printk(KERN_ERR "%s: Primary firmware is active\n", - dev->name); - return -ENODEV; - case 0x14b: - printk(KERN_ERR "%s: Tertiary firmware is active\n", - dev->name); - return -ENODEV; - case 0x1f: /* Intersil, Agere, Symbol Spectrum24 */ - case 0x21: /* Symbol Spectrum24 Trilogy */ - break; - default: - printk(KERN_NOTICE "%s: Unknown station ID, please report\n", - dev->name); - break; - } - - /* Default capabilities */ - priv->has_sensitivity = 1; - priv->has_mwo = 0; - priv->has_preamble = 0; - priv->has_port3 = 1; - priv->has_ibss = 1; - priv->has_wep = 0; - priv->has_big_wep = 0; - priv->has_alt_txcntl = 0; - priv->has_ext_scan = 0; - priv->has_wpa = 0; - priv->do_fw_download = 0; - - /* Determine capabilities from the firmware version */ - switch (priv->firmware_type) { - case FIRMWARE_TYPE_AGERE: - /* Lucent Wavelan IEEE, Lucent Orinoco, Cabletron RoamAbout, - ELSA, Melco, HP, IBM, Dell 1150, Compaq 110/210 */ - snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, - "Lucent/Agere %d.%02d", sta_id.major, sta_id.minor); - - firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor; - - priv->has_ibss = (firmver >= 0x60006); - priv->has_wep = (firmver >= 0x40020); - priv->has_big_wep = 1; /* FIXME: this is wrong - how do we tell - Gold cards from the others? */ - priv->has_mwo = (firmver >= 0x60000); - priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ - priv->ibss_port = 1; - priv->has_hostscan = (firmver >= 0x8000a); - priv->do_fw_download = 1; - priv->broken_monitor = (firmver >= 0x80000); - priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */ - priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */ - priv->has_wpa = (firmver >= 0x9002a); - /* Tested with Agere firmware : - * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II - * Tested CableTron firmware : 4.32 => Anton */ - break; - case FIRMWARE_TYPE_SYMBOL: - /* Symbol , 3Com AirConnect, Intel, Ericsson WLAN */ - /* Intel MAC : 00:02:B3:* */ - /* 3Com MAC : 00:50:DA:* */ - memset(tmp, 0, sizeof(tmp)); - /* Get the Symbol firmware version */ - err = hermes_read_ltv(hw, USER_BAP, - HERMES_RID_SECONDARYVERSION_SYMBOL, - SYMBOL_MAX_VER_LEN, NULL, &tmp); - if (err) { - printk(KERN_WARNING - "%s: Error %d reading Symbol firmware info. " - "Wildly guessing capabilities...\n", - dev->name, err); - firmver = 0; - tmp[0] = '\0'; - } else { - /* The firmware revision is a string, the format is - * something like : "V2.20-01". - * Quick and dirty parsing... - Jean II - */ - firmver = ((tmp[1] - '0') << 16) - | ((tmp[3] - '0') << 12) - | ((tmp[4] - '0') << 8) - | ((tmp[6] - '0') << 4) - | (tmp[7] - '0'); - - tmp[SYMBOL_MAX_VER_LEN] = '\0'; - } - - snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, - "Symbol %s", tmp); - - priv->has_ibss = (firmver >= 0x20000); - priv->has_wep = (firmver >= 0x15012); - priv->has_big_wep = (firmver >= 0x20000); - priv->has_pm = (firmver >= 0x20000 && firmver < 0x22000) || - (firmver >= 0x29000 && firmver < 0x30000) || - firmver >= 0x31000; - priv->has_preamble = (firmver >= 0x20000); - priv->ibss_port = 4; - - /* Symbol firmware is found on various cards, but - * there has been no attempt to check firmware - * download on non-spectrum_cs based cards. - * - * Given that the Agere firmware download works - * differently, we should avoid doing a firmware - * download with the Symbol algorithm on non-spectrum - * cards. - * - * For now we can identify a spectrum_cs based card - * because it has a firmware reset function. - */ - priv->do_fw_download = (priv->stop_fw != NULL); - - priv->broken_disableport = (firmver == 0x25013) || - (firmver >= 0x30000 && firmver <= 0x31000); - priv->has_hostscan = (firmver >= 0x31001) || - (firmver >= 0x29057 && firmver < 0x30000); - /* Tested with Intel firmware : 0x20015 => Jean II */ - /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */ - break; - case FIRMWARE_TYPE_INTERSIL: - /* D-Link, Linksys, Adtron, ZoomAir, and many others... - * Samsung, Compaq 100/200 and Proxim are slightly - * different and less well tested */ - /* D-Link MAC : 00:40:05:* */ - /* Addtron MAC : 00:90:D1:* */ - snprintf(priv->fw_name, sizeof(priv->fw_name) - 1, - "Intersil %d.%d.%d", sta_id.major, sta_id.minor, - sta_id.variant); - - firmver = ((unsigned long)sta_id.major << 16) | - ((unsigned long)sta_id.minor << 8) | sta_id.variant; - - priv->has_ibss = (firmver >= 0x000700); /* FIXME */ - priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); - priv->has_pm = (firmver >= 0x000700); - priv->has_hostscan = (firmver >= 0x010301); - - if (firmver >= 0x000800) - priv->ibss_port = 0; - else { - printk(KERN_NOTICE "%s: Intersil firmware earlier " - "than v0.8.x - several features not supported\n", - dev->name); - priv->ibss_port = 1; - } - break; - } - printk(KERN_DEBUG "%s: Firmware determined as %s\n", dev->name, - priv->fw_name); - - return 0; -} - -static int orinoco_init(struct net_device *dev) -{ - struct orinoco_private *priv = netdev_priv(dev); + struct device *dev = priv->dev; + struct wiphy *wiphy = priv_to_wiphy(priv); hermes_t *hw = &priv->hw; int err = 0; - struct hermes_idstring nickbuf; - u16 reclen; - int len; /* No need to lock, the hw_unavailable flag is already set in * alloc_orinocodev() */ @@ -2325,15 +1989,14 @@ static int orinoco_init(struct net_device *dev) /* Initialize the firmware */ err = hermes_init(hw); if (err != 0) { - printk(KERN_ERR "%s: failed to initialize firmware (err = %d)\n", - dev->name, err); + dev_err(dev, "Failed to initialize firmware (err = %d)\n", + err); goto out; } - err = determine_firmware(dev); + err = determine_fw_capabilities(priv); if (err != 0) { - printk(KERN_ERR "%s: Incompatible firmware, aborting\n", - dev->name); + dev_err(dev, "Incompatible firmware, aborting\n"); goto out; } @@ -2347,154 +2010,48 @@ static int orinoco_init(struct net_device *dev) priv->do_fw_download = 0; /* Check firmware version again */ - err = determine_firmware(dev); + err = determine_fw_capabilities(priv); if (err != 0) { - printk(KERN_ERR "%s: Incompatible firmware, aborting\n", - dev->name); + dev_err(dev, "Incompatible firmware, aborting\n"); goto out; } } if (priv->has_port3) - printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n", - dev->name); + dev_info(dev, "Ad-hoc demo mode supported\n"); if (priv->has_ibss) - printk(KERN_DEBUG "%s: IEEE standard IBSS ad-hoc mode supported\n", - dev->name); - if (priv->has_wep) { - printk(KERN_DEBUG "%s: WEP supported, %s-bit key\n", dev->name, - priv->has_big_wep ? "104" : "40"); - } + dev_info(dev, "IEEE standard IBSS ad-hoc mode supported\n"); + if (priv->has_wep) + dev_info(dev, "WEP supported, %s-bit key\n", + priv->has_big_wep ? "104" : "40"); if (priv->has_wpa) { - printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name); + dev_info(dev, "WPA-PSK supported\n"); if (orinoco_mic_init(priv)) { - printk(KERN_ERR "%s: Failed to setup MIC crypto " - "algorithm. Disabling WPA support\n", dev->name); + dev_err(dev, "Failed to setup MIC crypto algorithm. " + "Disabling WPA support\n"); priv->has_wpa = 0; } } - /* Now we have the firmware capabilities, allocate appropiate - * sized scan buffers */ - if (orinoco_bss_data_allocate(priv)) - goto out; - orinoco_bss_data_init(priv); - - /* Get the MAC address */ - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, - ETH_ALEN, NULL, dev->dev_addr); - if (err) { - printk(KERN_WARNING "%s: failed to read MAC address!\n", - dev->name); - goto out; - } - - printk(KERN_DEBUG "%s: MAC address %pM\n", - dev->name, dev->dev_addr); - - /* Get the station name */ - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, - sizeof(nickbuf), &reclen, &nickbuf); - if (err) { - printk(KERN_ERR "%s: failed to read station name\n", - dev->name); - goto out; - } - if (nickbuf.len) - len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len)); - else - len = min(IW_ESSID_MAX_SIZE, 2 * reclen); - memcpy(priv->nick, &nickbuf.val, len); - priv->nick[len] = '\0'; - - printk(KERN_DEBUG "%s: Station name \"%s\"\n", dev->name, priv->nick); - - err = orinoco_allocate_fid(dev); - if (err) { - printk(KERN_ERR "%s: failed to allocate NIC buffer!\n", - dev->name); - goto out; - } - - /* Get allowed channels */ - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST, - &priv->channel_mask); - if (err) { - printk(KERN_ERR "%s: failed to read channel list!\n", - dev->name); - goto out; - } - - /* Get initial AP density */ - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, - &priv->ap_density); - if (err || priv->ap_density < 1 || priv->ap_density > 3) - priv->has_sensitivity = 0; - - /* Get initial RTS threshold */ - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, - &priv->rts_thresh); - if (err) { - printk(KERN_ERR "%s: failed to read RTS threshold!\n", - dev->name); + err = orinoco_hw_read_card_settings(priv, wiphy->perm_addr); + if (err) goto out; - } - /* Get initial fragmentation settings */ - if (priv->has_mwo) - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFMWOROBUST_AGERE, - &priv->mwo_robust); - else - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, - &priv->frag_thresh); + err = orinoco_hw_allocate_fid(priv); if (err) { - printk(KERN_ERR "%s: failed to read fragmentation settings!\n", - dev->name); + dev_err(dev, "Failed to allocate NIC buffer!\n"); goto out; } - /* Power management setup */ - if (priv->has_pm) { - priv->pm_on = 0; - priv->pm_mcast = 1; - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFMAXSLEEPDURATION, - &priv->pm_period); - if (err) { - printk(KERN_ERR "%s: failed to read power management period!\n", - dev->name); - goto out; - } - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFPMHOLDOVERDURATION, - &priv->pm_timeout); - if (err) { - printk(KERN_ERR "%s: failed to read power management timeout!\n", - dev->name); - goto out; - } - } - - /* Preamble setup */ - if (priv->has_preamble) { - err = hermes_read_wordrec(hw, USER_BAP, - HERMES_RID_CNFPREAMBLE_SYMBOL, - &priv->preamble); - if (err) - goto out; - } - /* Set up the default configuration */ - priv->iw_mode = IW_MODE_INFRA; + priv->iw_mode = NL80211_IFTYPE_STATION; /* By default use IEEE/IBSS ad-hoc mode if we have it */ priv->prefer_port3 = priv->has_port3 && (!priv->has_ibss); set_port_type(priv); priv->channel = 0; /* use firmware default */ priv->promiscuous = 0; - priv->encode_alg = IW_ENCODE_ALG_NONE; + priv->encode_alg = ORINOCO_ALG_NONE; priv->tx_key = 0; priv->wpa_enabled = 0; priv->tkip_cm_active = 0; @@ -2502,20 +2059,25 @@ static int orinoco_init(struct net_device *dev) priv->wpa_ie_len = 0; priv->wpa_ie = NULL; + if (orinoco_wiphy_register(wiphy)) { + err = -ENODEV; + goto out; + } + /* Make the hardware available, as long as it hasn't been * removed elsewhere (e.g. by PCMCIA hot unplug) */ spin_lock_irq(&priv->lock); priv->hw_unavailable--; spin_unlock_irq(&priv->lock); - printk(KERN_DEBUG "%s: ready\n", dev->name); + dev_dbg(dev, "Ready\n"); out: return err; } +EXPORT_SYMBOL(orinoco_init); static const struct net_device_ops orinoco_netdev_ops = { - .ndo_init = orinoco_init, .ndo_open = orinoco_open, .ndo_stop = orinoco_stop, .ndo_start_xmit = orinoco_xmit, @@ -2527,40 +2089,64 @@ static const struct net_device_ops orinoco_netdev_ops = { .ndo_get_stats = orinoco_get_stats, }; -struct net_device +/* Allocate private data. + * + * This driver has a number of structures associated with it + * netdev - Net device structure for each network interface + * wiphy - structure associated with wireless phy + * wireless_dev (wdev) - structure for each wireless interface + * hw - structure for hermes chip info + * card - card specific structure for use by the card driver + * (airport, orinoco_cs) + * priv - orinoco private data + * device - generic linux device structure + * + * +---------+ +---------+ + * | wiphy | | netdev | + * | +-------+ | +-------+ + * | | priv | | | wdev | + * | | +-----+ +-+-------+ + * | | | hw | + * | +-+-----+ + * | | card | + * +-+-------+ + * + * priv has a link to netdev and device + * wdev has a link to wiphy + */ +struct orinoco_private *alloc_orinocodev(int sizeof_card, struct device *device, int (*hard_reset)(struct orinoco_private *), int (*stop_fw)(struct orinoco_private *, int)) { - struct net_device *dev; struct orinoco_private *priv; + struct wiphy *wiphy; - dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card); - if (!dev) + /* allocate wiphy + * NOTE: We only support a single virtual interface + * but this may change when monitor mode is added + */ + wiphy = wiphy_new(&orinoco_cfg_ops, + sizeof(struct orinoco_private) + sizeof_card); + if (!wiphy) return NULL; - priv = netdev_priv(dev); - priv->ndev = dev; + + priv = wiphy_priv(wiphy); + priv->dev = device; + if (sizeof_card) priv->card = (void *)((unsigned long)priv + sizeof(struct orinoco_private)); else priv->card = NULL; - priv->dev = device; - /* Setup / override net_device fields */ - dev->netdev_ops = &orinoco_netdev_ops; - dev->watchdog_timeo = HZ; /* 1 second timeout */ - dev->ethtool_ops = &orinoco_ethtool_ops; - dev->wireless_handlers = &orinoco_handler_def; + orinoco_wiphy_init(wiphy); + #ifdef WIRELESS_SPY priv->wireless_data.spy_data = &priv->spy_data; - dev->wireless_data = &priv->wireless_data; #endif - /* Reserve space in skb for the SNAP header */ - dev->hard_header_len += ENCAPS_OVERHEAD; - /* Set up default callbacks */ priv->hard_reset = hard_reset; priv->stop_fw = stop_fw; @@ -2576,9 +2162,12 @@ struct net_device INIT_LIST_HEAD(&priv->rx_list); tasklet_init(&priv->rx_tasklet, orinoco_rx_isr_tasklet, - (unsigned long) dev); + (unsigned long) priv); + + spin_lock_init(&priv->scan_lock); + INIT_LIST_HEAD(&priv->scan_list); + INIT_WORK(&priv->process_scan, orinoco_process_scan_results); - netif_carrier_off(dev); priv->last_linkstatus = 0xffff; #if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP) @@ -2589,14 +2178,91 @@ struct net_device /* Register PM notifiers */ orinoco_register_pm_notifier(priv); - return dev; + return priv; } EXPORT_SYMBOL(alloc_orinocodev); -void free_orinocodev(struct net_device *dev) +/* We can only support a single interface. We provide a separate + * function to set it up to distinguish between hardware + * initialisation and interface setup. + * + * The base_addr and irq parameters are passed on to netdev for use + * with SIOCGIFMAP. + */ +int orinoco_if_add(struct orinoco_private *priv, + unsigned long base_addr, + unsigned int irq) +{ + struct wiphy *wiphy = priv_to_wiphy(priv); + struct wireless_dev *wdev; + struct net_device *dev; + int ret; + + dev = alloc_etherdev(sizeof(struct wireless_dev)); + + if (!dev) + return -ENOMEM; + + /* Initialise wireless_dev */ + wdev = netdev_priv(dev); + wdev->wiphy = wiphy; + wdev->iftype = NL80211_IFTYPE_STATION; + + /* Setup / override net_device fields */ + dev->ieee80211_ptr = wdev; + dev->netdev_ops = &orinoco_netdev_ops; + dev->watchdog_timeo = HZ; /* 1 second timeout */ + dev->ethtool_ops = &orinoco_ethtool_ops; + dev->wireless_handlers = &orinoco_handler_def; +#ifdef WIRELESS_SPY + dev->wireless_data = &priv->wireless_data; +#endif + /* we use the default eth_mac_addr for setting the MAC addr */ + + /* Reserve space in skb for the SNAP header */ + dev->hard_header_len += ENCAPS_OVERHEAD; + + netif_carrier_off(dev); + + memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); + + dev->base_addr = base_addr; + dev->irq = irq; + + SET_NETDEV_DEV(dev, priv->dev); + ret = register_netdev(dev); + if (ret) + goto fail; + + priv->ndev = dev; + + /* Report what we've done */ + dev_dbg(priv->dev, "Registerred interface %s.\n", dev->name); + + return 0; + + fail: + free_netdev(dev); + return ret; +} +EXPORT_SYMBOL(orinoco_if_add); + +void orinoco_if_del(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + + unregister_netdev(dev); + free_netdev(dev); +} +EXPORT_SYMBOL(orinoco_if_del); + +void free_orinocodev(struct orinoco_private *priv) { - struct orinoco_private *priv = netdev_priv(dev); + struct wiphy *wiphy = priv_to_wiphy(priv); struct orinoco_rx_data *rx_data, *temp; + struct orinoco_scan_data *sd, *sdtemp; + + wiphy_unregister(wiphy); /* If the tasklet is scheduled when we call tasklet_kill it * will run one final time. However the tasklet will only @@ -2612,21 +2278,80 @@ void free_orinocodev(struct net_device *dev) kfree(rx_data); } + cancel_work_sync(&priv->process_scan); + /* Explicitly drain priv->scan_list */ + list_for_each_entry_safe(sd, sdtemp, &priv->scan_list, list) { + list_del(&sd->list); + + if ((sd->len > 0) && sd->buf) + kfree(sd->buf); + kfree(sd); + } + orinoco_unregister_pm_notifier(priv); orinoco_uncache_fw(priv); priv->wpa_ie_len = 0; kfree(priv->wpa_ie); orinoco_mic_free(priv); - orinoco_bss_data_free(priv); - free_netdev(dev); + wiphy_free(wiphy); } EXPORT_SYMBOL(free_orinocodev); +int orinoco_up(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + unsigned long flags; + int err; + + spin_lock_irqsave(&priv->lock, flags); + + err = orinoco_reinit_firmware(priv); + if (err) { + printk(KERN_ERR "%s: Error %d re-initializing firmware\n", + dev->name, err); + goto exit; + } + + netif_device_attach(dev); + priv->hw_unavailable--; + + if (priv->open && !priv->hw_unavailable) { + err = __orinoco_up(priv); + if (err) + printk(KERN_ERR "%s: Error %d restarting card\n", + dev->name, err); + } + +exit: + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} +EXPORT_SYMBOL(orinoco_up); + +void orinoco_down(struct orinoco_private *priv) +{ + struct net_device *dev = priv->ndev; + unsigned long flags; + int err; + + spin_lock_irqsave(&priv->lock, flags); + err = __orinoco_down(priv); + if (err) + printk(KERN_WARNING "%s: Error %d downing interface\n", + dev->name, err); + + netif_device_detach(dev); + priv->hw_unavailable++; + spin_unlock_irqrestore(&priv->lock, flags); +} +EXPORT_SYMBOL(orinoco_down); + static void orinoco_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1); strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1); diff --git a/drivers/net/wireless/orinoco/main.h b/drivers/net/wireless/orinoco/main.h index af2bae4fe395..21ab36cd76c7 100644 --- a/drivers/net/wireless/orinoco/main.h +++ b/drivers/net/wireless/orinoco/main.h @@ -29,10 +29,9 @@ struct net_device; struct work_struct; void set_port_type(struct orinoco_private *priv); -int __orinoco_program_rids(struct net_device *dev); +int orinoco_commit(struct orinoco_private *priv); void orinoco_reset(struct work_struct *work); - /* Information element helpers - find a home for these... */ static inline u8 *orinoco_get_ie(u8 *data, size_t len, enum ieee80211_eid eid) diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h index 8e5a72cc297f..9ac6f1dda4b0 100644 --- a/drivers/net/wireless/orinoco/orinoco.h +++ b/drivers/net/wireless/orinoco/orinoco.h @@ -14,6 +14,7 @@ #include <linux/netdevice.h> #include <linux/wireless.h> #include <net/iw_handler.h> +#include <net/cfg80211.h> #include "hermes.h" @@ -24,6 +25,7 @@ #define MAX_SCAN_LEN 4096 +#define ORINOCO_SEQ_LEN 8 #define ORINOCO_MAX_KEY_SIZE 14 #define ORINOCO_MAX_KEYS 4 @@ -41,24 +43,18 @@ struct orinoco_tkip_key { u8 rx_mic[MIC_KEYLEN]; }; +enum orinoco_alg { + ORINOCO_ALG_NONE, + ORINOCO_ALG_WEP, + ORINOCO_ALG_TKIP +}; + typedef enum { FIRMWARE_TYPE_AGERE, FIRMWARE_TYPE_INTERSIL, FIRMWARE_TYPE_SYMBOL } fwtype_t; -struct bss_element { - union hermes_scan_info bss; - unsigned long last_scanned; - struct list_head list; -}; - -struct xbss_element { - struct agere_ext_scan_info bss; - unsigned long last_scanned; - struct list_head list; -}; - struct firmware; struct orinoco_private { @@ -67,6 +63,10 @@ struct orinoco_private { int (*hard_reset)(struct orinoco_private *); int (*stop_fw)(struct orinoco_private *, int); + struct ieee80211_supported_band band; + struct ieee80211_channel channels[14]; + u32 cipher_suites[3]; + /* Synchronisation stuff */ spinlock_t lock; int hw_unavailable; @@ -114,12 +114,14 @@ struct orinoco_private { unsigned int do_fw_download:1; unsigned int broken_disableport:1; unsigned int broken_monitor:1; + unsigned int prefer_port3:1; /* Configuration paramaters */ - u32 iw_mode; - int prefer_port3; - u16 encode_alg, wep_restrict, tx_key; - struct orinoco_key keys[ORINOCO_MAX_KEYS]; + enum nl80211_iftype iw_mode; + enum orinoco_alg encode_alg; + u16 wep_restrict, tx_key; + struct key_params keys[ORINOCO_MAX_KEYS]; + int bitratemode; char nick[IW_ESSID_MAX_SIZE+1]; char desired_essid[IW_ESSID_MAX_SIZE+1]; @@ -140,18 +142,15 @@ struct orinoco_private { int promiscuous, mc_count; /* Scanning support */ - struct list_head bss_list; - struct list_head bss_free_list; - void *bss_xbss_data; - - int scan_inprogress; /* Scan pending... */ - u32 scan_mode; /* Type of scan done */ + struct cfg80211_scan_request *scan_request; + struct work_struct process_scan; + struct list_head scan_list; + spinlock_t scan_lock; /* protects the scan list */ /* WPA support */ u8 *wpa_ie; int wpa_ie_len; - struct orinoco_tkip_key tkip_key[ORINOCO_MAX_KEYS]; struct crypto_hash *rx_tfm_mic; struct crypto_hash *tx_tfm_mic; @@ -182,14 +181,18 @@ extern int orinoco_debug; /* Exported prototypes */ /********************************************************************/ -extern struct net_device *alloc_orinocodev( +extern struct orinoco_private *alloc_orinocodev( int sizeof_card, struct device *device, int (*hard_reset)(struct orinoco_private *), int (*stop_fw)(struct orinoco_private *, int)); -extern void free_orinocodev(struct net_device *dev); -extern int __orinoco_up(struct net_device *dev); -extern int __orinoco_down(struct net_device *dev); -extern int orinoco_reinit_firmware(struct net_device *dev); +extern void free_orinocodev(struct orinoco_private *priv); +extern int orinoco_init(struct orinoco_private *priv); +extern int orinoco_if_add(struct orinoco_private *priv, + unsigned long base_addr, + unsigned int irq); +extern void orinoco_if_del(struct orinoco_private *priv); +extern int orinoco_up(struct orinoco_private *priv); +extern void orinoco_down(struct orinoco_private *priv); extern irqreturn_t orinoco_interrupt(int irq, void *dev_id); /********************************************************************/ @@ -215,4 +218,10 @@ static inline void orinoco_unlock(struct orinoco_private *priv, spin_unlock_irqrestore(&priv->lock, *flags); } +/*** Navigate from net_device to orinoco_private ***/ +static inline struct orinoco_private *ndev_priv(struct net_device *dev) +{ + struct wireless_dev *wdev = netdev_priv(dev); + return wdev_priv(wdev); +} #endif /* _ORINOCO_H */ diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c index b381aed24d73..38c1c9d2abb8 100644 --- a/drivers/net/wireless/orinoco/orinoco_cs.c +++ b/drivers/net/wireless/orinoco/orinoco_cs.c @@ -106,26 +106,24 @@ orinoco_cs_hard_reset(struct orinoco_private *priv) static int orinoco_cs_probe(struct pcmcia_device *link) { - struct net_device *dev; struct orinoco_private *priv; struct orinoco_pccard *card; - dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link), - orinoco_cs_hard_reset, NULL); - if (!dev) + priv = alloc_orinocodev(sizeof(*card), &handle_to_dev(link), + orinoco_cs_hard_reset, NULL); + if (!priv) return -ENOMEM; - priv = netdev_priv(dev); card = priv->card; /* Link both structures together */ card->p_dev = link; - link->priv = dev; + link->priv = priv; /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = orinoco_interrupt; - link->irq.Instance = dev; + link->irq.Instance = priv; /* General socket configuration defaults can go here. In this * client, we assume very little, and rely on the CIS for @@ -146,14 +144,14 @@ orinoco_cs_probe(struct pcmcia_device *link) */ static void orinoco_cs_detach(struct pcmcia_device *link) { - struct net_device *dev = link->priv; + struct orinoco_private *priv = link->priv; if (link->dev_node) - unregister_netdev(dev); + orinoco_if_del(priv); orinoco_cs_release(link); - free_orinocodev(dev); + free_orinocodev(priv); } /* orinoco_cs_detach */ /* @@ -239,8 +237,7 @@ next_entry: static int orinoco_cs_config(struct pcmcia_device *link) { - struct net_device *dev = link->priv; - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = link->priv; struct orinoco_pccard *card = priv->card; hermes_t *hw = &priv->hw; int last_fn, last_ret; @@ -295,29 +292,27 @@ orinoco_cs_config(struct pcmcia_device *link) pcmcia_request_configuration(link, &link->conf)); /* Ok, we have the configuration, prepare to register the netdev */ - dev->base_addr = link->io.BasePort1; - dev->irq = link->irq.AssignedIRQ; card->node.major = card->node.minor = 0; - SET_NETDEV_DEV(dev, &handle_to_dev(link)); - /* Tell the stack we exist */ - if (register_netdev(dev) != 0) { - printk(KERN_ERR PFX "register_netdev() failed\n"); + /* Initialise the main driver */ + if (orinoco_init(priv) != 0) { + printk(KERN_ERR PFX "orinoco_init() failed\n"); + goto failed; + } + + /* Register an interface with the stack */ + if (orinoco_if_add(priv, link->io.BasePort1, + link->irq.AssignedIRQ) != 0) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto failed; } /* At this point, the dev_node_t structure(s) needs to be * initialized and arranged in a linked list at link->dev_node. */ - strcpy(card->node.dev_name, dev->name); + strcpy(card->node.dev_name, priv->ndev->name); link->dev_node = &card->node; /* link->dev_node being non-NULL is also * used to indicate that the * net_device has been registered */ - - /* Finally, report what we've done */ - printk(KERN_DEBUG "%s: " DRIVER_NAME " at %s, irq %d, io " - "0x%04x-0x%04x\n", dev->name, dev_name(dev->dev.parent), - link->irq.AssignedIRQ, link->io.BasePort1, - link->io.BasePort1 + link->io.NumPorts1 - 1); return 0; cs_failed: @@ -336,8 +331,7 @@ orinoco_cs_config(struct pcmcia_device *link) static void orinoco_cs_release(struct pcmcia_device *link) { - struct net_device *dev = link->priv; - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = link->priv; unsigned long flags; /* We're committed to taking the device away now, so mark the @@ -353,62 +347,26 @@ orinoco_cs_release(struct pcmcia_device *link) static int orinoco_cs_suspend(struct pcmcia_device *link) { - struct net_device *dev = link->priv; - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = link->priv; struct orinoco_pccard *card = priv->card; - int err = 0; - unsigned long flags; /* This is probably racy, but I can't think of a better way, short of rewriting the PCMCIA layer to not suck :-( */ - if (!test_bit(0, &card->hard_reset_in_progress)) { - spin_lock_irqsave(&priv->lock, flags); - - err = __orinoco_down(dev); - if (err) - printk(KERN_WARNING "%s: Error %d downing interface\n", - dev->name, err); - - netif_device_detach(dev); - priv->hw_unavailable++; - - spin_unlock_irqrestore(&priv->lock, flags); - } + if (!test_bit(0, &card->hard_reset_in_progress)) + orinoco_down(priv); return 0; } static int orinoco_cs_resume(struct pcmcia_device *link) { - struct net_device *dev = link->priv; - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = link->priv; struct orinoco_pccard *card = priv->card; int err = 0; - unsigned long flags; - - if (!test_bit(0, &card->hard_reset_in_progress)) { - err = orinoco_reinit_firmware(dev); - if (err) { - printk(KERN_ERR "%s: Error %d re-initializing firmware\n", - dev->name, err); - return -EIO; - } - - spin_lock_irqsave(&priv->lock, flags); - netif_device_attach(dev); - priv->hw_unavailable--; - - if (priv->open && !priv->hw_unavailable) { - err = __orinoco_up(dev); - if (err) - printk(KERN_ERR "%s: Error %d restarting card\n", - dev->name, err); - } - - spin_unlock_irqrestore(&priv->lock, flags); - } + if (!test_bit(0, &card->hard_reset_in_progress)) + err = orinoco_up(priv); return err; } diff --git a/drivers/net/wireless/orinoco/orinoco_nortel.c b/drivers/net/wireless/orinoco/orinoco_nortel.c index b01726255c6f..c13a4c383410 100644 --- a/drivers/net/wireless/orinoco/orinoco_nortel.c +++ b/drivers/net/wireless/orinoco/orinoco_nortel.c @@ -144,7 +144,6 @@ static int orinoco_nortel_init_one(struct pci_dev *pdev, int err; struct orinoco_private *priv; struct orinoco_pci_card *card; - struct net_device *dev; void __iomem *hermes_io, *bridge_io, *attr_io; err = pci_enable_device(pdev); @@ -181,24 +180,22 @@ static int orinoco_nortel_init_one(struct pci_dev *pdev, } /* Allocate network device */ - dev = alloc_orinocodev(sizeof(*card), &pdev->dev, - orinoco_nortel_cor_reset, NULL); - if (!dev) { + priv = alloc_orinocodev(sizeof(*card), &pdev->dev, + orinoco_nortel_cor_reset, NULL); + if (!priv) { printk(KERN_ERR PFX "Cannot allocate network device\n"); err = -ENOMEM; goto fail_alloc; } - priv = netdev_priv(dev); card = priv->card; card->bridge_io = bridge_io; card->attr_io = attr_io; - SET_NETDEV_DEV(dev, &pdev->dev); hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING); err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, - dev->name, dev); + DRIVER_NAME, priv); if (err) { printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); err = -EBUSY; @@ -217,24 +214,28 @@ static int orinoco_nortel_init_one(struct pci_dev *pdev, goto fail; } - err = register_netdev(dev); + err = orinoco_init(priv); if (err) { - printk(KERN_ERR PFX "Cannot register network device\n"); + printk(KERN_ERR PFX "orinoco_init() failed\n"); goto fail; } - pci_set_drvdata(pdev, dev); - printk(KERN_DEBUG "%s: " DRIVER_NAME " at %s\n", dev->name, - pci_name(pdev)); + err = orinoco_if_add(priv, 0, 0); + if (err) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); + goto fail; + } + + pci_set_drvdata(pdev, priv); return 0; fail: - free_irq(pdev->irq, dev); + free_irq(pdev->irq, priv); fail_irq: pci_set_drvdata(pdev, NULL); - free_orinocodev(dev); + free_orinocodev(priv); fail_alloc: pci_iounmap(pdev, hermes_io); @@ -256,17 +257,16 @@ static int orinoco_nortel_init_one(struct pci_dev *pdev, static void __devexit orinoco_nortel_remove_one(struct pci_dev *pdev) { - struct net_device *dev = pci_get_drvdata(pdev); - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = pci_get_drvdata(pdev); struct orinoco_pci_card *card = priv->card; /* Clear LEDs */ iowrite16(0, card->bridge_io + 10); - unregister_netdev(dev); - free_irq(pdev->irq, dev); + orinoco_if_del(priv); + free_irq(pdev->irq, priv); pci_set_drvdata(pdev, NULL); - free_orinocodev(dev); + free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_iounmap(pdev, card->attr_io); pci_iounmap(pdev, card->bridge_io); diff --git a/drivers/net/wireless/orinoco/orinoco_pci.c b/drivers/net/wireless/orinoco/orinoco_pci.c index 78cafff1fb2e..fea7781948e7 100644 --- a/drivers/net/wireless/orinoco/orinoco_pci.c +++ b/drivers/net/wireless/orinoco/orinoco_pci.c @@ -116,7 +116,6 @@ static int orinoco_pci_init_one(struct pci_dev *pdev, int err; struct orinoco_private *priv; struct orinoco_pci_card *card; - struct net_device *dev; void __iomem *hermes_io; err = pci_enable_device(pdev); @@ -139,22 +138,20 @@ static int orinoco_pci_init_one(struct pci_dev *pdev, } /* Allocate network device */ - dev = alloc_orinocodev(sizeof(*card), &pdev->dev, - orinoco_pci_cor_reset, NULL); - if (!dev) { + priv = alloc_orinocodev(sizeof(*card), &pdev->dev, + orinoco_pci_cor_reset, NULL); + if (!priv) { printk(KERN_ERR PFX "Cannot allocate network device\n"); err = -ENOMEM; goto fail_alloc; } - priv = netdev_priv(dev); card = priv->card; - SET_NETDEV_DEV(dev, &pdev->dev); hermes_struct_init(&priv->hw, hermes_io, HERMES_32BIT_REGSPACING); err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, - dev->name, dev); + DRIVER_NAME, priv); if (err) { printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); err = -EBUSY; @@ -167,24 +164,28 @@ static int orinoco_pci_init_one(struct pci_dev *pdev, goto fail; } - err = register_netdev(dev); + err = orinoco_init(priv); if (err) { - printk(KERN_ERR PFX "Cannot register network device\n"); + printk(KERN_ERR PFX "orinoco_init() failed\n"); goto fail; } - pci_set_drvdata(pdev, dev); - printk(KERN_DEBUG "%s: " DRIVER_NAME " at %s\n", dev->name, - pci_name(pdev)); + err = orinoco_if_add(priv, 0, 0); + if (err) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); + goto fail; + } + + pci_set_drvdata(pdev, priv); return 0; fail: - free_irq(pdev->irq, dev); + free_irq(pdev->irq, priv); fail_irq: pci_set_drvdata(pdev, NULL); - free_orinocodev(dev); + free_orinocodev(priv); fail_alloc: pci_iounmap(pdev, hermes_io); @@ -200,13 +201,12 @@ static int orinoco_pci_init_one(struct pci_dev *pdev, static void __devexit orinoco_pci_remove_one(struct pci_dev *pdev) { - struct net_device *dev = pci_get_drvdata(pdev); - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = pci_get_drvdata(pdev); - unregister_netdev(dev); - free_irq(pdev->irq, dev); + orinoco_if_del(priv); + free_irq(pdev->irq, priv); pci_set_drvdata(pdev, NULL); - free_orinocodev(dev); + free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/net/wireless/orinoco/orinoco_pci.h b/drivers/net/wireless/orinoco/orinoco_pci.h index c655b4a3de16..ea7231af40a8 100644 --- a/drivers/net/wireless/orinoco/orinoco_pci.h +++ b/drivers/net/wireless/orinoco/orinoco_pci.h @@ -21,30 +21,10 @@ struct orinoco_pci_card { #ifdef CONFIG_PM static int orinoco_pci_suspend(struct pci_dev *pdev, pm_message_t state) { - struct net_device *dev = pci_get_drvdata(pdev); - struct orinoco_private *priv = netdev_priv(dev); - unsigned long flags; - int err; - - err = orinoco_lock(priv, &flags); - if (err) { - printk(KERN_ERR "%s: cannot lock hardware for suspend\n", - dev->name); - return err; - } - - err = __orinoco_down(dev); - if (err) - printk(KERN_WARNING "%s: error %d bringing interface down " - "for suspend\n", dev->name, err); - - netif_device_detach(dev); - - priv->hw_unavailable++; - - orinoco_unlock(priv, &flags); + struct orinoco_private *priv = pci_get_drvdata(pdev); - free_irq(pdev->irq, dev); + orinoco_down(priv); + free_irq(pdev->irq, priv); pci_save_state(pdev); pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); @@ -54,9 +34,8 @@ static int orinoco_pci_suspend(struct pci_dev *pdev, pm_message_t state) static int orinoco_pci_resume(struct pci_dev *pdev) { - struct net_device *dev = pci_get_drvdata(pdev); - struct orinoco_private *priv = netdev_priv(dev); - unsigned long flags; + struct orinoco_private *priv = pci_get_drvdata(pdev); + struct net_device *dev = priv->ndev; int err; pci_set_power_state(pdev, 0); @@ -69,7 +48,7 @@ static int orinoco_pci_resume(struct pci_dev *pdev) pci_restore_state(pdev); err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, - dev->name, dev); + dev->name, priv); if (err) { printk(KERN_ERR "%s: cannot re-allocate IRQ on resume\n", dev->name); @@ -77,29 +56,9 @@ static int orinoco_pci_resume(struct pci_dev *pdev) return -EBUSY; } - err = orinoco_reinit_firmware(dev); - if (err) { - printk(KERN_ERR "%s: error %d re-initializing firmware " - "on resume\n", dev->name, err); - return err; - } - - spin_lock_irqsave(&priv->lock, flags); - - netif_device_attach(dev); + err = orinoco_up(priv); - priv->hw_unavailable--; - - if (priv->open && (!priv->hw_unavailable)) { - err = __orinoco_up(dev); - if (err) - printk(KERN_ERR "%s: Error %d restarting card on resume\n", - dev->name, err); - } - - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; + return err; } #else #define orinoco_pci_suspend NULL diff --git a/drivers/net/wireless/orinoco/orinoco_plx.c b/drivers/net/wireless/orinoco/orinoco_plx.c index a2a4471c0337..3f2942a1e4f5 100644 --- a/drivers/net/wireless/orinoco/orinoco_plx.c +++ b/drivers/net/wireless/orinoco/orinoco_plx.c @@ -183,7 +183,6 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, int err; struct orinoco_private *priv; struct orinoco_pci_card *card; - struct net_device *dev; void __iomem *hermes_io, *attr_io, *bridge_io; err = pci_enable_device(pdev); @@ -220,24 +219,22 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, } /* Allocate network device */ - dev = alloc_orinocodev(sizeof(*card), &pdev->dev, - orinoco_plx_cor_reset, NULL); - if (!dev) { + priv = alloc_orinocodev(sizeof(*card), &pdev->dev, + orinoco_plx_cor_reset, NULL); + if (!priv) { printk(KERN_ERR PFX "Cannot allocate network device\n"); err = -ENOMEM; goto fail_alloc; } - priv = netdev_priv(dev); card = priv->card; card->bridge_io = bridge_io; card->attr_io = attr_io; - SET_NETDEV_DEV(dev, &pdev->dev); hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING); err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, - dev->name, dev); + DRIVER_NAME, priv); if (err) { printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); err = -EBUSY; @@ -256,24 +253,28 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, goto fail; } - err = register_netdev(dev); + err = orinoco_init(priv); if (err) { - printk(KERN_ERR PFX "Cannot register network device\n"); + printk(KERN_ERR PFX "orinoco_init() failed\n"); goto fail; } - pci_set_drvdata(pdev, dev); - printk(KERN_DEBUG "%s: " DRIVER_NAME " at %s\n", dev->name, - pci_name(pdev)); + err = orinoco_if_add(priv, 0, 0); + if (err) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); + goto fail; + } + + pci_set_drvdata(pdev, priv); return 0; fail: - free_irq(pdev->irq, dev); + free_irq(pdev->irq, priv); fail_irq: pci_set_drvdata(pdev, NULL); - free_orinocodev(dev); + free_orinocodev(priv); fail_alloc: pci_iounmap(pdev, hermes_io); @@ -295,14 +296,13 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, static void __devexit orinoco_plx_remove_one(struct pci_dev *pdev) { - struct net_device *dev = pci_get_drvdata(pdev); - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = pci_get_drvdata(pdev); struct orinoco_pci_card *card = priv->card; - unregister_netdev(dev); - free_irq(pdev->irq, dev); + orinoco_if_del(priv); + free_irq(pdev->irq, priv); pci_set_drvdata(pdev, NULL); - free_orinocodev(dev); + free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_iounmap(pdev, card->attr_io); pci_iounmap(pdev, card->bridge_io); diff --git a/drivers/net/wireless/orinoco/orinoco_tmd.c b/drivers/net/wireless/orinoco/orinoco_tmd.c index cda0e6e4d7a1..d3452548cc71 100644 --- a/drivers/net/wireless/orinoco/orinoco_tmd.c +++ b/drivers/net/wireless/orinoco/orinoco_tmd.c @@ -94,7 +94,6 @@ static int orinoco_tmd_init_one(struct pci_dev *pdev, int err; struct orinoco_private *priv; struct orinoco_pci_card *card; - struct net_device *dev; void __iomem *hermes_io, *bridge_io; err = pci_enable_device(pdev); @@ -124,23 +123,21 @@ static int orinoco_tmd_init_one(struct pci_dev *pdev, } /* Allocate network device */ - dev = alloc_orinocodev(sizeof(*card), &pdev->dev, - orinoco_tmd_cor_reset, NULL); - if (!dev) { + priv = alloc_orinocodev(sizeof(*card), &pdev->dev, + orinoco_tmd_cor_reset, NULL); + if (!priv) { printk(KERN_ERR PFX "Cannot allocate network device\n"); err = -ENOMEM; goto fail_alloc; } - priv = netdev_priv(dev); card = priv->card; card->bridge_io = bridge_io; - SET_NETDEV_DEV(dev, &pdev->dev); hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING); err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED, - dev->name, dev); + DRIVER_NAME, priv); if (err) { printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq); err = -EBUSY; @@ -153,24 +150,28 @@ static int orinoco_tmd_init_one(struct pci_dev *pdev, goto fail; } - err = register_netdev(dev); + err = orinoco_init(priv); if (err) { - printk(KERN_ERR PFX "Cannot register network device\n"); + printk(KERN_ERR PFX "orinoco_init() failed\n"); goto fail; } - pci_set_drvdata(pdev, dev); - printk(KERN_DEBUG "%s: " DRIVER_NAME " at %s\n", dev->name, - pci_name(pdev)); + err = orinoco_if_add(priv, 0, 0); + if (err) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); + goto fail; + } + + pci_set_drvdata(pdev, priv); return 0; fail: - free_irq(pdev->irq, dev); + free_irq(pdev->irq, priv); fail_irq: pci_set_drvdata(pdev, NULL); - free_orinocodev(dev); + free_orinocodev(priv); fail_alloc: pci_iounmap(pdev, hermes_io); @@ -189,14 +190,13 @@ static int orinoco_tmd_init_one(struct pci_dev *pdev, static void __devexit orinoco_tmd_remove_one(struct pci_dev *pdev) { - struct net_device *dev = pci_get_drvdata(pdev); - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = pci_get_drvdata(pdev); struct orinoco_pci_card *card = priv->card; - unregister_netdev(dev); - free_irq(pdev->irq, dev); + orinoco_if_del(priv); + free_irq(pdev->irq, priv); pci_set_drvdata(pdev, NULL); - free_orinocodev(dev); + free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_iounmap(pdev, card->bridge_io); pci_release_regions(pdev); diff --git a/drivers/net/wireless/orinoco/scan.c b/drivers/net/wireless/orinoco/scan.c index 89d699d4dfe6..d2f10e9c2162 100644 --- a/drivers/net/wireless/orinoco/scan.c +++ b/drivers/net/wireless/orinoco/scan.c @@ -5,147 +5,166 @@ #include <linux/kernel.h> #include <linux/string.h> -#include <linux/etherdevice.h> +#include <linux/ieee80211.h> +#include <net/cfg80211.h> #include "hermes.h" #include "orinoco.h" +#include "main.h" #include "scan.h" -#define ORINOCO_MAX_BSS_COUNT 64 +#define ZERO_DBM_OFFSET 0x95 +#define MAX_SIGNAL_LEVEL 0x8A +#define MIN_SIGNAL_LEVEL 0x2F -#define PRIV_BSS ((struct bss_element *)priv->bss_xbss_data) -#define PRIV_XBSS ((struct xbss_element *)priv->bss_xbss_data) +#define SIGNAL_TO_DBM(x) \ + (clamp_t(s32, (x), MIN_SIGNAL_LEVEL, MAX_SIGNAL_LEVEL) \ + - ZERO_DBM_OFFSET) +#define SIGNAL_TO_MBM(x) (SIGNAL_TO_DBM(x) * 100) -int orinoco_bss_data_allocate(struct orinoco_private *priv) +static int symbol_build_supp_rates(u8 *buf, const __le16 *rates) { - if (priv->bss_xbss_data) - return 0; - - if (priv->has_ext_scan) - priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT * - sizeof(struct xbss_element), - GFP_KERNEL); - else - priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT * - sizeof(struct bss_element), - GFP_KERNEL); - - if (!priv->bss_xbss_data) { - printk(KERN_WARNING "Out of memory allocating beacons"); - return -ENOMEM; + int i; + u8 rate; + + buf[0] = WLAN_EID_SUPP_RATES; + for (i = 0; i < 5; i++) { + rate = le16_to_cpu(rates[i]); + /* NULL terminated */ + if (rate == 0x0) + break; + buf[i + 2] = rate; } - return 0; -} + buf[1] = i; -void orinoco_bss_data_free(struct orinoco_private *priv) -{ - kfree(priv->bss_xbss_data); - priv->bss_xbss_data = NULL; + return i + 2; } -void orinoco_bss_data_init(struct orinoco_private *priv) +static int prism_build_supp_rates(u8 *buf, const u8 *rates) { int i; - INIT_LIST_HEAD(&priv->bss_free_list); - INIT_LIST_HEAD(&priv->bss_list); - if (priv->has_ext_scan) - for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++) - list_add_tail(&(PRIV_XBSS[i].list), - &priv->bss_free_list); - else - for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++) - list_add_tail(&(PRIV_BSS[i].list), - &priv->bss_free_list); - -} - -void orinoco_clear_scan_results(struct orinoco_private *priv, - unsigned long scan_age) -{ - if (priv->has_ext_scan) { - struct xbss_element *bss; - struct xbss_element *tmp_bss; - - /* Blow away current list of scan results */ - list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) { - if (!scan_age || - time_after(jiffies, bss->last_scanned + scan_age)) { - list_move_tail(&bss->list, - &priv->bss_free_list); - /* Don't blow away ->list, just BSS data */ - memset(&bss->bss, 0, sizeof(bss->bss)); - bss->last_scanned = 0; - } - } - } else { - struct bss_element *bss; - struct bss_element *tmp_bss; - - /* Blow away current list of scan results */ - list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) { - if (!scan_age || - time_after(jiffies, bss->last_scanned + scan_age)) { - list_move_tail(&bss->list, - &priv->bss_free_list); - /* Don't blow away ->list, just BSS data */ - memset(&bss->bss, 0, sizeof(bss->bss)); - bss->last_scanned = 0; - } + buf[0] = WLAN_EID_SUPP_RATES; + for (i = 0; i < 8; i++) { + /* NULL terminated */ + if (rates[i] == 0x0) + break; + buf[i + 2] = rates[i]; + } + buf[1] = i; + + /* We might still have another 2 rates, which need to go in + * extended supported rates */ + if (i == 8 && rates[i] > 0) { + buf[10] = WLAN_EID_EXT_SUPP_RATES; + for (; i < 10; i++) { + /* NULL terminated */ + if (rates[i] == 0x0) + break; + buf[i + 2] = rates[i]; } + buf[11] = i - 8; } + + return (i < 8) ? i + 2 : i + 4; } -void orinoco_add_ext_scan_result(struct orinoco_private *priv, - struct agere_ext_scan_info *atom) +static void orinoco_add_hostscan_result(struct orinoco_private *priv, + const union hermes_scan_info *bss) { - struct xbss_element *bss = NULL; - int found = 0; - - /* Try to update an existing bss first */ - list_for_each_entry(bss, &priv->bss_list, list) { - if (compare_ether_addr(bss->bss.bssid, atom->bssid)) - continue; - /* ESSID lengths */ - if (bss->bss.data[1] != atom->data[1]) - continue; - if (memcmp(&bss->bss.data[2], &atom->data[2], - atom->data[1])) - continue; - found = 1; + struct wiphy *wiphy = priv_to_wiphy(priv); + struct ieee80211_channel *channel; + u8 *ie; + u8 ie_buf[46]; + u64 timestamp; + s32 signal; + u16 capability; + u16 beacon_interval; + int ie_len; + int freq; + int len; + + len = le16_to_cpu(bss->a.essid_len); + + /* Reconstruct SSID and bitrate IEs to pass up */ + ie_buf[0] = WLAN_EID_SSID; + ie_buf[1] = len; + memcpy(&ie_buf[2], bss->a.essid, len); + + ie = ie_buf + len + 2; + ie_len = ie_buf[1] + 2; + switch (priv->firmware_type) { + case FIRMWARE_TYPE_SYMBOL: + ie_len += symbol_build_supp_rates(ie, bss->s.rates); break; - } - /* Grab a bss off the free list */ - if (!found && !list_empty(&priv->bss_free_list)) { - bss = list_entry(priv->bss_free_list.next, - struct xbss_element, list); - list_del(priv->bss_free_list.next); + case FIRMWARE_TYPE_INTERSIL: + ie_len += prism_build_supp_rates(ie, bss->p.rates); + break; - list_add_tail(&bss->list, &priv->bss_list); + case FIRMWARE_TYPE_AGERE: + default: + break; } - if (bss) { - /* Always update the BSS to get latest beacon info */ - memcpy(&bss->bss, atom, sizeof(bss->bss)); - bss->last_scanned = jiffies; - } + freq = ieee80211_dsss_chan_to_freq(le16_to_cpu(bss->a.channel)); + channel = ieee80211_get_channel(wiphy, freq); + timestamp = 0; + capability = le16_to_cpu(bss->a.capabilities); + beacon_interval = le16_to_cpu(bss->a.beacon_interv); + signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level)); + + cfg80211_inform_bss(wiphy, channel, bss->a.bssid, timestamp, + capability, beacon_interval, ie_buf, ie_len, + signal, GFP_KERNEL); } -int orinoco_process_scan_results(struct orinoco_private *priv, - unsigned char *buf, - int len) +void orinoco_add_extscan_result(struct orinoco_private *priv, + struct agere_ext_scan_info *bss, + size_t len) { - int offset; /* In the scan data */ - union hermes_scan_info *atom; - int atom_len; + struct wiphy *wiphy = priv_to_wiphy(priv); + struct ieee80211_channel *channel; + u8 *ie; + u64 timestamp; + s32 signal; + u16 capability; + u16 beacon_interval; + size_t ie_len; + int chan, freq; + + ie_len = len - sizeof(*bss); + ie = orinoco_get_ie(bss->data, ie_len, WLAN_EID_DS_PARAMS); + chan = ie ? ie[2] : 0; + freq = ieee80211_dsss_chan_to_freq(chan); + channel = ieee80211_get_channel(wiphy, freq); + + timestamp = le64_to_cpu(bss->timestamp); + capability = le16_to_cpu(bss->capabilities); + beacon_interval = le16_to_cpu(bss->beacon_interval); + ie = bss->data; + signal = SIGNAL_TO_MBM(bss->level); + + cfg80211_inform_bss(wiphy, channel, bss->bssid, timestamp, + capability, beacon_interval, ie, ie_len, + signal, GFP_KERNEL); +} + +void orinoco_add_hostscan_results(struct orinoco_private *priv, + unsigned char *buf, + size_t len) +{ + int offset; /* In the scan data */ + size_t atom_len; + bool abort = false; switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: atom_len = sizeof(struct agere_scan_apinfo); offset = 0; break; + case FIRMWARE_TYPE_SYMBOL: /* Lack of documentation necessitates this hack. * Different firmwares have 68 or 76 byte long atoms. @@ -163,6 +182,7 @@ int orinoco_process_scan_results(struct orinoco_private *priv, atom_len = 68; offset = 0; break; + case FIRMWARE_TYPE_INTERSIL: offset = 4; if (priv->has_hostscan) { @@ -170,64 +190,41 @@ int orinoco_process_scan_results(struct orinoco_private *priv, /* Sanity check for atom_len */ if (atom_len < sizeof(struct prism2_scan_apinfo)) { printk(KERN_ERR "%s: Invalid atom_len in scan " - "data: %d\n", priv->ndev->name, + "data: %zu\n", priv->ndev->name, atom_len); - return -EIO; + abort = true; + goto scan_abort; } } else atom_len = offsetof(struct prism2_scan_apinfo, atim); break; + default: - return -EOPNOTSUPP; + abort = true; + goto scan_abort; } /* Check that we got an whole number of atoms */ if ((len - offset) % atom_len) { - printk(KERN_ERR "%s: Unexpected scan data length %d, " - "atom_len %d, offset %d\n", priv->ndev->name, len, + printk(KERN_ERR "%s: Unexpected scan data length %zu, " + "atom_len %zu, offset %d\n", priv->ndev->name, len, atom_len, offset); - return -EIO; + abort = true; + goto scan_abort; } - orinoco_clear_scan_results(priv, msecs_to_jiffies(15000)); - - /* Read the entries one by one */ + /* Process the entries one by one */ for (; offset + atom_len <= len; offset += atom_len) { - int found = 0; - struct bss_element *bss = NULL; + union hermes_scan_info *atom; - /* Get next atom */ atom = (union hermes_scan_info *) (buf + offset); - /* Try to update an existing bss first */ - list_for_each_entry(bss, &priv->bss_list, list) { - if (compare_ether_addr(bss->bss.a.bssid, atom->a.bssid)) - continue; - if (le16_to_cpu(bss->bss.a.essid_len) != - le16_to_cpu(atom->a.essid_len)) - continue; - if (memcmp(bss->bss.a.essid, atom->a.essid, - le16_to_cpu(atom->a.essid_len))) - continue; - found = 1; - break; - } - - /* Grab a bss off the free list */ - if (!found && !list_empty(&priv->bss_free_list)) { - bss = list_entry(priv->bss_free_list.next, - struct bss_element, list); - list_del(priv->bss_free_list.next); - - list_add_tail(&bss->list, &priv->bss_list); - } - - if (bss) { - /* Always update the BSS to get latest beacon info */ - memcpy(&bss->bss, atom, sizeof(bss->bss)); - bss->last_scanned = jiffies; - } + orinoco_add_hostscan_result(priv, atom); } - return 0; + scan_abort: + if (priv->scan_request) { + cfg80211_scan_done(priv->scan_request, abort); + priv->scan_request = NULL; + } } diff --git a/drivers/net/wireless/orinoco/scan.h b/drivers/net/wireless/orinoco/scan.h index f319f7466af1..2dc4e046dbdb 100644 --- a/drivers/net/wireless/orinoco/scan.h +++ b/drivers/net/wireless/orinoco/scan.h @@ -9,21 +9,12 @@ struct orinoco_private; struct agere_ext_scan_info; -/* Setup and free memory for scan results */ -int orinoco_bss_data_allocate(struct orinoco_private *priv); -void orinoco_bss_data_free(struct orinoco_private *priv); -void orinoco_bss_data_init(struct orinoco_private *priv); - /* Add scan results */ -void orinoco_add_ext_scan_result(struct orinoco_private *priv, - struct agere_ext_scan_info *atom); -int orinoco_process_scan_results(struct orinoco_private *dev, - unsigned char *buf, - int len); - -/* Clear scan results */ -void orinoco_clear_scan_results(struct orinoco_private *priv, - unsigned long scan_age); - +void orinoco_add_extscan_result(struct orinoco_private *priv, + struct agere_ext_scan_info *atom, + size_t len); +void orinoco_add_hostscan_results(struct orinoco_private *dev, + unsigned char *buf, + size_t len); #endif /* _ORINOCO_SCAN_H_ */ diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c index 38e5198e44c7..c361310b885d 100644 --- a/drivers/net/wireless/orinoco/spectrum_cs.c +++ b/drivers/net/wireless/orinoco/spectrum_cs.c @@ -178,27 +178,25 @@ spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle) static int spectrum_cs_probe(struct pcmcia_device *link) { - struct net_device *dev; struct orinoco_private *priv; struct orinoco_pccard *card; - dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link), - spectrum_cs_hard_reset, - spectrum_cs_stop_firmware); - if (!dev) + priv = alloc_orinocodev(sizeof(*card), &handle_to_dev(link), + spectrum_cs_hard_reset, + spectrum_cs_stop_firmware); + if (!priv) return -ENOMEM; - priv = netdev_priv(dev); card = priv->card; /* Link both structures together */ card->p_dev = link; - link->priv = dev; + link->priv = priv; /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING | IRQ_HANDLE_PRESENT; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = orinoco_interrupt; - link->irq.Instance = dev; + link->irq.Instance = priv; /* General socket configuration defaults can go here. In this * client, we assume very little, and rely on the CIS for @@ -219,14 +217,14 @@ spectrum_cs_probe(struct pcmcia_device *link) */ static void spectrum_cs_detach(struct pcmcia_device *link) { - struct net_device *dev = link->priv; + struct orinoco_private *priv = link->priv; if (link->dev_node) - unregister_netdev(dev); + orinoco_if_del(priv); spectrum_cs_release(link); - free_orinocodev(dev); + free_orinocodev(priv); } /* spectrum_cs_detach */ /* @@ -306,8 +304,7 @@ next_entry: static int spectrum_cs_config(struct pcmcia_device *link) { - struct net_device *dev = link->priv; - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = link->priv; struct orinoco_pccard *card = priv->card; hermes_t *hw = &priv->hw; int last_fn, last_ret; @@ -362,34 +359,31 @@ spectrum_cs_config(struct pcmcia_device *link) pcmcia_request_configuration(link, &link->conf)); /* Ok, we have the configuration, prepare to register the netdev */ - dev->base_addr = link->io.BasePort1; - dev->irq = link->irq.AssignedIRQ; card->node.major = card->node.minor = 0; /* Reset card */ if (spectrum_cs_hard_reset(priv) != 0) goto failed; - SET_NETDEV_DEV(dev, &handle_to_dev(link)); - /* Tell the stack we exist */ - if (register_netdev(dev) != 0) { - printk(KERN_ERR PFX "register_netdev() failed\n"); + /* Initialise the main driver */ + if (orinoco_init(priv) != 0) { + printk(KERN_ERR PFX "orinoco_init() failed\n"); + goto failed; + } + + /* Register an interface with the stack */ + if (orinoco_if_add(priv, link->io.BasePort1, + link->irq.AssignedIRQ) != 0) { + printk(KERN_ERR PFX "orinoco_if_add() failed\n"); goto failed; } /* At this point, the dev_node_t structure(s) needs to be * initialized and arranged in a linked list at link->dev_node. */ - strcpy(card->node.dev_name, dev->name); + strcpy(card->node.dev_name, priv->ndev->name); link->dev_node = &card->node; /* link->dev_node being non-NULL is also * used to indicate that the * net_device has been registered */ - - /* Finally, report what we've done */ - printk(KERN_DEBUG "%s: " DRIVER_NAME " at %s, irq %d, io " - "0x%04x-0x%04x\n", dev->name, dev_name(dev->dev.parent), - link->irq.AssignedIRQ, link->io.BasePort1, - link->io.BasePort1 + link->io.NumPorts1 - 1); - return 0; cs_failed: @@ -408,8 +402,7 @@ spectrum_cs_config(struct pcmcia_device *link) static void spectrum_cs_release(struct pcmcia_device *link) { - struct net_device *dev = link->priv; - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = link->priv; unsigned long flags; /* We're committed to taking the device away now, so mark the @@ -427,23 +420,11 @@ spectrum_cs_release(struct pcmcia_device *link) static int spectrum_cs_suspend(struct pcmcia_device *link) { - struct net_device *dev = link->priv; - struct orinoco_private *priv = netdev_priv(dev); - unsigned long flags; + struct orinoco_private *priv = link->priv; int err = 0; /* Mark the device as stopped, to block IO until later */ - spin_lock_irqsave(&priv->lock, flags); - - err = __orinoco_down(dev); - if (err) - printk(KERN_WARNING "%s: Error %d downing interface\n", - dev->name, err); - - netif_device_detach(dev); - priv->hw_unavailable++; - - spin_unlock_irqrestore(&priv->lock, flags); + orinoco_down(priv); return err; } @@ -451,33 +432,10 @@ spectrum_cs_suspend(struct pcmcia_device *link) static int spectrum_cs_resume(struct pcmcia_device *link) { - struct net_device *dev = link->priv; - struct orinoco_private *priv = netdev_priv(dev); - unsigned long flags; - int err; - - err = orinoco_reinit_firmware(dev); - if (err) { - printk(KERN_ERR "%s: Error %d re-initializing firmware\n", - dev->name, err); - return -EIO; - } - - spin_lock_irqsave(&priv->lock, flags); - - netif_device_attach(dev); - priv->hw_unavailable--; + struct orinoco_private *priv = link->priv; + int err = orinoco_up(priv); - if (priv->open && !priv->hw_unavailable) { - err = __orinoco_up(dev); - if (err) - printk(KERN_ERR "%s: Error %d restarting card\n", - dev->name, err); - } - - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; + return err; } diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c index 3f0814234392..7698fdd6a3a2 100644 --- a/drivers/net/wireless/orinoco/wext.c +++ b/drivers/net/wireless/orinoco/wext.c @@ -7,6 +7,7 @@ #include <linux/wireless.h> #include <linux/ieee80211.h> #include <net/iw_handler.h> +#include <net/cfg80211.h> #include "hermes.h" #include "hermes_rid.h" @@ -21,9 +22,70 @@ #define MAX_RID_LEN 1024 +/* Helper routine to record keys + * Do not call from interrupt context */ +static int orinoco_set_key(struct orinoco_private *priv, int index, + enum orinoco_alg alg, const u8 *key, int key_len, + const u8 *seq, int seq_len) +{ + kzfree(priv->keys[index].key); + kzfree(priv->keys[index].seq); + + if (key_len) { + priv->keys[index].key = kzalloc(key_len, GFP_KERNEL); + if (!priv->keys[index].key) + goto nomem; + } else + priv->keys[index].key = NULL; + + if (seq_len) { + priv->keys[index].seq = kzalloc(seq_len, GFP_KERNEL); + if (!priv->keys[index].seq) + goto free_key; + } else + priv->keys[index].seq = NULL; + + priv->keys[index].key_len = key_len; + priv->keys[index].seq_len = seq_len; + + if (key_len) + memcpy(priv->keys[index].key, key, key_len); + if (seq_len) + memcpy(priv->keys[index].seq, seq, seq_len); + + switch (alg) { + case ORINOCO_ALG_TKIP: + priv->keys[index].cipher = WLAN_CIPHER_SUITE_TKIP; + break; + + case ORINOCO_ALG_WEP: + priv->keys[index].cipher = (key_len > SMALL_KEY_SIZE) ? + WLAN_CIPHER_SUITE_WEP104 : WLAN_CIPHER_SUITE_WEP40; + break; + + case ORINOCO_ALG_NONE: + default: + priv->keys[index].cipher = 0; + break; + } + + return 0; + +free_key: + kfree(priv->keys[index].key); + priv->keys[index].key = NULL; + +nomem: + priv->keys[index].key_len = 0; + priv->keys[index].seq_len = 0; + priv->keys[index].cipher = 0; + + return -ENOMEM; +} + static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); hermes_t *hw = &priv->hw; struct iw_statistics *wstats = &priv->wstats; int err; @@ -51,7 +113,7 @@ static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev) * here so we're not safe to sleep here. */ hermes_inquire(hw, HERMES_INQ_TALLIES); - if (priv->iw_mode == IW_MODE_ADHOC) { + if (priv->iw_mode == NL80211_IFTYPE_ADHOC) { memset(&wstats->qual, 0, sizeof(wstats->qual)); /* If a spy address is defined, we report stats of the * first spy address - Jean II */ @@ -87,31 +149,12 @@ static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev) /* Wireless extensions */ /********************************************************************/ -static int orinoco_ioctl_getname(struct net_device *dev, - struct iw_request_info *info, - char *name, - char *extra) -{ - struct orinoco_private *priv = netdev_priv(dev); - int numrates; - int err; - - err = orinoco_hw_get_bitratelist(priv, &numrates, NULL, 0); - - if (!err && (numrates > 2)) - strcpy(name, "IEEE 802.11b"); - else - strcpy(name, "IEEE 802.11-DS"); - - return 0; -} - static int orinoco_ioctl_setwap(struct net_device *dev, struct iw_request_info *info, struct sockaddr *ap_addr, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -142,7 +185,7 @@ static int orinoco_ioctl_setwap(struct net_device *dev, goto out; } - if (priv->iw_mode != IW_MODE_INFRA) { + if (priv->iw_mode != NL80211_IFTYPE_STATION) { printk(KERN_WARNING "%s: Manual roaming supported only in " "managed mode\n", dev->name); err = -EOPNOTSUPP; @@ -172,9 +215,8 @@ static int orinoco_ioctl_getwap(struct net_device *dev, struct sockaddr *ap_addr, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); - hermes_t *hw = &priv->hw; int err = 0; unsigned long flags; @@ -182,197 +224,23 @@ static int orinoco_ioctl_getwap(struct net_device *dev, return -EBUSY; ap_addr->sa_family = ARPHRD_ETHER; - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, - ETH_ALEN, NULL, ap_addr->sa_data); + err = orinoco_hw_get_current_bssid(priv, ap_addr->sa_data); orinoco_unlock(priv, &flags); return err; } -static int orinoco_ioctl_setmode(struct net_device *dev, - struct iw_request_info *info, - u32 *mode, - char *extra) -{ - struct orinoco_private *priv = netdev_priv(dev); - int err = -EINPROGRESS; /* Call commit handler */ - unsigned long flags; - - if (priv->iw_mode == *mode) - return 0; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - switch (*mode) { - case IW_MODE_ADHOC: - if (!priv->has_ibss && !priv->has_port3) - err = -EOPNOTSUPP; - break; - - case IW_MODE_INFRA: - break; - - case IW_MODE_MONITOR: - if (priv->broken_monitor && !force_monitor) { - printk(KERN_WARNING "%s: Monitor mode support is " - "buggy in this firmware, not enabling\n", - dev->name); - err = -EOPNOTSUPP; - } - break; - - default: - err = -EOPNOTSUPP; - break; - } - - if (err == -EINPROGRESS) { - priv->iw_mode = *mode; - set_port_type(priv); - } - - orinoco_unlock(priv, &flags); - - return err; -} - -static int orinoco_ioctl_getmode(struct net_device *dev, - struct iw_request_info *info, - u32 *mode, - char *extra) -{ - struct orinoco_private *priv = netdev_priv(dev); - - *mode = priv->iw_mode; - return 0; -} - -static int orinoco_ioctl_getiwrange(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *rrq, - char *extra) -{ - struct orinoco_private *priv = netdev_priv(dev); - int err = 0; - struct iw_range *range = (struct iw_range *) extra; - int numrates; - int i, k; - - rrq->length = sizeof(struct iw_range); - memset(range, 0, sizeof(struct iw_range)); - - range->we_version_compiled = WIRELESS_EXT; - range->we_version_source = 22; - - /* Set available channels/frequencies */ - range->num_channels = NUM_CHANNELS; - k = 0; - for (i = 0; i < NUM_CHANNELS; i++) { - if (priv->channel_mask & (1 << i)) { - range->freq[k].i = i + 1; - range->freq[k].m = (ieee80211_dsss_chan_to_freq(i + 1) * - 100000); - range->freq[k].e = 1; - k++; - } - - if (k >= IW_MAX_FREQUENCIES) - break; - } - range->num_frequency = k; - range->sensitivity = 3; - - if (priv->has_wep) { - range->max_encoding_tokens = ORINOCO_MAX_KEYS; - range->encoding_size[0] = SMALL_KEY_SIZE; - range->num_encoding_sizes = 1; - - if (priv->has_big_wep) { - range->encoding_size[1] = LARGE_KEY_SIZE; - range->num_encoding_sizes = 2; - } - } - - if (priv->has_wpa) - range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_CIPHER_TKIP; - - if ((priv->iw_mode == IW_MODE_ADHOC) && (!SPY_NUMBER(priv))) { - /* Quality stats meaningless in ad-hoc mode */ - } else { - range->max_qual.qual = 0x8b - 0x2f; - range->max_qual.level = 0x2f - 0x95 - 1; - range->max_qual.noise = 0x2f - 0x95 - 1; - /* Need to get better values */ - range->avg_qual.qual = 0x24; - range->avg_qual.level = 0xC2; - range->avg_qual.noise = 0x9E; - } - - err = orinoco_hw_get_bitratelist(priv, &numrates, - range->bitrate, IW_MAX_BITRATES); - if (err) - return err; - range->num_bitrates = numrates; - - /* Set an indication of the max TCP throughput in bit/s that we can - * expect using this interface. May be use for QoS stuff... - * Jean II */ - if (numrates > 2) - range->throughput = 5 * 1000 * 1000; /* ~5 Mb/s */ - else - range->throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */ - - range->min_rts = 0; - range->max_rts = 2347; - range->min_frag = 256; - range->max_frag = 2346; - - range->min_pmp = 0; - range->max_pmp = 65535000; - range->min_pmt = 0; - range->max_pmt = 65535 * 1000; /* ??? */ - range->pmp_flags = IW_POWER_PERIOD; - range->pmt_flags = IW_POWER_TIMEOUT; - range->pm_capa = (IW_POWER_PERIOD | IW_POWER_TIMEOUT | - IW_POWER_UNICAST_R); - - range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; - range->retry_flags = IW_RETRY_LIMIT; - range->r_time_flags = IW_RETRY_LIFETIME; - range->min_retry = 0; - range->max_retry = 65535; /* ??? */ - range->min_r_time = 0; - range->max_r_time = 65535 * 1000; /* ??? */ - - if (priv->firmware_type == FIRMWARE_TYPE_AGERE) - range->scan_capa = IW_SCAN_CAPA_ESSID; - else - range->scan_capa = IW_SCAN_CAPA_NONE; - - /* Event capability (kernel) */ - IW_EVENT_CAPA_SET_KERNEL(range->event_capa); - /* Event capability (driver) */ - IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWTHRSPY); - IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP); - IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN); - IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP); - - return 0; -} - static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_request_info *info, struct iw_point *erq, char *keybuf) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int index = (erq->flags & IW_ENCODE_INDEX) - 1; int setindex = priv->tx_key; - int encode_alg = priv->encode_alg; + enum orinoco_alg encode_alg = priv->encode_alg; int restricted = priv->wep_restrict; - u16 xlen = 0; int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; @@ -392,25 +260,17 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, return -EBUSY; /* Clear any TKIP key we have */ - if ((priv->has_wpa) && (priv->encode_alg == IW_ENCODE_ALG_TKIP)) + if ((priv->has_wpa) && (priv->encode_alg == ORINOCO_ALG_TKIP)) (void) orinoco_clear_tkip_key(priv, setindex); if (erq->length > 0) { if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) index = priv->tx_key; - /* Adjust key length to a supported value */ - if (erq->length > SMALL_KEY_SIZE) - xlen = LARGE_KEY_SIZE; - else if (erq->length > 0) - xlen = SMALL_KEY_SIZE; - else - xlen = 0; - /* Switch on WEP if off */ - if ((encode_alg != IW_ENCODE_ALG_WEP) && (xlen > 0)) { + if (encode_alg != ORINOCO_ALG_WEP) { setindex = index; - encode_alg = IW_ENCODE_ALG_WEP; + encode_alg = ORINOCO_ALG_WEP; } } else { /* Important note : if the user do "iwconfig eth0 enc off", @@ -423,7 +283,7 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, } } else { /* Set the index : Check that the key is valid */ - if (priv->keys[index].len == 0) { + if (priv->keys[index].key_len == 0) { err = -EINVAL; goto out; } @@ -432,17 +292,15 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, } if (erq->flags & IW_ENCODE_DISABLED) - encode_alg = IW_ENCODE_ALG_NONE; + encode_alg = ORINOCO_ALG_NONE; if (erq->flags & IW_ENCODE_OPEN) restricted = 0; if (erq->flags & IW_ENCODE_RESTRICTED) restricted = 1; if (erq->pointer && erq->length > 0) { - priv->keys[index].len = cpu_to_le16(xlen); - memset(priv->keys[index].data, 0, - sizeof(priv->keys[index].data)); - memcpy(priv->keys[index].data, keybuf, erq->length); + err = orinoco_set_key(priv, index, ORINOCO_ALG_WEP, keybuf, + erq->length, NULL, 0); } priv->tx_key = setindex; @@ -469,9 +327,8 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq, char *keybuf) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int index = (erq->flags & IW_ENCODE_INDEX) - 1; - u16 xlen = 0; unsigned long flags; if (!priv->has_wep) @@ -493,11 +350,9 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev, else erq->flags |= IW_ENCODE_OPEN; - xlen = le16_to_cpu(priv->keys[index].len); - - erq->length = xlen; + erq->length = priv->keys[index].key_len; - memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE); + memcpy(keybuf, priv->keys[index].key, erq->length); orinoco_unlock(priv, &flags); return 0; @@ -508,7 +363,7 @@ static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq, char *essidbuf) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it @@ -539,7 +394,7 @@ static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq, char *essidbuf) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int active; int err = 0; unsigned long flags; @@ -562,59 +417,18 @@ static int orinoco_ioctl_getessid(struct net_device *dev, return 0; } -static int orinoco_ioctl_setnick(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *nrq, - char *nickbuf) -{ - struct orinoco_private *priv = netdev_priv(dev); - unsigned long flags; - - if (nrq->length > IW_ESSID_MAX_SIZE) - return -E2BIG; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - memset(priv->nick, 0, sizeof(priv->nick)); - memcpy(priv->nick, nickbuf, nrq->length); - - orinoco_unlock(priv, &flags); - - return -EINPROGRESS; /* Call commit handler */ -} - -static int orinoco_ioctl_getnick(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *nrq, - char *nickbuf) -{ - struct orinoco_private *priv = netdev_priv(dev); - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE); - orinoco_unlock(priv, &flags); - - nrq->length = strlen(priv->nick); - - return 0; -} - static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_request_info *info, struct iw_freq *frq, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int chan = -1; unsigned long flags; int err = -EINPROGRESS; /* Call commit handler */ /* In infrastructure mode the AP sets the channel */ - if (priv->iw_mode == IW_MODE_INFRA) + if (priv->iw_mode == NL80211_IFTYPE_STATION) return -EBUSY; if ((frq->e == 0) && (frq->m <= 1000)) { @@ -640,7 +454,7 @@ static int orinoco_ioctl_setfreq(struct net_device *dev, return -EBUSY; priv->channel = chan; - if (priv->iw_mode == IW_MODE_MONITOR) { + if (priv->iw_mode == NL80211_IFTYPE_MONITOR) { /* Fast channel change - no commit if successful */ hermes_t *hw = &priv->hw; err = hermes_docmd_wait(hw, HERMES_CMD_TEST | @@ -657,7 +471,7 @@ static int orinoco_ioctl_getfreq(struct net_device *dev, struct iw_freq *frq, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int tmp; /* Locking done in there */ @@ -676,7 +490,7 @@ static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); hermes_t *hw = &priv->hw; u16 val; int err; @@ -705,7 +519,7 @@ static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int val = srq->value; unsigned long flags; @@ -728,7 +542,7 @@ static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int val = rrq->value; unsigned long flags; @@ -752,7 +566,7 @@ static int orinoco_ioctl_getrts(struct net_device *dev, struct iw_param *rrq, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); rrq->value = priv->rts_thresh; rrq->disabled = (rrq->value == 2347); @@ -766,7 +580,7 @@ static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; @@ -806,7 +620,7 @@ static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); hermes_t *hw = &priv->hw; int err; u16 val; @@ -847,7 +661,7 @@ static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *rrq, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int ratemode; int bitrate; /* 100s of kilobits */ unsigned long flags; @@ -881,7 +695,7 @@ static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *rrq, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int err = 0; int bitrate, automatic; unsigned long flags; @@ -910,7 +724,7 @@ static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ unsigned long flags; @@ -964,7 +778,7 @@ static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); hermes_t *hw = &priv->hw; int err = 0; u16 enable, period, timeout, mcast; @@ -1018,13 +832,12 @@ static int orinoco_ioctl_set_encodeext(struct net_device *dev, union iwreq_data *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); struct iw_point *encoding = &wrqu->encoding; struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; int idx, alg = ext->alg, set_key = 1; unsigned long flags; int err = -EINVAL; - u16 key_len; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; @@ -1056,48 +869,41 @@ static int orinoco_ioctl_set_encodeext(struct net_device *dev, /* Set the requested key first */ switch (alg) { case IW_ENCODE_ALG_NONE: - priv->encode_alg = alg; - priv->keys[idx].len = 0; + priv->encode_alg = ORINOCO_ALG_NONE; + err = orinoco_set_key(priv, idx, ORINOCO_ALG_NONE, + NULL, 0, NULL, 0); break; case IW_ENCODE_ALG_WEP: - if (ext->key_len > SMALL_KEY_SIZE) - key_len = LARGE_KEY_SIZE; - else if (ext->key_len > 0) - key_len = SMALL_KEY_SIZE; - else + if (ext->key_len <= 0) goto out; - priv->encode_alg = alg; - priv->keys[idx].len = cpu_to_le16(key_len); - - key_len = min(ext->key_len, key_len); - - memset(priv->keys[idx].data, 0, ORINOCO_MAX_KEY_SIZE); - memcpy(priv->keys[idx].data, ext->key, key_len); + priv->encode_alg = ORINOCO_ALG_WEP; + err = orinoco_set_key(priv, idx, ORINOCO_ALG_WEP, + ext->key, ext->key_len, NULL, 0); break; case IW_ENCODE_ALG_TKIP: { - hermes_t *hw = &priv->hw; u8 *tkip_iv = NULL; if (!priv->has_wpa || - (ext->key_len > sizeof(priv->tkip_key[0]))) + (ext->key_len > sizeof(struct orinoco_tkip_key))) goto out; - priv->encode_alg = alg; - memset(&priv->tkip_key[idx], 0, - sizeof(priv->tkip_key[idx])); - memcpy(&priv->tkip_key[idx], ext->key, ext->key_len); + priv->encode_alg = ORINOCO_ALG_TKIP; if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) tkip_iv = &ext->rx_seq[0]; - err = __orinoco_hw_set_tkip_key(hw, idx, + err = orinoco_set_key(priv, idx, ORINOCO_ALG_TKIP, + ext->key, ext->key_len, tkip_iv, + ORINOCO_SEQ_LEN); + + err = __orinoco_hw_set_tkip_key(priv, idx, ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY, - (u8 *) &priv->tkip_key[idx], - tkip_iv, NULL); + priv->keys[idx].key, + tkip_iv, ORINOCO_SEQ_LEN, NULL, 0); if (err) printk(KERN_ERR "%s: Error %d setting TKIP key" "\n", dev->name, err); @@ -1120,7 +926,7 @@ static int orinoco_ioctl_get_encodeext(struct net_device *dev, union iwreq_data *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); struct iw_point *encoding = &wrqu->encoding; struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; int idx, max_key_len; @@ -1146,22 +952,22 @@ static int orinoco_ioctl_get_encodeext(struct net_device *dev, encoding->flags = idx + 1; memset(ext, 0, sizeof(*ext)); - ext->alg = priv->encode_alg; switch (priv->encode_alg) { - case IW_ENCODE_ALG_NONE: + case ORINOCO_ALG_NONE: + ext->alg = IW_ENCODE_ALG_NONE; ext->key_len = 0; encoding->flags |= IW_ENCODE_DISABLED; break; - case IW_ENCODE_ALG_WEP: - ext->key_len = min_t(u16, le16_to_cpu(priv->keys[idx].len), - max_key_len); - memcpy(ext->key, priv->keys[idx].data, ext->key_len); + case ORINOCO_ALG_WEP: + ext->alg = IW_ENCODE_ALG_WEP; + ext->key_len = min(priv->keys[idx].key_len, max_key_len); + memcpy(ext->key, priv->keys[idx].key, ext->key_len); encoding->flags |= IW_ENCODE_ENABLED; break; - case IW_ENCODE_ALG_TKIP: - ext->key_len = min_t(u16, sizeof(struct orinoco_tkip_key), - max_key_len); - memcpy(ext->key, &priv->tkip_key[idx], ext->key_len); + case ORINOCO_ALG_TKIP: + ext->alg = IW_ENCODE_ALG_TKIP; + ext->key_len = min(priv->keys[idx].key_len, max_key_len); + memcpy(ext->key, priv->keys[idx].key, ext->key_len); encoding->flags |= IW_ENCODE_ENABLED; break; } @@ -1177,7 +983,7 @@ static int orinoco_ioctl_set_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); hermes_t *hw = &priv->hw; struct iw_param *param = &wrqu->param; unsigned long flags; @@ -1255,7 +1061,7 @@ static int orinoco_ioctl_get_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); struct iw_param *param = &wrqu->param; unsigned long flags; int ret = 0; @@ -1295,7 +1101,7 @@ static int orinoco_ioctl_set_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); u8 *buf; unsigned long flags; @@ -1338,7 +1144,7 @@ static int orinoco_ioctl_get_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; int err = 0; @@ -1367,8 +1173,7 @@ static int orinoco_ioctl_set_mlme(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); - hermes_t *hw = &priv->hw; + struct orinoco_private *priv = ndev_priv(dev); struct iw_mlme *mlme = (struct iw_mlme *)extra; unsigned long flags; int ret = 0; @@ -1382,19 +1187,11 @@ static int orinoco_ioctl_set_mlme(struct net_device *dev, break; case IW_MLME_DISASSOC: - { - struct { - u8 addr[ETH_ALEN]; - __le16 reason_code; - } __attribute__ ((packed)) buf; - - memcpy(buf.addr, mlme->addr.sa_data, ETH_ALEN); - buf.reason_code = cpu_to_le16(mlme->reason_code); - ret = HERMES_WRITE_RECORD(hw, USER_BAP, - HERMES_RID_CNFDISASSOCIATE, - &buf); + + ret = orinoco_hw_disassociate(priv, mlme->addr.sa_data, + mlme->reason_code); break; - } + default: ret = -EOPNOTSUPP; } @@ -1408,7 +1205,7 @@ static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); hermes_t *hw = &priv->hw; int err = 0; u16 short_limit, long_limit, lifetime; @@ -1462,7 +1259,7 @@ static int orinoco_ioctl_reset(struct net_device *dev, void *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); if (!capable(CAP_NET_ADMIN)) return -EPERM; @@ -1487,14 +1284,14 @@ static int orinoco_ioctl_setibssport(struct net_device *dev, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int val = *((int *) extra); unsigned long flags; if (orinoco_lock(priv, &flags) != 0) return -EBUSY; - priv->ibss_port = val ; + priv->ibss_port = val; /* Actually update the mode we are using */ set_port_type(priv); @@ -1508,7 +1305,7 @@ static int orinoco_ioctl_getibssport(struct net_device *dev, void *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int *val = (int *) extra; *val = priv->ibss_port; @@ -1520,7 +1317,7 @@ static int orinoco_ioctl_setport3(struct net_device *dev, void *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int val = *((int *) extra); int err = 0; unsigned long flags; @@ -1566,7 +1363,7 @@ static int orinoco_ioctl_getport3(struct net_device *dev, void *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int *val = (int *) extra; *val = priv->prefer_port3; @@ -1578,7 +1375,7 @@ static int orinoco_ioctl_setpreamble(struct net_device *dev, void *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; int val; @@ -1610,7 +1407,7 @@ static int orinoco_ioctl_getpreamble(struct net_device *dev, void *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); int *val = (int *) extra; if (!priv->has_preamble) @@ -1630,7 +1427,7 @@ static int orinoco_ioctl_getrid(struct net_device *dev, struct iw_point *data, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); + struct orinoco_private *priv = ndev_priv(dev); hermes_t *hw = &priv->hw; int rid = data->flags; u16 length; @@ -1661,519 +1458,6 @@ static int orinoco_ioctl_getrid(struct net_device *dev, return err; } -/* Trigger a scan (look for other cells in the vicinity) */ -static int orinoco_ioctl_setscan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *srq, - char *extra) -{ - struct orinoco_private *priv = netdev_priv(dev); - hermes_t *hw = &priv->hw; - struct iw_scan_req *si = (struct iw_scan_req *) extra; - int err = 0; - unsigned long flags; - - /* Note : you may have realised that, as this is a SET operation, - * this is privileged and therefore a normal user can't - * perform scanning. - * This is not an error, while the device perform scanning, - * traffic doesn't flow, so it's a perfect DoS... - * Jean II */ - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - /* Scanning with port 0 disabled would fail */ - if (!netif_running(dev)) { - err = -ENETDOWN; - goto out; - } - - /* In monitor mode, the scan results are always empty. - * Probe responses are passed to the driver as received - * frames and could be processed in software. */ - if (priv->iw_mode == IW_MODE_MONITOR) { - err = -EOPNOTSUPP; - goto out; - } - - /* Note : because we don't lock out the irq handler, the way - * we access scan variables in priv is critical. - * o scan_inprogress : not touched by irq handler - * o scan_mode : not touched by irq handler - * Before modifying anything on those variables, please think hard ! - * Jean II */ - - /* Save flags */ - priv->scan_mode = srq->flags; - - /* Always trigger scanning, even if it's in progress. - * This way, if the info frame get lost, we will recover somewhat - * gracefully - Jean II */ - - if (priv->has_hostscan) { - switch (priv->firmware_type) { - case FIRMWARE_TYPE_SYMBOL: - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFHOSTSCAN_SYMBOL, - HERMES_HOSTSCAN_SYMBOL_ONCE | - HERMES_HOSTSCAN_SYMBOL_BCAST); - break; - case FIRMWARE_TYPE_INTERSIL: { - __le16 req[3]; - - req[0] = cpu_to_le16(0x3fff); /* All channels */ - req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */ - req[2] = 0; /* Any ESSID */ - err = HERMES_WRITE_RECORD(hw, USER_BAP, - HERMES_RID_CNFHOSTSCAN, &req); - } - break; - case FIRMWARE_TYPE_AGERE: - if (priv->scan_mode & IW_SCAN_THIS_ESSID) { - struct hermes_idstring idbuf; - size_t len = min(sizeof(idbuf.val), - (size_t) si->essid_len); - idbuf.len = cpu_to_le16(len); - memcpy(idbuf.val, si->essid, len); - - err = hermes_write_ltv(hw, USER_BAP, - HERMES_RID_CNFSCANSSID_AGERE, - HERMES_BYTES_TO_RECLEN(len + 2), - &idbuf); - } else - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFSCANSSID_AGERE, - 0); /* Any ESSID */ - if (err) - break; - - if (priv->has_ext_scan) { - /* Clear scan results at the start of - * an extended scan */ - orinoco_clear_scan_results(priv, - msecs_to_jiffies(15000)); - - /* TODO: Is this available on older firmware? - * Can we use it to scan specific channels - * for IW_SCAN_THIS_FREQ? */ - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFSCANCHANNELS2GHZ, - 0x7FFF); - if (err) - goto out; - - err = hermes_inquire(hw, - HERMES_INQ_CHANNELINFO); - } else - err = hermes_inquire(hw, HERMES_INQ_SCAN); - break; - } - } else - err = hermes_inquire(hw, HERMES_INQ_SCAN); - - /* One more client */ - if (!err) - priv->scan_inprogress = 1; - - out: - orinoco_unlock(priv, &flags); - return err; -} - -#define MAX_CUSTOM_LEN 64 - -/* Translate scan data returned from the card to a card independant - * format that the Wireless Tools will understand - Jean II */ -static inline char *orinoco_translate_scan(struct net_device *dev, - struct iw_request_info *info, - char *current_ev, - char *end_buf, - union hermes_scan_info *bss, - unsigned long last_scanned) -{ - struct orinoco_private *priv = netdev_priv(dev); - u16 capabilities; - u16 channel; - struct iw_event iwe; /* Temporary buffer */ - char custom[MAX_CUSTOM_LEN]; - - memset(&iwe, 0, sizeof(iwe)); - - /* First entry *MUST* be the AP MAC address */ - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, bss->a.bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_ADDR_LEN); - - /* Other entries will be displayed in the order we give them */ - - /* Add the ESSID */ - iwe.u.data.length = le16_to_cpu(bss->a.essid_len); - if (iwe.u.data.length > 32) - iwe.u.data.length = 32; - iwe.cmd = SIOCGIWESSID; - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, bss->a.essid); - - /* Add mode */ - iwe.cmd = SIOCGIWMODE; - capabilities = le16_to_cpu(bss->a.capabilities); - if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { - if (capabilities & WLAN_CAPABILITY_ESS) - iwe.u.mode = IW_MODE_MASTER; - else - iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_UINT_LEN); - } - - channel = bss->s.channel; - if ((channel >= 1) && (channel <= NUM_CHANNELS)) { - /* Add channel and frequency */ - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = channel; - iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_FREQ_LEN); - - iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000; - iwe.u.freq.e = 1; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_FREQ_LEN); - } - - /* Add quality statistics. level and noise in dB. No link quality */ - iwe.cmd = IWEVQUAL; - iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID; - iwe.u.qual.level = (__u8) le16_to_cpu(bss->a.level) - 0x95; - iwe.u.qual.noise = (__u8) le16_to_cpu(bss->a.noise) - 0x95; - /* Wireless tools prior to 27.pre22 will show link quality - * anyway, so we provide a reasonable value. */ - if (iwe.u.qual.level > iwe.u.qual.noise) - iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; - else - iwe.u.qual.qual = 0; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_QUAL_LEN); - - /* Add encryption capability */ - iwe.cmd = SIOCGIWENCODE; - if (capabilities & WLAN_CAPABILITY_PRIVACY) - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, NULL); - - /* Bit rate is not available in Lucent/Agere firmwares */ - if (priv->firmware_type != FIRMWARE_TYPE_AGERE) { - char *current_val = current_ev + iwe_stream_lcp_len(info); - int i; - int step; - - if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL) - step = 2; - else - step = 1; - - iwe.cmd = SIOCGIWRATE; - /* Those two flags are ignored... */ - iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; - /* Max 10 values */ - for (i = 0; i < 10; i += step) { - /* NULL terminated */ - if (bss->p.rates[i] == 0x0) - break; - /* Bit rate given in 500 kb/s units (+ 0x80) */ - iwe.u.bitrate.value = - ((bss->p.rates[i] & 0x7f) * 500000); - current_val = iwe_stream_add_value(info, current_ev, - current_val, - end_buf, &iwe, - IW_EV_PARAM_LEN); - } - /* Check if we added any event */ - if ((current_val - current_ev) > iwe_stream_lcp_len(info)) - current_ev = current_val; - } - - /* Beacon interval */ - iwe.cmd = IWEVCUSTOM; - iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, - "bcn_int=%d", - le16_to_cpu(bss->a.beacon_interv)); - if (iwe.u.data.length) - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, custom); - - /* Capabilites */ - iwe.cmd = IWEVCUSTOM; - iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, - "capab=0x%04x", - capabilities); - if (iwe.u.data.length) - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, custom); - - /* Add EXTRA: Age to display seconds since last beacon/probe response - * for given network. */ - iwe.cmd = IWEVCUSTOM; - iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, - " Last beacon: %dms ago", - jiffies_to_msecs(jiffies - last_scanned)); - if (iwe.u.data.length) - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, custom); - - return current_ev; -} - -static inline char *orinoco_translate_ext_scan(struct net_device *dev, - struct iw_request_info *info, - char *current_ev, - char *end_buf, - struct agere_ext_scan_info *bss, - unsigned long last_scanned) -{ - u16 capabilities; - u16 channel; - struct iw_event iwe; /* Temporary buffer */ - char custom[MAX_CUSTOM_LEN]; - u8 *ie; - - memset(&iwe, 0, sizeof(iwe)); - - /* First entry *MUST* be the AP MAC address */ - iwe.cmd = SIOCGIWAP; - iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_ADDR_LEN); - - /* Other entries will be displayed in the order we give them */ - - /* Add the ESSID */ - ie = bss->data; - iwe.u.data.length = ie[1]; - if (iwe.u.data.length) { - if (iwe.u.data.length > 32) - iwe.u.data.length = 32; - iwe.cmd = SIOCGIWESSID; - iwe.u.data.flags = 1; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, &ie[2]); - } - - /* Add mode */ - capabilities = le16_to_cpu(bss->capabilities); - if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) { - iwe.cmd = SIOCGIWMODE; - if (capabilities & WLAN_CAPABILITY_ESS) - iwe.u.mode = IW_MODE_MASTER; - else - iwe.u.mode = IW_MODE_ADHOC; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_UINT_LEN); - } - - ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_DS_PARAMS); - channel = ie ? ie[2] : 0; - if ((channel >= 1) && (channel <= NUM_CHANNELS)) { - /* Add channel and frequency */ - iwe.cmd = SIOCGIWFREQ; - iwe.u.freq.m = channel; - iwe.u.freq.e = 0; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_FREQ_LEN); - - iwe.u.freq.m = ieee80211_dsss_chan_to_freq(channel) * 100000; - iwe.u.freq.e = 1; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_FREQ_LEN); - } - - /* Add quality statistics. level and noise in dB. No link quality */ - iwe.cmd = IWEVQUAL; - iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID; - iwe.u.qual.level = bss->level - 0x95; - iwe.u.qual.noise = bss->noise - 0x95; - /* Wireless tools prior to 27.pre22 will show link quality - * anyway, so we provide a reasonable value. */ - if (iwe.u.qual.level > iwe.u.qual.noise) - iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise; - else - iwe.u.qual.qual = 0; - current_ev = iwe_stream_add_event(info, current_ev, end_buf, - &iwe, IW_EV_QUAL_LEN); - - /* Add encryption capability */ - iwe.cmd = SIOCGIWENCODE; - if (capabilities & WLAN_CAPABILITY_PRIVACY) - iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; - else - iwe.u.data.flags = IW_ENCODE_DISABLED; - iwe.u.data.length = 0; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, NULL); - - /* WPA IE */ - ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data)); - if (ie) { - iwe.cmd = IWEVGENIE; - iwe.u.data.length = ie[1] + 2; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, ie); - } - - /* RSN IE */ - ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_RSN); - if (ie) { - iwe.cmd = IWEVGENIE; - iwe.u.data.length = ie[1] + 2; - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, ie); - } - - ie = orinoco_get_ie(bss->data, sizeof(bss->data), WLAN_EID_SUPP_RATES); - if (ie) { - char *p = current_ev + iwe_stream_lcp_len(info); - int i; - - iwe.cmd = SIOCGIWRATE; - /* Those two flags are ignored... */ - iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; - - for (i = 2; i < (ie[1] + 2); i++) { - iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000); - p = iwe_stream_add_value(info, current_ev, p, end_buf, - &iwe, IW_EV_PARAM_LEN); - } - /* Check if we added any event */ - if (p > (current_ev + iwe_stream_lcp_len(info))) - current_ev = p; - } - - /* Timestamp */ - iwe.cmd = IWEVCUSTOM; - iwe.u.data.length = - snprintf(custom, MAX_CUSTOM_LEN, "tsf=%016llx", - (unsigned long long) le64_to_cpu(bss->timestamp)); - if (iwe.u.data.length) - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, custom); - - /* Beacon interval */ - iwe.cmd = IWEVCUSTOM; - iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, - "bcn_int=%d", - le16_to_cpu(bss->beacon_interval)); - if (iwe.u.data.length) - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, custom); - - /* Capabilites */ - iwe.cmd = IWEVCUSTOM; - iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, - "capab=0x%04x", - capabilities); - if (iwe.u.data.length) - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, custom); - - /* Add EXTRA: Age to display seconds since last beacon/probe response - * for given network. */ - iwe.cmd = IWEVCUSTOM; - iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN, - " Last beacon: %dms ago", - jiffies_to_msecs(jiffies - last_scanned)); - if (iwe.u.data.length) - current_ev = iwe_stream_add_point(info, current_ev, end_buf, - &iwe, custom); - - return current_ev; -} - -/* Return results of a scan */ -static int orinoco_ioctl_getscan(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *srq, - char *extra) -{ - struct orinoco_private *priv = netdev_priv(dev); - int err = 0; - unsigned long flags; - char *current_ev = extra; - - if (orinoco_lock(priv, &flags) != 0) - return -EBUSY; - - if (priv->scan_inprogress) { - /* Important note : we don't want to block the caller - * until results are ready for various reasons. - * First, managing wait queues is complex and racy. - * Second, we grab some rtnetlink lock before comming - * here (in dev_ioctl()). - * Third, we generate an Wireless Event, so the - * caller can wait itself on that - Jean II */ - err = -EAGAIN; - goto out; - } - - if (priv->has_ext_scan) { - struct xbss_element *bss; - - list_for_each_entry(bss, &priv->bss_list, list) { - /* Translate this entry to WE format */ - current_ev = - orinoco_translate_ext_scan(dev, info, - current_ev, - extra + srq->length, - &bss->bss, - bss->last_scanned); - - /* Check if there is space for one more entry */ - if ((extra + srq->length - current_ev) - <= IW_EV_ADDR_LEN) { - /* Ask user space to try again with a - * bigger buffer */ - err = -E2BIG; - goto out; - } - } - - } else { - struct bss_element *bss; - - list_for_each_entry(bss, &priv->bss_list, list) { - /* Translate this entry to WE format */ - current_ev = orinoco_translate_scan(dev, info, - current_ev, - extra + srq->length, - &bss->bss, - bss->last_scanned); - - /* Check if there is space for one more entry */ - if ((extra + srq->length - current_ev) - <= IW_EV_ADDR_LEN) { - /* Ask user space to try again with a - * bigger buffer */ - err = -E2BIG; - goto out; - } - } - } - - srq->length = (current_ev - extra); - srq->flags = (__u16) priv->scan_mode; - -out: - orinoco_unlock(priv, &flags); - return err; -} /* Commit handler, called after set operations */ static int orinoco_ioctl_commit(struct net_device *dev, @@ -2181,50 +1465,17 @@ static int orinoco_ioctl_commit(struct net_device *dev, void *wrqu, char *extra) { - struct orinoco_private *priv = netdev_priv(dev); - struct hermes *hw = &priv->hw; + struct orinoco_private *priv = ndev_priv(dev); unsigned long flags; int err = 0; if (!priv->open) return 0; - if (priv->broken_disableport) { - orinoco_reset(&priv->reset_work); - return 0; - } - if (orinoco_lock(priv, &flags) != 0) return err; - err = hermes_disable_port(hw, 0); - if (err) { - printk(KERN_WARNING "%s: Unable to disable port " - "while reconfiguring card\n", dev->name); - priv->broken_disableport = 1; - goto out; - } - - err = __orinoco_program_rids(dev); - if (err) { - printk(KERN_WARNING "%s: Unable to reconfigure card\n", - dev->name); - goto out; - } - - err = hermes_enable_port(hw, 0); - if (err) { - printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", - dev->name); - goto out; - } - - out: - if (err) { - printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); - schedule_work(&priv->reset_work); - err = 0; - } + err = orinoco_commit(priv); orinoco_unlock(priv, &flags); return err; @@ -2258,26 +1509,24 @@ static const struct iw_priv_args orinoco_privtab[] = { [IW_IOCTL_IDX(id)] = (iw_handler) func static const iw_handler orinoco_handler[] = { STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit), - STD_IW_HANDLER(SIOCGIWNAME, orinoco_ioctl_getname), + STD_IW_HANDLER(SIOCGIWNAME, cfg80211_wext_giwname), STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq), STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq), - STD_IW_HANDLER(SIOCSIWMODE, orinoco_ioctl_setmode), - STD_IW_HANDLER(SIOCGIWMODE, orinoco_ioctl_getmode), + STD_IW_HANDLER(SIOCSIWMODE, cfg80211_wext_siwmode), + STD_IW_HANDLER(SIOCGIWMODE, cfg80211_wext_giwmode), STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens), STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens), - STD_IW_HANDLER(SIOCGIWRANGE, orinoco_ioctl_getiwrange), + STD_IW_HANDLER(SIOCGIWRANGE, cfg80211_wext_giwrange), STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap), STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap), - STD_IW_HANDLER(SIOCSIWSCAN, orinoco_ioctl_setscan), - STD_IW_HANDLER(SIOCGIWSCAN, orinoco_ioctl_getscan), + STD_IW_HANDLER(SIOCSIWSCAN, cfg80211_wext_siwscan), + STD_IW_HANDLER(SIOCGIWSCAN, cfg80211_wext_giwscan), STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid), STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid), - STD_IW_HANDLER(SIOCSIWNICKN, orinoco_ioctl_setnick), - STD_IW_HANDLER(SIOCGIWNICKN, orinoco_ioctl_getnick), STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate), STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate), STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts), diff --git a/drivers/net/wireless/p54/Makefile b/drivers/net/wireless/p54/Makefile index c2050dee6293..b542e68f1781 100644 --- a/drivers/net/wireless/p54/Makefile +++ b/drivers/net/wireless/p54/Makefile @@ -1,3 +1,6 @@ +p54common-objs := eeprom.o fwio.o txrx.o main.o +p54common-$(CONFIG_P54_LEDS) += led.o + obj-$(CONFIG_P54_COMMON) += p54common.o obj-$(CONFIG_P54_USB) += p54usb.o obj-$(CONFIG_P54_PCI) += p54pci.o diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c new file mode 100644 index 000000000000..0efe67deedee --- /dev/null +++ b/drivers/net/wireless/p54/eeprom.c @@ -0,0 +1,753 @@ +/* + * EEPROM parser code for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/firmware.h> +#include <linux/etherdevice.h> +#include <linux/sort.h> + +#include <net/mac80211.h> + +#include "p54.h" +#include "eeprom.h" +#include "lmac.h" + +static struct ieee80211_rate p54_bgrates[] = { + { .bitrate = 10, .hw_value = 0, }, + { .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60, .hw_value = 4, }, + { .bitrate = 90, .hw_value = 5, }, + { .bitrate = 120, .hw_value = 6, }, + { .bitrate = 180, .hw_value = 7, }, + { .bitrate = 240, .hw_value = 8, }, + { .bitrate = 360, .hw_value = 9, }, + { .bitrate = 480, .hw_value = 10, }, + { .bitrate = 540, .hw_value = 11, }, +}; + +static struct ieee80211_rate p54_arates[] = { + { .bitrate = 60, .hw_value = 4, }, + { .bitrate = 90, .hw_value = 5, }, + { .bitrate = 120, .hw_value = 6, }, + { .bitrate = 180, .hw_value = 7, }, + { .bitrate = 240, .hw_value = 8, }, + { .bitrate = 360, .hw_value = 9, }, + { .bitrate = 480, .hw_value = 10, }, + { .bitrate = 540, .hw_value = 11, }, +}; + +#define CHAN_HAS_CAL BIT(0) +#define CHAN_HAS_LIMIT BIT(1) +#define CHAN_HAS_CURVE BIT(2) +#define CHAN_HAS_ALL (CHAN_HAS_CAL | CHAN_HAS_LIMIT | CHAN_HAS_CURVE) + +struct p54_channel_entry { + u16 freq; + u16 data; + int index; + enum ieee80211_band band; +}; + +struct p54_channel_list { + struct p54_channel_entry *channels; + size_t entries; + size_t max_entries; + size_t band_channel_num[IEEE80211_NUM_BANDS]; +}; + +static int p54_get_band_from_freq(u16 freq) +{ + /* FIXME: sync these values with the 802.11 spec */ + + if ((freq >= 2412) && (freq <= 2484)) + return IEEE80211_BAND_2GHZ; + + if ((freq >= 4920) && (freq <= 5825)) + return IEEE80211_BAND_5GHZ; + + return -1; +} + +static int p54_compare_channels(const void *_a, + const void *_b) +{ + const struct p54_channel_entry *a = _a; + const struct p54_channel_entry *b = _b; + + return a->index - b->index; +} + +static int p54_fill_band_bitrates(struct ieee80211_hw *dev, + struct ieee80211_supported_band *band_entry, + enum ieee80211_band band) +{ + /* TODO: generate rate array dynamically */ + + switch (band) { + case IEEE80211_BAND_2GHZ: + band_entry->bitrates = p54_bgrates; + band_entry->n_bitrates = ARRAY_SIZE(p54_bgrates); + break; + case IEEE80211_BAND_5GHZ: + band_entry->bitrates = p54_arates; + band_entry->n_bitrates = ARRAY_SIZE(p54_arates); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int p54_generate_band(struct ieee80211_hw *dev, + struct p54_channel_list *list, + enum ieee80211_band band) +{ + struct p54_common *priv = dev->priv; + struct ieee80211_supported_band *tmp, *old; + unsigned int i, j; + int ret = -ENOMEM; + + if ((!list->entries) || (!list->band_channel_num[band])) + return 0; + + tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) + goto err_out; + + tmp->channels = kzalloc(sizeof(struct ieee80211_channel) * + list->band_channel_num[band], GFP_KERNEL); + if (!tmp->channels) + goto err_out; + + ret = p54_fill_band_bitrates(dev, tmp, band); + if (ret) + goto err_out; + + for (i = 0, j = 0; (j < list->band_channel_num[band]) && + (i < list->entries); i++) { + + if (list->channels[i].band != band) + continue; + + if (list->channels[i].data != CHAN_HAS_ALL) { + printk(KERN_ERR "%s:%s%s%s is/are missing for " + "channel:%d [%d MHz].\n", + wiphy_name(dev->wiphy), + (list->channels[i].data & CHAN_HAS_CAL ? "" : + " [iqauto calibration data]"), + (list->channels[i].data & CHAN_HAS_LIMIT ? "" : + " [output power limits]"), + (list->channels[i].data & CHAN_HAS_CURVE ? "" : + " [curve data]"), + list->channels[i].index, list->channels[i].freq); + } + + tmp->channels[j].band = list->channels[i].band; + tmp->channels[j].center_freq = list->channels[i].freq; + j++; + } + + tmp->n_channels = list->band_channel_num[band]; + old = priv->band_table[band]; + priv->band_table[band] = tmp; + if (old) { + kfree(old->channels); + kfree(old); + } + + return 0; + +err_out: + if (tmp) { + kfree(tmp->channels); + kfree(tmp); + } + + return ret; +} + +static void p54_update_channel_param(struct p54_channel_list *list, + u16 freq, u16 data) +{ + int band, i; + + /* + * usually all lists in the eeprom are mostly sorted. + * so it's very likely that the entry we are looking for + * is right at the end of the list + */ + for (i = list->entries; i >= 0; i--) { + if (freq == list->channels[i].freq) { + list->channels[i].data |= data; + break; + } + } + + if ((i < 0) && (list->entries < list->max_entries)) { + /* entry does not exist yet. Initialize a new one. */ + band = p54_get_band_from_freq(freq); + + /* + * filter out frequencies which don't belong into + * any supported band. + */ + if (band < 0) + return ; + + i = list->entries++; + list->band_channel_num[band]++; + + list->channels[i].freq = freq; + list->channels[i].data = data; + list->channels[i].band = band; + list->channels[i].index = ieee80211_frequency_to_channel(freq); + /* TODO: parse output_limit and fill max_power */ + } +} + +static int p54_generate_channel_lists(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + struct p54_channel_list *list; + unsigned int i, j, max_channel_num; + int ret = -ENOMEM; + u16 freq; + + if ((priv->iq_autocal_len != priv->curve_data->entries) || + (priv->iq_autocal_len != priv->output_limit->entries)) + printk(KERN_ERR "%s: EEPROM is damaged... you may not be able" + "to use all channels with this device.\n", + wiphy_name(dev->wiphy)); + + max_channel_num = max_t(unsigned int, priv->output_limit->entries, + priv->iq_autocal_len); + max_channel_num = max_t(unsigned int, max_channel_num, + priv->curve_data->entries); + + list = kzalloc(sizeof(*list), GFP_KERNEL); + if (!list) + goto free; + + list->max_entries = max_channel_num; + list->channels = kzalloc(sizeof(struct p54_channel_entry) * + max_channel_num, GFP_KERNEL); + if (!list->channels) + goto free; + + for (i = 0; i < max_channel_num; i++) { + if (i < priv->iq_autocal_len) { + freq = le16_to_cpu(priv->iq_autocal[i].freq); + p54_update_channel_param(list, freq, CHAN_HAS_CAL); + } + + if (i < priv->output_limit->entries) { + freq = le16_to_cpup((__le16 *) (i * + priv->output_limit->entry_size + + priv->output_limit->offset + + priv->output_limit->data)); + + p54_update_channel_param(list, freq, CHAN_HAS_LIMIT); + } + + if (i < priv->curve_data->entries) { + freq = le16_to_cpup((__le16 *) (i * + priv->curve_data->entry_size + + priv->curve_data->offset + + priv->curve_data->data)); + + p54_update_channel_param(list, freq, CHAN_HAS_CURVE); + } + } + + /* sort the list by the channel index */ + sort(list->channels, list->entries, sizeof(struct p54_channel_entry), + p54_compare_channels, NULL); + + for (i = 0, j = 0; i < IEEE80211_NUM_BANDS; i++) { + if (list->band_channel_num[i]) { + ret = p54_generate_band(dev, list, i); + if (ret) + goto free; + + j++; + } + } + if (j == 0) { + /* no useable band available. */ + ret = -EINVAL; + } + +free: + if (list) { + kfree(list->channels); + kfree(list); + } + + return ret; +} + +static int p54_convert_rev0(struct ieee80211_hw *dev, + struct pda_pa_curve_data *curve_data) +{ + struct p54_common *priv = dev->priv; + struct p54_pa_curve_data_sample *dst; + struct pda_pa_curve_data_sample_rev0 *src; + size_t cd_len = sizeof(*curve_data) + + (curve_data->points_per_channel*sizeof(*dst) + 2) * + curve_data->channels; + unsigned int i, j; + void *source, *target; + + priv->curve_data = kmalloc(sizeof(*priv->curve_data) + cd_len, + GFP_KERNEL); + if (!priv->curve_data) + return -ENOMEM; + + priv->curve_data->entries = curve_data->channels; + priv->curve_data->entry_size = sizeof(__le16) + + sizeof(*dst) * curve_data->points_per_channel; + priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data); + priv->curve_data->len = cd_len; + memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data)); + source = curve_data->data; + target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data; + for (i = 0; i < curve_data->channels; i++) { + __le16 *freq = source; + source += sizeof(__le16); + *((__le16 *)target) = *freq; + target += sizeof(__le16); + for (j = 0; j < curve_data->points_per_channel; j++) { + dst = target; + src = source; + + dst->rf_power = src->rf_power; + dst->pa_detector = src->pa_detector; + dst->data_64qam = src->pcv; + /* "invent" the points for the other modulations */ +#define SUB(x, y) (u8)(((x) - (y)) > (x) ? 0 : (x) - (y)) + dst->data_16qam = SUB(src->pcv, 12); + dst->data_qpsk = SUB(dst->data_16qam, 12); + dst->data_bpsk = SUB(dst->data_qpsk, 12); + dst->data_barker = SUB(dst->data_bpsk, 14); +#undef SUB + target += sizeof(*dst); + source += sizeof(*src); + } + } + + return 0; +} + +static int p54_convert_rev1(struct ieee80211_hw *dev, + struct pda_pa_curve_data *curve_data) +{ + struct p54_common *priv = dev->priv; + struct p54_pa_curve_data_sample *dst; + struct pda_pa_curve_data_sample_rev1 *src; + size_t cd_len = sizeof(*curve_data) + + (curve_data->points_per_channel*sizeof(*dst) + 2) * + curve_data->channels; + unsigned int i, j; + void *source, *target; + + priv->curve_data = kzalloc(cd_len + sizeof(*priv->curve_data), + GFP_KERNEL); + if (!priv->curve_data) + return -ENOMEM; + + priv->curve_data->entries = curve_data->channels; + priv->curve_data->entry_size = sizeof(__le16) + + sizeof(*dst) * curve_data->points_per_channel; + priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data); + priv->curve_data->len = cd_len; + memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data)); + source = curve_data->data; + target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data; + for (i = 0; i < curve_data->channels; i++) { + __le16 *freq = source; + source += sizeof(__le16); + *((__le16 *)target) = *freq; + target += sizeof(__le16); + for (j = 0; j < curve_data->points_per_channel; j++) { + memcpy(target, source, sizeof(*src)); + + target += sizeof(*dst); + source += sizeof(*src); + } + source++; + } + + return 0; +} + +static const char *p54_rf_chips[] = { "INVALID-0", "Duette3", "Duette2", + "Frisbee", "Xbow", "Longbow", "INVALID-6", "INVALID-7" }; + +static void p54_parse_rssical(struct ieee80211_hw *dev, void *data, int len, + u16 type) +{ + struct p54_common *priv = dev->priv; + int offset = (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) ? 2 : 0; + int entry_size = sizeof(struct pda_rssi_cal_entry) + offset; + int num_entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2; + int i; + + if (len != (entry_size * num_entries)) { + printk(KERN_ERR "%s: unknown rssi calibration data packing " + " type:(%x) len:%d.\n", + wiphy_name(dev->wiphy), type, len); + + print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, + data, len); + + printk(KERN_ERR "%s: please report this issue.\n", + wiphy_name(dev->wiphy)); + return; + } + + for (i = 0; i < num_entries; i++) { + struct pda_rssi_cal_entry *cal = data + + (offset + i * entry_size); + priv->rssical_db[i].mul = (s16) le16_to_cpu(cal->mul); + priv->rssical_db[i].add = (s16) le16_to_cpu(cal->add); + } +} + +static void p54_parse_default_country(struct ieee80211_hw *dev, + void *data, int len) +{ + struct pda_country *country; + + if (len != sizeof(*country)) { + printk(KERN_ERR "%s: found possible invalid default country " + "eeprom entry. (entry size: %d)\n", + wiphy_name(dev->wiphy), len); + + print_hex_dump_bytes("country:", DUMP_PREFIX_NONE, + data, len); + + printk(KERN_ERR "%s: please report this issue.\n", + wiphy_name(dev->wiphy)); + return; + } + + country = (struct pda_country *) data; + if (country->flags == PDR_COUNTRY_CERT_CODE_PSEUDO) + regulatory_hint(dev->wiphy, country->alpha2); + else { + /* TODO: + * write a shared/common function that converts + * "Regulatory domain codes" (802.11-2007 14.8.2.2) + * into ISO/IEC 3166-1 alpha2 for regulatory_hint. + */ + } +} + +static int p54_convert_output_limits(struct ieee80211_hw *dev, + u8 *data, size_t len) +{ + struct p54_common *priv = dev->priv; + + if (len < 2) + return -EINVAL; + + if (data[0] != 0) { + printk(KERN_ERR "%s: unknown output power db revision:%x\n", + wiphy_name(dev->wiphy), data[0]); + return -EINVAL; + } + + if (2 + data[1] * sizeof(struct pda_channel_output_limit) > len) + return -EINVAL; + + priv->output_limit = kmalloc(data[1] * + sizeof(struct pda_channel_output_limit) + + sizeof(*priv->output_limit), GFP_KERNEL); + + if (!priv->output_limit) + return -ENOMEM; + + priv->output_limit->offset = 0; + priv->output_limit->entries = data[1]; + priv->output_limit->entry_size = + sizeof(struct pda_channel_output_limit); + priv->output_limit->len = priv->output_limit->entry_size * + priv->output_limit->entries + + priv->output_limit->offset; + + memcpy(priv->output_limit->data, &data[2], + data[1] * sizeof(struct pda_channel_output_limit)); + + return 0; +} + +static struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src, + size_t total_len) +{ + struct p54_cal_database *dst; + size_t payload_len, entries, entry_size, offset; + + payload_len = le16_to_cpu(src->len); + entries = le16_to_cpu(src->entries); + entry_size = le16_to_cpu(src->entry_size); + offset = le16_to_cpu(src->offset); + if (((entries * entry_size + offset) != payload_len) || + (payload_len + sizeof(*src) != total_len)) + return NULL; + + dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL); + if (!dst) + return NULL; + + dst->entries = entries; + dst->entry_size = entry_size; + dst->offset = offset; + dst->len = payload_len; + + memcpy(dst->data, src->data, payload_len); + return dst; +} + +int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) +{ + struct p54_common *priv = dev->priv; + struct eeprom_pda_wrap *wrap; + struct pda_entry *entry; + unsigned int data_len, entry_len; + void *tmp; + int err; + u8 *end = (u8 *)eeprom + len; + u16 synth = 0; + + wrap = (struct eeprom_pda_wrap *) eeprom; + entry = (void *)wrap->data + le16_to_cpu(wrap->len); + + /* verify that at least the entry length/code fits */ + while ((u8 *)entry <= end - sizeof(*entry)) { + entry_len = le16_to_cpu(entry->len); + data_len = ((entry_len - 1) << 1); + + /* abort if entry exceeds whole structure */ + if ((u8 *)entry + sizeof(*entry) + data_len > end) + break; + + switch (le16_to_cpu(entry->code)) { + case PDR_MAC_ADDRESS: + if (data_len != ETH_ALEN) + break; + SET_IEEE80211_PERM_ADDR(dev, entry->data); + break; + case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS: + if (priv->output_limit) + break; + err = p54_convert_output_limits(dev, entry->data, + data_len); + if (err) + goto err; + break; + case PDR_PRISM_PA_CAL_CURVE_DATA: { + struct pda_pa_curve_data *curve_data = + (struct pda_pa_curve_data *)entry->data; + if (data_len < sizeof(*curve_data)) { + err = -EINVAL; + goto err; + } + + switch (curve_data->cal_method_rev) { + case 0: + err = p54_convert_rev0(dev, curve_data); + break; + case 1: + err = p54_convert_rev1(dev, curve_data); + break; + default: + printk(KERN_ERR "%s: unknown curve data " + "revision %d\n", + wiphy_name(dev->wiphy), + curve_data->cal_method_rev); + err = -ENODEV; + break; + } + if (err) + goto err; + } + break; + case PDR_PRISM_ZIF_TX_IQ_CALIBRATION: + priv->iq_autocal = kmalloc(data_len, GFP_KERNEL); + if (!priv->iq_autocal) { + err = -ENOMEM; + goto err; + } + + memcpy(priv->iq_autocal, entry->data, data_len); + priv->iq_autocal_len = data_len / sizeof(struct pda_iq_autocal_entry); + break; + case PDR_DEFAULT_COUNTRY: + p54_parse_default_country(dev, entry->data, data_len); + break; + case PDR_INTERFACE_LIST: + tmp = entry->data; + while ((u8 *)tmp < entry->data + data_len) { + struct exp_if *exp_if = tmp; + if (exp_if->if_id == cpu_to_le16(IF_ID_ISL39000)) + synth = le16_to_cpu(exp_if->variant); + tmp += sizeof(*exp_if); + } + break; + case PDR_HARDWARE_PLATFORM_COMPONENT_ID: + if (data_len < 2) + break; + priv->version = *(u8 *)(entry->data + 1); + break; + case PDR_RSSI_LINEAR_APPROXIMATION: + case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND: + case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED: + p54_parse_rssical(dev, entry->data, data_len, + le16_to_cpu(entry->code)); + break; + case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM: { + __le16 *src = (void *) entry->data; + s16 *dst = (void *) &priv->rssical_db; + int i; + + if (data_len != sizeof(priv->rssical_db)) { + err = -EINVAL; + goto err; + } + for (i = 0; i < sizeof(priv->rssical_db) / + sizeof(*src); i++) + *(dst++) = (s16) le16_to_cpu(*(src++)); + } + break; + case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: { + struct pda_custom_wrapper *pda = (void *) entry->data; + if (priv->output_limit || data_len < sizeof(*pda)) + break; + priv->output_limit = p54_convert_db(pda, data_len); + } + break; + case PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM: { + struct pda_custom_wrapper *pda = (void *) entry->data; + if (priv->curve_data || data_len < sizeof(*pda)) + break; + priv->curve_data = p54_convert_db(pda, data_len); + } + break; + case PDR_END: + /* make it overrun */ + entry_len = len; + break; + default: + break; + } + + entry = (void *)entry + (entry_len + 1)*2; + } + + if (!synth || !priv->iq_autocal || !priv->output_limit || + !priv->curve_data) { + printk(KERN_ERR "%s: not all required entries found in eeprom!\n", + wiphy_name(dev->wiphy)); + err = -EINVAL; + goto err; + } + + err = p54_generate_channel_lists(dev); + if (err) + goto err; + + priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK; + if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW) + p54_init_xbow_synth(priv); + if (!(synth & PDR_SYNTH_24_GHZ_DISABLED)) + dev->wiphy->bands[IEEE80211_BAND_2GHZ] = + priv->band_table[IEEE80211_BAND_2GHZ]; + if (!(synth & PDR_SYNTH_5_GHZ_DISABLED)) + dev->wiphy->bands[IEEE80211_BAND_5GHZ] = + priv->band_table[IEEE80211_BAND_5GHZ]; + if ((synth & PDR_SYNTH_RX_DIV_MASK) == PDR_SYNTH_RX_DIV_SUPPORTED) + priv->rx_diversity_mask = 3; + if ((synth & PDR_SYNTH_TX_DIV_MASK) == PDR_SYNTH_TX_DIV_SUPPORTED) + priv->tx_diversity_mask = 3; + + if (!is_valid_ether_addr(dev->wiphy->perm_addr)) { + u8 perm_addr[ETH_ALEN]; + + printk(KERN_WARNING "%s: Invalid hwaddr! Using randomly generated MAC addr\n", + wiphy_name(dev->wiphy)); + random_ether_addr(perm_addr); + SET_IEEE80211_PERM_ADDR(dev, perm_addr); + } + + printk(KERN_INFO "%s: hwaddr %pM, MAC:isl38%02x RF:%s\n", + wiphy_name(dev->wiphy), dev->wiphy->perm_addr, priv->version, + p54_rf_chips[priv->rxhw]); + + return 0; + +err: + kfree(priv->iq_autocal); + kfree(priv->output_limit); + kfree(priv->curve_data); + priv->iq_autocal = NULL; + priv->output_limit = NULL; + priv->curve_data = NULL; + + printk(KERN_ERR "%s: eeprom parse failed!\n", + wiphy_name(dev->wiphy)); + return err; +} +EXPORT_SYMBOL_GPL(p54_parse_eeprom); + +int p54_read_eeprom(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + size_t eeprom_size = 0x2020, offset = 0, blocksize, maxblocksize; + int ret = -ENOMEM; + void *eeprom; + + maxblocksize = EEPROM_READBACK_LEN; + if (priv->fw_var >= 0x509) + maxblocksize -= 0xc; + else + maxblocksize -= 0x4; + + eeprom = kzalloc(eeprom_size, GFP_KERNEL); + if (unlikely(!eeprom)) + goto free; + + while (eeprom_size) { + blocksize = min(eeprom_size, maxblocksize); + ret = p54_download_eeprom(priv, (void *) (eeprom + offset), + offset, blocksize); + if (unlikely(ret)) + goto free; + + offset += blocksize; + eeprom_size -= blocksize; + } + + ret = p54_parse_eeprom(dev, eeprom, offset); +free: + kfree(eeprom); + return ret; +} +EXPORT_SYMBOL_GPL(p54_read_eeprom); diff --git a/drivers/net/wireless/p54/eeprom.h b/drivers/net/wireless/p54/eeprom.h new file mode 100644 index 000000000000..9051aef11249 --- /dev/null +++ b/drivers/net/wireless/p54/eeprom.h @@ -0,0 +1,226 @@ +/* + * eeprom specific definitions for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + * + * - LMAC API interface header file for STLC4560 (lmac_longbow.h) + * Copyright (C) 2007 Conexant Systems, Inc. + * + * - islmvc driver + * Copyright (C) 2001 Intersil Americas Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef EEPROM_H +#define EEPROM_H + +/* PDA defines are Copyright (C) 2005 Nokia Corporation (taken from islsm_pda.h) */ + +struct pda_entry { + __le16 len; /* includes both code and data */ + __le16 code; + u8 data[0]; +} __packed; + +struct eeprom_pda_wrap { + __le32 magic; + __le16 pad; + __le16 len; + __le32 arm_opcode; + u8 data[0]; +} __packed; + +struct p54_iq_autocal_entry { + __le16 iq_param[4]; +} __packed; + +struct pda_iq_autocal_entry { + __le16 freq; + struct p54_iq_autocal_entry params; +} __packed; + +struct pda_channel_output_limit { + __le16 freq; + u8 val_bpsk; + u8 val_qpsk; + u8 val_16qam; + u8 val_64qam; + u8 rate_set_mask; + u8 rate_set_size; +} __packed; + +struct pda_pa_curve_data_sample_rev0 { + u8 rf_power; + u8 pa_detector; + u8 pcv; +} __packed; + +struct pda_pa_curve_data_sample_rev1 { + u8 rf_power; + u8 pa_detector; + u8 data_barker; + u8 data_bpsk; + u8 data_qpsk; + u8 data_16qam; + u8 data_64qam; +} __packed; + +struct pda_pa_curve_data { + u8 cal_method_rev; + u8 channels; + u8 points_per_channel; + u8 padding; + u8 data[0]; +} __packed; + +struct pda_rssi_cal_entry { + __le16 mul; + __le16 add; +} __packed; + +struct pda_country { + u8 regdomain; + u8 alpha2[2]; + u8 flags; +} __packed; + +struct pda_antenna_gain { + struct { + u8 gain_5GHz; /* 0.25 dBi units */ + u8 gain_2GHz; /* 0.25 dBi units */ + } __packed antenna[0]; +} __packed; + +struct pda_custom_wrapper { + __le16 entries; + __le16 entry_size; + __le16 offset; + __le16 len; + u8 data[0]; +} __packed; + +/* + * this defines the PDR codes used to build PDAs as defined in document + * number 553155. The current implementation mirrors version 1.1 of the + * document and lists only PDRs supported by the ARM platform. + */ + +/* common and choice range (0x0000 - 0x0fff) */ +#define PDR_END 0x0000 +#define PDR_MANUFACTURING_PART_NUMBER 0x0001 +#define PDR_PDA_VERSION 0x0002 +#define PDR_NIC_SERIAL_NUMBER 0x0003 +#define PDR_NIC_RAM_SIZE 0x0005 +#define PDR_RFMODEM_SUP_RANGE 0x0006 +#define PDR_PRISM_MAC_SUP_RANGE 0x0007 +#define PDR_NIC_ID 0x0008 + +#define PDR_MAC_ADDRESS 0x0101 +#define PDR_REGULATORY_DOMAIN_LIST 0x0103 /* obsolete */ +#define PDR_ALLOWED_CHAN_SET 0x0104 +#define PDR_DEFAULT_CHAN 0x0105 +#define PDR_TEMPERATURE_TYPE 0x0107 + +#define PDR_IFR_SETTING 0x0200 +#define PDR_RFR_SETTING 0x0201 +#define PDR_3861_BASELINE_REG_SETTINGS 0x0202 +#define PDR_3861_SHADOW_REG_SETTINGS 0x0203 +#define PDR_3861_IFRF_REG_SETTINGS 0x0204 + +#define PDR_3861_CHAN_CALIB_SET_POINTS 0x0300 +#define PDR_3861_CHAN_CALIB_INTEGRATOR 0x0301 + +#define PDR_3842_PRISM_II_NIC_CONFIG 0x0400 +#define PDR_PRISM_USB_ID 0x0401 +#define PDR_PRISM_PCI_ID 0x0402 +#define PDR_PRISM_PCI_IF_CONFIG 0x0403 +#define PDR_PRISM_PCI_PM_CONFIG 0x0404 + +#define PDR_3861_MF_TEST_CHAN_SET_POINTS 0x0900 +#define PDR_3861_MF_TEST_CHAN_INTEGRATORS 0x0901 + +/* ARM range (0x1000 - 0x1fff) */ +#define PDR_COUNTRY_INFORMATION 0x1000 /* obsolete */ +#define PDR_INTERFACE_LIST 0x1001 +#define PDR_HARDWARE_PLATFORM_COMPONENT_ID 0x1002 +#define PDR_OEM_NAME 0x1003 +#define PDR_PRODUCT_NAME 0x1004 +#define PDR_UTF8_OEM_NAME 0x1005 +#define PDR_UTF8_PRODUCT_NAME 0x1006 +#define PDR_COUNTRY_LIST 0x1007 +#define PDR_DEFAULT_COUNTRY 0x1008 + +#define PDR_ANTENNA_GAIN 0x1100 + +#define PDR_PRISM_INDIGO_PA_CALIBRATION_DATA 0x1901 +#define PDR_RSSI_LINEAR_APPROXIMATION 0x1902 +#define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS 0x1903 +#define PDR_PRISM_PA_CAL_CURVE_DATA 0x1904 +#define PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND 0x1905 +#define PDR_PRISM_ZIF_TX_IQ_CALIBRATION 0x1906 +#define PDR_REGULATORY_POWER_LIMITS 0x1907 +#define PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED 0x1908 +#define PDR_RADIATED_TRANSMISSION_CORRECTION 0x1909 +#define PDR_PRISM_TX_IQ_CALIBRATION 0x190a + +/* reserved range (0x2000 - 0x7fff) */ + +/* customer range (0x8000 - 0xffff) */ +#define PDR_BASEBAND_REGISTERS 0x8000 +#define PDR_PER_CHANNEL_BASEBAND_REGISTERS 0x8001 + +/* used by our modificated eeprom image */ +#define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM 0xDEAD +#define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM 0xBEEF +#define PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM 0xB05D + +/* Interface Definitions */ +#define PDR_INTERFACE_ROLE_SERVER 0x0000 +#define PDR_INTERFACE_ROLE_CLIENT 0x0001 + +/* PDR definitions for default country & country list */ +#define PDR_COUNTRY_CERT_CODE 0x80 +#define PDR_COUNTRY_CERT_CODE_REAL 0x00 +#define PDR_COUNTRY_CERT_CODE_PSEUDO 0x80 +#define PDR_COUNTRY_CERT_BAND 0x40 +#define PDR_COUNTRY_CERT_BAND_2GHZ 0x00 +#define PDR_COUNTRY_CERT_BAND_5GHZ 0x40 +#define PDR_COUNTRY_CERT_IODOOR 0x30 +#define PDR_COUNTRY_CERT_IODOOR_BOTH 0x00 +#define PDR_COUNTRY_CERT_IODOOR_INDOOR 0x20 +#define PDR_COUNTRY_CERT_IODOOR_OUTDOOR 0x30 +#define PDR_COUNTRY_CERT_INDEX 0x0f + +/* Specific LMAC FW/HW variant definitions */ +#define PDR_SYNTH_FRONTEND_MASK 0x0007 +#define PDR_SYNTH_FRONTEND_DUETTE3 0x0001 +#define PDR_SYNTH_FRONTEND_DUETTE2 0x0002 +#define PDR_SYNTH_FRONTEND_FRISBEE 0x0003 +#define PDR_SYNTH_FRONTEND_XBOW 0x0004 +#define PDR_SYNTH_FRONTEND_LONGBOW 0x0005 +#define PDR_SYNTH_IQ_CAL_MASK 0x0018 +#define PDR_SYNTH_IQ_CAL_PA_DETECTOR 0x0000 +#define PDR_SYNTH_IQ_CAL_DISABLED 0x0008 +#define PDR_SYNTH_IQ_CAL_ZIF 0x0010 +#define PDR_SYNTH_FAA_SWITCH_MASK 0x0020 +#define PDR_SYNTH_FAA_SWITCH_ENABLED 0x0020 +#define PDR_SYNTH_24_GHZ_MASK 0x0040 +#define PDR_SYNTH_24_GHZ_DISABLED 0x0040 +#define PDR_SYNTH_5_GHZ_MASK 0x0080 +#define PDR_SYNTH_5_GHZ_DISABLED 0x0080 +#define PDR_SYNTH_RX_DIV_MASK 0x0100 +#define PDR_SYNTH_RX_DIV_SUPPORTED 0x0100 +#define PDR_SYNTH_TX_DIV_MASK 0x0200 +#define PDR_SYNTH_TX_DIV_SUPPORTED 0x0200 +#define PDR_SYNTH_ASM_MASK 0x0400 +#define PDR_SYNTH_ASM_XSWON 0x0400 + +#endif /* EEPROM_H */ diff --git a/drivers/net/wireless/p54/fwio.c b/drivers/net/wireless/p54/fwio.c new file mode 100644 index 000000000000..e7b9e9cb39f5 --- /dev/null +++ b/drivers/net/wireless/p54/fwio.c @@ -0,0 +1,716 @@ +/* + * Firmware I/O code for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/firmware.h> +#include <linux/etherdevice.h> + +#include <net/mac80211.h> + +#include "p54.h" +#include "eeprom.h" +#include "lmac.h" + +int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw) +{ + struct p54_common *priv = dev->priv; + struct exp_if *exp_if; + struct bootrec *bootrec; + u32 *data = (u32 *)fw->data; + u32 *end_data = (u32 *)fw->data + (fw->size >> 2); + u8 *fw_version = NULL; + size_t len; + int i; + int maxlen; + + if (priv->rx_start) + return 0; + + while (data < end_data && *data) + data++; + + while (data < end_data && !*data) + data++; + + bootrec = (struct bootrec *) data; + + while (bootrec->data <= end_data && (bootrec->data + + (len = le32_to_cpu(bootrec->len))) <= end_data) { + u32 code = le32_to_cpu(bootrec->code); + switch (code) { + case BR_CODE_COMPONENT_ID: + priv->fw_interface = be32_to_cpup((__be32 *) + bootrec->data); + switch (priv->fw_interface) { + case FW_LM86: + case FW_LM20: + case FW_LM87: { + char *iftype = (char *)bootrec->data; + printk(KERN_INFO "%s: p54 detected a LM%c%c " + "firmware\n", + wiphy_name(priv->hw->wiphy), + iftype[2], iftype[3]); + break; + } + case FW_FMAC: + default: + printk(KERN_ERR "%s: unsupported firmware\n", + wiphy_name(priv->hw->wiphy)); + return -ENODEV; + } + break; + case BR_CODE_COMPONENT_VERSION: + /* 24 bytes should be enough for all firmwares */ + if (strnlen((unsigned char *) bootrec->data, 24) < 24) + fw_version = (unsigned char *) bootrec->data; + break; + case BR_CODE_DESCR: { + struct bootrec_desc *desc = + (struct bootrec_desc *)bootrec->data; + priv->rx_start = le32_to_cpu(desc->rx_start); + /* FIXME add sanity checking */ + priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500; + priv->headroom = desc->headroom; + priv->tailroom = desc->tailroom; + priv->privacy_caps = desc->privacy_caps; + priv->rx_keycache_size = desc->rx_keycache_size; + if (le32_to_cpu(bootrec->len) == 11) + priv->rx_mtu = le16_to_cpu(desc->rx_mtu); + else + priv->rx_mtu = (size_t) + 0x620 - priv->tx_hdr_len; + maxlen = priv->tx_hdr_len + /* USB devices */ + sizeof(struct p54_rx_data) + + 4 + /* rx alignment */ + IEEE80211_MAX_FRAG_THRESHOLD; + if (priv->rx_mtu > maxlen && PAGE_SIZE == 4096) { + printk(KERN_INFO "p54: rx_mtu reduced from %d " + "to %d\n", priv->rx_mtu, maxlen); + priv->rx_mtu = maxlen; + } + break; + } + case BR_CODE_EXPOSED_IF: + exp_if = (struct exp_if *) bootrec->data; + for (i = 0; i < (len * sizeof(*exp_if) / 4); i++) + if (exp_if[i].if_id == cpu_to_le16(IF_ID_LMAC)) + priv->fw_var = le16_to_cpu(exp_if[i].variant); + break; + case BR_CODE_DEPENDENT_IF: + break; + case BR_CODE_END_OF_BRA: + case LEGACY_BR_CODE_END_OF_BRA: + end_data = NULL; + break; + default: + break; + } + bootrec = (struct bootrec *)&bootrec->data[len]; + } + + if (fw_version) + printk(KERN_INFO "%s: FW rev %s - Softmac protocol %x.%x\n", + wiphy_name(priv->hw->wiphy), fw_version, + priv->fw_var >> 8, priv->fw_var & 0xff); + + if (priv->fw_var < 0x500) + printk(KERN_INFO "%s: you are using an obsolete firmware. " + "visit http://wireless.kernel.org/en/users/Drivers/p54 " + "and grab one for \"kernel >= 2.6.28\"!\n", + wiphy_name(priv->hw->wiphy)); + + if (priv->fw_var >= 0x300) { + /* Firmware supports QoS, use it! */ + + if (priv->fw_var >= 0x500) { + priv->tx_stats[P54_QUEUE_AC_VO].limit = 16; + priv->tx_stats[P54_QUEUE_AC_VI].limit = 16; + priv->tx_stats[P54_QUEUE_AC_BE].limit = 16; + priv->tx_stats[P54_QUEUE_AC_BK].limit = 16; + } else { + priv->tx_stats[P54_QUEUE_AC_VO].limit = 3; + priv->tx_stats[P54_QUEUE_AC_VI].limit = 4; + priv->tx_stats[P54_QUEUE_AC_BE].limit = 3; + priv->tx_stats[P54_QUEUE_AC_BK].limit = 2; + } + priv->hw->queues = P54_QUEUE_AC_NUM; + } + + printk(KERN_INFO "%s: cryptographic accelerator " + "WEP:%s, TKIP:%s, CCMP:%s\n", wiphy_name(priv->hw->wiphy), + (priv->privacy_caps & BR_DESC_PRIV_CAP_WEP) ? "YES" : + "no", (priv->privacy_caps & (BR_DESC_PRIV_CAP_TKIP | + BR_DESC_PRIV_CAP_MICHAEL)) ? "YES" : "no", + (priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP) ? + "YES" : "no"); + + if (priv->rx_keycache_size) { + /* + * NOTE: + * + * The firmware provides at most 255 (0 - 254) slots + * for keys which are then used to offload decryption. + * As a result the 255 entry (aka 0xff) can be used + * safely by the driver to mark keys that didn't fit + * into the full cache. This trick saves us from + * keeping a extra list for uploaded keys. + */ + + priv->used_rxkeys = kzalloc(BITS_TO_LONGS( + priv->rx_keycache_size), GFP_KERNEL); + + if (!priv->used_rxkeys) + return -ENOMEM; + } + + return 0; +} +EXPORT_SYMBOL_GPL(p54_parse_firmware); + +static struct sk_buff *p54_alloc_skb(struct p54_common *priv, u16 hdr_flags, + u16 payload_len, u16 type, gfp_t memflags) +{ + struct p54_hdr *hdr; + struct sk_buff *skb; + size_t frame_len = sizeof(*hdr) + payload_len; + + if (frame_len > P54_MAX_CTRL_FRAME_LEN) + return NULL; + + if (unlikely(skb_queue_len(&priv->tx_pending) > 64)) + return NULL; + + skb = __dev_alloc_skb(priv->tx_hdr_len + frame_len, memflags); + if (!skb) + return NULL; + skb_reserve(skb, priv->tx_hdr_len); + + hdr = (struct p54_hdr *) skb_put(skb, sizeof(*hdr)); + hdr->flags = cpu_to_le16(hdr_flags); + hdr->len = cpu_to_le16(payload_len); + hdr->type = cpu_to_le16(type); + hdr->tries = hdr->rts_tries = 0; + return skb; +} + +int p54_download_eeprom(struct p54_common *priv, void *buf, + u16 offset, u16 len) +{ + struct p54_eeprom_lm86 *eeprom_hdr; + struct sk_buff *skb; + size_t eeprom_hdr_size; + int ret = 0; + + if (priv->fw_var >= 0x509) + eeprom_hdr_size = sizeof(*eeprom_hdr); + else + eeprom_hdr_size = 0x4; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, eeprom_hdr_size + + len, P54_CONTROL_TYPE_EEPROM_READBACK, + GFP_KERNEL); + if (unlikely(!skb)) + return -ENOMEM; + + mutex_lock(&priv->eeprom_mutex); + priv->eeprom = buf; + eeprom_hdr = (struct p54_eeprom_lm86 *) skb_put(skb, + eeprom_hdr_size + len); + + if (priv->fw_var < 0x509) { + eeprom_hdr->v1.offset = cpu_to_le16(offset); + eeprom_hdr->v1.len = cpu_to_le16(len); + } else { + eeprom_hdr->v2.offset = cpu_to_le32(offset); + eeprom_hdr->v2.len = cpu_to_le16(len); + eeprom_hdr->v2.magic2 = 0xf; + memcpy(eeprom_hdr->v2.magic, (const char *)"LOCK", 4); + } + + p54_tx(priv, skb); + + if (!wait_for_completion_interruptible_timeout( + &priv->eeprom_comp, HZ)) { + printk(KERN_ERR "%s: device does not respond!\n", + wiphy_name(priv->hw->wiphy)); + ret = -EBUSY; + } + priv->eeprom = NULL; + mutex_unlock(&priv->eeprom_mutex); + return ret; +} + +int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set) +{ + struct sk_buff *skb; + struct p54_tim *tim; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*tim), + P54_CONTROL_TYPE_TIM, GFP_ATOMIC); + if (unlikely(!skb)) + return -ENOMEM; + + tim = (struct p54_tim *) skb_put(skb, sizeof(*tim)); + tim->count = 1; + tim->entry[0] = cpu_to_le16(set ? (aid | 0x8000) : aid); + p54_tx(priv, skb); + return 0; +} + +int p54_sta_unlock(struct p54_common *priv, u8 *addr) +{ + struct sk_buff *skb; + struct p54_sta_unlock *sta; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*sta), + P54_CONTROL_TYPE_PSM_STA_UNLOCK, GFP_ATOMIC); + if (unlikely(!skb)) + return -ENOMEM; + + sta = (struct p54_sta_unlock *)skb_put(skb, sizeof(*sta)); + memcpy(sta->addr, addr, ETH_ALEN); + p54_tx(priv, skb); + return 0; +} + +int p54_tx_cancel(struct p54_common *priv, __le32 req_id) +{ + struct sk_buff *skb; + struct p54_txcancel *cancel; + u32 _req_id = le32_to_cpu(req_id); + + if (unlikely(_req_id < priv->rx_start || _req_id > priv->rx_end)) + return -EINVAL; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*cancel), + P54_CONTROL_TYPE_TXCANCEL, GFP_ATOMIC); + if (unlikely(!skb)) + return -ENOMEM; + + cancel = (struct p54_txcancel *)skb_put(skb, sizeof(*cancel)); + cancel->req_id = req_id; + p54_tx(priv, skb); + return 0; +} + +int p54_setup_mac(struct p54_common *priv) +{ + struct sk_buff *skb; + struct p54_setup_mac *setup; + u16 mode; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*setup), + P54_CONTROL_TYPE_SETUP, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + setup = (struct p54_setup_mac *) skb_put(skb, sizeof(*setup)); + if (!(priv->hw->conf.flags & IEEE80211_CONF_IDLE)) { + switch (priv->mode) { + case NL80211_IFTYPE_STATION: + mode = P54_FILTER_TYPE_STATION; + break; + case NL80211_IFTYPE_AP: + mode = P54_FILTER_TYPE_AP; + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + mode = P54_FILTER_TYPE_IBSS; + break; + case NL80211_IFTYPE_MONITOR: + mode = P54_FILTER_TYPE_PROMISCUOUS; + break; + default: + mode = P54_FILTER_TYPE_HIBERNATE; + break; + } + + /* + * "TRANSPARENT and PROMISCUOUS are mutually exclusive" + * STSW45X0C LMAC API - page 12 + */ + if (((priv->filter_flags & FIF_PROMISC_IN_BSS) || + (priv->filter_flags & FIF_OTHER_BSS)) && + (mode != P54_FILTER_TYPE_PROMISCUOUS)) + mode |= P54_FILTER_TYPE_TRANSPARENT; + } else { + mode = P54_FILTER_TYPE_HIBERNATE; + } + + setup->mac_mode = cpu_to_le16(mode); + memcpy(setup->mac_addr, priv->mac_addr, ETH_ALEN); + memcpy(setup->bssid, priv->bssid, ETH_ALEN); + setup->rx_antenna = 2 & priv->rx_diversity_mask; /* automatic */ + setup->rx_align = 0; + if (priv->fw_var < 0x500) { + setup->v1.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); + memset(setup->v1.rts_rates, 0, 8); + setup->v1.rx_addr = cpu_to_le32(priv->rx_end); + setup->v1.max_rx = cpu_to_le16(priv->rx_mtu); + setup->v1.rxhw = cpu_to_le16(priv->rxhw); + setup->v1.wakeup_timer = cpu_to_le16(priv->wakeup_timer); + setup->v1.unalloc0 = cpu_to_le16(0); + } else { + setup->v2.rx_addr = cpu_to_le32(priv->rx_end); + setup->v2.max_rx = cpu_to_le16(priv->rx_mtu); + setup->v2.rxhw = cpu_to_le16(priv->rxhw); + setup->v2.timer = cpu_to_le16(priv->wakeup_timer); + setup->v2.truncate = cpu_to_le16(48896); + setup->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); + setup->v2.sbss_offset = 0; + setup->v2.mcast_window = 0; + setup->v2.rx_rssi_threshold = 0; + setup->v2.rx_ed_threshold = 0; + setup->v2.ref_clock = cpu_to_le32(644245094); + setup->v2.lpf_bandwidth = cpu_to_le16(65535); + setup->v2.osc_start_delay = cpu_to_le16(65535); + } + p54_tx(priv, skb); + return 0; +} + +int p54_scan(struct p54_common *priv, u16 mode, u16 dwell) +{ + struct sk_buff *skb; + struct p54_hdr *hdr; + struct p54_scan_head *head; + struct p54_iq_autocal_entry *iq_autocal; + union p54_scan_body_union *body; + struct p54_scan_tail_rate *rate; + struct pda_rssi_cal_entry *rssi; + unsigned int i; + void *entry; + int band = priv->hw->conf.channel->band; + __le16 freq = cpu_to_le16(priv->hw->conf.channel->center_freq); + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*head) + + 2 + sizeof(*iq_autocal) + sizeof(*body) + + sizeof(*rate) + 2 * sizeof(*rssi), + P54_CONTROL_TYPE_SCAN, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + head = (struct p54_scan_head *) skb_put(skb, sizeof(*head)); + memset(head->scan_params, 0, sizeof(head->scan_params)); + head->mode = cpu_to_le16(mode); + head->dwell = cpu_to_le16(dwell); + head->freq = freq; + + if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { + __le16 *pa_power_points = (__le16 *) skb_put(skb, 2); + *pa_power_points = cpu_to_le16(0x0c); + } + + iq_autocal = (void *) skb_put(skb, sizeof(*iq_autocal)); + for (i = 0; i < priv->iq_autocal_len; i++) { + if (priv->iq_autocal[i].freq != freq) + continue; + + memcpy(iq_autocal, &priv->iq_autocal[i].params, + sizeof(struct p54_iq_autocal_entry)); + break; + } + if (i == priv->iq_autocal_len) + goto err; + + if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) + body = (void *) skb_put(skb, sizeof(body->longbow)); + else + body = (void *) skb_put(skb, sizeof(body->normal)); + + for (i = 0; i < priv->output_limit->entries; i++) { + __le16 *entry_freq = (void *) (priv->output_limit->data + + priv->output_limit->entry_size * i); + + if (*entry_freq != freq) + continue; + + if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { + memcpy(&body->longbow.power_limits, + (void *) entry_freq + sizeof(__le16), + priv->output_limit->entry_size); + } else { + struct pda_channel_output_limit *limits = + (void *) entry_freq; + + body->normal.val_barker = 0x38; + body->normal.val_bpsk = body->normal.dup_bpsk = + limits->val_bpsk; + body->normal.val_qpsk = body->normal.dup_qpsk = + limits->val_qpsk; + body->normal.val_16qam = body->normal.dup_16qam = + limits->val_16qam; + body->normal.val_64qam = body->normal.dup_64qam = + limits->val_64qam; + } + break; + } + if (i == priv->output_limit->entries) + goto err; + + entry = (void *)(priv->curve_data->data + priv->curve_data->offset); + for (i = 0; i < priv->curve_data->entries; i++) { + if (*((__le16 *)entry) != freq) { + entry += priv->curve_data->entry_size; + continue; + } + + if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { + memcpy(&body->longbow.curve_data, + (void *) entry + sizeof(__le16), + priv->curve_data->entry_size); + } else { + struct p54_scan_body *chan = &body->normal; + struct pda_pa_curve_data *curve_data = + (void *) priv->curve_data->data; + + entry += sizeof(__le16); + chan->pa_points_per_curve = 8; + memset(chan->curve_data, 0, sizeof(*chan->curve_data)); + memcpy(chan->curve_data, entry, + sizeof(struct p54_pa_curve_data_sample) * + min((u8)8, curve_data->points_per_channel)); + } + break; + } + if (i == priv->curve_data->entries) + goto err; + + if ((priv->fw_var >= 0x500) && (priv->fw_var < 0x509)) { + rate = (void *) skb_put(skb, sizeof(*rate)); + rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); + for (i = 0; i < sizeof(rate->rts_rates); i++) + rate->rts_rates[i] = i; + } + + rssi = (struct pda_rssi_cal_entry *) skb_put(skb, sizeof(*rssi)); + rssi->mul = cpu_to_le16(priv->rssical_db[band].mul); + rssi->add = cpu_to_le16(priv->rssical_db[band].add); + if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { + /* Longbow frontend needs ever more */ + rssi = (void *) skb_put(skb, sizeof(*rssi)); + rssi->mul = cpu_to_le16(priv->rssical_db[band].longbow_unkn); + rssi->add = cpu_to_le16(priv->rssical_db[band].longbow_unk2); + } + + if (priv->fw_var >= 0x509) { + rate = (void *) skb_put(skb, sizeof(*rate)); + rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); + for (i = 0; i < sizeof(rate->rts_rates); i++) + rate->rts_rates[i] = i; + } + + hdr = (struct p54_hdr *) skb->data; + hdr->len = cpu_to_le16(skb->len - sizeof(*hdr)); + + p54_tx(priv, skb); + return 0; + +err: + printk(KERN_ERR "%s: frequency change to channel %d failed.\n", + wiphy_name(priv->hw->wiphy), ieee80211_frequency_to_channel( + priv->hw->conf.channel->center_freq)); + + dev_kfree_skb_any(skb); + return -EINVAL; +} + +int p54_set_leds(struct p54_common *priv) +{ + struct sk_buff *skb; + struct p54_led *led; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*led), + P54_CONTROL_TYPE_LED, GFP_ATOMIC); + if (unlikely(!skb)) + return -ENOMEM; + + led = (struct p54_led *) skb_put(skb, sizeof(*led)); + led->flags = cpu_to_le16(0x0003); + led->mask[0] = led->mask[1] = cpu_to_le16(priv->softled_state); + led->delay[0] = cpu_to_le16(1); + led->delay[1] = cpu_to_le16(0); + p54_tx(priv, skb); + return 0; +} + +int p54_set_edcf(struct p54_common *priv) +{ + struct sk_buff *skb; + struct p54_edcf *edcf; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*edcf), + P54_CONTROL_TYPE_DCFINIT, GFP_ATOMIC); + if (unlikely(!skb)) + return -ENOMEM; + + edcf = (struct p54_edcf *)skb_put(skb, sizeof(*edcf)); + if (priv->use_short_slot) { + edcf->slottime = 9; + edcf->sifs = 0x10; + edcf->eofpad = 0x00; + } else { + edcf->slottime = 20; + edcf->sifs = 0x0a; + edcf->eofpad = 0x06; + } + /* (see prism54/isl_oid.h for further details) */ + edcf->frameburst = cpu_to_le16(0); + edcf->round_trip_delay = cpu_to_le16(0); + edcf->flags = 0; + memset(edcf->mapping, 0, sizeof(edcf->mapping)); + memcpy(edcf->queue, priv->qos_params, sizeof(edcf->queue)); + p54_tx(priv, skb); + return 0; +} + +int p54_set_ps(struct p54_common *priv) +{ + struct sk_buff *skb; + struct p54_psm *psm; + unsigned int i; + u16 mode; + + if (priv->hw->conf.flags & IEEE80211_CONF_PS && + !priv->powersave_override) + mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM | + P54_PSM_CHECKSUM | P54_PSM_MCBC; + else + mode = P54_PSM_CAM; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*psm), + P54_CONTROL_TYPE_PSM, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + psm = (struct p54_psm *)skb_put(skb, sizeof(*psm)); + psm->mode = cpu_to_le16(mode); + psm->aid = cpu_to_le16(priv->aid); + for (i = 0; i < ARRAY_SIZE(psm->intervals); i++) { + psm->intervals[i].interval = + cpu_to_le16(priv->hw->conf.listen_interval); + psm->intervals[i].periods = cpu_to_le16(1); + } + + psm->beacon_rssi_skip_max = 200; + psm->rssi_delta_threshold = 0; + psm->nr = 1; + psm->exclude[0] = WLAN_EID_TIM; + + p54_tx(priv, skb); + return 0; +} + +int p54_init_xbow_synth(struct p54_common *priv) +{ + struct sk_buff *skb; + struct p54_xbow_synth *xbow; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*xbow), + P54_CONTROL_TYPE_XBOW_SYNTH_CFG, GFP_KERNEL); + if (unlikely(!skb)) + return -ENOMEM; + + xbow = (struct p54_xbow_synth *)skb_put(skb, sizeof(*xbow)); + xbow->magic1 = cpu_to_le16(0x1); + xbow->magic2 = cpu_to_le16(0x2); + xbow->freq = cpu_to_le16(5390); + memset(xbow->padding, 0, sizeof(xbow->padding)); + p54_tx(priv, skb); + return 0; +} + +int p54_upload_key(struct p54_common *priv, u8 algo, int slot, u8 idx, u8 len, + u8 *addr, u8* key) +{ + struct sk_buff *skb; + struct p54_keycache *rxkey; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey), + P54_CONTROL_TYPE_RX_KEYCACHE, GFP_KERNEL); + if (unlikely(!skb)) + return -ENOMEM; + + rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey)); + rxkey->entry = slot; + rxkey->key_id = idx; + rxkey->key_type = algo; + if (addr) + memcpy(rxkey->mac, addr, ETH_ALEN); + else + memset(rxkey->mac, ~0, ETH_ALEN); + + switch (algo) { + case P54_CRYPTO_WEP: + case P54_CRYPTO_AESCCMP: + rxkey->key_len = min_t(u8, 16, len); + memcpy(rxkey->key, key, rxkey->key_len); + break; + + case P54_CRYPTO_TKIPMICHAEL: + rxkey->key_len = 24; + memcpy(rxkey->key, key, 16); + memcpy(&(rxkey->key[16]), &(key + [NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8); + break; + + case P54_CRYPTO_NONE: + rxkey->key_len = 0; + memset(rxkey->key, 0, sizeof(rxkey->key)); + break; + + default: + printk(KERN_ERR "%s: invalid cryptographic algorithm: %d\n", + wiphy_name(priv->hw->wiphy), algo); + dev_kfree_skb(skb); + return -EINVAL; + } + + p54_tx(priv, skb); + return 0; +} + +int p54_fetch_statistics(struct p54_common *priv) +{ + struct ieee80211_tx_info *txinfo; + struct p54_tx_info *p54info; + struct sk_buff *skb; + + skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL, + sizeof(struct p54_statistics), + P54_CONTROL_TYPE_STAT_READBACK, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + /* + * The statistic feedback causes some extra headaches here, if it + * is not to crash/corrupt the firmware data structures. + * + * Unlike all other Control Get OIDs we can not use helpers like + * skb_put to reserve the space for the data we're requesting. + * Instead the extra frame length -which will hold the results later- + * will only be told to the p54_assign_address, so that following + * frames won't be placed into the allegedly empty area. + */ + txinfo = IEEE80211_SKB_CB(skb); + p54info = (void *) txinfo->rate_driver_data; + p54info->extra_len = sizeof(struct p54_statistics); + + p54_tx(priv, skb); + return 0; +} diff --git a/drivers/net/wireless/p54/led.c b/drivers/net/wireless/p54/led.c new file mode 100644 index 000000000000..9575ac033630 --- /dev/null +++ b/drivers/net/wireless/p54/led.c @@ -0,0 +1,162 @@ +/* + * Common code for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/firmware.h> +#include <linux/etherdevice.h> + +#include <net/mac80211.h> +#ifdef CONFIG_P54_LEDS +#include <linux/leds.h> +#endif /* CONFIG_P54_LEDS */ + +#include "p54.h" +#include "lmac.h" + +static void p54_update_leds(struct work_struct *work) +{ + struct p54_common *priv = container_of(work, struct p54_common, + led_work.work); + int err, i, tmp, blink_delay = 400; + bool rerun = false; + + /* Don't toggle the LED, when the device is down. */ + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + return ; + + for (i = 0; i < ARRAY_SIZE(priv->leds); i++) + if (priv->leds[i].toggled) { + priv->softled_state |= BIT(i); + + tmp = 70 + 200 / (priv->leds[i].toggled); + if (tmp < blink_delay) + blink_delay = tmp; + + if (priv->leds[i].led_dev.brightness == LED_OFF) + rerun = true; + + priv->leds[i].toggled = + !!priv->leds[i].led_dev.brightness; + } else + priv->softled_state &= ~BIT(i); + + err = p54_set_leds(priv); + if (err && net_ratelimit()) + printk(KERN_ERR "%s: failed to update LEDs (%d).\n", + wiphy_name(priv->hw->wiphy), err); + + if (rerun) + ieee80211_queue_delayed_work(priv->hw, &priv->led_work, + msecs_to_jiffies(blink_delay)); +} + +static void p54_led_brightness_set(struct led_classdev *led_dev, + enum led_brightness brightness) +{ + struct p54_led_dev *led = container_of(led_dev, struct p54_led_dev, + led_dev); + struct ieee80211_hw *dev = led->hw_dev; + struct p54_common *priv = dev->priv; + + if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) + return ; + + if ((brightness) && (led->registered)) { + led->toggled++; + ieee80211_queue_delayed_work(priv->hw, &priv->led_work, HZ/10); + } +} + +static int p54_register_led(struct p54_common *priv, + unsigned int led_index, + char *name, char *trigger) +{ + struct p54_led_dev *led = &priv->leds[led_index]; + int err; + + if (led->registered) + return -EEXIST; + + snprintf(led->name, sizeof(led->name), "p54-%s::%s", + wiphy_name(priv->hw->wiphy), name); + led->hw_dev = priv->hw; + led->index = led_index; + led->led_dev.name = led->name; + led->led_dev.default_trigger = trigger; + led->led_dev.brightness_set = p54_led_brightness_set; + + err = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_dev); + if (err) + printk(KERN_ERR "%s: Failed to register %s LED.\n", + wiphy_name(priv->hw->wiphy), name); + else + led->registered = 1; + + return err; +} + +int p54_init_leds(struct p54_common *priv) +{ + int err; + + /* + * TODO: + * Figure out if the EEPROM contains some hints about the number + * of available/programmable LEDs of the device. + */ + + INIT_DELAYED_WORK(&priv->led_work, p54_update_leds); + + err = p54_register_led(priv, 0, "assoc", + ieee80211_get_assoc_led_name(priv->hw)); + if (err) + return err; + + err = p54_register_led(priv, 1, "tx", + ieee80211_get_tx_led_name(priv->hw)); + if (err) + return err; + + err = p54_register_led(priv, 2, "rx", + ieee80211_get_rx_led_name(priv->hw)); + if (err) + return err; + + err = p54_register_led(priv, 3, "radio", + ieee80211_get_radio_led_name(priv->hw)); + if (err) + return err; + + err = p54_set_leds(priv); + return err; +} + +void p54_unregister_leds(struct p54_common *priv) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(priv->leds); i++) { + if (priv->leds[i].registered) { + priv->leds[i].registered = false; + priv->leds[i].toggled = 0; + led_classdev_unregister(&priv->leds[i].led_dev); + } + } + + cancel_delayed_work_sync(&priv->led_work); +} diff --git a/drivers/net/wireless/p54/lmac.h b/drivers/net/wireless/p54/lmac.h new file mode 100644 index 000000000000..04b63ec80fa4 --- /dev/null +++ b/drivers/net/wireless/p54/lmac.h @@ -0,0 +1,558 @@ +/* + * LMAC Interface specific definitions for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * Copyright (c) 2007 - 2009, Christian Lamparter <chunkeey@web.de> + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + * + * - LMAC API interface header file for STLC4560 (lmac_longbow.h) + * Copyright (C) 2007 Conexant Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef LMAC_H +#define LMAC_H + +enum p54_control_frame_types { + P54_CONTROL_TYPE_SETUP = 0, + P54_CONTROL_TYPE_SCAN, + P54_CONTROL_TYPE_TRAP, + P54_CONTROL_TYPE_DCFINIT, + P54_CONTROL_TYPE_RX_KEYCACHE, + P54_CONTROL_TYPE_TIM, + P54_CONTROL_TYPE_PSM, + P54_CONTROL_TYPE_TXCANCEL, + P54_CONTROL_TYPE_TXDONE, + P54_CONTROL_TYPE_BURST, + P54_CONTROL_TYPE_STAT_READBACK, + P54_CONTROL_TYPE_BBP, + P54_CONTROL_TYPE_EEPROM_READBACK, + P54_CONTROL_TYPE_LED, + P54_CONTROL_TYPE_GPIO, + P54_CONTROL_TYPE_TIMER, + P54_CONTROL_TYPE_MODULATION, + P54_CONTROL_TYPE_SYNTH_CONFIG, + P54_CONTROL_TYPE_DETECTOR_VALUE, + P54_CONTROL_TYPE_XBOW_SYNTH_CFG, + P54_CONTROL_TYPE_CCE_QUIET, + P54_CONTROL_TYPE_PSM_STA_UNLOCK, + P54_CONTROL_TYPE_PCS, + P54_CONTROL_TYPE_BT_BALANCER = 28, + P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE = 30, + P54_CONTROL_TYPE_ARPTABLE = 31, + P54_CONTROL_TYPE_BT_OPTIONS = 35, +}; + +#define P54_HDR_FLAG_CONTROL BIT(15) +#define P54_HDR_FLAG_CONTROL_OPSET (BIT(15) + BIT(0)) +#define P54_HDR_FLAG_DATA_ALIGN BIT(14) + +#define P54_HDR_FLAG_DATA_OUT_PROMISC BIT(0) +#define P54_HDR_FLAG_DATA_OUT_TIMESTAMP BIT(1) +#define P54_HDR_FLAG_DATA_OUT_SEQNR BIT(2) +#define P54_HDR_FLAG_DATA_OUT_BIT3 BIT(3) +#define P54_HDR_FLAG_DATA_OUT_BURST BIT(4) +#define P54_HDR_FLAG_DATA_OUT_NOCANCEL BIT(5) +#define P54_HDR_FLAG_DATA_OUT_CLEARTIM BIT(6) +#define P54_HDR_FLAG_DATA_OUT_HITCHHIKE BIT(7) +#define P54_HDR_FLAG_DATA_OUT_COMPRESS BIT(8) +#define P54_HDR_FLAG_DATA_OUT_CONCAT BIT(9) +#define P54_HDR_FLAG_DATA_OUT_PCS_ACCEPT BIT(10) +#define P54_HDR_FLAG_DATA_OUT_WAITEOSP BIT(11) + +#define P54_HDR_FLAG_DATA_IN_FCS_GOOD BIT(0) +#define P54_HDR_FLAG_DATA_IN_MATCH_MAC BIT(1) +#define P54_HDR_FLAG_DATA_IN_MCBC BIT(2) +#define P54_HDR_FLAG_DATA_IN_BEACON BIT(3) +#define P54_HDR_FLAG_DATA_IN_MATCH_BSS BIT(4) +#define P54_HDR_FLAG_DATA_IN_BCAST_BSS BIT(5) +#define P54_HDR_FLAG_DATA_IN_DATA BIT(6) +#define P54_HDR_FLAG_DATA_IN_TRUNCATED BIT(7) +#define P54_HDR_FLAG_DATA_IN_BIT8 BIT(8) +#define P54_HDR_FLAG_DATA_IN_TRANSPARENT BIT(9) + +struct p54_hdr { + __le16 flags; + __le16 len; + __le32 req_id; + __le16 type; /* enum p54_control_frame_types */ + u8 rts_tries; + u8 tries; + u8 data[0]; +} __packed; + +#define GET_REQ_ID(skb) \ + (((struct p54_hdr *) ((struct sk_buff *) skb)->data)->req_id) \ + +#define FREE_AFTER_TX(skb) \ + ((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \ + flags) == cpu_to_le16(P54_HDR_FLAG_CONTROL_OPSET)) + +#define IS_DATA_FRAME(skb) \ + (!((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \ + flags) & cpu_to_le16(P54_HDR_FLAG_CONTROL))) + +#define GET_HW_QUEUE(skb) \ + (((struct p54_tx_data *)((struct p54_hdr *) \ + skb->data)->data)->hw_queue) + +/* + * shared interface ID definitions + * The interface ID is a unique identification of a specific interface. + * The following values are reserved: 0x0000, 0x0002, 0x0012, 0x0014, 0x0015 + */ +#define IF_ID_ISL36356A 0x0001 /* ISL36356A <-> Firmware */ +#define IF_ID_MVC 0x0003 /* MAC Virtual Coprocessor */ +#define IF_ID_DEBUG 0x0008 /* PolDebug Interface */ +#define IF_ID_PRODUCT 0x0009 +#define IF_ID_OEM 0x000a +#define IF_ID_PCI3877 0x000b /* 3877 <-> Host PCI */ +#define IF_ID_ISL37704C 0x000c /* ISL37704C <-> Fw */ +#define IF_ID_ISL39000 0x000f /* ISL39000 <-> Fw */ +#define IF_ID_ISL39300A 0x0010 /* ISL39300A <-> Fw */ +#define IF_ID_ISL37700_UAP 0x0016 /* ISL37700 uAP Fw <-> Fw */ +#define IF_ID_ISL39000_UAP 0x0017 /* ISL39000 uAP Fw <-> Fw */ +#define IF_ID_LMAC 0x001a /* Interface exposed by LMAC */ + +struct exp_if { + __le16 role; + __le16 if_id; + __le16 variant; + __le16 btm_compat; + __le16 top_compat; +} __packed; + +struct dep_if { + __le16 role; + __le16 if_id; + __le16 variant; +} __packed; + +/* driver <-> lmac definitions */ +struct p54_eeprom_lm86 { + union { + struct { + __le16 offset; + __le16 len; + u8 data[0]; + } __packed v1; + struct { + __le32 offset; + __le16 len; + u8 magic2; + u8 pad; + u8 magic[4]; + u8 data[0]; + } __packed v2; + } __packed; +} __packed; + +enum p54_rx_decrypt_status { + P54_DECRYPT_NONE = 0, + P54_DECRYPT_OK, + P54_DECRYPT_NOKEY, + P54_DECRYPT_NOMICHAEL, + P54_DECRYPT_NOCKIPMIC, + P54_DECRYPT_FAIL_WEP, + P54_DECRYPT_FAIL_TKIP, + P54_DECRYPT_FAIL_MICHAEL, + P54_DECRYPT_FAIL_CKIPKP, + P54_DECRYPT_FAIL_CKIPMIC, + P54_DECRYPT_FAIL_AESCCMP +}; + +struct p54_rx_data { + __le16 flags; + __le16 len; + __le16 freq; + u8 antenna; + u8 rate; + u8 rssi; + u8 quality; + u8 decrypt_status; + u8 rssi_raw; + __le32 tsf32; + __le32 unalloc0; + u8 align[0]; +} __packed; + +enum p54_trap_type { + P54_TRAP_SCAN = 0, + P54_TRAP_TIMER, + P54_TRAP_BEACON_TX, + P54_TRAP_FAA_RADIO_ON, + P54_TRAP_FAA_RADIO_OFF, + P54_TRAP_RADAR, + P54_TRAP_NO_BEACON, + P54_TRAP_TBTT, + P54_TRAP_SCO_ENTER, + P54_TRAP_SCO_EXIT +}; + +struct p54_trap { + __le16 event; + __le16 frequency; +} __packed; + +enum p54_frame_sent_status { + P54_TX_OK = 0, + P54_TX_FAILED, + P54_TX_PSM, + P54_TX_PSM_CANCELLED = 4 +}; + +struct p54_frame_sent { + u8 status; + u8 tries; + u8 ack_rssi; + u8 quality; + __le16 seq; + u8 antenna; + u8 padding; +} __packed; + +enum p54_tx_data_crypt { + P54_CRYPTO_NONE = 0, + P54_CRYPTO_WEP, + P54_CRYPTO_TKIP, + P54_CRYPTO_TKIPMICHAEL, + P54_CRYPTO_CCX_WEPMIC, + P54_CRYPTO_CCX_KPMIC, + P54_CRYPTO_CCX_KP, + P54_CRYPTO_AESCCMP +}; + +enum p54_tx_data_queue { + P54_QUEUE_BEACON = 0, + P54_QUEUE_FWSCAN = 1, + P54_QUEUE_MGMT = 2, + P54_QUEUE_CAB = 3, + P54_QUEUE_DATA = 4, + + P54_QUEUE_AC_NUM = 4, + P54_QUEUE_AC_VO = 4, + P54_QUEUE_AC_VI = 5, + P54_QUEUE_AC_BE = 6, + P54_QUEUE_AC_BK = 7, + + /* keep last */ + P54_QUEUE_NUM = 8, +}; + +#define IS_QOS_QUEUE(n) (n >= P54_QUEUE_DATA) + +struct p54_tx_data { + u8 rateset[8]; + u8 rts_rate_idx; + u8 crypt_offset; + u8 key_type; + u8 key_len; + u8 key[16]; + u8 hw_queue; + u8 backlog; + __le16 durations[4]; + u8 tx_antenna; + union { + struct { + u8 cts_rate; + __le16 output_power; + } __packed longbow; + struct { + u8 output_power; + u8 cts_rate; + u8 unalloc; + } __packed normal; + } __packed; + u8 unalloc2[2]; + u8 align[0]; +} __packed; + +/* unit is ms */ +#define P54_TX_FRAME_LIFETIME 2000 +#define P54_TX_TIMEOUT 4000 +#define P54_STATISTICS_UPDATE 5000 + +#define P54_FILTER_TYPE_NONE 0 +#define P54_FILTER_TYPE_STATION BIT(0) +#define P54_FILTER_TYPE_IBSS BIT(1) +#define P54_FILTER_TYPE_AP BIT(2) +#define P54_FILTER_TYPE_TRANSPARENT BIT(3) +#define P54_FILTER_TYPE_PROMISCUOUS BIT(4) +#define P54_FILTER_TYPE_HIBERNATE BIT(5) +#define P54_FILTER_TYPE_NOACK BIT(6) +#define P54_FILTER_TYPE_RX_DISABLED BIT(7) + +struct p54_setup_mac { + __le16 mac_mode; + u8 mac_addr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u8 rx_antenna; + u8 rx_align; + union { + struct { + __le32 basic_rate_mask; + u8 rts_rates[8]; + __le32 rx_addr; + __le16 max_rx; + __le16 rxhw; + __le16 wakeup_timer; + __le16 unalloc0; + } __packed v1; + struct { + __le32 rx_addr; + __le16 max_rx; + __le16 rxhw; + __le16 timer; + __le16 truncate; + __le32 basic_rate_mask; + u8 sbss_offset; + u8 mcast_window; + u8 rx_rssi_threshold; + u8 rx_ed_threshold; + __le32 ref_clock; + __le16 lpf_bandwidth; + __le16 osc_start_delay; + } __packed v2; + } __packed; +} __packed; + +#define P54_SETUP_V1_LEN 40 +#define P54_SETUP_V2_LEN (sizeof(struct p54_setup_mac)) + +#define P54_SCAN_EXIT BIT(0) +#define P54_SCAN_TRAP BIT(1) +#define P54_SCAN_ACTIVE BIT(2) +#define P54_SCAN_FILTER BIT(3) + +struct p54_scan_head { + __le16 mode; + __le16 dwell; + u8 scan_params[20]; + __le16 freq; +} __packed; + +struct p54_pa_curve_data_sample { + u8 rf_power; + u8 pa_detector; + u8 data_barker; + u8 data_bpsk; + u8 data_qpsk; + u8 data_16qam; + u8 data_64qam; + u8 padding; +} __packed; + +struct p54_scan_body { + u8 pa_points_per_curve; + u8 val_barker; + u8 val_bpsk; + u8 val_qpsk; + u8 val_16qam; + u8 val_64qam; + struct p54_pa_curve_data_sample curve_data[8]; + u8 dup_bpsk; + u8 dup_qpsk; + u8 dup_16qam; + u8 dup_64qam; +} __packed; + +/* + * Warning: Longbow's structures are bogus. + */ +struct p54_channel_output_limit_longbow { + __le16 rf_power_points[12]; +} __packed; + +struct p54_pa_curve_data_sample_longbow { + __le16 rf_power; + __le16 pa_detector; + struct { + __le16 data[4]; + } points[3] __packed; +} __packed; + +struct p54_scan_body_longbow { + struct p54_channel_output_limit_longbow power_limits; + struct p54_pa_curve_data_sample_longbow curve_data[8]; + __le16 unkn[6]; /* maybe more power_limits or rate_mask */ +} __packed; + +union p54_scan_body_union { + struct p54_scan_body normal; + struct p54_scan_body_longbow longbow; +} __packed; + +struct p54_scan_tail_rate { + __le32 basic_rate_mask; + u8 rts_rates[8]; +} __packed; + +struct p54_led { + __le16 flags; + __le16 mask[2]; + __le16 delay[2]; +} __packed; + +struct p54_edcf { + u8 flags; + u8 slottime; + u8 sifs; + u8 eofpad; + struct p54_edcf_queue_param queue[8]; + u8 mapping[4]; + __le16 frameburst; + __le16 round_trip_delay; +} __packed; + +struct p54_statistics { + __le32 rx_success; + __le32 rx_bad_fcs; + __le32 rx_abort; + __le32 rx_abort_phy; + __le32 rts_success; + __le32 rts_fail; + __le32 tsf32; + __le32 airtime; + __le32 noise; + __le32 sample_noise[8]; + __le32 sample_cca; + __le32 sample_tx; +} __packed; + +struct p54_xbow_synth { + __le16 magic1; + __le16 magic2; + __le16 freq; + u32 padding[5]; +} __packed; + +struct p54_timer { + __le32 interval; +} __packed; + +struct p54_keycache { + u8 entry; + u8 key_id; + u8 mac[ETH_ALEN]; + u8 padding[2]; + u8 key_type; + u8 key_len; + u8 key[24]; +} __packed; + +struct p54_burst { + u8 flags; + u8 queue; + u8 backlog; + u8 pad; + __le16 durations[32]; +} __packed; + +struct p54_psm_interval { + __le16 interval; + __le16 periods; +} __packed; + +#define P54_PSM_CAM 0 +#define P54_PSM BIT(0) +#define P54_PSM_DTIM BIT(1) +#define P54_PSM_MCBC BIT(2) +#define P54_PSM_CHECKSUM BIT(3) +#define P54_PSM_SKIP_MORE_DATA BIT(4) +#define P54_PSM_BEACON_TIMEOUT BIT(5) +#define P54_PSM_HFOSLEEP BIT(6) +#define P54_PSM_AUTOSWITCH_SLEEP BIT(7) +#define P54_PSM_LPIT BIT(8) +#define P54_PSM_BF_UCAST_SKIP BIT(9) +#define P54_PSM_BF_MCAST_SKIP BIT(10) + +struct p54_psm { + __le16 mode; + __le16 aid; + struct p54_psm_interval intervals[4]; + u8 beacon_rssi_skip_max; + u8 rssi_delta_threshold; + u8 nr; + u8 exclude[1]; +} __packed; + +#define MC_FILTER_ADDRESS_NUM 4 + +struct p54_group_address_table { + __le16 filter_enable; + __le16 num_address; + u8 mac_list[MC_FILTER_ADDRESS_NUM][ETH_ALEN]; +} __packed; + +struct p54_txcancel { + __le32 req_id; +} __packed; + +struct p54_sta_unlock { + u8 addr[ETH_ALEN]; + u16 padding; +} __packed; + +#define P54_TIM_CLEAR BIT(15) +struct p54_tim { + u8 count; + u8 padding[3]; + __le16 entry[8]; +} __packed; + +struct p54_cce_quiet { + __le32 period; +} __packed; + +struct p54_bt_balancer { + __le16 prio_thresh; + __le16 acl_thresh; +} __packed; + +struct p54_arp_table { + __le16 filter_enable; + u8 ipv4_addr[4]; +} __packed; + +/* LED control */ +int p54_set_leds(struct p54_common *priv); +int p54_init_leds(struct p54_common *priv); +void p54_unregister_leds(struct p54_common *priv); + +/* xmit functions */ +int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb); +int p54_tx_cancel(struct p54_common *priv, __le32 req_id); +void p54_tx(struct p54_common *priv, struct sk_buff *skb); + +/* synth/phy configuration */ +int p54_init_xbow_synth(struct p54_common *priv); +int p54_scan(struct p54_common *priv, u16 mode, u16 dwell); + +/* MAC */ +int p54_sta_unlock(struct p54_common *priv, u8 *addr); +int p54_update_beacon_tim(struct p54_common *priv, u16 aid, bool set); +int p54_setup_mac(struct p54_common *priv); +int p54_set_ps(struct p54_common *priv); +int p54_fetch_statistics(struct p54_common *priv); + +/* e/v DCF setup */ +int p54_set_edcf(struct p54_common *priv); + +/* cryptographic engine */ +int p54_upload_key(struct p54_common *priv, u8 algo, int slot, + u8 idx, u8 len, u8 *addr, u8* key); + +/* eeprom */ +int p54_download_eeprom(struct p54_common *priv, void *buf, + u16 offset, u16 len); + +/* utility */ +u8 *p54_find_ie(struct sk_buff *skb, u8 ie); + +#endif /* LMAC_H */ diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c new file mode 100644 index 000000000000..4d486bf9f725 --- /dev/null +++ b/drivers/net/wireless/p54/main.c @@ -0,0 +1,648 @@ +/* + * mac80211 glue code for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/firmware.h> +#include <linux/etherdevice.h> + +#include <net/mac80211.h> + +#include "p54.h" +#include "lmac.h" + +static int modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); +MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); +MODULE_DESCRIPTION("Softmac Prism54 common code"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("prism54common"); + +static void p54_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) +{ + struct p54_common *priv = dev->priv; + switch (notify_cmd) { + case STA_NOTIFY_ADD: + case STA_NOTIFY_REMOVE: + /* + * Notify the firmware that we don't want or we don't + * need to buffer frames for this station anymore. + */ + + p54_sta_unlock(priv, sta->addr); + break; + case STA_NOTIFY_AWAKE: + /* update the firmware's filter table */ + p54_sta_unlock(priv, sta->addr); + break; + default: + break; + } +} + +static int p54_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set) +{ + struct p54_common *priv = dev->priv; + + return p54_update_beacon_tim(priv, sta->aid, set); +} + +u8 *p54_find_ie(struct sk_buff *skb, u8 ie) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + u8 *pos, *end; + + if (skb->len <= sizeof(mgmt)) + return NULL; + + pos = (u8 *)mgmt->u.beacon.variable; + end = skb->data + skb->len; + while (pos < end) { + if (pos + 2 + pos[1] > end) + return NULL; + + if (pos[0] == ie) + return pos; + + pos += 2 + pos[1]; + } + return NULL; +} + +static int p54_beacon_format_ie_tim(struct sk_buff *skb) +{ + /* + * the good excuse for this mess is ... the firmware. + * The dummy TIM MUST be at the end of the beacon frame, + * because it'll be overwritten! + */ + u8 *tim; + u8 dtim_len; + u8 dtim_period; + u8 *next; + + tim = p54_find_ie(skb, WLAN_EID_TIM); + if (!tim) + return 0; + + dtim_len = tim[1]; + dtim_period = tim[3]; + next = tim + 2 + dtim_len; + + if (dtim_len < 3) + return -EINVAL; + + memmove(tim, next, skb_tail_pointer(skb) - next); + tim = skb_tail_pointer(skb) - (dtim_len + 2); + + /* add the dummy at the end */ + tim[0] = WLAN_EID_TIM; + tim[1] = 3; + tim[2] = 0; + tim[3] = dtim_period; + tim[4] = 0; + + if (dtim_len > 3) + skb_trim(skb, skb->len - (dtim_len - 3)); + + return 0; +} + +static int p54_beacon_update(struct p54_common *priv, + struct ieee80211_vif *vif) +{ + struct sk_buff *beacon; + int ret; + + beacon = ieee80211_beacon_get(priv->hw, vif); + if (!beacon) + return -ENOMEM; + ret = p54_beacon_format_ie_tim(beacon); + if (ret) + return ret; + + /* + * During operation, the firmware takes care of beaconing. + * The driver only needs to upload a new beacon template, once + * the template was changed by the stack or userspace. + * + * LMAC API 3.2.2 also specifies that the driver does not need + * to cancel the old beacon template by hand, instead the firmware + * will release the previous one through the feedback mechanism. + */ + WARN_ON(p54_tx_80211(priv->hw, beacon)); + priv->tsf_high32 = 0; + priv->tsf_low32 = 0; + + return 0; +} + +static int p54_start(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + int err; + + mutex_lock(&priv->conf_mutex); + err = priv->open(dev); + if (err) + goto out; + P54_SET_QUEUE(priv->qos_params[0], 0x0002, 0x0003, 0x0007, 47); + P54_SET_QUEUE(priv->qos_params[1], 0x0002, 0x0007, 0x000f, 94); + P54_SET_QUEUE(priv->qos_params[2], 0x0003, 0x000f, 0x03ff, 0); + P54_SET_QUEUE(priv->qos_params[3], 0x0007, 0x000f, 0x03ff, 0); + err = p54_set_edcf(priv); + if (err) + goto out; + + memset(priv->bssid, ~0, ETH_ALEN); + priv->mode = NL80211_IFTYPE_MONITOR; + err = p54_setup_mac(priv); + if (err) { + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + goto out; + } + + ieee80211_queue_delayed_work(dev, &priv->work, 0); + + priv->softled_state = 0; + err = p54_set_leds(priv); + +out: + mutex_unlock(&priv->conf_mutex); + return err; +} + +static void p54_stop(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + int i; + + mutex_lock(&priv->conf_mutex); + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->softled_state = 0; + p54_set_leds(priv); + + cancel_delayed_work_sync(&priv->work); + + priv->stop(dev); + skb_queue_purge(&priv->tx_pending); + skb_queue_purge(&priv->tx_queue); + for (i = 0; i < P54_QUEUE_NUM; i++) { + priv->tx_stats[i].count = 0; + priv->tx_stats[i].len = 0; + } + + priv->beacon_req_id = cpu_to_le32(0); + priv->tsf_high32 = priv->tsf_low32 = 0; + mutex_unlock(&priv->conf_mutex); +} + +static int p54_add_interface(struct ieee80211_hw *dev, + struct ieee80211_if_init_conf *conf) +{ + struct p54_common *priv = dev->priv; + + mutex_lock(&priv->conf_mutex); + if (priv->mode != NL80211_IFTYPE_MONITOR) { + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + priv->vif = conf->vif; + + switch (conf->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MESH_POINT: + priv->mode = conf->type; + break; + default: + mutex_unlock(&priv->conf_mutex); + return -EOPNOTSUPP; + } + + memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN); + p54_setup_mac(priv); + mutex_unlock(&priv->conf_mutex); + return 0; +} + +static void p54_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_if_init_conf *conf) +{ + struct p54_common *priv = dev->priv; + + mutex_lock(&priv->conf_mutex); + priv->vif = NULL; + + /* + * LMAC API 3.2.2 states that any active beacon template must be + * canceled by the driver before attempting a mode transition. + */ + if (le32_to_cpu(priv->beacon_req_id) != 0) { + p54_tx_cancel(priv, priv->beacon_req_id); + wait_for_completion_interruptible_timeout(&priv->beacon_comp, HZ); + } + priv->mode = NL80211_IFTYPE_MONITOR; + memset(priv->mac_addr, 0, ETH_ALEN); + memset(priv->bssid, 0, ETH_ALEN); + p54_setup_mac(priv); + mutex_unlock(&priv->conf_mutex); +} + +static int p54_config(struct ieee80211_hw *dev, u32 changed) +{ + int ret = 0; + struct p54_common *priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; + + mutex_lock(&priv->conf_mutex); + if (changed & IEEE80211_CONF_CHANGE_POWER) + priv->output_power = conf->power_level << 2; + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + ret = p54_scan(priv, P54_SCAN_EXIT, 0); + if (ret) + goto out; + } + if (changed & IEEE80211_CONF_CHANGE_PS) { + ret = p54_set_ps(priv); + if (ret) + goto out; + } + if (changed & IEEE80211_CONF_CHANGE_IDLE) { + ret = p54_setup_mac(priv); + if (ret) + goto out; + } + +out: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +static void p54_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct p54_common *priv = dev->priv; + + *total_flags &= FIF_PROMISC_IN_BSS | + FIF_OTHER_BSS; + + priv->filter_flags = *total_flags; + + if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) + p54_setup_mac(priv); +} + +static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct p54_common *priv = dev->priv; + int ret; + + mutex_lock(&priv->conf_mutex); + if (queue < dev->queues) { + P54_SET_QUEUE(priv->qos_params[queue], params->aifs, + params->cw_min, params->cw_max, params->txop); + ret = p54_set_edcf(priv); + } else + ret = -EINVAL; + mutex_unlock(&priv->conf_mutex); + return ret; +} + +static void p54_work(struct work_struct *work) +{ + struct p54_common *priv = container_of(work, struct p54_common, + work.work); + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) + return ; + + /* + * TODO: walk through tx_queue and do the following tasks + * 1. initiate bursts. + * 2. cancel stuck frames / reset the device if necessary. + */ + + p54_fetch_statistics(priv); +} + +static int p54_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats) +{ + struct p54_common *priv = dev->priv; + + memcpy(stats, &priv->stats, sizeof(*stats)); + return 0; +} + +static int p54_get_tx_stats(struct ieee80211_hw *dev, + struct ieee80211_tx_queue_stats *stats) +{ + struct p54_common *priv = dev->priv; + + memcpy(stats, &priv->tx_stats[P54_QUEUE_DATA], + sizeof(stats[0]) * dev->queues); + return 0; +} + +static void p54_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct p54_common *priv = dev->priv; + + mutex_lock(&priv->conf_mutex); + if (changed & BSS_CHANGED_BSSID) { + memcpy(priv->bssid, info->bssid, ETH_ALEN); + p54_setup_mac(priv); + } + + if (changed & BSS_CHANGED_BEACON) { + p54_scan(priv, P54_SCAN_EXIT, 0); + p54_setup_mac(priv); + p54_beacon_update(priv, vif); + p54_set_edcf(priv); + } + + if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BEACON)) { + priv->use_short_slot = info->use_short_slot; + p54_set_edcf(priv); + } + if (changed & BSS_CHANGED_BASIC_RATES) { + if (dev->conf.channel->band == IEEE80211_BAND_5GHZ) + priv->basic_rate_mask = (info->basic_rates << 4); + else + priv->basic_rate_mask = info->basic_rates; + p54_setup_mac(priv); + if (priv->fw_var >= 0x500) + p54_scan(priv, P54_SCAN_EXIT, 0); + } + if (changed & BSS_CHANGED_ASSOC) { + if (info->assoc) { + priv->aid = info->aid; + priv->wakeup_timer = info->beacon_int * + info->dtim_period * 5; + p54_setup_mac(priv); + } else { + priv->wakeup_timer = 500; + priv->aid = 0; + } + } + + mutex_unlock(&priv->conf_mutex); +} + +static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct p54_common *priv = dev->priv; + int slot, ret = 0; + u8 algo = 0; + u8 *addr = NULL; + + if (modparam_nohwcrypt) + return -EOPNOTSUPP; + + mutex_lock(&priv->conf_mutex); + if (cmd == SET_KEY) { + switch (key->alg) { + case ALG_TKIP: + if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL | + BR_DESC_PRIV_CAP_TKIP))) { + ret = -EOPNOTSUPP; + goto out_unlock; + } + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + algo = P54_CRYPTO_TKIPMICHAEL; + break; + case ALG_WEP: + if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP)) { + ret = -EOPNOTSUPP; + goto out_unlock; + } + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + algo = P54_CRYPTO_WEP; + break; + case ALG_CCMP: + if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)) { + ret = -EOPNOTSUPP; + goto out_unlock; + } + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + algo = P54_CRYPTO_AESCCMP; + break; + default: + ret = -EOPNOTSUPP; + goto out_unlock; + } + slot = bitmap_find_free_region(priv->used_rxkeys, + priv->rx_keycache_size, 0); + + if (slot < 0) { + /* + * The device supports the choosen algorithm, but the + * firmware does not provide enough key slots to store + * all of them. + * But encryption offload for outgoing frames is always + * possible, so we just pretend that the upload was + * successful and do the decryption in software. + */ + + /* mark the key as invalid. */ + key->hw_key_idx = 0xff; + goto out_unlock; + } + } else { + slot = key->hw_key_idx; + + if (slot == 0xff) { + /* This key was not uploaded into the rx key cache. */ + + goto out_unlock; + } + + bitmap_release_region(priv->used_rxkeys, slot, 0); + algo = 0; + } + + if (sta) + addr = sta->addr; + + ret = p54_upload_key(priv, algo, slot, key->keyidx, + key->keylen, addr, key->key); + if (ret) { + bitmap_release_region(priv->used_rxkeys, slot, 0); + ret = -EOPNOTSUPP; + goto out_unlock; + } + + key->hw_key_idx = slot; + +out_unlock: + mutex_unlock(&priv->conf_mutex); + return ret; +} + +static const struct ieee80211_ops p54_ops = { + .tx = p54_tx_80211, + .start = p54_start, + .stop = p54_stop, + .add_interface = p54_add_interface, + .remove_interface = p54_remove_interface, + .set_tim = p54_set_tim, + .sta_notify = p54_sta_notify, + .set_key = p54_set_key, + .config = p54_config, + .bss_info_changed = p54_bss_info_changed, + .configure_filter = p54_configure_filter, + .conf_tx = p54_conf_tx, + .get_stats = p54_get_stats, + .get_tx_stats = p54_get_tx_stats +}; + +struct ieee80211_hw *p54_init_common(size_t priv_data_len) +{ + struct ieee80211_hw *dev; + struct p54_common *priv; + + dev = ieee80211_alloc_hw(priv_data_len, &p54_ops); + if (!dev) + return NULL; + + priv = dev->priv; + priv->hw = dev; + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->basic_rate_mask = 0x15f; + spin_lock_init(&priv->tx_stats_lock); + skb_queue_head_init(&priv->tx_queue); + skb_queue_head_init(&priv->tx_pending); + dev->flags = IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK | + IEEE80211_HW_BEACON_FILTER | + IEEE80211_HW_NOISE_DBM; + + dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT); + + dev->channel_change_time = 1000; /* TODO: find actual value */ + priv->beacon_req_id = cpu_to_le32(0); + priv->tx_stats[P54_QUEUE_BEACON].limit = 1; + priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1; + priv->tx_stats[P54_QUEUE_MGMT].limit = 3; + priv->tx_stats[P54_QUEUE_CAB].limit = 3; + priv->tx_stats[P54_QUEUE_DATA].limit = 5; + dev->queues = 1; + priv->noise = -94; + /* + * We support at most 8 tries no matter which rate they're at, + * we cannot support max_rates * max_rate_tries as we set it + * here, but setting it correctly to 4/2 or so would limit us + * artificially if the RC algorithm wants just two rates, so + * let's say 4/7, we'll redistribute it at TX time, see the + * comments there. + */ + dev->max_rates = 4; + dev->max_rate_tries = 7; + dev->extra_tx_headroom = sizeof(struct p54_hdr) + 4 + + sizeof(struct p54_tx_data); + + /* + * For now, disable PS by default because it affects + * link stability significantly. + */ + dev->wiphy->ps_default = false; + + mutex_init(&priv->conf_mutex); + mutex_init(&priv->eeprom_mutex); + init_completion(&priv->eeprom_comp); + init_completion(&priv->beacon_comp); + INIT_DELAYED_WORK(&priv->work, p54_work); + + return dev; +} +EXPORT_SYMBOL_GPL(p54_init_common); + +int p54_register_common(struct ieee80211_hw *dev, struct device *pdev) +{ + struct p54_common *priv = dev->priv; + int err; + + err = ieee80211_register_hw(dev); + if (err) { + dev_err(pdev, "Cannot register device (%d).\n", err); + return err; + } + +#ifdef CONFIG_P54_LEDS + err = p54_init_leds(priv); + if (err) + return err; +#endif /* CONFIG_P54_LEDS */ + + dev_info(pdev, "is registered as '%s'\n", wiphy_name(dev->wiphy)); + return 0; +} +EXPORT_SYMBOL_GPL(p54_register_common); + +void p54_free_common(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + unsigned int i; + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + kfree(priv->band_table[i]); + + kfree(priv->iq_autocal); + kfree(priv->output_limit); + kfree(priv->curve_data); + kfree(priv->used_rxkeys); + priv->iq_autocal = NULL; + priv->output_limit = NULL; + priv->curve_data = NULL; + priv->used_rxkeys = NULL; + ieee80211_free_hw(dev); +} +EXPORT_SYMBOL_GPL(p54_free_common); + +void p54_unregister_common(struct ieee80211_hw *dev) +{ + struct p54_common *priv = dev->priv; + +#ifdef CONFIG_P54_LEDS + p54_unregister_leds(priv); +#endif /* CONFIG_P54_LEDS */ + + ieee80211_unregister_hw(dev); + mutex_destroy(&priv->conf_mutex); + mutex_destroy(&priv->eeprom_mutex); +} +EXPORT_SYMBOL_GPL(p54_unregister_common); diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h index db3df947d8ed..1afc39410e85 100644 --- a/drivers/net/wireless/p54/p54.h +++ b/drivers/net/wireless/p54/p54.h @@ -1,6 +1,3 @@ -#ifndef P54_H -#define P54_H - /* * Shared defines for all mac80211 Prism54 code * @@ -14,39 +11,78 @@ * published by the Free Software Foundation. */ +#ifndef P54_H +#define P54_H + #ifdef CONFIG_P54_LEDS #include <linux/leds.h> #endif /* CONFIG_P54_LEDS */ -enum p54_control_frame_types { - P54_CONTROL_TYPE_SETUP = 0, - P54_CONTROL_TYPE_SCAN, - P54_CONTROL_TYPE_TRAP, - P54_CONTROL_TYPE_DCFINIT, - P54_CONTROL_TYPE_RX_KEYCACHE, - P54_CONTROL_TYPE_TIM, - P54_CONTROL_TYPE_PSM, - P54_CONTROL_TYPE_TXCANCEL, - P54_CONTROL_TYPE_TXDONE, - P54_CONTROL_TYPE_BURST, - P54_CONTROL_TYPE_STAT_READBACK, - P54_CONTROL_TYPE_BBP, - P54_CONTROL_TYPE_EEPROM_READBACK, - P54_CONTROL_TYPE_LED, - P54_CONTROL_TYPE_GPIO, - P54_CONTROL_TYPE_TIMER, - P54_CONTROL_TYPE_MODULATION, - P54_CONTROL_TYPE_SYNTH_CONFIG, - P54_CONTROL_TYPE_DETECTOR_VALUE, - P54_CONTROL_TYPE_XBOW_SYNTH_CFG, - P54_CONTROL_TYPE_CCE_QUIET, - P54_CONTROL_TYPE_PSM_STA_UNLOCK, - P54_CONTROL_TYPE_PCS, - P54_CONTROL_TYPE_BT_BALANCER = 28, - P54_CONTROL_TYPE_GROUP_ADDRESS_TABLE = 30, - P54_CONTROL_TYPE_ARPTABLE = 31, - P54_CONTROL_TYPE_BT_OPTIONS = 35 -}; +#define ISL38XX_DEV_FIRMWARE_ADDR 0x20000 + +#define BR_CODE_MIN 0x80000000 +#define BR_CODE_COMPONENT_ID 0x80000001 +#define BR_CODE_COMPONENT_VERSION 0x80000002 +#define BR_CODE_DEPENDENT_IF 0x80000003 +#define BR_CODE_EXPOSED_IF 0x80000004 +#define BR_CODE_DESCR 0x80000101 +#define BR_CODE_MAX 0x8FFFFFFF +#define BR_CODE_END_OF_BRA 0xFF0000FF +#define LEGACY_BR_CODE_END_OF_BRA 0xFFFFFFFF + +struct bootrec { + __le32 code; + __le32 len; + u32 data[10]; +} __packed; + +/* Interface role definitions */ +#define BR_INTERFACE_ROLE_SERVER 0x0000 +#define BR_INTERFACE_ROLE_CLIENT 0x8000 + +#define BR_DESC_PRIV_CAP_WEP BIT(0) +#define BR_DESC_PRIV_CAP_TKIP BIT(1) +#define BR_DESC_PRIV_CAP_MICHAEL BIT(2) +#define BR_DESC_PRIV_CAP_CCX_CP BIT(3) +#define BR_DESC_PRIV_CAP_CCX_MIC BIT(4) +#define BR_DESC_PRIV_CAP_AESCCMP BIT(5) + +struct bootrec_desc { + __le16 modes; + __le16 flags; + __le32 rx_start; + __le32 rx_end; + u8 headroom; + u8 tailroom; + u8 tx_queues; + u8 tx_depth; + u8 privacy_caps; + u8 rx_keycache_size; + u8 time_size; + u8 padding; + u8 rates[16]; + u8 padding2[4]; + __le16 rx_mtu; +} __packed; + +#define FW_FMAC 0x464d4143 +#define FW_LM86 0x4c4d3836 +#define FW_LM87 0x4c4d3837 +#define FW_LM20 0x4c4d3230 + +struct bootrec_comp_id { + __le32 fw_variant; +} __packed; + +struct bootrec_comp_ver { + char fw_version[24]; +} __packed; + +struct bootrec_end { + __le16 crc; + u8 padding[2]; + u8 md5[16]; +} __packed; /* provide 16 bytes for the transport back-end */ #define P54_TX_INFO_DATA_SIZE 16 @@ -55,34 +91,30 @@ enum p54_control_frame_types { struct p54_tx_info { u32 start_addr; u32 end_addr; - void *data[P54_TX_INFO_DATA_SIZE / sizeof(void *)]; + union { + void *data[P54_TX_INFO_DATA_SIZE / sizeof(void *)]; + struct { + u32 extra_len; + }; + }; }; #define P54_MAX_CTRL_FRAME_LEN 0x1000 -#define P54_HDR_FLAG_CONTROL BIT(15) -#define P54_HDR_FLAG_CONTROL_OPSET (BIT(15) + BIT(0)) - -struct p54_hdr { - __le16 flags; - __le16 len; - __le32 req_id; - __le16 type; /* enum p54_control_frame_types */ - u8 rts_tries; - u8 tries; - u8 data[0]; -} __attribute__ ((packed)); - -#define FREE_AFTER_TX(skb) \ - ((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \ - flags) == cpu_to_le16(P54_HDR_FLAG_CONTROL_OPSET)) +#define P54_SET_QUEUE(queue, ai_fs, cw_min, cw_max, _txop) \ +do { \ + queue.aifs = cpu_to_le16(ai_fs); \ + queue.cwmin = cpu_to_le16(cw_min); \ + queue.cwmax = cpu_to_le16(cw_max); \ + queue.txop = cpu_to_le16(_txop); \ +} while (0) struct p54_edcf_queue_param { __le16 aifs; __le16 cwmin; __le16 cwmax; __le16 txop; -} __attribute__ ((packed)); +} __packed; struct p54_rssi_linear_approximation { s16 mul; @@ -101,13 +133,6 @@ struct p54_cal_database { #define EEPROM_READBACK_LEN 0x3fc -#define ISL38XX_DEV_FIRMWARE_ADDR 0x20000 - -#define FW_FMAC 0x464d4143 -#define FW_LM86 0x4c4d3836 -#define FW_LM87 0x4c4d3837 -#define FW_LM20 0x4c4d3230 - enum fw_state { FW_STATE_OFF, FW_STATE_BOOTING, @@ -138,6 +163,7 @@ struct p54_common { void (*tx)(struct ieee80211_hw *dev, struct sk_buff *skb); int (*open)(struct ieee80211_hw *dev); void (*stop)(struct ieee80211_hw *dev); + struct sk_buff_head tx_pending; struct sk_buff_head tx_queue; struct mutex conf_mutex; @@ -156,6 +182,7 @@ struct p54_common { /* (e)DCF / QOS state */ bool use_short_slot; + spinlock_t tx_stats_lock; struct ieee80211_tx_queue_stats tx_stats[8]; struct p54_edcf_queue_param qos_params[8]; @@ -171,6 +198,7 @@ struct p54_common { struct p54_cal_database *curve_data; struct p54_cal_database *output_limit; struct p54_rssi_linear_approximation rssical_db[IEEE80211_NUM_BANDS]; + struct ieee80211_supported_band *band_table[IEEE80211_NUM_BANDS]; /* BBP/MAC state */ u8 mac_addr[ETH_ALEN]; @@ -181,7 +209,9 @@ struct p54_common { u32 tsf_low32, tsf_high32; u32 basic_rate_mask; u16 aid; - struct sk_buff *cached_beacon; + bool powersave_override; + __le32 beacon_req_id; + struct completion beacon_comp; /* cryptographic engine information */ u8 privacy_caps; @@ -202,15 +232,20 @@ struct p54_common { /* eeprom handling */ void *eeprom; struct completion eeprom_comp; + struct mutex eeprom_mutex; }; +/* interfaces for the drivers */ int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb); void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb); int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw); int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len); int p54_read_eeprom(struct ieee80211_hw *dev); + struct ieee80211_hw *p54_init_common(size_t priv_data_len); int p54_register_common(struct ieee80211_hw *dev, struct device *pdev); void p54_free_common(struct ieee80211_hw *dev); +void p54_unregister_common(struct ieee80211_hw *dev); + #endif /* P54_H */ diff --git a/drivers/net/wireless/p54/p54common.c b/drivers/net/wireless/p54/p54common.c deleted file mode 100644 index 22ca122bd798..000000000000 --- a/drivers/net/wireless/p54/p54common.c +++ /dev/null @@ -1,2688 +0,0 @@ -/* - * Common code for mac80211 Prism54 drivers - * - * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> - * Copyright (c) 2007, Christian Lamparter <chunkeey@web.de> - * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> - * - * Based on: - * - the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. - * - stlc45xx driver - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/init.h> -#include <linux/firmware.h> -#include <linux/etherdevice.h> - -#include <net/mac80211.h> -#ifdef CONFIG_P54_LEDS -#include <linux/leds.h> -#endif /* CONFIG_P54_LEDS */ - -#include "p54.h" -#include "p54common.h" - -static int modparam_nohwcrypt; -module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); -MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); -MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); -MODULE_DESCRIPTION("Softmac Prism54 common code"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("prism54common"); - -static struct ieee80211_rate p54_bgrates[] = { - { .bitrate = 10, .hw_value = 0, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 20, .hw_value = 1, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 55, .hw_value = 2, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 110, .hw_value = 3, .flags = IEEE80211_RATE_SHORT_PREAMBLE }, - { .bitrate = 60, .hw_value = 4, }, - { .bitrate = 90, .hw_value = 5, }, - { .bitrate = 120, .hw_value = 6, }, - { .bitrate = 180, .hw_value = 7, }, - { .bitrate = 240, .hw_value = 8, }, - { .bitrate = 360, .hw_value = 9, }, - { .bitrate = 480, .hw_value = 10, }, - { .bitrate = 540, .hw_value = 11, }, -}; - -static struct ieee80211_channel p54_bgchannels[] = { - { .center_freq = 2412, .hw_value = 1, }, - { .center_freq = 2417, .hw_value = 2, }, - { .center_freq = 2422, .hw_value = 3, }, - { .center_freq = 2427, .hw_value = 4, }, - { .center_freq = 2432, .hw_value = 5, }, - { .center_freq = 2437, .hw_value = 6, }, - { .center_freq = 2442, .hw_value = 7, }, - { .center_freq = 2447, .hw_value = 8, }, - { .center_freq = 2452, .hw_value = 9, }, - { .center_freq = 2457, .hw_value = 10, }, - { .center_freq = 2462, .hw_value = 11, }, - { .center_freq = 2467, .hw_value = 12, }, - { .center_freq = 2472, .hw_value = 13, }, - { .center_freq = 2484, .hw_value = 14, }, -}; - -static struct ieee80211_supported_band band_2GHz = { - .channels = p54_bgchannels, - .n_channels = ARRAY_SIZE(p54_bgchannels), - .bitrates = p54_bgrates, - .n_bitrates = ARRAY_SIZE(p54_bgrates), -}; - -static struct ieee80211_rate p54_arates[] = { - { .bitrate = 60, .hw_value = 4, }, - { .bitrate = 90, .hw_value = 5, }, - { .bitrate = 120, .hw_value = 6, }, - { .bitrate = 180, .hw_value = 7, }, - { .bitrate = 240, .hw_value = 8, }, - { .bitrate = 360, .hw_value = 9, }, - { .bitrate = 480, .hw_value = 10, }, - { .bitrate = 540, .hw_value = 11, }, -}; - -static struct ieee80211_channel p54_achannels[] = { - { .center_freq = 4920 }, - { .center_freq = 4940 }, - { .center_freq = 4960 }, - { .center_freq = 4980 }, - { .center_freq = 5040 }, - { .center_freq = 5060 }, - { .center_freq = 5080 }, - { .center_freq = 5170 }, - { .center_freq = 5180 }, - { .center_freq = 5190 }, - { .center_freq = 5200 }, - { .center_freq = 5210 }, - { .center_freq = 5220 }, - { .center_freq = 5230 }, - { .center_freq = 5240 }, - { .center_freq = 5260 }, - { .center_freq = 5280 }, - { .center_freq = 5300 }, - { .center_freq = 5320 }, - { .center_freq = 5500 }, - { .center_freq = 5520 }, - { .center_freq = 5540 }, - { .center_freq = 5560 }, - { .center_freq = 5580 }, - { .center_freq = 5600 }, - { .center_freq = 5620 }, - { .center_freq = 5640 }, - { .center_freq = 5660 }, - { .center_freq = 5680 }, - { .center_freq = 5700 }, - { .center_freq = 5745 }, - { .center_freq = 5765 }, - { .center_freq = 5785 }, - { .center_freq = 5805 }, - { .center_freq = 5825 }, -}; - -static struct ieee80211_supported_band band_5GHz = { - .channels = p54_achannels, - .n_channels = ARRAY_SIZE(p54_achannels), - .bitrates = p54_arates, - .n_bitrates = ARRAY_SIZE(p54_arates), -}; - -int p54_parse_firmware(struct ieee80211_hw *dev, const struct firmware *fw) -{ - struct p54_common *priv = dev->priv; - struct bootrec_exp_if *exp_if; - struct bootrec *bootrec; - u32 *data = (u32 *)fw->data; - u32 *end_data = (u32 *)fw->data + (fw->size >> 2); - u8 *fw_version = NULL; - size_t len; - int i; - int maxlen; - - if (priv->rx_start) - return 0; - - while (data < end_data && *data) - data++; - - while (data < end_data && !*data) - data++; - - bootrec = (struct bootrec *) data; - - while (bootrec->data <= end_data && - (bootrec->data + (len = le32_to_cpu(bootrec->len))) <= end_data) { - u32 code = le32_to_cpu(bootrec->code); - switch (code) { - case BR_CODE_COMPONENT_ID: - priv->fw_interface = be32_to_cpup((__be32 *) - bootrec->data); - switch (priv->fw_interface) { - case FW_LM86: - case FW_LM20: - case FW_LM87: { - char *iftype = (char *)bootrec->data; - printk(KERN_INFO "%s: p54 detected a LM%c%c " - "firmware\n", - wiphy_name(dev->wiphy), - iftype[2], iftype[3]); - break; - } - case FW_FMAC: - default: - printk(KERN_ERR "%s: unsupported firmware\n", - wiphy_name(dev->wiphy)); - return -ENODEV; - } - break; - case BR_CODE_COMPONENT_VERSION: - /* 24 bytes should be enough for all firmwares */ - if (strnlen((unsigned char*)bootrec->data, 24) < 24) - fw_version = (unsigned char*)bootrec->data; - break; - case BR_CODE_DESCR: { - struct bootrec_desc *desc = - (struct bootrec_desc *)bootrec->data; - priv->rx_start = le32_to_cpu(desc->rx_start); - /* FIXME add sanity checking */ - priv->rx_end = le32_to_cpu(desc->rx_end) - 0x3500; - priv->headroom = desc->headroom; - priv->tailroom = desc->tailroom; - priv->privacy_caps = desc->privacy_caps; - priv->rx_keycache_size = desc->rx_keycache_size; - if (le32_to_cpu(bootrec->len) == 11) - priv->rx_mtu = le16_to_cpu(desc->rx_mtu); - else - priv->rx_mtu = (size_t) - 0x620 - priv->tx_hdr_len; - maxlen = priv->tx_hdr_len + /* USB devices */ - sizeof(struct p54_rx_data) + - 4 + /* rx alignment */ - IEEE80211_MAX_FRAG_THRESHOLD; - if (priv->rx_mtu > maxlen && PAGE_SIZE == 4096) { - printk(KERN_INFO "p54: rx_mtu reduced from %d " - "to %d\n", priv->rx_mtu, - maxlen); - priv->rx_mtu = maxlen; - } - break; - } - case BR_CODE_EXPOSED_IF: - exp_if = (struct bootrec_exp_if *) bootrec->data; - for (i = 0; i < (len * sizeof(*exp_if) / 4); i++) - if (exp_if[i].if_id == cpu_to_le16(0x1a)) - priv->fw_var = le16_to_cpu(exp_if[i].variant); - break; - case BR_CODE_DEPENDENT_IF: - break; - case BR_CODE_END_OF_BRA: - case LEGACY_BR_CODE_END_OF_BRA: - end_data = NULL; - break; - default: - break; - } - bootrec = (struct bootrec *)&bootrec->data[len]; - } - - if (fw_version) - printk(KERN_INFO "%s: FW rev %s - Softmac protocol %x.%x\n", - wiphy_name(dev->wiphy), fw_version, - priv->fw_var >> 8, priv->fw_var & 0xff); - - if (priv->fw_var < 0x500) - printk(KERN_INFO "%s: you are using an obsolete firmware. " - "visit http://wireless.kernel.org/en/users/Drivers/p54 " - "and grab one for \"kernel >= 2.6.28\"!\n", - wiphy_name(dev->wiphy)); - - if (priv->fw_var >= 0x300) { - /* Firmware supports QoS, use it! */ - priv->tx_stats[P54_QUEUE_AC_VO].limit = 3; - priv->tx_stats[P54_QUEUE_AC_VI].limit = 4; - priv->tx_stats[P54_QUEUE_AC_BE].limit = 3; - priv->tx_stats[P54_QUEUE_AC_BK].limit = 2; - dev->queues = P54_QUEUE_AC_NUM; - } - - if (!modparam_nohwcrypt) { - printk(KERN_INFO "%s: cryptographic accelerator " - "WEP:%s, TKIP:%s, CCMP:%s\n", - wiphy_name(dev->wiphy), - (priv->privacy_caps & BR_DESC_PRIV_CAP_WEP) ? "YES" : - "no", (priv->privacy_caps & (BR_DESC_PRIV_CAP_TKIP | - BR_DESC_PRIV_CAP_MICHAEL)) ? "YES" : "no", - (priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP) ? - "YES" : "no"); - - if (priv->rx_keycache_size) { - /* - * NOTE: - * - * The firmware provides at most 255 (0 - 254) slots - * for keys which are then used to offload decryption. - * As a result the 255 entry (aka 0xff) can be used - * safely by the driver to mark keys that didn't fit - * into the full cache. This trick saves us from - * keeping a extra list for uploaded keys. - */ - - priv->used_rxkeys = kzalloc(BITS_TO_LONGS( - priv->rx_keycache_size), GFP_KERNEL); - - if (!priv->used_rxkeys) - return -ENOMEM; - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(p54_parse_firmware); - -static int p54_convert_rev0(struct ieee80211_hw *dev, - struct pda_pa_curve_data *curve_data) -{ - struct p54_common *priv = dev->priv; - struct p54_pa_curve_data_sample *dst; - struct pda_pa_curve_data_sample_rev0 *src; - size_t cd_len = sizeof(*curve_data) + - (curve_data->points_per_channel*sizeof(*dst) + 2) * - curve_data->channels; - unsigned int i, j; - void *source, *target; - - priv->curve_data = kmalloc(sizeof(*priv->curve_data) + cd_len, - GFP_KERNEL); - if (!priv->curve_data) - return -ENOMEM; - - priv->curve_data->entries = curve_data->channels; - priv->curve_data->entry_size = sizeof(__le16) + - sizeof(*dst) * curve_data->points_per_channel; - priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data); - priv->curve_data->len = cd_len; - memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data)); - source = curve_data->data; - target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data; - for (i = 0; i < curve_data->channels; i++) { - __le16 *freq = source; - source += sizeof(__le16); - *((__le16 *)target) = *freq; - target += sizeof(__le16); - for (j = 0; j < curve_data->points_per_channel; j++) { - dst = target; - src = source; - - dst->rf_power = src->rf_power; - dst->pa_detector = src->pa_detector; - dst->data_64qam = src->pcv; - /* "invent" the points for the other modulations */ -#define SUB(x,y) (u8)((x) - (y)) > (x) ? 0 : (x) - (y) - dst->data_16qam = SUB(src->pcv, 12); - dst->data_qpsk = SUB(dst->data_16qam, 12); - dst->data_bpsk = SUB(dst->data_qpsk, 12); - dst->data_barker = SUB(dst->data_bpsk, 14); -#undef SUB - target += sizeof(*dst); - source += sizeof(*src); - } - } - - return 0; -} - -static int p54_convert_rev1(struct ieee80211_hw *dev, - struct pda_pa_curve_data *curve_data) -{ - struct p54_common *priv = dev->priv; - struct p54_pa_curve_data_sample *dst; - struct pda_pa_curve_data_sample_rev1 *src; - size_t cd_len = sizeof(*curve_data) + - (curve_data->points_per_channel*sizeof(*dst) + 2) * - curve_data->channels; - unsigned int i, j; - void *source, *target; - - priv->curve_data = kzalloc(cd_len + sizeof(*priv->curve_data), - GFP_KERNEL); - if (!priv->curve_data) - return -ENOMEM; - - priv->curve_data->entries = curve_data->channels; - priv->curve_data->entry_size = sizeof(__le16) + - sizeof(*dst) * curve_data->points_per_channel; - priv->curve_data->offset = offsetof(struct pda_pa_curve_data, data); - priv->curve_data->len = cd_len; - memcpy(priv->curve_data->data, curve_data, sizeof(*curve_data)); - source = curve_data->data; - target = ((struct pda_pa_curve_data *) priv->curve_data->data)->data; - for (i = 0; i < curve_data->channels; i++) { - __le16 *freq = source; - source += sizeof(__le16); - *((__le16 *)target) = *freq; - target += sizeof(__le16); - for (j = 0; j < curve_data->points_per_channel; j++) { - memcpy(target, source, sizeof(*src)); - - target += sizeof(*dst); - source += sizeof(*src); - } - source++; - } - - return 0; -} - -static const char *p54_rf_chips[] = { "NULL", "Duette3", "Duette2", - "Frisbee", "Xbow", "Longbow", "NULL", "NULL" }; -static int p54_init_xbow_synth(struct ieee80211_hw *dev); - -static void p54_parse_rssical(struct ieee80211_hw *dev, void *data, int len, - u16 type) -{ - struct p54_common *priv = dev->priv; - int offset = (type == PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED) ? 2 : 0; - int entry_size = sizeof(struct pda_rssi_cal_entry) + offset; - int num_entries = (type == PDR_RSSI_LINEAR_APPROXIMATION) ? 1 : 2; - int i; - - if (len != (entry_size * num_entries)) { - printk(KERN_ERR "%s: unknown rssi calibration data packing " - " type:(%x) len:%d.\n", - wiphy_name(dev->wiphy), type, len); - - print_hex_dump_bytes("rssical:", DUMP_PREFIX_NONE, - data, len); - - printk(KERN_ERR "%s: please report this issue.\n", - wiphy_name(dev->wiphy)); - return; - } - - for (i = 0; i < num_entries; i++) { - struct pda_rssi_cal_entry *cal = data + - (offset + i * entry_size); - priv->rssical_db[i].mul = (s16) le16_to_cpu(cal->mul); - priv->rssical_db[i].add = (s16) le16_to_cpu(cal->add); - } -} - -static void p54_parse_default_country(struct ieee80211_hw *dev, - void *data, int len) -{ - struct pda_country *country; - - if (len != sizeof(*country)) { - printk(KERN_ERR "%s: found possible invalid default country " - "eeprom entry. (entry size: %d)\n", - wiphy_name(dev->wiphy), len); - - print_hex_dump_bytes("country:", DUMP_PREFIX_NONE, - data, len); - - printk(KERN_ERR "%s: please report this issue.\n", - wiphy_name(dev->wiphy)); - return; - } - - country = (struct pda_country *) data; - if (country->flags == PDR_COUNTRY_CERT_CODE_PSEUDO) - regulatory_hint(dev->wiphy, country->alpha2); - else { - /* TODO: - * write a shared/common function that converts - * "Regulatory domain codes" (802.11-2007 14.8.2.2) - * into ISO/IEC 3166-1 alpha2 for regulatory_hint. - */ - } -} - -static int p54_convert_output_limits(struct ieee80211_hw *dev, - u8 *data, size_t len) -{ - struct p54_common *priv = dev->priv; - - if (len < 2) - return -EINVAL; - - if (data[0] != 0) { - printk(KERN_ERR "%s: unknown output power db revision:%x\n", - wiphy_name(dev->wiphy), data[0]); - return -EINVAL; - } - - if (2 + data[1] * sizeof(struct pda_channel_output_limit) > len) - return -EINVAL; - - priv->output_limit = kmalloc(data[1] * - sizeof(struct pda_channel_output_limit) + - sizeof(*priv->output_limit), GFP_KERNEL); - - if (!priv->output_limit) - return -ENOMEM; - - priv->output_limit->offset = 0; - priv->output_limit->entries = data[1]; - priv->output_limit->entry_size = - sizeof(struct pda_channel_output_limit); - priv->output_limit->len = priv->output_limit->entry_size * - priv->output_limit->entries + - priv->output_limit->offset; - - memcpy(priv->output_limit->data, &data[2], - data[1] * sizeof(struct pda_channel_output_limit)); - - return 0; -} - -static struct p54_cal_database *p54_convert_db(struct pda_custom_wrapper *src, - size_t total_len) -{ - struct p54_cal_database *dst; - size_t payload_len, entries, entry_size, offset; - - payload_len = le16_to_cpu(src->len); - entries = le16_to_cpu(src->entries); - entry_size = le16_to_cpu(src->entry_size); - offset = le16_to_cpu(src->offset); - if (((entries * entry_size + offset) != payload_len) || - (payload_len + sizeof(*src) != total_len)) - return NULL; - - dst = kmalloc(sizeof(*dst) + payload_len, GFP_KERNEL); - if (!dst) - return NULL; - - dst->entries = entries; - dst->entry_size = entry_size; - dst->offset = offset; - dst->len = payload_len; - - memcpy(dst->data, src->data, payload_len); - return dst; -} - -int p54_parse_eeprom(struct ieee80211_hw *dev, void *eeprom, int len) -{ - struct p54_common *priv = dev->priv; - struct eeprom_pda_wrap *wrap = NULL; - struct pda_entry *entry; - unsigned int data_len, entry_len; - void *tmp; - int err; - u8 *end = (u8 *)eeprom + len; - u16 synth = 0; - - wrap = (struct eeprom_pda_wrap *) eeprom; - entry = (void *)wrap->data + le16_to_cpu(wrap->len); - - /* verify that at least the entry length/code fits */ - while ((u8 *)entry <= end - sizeof(*entry)) { - entry_len = le16_to_cpu(entry->len); - data_len = ((entry_len - 1) << 1); - - /* abort if entry exceeds whole structure */ - if ((u8 *)entry + sizeof(*entry) + data_len > end) - break; - - switch (le16_to_cpu(entry->code)) { - case PDR_MAC_ADDRESS: - if (data_len != ETH_ALEN) - break; - SET_IEEE80211_PERM_ADDR(dev, entry->data); - break; - case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS: - if (priv->output_limit) - break; - err = p54_convert_output_limits(dev, entry->data, - data_len); - if (err) - goto err; - break; - case PDR_PRISM_PA_CAL_CURVE_DATA: { - struct pda_pa_curve_data *curve_data = - (struct pda_pa_curve_data *)entry->data; - if (data_len < sizeof(*curve_data)) { - err = -EINVAL; - goto err; - } - - switch (curve_data->cal_method_rev) { - case 0: - err = p54_convert_rev0(dev, curve_data); - break; - case 1: - err = p54_convert_rev1(dev, curve_data); - break; - default: - printk(KERN_ERR "%s: unknown curve data " - "revision %d\n", - wiphy_name(dev->wiphy), - curve_data->cal_method_rev); - err = -ENODEV; - break; - } - if (err) - goto err; - } - break; - case PDR_PRISM_ZIF_TX_IQ_CALIBRATION: - priv->iq_autocal = kmalloc(data_len, GFP_KERNEL); - if (!priv->iq_autocal) { - err = -ENOMEM; - goto err; - } - - memcpy(priv->iq_autocal, entry->data, data_len); - priv->iq_autocal_len = data_len / sizeof(struct pda_iq_autocal_entry); - break; - case PDR_DEFAULT_COUNTRY: - p54_parse_default_country(dev, entry->data, data_len); - break; - case PDR_INTERFACE_LIST: - tmp = entry->data; - while ((u8 *)tmp < entry->data + data_len) { - struct bootrec_exp_if *exp_if = tmp; - if (le16_to_cpu(exp_if->if_id) == 0xf) - synth = le16_to_cpu(exp_if->variant); - tmp += sizeof(struct bootrec_exp_if); - } - break; - case PDR_HARDWARE_PLATFORM_COMPONENT_ID: - if (data_len < 2) - break; - priv->version = *(u8 *)(entry->data + 1); - break; - case PDR_RSSI_LINEAR_APPROXIMATION: - case PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND: - case PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED: - p54_parse_rssical(dev, entry->data, data_len, - le16_to_cpu(entry->code)); - break; - case PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM: { - __le16 *src = (void *) entry->data; - s16 *dst = (void *) &priv->rssical_db; - int i; - - if (data_len != sizeof(priv->rssical_db)) { - err = -EINVAL; - goto err; - } - for (i = 0; i < sizeof(priv->rssical_db) / - sizeof(*src); i++) - *(dst++) = (s16) le16_to_cpu(*(src++)); - } - break; - case PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM: { - struct pda_custom_wrapper *pda = (void *) entry->data; - if (priv->output_limit || data_len < sizeof(*pda)) - break; - priv->output_limit = p54_convert_db(pda, data_len); - } - break; - case PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM: { - struct pda_custom_wrapper *pda = (void *) entry->data; - if (priv->curve_data || data_len < sizeof(*pda)) - break; - priv->curve_data = p54_convert_db(pda, data_len); - } - break; - case PDR_END: - /* make it overrun */ - entry_len = len; - break; - case PDR_MANUFACTURING_PART_NUMBER: - case PDR_PDA_VERSION: - case PDR_NIC_SERIAL_NUMBER: - case PDR_REGULATORY_DOMAIN_LIST: - case PDR_TEMPERATURE_TYPE: - case PDR_PRISM_PCI_IDENTIFIER: - case PDR_COUNTRY_INFORMATION: - case PDR_OEM_NAME: - case PDR_PRODUCT_NAME: - case PDR_UTF8_OEM_NAME: - case PDR_UTF8_PRODUCT_NAME: - case PDR_COUNTRY_LIST: - case PDR_ANTENNA_GAIN: - case PDR_PRISM_INDIGO_PA_CALIBRATION_DATA: - case PDR_REGULATORY_POWER_LIMITS: - case PDR_RADIATED_TRANSMISSION_CORRECTION: - case PDR_PRISM_TX_IQ_CALIBRATION: - case PDR_BASEBAND_REGISTERS: - case PDR_PER_CHANNEL_BASEBAND_REGISTERS: - break; - default: - printk(KERN_INFO "%s: unknown eeprom code : 0x%x\n", - wiphy_name(dev->wiphy), - le16_to_cpu(entry->code)); - break; - } - - entry = (void *)entry + (entry_len + 1)*2; - } - - if (!synth || !priv->iq_autocal || !priv->output_limit || - !priv->curve_data) { - printk(KERN_ERR "%s: not all required entries found in eeprom!\n", - wiphy_name(dev->wiphy)); - err = -EINVAL; - goto err; - } - - priv->rxhw = synth & PDR_SYNTH_FRONTEND_MASK; - if (priv->rxhw == PDR_SYNTH_FRONTEND_XBOW) - p54_init_xbow_synth(dev); - if (!(synth & PDR_SYNTH_24_GHZ_DISABLED)) - dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &band_2GHz; - if (!(synth & PDR_SYNTH_5_GHZ_DISABLED)) - dev->wiphy->bands[IEEE80211_BAND_5GHZ] = &band_5GHz; - if ((synth & PDR_SYNTH_RX_DIV_MASK) == PDR_SYNTH_RX_DIV_SUPPORTED) - priv->rx_diversity_mask = 3; - if ((synth & PDR_SYNTH_TX_DIV_MASK) == PDR_SYNTH_TX_DIV_SUPPORTED) - priv->tx_diversity_mask = 3; - - if (!is_valid_ether_addr(dev->wiphy->perm_addr)) { - u8 perm_addr[ETH_ALEN]; - - printk(KERN_WARNING "%s: Invalid hwaddr! Using randomly generated MAC addr\n", - wiphy_name(dev->wiphy)); - random_ether_addr(perm_addr); - SET_IEEE80211_PERM_ADDR(dev, perm_addr); - } - - printk(KERN_INFO "%s: hwaddr %pM, MAC:isl38%02x RF:%s\n", - wiphy_name(dev->wiphy), - dev->wiphy->perm_addr, - priv->version, p54_rf_chips[priv->rxhw]); - - return 0; - - err: - if (priv->iq_autocal) { - kfree(priv->iq_autocal); - priv->iq_autocal = NULL; - } - - if (priv->output_limit) { - kfree(priv->output_limit); - priv->output_limit = NULL; - } - - if (priv->curve_data) { - kfree(priv->curve_data); - priv->curve_data = NULL; - } - - printk(KERN_ERR "%s: eeprom parse failed!\n", - wiphy_name(dev->wiphy)); - return err; -} -EXPORT_SYMBOL_GPL(p54_parse_eeprom); - -static int p54_rssi_to_dbm(struct ieee80211_hw *dev, int rssi) -{ - struct p54_common *priv = dev->priv; - int band = dev->conf.channel->band; - - if (priv->rxhw != PDR_SYNTH_FRONTEND_LONGBOW) - return ((rssi * priv->rssical_db[band].mul) / 64 + - priv->rssical_db[band].add) / 4; - else - /* - * TODO: find the correct formula - */ - return ((rssi * priv->rssical_db[band].mul) / 64 + - priv->rssical_db[band].add) / 4; -} - -static int p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct p54_common *priv = dev->priv; - struct p54_rx_data *hdr = (struct p54_rx_data *) skb->data; - struct ieee80211_rx_status rx_status = {0}; - u16 freq = le16_to_cpu(hdr->freq); - size_t header_len = sizeof(*hdr); - u32 tsf32; - u8 rate = hdr->rate & 0xf; - - /* - * If the device is in a unspecified state we have to - * ignore all data frames. Else we could end up with a - * nasty crash. - */ - if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) - return 0; - - if (!(hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_IN_FCS_GOOD))) { - return 0; - } - - if (hdr->decrypt_status == P54_DECRYPT_OK) - rx_status.flag |= RX_FLAG_DECRYPTED; - if ((hdr->decrypt_status == P54_DECRYPT_FAIL_MICHAEL) || - (hdr->decrypt_status == P54_DECRYPT_FAIL_TKIP)) - rx_status.flag |= RX_FLAG_MMIC_ERROR; - - rx_status.signal = p54_rssi_to_dbm(dev, hdr->rssi); - rx_status.noise = priv->noise; - if (hdr->rate & 0x10) - rx_status.flag |= RX_FLAG_SHORTPRE; - if (dev->conf.channel->band == IEEE80211_BAND_5GHZ) - rx_status.rate_idx = (rate < 4) ? 0 : rate - 4; - else - rx_status.rate_idx = rate; - - rx_status.freq = freq; - rx_status.band = dev->conf.channel->band; - rx_status.antenna = hdr->antenna; - - tsf32 = le32_to_cpu(hdr->tsf32); - if (tsf32 < priv->tsf_low32) - priv->tsf_high32++; - rx_status.mactime = ((u64)priv->tsf_high32) << 32 | tsf32; - priv->tsf_low32 = tsf32; - - rx_status.flag |= RX_FLAG_TSFT; - - if (hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) - header_len += hdr->align[0]; - - skb_pull(skb, header_len); - skb_trim(skb, le16_to_cpu(hdr->len)); - - ieee80211_rx_irqsafe(dev, skb, &rx_status); - - queue_delayed_work(dev->workqueue, &priv->work, - msecs_to_jiffies(P54_STATISTICS_UPDATE)); - - return -1; -} - -static void inline p54_wake_free_queues(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - int i; - - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) - return ; - - for (i = 0; i < dev->queues; i++) - if (priv->tx_stats[i + P54_QUEUE_DATA].len < - priv->tx_stats[i + P54_QUEUE_DATA].limit) - ieee80211_wake_queue(dev, i); -} - -void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct p54_common *priv = dev->priv; - struct ieee80211_tx_info *info; - struct p54_tx_info *range; - unsigned long flags; - - if (unlikely(!skb || !dev || !skb_queue_len(&priv->tx_queue))) - return; - - /* - * don't try to free an already unlinked skb - */ - if (unlikely((!skb->next) || (!skb->prev))) - return; - - spin_lock_irqsave(&priv->tx_queue.lock, flags); - info = IEEE80211_SKB_CB(skb); - range = (void *)info->rate_driver_data; - if (skb->prev != (struct sk_buff *)&priv->tx_queue) { - struct ieee80211_tx_info *ni; - struct p54_tx_info *mr; - - ni = IEEE80211_SKB_CB(skb->prev); - mr = (struct p54_tx_info *)ni->rate_driver_data; - } - if (skb->next != (struct sk_buff *)&priv->tx_queue) { - struct ieee80211_tx_info *ni; - struct p54_tx_info *mr; - - ni = IEEE80211_SKB_CB(skb->next); - mr = (struct p54_tx_info *)ni->rate_driver_data; - } - __skb_unlink(skb, &priv->tx_queue); - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); - dev_kfree_skb_any(skb); - p54_wake_free_queues(dev); -} -EXPORT_SYMBOL_GPL(p54_free_skb); - -static struct sk_buff *p54_find_tx_entry(struct ieee80211_hw *dev, - __le32 req_id) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *entry; - unsigned long flags; - - spin_lock_irqsave(&priv->tx_queue.lock, flags); - entry = priv->tx_queue.next; - while (entry != (struct sk_buff *)&priv->tx_queue) { - struct p54_hdr *hdr = (struct p54_hdr *) entry->data; - - if (hdr->req_id == req_id) { - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); - return entry; - } - entry = entry->next; - } - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); - return NULL; -} - -static void p54_rx_frame_sent(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct p54_common *priv = dev->priv; - struct p54_hdr *hdr = (struct p54_hdr *) skb->data; - struct p54_frame_sent *payload = (struct p54_frame_sent *) hdr->data; - struct sk_buff *entry; - u32 addr = le32_to_cpu(hdr->req_id) - priv->headroom; - struct p54_tx_info *range = NULL; - unsigned long flags; - int count, idx; - - spin_lock_irqsave(&priv->tx_queue.lock, flags); - entry = (struct sk_buff *) priv->tx_queue.next; - while (entry != (struct sk_buff *)&priv->tx_queue) { - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(entry); - struct p54_hdr *entry_hdr; - struct p54_tx_data *entry_data; - unsigned int pad = 0, frame_len; - - range = (void *)info->rate_driver_data; - if (range->start_addr != addr) { - entry = entry->next; - continue; - } - - if (entry->next != (struct sk_buff *)&priv->tx_queue) { - struct ieee80211_tx_info *ni; - struct p54_tx_info *mr; - - ni = IEEE80211_SKB_CB(entry->next); - mr = (struct p54_tx_info *)ni->rate_driver_data; - } - - __skb_unlink(entry, &priv->tx_queue); - - frame_len = entry->len; - entry_hdr = (struct p54_hdr *) entry->data; - entry_data = (struct p54_tx_data *) entry_hdr->data; - if (priv->tx_stats[entry_data->hw_queue].len) - priv->tx_stats[entry_data->hw_queue].len--; - priv->stats.dot11ACKFailureCount += payload->tries - 1; - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); - - /* - * Frames in P54_QUEUE_FWSCAN and P54_QUEUE_BEACON are - * generated by the driver. Therefore tx_status is bogus - * and we don't want to confuse the mac80211 stack. - */ - if (unlikely(entry_data->hw_queue < P54_QUEUE_FWSCAN)) { - if (entry_data->hw_queue == P54_QUEUE_BEACON) - priv->cached_beacon = NULL; - - kfree_skb(entry); - goto out; - } - - /* - * Clear manually, ieee80211_tx_info_clear_status would - * clear the counts too and we need them. - */ - memset(&info->status.ampdu_ack_len, 0, - sizeof(struct ieee80211_tx_info) - - offsetof(struct ieee80211_tx_info, status.ampdu_ack_len)); - BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, - status.ampdu_ack_len) != 23); - - if (entry_hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) - pad = entry_data->align[0]; - - /* walk through the rates array and adjust the counts */ - count = payload->tries; - for (idx = 0; idx < 4; idx++) { - if (count >= info->status.rates[idx].count) { - count -= info->status.rates[idx].count; - } else if (count > 0) { - info->status.rates[idx].count = count; - count = 0; - } else { - info->status.rates[idx].idx = -1; - info->status.rates[idx].count = 0; - } - } - - if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && - (!payload->status)) - info->flags |= IEEE80211_TX_STAT_ACK; - if (payload->status & P54_TX_PSM_CANCELLED) - info->flags |= IEEE80211_TX_STAT_TX_FILTERED; - info->status.ack_signal = p54_rssi_to_dbm(dev, - (int)payload->ack_rssi); - - /* Undo all changes to the frame. */ - switch (entry_data->key_type) { - case P54_CRYPTO_TKIPMICHAEL: { - u8 *iv = (u8 *)(entry_data->align + pad + - entry_data->crypt_offset); - - /* Restore the original TKIP IV. */ - iv[2] = iv[0]; - iv[0] = iv[1]; - iv[1] = (iv[0] | 0x20) & 0x7f; /* WEPSeed - 8.3.2.2 */ - - frame_len -= 12; /* remove TKIP_MMIC + TKIP_ICV */ - break; - } - case P54_CRYPTO_AESCCMP: - frame_len -= 8; /* remove CCMP_MIC */ - break; - case P54_CRYPTO_WEP: - frame_len -= 4; /* remove WEP_ICV */ - break; - } - skb_trim(entry, frame_len); - skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data)); - ieee80211_tx_status_irqsafe(dev, entry); - goto out; - } - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); - -out: - p54_wake_free_queues(dev); -} - -static void p54_rx_eeprom_readback(struct ieee80211_hw *dev, - struct sk_buff *skb) -{ - struct p54_hdr *hdr = (struct p54_hdr *) skb->data; - struct p54_eeprom_lm86 *eeprom = (struct p54_eeprom_lm86 *) hdr->data; - struct p54_common *priv = dev->priv; - - if (!priv->eeprom) - return ; - - if (priv->fw_var >= 0x509) { - memcpy(priv->eeprom, eeprom->v2.data, - le16_to_cpu(eeprom->v2.len)); - } else { - memcpy(priv->eeprom, eeprom->v1.data, - le16_to_cpu(eeprom->v1.len)); - } - - complete(&priv->eeprom_comp); -} - -static void p54_rx_stats(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct p54_common *priv = dev->priv; - struct p54_hdr *hdr = (struct p54_hdr *) skb->data; - struct p54_statistics *stats = (struct p54_statistics *) hdr->data; - u32 tsf32; - - if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) - return ; - - tsf32 = le32_to_cpu(stats->tsf32); - if (tsf32 < priv->tsf_low32) - priv->tsf_high32++; - priv->tsf_low32 = tsf32; - - priv->stats.dot11RTSFailureCount = le32_to_cpu(stats->rts_fail); - priv->stats.dot11RTSSuccessCount = le32_to_cpu(stats->rts_success); - priv->stats.dot11FCSErrorCount = le32_to_cpu(stats->rx_bad_fcs); - - priv->noise = p54_rssi_to_dbm(dev, le32_to_cpu(stats->noise)); - - p54_free_skb(dev, p54_find_tx_entry(dev, hdr->req_id)); -} - -static void p54_rx_trap(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct p54_common *priv = dev->priv; - struct p54_hdr *hdr = (struct p54_hdr *) skb->data; - struct p54_trap *trap = (struct p54_trap *) hdr->data; - u16 event = le16_to_cpu(trap->event); - u16 freq = le16_to_cpu(trap->frequency); - - switch (event) { - case P54_TRAP_BEACON_TX: - break; - case P54_TRAP_RADAR: - printk(KERN_INFO "%s: radar (freq:%d MHz)\n", - wiphy_name(dev->wiphy), freq); - break; - case P54_TRAP_NO_BEACON: - if (priv->vif) - ieee80211_beacon_loss(priv->vif); - break; - case P54_TRAP_SCAN: - break; - case P54_TRAP_TBTT: - break; - case P54_TRAP_TIMER: - break; - default: - printk(KERN_INFO "%s: received event:%x freq:%d\n", - wiphy_name(dev->wiphy), event, freq); - break; - } -} - -static int p54_rx_control(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct p54_hdr *hdr = (struct p54_hdr *) skb->data; - - switch (le16_to_cpu(hdr->type)) { - case P54_CONTROL_TYPE_TXDONE: - p54_rx_frame_sent(dev, skb); - break; - case P54_CONTROL_TYPE_TRAP: - p54_rx_trap(dev, skb); - break; - case P54_CONTROL_TYPE_BBP: - break; - case P54_CONTROL_TYPE_STAT_READBACK: - p54_rx_stats(dev, skb); - break; - case P54_CONTROL_TYPE_EEPROM_READBACK: - p54_rx_eeprom_readback(dev, skb); - break; - default: - printk(KERN_DEBUG "%s: not handling 0x%02x type control frame\n", - wiphy_name(dev->wiphy), le16_to_cpu(hdr->type)); - break; - } - - return 0; -} - -/* returns zero if skb can be reused */ -int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - u16 type = le16_to_cpu(*((__le16 *)skb->data)); - - if (type & P54_HDR_FLAG_CONTROL) - return p54_rx_control(dev, skb); - else - return p54_rx_data(dev, skb); -} -EXPORT_SYMBOL_GPL(p54_rx); - -/* - * So, the firmware is somewhat stupid and doesn't know what places in its - * memory incoming data should go to. By poking around in the firmware, we - * can find some unused memory to upload our packets to. However, data that we - * want the card to TX needs to stay intact until the card has told us that - * it is done with it. This function finds empty places we can upload to and - * marks allocated areas as reserved if necessary. p54_rx_frame_sent or - * p54_free_skb frees allocated areas. - */ -static int p54_assign_address(struct ieee80211_hw *dev, struct sk_buff *skb, - struct p54_hdr *data, u32 len) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *entry; - struct sk_buff *target_skb = NULL; - struct ieee80211_tx_info *info; - struct p54_tx_info *range; - u32 last_addr = priv->rx_start; - u32 largest_hole = 0; - u32 target_addr = priv->rx_start; - unsigned long flags; - unsigned int left; - len = (len + priv->headroom + priv->tailroom + 3) & ~0x3; - - if (!skb) - return -EINVAL; - - spin_lock_irqsave(&priv->tx_queue.lock, flags); - - left = skb_queue_len(&priv->tx_queue); - if (unlikely(left >= 28)) { - /* - * The tx_queue is nearly full! - * We have throttle normal data traffic, because we must - * have a few spare slots for control frames left. - */ - ieee80211_stop_queues(dev); - queue_delayed_work(dev->workqueue, &priv->work, - msecs_to_jiffies(P54_TX_TIMEOUT)); - - if (unlikely(left == 32)) { - /* - * The tx_queue is now really full. - * - * TODO: check if the device has crashed and reset it. - */ - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); - return -ENOSPC; - } - } - - entry = priv->tx_queue.next; - while (left--) { - u32 hole_size; - info = IEEE80211_SKB_CB(entry); - range = (void *)info->rate_driver_data; - hole_size = range->start_addr - last_addr; - if (!target_skb && hole_size >= len) { - target_skb = entry->prev; - hole_size -= len; - target_addr = last_addr; - } - largest_hole = max(largest_hole, hole_size); - last_addr = range->end_addr; - entry = entry->next; - } - if (!target_skb && priv->rx_end - last_addr >= len) { - target_skb = priv->tx_queue.prev; - largest_hole = max(largest_hole, priv->rx_end - last_addr - len); - if (!skb_queue_empty(&priv->tx_queue)) { - info = IEEE80211_SKB_CB(target_skb); - range = (void *)info->rate_driver_data; - target_addr = range->end_addr; - } - } else - largest_hole = max(largest_hole, priv->rx_end - last_addr); - - if (!target_skb) { - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); - ieee80211_stop_queues(dev); - return -ENOSPC; - } - - info = IEEE80211_SKB_CB(skb); - range = (void *)info->rate_driver_data; - range->start_addr = target_addr; - range->end_addr = target_addr + len; - __skb_queue_after(&priv->tx_queue, target_skb, skb); - spin_unlock_irqrestore(&priv->tx_queue.lock, flags); - - if (largest_hole < priv->headroom + sizeof(struct p54_hdr) + - 48 + IEEE80211_MAX_RTS_THRESHOLD + priv->tailroom) - ieee80211_stop_queues(dev); - - data->req_id = cpu_to_le32(target_addr + priv->headroom); - return 0; -} - -static struct sk_buff *p54_alloc_skb(struct ieee80211_hw *dev, u16 hdr_flags, - u16 payload_len, u16 type, gfp_t memflags) -{ - struct p54_common *priv = dev->priv; - struct p54_hdr *hdr; - struct sk_buff *skb; - size_t frame_len = sizeof(*hdr) + payload_len; - - if (frame_len > P54_MAX_CTRL_FRAME_LEN) - return NULL; - - skb = __dev_alloc_skb(priv->tx_hdr_len + frame_len, memflags); - if (!skb) - return NULL; - skb_reserve(skb, priv->tx_hdr_len); - - hdr = (struct p54_hdr *) skb_put(skb, sizeof(*hdr)); - hdr->flags = cpu_to_le16(hdr_flags); - hdr->len = cpu_to_le16(payload_len); - hdr->type = cpu_to_le16(type); - hdr->tries = hdr->rts_tries = 0; - - if (p54_assign_address(dev, skb, hdr, frame_len)) { - kfree_skb(skb); - return NULL; - } - return skb; -} - -int p54_read_eeprom(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - struct p54_eeprom_lm86 *eeprom_hdr; - struct sk_buff *skb; - size_t eeprom_size = 0x2020, offset = 0, blocksize, maxblocksize; - int ret = -ENOMEM; - void *eeprom = NULL; - - maxblocksize = EEPROM_READBACK_LEN; - if (priv->fw_var >= 0x509) - maxblocksize -= 0xc; - else - maxblocksize -= 0x4; - - skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL, sizeof(*eeprom_hdr) + - maxblocksize, P54_CONTROL_TYPE_EEPROM_READBACK, - GFP_KERNEL); - if (!skb) - goto free; - priv->eeprom = kzalloc(EEPROM_READBACK_LEN, GFP_KERNEL); - if (!priv->eeprom) - goto free; - eeprom = kzalloc(eeprom_size, GFP_KERNEL); - if (!eeprom) - goto free; - - eeprom_hdr = (struct p54_eeprom_lm86 *) skb_put(skb, - sizeof(*eeprom_hdr) + maxblocksize); - - while (eeprom_size) { - blocksize = min(eeprom_size, maxblocksize); - if (priv->fw_var < 0x509) { - eeprom_hdr->v1.offset = cpu_to_le16(offset); - eeprom_hdr->v1.len = cpu_to_le16(blocksize); - } else { - eeprom_hdr->v2.offset = cpu_to_le32(offset); - eeprom_hdr->v2.len = cpu_to_le16(blocksize); - eeprom_hdr->v2.magic2 = 0xf; - memcpy(eeprom_hdr->v2.magic, (const char *)"LOCK", 4); - } - priv->tx(dev, skb); - - if (!wait_for_completion_interruptible_timeout(&priv->eeprom_comp, HZ)) { - printk(KERN_ERR "%s: device does not respond!\n", - wiphy_name(dev->wiphy)); - ret = -EBUSY; - goto free; - } - - memcpy(eeprom + offset, priv->eeprom, blocksize); - offset += blocksize; - eeprom_size -= blocksize; - } - - ret = p54_parse_eeprom(dev, eeprom, offset); -free: - kfree(priv->eeprom); - priv->eeprom = NULL; - p54_free_skb(dev, skb); - kfree(eeprom); - - return ret; -} -EXPORT_SYMBOL_GPL(p54_read_eeprom); - -static int p54_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, - bool set) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *skb; - struct p54_tim *tim; - - skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*tim), - P54_CONTROL_TYPE_TIM, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - tim = (struct p54_tim *) skb_put(skb, sizeof(*tim)); - tim->count = 1; - tim->entry[0] = cpu_to_le16(set ? (sta->aid | 0x8000) : sta->aid); - priv->tx(dev, skb); - return 0; -} - -static int p54_sta_unlock(struct ieee80211_hw *dev, u8 *addr) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *skb; - struct p54_sta_unlock *sta; - - skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*sta), - P54_CONTROL_TYPE_PSM_STA_UNLOCK, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - sta = (struct p54_sta_unlock *)skb_put(skb, sizeof(*sta)); - memcpy(sta->addr, addr, ETH_ALEN); - priv->tx(dev, skb); - return 0; -} - -static void p54_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, - enum sta_notify_cmd notify_cmd, - struct ieee80211_sta *sta) -{ - switch (notify_cmd) { - case STA_NOTIFY_ADD: - case STA_NOTIFY_REMOVE: - /* - * Notify the firmware that we don't want or we don't - * need to buffer frames for this station anymore. - */ - - p54_sta_unlock(dev, sta->addr); - break; - case STA_NOTIFY_AWAKE: - /* update the firmware's filter table */ - p54_sta_unlock(dev, sta->addr); - break; - default: - break; - } -} - -static int p54_tx_cancel(struct ieee80211_hw *dev, struct sk_buff *entry) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *skb; - struct p54_hdr *hdr; - struct p54_txcancel *cancel; - - skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*cancel), - P54_CONTROL_TYPE_TXCANCEL, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - hdr = (void *)entry->data; - cancel = (struct p54_txcancel *)skb_put(skb, sizeof(*cancel)); - cancel->req_id = hdr->req_id; - priv->tx(dev, skb); - return 0; -} - -static int p54_tx_fill(struct ieee80211_hw *dev, struct sk_buff *skb, - struct ieee80211_tx_info *info, u8 *queue, size_t *extra_len, - u16 *flags, u16 *aid) -{ - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct p54_common *priv = dev->priv; - int ret = 1; - - switch (priv->mode) { - case NL80211_IFTYPE_MONITOR: - /* - * We have to set P54_HDR_FLAG_DATA_OUT_PROMISC for - * every frame in promiscuous/monitor mode. - * see STSW45x0C LMAC API - page 12. - */ - *aid = 0; - *flags = P54_HDR_FLAG_DATA_OUT_PROMISC; - *queue += P54_QUEUE_DATA; - break; - case NL80211_IFTYPE_STATION: - *aid = 1; - if (unlikely(ieee80211_is_mgmt(hdr->frame_control))) { - *queue = P54_QUEUE_MGMT; - ret = 0; - } else - *queue += P54_QUEUE_DATA; - break; - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: - if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { - *aid = 0; - *queue = P54_QUEUE_CAB; - return 0; - } - - if (unlikely(ieee80211_is_mgmt(hdr->frame_control))) { - if (ieee80211_is_probe_resp(hdr->frame_control)) { - *aid = 0; - *queue = P54_QUEUE_MGMT; - *flags = P54_HDR_FLAG_DATA_OUT_TIMESTAMP | - P54_HDR_FLAG_DATA_OUT_NOCANCEL; - return 0; - } else if (ieee80211_is_beacon(hdr->frame_control)) { - *aid = 0; - - if (info->flags & IEEE80211_TX_CTL_INJECTED) { - /* - * Injecting beacons on top of a AP is - * not a good idea... nevertheless, - * it should be doable. - */ - - *queue += P54_QUEUE_DATA; - return 1; - } - - *flags = P54_HDR_FLAG_DATA_OUT_TIMESTAMP; - *queue = P54_QUEUE_BEACON; - *extra_len = IEEE80211_MAX_TIM_LEN; - return 0; - } else { - *queue = P54_QUEUE_MGMT; - ret = 0; - } - } else - *queue += P54_QUEUE_DATA; - - if (info->control.sta) - *aid = info->control.sta->aid; - - if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) - *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; - break; - } - return ret; -} - -static u8 p54_convert_algo(enum ieee80211_key_alg alg) -{ - switch (alg) { - case ALG_WEP: - return P54_CRYPTO_WEP; - case ALG_TKIP: - return P54_CRYPTO_TKIPMICHAEL; - case ALG_CCMP: - return P54_CRYPTO_AESCCMP; - default: - return 0; - } -} - -static int p54_tx(struct ieee80211_hw *dev, struct sk_buff *skb) -{ - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_tx_queue_stats *current_queue; - struct p54_common *priv = dev->priv; - struct p54_hdr *hdr; - struct p54_tx_data *txhdr; - size_t padding, len, tim_len = 0; - int i, j, ridx, ret; - u16 hdr_flags = 0, aid = 0; - u8 rate, queue, crypt_offset = 0; - u8 cts_rate = 0x20; - u8 rc_flags; - u8 calculated_tries[4]; - u8 nrates = 0, nremaining = 8; - - queue = skb_get_queue_mapping(skb); - - ret = p54_tx_fill(dev, skb, info, &queue, &tim_len, &hdr_flags, &aid); - current_queue = &priv->tx_stats[queue]; - if (unlikely((current_queue->len > current_queue->limit) && ret)) - return NETDEV_TX_BUSY; - current_queue->len++; - current_queue->count++; - if ((current_queue->len == current_queue->limit) && ret) - ieee80211_stop_queue(dev, skb_get_queue_mapping(skb)); - - padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3; - len = skb->len; - - if (info->control.hw_key) { - crypt_offset = ieee80211_get_hdrlen_from_skb(skb); - if (info->control.hw_key->alg == ALG_TKIP) { - u8 *iv = (u8 *)(skb->data + crypt_offset); - /* - * The firmware excepts that the IV has to have - * this special format - */ - iv[1] = iv[0]; - iv[0] = iv[2]; - iv[2] = 0; - } - } - - txhdr = (struct p54_tx_data *) skb_push(skb, sizeof(*txhdr) + padding); - hdr = (struct p54_hdr *) skb_push(skb, sizeof(*hdr)); - - if (padding) - hdr_flags |= P54_HDR_FLAG_DATA_ALIGN; - hdr->type = cpu_to_le16(aid); - hdr->rts_tries = info->control.rates[0].count; - - /* - * we register the rates in perfect order, and - * RTS/CTS won't happen on 5 GHz - */ - cts_rate = info->control.rts_cts_rate_idx; - - memset(&txhdr->rateset, 0, sizeof(txhdr->rateset)); - - /* see how many rates got used */ - for (i = 0; i < 4; i++) { - if (info->control.rates[i].idx < 0) - break; - nrates++; - } - - /* limit tries to 8/nrates per rate */ - for (i = 0; i < nrates; i++) { - /* - * The magic expression here is equivalent to 8/nrates for - * all values that matter, but avoids division and jumps. - * Note that nrates can only take the values 1 through 4. - */ - calculated_tries[i] = min_t(int, ((15 >> nrates) | 1) + 1, - info->control.rates[i].count); - nremaining -= calculated_tries[i]; - } - - /* if there are tries left, distribute from back to front */ - for (i = nrates - 1; nremaining > 0 && i >= 0; i--) { - int tmp = info->control.rates[i].count - calculated_tries[i]; - - if (tmp <= 0) - continue; - /* RC requested more tries at this rate */ - - tmp = min_t(int, tmp, nremaining); - calculated_tries[i] += tmp; - nremaining -= tmp; - } - - ridx = 0; - for (i = 0; i < nrates && ridx < 8; i++) { - /* we register the rates in perfect order */ - rate = info->control.rates[i].idx; - if (info->band == IEEE80211_BAND_5GHZ) - rate += 4; - - /* store the count we actually calculated for TX status */ - info->control.rates[i].count = calculated_tries[i]; - - rc_flags = info->control.rates[i].flags; - if (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) { - rate |= 0x10; - cts_rate |= 0x10; - } - if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) - rate |= 0x40; - else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) - rate |= 0x20; - for (j = 0; j < calculated_tries[i] && ridx < 8; j++) { - txhdr->rateset[ridx] = rate; - ridx++; - } - } - - if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) - hdr_flags |= P54_HDR_FLAG_DATA_OUT_SEQNR; - - /* TODO: enable bursting */ - hdr->flags = cpu_to_le16(hdr_flags); - hdr->tries = ridx; - txhdr->rts_rate_idx = 0; - if (info->control.hw_key) { - txhdr->key_type = p54_convert_algo(info->control.hw_key->alg); - txhdr->key_len = min((u8)16, info->control.hw_key->keylen); - memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len); - if (info->control.hw_key->alg == ALG_TKIP) { - if (unlikely(skb_tailroom(skb) < 12)) - goto err; - /* reserve space for the MIC key */ - len += 8; - memcpy(skb_put(skb, 8), &(info->control.hw_key->key - [NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]), 8); - } - /* reserve some space for ICV */ - len += info->control.hw_key->icv_len; - memset(skb_put(skb, info->control.hw_key->icv_len), 0, - info->control.hw_key->icv_len); - } else { - txhdr->key_type = 0; - txhdr->key_len = 0; - } - txhdr->crypt_offset = crypt_offset; - txhdr->hw_queue = queue; - txhdr->backlog = current_queue->len; - memset(txhdr->durations, 0, sizeof(txhdr->durations)); - txhdr->tx_antenna = ((info->antenna_sel_tx == 0) ? - 2 : info->antenna_sel_tx - 1) & priv->tx_diversity_mask; - if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { - txhdr->longbow.cts_rate = cts_rate; - txhdr->longbow.output_power = cpu_to_le16(priv->output_power); - } else { - txhdr->normal.output_power = priv->output_power; - txhdr->normal.cts_rate = cts_rate; - } - if (padding) - txhdr->align[0] = padding; - - hdr->len = cpu_to_le16(len); - /* modifies skb->cb and with it info, so must be last! */ - if (unlikely(p54_assign_address(dev, skb, hdr, skb->len + tim_len))) - goto err; - priv->tx(dev, skb); - - queue_delayed_work(dev->workqueue, &priv->work, - msecs_to_jiffies(P54_TX_FRAME_LIFETIME)); - - return NETDEV_TX_OK; - - err: - skb_pull(skb, sizeof(*hdr) + sizeof(*txhdr) + padding); - current_queue->len--; - current_queue->count--; - return NETDEV_TX_BUSY; -} - -static int p54_setup_mac(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *skb; - struct p54_setup_mac *setup; - u16 mode; - - skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*setup), - P54_CONTROL_TYPE_SETUP, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - setup = (struct p54_setup_mac *) skb_put(skb, sizeof(*setup)); - if (dev->conf.radio_enabled) { - switch (priv->mode) { - case NL80211_IFTYPE_STATION: - mode = P54_FILTER_TYPE_STATION; - break; - case NL80211_IFTYPE_AP: - mode = P54_FILTER_TYPE_AP; - break; - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: - mode = P54_FILTER_TYPE_IBSS; - break; - case NL80211_IFTYPE_MONITOR: - mode = P54_FILTER_TYPE_PROMISCUOUS; - break; - default: - mode = P54_FILTER_TYPE_HIBERNATE; - break; - } - - /* - * "TRANSPARENT and PROMISCUOUS are mutually exclusive" - * STSW45X0C LMAC API - page 12 - */ - if (((priv->filter_flags & FIF_PROMISC_IN_BSS) || - (priv->filter_flags & FIF_OTHER_BSS)) && - (mode != P54_FILTER_TYPE_PROMISCUOUS)) - mode |= P54_FILTER_TYPE_TRANSPARENT; - } else - mode = P54_FILTER_TYPE_HIBERNATE; - - setup->mac_mode = cpu_to_le16(mode); - memcpy(setup->mac_addr, priv->mac_addr, ETH_ALEN); - memcpy(setup->bssid, priv->bssid, ETH_ALEN); - setup->rx_antenna = 2 & priv->rx_diversity_mask; /* automatic */ - setup->rx_align = 0; - if (priv->fw_var < 0x500) { - setup->v1.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); - memset(setup->v1.rts_rates, 0, 8); - setup->v1.rx_addr = cpu_to_le32(priv->rx_end); - setup->v1.max_rx = cpu_to_le16(priv->rx_mtu); - setup->v1.rxhw = cpu_to_le16(priv->rxhw); - setup->v1.wakeup_timer = cpu_to_le16(priv->wakeup_timer); - setup->v1.unalloc0 = cpu_to_le16(0); - } else { - setup->v2.rx_addr = cpu_to_le32(priv->rx_end); - setup->v2.max_rx = cpu_to_le16(priv->rx_mtu); - setup->v2.rxhw = cpu_to_le16(priv->rxhw); - setup->v2.timer = cpu_to_le16(priv->wakeup_timer); - setup->v2.truncate = cpu_to_le16(48896); - setup->v2.basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); - setup->v2.sbss_offset = 0; - setup->v2.mcast_window = 0; - setup->v2.rx_rssi_threshold = 0; - setup->v2.rx_ed_threshold = 0; - setup->v2.ref_clock = cpu_to_le32(644245094); - setup->v2.lpf_bandwidth = cpu_to_le16(65535); - setup->v2.osc_start_delay = cpu_to_le16(65535); - } - priv->tx(dev, skb); - return 0; -} - -static int p54_scan(struct ieee80211_hw *dev, u16 mode, u16 dwell) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *skb; - struct p54_hdr *hdr; - struct p54_scan_head *head; - struct p54_iq_autocal_entry *iq_autocal; - union p54_scan_body_union *body; - struct p54_scan_tail_rate *rate; - struct pda_rssi_cal_entry *rssi; - unsigned int i; - void *entry; - int band = dev->conf.channel->band; - __le16 freq = cpu_to_le16(dev->conf.channel->center_freq); - - skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*head) + - 2 + sizeof(*iq_autocal) + sizeof(*body) + - sizeof(*rate) + 2 * sizeof(*rssi), - P54_CONTROL_TYPE_SCAN, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - head = (struct p54_scan_head *) skb_put(skb, sizeof(*head)); - memset(head->scan_params, 0, sizeof(head->scan_params)); - head->mode = cpu_to_le16(mode); - head->dwell = cpu_to_le16(dwell); - head->freq = freq; - - if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { - __le16 *pa_power_points = (__le16 *) skb_put(skb, 2); - *pa_power_points = cpu_to_le16(0x0c); - } - - iq_autocal = (void *) skb_put(skb, sizeof(*iq_autocal)); - for (i = 0; i < priv->iq_autocal_len; i++) { - if (priv->iq_autocal[i].freq != freq) - continue; - - memcpy(iq_autocal, &priv->iq_autocal[i].params, - sizeof(struct p54_iq_autocal_entry)); - break; - } - if (i == priv->iq_autocal_len) - goto err; - - if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) - body = (void *) skb_put(skb, sizeof(body->longbow)); - else - body = (void *) skb_put(skb, sizeof(body->normal)); - - for (i = 0; i < priv->output_limit->entries; i++) { - __le16 *entry_freq = (void *) (priv->output_limit->data + - priv->output_limit->entry_size * i); - - if (*entry_freq != freq) - continue; - - if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { - memcpy(&body->longbow.power_limits, - (void *) entry_freq + sizeof(__le16), - priv->output_limit->entry_size); - } else { - struct pda_channel_output_limit *limits = - (void *) entry_freq; - - body->normal.val_barker = 0x38; - body->normal.val_bpsk = body->normal.dup_bpsk = - limits->val_bpsk; - body->normal.val_qpsk = body->normal.dup_qpsk = - limits->val_qpsk; - body->normal.val_16qam = body->normal.dup_16qam = - limits->val_16qam; - body->normal.val_64qam = body->normal.dup_64qam = - limits->val_64qam; - } - break; - } - if (i == priv->output_limit->entries) - goto err; - - entry = (void *)(priv->curve_data->data + priv->curve_data->offset); - for (i = 0; i < priv->curve_data->entries; i++) { - if (*((__le16 *)entry) != freq) { - entry += priv->curve_data->entry_size; - continue; - } - - if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { - memcpy(&body->longbow.curve_data, - (void *) entry + sizeof(__le16), - priv->curve_data->entry_size); - } else { - struct p54_scan_body *chan = &body->normal; - struct pda_pa_curve_data *curve_data = - (void *) priv->curve_data->data; - - entry += sizeof(__le16); - chan->pa_points_per_curve = 8; - memset(chan->curve_data, 0, sizeof(*chan->curve_data)); - memcpy(chan->curve_data, entry, - sizeof(struct p54_pa_curve_data_sample) * - min((u8)8, curve_data->points_per_channel)); - } - break; - } - if (i == priv->curve_data->entries) - goto err; - - if ((priv->fw_var >= 0x500) && (priv->fw_var < 0x509)) { - rate = (void *) skb_put(skb, sizeof(*rate)); - rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); - for (i = 0; i < sizeof(rate->rts_rates); i++) - rate->rts_rates[i] = i; - } - - rssi = (struct pda_rssi_cal_entry *) skb_put(skb, sizeof(*rssi)); - rssi->mul = cpu_to_le16(priv->rssical_db[band].mul); - rssi->add = cpu_to_le16(priv->rssical_db[band].add); - if (priv->rxhw == PDR_SYNTH_FRONTEND_LONGBOW) { - /* Longbow frontend needs ever more */ - rssi = (void *) skb_put(skb, sizeof(*rssi)); - rssi->mul = cpu_to_le16(priv->rssical_db[band].longbow_unkn); - rssi->add = cpu_to_le16(priv->rssical_db[band].longbow_unk2); - } - - if (priv->fw_var >= 0x509) { - rate = (void *) skb_put(skb, sizeof(*rate)); - rate->basic_rate_mask = cpu_to_le32(priv->basic_rate_mask); - for (i = 0; i < sizeof(rate->rts_rates); i++) - rate->rts_rates[i] = i; - } - - hdr = (struct p54_hdr *) skb->data; - hdr->len = cpu_to_le16(skb->len - sizeof(*hdr)); - - priv->tx(dev, skb); - return 0; - - err: - printk(KERN_ERR "%s: frequency change failed\n", wiphy_name(dev->wiphy)); - p54_free_skb(dev, skb); - return -EINVAL; -} - -static int p54_set_leds(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *skb; - struct p54_led *led; - - skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*led), - P54_CONTROL_TYPE_LED, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - led = (struct p54_led *) skb_put(skb, sizeof(*led)); - led->flags = cpu_to_le16(0x0003); - led->mask[0] = led->mask[1] = cpu_to_le16(priv->softled_state); - led->delay[0] = cpu_to_le16(1); - led->delay[1] = cpu_to_le16(0); - priv->tx(dev, skb); - return 0; -} - -#define P54_SET_QUEUE(queue, ai_fs, cw_min, cw_max, _txop) \ -do { \ - queue.aifs = cpu_to_le16(ai_fs); \ - queue.cwmin = cpu_to_le16(cw_min); \ - queue.cwmax = cpu_to_le16(cw_max); \ - queue.txop = cpu_to_le16(_txop); \ -} while(0) - -static int p54_set_edcf(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *skb; - struct p54_edcf *edcf; - - skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*edcf), - P54_CONTROL_TYPE_DCFINIT, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - edcf = (struct p54_edcf *)skb_put(skb, sizeof(*edcf)); - if (priv->use_short_slot) { - edcf->slottime = 9; - edcf->sifs = 0x10; - edcf->eofpad = 0x00; - } else { - edcf->slottime = 20; - edcf->sifs = 0x0a; - edcf->eofpad = 0x06; - } - /* (see prism54/isl_oid.h for further details) */ - edcf->frameburst = cpu_to_le16(0); - edcf->round_trip_delay = cpu_to_le16(0); - edcf->flags = 0; - memset(edcf->mapping, 0, sizeof(edcf->mapping)); - memcpy(edcf->queue, priv->qos_params, sizeof(edcf->queue)); - priv->tx(dev, skb); - return 0; -} - -static int p54_set_ps(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *skb; - struct p54_psm *psm; - u16 mode; - int i; - - if (dev->conf.flags & IEEE80211_CONF_PS) - mode = P54_PSM | P54_PSM_BEACON_TIMEOUT | P54_PSM_DTIM | - P54_PSM_CHECKSUM | P54_PSM_MCBC; - else - mode = P54_PSM_CAM; - - skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*psm), - P54_CONTROL_TYPE_PSM, GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - psm = (struct p54_psm *)skb_put(skb, sizeof(*psm)); - psm->mode = cpu_to_le16(mode); - psm->aid = cpu_to_le16(priv->aid); - for (i = 0; i < ARRAY_SIZE(psm->intervals); i++) { - psm->intervals[i].interval = - cpu_to_le16(dev->conf.listen_interval); - psm->intervals[i].periods = cpu_to_le16(1); - } - - psm->beacon_rssi_skip_max = 200; - psm->rssi_delta_threshold = 0; - psm->nr = 10; - psm->exclude[0] = 0; - - priv->tx(dev, skb); - - return 0; -} - -static int p54_beacon_tim(struct sk_buff *skb) -{ - /* - * the good excuse for this mess is ... the firmware. - * The dummy TIM MUST be at the end of the beacon frame, - * because it'll be overwritten! - */ - - struct ieee80211_mgmt *mgmt = (void *)skb->data; - u8 *pos, *end; - - if (skb->len <= sizeof(mgmt)) - return -EINVAL; - - pos = (u8 *)mgmt->u.beacon.variable; - end = skb->data + skb->len; - while (pos < end) { - if (pos + 2 + pos[1] > end) - return -EINVAL; - - if (pos[0] == WLAN_EID_TIM) { - u8 dtim_len = pos[1]; - u8 dtim_period = pos[3]; - u8 *next = pos + 2 + dtim_len; - - if (dtim_len < 3) - return -EINVAL; - - memmove(pos, next, end - next); - - if (dtim_len > 3) - skb_trim(skb, skb->len - (dtim_len - 3)); - - pos = end - (dtim_len + 2); - - /* add the dummy at the end */ - pos[0] = WLAN_EID_TIM; - pos[1] = 3; - pos[2] = 0; - pos[3] = dtim_period; - pos[4] = 0; - return 0; - } - pos += 2 + pos[1]; - } - return 0; -} - -static int p54_beacon_update(struct ieee80211_hw *dev, - struct ieee80211_vif *vif) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *beacon; - int ret; - - if (priv->cached_beacon) { - p54_tx_cancel(dev, priv->cached_beacon); - /* wait for the last beacon the be freed */ - msleep(10); - } - - beacon = ieee80211_beacon_get(dev, vif); - if (!beacon) - return -ENOMEM; - ret = p54_beacon_tim(beacon); - if (ret) - return ret; - ret = p54_tx(dev, beacon); - if (ret) - return ret; - priv->cached_beacon = beacon; - priv->tsf_high32 = 0; - priv->tsf_low32 = 0; - - return 0; -} - -static int p54_start(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - int err; - - mutex_lock(&priv->conf_mutex); - err = priv->open(dev); - if (err) - goto out; - P54_SET_QUEUE(priv->qos_params[0], 0x0002, 0x0003, 0x0007, 47); - P54_SET_QUEUE(priv->qos_params[1], 0x0002, 0x0007, 0x000f, 94); - P54_SET_QUEUE(priv->qos_params[2], 0x0003, 0x000f, 0x03ff, 0); - P54_SET_QUEUE(priv->qos_params[3], 0x0007, 0x000f, 0x03ff, 0); - err = p54_set_edcf(dev); - if (err) - goto out; - - memset(priv->bssid, ~0, ETH_ALEN); - priv->mode = NL80211_IFTYPE_MONITOR; - err = p54_setup_mac(dev); - if (err) { - priv->mode = NL80211_IFTYPE_UNSPECIFIED; - goto out; - } - - queue_delayed_work(dev->workqueue, &priv->work, 0); - - priv->softled_state = 0; - err = p54_set_leds(dev); - -out: - mutex_unlock(&priv->conf_mutex); - return err; -} - -static void p54_stop(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *skb; - - mutex_lock(&priv->conf_mutex); - priv->mode = NL80211_IFTYPE_UNSPECIFIED; - priv->softled_state = 0; - p54_set_leds(dev); - -#ifdef CONFIG_P54_LEDS - cancel_delayed_work_sync(&priv->led_work); -#endif /* CONFIG_P54_LEDS */ - cancel_delayed_work_sync(&priv->work); - if (priv->cached_beacon) - p54_tx_cancel(dev, priv->cached_beacon); - - priv->stop(dev); - while ((skb = skb_dequeue(&priv->tx_queue))) - kfree_skb(skb); - priv->cached_beacon = NULL; - priv->tsf_high32 = priv->tsf_low32 = 0; - mutex_unlock(&priv->conf_mutex); -} - -static int p54_add_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) -{ - struct p54_common *priv = dev->priv; - - mutex_lock(&priv->conf_mutex); - if (priv->mode != NL80211_IFTYPE_MONITOR) { - mutex_unlock(&priv->conf_mutex); - return -EOPNOTSUPP; - } - - priv->vif = conf->vif; - - switch (conf->type) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_MESH_POINT: - priv->mode = conf->type; - break; - default: - mutex_unlock(&priv->conf_mutex); - return -EOPNOTSUPP; - } - - memcpy(priv->mac_addr, conf->mac_addr, ETH_ALEN); - p54_setup_mac(dev); - mutex_unlock(&priv->conf_mutex); - return 0; -} - -static void p54_remove_interface(struct ieee80211_hw *dev, - struct ieee80211_if_init_conf *conf) -{ - struct p54_common *priv = dev->priv; - - mutex_lock(&priv->conf_mutex); - priv->vif = NULL; - if (priv->cached_beacon) - p54_tx_cancel(dev, priv->cached_beacon); - priv->mode = NL80211_IFTYPE_MONITOR; - memset(priv->mac_addr, 0, ETH_ALEN); - memset(priv->bssid, 0, ETH_ALEN); - p54_setup_mac(dev); - mutex_unlock(&priv->conf_mutex); -} - -static int p54_config(struct ieee80211_hw *dev, u32 changed) -{ - int ret = 0; - struct p54_common *priv = dev->priv; - struct ieee80211_conf *conf = &dev->conf; - - mutex_lock(&priv->conf_mutex); - if (changed & IEEE80211_CONF_CHANGE_POWER) - priv->output_power = conf->power_level << 2; - if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED) { - ret = p54_setup_mac(dev); - if (ret) - goto out; - } - if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - ret = p54_scan(dev, P54_SCAN_EXIT, 0); - if (ret) - goto out; - } - if (changed & IEEE80211_CONF_CHANGE_PS) { - ret = p54_set_ps(dev); - if (ret) - goto out; - } - -out: - mutex_unlock(&priv->conf_mutex); - return ret; -} - -static void p54_configure_filter(struct ieee80211_hw *dev, - unsigned int changed_flags, - unsigned int *total_flags, - int mc_count, struct dev_mc_list *mclist) -{ - struct p54_common *priv = dev->priv; - - *total_flags &= FIF_PROMISC_IN_BSS | - FIF_OTHER_BSS; - - priv->filter_flags = *total_flags; - - if (changed_flags & (FIF_PROMISC_IN_BSS | FIF_OTHER_BSS)) - p54_setup_mac(dev); -} - -static int p54_conf_tx(struct ieee80211_hw *dev, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - struct p54_common *priv = dev->priv; - int ret; - - mutex_lock(&priv->conf_mutex); - if ((params) && !(queue > 4)) { - P54_SET_QUEUE(priv->qos_params[queue], params->aifs, - params->cw_min, params->cw_max, params->txop); - ret = p54_set_edcf(dev); - } else - ret = -EINVAL; - mutex_unlock(&priv->conf_mutex); - return ret; -} - -static int p54_init_xbow_synth(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *skb; - struct p54_xbow_synth *xbow; - - skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*xbow), - P54_CONTROL_TYPE_XBOW_SYNTH_CFG, GFP_KERNEL); - if (!skb) - return -ENOMEM; - - xbow = (struct p54_xbow_synth *)skb_put(skb, sizeof(*xbow)); - xbow->magic1 = cpu_to_le16(0x1); - xbow->magic2 = cpu_to_le16(0x2); - xbow->freq = cpu_to_le16(5390); - memset(xbow->padding, 0, sizeof(xbow->padding)); - priv->tx(dev, skb); - return 0; -} - -static void p54_work(struct work_struct *work) -{ - struct p54_common *priv = container_of(work, struct p54_common, - work.work); - struct ieee80211_hw *dev = priv->hw; - struct sk_buff *skb; - - if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) - return ; - - /* - * TODO: walk through tx_queue and do the following tasks - * 1. initiate bursts. - * 2. cancel stuck frames / reset the device if necessary. - */ - - skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL, - sizeof(struct p54_statistics), - P54_CONTROL_TYPE_STAT_READBACK, GFP_KERNEL); - if (!skb) - return ; - - priv->tx(dev, skb); -} - -static int p54_get_stats(struct ieee80211_hw *dev, - struct ieee80211_low_level_stats *stats) -{ - struct p54_common *priv = dev->priv; - - memcpy(stats, &priv->stats, sizeof(*stats)); - return 0; -} - -static int p54_get_tx_stats(struct ieee80211_hw *dev, - struct ieee80211_tx_queue_stats *stats) -{ - struct p54_common *priv = dev->priv; - - memcpy(stats, &priv->tx_stats[P54_QUEUE_DATA], - sizeof(stats[0]) * dev->queues); - return 0; -} - -static void p54_bss_info_changed(struct ieee80211_hw *dev, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed) -{ - struct p54_common *priv = dev->priv; - int ret; - - mutex_lock(&priv->conf_mutex); - if (changed & BSS_CHANGED_BSSID) { - memcpy(priv->bssid, info->bssid, ETH_ALEN); - ret = p54_setup_mac(dev); - if (ret) - goto out; - } - - if (changed & BSS_CHANGED_BEACON) { - ret = p54_scan(dev, P54_SCAN_EXIT, 0); - if (ret) - goto out; - ret = p54_setup_mac(dev); - if (ret) - goto out; - ret = p54_beacon_update(dev, vif); - if (ret) - goto out; - } - /* XXX: this mimics having two callbacks... clean up */ - out: - mutex_unlock(&priv->conf_mutex); - - if (changed & (BSS_CHANGED_ERP_SLOT | BSS_CHANGED_BEACON)) { - priv->use_short_slot = info->use_short_slot; - p54_set_edcf(dev); - } - if (changed & BSS_CHANGED_BASIC_RATES) { - if (dev->conf.channel->band == IEEE80211_BAND_5GHZ) - priv->basic_rate_mask = (info->basic_rates << 4); - else - priv->basic_rate_mask = info->basic_rates; - p54_setup_mac(dev); - if (priv->fw_var >= 0x500) - p54_scan(dev, P54_SCAN_EXIT, 0); - } - if (changed & BSS_CHANGED_ASSOC) { - if (info->assoc) { - priv->aid = info->aid; - priv->wakeup_timer = info->beacon_int * - info->dtim_period * 5; - p54_setup_mac(dev); - } - } -} - -static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, - struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct p54_common *priv = dev->priv; - struct sk_buff *skb; - struct p54_keycache *rxkey; - int slot, ret = 0; - u8 algo = 0; - - if (modparam_nohwcrypt) - return -EOPNOTSUPP; - - mutex_lock(&priv->conf_mutex); - if (cmd == SET_KEY) { - switch (key->alg) { - case ALG_TKIP: - if (!(priv->privacy_caps & (BR_DESC_PRIV_CAP_MICHAEL | - BR_DESC_PRIV_CAP_TKIP))) { - ret = -EOPNOTSUPP; - goto out_unlock; - } - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - algo = P54_CRYPTO_TKIPMICHAEL; - break; - case ALG_WEP: - if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_WEP)) { - ret = -EOPNOTSUPP; - goto out_unlock; - } - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - algo = P54_CRYPTO_WEP; - break; - case ALG_CCMP: - if (!(priv->privacy_caps & BR_DESC_PRIV_CAP_AESCCMP)) { - ret = -EOPNOTSUPP; - goto out_unlock; - } - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - algo = P54_CRYPTO_AESCCMP; - break; - default: - ret = -EOPNOTSUPP; - goto out_unlock; - } - slot = bitmap_find_free_region(priv->used_rxkeys, - priv->rx_keycache_size, 0); - - if (slot < 0) { - /* - * The device supports the choosen algorithm, but the - * firmware does not provide enough key slots to store - * all of them. - * But encryption offload for outgoing frames is always - * possible, so we just pretend that the upload was - * successful and do the decryption in software. - */ - - /* mark the key as invalid. */ - key->hw_key_idx = 0xff; - goto out_unlock; - } - } else { - slot = key->hw_key_idx; - - if (slot == 0xff) { - /* This key was not uploaded into the rx key cache. */ - - goto out_unlock; - } - - bitmap_release_region(priv->used_rxkeys, slot, 0); - algo = 0; - } - - skb = p54_alloc_skb(dev, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*rxkey), - P54_CONTROL_TYPE_RX_KEYCACHE, GFP_KERNEL); - if (!skb) { - bitmap_release_region(priv->used_rxkeys, slot, 0); - ret = -ENOSPC; - goto out_unlock; - } - - rxkey = (struct p54_keycache *)skb_put(skb, sizeof(*rxkey)); - rxkey->entry = slot; - rxkey->key_id = key->keyidx; - rxkey->key_type = algo; - if (sta) - memcpy(rxkey->mac, sta->addr, ETH_ALEN); - else - memset(rxkey->mac, ~0, ETH_ALEN); - if (key->alg != ALG_TKIP) { - rxkey->key_len = min((u8)16, key->keylen); - memcpy(rxkey->key, key->key, rxkey->key_len); - } else { - rxkey->key_len = 24; - memcpy(rxkey->key, key->key, 16); - memcpy(&(rxkey->key[16]), &(key->key - [NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY]), 8); - } - - priv->tx(dev, skb); - key->hw_key_idx = slot; - -out_unlock: - mutex_unlock(&priv->conf_mutex); - return ret; -} - -#ifdef CONFIG_P54_LEDS -static void p54_update_leds(struct work_struct *work) -{ - struct p54_common *priv = container_of(work, struct p54_common, - led_work.work); - int err, i, tmp, blink_delay = 400; - bool rerun = false; - - /* Don't toggle the LED, when the device is down. */ - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) - return ; - - for (i = 0; i < ARRAY_SIZE(priv->leds); i++) - if (priv->leds[i].toggled) { - priv->softled_state |= BIT(i); - - tmp = 70 + 200 / (priv->leds[i].toggled); - if (tmp < blink_delay) - blink_delay = tmp; - - if (priv->leds[i].led_dev.brightness == LED_OFF) - rerun = true; - - priv->leds[i].toggled = - !!priv->leds[i].led_dev.brightness; - } else - priv->softled_state &= ~BIT(i); - - err = p54_set_leds(priv->hw); - if (err && net_ratelimit()) - printk(KERN_ERR "%s: failed to update LEDs.\n", - wiphy_name(priv->hw->wiphy)); - - if (rerun) - queue_delayed_work(priv->hw->workqueue, &priv->led_work, - msecs_to_jiffies(blink_delay)); -} - -static void p54_led_brightness_set(struct led_classdev *led_dev, - enum led_brightness brightness) -{ - struct p54_led_dev *led = container_of(led_dev, struct p54_led_dev, - led_dev); - struct ieee80211_hw *dev = led->hw_dev; - struct p54_common *priv = dev->priv; - - if (priv->mode == NL80211_IFTYPE_UNSPECIFIED) - return ; - - if (brightness) { - led->toggled++; - queue_delayed_work(priv->hw->workqueue, &priv->led_work, - HZ/10); - } -} - -static int p54_register_led(struct ieee80211_hw *dev, - unsigned int led_index, - char *name, char *trigger) -{ - struct p54_common *priv = dev->priv; - struct p54_led_dev *led = &priv->leds[led_index]; - int err; - - if (led->registered) - return -EEXIST; - - snprintf(led->name, sizeof(led->name), "p54-%s::%s", - wiphy_name(dev->wiphy), name); - led->hw_dev = dev; - led->index = led_index; - led->led_dev.name = led->name; - led->led_dev.default_trigger = trigger; - led->led_dev.brightness_set = p54_led_brightness_set; - - err = led_classdev_register(wiphy_dev(dev->wiphy), &led->led_dev); - if (err) - printk(KERN_ERR "%s: Failed to register %s LED.\n", - wiphy_name(dev->wiphy), name); - else - led->registered = 1; - - return err; -} - -static int p54_init_leds(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - int err; - - /* - * TODO: - * Figure out if the EEPROM contains some hints about the number - * of available/programmable LEDs of the device. - */ - - INIT_DELAYED_WORK(&priv->led_work, p54_update_leds); - - err = p54_register_led(dev, 0, "assoc", - ieee80211_get_assoc_led_name(dev)); - if (err) - return err; - - err = p54_register_led(dev, 1, "tx", - ieee80211_get_tx_led_name(dev)); - if (err) - return err; - - err = p54_register_led(dev, 2, "rx", - ieee80211_get_rx_led_name(dev)); - if (err) - return err; - - err = p54_register_led(dev, 3, "radio", - ieee80211_get_radio_led_name(dev)); - if (err) - return err; - - err = p54_set_leds(dev); - return err; -} - -static void p54_unregister_leds(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - int i; - - for (i = 0; i < ARRAY_SIZE(priv->leds); i++) - if (priv->leds[i].registered) - led_classdev_unregister(&priv->leds[i].led_dev); -} -#endif /* CONFIG_P54_LEDS */ - -static const struct ieee80211_ops p54_ops = { - .tx = p54_tx, - .start = p54_start, - .stop = p54_stop, - .add_interface = p54_add_interface, - .remove_interface = p54_remove_interface, - .set_tim = p54_set_tim, - .sta_notify = p54_sta_notify, - .set_key = p54_set_key, - .config = p54_config, - .bss_info_changed = p54_bss_info_changed, - .configure_filter = p54_configure_filter, - .conf_tx = p54_conf_tx, - .get_stats = p54_get_stats, - .get_tx_stats = p54_get_tx_stats -}; - -struct ieee80211_hw *p54_init_common(size_t priv_data_len) -{ - struct ieee80211_hw *dev; - struct p54_common *priv; - - dev = ieee80211_alloc_hw(priv_data_len, &p54_ops); - if (!dev) - return NULL; - - priv = dev->priv; - priv->hw = dev; - priv->mode = NL80211_IFTYPE_UNSPECIFIED; - priv->basic_rate_mask = 0x15f; - skb_queue_head_init(&priv->tx_queue); - dev->flags = IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_NOISE_DBM; - - dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_MESH_POINT); - - dev->channel_change_time = 1000; /* TODO: find actual value */ - priv->tx_stats[P54_QUEUE_BEACON].limit = 1; - priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1; - priv->tx_stats[P54_QUEUE_MGMT].limit = 3; - priv->tx_stats[P54_QUEUE_CAB].limit = 3; - priv->tx_stats[P54_QUEUE_DATA].limit = 5; - dev->queues = 1; - priv->noise = -94; - /* - * We support at most 8 tries no matter which rate they're at, - * we cannot support max_rates * max_rate_tries as we set it - * here, but setting it correctly to 4/2 or so would limit us - * artificially if the RC algorithm wants just two rates, so - * let's say 4/7, we'll redistribute it at TX time, see the - * comments there. - */ - dev->max_rates = 4; - dev->max_rate_tries = 7; - dev->extra_tx_headroom = sizeof(struct p54_hdr) + 4 + - sizeof(struct p54_tx_data); - - mutex_init(&priv->conf_mutex); - init_completion(&priv->eeprom_comp); - INIT_DELAYED_WORK(&priv->work, p54_work); - - return dev; -} -EXPORT_SYMBOL_GPL(p54_init_common); - -int p54_register_common(struct ieee80211_hw *dev, struct device *pdev) -{ - int err; - - err = ieee80211_register_hw(dev); - if (err) { - dev_err(pdev, "Cannot register device (%d).\n", err); - return err; - } - -#ifdef CONFIG_P54_LEDS - err = p54_init_leds(dev); - if (err) - return err; -#endif /* CONFIG_P54_LEDS */ - - dev_info(pdev, "is registered as '%s'\n", wiphy_name(dev->wiphy)); - return 0; -} -EXPORT_SYMBOL_GPL(p54_register_common); - -void p54_free_common(struct ieee80211_hw *dev) -{ - struct p54_common *priv = dev->priv; - kfree(priv->iq_autocal); - kfree(priv->output_limit); - kfree(priv->curve_data); - kfree(priv->used_rxkeys); - -#ifdef CONFIG_P54_LEDS - p54_unregister_leds(dev); -#endif /* CONFIG_P54_LEDS */ -} -EXPORT_SYMBOL_GPL(p54_free_common); diff --git a/drivers/net/wireless/p54/p54common.h b/drivers/net/wireless/p54/p54common.h deleted file mode 100644 index 75ead7a150fc..000000000000 --- a/drivers/net/wireless/p54/p54common.h +++ /dev/null @@ -1,644 +0,0 @@ -#ifndef P54COMMON_H -#define P54COMMON_H - -/* - * Common code specific definitions for mac80211 Prism54 drivers - * - * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> - * Copyright (c) 2007, Christian Lamparter <chunkeey@web.de> - * - * Based on: - * - the islsm (softmac prism54) driver, which is: - * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. - * - * - LMAC API interface header file for STLC4560 (lmac_longbow.h) - * Copyright (C) 2007 Conexant Systems, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -struct bootrec { - __le32 code; - __le32 len; - u32 data[10]; -} __attribute__((packed)); - -#define PDR_SYNTH_FRONTEND_MASK 0x0007 -#define PDR_SYNTH_FRONTEND_DUETTE3 0x0001 -#define PDR_SYNTH_FRONTEND_DUETTE2 0x0002 -#define PDR_SYNTH_FRONTEND_FRISBEE 0x0003 -#define PDR_SYNTH_FRONTEND_XBOW 0x0004 -#define PDR_SYNTH_FRONTEND_LONGBOW 0x0005 -#define PDR_SYNTH_IQ_CAL_MASK 0x0018 -#define PDR_SYNTH_IQ_CAL_PA_DETECTOR 0x0000 -#define PDR_SYNTH_IQ_CAL_DISABLED 0x0008 -#define PDR_SYNTH_IQ_CAL_ZIF 0x0010 -#define PDR_SYNTH_FAA_SWITCH_MASK 0x0020 -#define PDR_SYNTH_FAA_SWITCH_ENABLED 0x0020 -#define PDR_SYNTH_24_GHZ_MASK 0x0040 -#define PDR_SYNTH_24_GHZ_DISABLED 0x0040 -#define PDR_SYNTH_5_GHZ_MASK 0x0080 -#define PDR_SYNTH_5_GHZ_DISABLED 0x0080 -#define PDR_SYNTH_RX_DIV_MASK 0x0100 -#define PDR_SYNTH_RX_DIV_SUPPORTED 0x0100 -#define PDR_SYNTH_TX_DIV_MASK 0x0200 -#define PDR_SYNTH_TX_DIV_SUPPORTED 0x0200 - -struct bootrec_exp_if { - __le16 role; - __le16 if_id; - __le16 variant; - __le16 btm_compat; - __le16 top_compat; -} __attribute__((packed)); - -#define BR_DESC_PRIV_CAP_WEP BIT(0) -#define BR_DESC_PRIV_CAP_TKIP BIT(1) -#define BR_DESC_PRIV_CAP_MICHAEL BIT(2) -#define BR_DESC_PRIV_CAP_CCX_CP BIT(3) -#define BR_DESC_PRIV_CAP_CCX_MIC BIT(4) -#define BR_DESC_PRIV_CAP_AESCCMP BIT(5) - -struct bootrec_desc { - __le16 modes; - __le16 flags; - __le32 rx_start; - __le32 rx_end; - u8 headroom; - u8 tailroom; - u8 tx_queues; - u8 tx_depth; - u8 privacy_caps; - u8 rx_keycache_size; - u8 time_size; - u8 padding; - u8 rates[16]; - u8 padding2[4]; - __le16 rx_mtu; -} __attribute__((packed)); - -#define BR_CODE_MIN 0x80000000 -#define BR_CODE_COMPONENT_ID 0x80000001 -#define BR_CODE_COMPONENT_VERSION 0x80000002 -#define BR_CODE_DEPENDENT_IF 0x80000003 -#define BR_CODE_EXPOSED_IF 0x80000004 -#define BR_CODE_DESCR 0x80000101 -#define BR_CODE_MAX 0x8FFFFFFF -#define BR_CODE_END_OF_BRA 0xFF0000FF -#define LEGACY_BR_CODE_END_OF_BRA 0xFFFFFFFF - -#define P54_HDR_FLAG_DATA_ALIGN BIT(14) -#define P54_HDR_FLAG_DATA_OUT_PROMISC BIT(0) -#define P54_HDR_FLAG_DATA_OUT_TIMESTAMP BIT(1) -#define P54_HDR_FLAG_DATA_OUT_SEQNR BIT(2) -#define P54_HDR_FLAG_DATA_OUT_BIT3 BIT(3) -#define P54_HDR_FLAG_DATA_OUT_BURST BIT(4) -#define P54_HDR_FLAG_DATA_OUT_NOCANCEL BIT(5) -#define P54_HDR_FLAG_DATA_OUT_CLEARTIM BIT(6) -#define P54_HDR_FLAG_DATA_OUT_HITCHHIKE BIT(7) -#define P54_HDR_FLAG_DATA_OUT_COMPRESS BIT(8) -#define P54_HDR_FLAG_DATA_OUT_CONCAT BIT(9) -#define P54_HDR_FLAG_DATA_OUT_PCS_ACCEPT BIT(10) -#define P54_HDR_FLAG_DATA_OUT_WAITEOSP BIT(11) - -#define P54_HDR_FLAG_DATA_IN_FCS_GOOD BIT(0) -#define P54_HDR_FLAG_DATA_IN_MATCH_MAC BIT(1) -#define P54_HDR_FLAG_DATA_IN_MCBC BIT(2) -#define P54_HDR_FLAG_DATA_IN_BEACON BIT(3) -#define P54_HDR_FLAG_DATA_IN_MATCH_BSS BIT(4) -#define P54_HDR_FLAG_DATA_IN_BCAST_BSS BIT(5) -#define P54_HDR_FLAG_DATA_IN_DATA BIT(6) -#define P54_HDR_FLAG_DATA_IN_TRUNCATED BIT(7) -#define P54_HDR_FLAG_DATA_IN_BIT8 BIT(8) -#define P54_HDR_FLAG_DATA_IN_TRANSPARENT BIT(9) - -/* PDA defines are Copyright (C) 2005 Nokia Corporation (taken from islsm_pda.h) */ - -struct pda_entry { - __le16 len; /* includes both code and data */ - __le16 code; - u8 data[0]; -} __attribute__ ((packed)); - -struct eeprom_pda_wrap { - __le32 magic; - __le16 pad; - __le16 len; - __le32 arm_opcode; - u8 data[0]; -} __attribute__ ((packed)); - -struct p54_iq_autocal_entry { - __le16 iq_param[4]; -} __attribute__ ((packed)); - -struct pda_iq_autocal_entry { - __le16 freq; - struct p54_iq_autocal_entry params; -} __attribute__ ((packed)); - -struct pda_channel_output_limit { - __le16 freq; - u8 val_bpsk; - u8 val_qpsk; - u8 val_16qam; - u8 val_64qam; - u8 rate_set_mask; - u8 rate_set_size; -} __attribute__ ((packed)); - -struct pda_pa_curve_data_sample_rev0 { - u8 rf_power; - u8 pa_detector; - u8 pcv; -} __attribute__ ((packed)); - -struct pda_pa_curve_data_sample_rev1 { - u8 rf_power; - u8 pa_detector; - u8 data_barker; - u8 data_bpsk; - u8 data_qpsk; - u8 data_16qam; - u8 data_64qam; -} __attribute__ ((packed)); - -struct p54_pa_curve_data_sample { - u8 rf_power; - u8 pa_detector; - u8 data_barker; - u8 data_bpsk; - u8 data_qpsk; - u8 data_16qam; - u8 data_64qam; - u8 padding; -} __attribute__ ((packed)); - -struct pda_pa_curve_data { - u8 cal_method_rev; - u8 channels; - u8 points_per_channel; - u8 padding; - u8 data[0]; -} __attribute__ ((packed)); - -struct pda_rssi_cal_entry { - __le16 mul; - __le16 add; -} __attribute__ ((packed)); - -struct pda_country { - u8 regdomain; - u8 alpha2[2]; - u8 flags; -} __attribute__ ((packed)); - -/* - * Warning: Longbow's structures are bogus. - */ -struct p54_channel_output_limit_longbow { - __le16 rf_power_points[12]; -} __attribute__ ((packed)); - -struct p54_pa_curve_data_sample_longbow { - __le16 rf_power; - __le16 pa_detector; - struct { - __le16 data[4]; - } points[3] __attribute__ ((packed)); -} __attribute__ ((packed)); - -struct pda_custom_wrapper { - __le16 entries; - __le16 entry_size; - __le16 offset; - __le16 len; - u8 data[0]; -} __attribute__ ((packed)); - -/* - * this defines the PDR codes used to build PDAs as defined in document - * number 553155. The current implementation mirrors version 1.1 of the - * document and lists only PDRs supported by the ARM platform. - */ - -/* common and choice range (0x0000 - 0x0fff) */ -#define PDR_END 0x0000 -#define PDR_MANUFACTURING_PART_NUMBER 0x0001 -#define PDR_PDA_VERSION 0x0002 -#define PDR_NIC_SERIAL_NUMBER 0x0003 - -#define PDR_MAC_ADDRESS 0x0101 -#define PDR_REGULATORY_DOMAIN_LIST 0x0103 -#define PDR_TEMPERATURE_TYPE 0x0107 - -#define PDR_PRISM_PCI_IDENTIFIER 0x0402 - -/* ARM range (0x1000 - 0x1fff) */ -#define PDR_COUNTRY_INFORMATION 0x1000 -#define PDR_INTERFACE_LIST 0x1001 -#define PDR_HARDWARE_PLATFORM_COMPONENT_ID 0x1002 -#define PDR_OEM_NAME 0x1003 -#define PDR_PRODUCT_NAME 0x1004 -#define PDR_UTF8_OEM_NAME 0x1005 -#define PDR_UTF8_PRODUCT_NAME 0x1006 -#define PDR_COUNTRY_LIST 0x1007 -#define PDR_DEFAULT_COUNTRY 0x1008 - -#define PDR_ANTENNA_GAIN 0x1100 - -#define PDR_PRISM_INDIGO_PA_CALIBRATION_DATA 0x1901 -#define PDR_RSSI_LINEAR_APPROXIMATION 0x1902 -#define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS 0x1903 -#define PDR_PRISM_PA_CAL_CURVE_DATA 0x1904 -#define PDR_RSSI_LINEAR_APPROXIMATION_DUAL_BAND 0x1905 -#define PDR_PRISM_ZIF_TX_IQ_CALIBRATION 0x1906 -#define PDR_REGULATORY_POWER_LIMITS 0x1907 -#define PDR_RSSI_LINEAR_APPROXIMATION_EXTENDED 0x1908 -#define PDR_RADIATED_TRANSMISSION_CORRECTION 0x1909 -#define PDR_PRISM_TX_IQ_CALIBRATION 0x190a - -/* reserved range (0x2000 - 0x7fff) */ - -/* customer range (0x8000 - 0xffff) */ -#define PDR_BASEBAND_REGISTERS 0x8000 -#define PDR_PER_CHANNEL_BASEBAND_REGISTERS 0x8001 - -/* used by our modificated eeprom image */ -#define PDR_RSSI_LINEAR_APPROXIMATION_CUSTOM 0xDEAD -#define PDR_PRISM_PA_CAL_OUTPUT_POWER_LIMITS_CUSTOM 0xBEEF -#define PDR_PRISM_PA_CAL_CURVE_DATA_CUSTOM 0xB05D - -/* PDR definitions for default country & country list */ -#define PDR_COUNTRY_CERT_CODE 0x80 -#define PDR_COUNTRY_CERT_CODE_REAL 0x00 -#define PDR_COUNTRY_CERT_CODE_PSEUDO 0x80 -#define PDR_COUNTRY_CERT_BAND 0x40 -#define PDR_COUNTRY_CERT_BAND_2GHZ 0x00 -#define PDR_COUNTRY_CERT_BAND_5GHZ 0x40 -#define PDR_COUNTRY_CERT_IODOOR 0x30 -#define PDR_COUNTRY_CERT_IODOOR_BOTH 0x00 -#define PDR_COUNTRY_CERT_IODOOR_INDOOR 0x20 -#define PDR_COUNTRY_CERT_IODOOR_OUTDOOR 0x30 -#define PDR_COUNTRY_CERT_INDEX 0x0F - -struct p54_eeprom_lm86 { - union { - struct { - __le16 offset; - __le16 len; - u8 data[0]; - } v1; - struct { - __le32 offset; - __le16 len; - u8 magic2; - u8 pad; - u8 magic[4]; - u8 data[0]; - } v2; - } __attribute__ ((packed)); -} __attribute__ ((packed)); - -enum p54_rx_decrypt_status { - P54_DECRYPT_NONE = 0, - P54_DECRYPT_OK, - P54_DECRYPT_NOKEY, - P54_DECRYPT_NOMICHAEL, - P54_DECRYPT_NOCKIPMIC, - P54_DECRYPT_FAIL_WEP, - P54_DECRYPT_FAIL_TKIP, - P54_DECRYPT_FAIL_MICHAEL, - P54_DECRYPT_FAIL_CKIPKP, - P54_DECRYPT_FAIL_CKIPMIC, - P54_DECRYPT_FAIL_AESCCMP -}; - -struct p54_rx_data { - __le16 flags; - __le16 len; - __le16 freq; - u8 antenna; - u8 rate; - u8 rssi; - u8 quality; - u8 decrypt_status; - u8 rssi_raw; - __le32 tsf32; - __le32 unalloc0; - u8 align[0]; -} __attribute__ ((packed)); - -enum p54_trap_type { - P54_TRAP_SCAN = 0, - P54_TRAP_TIMER, - P54_TRAP_BEACON_TX, - P54_TRAP_FAA_RADIO_ON, - P54_TRAP_FAA_RADIO_OFF, - P54_TRAP_RADAR, - P54_TRAP_NO_BEACON, - P54_TRAP_TBTT, - P54_TRAP_SCO_ENTER, - P54_TRAP_SCO_EXIT -}; - -struct p54_trap { - __le16 event; - __le16 frequency; -} __attribute__ ((packed)); - -enum p54_frame_sent_status { - P54_TX_OK = 0, - P54_TX_FAILED, - P54_TX_PSM, - P54_TX_PSM_CANCELLED = 4 -}; - -struct p54_frame_sent { - u8 status; - u8 tries; - u8 ack_rssi; - u8 quality; - __le16 seq; - u8 antenna; - u8 padding; -} __attribute__ ((packed)); - -enum p54_tx_data_crypt { - P54_CRYPTO_NONE = 0, - P54_CRYPTO_WEP, - P54_CRYPTO_TKIP, - P54_CRYPTO_TKIPMICHAEL, - P54_CRYPTO_CCX_WEPMIC, - P54_CRYPTO_CCX_KPMIC, - P54_CRYPTO_CCX_KP, - P54_CRYPTO_AESCCMP -}; - -enum p54_tx_data_queue { - P54_QUEUE_BEACON = 0, - P54_QUEUE_FWSCAN = 1, - P54_QUEUE_MGMT = 2, - P54_QUEUE_CAB = 3, - P54_QUEUE_DATA = 4, - - P54_QUEUE_AC_NUM = 4, - P54_QUEUE_AC_VO = 4, - P54_QUEUE_AC_VI = 5, - P54_QUEUE_AC_BE = 6, - P54_QUEUE_AC_BK = 7, - - /* keep last */ - P54_QUEUE_NUM = 8, -}; - -struct p54_tx_data { - u8 rateset[8]; - u8 rts_rate_idx; - u8 crypt_offset; - u8 key_type; - u8 key_len; - u8 key[16]; - u8 hw_queue; - u8 backlog; - __le16 durations[4]; - u8 tx_antenna; - union { - struct { - u8 cts_rate; - __le16 output_power; - } __attribute__((packed)) longbow; - struct { - u8 output_power; - u8 cts_rate; - u8 unalloc; - } __attribute__ ((packed)) normal; - } __attribute__ ((packed)); - u8 unalloc2[2]; - u8 align[0]; -} __attribute__ ((packed)); - -/* unit is ms */ -#define P54_TX_FRAME_LIFETIME 2000 -#define P54_TX_TIMEOUT 4000 -#define P54_STATISTICS_UPDATE 5000 - -#define P54_FILTER_TYPE_NONE 0 -#define P54_FILTER_TYPE_STATION BIT(0) -#define P54_FILTER_TYPE_IBSS BIT(1) -#define P54_FILTER_TYPE_AP BIT(2) -#define P54_FILTER_TYPE_TRANSPARENT BIT(3) -#define P54_FILTER_TYPE_PROMISCUOUS BIT(4) -#define P54_FILTER_TYPE_HIBERNATE BIT(5) -#define P54_FILTER_TYPE_NOACK BIT(6) -#define P54_FILTER_TYPE_RX_DISABLED BIT(7) - -struct p54_setup_mac { - __le16 mac_mode; - u8 mac_addr[ETH_ALEN]; - u8 bssid[ETH_ALEN]; - u8 rx_antenna; - u8 rx_align; - union { - struct { - __le32 basic_rate_mask; - u8 rts_rates[8]; - __le32 rx_addr; - __le16 max_rx; - __le16 rxhw; - __le16 wakeup_timer; - __le16 unalloc0; - } v1 __attribute__ ((packed)); - struct { - __le32 rx_addr; - __le16 max_rx; - __le16 rxhw; - __le16 timer; - __le16 truncate; - __le32 basic_rate_mask; - u8 sbss_offset; - u8 mcast_window; - u8 rx_rssi_threshold; - u8 rx_ed_threshold; - __le32 ref_clock; - __le16 lpf_bandwidth; - __le16 osc_start_delay; - } v2 __attribute__ ((packed)); - } __attribute__ ((packed)); -} __attribute__ ((packed)); - -#define P54_SETUP_V1_LEN 40 -#define P54_SETUP_V2_LEN (sizeof(struct p54_setup_mac)) - -#define P54_SCAN_EXIT BIT(0) -#define P54_SCAN_TRAP BIT(1) -#define P54_SCAN_ACTIVE BIT(2) -#define P54_SCAN_FILTER BIT(3) - -struct p54_scan_head { - __le16 mode; - __le16 dwell; - u8 scan_params[20]; - __le16 freq; -} __attribute__ ((packed)); - -struct p54_scan_body { - u8 pa_points_per_curve; - u8 val_barker; - u8 val_bpsk; - u8 val_qpsk; - u8 val_16qam; - u8 val_64qam; - struct p54_pa_curve_data_sample curve_data[8]; - u8 dup_bpsk; - u8 dup_qpsk; - u8 dup_16qam; - u8 dup_64qam; -} __attribute__ ((packed)); - -struct p54_scan_body_longbow { - struct p54_channel_output_limit_longbow power_limits; - struct p54_pa_curve_data_sample_longbow curve_data[8]; - __le16 unkn[6]; /* maybe more power_limits or rate_mask */ -} __attribute__ ((packed)); - -union p54_scan_body_union { - struct p54_scan_body normal; - struct p54_scan_body_longbow longbow; -} __attribute__ ((packed)); - -struct p54_scan_tail_rate { - __le32 basic_rate_mask; - u8 rts_rates[8]; -} __attribute__ ((packed)); - -struct p54_led { - __le16 flags; - __le16 mask[2]; - __le16 delay[2]; -} __attribute__ ((packed)); - -struct p54_edcf { - u8 flags; - u8 slottime; - u8 sifs; - u8 eofpad; - struct p54_edcf_queue_param queue[8]; - u8 mapping[4]; - __le16 frameburst; - __le16 round_trip_delay; -} __attribute__ ((packed)); - -struct p54_statistics { - __le32 rx_success; - __le32 rx_bad_fcs; - __le32 rx_abort; - __le32 rx_abort_phy; - __le32 rts_success; - __le32 rts_fail; - __le32 tsf32; - __le32 airtime; - __le32 noise; - __le32 sample_noise[8]; - __le32 sample_cca; - __le32 sample_tx; -} __attribute__ ((packed)); - -struct p54_xbow_synth { - __le16 magic1; - __le16 magic2; - __le16 freq; - u32 padding[5]; -} __attribute__ ((packed)); - -struct p54_timer { - __le32 interval; -} __attribute__ ((packed)); - -struct p54_keycache { - u8 entry; - u8 key_id; - u8 mac[ETH_ALEN]; - u8 padding[2]; - u8 key_type; - u8 key_len; - u8 key[24]; -} __attribute__ ((packed)); - -struct p54_burst { - u8 flags; - u8 queue; - u8 backlog; - u8 pad; - __le16 durations[32]; -} __attribute__ ((packed)); - -struct p54_psm_interval { - __le16 interval; - __le16 periods; -} __attribute__ ((packed)); - -#define P54_PSM_CAM 0 -#define P54_PSM BIT(0) -#define P54_PSM_DTIM BIT(1) -#define P54_PSM_MCBC BIT(2) -#define P54_PSM_CHECKSUM BIT(3) -#define P54_PSM_SKIP_MORE_DATA BIT(4) -#define P54_PSM_BEACON_TIMEOUT BIT(5) -#define P54_PSM_HFOSLEEP BIT(6) -#define P54_PSM_AUTOSWITCH_SLEEP BIT(7) -#define P54_PSM_LPIT BIT(8) -#define P54_PSM_BF_UCAST_SKIP BIT(9) -#define P54_PSM_BF_MCAST_SKIP BIT(10) - -struct p54_psm { - __le16 mode; - __le16 aid; - struct p54_psm_interval intervals[4]; - u8 beacon_rssi_skip_max; - u8 rssi_delta_threshold; - u8 nr; - u8 exclude[1]; -} __attribute__ ((packed)); - -#define MC_FILTER_ADDRESS_NUM 4 - -struct p54_group_address_table { - __le16 filter_enable; - __le16 num_address; - u8 mac_list[MC_FILTER_ADDRESS_NUM][ETH_ALEN]; -} __attribute__ ((packed)); - -struct p54_txcancel { - __le32 req_id; -} __attribute__ ((packed)); - -struct p54_sta_unlock { - u8 addr[ETH_ALEN]; - u16 padding; -} __attribute__ ((packed)); - -#define P54_TIM_CLEAR BIT(15) -struct p54_tim { - u8 count; - u8 padding[3]; - __le16 entry[8]; -} __attribute__ ((packed)); - -struct p54_cce_quiet { - __le32 period; -} __attribute__ ((packed)); - -struct p54_bt_balancer { - __le16 prio_thresh; - __le16 acl_thresh; -} __attribute__ ((packed)); - -struct p54_arp_table { - __le16 filter_enable; - u8 ipv4_addr[4]; -} __attribute__ ((packed)); - -#endif /* P54COMMON_H */ diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c index b1610ea4bb3d..d348c265e867 100644 --- a/drivers/net/wireless/p54/p54pci.c +++ b/drivers/net/wireless/p54/p54pci.c @@ -22,6 +22,7 @@ #include <net/mac80211.h> #include "p54.h" +#include "lmac.h" #include "p54pci.h" MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); @@ -564,7 +565,6 @@ static int __devinit p54p_probe(struct pci_dev *pdev, err_free_common: release_firmware(priv->firmware); - p54_free_common(dev); pci_free_consistent(pdev, sizeof(*priv->ring_control), priv->ring_control, priv->ring_control_dma); @@ -573,7 +573,7 @@ static int __devinit p54p_probe(struct pci_dev *pdev, err_free_dev: pci_set_drvdata(pdev, NULL); - ieee80211_free_hw(dev); + p54_free_common(dev); err_free_reg: pci_release_regions(pdev); @@ -590,16 +590,15 @@ static void __devexit p54p_remove(struct pci_dev *pdev) if (!dev) return; - ieee80211_unregister_hw(dev); + p54_unregister_common(dev); priv = dev->priv; release_firmware(priv->firmware); pci_free_consistent(pdev, sizeof(*priv->ring_control), priv->ring_control, priv->ring_control_dma); - p54_free_common(dev); iounmap(priv->map); pci_release_regions(pdev); pci_disable_device(pdev); - ieee80211_free_hw(dev); + p54_free_common(dev); } #ifdef CONFIG_PM diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 72c7dbd39d0a..05458d9249ce 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -34,7 +34,7 @@ #include "p54spi_eeprom.h" #include "p54.h" -#include "p54common.h" +#include "lmac.h" MODULE_FIRMWARE("3826.arm"); MODULE_ALIAS("stlc45xx"); @@ -111,15 +111,6 @@ static void p54spi_spi_write(struct p54s_priv *priv, u8 address, spi_sync(priv->spi, &m); } -static u16 p54spi_read16(struct p54s_priv *priv, u8 addr) -{ - __le16 val; - - p54spi_spi_read(priv, addr, &val, sizeof(val)); - - return le16_to_cpu(val); -} - static u32 p54spi_read32(struct p54s_priv *priv, u8 addr) { __le32 val; @@ -139,37 +130,12 @@ static inline void p54spi_write32(struct p54s_priv *priv, u8 addr, __le32 val) p54spi_spi_write(priv, addr, &val, sizeof(val)); } -struct p54spi_spi_reg { - u16 address; /* __le16 ? */ - u16 length; - char *name; -}; - -static const struct p54spi_spi_reg p54spi_registers_array[] = -{ - { SPI_ADRS_ARM_INTERRUPTS, 32, "ARM_INT " }, - { SPI_ADRS_ARM_INT_EN, 32, "ARM_INT_ENA " }, - { SPI_ADRS_HOST_INTERRUPTS, 32, "HOST_INT " }, - { SPI_ADRS_HOST_INT_EN, 32, "HOST_INT_ENA" }, - { SPI_ADRS_HOST_INT_ACK, 32, "HOST_INT_ACK" }, - { SPI_ADRS_GEN_PURP_1, 32, "GP1_COMM " }, - { SPI_ADRS_GEN_PURP_2, 32, "GP2_COMM " }, - { SPI_ADRS_DEV_CTRL_STAT, 32, "DEV_CTRL_STA" }, - { SPI_ADRS_DMA_DATA, 16, "DMA_DATA " }, - { SPI_ADRS_DMA_WRITE_CTRL, 16, "DMA_WR_CTRL " }, - { SPI_ADRS_DMA_WRITE_LEN, 16, "DMA_WR_LEN " }, - { SPI_ADRS_DMA_WRITE_BASE, 32, "DMA_WR_BASE " }, - { SPI_ADRS_DMA_READ_CTRL, 16, "DMA_RD_CTRL " }, - { SPI_ADRS_DMA_READ_LEN, 16, "DMA_RD_LEN " }, - { SPI_ADRS_DMA_WRITE_BASE, 32, "DMA_RD_BASE " } -}; - -static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, __le32 bits) +static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, u32 bits) { int i; for (i = 0; i < 2000; i++) { - __le32 buffer = p54spi_read32(priv, reg); + u32 buffer = p54spi_read32(priv, reg); if ((buffer & bits) == bits) return 1; } @@ -179,8 +145,7 @@ static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, __le32 bits) static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base, const void *buf, size_t len) { - if (!p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL, - cpu_to_le32(HOST_ALLOWED))) { + if (!p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL, HOST_ALLOWED)) { dev_err(&priv->spi->dev, "spi_write_dma not allowed " "to DMA write.\n"); return -EAGAIN; @@ -333,7 +298,7 @@ static int p54spi_wakeup(struct p54s_priv *priv) /* And wait for the READY interrupt */ if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS, - cpu_to_le32(SPI_HOST_INT_READY))) { + SPI_HOST_INT_READY)) { dev_err(&priv->spi->dev, "INT_READY timeout\n"); return -EBUSY; } @@ -426,7 +391,7 @@ static irqreturn_t p54spi_interrupt(int irq, void *config) struct spi_device *spi = config; struct p54s_priv *priv = dev_get_drvdata(&spi->dev); - queue_work(priv->hw->workqueue, &priv->work); + ieee80211_queue_work(priv->hw, &priv->work); return IRQ_HANDLED; } @@ -444,7 +409,7 @@ static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb) goto out; if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS, - cpu_to_le32(SPI_HOST_INT_WR_READY))) { + SPI_HOST_INT_WR_READY)) { dev_err(&priv->spi->dev, "WR_READY timeout\n"); ret = -EAGAIN; goto out; @@ -514,7 +479,7 @@ static void p54spi_op_tx(struct ieee80211_hw *dev, struct sk_buff *skb) list_add_tail(&di->tx_list, &priv->tx_pending); spin_unlock_irqrestore(&priv->tx_lock, flags); - queue_work(priv->hw->workqueue, &priv->work); + ieee80211_queue_work(priv->hw, &priv->work); } static void p54spi_work(struct work_struct *work) @@ -713,7 +678,7 @@ static int __devexit p54spi_remove(struct spi_device *spi) { struct p54s_priv *priv = dev_get_drvdata(&spi->dev); - ieee80211_unregister_hw(priv->hw); + p54_unregister_common(priv->hw); free_irq(gpio_to_irq(p54spi_gpio_irq), spi); @@ -724,7 +689,6 @@ static int __devexit p54spi_remove(struct spi_device *spi) mutex_destroy(&priv->mutex); p54_free_common(priv->hw); - ieee80211_free_hw(priv->hw); return 0; } diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 0e877a104a89..e44460ff149c 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -22,6 +22,7 @@ #include <net/mac80211.h> #include "p54.h" +#include "lmac.h" #include "p54usb.h" MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); @@ -245,8 +246,10 @@ static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb) struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); data_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!data_urb) + if (!data_urb) { + p54_free_skb(dev, skb); return; + } hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len); hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id; @@ -268,27 +271,22 @@ static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb) static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) { struct p54u_priv *priv = dev->priv; - struct urb *int_urb, *data_urb; + struct urb *int_urb = NULL, *data_urb = NULL; struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); - struct net2280_reg_write *reg; - int err = 0; + struct net2280_reg_write *reg = NULL; + int err = -ENOMEM; reg = kmalloc(sizeof(*reg), GFP_ATOMIC); if (!reg) - return; + goto out; int_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!int_urb) { - kfree(reg); - return; - } + if (!int_urb) + goto out; data_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!data_urb) { - kfree(reg); - usb_free_urb(int_urb); - return; - } + if (!data_urb) + goto out; reg->port = cpu_to_le16(NET2280_DEV_U32); reg->addr = cpu_to_le32(P54U_DEV_BASE); @@ -303,11 +301,12 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) p54u_tx_dummy_cb, dev); /* - * This flag triggers a code path in the USB subsystem that will - * free what's inside the transfer_buffer after the callback routine - * has completed. + * URB_FREE_BUFFER triggers a code path in the USB subsystem that will + * free what is inside the transfer_buffer after the last reference to + * the int_urb is dropped. */ int_urb->transfer_flags |= URB_FREE_BUFFER | URB_ZERO_PACKET; + reg = NULL; usb_fill_bulk_urb(data_urb, priv->udev, usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), @@ -328,12 +327,12 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) usb_unanchor_urb(data_urb); goto out; } - out: +out: usb_free_urb(int_urb); usb_free_urb(data_urb); if (err) { - skb_pull(skb, sizeof(*hdr)); + kfree(reg); p54_free_skb(dev, skb); } } @@ -961,7 +960,7 @@ err_free_fw: release_firmware(priv->fw); err_free_dev: - ieee80211_free_hw(dev); + p54_free_common(dev); usb_set_intfdata(intf, NULL); usb_put_dev(udev); return err; @@ -975,13 +974,12 @@ static void __devexit p54u_disconnect(struct usb_interface *intf) if (!dev) return; - ieee80211_unregister_hw(dev); + p54_unregister_common(dev); priv = dev->priv; usb_put_dev(interface_to_usbdev(intf)); release_firmware(priv->fw); p54_free_common(dev); - ieee80211_free_hw(dev); } static int p54u_pre_reset(struct usb_interface *intf) diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c new file mode 100644 index 000000000000..b6dda2b27fb5 --- /dev/null +++ b/drivers/net/wireless/p54/txrx.c @@ -0,0 +1,869 @@ +/* + * Common code for mac80211 Prism54 drivers + * + * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net> + * Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de> + * Copyright 2008, Johannes Berg <johannes@sipsolutions.net> + * + * Based on: + * - the islsm (softmac prism54) driver, which is: + * Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al. + * - stlc45xx driver + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/firmware.h> +#include <linux/etherdevice.h> + +#include <net/mac80211.h> + +#include "p54.h" +#include "lmac.h" + +#ifdef P54_MM_DEBUG +static void p54_dump_tx_queue(struct p54_common *priv) +{ + unsigned long flags; + struct ieee80211_tx_info *info; + struct p54_tx_info *range; + struct sk_buff *skb; + struct p54_hdr *hdr; + unsigned int i = 0; + u32 prev_addr; + u32 largest_hole = 0, free; + + spin_lock_irqsave(&priv->tx_queue.lock, flags); + printk(KERN_DEBUG "%s: / --- tx queue dump (%d entries) --- \n", + wiphy_name(priv->hw->wiphy), skb_queue_len(&priv->tx_queue)); + + prev_addr = priv->rx_start; + skb_queue_walk(&priv->tx_queue, skb) { + info = IEEE80211_SKB_CB(skb); + range = (void *) info->rate_driver_data; + hdr = (void *) skb->data; + + free = range->start_addr - prev_addr; + printk(KERN_DEBUG "%s: | [%02d] => [skb:%p skb_len:0x%04x " + "hdr:{flags:%02x len:%04x req_id:%04x type:%02x} " + "mem:{start:%04x end:%04x, free:%d}]\n", + wiphy_name(priv->hw->wiphy), i++, skb, skb->len, + le16_to_cpu(hdr->flags), le16_to_cpu(hdr->len), + le32_to_cpu(hdr->req_id), le16_to_cpu(hdr->type), + range->start_addr, range->end_addr, free); + + prev_addr = range->end_addr; + largest_hole = max(largest_hole, free); + } + free = priv->rx_end - prev_addr; + largest_hole = max(largest_hole, free); + printk(KERN_DEBUG "%s: \\ --- [free: %d], largest free block: %d ---\n", + wiphy_name(priv->hw->wiphy), free, largest_hole); + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); +} +#endif /* P54_MM_DEBUG */ + +/* + * So, the firmware is somewhat stupid and doesn't know what places in its + * memory incoming data should go to. By poking around in the firmware, we + * can find some unused memory to upload our packets to. However, data that we + * want the card to TX needs to stay intact until the card has told us that + * it is done with it. This function finds empty places we can upload to and + * marks allocated areas as reserved if necessary. p54_find_and_unlink_skb or + * p54_free_skb frees allocated areas. + */ +static int p54_assign_address(struct p54_common *priv, struct sk_buff *skb) +{ + struct sk_buff *entry, *target_skb = NULL; + struct ieee80211_tx_info *info; + struct p54_tx_info *range; + struct p54_hdr *data = (void *) skb->data; + unsigned long flags; + u32 last_addr = priv->rx_start; + u32 target_addr = priv->rx_start; + u16 len = priv->headroom + skb->len + priv->tailroom + 3; + + info = IEEE80211_SKB_CB(skb); + range = (void *) info->rate_driver_data; + len = (range->extra_len + len) & ~0x3; + + spin_lock_irqsave(&priv->tx_queue.lock, flags); + if (unlikely(skb_queue_len(&priv->tx_queue) == 32)) { + /* + * The tx_queue is now really full. + * + * TODO: check if the device has crashed and reset it. + */ + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); + return -EBUSY; + } + + skb_queue_walk(&priv->tx_queue, entry) { + u32 hole_size; + info = IEEE80211_SKB_CB(entry); + range = (void *) info->rate_driver_data; + hole_size = range->start_addr - last_addr; + + if (!target_skb && hole_size >= len) { + target_skb = entry->prev; + hole_size -= len; + target_addr = last_addr; + break; + } + last_addr = range->end_addr; + } + if (unlikely(!target_skb)) { + if (priv->rx_end - last_addr >= len) { + target_skb = priv->tx_queue.prev; + if (!skb_queue_empty(&priv->tx_queue)) { + info = IEEE80211_SKB_CB(target_skb); + range = (void *)info->rate_driver_data; + target_addr = range->end_addr; + } + } else { + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); + return -ENOSPC; + } + } + + info = IEEE80211_SKB_CB(skb); + range = (void *) info->rate_driver_data; + range->start_addr = target_addr; + range->end_addr = target_addr + len; + data->req_id = cpu_to_le32(target_addr + priv->headroom); + if (IS_DATA_FRAME(skb) && + unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) + priv->beacon_req_id = data->req_id; + + __skb_queue_after(&priv->tx_queue, target_skb, skb); + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); + return 0; +} + +static void p54_tx_pending(struct p54_common *priv) +{ + struct sk_buff *skb; + int ret; + + skb = skb_dequeue(&priv->tx_pending); + if (unlikely(!skb)) + return ; + + ret = p54_assign_address(priv, skb); + if (unlikely(ret)) + skb_queue_head(&priv->tx_pending, skb); + else + priv->tx(priv->hw, skb); +} + +static void p54_wake_queues(struct p54_common *priv) +{ + unsigned long flags; + unsigned int i; + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) + return ; + + p54_tx_pending(priv); + + spin_lock_irqsave(&priv->tx_stats_lock, flags); + for (i = 0; i < priv->hw->queues; i++) { + if (priv->tx_stats[i + P54_QUEUE_DATA].len < + priv->tx_stats[i + P54_QUEUE_DATA].limit) + ieee80211_wake_queue(priv->hw, i); + } + spin_unlock_irqrestore(&priv->tx_stats_lock, flags); +} + +static int p54_tx_qos_accounting_alloc(struct p54_common *priv, + struct sk_buff *skb, + const u16 p54_queue) +{ + struct ieee80211_tx_queue_stats *queue; + unsigned long flags; + + if (WARN_ON(p54_queue > P54_QUEUE_NUM)) + return -EINVAL; + + queue = &priv->tx_stats[p54_queue]; + + spin_lock_irqsave(&priv->tx_stats_lock, flags); + if (unlikely(queue->len >= queue->limit && IS_QOS_QUEUE(p54_queue))) { + spin_unlock_irqrestore(&priv->tx_stats_lock, flags); + return -ENOSPC; + } + + queue->len++; + queue->count++; + + if (unlikely(queue->len == queue->limit && IS_QOS_QUEUE(p54_queue))) { + u16 ac_queue = p54_queue - P54_QUEUE_DATA; + ieee80211_stop_queue(priv->hw, ac_queue); + } + + spin_unlock_irqrestore(&priv->tx_stats_lock, flags); + return 0; +} + +static void p54_tx_qos_accounting_free(struct p54_common *priv, + struct sk_buff *skb) +{ + if (IS_DATA_FRAME(skb)) { + unsigned long flags; + + spin_lock_irqsave(&priv->tx_stats_lock, flags); + priv->tx_stats[GET_HW_QUEUE(skb)].len--; + spin_unlock_irqrestore(&priv->tx_stats_lock, flags); + + if (unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) { + if (priv->beacon_req_id == GET_REQ_ID(skb)) { + /* this is the active beacon set anymore */ + priv->beacon_req_id = 0; + } + complete(&priv->beacon_comp); + } + } + p54_wake_queues(priv); +} + +void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct p54_common *priv = dev->priv; + if (unlikely(!skb)) + return ; + + skb_unlink(skb, &priv->tx_queue); + p54_tx_qos_accounting_free(priv, skb); + dev_kfree_skb_any(skb); +} +EXPORT_SYMBOL_GPL(p54_free_skb); + +static struct sk_buff *p54_find_and_unlink_skb(struct p54_common *priv, + const __le32 req_id) +{ + struct sk_buff *entry; + unsigned long flags; + + spin_lock_irqsave(&priv->tx_queue.lock, flags); + skb_queue_walk(&priv->tx_queue, entry) { + struct p54_hdr *hdr = (struct p54_hdr *) entry->data; + + if (hdr->req_id == req_id) { + __skb_unlink(entry, &priv->tx_queue); + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); + p54_tx_qos_accounting_free(priv, entry); + return entry; + } + } + spin_unlock_irqrestore(&priv->tx_queue.lock, flags); + return NULL; +} + +void p54_tx(struct p54_common *priv, struct sk_buff *skb) +{ + skb_queue_tail(&priv->tx_pending, skb); + p54_tx_pending(priv); +} + +static int p54_rssi_to_dbm(struct p54_common *priv, int rssi) +{ + int band = priv->hw->conf.channel->band; + + if (priv->rxhw != 5) + return ((rssi * priv->rssical_db[band].mul) / 64 + + priv->rssical_db[band].add) / 4; + else + /* + * TODO: find the correct formula + */ + return ((rssi * priv->rssical_db[band].mul) / 64 + + priv->rssical_db[band].add) / 4; +} + +/* + * Even if the firmware is capable of dealing with incoming traffic, + * while dozing, we have to prepared in case mac80211 uses PS-POLL + * to retrieve outstanding frames from our AP. + * (see comment in net/mac80211/mlme.c @ line 1993) + */ +static void p54_pspoll_workaround(struct p54_common *priv, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (void *) skb->data; + struct ieee80211_tim_ie *tim_ie; + u8 *tim; + u8 tim_len; + bool new_psm; + + /* only beacons have a TIM IE */ + if (!ieee80211_is_beacon(hdr->frame_control)) + return; + + if (!priv->aid) + return; + + /* only consider beacons from the associated BSSID */ + if (compare_ether_addr(hdr->addr3, priv->bssid)) + return; + + tim = p54_find_ie(skb, WLAN_EID_TIM); + if (!tim) + return; + + tim_len = tim[1]; + tim_ie = (struct ieee80211_tim_ie *) &tim[2]; + + new_psm = ieee80211_check_tim(tim_ie, tim_len, priv->aid); + if (new_psm != priv->powersave_override) { + priv->powersave_override = new_psm; + p54_set_ps(priv); + } +} + +static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb) +{ + struct p54_rx_data *hdr = (struct p54_rx_data *) skb->data; + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + u16 freq = le16_to_cpu(hdr->freq); + size_t header_len = sizeof(*hdr); + u32 tsf32; + u8 rate = hdr->rate & 0xf; + + /* + * If the device is in a unspecified state we have to + * ignore all data frames. Else we could end up with a + * nasty crash. + */ + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) + return 0; + + if (!(hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_IN_FCS_GOOD))) + return 0; + + if (hdr->decrypt_status == P54_DECRYPT_OK) + rx_status->flag |= RX_FLAG_DECRYPTED; + if ((hdr->decrypt_status == P54_DECRYPT_FAIL_MICHAEL) || + (hdr->decrypt_status == P54_DECRYPT_FAIL_TKIP)) + rx_status->flag |= RX_FLAG_MMIC_ERROR; + + rx_status->signal = p54_rssi_to_dbm(priv, hdr->rssi); + rx_status->noise = priv->noise; + if (hdr->rate & 0x10) + rx_status->flag |= RX_FLAG_SHORTPRE; + if (priv->hw->conf.channel->band == IEEE80211_BAND_5GHZ) + rx_status->rate_idx = (rate < 4) ? 0 : rate - 4; + else + rx_status->rate_idx = rate; + + rx_status->freq = freq; + rx_status->band = priv->hw->conf.channel->band; + rx_status->antenna = hdr->antenna; + + tsf32 = le32_to_cpu(hdr->tsf32); + if (tsf32 < priv->tsf_low32) + priv->tsf_high32++; + rx_status->mactime = ((u64)priv->tsf_high32) << 32 | tsf32; + priv->tsf_low32 = tsf32; + + rx_status->flag |= RX_FLAG_TSFT; + + if (hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) + header_len += hdr->align[0]; + + skb_pull(skb, header_len); + skb_trim(skb, le16_to_cpu(hdr->len)); + if (unlikely(priv->hw->conf.flags & IEEE80211_CONF_PS)) + p54_pspoll_workaround(priv, skb); + + ieee80211_rx_irqsafe(priv->hw, skb); + + ieee80211_queue_delayed_work(priv->hw, &priv->work, + msecs_to_jiffies(P54_STATISTICS_UPDATE)); + + return -1; +} + +static void p54_rx_frame_sent(struct p54_common *priv, struct sk_buff *skb) +{ + struct p54_hdr *hdr = (struct p54_hdr *) skb->data; + struct p54_frame_sent *payload = (struct p54_frame_sent *) hdr->data; + struct ieee80211_tx_info *info; + struct p54_hdr *entry_hdr; + struct p54_tx_data *entry_data; + struct sk_buff *entry; + unsigned int pad = 0, frame_len; + int count, idx; + + entry = p54_find_and_unlink_skb(priv, hdr->req_id); + if (unlikely(!entry)) + return ; + + frame_len = entry->len; + info = IEEE80211_SKB_CB(entry); + entry_hdr = (struct p54_hdr *) entry->data; + entry_data = (struct p54_tx_data *) entry_hdr->data; + priv->stats.dot11ACKFailureCount += payload->tries - 1; + + /* + * Frames in P54_QUEUE_FWSCAN and P54_QUEUE_BEACON are + * generated by the driver. Therefore tx_status is bogus + * and we don't want to confuse the mac80211 stack. + */ + if (unlikely(entry_data->hw_queue < P54_QUEUE_FWSCAN)) { + dev_kfree_skb_any(entry); + return ; + } + + /* + * Clear manually, ieee80211_tx_info_clear_status would + * clear the counts too and we need them. + */ + memset(&info->status.ampdu_ack_len, 0, + sizeof(struct ieee80211_tx_info) - + offsetof(struct ieee80211_tx_info, status.ampdu_ack_len)); + BUILD_BUG_ON(offsetof(struct ieee80211_tx_info, + status.ampdu_ack_len) != 23); + + if (entry_hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) + pad = entry_data->align[0]; + + /* walk through the rates array and adjust the counts */ + count = payload->tries; + for (idx = 0; idx < 4; idx++) { + if (count >= info->status.rates[idx].count) { + count -= info->status.rates[idx].count; + } else if (count > 0) { + info->status.rates[idx].count = count; + count = 0; + } else { + info->status.rates[idx].idx = -1; + info->status.rates[idx].count = 0; + } + } + + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && + (!payload->status)) + info->flags |= IEEE80211_TX_STAT_ACK; + if (payload->status & P54_TX_PSM_CANCELLED) + info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + info->status.ack_signal = p54_rssi_to_dbm(priv, + (int)payload->ack_rssi); + + /* Undo all changes to the frame. */ + switch (entry_data->key_type) { + case P54_CRYPTO_TKIPMICHAEL: { + u8 *iv = (u8 *)(entry_data->align + pad + + entry_data->crypt_offset); + + /* Restore the original TKIP IV. */ + iv[2] = iv[0]; + iv[0] = iv[1]; + iv[1] = (iv[0] | 0x20) & 0x7f; /* WEPSeed - 8.3.2.2 */ + + frame_len -= 12; /* remove TKIP_MMIC + TKIP_ICV */ + break; + } + case P54_CRYPTO_AESCCMP: + frame_len -= 8; /* remove CCMP_MIC */ + break; + case P54_CRYPTO_WEP: + frame_len -= 4; /* remove WEP_ICV */ + break; + } + + skb_trim(entry, frame_len); + skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data)); + ieee80211_tx_status_irqsafe(priv->hw, entry); +} + +static void p54_rx_eeprom_readback(struct p54_common *priv, + struct sk_buff *skb) +{ + struct p54_hdr *hdr = (struct p54_hdr *) skb->data; + struct p54_eeprom_lm86 *eeprom = (struct p54_eeprom_lm86 *) hdr->data; + struct sk_buff *tmp; + + if (!priv->eeprom) + return ; + + if (priv->fw_var >= 0x509) { + memcpy(priv->eeprom, eeprom->v2.data, + le16_to_cpu(eeprom->v2.len)); + } else { + memcpy(priv->eeprom, eeprom->v1.data, + le16_to_cpu(eeprom->v1.len)); + } + + priv->eeprom = NULL; + tmp = p54_find_and_unlink_skb(priv, hdr->req_id); + dev_kfree_skb_any(tmp); + complete(&priv->eeprom_comp); +} + +static void p54_rx_stats(struct p54_common *priv, struct sk_buff *skb) +{ + struct p54_hdr *hdr = (struct p54_hdr *) skb->data; + struct p54_statistics *stats = (struct p54_statistics *) hdr->data; + struct sk_buff *tmp; + u32 tsf32; + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) + return ; + + tsf32 = le32_to_cpu(stats->tsf32); + if (tsf32 < priv->tsf_low32) + priv->tsf_high32++; + priv->tsf_low32 = tsf32; + + priv->stats.dot11RTSFailureCount = le32_to_cpu(stats->rts_fail); + priv->stats.dot11RTSSuccessCount = le32_to_cpu(stats->rts_success); + priv->stats.dot11FCSErrorCount = le32_to_cpu(stats->rx_bad_fcs); + + priv->noise = p54_rssi_to_dbm(priv, le32_to_cpu(stats->noise)); + + tmp = p54_find_and_unlink_skb(priv, hdr->req_id); + dev_kfree_skb_any(tmp); +} + +static void p54_rx_trap(struct p54_common *priv, struct sk_buff *skb) +{ + struct p54_hdr *hdr = (struct p54_hdr *) skb->data; + struct p54_trap *trap = (struct p54_trap *) hdr->data; + u16 event = le16_to_cpu(trap->event); + u16 freq = le16_to_cpu(trap->frequency); + + switch (event) { + case P54_TRAP_BEACON_TX: + break; + case P54_TRAP_RADAR: + printk(KERN_INFO "%s: radar (freq:%d MHz)\n", + wiphy_name(priv->hw->wiphy), freq); + break; + case P54_TRAP_NO_BEACON: + if (priv->vif) + ieee80211_beacon_loss(priv->vif); + break; + case P54_TRAP_SCAN: + break; + case P54_TRAP_TBTT: + break; + case P54_TRAP_TIMER: + break; + case P54_TRAP_FAA_RADIO_OFF: + wiphy_rfkill_set_hw_state(priv->hw->wiphy, true); + break; + case P54_TRAP_FAA_RADIO_ON: + wiphy_rfkill_set_hw_state(priv->hw->wiphy, false); + break; + default: + printk(KERN_INFO "%s: received event:%x freq:%d\n", + wiphy_name(priv->hw->wiphy), event, freq); + break; + } +} + +static int p54_rx_control(struct p54_common *priv, struct sk_buff *skb) +{ + struct p54_hdr *hdr = (struct p54_hdr *) skb->data; + + switch (le16_to_cpu(hdr->type)) { + case P54_CONTROL_TYPE_TXDONE: + p54_rx_frame_sent(priv, skb); + break; + case P54_CONTROL_TYPE_TRAP: + p54_rx_trap(priv, skb); + break; + case P54_CONTROL_TYPE_BBP: + break; + case P54_CONTROL_TYPE_STAT_READBACK: + p54_rx_stats(priv, skb); + break; + case P54_CONTROL_TYPE_EEPROM_READBACK: + p54_rx_eeprom_readback(priv, skb); + break; + default: + printk(KERN_DEBUG "%s: not handling 0x%02x type control frame\n", + wiphy_name(priv->hw->wiphy), le16_to_cpu(hdr->type)); + break; + } + return 0; +} + +/* returns zero if skb can be reused */ +int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct p54_common *priv = dev->priv; + u16 type = le16_to_cpu(*((__le16 *)skb->data)); + + if (type & P54_HDR_FLAG_CONTROL) + return p54_rx_control(priv, skb); + else + return p54_rx_data(priv, skb); +} +EXPORT_SYMBOL_GPL(p54_rx); + +static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb, + struct ieee80211_tx_info *info, u8 *queue, + u32 *extra_len, u16 *flags, u16 *aid, + bool *burst_possible) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + if (ieee80211_is_data_qos(hdr->frame_control)) + *burst_possible = true; + else + *burst_possible = false; + + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) + *flags |= P54_HDR_FLAG_DATA_OUT_SEQNR; + + if (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE) + *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; + + if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) + *flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL; + + *queue = skb_get_queue_mapping(skb) + P54_QUEUE_DATA; + + switch (priv->mode) { + case NL80211_IFTYPE_MONITOR: + /* + * We have to set P54_HDR_FLAG_DATA_OUT_PROMISC for + * every frame in promiscuous/monitor mode. + * see STSW45x0C LMAC API - page 12. + */ + *aid = 0; + *flags |= P54_HDR_FLAG_DATA_OUT_PROMISC; + break; + case NL80211_IFTYPE_STATION: + *aid = 1; + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { + *aid = 0; + *queue = P54_QUEUE_CAB; + return; + } + + if (unlikely(ieee80211_is_mgmt(hdr->frame_control))) { + if (ieee80211_is_probe_resp(hdr->frame_control)) { + *aid = 0; + *flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP | + P54_HDR_FLAG_DATA_OUT_NOCANCEL; + return; + } else if (ieee80211_is_beacon(hdr->frame_control)) { + *aid = 0; + + if (info->flags & IEEE80211_TX_CTL_INJECTED) { + /* + * Injecting beacons on top of a AP is + * not a good idea... nevertheless, + * it should be doable. + */ + + return; + } + + *flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP; + *queue = P54_QUEUE_BEACON; + *extra_len = IEEE80211_MAX_TIM_LEN; + return; + } + } + + if (info->control.sta) + *aid = info->control.sta->aid; + break; + } +} + +static u8 p54_convert_algo(enum ieee80211_key_alg alg) +{ + switch (alg) { + case ALG_WEP: + return P54_CRYPTO_WEP; + case ALG_TKIP: + return P54_CRYPTO_TKIPMICHAEL; + case ALG_CCMP: + return P54_CRYPTO_AESCCMP; + default: + return 0; + } +} + +int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct p54_common *priv = dev->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct p54_tx_info *p54info; + struct p54_hdr *hdr; + struct p54_tx_data *txhdr; + unsigned int padding, len, extra_len; + int i, j, ridx; + u16 hdr_flags = 0, aid = 0; + u8 rate, queue = 0, crypt_offset = 0; + u8 cts_rate = 0x20; + u8 rc_flags; + u8 calculated_tries[4]; + u8 nrates = 0, nremaining = 8; + bool burst_allowed = false; + + p54_tx_80211_header(priv, skb, info, &queue, &extra_len, + &hdr_flags, &aid, &burst_allowed); + + if (p54_tx_qos_accounting_alloc(priv, skb, queue)) { + if (!IS_QOS_QUEUE(queue)) { + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } else { + return NETDEV_TX_BUSY; + } + } + + padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3; + len = skb->len; + + if (info->control.hw_key) { + crypt_offset = ieee80211_get_hdrlen_from_skb(skb); + if (info->control.hw_key->alg == ALG_TKIP) { + u8 *iv = (u8 *)(skb->data + crypt_offset); + /* + * The firmware excepts that the IV has to have + * this special format + */ + iv[1] = iv[0]; + iv[0] = iv[2]; + iv[2] = 0; + } + } + + txhdr = (struct p54_tx_data *) skb_push(skb, sizeof(*txhdr) + padding); + hdr = (struct p54_hdr *) skb_push(skb, sizeof(*hdr)); + + if (padding) + hdr_flags |= P54_HDR_FLAG_DATA_ALIGN; + hdr->type = cpu_to_le16(aid); + hdr->rts_tries = info->control.rates[0].count; + + /* + * we register the rates in perfect order, and + * RTS/CTS won't happen on 5 GHz + */ + cts_rate = info->control.rts_cts_rate_idx; + + memset(&txhdr->rateset, 0, sizeof(txhdr->rateset)); + + /* see how many rates got used */ + for (i = 0; i < dev->max_rates; i++) { + if (info->control.rates[i].idx < 0) + break; + nrates++; + } + + /* limit tries to 8/nrates per rate */ + for (i = 0; i < nrates; i++) { + /* + * The magic expression here is equivalent to 8/nrates for + * all values that matter, but avoids division and jumps. + * Note that nrates can only take the values 1 through 4. + */ + calculated_tries[i] = min_t(int, ((15 >> nrates) | 1) + 1, + info->control.rates[i].count); + nremaining -= calculated_tries[i]; + } + + /* if there are tries left, distribute from back to front */ + for (i = nrates - 1; nremaining > 0 && i >= 0; i--) { + int tmp = info->control.rates[i].count - calculated_tries[i]; + + if (tmp <= 0) + continue; + /* RC requested more tries at this rate */ + + tmp = min_t(int, tmp, nremaining); + calculated_tries[i] += tmp; + nremaining -= tmp; + } + + ridx = 0; + for (i = 0; i < nrates && ridx < 8; i++) { + /* we register the rates in perfect order */ + rate = info->control.rates[i].idx; + if (info->band == IEEE80211_BAND_5GHZ) + rate += 4; + + /* store the count we actually calculated for TX status */ + info->control.rates[i].count = calculated_tries[i]; + + rc_flags = info->control.rates[i].flags; + if (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) { + rate |= 0x10; + cts_rate |= 0x10; + } + if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { + burst_allowed = false; + rate |= 0x40; + } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { + rate |= 0x20; + burst_allowed = false; + } + for (j = 0; j < calculated_tries[i] && ridx < 8; j++) { + txhdr->rateset[ridx] = rate; + ridx++; + } + } + + if (burst_allowed) + hdr_flags |= P54_HDR_FLAG_DATA_OUT_BURST; + + /* TODO: enable bursting */ + hdr->flags = cpu_to_le16(hdr_flags); + hdr->tries = ridx; + txhdr->rts_rate_idx = 0; + if (info->control.hw_key) { + txhdr->key_type = p54_convert_algo(info->control.hw_key->alg); + txhdr->key_len = min((u8)16, info->control.hw_key->keylen); + memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len); + if (info->control.hw_key->alg == ALG_TKIP) { + /* reserve space for the MIC key */ + len += 8; + memcpy(skb_put(skb, 8), &(info->control.hw_key->key + [NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]), 8); + } + /* reserve some space for ICV */ + len += info->control.hw_key->icv_len; + memset(skb_put(skb, info->control.hw_key->icv_len), 0, + info->control.hw_key->icv_len); + } else { + txhdr->key_type = 0; + txhdr->key_len = 0; + } + txhdr->crypt_offset = crypt_offset; + txhdr->hw_queue = queue; + txhdr->backlog = priv->tx_stats[queue].len - 1; + memset(txhdr->durations, 0, sizeof(txhdr->durations)); + txhdr->tx_antenna = ((info->antenna_sel_tx == 0) ? + 2 : info->antenna_sel_tx - 1) & priv->tx_diversity_mask; + if (priv->rxhw == 5) { + txhdr->longbow.cts_rate = cts_rate; + txhdr->longbow.output_power = cpu_to_le16(priv->output_power); + } else { + txhdr->normal.output_power = priv->output_power; + txhdr->normal.cts_rate = cts_rate; + } + if (padding) + txhdr->align[0] = padding; + + hdr->len = cpu_to_le16(len); + /* modifies skb->cb and with it info, so must be last! */ + p54info = (void *) info->rate_driver_data; + p54info->extra_len = extra_len; + + p54_tx(priv, skb); + return NETDEV_TX_OK; +} diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/prism54/islpci_eth.c index 8f6210993448..872b64783e78 100644 --- a/drivers/net/wireless/prism54/islpci_eth.c +++ b/drivers/net/wireless/prism54/islpci_eth.c @@ -50,7 +50,7 @@ islpci_eth_cleanup_transmit(islpci_private *priv, /* check for holes in the arrays caused by multi fragment frames * searching for the last fragment of a frame */ - if (priv->pci_map_tx_address[index] != (dma_addr_t) NULL) { + if (priv->pci_map_tx_address[index]) { /* entry is the last fragment of a frame * free the skb structure and unmap pci memory */ skb = priv->data_low_tx[index]; @@ -72,7 +72,7 @@ islpci_eth_cleanup_transmit(islpci_private *priv, } } -int +netdev_tx_t islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev) { islpci_private *priv = netdev_priv(ndev); @@ -234,7 +234,7 @@ islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev) /* unlock the driver code */ spin_unlock_irqrestore(&priv->slock, flags); - return 0; + return NETDEV_TX_OK; drop_free: ndev->stats.tx_dropped++; @@ -450,7 +450,7 @@ islpci_eth_receive(islpci_private *priv) pci_map_single(priv->pdev, (void *) skb->data, MAX_FRAGMENT_SIZE_RX + 2, PCI_DMA_FROMDEVICE); - if (unlikely(priv->pci_map_rx_address[index] == (dma_addr_t) NULL)) { + if (unlikely(!priv->pci_map_rx_address[index])) { /* error mapping the buffer to device accessable memory address */ DEBUG(SHOW_ERROR_MESSAGES, "Error mapping DMA address\n"); diff --git a/drivers/net/wireless/prism54/islpci_eth.h b/drivers/net/wireless/prism54/islpci_eth.h index 61454d32d74d..54f9a4b7bf9b 100644 --- a/drivers/net/wireless/prism54/islpci_eth.h +++ b/drivers/net/wireless/prism54/islpci_eth.h @@ -64,7 +64,7 @@ struct avs_80211_1_header { }; void islpci_eth_cleanup_transmit(islpci_private *, isl38xx_control_block *); -int islpci_eth_transmit(struct sk_buff *, struct net_device *); +netdev_tx_t islpci_eth_transmit(struct sk_buff *, struct net_device *); int islpci_eth_receive(islpci_private *); void islpci_eth_tx_timeout(struct net_device *); void islpci_do_reset_and_wake(struct work_struct *); diff --git a/drivers/net/wireless/prism54/islpci_hotplug.c b/drivers/net/wireless/prism54/islpci_hotplug.c index 30876728d7e6..83d366258c81 100644 --- a/drivers/net/wireless/prism54/islpci_hotplug.c +++ b/drivers/net/wireless/prism54/islpci_hotplug.c @@ -49,9 +49,7 @@ static const struct pci_device_id prism54_id_tbl[] = { /* 3COM 3CRWE154G72 Wireless LAN adapter */ { - 0x10b7, 0x6001, - PCI_ANY_ID, PCI_ANY_ID, - 0, 0, 0 + PCI_VDEVICE(3COM, 0x6001), 0 }, /* Intersil PRISM Indigo Wireless LAN adapter */ diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 698b11b1cadb..88cd58eb3b9f 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -104,7 +104,8 @@ static int ray_dev_init(struct net_device *dev); static const struct ethtool_ops netdev_ethtool_ops; static int ray_open(struct net_device *dev); -static int ray_dev_start_xmit(struct sk_buff *skb, struct net_device *dev); +static netdev_tx_t ray_dev_start_xmit(struct sk_buff *skb, + struct net_device *dev); static void set_multicast_list(struct net_device *dev); static void ray_update_multi_list(struct net_device *dev, int all); static int translate_frame(ray_dev_t *local, struct tx_msg __iomem *ptx, @@ -915,16 +916,19 @@ static int ray_dev_config(struct net_device *dev, struct ifmap *map) } /*===========================================================================*/ -static int ray_dev_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t ray_dev_start_xmit(struct sk_buff *skb, + struct net_device *dev) { ray_dev_t *local = netdev_priv(dev); struct pcmcia_device *link = local->finder; short length = skb->len; - if (!(pcmcia_dev_present(link))) { + if (!pcmcia_dev_present(link)) { DEBUG(2, "ray_dev_start_xmit - device not present\n"); - return NETDEV_TX_LOCKED; + dev_kfree_skb(skb); + return NETDEV_TX_OK; } + DEBUG(3, "ray_dev_start_xmit(skb=%p, dev=%p)\n", skb, dev); if (local->authentication_state == NEED_TO_AUTH) { DEBUG(0, "ray_cs Sending authentication request.\n"); @@ -937,7 +941,7 @@ static int ray_dev_start_xmit(struct sk_buff *skb, struct net_device *dev) if (length < ETH_ZLEN) { if (skb_padto(skb, ETH_ZLEN)) - return 0; + return NETDEV_TX_OK; length = ETH_ZLEN; } switch (ray_hw_xmit(skb->data, length, dev, DATA_TYPE)) { @@ -951,9 +955,9 @@ static int ray_dev_start_xmit(struct sk_buff *skb, struct net_device *dev) default: dev->trans_start = jiffies; dev_kfree_skb(skb); - return 0; } - return 0; + + return NETDEV_TX_OK; } /* ray_dev_start_xmit */ /*===========================================================================*/ @@ -1511,9 +1515,6 @@ static iw_stats *ray_get_wireless_stats(struct net_device *dev) struct pcmcia_device *link = local->finder; struct status __iomem *p = local->sram + STATUS_BASE; - if (local == (ray_dev_t *) NULL) - return (iw_stats *) NULL; - local->wstats.status = local->card_status; #ifdef WIRELESS_SPY if ((local->spy_data.spy_number > 0) diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 3bec3dbd3450..402d36757765 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -100,7 +100,6 @@ MODULE_PARM_DESC(workaround_interval, #define OID_GEN_RCV_ERROR cpu_to_le32(0x00020104) #define OID_GEN_RCV_NO_BUFFER cpu_to_le32(0x00020105) -#define OID_802_3_PERMANENT_ADDRESS cpu_to_le32(0x01010101) #define OID_802_3_CURRENT_ADDRESS cpu_to_le32(0x01010102) #define OID_802_3_MULTICAST_LIST cpu_to_le32(0x01010103) #define OID_802_3_MAXIMUM_LIST_SIZE cpu_to_le32(0x01010104) @@ -139,9 +138,15 @@ MODULE_PARM_DESC(workaround_interval, /* Assume that Broadcom 4320 (only chipset at time of writing known to be * based on wireless rndis) has default txpower of 13dBm. * This value is from Linksys WUSB54GSC User Guide, Appendix F: Specifications. - * 13dBm == 19.9mW + * 100% : 20 mW ~ 13dBm + * 75% : 15 mW ~ 12dBm + * 50% : 10 mW ~ 10dBm + * 25% : 5 mW ~ 7dBm */ -#define BCM4320_DEFAULT_TXPOWER 20 +#define BCM4320_DEFAULT_TXPOWER_DBM_100 13 +#define BCM4320_DEFAULT_TXPOWER_DBM_75 12 +#define BCM4320_DEFAULT_TXPOWER_DBM_50 10 +#define BCM4320_DEFAULT_TXPOWER_DBM_25 7 /* codes for "status" field of completion messages */ @@ -196,6 +201,24 @@ enum ndis_80211_priv_filter { NDIS_80211_PRIV_8021X_WEP }; +enum ndis_80211_status_type { + NDIS_80211_STATUSTYPE_AUTHENTICATION, + NDIS_80211_STATUSTYPE_MEDIASTREAMMODE, + NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST, + NDIS_80211_STATUSTYPE_RADIOSTATE, +}; + +enum ndis_80211_media_stream_mode { + NDIS_80211_MEDIA_STREAM_OFF, + NDIS_80211_MEDIA_STREAM_ON +}; + +enum ndis_80211_radio_status { + NDIS_80211_RADIO_STATUS_ON, + NDIS_80211_RADIO_STATUS_HARDWARE_OFF, + NDIS_80211_RADIO_STATUS_SOFTWARE_OFF, +}; + enum ndis_80211_addkey_bits { NDIS_80211_ADDKEY_8021X_AUTH = cpu_to_le32(1 << 28), NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ = cpu_to_le32(1 << 29), @@ -208,6 +231,35 @@ enum ndis_80211_addwep_bits { NDIS_80211_ADDWEP_TRANSMIT_KEY = cpu_to_le32(1 << 31) }; +struct ndis_80211_auth_request { + __le32 length; + u8 bssid[6]; + u8 padding[2]; + __le32 flags; +} __attribute__((packed)); + +struct ndis_80211_pmkid_candidate { + u8 bssid[6]; + u8 padding[2]; + __le32 flags; +} __attribute__((packed)); + +struct ndis_80211_pmkid_cand_list { + __le32 version; + __le32 num_candidates; + struct ndis_80211_pmkid_candidate candidate_list[0]; +} __attribute__((packed)); + +struct ndis_80211_status_indication { + __le32 status_type; + union { + enum ndis_80211_media_stream_mode media_stream_mode; + enum ndis_80211_radio_status radio_status; + struct ndis_80211_auth_request auth_request[0]; + struct ndis_80211_pmkid_cand_list cand_list; + } u; +} __attribute__((packed)); + struct ndis_80211_ssid { __le32 length; u8 essid[NDIS_802_11_LENGTH_SSID]; @@ -275,6 +327,7 @@ struct ndis_80211_remove_key { __le32 size; __le32 index; u8 bssid[6]; + u8 padding[2]; } __attribute__((packed)); struct ndis_config_param { @@ -305,13 +358,6 @@ struct ndis_80211_assoc_info { __le32 offset_resp_ies; } __attribute__((packed)); -/* these have to match what is in wpa_supplicant */ -enum wpa_alg { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP }; -enum wpa_cipher { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, - CIPHER_WEP104 }; -enum wpa_key_mgmt { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, - KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE }; - /* * private data */ @@ -326,6 +372,15 @@ enum wpa_key_mgmt { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, #define WORK_LINK_DOWN (1<<1) #define WORK_SET_MULTICAST_LIST (1<<2) +#define RNDIS_WLAN_ALG_NONE 0 +#define RNDIS_WLAN_ALG_WEP (1<<0) +#define RNDIS_WLAN_ALG_TKIP (1<<1) +#define RNDIS_WLAN_ALG_CCMP (1<<2) + +#define RNDIS_WLAN_KEY_MGMT_NONE 0 +#define RNDIS_WLAN_KEY_MGMT_802_1X (1<<0) +#define RNDIS_WLAN_KEY_MGMT_PSK (1<<1) + #define COMMAND_BUFFER_SIZE (CONTROL_BUFFER_SIZE + sizeof(struct rndis_set)) static const struct ieee80211_channel rndis_channels[] = { @@ -360,6 +415,22 @@ static const struct ieee80211_rate rndis_rates[] = { { .bitrate = 540 } }; +static const u32 rndis_cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, +}; + +struct rndis_wlan_encr_key { + int len; + u32 cipher; + u8 material[32]; + u8 bssid[ETH_ALEN]; + bool pairwise; + bool tx_key; +}; + /* RNDIS device private data */ struct rndis_wlan_private { struct usbnet *usbdev; @@ -369,19 +440,17 @@ struct rndis_wlan_private { struct cfg80211_scan_request *scan_request; struct workqueue_struct *workqueue; - struct delayed_work stats_work; + struct delayed_work dev_poller_work; struct delayed_work scan_work; struct work_struct work; struct mutex command_lock; - spinlock_t stats_lock; unsigned long work_pending; + int last_qual; struct ieee80211_supported_band band; struct ieee80211_channel channels[ARRAY_SIZE(rndis_channels)]; struct ieee80211_rate rates[ARRAY_SIZE(rndis_rates)]; - - struct iw_statistics iwstats; - struct iw_statistics privstats; + u32 cipher_suites[ARRAY_SIZE(rndis_cipher_suites)]; int caps; int multicast_size; @@ -399,16 +468,17 @@ struct rndis_wlan_private { /* hardware state */ int radio_on; int infra_mode; + bool connected; + u8 bssid[ETH_ALEN]; struct ndis_80211_ssid essid; + __le32 current_command_oid; /* encryption stuff */ int encr_tx_key_index; - char encr_keys[4][32]; - int encr_key_len[4]; - char encr_key_wpa[4]; + struct rndis_wlan_encr_key encr_keys[4]; + enum nl80211_auth_type wpa_auth_type; int wpa_version; int wpa_keymgmt; - int wpa_authalg; int wpa_ie_len; u8 *wpa_ie; int wpa_cipher_pair; @@ -420,26 +490,70 @@ struct rndis_wlan_private { /* * cfg80211 ops */ -static int rndis_change_virtual_intf(struct wiphy *wiphy, int ifindex, +static int rndis_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params); static int rndis_scan(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_scan_request *request); +static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed); + +static int rndis_set_tx_power(struct wiphy *wiphy, enum tx_power_setting type, + int dbm); +static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm); + +static int rndis_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme); + +static int rndis_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code); + +static int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params); + +static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev); + +static int rndis_set_channel(struct wiphy *wiphy, + struct ieee80211_channel *chan, enum nl80211_channel_type channel_type); + +static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, const u8 *mac_addr, + struct key_params *params); + +static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, const u8 *mac_addr); + +static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index); + +static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_info *sinfo); + +static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *mac, struct station_info *sinfo); + static struct cfg80211_ops rndis_config_ops = { .change_virtual_intf = rndis_change_virtual_intf, .scan = rndis_scan, + .set_wiphy_params = rndis_set_wiphy_params, + .set_tx_power = rndis_set_tx_power, + .get_tx_power = rndis_get_tx_power, + .connect = rndis_connect, + .disconnect = rndis_disconnect, + .join_ibss = rndis_join_ibss, + .leave_ibss = rndis_leave_ibss, + .set_channel = rndis_set_channel, + .add_key = rndis_add_key, + .del_key = rndis_del_key, + .set_default_key = rndis_set_default_key, + .get_station = rndis_get_station, + .dump_station = rndis_dump_station, }; static void *rndis_wiphy_privid = &rndis_wiphy_privid; -static const int bcm4320_power_output[4] = { 25, 50, 75, 100 }; - -static const unsigned char zero_bssid[ETH_ALEN] = {0,}; -static const unsigned char ffff_bssid[ETH_ALEN] = { 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff }; - static struct rndis_wlan_private *get_rndis_wlan_priv(struct usbnet *dev) { @@ -447,13 +561,121 @@ static struct rndis_wlan_private *get_rndis_wlan_priv(struct usbnet *dev) } -static u32 get_bcm4320_power(struct rndis_wlan_private *priv) +static u32 get_bcm4320_power_dbm(struct rndis_wlan_private *priv) +{ + switch (priv->param_power_output) { + default: + case 3: + return BCM4320_DEFAULT_TXPOWER_DBM_100; + case 2: + return BCM4320_DEFAULT_TXPOWER_DBM_75; + case 1: + return BCM4320_DEFAULT_TXPOWER_DBM_50; + case 0: + return BCM4320_DEFAULT_TXPOWER_DBM_25; + } +} + + +static bool is_wpa_key(struct rndis_wlan_private *priv, int idx) { - return BCM4320_DEFAULT_TXPOWER * - bcm4320_power_output[priv->param_power_output] / 100; + int cipher = priv->encr_keys[idx].cipher; + + return (cipher == WLAN_CIPHER_SUITE_CCMP || + cipher == WLAN_CIPHER_SUITE_TKIP); } +static int rndis_cipher_to_alg(u32 cipher) +{ + switch (cipher) { + default: + return RNDIS_WLAN_ALG_NONE; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + return RNDIS_WLAN_ALG_WEP; + case WLAN_CIPHER_SUITE_TKIP: + return RNDIS_WLAN_ALG_TKIP; + case WLAN_CIPHER_SUITE_CCMP: + return RNDIS_WLAN_ALG_CCMP; + } +} + +static int rndis_akm_suite_to_key_mgmt(u32 akm_suite) +{ + switch (akm_suite) { + default: + return RNDIS_WLAN_KEY_MGMT_NONE; + case WLAN_AKM_SUITE_8021X: + return RNDIS_WLAN_KEY_MGMT_802_1X; + case WLAN_AKM_SUITE_PSK: + return RNDIS_WLAN_KEY_MGMT_PSK; + } +} + + +#ifdef DEBUG +static const char *oid_to_string(__le32 oid) +{ + switch (oid) { +#define OID_STR(oid) case oid: return(#oid) + /* from rndis_host.h */ + OID_STR(OID_802_3_PERMANENT_ADDRESS); + OID_STR(OID_GEN_MAXIMUM_FRAME_SIZE); + OID_STR(OID_GEN_CURRENT_PACKET_FILTER); + OID_STR(OID_GEN_PHYSICAL_MEDIUM); + + /* from rndis_wlan.c */ + OID_STR(OID_GEN_LINK_SPEED); + OID_STR(OID_GEN_RNDIS_CONFIG_PARAMETER); + + OID_STR(OID_GEN_XMIT_OK); + OID_STR(OID_GEN_RCV_OK); + OID_STR(OID_GEN_XMIT_ERROR); + OID_STR(OID_GEN_RCV_ERROR); + OID_STR(OID_GEN_RCV_NO_BUFFER); + + OID_STR(OID_802_3_CURRENT_ADDRESS); + OID_STR(OID_802_3_MULTICAST_LIST); + OID_STR(OID_802_3_MAXIMUM_LIST_SIZE); + + OID_STR(OID_802_11_BSSID); + OID_STR(OID_802_11_SSID); + OID_STR(OID_802_11_INFRASTRUCTURE_MODE); + OID_STR(OID_802_11_ADD_WEP); + OID_STR(OID_802_11_REMOVE_WEP); + OID_STR(OID_802_11_DISASSOCIATE); + OID_STR(OID_802_11_AUTHENTICATION_MODE); + OID_STR(OID_802_11_PRIVACY_FILTER); + OID_STR(OID_802_11_BSSID_LIST_SCAN); + OID_STR(OID_802_11_ENCRYPTION_STATUS); + OID_STR(OID_802_11_ADD_KEY); + OID_STR(OID_802_11_REMOVE_KEY); + OID_STR(OID_802_11_ASSOCIATION_INFORMATION); + OID_STR(OID_802_11_PMKID); + OID_STR(OID_802_11_NETWORK_TYPES_SUPPORTED); + OID_STR(OID_802_11_NETWORK_TYPE_IN_USE); + OID_STR(OID_802_11_TX_POWER_LEVEL); + OID_STR(OID_802_11_RSSI); + OID_STR(OID_802_11_RSSI_TRIGGER); + OID_STR(OID_802_11_FRAGMENTATION_THRESHOLD); + OID_STR(OID_802_11_RTS_THRESHOLD); + OID_STR(OID_802_11_SUPPORTED_RATES); + OID_STR(OID_802_11_CONFIGURATION); + OID_STR(OID_802_11_BSSID_LIST); +#undef OID_STR + } + + return "?"; +} +#else +static const char *oid_to_string(__le32 oid) +{ + return "?"; +} +#endif + + /* translate error code */ static int rndis_error_status(__le32 rndis_status) { @@ -508,12 +730,25 @@ static int rndis_query_oid(struct usbnet *dev, __le32 oid, void *data, int *len) u.get->msg_len = cpu_to_le32(sizeof *u.get); u.get->oid = oid; + priv->current_command_oid = oid; ret = rndis_command(dev, u.header, buflen); + priv->current_command_oid = 0; + if (ret < 0) + devdbg(dev, "rndis_query_oid(%s): rndis_command() failed, %d " + "(%08x)", oid_to_string(oid), ret, + le32_to_cpu(u.get_c->status)); + if (ret == 0) { ret = le32_to_cpu(u.get_c->len); - *len = (*len > ret) ? ret : *len; + if (ret > *len) + *len = ret; memcpy(data, u.buf + le32_to_cpu(u.get_c->offset) + 8, *len); ret = rndis_error_status(u.get_c->status); + + if (ret < 0) + devdbg(dev, "rndis_query_oid(%s): device returned " + "error, 0x%08x (%d)", oid_to_string(oid), + le32_to_cpu(u.get_c->status), ret); } mutex_unlock(&priv->command_lock); @@ -558,10 +793,23 @@ static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len) u.set->handle = cpu_to_le32(0); memcpy(u.buf + sizeof(*u.set), data, len); + priv->current_command_oid = oid; ret = rndis_command(dev, u.header, buflen); - if (ret == 0) + priv->current_command_oid = 0; + if (ret < 0) + devdbg(dev, "rndis_set_oid(%s): rndis_command() failed, %d " + "(%08x)", oid_to_string(oid), ret, + le32_to_cpu(u.set_c->status)); + + if (ret == 0) { ret = rndis_error_status(u.set_c->status); + if (ret < 0) + devdbg(dev, "rndis_set_oid(%s): device returned error, " + "0x%08x (%d)", oid_to_string(oid), + le32_to_cpu(u.set_c->status), ret); + } + mutex_unlock(&priv->command_lock); if (u.buf != priv->command_buffer) @@ -570,6 +818,29 @@ static int rndis_set_oid(struct usbnet *dev, __le32 oid, void *data, int len) } +static int rndis_reset(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct rndis_reset *reset; + int ret; + + mutex_lock(&priv->command_lock); + + reset = (void *)priv->command_buffer; + memset(reset, 0, sizeof(*reset)); + reset->msg_type = RNDIS_MSG_RESET; + reset->msg_len = cpu_to_le32(sizeof(*reset)); + priv->current_command_oid = 0; + ret = rndis_command(usbdev, (void *)reset, CONTROL_BUFFER_SIZE); + + mutex_unlock(&priv->command_lock); + + if (ret < 0) + return ret; + return 0; +} + + /* * Specs say that we can only set config parameters only soon after device * initialization. @@ -676,74 +947,12 @@ static int level_to_qual(int level) } -static void dsconfig_to_freq(unsigned int dsconfig, struct iw_freq *freq) -{ - freq->e = 0; - freq->i = 0; - freq->flags = 0; - - /* see comment in wireless.h above the "struct iw_freq" - * definition for an explanation of this if - * NOTE: 1000000 is due to the kHz - */ - if (dsconfig > 1000000) { - freq->m = dsconfig / 10; - freq->e = 1; - } else - freq->m = dsconfig; - - /* convert from kHz to Hz */ - freq->e += 3; -} - - -static int freq_to_dsconfig(struct iw_freq *freq, unsigned int *dsconfig) -{ - if (freq->m < 1000 && freq->e == 0) { - if (freq->m >= 1 && freq->m <= 14) - *dsconfig = ieee80211_dsss_chan_to_freq(freq->m) * 1000; - else - return -1; - } else { - int i; - *dsconfig = freq->m; - for (i = freq->e; i > 0; i--) - *dsconfig *= 10; - *dsconfig /= 1000; - } - - return 0; -} - - /* * common functions */ -static int -add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index); - -static int get_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid) -{ - int ret, len; - - len = sizeof(*ssid); - ret = rndis_query_oid(usbdev, OID_802_11_SSID, ssid, &len); - - if (ret != 0) - ssid->length = 0; - -#ifdef DEBUG - { - unsigned char tmp[NDIS_802_11_LENGTH_SSID + 1]; - - memcpy(tmp, ssid->essid, le32_to_cpu(ssid->length)); - tmp[le32_to_cpu(ssid->length)] = 0; - devdbg(usbdev, "get_essid: '%s', ret: %d", tmp, ret); - } -#endif - return ret; -} - +static int set_infra_mode(struct usbnet *usbdev, int mode); +static void restore_keys(struct usbnet *usbdev); +static int rndis_check_bssid_list(struct usbnet *usbdev); static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid) { @@ -751,6 +960,10 @@ static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid) int ret; ret = rndis_set_oid(usbdev, OID_802_11_SSID, ssid, sizeof(*ssid)); + if (ret < 0) { + devwarn(usbdev, "setting SSID failed (%08X)", ret); + return ret; + } if (ret == 0) { memcpy(&priv->essid, ssid, sizeof(priv->essid)); priv->radio_on = 1; @@ -760,6 +973,25 @@ static int set_essid(struct usbnet *usbdev, struct ndis_80211_ssid *ssid) return ret; } +static int set_bssid(struct usbnet *usbdev, u8 bssid[ETH_ALEN]) +{ + int ret; + + ret = rndis_set_oid(usbdev, OID_802_11_BSSID, bssid, ETH_ALEN); + if (ret < 0) { + devwarn(usbdev, "setting BSSID[%pM] failed (%08X)", bssid, ret); + return ret; + } + + return ret; +} + +static int clear_bssid(struct usbnet *usbdev) +{ + u8 broadcast_mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + return set_bssid(usbdev, broadcast_mac); +} static int get_bssid(struct usbnet *usbdev, u8 bssid[ETH_ALEN]) { @@ -781,14 +1013,18 @@ static int get_association_info(struct usbnet *usbdev, info, &len); } -static int is_associated(struct usbnet *usbdev) +static bool is_associated(struct usbnet *usbdev) { + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); u8 bssid[ETH_ALEN]; int ret; + if (!priv->radio_on) + return false; + ret = get_bssid(usbdev, bssid); - return(ret == 0 && memcmp(bssid, zero_bssid, ETH_ALEN) != 0); + return (ret == 0 && !is_zero_ether_addr(bssid)); } @@ -812,6 +1048,11 @@ static int disassociate(struct usbnet *usbdev, int reset_ssid) /* disassociate causes radio to be turned off; if reset_ssid * is given, set random ssid to enable radio */ if (reset_ssid) { + /* Set device to infrastructure mode so we don't get ad-hoc + * 'media connect' indications with the random ssid. + */ + set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); + ssid.length = cpu_to_le32(sizeof(ssid.essid)); get_random_bytes(&ssid.essid[2], sizeof(ssid.essid)-2); ssid.essid[0] = 0x1; @@ -824,34 +1065,34 @@ static int disassociate(struct usbnet *usbdev, int reset_ssid) } -static int set_auth_mode(struct usbnet *usbdev, int wpa_version, int authalg) +static int set_auth_mode(struct usbnet *usbdev, u32 wpa_version, + enum nl80211_auth_type auth_type, int keymgmt) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); __le32 tmp; int auth_mode, ret; devdbg(usbdev, "set_auth_mode: wpa_version=0x%x authalg=0x%x " - "keymgmt=0x%x", wpa_version, authalg, priv->wpa_keymgmt); + "keymgmt=0x%x", wpa_version, auth_type, keymgmt); - if (wpa_version & IW_AUTH_WPA_VERSION_WPA2) { - if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_802_1X) + if (wpa_version & NL80211_WPA_VERSION_2) { + if (keymgmt & RNDIS_WLAN_KEY_MGMT_802_1X) auth_mode = NDIS_80211_AUTH_WPA2; else auth_mode = NDIS_80211_AUTH_WPA2_PSK; - } else if (wpa_version & IW_AUTH_WPA_VERSION_WPA) { - if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_802_1X) + } else if (wpa_version & NL80211_WPA_VERSION_1) { + if (keymgmt & RNDIS_WLAN_KEY_MGMT_802_1X) auth_mode = NDIS_80211_AUTH_WPA; - else if (priv->wpa_keymgmt & IW_AUTH_KEY_MGMT_PSK) + else if (keymgmt & RNDIS_WLAN_KEY_MGMT_PSK) auth_mode = NDIS_80211_AUTH_WPA_PSK; else auth_mode = NDIS_80211_AUTH_WPA_NONE; - } else if (authalg & IW_AUTH_ALG_SHARED_KEY) { - if (authalg & IW_AUTH_ALG_OPEN_SYSTEM) - auth_mode = NDIS_80211_AUTH_AUTO_SWITCH; - else - auth_mode = NDIS_80211_AUTH_SHARED; - } else + } else if (auth_type == NL80211_AUTHTYPE_SHARED_KEY) + auth_mode = NDIS_80211_AUTH_SHARED; + else if (auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) auth_mode = NDIS_80211_AUTH_OPEN; + else + return -ENOTSUPP; tmp = cpu_to_le32(auth_mode); ret = rndis_set_oid(usbdev, OID_802_11_AUTHENTICATION_MODE, &tmp, @@ -862,7 +1103,9 @@ static int set_auth_mode(struct usbnet *usbdev, int wpa_version, int authalg) } priv->wpa_version = wpa_version; - priv->wpa_authalg = authalg; + priv->wpa_auth_type = auth_type; + priv->wpa_keymgmt = keymgmt; + return 0; } @@ -874,8 +1117,8 @@ static int set_priv_filter(struct usbnet *usbdev) devdbg(usbdev, "set_priv_filter: wpa_version=0x%x", priv->wpa_version); - if (priv->wpa_version & IW_AUTH_WPA_VERSION_WPA2 || - priv->wpa_version & IW_AUTH_WPA_VERSION_WPA) + if (priv->wpa_version & NL80211_WPA_VERSION_2 || + priv->wpa_version & NL80211_WPA_VERSION_1) tmp = cpu_to_le32(NDIS_80211_PRIV_8021X_WEP); else tmp = cpu_to_le32(NDIS_80211_PRIV_ACCEPT_ALL); @@ -892,19 +1135,17 @@ static int set_encr_mode(struct usbnet *usbdev, int pairwise, int groupwise) int encr_mode, ret; devdbg(usbdev, "set_encr_mode: cipher_pair=0x%x cipher_group=0x%x", - pairwise, - groupwise); + pairwise, groupwise); - if (pairwise & IW_AUTH_CIPHER_CCMP) + if (pairwise & RNDIS_WLAN_ALG_CCMP) encr_mode = NDIS_80211_ENCR_CCMP_ENABLED; - else if (pairwise & IW_AUTH_CIPHER_TKIP) + else if (pairwise & RNDIS_WLAN_ALG_TKIP) encr_mode = NDIS_80211_ENCR_TKIP_ENABLED; - else if (pairwise & - (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104)) + else if (pairwise & RNDIS_WLAN_ALG_WEP) encr_mode = NDIS_80211_ENCR_WEP_ENABLED; - else if (groupwise & IW_AUTH_CIPHER_CCMP) + else if (groupwise & RNDIS_WLAN_ALG_CCMP) encr_mode = NDIS_80211_ENCR_CCMP_ENABLED; - else if (groupwise & IW_AUTH_CIPHER_TKIP) + else if (groupwise & RNDIS_WLAN_ALG_TKIP) encr_mode = NDIS_80211_ENCR_TKIP_ENABLED; else encr_mode = NDIS_80211_ENCR_DISABLED; @@ -923,23 +1164,11 @@ static int set_encr_mode(struct usbnet *usbdev, int pairwise, int groupwise) } -static int set_assoc_params(struct usbnet *usbdev) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - - set_auth_mode(usbdev, priv->wpa_version, priv->wpa_authalg); - set_priv_filter(usbdev); - set_encr_mode(usbdev, priv->wpa_cipher_pair, priv->wpa_cipher_group); - - return 0; -} - - static int set_infra_mode(struct usbnet *usbdev, int mode) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); __le32 tmp; - int ret, i; + int ret; devdbg(usbdev, "set_infra_mode: infra_mode=0x%x", priv->infra_mode); @@ -954,32 +1183,50 @@ static int set_infra_mode(struct usbnet *usbdev, int mode) /* NDIS drivers clear keys when infrastructure mode is * changed. But Linux tools assume otherwise. So set the * keys */ - if (priv->wpa_keymgmt == 0 || - priv->wpa_keymgmt == IW_AUTH_KEY_MGMT_802_1X) { - for (i = 0; i < 4; i++) { - if (priv->encr_key_len[i] > 0 && !priv->encr_key_wpa[i]) - add_wep_key(usbdev, priv->encr_keys[i], - priv->encr_key_len[i], i); - } - } + restore_keys(usbdev); priv->infra_mode = mode; return 0; } -static void set_default_iw_params(struct usbnet *usbdev) +static int set_rts_threshold(struct usbnet *usbdev, u32 rts_threshold) { - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + __le32 tmp; + + devdbg(usbdev, "set_rts_threshold %i", rts_threshold); + + if (rts_threshold < 0 || rts_threshold > 2347) + rts_threshold = 2347; - priv->wpa_keymgmt = 0; - priv->wpa_version = 0; + tmp = cpu_to_le32(rts_threshold); + return rndis_set_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp, + sizeof(tmp)); +} + +static int set_frag_threshold(struct usbnet *usbdev, u32 frag_threshold) +{ + __le32 tmp; + + devdbg(usbdev, "set_frag_threshold %i", frag_threshold); + + if (frag_threshold < 256 || frag_threshold > 2346) + frag_threshold = 2346; + + tmp = cpu_to_le32(frag_threshold); + return rndis_set_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp, + sizeof(tmp)); +} + + +static void set_default_iw_params(struct usbnet *usbdev) +{ set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); - set_auth_mode(usbdev, IW_AUTH_WPA_VERSION_DISABLED, - IW_AUTH_ALG_OPEN_SYSTEM); + set_auth_mode(usbdev, 0, NL80211_AUTHTYPE_OPEN_SYSTEM, + RNDIS_WLAN_KEY_MGMT_NONE); set_priv_filter(usbdev); - set_encr_mode(usbdev, IW_AUTH_CIPHER_NONE, IW_AUTH_CIPHER_NONE); + set_encr_mode(usbdev, RNDIS_WLAN_ALG_NONE, RNDIS_WLAN_ALG_NONE); } @@ -993,16 +1240,56 @@ static int deauthenticate(struct usbnet *usbdev) } +static int set_channel(struct usbnet *usbdev, int channel) +{ + struct ndis_80211_conf config; + unsigned int dsconfig; + int len, ret; + + devdbg(usbdev, "set_channel(%d)", channel); + + /* this OID is valid only when not associated */ + if (is_associated(usbdev)) + return 0; + + dsconfig = ieee80211_dsss_chan_to_freq(channel) * 1000; + + len = sizeof(config); + ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len); + if (ret < 0) { + devdbg(usbdev, "set_channel: querying configuration failed"); + return ret; + } + + config.ds_config = cpu_to_le32(dsconfig); + ret = rndis_set_oid(usbdev, OID_802_11_CONFIGURATION, &config, + sizeof(config)); + + devdbg(usbdev, "set_channel: %d -> %d", channel, ret); + + return ret; +} + + /* index must be 0 - N, as per NDIS */ -static int add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index) +static int add_wep_key(struct usbnet *usbdev, const u8 *key, int key_len, + int index) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ndis_80211_wep_key ndis_key; + u32 cipher; int ret; - if (key_len <= 0 || key_len > 32 || index < 0 || index >= 4) + devdbg(usbdev, "add_wep_key(idx: %d, len: %d)", index, key_len); + + if ((key_len != 5 && key_len != 13) || index < 0 || index > 3) return -EINVAL; + if (key_len == 5) + cipher = WLAN_CIPHER_SUITE_WEP40; + else + cipher = WLAN_CIPHER_SUITE_WEP104; + memset(&ndis_key, 0, sizeof(ndis_key)); ndis_key.size = cpu_to_le32(sizeof(ndis_key)); @@ -1012,8 +1299,8 @@ static int add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index) if (index == priv->encr_tx_key_index) { ndis_key.index |= NDIS_80211_ADDWEP_TRANSMIT_KEY; - ret = set_encr_mode(usbdev, IW_AUTH_CIPHER_WEP104, - IW_AUTH_CIPHER_NONE); + ret = set_encr_mode(usbdev, RNDIS_WLAN_ALG_WEP, + RNDIS_WLAN_ALG_NONE); if (ret) devwarn(usbdev, "encryption couldn't be enabled (%08X)", ret); @@ -1027,30 +1314,52 @@ static int add_wep_key(struct usbnet *usbdev, char *key, int key_len, int index) return ret; } - priv->encr_key_len[index] = key_len; - priv->encr_key_wpa[index] = 0; - memcpy(&priv->encr_keys[index], key, key_len); + priv->encr_keys[index].len = key_len; + priv->encr_keys[index].cipher = cipher; + memcpy(&priv->encr_keys[index].material, key, key_len); + memset(&priv->encr_keys[index].bssid, 0xff, ETH_ALEN); return 0; } static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len, - int index, const struct sockaddr *addr, - const u8 *rx_seq, int alg, int flags) + int index, const u8 *addr, const u8 *rx_seq, + int seq_len, u32 cipher, int flags) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ndis_80211_key ndis_key; + bool is_addr_ok; int ret; - if (index < 0 || index >= 4) - return -EINVAL; - if (key_len > sizeof(ndis_key.material) || key_len < 0) + if (index < 0 || index >= 4) { + devdbg(usbdev, "add_wpa_key: index out of range (%i)", index); return -EINVAL; - if ((flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) && !rx_seq) + } + if (key_len > sizeof(ndis_key.material) || key_len < 0) { + devdbg(usbdev, "add_wpa_key: key length out of range (%i)", + key_len); return -EINVAL; - if ((flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) && !addr) + } + if (flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) { + if (!rx_seq || seq_len <= 0) { + devdbg(usbdev, "add_wpa_key: recv seq flag without" + "buffer"); + return -EINVAL; + } + if (rx_seq && seq_len > sizeof(ndis_key.rsc)) { + devdbg(usbdev, "add_wpa_key: too big recv seq buffer"); + return -EINVAL; + } + } + + is_addr_ok = addr && !is_zero_ether_addr(addr) && + !is_broadcast_ether_addr(addr); + if ((flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) && !is_addr_ok) { + devdbg(usbdev, "add_wpa_key: pairwise but bssid invalid (%pM)", + addr); return -EINVAL; + } devdbg(usbdev, "add_wpa_key(%i): flags:%i%i%i", index, !!(flags & NDIS_80211_ADDKEY_TRANSMIT_KEY), @@ -1064,7 +1373,7 @@ static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len, ndis_key.length = cpu_to_le32(key_len); ndis_key.index = cpu_to_le32(index) | flags; - if (alg == IW_ENCODE_ALG_TKIP && key_len == 32) { + if (cipher == WLAN_CIPHER_SUITE_TKIP && key_len == 32) { /* wpa_supplicant gives us the Michael MIC RX/TX keys in * different order than NDIS spec, so swap the order here. */ memcpy(ndis_key.material, key, 16); @@ -1074,11 +1383,11 @@ static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len, memcpy(ndis_key.material, key, key_len); if (flags & NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ) - memcpy(ndis_key.rsc, rx_seq, 6); + memcpy(ndis_key.rsc, rx_seq, seq_len); if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) { /* pairwise key */ - memcpy(ndis_key.bssid, addr->sa_data, ETH_ALEN); + memcpy(ndis_key.bssid, addr, ETH_ALEN); } else { /* group key */ if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) @@ -1093,8 +1402,14 @@ static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len, if (ret != 0) return ret; - priv->encr_key_len[index] = key_len; - priv->encr_key_wpa[index] = 1; + memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index])); + priv->encr_keys[index].len = key_len; + priv->encr_keys[index].cipher = cipher; + memcpy(&priv->encr_keys[index].material, key, key_len); + if (flags & NDIS_80211_ADDKEY_PAIRWISE_KEY) + memcpy(&priv->encr_keys[index].bssid, ndis_key.bssid, ETH_ALEN); + else + memset(&priv->encr_keys[index].bssid, 0xff, ETH_ALEN); if (flags & NDIS_80211_ADDKEY_TRANSMIT_KEY) priv->encr_tx_key_index = index; @@ -1103,30 +1418,65 @@ static int add_wpa_key(struct usbnet *usbdev, const u8 *key, int key_len, } +static int restore_key(struct usbnet *usbdev, int key_idx) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct rndis_wlan_encr_key key; + + if (is_wpa_key(priv, key_idx)) + return 0; + + key = priv->encr_keys[key_idx]; + + devdbg(usbdev, "restore_key: %i:%i", key_idx, key.len); + + if (key.len == 0) + return 0; + + return add_wep_key(usbdev, key.material, key.len, key_idx); +} + + +static void restore_keys(struct usbnet *usbdev) +{ + int i; + + for (i = 0; i < 4; i++) + restore_key(usbdev, i); +} + + +static void clear_key(struct rndis_wlan_private *priv, int idx) +{ + memset(&priv->encr_keys[idx], 0, sizeof(priv->encr_keys[idx])); +} + + /* remove_key is for both wep and wpa */ -static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN]) +static int remove_key(struct usbnet *usbdev, int index, const u8 *bssid) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ndis_80211_remove_key remove_key; __le32 keyindex; + bool is_wpa; int ret; - if (priv->encr_key_len[index] == 0) + if (priv->encr_keys[index].len == 0) return 0; - priv->encr_key_len[index] = 0; - priv->encr_key_wpa[index] = 0; - memset(&priv->encr_keys[index], 0, sizeof(priv->encr_keys[index])); + is_wpa = is_wpa_key(priv, index); + + devdbg(usbdev, "remove_key: %i:%s:%i", index, is_wpa ? "wpa" : "wep", + priv->encr_keys[index].len); + + clear_key(priv, index); - if (priv->wpa_cipher_pair == IW_AUTH_CIPHER_TKIP || - priv->wpa_cipher_pair == IW_AUTH_CIPHER_CCMP || - priv->wpa_cipher_group == IW_AUTH_CIPHER_TKIP || - priv->wpa_cipher_group == IW_AUTH_CIPHER_CCMP) { + if (is_wpa) { remove_key.size = cpu_to_le32(sizeof(remove_key)); remove_key.index = cpu_to_le32(index); if (bssid) { /* pairwise key */ - if (memcmp(bssid, ffff_bssid, ETH_ALEN) != 0) + if (!is_broadcast_ether_addr(bssid)) remove_key.index |= NDIS_80211_ADDKEY_PAIRWISE_KEY; memcpy(remove_key.bssid, bssid, @@ -1153,7 +1503,7 @@ static int remove_key(struct usbnet *usbdev, int index, u8 bssid[ETH_ALEN]) /* if it is transmit key, disable encryption */ if (index == priv->encr_tx_key_index) - set_encr_mode(usbdev, IW_AUTH_CIPHER_NONE, IW_AUTH_CIPHER_NONE); + set_encr_mode(usbdev, RNDIS_WLAN_ALG_NONE, RNDIS_WLAN_ALG_NONE); return 0; } @@ -1222,20 +1572,15 @@ static void set_multicast_list(struct usbnet *usbdev) /* * cfg80211 ops */ -static int rndis_change_virtual_intf(struct wiphy *wiphy, int ifindex, +static int rndis_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { - struct net_device *dev; - struct usbnet *usbdev; + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; int mode; - /* we're under RTNL */ - dev = __dev_get_by_index(&init_net, ifindex); - if (!dev) - return -ENODEV; - usbdev = netdev_priv(dev); - switch (type) { case NL80211_IFTYPE_ADHOC: mode = NDIS_80211_INFRA_ADHOC; @@ -1247,11 +1592,71 @@ static int rndis_change_virtual_intf(struct wiphy *wiphy, int ifindex, return -EINVAL; } + priv->wdev.iftype = type; + return set_infra_mode(usbdev, mode); } -#define SCAN_DELAY_JIFFIES (HZ) +static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + int err; + + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + err = set_frag_threshold(usbdev, wiphy->frag_threshold); + if (err < 0) + return err; + } + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + err = set_rts_threshold(usbdev, wiphy->rts_threshold); + if (err < 0) + return err; + } + + return 0; +} + + +static int rndis_set_tx_power(struct wiphy *wiphy, enum tx_power_setting type, + int dbm) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + + devdbg(usbdev, "rndis_set_tx_power type:0x%x dbm:%i", type, dbm); + + /* Device doesn't support changing txpower after initialization, only + * turn off/on radio. Support 'auto' mode and setting same dBm that is + * currently used. + */ + if (type == TX_POWER_AUTOMATIC || dbm == get_bcm4320_power_dbm(priv)) { + if (!priv->radio_on) + disassociate(usbdev, 1); /* turn on radio */ + + return 0; + } + + return -ENOTSUPP; +} + + +static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + + *dbm = get_bcm4320_power_dbm(priv); + + devdbg(usbdev, "rndis_get_tx_power dbm:%i", *dbm); + + return 0; +} + + +#define SCAN_DELAY_JIFFIES (6 * HZ) static int rndis_scan(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_scan_request *request) { @@ -1262,6 +1667,11 @@ static int rndis_scan(struct wiphy *wiphy, struct net_device *dev, devdbg(usbdev, "cfg80211.scan"); + /* Get current bssid list from device before new scan, as new scan + * clears internal bssid list. + */ + rndis_check_bssid_list(usbdev); + if (!request) return -EINVAL; @@ -1296,6 +1706,9 @@ static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev, int ie_len, bssid_len; u8 *ie; + devdbg(usbdev, " found bssid: '%.32s' [%pM]", bssid->ssid.essid, + bssid->mac); + /* parse bssid structure */ bssid_len = le32_to_cpu(bssid->length); @@ -1335,10 +1748,12 @@ static int rndis_check_bssid_list(struct usbnet *usbdev) struct ndis_80211_bssid_list_ex *bssid_list; struct ndis_80211_bssid_ex *bssid; int ret = -EINVAL, len, count, bssid_len; + bool resized = false; devdbg(usbdev, "check_bssid_list"); len = CONTROL_BUFFER_SIZE; +resize_buf: buf = kmalloc(len, GFP_KERNEL); if (!buf) { ret = -ENOMEM; @@ -1349,11 +1764,18 @@ static int rndis_check_bssid_list(struct usbnet *usbdev) if (ret != 0) goto out; + if (!resized && len > CONTROL_BUFFER_SIZE) { + resized = true; + kfree(buf); + goto resize_buf; + } + bssid_list = buf; bssid = bssid_list->bssid; bssid_len = le32_to_cpu(bssid->length); count = le32_to_cpu(bssid_list->num_items); - devdbg(usbdev, "check_bssid_list: %d BSSIDs found", count); + devdbg(usbdev, "check_bssid_list: %d BSSIDs found (buflen: %d)", count, + len); while (count && ((void *)bssid + bssid_len) <= (buf + len)) { rndis_bss_info_update(usbdev, bssid); @@ -1378,6 +1800,9 @@ static void rndis_get_scan_results(struct work_struct *work) devdbg(usbdev, "get_scan_results"); + if (!priv->scan_request) + return; + ret = rndis_check_bssid_list(usbdev); cfg80211_scan_done(priv->scan_request, ret < 0); @@ -1385,759 +1810,717 @@ static void rndis_get_scan_results(struct work_struct *work) priv->scan_request = NULL; } - -/* - * wireless extension handlers - */ - -static int rndis_iw_commit(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - /* dummy op */ - return 0; -} - - -static int rndis_iw_set_essid(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *essid) +static int rndis_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) { + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + struct ieee80211_channel *channel = sme->channel; struct ndis_80211_ssid ssid; - int length = wrqu->essid.length; - struct usbnet *usbdev = netdev_priv(dev); + int pairwise = RNDIS_WLAN_ALG_NONE; + int groupwise = RNDIS_WLAN_ALG_NONE; + int keymgmt = RNDIS_WLAN_KEY_MGMT_NONE; + int length, i, ret, chan = -1; + + if (channel) + chan = ieee80211_frequency_to_channel(channel->center_freq); + + groupwise = rndis_cipher_to_alg(sme->crypto.cipher_group); + for (i = 0; i < sme->crypto.n_ciphers_pairwise; i++) + pairwise |= + rndis_cipher_to_alg(sme->crypto.ciphers_pairwise[i]); + + if (sme->crypto.n_ciphers_pairwise > 0 && + pairwise == RNDIS_WLAN_ALG_NONE) { + deverr(usbdev, "Unsupported pairwise cipher"); + return -ENOTSUPP; + } - devdbg(usbdev, "SIOCSIWESSID: [flags:%d,len:%d] '%.32s'", - wrqu->essid.flags, wrqu->essid.length, essid); + for (i = 0; i < sme->crypto.n_akm_suites; i++) + keymgmt |= + rndis_akm_suite_to_key_mgmt(sme->crypto.akm_suites[i]); - if (length > NDIS_802_11_LENGTH_SSID) - length = NDIS_802_11_LENGTH_SSID; + if (sme->crypto.n_akm_suites > 0 && + keymgmt == RNDIS_WLAN_KEY_MGMT_NONE) { + deverr(usbdev, "Invalid keymgmt"); + return -ENOTSUPP; + } - ssid.length = cpu_to_le32(length); - if (length > 0) - memcpy(ssid.essid, essid, length); - else - memset(ssid.essid, 0, NDIS_802_11_LENGTH_SSID); + devdbg(usbdev, "cfg80211.connect('%.32s':[%pM]:%d:[%d,0x%x:0x%x]:[0x%x:" + "0x%x]:0x%x)", sme->ssid, sme->bssid, chan, + sme->privacy, sme->crypto.wpa_versions, sme->auth_type, + groupwise, pairwise, keymgmt); - set_assoc_params(usbdev); + if (is_associated(usbdev)) + disassociate(usbdev, false); - if (!wrqu->essid.flags || length == 0) - return disassociate(usbdev, 1); - else - return set_essid(usbdev, &ssid); -} + ret = set_infra_mode(usbdev, NDIS_80211_INFRA_INFRA); + if (ret < 0) { + devdbg(usbdev, "connect: set_infra_mode failed, %d", ret); + goto err_turn_radio_on; + } + ret = set_auth_mode(usbdev, sme->crypto.wpa_versions, sme->auth_type, + keymgmt); + if (ret < 0) { + devdbg(usbdev, "connect: set_auth_mode failed, %d", ret); + goto err_turn_radio_on; + } -static int rndis_iw_get_essid(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *essid) -{ - struct ndis_80211_ssid ssid; - struct usbnet *usbdev = netdev_priv(dev); - int ret; + set_priv_filter(usbdev); - ret = get_essid(usbdev, &ssid); + ret = set_encr_mode(usbdev, pairwise, groupwise); + if (ret < 0) { + devdbg(usbdev, "connect: set_encr_mode failed, %d", ret); + goto err_turn_radio_on; + } - if (ret == 0 && le32_to_cpu(ssid.length) > 0) { - wrqu->essid.flags = 1; - wrqu->essid.length = le32_to_cpu(ssid.length); - memcpy(essid, ssid.essid, wrqu->essid.length); - essid[wrqu->essid.length] = 0; - } else { - memset(essid, 0, sizeof(NDIS_802_11_LENGTH_SSID)); - wrqu->essid.flags = 0; - wrqu->essid.length = 0; + if (channel) { + ret = set_channel(usbdev, chan); + if (ret < 0) { + devdbg(usbdev, "connect: set_channel failed, %d", ret); + goto err_turn_radio_on; + } } - devdbg(usbdev, "SIOCGIWESSID: %s", essid); - return ret; -} + if (sme->key && ((groupwise | pairwise) & RNDIS_WLAN_ALG_WEP)) { + priv->encr_tx_key_index = sme->key_idx; + ret = add_wep_key(usbdev, sme->key, sme->key_len, sme->key_idx); + if (ret < 0) { + devdbg(usbdev, "connect: add_wep_key failed, %d " + "(%d, %d)", ret, sme->key_len, sme->key_idx); + goto err_turn_radio_on; + } + } -static int rndis_iw_get_bssid(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - unsigned char bssid[ETH_ALEN]; - int ret; + if (sme->bssid && !is_zero_ether_addr(sme->bssid) && + !is_broadcast_ether_addr(sme->bssid)) { + ret = set_bssid(usbdev, sme->bssid); + if (ret < 0) { + devdbg(usbdev, "connect: set_bssid failed, %d", ret); + goto err_turn_radio_on; + } + } else + clear_bssid(usbdev); - ret = get_bssid(usbdev, bssid); + length = sme->ssid_len; + if (length > NDIS_802_11_LENGTH_SSID) + length = NDIS_802_11_LENGTH_SSID; - if (ret == 0) - devdbg(usbdev, "SIOCGIWAP: %pM", bssid); - else - devdbg(usbdev, "SIOCGIWAP: <not associated>"); + memset(&ssid, 0, sizeof(ssid)); + ssid.length = cpu_to_le32(length); + memcpy(ssid.essid, sme->ssid, length); - wrqu->ap_addr.sa_family = ARPHRD_ETHER; - memcpy(wrqu->ap_addr.sa_data, bssid, ETH_ALEN); + /* Pause and purge rx queue, so we don't pass packets before + * 'media connect'-indication. + */ + usbnet_pause_rx(usbdev); + usbnet_purge_paused_rxq(usbdev); + ret = set_essid(usbdev, &ssid); + if (ret < 0) + devdbg(usbdev, "connect: set_essid failed, %d", ret); return ret; -} +err_turn_radio_on: + disassociate(usbdev, 1); -static int rndis_iw_set_bssid(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - u8 *bssid = (u8 *)wrqu->ap_addr.sa_data; - int ret; + return ret; +} - devdbg(usbdev, "SIOCSIWAP: %pM", bssid); +static int rndis_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; - ret = rndis_set_oid(usbdev, OID_802_11_BSSID, bssid, ETH_ALEN); + devdbg(usbdev, "cfg80211.disconnect(%d)", reason_code); - /* user apps may set ap's mac address, which is not required; - * they may fail to work if this function fails, so return - * success */ - if (ret) - devwarn(usbdev, "setting AP mac address failed (%08X)", ret); + priv->connected = false; + memset(priv->bssid, 0, ETH_ALEN); - return 0; + return deauthenticate(usbdev); } - -static int rndis_iw_set_auth(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +static int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params) { - struct iw_param *p = &wrqu->param; - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - int ret = -ENOTSUPP; + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + struct ieee80211_channel *channel = params->channel; + struct ndis_80211_ssid ssid; + enum nl80211_auth_type auth_type; + int ret, alg, length, chan = -1; - switch (p->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - devdbg(usbdev, "SIOCSIWAUTH: WPA_VERSION, %08x", p->value); - priv->wpa_version = p->value; - ret = 0; - break; + if (channel) + chan = ieee80211_frequency_to_channel(channel->center_freq); - case IW_AUTH_CIPHER_PAIRWISE: - devdbg(usbdev, "SIOCSIWAUTH: CIPHER_PAIRWISE, %08x", p->value); - priv->wpa_cipher_pair = p->value; - ret = 0; - break; + /* TODO: How to handle ad-hoc encryption? + * connect() has *key, join_ibss() doesn't. RNDIS requires key to be + * pre-shared for encryption (open/shared/wpa), is key set before + * join_ibss? Which auth_type to use (not in params)? What about WPA? + */ + if (params->privacy) { + auth_type = NL80211_AUTHTYPE_SHARED_KEY; + alg = RNDIS_WLAN_ALG_WEP; + } else { + auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; + alg = RNDIS_WLAN_ALG_NONE; + } - case IW_AUTH_CIPHER_GROUP: - devdbg(usbdev, "SIOCSIWAUTH: CIPHER_GROUP, %08x", p->value); - priv->wpa_cipher_group = p->value; - ret = 0; - break; + devdbg(usbdev, "cfg80211.join_ibss('%.32s':[%pM]:%d:%d)", params->ssid, + params->bssid, chan, params->privacy); - case IW_AUTH_KEY_MGMT: - devdbg(usbdev, "SIOCSIWAUTH: KEY_MGMT, %08x", p->value); - priv->wpa_keymgmt = p->value; - ret = 0; - break; + if (is_associated(usbdev)) + disassociate(usbdev, false); - case IW_AUTH_TKIP_COUNTERMEASURES: - devdbg(usbdev, "SIOCSIWAUTH: TKIP_COUNTERMEASURES, %08x", - p->value); - ret = 0; - break; + ret = set_infra_mode(usbdev, NDIS_80211_INFRA_ADHOC); + if (ret < 0) { + devdbg(usbdev, "join_ibss: set_infra_mode failed, %d", ret); + goto err_turn_radio_on; + } - case IW_AUTH_DROP_UNENCRYPTED: - devdbg(usbdev, "SIOCSIWAUTH: DROP_UNENCRYPTED, %08x", p->value); - ret = 0; - break; + ret = set_auth_mode(usbdev, 0, auth_type, RNDIS_WLAN_KEY_MGMT_NONE); + if (ret < 0) { + devdbg(usbdev, "join_ibss: set_auth_mode failed, %d", ret); + goto err_turn_radio_on; + } - case IW_AUTH_80211_AUTH_ALG: - devdbg(usbdev, "SIOCSIWAUTH: 80211_AUTH_ALG, %08x", p->value); - priv->wpa_authalg = p->value; - ret = 0; - break; + set_priv_filter(usbdev); - case IW_AUTH_WPA_ENABLED: - devdbg(usbdev, "SIOCSIWAUTH: WPA_ENABLED, %08x", p->value); - if (wrqu->param.value) - deauthenticate(usbdev); - ret = 0; - break; + ret = set_encr_mode(usbdev, alg, RNDIS_WLAN_ALG_NONE); + if (ret < 0) { + devdbg(usbdev, "join_ibss: set_encr_mode failed, %d", ret); + goto err_turn_radio_on; + } - case IW_AUTH_RX_UNENCRYPTED_EAPOL: - devdbg(usbdev, "SIOCSIWAUTH: RX_UNENCRYPTED_EAPOL, %08x", - p->value); - ret = 0; - break; + if (channel) { + ret = set_channel(usbdev, chan); + if (ret < 0) { + devdbg(usbdev, "join_ibss: set_channel failed, %d", + ret); + goto err_turn_radio_on; + } + } - case IW_AUTH_ROAMING_CONTROL: - devdbg(usbdev, "SIOCSIWAUTH: ROAMING_CONTROL, %08x", p->value); - ret = 0; - break; + if (params->bssid && !is_zero_ether_addr(params->bssid) && + !is_broadcast_ether_addr(params->bssid)) { + ret = set_bssid(usbdev, params->bssid); + if (ret < 0) { + devdbg(usbdev, "join_ibss: set_bssid failed, %d", ret); + goto err_turn_radio_on; + } + } else + clear_bssid(usbdev); - case IW_AUTH_PRIVACY_INVOKED: - devdbg(usbdev, "SIOCSIWAUTH: invalid cmd %d", - wrqu->param.flags & IW_AUTH_INDEX); - return -EOPNOTSUPP; + length = params->ssid_len; + if (length > NDIS_802_11_LENGTH_SSID) + length = NDIS_802_11_LENGTH_SSID; - default: - devdbg(usbdev, "SIOCSIWAUTH: UNKNOWN %08x, %08x", - p->flags & IW_AUTH_INDEX, p->value); - } - return ret; -} + memset(&ssid, 0, sizeof(ssid)); + ssid.length = cpu_to_le32(length); + memcpy(ssid.essid, params->ssid, length); + /* Don't need to pause rx queue for ad-hoc. */ + usbnet_purge_paused_rxq(usbdev); + usbnet_resume_rx(usbdev); -static int rndis_iw_get_auth(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct iw_param *p = &wrqu->param; - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + ret = set_essid(usbdev, &ssid); + if (ret < 0) + devdbg(usbdev, "join_ibss: set_essid failed, %d", ret); + return ret; - switch (p->flags & IW_AUTH_INDEX) { - case IW_AUTH_WPA_VERSION: - p->value = priv->wpa_version; - break; - case IW_AUTH_CIPHER_PAIRWISE: - p->value = priv->wpa_cipher_pair; - break; - case IW_AUTH_CIPHER_GROUP: - p->value = priv->wpa_cipher_group; - break; - case IW_AUTH_KEY_MGMT: - p->value = priv->wpa_keymgmt; - break; - case IW_AUTH_80211_AUTH_ALG: - p->value = priv->wpa_authalg; - break; - default: - devdbg(usbdev, "SIOCGIWAUTH: invalid cmd %d", - wrqu->param.flags & IW_AUTH_INDEX); - return -EOPNOTSUPP; - } - return 0; -} +err_turn_radio_on: + disassociate(usbdev, 1); + return ret; +} -static int rndis_iw_set_encode(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +static int rndis_leave_ibss(struct wiphy *wiphy, struct net_device *dev) { - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - int ret, index, key_len; - u8 *key; + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; - index = (wrqu->encoding.flags & IW_ENCODE_INDEX); + devdbg(usbdev, "cfg80211.leave_ibss()"); - /* iwconfig gives index as 1 - N */ - if (index > 0) - index--; - else - index = priv->encr_tx_key_index; + priv->connected = false; + memset(priv->bssid, 0, ETH_ALEN); - if (index < 0 || index >= 4) { - devwarn(usbdev, "encryption index out of range (%u)", index); - return -EINVAL; - } + return deauthenticate(usbdev); +} - /* remove key if disabled */ - if (wrqu->data.flags & IW_ENCODE_DISABLED) { - if (remove_key(usbdev, index, NULL)) - return -EINVAL; - else - return 0; - } +static int rndis_set_channel(struct wiphy *wiphy, + struct ieee80211_channel *chan, enum nl80211_channel_type channel_type) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; - /* global encryption state (for all keys) */ - if (wrqu->data.flags & IW_ENCODE_OPEN) - ret = set_auth_mode(usbdev, IW_AUTH_WPA_VERSION_DISABLED, - IW_AUTH_ALG_OPEN_SYSTEM); - else /*if (wrqu->data.flags & IW_ENCODE_RESTRICTED)*/ - ret = set_auth_mode(usbdev, IW_AUTH_WPA_VERSION_DISABLED, - IW_AUTH_ALG_SHARED_KEY); - if (ret != 0) - return ret; + return set_channel(usbdev, + ieee80211_frequency_to_channel(chan->center_freq)); +} - if (wrqu->data.length > 0) { - key_len = wrqu->data.length; - key = extra; - } else { - /* must be set as tx key */ - if (priv->encr_key_len[index] == 0) - return -EINVAL; - key_len = priv->encr_key_len[index]; - key = priv->encr_keys[index]; - priv->encr_tx_key_index = index; +static int rndis_add_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, const u8 *mac_addr, + struct key_params *params) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + int flags; + + devdbg(usbdev, "rndis_add_key(%i, %pM, %08x)", key_index, mac_addr, + params->cipher); + + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + return add_wep_key(usbdev, params->key, params->key_len, + key_index); + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + flags = 0; + + if (params->seq && params->seq_len > 0) + flags |= NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ; + if (mac_addr) + flags |= NDIS_80211_ADDKEY_PAIRWISE_KEY | + NDIS_80211_ADDKEY_TRANSMIT_KEY; + + return add_wpa_key(usbdev, params->key, params->key_len, + key_index, mac_addr, params->seq, + params->seq_len, params->cipher, flags); + default: + devdbg(usbdev, "rndis_add_key: unsupported cipher %08x", + params->cipher); + return -ENOTSUPP; } - - if (add_wep_key(usbdev, key, key_len, index) != 0) - return -EINVAL; - - if (index == priv->encr_tx_key_index) - /* ndis drivers want essid to be set after setting encr */ - set_essid(usbdev, &priv->essid); - - return 0; } - -static int rndis_iw_set_encode_ext(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +static int rndis_del_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, const u8 *mac_addr) { - struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - int keyidx, flags; + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; - keyidx = wrqu->encoding.flags & IW_ENCODE_INDEX; + devdbg(usbdev, "rndis_del_key(%i, %pM)", key_index, mac_addr); - /* iwconfig gives index as 1 - N */ - if (keyidx) - keyidx--; - else - keyidx = priv->encr_tx_key_index; + return remove_key(usbdev, key_index, mac_addr); +} - if (keyidx < 0 || keyidx >= 4) - return -EINVAL; +static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index) +{ + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; + struct rndis_wlan_encr_key key; - if (ext->alg == WPA_ALG_WEP) { - if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) - priv->encr_tx_key_index = keyidx; - return add_wep_key(usbdev, ext->key, ext->key_len, keyidx); - } + devdbg(usbdev, "rndis_set_default_key(%i)", key_index); - if ((wrqu->encoding.flags & IW_ENCODE_DISABLED) || - ext->alg == IW_ENCODE_ALG_NONE || ext->key_len == 0) - return remove_key(usbdev, keyidx, NULL); + priv->encr_tx_key_index = key_index; - flags = 0; - if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) - flags |= NDIS_80211_ADDKEY_SET_INIT_RECV_SEQ; - if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)) - flags |= NDIS_80211_ADDKEY_PAIRWISE_KEY; - if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) - flags |= NDIS_80211_ADDKEY_TRANSMIT_KEY; + key = priv->encr_keys[key_index]; - return add_wpa_key(usbdev, ext->key, ext->key_len, keyidx, &ext->addr, - ext->rx_seq, ext->alg, flags); + return add_wep_key(usbdev, key.material, key.len, key_index); } - -static int rndis_iw_set_genie(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +static void rndis_fill_station_info(struct usbnet *usbdev, + struct station_info *sinfo) { - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - int ret = 0; + __le32 linkspeed, rssi; + int ret, len; -#ifdef DEBUG - int j; - u8 *gie = extra; - for (j = 0; j < wrqu->data.length; j += 8) - devdbg(usbdev, - "SIOCSIWGENIE %04x - " - "%02x %02x %02x %02x %02x %02x %02x %02x", j, - gie[j + 0], gie[j + 1], gie[j + 2], gie[j + 3], - gie[j + 4], gie[j + 5], gie[j + 6], gie[j + 7]); -#endif - /* clear existing IEs */ - if (priv->wpa_ie_len) { - kfree(priv->wpa_ie); - priv->wpa_ie_len = 0; + memset(sinfo, 0, sizeof(*sinfo)); + + len = sizeof(linkspeed); + ret = rndis_query_oid(usbdev, OID_GEN_LINK_SPEED, &linkspeed, &len); + if (ret == 0) { + sinfo->txrate.legacy = le32_to_cpu(linkspeed) / 1000; + sinfo->filled |= STATION_INFO_TX_BITRATE; } - /* set new IEs */ - priv->wpa_ie = kmalloc(wrqu->data.length, GFP_KERNEL); - if (priv->wpa_ie) { - priv->wpa_ie_len = wrqu->data.length; - memcpy(priv->wpa_ie, extra, priv->wpa_ie_len); - } else - ret = -ENOMEM; - return ret; + len = sizeof(rssi); + ret = rndis_query_oid(usbdev, OID_802_11_RSSI, &rssi, &len); + if (ret == 0) { + sinfo->signal = level_to_qual(le32_to_cpu(rssi)); + sinfo->filled |= STATION_INFO_SIGNAL; + } } - -static int rndis_iw_get_genie(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_info *sinfo) { - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - - devdbg(usbdev, "SIOCGIWGENIE"); - - if (priv->wpa_ie_len == 0 || priv->wpa_ie == NULL) { - wrqu->data.length = 0; - return 0; - } + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; - if (wrqu->data.length < priv->wpa_ie_len) - return -E2BIG; + if (compare_ether_addr(priv->bssid, mac)) + return -ENOENT; - wrqu->data.length = priv->wpa_ie_len; - memcpy(extra, priv->wpa_ie, priv->wpa_ie_len); + rndis_fill_station_info(usbdev, sinfo); return 0; } - -static int rndis_iw_set_rts(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *mac, struct station_info *sinfo) { - struct usbnet *usbdev = netdev_priv(dev); - __le32 tmp; - devdbg(usbdev, "SIOCSIWRTS"); + struct rndis_wlan_private *priv = wiphy_priv(wiphy); + struct usbnet *usbdev = priv->usbdev; - tmp = cpu_to_le32(wrqu->rts.value); - return rndis_set_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp, - sizeof(tmp)); + if (idx != 0) + return -ENOENT; + + memcpy(mac, priv->bssid, ETH_ALEN); + + rndis_fill_station_info(usbdev, sinfo); + + return 0; } -static int rndis_iw_get_rts(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +static void rndis_wlan_do_link_up_work(struct usbnet *usbdev) { - struct usbnet *usbdev = netdev_priv(dev); - __le32 tmp; - int len, ret; + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct ndis_80211_assoc_info *info; + u8 assoc_buf[sizeof(*info) + IW_CUSTOM_MAX + 32]; + u8 bssid[ETH_ALEN]; + int resp_ie_len, req_ie_len; + u8 *req_ie, *resp_ie; + int ret, offset; + bool roamed = false; - len = sizeof(tmp); - ret = rndis_query_oid(usbdev, OID_802_11_RTS_THRESHOLD, &tmp, &len); - if (ret == 0) { - wrqu->rts.value = le32_to_cpu(tmp); - wrqu->rts.flags = 1; - wrqu->rts.disabled = 0; + if (priv->infra_mode == NDIS_80211_INFRA_INFRA && priv->connected) { + /* received media connect indication while connected, either + * device reassociated with same AP or roamed to new. */ + roamed = true; } - devdbg(usbdev, "SIOCGIWRTS: %d", wrqu->rts.value); + req_ie_len = 0; + resp_ie_len = 0; + req_ie = NULL; + resp_ie = NULL; - return ret; -} + if (priv->infra_mode == NDIS_80211_INFRA_INFRA) { + memset(assoc_buf, 0, sizeof(assoc_buf)); + info = (void *)assoc_buf; + /* Get association info IEs from device and send them back to + * userspace. */ + ret = get_association_info(usbdev, info, sizeof(assoc_buf)); + if (!ret) { + req_ie_len = le32_to_cpu(info->req_ie_length); + if (req_ie_len > 0) { + offset = le32_to_cpu(info->offset_req_ies); + req_ie = (u8 *)info + offset; + } -static int rndis_iw_set_frag(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - __le32 tmp; + resp_ie_len = le32_to_cpu(info->resp_ie_length); + if (resp_ie_len > 0) { + offset = le32_to_cpu(info->offset_resp_ies); + resp_ie = (u8 *)info + offset; + } + } + } else if (WARN_ON(priv->infra_mode != NDIS_80211_INFRA_ADHOC)) + return; - devdbg(usbdev, "SIOCSIWFRAG"); + ret = get_bssid(usbdev, bssid); + if (ret < 0) + memset(bssid, 0, sizeof(bssid)); - tmp = cpu_to_le32(wrqu->frag.value); - return rndis_set_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp, - sizeof(tmp)); -} + devdbg(usbdev, "link up work: [%pM] %s", bssid, roamed ? "roamed" : ""); + /* Internal bss list in device always contains at least the currently + * connected bss and we can get it to cfg80211 with + * rndis_check_bssid_list(). + * NOTE: This is true for Broadcom chip, but not mentioned in RNDIS + * spec. + */ + rndis_check_bssid_list(usbdev); -static int rndis_iw_get_frag(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - __le32 tmp; - int len, ret; + if (priv->infra_mode == NDIS_80211_INFRA_INFRA) { + if (!roamed) + cfg80211_connect_result(usbdev->net, bssid, req_ie, + req_ie_len, resp_ie, + resp_ie_len, 0, GFP_KERNEL); + else + cfg80211_roamed(usbdev->net, bssid, req_ie, req_ie_len, + resp_ie, resp_ie_len, GFP_KERNEL); + } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) + cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL); - len = sizeof(tmp); - ret = rndis_query_oid(usbdev, OID_802_11_FRAGMENTATION_THRESHOLD, &tmp, - &len); - if (ret == 0) { - wrqu->frag.value = le32_to_cpu(tmp); - wrqu->frag.flags = 1; - wrqu->frag.disabled = 0; - } - devdbg(usbdev, "SIOCGIWFRAG: %d", wrqu->frag.value); - return ret; -} + priv->connected = true; + memcpy(priv->bssid, bssid, ETH_ALEN); + usbnet_resume_rx(usbdev); + netif_carrier_on(usbdev->net); +} -static int rndis_iw_set_freq(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +static void rndis_wlan_do_link_down_work(struct usbnet *usbdev) { - struct usbnet *usbdev = netdev_priv(dev); - struct ndis_80211_conf config; - unsigned int dsconfig; - int len, ret; - - /* this OID is valid only when not associated */ - if (is_associated(usbdev)) - return 0; - - dsconfig = 0; - if (freq_to_dsconfig(&wrqu->freq, &dsconfig)) - return -EINVAL; - - len = sizeof(config); - ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len); - if (ret != 0) { - devdbg(usbdev, "SIOCSIWFREQ: querying configuration failed"); - return 0; - } + union iwreq_data evt; - config.ds_config = cpu_to_le32(dsconfig); + netif_carrier_off(usbdev->net); - devdbg(usbdev, "SIOCSIWFREQ: %d * 10^%d", wrqu->freq.m, wrqu->freq.e); - return rndis_set_oid(usbdev, OID_802_11_CONFIGURATION, &config, - sizeof(config)); + evt.data.flags = 0; + evt.data.length = 0; + memset(evt.ap_addr.sa_data, 0, ETH_ALEN); + wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL); } - -static int rndis_iw_get_freq(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +static void rndis_wlan_worker(struct work_struct *work) { - struct usbnet *usbdev = netdev_priv(dev); - struct ndis_80211_conf config; - int len, ret; + struct rndis_wlan_private *priv = + container_of(work, struct rndis_wlan_private, work); + struct usbnet *usbdev = priv->usbdev; - len = sizeof(config); - ret = rndis_query_oid(usbdev, OID_802_11_CONFIGURATION, &config, &len); - if (ret == 0) - dsconfig_to_freq(le32_to_cpu(config.ds_config), &wrqu->freq); + if (test_and_clear_bit(WORK_LINK_UP, &priv->work_pending)) + rndis_wlan_do_link_up_work(usbdev); - devdbg(usbdev, "SIOCGIWFREQ: %d", wrqu->freq.m); - return ret; -} + if (test_and_clear_bit(WORK_LINK_DOWN, &priv->work_pending)) + rndis_wlan_do_link_down_work(usbdev); + if (test_and_clear_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending)) + set_multicast_list(usbdev); +} -static int rndis_iw_get_txpower(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +static void rndis_wlan_set_multicast_list(struct net_device *dev) { struct usbnet *usbdev = netdev_priv(dev); struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - __le32 tx_power; - - if (priv->radio_on) { - /* fake since changing tx_power (by userlevel) not supported */ - tx_power = cpu_to_le32(get_bcm4320_power(priv)); - - wrqu->txpower.flags = IW_TXPOW_MWATT; - wrqu->txpower.value = le32_to_cpu(tx_power); - wrqu->txpower.disabled = 0; - } else { - wrqu->txpower.flags = IW_TXPOW_MWATT; - wrqu->txpower.value = 0; - wrqu->txpower.disabled = 1; - } - devdbg(usbdev, "SIOCGIWTXPOW: %d", wrqu->txpower.value); + if (test_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending)) + return; - return 0; + set_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending); + queue_work(priv->workqueue, &priv->work); } -static int rndis_iw_set_txpower(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +static void rndis_wlan_auth_indication(struct usbnet *usbdev, + struct ndis_80211_status_indication *indication, + int len) { - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - __le32 tx_power = 0; - - if (!wrqu->txpower.disabled) { - if (wrqu->txpower.flags == IW_TXPOW_MWATT) - tx_power = cpu_to_le32(wrqu->txpower.value); - else { /* wrqu->txpower.flags == IW_TXPOW_DBM */ - if (wrqu->txpower.value > 20) - tx_power = cpu_to_le32(128); - else if (wrqu->txpower.value < -43) - tx_power = cpu_to_le32(127); - else { - signed char tmp; - tmp = wrqu->txpower.value; - tmp = -12 - tmp; - tmp <<= 2; - tx_power = cpu_to_le32((unsigned char)tmp); - } - } + u8 *buf; + const char *type; + int flags, buflen, key_id; + bool pairwise_error, group_error; + struct ndis_80211_auth_request *auth_req; + enum nl80211_key_type key_type; + + /* must have at least one array entry */ + if (len < offsetof(struct ndis_80211_status_indication, u) + + sizeof(struct ndis_80211_auth_request)) { + devinfo(usbdev, "authentication indication: " + "too short message (%i)", len); + return; } - devdbg(usbdev, "SIOCSIWTXPOW: %d", le32_to_cpu(tx_power)); + buf = (void *)&indication->u.auth_request[0]; + buflen = len - offsetof(struct ndis_80211_status_indication, u); + + while (buflen >= sizeof(*auth_req)) { + auth_req = (void *)buf; + type = "unknown"; + flags = le32_to_cpu(auth_req->flags); + pairwise_error = false; + group_error = false; + + if (flags & 0x1) + type = "reauth request"; + if (flags & 0x2) + type = "key update request"; + if (flags & 0x6) { + pairwise_error = true; + type = "pairwise_error"; + } + if (flags & 0xe) { + group_error = true; + type = "group_error"; + } - if (le32_to_cpu(tx_power) != 0) { - /* txpower unsupported, just turn radio on */ - if (!priv->radio_on) - return disassociate(usbdev, 1); - return 0; /* all ready on */ - } + devinfo(usbdev, "authentication indication: %s (0x%08x)", type, + le32_to_cpu(auth_req->flags)); - /* tx_power == 0, turn off radio */ - return disassociate(usbdev, 0); -} + if (pairwise_error) { + key_type = NL80211_KEYTYPE_PAIRWISE; + key_id = -1; + cfg80211_michael_mic_failure(usbdev->net, + auth_req->bssid, + key_type, key_id, NULL, + GFP_KERNEL); + } -static int rndis_iw_get_rate(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) -{ - struct usbnet *usbdev = netdev_priv(dev); - __le32 tmp; - int ret, len; + if (group_error) { + key_type = NL80211_KEYTYPE_GROUP; + key_id = -1; - len = sizeof(tmp); - ret = rndis_query_oid(usbdev, OID_GEN_LINK_SPEED, &tmp, &len); - if (ret == 0) { - wrqu->bitrate.value = le32_to_cpu(tmp) * 100; - wrqu->bitrate.disabled = 0; - wrqu->bitrate.flags = 1; + cfg80211_michael_mic_failure(usbdev->net, + auth_req->bssid, + key_type, key_id, NULL, + GFP_KERNEL); + } + + buflen -= le32_to_cpu(auth_req->length); + buf += le32_to_cpu(auth_req->length); } - return ret; } - -static int rndis_iw_set_mlme(struct net_device *dev, - struct iw_request_info *info, union iwreq_data *wrqu, char *extra) +static void rndis_wlan_pmkid_cand_list_indication(struct usbnet *usbdev, + struct ndis_80211_status_indication *indication, + int len) { - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - struct iw_mlme *mlme = (struct iw_mlme *)extra; - unsigned char bssid[ETH_ALEN]; + struct ndis_80211_pmkid_cand_list *cand_list; + int list_len, expected_len, i; - get_bssid(usbdev, bssid); + if (len < offsetof(struct ndis_80211_status_indication, u) + + sizeof(struct ndis_80211_pmkid_cand_list)) { + devinfo(usbdev, "pmkid candidate list indication: " + "too short message (%i)", len); + return; + } - if (memcmp(bssid, mlme->addr.sa_data, ETH_ALEN)) - return -EINVAL; + list_len = le32_to_cpu(indication->u.cand_list.num_candidates) * + sizeof(struct ndis_80211_pmkid_candidate); + expected_len = sizeof(struct ndis_80211_pmkid_cand_list) + list_len + + offsetof(struct ndis_80211_status_indication, u); - switch (mlme->cmd) { - case IW_MLME_DEAUTH: - return deauthenticate(usbdev); - case IW_MLME_DISASSOC: - return disassociate(usbdev, priv->radio_on); - default: - return -EOPNOTSUPP; + if (len < expected_len) { + devinfo(usbdev, "pmkid candidate list indication: " + "list larger than buffer (%i < %i)", + len, expected_len); + return; } - return 0; -} + cand_list = &indication->u.cand_list; + devinfo(usbdev, "pmkid candidate list indication: " + "version %i, candidates %i", + le32_to_cpu(cand_list->version), + le32_to_cpu(cand_list->num_candidates)); -static struct iw_statistics *rndis_get_wireless_stats(struct net_device *dev) -{ - struct usbnet *usbdev = netdev_priv(dev); - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); - unsigned long flags; - - spin_lock_irqsave(&priv->stats_lock, flags); - memcpy(&priv->iwstats, &priv->privstats, sizeof(priv->iwstats)); - spin_unlock_irqrestore(&priv->stats_lock, flags); - - return &priv->iwstats; -} - - -#define IW_IOCTL(x) [(x) - SIOCSIWCOMMIT] -static const iw_handler rndis_iw_handler[] = -{ - IW_IOCTL(SIOCSIWCOMMIT) = rndis_iw_commit, - IW_IOCTL(SIOCGIWNAME) = (iw_handler) cfg80211_wext_giwname, - IW_IOCTL(SIOCSIWFREQ) = rndis_iw_set_freq, - IW_IOCTL(SIOCGIWFREQ) = rndis_iw_get_freq, - IW_IOCTL(SIOCSIWMODE) = (iw_handler) cfg80211_wext_siwmode, - IW_IOCTL(SIOCGIWMODE) = (iw_handler) cfg80211_wext_giwmode, - IW_IOCTL(SIOCGIWRANGE) = (iw_handler) cfg80211_wext_giwrange, - IW_IOCTL(SIOCSIWAP) = rndis_iw_set_bssid, - IW_IOCTL(SIOCGIWAP) = rndis_iw_get_bssid, - IW_IOCTL(SIOCSIWSCAN) = (iw_handler) cfg80211_wext_siwscan, - IW_IOCTL(SIOCGIWSCAN) = (iw_handler) cfg80211_wext_giwscan, - IW_IOCTL(SIOCSIWESSID) = rndis_iw_set_essid, - IW_IOCTL(SIOCGIWESSID) = rndis_iw_get_essid, - IW_IOCTL(SIOCGIWRATE) = rndis_iw_get_rate, - IW_IOCTL(SIOCSIWRTS) = rndis_iw_set_rts, - IW_IOCTL(SIOCGIWRTS) = rndis_iw_get_rts, - IW_IOCTL(SIOCSIWFRAG) = rndis_iw_set_frag, - IW_IOCTL(SIOCGIWFRAG) = rndis_iw_get_frag, - IW_IOCTL(SIOCSIWTXPOW) = rndis_iw_set_txpower, - IW_IOCTL(SIOCGIWTXPOW) = rndis_iw_get_txpower, - IW_IOCTL(SIOCSIWENCODE) = rndis_iw_set_encode, - IW_IOCTL(SIOCSIWENCODEEXT) = rndis_iw_set_encode_ext, - IW_IOCTL(SIOCSIWAUTH) = rndis_iw_set_auth, - IW_IOCTL(SIOCGIWAUTH) = rndis_iw_get_auth, - IW_IOCTL(SIOCSIWGENIE) = rndis_iw_set_genie, - IW_IOCTL(SIOCGIWGENIE) = rndis_iw_get_genie, - IW_IOCTL(SIOCSIWMLME) = rndis_iw_set_mlme, -}; + if (le32_to_cpu(cand_list->version) != 1) + return; -static const iw_handler rndis_wlan_private_handler[] = { -}; + for (i = 0; i < le32_to_cpu(cand_list->num_candidates); i++) { + struct ndis_80211_pmkid_candidate *cand = + &cand_list->candidate_list[i]; -static const struct iw_priv_args rndis_wlan_private_args[] = { -}; + devdbg(usbdev, "cand[%i]: flags: 0x%08x, bssid: %pM", + i, le32_to_cpu(cand->flags), cand->bssid); +#if 0 + struct iw_pmkid_cand pcand; + union iwreq_data wrqu; -static const struct iw_handler_def rndis_iw_handlers = { - .num_standard = ARRAY_SIZE(rndis_iw_handler), - .num_private = ARRAY_SIZE(rndis_wlan_private_handler), - .num_private_args = ARRAY_SIZE(rndis_wlan_private_args), - .standard = (iw_handler *)rndis_iw_handler, - .private = (iw_handler *)rndis_wlan_private_handler, - .private_args = (struct iw_priv_args *)rndis_wlan_private_args, - .get_wireless_stats = rndis_get_wireless_stats, -}; + memset(&pcand, 0, sizeof(pcand)); + if (le32_to_cpu(cand->flags) & 0x01) + pcand.flags |= IW_PMKID_CAND_PREAUTH; + pcand.index = i; + memcpy(pcand.bssid.sa_data, cand->bssid, ETH_ALEN); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = sizeof(pcand); + wireless_send_event(usbdev->net, IWEVPMKIDCAND, &wrqu, + (u8 *)&pcand); +#endif + } +} -static void rndis_wlan_worker(struct work_struct *work) +static void rndis_wlan_media_specific_indication(struct usbnet *usbdev, + struct rndis_indicate *msg, int buflen) { - struct rndis_wlan_private *priv = - container_of(work, struct rndis_wlan_private, work); - struct usbnet *usbdev = priv->usbdev; - union iwreq_data evt; - unsigned char bssid[ETH_ALEN]; - struct ndis_80211_assoc_info *info; - int assoc_size = sizeof(*info) + IW_CUSTOM_MAX + 32; - int ret, offset; + struct ndis_80211_status_indication *indication; + int len, offset; - if (test_and_clear_bit(WORK_LINK_UP, &priv->work_pending)) { - netif_carrier_on(usbdev->net); + offset = offsetof(struct rndis_indicate, status) + + le32_to_cpu(msg->offset); + len = le32_to_cpu(msg->length); - info = kzalloc(assoc_size, GFP_KERNEL); - if (!info) - goto get_bssid; + if (len < 8) { + devinfo(usbdev, "media specific indication, " + "ignore too short message (%i < 8)", len); + return; + } - /* Get association info IEs from device and send them back to - * userspace. */ - ret = get_association_info(usbdev, info, assoc_size); - if (!ret) { - evt.data.length = le32_to_cpu(info->req_ie_length); - if (evt.data.length > 0) { - offset = le32_to_cpu(info->offset_req_ies); - wireless_send_event(usbdev->net, - IWEVASSOCREQIE, &evt, - (char *)info + offset); - } + if (offset + len > buflen) { + devinfo(usbdev, "media specific indication, " + "too large to fit to buffer (%i > %i)", + offset + len, buflen); + return; + } - evt.data.length = le32_to_cpu(info->resp_ie_length); - if (evt.data.length > 0) { - offset = le32_to_cpu(info->offset_resp_ies); - wireless_send_event(usbdev->net, - IWEVASSOCRESPIE, &evt, - (char *)info + offset); - } - } + indication = (void *)((u8 *)msg + offset); - kfree(info); + switch (le32_to_cpu(indication->status_type)) { + case NDIS_80211_STATUSTYPE_RADIOSTATE: + devinfo(usbdev, "radio state indication: %i", + le32_to_cpu(indication->u.radio_status)); + return; -get_bssid: - ret = get_bssid(usbdev, bssid); - if (!ret) { - evt.data.flags = 0; - evt.data.length = 0; - memcpy(evt.ap_addr.sa_data, bssid, ETH_ALEN); - wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL); - } - } + case NDIS_80211_STATUSTYPE_MEDIASTREAMMODE: + devinfo(usbdev, "media stream mode indication: %i", + le32_to_cpu(indication->u.media_stream_mode)); + return; - if (test_and_clear_bit(WORK_LINK_DOWN, &priv->work_pending)) { - netif_carrier_off(usbdev->net); + case NDIS_80211_STATUSTYPE_AUTHENTICATION: + rndis_wlan_auth_indication(usbdev, indication, len); + return; - evt.data.flags = 0; - evt.data.length = 0; - memset(evt.ap_addr.sa_data, 0, ETH_ALEN); - wireless_send_event(usbdev->net, SIOCGIWAP, &evt, NULL); - } + case NDIS_80211_STATUSTYPE_PMKID_CANDIDATELIST: + rndis_wlan_pmkid_cand_list_indication(usbdev, indication, len); + return; - if (test_and_clear_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending)) - set_multicast_list(usbdev); + default: + devinfo(usbdev, "media specific indication: " + "unknown status type 0x%08x", + le32_to_cpu(indication->status_type)); + } } -static void rndis_wlan_set_multicast_list(struct net_device *dev) + +static void rndis_wlan_indication(struct usbnet *usbdev, void *ind, int buflen) { - struct usbnet *usbdev = netdev_priv(dev); struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + struct rndis_indicate *msg = ind; + + switch (msg->status) { + case RNDIS_STATUS_MEDIA_CONNECT: + if (priv->current_command_oid == OID_802_11_ADD_KEY) { + /* OID_802_11_ADD_KEY causes sometimes extra + * "media connect" indications which confuses driver + * and userspace to think that device is + * roaming/reassociating when it isn't. + */ + devdbg(usbdev, "ignored OID_802_11_ADD_KEY triggered " + "'media connect'"); + return; + } - if (test_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending)) - return; + usbnet_pause_rx(usbdev); - set_bit(WORK_SET_MULTICAST_LIST, &priv->work_pending); - queue_work(priv->workqueue, &priv->work); -} + devinfo(usbdev, "media connect"); -static void rndis_wlan_link_change(struct usbnet *usbdev, int state) -{ - struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + /* queue work to avoid recursive calls into rndis_command */ + set_bit(WORK_LINK_UP, &priv->work_pending); + queue_work(priv->workqueue, &priv->work); + break; - /* queue work to avoid recursive calls into rndis_command */ - set_bit(state ? WORK_LINK_UP : WORK_LINK_DOWN, &priv->work_pending); - queue_work(priv->workqueue, &priv->work); + case RNDIS_STATUS_MEDIA_DISCONNECT: + devinfo(usbdev, "media disconnect"); + + /* queue work to avoid recursive calls into rndis_command */ + set_bit(WORK_LINK_DOWN, &priv->work_pending); + queue_work(priv->workqueue, &priv->work); + break; + + case RNDIS_STATUS_MEDIA_SPECIFIC_INDICATION: + rndis_wlan_media_specific_indication(usbdev, msg, buflen); + break; + + default: + devinfo(usbdev, "indication: 0x%08x", + le32_to_cpu(msg->status)); + break; + } } @@ -2178,77 +2561,44 @@ static int rndis_wlan_get_caps(struct usbnet *usbdev) } -#define STATS_UPDATE_JIFFIES (HZ) -static void rndis_update_wireless_stats(struct work_struct *work) +#define DEVICE_POLLER_JIFFIES (HZ) +static void rndis_device_poller(struct work_struct *work) { struct rndis_wlan_private *priv = - container_of(work, struct rndis_wlan_private, stats_work.work); + container_of(work, struct rndis_wlan_private, + dev_poller_work.work); struct usbnet *usbdev = priv->usbdev; - struct iw_statistics iwstats; __le32 rssi, tmp; int len, ret, j; - unsigned long flags; - int update_jiffies = STATS_UPDATE_JIFFIES; + int update_jiffies = DEVICE_POLLER_JIFFIES; void *buf; - spin_lock_irqsave(&priv->stats_lock, flags); - memcpy(&iwstats, &priv->privstats, sizeof(iwstats)); - spin_unlock_irqrestore(&priv->stats_lock, flags); - - /* only update stats when connected */ - if (!is_associated(usbdev)) { - iwstats.qual.qual = 0; - iwstats.qual.level = 0; - iwstats.qual.updated = IW_QUAL_QUAL_UPDATED - | IW_QUAL_LEVEL_UPDATED - | IW_QUAL_NOISE_INVALID - | IW_QUAL_QUAL_INVALID - | IW_QUAL_LEVEL_INVALID; + /* Only check/do workaround when connected. Calling is_associated() + * also polls device with rndis_command() and catches for media link + * indications. + */ + if (!is_associated(usbdev)) goto end; - } len = sizeof(rssi); ret = rndis_query_oid(usbdev, OID_802_11_RSSI, &rssi, &len); - - devdbg(usbdev, "stats: OID_802_11_RSSI -> %d, rssi:%d", ret, - le32_to_cpu(rssi)); - if (ret == 0) { - memset(&iwstats.qual, 0, sizeof(iwstats.qual)); - iwstats.qual.qual = level_to_qual(le32_to_cpu(rssi)); - iwstats.qual.level = level_to_qual(le32_to_cpu(rssi)); - iwstats.qual.updated = IW_QUAL_QUAL_UPDATED - | IW_QUAL_LEVEL_UPDATED - | IW_QUAL_NOISE_INVALID; - } - - memset(&iwstats.discard, 0, sizeof(iwstats.discard)); - - len = sizeof(tmp); - ret = rndis_query_oid(usbdev, OID_GEN_XMIT_ERROR, &tmp, &len); - if (ret == 0) - iwstats.discard.misc += le32_to_cpu(tmp); - - len = sizeof(tmp); - ret = rndis_query_oid(usbdev, OID_GEN_RCV_ERROR, &tmp, &len); if (ret == 0) - iwstats.discard.misc += le32_to_cpu(tmp); + priv->last_qual = level_to_qual(le32_to_cpu(rssi)); - len = sizeof(tmp); - ret = rndis_query_oid(usbdev, OID_GEN_RCV_NO_BUFFER, &tmp, &len); - if (ret == 0) - iwstats.discard.misc += le32_to_cpu(tmp); + devdbg(usbdev, "dev-poller: OID_802_11_RSSI -> %d, rssi:%d, qual: %d", + ret, le32_to_cpu(rssi), level_to_qual(le32_to_cpu(rssi))); /* Workaround transfer stalls on poor quality links. * TODO: find right way to fix these stalls (as stalls do not happen * with ndiswrapper/windows driver). */ - if (iwstats.qual.qual <= 25) { + if (priv->last_qual <= 25) { /* Decrease stats worker interval to catch stalls. * faster. Faster than 400-500ms causes packet loss, * Slower doesn't catch stalls fast enough. */ j = msecs_to_jiffies(priv->param_workaround_interval); - if (j > STATS_UPDATE_JIFFIES) - j = STATS_UPDATE_JIFFIES; + if (j > DEVICE_POLLER_JIFFIES) + j = DEVICE_POLLER_JIFFIES; else if (j <= 0) j = 1; update_jiffies = j; @@ -2268,11 +2618,8 @@ static void rndis_update_wireless_stats(struct work_struct *work) rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len); kfree(buf); } -end: - spin_lock_irqsave(&priv->stats_lock, flags); - memcpy(&priv->privstats, &iwstats, sizeof(iwstats)); - spin_unlock_irqrestore(&priv->stats_lock, flags); +end: if (update_jiffies >= HZ) update_jiffies = round_jiffies_relative(update_jiffies); else { @@ -2281,7 +2628,8 @@ end: update_jiffies = j; } - queue_delayed_work(priv->workqueue, &priv->stats_work, update_jiffies); + queue_delayed_work(priv->workqueue, &priv->dev_poller_work, + update_jiffies); } @@ -2398,16 +2746,14 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) * Otherwise we'll be in big trouble in rndis_wlan_early_init(). */ usbdev->driver_priv = priv; - usbdev->net->wireless_handlers = &rndis_iw_handlers; priv->usbdev = usbdev; mutex_init(&priv->command_lock); - spin_lock_init(&priv->stats_lock); /* because rndis_command() sleeps we need to use workqueue */ priv->workqueue = create_singlethread_workqueue("rndis_wlan"); INIT_WORK(&priv->work, rndis_wlan_worker); - INIT_DELAYED_WORK(&priv->stats_work, rndis_update_wireless_stats); + INIT_DELAYED_WORK(&priv->dev_poller_work, rndis_device_poller); INIT_DELAYED_WORK(&priv->scan_work, rndis_get_scan_results); /* try bind rndis_host */ @@ -2439,14 +2785,6 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) else usbdev->net->flags &= ~IFF_MULTICAST; - priv->iwstats.qual.qual = 0; - priv->iwstats.qual.level = 0; - priv->iwstats.qual.updated = IW_QUAL_QUAL_UPDATED - | IW_QUAL_LEVEL_UPDATED - | IW_QUAL_NOISE_INVALID - | IW_QUAL_QUAL_INVALID - | IW_QUAL_LEVEL_INVALID; - /* fill-out wiphy structure and register w/ cfg80211 */ memcpy(wiphy->perm_addr, usbdev->net->dev_addr, ETH_ALEN); wiphy->privid = rndis_wiphy_privid; @@ -2454,7 +2792,7 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) | BIT(NL80211_IFTYPE_ADHOC); wiphy->max_scan_ssids = 1; - /* TODO: fill-out band information based on priv->caps */ + /* TODO: fill-out band/encr information based on priv->caps */ rndis_wlan_get_caps(usbdev); memcpy(priv->channels, rndis_channels, sizeof(rndis_channels)); @@ -2466,6 +2804,11 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; + memcpy(priv->cipher_suites, rndis_cipher_suites, + sizeof(rndis_cipher_suites)); + wiphy->cipher_suites = priv->cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(rndis_cipher_suites); + set_wiphy_dev(wiphy, &usbdev->udev->dev); if (wiphy_register(wiphy)) { @@ -2475,18 +2818,19 @@ static int rndis_wlan_bind(struct usbnet *usbdev, struct usb_interface *intf) set_default_iw_params(usbdev); + /* set default rts/frag */ + rndis_set_wiphy_params(wiphy, + WIPHY_PARAM_FRAG_THRESHOLD | WIPHY_PARAM_RTS_THRESHOLD); + /* turn radio on */ priv->radio_on = 1; disassociate(usbdev, 1); netif_carrier_off(usbdev->net); - queue_delayed_work(priv->workqueue, &priv->stats_work, - round_jiffies_relative(STATS_UPDATE_JIFFIES)); - return 0; fail: - cancel_delayed_work_sync(&priv->stats_work); + cancel_delayed_work_sync(&priv->dev_poller_work); cancel_delayed_work_sync(&priv->scan_work); cancel_work_sync(&priv->work); flush_workqueue(priv->workqueue); @@ -2504,7 +2848,7 @@ static void rndis_wlan_unbind(struct usbnet *usbdev, struct usb_interface *intf) /* turn radio off */ disassociate(usbdev, 0); - cancel_delayed_work_sync(&priv->stats_work); + cancel_delayed_work_sync(&priv->dev_poller_work); cancel_delayed_work_sync(&priv->scan_work); cancel_work_sync(&priv->work); flush_workqueue(priv->workqueue); @@ -2522,47 +2866,100 @@ static void rndis_wlan_unbind(struct usbnet *usbdev, struct usb_interface *intf) static int rndis_wlan_reset(struct usbnet *usbdev) { + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + int retval; + + devdbg(usbdev, "rndis_wlan_reset"); + + retval = rndis_reset(usbdev); + if (retval) + devwarn(usbdev, "rndis_reset() failed: %d", retval); + + /* rndis_reset cleared multicast list, so restore here. + (set_multicast_list() also turns on current packet filter) */ + set_multicast_list(usbdev); + + queue_delayed_work(priv->workqueue, &priv->dev_poller_work, + round_jiffies_relative(DEVICE_POLLER_JIFFIES)); + return deauthenticate(usbdev); } +static int rndis_wlan_stop(struct usbnet *usbdev) +{ + struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); + int retval; + __le32 filter; + + devdbg(usbdev, "rndis_wlan_stop"); + + retval = disassociate(usbdev, 0); + + priv->work_pending = 0; + cancel_delayed_work_sync(&priv->dev_poller_work); + cancel_delayed_work_sync(&priv->scan_work); + cancel_work_sync(&priv->work); + flush_workqueue(priv->workqueue); + + if (priv->scan_request) { + cfg80211_scan_done(priv->scan_request, true); + priv->scan_request = NULL; + } + + /* Set current packet filter zero to block receiving data packets from + device. */ + filter = 0; + rndis_set_oid(usbdev, OID_GEN_CURRENT_PACKET_FILTER, &filter, + sizeof(filter)); + + return retval; +} + + static const struct driver_info bcm4320b_info = { .description = "Wireless RNDIS device, BCM4320b based", - .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT, + .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | + FLAG_AVOID_UNLINK_URBS, .bind = rndis_wlan_bind, .unbind = rndis_wlan_unbind, .status = rndis_status, .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, .reset = rndis_wlan_reset, + .stop = rndis_wlan_stop, .early_init = bcm4320b_early_init, - .link_change = rndis_wlan_link_change, + .indication = rndis_wlan_indication, }; static const struct driver_info bcm4320a_info = { .description = "Wireless RNDIS device, BCM4320a based", - .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT, + .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | + FLAG_AVOID_UNLINK_URBS, .bind = rndis_wlan_bind, .unbind = rndis_wlan_unbind, .status = rndis_status, .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, .reset = rndis_wlan_reset, + .stop = rndis_wlan_stop, .early_init = bcm4320a_early_init, - .link_change = rndis_wlan_link_change, + .indication = rndis_wlan_indication, }; static const struct driver_info rndis_wlan_info = { .description = "Wireless RNDIS device", - .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT, + .flags = FLAG_WLAN | FLAG_FRAMING_RN | FLAG_NO_SETINT | + FLAG_AVOID_UNLINK_URBS, .bind = rndis_wlan_bind, .unbind = rndis_wlan_unbind, .status = rndis_status, .rx_fixup = rndis_rx_fixup, .tx_fixup = rndis_tx_fixup, .reset = rndis_wlan_reset, + .stop = rndis_wlan_stop, .early_init = bcm4320a_early_init, - .link_change = rndis_wlan_link_change, + .indication = rndis_wlan_indication, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index 8aab3e6754bd..ed1f997e3521 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -1,8 +1,8 @@ menuconfig RT2X00 tristate "Ralink driver support" - depends on MAC80211 && WLAN_80211 && EXPERIMENTAL + depends on MAC80211 && WLAN_80211 ---help--- - This will enable the experimental support for the Ralink drivers, + This will enable the support for the Ralink drivers, developed in the rt2x00 project <http://rt2x00.serialmonkey.com>. These drivers make use of the mac80211 stack. @@ -79,14 +79,14 @@ config RT73USB config RT2800USB tristate "Ralink rt2800 (USB) support" - depends on USB + depends on USB && EXPERIMENTAL select RT2X00_LIB_USB select RT2X00_LIB_HT select RT2X00_LIB_FIRMWARE select RT2X00_LIB_CRYPTO select CRC_CCITT ---help--- - This adds support for rt2800 wireless chipset family. + This adds experimental support for rt2800 wireless chipset family. Supported chips: RT2770, RT2870 & RT3070. When compiled as a module, this driver will be called "rt2800usb.ko". @@ -112,14 +112,6 @@ config RT2X00_LIB_FIRMWARE config RT2X00_LIB_CRYPTO boolean -config RT2X00_LIB_RFKILL - boolean - default y if (RT2X00_LIB=y && INPUT=y) || (RT2X00_LIB=m && INPUT!=n) - select INPUT_POLLDEV - -comment "rt2x00 rfkill support disabled due to modularized INPUT and built-in rt2x00" - depends on RT2X00_LIB=y && INPUT=m - config RT2X00_LIB_LEDS boolean default y if (RT2X00_LIB=y && LEDS_CLASS=y) || (RT2X00_LIB=m && LEDS_CLASS!=n) diff --git a/drivers/net/wireless/rt2x00/Makefile b/drivers/net/wireless/rt2x00/Makefile index bfc7226f0afe..13043ea97667 100644 --- a/drivers/net/wireless/rt2x00/Makefile +++ b/drivers/net/wireless/rt2x00/Makefile @@ -5,7 +5,6 @@ rt2x00lib-y += rt2x00queue.o rt2x00lib-y += rt2x00link.o rt2x00lib-$(CONFIG_RT2X00_LIB_DEBUGFS) += rt2x00debug.o rt2x00lib-$(CONFIG_RT2X00_LIB_CRYPTO) += rt2x00crypto.o -rt2x00lib-$(CONFIG_RT2X00_LIB_RFKILL) += rt2x00rfkill.o rt2x00lib-$(CONFIG_RT2X00_LIB_FIRMWARE) += rt2x00firmware.o rt2x00lib-$(CONFIG_RT2X00_LIB_LEDS) += rt2x00leds.o rt2x00lib-$(CONFIG_RT2X00_LIB_HT) += rt2x00ht.o diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index 435f945fe64d..164df9347a2f 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -199,7 +199,6 @@ static const struct rt2x00debug rt2400pci_rt2x00debug = { }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -#ifdef CONFIG_RT2X00_LIB_RFKILL static int rt2400pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; @@ -207,9 +206,6 @@ static int rt2400pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) rt2x00pci_register_read(rt2x00dev, GPIOCSR, ®); return rt2x00_get_field32(reg, GPIOCSR_BIT0); } -#else -#define rt2400pci_rfkill_poll NULL -#endif /* CONFIG_RT2X00_LIB_RFKILL */ #ifdef CONFIG_RT2X00_LIB_LEDS static void rt2400pci_brightness_set(struct led_classdev *led_cdev, @@ -1073,8 +1069,6 @@ static void rt2400pci_write_beacon(struct queue_entry *entry) * otherwise we might be sending out invalid data. */ rt2x00pci_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); - rt2x00_set_field32(®, CSR14_TBCN, 0); rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); rt2x00pci_register_write(rt2x00dev, CSR14, reg); @@ -1391,10 +1385,8 @@ static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Detect if this device has an hardware controlled radio. */ -#ifdef CONFIG_RT2X00_LIB_RFKILL if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); -#endif /* CONFIG_RT2X00_LIB_RFKILL */ /* * Check if the BBP tuning should be enabled. @@ -1567,12 +1559,14 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = { .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, .configure_filter = rt2x00mac_configure_filter, + .set_tim = rt2x00mac_set_tim, .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2400pci_conf_tx, .get_tx_stats = rt2x00mac_get_tx_stats, .get_tsf = rt2400pci_get_tsf, .tx_last_beacon = rt2400pci_tx_last_beacon, + .rfkill_poll = rt2x00mac_rfkill_poll, }; static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = { diff --git a/drivers/net/wireless/rt2x00/rt2400pci.h b/drivers/net/wireless/rt2x00/rt2400pci.h index ec3b004ddc3c..ccd644104ad1 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.h +++ b/drivers/net/wireless/rt2x00/rt2400pci.h @@ -928,7 +928,7 @@ #define RXD_W7_RESERVED FIELD32(0xffffffff) /* - * Macro's for converting txpower from EEPROM to mac80211 value + * Macros for converting txpower from EEPROM to mac80211 value * and from mac80211 value to register value. * NOTE: Logics in rt2400pci for txpower are reversed * compared to the other rt2x00 drivers. A higher txpower diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index 08b30d01e67d..4186582f2770 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -199,7 +199,6 @@ static const struct rt2x00debug rt2500pci_rt2x00debug = { }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -#ifdef CONFIG_RT2X00_LIB_RFKILL static int rt2500pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; @@ -207,9 +206,6 @@ static int rt2500pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) rt2x00pci_register_read(rt2x00dev, GPIOCSR, ®); return rt2x00_get_field32(reg, GPIOCSR_BIT0); } -#else -#define rt2500pci_rfkill_poll NULL -#endif /* CONFIG_RT2X00_LIB_RFKILL */ #ifdef CONFIG_RT2X00_LIB_LEDS static void rt2500pci_brightness_set(struct led_classdev *led_cdev, @@ -1231,8 +1227,6 @@ static void rt2500pci_write_beacon(struct queue_entry *entry) * otherwise we might be sending out invalid data. */ rt2x00pci_register_read(rt2x00dev, CSR14, ®); - rt2x00_set_field32(®, CSR14_TSF_COUNT, 0); - rt2x00_set_field32(®, CSR14_TBCN, 0); rt2x00_set_field32(®, CSR14_BEACON_GEN, 0); rt2x00pci_register_write(rt2x00dev, CSR14, reg); @@ -1548,10 +1542,8 @@ static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Detect if this device has an hardware controlled radio. */ -#ifdef CONFIG_RT2X00_LIB_RFKILL if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); -#endif /* CONFIG_RT2X00_LIB_RFKILL */ /* * Check if the BBP tuning should be enabled. @@ -1866,12 +1858,14 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = { .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, .configure_filter = rt2x00mac_configure_filter, + .set_tim = rt2x00mac_set_tim, .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2x00mac_conf_tx, .get_tx_stats = rt2x00mac_get_tx_stats, .get_tsf = rt2500pci_get_tsf, .tx_last_beacon = rt2500pci_tx_last_beacon, + .rfkill_poll = rt2x00mac_rfkill_poll, }; static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = { diff --git a/drivers/net/wireless/rt2x00/rt2500pci.h b/drivers/net/wireless/rt2x00/rt2500pci.h index ce2f065c7486..54d37957883c 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.h +++ b/drivers/net/wireless/rt2x00/rt2500pci.h @@ -1218,7 +1218,7 @@ #define RXD_W10_DROP FIELD32(0x00000001) /* - * Macro's for converting txpower from EEPROM to mac80211 value + * Macros for converting txpower from EEPROM to mac80211 value * and from mac80211 value to register value. */ #define MIN_TXPOWER 0 diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index ce75426764a1..b04f59bab3b0 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -277,7 +277,6 @@ static const struct rt2x00debug rt2500usb_rt2x00debug = { }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -#ifdef CONFIG_RT2X00_LIB_RFKILL static int rt2500usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u16 reg; @@ -285,9 +284,6 @@ static int rt2500usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) rt2500usb_register_read(rt2x00dev, MAC_CSR19, ®); return rt2x00_get_field32(reg, MAC_CSR19_BIT7); } -#else -#define rt2500usb_rfkill_poll NULL -#endif /* CONFIG_RT2X00_LIB_RFKILL */ #ifdef CONFIG_RT2X00_LIB_LEDS static void rt2500usb_brightness_set(struct led_classdev *led_cdev, @@ -1242,8 +1238,6 @@ static void rt2500usb_write_beacon(struct queue_entry *entry) * otherwise we might be sending out invalid data. */ rt2500usb_register_read(rt2x00dev, TXRX_CSR19, ®); - rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 0); - rt2x00_set_field16(®, TXRX_CSR19_TBCN, 0); rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 0); rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); @@ -1291,7 +1285,7 @@ static int rt2500usb_get_tx_data_len(struct queue_entry *entry) static void rt2500usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev, const enum data_queue_qid queue) { - u16 reg; + u16 reg, reg0; if (queue != QID_BEACON) { rt2x00usb_kick_tx_queue(rt2x00dev, queue); @@ -1302,16 +1296,19 @@ static void rt2500usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev, if (!rt2x00_get_field16(reg, TXRX_CSR19_BEACON_GEN)) { rt2x00_set_field16(®, TXRX_CSR19_TSF_COUNT, 1); rt2x00_set_field16(®, TXRX_CSR19_TBCN, 1); + reg0 = reg; rt2x00_set_field16(®, TXRX_CSR19_BEACON_GEN, 1); /* * Beacon generation will fail initially. - * To prevent this we need to register the TXRX_CSR19 - * register several times. + * To prevent this we need to change the TXRX_CSR19 + * register several times (reg0 is the same as reg + * except for TXRX_CSR19_BEACON_GEN, which is 0 in reg0 + * and 1 in reg). */ rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0); rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); - rt2500usb_register_write(rt2x00dev, TXRX_CSR19, 0); + rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg0); rt2500usb_register_write(rt2x00dev, TXRX_CSR19, reg); } } @@ -1603,10 +1600,8 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Detect if this device has an hardware controlled radio. */ -#ifdef CONFIG_RT2X00_LIB_RFKILL if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); -#endif /* CONFIG_RT2X00_LIB_RFKILL */ /* * Check if the BBP tuning should be disabled. @@ -1879,7 +1874,6 @@ 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); if (!modparam_nohwcrypt) { __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_COPY_IV, &rt2x00dev->flags); @@ -1902,11 +1896,13 @@ static const struct ieee80211_ops rt2500usb_mac80211_ops = { .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, .configure_filter = rt2x00mac_configure_filter, + .set_tim = rt2x00mac_set_tim, .set_key = rt2x00mac_set_key, .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt2x00mac_conf_tx, .get_tx_stats = rt2x00mac_get_tx_stats, + .rfkill_poll = rt2x00mac_rfkill_poll, }; static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = { diff --git a/drivers/net/wireless/rt2x00/rt2500usb.h b/drivers/net/wireless/rt2x00/rt2500usb.h index 5bc46fe72179..b01edca42583 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.h +++ b/drivers/net/wireless/rt2x00/rt2500usb.h @@ -831,7 +831,7 @@ #define RXD_W3_EIV FIELD32(0xffffffff) /* - * Macro's for converting txpower from EEPROM to mac80211 value + * Macros for converting txpower from EEPROM to mac80211 value * and from mac80211 value to register value. */ #define MIN_TXPOWER 0 diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 37561667925b..639dc6cc04b9 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -264,7 +264,6 @@ static const struct rt2x00debug rt2800usb_rt2x00debug = { }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -#ifdef CONFIG_RT2X00_LIB_RFKILL static int rt2800usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; @@ -272,9 +271,6 @@ static int rt2800usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) rt2x00usb_register_read(rt2x00dev, GPIO_CTRL_CFG, ®); return rt2x00_get_field32(reg, GPIO_CTRL_CFG_BIT2); } -#else -#define rt2800usb_rfkill_poll NULL -#endif /* CONFIG_RT2X00_LIB_RFKILL */ #ifdef CONFIG_RT2X00_LIB_LEDS static void rt2800usb_brightness_set(struct led_classdev *led_cdev, @@ -522,7 +518,7 @@ static void rt2800usb_config_filter(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(®, RX_FILTER_CFG_DROP_RTS, !(filter_flags & FIF_CONTROL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PSPOLL, - !(filter_flags & FIF_CONTROL)); + !(filter_flags & FIF_PSPOLL)); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BA, 1); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_BAR, 0); rt2x00_set_field32(®, RX_FILTER_CFG_DROP_CNTL, @@ -1467,6 +1463,10 @@ static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev) /* * ASIC will keep garbage value after boot, clear encryption keys. */ + for (i = 0; i < 4; i++) + rt2x00usb_register_write(rt2x00dev, + SHARED_KEY_MODE_ENTRY(i), 0); + for (i = 0; i < 256; i++) { u32 wcid[2] = { 0xffffffff, 0x00ffffff }; rt2x00usb_register_multiwrite(rt2x00dev, MAC_WCID_ENTRY(i), @@ -1476,10 +1476,6 @@ static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00usb_register_write(rt2x00dev, MAC_IVEIV_ENTRY(i), 0); } - for (i = 0; i < 16; i++) - rt2x00usb_register_write(rt2x00dev, - SHARED_KEY_MODE_ENTRY(i), 0); - /* * Clear all beacons * For the Beacon base registers we only need to clear @@ -1524,7 +1520,7 @@ static int rt2800usb_init_registers(struct rt2x00_dev *rt2x00dev) rt2x00usb_register_read(rt2x00dev, LG_FBK_CFG0, ®); rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS0FBK, 8); rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS1FBK, 8); - rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS2FBK, 3); + rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS2FBK, 9); rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS3FBK, 10); rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS4FBK, 11); rt2x00_set_field32(®, LG_FBK_CFG0_OFDMMCS5FBK, 12); @@ -1914,7 +1910,7 @@ static int rt2800usb_set_device_state(struct rt2x00_dev *rt2x00dev, /* * Before the radio can be enabled, the device first has * to be woken up. After that it needs a bit of time - * to be fully awake and the radio can be enabled. + * to be fully awake and then the radio can be enabled. */ rt2800usb_set_state(rt2x00dev, STATE_AWAKE); msleep(1); @@ -1922,7 +1918,7 @@ static int rt2800usb_set_device_state(struct rt2x00_dev *rt2x00dev, break; case STATE_RADIO_OFF: /* - * After the radio has been disablee, the device should + * After the radio has been disabled, the device should * be put to sleep for powersaving. */ rt2800usb_disable_radio(rt2x00dev); @@ -1999,11 +1995,11 @@ static void rt2800usb_write_tx_desc(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(&word, TXWI_W1_BW_WIN_SIZE, txdesc->ba_size); rt2x00_set_field32(&word, TXWI_W1_WIRELESS_CLI_ID, test_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags) ? - txdesc->key_idx : 0xff); + (skbdesc->entry->entry_idx + 1) : 0xff); rt2x00_set_field32(&word, TXWI_W1_MPDU_TOTAL_BYTE_COUNT, skb->len - txdesc->l2pad); rt2x00_set_field32(&word, TXWI_W1_PACKETID, - skbdesc->entry->entry_idx); + skbdesc->entry->queue->qid + 1); rt2x00_desc_write(txwi, 1, word); /* @@ -2054,8 +2050,6 @@ static void rt2800usb_write_beacon(struct queue_entry *entry) * otherwise we might be sending out invalid data. */ rt2x00usb_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); rt2x00usb_register_write(rt2x00dev, BCN_TIME_CFG, reg); @@ -2169,8 +2163,10 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry, if (rt2x00_get_field32(rxd0, RXD_W0_MY_BSS)) rxdesc->dev_flags |= RXDONE_MY_BSS; - if (rt2x00_get_field32(rxd0, RXD_W0_L2PAD)) + if (rt2x00_get_field32(rxd0, RXD_W0_L2PAD)) { rxdesc->dev_flags |= RXDONE_L2PAD; + skbdesc->flags |= SKBDESC_L2_PADDED; + } if (rt2x00_get_field32(rxwi1, RXWI_W1_SHORT_GI)) rxdesc->flags |= RX_FLAG_SHORT_GI; @@ -2224,10 +2220,8 @@ static int rt2800usb_validate_eeprom(struct rt2x00_dev *rt2x00dev) */ mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0); if (!is_valid_ether_addr(mac)) { - DECLARE_MAC_BUF(macbuf); - random_ether_addr(mac); - EEPROM(rt2x00dev, "MAC: %s\n", print_mac(macbuf, mac)); + EEPROM(rt2x00dev, "MAC: %pM\n", mac); } rt2x00_eeprom_read(rt2x00dev, EEPROM_ANTENNA, &word); @@ -2385,10 +2379,8 @@ static int rt2800usb_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Detect if this device has an hardware controlled radio. */ -#ifdef CONFIG_RT2X00_LIB_RFKILL if (rt2x00_get_field16(eeprom, EEPROM_NIC_HW_RADIO)) __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); -#endif /* CONFIG_RT2X00_LIB_RFKILL */ /* * Store led settings, for correct led behaviour. @@ -2632,10 +2624,16 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) return retval; /* + * This device has multiple filters for control frames + * and has a separate filter for PS Poll frames. + */ + __set_bit(DRIVER_SUPPORT_CONTROL_FILTERS, &rt2x00dev->flags); + __set_bit(DRIVER_SUPPORT_CONTROL_FILTER_PSPOLL, &rt2x00dev->flags); + + /* * This device requires firmware. */ __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); - __set_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags); if (!modparam_nohwcrypt) __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); @@ -2792,6 +2790,7 @@ static const struct ieee80211_ops rt2800usb_mac80211_ops = { .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, .configure_filter = rt2x00mac_configure_filter, + .set_tim = rt2x00mac_set_tim, .set_key = rt2x00mac_set_key, .get_stats = rt2x00mac_get_stats, .get_tkip_seq = rt2800usb_get_tkip_seq, @@ -2800,6 +2799,7 @@ static const struct ieee80211_ops rt2800usb_mac80211_ops = { .conf_tx = rt2800usb_conf_tx, .get_tx_stats = rt2x00mac_get_tx_stats, .get_tsf = rt2800usb_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, }; static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { diff --git a/drivers/net/wireless/rt2x00/rt2800usb.h b/drivers/net/wireless/rt2x00/rt2800usb.h index 61a8be61d3f5..4d9991c9a51c 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.h +++ b/drivers/net/wireless/rt2x00/rt2800usb.h @@ -36,6 +36,9 @@ * RF2750 2.4G/5G 1T2R * RF3020 2.4G 1T1R * RF2020 2.4G B/G + * RF3021 2.4G 1T2R + * RF3022 2.4G 2T2R + * RF3052 2.4G 2T2R */ #define RF2820 0x0001 #define RF2850 0x0002 @@ -43,6 +46,9 @@ #define RF2750 0x0004 #define RF3020 0x0005 #define RF2020 0x0006 +#define RF3021 0x0007 +#define RF3022 0x0008 +#define RF3052 0x0009 /* * RT2870 version @@ -1300,8 +1306,8 @@ * PAIRWISE_KEY_TABLE_BASE: 32-byte * 256 entry * MAC_IVEIV_TABLE_BASE: 8-byte * 256-entry * MAC_WCID_ATTRIBUTE_BASE: 4-byte * 256-entry - * SHARED_KEY_TABLE_BASE: 32-byte * 16-entry - * SHARED_KEY_MODE_BASE: 4-byte * 16-entry + * SHARED_KEY_TABLE_BASE: 32 bytes * 32-entry + * SHARED_KEY_MODE_BASE: 4 bits * 32-entry */ #define MAC_WCID_BASE 0x1800 #define PAIRWISE_KEY_TABLE_BASE 0x4000 @@ -1921,7 +1927,7 @@ struct mac_iveiv_entry { #define RXWI_W3_SNR1 FIELD32(0x0000ff00) /* - * Macro's for converting txpower from EEPROM to mac80211 value + * Macros for converting txpower from EEPROM to mac80211 value * and from mac80211 value to register value. */ #define MIN_G_TXPOWER 0 diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 49c9e2c1433d..555a777db6df 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -134,6 +134,17 @@ GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) /* + * Structure for average calculation + * The avg field contains the actual average value, + * but avg_weight is internally used during calculations + * to prevent rounding errors. + */ +struct avg_val { + int avg; + int avg_weight; +}; + +/* * Chipset identification * The chipset on the device is composed of a RT and RF chip. * The chipset combination is important for determining device capabilities. @@ -245,21 +256,18 @@ struct link_ant { struct antenna_setup active; /* - * RSSI information for the different antenna's. - * These statistics are used to determine when - * to switch antenna when using software diversity. - * - * rssi[0] -> Antenna A RSSI - * rssi[1] -> Antenna B RSSI + * RSSI history information for the antenna. + * Used to determine when to switch antenna + * when using software diversity. */ - int rssi_history[2]; + int rssi_history; /* * Current RSSI average of the currently active antenna. * Similar to the avg_rssi in the link_qual structure * this value is updated by using the walking average. */ - int rssi_ant; + struct avg_val rssi_ant; }; /* @@ -288,7 +296,7 @@ struct link { /* * Currently active average RSSI value */ - int avg_rssi; + struct avg_val avg_rssi; /* * Currently precalculated percentages of successful @@ -326,6 +334,11 @@ struct rt2x00_intf { u8 bssid[ETH_ALEN]; /* + * beacon->skb must be protected with the mutex. + */ + struct mutex beacon_skb_mutex; + + /* * Entry in the beacon queue which belongs to * this interface. Each interface has its own * dedicated beacon entry. @@ -337,8 +350,6 @@ struct rt2x00_intf { */ unsigned int delayed_flags; #define DELAYED_UPDATE_BEACON 0x00000001 -#define DELAYED_CONFIG_ERP 0x00000002 -#define DELAYED_LED_ASSOC 0x00000004 /* * Software sequence counter, this is only required @@ -594,7 +605,6 @@ enum rt2x00_flags { DEVICE_STATE_INITIALIZED, DEVICE_STATE_STARTED, DEVICE_STATE_ENABLED_RADIO, - DEVICE_STATE_DISABLED_RADIO_HW, /* * Driver requirements @@ -602,7 +612,6 @@ enum rt2x00_flags { DRIVER_REQUIRE_FIRMWARE, DRIVER_REQUIRE_BEACON_GUARD, DRIVER_REQUIRE_ATIM_QUEUE, - DRIVER_REQUIRE_SCHEDULED, DRIVER_REQUIRE_DMA, DRIVER_REQUIRE_COPY_IV, DRIVER_REQUIRE_L2PAD, @@ -612,6 +621,8 @@ enum rt2x00_flags { */ CONFIG_SUPPORT_HW_BUTTON, CONFIG_SUPPORT_HW_CRYPTO, + DRIVER_SUPPORT_CONTROL_FILTERS, + DRIVER_SUPPORT_CONTROL_FILTER_PSPOLL, /* * Driver configuration @@ -634,7 +645,7 @@ struct rt2x00_dev { * The structure stored in here depends on the * system bus (PCI or USB). * When accessing this variable, the rt2x00dev_{pci,usb} - * macro's should be used for correct typecasting. + * macros should be used for correct typecasting. */ struct device *dev; @@ -651,18 +662,6 @@ struct rt2x00_dev { enum ieee80211_band curr_band; /* - * rfkill structure for RF state switching support. - * This will only be compiled in when required. - */ -#ifdef CONFIG_RT2X00_LIB_RFKILL - unsigned long rfkill_state; -#define RFKILL_STATE_ALLOCATED 1 -#define RFKILL_STATE_REGISTERED 2 -#define RFKILL_STATE_BLOCKED 3 - struct input_polled_dev *rfkill_poll_dev; -#endif /* CONFIG_RT2X00_LIB_RFKILL */ - - /* * If enabled, the debugfs interface structures * required for deregistration of debugfs. */ @@ -824,7 +823,6 @@ struct rt2x00_dev { * due to RTNL locking requirements. */ struct work_struct intf_work; - struct work_struct filter_work; /* * Data queue arrays for RX, TX and Beacon. @@ -976,7 +974,9 @@ int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed); void rt2x00mac_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list); + u64 multicast); +int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + bool set); #ifdef CONFIG_RT2X00_LIB_CRYPTO int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -994,6 +994,7 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, u32 changes); int rt2x00mac_conf_tx(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params); +void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw); /* * Driver allocation handlers. diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index 3e019a12df2e..3501788ab498 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c @@ -124,24 +124,34 @@ enum antenna rt2x00lib_config_antenna_check(enum antenna current_ant, } void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant) + struct antenna_setup config) { + struct link_ant *ant = &rt2x00dev->link.ant; struct antenna_setup *def = &rt2x00dev->default_ant; struct antenna_setup *active = &rt2x00dev->link.ant.active; /* * Failsafe: Make sure we are not sending the * ANTENNA_SW_DIVERSITY state to the driver. - * If that happes fallback to hardware default, + * If that happens, fallback to hardware defaults, * or our own default. + * If diversity handling is active for a particular antenna, + * we shouldn't overwrite that antenna. * The calls to rt2x00lib_config_antenna_check() * might have caused that we restore back to the already * active setting. If that has happened we can quit. */ - ant->rx = rt2x00lib_config_antenna_check(ant->rx, def->rx); - ant->tx = rt2x00lib_config_antenna_check(ant->tx, def->tx); + if (!(ant->flags & ANTENNA_RX_DIVERSITY)) + config.rx = rt2x00lib_config_antenna_check(config.rx, def->rx); + else + config.rx = active->rx; - if (ant->rx == active->rx && ant->tx == active->tx) + if (!(ant->flags & ANTENNA_TX_DIVERSITY)) + config.tx = rt2x00lib_config_antenna_check(config.tx, def->tx); + else + config.tx = active->tx; + + if (config.rx == active->rx && config.tx == active->tx) return; /* @@ -156,11 +166,11 @@ void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, * The latter is required since we need to recalibrate the * noise-sensitivity ratio for the new setup. */ - rt2x00dev->ops->lib->config_ant(rt2x00dev, ant); + rt2x00dev->ops->lib->config_ant(rt2x00dev, &config); rt2x00link_reset_tuner(rt2x00dev, true); - memcpy(active, ant, sizeof(*ant)); + memcpy(active, &config, sizeof(config)); if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON_LINK); diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c index bc4e81e21841..30fbd3bbe08b 100644 --- a/drivers/net/wireless/rt2x00/rt2x00crypto.c +++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c @@ -53,8 +53,7 @@ void rt2x00crypto_create_tx_descriptor(struct queue_entry *entry, struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(entry->skb); struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; - if (!test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags) || - !hw_key || entry->skb->do_not_encrypt) + if (!test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags) || !hw_key) return; __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags); @@ -82,8 +81,7 @@ unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, struct ieee80211_key_conf *key = tx_info->control.hw_key; unsigned int overhead = 0; - if (!test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags) || - !key || skb->do_not_encrypt) + if (!test_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags) || !key) return overhead; /* @@ -131,7 +129,7 @@ void rt2x00crypto_tx_remove_iv(struct sk_buff *skb, struct txentry_desc *txdesc) /* Pull buffer to correct size */ skb_pull(skb, txdesc->iv_len); - /* IV/EIV data has officially be stripped */ + /* IV/EIV data has officially been stripped */ skbdesc->flags |= SKBDESC_IV_STRIPPED; } diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 57813e72c808..3f8c70ebe9ad 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -40,8 +40,7 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev) * Don't enable the radio twice. * And check if the hardware button has been disabled. */ - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) || - test_bit(DEVICE_STATE_DISABLED_RADIO_HW, &rt2x00dev->flags)) + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return 0; /* @@ -119,20 +118,11 @@ void rt2x00lib_toggle_rx(struct rt2x00_dev *rt2x00dev, enum dev_state state) rt2x00link_start_tuner(rt2x00dev); } -static void rt2x00lib_packetfilter_scheduled(struct work_struct *work) -{ - struct rt2x00_dev *rt2x00dev = - container_of(work, struct rt2x00_dev, filter_work); - - rt2x00dev->ops->lib->config_filter(rt2x00dev, rt2x00dev->packet_filter); -} - static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = data; struct rt2x00_intf *intf = vif_to_intf(vif); - struct ieee80211_bss_conf conf; int delayed_flags; /* @@ -142,7 +132,6 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac, */ spin_lock(&intf->lock); - memcpy(&conf, &vif->bss_conf, sizeof(conf)); delayed_flags = intf->delayed_flags; intf->delayed_flags = 0; @@ -159,12 +148,6 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac, if (delayed_flags & DELAYED_UPDATE_BEACON) rt2x00queue_update_beacon(rt2x00dev, vif, true); - - if (delayed_flags & DELAYED_CONFIG_ERP) - rt2x00lib_config_erp(rt2x00dev, intf, &conf); - - if (delayed_flags & DELAYED_LED_ASSOC) - rt2x00leds_led_assoc(rt2x00dev, !!rt2x00dev->intf_associated); } static void rt2x00lib_intf_scheduled(struct work_struct *work) @@ -187,7 +170,6 @@ static void rt2x00lib_intf_scheduled(struct work_struct *work) static void rt2x00lib_beacondone_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { - struct rt2x00_dev *rt2x00dev = data; struct rt2x00_intf *intf = vif_to_intf(vif); if (vif->type != NL80211_IFTYPE_AP && @@ -196,12 +178,6 @@ static void rt2x00lib_beacondone_iter(void *data, u8 *mac, vif->type != NL80211_IFTYPE_WDS) return; - /* - * Clean up the beacon skb. - */ - rt2x00queue_free_skb(rt2x00dev, intf->beacon->skb); - intf->beacon->skb = NULL; - spin_lock(&intf->lock); intf->delayed_flags |= DELAYED_UPDATE_BEACON; spin_unlock(&intf->lock); @@ -216,7 +192,7 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) rt2x00lib_beacondone_iter, rt2x00dev); - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->intf_work); + ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work); } EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); @@ -228,7 +204,8 @@ void rt2x00lib_txdone(struct queue_entry *entry, struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); enum data_queue_qid qid = skb_get_queue_mapping(entry->skb); unsigned int header_length = ieee80211_get_hdrlen_from_skb(entry->skb); - u8 rate_idx, rate_flags; + u8 rate_idx, rate_flags, retry_rates; + unsigned int i; /* * Unmap the skb. @@ -267,16 +244,27 @@ void rt2x00lib_txdone(struct queue_entry *entry, rate_idx = skbdesc->tx_rate_idx; rate_flags = skbdesc->tx_rate_flags; + retry_rates = test_bit(TXDONE_FALLBACK, &txdesc->flags) ? + (txdesc->retry + 1) : 1; /* * Initialize TX status */ memset(&tx_info->status, 0, sizeof(tx_info->status)); tx_info->status.ack_signal = 0; - tx_info->status.rates[0].idx = rate_idx; - tx_info->status.rates[0].flags = rate_flags; - tx_info->status.rates[0].count = txdesc->retry + 1; - tx_info->status.rates[1].idx = -1; /* terminate */ + + /* + * Frame was send with retries, hardware tried + * different rates to send out the frame, at each + * retry it lowered the rate 1 step. + */ + for (i = 0; i < retry_rates && i < IEEE80211_TX_MAX_RATES; i++) { + tx_info->status.rates[i].idx = rate_idx - i; + tx_info->status.rates[i].flags = rate_flags; + tx_info->status.rates[i].count = 1; + } + if (i < (IEEE80211_TX_MAX_RATES -1)) + tx_info->status.rates[i].idx = -1; /* terminate */ if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) { if (test_bit(TXDONE_SUCCESS, &txdesc->flags) || @@ -449,7 +437,8 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, * mac80211 will clean up the skb structure. */ rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb); - ieee80211_rx_irqsafe(rt2x00dev->hw, entry->skb, rx_status); + memcpy(IEEE80211_SKB_RXCB(entry->skb), rx_status, sizeof(*rx_status)); + ieee80211_rx_irqsafe(rt2x00dev->hw, entry->skb); /* * Replace the skb with the freshly allocated one. @@ -785,6 +774,13 @@ int rt2x00lib_start(struct rt2x00_dev *rt2x00dev) rt2x00dev->intf_sta_count = 0; rt2x00dev->intf_associated = 0; + /* Enable the radio */ + retval = rt2x00lib_enable_radio(rt2x00dev); + if (retval) { + rt2x00queue_uninitialize(rt2x00dev); + return retval; + } + set_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags); return 0; @@ -847,7 +843,6 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) * Initialize configuration work. */ INIT_WORK(&rt2x00dev->intf_work, rt2x00lib_intf_scheduled); - INIT_WORK(&rt2x00dev->filter_work, rt2x00lib_packetfilter_scheduled); /* * Allocate queue array. @@ -870,7 +865,6 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) */ rt2x00link_register(rt2x00dev); rt2x00leds_register(rt2x00dev); - rt2x00rfkill_allocate(rt2x00dev); rt2x00debug_register(rt2x00dev); set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); @@ -894,6 +888,11 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) rt2x00lib_disable_radio(rt2x00dev); /* + * Stop all work. + */ + cancel_work_sync(&rt2x00dev->intf_work); + + /* * Uninitialize device. */ rt2x00lib_uninitialize(rt2x00dev); @@ -902,7 +901,6 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) * Free extra components */ rt2x00debug_deregister(rt2x00dev); - rt2x00rfkill_free(rt2x00dev); rt2x00leds_unregister(rt2x00dev); /* diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h index 0bf2715fa93a..eeb2881e818e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00lib.h +++ b/drivers/net/wireless/rt2x00/rt2x00lib.h @@ -30,10 +30,8 @@ /* * Interval defines - * Both the link tuner as the rfkill will be called once per second. */ #define LINK_TUNE_INTERVAL round_jiffies_relative(HZ) -#define RFKILL_POLL_INTERVAL 1000 /* * rt2x00_rate: Per rate device information @@ -90,7 +88,7 @@ void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf, struct ieee80211_bss_conf *conf); void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, - struct antenna_setup *ant); + struct antenna_setup ant); void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, struct ieee80211_conf *conf, const unsigned int changed_flags); @@ -386,29 +384,18 @@ static inline void rt2x00ht_create_tx_descriptor(struct queue_entry *entry, /* * RFkill handlers. */ -#ifdef CONFIG_RT2X00_LIB_RFKILL -void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev); -void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev); -void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev); -void rt2x00rfkill_free(struct rt2x00_dev *rt2x00dev); -#else static inline void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev) { + if (test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags)) + wiphy_rfkill_start_polling(rt2x00dev->hw->wiphy); } static inline void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev) { + if (test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags)) + wiphy_rfkill_stop_polling(rt2x00dev->hw->wiphy); } -static inline void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev) -{ -} - -static inline void rt2x00rfkill_free(struct rt2x00_dev *rt2x00dev) -{ -} -#endif /* CONFIG_RT2X00_LIB_RFKILL */ - /* * LED handlers */ diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/rt2x00/rt2x00link.c index eb9b981b9139..c64db0ba7f40 100644 --- a/drivers/net/wireless/rt2x00/rt2x00link.c +++ b/drivers/net/wireless/rt2x00/rt2x00link.c @@ -46,7 +46,15 @@ #define DEFAULT_PERCENTAGE 50 /* - * Small helper macro to work with moving/walking averages. + * Small helper macro for percentage calculation + * This is a very simple macro with the only catch that it will + * produce a default value in case no total value was provided. + */ +#define PERCENTAGE(__value, __total) \ + ( (__total) ? (((__value) * 100) / (__total)) : (DEFAULT_PERCENTAGE) ) + +/* + * Helper struct and macro to work with moving/walking averages. * When adding a value to the average value the following calculation * is needed: * @@ -60,18 +68,28 @@ * for a few minutes but when the device is moved away from the AP * the average will not decrease fast enough to compensate. * The walking average compensates this and will move towards - * the new values correctly allowing a effective link tuning. - */ -#define MOVING_AVERAGE(__avg, __val, __samples) \ - ( (((__avg) * ((__samples) - 1)) + (__val)) / (__samples) ) - -/* - * Small helper macro for percentage calculation - * This is a very simple macro with the only catch that it will - * produce a default value in case no total value was provided. + * the new values correctly allowing a effective link tuning, + * the speed of the average moving towards other values depends + * on the value for the number of samples. The higher the number + * of samples, the slower the average will move. + * We use two variables to keep track of the average value to + * compensate for the rounding errors. This can be a significant + * error (>5dBm) if the factor is too low. */ -#define PERCENTAGE(__value, __total) \ - ( (__total) ? (((__value) * 100) / (__total)) : (DEFAULT_PERCENTAGE) ) +#define AVG_SAMPLES 8 +#define AVG_FACTOR 1000 +#define MOVING_AVERAGE(__avg, __val) \ +({ \ + struct avg_val __new; \ + __new.avg_weight = \ + (__avg).avg_weight ? \ + ((((__avg).avg_weight * ((AVG_SAMPLES) - 1)) + \ + ((__val) * (AVG_FACTOR))) / \ + (AVG_SAMPLES) ) : \ + ((__val) * (AVG_FACTOR)); \ + __new.avg = __new.avg_weight / (AVG_FACTOR); \ + __new; \ +}) /* * For calculating the Signal quality we have determined @@ -98,56 +116,41 @@ static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev) { struct link_ant *ant = &rt2x00dev->link.ant; - if (ant->rssi_ant && rt2x00dev->link.qual.rx_success) - return ant->rssi_ant; + if (ant->rssi_ant.avg && rt2x00dev->link.qual.rx_success) + return ant->rssi_ant.avg; return DEFAULT_RSSI; } -static int rt2x00link_antenna_get_rssi_history(struct rt2x00_dev *rt2x00dev, - enum antenna antenna) +static int rt2x00link_antenna_get_rssi_history(struct rt2x00_dev *rt2x00dev) { struct link_ant *ant = &rt2x00dev->link.ant; - if (ant->rssi_history[antenna - ANTENNA_A]) - return ant->rssi_history[antenna - ANTENNA_A]; + if (ant->rssi_history) + return ant->rssi_history; return DEFAULT_RSSI; } -/* Small wrapper for rt2x00link_antenna_get_rssi_history() */ -#define rt2x00link_antenna_get_rssi_rx_history(__dev) \ - rt2x00link_antenna_get_rssi_history((__dev), \ - (__dev)->link.ant.active.rx) -#define rt2x00link_antenna_get_rssi_tx_history(__dev) \ - rt2x00link_antenna_get_rssi_history((__dev), \ - (__dev)->link.ant.active.tx) static void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev, - enum antenna antenna, int rssi) { struct link_ant *ant = &rt2x00dev->link.ant; - ant->rssi_history[ant->active.rx - ANTENNA_A] = rssi; + ant->rssi_history = rssi; } -/* Small wrapper for rt2x00link_antenna_get_rssi_history() */ -#define rt2x00link_antenna_update_rssi_rx_history(__dev, __rssi) \ - rt2x00link_antenna_update_rssi_history((__dev), \ - (__dev)->link.ant.active.rx, \ - (__rssi)) -#define rt2x00link_antenna_update_rssi_tx_history(__dev, __rssi) \ - rt2x00link_antenna_update_rssi_history((__dev), \ - (__dev)->link.ant.active.tx, \ - (__rssi)) static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev) { - rt2x00dev->link.ant.rssi_ant = 0; + rt2x00dev->link.ant.rssi_ant.avg = 0; + rt2x00dev->link.ant.rssi_ant.avg_weight = 0; } static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev) { struct link_ant *ant = &rt2x00dev->link.ant; struct antenna_setup new_ant; - int sample_a = rt2x00link_antenna_get_rssi_history(rt2x00dev, ANTENNA_A); - int sample_b = rt2x00link_antenna_get_rssi_history(rt2x00dev, ANTENNA_B); + int other_antenna; + + int sample_current = rt2x00link_antenna_get_link_rssi(rt2x00dev); + int sample_other = rt2x00link_antenna_get_rssi_history(rt2x00dev); memcpy(&new_ant, &ant->active, sizeof(new_ant)); @@ -158,22 +161,27 @@ static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev) /* * During the last period we have sampled the RSSI - * from both antenna's. It now is time to determine + * from both antennas. It now is time to determine * which antenna demonstrated the best performance. * When we are already on the antenna with the best - * performance, then there really is nothing for us - * left to do. + * performance, just create a good starting point + * for the history and we are done. */ - if (sample_a == sample_b) + if (sample_current >= sample_other) { + rt2x00link_antenna_update_rssi_history(rt2x00dev, + sample_current); return; + } + + other_antenna = (ant->active.rx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; if (ant->flags & ANTENNA_RX_DIVERSITY) - new_ant.rx = (sample_a > sample_b) ? ANTENNA_A : ANTENNA_B; + new_ant.rx = other_antenna; if (ant->flags & ANTENNA_TX_DIVERSITY) - new_ant.tx = (sample_a > sample_b) ? ANTENNA_A : ANTENNA_B; + new_ant.tx = other_antenna; - rt2x00lib_config_antenna(rt2x00dev, &new_ant); + rt2x00lib_config_antenna(rt2x00dev, new_ant); } static void rt2x00lib_antenna_diversity_eval(struct rt2x00_dev *rt2x00dev) @@ -190,8 +198,8 @@ static void rt2x00lib_antenna_diversity_eval(struct rt2x00_dev *rt2x00dev) * after that update the history with the current value. */ rssi_curr = rt2x00link_antenna_get_link_rssi(rt2x00dev); - rssi_old = rt2x00link_antenna_get_rssi_rx_history(rt2x00dev); - rt2x00link_antenna_update_rssi_rx_history(rt2x00dev, rssi_curr); + rssi_old = rt2x00link_antenna_get_rssi_history(rt2x00dev); + rt2x00link_antenna_update_rssi_history(rt2x00dev, rssi_curr); /* * Legacy driver indicates that we should swap antenna's @@ -213,12 +221,13 @@ static void rt2x00lib_antenna_diversity_eval(struct rt2x00_dev *rt2x00dev) if (ant->flags & ANTENNA_TX_DIVERSITY) new_ant.tx = (new_ant.tx == ANTENNA_A) ? ANTENNA_B : ANTENNA_A; - rt2x00lib_config_antenna(rt2x00dev, &new_ant); + rt2x00lib_config_antenna(rt2x00dev, new_ant); } -static void rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev) +static bool rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev) { struct link_ant *ant = &rt2x00dev->link.ant; + unsigned int flags = ant->flags; /* * Determine if software diversity is enabled for @@ -226,30 +235,38 @@ static void rt2x00lib_antenna_diversity(struct rt2x00_dev *rt2x00dev) * Always perform this check since within the link * tuner interval the configuration might have changed. */ - ant->flags &= ~ANTENNA_RX_DIVERSITY; - ant->flags &= ~ANTENNA_TX_DIVERSITY; + flags &= ~ANTENNA_RX_DIVERSITY; + flags &= ~ANTENNA_TX_DIVERSITY; if (rt2x00dev->default_ant.rx == ANTENNA_SW_DIVERSITY) - ant->flags |= ANTENNA_RX_DIVERSITY; + flags |= ANTENNA_RX_DIVERSITY; if (rt2x00dev->default_ant.tx == ANTENNA_SW_DIVERSITY) - ant->flags |= ANTENNA_TX_DIVERSITY; + flags |= ANTENNA_TX_DIVERSITY; if (!(ant->flags & ANTENNA_RX_DIVERSITY) && !(ant->flags & ANTENNA_TX_DIVERSITY)) { ant->flags = 0; - return; + return true; } + /* Update flags */ + ant->flags = flags; + /* * If we have only sampled the data over the last period * we should now harvest the data. Otherwise just evaluate * the data. The latter should only be performed once * every 2 seconds. */ - if (ant->flags & ANTENNA_MODE_SAMPLE) + if (ant->flags & ANTENNA_MODE_SAMPLE) { rt2x00lib_antenna_diversity_sample(rt2x00dev); - else if (rt2x00dev->link.count & 1) + return true; + } else if (rt2x00dev->link.count & 1) { rt2x00lib_antenna_diversity_eval(rt2x00dev); + return true; + } + + return false; } void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, @@ -260,8 +277,6 @@ void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, struct link_qual *qual = &rt2x00dev->link.qual; struct link_ant *ant = &rt2x00dev->link.ant; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - int avg_rssi = rxdesc->rssi; - int ant_rssi = rxdesc->rssi; /* * Frame was received successfully since non-succesfull @@ -281,16 +296,12 @@ void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, /* * Update global RSSI */ - if (link->avg_rssi) - avg_rssi = MOVING_AVERAGE(link->avg_rssi, rxdesc->rssi, 8); - link->avg_rssi = avg_rssi; + link->avg_rssi = MOVING_AVERAGE(link->avg_rssi, rxdesc->rssi); /* * Update antenna RSSI */ - if (ant->rssi_ant) - ant_rssi = MOVING_AVERAGE(ant->rssi_ant, rxdesc->rssi, 8); - ant->rssi_ant = ant_rssi; + ant->rssi_ant = MOVING_AVERAGE(ant->rssi_ant, rxdesc->rssi); } static void rt2x00link_precalculate_signal(struct rt2x00_dev *rt2x00dev) @@ -351,8 +362,8 @@ void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev) rt2x00link_reset_tuner(rt2x00dev, false); - queue_delayed_work(rt2x00dev->hw->workqueue, - &link->work, LINK_TUNE_INTERVAL); + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->work, LINK_TUNE_INTERVAL); } void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev) @@ -423,10 +434,10 @@ static void rt2x00link_tuner(struct work_struct *work) * collect the RSSI data we could use this. Otherwise we * must fallback to the default RSSI value. */ - if (!link->avg_rssi || !qual->rx_success) + if (!link->avg_rssi.avg || !qual->rx_success) qual->rssi = DEFAULT_RSSI; else - qual->rssi = link->avg_rssi; + qual->rssi = link->avg_rssi.avg; /* * Only perform the link tuning when Link tuning @@ -444,25 +455,22 @@ static void rt2x00link_tuner(struct work_struct *work) /* * Send a signal to the led to update the led signal strength. */ - rt2x00leds_led_quality(rt2x00dev, link->avg_rssi); - - /* - * Evaluate antenna setup, make this the last step since this could - * possibly reset some statistics. - */ - rt2x00lib_antenna_diversity(rt2x00dev); + rt2x00leds_led_quality(rt2x00dev, qual->rssi); /* - * Reset the quality counters which recounted during each period. + * Evaluate antenna setup, make this the last step when + * rt2x00lib_antenna_diversity made changes the quality + * statistics will be reset. */ - rt2x00link_reset_qual(rt2x00dev); + if (rt2x00lib_antenna_diversity(rt2x00dev)) + rt2x00link_reset_qual(rt2x00dev); /* * Increase tuner counter, and reschedule the next link tuner run. */ link->count++; - queue_delayed_work(rt2x00dev->hw->workqueue, - &link->work, LINK_TUNE_INTERVAL); + ieee80211_queue_delayed_work(rt2x00dev->hw, + &link->work, LINK_TUNE_INTERVAL); } void rt2x00link_register(struct rt2x00_dev *rt2x00dev) diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index c4c06b4e1f08..929b85f34f38 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -73,7 +73,8 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, else rts_info->flags &= ~IEEE80211_TX_CTL_NO_ACK; - skb->do_not_encrypt = 1; + /* Disable hardware encryption */ + rts_info->control.hw_key = NULL; /* * RTS/CTS frame should use the length of the frame plus any @@ -273,6 +274,7 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw, spin_lock_init(&intf->lock); spin_lock_init(&intf->seqlock); + mutex_init(&intf->beacon_skb_mutex); intf->beacon = entry; if (conf->type == NL80211_IFTYPE_AP) @@ -337,54 +339,38 @@ int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed) { struct rt2x00_dev *rt2x00dev = hw->priv; struct ieee80211_conf *conf = &hw->conf; - int status; /* - * Mac80211 might be calling this function while we are trying + * mac80211 might be calling this function while we are trying * to remove the device or perhaps suspending it. */ if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return 0; /* - * Only change device state when the radio is enabled. It does not - * matter what parameters we have configured when the radio is disabled - * because we won't be able to send or receive anyway. Also note that - * some configuration parameters (e.g. channel and antenna values) can - * only be set when the radio is enabled. + * Some configuration parameters (e.g. channel and antenna values) can + * only be set when the radio is enabled, but do require the RX to + * be off. */ - if (conf->radio_enabled) { - /* For programming the values, we have to turn RX off */ - rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); + rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); - /* Enable the radio */ - status = rt2x00lib_enable_radio(rt2x00dev); - if (unlikely(status)) - return status; + /* + * When we've just turned on the radio, we want to reprogram + * everything to ensure a consistent state + */ + rt2x00lib_config(rt2x00dev, conf, changed); - /* - * When we've just turned on the radio, we want to reprogram - * everything to ensure a consistent state - */ - rt2x00lib_config(rt2x00dev, conf, changed); + /* + * After the radio has been enabled we need to configure + * the antenna to the default settings. rt2x00lib_config_antenna() + * should determine if any action should be taken based on + * checking if diversity has been enabled or no antenna changes + * have been made since the last configuration change. + */ + rt2x00lib_config_antenna(rt2x00dev, rt2x00dev->default_ant); - /* - * The radio was enabled, configure the antenna to the - * default settings, the link tuner will later start - * continue configuring the antenna based on the software - * diversity. But for non-diversity configurations, we need - * to have configured the correct state now. - */ - if (changed & IEEE80211_CONF_CHANGE_RADIO_ENABLED) - rt2x00lib_config_antenna(rt2x00dev, - &rt2x00dev->default_ant); - - /* Turn RX back on */ - rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); - } else { - /* Disable the radio */ - rt2x00lib_disable_radio(rt2x00dev); - } + /* Turn RX back on */ + rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); return 0; } @@ -393,7 +379,7 @@ EXPORT_SYMBOL_GPL(rt2x00mac_config); void rt2x00mac_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mc_list) + u64 multicast) { struct rt2x00_dev *rt2x00dev = hw->priv; @@ -406,6 +392,7 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw, FIF_FCSFAIL | FIF_PLCPFAIL | FIF_CONTROL | + FIF_PSPOLL | FIF_OTHER_BSS | FIF_PROMISC_IN_BSS; @@ -421,19 +408,42 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw, *total_flags |= FIF_PROMISC_IN_BSS | FIF_OTHER_BSS; /* + * If the device has a single filter for all control frames, + * FIF_CONTROL and FIF_PSPOLL flags imply each other. + * And if the device has more than one filter for control frames + * of different types, but has no a separate filter for PS Poll frames, + * FIF_CONTROL flag implies FIF_PSPOLL. + */ + if (!test_bit(DRIVER_SUPPORT_CONTROL_FILTERS, &rt2x00dev->flags)) { + if (*total_flags & FIF_CONTROL || *total_flags & FIF_PSPOLL) + *total_flags |= FIF_CONTROL | FIF_PSPOLL; + } + if (!test_bit(DRIVER_SUPPORT_CONTROL_FILTER_PSPOLL, &rt2x00dev->flags)) { + if (*total_flags & FIF_CONTROL) + *total_flags |= FIF_PSPOLL; + } + + /* * Check if there is any work left for us. */ if (rt2x00dev->packet_filter == *total_flags) return; rt2x00dev->packet_filter = *total_flags; - if (!test_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags)) - rt2x00dev->ops->lib->config_filter(rt2x00dev, *total_flags); - else - queue_work(rt2x00dev->hw->workqueue, &rt2x00dev->filter_work); + rt2x00dev->ops->lib->config_filter(rt2x00dev, *total_flags); } EXPORT_SYMBOL_GPL(rt2x00mac_configure_filter); +int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + bool set) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + + rt2x00lib_beacondone(rt2x00dev); + return 0; +} +EXPORT_SYMBOL_GPL(rt2x00mac_set_tim); + #ifdef CONFIG_RT2X00_LIB_CRYPTO static void memcpy_tkip(struct rt2x00lib_crypto *crypto, u8 *key, u8 key_len) { @@ -572,11 +582,10 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, { struct rt2x00_dev *rt2x00dev = hw->priv; struct rt2x00_intf *intf = vif_to_intf(vif); - unsigned int delayed = 0; int update_bssid = 0; /* - * Mac80211 might be calling this function while we are trying + * mac80211 might be calling this function while we are trying * to remove the device or perhaps suspending it. */ if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) @@ -626,30 +635,15 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, else rt2x00dev->intf_associated--; - if (!test_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags)) - rt2x00leds_led_assoc(rt2x00dev, - !!rt2x00dev->intf_associated); - else - delayed |= DELAYED_LED_ASSOC; + rt2x00leds_led_assoc(rt2x00dev, !!rt2x00dev->intf_associated); } /* * When the erp information has changed, we should perform * additional configuration steps. For all other changes we are done. */ - if (changes & ~(BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) { - if (!test_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags)) - rt2x00lib_config_erp(rt2x00dev, intf, bss_conf); - else - delayed |= DELAYED_CONFIG_ERP; - } - - spin_lock(&intf->lock); - if (delayed) { - intf->delayed_flags |= delayed; - schedule_work(&rt2x00dev->intf_work); - } - spin_unlock(&intf->lock); + if (changes & ~(BSS_CHANGED_ASSOC | BSS_CHANGED_HT)) + rt2x00lib_config_erp(rt2x00dev, intf, bss_conf); } EXPORT_SYMBOL_GPL(rt2x00mac_bss_info_changed); @@ -687,3 +681,12 @@ int rt2x00mac_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, return 0; } EXPORT_SYMBOL_GPL(rt2x00mac_conf_tx); + +void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw) +{ + struct rt2x00_dev *rt2x00dev = hw->priv; + bool active = !!rt2x00dev->ops->lib->rfkill_poll(rt2x00dev); + + wiphy_rfkill_set_hw_state(hw->wiphy, !active); +} +EXPORT_SYMBOL_GPL(rt2x00mac_rfkill_poll); diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 44e5b3279ca7..06af823efd83 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -324,7 +324,8 @@ static void rt2x00queue_create_tx_descriptor(struct queue_entry *entry, /* * Check if more fragments are pending */ - if (ieee80211_has_morefrags(hdr->frame_control)) { + if (ieee80211_has_morefrags(hdr->frame_control) || + (tx_info->flags & IEEE80211_TX_CTL_MORE_FRAMES)) { __set_bit(ENTRY_TXD_BURST, &txdesc->flags); __set_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags); } @@ -452,9 +453,21 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb) rt2x00crypto_tx_remove_iv(skb, &txdesc); } + /* + * When DMA allocation is required we should guarentee to the + * driver that the DMA is aligned to a 4-byte boundary. + * Aligning the header to this boundary can be done by calling + * rt2x00queue_payload_align with the header length of 0. + * However some drivers require L2 padding to pad the payload + * rather then the header. This could be a requirement for + * PCI and USB devices, while header alignment only is valid + * for PCI devices. + */ if (test_bit(DRIVER_REQUIRE_L2PAD, &queue->rt2x00dev->flags)) rt2x00queue_payload_align(entry->skb, true, txdesc.header_length); + else if (test_bit(DRIVER_REQUIRE_DMA, &queue->rt2x00dev->flags)) + rt2x00queue_payload_align(entry->skb, false, 0); /* * It could be possible that the queue was corrupted and this @@ -490,14 +503,25 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, if (unlikely(!intf->beacon)) return -ENOBUFS; + mutex_lock(&intf->beacon_skb_mutex); + + /* + * Clean up the beacon skb. + */ + rt2x00queue_free_skb(rt2x00dev, intf->beacon->skb); + intf->beacon->skb = NULL; + if (!enable_beacon) { rt2x00dev->ops->lib->kill_tx_queue(rt2x00dev, QID_BEACON); + mutex_unlock(&intf->beacon_skb_mutex); return 0; } intf->beacon->skb = ieee80211_beacon_get(rt2x00dev->hw, vif); - if (!intf->beacon->skb) + if (!intf->beacon->skb) { + mutex_unlock(&intf->beacon_skb_mutex); return -ENOMEM; + } /* * Copy all TX descriptor information into txdesc, @@ -535,6 +559,8 @@ int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev, rt2x00dev->ops->lib->write_beacon(intf->beacon); rt2x00dev->ops->lib->kick_tx_queue(rt2x00dev, QID_BEACON); + mutex_unlock(&intf->beacon_skb_mutex); + return 0; } diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h index b5e06347c8a7..a5591fb2b191 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/rt2x00/rt2x00queue.h @@ -29,7 +29,7 @@ #include <linux/prefetch.h> /** - * DOC: Entrie frame size + * DOC: Entry frame size * * Ralink PCI devices demand the Frame size to be a multiple of 128 bytes, * for USB devices this restriction does not apply, but the value of @@ -45,13 +45,13 @@ /** * DOC: Number of entries per queue * - * Under normal load without fragmentation 12 entries are sufficient + * Under normal load without fragmentation, 12 entries are sufficient * without the queue being filled up to the maximum. When using fragmentation - * and the queue threshold code we need to add some additional margins to + * and the queue threshold code, we need to add some additional margins to * make sure the queue will never (or only under extreme load) fill up * completely. - * Since we don't use preallocated DMA having a large number of queue entries - * will have only minimal impact on the memory requirements for the queue. + * Since we don't use preallocated DMA, having a large number of queue entries + * will have minimal impact on the memory requirements for the queue. */ #define RX_ENTRIES 24 #define TX_ENTRIES 24 @@ -214,6 +214,7 @@ struct rxdone_entry_desc { * * @TXDONE_UNKNOWN: Hardware could not determine success of transmission. * @TXDONE_SUCCESS: Frame was successfully send + * @TXDONE_FALLBACK: Frame was successfully send using a fallback rate. * @TXDONE_FAILURE: Frame was not successfully send * @TXDONE_EXCESSIVE_RETRY: In addition to &TXDONE_FAILURE, the * frame transmission failed due to excessive retries. @@ -221,6 +222,7 @@ struct rxdone_entry_desc { enum txdone_entry_desc_flags { TXDONE_UNKNOWN, TXDONE_SUCCESS, + TXDONE_FALLBACK, TXDONE_FAILURE, TXDONE_EXCESSIVE_RETRY, }; diff --git a/drivers/net/wireless/rt2x00/rt2x00reg.h b/drivers/net/wireless/rt2x00/rt2x00reg.h index 861322d97fce..983e52e127a7 100644 --- a/drivers/net/wireless/rt2x00/rt2x00reg.h +++ b/drivers/net/wireless/rt2x00/rt2x00reg.h @@ -176,8 +176,8 @@ struct rt2x00_field32 { #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. - * These macro's behaves the same as the __ffs() function with + * Macros to find first set bit in a variable. + * These macros behave the same as the __ffs() functions but * the most important difference that this is done during * compile-time rather then run-time. */ diff --git a/drivers/net/wireless/rt2x00/rt2x00rfkill.c b/drivers/net/wireless/rt2x00/rt2x00rfkill.c deleted file mode 100644 index b6d4c6700bf3..000000000000 --- a/drivers/net/wireless/rt2x00/rt2x00rfkill.c +++ /dev/null @@ -1,127 +0,0 @@ -/* - Copyright (C) 2004 - 2009 rt2x00 SourceForge Project - <http://rt2x00.serialmonkey.com> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - Module: rt2x00rfkill - Abstract: rt2x00 rfkill routines. - */ - -#include <linux/kernel.h> -#include <linux/module.h> - -#include "rt2x00.h" -#include "rt2x00lib.h" - -static void rt2x00rfkill_poll(struct input_polled_dev *poll_dev) -{ - struct rt2x00_dev *rt2x00dev = poll_dev->private; - int state, old_state; - - if (!test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state) || - !test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags)) - return; - - /* - * Poll latest state, if the state is different then the previous state, - * we should generate an input event. - */ - state = !!rt2x00dev->ops->lib->rfkill_poll(rt2x00dev); - old_state = !!test_bit(RFKILL_STATE_BLOCKED, &rt2x00dev->rfkill_state); - - if (old_state != state) { - input_report_switch(poll_dev->input, SW_RFKILL_ALL, state); - change_bit(RFKILL_STATE_BLOCKED, &rt2x00dev->rfkill_state); - } -} - -void rt2x00rfkill_register(struct rt2x00_dev *rt2x00dev) -{ - if (!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state) || - test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state)) - return; - - if (input_register_polled_device(rt2x00dev->rfkill_poll_dev)) { - ERROR(rt2x00dev, "Failed to register polled device.\n"); - return; - } - - __set_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state); - - /* - * Force initial poll which will detect the initial device state, - * and correctly sends the signal to the input layer about this - * state. - */ - rt2x00rfkill_poll(rt2x00dev->rfkill_poll_dev); -} - -void rt2x00rfkill_unregister(struct rt2x00_dev *rt2x00dev) -{ - if (!test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state) || - !test_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state)) - return; - - input_unregister_polled_device(rt2x00dev->rfkill_poll_dev); - - __clear_bit(RFKILL_STATE_REGISTERED, &rt2x00dev->rfkill_state); -} - -void rt2x00rfkill_allocate(struct rt2x00_dev *rt2x00dev) -{ - struct input_polled_dev *poll_dev; - - if (test_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state) || - !test_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags)) - return; - - poll_dev = input_allocate_polled_device(); - if (!poll_dev) { - ERROR(rt2x00dev, "Failed to allocate polled device.\n"); - return; - } - - poll_dev->private = rt2x00dev; - poll_dev->poll = rt2x00rfkill_poll; - poll_dev->poll_interval = RFKILL_POLL_INTERVAL; - - poll_dev->input->name = rt2x00dev->ops->name; - poll_dev->input->phys = wiphy_name(rt2x00dev->hw->wiphy); - poll_dev->input->id.bustype = BUS_HOST; - poll_dev->input->id.vendor = 0x1814; - poll_dev->input->id.product = rt2x00dev->chip.rt; - poll_dev->input->id.version = rt2x00dev->chip.rev; - poll_dev->input->dev.parent = wiphy_dev(rt2x00dev->hw->wiphy); - poll_dev->input->evbit[0] = BIT(EV_SW); - poll_dev->input->swbit[0] = BIT(SW_RFKILL_ALL); - - rt2x00dev->rfkill_poll_dev = poll_dev; - - __set_bit(RFKILL_STATE_ALLOCATED, &rt2x00dev->rfkill_state); -} - -void rt2x00rfkill_free(struct rt2x00_dev *rt2x00dev) -{ - if (!__test_and_clear_bit(RFKILL_STATE_ALLOCATED, - &rt2x00dev->rfkill_state)) - return; - - input_free_polled_device(rt2x00dev->rfkill_poll_dev); - rt2x00dev->rfkill_poll_dev = NULL; -} diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 49b29ff90c47..f4b4b86da4da 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -237,7 +237,6 @@ static const struct rt2x00debug rt61pci_rt2x00debug = { }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -#ifdef CONFIG_RT2X00_LIB_RFKILL static int rt61pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; @@ -245,9 +244,6 @@ static int rt61pci_rfkill_poll(struct rt2x00_dev *rt2x00dev) rt2x00pci_register_read(rt2x00dev, MAC_CSR13, ®); return rt2x00_get_field32(reg, MAC_CSR13_BIT5); } -#else -#define rt61pci_rfkill_poll NULL -#endif /* CONFIG_RT2X00_LIB_RFKILL */ #ifdef CONFIG_RT2X00_LIB_LEDS static void rt61pci_brightness_set(struct led_classdev *led_cdev, @@ -534,7 +530,7 @@ static void rt61pci_config_filter(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, !(filter_flags & FIF_PLCPFAIL)); rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, - !(filter_flags & FIF_CONTROL)); + !(filter_flags & (FIF_CONTROL | FIF_PSPOLL))); rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, !(filter_flags & FIF_PROMISC_IN_BSS)); rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, @@ -1859,8 +1855,6 @@ static void rt61pci_write_beacon(struct queue_entry *entry) * otherwise we might be sending out invalid data. */ rt2x00pci_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); - rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); rt2x00pci_register_write(rt2x00dev, TXRX_CSR9, reg); @@ -2316,7 +2310,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) } /* - * Determine number of antenna's. + * Determine number of antennas. */ if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_NUM) == 2) __set_bit(CONFIG_DOUBLE_ANTENNA, &rt2x00dev->flags); @@ -2338,10 +2332,8 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Detect if this device has an hardware controlled radio. */ -#ifdef CONFIG_RT2X00_LIB_RFKILL if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); -#endif /* CONFIG_RT2X00_LIB_RFKILL */ /* * Read frequency offset and RF programming sequence. @@ -2607,6 +2599,11 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) int retval; /* + * Disable power saving. + */ + rt2x00pci_register_write(rt2x00dev, SOFT_RESET_CSR, 0x00000007); + + /* * Allocate eeprom data. */ retval = rt61pci_validate_eeprom(rt2x00dev); @@ -2625,6 +2622,12 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev) return retval; /* + * This device has multiple filters for control frames, + * but has no a separate filter for PS Poll frames. + */ + __set_bit(DRIVER_SUPPORT_CONTROL_FILTERS, &rt2x00dev->flags); + + /* * This device requires firmware and DMA mapped skbs. */ __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); @@ -2722,12 +2725,14 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = { .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, .configure_filter = rt2x00mac_configure_filter, + .set_tim = rt2x00mac_set_tim, .set_key = rt2x00mac_set_key, .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt61pci_conf_tx, .get_tx_stats = rt2x00mac_get_tx_stats, .get_tsf = rt61pci_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, }; static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/rt2x00/rt61pci.h index 6c71f77c8165..93eb699165cc 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.h +++ b/drivers/net/wireless/rt2x00/rt61pci.h @@ -1476,7 +1476,7 @@ struct hw_pairwise_ta_entry { #define RXD_W15_RESERVED FIELD32(0xffffffff) /* - * Macro's for converting txpower from EEPROM to mac80211 value + * Macros for converting txpower from EEPROM to mac80211 value * and from mac80211 value to register value. */ #define MIN_TXPOWER 0 diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index c18848836f2d..90e117263051 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -183,7 +183,6 @@ static const struct rt2x00debug rt73usb_rt2x00debug = { }; #endif /* CONFIG_RT2X00_LIB_DEBUGFS */ -#ifdef CONFIG_RT2X00_LIB_RFKILL static int rt73usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) { u32 reg; @@ -191,9 +190,6 @@ static int rt73usb_rfkill_poll(struct rt2x00_dev *rt2x00dev) rt2x00usb_register_read(rt2x00dev, MAC_CSR13, ®); return rt2x00_get_field32(reg, MAC_CSR13_BIT7); } -#else -#define rt73usb_rfkill_poll NULL -#endif /* CONFIG_RT2X00_LIB_RFKILL */ #ifdef CONFIG_RT2X00_LIB_LEDS static void rt73usb_brightness_set(struct led_classdev *led_cdev, @@ -497,7 +493,7 @@ static void rt73usb_config_filter(struct rt2x00_dev *rt2x00dev, rt2x00_set_field32(®, TXRX_CSR0_DROP_PHYSICAL, !(filter_flags & FIF_PLCPFAIL)); rt2x00_set_field32(®, TXRX_CSR0_DROP_CONTROL, - !(filter_flags & FIF_CONTROL)); + !(filter_flags & (FIF_CONTROL | FIF_PSPOLL))); rt2x00_set_field32(®, TXRX_CSR0_DROP_NOT_TO_ME, !(filter_flags & FIF_PROMISC_IN_BSS)); rt2x00_set_field32(®, TXRX_CSR0_DROP_TO_DS, @@ -1531,8 +1527,6 @@ static void rt73usb_write_beacon(struct queue_entry *entry) * otherwise we might be sending out invalid data. */ rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); - rt2x00_set_field32(®, TXRX_CSR9_TSF_TICKING, 0); - rt2x00_set_field32(®, TXRX_CSR9_TBTT_ENABLE, 0); rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); @@ -1863,10 +1857,8 @@ static int rt73usb_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Detect if this device has an hardware controlled radio. */ -#ifdef CONFIG_RT2X00_LIB_RFKILL if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); -#endif /* CONFIG_RT2X00_LIB_RFKILL */ /* * Read frequency offset. @@ -2150,10 +2142,15 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev) return retval; /* + * This device has multiple filters for control frames, + * but has no a separate filter for PS Poll frames. + */ + __set_bit(DRIVER_SUPPORT_CONTROL_FILTERS, &rt2x00dev->flags); + + /* * This device requires firmware. */ __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); - __set_bit(DRIVER_REQUIRE_SCHEDULED, &rt2x00dev->flags); if (!modparam_nohwcrypt) __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); @@ -2247,12 +2244,14 @@ static const struct ieee80211_ops rt73usb_mac80211_ops = { .remove_interface = rt2x00mac_remove_interface, .config = rt2x00mac_config, .configure_filter = rt2x00mac_configure_filter, + .set_tim = rt2x00mac_set_tim, .set_key = rt2x00mac_set_key, .get_stats = rt2x00mac_get_stats, .bss_info_changed = rt2x00mac_bss_info_changed, .conf_tx = rt73usb_conf_tx, .get_tx_stats = rt2x00mac_get_tx_stats, .get_tsf = rt73usb_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, }; static const struct rt2x00lib_ops rt73usb_rt2x00_ops = { diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/rt2x00/rt73usb.h index c8016f65b4bd..81fe0be51c42 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.h +++ b/drivers/net/wireless/rt2x00/rt73usb.h @@ -809,7 +809,7 @@ struct hw_pairwise_ta_entry { /* * EEPROM antenna. - * ANTENNA_NUM: Number of antenna's. + * ANTENNA_NUM: Number of antennas. * TX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * RX_DEFAULT: Default antenna 0: diversity, 1: A, 2: B. * FRAME_TYPE: 0: DPDT , 1: SPDT , noted this bit is valid for g only. @@ -1058,7 +1058,7 @@ struct hw_pairwise_ta_entry { #define RXD_W5_RESERVED FIELD32(0xffffffff) /* - * Macro's for converting txpower from EEPROM to mac80211 value + * Macros for converting txpower from EEPROM to mac80211 value * and from mac80211 value to register value. */ #define MIN_TXPOWER 0 diff --git a/drivers/net/wireless/rtl818x/Makefile b/drivers/net/wireless/rtl818x/Makefile index 37e3d4db0c40..93cbfbedb46d 100644 --- a/drivers/net/wireless/rtl818x/Makefile +++ b/drivers/net/wireless/rtl818x/Makefile @@ -1,5 +1,5 @@ rtl8180-objs := rtl8180_dev.o rtl8180_rtl8225.o rtl8180_sa2400.o rtl8180_max2820.o rtl8180_grf5101.o -rtl8187-objs := rtl8187_dev.o rtl8187_rtl8225.o rtl8187_leds.o +rtl8187-objs := rtl8187_dev.o rtl8187_rtl8225.o rtl8187_leds.o rtl8187_rfkill.o obj-$(CONFIG_RTL8180) += rtl8180.o obj-$(CONFIG_RTL8187) += rtl8187.o diff --git a/drivers/net/wireless/rtl818x/rtl8180_dev.c b/drivers/net/wireless/rtl818x/rtl8180_dev.c index 7e65d7c31802..16429c49139c 100644 --- a/drivers/net/wireless/rtl818x/rtl8180_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180_dev.c @@ -143,7 +143,8 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; - ieee80211_rx_irqsafe(dev, skb, &rx_status); + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(dev, skb); skb = new_skb; priv->rx_buf[priv->rx_idx] = skb; @@ -280,7 +281,7 @@ static int rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb) (ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10); remainder = (16 * (skb->len + 4)) % ((ieee80211_get_tx_rate(dev, info)->bitrate * 2) / 10); - if (remainder > 0 && remainder <= 6) + if (remainder <= 6) plcp_len |= 1 << 15; } @@ -727,10 +728,16 @@ static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, priv->rf->conf_erp(dev, info); } +static u64 rtl8180_prepare_multicast(struct ieee80211_hw *dev, int mc_count, + struct dev_addr_list *mc_list) +{ + return mc_count; +} + static void rtl8180_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mclist) + u64 multicast) { struct rtl8180_priv *priv = dev->priv; @@ -740,7 +747,7 @@ static void rtl8180_configure_filter(struct ieee80211_hw *dev, priv->rx_conf ^= RTL818X_RX_CONF_CTRL; if (changed_flags & FIF_OTHER_BSS) priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; - if (*total_flags & FIF_ALLMULTI || mc_count > 0) + if (*total_flags & FIF_ALLMULTI || multicast > 0) priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; else priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; @@ -767,6 +774,7 @@ static const struct ieee80211_ops rtl8180_ops = { .remove_interface = rtl8180_remove_interface, .config = rtl8180_config, .bss_info_changed = rtl8180_bss_info_changed, + .prepare_multicast = rtl8180_prepare_multicast, .configure_filter = rtl8180_configure_filter, }; diff --git a/drivers/net/wireless/rtl818x/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187.h index c09bfefc70f3..bf9175a8c1f4 100644 --- a/drivers/net/wireless/rtl818x/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187.h @@ -133,6 +133,7 @@ struct rtl8187_priv { __le16 bits16; __le32 bits32; } *io_dmabuf; + bool rfkill_off; }; void rtl8187_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c index 87a95588a8e3..2017ccc00145 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c @@ -32,6 +32,7 @@ #ifdef CONFIG_RTL8187_LEDS #include "rtl8187_leds.h" #endif +#include "rtl8187_rfkill.h" MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>"); @@ -220,7 +221,7 @@ static void rtl8187_tx_cb(struct urb *urb) * reading a register in the device. We are in interrupt mode * here, thus queue the skb and finish on a work queue. */ skb_queue_tail(&priv->b_tx_status.queue, skb); - queue_delayed_work(hw->workqueue, &priv->work, 0); + ieee80211_queue_delayed_work(hw, &priv->work, 0); } } @@ -380,7 +381,8 @@ static void rtl8187_rx_cb(struct urb *urb) rx_status.flag |= RX_FLAG_TSFT; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; - ieee80211_rx_irqsafe(dev, skb, &rx_status); + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); + ieee80211_rx_irqsafe(dev, skb); skb = dev_alloc_skb(RTL8187_MAX_RX); if (unlikely(!skb)) { @@ -647,10 +649,10 @@ static int rtl8187_init_hw(struct ieee80211_hw *dev) /* setup card */ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); - rtl818x_iowrite8(priv, &priv->map->GPIO, 0); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); - rtl818x_iowrite8(priv, &priv->map->GPIO, 1); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 1); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); @@ -673,11 +675,11 @@ static int rtl8187_init_hw(struct ieee80211_hw *dev) /* host_usb_init */ rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0); - rtl818x_iowrite8(priv, &priv->map->GPIO, 0); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0); reg = rtl818x_ioread8(priv, (u8 *)0xFE53); rtl818x_iowrite8(priv, (u8 *)0xFE53, reg | (1 << 7)); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, (4 << 8)); - rtl818x_iowrite8(priv, &priv->map->GPIO, 0x20); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x20); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0); rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x80); rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x80); @@ -909,12 +911,12 @@ static int rtl8187_start(struct ieee80211_hw *dev) u32 reg; int ret; + mutex_lock(&priv->conf_mutex); + ret = (!priv->is_rtl8187b) ? rtl8187_init_hw(dev) : rtl8187b_init_hw(dev); if (ret) - return ret; - - mutex_lock(&priv->conf_mutex); + goto rtl8187_start_exit; init_usb_anchor(&priv->anchored); priv->dev = dev; @@ -941,8 +943,7 @@ static int rtl8187_start(struct ieee80211_hw *dev) (7 << 21 /* MAX TX DMA */)); rtl8187_init_urbs(dev); rtl8187b_init_status_urb(dev); - mutex_unlock(&priv->conf_mutex); - return 0; + goto rtl8187_start_exit; } rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); @@ -986,9 +987,10 @@ static int rtl8187_start(struct ieee80211_hw *dev) reg |= RTL818X_CMD_RX_ENABLE; rtl818x_iowrite8(priv, &priv->map->CMD, reg); INIT_DELAYED_WORK(&priv->work, rtl8187_work); - mutex_unlock(&priv->conf_mutex); - return 0; +rtl8187_start_exit: + mutex_unlock(&priv->conf_mutex); + return ret; } static void rtl8187_stop(struct ieee80211_hw *dev) @@ -1016,9 +1018,10 @@ static void rtl8187_stop(struct ieee80211_hw *dev) dev_kfree_skb_any(skb); usb_kill_anchored_urbs(&priv->anchored); + mutex_unlock(&priv->conf_mutex); + if (!priv->is_rtl8187b) cancel_delayed_work_sync(&priv->work); - mutex_unlock(&priv->conf_mutex); } static int rtl8187_add_interface(struct ieee80211_hw *dev, @@ -1197,10 +1200,16 @@ static void rtl8187_bss_info_changed(struct ieee80211_hw *dev, info->use_short_preamble); } +static u64 rtl8187_prepare_multicast(struct ieee80211_hw *dev, + int mc_count, struct dev_addr_list *mc_list) +{ + return mc_count; +} + static void rtl8187_configure_filter(struct ieee80211_hw *dev, unsigned int changed_flags, unsigned int *total_flags, - int mc_count, struct dev_addr_list *mclist) + u64 multicast) { struct rtl8187_priv *priv = dev->priv; @@ -1210,7 +1219,7 @@ static void rtl8187_configure_filter(struct ieee80211_hw *dev, priv->rx_conf ^= RTL818X_RX_CONF_CTRL; if (changed_flags & FIF_OTHER_BSS) priv->rx_conf ^= RTL818X_RX_CONF_MONITOR; - if (*total_flags & FIF_ALLMULTI || mc_count > 0) + if (*total_flags & FIF_ALLMULTI || multicast > 0) priv->rx_conf |= RTL818X_RX_CONF_MULTICAST; else priv->rx_conf &= ~RTL818X_RX_CONF_MULTICAST; @@ -1273,8 +1282,10 @@ static const struct ieee80211_ops rtl8187_ops = { .remove_interface = rtl8187_remove_interface, .config = rtl8187_config, .bss_info_changed = rtl8187_bss_info_changed, + .prepare_multicast = rtl8187_prepare_multicast, .configure_filter = rtl8187_configure_filter, - .conf_tx = rtl8187_conf_tx + .conf_tx = rtl8187_conf_tx, + .rfkill_poll = rtl8187_rfkill_poll }; static void rtl8187_eeprom_register_read(struct eeprom_93cx6 *eeprom) @@ -1514,6 +1525,7 @@ static int __devinit rtl8187_probe(struct usb_interface *intf, reg &= 0xFF; rtl8187_leds_init(dev, reg); #endif + rtl8187_rfkill_init(dev); return 0; @@ -1537,6 +1549,7 @@ static void __devexit rtl8187_disconnect(struct usb_interface *intf) #ifdef CONFIG_RTL8187_LEDS rtl8187_leds_exit(dev); #endif + rtl8187_rfkill_exit(dev); ieee80211_unregister_hw(dev); priv = dev->priv; diff --git a/drivers/net/wireless/rtl818x/rtl8187_leds.c b/drivers/net/wireless/rtl818x/rtl8187_leds.c index cf9f899fe0e6..a1c670fc1552 100644 --- a/drivers/net/wireless/rtl818x/rtl8187_leds.c +++ b/drivers/net/wireless/rtl818x/rtl8187_leds.c @@ -42,7 +42,7 @@ static void led_turn_on(struct work_struct *work) mutex_lock(&priv->conf_mutex); switch (led->ledpin) { case LED_PIN_GPIO0: - rtl818x_iowrite8(priv, &priv->map->GPIO, 0x01); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x00); break; case LED_PIN_LED0: @@ -80,7 +80,7 @@ static void led_turn_off(struct work_struct *work) mutex_lock(&priv->conf_mutex); switch (led->ledpin) { case LED_PIN_GPIO0: - rtl818x_iowrite8(priv, &priv->map->GPIO, 0x01); + rtl818x_iowrite8(priv, &priv->map->GPIO0, 0x01); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0x01); break; case LED_PIN_LED0: @@ -108,11 +108,11 @@ static void rtl8187_led_brightness_set(struct led_classdev *led_dev, struct rtl8187_priv *priv = hw->priv; if (brightness == LED_OFF) { - queue_delayed_work(hw->workqueue, &priv->led_off, 0); + ieee80211_queue_delayed_work(hw, &priv->led_off, 0); /* The LED is off for 1/20 sec so that it just blinks. */ - queue_delayed_work(hw->workqueue, &priv->led_on, HZ / 20); + ieee80211_queue_delayed_work(hw, &priv->led_on, HZ / 20); } else - queue_delayed_work(hw->workqueue, &priv->led_on, 0); + ieee80211_queue_delayed_work(hw, &priv->led_on, 0); } static int rtl8187_register_led(struct ieee80211_hw *dev, @@ -193,7 +193,7 @@ void rtl8187_leds_init(struct ieee80211_hw *dev, u16 custid) err = rtl8187_register_led(dev, &priv->led_rx, name, ieee80211_get_rx_led_name(dev), ledpin); if (!err) { - queue_delayed_work(dev->workqueue, &priv->led_on, 0); + ieee80211_queue_delayed_work(dev, &priv->led_on, 0); return; } /* registration of RX LED failed - unregister TX */ @@ -209,7 +209,7 @@ void rtl8187_leds_exit(struct ieee80211_hw *dev) struct rtl8187_priv *priv = dev->priv; /* turn the LED off before exiting */ - queue_delayed_work(dev->workqueue, &priv->led_off, 0); + ieee80211_queue_delayed_work(dev, &priv->led_off, 0); cancel_delayed_work_sync(&priv->led_off); cancel_delayed_work_sync(&priv->led_on); rtl8187_unregister_led(&priv->led_rx); diff --git a/drivers/net/wireless/rtl818x/rtl8187_rfkill.c b/drivers/net/wireless/rtl818x/rtl8187_rfkill.c new file mode 100644 index 000000000000..9fab13e4004e --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187_rfkill.c @@ -0,0 +1,63 @@ +/* + * Linux RFKILL support for RTL8187 + * + * Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@mandriva.com.br> + * + * Based on the RFKILL handling in the r8187 driver, which is: + * Copyright (c) Realtek Semiconductor Corp. All rights reserved. + * + * Thanks to Realtek for their support! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/types.h> +#include <linux/usb.h> +#include <net/mac80211.h> + +#include "rtl8187.h" + +static bool rtl8187_is_radio_enabled(struct rtl8187_priv *priv) +{ + u8 gpio; + + gpio = rtl818x_ioread8(priv, &priv->map->GPIO0); + rtl818x_iowrite8(priv, &priv->map->GPIO0, gpio & ~0x02); + gpio = rtl818x_ioread8(priv, &priv->map->GPIO1); + + return gpio & 0x02; +} + +void rtl8187_rfkill_init(struct ieee80211_hw *hw) +{ + struct rtl8187_priv *priv = hw->priv; + + priv->rfkill_off = rtl8187_is_radio_enabled(priv); + printk(KERN_INFO "rtl8187: wireless switch is %s\n", + priv->rfkill_off ? "on" : "off"); + wiphy_rfkill_set_hw_state(hw->wiphy, !priv->rfkill_off); + wiphy_rfkill_start_polling(hw->wiphy); +} + +void rtl8187_rfkill_poll(struct ieee80211_hw *hw) +{ + bool enabled; + struct rtl8187_priv *priv = hw->priv; + + mutex_lock(&priv->conf_mutex); + enabled = rtl8187_is_radio_enabled(priv); + if (unlikely(enabled != priv->rfkill_off)) { + priv->rfkill_off = enabled; + printk(KERN_INFO "rtl8187: wireless radio switch turned %s\n", + enabled ? "on" : "off"); + wiphy_rfkill_set_hw_state(hw->wiphy, !enabled); + } + mutex_unlock(&priv->conf_mutex); +} + +void rtl8187_rfkill_exit(struct ieee80211_hw *hw) +{ + wiphy_rfkill_stop_polling(hw->wiphy); +} diff --git a/drivers/net/wireless/rtl818x/rtl8187_rfkill.h b/drivers/net/wireless/rtl818x/rtl8187_rfkill.h new file mode 100644 index 000000000000..e12575e96d11 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8187_rfkill.h @@ -0,0 +1,8 @@ +#ifndef RTL8187_RFKILL_H +#define RTL8187_RFKILL_H + +void rtl8187_rfkill_init(struct ieee80211_hw *hw); +void rtl8187_rfkill_poll(struct ieee80211_hw *hw); +void rtl8187_rfkill_exit(struct ieee80211_hw *hw); + +#endif /* RTL8187_RFKILL_H */ diff --git a/drivers/net/wireless/rtl818x/rtl818x.h b/drivers/net/wireless/rtl818x/rtl818x.h index 34a5555cc19c..8522490d2e29 100644 --- a/drivers/net/wireless/rtl818x/rtl818x.h +++ b/drivers/net/wireless/rtl818x/rtl818x.h @@ -138,8 +138,9 @@ struct rtl818x_csr { __le32 RF_PARA; __le32 RF_TIMING; u8 GP_ENABLE; - u8 GPIO; - u8 reserved_12[2]; + u8 GPIO0; + u8 GPIO1; + u8 reserved_12; __le32 HSSI_PARA; u8 reserved_13[4]; u8 TX_AGC_CTL; @@ -194,8 +195,18 @@ struct rtl818x_rf_ops { void (*conf_erp)(struct ieee80211_hw *, struct ieee80211_bss_conf *); }; -/* Tx/Rx flags are common between RTL818X chips */ - +/** + * enum rtl818x_tx_desc_flags - Tx/Rx flags are common between RTL818X chips + * + * @RTL818X_TX_DESC_FLAG_NO_ENC: Disable hardware based encryption. + * @RTL818X_TX_DESC_FLAG_TX_OK: TX frame was ACKed. + * @RTL818X_TX_DESC_FLAG_SPLCP: Use short preamble. + * @RTL818X_TX_DESC_FLAG_MOREFRAG: More fragments follow. + * @RTL818X_TX_DESC_FLAG_CTS: Use CTS-to-self protection. + * @RTL818X_TX_DESC_FLAG_RTS: Use RTS/CTS protection. + * @RTL818X_TX_DESC_FLAG_LS: Last segment of the frame. + * @RTL818X_TX_DESC_FLAG_FS: First segment of the frame. + */ enum rtl818x_tx_desc_flags { RTL818X_TX_DESC_FLAG_NO_ENC = (1 << 15), RTL818X_TX_DESC_FLAG_TX_OK = (1 << 15), diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c index 38366a56b71f..ea6a87c19319 100644 --- a/drivers/net/wireless/strip.c +++ b/drivers/net/wireless/strip.c @@ -1533,7 +1533,7 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb) } /* Encapsulate a datagram and kick it into a TTY queue. */ -static int strip_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t strip_xmit(struct sk_buff *skb, struct net_device *dev) { struct strip *strip_info = netdev_priv(dev); @@ -1550,9 +1550,12 @@ static int strip_xmit(struct sk_buff *skb, struct net_device *dev) if (time_after(jiffies, strip_info->pps_timer + HZ)) { unsigned long t = jiffies - strip_info->pps_timer; - unsigned long rx_pps_count = (strip_info->rx_pps_count * HZ * 8 + t / 2) / t; - unsigned long tx_pps_count = (strip_info->tx_pps_count * HZ * 8 + t / 2) / t; - unsigned long sx_pps_count = (strip_info->sx_pps_count * HZ * 8 + t / 2) / t; + unsigned long rx_pps_count = + DIV_ROUND_CLOSEST(strip_info->rx_pps_count*HZ*8, t); + unsigned long tx_pps_count = + DIV_ROUND_CLOSEST(strip_info->tx_pps_count*HZ*8, t); + unsigned long sx_pps_count = + DIV_ROUND_CLOSEST(strip_info->sx_pps_count*HZ*8, t); strip_info->pps_timer = jiffies; strip_info->rx_pps_count = 0; @@ -1582,7 +1585,7 @@ static int strip_xmit(struct sk_buff *skb, struct net_device *dev) if (skb) dev_kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } /* diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c index ab7fc5c0c8b4..d634b2da3b84 100644 --- a/drivers/net/wireless/wavelan.c +++ b/drivers/net/wireless/wavelan.c @@ -2841,7 +2841,8 @@ static int wv_packet_write(struct net_device * dev, void *buf, short length) * the packet. We also prevent reentrance. Then we call the function * to send the packet. */ -static int wavelan_packet_xmit(struct sk_buff *skb, struct net_device * dev) +static netdev_tx_t wavelan_packet_xmit(struct sk_buff *skb, + struct net_device * dev) { net_local *lp = netdev_priv(dev); unsigned long flags; @@ -2891,7 +2892,7 @@ static int wavelan_packet_xmit(struct sk_buff *skb, struct net_device * dev) #ifdef DEBUG_TX_TRACE printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name); #endif - return 0; + return NETDEV_TX_OK; } /*********************** HARDWARE CONFIGURATION ***********************/ diff --git a/drivers/net/wireless/wavelan.p.h b/drivers/net/wireless/wavelan.p.h index 2daa0210d789..dbe8de6e5f52 100644 --- a/drivers/net/wireless/wavelan.p.h +++ b/drivers/net/wireless/wavelan.p.h @@ -611,7 +611,7 @@ static inline int wv_packet_write(struct net_device *, /* Write a packet to the Tx buffer. */ void *, short); -static int +static netdev_tx_t wavelan_packet_xmit(struct sk_buff *, /* Send a packet. */ struct net_device *); /* -------------------- HARDWARE CONFIGURATION -------------------- */ diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c index c6d300666ad8..431a20ec6db6 100644 --- a/drivers/net/wireless/wavelan_cs.c +++ b/drivers/net/wireless/wavelan_cs.c @@ -3078,7 +3078,7 @@ wv_packet_write(struct net_device * dev, * the packet. We also prevent reentrance. Then, we call the function * to send the packet... */ -static int +static netdev_tx_t wavelan_packet_xmit(struct sk_buff * skb, struct net_device * dev) { @@ -3113,7 +3113,7 @@ wavelan_packet_xmit(struct sk_buff * skb, * able to detect collisions, therefore in theory we don't really * need to pad. Jean II */ if (skb_padto(skb, ETH_ZLEN)) - return 0; + return NETDEV_TX_OK; wv_packet_write(dev, skb->data, skb->len); @@ -3122,7 +3122,7 @@ wavelan_packet_xmit(struct sk_buff * skb, #ifdef DEBUG_TX_TRACE printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name); #endif - return(0); + return NETDEV_TX_OK; } /********************** HARDWARE CONFIGURATION **********************/ diff --git a/drivers/net/wireless/wavelan_cs.p.h b/drivers/net/wireless/wavelan_cs.p.h index 706fd3007d21..81d91531c4f9 100644 --- a/drivers/net/wireless/wavelan_cs.p.h +++ b/drivers/net/wireless/wavelan_cs.p.h @@ -707,7 +707,7 @@ static void wv_packet_write(struct net_device *, /* Write a packet to the Tx buffer */ void *, short); -static int +static netdev_tx_t wavelan_packet_xmit(struct sk_buff *, /* Send a packet */ struct net_device *); /* -------------------- HARDWARE CONFIGURATION -------------------- */ diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig index a82c4cd436d8..7b14d5bc63d6 100644 --- a/drivers/net/wireless/wl12xx/Kconfig +++ b/drivers/net/wireless/wl12xx/Kconfig @@ -1,11 +1,52 @@ -config WL12XX - tristate "TI wl1251/wl1271 support" - depends on MAC80211 && WLAN_80211 && SPI_MASTER && GENERIC_HARDIRQS && EXPERIMENTAL +menuconfig WL12XX + boolean "TI wl12xx driver support" + depends on MAC80211 && WLAN_80211 && EXPERIMENTAL + ---help--- + This will enable TI wl12xx driver support. The drivers make + use of the mac80211 stack. + +config WL1251 + tristate "TI wl1251 support" + depends on WL12XX && GENERIC_HARDIRQS select FW_LOADER select CRC7 ---help--- This module adds support for wireless adapters based on - TI wl1251/wl1271 chipsets. + TI wl1251 chipset. + + If you choose to build a module, it'll be called wl1251. Say + N if unsure. + +config WL1251_SPI + tristate "TI wl1251 SPI support" + depends on WL1251 && SPI_MASTER + ---help--- + This module adds support for the SPI interface of adapters using + TI wl1251 chipset. Select this if your platform is using + the SPI bus. + + If you choose to build a module, it'll be called wl1251_spi. + Say N if unsure. + +config WL1251_SDIO + tristate "TI wl1251 SDIO support" + depends on WL1251 && MMC + ---help--- + This module adds support for the SDIO interface of adapters using + TI wl1251 chipset. Select this if your platform is using + the SDIO bus. + + If you choose to build a module, it'll be called + wl1251_sdio. Say N if unsure. + +config WL1271 + tristate "TI wl1271 support" + depends on WL12XX && SPI_MASTER && GENERIC_HARDIRQS + select FW_LOADER + select CRC7 + ---help--- + This module adds support for wireless adapters based on the + TI wl1271 chipset. - If you choose to build a module, it'll be called wl12xx. Say N if + If you choose to build a module, it'll be called wl1271. Say N if unsure. diff --git a/drivers/net/wireless/wl12xx/Makefile b/drivers/net/wireless/wl12xx/Makefile index d43de27dc54c..62e37ad01cc0 100644 --- a/drivers/net/wireless/wl12xx/Makefile +++ b/drivers/net/wireless/wl12xx/Makefile @@ -1,4 +1,14 @@ -wl12xx-objs = main.o spi.o event.o tx.o rx.o \ - ps.o cmd.o acx.o boot.o init.o wl1251.o \ - debugfs.o -obj-$(CONFIG_WL12XX) += wl12xx.o +wl1251-objs = wl1251_main.o wl1251_event.o \ + wl1251_tx.o wl1251_rx.o wl1251_ps.o wl1251_cmd.o \ + wl1251_acx.o wl1251_boot.o wl1251_init.o \ + wl1251_debugfs.o wl1251_io.o + +obj-$(CONFIG_WL1251) += wl1251.o +obj-$(CONFIG_WL1251_SPI) += wl1251_spi.o +obj-$(CONFIG_WL1251_SDIO) += wl1251_sdio.o + +wl1271-objs = wl1271_main.o wl1271_spi.o wl1271_cmd.o \ + wl1271_event.o wl1271_tx.o wl1271_rx.o \ + wl1271_ps.o wl1271_acx.o wl1271_boot.o \ + wl1271_init.o wl1271_debugfs.o +obj-$(CONFIG_WL1271) += wl1271.o diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c deleted file mode 100644 index 1cfd458ad5ab..000000000000 --- a/drivers/net/wireless/wl12xx/acx.c +++ /dev/null @@ -1,689 +0,0 @@ -#include "acx.h" - -#include <linux/module.h> -#include <linux/crc7.h> -#include <linux/spi/spi.h> - -#include "wl12xx.h" -#include "wl12xx_80211.h" -#include "reg.h" -#include "spi.h" -#include "ps.h" - -int wl12xx_acx_frame_rates(struct wl12xx *wl, u8 ctrl_rate, u8 ctrl_mod, - u8 mgt_rate, u8 mgt_mod) -{ - int ret; - struct acx_fw_gen_frame_rates rates; - - wl12xx_debug(DEBUG_ACX, "acx frame rates"); - - rates.header.id = ACX_FW_GEN_FRAME_RATES; - rates.header.len = sizeof(struct acx_fw_gen_frame_rates) - - sizeof(struct acx_header); - - rates.tx_ctrl_frame_rate = ctrl_rate; - rates.tx_ctrl_frame_mod = ctrl_mod; - rates.tx_mgt_frame_rate = mgt_rate; - rates.tx_mgt_frame_mod = mgt_mod; - - ret = wl12xx_cmd_configure(wl, &rates, sizeof(rates)); - if (ret < 0) { - wl12xx_error("Failed to set FW rates and modulation"); - return ret; - } - - return 0; -} - - -int wl12xx_acx_station_id(struct wl12xx *wl) -{ - int ret, i; - struct dot11_station_id mac; - - wl12xx_debug(DEBUG_ACX, "acx dot11_station_id"); - - mac.header.id = DOT11_STATION_ID; - mac.header.len = sizeof(mac) - sizeof(struct acx_header); - - for (i = 0; i < ETH_ALEN; i++) - mac.mac[i] = wl->mac_addr[ETH_ALEN - 1 - i]; - - ret = wl12xx_cmd_configure(wl, &mac, sizeof(mac)); - if (ret < 0) - return ret; - - return 0; -} - -int wl12xx_acx_default_key(struct wl12xx *wl, u8 key_id) -{ - struct acx_dot11_default_key default_key; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx dot11_default_key (%d)", key_id); - - default_key.header.id = DOT11_DEFAULT_KEY; - default_key.header.len = sizeof(default_key) - - sizeof(struct acx_header); - - default_key.id = key_id; - - ret = wl12xx_cmd_configure(wl, &default_key, sizeof(default_key)); - if (ret < 0) { - wl12xx_error("Couldnt set default key"); - return ret; - } - - wl->default_key = key_id; - - return 0; -} - -int wl12xx_acx_wake_up_conditions(struct wl12xx *wl, u8 listen_interval) -{ - struct acx_wake_up_condition wake_up; - - wl12xx_debug(DEBUG_ACX, "acx wake up conditions"); - - wake_up.header.id = ACX_WAKE_UP_CONDITIONS; - wake_up.header.len = sizeof(wake_up) - sizeof(struct acx_header); - - wake_up.wake_up_event = WAKE_UP_EVENT_DTIM_BITMAP; - wake_up.listen_interval = listen_interval; - - return wl12xx_cmd_configure(wl, &wake_up, sizeof(wake_up)); -} - -int wl12xx_acx_sleep_auth(struct wl12xx *wl, u8 sleep_auth) -{ - int ret; - struct acx_sleep_auth auth; - - wl12xx_debug(DEBUG_ACX, "acx sleep auth"); - - auth.header.id = ACX_SLEEP_AUTH; - auth.header.len = sizeof(auth) - sizeof(struct acx_header); - - auth.sleep_auth = sleep_auth; - - ret = wl12xx_cmd_configure(wl, &auth, sizeof(auth)); - if (ret < 0) - return ret; - - return 0; -} - -int wl12xx_acx_fw_version(struct wl12xx *wl, char *buf, size_t len) -{ - struct wl12xx_command cmd; - struct acx_revision *rev; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx fw rev"); - - memset(&cmd, 0, sizeof(cmd)); - - ret = wl12xx_cmd_interrogate(wl, ACX_FW_REV, sizeof(*rev), &cmd); - if (ret < 0) { - wl12xx_warning("ACX_FW_REV interrogate failed"); - return ret; - } - - rev = (struct acx_revision *) &cmd.parameters; - - /* be careful with the buffer sizes */ - strncpy(buf, rev->fw_version, min(len, sizeof(rev->fw_version))); - - /* - * if the firmware version string is exactly - * sizeof(rev->fw_version) long or fw_len is less than - * sizeof(rev->fw_version) it won't be null terminated - */ - buf[min(len, sizeof(rev->fw_version)) - 1] = '\0'; - - return 0; -} - -int wl12xx_acx_tx_power(struct wl12xx *wl, int power) -{ - struct acx_current_tx_power ie; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx dot11_cur_tx_pwr"); - - if (power < 0 || power > 25) - return -EINVAL; - - memset(&ie, 0, sizeof(ie)); - - ie.header.id = DOT11_CUR_TX_PWR; - ie.header.len = sizeof(ie) - sizeof(struct acx_header); - ie.current_tx_power = power * 10; - - ret = wl12xx_cmd_configure(wl, &ie, sizeof(ie)); - if (ret < 0) { - wl12xx_warning("configure of tx power failed: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_feature_cfg(struct wl12xx *wl) -{ - struct acx_feature_config feature; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx feature cfg"); - - memset(&feature, 0, sizeof(feature)); - - feature.header.id = ACX_FEATURE_CFG; - feature.header.len = sizeof(feature) - sizeof(struct acx_header); - - /* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE are disabled */ - feature.data_flow_options = 0; - feature.options = 0; - - ret = wl12xx_cmd_configure(wl, &feature, sizeof(feature)); - if (ret < 0) - wl12xx_error("Couldnt set HW encryption"); - - return ret; -} - -int wl12xx_acx_mem_map(struct wl12xx *wl, void *mem_map, size_t len) -{ - struct wl12xx_command cmd; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx mem map"); - - ret = wl12xx_cmd_interrogate(wl, ACX_MEM_MAP, len, &cmd); - if (ret < 0) - return ret; - else if (cmd.status != CMD_STATUS_SUCCESS) - return -EIO; - - memcpy(mem_map, &cmd.parameters, len); - - return 0; -} - -int wl12xx_acx_data_path_params(struct wl12xx *wl, - struct acx_data_path_params_resp *data_path) -{ - struct acx_data_path_params params; - struct wl12xx_command cmd; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx data path params"); - - params.rx_packet_ring_chunk_size = DP_RX_PACKET_RING_CHUNK_SIZE; - params.tx_packet_ring_chunk_size = DP_TX_PACKET_RING_CHUNK_SIZE; - - params.rx_packet_ring_chunk_num = DP_RX_PACKET_RING_CHUNK_NUM; - params.tx_packet_ring_chunk_num = DP_TX_PACKET_RING_CHUNK_NUM; - - params.tx_complete_threshold = 1; - - params.tx_complete_ring_depth = FW_TX_CMPLT_BLOCK_SIZE; - - params.tx_complete_timeout = DP_TX_COMPLETE_TIME_OUT; - - params.header.id = ACX_DATA_PATH_PARAMS; - params.header.len = sizeof(params) - sizeof(struct acx_header); - - ret = wl12xx_cmd_configure(wl, ¶ms, sizeof(params)); - if (ret < 0) - return ret; - - - ret = wl12xx_cmd_interrogate(wl, ACX_DATA_PATH_PARAMS, - sizeof(struct acx_data_path_params_resp), - &cmd); - - if (ret < 0) { - wl12xx_warning("failed to read data path parameters: %d", ret); - return ret; - } else if (cmd.status != CMD_STATUS_SUCCESS) { - wl12xx_warning("data path parameter acx status failed"); - return -EIO; - } - - memcpy(data_path, &cmd.parameters, sizeof(*data_path)); - - return 0; -} - -int wl12xx_acx_rx_msdu_life_time(struct wl12xx *wl, u32 life_time) -{ - struct rx_msdu_lifetime msdu_lifetime; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx rx msdu life time"); - - msdu_lifetime.header.id = DOT11_RX_MSDU_LIFE_TIME; - msdu_lifetime.header.len = sizeof(msdu_lifetime) - - sizeof(struct acx_header); - msdu_lifetime.lifetime = life_time; - - ret = wl12xx_cmd_configure(wl, &msdu_lifetime, sizeof(msdu_lifetime)); - if (ret < 0) { - wl12xx_warning("failed to set rx msdu life time: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_rx_config(struct wl12xx *wl, u32 config, u32 filter) -{ - struct acx_rx_config rx_config; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx rx config"); - - rx_config.header.id = ACX_RX_CFG; - rx_config.header.len = sizeof(rx_config) - sizeof(struct acx_header); - rx_config.config_options = config; - rx_config.filter_options = filter; - - ret = wl12xx_cmd_configure(wl, &rx_config, sizeof(rx_config)); - if (ret < 0) { - wl12xx_warning("failed to set rx config: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_pd_threshold(struct wl12xx *wl) -{ - struct acx_packet_detection packet_detection; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx data pd threshold"); - - /* FIXME: threshold value not set */ - packet_detection.header.id = ACX_PD_THRESHOLD; - packet_detection.header.len = sizeof(packet_detection) - - sizeof(struct acx_header); - - ret = wl12xx_cmd_configure(wl, &packet_detection, - sizeof(packet_detection)); - if (ret < 0) { - wl12xx_warning("failed to set pd threshold: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_slot(struct wl12xx *wl, enum acx_slot_type slot_time) -{ - struct acx_slot slot; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx slot"); - - slot.header.id = ACX_SLOT; - slot.header.len = sizeof(slot) - sizeof(struct acx_header); - - slot.wone_index = STATION_WONE_INDEX; - slot.slot_time = slot_time; - - ret = wl12xx_cmd_configure(wl, &slot, sizeof(slot)); - if (ret < 0) { - wl12xx_warning("failed to set slot time: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_group_address_tbl(struct wl12xx *wl) -{ - struct multicast_grp_addr_start multicast; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx group address tbl"); - - /* MAC filtering */ - multicast.header.id = DOT11_GROUP_ADDRESS_TBL; - multicast.header.len = sizeof(multicast) - sizeof(struct acx_header); - - multicast.enabled = 0; - multicast.num_groups = 0; - memset(multicast.mac_table, 0, ADDRESS_GROUP_MAX_LEN); - - ret = wl12xx_cmd_configure(wl, &multicast, sizeof(multicast)); - if (ret < 0) { - wl12xx_warning("failed to set group addr table: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_service_period_timeout(struct wl12xx *wl) -{ - struct acx_rx_timeout rx_timeout; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx service period timeout"); - - /* RX timeout */ - rx_timeout.header.id = ACX_SERVICE_PERIOD_TIMEOUT; - rx_timeout.header.len = sizeof(rx_timeout) - sizeof(struct acx_header); - - rx_timeout.ps_poll_timeout = RX_TIMEOUT_PS_POLL_DEF; - rx_timeout.upsd_timeout = RX_TIMEOUT_UPSD_DEF; - - ret = wl12xx_cmd_configure(wl, &rx_timeout, sizeof(rx_timeout)); - if (ret < 0) { - wl12xx_warning("failed to set service period timeout: %d", - ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_rts_threshold(struct wl12xx *wl, u16 rts_threshold) -{ - struct acx_rts_threshold rts; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx rts threshold"); - - rts.header.id = DOT11_RTS_THRESHOLD; - rts.header.len = sizeof(rts) - sizeof(struct acx_header); - - rts.threshold = rts_threshold; - - ret = wl12xx_cmd_configure(wl, &rts, sizeof(rts)); - if (ret < 0) { - wl12xx_warning("failed to set rts threshold: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_beacon_filter_opt(struct wl12xx *wl) -{ - struct acx_beacon_filter_option beacon_filter; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx beacon filter opt"); - - beacon_filter.header.id = ACX_BEACON_FILTER_OPT; - beacon_filter.header.len = sizeof(beacon_filter) - - sizeof(struct acx_header); - - beacon_filter.enable = 0; - beacon_filter.max_num_beacons = 0; - - ret = wl12xx_cmd_configure(wl, &beacon_filter, sizeof(beacon_filter)); - if (ret < 0) { - wl12xx_warning("failed to set beacon filter opt: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_beacon_filter_table(struct wl12xx *wl) -{ - struct acx_beacon_filter_ie_table ie_table; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx beacon filter table"); - - ie_table.header.id = ACX_BEACON_FILTER_TABLE; - ie_table.header.len = sizeof(ie_table) - sizeof(struct acx_header); - - ie_table.num_ie = 0; - memset(ie_table.table, 0, BEACON_FILTER_TABLE_MAX_SIZE); - - ret = wl12xx_cmd_configure(wl, &ie_table, sizeof(ie_table)); - if (ret < 0) { - wl12xx_warning("failed to set beacon filter table: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_sg_enable(struct wl12xx *wl) -{ - struct acx_bt_wlan_coex pta; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx sg enable"); - - pta.header.id = ACX_SG_ENABLE; - pta.header.len = sizeof(pta) - sizeof(struct acx_header); - - pta.enable = SG_ENABLE; - - ret = wl12xx_cmd_configure(wl, &pta, sizeof(pta)); - if (ret < 0) { - wl12xx_warning("failed to set softgemini enable: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_sg_cfg(struct wl12xx *wl) -{ - struct acx_bt_wlan_coex_param param; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx sg cfg"); - - /* BT-WLAN coext parameters */ - param.header.id = ACX_SG_CFG; - param.header.len = sizeof(param) - sizeof(struct acx_header); - - param.min_rate = RATE_INDEX_24MBPS; - param.bt_hp_max_time = PTA_BT_HP_MAXTIME_DEF; - param.wlan_hp_max_time = PTA_WLAN_HP_MAX_TIME_DEF; - param.sense_disable_timer = PTA_SENSE_DISABLE_TIMER_DEF; - param.rx_time_bt_hp = PTA_PROTECTIVE_RX_TIME_DEF; - param.tx_time_bt_hp = PTA_PROTECTIVE_TX_TIME_DEF; - param.rx_time_bt_hp_fast = PTA_PROTECTIVE_RX_TIME_FAST_DEF; - param.tx_time_bt_hp_fast = PTA_PROTECTIVE_TX_TIME_FAST_DEF; - param.wlan_cycle_fast = PTA_CYCLE_TIME_FAST_DEF; - param.bt_anti_starvation_period = PTA_ANTI_STARVE_PERIOD_DEF; - param.next_bt_lp_packet = PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF; - param.wake_up_beacon = PTA_TIME_BEFORE_BEACON_DEF; - param.hp_dm_max_guard_time = PTA_HPDM_MAX_TIME_DEF; - param.next_wlan_packet = PTA_TIME_OUT_NEXT_WLAN_DEF; - param.antenna_type = PTA_ANTENNA_TYPE_DEF; - param.signal_type = PTA_SIGNALING_TYPE_DEF; - param.afh_leverage_on = PTA_AFH_LEVERAGE_ON_DEF; - param.quiet_cycle_num = PTA_NUMBER_QUIET_CYCLE_DEF; - param.max_cts = PTA_MAX_NUM_CTS_DEF; - param.wlan_packets_num = PTA_NUMBER_OF_WLAN_PACKETS_DEF; - param.bt_packets_num = PTA_NUMBER_OF_BT_PACKETS_DEF; - param.missed_rx_avalanche = PTA_RX_FOR_AVALANCHE_DEF; - param.wlan_elp_hp = PTA_ELP_HP_DEF; - param.bt_anti_starvation_cycles = PTA_ANTI_STARVE_NUM_CYCLE_DEF; - param.ack_mode_dual_ant = PTA_ACK_MODE_DEF; - param.pa_sd_enable = PTA_ALLOW_PA_SD_DEF; - param.pta_auto_mode_enable = PTA_AUTO_MODE_NO_CTS_DEF; - param.bt_hp_respected_num = PTA_BT_HP_RESPECTED_DEF; - - ret = wl12xx_cmd_configure(wl, ¶m, sizeof(param)); - if (ret < 0) { - wl12xx_warning("failed to set sg config: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_cca_threshold(struct wl12xx *wl) -{ - struct acx_energy_detection detection; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx cca threshold"); - - detection.header.id = ACX_CCA_THRESHOLD; - detection.header.len = sizeof(detection) - sizeof(struct acx_header); - - detection.rx_cca_threshold = CCA_THRSH_DISABLE_ENERGY_D; - detection.tx_energy_detection = 0; - - ret = wl12xx_cmd_configure(wl, &detection, sizeof(detection)); - if (ret < 0) { - wl12xx_warning("failed to set cca threshold: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_bcn_dtim_options(struct wl12xx *wl) -{ - struct acx_beacon_broadcast bb; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx bcn dtim options"); - - bb.header.id = ACX_BCN_DTIM_OPTIONS; - bb.header.len = sizeof(bb) - sizeof(struct acx_header); - - bb.beacon_rx_timeout = BCN_RX_TIMEOUT_DEF_VALUE; - bb.broadcast_timeout = BROADCAST_RX_TIMEOUT_DEF_VALUE; - bb.rx_broadcast_in_ps = RX_BROADCAST_IN_PS_DEF_VALUE; - bb.ps_poll_threshold = CONSECUTIVE_PS_POLL_FAILURE_DEF; - - ret = wl12xx_cmd_configure(wl, &bb, sizeof(bb)); - if (ret < 0) { - wl12xx_warning("failed to set rx config: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_aid(struct wl12xx *wl, u16 aid) -{ - struct acx_aid acx_aid; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx aid"); - - acx_aid.header.id = ACX_AID; - acx_aid.header.len = sizeof(acx_aid) - sizeof(struct acx_header); - - acx_aid.aid = aid; - - ret = wl12xx_cmd_configure(wl, &acx_aid, sizeof(acx_aid)); - if (ret < 0) { - wl12xx_warning("failed to set aid: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_event_mbox_mask(struct wl12xx *wl, u32 event_mask) -{ - struct acx_event_mask mask; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx event mbox mask"); - - mask.header.id = ACX_EVENT_MBOX_MASK; - mask.header.len = sizeof(mask) - sizeof(struct acx_header); - - /* high event mask is unused */ - mask.high_event_mask = 0xffffffff; - - mask.event_mask = event_mask; - - ret = wl12xx_cmd_configure(wl, &mask, sizeof(mask)); - if (ret < 0) { - wl12xx_warning("failed to set aid: %d", ret); - return ret; - } - - return 0; -} - -int wl12xx_acx_set_preamble(struct wl12xx *wl, enum acx_preamble_type preamble) -{ - struct acx_preamble ie; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx_set_preamble"); - - memset(&ie, 0, sizeof(ie)); - - ie.header.id = ACX_PREAMBLE_TYPE; - ie.header.len = sizeof(ie) - sizeof(struct acx_header); - ie.preamble = preamble; - ret = wl12xx_cmd_configure(wl, &ie, sizeof(ie)); - if (ret < 0) { - wl12xx_warning("Setting of preamble failed: %d", ret); - return ret; - } - return 0; -} - -int wl12xx_acx_cts_protect(struct wl12xx *wl, - enum acx_ctsprotect_type ctsprotect) -{ - struct acx_ctsprotect ie; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx_set_ctsprotect"); - - memset(&ie, 0, sizeof(ie)); - - ie.header.id = ACX_CTS_PROTECTION; - ie.header.len = sizeof(ie) - sizeof(struct acx_header); - ie.ctsprotect = ctsprotect; - ret = wl12xx_cmd_configure(wl, &ie, sizeof(ie)); - if (ret < 0) { - wl12xx_warning("Setting of ctsprotect failed: %d", ret); - return ret; - } - return 0; -} - -int wl12xx_acx_statistics(struct wl12xx *wl, struct acx_statistics *stats) -{ - struct wl12xx_command *answer; - int ret; - - wl12xx_debug(DEBUG_ACX, "acx statistics"); - - answer = kmalloc(sizeof(*answer), GFP_KERNEL); - if (!answer) { - wl12xx_warning("could not allocate memory for acx statistics"); - ret = -ENOMEM; - goto out; - } - - ret = wl12xx_cmd_interrogate(wl, ACX_STATISTICS, sizeof(*answer), - answer); - if (ret < 0) { - wl12xx_warning("acx statistics failed: %d", ret); - goto out; - } - - memcpy(stats, answer->parameters, sizeof(*stats)); - -out: - kfree(answer); - return ret; -} diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c deleted file mode 100644 index 48ac08c429bd..000000000000 --- a/drivers/net/wireless/wl12xx/boot.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * This file is part of wl12xx - * - * Copyright (C) 2008 Nokia Corporation - * - * Contact: Kalle Valo <kalle.valo@nokia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include <linux/gpio.h> - -#include "reg.h" -#include "boot.h" -#include "spi.h" -#include "event.h" - -static void wl12xx_boot_enable_interrupts(struct wl12xx *wl) -{ - enable_irq(wl->irq); -} - -void wl12xx_boot_target_enable_interrupts(struct wl12xx *wl) -{ - wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); - wl12xx_reg_write32(wl, HI_CFG, HI_CFG_DEF_VAL); -} - -int wl12xx_boot_soft_reset(struct wl12xx *wl) -{ - unsigned long timeout; - u32 boot_data; - - /* perform soft reset */ - wl12xx_reg_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); - - /* SOFT_RESET is self clearing */ - timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); - while (1) { - boot_data = wl12xx_reg_read32(wl, ACX_REG_SLV_SOFT_RESET); - wl12xx_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); - if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) - break; - - if (time_after(jiffies, timeout)) { - /* 1.2 check pWhalBus->uSelfClearTime if the - * timeout was reached */ - wl12xx_error("soft reset timeout"); - return -1; - } - - udelay(SOFT_RESET_STALL_TIME); - } - - /* disable Rx/Tx */ - wl12xx_reg_write32(wl, ENABLE, 0x0); - - /* disable auto calibration on start*/ - wl12xx_reg_write32(wl, SPARE_A2, 0xffff); - - return 0; -} - -int wl12xx_boot_init_seq(struct wl12xx *wl) -{ - u32 scr_pad6, init_data, tmp, elp_cmd, ref_freq; - - /* - * col #1: INTEGER_DIVIDER - * col #2: FRACTIONAL_DIVIDER - * col #3: ATTN_BB - * col #4: ALPHA_BB - * col #5: STOP_TIME_BB - * col #6: BB_PLL_LOOP_FILTER - */ - static const u32 LUT[REF_FREQ_NUM][LUT_PARAM_NUM] = { - - { 83, 87381, 0xB, 5, 0xF00, 3}, /* REF_FREQ_19_2*/ - { 61, 141154, 0xB, 5, 0x1450, 2}, /* REF_FREQ_26_0*/ - { 41, 174763, 0xC, 6, 0x2D00, 1}, /* REF_FREQ_38_4*/ - { 40, 0, 0xC, 6, 0x2EE0, 1}, /* REF_FREQ_40_0*/ - { 47, 162280, 0xC, 6, 0x2760, 1} /* REF_FREQ_33_6 */ - }; - - /* read NVS params */ - scr_pad6 = wl12xx_reg_read32(wl, SCR_PAD6); - wl12xx_debug(DEBUG_BOOT, "scr_pad6 0x%x", scr_pad6); - - /* read ELP_CMD */ - elp_cmd = wl12xx_reg_read32(wl, ELP_CMD); - wl12xx_debug(DEBUG_BOOT, "elp_cmd 0x%x", elp_cmd); - - /* set the BB calibration time to be 300 usec (PLL_CAL_TIME) */ - ref_freq = scr_pad6 & 0x000000FF; - wl12xx_debug(DEBUG_BOOT, "ref_freq 0x%x", ref_freq); - - wl12xx_reg_write32(wl, PLL_CAL_TIME, 0x9); - - /* - * PG 1.2: set the clock buffer time to be 210 usec (CLK_BUF_TIME) - */ - wl12xx_reg_write32(wl, CLK_BUF_TIME, 0x6); - - /* - * set the clock detect feature to work in the restart wu procedure - * (ELP_CFG_MODE[14]) and Select the clock source type - * (ELP_CFG_MODE[13:12]) - */ - tmp = ((scr_pad6 & 0x0000FF00) << 4) | 0x00004000; - wl12xx_reg_write32(wl, ELP_CFG_MODE, tmp); - - /* PG 1.2: enable the BB PLL fix. Enable the PLL_LIMP_CLK_EN_CMD */ - elp_cmd |= 0x00000040; - wl12xx_reg_write32(wl, ELP_CMD, elp_cmd); - - /* PG 1.2: Set the BB PLL stable time to be 1000usec - * (PLL_STABLE_TIME) */ - wl12xx_reg_write32(wl, CFG_PLL_SYNC_CNT, 0x20); - - /* PG 1.2: read clock request time */ - init_data = wl12xx_reg_read32(wl, CLK_REQ_TIME); - - /* - * PG 1.2: set the clock request time to be ref_clk_settling_time - - * 1ms = 4ms - */ - if (init_data > 0x21) - tmp = init_data - 0x21; - else - tmp = 0; - wl12xx_reg_write32(wl, CLK_REQ_TIME, tmp); - - /* set BB PLL configurations in RF AFE */ - wl12xx_reg_write32(wl, 0x003058cc, 0x4B5); - - /* set RF_AFE_REG_5 */ - wl12xx_reg_write32(wl, 0x003058d4, 0x50); - - /* set RF_AFE_CTRL_REG_2 */ - wl12xx_reg_write32(wl, 0x00305948, 0x11c001); - - /* - * change RF PLL and BB PLL divider for VCO clock and adjust VCO - * bais current(RF_AFE_REG_13) - */ - wl12xx_reg_write32(wl, 0x003058f4, 0x1e); - - /* set BB PLL configurations */ - tmp = LUT[ref_freq][LUT_PARAM_INTEGER_DIVIDER] | 0x00017000; - wl12xx_reg_write32(wl, 0x00305840, tmp); - - /* set fractional divider according to Appendix C-BB PLL - * Calculations - */ - tmp = LUT[ref_freq][LUT_PARAM_FRACTIONAL_DIVIDER]; - wl12xx_reg_write32(wl, 0x00305844, tmp); - - /* set the initial data for the sigma delta */ - wl12xx_reg_write32(wl, 0x00305848, 0x3039); - - /* - * set the accumulator attenuation value, calibration loop1 - * (alpha), calibration loop2 (beta), calibration loop3 (gamma) and - * the VCO gain - */ - tmp = (LUT[ref_freq][LUT_PARAM_ATTN_BB] << 16) | - (LUT[ref_freq][LUT_PARAM_ALPHA_BB] << 12) | 0x1; - wl12xx_reg_write32(wl, 0x00305854, tmp); - - /* - * set the calibration stop time after holdoff time expires and set - * settling time HOLD_OFF_TIME_BB - */ - tmp = LUT[ref_freq][LUT_PARAM_STOP_TIME_BB] | 0x000A0000; - wl12xx_reg_write32(wl, 0x00305858, tmp); - - /* - * set BB PLL Loop filter capacitor3- BB_C3[2:0] and set BB PLL - * constant leakage current to linearize PFD to 0uA - - * BB_ILOOPF[7:3] - */ - tmp = LUT[ref_freq][LUT_PARAM_BB_PLL_LOOP_FILTER] | 0x00000030; - wl12xx_reg_write32(wl, 0x003058f8, tmp); - - /* - * set regulator output voltage for n divider to - * 1.35-BB_REFDIV[1:0], set charge pump current- BB_CPGAIN[4:2], - * set BB PLL Loop filter capacitor2- BB_C2[7:5], set gain of BB - * PLL auto-call to normal mode- BB_CALGAIN_3DB[8] - */ - wl12xx_reg_write32(wl, 0x003058f0, 0x29); - - /* enable restart wakeup sequence (ELP_CMD[0]) */ - wl12xx_reg_write32(wl, ELP_CMD, elp_cmd | 0x1); - - /* restart sequence completed */ - udelay(2000); - - return 0; -} - -int wl12xx_boot_run_firmware(struct wl12xx *wl) -{ - int loop, ret; - u32 chip_id, interrupt; - - wl->chip.op_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); - - chip_id = wl12xx_reg_read32(wl, CHIP_ID_B); - - wl12xx_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); - - if (chip_id != wl->chip.id) { - wl12xx_error("chip id doesn't match after firmware boot"); - return -EIO; - } - - /* wait for init to complete */ - loop = 0; - while (loop++ < INIT_LOOP) { - udelay(INIT_LOOP_DELAY); - interrupt = wl12xx_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); - - if (interrupt == 0xffffffff) { - wl12xx_error("error reading hardware complete " - "init indication"); - return -EIO; - } - /* check that ACX_INTR_INIT_COMPLETE is enabled */ - else if (interrupt & wl->chip.intr_init_complete) { - wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_ACK, - wl->chip.intr_init_complete); - break; - } - } - - if (loop >= INIT_LOOP) { - wl12xx_error("timeout waiting for the hardware to " - "complete initialization"); - return -EIO; - } - - /* get hardware config command mail box */ - wl->cmd_box_addr = wl12xx_reg_read32(wl, REG_COMMAND_MAILBOX_PTR); - - /* get hardware config event mail box */ - wl->event_box_addr = wl12xx_reg_read32(wl, REG_EVENT_MAILBOX_PTR); - - /* set the working partition to its "running" mode offset */ - wl12xx_set_partition(wl, - wl->chip.p_table[PART_WORK].mem.start, - wl->chip.p_table[PART_WORK].mem.size, - wl->chip.p_table[PART_WORK].reg.start, - wl->chip.p_table[PART_WORK].reg.size); - - wl12xx_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x", - wl->cmd_box_addr, wl->event_box_addr); - - /* - * in case of full asynchronous mode the firmware event must be - * ready to receive event from the command mailbox - */ - - /* enable gpio interrupts */ - wl12xx_boot_enable_interrupts(wl); - - wl->chip.op_target_enable_interrupts(wl); - - /* unmask all mbox events */ - wl->event_mask = 0xffffffff; - - ret = wl12xx_event_unmask(wl); - if (ret < 0) { - wl12xx_error("EVENT mask setting failed"); - return ret; - } - - wl12xx_event_mbox_config(wl); - - /* firmware startup completed */ - return 0; -} diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c deleted file mode 100644 index f73ab602b7ae..000000000000 --- a/drivers/net/wireless/wl12xx/cmd.c +++ /dev/null @@ -1,353 +0,0 @@ -#include "cmd.h" - -#include <linux/module.h> -#include <linux/crc7.h> -#include <linux/spi/spi.h> - -#include "wl12xx.h" -#include "wl12xx_80211.h" -#include "reg.h" -#include "spi.h" -#include "ps.h" - -int wl12xx_cmd_send(struct wl12xx *wl, u16 type, void *buf, size_t buf_len) -{ - struct wl12xx_command cmd; - unsigned long timeout; - size_t cmd_len; - u32 intr; - int ret = 0; - - memset(&cmd, 0, sizeof(cmd)); - cmd.id = type; - cmd.status = 0; - memcpy(cmd.parameters, buf, buf_len); - cmd_len = ALIGN(buf_len, 4) + CMDMBOX_HEADER_LEN; - - wl12xx_ps_elp_wakeup(wl); - - wl12xx_spi_mem_write(wl, wl->cmd_box_addr, &cmd, cmd_len); - - wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); - - timeout = jiffies + msecs_to_jiffies(WL12XX_COMMAND_TIMEOUT); - - intr = wl12xx_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); - while (!(intr & wl->chip.intr_cmd_complete)) { - if (time_after(jiffies, timeout)) { - wl12xx_error("command complete timeout"); - ret = -ETIMEDOUT; - goto out; - } - - msleep(1); - - intr = wl12xx_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); - } - - wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_ACK, - wl->chip.intr_cmd_complete); - -out: - wl12xx_ps_elp_sleep(wl); - - return ret; -} - -int wl12xx_cmd_test(struct wl12xx *wl, void *buf, size_t buf_len, u8 answer) -{ - int ret; - - wl12xx_debug(DEBUG_CMD, "cmd test"); - - ret = wl12xx_cmd_send(wl, CMD_TEST, buf, buf_len); - if (ret < 0) { - wl12xx_warning("TEST command failed"); - return ret; - } - - if (answer) { - struct wl12xx_command *cmd_answer; - - /* - * The test command got in, we can read the answer. - * The answer would be a wl12xx_command, where the - * parameter array contains the actual answer. - */ - - wl12xx_ps_elp_wakeup(wl); - - wl12xx_spi_mem_read(wl, wl->cmd_box_addr, buf, buf_len); - - wl12xx_ps_elp_sleep(wl); - - cmd_answer = buf; - if (cmd_answer->status != CMD_STATUS_SUCCESS) - wl12xx_error("TEST command answer error: %d", - cmd_answer->status); - } - - return 0; -} - - -int wl12xx_cmd_interrogate(struct wl12xx *wl, u16 ie_id, u16 ie_len, - void *answer) -{ - struct wl12xx_command *cmd; - struct acx_header header; - int ret; - - wl12xx_debug(DEBUG_CMD, "cmd interrogate"); - - header.id = ie_id; - header.len = ie_len - sizeof(header); - - ret = wl12xx_cmd_send(wl, CMD_INTERROGATE, &header, sizeof(header)); - if (ret < 0) { - wl12xx_error("INTERROGATE command failed"); - return ret; - } - - wl12xx_ps_elp_wakeup(wl); - - /* the interrogate command got in, we can read the answer */ - wl12xx_spi_mem_read(wl, wl->cmd_box_addr, answer, - CMDMBOX_HEADER_LEN + ie_len); - - wl12xx_ps_elp_sleep(wl); - - cmd = answer; - if (cmd->status != CMD_STATUS_SUCCESS) - wl12xx_error("INTERROGATE command error: %d", - cmd->status); - - return 0; - -} - -int wl12xx_cmd_configure(struct wl12xx *wl, void *ie, int ie_len) -{ - int ret; - - wl12xx_debug(DEBUG_CMD, "cmd configure"); - - ret = wl12xx_cmd_send(wl, CMD_CONFIGURE, ie, - ie_len); - if (ret < 0) { - wl12xx_warning("CONFIGURE command NOK"); - return ret; - } - - return 0; - -} - -int wl12xx_cmd_vbm(struct wl12xx *wl, u8 identity, - void *bitmap, u16 bitmap_len, u8 bitmap_control) -{ - struct vbm_update_request vbm; - int ret; - - wl12xx_debug(DEBUG_CMD, "cmd vbm"); - - /* Count and period will be filled by the target */ - vbm.tim.bitmap_ctrl = bitmap_control; - if (bitmap_len > PARTIAL_VBM_MAX) { - wl12xx_warning("cmd vbm len is %d B, truncating to %d", - bitmap_len, PARTIAL_VBM_MAX); - bitmap_len = PARTIAL_VBM_MAX; - } - memcpy(vbm.tim.pvb_field, bitmap, bitmap_len); - vbm.tim.identity = identity; - vbm.tim.length = bitmap_len + 3; - - vbm.len = cpu_to_le16(bitmap_len + 5); - - ret = wl12xx_cmd_send(wl, CMD_VBM, &vbm, sizeof(vbm)); - if (ret < 0) { - wl12xx_error("VBM command failed"); - return ret; - } - - return 0; -} - -int wl12xx_cmd_data_path(struct wl12xx *wl, u8 channel, u8 enable) -{ - int ret; - u16 cmd_rx, cmd_tx; - - wl12xx_debug(DEBUG_CMD, "cmd data path"); - - if (enable) { - cmd_rx = CMD_ENABLE_RX; - cmd_tx = CMD_ENABLE_TX; - } else { - cmd_rx = CMD_DISABLE_RX; - cmd_tx = CMD_DISABLE_TX; - } - - ret = wl12xx_cmd_send(wl, cmd_rx, &channel, sizeof(channel)); - if (ret < 0) { - wl12xx_error("rx %s cmd for channel %d failed", - enable ? "start" : "stop", channel); - return ret; - } - - wl12xx_debug(DEBUG_BOOT, "rx %s cmd channel %d", - enable ? "start" : "stop", channel); - - ret = wl12xx_cmd_send(wl, cmd_tx, &channel, sizeof(channel)); - if (ret < 0) { - wl12xx_error("tx %s cmd for channel %d failed", - enable ? "start" : "stop", channel); - return ret; - } - - wl12xx_debug(DEBUG_BOOT, "tx %s cmd channel %d", - enable ? "start" : "stop", channel); - - return 0; -} - -int wl12xx_cmd_join(struct wl12xx *wl, u8 bss_type, u8 dtim_interval, - u16 beacon_interval, u8 wait) -{ - unsigned long timeout; - struct cmd_join join = {}; - int ret, i; - u8 *bssid; - - /* FIXME: this should be in main.c */ - ret = wl12xx_acx_frame_rates(wl, DEFAULT_HW_GEN_TX_RATE, - DEFAULT_HW_GEN_MODULATION_TYPE, - wl->tx_mgmt_frm_rate, - wl->tx_mgmt_frm_mod); - if (ret < 0) - return ret; - - wl12xx_debug(DEBUG_CMD, "cmd join"); - - /* Reverse order BSSID */ - bssid = (u8 *)&join.bssid_lsb; - for (i = 0; i < ETH_ALEN; i++) - bssid[i] = wl->bssid[ETH_ALEN - i - 1]; - - join.rx_config_options = wl->rx_config; - join.rx_filter_options = wl->rx_filter; - - join.basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS | - RATE_MASK_5_5MBPS | RATE_MASK_11MBPS; - - join.beacon_interval = beacon_interval; - join.dtim_interval = dtim_interval; - join.bss_type = bss_type; - join.channel = wl->channel; - join.ctrl = JOIN_CMD_CTRL_TX_FLUSH; - - ret = wl12xx_cmd_send(wl, CMD_START_JOIN, &join, sizeof(join)); - if (ret < 0) { - wl12xx_error("failed to initiate cmd join"); - return ret; - } - - timeout = msecs_to_jiffies(JOIN_TIMEOUT); - - /* - * ugly hack: we should wait for JOIN_EVENT_COMPLETE_ID but to - * simplify locking we just sleep instead, for now - */ - if (wait) - msleep(10); - - return 0; -} - -int wl12xx_cmd_ps_mode(struct wl12xx *wl, u8 ps_mode) -{ - int ret; - struct acx_ps_params ps_params; - - /* FIXME: this should be in ps.c */ - ret = wl12xx_acx_wake_up_conditions(wl, wl->listen_int); - if (ret < 0) { - wl12xx_error("Couldnt set wake up conditions"); - return ret; - } - - wl12xx_debug(DEBUG_CMD, "cmd set ps mode"); - - ps_params.ps_mode = ps_mode; - ps_params.send_null_data = 1; - ps_params.retries = 5; - ps_params.hang_over_period = 128; - ps_params.null_data_rate = 1; /* 1 Mbps */ - - ret = wl12xx_cmd_send(wl, CMD_SET_PS_MODE, &ps_params, - sizeof(ps_params)); - if (ret < 0) { - wl12xx_error("cmd set_ps_mode failed"); - return ret; - } - - return 0; -} - -int wl12xx_cmd_read_memory(struct wl12xx *wl, u32 addr, u32 len, void *answer) -{ - struct cmd_read_write_memory mem_cmd, *mem_answer; - struct wl12xx_command cmd; - int ret; - - wl12xx_debug(DEBUG_CMD, "cmd read memory"); - - memset(&mem_cmd, 0, sizeof(mem_cmd)); - mem_cmd.addr = addr; - mem_cmd.size = len; - - ret = wl12xx_cmd_send(wl, CMD_READ_MEMORY, &mem_cmd, sizeof(mem_cmd)); - if (ret < 0) { - wl12xx_error("read memory command failed: %d", ret); - return ret; - } - - /* the read command got in, we can now read the answer */ - wl12xx_spi_mem_read(wl, wl->cmd_box_addr, &cmd, - CMDMBOX_HEADER_LEN + sizeof(mem_cmd)); - - if (cmd.status != CMD_STATUS_SUCCESS) - wl12xx_error("error in read command result: %d", cmd.status); - - mem_answer = (struct cmd_read_write_memory *) cmd.parameters; - memcpy(answer, mem_answer->value, len); - - return 0; -} - -int wl12xx_cmd_template_set(struct wl12xx *wl, u16 cmd_id, - void *buf, size_t buf_len) -{ - struct wl12xx_cmd_packet_template template; - int ret; - - wl12xx_debug(DEBUG_CMD, "cmd template %d", cmd_id); - - memset(&template, 0, sizeof(template)); - - WARN_ON(buf_len > WL12XX_MAX_TEMPLATE_SIZE); - buf_len = min_t(size_t, buf_len, WL12XX_MAX_TEMPLATE_SIZE); - template.size = cpu_to_le16(buf_len); - - if (buf) - memcpy(template.template, buf, buf_len); - - ret = wl12xx_cmd_send(wl, cmd_id, &template, - sizeof(template.size) + buf_len); - if (ret < 0) { - wl12xx_warning("cmd set_template failed: %d", ret); - return ret; - } - - return 0; -} diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c deleted file mode 100644 index 2a573a6010bd..000000000000 --- a/drivers/net/wireless/wl12xx/init.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * This file is part of wl12xx - * - * Copyright (C) 2009 Nokia Corporation - * - * Contact: Kalle Valo <kalle.valo@nokia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include <linux/kernel.h> -#include <linux/module.h> - -#include "init.h" -#include "wl12xx_80211.h" -#include "acx.h" -#include "cmd.h" - -int wl12xx_hw_init_hwenc_config(struct wl12xx *wl) -{ - int ret; - - ret = wl12xx_acx_feature_cfg(wl); - if (ret < 0) { - wl12xx_warning("couldn't set feature config"); - return ret; - } - - ret = wl12xx_acx_default_key(wl, wl->default_key); - if (ret < 0) { - wl12xx_warning("couldn't set default key"); - return ret; - } - - return 0; -} - -int wl12xx_hw_init_templates_config(struct wl12xx *wl) -{ - int ret; - u8 partial_vbm[PARTIAL_VBM_MAX]; - - /* send empty templates for fw memory reservation */ - ret = wl12xx_cmd_template_set(wl, CMD_PROBE_REQ, NULL, - sizeof(struct wl12xx_probe_req_template)); - if (ret < 0) - return ret; - - ret = wl12xx_cmd_template_set(wl, CMD_NULL_DATA, NULL, - sizeof(struct wl12xx_null_data_template)); - if (ret < 0) - return ret; - - ret = wl12xx_cmd_template_set(wl, CMD_PS_POLL, NULL, - sizeof(struct wl12xx_ps_poll_template)); - if (ret < 0) - return ret; - - ret = wl12xx_cmd_template_set(wl, CMD_QOS_NULL_DATA, NULL, - sizeof - (struct wl12xx_qos_null_data_template)); - if (ret < 0) - return ret; - - ret = wl12xx_cmd_template_set(wl, CMD_PROBE_RESP, NULL, - sizeof - (struct wl12xx_probe_resp_template)); - if (ret < 0) - return ret; - - ret = wl12xx_cmd_template_set(wl, CMD_BEACON, NULL, - sizeof - (struct wl12xx_beacon_template)); - if (ret < 0) - return ret; - - /* tim templates, first reserve space then allocate an empty one */ - memset(partial_vbm, 0, PARTIAL_VBM_MAX); - ret = wl12xx_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, PARTIAL_VBM_MAX, 0); - if (ret < 0) - return ret; - - ret = wl12xx_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, 1, 0); - if (ret < 0) - return ret; - - return 0; -} - -int wl12xx_hw_init_rx_config(struct wl12xx *wl, u32 config, u32 filter) -{ - int ret; - - ret = wl12xx_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF); - if (ret < 0) - return ret; - - ret = wl12xx_acx_rx_config(wl, config, filter); - if (ret < 0) - return ret; - - return 0; -} - -int wl12xx_hw_init_phy_config(struct wl12xx *wl) -{ - int ret; - - ret = wl12xx_acx_pd_threshold(wl); - if (ret < 0) - return ret; - - ret = wl12xx_acx_slot(wl, DEFAULT_SLOT_TIME); - if (ret < 0) - return ret; - - ret = wl12xx_acx_group_address_tbl(wl); - if (ret < 0) - return ret; - - ret = wl12xx_acx_service_period_timeout(wl); - if (ret < 0) - return ret; - - ret = wl12xx_acx_rts_threshold(wl, RTS_THRESHOLD_DEF); - if (ret < 0) - return ret; - - return 0; -} - -int wl12xx_hw_init_beacon_filter(struct wl12xx *wl) -{ - int ret; - - ret = wl12xx_acx_beacon_filter_opt(wl); - if (ret < 0) - return ret; - - ret = wl12xx_acx_beacon_filter_table(wl); - if (ret < 0) - return ret; - - return 0; -} - -int wl12xx_hw_init_pta(struct wl12xx *wl) -{ - int ret; - - ret = wl12xx_acx_sg_enable(wl); - if (ret < 0) - return ret; - - ret = wl12xx_acx_sg_cfg(wl); - if (ret < 0) - return ret; - - return 0; -} - -int wl12xx_hw_init_energy_detection(struct wl12xx *wl) -{ - int ret; - - ret = wl12xx_acx_cca_threshold(wl); - if (ret < 0) - return ret; - - return 0; -} - -int wl12xx_hw_init_beacon_broadcast(struct wl12xx *wl) -{ - int ret; - - ret = wl12xx_acx_bcn_dtim_options(wl); - if (ret < 0) - return ret; - - return 0; -} - -int wl12xx_hw_init_power_auth(struct wl12xx *wl) -{ - return wl12xx_acx_sleep_auth(wl, WL12XX_PSM_CAM); -} diff --git a/drivers/net/wireless/wl12xx/wl1251.c b/drivers/net/wireless/wl12xx/wl1251.c deleted file mode 100644 index ce1561a41fa4..000000000000 --- a/drivers/net/wireless/wl12xx/wl1251.c +++ /dev/null @@ -1,709 +0,0 @@ -/* - * This file is part of wl12xx - * - * Copyright (C) 2008-2009 Nokia Corporation - * - * Contact: Kalle Valo <kalle.valo@nokia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 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 St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include <linux/kernel.h> -#include <linux/module.h> - -#include "wl1251.h" -#include "reg.h" -#include "spi.h" -#include "boot.h" -#include "event.h" -#include "acx.h" -#include "tx.h" -#include "rx.h" -#include "ps.h" -#include "init.h" - -static struct wl12xx_partition_set wl1251_part_table[PART_TABLE_LEN] = { - [PART_DOWN] = { - .mem = { - .start = 0x00000000, - .size = 0x00016800 - }, - .reg = { - .start = REGISTERS_BASE, - .size = REGISTERS_DOWN_SIZE - }, - }, - - [PART_WORK] = { - .mem = { - .start = 0x00028000, - .size = 0x00014000 - }, - .reg = { - .start = REGISTERS_BASE, - .size = REGISTERS_WORK_SIZE - }, - }, - - /* WL1251 doesn't use the DRPW partition, so we don't set it here */ -}; - -static enum wl12xx_acx_int_reg wl1251_acx_reg_table[ACX_REG_TABLE_LEN] = { - [ACX_REG_INTERRUPT_TRIG] = (REGISTERS_BASE + 0x0474), - [ACX_REG_INTERRUPT_TRIG_H] = (REGISTERS_BASE + 0x0478), - [ACX_REG_INTERRUPT_MASK] = (REGISTERS_BASE + 0x0494), - [ACX_REG_HINT_MASK_SET] = (REGISTERS_BASE + 0x0498), - [ACX_REG_HINT_MASK_CLR] = (REGISTERS_BASE + 0x049C), - [ACX_REG_INTERRUPT_NO_CLEAR] = (REGISTERS_BASE + 0x04B0), - [ACX_REG_INTERRUPT_CLEAR] = (REGISTERS_BASE + 0x04A4), - [ACX_REG_INTERRUPT_ACK] = (REGISTERS_BASE + 0x04A8), - [ACX_REG_SLV_SOFT_RESET] = (REGISTERS_BASE + 0x0000), - [ACX_REG_EE_START] = (REGISTERS_BASE + 0x080C), - [ACX_REG_ECPU_CONTROL] = (REGISTERS_BASE + 0x0804) -}; - -static int wl1251_upload_firmware(struct wl12xx *wl) -{ - struct wl12xx_partition_set *p_table = wl->chip.p_table; - int addr, chunk_num, partition_limit; - size_t fw_data_len; - u8 *p; - - /* whal_FwCtrl_LoadFwImageSm() */ - - wl12xx_debug(DEBUG_BOOT, "chip id before fw upload: 0x%x", - wl12xx_reg_read32(wl, CHIP_ID_B)); - - /* 10.0 check firmware length and set partition */ - fw_data_len = (wl->fw[4] << 24) | (wl->fw[5] << 16) | - (wl->fw[6] << 8) | (wl->fw[7]); - - wl12xx_debug(DEBUG_BOOT, "fw_data_len %zu chunk_size %d", fw_data_len, - CHUNK_SIZE); - - if ((fw_data_len % 4) != 0) { - wl12xx_error("firmware length not multiple of four"); - return -EIO; - } - - wl12xx_set_partition(wl, - p_table[PART_DOWN].mem.start, - p_table[PART_DOWN].mem.size, - p_table[PART_DOWN].reg.start, - p_table[PART_DOWN].reg.size); - - /* 10.1 set partition limit and chunk num */ - chunk_num = 0; - partition_limit = p_table[PART_DOWN].mem.size; - - while (chunk_num < fw_data_len / CHUNK_SIZE) { - /* 10.2 update partition, if needed */ - addr = p_table[PART_DOWN].mem.start + - (chunk_num + 2) * CHUNK_SIZE; - if (addr > partition_limit) { - addr = p_table[PART_DOWN].mem.start + - chunk_num * CHUNK_SIZE; - partition_limit = chunk_num * CHUNK_SIZE + - p_table[PART_DOWN].mem.size; - wl12xx_set_partition(wl, - addr, - p_table[PART_DOWN].mem.size, - p_table[PART_DOWN].reg.start, - p_table[PART_DOWN].reg.size); - } - - /* 10.3 upload the chunk */ - addr = p_table[PART_DOWN].mem.start + chunk_num * CHUNK_SIZE; - p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; - wl12xx_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", - p, addr); - wl12xx_spi_mem_write(wl, addr, p, CHUNK_SIZE); - - chunk_num++; - } - - /* 10.4 upload the last chunk */ - addr = p_table[PART_DOWN].mem.start + chunk_num * CHUNK_SIZE; - p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; - wl12xx_debug(DEBUG_BOOT, "uploading fw last chunk (%zu B) 0x%p to 0x%x", - fw_data_len % CHUNK_SIZE, p, addr); - wl12xx_spi_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE); - - return 0; -} - -static int wl1251_upload_nvs(struct wl12xx *wl) -{ - size_t nvs_len, nvs_bytes_written, burst_len; - int nvs_start, i; - u32 dest_addr, val; - u8 *nvs_ptr, *nvs; - - nvs = wl->nvs; - if (nvs == NULL) - return -ENODEV; - - nvs_ptr = nvs; - - nvs_len = wl->nvs_len; - nvs_start = wl->fw_len; - - /* - * Layout before the actual NVS tables: - * 1 byte : burst length. - * 2 bytes: destination address. - * n bytes: data to burst copy. - * - * This is ended by a 0 length, then the NVS tables. - */ - - while (nvs_ptr[0]) { - burst_len = nvs_ptr[0]; - dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8)); - - /* We move our pointer to the data */ - nvs_ptr += 3; - - for (i = 0; i < burst_len; i++) { - val = (nvs_ptr[0] | (nvs_ptr[1] << 8) - | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); - - wl12xx_debug(DEBUG_BOOT, - "nvs burst write 0x%x: 0x%x", - dest_addr, val); - wl12xx_mem_write32(wl, dest_addr, val); - - nvs_ptr += 4; - dest_addr += 4; - } - } - - /* - * We've reached the first zero length, the first NVS table - * is 7 bytes further. - */ - nvs_ptr += 7; - nvs_len -= nvs_ptr - nvs; - nvs_len = ALIGN(nvs_len, 4); - - /* Now we must set the partition correctly */ - wl12xx_set_partition(wl, nvs_start, - wl->chip.p_table[PART_DOWN].mem.size, - wl->chip.p_table[PART_DOWN].reg.start, - wl->chip.p_table[PART_DOWN].reg.size); - - /* And finally we upload the NVS tables */ - nvs_bytes_written = 0; - while (nvs_bytes_written < nvs_len) { - val = (nvs_ptr[0] | (nvs_ptr[1] << 8) - | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); - - val = cpu_to_le32(val); - - wl12xx_debug(DEBUG_BOOT, - "nvs write table 0x%x: 0x%x", - nvs_start, val); - wl12xx_mem_write32(wl, nvs_start, val); - - nvs_ptr += 4; - nvs_bytes_written += 4; - nvs_start += 4; - } - - return 0; -} - -static int wl1251_boot(struct wl12xx *wl) -{ - int ret = 0, minor_minor_e2_ver; - u32 tmp, boot_data; - - ret = wl12xx_boot_soft_reset(wl); - if (ret < 0) - goto out; - - /* 2. start processing NVS file */ - ret = wl->chip.op_upload_nvs(wl); - if (ret < 0) - goto out; - - /* write firmware's last address (ie. it's length) to - * ACX_EEPROMLESS_IND_REG */ - wl12xx_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len); - - /* 6. read the EEPROM parameters */ - tmp = wl12xx_reg_read32(wl, SCR_PAD2); - - /* 7. read bootdata */ - wl->boot_attr.radio_type = (tmp & 0x0000FF00) >> 8; - wl->boot_attr.major = (tmp & 0x00FF0000) >> 16; - tmp = wl12xx_reg_read32(wl, SCR_PAD3); - - /* 8. check bootdata and call restart sequence */ - wl->boot_attr.minor = (tmp & 0x00FF0000) >> 16; - minor_minor_e2_ver = (tmp & 0xFF000000) >> 24; - - wl12xx_debug(DEBUG_BOOT, "radioType 0x%x majorE2Ver 0x%x " - "minorE2Ver 0x%x minor_minor_e2_ver 0x%x", - wl->boot_attr.radio_type, wl->boot_attr.major, - wl->boot_attr.minor, minor_minor_e2_ver); - - ret = wl12xx_boot_init_seq(wl); - if (ret < 0) - goto out; - - /* 9. NVS processing done */ - boot_data = wl12xx_reg_read32(wl, ACX_REG_ECPU_CONTROL); - - wl12xx_debug(DEBUG_BOOT, "halt boot_data 0x%x", boot_data); - - /* 10. check that ECPU_CONTROL_HALT bits are set in - * pWhalBus->uBootData and start uploading firmware - */ - if ((boot_data & ECPU_CONTROL_HALT) == 0) { - wl12xx_error("boot failed, ECPU_CONTROL_HALT not set"); - ret = -EIO; - goto out; - } - - ret = wl->chip.op_upload_fw(wl); - if (ret < 0) - goto out; - - /* 10.5 start firmware */ - ret = wl12xx_boot_run_firmware(wl); - if (ret < 0) - goto out; - - /* Get and save the firmware version */ - wl12xx_acx_fw_version(wl, wl->chip.fw_ver, sizeof(wl->chip.fw_ver)); - -out: - return ret; -} - -static int wl1251_mem_cfg(struct wl12xx *wl) -{ - struct wl1251_acx_config_memory mem_conf; - int ret, i; - - wl12xx_debug(DEBUG_ACX, "wl1251 mem cfg"); - - /* memory config */ - mem_conf.mem_config.num_stations = cpu_to_le16(DEFAULT_NUM_STATIONS); - mem_conf.mem_config.rx_mem_block_num = 35; - mem_conf.mem_config.tx_min_mem_block_num = 64; - mem_conf.mem_config.num_tx_queues = MAX_TX_QUEUES; - mem_conf.mem_config.host_if_options = HOSTIF_PKT_RING; - mem_conf.mem_config.num_ssid_profiles = 1; - mem_conf.mem_config.debug_buffer_size = - cpu_to_le16(TRACE_BUFFER_MAX_SIZE); - - /* RX queue config */ - mem_conf.rx_queue_config.dma_address = 0; - mem_conf.rx_queue_config.num_descs = ACX_RX_DESC_DEF; - mem_conf.rx_queue_config.priority = DEFAULT_RXQ_PRIORITY; - mem_conf.rx_queue_config.type = DEFAULT_RXQ_TYPE; - - /* TX queue config */ - for (i = 0; i < MAX_TX_QUEUES; i++) { - mem_conf.tx_queue_config[i].num_descs = ACX_TX_DESC_DEF; - mem_conf.tx_queue_config[i].attributes = i; - } - - mem_conf.header.id = ACX_MEM_CFG; - mem_conf.header.len = sizeof(struct wl1251_acx_config_memory) - - sizeof(struct acx_header); - mem_conf.header.len -= - (MAX_TX_QUEUE_CONFIGS - mem_conf.mem_config.num_tx_queues) * - sizeof(struct wl1251_acx_tx_queue_config); - - ret = wl12xx_cmd_configure(wl, &mem_conf, - sizeof(struct wl1251_acx_config_memory)); - if (ret < 0) - wl12xx_warning("wl1251 mem config failed: %d", ret); - - return ret; -} - -static int wl1251_hw_init_mem_config(struct wl12xx *wl) -{ - int ret; - - ret = wl1251_mem_cfg(wl); - if (ret < 0) - return ret; - - wl->target_mem_map = kzalloc(sizeof(struct wl1251_acx_mem_map), - GFP_KERNEL); - if (!wl->target_mem_map) { - wl12xx_error("couldn't allocate target memory map"); - return -ENOMEM; - } - - /* we now ask for the firmware built memory map */ - ret = wl12xx_acx_mem_map(wl, wl->target_mem_map, - sizeof(struct wl1251_acx_mem_map)); - if (ret < 0) { - wl12xx_error("couldn't retrieve firmware memory map"); - kfree(wl->target_mem_map); - wl->target_mem_map = NULL; - return ret; - } - - return 0; -} - -static void wl1251_set_ecpu_ctrl(struct wl12xx *wl, u32 flag) -{ - u32 cpu_ctrl; - - /* 10.5.0 run the firmware (I) */ - cpu_ctrl = wl12xx_reg_read32(wl, ACX_REG_ECPU_CONTROL); - - /* 10.5.1 run the firmware (II) */ - cpu_ctrl &= ~flag; - wl12xx_reg_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); -} - -static void wl1251_target_enable_interrupts(struct wl12xx *wl) -{ - /* Enable target's interrupts */ - wl->intr_mask = WL1251_ACX_INTR_RX0_DATA | - WL1251_ACX_INTR_RX1_DATA | - WL1251_ACX_INTR_TX_RESULT | - WL1251_ACX_INTR_EVENT_A | - WL1251_ACX_INTR_EVENT_B | - WL1251_ACX_INTR_INIT_COMPLETE; - wl12xx_boot_target_enable_interrupts(wl); -} - -static void wl1251_irq_work(struct work_struct *work) -{ - u32 intr; - struct wl12xx *wl = - container_of(work, struct wl12xx, irq_work); - - mutex_lock(&wl->mutex); - - wl12xx_debug(DEBUG_IRQ, "IRQ work"); - - if (wl->state == WL12XX_STATE_OFF) - goto out; - - wl12xx_ps_elp_wakeup(wl); - - wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1251_ACX_INTR_ALL); - - intr = wl12xx_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); - wl12xx_debug(DEBUG_IRQ, "intr: 0x%x", intr); - - if (wl->data_path) { - wl12xx_spi_mem_read(wl, wl->data_path->rx_control_addr, - &wl->rx_counter, sizeof(u32)); - - /* We handle a frmware bug here */ - switch ((wl->rx_counter - wl->rx_handled) & 0xf) { - case 0: - wl12xx_debug(DEBUG_IRQ, "RX: FW and host in sync"); - intr &= ~WL1251_ACX_INTR_RX0_DATA; - intr &= ~WL1251_ACX_INTR_RX1_DATA; - break; - case 1: - wl12xx_debug(DEBUG_IRQ, "RX: FW +1"); - intr |= WL1251_ACX_INTR_RX0_DATA; - intr &= ~WL1251_ACX_INTR_RX1_DATA; - break; - case 2: - wl12xx_debug(DEBUG_IRQ, "RX: FW +2"); - intr |= WL1251_ACX_INTR_RX0_DATA; - intr |= WL1251_ACX_INTR_RX1_DATA; - break; - default: - wl12xx_warning("RX: FW and host out of sync: %d", - wl->rx_counter - wl->rx_handled); - break; - } - - wl->rx_handled = wl->rx_counter; - - - wl12xx_debug(DEBUG_IRQ, "RX counter: %d", wl->rx_counter); - } - - intr &= wl->intr_mask; - - if (intr == 0) { - wl12xx_debug(DEBUG_IRQ, "INTR is 0"); - wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_MASK, - ~(wl->intr_mask)); - - goto out_sleep; - } - - if (intr & WL1251_ACX_INTR_RX0_DATA) { - wl12xx_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); - wl12xx_rx(wl); - } - - if (intr & WL1251_ACX_INTR_RX1_DATA) { - wl12xx_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA"); - wl12xx_rx(wl); - } - - if (intr & WL1251_ACX_INTR_TX_RESULT) { - wl12xx_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); - wl12xx_tx_complete(wl); - } - - if (intr & (WL1251_ACX_INTR_EVENT_A | WL1251_ACX_INTR_EVENT_B)) { - wl12xx_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)", intr); - if (intr & WL1251_ACX_INTR_EVENT_A) - wl12xx_event_handle(wl, 0); - else - wl12xx_event_handle(wl, 1); - } - - if (intr & WL1251_ACX_INTR_INIT_COMPLETE) - wl12xx_debug(DEBUG_IRQ, "WL1251_ACX_INTR_INIT_COMPLETE"); - - wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); - -out_sleep: - wl12xx_ps_elp_sleep(wl); -out: - mutex_unlock(&wl->mutex); -} - -static int wl1251_hw_init_txq_fill(u8 qid, - struct acx_tx_queue_qos_config *config, - u32 num_blocks) -{ - config->qid = qid; - - switch (qid) { - case QOS_AC_BE: - config->high_threshold = - (QOS_TX_HIGH_BE_DEF * num_blocks) / 100; - config->low_threshold = - (QOS_TX_LOW_BE_DEF * num_blocks) / 100; - break; - case QOS_AC_BK: - config->high_threshold = - (QOS_TX_HIGH_BK_DEF * num_blocks) / 100; - config->low_threshold = - (QOS_TX_LOW_BK_DEF * num_blocks) / 100; - break; - case QOS_AC_VI: - config->high_threshold = - (QOS_TX_HIGH_VI_DEF * num_blocks) / 100; - config->low_threshold = - (QOS_TX_LOW_VI_DEF * num_blocks) / 100; - break; - case QOS_AC_VO: - config->high_threshold = - (QOS_TX_HIGH_VO_DEF * num_blocks) / 100; - config->low_threshold = - (QOS_TX_LOW_VO_DEF * num_blocks) / 100; - break; - default: - wl12xx_error("Invalid TX queue id: %d", qid); - return -EINVAL; - } - - return 0; -} - -static int wl1251_hw_init_tx_queue_config(struct wl12xx *wl) -{ - struct acx_tx_queue_qos_config config; - struct wl1251_acx_mem_map *wl_mem_map = wl->target_mem_map; - int ret, i; - - wl12xx_debug(DEBUG_ACX, "acx tx queue config"); - - config.header.id = ACX_TX_QUEUE_CFG; - config.header.len = sizeof(struct acx_tx_queue_qos_config) - - sizeof(struct acx_header); - - for (i = 0; i < MAX_NUM_OF_AC; i++) { - ret = wl1251_hw_init_txq_fill(i, &config, - wl_mem_map->num_tx_mem_blocks); - if (ret < 0) - return ret; - - ret = wl12xx_cmd_configure(wl, &config, sizeof(config)); - if (ret < 0) - return ret; - } - - return 0; -} - -static int wl1251_hw_init_data_path_config(struct wl12xx *wl) -{ - int ret; - - /* asking for the data path parameters */ - wl->data_path = kzalloc(sizeof(struct acx_data_path_params_resp), - GFP_KERNEL); - if (!wl->data_path) { - wl12xx_error("Couldnt allocate data path parameters"); - return -ENOMEM; - } - - ret = wl12xx_acx_data_path_params(wl, wl->data_path); - if (ret < 0) { - kfree(wl->data_path); - wl->data_path = NULL; - return ret; - } - - return 0; -} - -static int wl1251_hw_init(struct wl12xx *wl) -{ - struct wl1251_acx_mem_map *wl_mem_map; - int ret; - - ret = wl12xx_hw_init_hwenc_config(wl); - if (ret < 0) - return ret; - - /* Template settings */ - ret = wl12xx_hw_init_templates_config(wl); - if (ret < 0) - return ret; - - /* Default memory configuration */ - ret = wl1251_hw_init_mem_config(wl); - if (ret < 0) - return ret; - - /* Default data path configuration */ - ret = wl1251_hw_init_data_path_config(wl); - if (ret < 0) - goto out_free_memmap; - - /* RX config */ - ret = wl12xx_hw_init_rx_config(wl, - RX_CFG_PROMISCUOUS | RX_CFG_TSF, - RX_FILTER_OPTION_DEF); - /* RX_CONFIG_OPTION_ANY_DST_ANY_BSS, - RX_FILTER_OPTION_FILTER_ALL); */ - if (ret < 0) - goto out_free_data_path; - - /* TX queues config */ - ret = wl1251_hw_init_tx_queue_config(wl); - if (ret < 0) - goto out_free_data_path; - - /* PHY layer config */ - ret = wl12xx_hw_init_phy_config(wl); - if (ret < 0) - goto out_free_data_path; - - /* Beacon filtering */ - ret = wl12xx_hw_init_beacon_filter(wl); - if (ret < 0) - goto out_free_data_path; - - /* Bluetooth WLAN coexistence */ - ret = wl12xx_hw_init_pta(wl); - if (ret < 0) - goto out_free_data_path; - - /* Energy detection */ - ret = wl12xx_hw_init_energy_detection(wl); - if (ret < 0) - goto out_free_data_path; - - /* Beacons and boradcast settings */ - ret = wl12xx_hw_init_beacon_broadcast(wl); - if (ret < 0) - goto out_free_data_path; - - /* Enable data path */ - ret = wl12xx_cmd_data_path(wl, wl->channel, 1); - if (ret < 0) - goto out_free_data_path; - - /* Default power state */ - ret = wl12xx_hw_init_power_auth(wl); - if (ret < 0) - goto out_free_data_path; - - wl_mem_map = wl->target_mem_map; - wl12xx_info("%d tx blocks at 0x%x, %d rx blocks at 0x%x", - wl_mem_map->num_tx_mem_blocks, - wl->data_path->tx_control_addr, - wl_mem_map->num_rx_mem_blocks, - wl->data_path->rx_control_addr); - - return 0; - - out_free_data_path: - kfree(wl->data_path); - - out_free_memmap: - kfree(wl->target_mem_map); - - return ret; -} - -static int wl1251_plt_init(struct wl12xx *wl) -{ - int ret; - - ret = wl1251_hw_init_mem_config(wl); - if (ret < 0) - return ret; - - ret = wl12xx_cmd_data_path(wl, wl->channel, 1); - if (ret < 0) - return ret; - - return 0; -} - -void wl1251_setup(struct wl12xx *wl) -{ - /* FIXME: Is it better to use strncpy here or is this ok? */ - wl->chip.fw_filename = WL1251_FW_NAME; - wl->chip.nvs_filename = WL1251_NVS_NAME; - - /* Now we know what chip we're using, so adjust the power on sleep - * time accordingly */ - wl->chip.power_on_sleep = WL1251_POWER_ON_SLEEP; - - wl->chip.intr_cmd_complete = WL1251_ACX_INTR_CMD_COMPLETE; - wl->chip.intr_init_complete = WL1251_ACX_INTR_INIT_COMPLETE; - - wl->chip.op_upload_nvs = wl1251_upload_nvs; - wl->chip.op_upload_fw = wl1251_upload_firmware; - wl->chip.op_boot = wl1251_boot; - wl->chip.op_set_ecpu_ctrl = wl1251_set_ecpu_ctrl; - wl->chip.op_target_enable_interrupts = wl1251_target_enable_interrupts; - wl->chip.op_hw_init = wl1251_hw_init; - wl->chip.op_plt_init = wl1251_plt_init; - - wl->chip.p_table = wl1251_part_table; - wl->chip.acx_reg_table = wl1251_acx_reg_table; - - INIT_WORK(&wl->irq_work, wl1251_irq_work); -} diff --git a/drivers/net/wireless/wl12xx/wl1251.h b/drivers/net/wireless/wl12xx/wl1251.h index 1f4a44330394..998e4b6252bd 100644 --- a/drivers/net/wireless/wl12xx/wl1251.h +++ b/drivers/net/wireless/wl12xx/wl1251.h @@ -1,7 +1,8 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * - * Copyright (C) 2008 Nokia Corporation + * Copyright (c) 1998-2007 Texas Instruments Incorporated + * Copyright (C) 2008-2009 Nokia Corporation * * Contact: Kalle Valo <kalle.valo@nokia.com> * @@ -24,142 +25,400 @@ #ifndef __WL1251_H__ #define __WL1251_H__ +#include <linux/mutex.h> +#include <linux/list.h> #include <linux/bitops.h> - -#include "wl12xx.h" -#include "acx.h" - -#define WL1251_FW_NAME "wl1251-fw.bin" -#define WL1251_NVS_NAME "wl1251-nvs.bin" - -#define WL1251_POWER_ON_SLEEP 10 /* in miliseconds */ - -void wl1251_setup(struct wl12xx *wl); - - -struct wl1251_acx_memory { - __le16 num_stations; /* number of STAs to be supported. */ - u16 reserved_1; +#include <net/mac80211.h> + +#define DRIVER_NAME "wl1251" +#define DRIVER_PREFIX DRIVER_NAME ": " + +enum { + DEBUG_NONE = 0, + DEBUG_IRQ = BIT(0), + DEBUG_SPI = BIT(1), + DEBUG_BOOT = BIT(2), + DEBUG_MAILBOX = BIT(3), + DEBUG_NETLINK = BIT(4), + DEBUG_EVENT = BIT(5), + DEBUG_TX = BIT(6), + DEBUG_RX = BIT(7), + DEBUG_SCAN = BIT(8), + DEBUG_CRYPT = BIT(9), + DEBUG_PSM = BIT(10), + DEBUG_MAC80211 = BIT(11), + DEBUG_CMD = BIT(12), + DEBUG_ACX = BIT(13), + DEBUG_ALL = ~0, +}; + +#define DEBUG_LEVEL (DEBUG_NONE) + +#define DEBUG_DUMP_LIMIT 1024 + +#define wl1251_error(fmt, arg...) \ + printk(KERN_ERR DRIVER_PREFIX "ERROR " fmt "\n", ##arg) + +#define wl1251_warning(fmt, arg...) \ + printk(KERN_WARNING DRIVER_PREFIX "WARNING " fmt "\n", ##arg) + +#define wl1251_notice(fmt, arg...) \ + printk(KERN_INFO DRIVER_PREFIX fmt "\n", ##arg) + +#define wl1251_info(fmt, arg...) \ + printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg) + +#define wl1251_debug(level, fmt, arg...) \ + do { \ + if (level & DEBUG_LEVEL) \ + printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg); \ + } while (0) + +#define wl1251_dump(level, prefix, buf, len) \ + do { \ + if (level & DEBUG_LEVEL) \ + print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \ + DUMP_PREFIX_OFFSET, 16, 1, \ + buf, \ + min_t(size_t, len, DEBUG_DUMP_LIMIT), \ + 0); \ + } while (0) + +#define wl1251_dump_ascii(level, prefix, buf, len) \ + do { \ + if (level & DEBUG_LEVEL) \ + print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \ + DUMP_PREFIX_OFFSET, 16, 1, \ + buf, \ + min_t(size_t, len, DEBUG_DUMP_LIMIT), \ + true); \ + } while (0) + +#define WL1251_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \ + CFG_BSSID_FILTER_EN) + +#define WL1251_DEFAULT_RX_FILTER (CFG_RX_PRSP_EN | \ + CFG_RX_MGMT_EN | \ + CFG_RX_DATA_EN | \ + CFG_RX_CTL_EN | \ + CFG_RX_BCN_EN | \ + CFG_RX_AUTH_EN | \ + CFG_RX_ASSOC_EN) + +#define WL1251_BUSY_WORD_LEN 8 + +struct boot_attr { + u32 radio_type; + u8 mac_clock; + u8 arm_clock; + int firmware_debug; + u32 minor; + u32 major; + u32 bugfix; +}; + +enum wl1251_state { + WL1251_STATE_OFF, + WL1251_STATE_ON, + WL1251_STATE_PLT, +}; + +enum wl1251_partition_type { + PART_DOWN, + PART_WORK, + PART_DRPW, + + PART_TABLE_LEN +}; + +struct wl1251_partition { + u32 size; + u32 start; +}; + +struct wl1251_partition_set { + struct wl1251_partition mem; + struct wl1251_partition reg; +}; + +struct wl1251; + +struct wl1251_stats { + struct acx_statistics *fw_stats; + unsigned long fw_stats_update; + + unsigned int retry_count; + unsigned int excessive_retries; +}; + +struct wl1251_debugfs { + struct dentry *rootdir; + struct dentry *fw_statistics; + + struct dentry *tx_internal_desc_overflow; + + struct dentry *rx_out_of_mem; + struct dentry *rx_hdr_overflow; + struct dentry *rx_hw_stuck; + struct dentry *rx_dropped; + struct dentry *rx_fcs_err; + struct dentry *rx_xfr_hint_trig; + struct dentry *rx_path_reset; + struct dentry *rx_reset_counter; + + struct dentry *dma_rx_requested; + struct dentry *dma_rx_errors; + struct dentry *dma_tx_requested; + struct dentry *dma_tx_errors; + + struct dentry *isr_cmd_cmplt; + struct dentry *isr_fiqs; + struct dentry *isr_rx_headers; + struct dentry *isr_rx_mem_overflow; + struct dentry *isr_rx_rdys; + struct dentry *isr_irqs; + struct dentry *isr_tx_procs; + struct dentry *isr_decrypt_done; + struct dentry *isr_dma0_done; + struct dentry *isr_dma1_done; + struct dentry *isr_tx_exch_complete; + struct dentry *isr_commands; + struct dentry *isr_rx_procs; + struct dentry *isr_hw_pm_mode_changes; + struct dentry *isr_host_acknowledges; + struct dentry *isr_pci_pm; + struct dentry *isr_wakeups; + struct dentry *isr_low_rssi; + + struct dentry *wep_addr_key_count; + struct dentry *wep_default_key_count; + /* skipping wep.reserved */ + struct dentry *wep_key_not_found; + struct dentry *wep_decrypt_fail; + struct dentry *wep_packets; + struct dentry *wep_interrupt; + + struct dentry *pwr_ps_enter; + struct dentry *pwr_elp_enter; + struct dentry *pwr_missing_bcns; + struct dentry *pwr_wake_on_host; + struct dentry *pwr_wake_on_timer_exp; + struct dentry *pwr_tx_with_ps; + struct dentry *pwr_tx_without_ps; + struct dentry *pwr_rcvd_beacons; + struct dentry *pwr_power_save_off; + struct dentry *pwr_enable_ps; + struct dentry *pwr_disable_ps; + struct dentry *pwr_fix_tsf_ps; + /* skipping cont_miss_bcns_spread for now */ + struct dentry *pwr_rcvd_awake_beacons; + + struct dentry *mic_rx_pkts; + struct dentry *mic_calc_failure; + + struct dentry *aes_encrypt_fail; + struct dentry *aes_decrypt_fail; + struct dentry *aes_encrypt_packets; + struct dentry *aes_decrypt_packets; + struct dentry *aes_encrypt_interrupt; + struct dentry *aes_decrypt_interrupt; + + struct dentry *event_heart_beat; + struct dentry *event_calibration; + struct dentry *event_rx_mismatch; + struct dentry *event_rx_mem_empty; + struct dentry *event_rx_pool; + struct dentry *event_oom_late; + struct dentry *event_phy_transmit_error; + struct dentry *event_tx_stuck; + + struct dentry *ps_pspoll_timeouts; + struct dentry *ps_upsd_timeouts; + struct dentry *ps_upsd_max_sptime; + struct dentry *ps_upsd_max_apturn; + struct dentry *ps_pspoll_max_apturn; + struct dentry *ps_pspoll_utilization; + struct dentry *ps_upsd_utilization; + + struct dentry *rxpipe_rx_prep_beacon_drop; + struct dentry *rxpipe_descr_host_int_trig_rx_data; + struct dentry *rxpipe_beacon_buffer_thres_host_int_trig_rx_data; + struct dentry *rxpipe_missed_beacon_host_int_trig_rx_data; + struct dentry *rxpipe_tx_xfr_host_int_trig_rx_data; + + struct dentry *tx_queue_len; + + struct dentry *retry_count; + struct dentry *excessive_retries; +}; + +struct wl1251_if_operations { + void (*read)(struct wl1251 *wl, int addr, void *buf, size_t len); + void (*write)(struct wl1251 *wl, int addr, void *buf, size_t len); + void (*reset)(struct wl1251 *wl); + void (*enable_irq)(struct wl1251 *wl); + void (*disable_irq)(struct wl1251 *wl); +}; + +struct wl1251 { + struct ieee80211_hw *hw; + bool mac80211_registered; + + void *if_priv; + const struct wl1251_if_operations *if_ops; + + void (*set_power)(bool enable); + int irq; + + enum wl1251_state state; + struct mutex mutex; + + int physical_mem_addr; + int physical_reg_addr; + int virtual_mem_addr; + int virtual_reg_addr; + + int cmd_box_addr; + int event_box_addr; + struct boot_attr boot_attr; + + u8 *fw; + size_t fw_len; + u8 *nvs; + size_t nvs_len; + + u8 bssid[ETH_ALEN]; + u8 mac_addr[ETH_ALEN]; + u8 bss_type; + u8 listen_int; + int channel; + + void *target_mem_map; + struct acx_data_path_params_resp *data_path; + + /* Number of TX packets transferred to the FW, modulo 16 */ + u32 data_in_count; + + /* Frames scheduled for transmission, not handled yet */ + struct sk_buff_head tx_queue; + bool tx_queue_stopped; + + struct work_struct tx_work; + struct work_struct filter_work; + + /* Pending TX frames */ + struct sk_buff *tx_frames[16]; /* - * Nmber of memory buffers for the RX mem pool. - * The actual number may be less if there are - * not enough blocks left for the minimum num - * of TX ones. + * Index pointing to the next TX complete entry + * in the cyclic XT complete array we get from + * the FW. */ - u8 rx_mem_block_num; - u8 reserved_2; - u8 num_tx_queues; /* From 1 to 16 */ - u8 host_if_options; /* HOST_IF* */ - u8 tx_min_mem_block_num; - u8 num_ssid_profiles; - __le16 debug_buffer_size; -} __attribute__ ((packed)); + u32 next_tx_complete; + /* FW Rx counter */ + u32 rx_counter; -#define ACX_RX_DESC_MIN 1 -#define ACX_RX_DESC_MAX 127 -#define ACX_RX_DESC_DEF 32 -struct wl1251_acx_rx_queue_config { - u8 num_descs; - u8 pad; - u8 type; - u8 priority; - __le32 dma_address; -} __attribute__ ((packed)); + /* Rx frames handled */ + u32 rx_handled; -#define ACX_TX_DESC_MIN 1 -#define ACX_TX_DESC_MAX 127 -#define ACX_TX_DESC_DEF 16 -struct wl1251_acx_tx_queue_config { - u8 num_descs; - u8 pad[2]; - u8 attributes; -} __attribute__ ((packed)); + /* Current double buffer */ + u32 rx_current_buffer; + u32 rx_last_id; -#define MAX_TX_QUEUE_CONFIGS 5 -#define MAX_TX_QUEUES 4 -struct wl1251_acx_config_memory { - struct acx_header header; + /* The target interrupt mask */ + u32 intr_mask; + struct work_struct irq_work; - struct wl1251_acx_memory mem_config; - struct wl1251_acx_rx_queue_config rx_queue_config; - struct wl1251_acx_tx_queue_config tx_queue_config[MAX_TX_QUEUE_CONFIGS]; -} __attribute__ ((packed)); + /* The mbox event mask */ + u32 event_mask; -struct wl1251_acx_mem_map { - struct acx_header header; + /* Mailbox pointers */ + u32 mbox_ptr[2]; - void *code_start; - void *code_end; + /* Are we currently scanning */ + bool scanning; - void *wep_defkey_start; - void *wep_defkey_end; + /* Our association ID */ + u16 aid; - void *sta_table_start; - void *sta_table_end; + /* Default key (for WEP) */ + u32 default_key; - void *packet_template_start; - void *packet_template_end; + unsigned int tx_mgmt_frm_rate; + unsigned int tx_mgmt_frm_mod; - void *queue_memory_start; - void *queue_memory_end; + unsigned int rx_config; + unsigned int rx_filter; - void *packet_memory_pool_start; - void *packet_memory_pool_end; + /* is firmware in elp mode */ + bool elp; - void *debug_buffer1_start; - void *debug_buffer1_end; + /* we can be in psm, but not in elp, we have to differentiate */ + bool psm; - void *debug_buffer2_start; - void *debug_buffer2_end; + /* PSM mode requested */ + bool psm_requested; - /* Number of blocks FW allocated for TX packets */ - u32 num_tx_mem_blocks; + u16 beacon_int; + u8 dtim_period; - /* Number of blocks FW allocated for RX packets */ - u32 num_rx_mem_blocks; -} __attribute__ ((packed)); + /* in dBm */ + int power_level; -/************************************************************************* + struct wl1251_stats stats; + struct wl1251_debugfs debugfs; - Host Interrupt Register (WiLink -> Host) + u32 buffer_32; + u32 buffer_cmd; + u8 buffer_busyword[WL1251_BUSY_WORD_LEN]; + struct wl1251_rx_descriptor *rx_descriptor; -**************************************************************************/ + u32 chip_id; + char fw_ver[21]; +}; -/* RX packet is ready in Xfer buffer #0 */ -#define WL1251_ACX_INTR_RX0_DATA BIT(0) +int wl1251_plt_start(struct wl1251 *wl); +int wl1251_plt_stop(struct wl1251 *wl); -/* TX result(s) are in the TX complete buffer */ -#define WL1251_ACX_INTR_TX_RESULT BIT(1) +struct ieee80211_hw *wl1251_alloc_hw(void); +int wl1251_free_hw(struct wl1251 *wl); +int wl1251_init_ieee80211(struct wl1251 *wl); +void wl1251_enable_interrupts(struct wl1251 *wl); +void wl1251_disable_interrupts(struct wl1251 *wl); -/* OBSOLETE */ -#define WL1251_ACX_INTR_TX_XFR BIT(2) +#define DEFAULT_HW_GEN_MODULATION_TYPE CCK_LONG /* Long Preamble */ +#define DEFAULT_HW_GEN_TX_RATE RATE_2MBPS +#define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */ -/* RX packet is ready in Xfer buffer #1 */ -#define WL1251_ACX_INTR_RX1_DATA BIT(3) +#define WL1251_DEFAULT_POWER_LEVEL 20 -/* Event was entered to Event MBOX #A */ -#define WL1251_ACX_INTR_EVENT_A BIT(4) +#define WL1251_TX_QUEUE_MAX_LENGTH 20 -/* Event was entered to Event MBOX #B */ -#define WL1251_ACX_INTR_EVENT_B BIT(5) +#define WL1251_DEFAULT_BEACON_INT 100 +#define WL1251_DEFAULT_DTIM_PERIOD 1 -/* OBSOLETE */ -#define WL1251_ACX_INTR_WAKE_ON_HOST BIT(6) +#define WL1251_DEFAULT_CHANNEL 0 -/* Trace meassge on MBOX #A */ -#define WL1251_ACX_INTR_TRACE_A BIT(7) +#define CHIP_ID_1251_PG10 (0x7010101) +#define CHIP_ID_1251_PG11 (0x7020101) +#define CHIP_ID_1251_PG12 (0x7030101) +#define CHIP_ID_1271_PG10 (0x4030101) +#define CHIP_ID_1271_PG20 (0x4030111) -/* Trace meassge on MBOX #B */ -#define WL1251_ACX_INTR_TRACE_B BIT(8) +#define WL1251_FW_NAME "wl1251-fw.bin" +#define WL1251_NVS_NAME "wl1251-nvs.bin" -/* Command processing completion */ -#define WL1251_ACX_INTR_CMD_COMPLETE BIT(9) +#define WL1251_POWER_ON_SLEEP 10 /* in miliseconds */ -/* Init sequence is done */ -#define WL1251_ACX_INTR_INIT_COMPLETE BIT(14) +#define WL1251_PART_DOWN_MEM_START 0x0 +#define WL1251_PART_DOWN_MEM_SIZE 0x16800 +#define WL1251_PART_DOWN_REG_START REGISTERS_BASE +#define WL1251_PART_DOWN_REG_SIZE REGISTERS_DOWN_SIZE -#define WL1251_ACX_INTR_ALL 0xFFFFFFFF +#define WL1251_PART_WORK_MEM_START 0x28000 +#define WL1251_PART_WORK_MEM_SIZE 0x14000 +#define WL1251_PART_WORK_REG_START REGISTERS_BASE +#define WL1251_PART_WORK_REG_SIZE REGISTERS_WORK_SIZE #endif diff --git a/drivers/net/wireless/wl12xx/wl1251_acx.c b/drivers/net/wireless/wl12xx/wl1251_acx.c new file mode 100644 index 000000000000..10b26c4532c9 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1251_acx.c @@ -0,0 +1,918 @@ +#include "wl1251_acx.h" + +#include <linux/module.h> +#include <linux/crc7.h> + +#include "wl1251.h" +#include "wl1251_reg.h" +#include "wl1251_cmd.h" +#include "wl1251_ps.h" + +int wl1251_acx_frame_rates(struct wl1251 *wl, u8 ctrl_rate, u8 ctrl_mod, + u8 mgt_rate, u8 mgt_mod) +{ + struct acx_fw_gen_frame_rates *rates; + int ret; + + wl1251_debug(DEBUG_ACX, "acx frame rates"); + + rates = kzalloc(sizeof(*rates), GFP_KERNEL); + if (!rates) { + ret = -ENOMEM; + goto out; + } + + rates->tx_ctrl_frame_rate = ctrl_rate; + rates->tx_ctrl_frame_mod = ctrl_mod; + rates->tx_mgt_frame_rate = mgt_rate; + rates->tx_mgt_frame_mod = mgt_mod; + + ret = wl1251_cmd_configure(wl, ACX_FW_GEN_FRAME_RATES, + rates, sizeof(*rates)); + if (ret < 0) { + wl1251_error("Failed to set FW rates and modulation"); + goto out; + } + +out: + kfree(rates); + return ret; +} + + +int wl1251_acx_station_id(struct wl1251 *wl) +{ + struct acx_dot11_station_id *mac; + int ret, i; + + wl1251_debug(DEBUG_ACX, "acx dot11_station_id"); + + mac = kzalloc(sizeof(*mac), GFP_KERNEL); + if (!mac) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < ETH_ALEN; i++) + mac->mac[i] = wl->mac_addr[ETH_ALEN - 1 - i]; + + ret = wl1251_cmd_configure(wl, DOT11_STATION_ID, mac, sizeof(*mac)); + if (ret < 0) + goto out; + +out: + kfree(mac); + return ret; +} + +int wl1251_acx_default_key(struct wl1251 *wl, u8 key_id) +{ + struct acx_dot11_default_key *default_key; + int ret; + + wl1251_debug(DEBUG_ACX, "acx dot11_default_key (%d)", key_id); + + default_key = kzalloc(sizeof(*default_key), GFP_KERNEL); + if (!default_key) { + ret = -ENOMEM; + goto out; + } + + default_key->id = key_id; + + ret = wl1251_cmd_configure(wl, DOT11_DEFAULT_KEY, + default_key, sizeof(*default_key)); + if (ret < 0) { + wl1251_error("Couldn't set default key"); + goto out; + } + + wl->default_key = key_id; + +out: + kfree(default_key); + return ret; +} + +int wl1251_acx_wake_up_conditions(struct wl1251 *wl, u8 wake_up_event, + u8 listen_interval) +{ + struct acx_wake_up_condition *wake_up; + int ret; + + wl1251_debug(DEBUG_ACX, "acx wake up conditions"); + + wake_up = kzalloc(sizeof(*wake_up), GFP_KERNEL); + if (!wake_up) { + ret = -ENOMEM; + goto out; + } + + wake_up->wake_up_event = wake_up_event; + wake_up->listen_interval = listen_interval; + + ret = wl1251_cmd_configure(wl, ACX_WAKE_UP_CONDITIONS, + wake_up, sizeof(*wake_up)); + if (ret < 0) { + wl1251_warning("could not set wake up conditions: %d", ret); + goto out; + } + +out: + kfree(wake_up); + return ret; +} + +int wl1251_acx_sleep_auth(struct wl1251 *wl, u8 sleep_auth) +{ + struct acx_sleep_auth *auth; + int ret; + + wl1251_debug(DEBUG_ACX, "acx sleep auth"); + + auth = kzalloc(sizeof(*auth), GFP_KERNEL); + if (!auth) { + ret = -ENOMEM; + goto out; + } + + auth->sleep_auth = sleep_auth; + + ret = wl1251_cmd_configure(wl, ACX_SLEEP_AUTH, auth, sizeof(*auth)); + if (ret < 0) + return ret; + +out: + kfree(auth); + return ret; +} + +int wl1251_acx_fw_version(struct wl1251 *wl, char *buf, size_t len) +{ + struct acx_revision *rev; + int ret; + + wl1251_debug(DEBUG_ACX, "acx fw rev"); + + rev = kzalloc(sizeof(*rev), GFP_KERNEL); + if (!rev) { + ret = -ENOMEM; + goto out; + } + + ret = wl1251_cmd_interrogate(wl, ACX_FW_REV, rev, sizeof(*rev)); + if (ret < 0) { + wl1251_warning("ACX_FW_REV interrogate failed"); + goto out; + } + + /* be careful with the buffer sizes */ + strncpy(buf, rev->fw_version, min(len, sizeof(rev->fw_version))); + + /* + * if the firmware version string is exactly + * sizeof(rev->fw_version) long or fw_len is less than + * sizeof(rev->fw_version) it won't be null terminated + */ + buf[min(len, sizeof(rev->fw_version)) - 1] = '\0'; + +out: + kfree(rev); + return ret; +} + +int wl1251_acx_tx_power(struct wl1251 *wl, int power) +{ + struct acx_current_tx_power *acx; + int ret; + + wl1251_debug(DEBUG_ACX, "acx dot11_cur_tx_pwr"); + + if (power < 0 || power > 25) + return -EINVAL; + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->current_tx_power = power * 10; + + ret = wl1251_cmd_configure(wl, DOT11_CUR_TX_PWR, acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("configure of tx power failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1251_acx_feature_cfg(struct wl1251 *wl) +{ + struct acx_feature_config *feature; + int ret; + + wl1251_debug(DEBUG_ACX, "acx feature cfg"); + + feature = kzalloc(sizeof(*feature), GFP_KERNEL); + if (!feature) { + ret = -ENOMEM; + goto out; + } + + /* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE are disabled */ + feature->data_flow_options = 0; + feature->options = 0; + + ret = wl1251_cmd_configure(wl, ACX_FEATURE_CFG, + feature, sizeof(*feature)); + if (ret < 0) { + wl1251_error("Couldn't set HW encryption"); + goto out; + } + +out: + kfree(feature); + return ret; +} + +int wl1251_acx_mem_map(struct wl1251 *wl, struct acx_header *mem_map, + size_t len) +{ + int ret; + + wl1251_debug(DEBUG_ACX, "acx mem map"); + + ret = wl1251_cmd_interrogate(wl, ACX_MEM_MAP, mem_map, len); + if (ret < 0) + return ret; + + return 0; +} + +int wl1251_acx_data_path_params(struct wl1251 *wl, + struct acx_data_path_params_resp *resp) +{ + struct acx_data_path_params *params; + int ret; + + wl1251_debug(DEBUG_ACX, "acx data path params"); + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + ret = -ENOMEM; + goto out; + } + + params->rx_packet_ring_chunk_size = DP_RX_PACKET_RING_CHUNK_SIZE; + params->tx_packet_ring_chunk_size = DP_TX_PACKET_RING_CHUNK_SIZE; + + params->rx_packet_ring_chunk_num = DP_RX_PACKET_RING_CHUNK_NUM; + params->tx_packet_ring_chunk_num = DP_TX_PACKET_RING_CHUNK_NUM; + + params->tx_complete_threshold = 1; + + params->tx_complete_ring_depth = FW_TX_CMPLT_BLOCK_SIZE; + + params->tx_complete_timeout = DP_TX_COMPLETE_TIME_OUT; + + ret = wl1251_cmd_configure(wl, ACX_DATA_PATH_PARAMS, + params, sizeof(*params)); + if (ret < 0) + goto out; + + /* FIXME: shouldn't this be ACX_DATA_PATH_RESP_PARAMS? */ + ret = wl1251_cmd_interrogate(wl, ACX_DATA_PATH_PARAMS, + resp, sizeof(*resp)); + + if (ret < 0) { + wl1251_warning("failed to read data path parameters: %d", ret); + goto out; + } else if (resp->header.cmd.status != CMD_STATUS_SUCCESS) { + wl1251_warning("data path parameter acx status failed"); + ret = -EIO; + goto out; + } + +out: + kfree(params); + return ret; +} + +int wl1251_acx_rx_msdu_life_time(struct wl1251 *wl, u32 life_time) +{ + struct acx_rx_msdu_lifetime *acx; + int ret; + + wl1251_debug(DEBUG_ACX, "acx rx msdu life time"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->lifetime = life_time; + ret = wl1251_cmd_configure(wl, DOT11_RX_MSDU_LIFE_TIME, + acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("failed to set rx msdu life time: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1251_acx_rx_config(struct wl1251 *wl, u32 config, u32 filter) +{ + struct acx_rx_config *rx_config; + int ret; + + wl1251_debug(DEBUG_ACX, "acx rx config"); + + rx_config = kzalloc(sizeof(*rx_config), GFP_KERNEL); + if (!rx_config) { + ret = -ENOMEM; + goto out; + } + + rx_config->config_options = config; + rx_config->filter_options = filter; + + ret = wl1251_cmd_configure(wl, ACX_RX_CFG, + rx_config, sizeof(*rx_config)); + if (ret < 0) { + wl1251_warning("failed to set rx config: %d", ret); + goto out; + } + +out: + kfree(rx_config); + return ret; +} + +int wl1251_acx_pd_threshold(struct wl1251 *wl) +{ + struct acx_packet_detection *pd; + int ret; + + wl1251_debug(DEBUG_ACX, "acx data pd threshold"); + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) { + ret = -ENOMEM; + goto out; + } + + /* FIXME: threshold value not set */ + + ret = wl1251_cmd_configure(wl, ACX_PD_THRESHOLD, pd, sizeof(*pd)); + if (ret < 0) { + wl1251_warning("failed to set pd threshold: %d", ret); + goto out; + } + +out: + kfree(pd); + return 0; +} + +int wl1251_acx_slot(struct wl1251 *wl, enum acx_slot_type slot_time) +{ + struct acx_slot *slot; + int ret; + + wl1251_debug(DEBUG_ACX, "acx slot"); + + slot = kzalloc(sizeof(*slot), GFP_KERNEL); + if (!slot) { + ret = -ENOMEM; + goto out; + } + + slot->wone_index = STATION_WONE_INDEX; + slot->slot_time = slot_time; + + ret = wl1251_cmd_configure(wl, ACX_SLOT, slot, sizeof(*slot)); + if (ret < 0) { + wl1251_warning("failed to set slot time: %d", ret); + goto out; + } + +out: + kfree(slot); + return ret; +} + +int wl1251_acx_group_address_tbl(struct wl1251 *wl) +{ + struct acx_dot11_grp_addr_tbl *acx; + int ret; + + wl1251_debug(DEBUG_ACX, "acx group address tbl"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + /* MAC filtering */ + acx->enabled = 0; + acx->num_groups = 0; + memset(acx->mac_table, 0, ADDRESS_GROUP_MAX_LEN); + + ret = wl1251_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL, + acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("failed to set group addr table: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1251_acx_service_period_timeout(struct wl1251 *wl) +{ + struct acx_rx_timeout *rx_timeout; + int ret; + + rx_timeout = kzalloc(sizeof(*rx_timeout), GFP_KERNEL); + if (!rx_timeout) { + ret = -ENOMEM; + goto out; + } + + wl1251_debug(DEBUG_ACX, "acx service period timeout"); + + rx_timeout->ps_poll_timeout = RX_TIMEOUT_PS_POLL_DEF; + rx_timeout->upsd_timeout = RX_TIMEOUT_UPSD_DEF; + + ret = wl1251_cmd_configure(wl, ACX_SERVICE_PERIOD_TIMEOUT, + rx_timeout, sizeof(*rx_timeout)); + if (ret < 0) { + wl1251_warning("failed to set service period timeout: %d", + ret); + goto out; + } + +out: + kfree(rx_timeout); + return ret; +} + +int wl1251_acx_rts_threshold(struct wl1251 *wl, u16 rts_threshold) +{ + struct acx_rts_threshold *rts; + int ret; + + wl1251_debug(DEBUG_ACX, "acx rts threshold"); + + rts = kzalloc(sizeof(*rts), GFP_KERNEL); + if (!rts) { + ret = -ENOMEM; + goto out; + } + + rts->threshold = rts_threshold; + + ret = wl1251_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts)); + if (ret < 0) { + wl1251_warning("failed to set rts threshold: %d", ret); + goto out; + } + +out: + kfree(rts); + return ret; +} + +int wl1251_acx_beacon_filter_opt(struct wl1251 *wl) +{ + struct acx_beacon_filter_option *beacon_filter; + int ret; + + wl1251_debug(DEBUG_ACX, "acx beacon filter opt"); + + beacon_filter = kzalloc(sizeof(*beacon_filter), GFP_KERNEL); + if (!beacon_filter) { + ret = -ENOMEM; + goto out; + } + + beacon_filter->enable = 0; + beacon_filter->max_num_beacons = 0; + + ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_OPT, + beacon_filter, sizeof(*beacon_filter)); + if (ret < 0) { + wl1251_warning("failed to set beacon filter opt: %d", ret); + goto out; + } + +out: + kfree(beacon_filter); + return ret; +} + +int wl1251_acx_beacon_filter_table(struct wl1251 *wl) +{ + struct acx_beacon_filter_ie_table *ie_table; + int ret; + + wl1251_debug(DEBUG_ACX, "acx beacon filter table"); + + ie_table = kzalloc(sizeof(*ie_table), GFP_KERNEL); + if (!ie_table) { + ret = -ENOMEM; + goto out; + } + + ie_table->num_ie = 0; + memset(ie_table->table, 0, BEACON_FILTER_TABLE_MAX_SIZE); + + ret = wl1251_cmd_configure(wl, ACX_BEACON_FILTER_TABLE, + ie_table, sizeof(*ie_table)); + if (ret < 0) { + wl1251_warning("failed to set beacon filter table: %d", ret); + goto out; + } + +out: + kfree(ie_table); + return ret; +} + +int wl1251_acx_sg_enable(struct wl1251 *wl) +{ + struct acx_bt_wlan_coex *pta; + int ret; + + wl1251_debug(DEBUG_ACX, "acx sg enable"); + + pta = kzalloc(sizeof(*pta), GFP_KERNEL); + if (!pta) { + ret = -ENOMEM; + goto out; + } + + pta->enable = SG_ENABLE; + + ret = wl1251_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta)); + if (ret < 0) { + wl1251_warning("failed to set softgemini enable: %d", ret); + goto out; + } + +out: + kfree(pta); + return ret; +} + +int wl1251_acx_sg_cfg(struct wl1251 *wl) +{ + struct acx_bt_wlan_coex_param *param; + int ret; + + wl1251_debug(DEBUG_ACX, "acx sg cfg"); + + param = kzalloc(sizeof(*param), GFP_KERNEL); + if (!param) { + ret = -ENOMEM; + goto out; + } + + /* BT-WLAN coext parameters */ + param->min_rate = RATE_INDEX_24MBPS; + param->bt_hp_max_time = PTA_BT_HP_MAXTIME_DEF; + param->wlan_hp_max_time = PTA_WLAN_HP_MAX_TIME_DEF; + param->sense_disable_timer = PTA_SENSE_DISABLE_TIMER_DEF; + param->rx_time_bt_hp = PTA_PROTECTIVE_RX_TIME_DEF; + param->tx_time_bt_hp = PTA_PROTECTIVE_TX_TIME_DEF; + param->rx_time_bt_hp_fast = PTA_PROTECTIVE_RX_TIME_FAST_DEF; + param->tx_time_bt_hp_fast = PTA_PROTECTIVE_TX_TIME_FAST_DEF; + param->wlan_cycle_fast = PTA_CYCLE_TIME_FAST_DEF; + param->bt_anti_starvation_period = PTA_ANTI_STARVE_PERIOD_DEF; + param->next_bt_lp_packet = PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF; + param->wake_up_beacon = PTA_TIME_BEFORE_BEACON_DEF; + param->hp_dm_max_guard_time = PTA_HPDM_MAX_TIME_DEF; + param->next_wlan_packet = PTA_TIME_OUT_NEXT_WLAN_DEF; + param->antenna_type = PTA_ANTENNA_TYPE_DEF; + param->signal_type = PTA_SIGNALING_TYPE_DEF; + param->afh_leverage_on = PTA_AFH_LEVERAGE_ON_DEF; + param->quiet_cycle_num = PTA_NUMBER_QUIET_CYCLE_DEF; + param->max_cts = PTA_MAX_NUM_CTS_DEF; + param->wlan_packets_num = PTA_NUMBER_OF_WLAN_PACKETS_DEF; + param->bt_packets_num = PTA_NUMBER_OF_BT_PACKETS_DEF; + param->missed_rx_avalanche = PTA_RX_FOR_AVALANCHE_DEF; + param->wlan_elp_hp = PTA_ELP_HP_DEF; + param->bt_anti_starvation_cycles = PTA_ANTI_STARVE_NUM_CYCLE_DEF; + param->ack_mode_dual_ant = PTA_ACK_MODE_DEF; + param->pa_sd_enable = PTA_ALLOW_PA_SD_DEF; + param->pta_auto_mode_enable = PTA_AUTO_MODE_NO_CTS_DEF; + param->bt_hp_respected_num = PTA_BT_HP_RESPECTED_DEF; + + ret = wl1251_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); + if (ret < 0) { + wl1251_warning("failed to set sg config: %d", ret); + goto out; + } + +out: + kfree(param); + return ret; +} + +int wl1251_acx_cca_threshold(struct wl1251 *wl) +{ + struct acx_energy_detection *detection; + int ret; + + wl1251_debug(DEBUG_ACX, "acx cca threshold"); + + detection = kzalloc(sizeof(*detection), GFP_KERNEL); + if (!detection) { + ret = -ENOMEM; + goto out; + } + + detection->rx_cca_threshold = CCA_THRSH_DISABLE_ENERGY_D; + detection->tx_energy_detection = 0; + + ret = wl1251_cmd_configure(wl, ACX_CCA_THRESHOLD, + detection, sizeof(*detection)); + if (ret < 0) { + wl1251_warning("failed to set cca threshold: %d", ret); + return ret; + } + +out: + kfree(detection); + return ret; +} + +int wl1251_acx_bcn_dtim_options(struct wl1251 *wl) +{ + struct acx_beacon_broadcast *bb; + int ret; + + wl1251_debug(DEBUG_ACX, "acx bcn dtim options"); + + bb = kzalloc(sizeof(*bb), GFP_KERNEL); + if (!bb) { + ret = -ENOMEM; + goto out; + } + + bb->beacon_rx_timeout = BCN_RX_TIMEOUT_DEF_VALUE; + bb->broadcast_timeout = BROADCAST_RX_TIMEOUT_DEF_VALUE; + bb->rx_broadcast_in_ps = RX_BROADCAST_IN_PS_DEF_VALUE; + bb->ps_poll_threshold = CONSECUTIVE_PS_POLL_FAILURE_DEF; + + ret = wl1251_cmd_configure(wl, ACX_BCN_DTIM_OPTIONS, bb, sizeof(*bb)); + if (ret < 0) { + wl1251_warning("failed to set rx config: %d", ret); + goto out; + } + +out: + kfree(bb); + return ret; +} + +int wl1251_acx_aid(struct wl1251 *wl, u16 aid) +{ + struct acx_aid *acx_aid; + int ret; + + wl1251_debug(DEBUG_ACX, "acx aid"); + + acx_aid = kzalloc(sizeof(*acx_aid), GFP_KERNEL); + if (!acx_aid) { + ret = -ENOMEM; + goto out; + } + + acx_aid->aid = aid; + + ret = wl1251_cmd_configure(wl, ACX_AID, acx_aid, sizeof(*acx_aid)); + if (ret < 0) { + wl1251_warning("failed to set aid: %d", ret); + goto out; + } + +out: + kfree(acx_aid); + return ret; +} + +int wl1251_acx_event_mbox_mask(struct wl1251 *wl, u32 event_mask) +{ + struct acx_event_mask *mask; + int ret; + + wl1251_debug(DEBUG_ACX, "acx event mbox mask"); + + mask = kzalloc(sizeof(*mask), GFP_KERNEL); + if (!mask) { + ret = -ENOMEM; + goto out; + } + + /* high event mask is unused */ + mask->high_event_mask = 0xffffffff; + + mask->event_mask = event_mask; + + ret = wl1251_cmd_configure(wl, ACX_EVENT_MBOX_MASK, + mask, sizeof(*mask)); + if (ret < 0) { + wl1251_warning("failed to set acx_event_mbox_mask: %d", ret); + goto out; + } + +out: + kfree(mask); + return ret; +} + +int wl1251_acx_set_preamble(struct wl1251 *wl, enum acx_preamble_type preamble) +{ + struct acx_preamble *acx; + int ret; + + wl1251_debug(DEBUG_ACX, "acx_set_preamble"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->preamble = preamble; + + ret = wl1251_cmd_configure(wl, ACX_PREAMBLE_TYPE, acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("Setting of preamble failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1251_acx_cts_protect(struct wl1251 *wl, + enum acx_ctsprotect_type ctsprotect) +{ + struct acx_ctsprotect *acx; + int ret; + + wl1251_debug(DEBUG_ACX, "acx_set_ctsprotect"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->ctsprotect = ctsprotect; + + ret = wl1251_cmd_configure(wl, ACX_CTS_PROTECTION, acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("Setting of ctsprotect failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1251_acx_tsf_info(struct wl1251 *wl, u64 *mactime) +{ + struct acx_tsf_info *tsf_info; + int ret; + + tsf_info = kzalloc(sizeof(*tsf_info), GFP_KERNEL); + if (!tsf_info) { + ret = -ENOMEM; + goto out; + } + + ret = wl1251_cmd_interrogate(wl, ACX_TSF_INFO, + tsf_info, sizeof(*tsf_info)); + if (ret < 0) { + wl1251_warning("ACX_FW_REV interrogate failed"); + goto out; + } + + *mactime = tsf_info->current_tsf_lsb | + (tsf_info->current_tsf_msb << 31); + +out: + kfree(tsf_info); + return ret; +} + +int wl1251_acx_statistics(struct wl1251 *wl, struct acx_statistics *stats) +{ + int ret; + + wl1251_debug(DEBUG_ACX, "acx statistics"); + + ret = wl1251_cmd_interrogate(wl, ACX_STATISTICS, stats, + sizeof(*stats)); + if (ret < 0) { + wl1251_warning("acx statistics failed: %d", ret); + return -ENOMEM; + } + + return 0; +} + +int wl1251_acx_rate_policies(struct wl1251 *wl) +{ + struct acx_rate_policy *acx; + int ret = 0; + + wl1251_debug(DEBUG_ACX, "acx rate policies"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + + if (!acx) { + ret = -ENOMEM; + goto out; + } + + /* configure one default (one-size-fits-all) rate class */ + acx->rate_class_cnt = 1; + acx->rate_class[0].enabled_rates = ACX_RATE_MASK_UNSPECIFIED; + acx->rate_class[0].short_retry_limit = ACX_RATE_RETRY_LIMIT; + acx->rate_class[0].long_retry_limit = ACX_RATE_RETRY_LIMIT; + acx->rate_class[0].aflags = 0; + + ret = wl1251_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); + if (ret < 0) { + wl1251_warning("Setting of rate policies failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1251_acx_mem_cfg(struct wl1251 *wl) +{ + struct wl1251_acx_config_memory *mem_conf; + int ret, i; + + wl1251_debug(DEBUG_ACX, "acx mem cfg"); + + mem_conf = kzalloc(sizeof(*mem_conf), GFP_KERNEL); + if (!mem_conf) { + ret = -ENOMEM; + goto out; + } + + /* memory config */ + mem_conf->mem_config.num_stations = cpu_to_le16(DEFAULT_NUM_STATIONS); + mem_conf->mem_config.rx_mem_block_num = 35; + mem_conf->mem_config.tx_min_mem_block_num = 64; + mem_conf->mem_config.num_tx_queues = MAX_TX_QUEUES; + mem_conf->mem_config.host_if_options = HOSTIF_PKT_RING; + mem_conf->mem_config.num_ssid_profiles = 1; + mem_conf->mem_config.debug_buffer_size = + cpu_to_le16(TRACE_BUFFER_MAX_SIZE); + + /* RX queue config */ + mem_conf->rx_queue_config.dma_address = 0; + mem_conf->rx_queue_config.num_descs = ACX_RX_DESC_DEF; + mem_conf->rx_queue_config.priority = DEFAULT_RXQ_PRIORITY; + mem_conf->rx_queue_config.type = DEFAULT_RXQ_TYPE; + + /* TX queue config */ + for (i = 0; i < MAX_TX_QUEUES; i++) { + mem_conf->tx_queue_config[i].num_descs = ACX_TX_DESC_DEF; + mem_conf->tx_queue_config[i].attributes = i; + } + + ret = wl1251_cmd_configure(wl, ACX_MEM_CFG, mem_conf, + sizeof(*mem_conf)); + if (ret < 0) { + wl1251_warning("wl1251 mem config failed: %d", ret); + goto out; + } + +out: + kfree(mem_conf); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/wl1251_acx.h index fb2d2340993c..cafb91459504 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/wl1251_acx.h @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation @@ -22,14 +22,20 @@ * */ -#ifndef __WL12XX_ACX_H__ -#define __WL12XX_ACX_H__ +#ifndef __WL1251_ACX_H__ +#define __WL1251_ACX_H__ -#include "wl12xx.h" +#include "wl1251.h" +#include "wl1251_cmd.h" /* Target's information element */ struct acx_header { + struct wl1251_cmd_header cmd; + + /* acx (or information element) header */ u16 id; + + /* payload length (not including headers */ u16 len; }; @@ -85,15 +91,15 @@ struct acx_revision { u32 hw_version; } __attribute__ ((packed)); -enum wl12xx_psm_mode { +enum wl1251_psm_mode { /* Active mode */ - WL12XX_PSM_CAM = 0, + WL1251_PSM_CAM = 0, /* Power save mode */ - WL12XX_PSM_PS = 1, + WL1251_PSM_PS = 1, /* Extreme low power */ - WL12XX_PSM_ELP = 2, + WL1251_PSM_ELP = 2, }; struct acx_sleep_auth { @@ -107,25 +113,6 @@ struct acx_sleep_auth { u8 padding[3]; } __attribute__ ((packed)); -#define TIM_ELE_ID 5 -#define PARTIAL_VBM_MAX 251 - -struct tim { - u8 identity; - u8 length; - u8 dtim_count; - u8 dtim_period; - u8 bitmap_ctrl; - u8 pvb_field[PARTIAL_VBM_MAX]; /* Partial Virtual Bitmap */ -} __attribute__ ((packed)); - -/* Virtual Bit Map update */ -struct vbm_update_request { - __le16 len; - u8 padding[2]; - struct tim tim; -} __attribute__ ((packed)); - enum { HOSTIF_PCI_MASTER_HOST_INDIRECT, HOSTIF_PCI_MASTER_HOST_DIRECT, @@ -202,7 +189,7 @@ struct acx_data_path_params_resp { #define RX_MSDU_LIFETIME_MAX 0xFFFFFFFF #define RX_MSDU_LIFETIME_DEF 512000 -struct rx_msdu_lifetime { +struct acx_rx_msdu_lifetime { struct acx_header header; /* @@ -368,7 +355,7 @@ struct acx_slot { #define ADDRESS_GROUP_MAX (8) #define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ADDRESS_GROUP_MAX) -struct multicast_grp_addr_start { +struct acx_dot11_grp_addr_tbl { struct acx_header header; u8 enabled; @@ -730,22 +717,13 @@ struct acx_fw_gen_frame_rates { } __attribute__ ((packed)); /* STA MAC */ -struct dot11_station_id { +struct acx_dot11_station_id { struct acx_header header; u8 mac[ETH_ALEN]; u8 pad[2]; } __attribute__ ((packed)); -/* HW encryption keys */ -#define NUM_ACCESS_CATEGORIES_COPY 4 -#define MAX_KEY_SIZE 32 - -/* When set, disable HW encryption */ -#define DF_ENCRYPTION_DISABLE 0x01 -/* When set, disable HW decryption */ -#define DF_SNIFF_MODE_ENABLE 0x80 - struct acx_feature_config { struct acx_header header; @@ -753,67 +731,6 @@ struct acx_feature_config { u32 data_flow_options; } __attribute__ ((packed)); -enum acx_key_action { - KEY_ADD_OR_REPLACE = 1, - KEY_REMOVE = 2, - KEY_SET_ID = 3, - MAX_KEY_ACTION = 0xffff, -}; - -enum acx_key_type { - KEY_WEP_DEFAULT = 0, - KEY_WEP_ADDR = 1, - KEY_AES_GROUP = 4, - KEY_AES_PAIRWISE = 5, - KEY_WEP_GROUP = 6, - KEY_TKIP_MIC_GROUP = 10, - KEY_TKIP_MIC_PAIRWISE = 11, -}; - -/* - * - * key_type_e key size key format - * ---------- --------- ---------- - * 0x00 5, 13, 29 Key data - * 0x01 5, 13, 29 Key data - * 0x04 16 16 bytes of key data - * 0x05 16 16 bytes of key data - * 0x0a 32 16 bytes of TKIP key data - * 8 bytes of RX MIC key data - * 8 bytes of TX MIC key data - * 0x0b 32 16 bytes of TKIP key data - * 8 bytes of RX MIC key data - * 8 bytes of TX MIC key data - * - */ - -struct acx_set_key { - /* Ignored for default WEP key */ - u8 addr[ETH_ALEN]; - - /* key_action_e */ - u16 key_action; - - u16 reserved_1; - - /* key size in bytes */ - u8 key_size; - - /* key_type_e */ - u8 key_type; - u8 ssid_profile; - - /* - * TKIP, AES: frame's key id field. - * For WEP default key: key id; - */ - u8 id; - u8 reserved_2[6]; - u8 key[MAX_KEY_SIZE]; - u16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY]; - u32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY]; -} __attribute__ ((packed)); - struct acx_current_tx_power { struct acx_header header; @@ -839,26 +756,6 @@ struct acx_tsf_info { u8 pad[3]; } __attribute__ ((packed)); -/* 802.11 PS */ -enum acx_ps_mode { - STATION_ACTIVE_MODE, - STATION_POWER_SAVE_MODE -}; - -struct acx_ps_params { - u8 ps_mode; /* STATION_* */ - u8 send_null_data; /* Do we have to send NULL data packet ? */ - u8 retries; /* Number of retires for the initial NULL data packet */ - - /* - * TUs during which the target stays awake after switching - * to power save mode. - */ - u8 hang_over_period; - u16 null_data_rate; - u8 pad[2]; -} __attribute__ ((packed)); - enum acx_wake_up_event { WAKE_UP_EVENT_BEACON_BITMAP = 0x01, /* Wake on every Beacon*/ WAKE_UP_EVENT_DTIM_BITMAP = 0x02, /* Wake on every DTIM*/ @@ -892,6 +789,7 @@ enum acx_preamble_type { struct acx_preamble { struct acx_header header; + /* * When set, the WiLink transmits the frames with a short preamble and * when cleared, the WiLink transmits the frames with a long preamble. @@ -1133,6 +1031,150 @@ struct acx_statistics { struct acx_rxpipe_statistics rxpipe; } __attribute__ ((packed)); +#define ACX_MAX_RATE_CLASSES 8 +#define ACX_RATE_MASK_UNSPECIFIED 0 +#define ACX_RATE_RETRY_LIMIT 10 + +struct acx_rate_class { + u32 enabled_rates; + u8 short_retry_limit; + u8 long_retry_limit; + u8 aflags; + u8 reserved; +}; + +struct acx_rate_policy { + struct acx_header header; + + u32 rate_class_cnt; + struct acx_rate_class rate_class[ACX_MAX_RATE_CLASSES]; +} __attribute__ ((packed)); + +struct wl1251_acx_memory { + __le16 num_stations; /* number of STAs to be supported. */ + u16 reserved_1; + + /* + * Nmber of memory buffers for the RX mem pool. + * The actual number may be less if there are + * not enough blocks left for the minimum num + * of TX ones. + */ + u8 rx_mem_block_num; + u8 reserved_2; + u8 num_tx_queues; /* From 1 to 16 */ + u8 host_if_options; /* HOST_IF* */ + u8 tx_min_mem_block_num; + u8 num_ssid_profiles; + __le16 debug_buffer_size; +} __attribute__ ((packed)); + + +#define ACX_RX_DESC_MIN 1 +#define ACX_RX_DESC_MAX 127 +#define ACX_RX_DESC_DEF 32 +struct wl1251_acx_rx_queue_config { + u8 num_descs; + u8 pad; + u8 type; + u8 priority; + __le32 dma_address; +} __attribute__ ((packed)); + +#define ACX_TX_DESC_MIN 1 +#define ACX_TX_DESC_MAX 127 +#define ACX_TX_DESC_DEF 16 +struct wl1251_acx_tx_queue_config { + u8 num_descs; + u8 pad[2]; + u8 attributes; +} __attribute__ ((packed)); + +#define MAX_TX_QUEUE_CONFIGS 5 +#define MAX_TX_QUEUES 4 +struct wl1251_acx_config_memory { + struct acx_header header; + + struct wl1251_acx_memory mem_config; + struct wl1251_acx_rx_queue_config rx_queue_config; + struct wl1251_acx_tx_queue_config tx_queue_config[MAX_TX_QUEUE_CONFIGS]; +} __attribute__ ((packed)); + +struct wl1251_acx_mem_map { + struct acx_header header; + + void *code_start; + void *code_end; + + void *wep_defkey_start; + void *wep_defkey_end; + + void *sta_table_start; + void *sta_table_end; + + void *packet_template_start; + void *packet_template_end; + + void *queue_memory_start; + void *queue_memory_end; + + void *packet_memory_pool_start; + void *packet_memory_pool_end; + + void *debug_buffer1_start; + void *debug_buffer1_end; + + void *debug_buffer2_start; + void *debug_buffer2_end; + + /* Number of blocks FW allocated for TX packets */ + u32 num_tx_mem_blocks; + + /* Number of blocks FW allocated for RX packets */ + u32 num_rx_mem_blocks; +} __attribute__ ((packed)); + +/************************************************************************* + + Host Interrupt Register (WiLink -> Host) + +**************************************************************************/ + +/* RX packet is ready in Xfer buffer #0 */ +#define WL1251_ACX_INTR_RX0_DATA BIT(0) + +/* TX result(s) are in the TX complete buffer */ +#define WL1251_ACX_INTR_TX_RESULT BIT(1) + +/* OBSOLETE */ +#define WL1251_ACX_INTR_TX_XFR BIT(2) + +/* RX packet is ready in Xfer buffer #1 */ +#define WL1251_ACX_INTR_RX1_DATA BIT(3) + +/* Event was entered to Event MBOX #A */ +#define WL1251_ACX_INTR_EVENT_A BIT(4) + +/* Event was entered to Event MBOX #B */ +#define WL1251_ACX_INTR_EVENT_B BIT(5) + +/* OBSOLETE */ +#define WL1251_ACX_INTR_WAKE_ON_HOST BIT(6) + +/* Trace meassge on MBOX #A */ +#define WL1251_ACX_INTR_TRACE_A BIT(7) + +/* Trace meassge on MBOX #B */ +#define WL1251_ACX_INTR_TRACE_B BIT(8) + +/* Command processing completion */ +#define WL1251_ACX_INTR_CMD_COMPLETE BIT(9) + +/* Init sequence is done */ +#define WL1251_ACX_INTR_INIT_COMPLETE BIT(14) + +#define WL1251_ACX_INTR_ALL 0xFFFFFFFF + enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, @@ -1210,36 +1252,41 @@ enum { }; -int wl12xx_acx_frame_rates(struct wl12xx *wl, u8 ctrl_rate, u8 ctrl_mod, +int wl1251_acx_frame_rates(struct wl1251 *wl, u8 ctrl_rate, u8 ctrl_mod, u8 mgt_rate, u8 mgt_mod); -int wl12xx_acx_station_id(struct wl12xx *wl); -int wl12xx_acx_default_key(struct wl12xx *wl, u8 key_id); -int wl12xx_acx_wake_up_conditions(struct wl12xx *wl, u8 listen_interval); -int wl12xx_acx_sleep_auth(struct wl12xx *wl, u8 sleep_auth); -int wl12xx_acx_fw_version(struct wl12xx *wl, char *buf, size_t len); -int wl12xx_acx_tx_power(struct wl12xx *wl, int power); -int wl12xx_acx_feature_cfg(struct wl12xx *wl); -int wl12xx_acx_mem_map(struct wl12xx *wl, void *mem_map, size_t len); -int wl12xx_acx_data_path_params(struct wl12xx *wl, +int wl1251_acx_station_id(struct wl1251 *wl); +int wl1251_acx_default_key(struct wl1251 *wl, u8 key_id); +int wl1251_acx_wake_up_conditions(struct wl1251 *wl, u8 wake_up_event, + u8 listen_interval); +int wl1251_acx_sleep_auth(struct wl1251 *wl, u8 sleep_auth); +int wl1251_acx_fw_version(struct wl1251 *wl, char *buf, size_t len); +int wl1251_acx_tx_power(struct wl1251 *wl, int power); +int wl1251_acx_feature_cfg(struct wl1251 *wl); +int wl1251_acx_mem_map(struct wl1251 *wl, + struct acx_header *mem_map, size_t len); +int wl1251_acx_data_path_params(struct wl1251 *wl, struct acx_data_path_params_resp *data_path); -int wl12xx_acx_rx_msdu_life_time(struct wl12xx *wl, u32 life_time); -int wl12xx_acx_rx_config(struct wl12xx *wl, u32 config, u32 filter); -int wl12xx_acx_pd_threshold(struct wl12xx *wl); -int wl12xx_acx_slot(struct wl12xx *wl, enum acx_slot_type slot_time); -int wl12xx_acx_group_address_tbl(struct wl12xx *wl); -int wl12xx_acx_service_period_timeout(struct wl12xx *wl); -int wl12xx_acx_rts_threshold(struct wl12xx *wl, u16 rts_threshold); -int wl12xx_acx_beacon_filter_opt(struct wl12xx *wl); -int wl12xx_acx_beacon_filter_table(struct wl12xx *wl); -int wl12xx_acx_sg_enable(struct wl12xx *wl); -int wl12xx_acx_sg_cfg(struct wl12xx *wl); -int wl12xx_acx_cca_threshold(struct wl12xx *wl); -int wl12xx_acx_bcn_dtim_options(struct wl12xx *wl); -int wl12xx_acx_aid(struct wl12xx *wl, u16 aid); -int wl12xx_acx_event_mbox_mask(struct wl12xx *wl, u32 event_mask); -int wl12xx_acx_set_preamble(struct wl12xx *wl, enum acx_preamble_type preamble); -int wl12xx_acx_cts_protect(struct wl12xx *wl, +int wl1251_acx_rx_msdu_life_time(struct wl1251 *wl, u32 life_time); +int wl1251_acx_rx_config(struct wl1251 *wl, u32 config, u32 filter); +int wl1251_acx_pd_threshold(struct wl1251 *wl); +int wl1251_acx_slot(struct wl1251 *wl, enum acx_slot_type slot_time); +int wl1251_acx_group_address_tbl(struct wl1251 *wl); +int wl1251_acx_service_period_timeout(struct wl1251 *wl); +int wl1251_acx_rts_threshold(struct wl1251 *wl, u16 rts_threshold); +int wl1251_acx_beacon_filter_opt(struct wl1251 *wl); +int wl1251_acx_beacon_filter_table(struct wl1251 *wl); +int wl1251_acx_sg_enable(struct wl1251 *wl); +int wl1251_acx_sg_cfg(struct wl1251 *wl); +int wl1251_acx_cca_threshold(struct wl1251 *wl); +int wl1251_acx_bcn_dtim_options(struct wl1251 *wl); +int wl1251_acx_aid(struct wl1251 *wl, u16 aid); +int wl1251_acx_event_mbox_mask(struct wl1251 *wl, u32 event_mask); +int wl1251_acx_set_preamble(struct wl1251 *wl, enum acx_preamble_type preamble); +int wl1251_acx_cts_protect(struct wl1251 *wl, enum acx_ctsprotect_type ctsprotect); -int wl12xx_acx_statistics(struct wl12xx *wl, struct acx_statistics *stats); +int wl1251_acx_statistics(struct wl1251 *wl, struct acx_statistics *stats); +int wl1251_acx_tsf_info(struct wl1251 *wl, u64 *mactime); +int wl1251_acx_rate_policies(struct wl1251 *wl); +int wl1251_acx_mem_cfg(struct wl1251 *wl); -#endif /* __WL12XX_ACX_H__ */ +#endif /* __WL1251_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1251_boot.c b/drivers/net/wireless/wl12xx/wl1251_boot.c new file mode 100644 index 000000000000..452d748e42c6 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1251_boot.c @@ -0,0 +1,530 @@ +/* + * This file is part of wl1251 + * + * Copyright (C) 2008 Nokia Corporation + * + * Contact: Kalle Valo <kalle.valo@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/gpio.h> + +#include "wl1251_reg.h" +#include "wl1251_boot.h" +#include "wl1251_io.h" +#include "wl1251_spi.h" +#include "wl1251_event.h" +#include "wl1251_acx.h" + +void wl1251_boot_target_enable_interrupts(struct wl1251 *wl) +{ + wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); + wl1251_reg_write32(wl, HI_CFG, HI_CFG_DEF_VAL); +} + +int wl1251_boot_soft_reset(struct wl1251 *wl) +{ + unsigned long timeout; + u32 boot_data; + + /* perform soft reset */ + wl1251_reg_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); + + /* SOFT_RESET is self clearing */ + timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); + while (1) { + boot_data = wl1251_reg_read32(wl, ACX_REG_SLV_SOFT_RESET); + wl1251_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); + if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) + break; + + if (time_after(jiffies, timeout)) { + /* 1.2 check pWhalBus->uSelfClearTime if the + * timeout was reached */ + wl1251_error("soft reset timeout"); + return -1; + } + + udelay(SOFT_RESET_STALL_TIME); + } + + /* disable Rx/Tx */ + wl1251_reg_write32(wl, ENABLE, 0x0); + + /* disable auto calibration on start*/ + wl1251_reg_write32(wl, SPARE_A2, 0xffff); + + return 0; +} + +int wl1251_boot_init_seq(struct wl1251 *wl) +{ + u32 scr_pad6, init_data, tmp, elp_cmd, ref_freq; + + /* + * col #1: INTEGER_DIVIDER + * col #2: FRACTIONAL_DIVIDER + * col #3: ATTN_BB + * col #4: ALPHA_BB + * col #5: STOP_TIME_BB + * col #6: BB_PLL_LOOP_FILTER + */ + static const u32 LUT[REF_FREQ_NUM][LUT_PARAM_NUM] = { + + { 83, 87381, 0xB, 5, 0xF00, 3}, /* REF_FREQ_19_2*/ + { 61, 141154, 0xB, 5, 0x1450, 2}, /* REF_FREQ_26_0*/ + { 41, 174763, 0xC, 6, 0x2D00, 1}, /* REF_FREQ_38_4*/ + { 40, 0, 0xC, 6, 0x2EE0, 1}, /* REF_FREQ_40_0*/ + { 47, 162280, 0xC, 6, 0x2760, 1} /* REF_FREQ_33_6 */ + }; + + /* read NVS params */ + scr_pad6 = wl1251_reg_read32(wl, SCR_PAD6); + wl1251_debug(DEBUG_BOOT, "scr_pad6 0x%x", scr_pad6); + + /* read ELP_CMD */ + elp_cmd = wl1251_reg_read32(wl, ELP_CMD); + wl1251_debug(DEBUG_BOOT, "elp_cmd 0x%x", elp_cmd); + + /* set the BB calibration time to be 300 usec (PLL_CAL_TIME) */ + ref_freq = scr_pad6 & 0x000000FF; + wl1251_debug(DEBUG_BOOT, "ref_freq 0x%x", ref_freq); + + wl1251_reg_write32(wl, PLL_CAL_TIME, 0x9); + + /* + * PG 1.2: set the clock buffer time to be 210 usec (CLK_BUF_TIME) + */ + wl1251_reg_write32(wl, CLK_BUF_TIME, 0x6); + + /* + * set the clock detect feature to work in the restart wu procedure + * (ELP_CFG_MODE[14]) and Select the clock source type + * (ELP_CFG_MODE[13:12]) + */ + tmp = ((scr_pad6 & 0x0000FF00) << 4) | 0x00004000; + wl1251_reg_write32(wl, ELP_CFG_MODE, tmp); + + /* PG 1.2: enable the BB PLL fix. Enable the PLL_LIMP_CLK_EN_CMD */ + elp_cmd |= 0x00000040; + wl1251_reg_write32(wl, ELP_CMD, elp_cmd); + + /* PG 1.2: Set the BB PLL stable time to be 1000usec + * (PLL_STABLE_TIME) */ + wl1251_reg_write32(wl, CFG_PLL_SYNC_CNT, 0x20); + + /* PG 1.2: read clock request time */ + init_data = wl1251_reg_read32(wl, CLK_REQ_TIME); + + /* + * PG 1.2: set the clock request time to be ref_clk_settling_time - + * 1ms = 4ms + */ + if (init_data > 0x21) + tmp = init_data - 0x21; + else + tmp = 0; + wl1251_reg_write32(wl, CLK_REQ_TIME, tmp); + + /* set BB PLL configurations in RF AFE */ + wl1251_reg_write32(wl, 0x003058cc, 0x4B5); + + /* set RF_AFE_REG_5 */ + wl1251_reg_write32(wl, 0x003058d4, 0x50); + + /* set RF_AFE_CTRL_REG_2 */ + wl1251_reg_write32(wl, 0x00305948, 0x11c001); + + /* + * change RF PLL and BB PLL divider for VCO clock and adjust VCO + * bais current(RF_AFE_REG_13) + */ + wl1251_reg_write32(wl, 0x003058f4, 0x1e); + + /* set BB PLL configurations */ + tmp = LUT[ref_freq][LUT_PARAM_INTEGER_DIVIDER] | 0x00017000; + wl1251_reg_write32(wl, 0x00305840, tmp); + + /* set fractional divider according to Appendix C-BB PLL + * Calculations + */ + tmp = LUT[ref_freq][LUT_PARAM_FRACTIONAL_DIVIDER]; + wl1251_reg_write32(wl, 0x00305844, tmp); + + /* set the initial data for the sigma delta */ + wl1251_reg_write32(wl, 0x00305848, 0x3039); + + /* + * set the accumulator attenuation value, calibration loop1 + * (alpha), calibration loop2 (beta), calibration loop3 (gamma) and + * the VCO gain + */ + tmp = (LUT[ref_freq][LUT_PARAM_ATTN_BB] << 16) | + (LUT[ref_freq][LUT_PARAM_ALPHA_BB] << 12) | 0x1; + wl1251_reg_write32(wl, 0x00305854, tmp); + + /* + * set the calibration stop time after holdoff time expires and set + * settling time HOLD_OFF_TIME_BB + */ + tmp = LUT[ref_freq][LUT_PARAM_STOP_TIME_BB] | 0x000A0000; + wl1251_reg_write32(wl, 0x00305858, tmp); + + /* + * set BB PLL Loop filter capacitor3- BB_C3[2:0] and set BB PLL + * constant leakage current to linearize PFD to 0uA - + * BB_ILOOPF[7:3] + */ + tmp = LUT[ref_freq][LUT_PARAM_BB_PLL_LOOP_FILTER] | 0x00000030; + wl1251_reg_write32(wl, 0x003058f8, tmp); + + /* + * set regulator output voltage for n divider to + * 1.35-BB_REFDIV[1:0], set charge pump current- BB_CPGAIN[4:2], + * set BB PLL Loop filter capacitor2- BB_C2[7:5], set gain of BB + * PLL auto-call to normal mode- BB_CALGAIN_3DB[8] + */ + wl1251_reg_write32(wl, 0x003058f0, 0x29); + + /* enable restart wakeup sequence (ELP_CMD[0]) */ + wl1251_reg_write32(wl, ELP_CMD, elp_cmd | 0x1); + + /* restart sequence completed */ + udelay(2000); + + return 0; +} + +static void wl1251_boot_set_ecpu_ctrl(struct wl1251 *wl, u32 flag) +{ + u32 cpu_ctrl; + + /* 10.5.0 run the firmware (I) */ + cpu_ctrl = wl1251_reg_read32(wl, ACX_REG_ECPU_CONTROL); + + /* 10.5.1 run the firmware (II) */ + cpu_ctrl &= ~flag; + wl1251_reg_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); +} + +int wl1251_boot_run_firmware(struct wl1251 *wl) +{ + int loop, ret; + u32 chip_id, interrupt; + + wl1251_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); + + chip_id = wl1251_reg_read32(wl, CHIP_ID_B); + + wl1251_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); + + if (chip_id != wl->chip_id) { + wl1251_error("chip id doesn't match after firmware boot"); + return -EIO; + } + + /* wait for init to complete */ + loop = 0; + while (loop++ < INIT_LOOP) { + udelay(INIT_LOOP_DELAY); + interrupt = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + + if (interrupt == 0xffffffff) { + wl1251_error("error reading hardware complete " + "init indication"); + return -EIO; + } + /* check that ACX_INTR_INIT_COMPLETE is enabled */ + else if (interrupt & WL1251_ACX_INTR_INIT_COMPLETE) { + wl1251_reg_write32(wl, ACX_REG_INTERRUPT_ACK, + WL1251_ACX_INTR_INIT_COMPLETE); + break; + } + } + + if (loop >= INIT_LOOP) { + wl1251_error("timeout waiting for the hardware to " + "complete initialization"); + return -EIO; + } + + /* get hardware config command mail box */ + wl->cmd_box_addr = wl1251_reg_read32(wl, REG_COMMAND_MAILBOX_PTR); + + /* get hardware config event mail box */ + wl->event_box_addr = wl1251_reg_read32(wl, REG_EVENT_MAILBOX_PTR); + + /* set the working partition to its "running" mode offset */ + wl1251_set_partition(wl, WL1251_PART_WORK_MEM_START, + WL1251_PART_WORK_MEM_SIZE, + WL1251_PART_WORK_REG_START, + WL1251_PART_WORK_REG_SIZE); + + wl1251_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x", + wl->cmd_box_addr, wl->event_box_addr); + + wl1251_acx_fw_version(wl, wl->fw_ver, sizeof(wl->fw_ver)); + + /* + * in case of full asynchronous mode the firmware event must be + * ready to receive event from the command mailbox + */ + + /* enable gpio interrupts */ + wl1251_enable_interrupts(wl); + + /* Enable target's interrupts */ + wl->intr_mask = WL1251_ACX_INTR_RX0_DATA | + WL1251_ACX_INTR_RX1_DATA | + WL1251_ACX_INTR_TX_RESULT | + WL1251_ACX_INTR_EVENT_A | + WL1251_ACX_INTR_EVENT_B | + WL1251_ACX_INTR_INIT_COMPLETE; + wl1251_boot_target_enable_interrupts(wl); + + /* unmask all mbox events */ + wl->event_mask = 0xffffffff; + + ret = wl1251_event_unmask(wl); + if (ret < 0) { + wl1251_error("EVENT mask setting failed"); + return ret; + } + + wl1251_event_mbox_config(wl); + + /* firmware startup completed */ + return 0; +} + +static int wl1251_boot_upload_firmware(struct wl1251 *wl) +{ + int addr, chunk_num, partition_limit; + size_t fw_data_len; + u8 *p; + + /* whal_FwCtrl_LoadFwImageSm() */ + + wl1251_debug(DEBUG_BOOT, "chip id before fw upload: 0x%x", + wl1251_reg_read32(wl, CHIP_ID_B)); + + /* 10.0 check firmware length and set partition */ + fw_data_len = (wl->fw[4] << 24) | (wl->fw[5] << 16) | + (wl->fw[6] << 8) | (wl->fw[7]); + + wl1251_debug(DEBUG_BOOT, "fw_data_len %zu chunk_size %d", fw_data_len, + CHUNK_SIZE); + + if ((fw_data_len % 4) != 0) { + wl1251_error("firmware length not multiple of four"); + return -EIO; + } + + wl1251_set_partition(wl, WL1251_PART_DOWN_MEM_START, + WL1251_PART_DOWN_MEM_SIZE, + WL1251_PART_DOWN_REG_START, + WL1251_PART_DOWN_REG_SIZE); + + /* 10.1 set partition limit and chunk num */ + chunk_num = 0; + partition_limit = WL1251_PART_DOWN_MEM_SIZE; + + while (chunk_num < fw_data_len / CHUNK_SIZE) { + /* 10.2 update partition, if needed */ + addr = WL1251_PART_DOWN_MEM_START + + (chunk_num + 2) * CHUNK_SIZE; + if (addr > partition_limit) { + addr = WL1251_PART_DOWN_MEM_START + + chunk_num * CHUNK_SIZE; + partition_limit = chunk_num * CHUNK_SIZE + + WL1251_PART_DOWN_MEM_SIZE; + wl1251_set_partition(wl, + addr, + WL1251_PART_DOWN_MEM_SIZE, + WL1251_PART_DOWN_REG_START, + WL1251_PART_DOWN_REG_SIZE); + } + + /* 10.3 upload the chunk */ + addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; + p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; + wl1251_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", + p, addr); + wl1251_mem_write(wl, addr, p, CHUNK_SIZE); + + chunk_num++; + } + + /* 10.4 upload the last chunk */ + addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; + p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; + wl1251_debug(DEBUG_BOOT, "uploading fw last chunk (%zu B) 0x%p to 0x%x", + fw_data_len % CHUNK_SIZE, p, addr); + wl1251_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE); + + return 0; +} + +static int wl1251_boot_upload_nvs(struct wl1251 *wl) +{ + size_t nvs_len, nvs_bytes_written, burst_len; + int nvs_start, i; + u32 dest_addr, val; + u8 *nvs_ptr, *nvs; + + nvs = wl->nvs; + if (nvs == NULL) + return -ENODEV; + + nvs_ptr = nvs; + + nvs_len = wl->nvs_len; + nvs_start = wl->fw_len; + + /* + * Layout before the actual NVS tables: + * 1 byte : burst length. + * 2 bytes: destination address. + * n bytes: data to burst copy. + * + * This is ended by a 0 length, then the NVS tables. + */ + + while (nvs_ptr[0]) { + burst_len = nvs_ptr[0]; + dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8)); + + /* We move our pointer to the data */ + nvs_ptr += 3; + + for (i = 0; i < burst_len; i++) { + val = (nvs_ptr[0] | (nvs_ptr[1] << 8) + | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); + + wl1251_debug(DEBUG_BOOT, + "nvs burst write 0x%x: 0x%x", + dest_addr, val); + wl1251_mem_write32(wl, dest_addr, val); + + nvs_ptr += 4; + dest_addr += 4; + } + } + + /* + * We've reached the first zero length, the first NVS table + * is 7 bytes further. + */ + nvs_ptr += 7; + nvs_len -= nvs_ptr - nvs; + nvs_len = ALIGN(nvs_len, 4); + + /* Now we must set the partition correctly */ + wl1251_set_partition(wl, nvs_start, + WL1251_PART_DOWN_MEM_SIZE, + WL1251_PART_DOWN_REG_START, + WL1251_PART_DOWN_REG_SIZE); + + /* And finally we upload the NVS tables */ + nvs_bytes_written = 0; + while (nvs_bytes_written < nvs_len) { + val = (nvs_ptr[0] | (nvs_ptr[1] << 8) + | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); + + val = cpu_to_le32(val); + + wl1251_debug(DEBUG_BOOT, + "nvs write table 0x%x: 0x%x", + nvs_start, val); + wl1251_mem_write32(wl, nvs_start, val); + + nvs_ptr += 4; + nvs_bytes_written += 4; + nvs_start += 4; + } + + return 0; +} + +int wl1251_boot(struct wl1251 *wl) +{ + int ret = 0, minor_minor_e2_ver; + u32 tmp, boot_data; + + /* halt embedded ARM CPU while loading firmware */ + wl1251_reg_write32(wl, ACX_REG_ECPU_CONTROL, ECPU_CONTROL_HALT); + + ret = wl1251_boot_soft_reset(wl); + if (ret < 0) + goto out; + + /* 2. start processing NVS file */ + ret = wl1251_boot_upload_nvs(wl); + if (ret < 0) + goto out; + + /* write firmware's last address (ie. it's length) to + * ACX_EEPROMLESS_IND_REG */ + wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len); + + /* 6. read the EEPROM parameters */ + tmp = wl1251_reg_read32(wl, SCR_PAD2); + + /* 7. read bootdata */ + wl->boot_attr.radio_type = (tmp & 0x0000FF00) >> 8; + wl->boot_attr.major = (tmp & 0x00FF0000) >> 16; + tmp = wl1251_reg_read32(wl, SCR_PAD3); + + /* 8. check bootdata and call restart sequence */ + wl->boot_attr.minor = (tmp & 0x00FF0000) >> 16; + minor_minor_e2_ver = (tmp & 0xFF000000) >> 24; + + wl1251_debug(DEBUG_BOOT, "radioType 0x%x majorE2Ver 0x%x " + "minorE2Ver 0x%x minor_minor_e2_ver 0x%x", + wl->boot_attr.radio_type, wl->boot_attr.major, + wl->boot_attr.minor, minor_minor_e2_ver); + + ret = wl1251_boot_init_seq(wl); + if (ret < 0) + goto out; + + /* 9. NVS processing done */ + boot_data = wl1251_reg_read32(wl, ACX_REG_ECPU_CONTROL); + + wl1251_debug(DEBUG_BOOT, "halt boot_data 0x%x", boot_data); + + /* 10. check that ECPU_CONTROL_HALT bits are set in + * pWhalBus->uBootData and start uploading firmware + */ + if ((boot_data & ECPU_CONTROL_HALT) == 0) { + wl1251_error("boot failed, ECPU_CONTROL_HALT not set"); + ret = -EIO; + goto out; + } + + ret = wl1251_boot_upload_firmware(wl); + if (ret < 0) + goto out; + + /* 10.5 start firmware */ + ret = wl1251_boot_run_firmware(wl); + if (ret < 0) + goto out; + +out: + return ret; +} diff --git a/drivers/net/wireless/wl12xx/boot.h b/drivers/net/wireless/wl12xx/wl1251_boot.h index 4fa73132baae..90063697e8f2 100644 --- a/drivers/net/wireless/wl12xx/boot.h +++ b/drivers/net/wireless/wl12xx/wl1251_boot.h @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (C) 2008 Nokia Corporation * @@ -24,12 +24,13 @@ #ifndef __BOOT_H__ #define __BOOT_H__ -#include "wl12xx.h" +#include "wl1251.h" -int wl12xx_boot_soft_reset(struct wl12xx *wl); -int wl12xx_boot_init_seq(struct wl12xx *wl); -int wl12xx_boot_run_firmware(struct wl12xx *wl); -void wl12xx_boot_target_enable_interrupts(struct wl12xx *wl); +int wl1251_boot_soft_reset(struct wl1251 *wl); +int wl1251_boot_init_seq(struct wl1251 *wl); +int wl1251_boot_run_firmware(struct wl1251 *wl); +void wl1251_boot_target_enable_interrupts(struct wl1251 *wl); +int wl1251_boot(struct wl1251 *wl); /* number of times we try to read the INIT interrupt */ #define INIT_LOOP 20000 diff --git a/drivers/net/wireless/wl12xx/wl1251_cmd.c b/drivers/net/wireless/wl12xx/wl1251_cmd.c new file mode 100644 index 000000000000..770f260726bd --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1251_cmd.c @@ -0,0 +1,412 @@ +#include "wl1251_cmd.h" + +#include <linux/module.h> +#include <linux/crc7.h> + +#include "wl1251.h" +#include "wl1251_reg.h" +#include "wl1251_io.h" +#include "wl1251_ps.h" +#include "wl1251_acx.h" + +/** + * send command to firmware + * + * @wl: wl struct + * @id: command id + * @buf: buffer containing the command, must work with dma + * @len: length of the buffer + */ +int wl1251_cmd_send(struct wl1251 *wl, u16 id, void *buf, size_t len) +{ + struct wl1251_cmd_header *cmd; + unsigned long timeout; + u32 intr; + int ret = 0; + + cmd = buf; + cmd->id = id; + cmd->status = 0; + + WARN_ON(len % 4 != 0); + + wl1251_mem_write(wl, wl->cmd_box_addr, buf, len); + + wl1251_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); + + timeout = jiffies + msecs_to_jiffies(WL1251_COMMAND_TIMEOUT); + + intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + while (!(intr & WL1251_ACX_INTR_CMD_COMPLETE)) { + if (time_after(jiffies, timeout)) { + wl1251_error("command complete timeout"); + ret = -ETIMEDOUT; + goto out; + } + + msleep(1); + + intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + } + + wl1251_reg_write32(wl, ACX_REG_INTERRUPT_ACK, + WL1251_ACX_INTR_CMD_COMPLETE); + +out: + return ret; +} + +/** + * send test command to firmware + * + * @wl: wl struct + * @buf: buffer containing the command, with all headers, must work with dma + * @len: length of the buffer + * @answer: is answer needed + */ +int wl1251_cmd_test(struct wl1251 *wl, void *buf, size_t buf_len, u8 answer) +{ + int ret; + + wl1251_debug(DEBUG_CMD, "cmd test"); + + ret = wl1251_cmd_send(wl, CMD_TEST, buf, buf_len); + + if (ret < 0) { + wl1251_warning("TEST command failed"); + return ret; + } + + if (answer) { + struct wl1251_command *cmd_answer; + + /* + * The test command got in, we can read the answer. + * The answer would be a wl1251_command, where the + * parameter array contains the actual answer. + */ + wl1251_mem_read(wl, wl->cmd_box_addr, buf, buf_len); + + cmd_answer = buf; + + if (cmd_answer->header.status != CMD_STATUS_SUCCESS) + wl1251_error("TEST command answer error: %d", + cmd_answer->header.status); + } + + return 0; +} + +/** + * read acx from firmware + * + * @wl: wl struct + * @id: acx id + * @buf: buffer for the response, including all headers, must work with dma + * @len: lenght of buf + */ +int wl1251_cmd_interrogate(struct wl1251 *wl, u16 id, void *buf, size_t len) +{ + struct acx_header *acx = buf; + int ret; + + wl1251_debug(DEBUG_CMD, "cmd interrogate"); + + acx->id = id; + + /* payload length, does not include any headers */ + acx->len = len - sizeof(*acx); + + ret = wl1251_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx)); + if (ret < 0) { + wl1251_error("INTERROGATE command failed"); + goto out; + } + + /* the interrogate command got in, we can read the answer */ + wl1251_mem_read(wl, wl->cmd_box_addr, buf, len); + + acx = buf; + if (acx->cmd.status != CMD_STATUS_SUCCESS) + wl1251_error("INTERROGATE command error: %d", + acx->cmd.status); + +out: + return ret; +} + +/** + * write acx value to firmware + * + * @wl: wl struct + * @id: acx id + * @buf: buffer containing acx, including all headers, must work with dma + * @len: length of buf + */ +int wl1251_cmd_configure(struct wl1251 *wl, u16 id, void *buf, size_t len) +{ + struct acx_header *acx = buf; + int ret; + + wl1251_debug(DEBUG_CMD, "cmd configure"); + + acx->id = id; + + /* payload length, does not include any headers */ + acx->len = len - sizeof(*acx); + + ret = wl1251_cmd_send(wl, CMD_CONFIGURE, acx, len); + if (ret < 0) { + wl1251_warning("CONFIGURE command NOK"); + return ret; + } + + return 0; +} + +int wl1251_cmd_vbm(struct wl1251 *wl, u8 identity, + void *bitmap, u16 bitmap_len, u8 bitmap_control) +{ + struct wl1251_cmd_vbm_update *vbm; + int ret; + + wl1251_debug(DEBUG_CMD, "cmd vbm"); + + vbm = kzalloc(sizeof(*vbm), GFP_KERNEL); + if (!vbm) { + ret = -ENOMEM; + goto out; + } + + /* Count and period will be filled by the target */ + vbm->tim.bitmap_ctrl = bitmap_control; + if (bitmap_len > PARTIAL_VBM_MAX) { + wl1251_warning("cmd vbm len is %d B, truncating to %d", + bitmap_len, PARTIAL_VBM_MAX); + bitmap_len = PARTIAL_VBM_MAX; + } + memcpy(vbm->tim.pvb_field, bitmap, bitmap_len); + vbm->tim.identity = identity; + vbm->tim.length = bitmap_len + 3; + + vbm->len = cpu_to_le16(bitmap_len + 5); + + ret = wl1251_cmd_send(wl, CMD_VBM, vbm, sizeof(*vbm)); + if (ret < 0) { + wl1251_error("VBM command failed"); + goto out; + } + +out: + kfree(vbm); + return 0; +} + +int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable) +{ + struct cmd_enabledisable_path *cmd; + int ret; + u16 cmd_rx, cmd_tx; + + wl1251_debug(DEBUG_CMD, "cmd data path"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->channel = channel; + + if (enable) { + cmd_rx = CMD_ENABLE_RX; + cmd_tx = CMD_ENABLE_TX; + } else { + cmd_rx = CMD_DISABLE_RX; + cmd_tx = CMD_DISABLE_TX; + } + + ret = wl1251_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1251_error("rx %s cmd for channel %d failed", + enable ? "start" : "stop", channel); + goto out; + } + + wl1251_debug(DEBUG_BOOT, "rx %s cmd channel %d", + enable ? "start" : "stop", channel); + + ret = wl1251_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1251_error("tx %s cmd for channel %d failed", + enable ? "start" : "stop", channel); + return ret; + } + + wl1251_debug(DEBUG_BOOT, "tx %s cmd channel %d", + enable ? "start" : "stop", channel); + +out: + kfree(cmd); + return ret; +} + +int wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel, + u16 beacon_interval, u8 dtim_interval) +{ + struct cmd_join *join; + int ret, i; + u8 *bssid; + + join = kzalloc(sizeof(*join), GFP_KERNEL); + if (!join) { + ret = -ENOMEM; + goto out; + } + + wl1251_debug(DEBUG_CMD, "cmd join%s ch %d %d/%d", + bss_type == BSS_TYPE_IBSS ? " ibss" : "", + channel, beacon_interval, dtim_interval); + + /* Reverse order BSSID */ + bssid = (u8 *) &join->bssid_lsb; + for (i = 0; i < ETH_ALEN; i++) + bssid[i] = wl->bssid[ETH_ALEN - i - 1]; + + join->rx_config_options = wl->rx_config; + join->rx_filter_options = wl->rx_filter; + + /* + * FIXME: disable temporarily all filters because after commit + * 9cef8737 "mac80211: fix managed mode BSSID handling" broke + * association. The filter logic needs to be implemented properly + * and once that is done, this hack can be removed. + */ + join->rx_config_options = 0; + join->rx_filter_options = WL1251_DEFAULT_RX_FILTER; + + join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS | + RATE_MASK_5_5MBPS | RATE_MASK_11MBPS; + + join->beacon_interval = beacon_interval; + join->dtim_interval = dtim_interval; + join->bss_type = bss_type; + join->channel = channel; + join->ctrl = JOIN_CMD_CTRL_TX_FLUSH; + + ret = wl1251_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join)); + if (ret < 0) { + wl1251_error("failed to initiate cmd join"); + goto out; + } + +out: + kfree(join); + return ret; +} + +int wl1251_cmd_ps_mode(struct wl1251 *wl, u8 ps_mode) +{ + struct wl1251_cmd_ps_params *ps_params = NULL; + int ret = 0; + + wl1251_debug(DEBUG_CMD, "cmd set ps mode"); + + ps_params = kzalloc(sizeof(*ps_params), GFP_KERNEL); + if (!ps_params) { + ret = -ENOMEM; + goto out; + } + + ps_params->ps_mode = ps_mode; + ps_params->send_null_data = 1; + ps_params->retries = 5; + ps_params->hang_over_period = 128; + ps_params->null_data_rate = 1; /* 1 Mbps */ + + ret = wl1251_cmd_send(wl, CMD_SET_PS_MODE, ps_params, + sizeof(*ps_params)); + if (ret < 0) { + wl1251_error("cmd set_ps_mode failed"); + goto out; + } + +out: + kfree(ps_params); + return ret; +} + +int wl1251_cmd_read_memory(struct wl1251 *wl, u32 addr, void *answer, + size_t len) +{ + struct cmd_read_write_memory *cmd; + int ret = 0; + + wl1251_debug(DEBUG_CMD, "cmd read memory"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + WARN_ON(len > MAX_READ_SIZE); + len = min_t(size_t, len, MAX_READ_SIZE); + + cmd->addr = addr; + cmd->size = len; + + ret = wl1251_cmd_send(wl, CMD_READ_MEMORY, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1251_error("read memory command failed: %d", ret); + goto out; + } + + /* the read command got in, we can now read the answer */ + wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); + + if (cmd->header.status != CMD_STATUS_SUCCESS) + wl1251_error("error in read command result: %d", + cmd->header.status); + + memcpy(answer, cmd->value, len); + +out: + kfree(cmd); + return ret; +} + +int wl1251_cmd_template_set(struct wl1251 *wl, u16 cmd_id, + void *buf, size_t buf_len) +{ + struct wl1251_cmd_packet_template *cmd; + size_t cmd_len; + int ret = 0; + + wl1251_debug(DEBUG_CMD, "cmd template %d", cmd_id); + + WARN_ON(buf_len > WL1251_MAX_TEMPLATE_SIZE); + buf_len = min_t(size_t, buf_len, WL1251_MAX_TEMPLATE_SIZE); + cmd_len = ALIGN(sizeof(*cmd) + buf_len, 4); + + cmd = kzalloc(cmd_len, GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->size = cpu_to_le16(buf_len); + + if (buf) + memcpy(cmd->data, buf, buf_len); + + ret = wl1251_cmd_send(wl, cmd_id, cmd, cmd_len); + if (ret < 0) { + wl1251_warning("cmd set_template failed: %d", ret); + goto out; + } + +out: + kfree(cmd); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/wl1251_cmd.h index aa307dcd081f..dff798ad0ef5 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/wl1251_cmd.h @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation @@ -22,37 +22,32 @@ * */ -#ifndef __WL12XX_CMD_H__ -#define __WL12XX_CMD_H__ +#ifndef __WL1251_CMD_H__ +#define __WL1251_CMD_H__ -#include "wl12xx.h" +#include "wl1251.h" -int wl12xx_cmd_send(struct wl12xx *wl, u16 type, void *buf, size_t buf_len); -int wl12xx_cmd_test(struct wl12xx *wl, void *buf, size_t buf_len, u8 answer); -int wl12xx_cmd_interrogate(struct wl12xx *wl, u16 ie_id, u16 ie_len, - void *answer); -int wl12xx_cmd_configure(struct wl12xx *wl, void *ie, int ie_len); -int wl12xx_cmd_vbm(struct wl12xx *wl, u8 identity, +struct acx_header; + +int wl1251_cmd_send(struct wl1251 *wl, u16 type, void *buf, size_t buf_len); +int wl1251_cmd_test(struct wl1251 *wl, void *buf, size_t buf_len, u8 answer); +int wl1251_cmd_interrogate(struct wl1251 *wl, u16 id, void *buf, size_t len); +int wl1251_cmd_configure(struct wl1251 *wl, u16 id, void *buf, size_t len); +int wl1251_cmd_vbm(struct wl1251 *wl, u8 identity, void *bitmap, u16 bitmap_len, u8 bitmap_control); -int wl12xx_cmd_data_path(struct wl12xx *wl, u8 channel, u8 enable); -int wl12xx_cmd_join(struct wl12xx *wl, u8 bss_type, u8 dtim_interval, - u16 beacon_interval, u8 wait); -int wl12xx_cmd_ps_mode(struct wl12xx *wl, u8 ps_mode); -int wl12xx_cmd_read_memory(struct wl12xx *wl, u32 addr, u32 len, void *answer); -int wl12xx_cmd_template_set(struct wl12xx *wl, u16 cmd_id, +int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable); +int wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel, + u16 beacon_interval, u8 dtim_interval); +int wl1251_cmd_ps_mode(struct wl1251 *wl, u8 ps_mode); +int wl1251_cmd_read_memory(struct wl1251 *wl, u32 addr, void *answer, + size_t len); +int wl1251_cmd_template_set(struct wl1251 *wl, u16 cmd_id, void *buf, size_t buf_len); /* unit ms */ -#define WL12XX_COMMAND_TIMEOUT 2000 - -#define WL12XX_MAX_TEMPLATE_SIZE 300 +#define WL1251_COMMAND_TIMEOUT 2000 -struct wl12xx_cmd_packet_template { - __le16 size; - u8 template[WL12XX_MAX_TEMPLATE_SIZE]; -} __attribute__ ((packed)); - -enum wl12xx_commands { +enum wl1251_commands { CMD_RESET = 0, CMD_INTERROGATE = 1, /*use this to read information elements*/ CMD_CONFIGURE = 2, /*use this to write information elements*/ @@ -100,9 +95,15 @@ enum wl12xx_commands { #define MAX_CMD_PARAMS 572 -struct wl12xx_command { +struct wl1251_cmd_header { u16 id; u16 status; + /* payload */ + u8 data[0]; +} __attribute__ ((packed)); + +struct wl1251_command { + struct wl1251_cmd_header header; u8 parameters[MAX_CMD_PARAMS]; }; @@ -144,6 +145,8 @@ enum { #define MAX_READ_SIZE 256 struct cmd_read_write_memory { + struct wl1251_cmd_header header; + /* The address of the memory to read from or write to.*/ u32 addr; @@ -211,6 +214,8 @@ struct basic_scan_channel_parameters { #define SCAN_MAX_NUM_OF_CHANNELS 16 struct cmd_scan { + struct wl1251_cmd_header header; + struct basic_scan_parameters params; struct basic_scan_channel_parameters channels[SCAN_MAX_NUM_OF_CHANNELS]; } __attribute__ ((packed)); @@ -227,6 +232,8 @@ enum { struct cmd_join { + struct wl1251_cmd_header header; + u32 bssid_lsb; u16 bssid_msb; u16 beacon_interval; /* in TBTTs */ @@ -261,5 +268,140 @@ struct cmd_join { u8 reserved; } __attribute__ ((packed)); +struct cmd_enabledisable_path { + struct wl1251_cmd_header header; + + u8 channel; + u8 padding[3]; +} __attribute__ ((packed)); + +#define WL1251_MAX_TEMPLATE_SIZE 300 + +struct wl1251_cmd_packet_template { + struct wl1251_cmd_header header; + + __le16 size; + u8 data[0]; +} __attribute__ ((packed)); + +#define TIM_ELE_ID 5 +#define PARTIAL_VBM_MAX 251 + +struct wl1251_tim { + u8 identity; + u8 length; + u8 dtim_count; + u8 dtim_period; + u8 bitmap_ctrl; + u8 pvb_field[PARTIAL_VBM_MAX]; /* Partial Virtual Bitmap */ +} __attribute__ ((packed)); + +/* Virtual Bit Map update */ +struct wl1251_cmd_vbm_update { + struct wl1251_cmd_header header; + __le16 len; + u8 padding[2]; + struct wl1251_tim tim; +} __attribute__ ((packed)); + +enum wl1251_cmd_ps_mode { + STATION_ACTIVE_MODE, + STATION_POWER_SAVE_MODE +}; + +struct wl1251_cmd_ps_params { + struct wl1251_cmd_header header; + + u8 ps_mode; /* STATION_* */ + u8 send_null_data; /* Do we have to send NULL data packet ? */ + u8 retries; /* Number of retires for the initial NULL data packet */ + + /* + * TUs during which the target stays awake after switching + * to power save mode. + */ + u8 hang_over_period; + u16 null_data_rate; + u8 pad[2]; +} __attribute__ ((packed)); + +struct wl1251_cmd_trigger_scan_to { + struct wl1251_cmd_header header; + + u32 timeout; +}; + +/* HW encryption keys */ +#define NUM_ACCESS_CATEGORIES_COPY 4 +#define MAX_KEY_SIZE 32 + +/* When set, disable HW encryption */ +#define DF_ENCRYPTION_DISABLE 0x01 +/* When set, disable HW decryption */ +#define DF_SNIFF_MODE_ENABLE 0x80 + +enum wl1251_cmd_key_action { + KEY_ADD_OR_REPLACE = 1, + KEY_REMOVE = 2, + KEY_SET_ID = 3, + MAX_KEY_ACTION = 0xffff, +}; + +enum wl1251_cmd_key_type { + KEY_WEP_DEFAULT = 0, + KEY_WEP_ADDR = 1, + KEY_AES_GROUP = 4, + KEY_AES_PAIRWISE = 5, + KEY_WEP_GROUP = 6, + KEY_TKIP_MIC_GROUP = 10, + KEY_TKIP_MIC_PAIRWISE = 11, +}; + +/* + * + * key_type_e key size key format + * ---------- --------- ---------- + * 0x00 5, 13, 29 Key data + * 0x01 5, 13, 29 Key data + * 0x04 16 16 bytes of key data + * 0x05 16 16 bytes of key data + * 0x0a 32 16 bytes of TKIP key data + * 8 bytes of RX MIC key data + * 8 bytes of TX MIC key data + * 0x0b 32 16 bytes of TKIP key data + * 8 bytes of RX MIC key data + * 8 bytes of TX MIC key data + * + */ + +struct wl1251_cmd_set_keys { + struct wl1251_cmd_header header; + + /* Ignored for default WEP key */ + u8 addr[ETH_ALEN]; + + /* key_action_e */ + u16 key_action; + + u16 reserved_1; + + /* key size in bytes */ + u8 key_size; + + /* key_type_e */ + u8 key_type; + u8 ssid_profile; + + /* + * TKIP, AES: frame's key id field. + * For WEP default key: key id; + */ + u8 id; + u8 reserved_2[6]; + u8 key[MAX_KEY_SIZE]; + u16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY]; + u32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY]; +} __attribute__ ((packed)); + -#endif /* __WL12XX_CMD_H__ */ +#endif /* __WL1251_CMD_H__ */ diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/wl1251_debugfs.c index cdb368ce4dae..a00723059f83 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/wl1251_debugfs.c @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (C) 2009 Nokia Corporation * @@ -21,15 +21,16 @@ * */ -#include "debugfs.h" +#include "wl1251_debugfs.h" #include <linux/skbuff.h> -#include "wl12xx.h" -#include "acx.h" +#include "wl1251.h" +#include "wl1251_acx.h" +#include "wl1251_ps.h" /* ms */ -#define WL12XX_DEBUGFS_STATS_LIFETIME 1000 +#define WL1251_DEBUGFS_STATS_LIFETIME 1000 /* debugfs macros idea from mac80211 */ @@ -37,7 +38,7 @@ static ssize_t name## _read(struct file *file, char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ - struct wl12xx *wl = file->private_data; \ + struct wl1251 *wl = file->private_data; \ char buf[buflen]; \ int res; \ \ @@ -47,7 +48,7 @@ static ssize_t name## _read(struct file *file, char __user *userbuf, \ \ static const struct file_operations name## _ops = { \ .read = name## _read, \ - .open = wl12xx_open_file_generic, \ + .open = wl1251_open_file_generic, \ }; #define DEBUGFS_ADD(name, parent) \ @@ -70,11 +71,11 @@ static ssize_t sub## _ ##name## _read(struct file *file, \ char __user *userbuf, \ size_t count, loff_t *ppos) \ { \ - struct wl12xx *wl = file->private_data; \ + struct wl1251 *wl = file->private_data; \ char buf[buflen]; \ int res; \ \ - wl12xx_debugfs_update_stats(wl); \ + wl1251_debugfs_update_stats(wl); \ \ res = scnprintf(buf, buflen, fmt "\n", \ wl->stats.fw_stats->sub.name); \ @@ -83,7 +84,7 @@ static ssize_t sub## _ ##name## _read(struct file *file, \ \ static const struct file_operations sub## _ ##name## _ops = { \ .read = sub## _ ##name## _read, \ - .open = wl12xx_open_file_generic, \ + .open = wl1251_open_file_generic, \ }; #define DEBUGFS_FWSTATS_ADD(sub, name) \ @@ -92,21 +93,30 @@ static const struct file_operations sub## _ ##name## _ops = { \ #define DEBUGFS_FWSTATS_DEL(sub, name) \ DEBUGFS_DEL(sub## _ ##name) -static void wl12xx_debugfs_update_stats(struct wl12xx *wl) +static void wl1251_debugfs_update_stats(struct wl1251 *wl) { + int ret; + mutex_lock(&wl->mutex); - if (wl->state == WL12XX_STATE_ON && + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + if (wl->state == WL1251_STATE_ON && time_after(jiffies, wl->stats.fw_stats_update + - msecs_to_jiffies(WL12XX_DEBUGFS_STATS_LIFETIME))) { - wl12xx_acx_statistics(wl, wl->stats.fw_stats); + msecs_to_jiffies(WL1251_DEBUGFS_STATS_LIFETIME))) { + wl1251_acx_statistics(wl, wl->stats.fw_stats); wl->stats.fw_stats_update = jiffies; } + wl1251_ps_elp_sleep(wl); + +out: mutex_unlock(&wl->mutex); } -static int wl12xx_open_file_generic(struct inode *inode, struct file *file) +static int wl1251_open_file_generic(struct inode *inode, struct file *file) { file->private_data = inode->i_private; return 0; @@ -211,7 +221,7 @@ DEBUGFS_READONLY_FILE(excessive_retries, 20, "%u", static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { - struct wl12xx *wl = file->private_data; + struct wl1251 *wl = file->private_data; u32 queue_len; char buf[20]; int res; @@ -224,10 +234,10 @@ static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf, static const struct file_operations tx_queue_len_ops = { .read = tx_queue_len_read, - .open = wl12xx_open_file_generic, + .open = wl1251_open_file_generic, }; -static void wl12xx_debugfs_delete_files(struct wl12xx *wl) +static void wl1251_debugfs_delete_files(struct wl1251 *wl) { DEBUGFS_FWSTATS_DEL(tx, internal_desc_overflow); @@ -325,7 +335,7 @@ static void wl12xx_debugfs_delete_files(struct wl12xx *wl) DEBUGFS_DEL(excessive_retries); } -static int wl12xx_debugfs_add_files(struct wl12xx *wl) +static int wl1251_debugfs_add_files(struct wl1251 *wl) { int ret = 0; @@ -426,19 +436,19 @@ static int wl12xx_debugfs_add_files(struct wl12xx *wl) out: if (ret < 0) - wl12xx_debugfs_delete_files(wl); + wl1251_debugfs_delete_files(wl); return ret; } -void wl12xx_debugfs_reset(struct wl12xx *wl) +void wl1251_debugfs_reset(struct wl1251 *wl) { memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats)); wl->stats.retry_count = 0; wl->stats.excessive_retries = 0; } -int wl12xx_debugfs_init(struct wl12xx *wl) +int wl1251_debugfs_init(struct wl1251 *wl) { int ret; @@ -469,7 +479,7 @@ int wl12xx_debugfs_init(struct wl12xx *wl) wl->stats.fw_stats_update = jiffies; - ret = wl12xx_debugfs_add_files(wl); + ret = wl1251_debugfs_add_files(wl); if (ret < 0) goto err_file; @@ -492,9 +502,9 @@ err: return ret; } -void wl12xx_debugfs_exit(struct wl12xx *wl) +void wl1251_debugfs_exit(struct wl1251 *wl) { - wl12xx_debugfs_delete_files(wl); + wl1251_debugfs_delete_files(wl); kfree(wl->stats.fw_stats); wl->stats.fw_stats = NULL; diff --git a/drivers/net/wireless/wl12xx/debugfs.h b/drivers/net/wireless/wl12xx/wl1251_debugfs.h index 562cdcbcc874..6dc3d080853c 100644 --- a/drivers/net/wireless/wl12xx/debugfs.h +++ b/drivers/net/wireless/wl12xx/wl1251_debugfs.h @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (C) 2009 Nokia Corporation * @@ -21,13 +21,13 @@ * */ -#ifndef WL12XX_DEBUGFS_H -#define WL12XX_DEBUGFS_H +#ifndef WL1251_DEBUGFS_H +#define WL1251_DEBUGFS_H -#include "wl12xx.h" +#include "wl1251.h" -int wl12xx_debugfs_init(struct wl12xx *wl); -void wl12xx_debugfs_exit(struct wl12xx *wl); -void wl12xx_debugfs_reset(struct wl12xx *wl); +int wl1251_debugfs_init(struct wl1251 *wl); +void wl1251_debugfs_exit(struct wl1251 *wl); +void wl1251_debugfs_reset(struct wl1251 *wl); -#endif /* WL12XX_DEBUGFS_H */ +#endif /* WL1251_DEBUGFS_H */ diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/wl1251_event.c index 99529ca89a7e..00076c4a8a21 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/wl1251_event.c @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation @@ -22,16 +22,16 @@ * */ -#include "wl12xx.h" -#include "reg.h" -#include "spi.h" -#include "event.h" -#include "ps.h" +#include "wl1251.h" +#include "wl1251_reg.h" +#include "wl1251_io.h" +#include "wl1251_event.h" +#include "wl1251_ps.h" -static int wl12xx_event_scan_complete(struct wl12xx *wl, +static int wl1251_event_scan_complete(struct wl1251 *wl, struct event_mailbox *mbox) { - wl12xx_debug(DEBUG_EVENT, "status: 0x%x, channels: %d", + wl1251_debug(DEBUG_EVENT, "status: 0x%x, channels: %d", mbox->scheduled_scan_status, mbox->scheduled_scan_channels); @@ -39,40 +39,41 @@ static int wl12xx_event_scan_complete(struct wl12xx *wl, mutex_unlock(&wl->mutex); ieee80211_scan_completed(wl->hw, false); mutex_lock(&wl->mutex); + wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan completed"); wl->scanning = false; } return 0; } -static void wl12xx_event_mbox_dump(struct event_mailbox *mbox) +static void wl1251_event_mbox_dump(struct event_mailbox *mbox) { - wl12xx_debug(DEBUG_EVENT, "MBOX DUMP:"); - wl12xx_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector); - wl12xx_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask); + wl1251_debug(DEBUG_EVENT, "MBOX DUMP:"); + wl1251_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector); + wl1251_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask); } -static int wl12xx_event_process(struct wl12xx *wl, struct event_mailbox *mbox) +static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox) { int ret; u32 vector; - wl12xx_event_mbox_dump(mbox); + wl1251_event_mbox_dump(mbox); vector = mbox->events_vector & ~(mbox->events_mask); - wl12xx_debug(DEBUG_EVENT, "vector: 0x%x", vector); + wl1251_debug(DEBUG_EVENT, "vector: 0x%x", vector); if (vector & SCAN_COMPLETE_EVENT_ID) { - ret = wl12xx_event_scan_complete(wl, mbox); + ret = wl1251_event_scan_complete(wl, mbox); if (ret < 0) return ret; } if (vector & BSS_LOSE_EVENT_ID) { - wl12xx_debug(DEBUG_EVENT, "BSS_LOSE_EVENT"); + wl1251_debug(DEBUG_EVENT, "BSS_LOSE_EVENT"); if (wl->psm_requested && wl->psm) { - ret = wl12xx_ps_set_mode(wl, STATION_ACTIVE_MODE); + ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE); if (ret < 0) return ret; } @@ -81,47 +82,47 @@ static int wl12xx_event_process(struct wl12xx *wl, struct event_mailbox *mbox) return 0; } -int wl12xx_event_unmask(struct wl12xx *wl) +int wl1251_event_unmask(struct wl1251 *wl) { int ret; - ret = wl12xx_acx_event_mbox_mask(wl, ~(wl->event_mask)); + ret = wl1251_acx_event_mbox_mask(wl, ~(wl->event_mask)); if (ret < 0) return ret; return 0; } -void wl12xx_event_mbox_config(struct wl12xx *wl) +void wl1251_event_mbox_config(struct wl1251 *wl) { - wl->mbox_ptr[0] = wl12xx_reg_read32(wl, REG_EVENT_MAILBOX_PTR); + wl->mbox_ptr[0] = wl1251_reg_read32(wl, REG_EVENT_MAILBOX_PTR); wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); - wl12xx_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x", + wl1251_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x", wl->mbox_ptr[0], wl->mbox_ptr[1]); } -int wl12xx_event_handle(struct wl12xx *wl, u8 mbox_num) +int wl1251_event_handle(struct wl1251 *wl, u8 mbox_num) { struct event_mailbox mbox; int ret; - wl12xx_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num); + wl1251_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num); if (mbox_num > 1) return -EINVAL; /* first we read the mbox descriptor */ - wl12xx_spi_mem_read(wl, wl->mbox_ptr[mbox_num], &mbox, + wl1251_mem_read(wl, wl->mbox_ptr[mbox_num], &mbox, sizeof(struct event_mailbox)); /* process the descriptor */ - ret = wl12xx_event_process(wl, &mbox); + ret = wl1251_event_process(wl, &mbox); if (ret < 0) return ret; /* then we let the firmware know it can go on...*/ - wl12xx_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK); + wl1251_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK); return 0; } diff --git a/drivers/net/wireless/wl12xx/event.h b/drivers/net/wireless/wl12xx/wl1251_event.h index 1f4c2f7438a7..be0ac54d6246 100644 --- a/drivers/net/wireless/wl12xx/event.h +++ b/drivers/net/wireless/wl12xx/wl1251_event.h @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation @@ -22,8 +22,8 @@ * */ -#ifndef __WL12XX_EVENT_H__ -#define __WL12XX_EVENT_H__ +#ifndef __WL1251_EVENT_H__ +#define __WL1251_EVENT_H__ /* * Mbox events @@ -114,8 +114,8 @@ struct event_mailbox { u8 padding[19]; } __attribute__ ((packed)); -int wl12xx_event_unmask(struct wl12xx *wl); -void wl12xx_event_mbox_config(struct wl12xx *wl); -int wl12xx_event_handle(struct wl12xx *wl, u8 mbox); +int wl1251_event_unmask(struct wl1251 *wl); +void wl1251_event_mbox_config(struct wl1251 *wl); +int wl1251_event_handle(struct wl1251 *wl, u8 mbox); #endif diff --git a/drivers/net/wireless/wl12xx/wl1251_init.c b/drivers/net/wireless/wl12xx/wl1251_init.c new file mode 100644 index 000000000000..b2ee4f468fc4 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1251_init.c @@ -0,0 +1,413 @@ +/* + * This file is part of wl1251 + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Kalle Valo <kalle.valo@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> + +#include "wl1251_init.h" +#include "wl12xx_80211.h" +#include "wl1251_acx.h" +#include "wl1251_cmd.h" +#include "wl1251_reg.h" + +int wl1251_hw_init_hwenc_config(struct wl1251 *wl) +{ + int ret; + + ret = wl1251_acx_feature_cfg(wl); + if (ret < 0) { + wl1251_warning("couldn't set feature config"); + return ret; + } + + ret = wl1251_acx_default_key(wl, wl->default_key); + if (ret < 0) { + wl1251_warning("couldn't set default key"); + return ret; + } + + return 0; +} + +int wl1251_hw_init_templates_config(struct wl1251 *wl) +{ + int ret; + u8 partial_vbm[PARTIAL_VBM_MAX]; + + /* send empty templates for fw memory reservation */ + ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, NULL, + sizeof(struct wl12xx_probe_req_template)); + if (ret < 0) + return ret; + + ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, NULL, + sizeof(struct wl12xx_null_data_template)); + if (ret < 0) + return ret; + + ret = wl1251_cmd_template_set(wl, CMD_PS_POLL, NULL, + sizeof(struct wl12xx_ps_poll_template)); + if (ret < 0) + return ret; + + ret = wl1251_cmd_template_set(wl, CMD_QOS_NULL_DATA, NULL, + sizeof + (struct wl12xx_qos_null_data_template)); + if (ret < 0) + return ret; + + ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, NULL, + sizeof + (struct wl12xx_probe_resp_template)); + if (ret < 0) + return ret; + + ret = wl1251_cmd_template_set(wl, CMD_BEACON, NULL, + sizeof + (struct wl12xx_beacon_template)); + if (ret < 0) + return ret; + + /* tim templates, first reserve space then allocate an empty one */ + memset(partial_vbm, 0, PARTIAL_VBM_MAX); + ret = wl1251_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, PARTIAL_VBM_MAX, 0); + if (ret < 0) + return ret; + + ret = wl1251_cmd_vbm(wl, TIM_ELE_ID, partial_vbm, 1, 0); + if (ret < 0) + return ret; + + return 0; +} + +int wl1251_hw_init_rx_config(struct wl1251 *wl, u32 config, u32 filter) +{ + int ret; + + ret = wl1251_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF); + if (ret < 0) + return ret; + + ret = wl1251_acx_rx_config(wl, config, filter); + if (ret < 0) + return ret; + + return 0; +} + +int wl1251_hw_init_phy_config(struct wl1251 *wl) +{ + int ret; + + ret = wl1251_acx_pd_threshold(wl); + if (ret < 0) + return ret; + + ret = wl1251_acx_slot(wl, DEFAULT_SLOT_TIME); + if (ret < 0) + return ret; + + ret = wl1251_acx_group_address_tbl(wl); + if (ret < 0) + return ret; + + ret = wl1251_acx_service_period_timeout(wl); + if (ret < 0) + return ret; + + ret = wl1251_acx_rts_threshold(wl, RTS_THRESHOLD_DEF); + if (ret < 0) + return ret; + + return 0; +} + +int wl1251_hw_init_beacon_filter(struct wl1251 *wl) +{ + int ret; + + ret = wl1251_acx_beacon_filter_opt(wl); + if (ret < 0) + return ret; + + ret = wl1251_acx_beacon_filter_table(wl); + if (ret < 0) + return ret; + + return 0; +} + +int wl1251_hw_init_pta(struct wl1251 *wl) +{ + int ret; + + ret = wl1251_acx_sg_enable(wl); + if (ret < 0) + return ret; + + ret = wl1251_acx_sg_cfg(wl); + if (ret < 0) + return ret; + + return 0; +} + +int wl1251_hw_init_energy_detection(struct wl1251 *wl) +{ + int ret; + + ret = wl1251_acx_cca_threshold(wl); + if (ret < 0) + return ret; + + return 0; +} + +int wl1251_hw_init_beacon_broadcast(struct wl1251 *wl) +{ + int ret; + + ret = wl1251_acx_bcn_dtim_options(wl); + if (ret < 0) + return ret; + + return 0; +} + +int wl1251_hw_init_power_auth(struct wl1251 *wl) +{ + return wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM); +} + +int wl1251_hw_init_mem_config(struct wl1251 *wl) +{ + int ret; + + ret = wl1251_acx_mem_cfg(wl); + if (ret < 0) + return ret; + + wl->target_mem_map = kzalloc(sizeof(struct wl1251_acx_mem_map), + GFP_KERNEL); + if (!wl->target_mem_map) { + wl1251_error("couldn't allocate target memory map"); + return -ENOMEM; + } + + /* we now ask for the firmware built memory map */ + ret = wl1251_acx_mem_map(wl, wl->target_mem_map, + sizeof(struct wl1251_acx_mem_map)); + if (ret < 0) { + wl1251_error("couldn't retrieve firmware memory map"); + kfree(wl->target_mem_map); + wl->target_mem_map = NULL; + return ret; + } + + return 0; +} + +static int wl1251_hw_init_txq_fill(u8 qid, + struct acx_tx_queue_qos_config *config, + u32 num_blocks) +{ + config->qid = qid; + + switch (qid) { + case QOS_AC_BE: + config->high_threshold = + (QOS_TX_HIGH_BE_DEF * num_blocks) / 100; + config->low_threshold = + (QOS_TX_LOW_BE_DEF * num_blocks) / 100; + break; + case QOS_AC_BK: + config->high_threshold = + (QOS_TX_HIGH_BK_DEF * num_blocks) / 100; + config->low_threshold = + (QOS_TX_LOW_BK_DEF * num_blocks) / 100; + break; + case QOS_AC_VI: + config->high_threshold = + (QOS_TX_HIGH_VI_DEF * num_blocks) / 100; + config->low_threshold = + (QOS_TX_LOW_VI_DEF * num_blocks) / 100; + break; + case QOS_AC_VO: + config->high_threshold = + (QOS_TX_HIGH_VO_DEF * num_blocks) / 100; + config->low_threshold = + (QOS_TX_LOW_VO_DEF * num_blocks) / 100; + break; + default: + wl1251_error("Invalid TX queue id: %d", qid); + return -EINVAL; + } + + return 0; +} + +static int wl1251_hw_init_tx_queue_config(struct wl1251 *wl) +{ + struct acx_tx_queue_qos_config *config; + struct wl1251_acx_mem_map *wl_mem_map = wl->target_mem_map; + int ret, i; + + wl1251_debug(DEBUG_ACX, "acx tx queue config"); + + config = kzalloc(sizeof(*config), GFP_KERNEL); + if (!config) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < MAX_NUM_OF_AC; i++) { + ret = wl1251_hw_init_txq_fill(i, config, + wl_mem_map->num_tx_mem_blocks); + if (ret < 0) + goto out; + + ret = wl1251_cmd_configure(wl, ACX_TX_QUEUE_CFG, + config, sizeof(*config)); + if (ret < 0) + goto out; + } + +out: + kfree(config); + return ret; +} + +static int wl1251_hw_init_data_path_config(struct wl1251 *wl) +{ + int ret; + + /* asking for the data path parameters */ + wl->data_path = kzalloc(sizeof(struct acx_data_path_params_resp), + GFP_KERNEL); + if (!wl->data_path) { + wl1251_error("Couldnt allocate data path parameters"); + return -ENOMEM; + } + + ret = wl1251_acx_data_path_params(wl, wl->data_path); + if (ret < 0) { + kfree(wl->data_path); + wl->data_path = NULL; + return ret; + } + + return 0; +} + + +int wl1251_hw_init(struct wl1251 *wl) +{ + struct wl1251_acx_mem_map *wl_mem_map; + int ret; + + ret = wl1251_hw_init_hwenc_config(wl); + if (ret < 0) + return ret; + + /* Template settings */ + ret = wl1251_hw_init_templates_config(wl); + if (ret < 0) + return ret; + + /* Default memory configuration */ + ret = wl1251_hw_init_mem_config(wl); + if (ret < 0) + return ret; + + /* Default data path configuration */ + ret = wl1251_hw_init_data_path_config(wl); + if (ret < 0) + goto out_free_memmap; + + /* RX config */ + ret = wl1251_hw_init_rx_config(wl, + RX_CFG_PROMISCUOUS | RX_CFG_TSF, + RX_FILTER_OPTION_DEF); + /* RX_CONFIG_OPTION_ANY_DST_ANY_BSS, + RX_FILTER_OPTION_FILTER_ALL); */ + if (ret < 0) + goto out_free_data_path; + + /* TX queues config */ + ret = wl1251_hw_init_tx_queue_config(wl); + if (ret < 0) + goto out_free_data_path; + + /* PHY layer config */ + ret = wl1251_hw_init_phy_config(wl); + if (ret < 0) + goto out_free_data_path; + + /* Beacon filtering */ + ret = wl1251_hw_init_beacon_filter(wl); + if (ret < 0) + goto out_free_data_path; + + /* Bluetooth WLAN coexistence */ + ret = wl1251_hw_init_pta(wl); + if (ret < 0) + goto out_free_data_path; + + /* Energy detection */ + ret = wl1251_hw_init_energy_detection(wl); + if (ret < 0) + goto out_free_data_path; + + /* Beacons and boradcast settings */ + ret = wl1251_hw_init_beacon_broadcast(wl); + if (ret < 0) + goto out_free_data_path; + + /* Enable data path */ + ret = wl1251_cmd_data_path(wl, wl->channel, 1); + if (ret < 0) + goto out_free_data_path; + + /* Default power state */ + ret = wl1251_hw_init_power_auth(wl); + if (ret < 0) + goto out_free_data_path; + + wl_mem_map = wl->target_mem_map; + wl1251_info("%d tx blocks at 0x%x, %d rx blocks at 0x%x", + wl_mem_map->num_tx_mem_blocks, + wl->data_path->tx_control_addr, + wl_mem_map->num_rx_mem_blocks, + wl->data_path->rx_control_addr); + + return 0; + + out_free_data_path: + kfree(wl->data_path); + + out_free_memmap: + kfree(wl->target_mem_map); + + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1251_init.h b/drivers/net/wireless/wl12xx/wl1251_init.h new file mode 100644 index 000000000000..b3b25ec885ea --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1251_init.h @@ -0,0 +1,41 @@ +/* + * This file is part of wl1251 + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Kalle Valo <kalle.valo@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1251_INIT_H__ +#define __WL1251_INIT_H__ + +#include "wl1251.h" + +int wl1251_hw_init_hwenc_config(struct wl1251 *wl); +int wl1251_hw_init_templates_config(struct wl1251 *wl); +int wl1251_hw_init_rx_config(struct wl1251 *wl, u32 config, u32 filter); +int wl1251_hw_init_phy_config(struct wl1251 *wl); +int wl1251_hw_init_beacon_filter(struct wl1251 *wl); +int wl1251_hw_init_pta(struct wl1251 *wl); +int wl1251_hw_init_energy_detection(struct wl1251 *wl); +int wl1251_hw_init_beacon_broadcast(struct wl1251 *wl); +int wl1251_hw_init_power_auth(struct wl1251 *wl); +int wl1251_hw_init_mem_config(struct wl1251 *wl); +int wl1251_hw_init(struct wl1251 *wl); + +#endif diff --git a/drivers/net/wireless/wl12xx/wl1251_io.c b/drivers/net/wireless/wl12xx/wl1251_io.c new file mode 100644 index 000000000000..f1c232e0887f --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1251_io.c @@ -0,0 +1,196 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2008 Nokia Corporation + * + * Contact: Kalle Valo <kalle.valo@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "wl1251.h" +#include "wl1251_reg.h" +#include "wl1251_io.h" + +/* FIXME: this is static data nowadays and the table can be removed */ +static enum wl12xx_acx_int_reg wl1251_io_reg_table[ACX_REG_TABLE_LEN] = { + [ACX_REG_INTERRUPT_TRIG] = (REGISTERS_BASE + 0x0474), + [ACX_REG_INTERRUPT_TRIG_H] = (REGISTERS_BASE + 0x0478), + [ACX_REG_INTERRUPT_MASK] = (REGISTERS_BASE + 0x0494), + [ACX_REG_HINT_MASK_SET] = (REGISTERS_BASE + 0x0498), + [ACX_REG_HINT_MASK_CLR] = (REGISTERS_BASE + 0x049C), + [ACX_REG_INTERRUPT_NO_CLEAR] = (REGISTERS_BASE + 0x04B0), + [ACX_REG_INTERRUPT_CLEAR] = (REGISTERS_BASE + 0x04A4), + [ACX_REG_INTERRUPT_ACK] = (REGISTERS_BASE + 0x04A8), + [ACX_REG_SLV_SOFT_RESET] = (REGISTERS_BASE + 0x0000), + [ACX_REG_EE_START] = (REGISTERS_BASE + 0x080C), + [ACX_REG_ECPU_CONTROL] = (REGISTERS_BASE + 0x0804) +}; + +static int wl1251_translate_reg_addr(struct wl1251 *wl, int addr) +{ + /* If the address is lower than REGISTERS_BASE, it means that this is + * a chip-specific register address, so look it up in the registers + * table */ + if (addr < REGISTERS_BASE) { + /* Make sure we don't go over the table */ + if (addr >= ACX_REG_TABLE_LEN) { + wl1251_error("address out of range (%d)", addr); + return -EINVAL; + } + addr = wl1251_io_reg_table[addr]; + } + + return addr - wl->physical_reg_addr + wl->virtual_reg_addr; +} + +static int wl1251_translate_mem_addr(struct wl1251 *wl, int addr) +{ + return addr - wl->physical_mem_addr + wl->virtual_mem_addr; +} + +void wl1251_mem_read(struct wl1251 *wl, int addr, void *buf, size_t len) +{ + int physical; + + physical = wl1251_translate_mem_addr(wl, addr); + + wl->if_ops->read(wl, physical, buf, len); +} + +void wl1251_mem_write(struct wl1251 *wl, int addr, void *buf, size_t len) +{ + int physical; + + physical = wl1251_translate_mem_addr(wl, addr); + + wl->if_ops->write(wl, physical, buf, len); +} + +u32 wl1251_mem_read32(struct wl1251 *wl, int addr) +{ + return wl1251_read32(wl, wl1251_translate_mem_addr(wl, addr)); +} + +void wl1251_mem_write32(struct wl1251 *wl, int addr, u32 val) +{ + wl1251_write32(wl, wl1251_translate_mem_addr(wl, addr), val); +} + +u32 wl1251_reg_read32(struct wl1251 *wl, int addr) +{ + return wl1251_read32(wl, wl1251_translate_reg_addr(wl, addr)); +} + +void wl1251_reg_write32(struct wl1251 *wl, int addr, u32 val) +{ + wl1251_write32(wl, wl1251_translate_reg_addr(wl, addr), val); +} + +/* Set the partitions to access the chip addresses. + * + * There are two VIRTUAL partitions (the memory partition and the + * registers partition), which are mapped to two different areas of the + * PHYSICAL (hardware) memory. This function also makes other checks to + * ensure that the partitions are not overlapping. In the diagram below, the + * memory partition comes before the register partition, but the opposite is + * also supported. + * + * PHYSICAL address + * space + * + * | | + * ...+----+--> mem_start + * VIRTUAL address ... | | + * space ... | | [PART_0] + * ... | | + * 0x00000000 <--+----+... ...+----+--> mem_start + mem_size + * | | ... | | + * |MEM | ... | | + * | | ... | | + * part_size <--+----+... | | {unused area) + * | | ... | | + * |REG | ... | | + * part_size | | ... | | + * + <--+----+... ...+----+--> reg_start + * reg_size ... | | + * ... | | [PART_1] + * ... | | + * ...+----+--> reg_start + reg_size + * | | + * + */ +void wl1251_set_partition(struct wl1251 *wl, + u32 mem_start, u32 mem_size, + u32 reg_start, u32 reg_size) +{ + struct wl1251_partition partition[2]; + + wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", + mem_start, mem_size); + wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", + reg_start, reg_size); + + /* Make sure that the two partitions together don't exceed the + * address range */ + if ((mem_size + reg_size) > HW_ACCESS_MEMORY_MAX_RANGE) { + wl1251_debug(DEBUG_SPI, "Total size exceeds maximum virtual" + " address range. Truncating partition[0]."); + mem_size = HW_ACCESS_MEMORY_MAX_RANGE - reg_size; + wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", + mem_start, mem_size); + wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", + reg_start, reg_size); + } + + if ((mem_start < reg_start) && + ((mem_start + mem_size) > reg_start)) { + /* Guarantee that the memory partition doesn't overlap the + * registers partition */ + wl1251_debug(DEBUG_SPI, "End of partition[0] is " + "overlapping partition[1]. Adjusted."); + mem_size = reg_start - mem_start; + wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", + mem_start, mem_size); + wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", + reg_start, reg_size); + } else if ((reg_start < mem_start) && + ((reg_start + reg_size) > mem_start)) { + /* Guarantee that the register partition doesn't overlap the + * memory partition */ + wl1251_debug(DEBUG_SPI, "End of partition[1] is" + " overlapping partition[0]. Adjusted."); + reg_size = mem_start - reg_start; + wl1251_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", + mem_start, mem_size); + wl1251_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", + reg_start, reg_size); + } + + partition[0].start = mem_start; + partition[0].size = mem_size; + partition[1].start = reg_start; + partition[1].size = reg_size; + + wl->physical_mem_addr = mem_start; + wl->physical_reg_addr = reg_start; + + wl->virtual_mem_addr = 0; + wl->virtual_reg_addr = mem_size; + + wl->if_ops->write(wl, HW_ACCESS_PART0_SIZE_ADDR, partition, + sizeof(partition)); +} diff --git a/drivers/net/wireless/wl12xx/wl1251_io.h b/drivers/net/wireless/wl12xx/wl1251_io.h new file mode 100644 index 000000000000..b89d2ac62efb --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1251_io.h @@ -0,0 +1,64 @@ +/* + * This file is part of wl12xx + * + * Copyright (C) 2008 Nokia Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#ifndef __WL1251_IO_H__ +#define __WL1251_IO_H__ + +#include "wl1251.h" + +#define HW_ACCESS_MEMORY_MAX_RANGE 0x1FFC0 + +#define HW_ACCESS_PART0_SIZE_ADDR 0x1FFC0 +#define HW_ACCESS_PART0_START_ADDR 0x1FFC4 +#define HW_ACCESS_PART1_SIZE_ADDR 0x1FFC8 +#define HW_ACCESS_PART1_START_ADDR 0x1FFCC + +#define HW_ACCESS_REGISTER_SIZE 4 + +#define HW_ACCESS_PRAM_MAX_RANGE 0x3c000 + +static inline u32 wl1251_read32(struct wl1251 *wl, int addr) +{ + u32 response; + + wl->if_ops->read(wl, addr, &response, sizeof(u32)); + + return response; +} + +static inline void wl1251_write32(struct wl1251 *wl, int addr, u32 val) +{ + wl->if_ops->write(wl, addr, &val, sizeof(u32)); +} + +/* Memory target IO, address is translated to partition 0 */ +void wl1251_mem_read(struct wl1251 *wl, int addr, void *buf, size_t len); +void wl1251_mem_write(struct wl1251 *wl, int addr, void *buf, size_t len); +u32 wl1251_mem_read32(struct wl1251 *wl, int addr); +void wl1251_mem_write32(struct wl1251 *wl, int addr, u32 val); +/* Registers IO */ +u32 wl1251_reg_read32(struct wl1251 *wl, int addr); +void wl1251_reg_write32(struct wl1251 *wl, int addr, u32 val); + +void wl1251_set_partition(struct wl1251 *wl, + u32 part_start, u32 part_size, + u32 reg_start, u32 reg_size); + +#endif diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 603d6114882e..5809ef5b18f8 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (C) 2008-2009 Nokia Corporation * @@ -26,65 +26,57 @@ #include <linux/firmware.h> #include <linux/delay.h> #include <linux/irq.h> -#include <linux/spi/spi.h> #include <linux/crc32.h> #include <linux/etherdevice.h> -#include <linux/spi/wl12xx.h> -#include "wl12xx.h" -#include "wl12xx_80211.h" -#include "reg.h" #include "wl1251.h" -#include "spi.h" -#include "event.h" -#include "tx.h" -#include "rx.h" -#include "ps.h" -#include "init.h" -#include "debugfs.h" - -static void wl12xx_disable_interrupts(struct wl12xx *wl) +#include "wl12xx_80211.h" +#include "wl1251_reg.h" +#include "wl1251_io.h" +#include "wl1251_cmd.h" +#include "wl1251_event.h" +#include "wl1251_tx.h" +#include "wl1251_rx.h" +#include "wl1251_ps.h" +#include "wl1251_init.h" +#include "wl1251_debugfs.h" +#include "wl1251_boot.h" + +void wl1251_enable_interrupts(struct wl1251 *wl) { - disable_irq(wl->irq); + wl->if_ops->enable_irq(wl); } -static void wl12xx_power_off(struct wl12xx *wl) +void wl1251_disable_interrupts(struct wl1251 *wl) { - wl->set_power(false); + wl->if_ops->disable_irq(wl); } -static void wl12xx_power_on(struct wl12xx *wl) +static void wl1251_power_off(struct wl1251 *wl) { - wl->set_power(true); + wl->set_power(false); } -static irqreturn_t wl12xx_irq(int irq, void *cookie) +static void wl1251_power_on(struct wl1251 *wl) { - struct wl12xx *wl; - - wl12xx_debug(DEBUG_IRQ, "IRQ"); - - wl = cookie; - - schedule_work(&wl->irq_work); - - return IRQ_HANDLED; + wl->set_power(true); } -static int wl12xx_fetch_firmware(struct wl12xx *wl) +static int wl1251_fetch_firmware(struct wl1251 *wl) { const struct firmware *fw; + struct device *dev = wiphy_dev(wl->hw->wiphy); int ret; - ret = request_firmware(&fw, wl->chip.fw_filename, &wl->spi->dev); + ret = request_firmware(&fw, WL1251_FW_NAME, dev); if (ret < 0) { - wl12xx_error("could not get firmware: %d", ret); + wl1251_error("could not get firmware: %d", ret); return ret; } if (fw->size % 4) { - wl12xx_error("firmware size is not multiple of 32 bits: %zu", + wl1251_error("firmware size is not multiple of 32 bits: %zu", fw->size); ret = -EILSEQ; goto out; @@ -94,7 +86,7 @@ static int wl12xx_fetch_firmware(struct wl12xx *wl) wl->fw = kmalloc(wl->fw_len, GFP_KERNEL); if (!wl->fw) { - wl12xx_error("could not allocate memory for the firmware"); + wl1251_error("could not allocate memory for the firmware"); ret = -ENOMEM; goto out; } @@ -109,20 +101,21 @@ out: return ret; } -static int wl12xx_fetch_nvs(struct wl12xx *wl) +static int wl1251_fetch_nvs(struct wl1251 *wl) { const struct firmware *fw; + struct device *dev = wiphy_dev(wl->hw->wiphy); int ret; - ret = request_firmware(&fw, wl->chip.nvs_filename, &wl->spi->dev); + ret = request_firmware(&fw, WL1251_NVS_NAME, dev); if (ret < 0) { - wl12xx_error("could not get nvs file: %d", ret); + wl1251_error("could not get nvs file: %d", ret); return ret; } if (fw->size % 4) { - wl12xx_error("nvs size is not multiple of 32 bits: %zu", + wl1251_error("nvs size is not multiple of 32 bits: %zu", fw->size); ret = -EILSEQ; goto out; @@ -132,7 +125,7 @@ static int wl12xx_fetch_nvs(struct wl12xx *wl) wl->nvs = kmalloc(wl->nvs_len, GFP_KERNEL); if (!wl->nvs) { - wl12xx_error("could not allocate memory for the nvs file"); + wl1251_error("could not allocate memory for the nvs file"); ret = -ENOMEM; goto out; } @@ -147,74 +140,66 @@ out: return ret; } -static void wl12xx_fw_wakeup(struct wl12xx *wl) +static void wl1251_fw_wakeup(struct wl1251 *wl) { u32 elp_reg; elp_reg = ELPCTRL_WAKE_UP; - wl12xx_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg); - elp_reg = wl12xx_read32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); + wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg); + elp_reg = wl1251_read32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); - if (!(elp_reg & ELPCTRL_WLAN_READY)) { - wl12xx_warning("WLAN not ready"); - elp_reg = ELPCTRL_WAKE_UP_WLAN_READY; - wl12xx_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg); - } + if (!(elp_reg & ELPCTRL_WLAN_READY)) + wl1251_warning("WLAN not ready"); } -static int wl12xx_chip_wakeup(struct wl12xx *wl) +static int wl1251_chip_wakeup(struct wl1251 *wl) { int ret = 0; - wl12xx_power_on(wl); - msleep(wl->chip.power_on_sleep); - wl12xx_spi_reset(wl); - wl12xx_spi_init(wl); + wl1251_power_on(wl); + msleep(WL1251_POWER_ON_SLEEP); + wl->if_ops->reset(wl); /* We don't need a real memory partition here, because we only want * to use the registers at this point. */ - wl12xx_set_partition(wl, + wl1251_set_partition(wl, 0x00000000, 0x00000000, REGISTERS_BASE, REGISTERS_DOWN_SIZE); /* ELP module wake up */ - wl12xx_fw_wakeup(wl); + wl1251_fw_wakeup(wl); /* whal_FwCtrl_BootSm() */ /* 0. read chip id from CHIP_ID */ - wl->chip.id = wl12xx_reg_read32(wl, CHIP_ID_B); + wl->chip_id = wl1251_reg_read32(wl, CHIP_ID_B); /* 1. check if chip id is valid */ - switch (wl->chip.id) { + switch (wl->chip_id) { case CHIP_ID_1251_PG12: - wl12xx_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG12)", - wl->chip.id); - - wl1251_setup(wl); - + wl1251_debug(DEBUG_BOOT, "chip id 0x%x (1251 PG12)", + wl->chip_id); break; - case CHIP_ID_1271_PG10: case CHIP_ID_1251_PG10: case CHIP_ID_1251_PG11: default: - wl12xx_error("unsupported chip id: 0x%x", wl->chip.id); + wl1251_error("unsupported chip id: 0x%x", wl->chip_id); ret = -ENODEV; goto out; } if (wl->fw == NULL) { - ret = wl12xx_fetch_firmware(wl); + ret = wl1251_fetch_firmware(wl); if (ret < 0) goto out; } /* No NVS from netlink, try to get it from the filesystem */ if (wl->nvs == NULL) { - ret = wl12xx_fetch_nvs(wl); + ret = wl1251_fetch_nvs(wl); if (ret < 0) goto out; } @@ -223,88 +208,180 @@ out: return ret; } -static void wl12xx_filter_work(struct work_struct *work) +static void wl1251_irq_work(struct work_struct *work) { - struct wl12xx *wl = - container_of(work, struct wl12xx, filter_work); + u32 intr; + struct wl1251 *wl = + container_of(work, struct wl1251, irq_work); int ret; mutex_lock(&wl->mutex); - if (wl->state == WL12XX_STATE_OFF) + wl1251_debug(DEBUG_IRQ, "IRQ work"); + + if (wl->state == WL1251_STATE_OFF) goto out; - ret = wl12xx_cmd_join(wl, wl->bss_type, 1, 100, 0); + ret = wl1251_ps_elp_wakeup(wl); if (ret < 0) goto out; -out: - mutex_unlock(&wl->mutex); -} + wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1251_ACX_INTR_ALL); + + intr = wl1251_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); + wl1251_debug(DEBUG_IRQ, "intr: 0x%x", intr); + + if (wl->data_path) { + wl->rx_counter = + wl1251_mem_read32(wl, wl->data_path->rx_control_addr); + + /* We handle a frmware bug here */ + switch ((wl->rx_counter - wl->rx_handled) & 0xf) { + case 0: + wl1251_debug(DEBUG_IRQ, "RX: FW and host in sync"); + intr &= ~WL1251_ACX_INTR_RX0_DATA; + intr &= ~WL1251_ACX_INTR_RX1_DATA; + break; + case 1: + wl1251_debug(DEBUG_IRQ, "RX: FW +1"); + intr |= WL1251_ACX_INTR_RX0_DATA; + intr &= ~WL1251_ACX_INTR_RX1_DATA; + break; + case 2: + wl1251_debug(DEBUG_IRQ, "RX: FW +2"); + intr |= WL1251_ACX_INTR_RX0_DATA; + intr |= WL1251_ACX_INTR_RX1_DATA; + break; + default: + wl1251_warning("RX: FW and host out of sync: %d", + wl->rx_counter - wl->rx_handled); + break; + } -int wl12xx_plt_start(struct wl12xx *wl) -{ - int ret; + wl->rx_handled = wl->rx_counter; - wl12xx_notice("power up"); - if (wl->state != WL12XX_STATE_OFF) { - wl12xx_error("cannot go into PLT state because not " - "in off state: %d", wl->state); - return -EBUSY; + wl1251_debug(DEBUG_IRQ, "RX counter: %d", wl->rx_counter); } - wl->state = WL12XX_STATE_PLT; + intr &= wl->intr_mask; - ret = wl12xx_chip_wakeup(wl); - if (ret < 0) - return ret; + if (intr == 0) { + wl1251_debug(DEBUG_IRQ, "INTR is 0"); + wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, + ~(wl->intr_mask)); + + goto out_sleep; + } - ret = wl->chip.op_boot(wl); + if (intr & WL1251_ACX_INTR_RX0_DATA) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX0_DATA"); + wl1251_rx(wl); + } + + if (intr & WL1251_ACX_INTR_RX1_DATA) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_RX1_DATA"); + wl1251_rx(wl); + } + + if (intr & WL1251_ACX_INTR_TX_RESULT) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_TX_RESULT"); + wl1251_tx_complete(wl); + } + + if (intr & (WL1251_ACX_INTR_EVENT_A | WL1251_ACX_INTR_EVENT_B)) { + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_EVENT (0x%x)", intr); + if (intr & WL1251_ACX_INTR_EVENT_A) + wl1251_event_handle(wl, 0); + else + wl1251_event_handle(wl, 1); + } + + if (intr & WL1251_ACX_INTR_INIT_COMPLETE) + wl1251_debug(DEBUG_IRQ, "WL1251_ACX_INTR_INIT_COMPLETE"); + + wl1251_reg_write32(wl, ACX_REG_INTERRUPT_MASK, ~(wl->intr_mask)); + +out_sleep: + wl1251_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); +} + +static int wl1251_join(struct wl1251 *wl, u8 bss_type, u8 channel, + u16 beacon_interval, u8 dtim_period) +{ + int ret; + + ret = wl1251_acx_frame_rates(wl, DEFAULT_HW_GEN_TX_RATE, + DEFAULT_HW_GEN_MODULATION_TYPE, + wl->tx_mgmt_frm_rate, + wl->tx_mgmt_frm_mod); if (ret < 0) - return ret; + goto out; - wl12xx_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver); - ret = wl->chip.op_plt_init(wl); + ret = wl1251_cmd_join(wl, bss_type, channel, beacon_interval, + dtim_period); if (ret < 0) - return ret; + goto out; - return 0; + /* + * FIXME: we should wait for JOIN_EVENT_COMPLETE_ID but to simplify + * locking we just sleep instead, for now + */ + msleep(10); + +out: + return ret; } -int wl12xx_plt_stop(struct wl12xx *wl) +static void wl1251_filter_work(struct work_struct *work) { - wl12xx_notice("power down"); + struct wl1251 *wl = + container_of(work, struct wl1251, filter_work); + int ret; - if (wl->state != WL12XX_STATE_PLT) { - wl12xx_error("cannot power down because not in PLT " - "state: %d", wl->state); - return -EBUSY; - } + mutex_lock(&wl->mutex); - wl12xx_disable_interrupts(wl); - wl12xx_power_off(wl); + if (wl->state == WL1251_STATE_OFF) + goto out; - wl->state = WL12XX_STATE_OFF; + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out; - return 0; -} + ret = wl1251_join(wl, wl->bss_type, wl->channel, wl->beacon_int, + wl->dtim_period); + if (ret < 0) + goto out_sleep; +out_sleep: + wl1251_ps_elp_sleep(wl); -static int wl12xx_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +out: + mutex_unlock(&wl->mutex); +} + +static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { - struct wl12xx *wl = hw->priv; + struct wl1251 *wl = hw->priv; skb_queue_tail(&wl->tx_queue, skb); - schedule_work(&wl->tx_work); + /* + * The chip specific setup must run before the first TX packet - + * before that, the tx_work will not be initialized! + */ + + ieee80211_queue_work(wl->hw, &wl->tx_work); /* * The workqueue is slow to process the tx_queue and we need stop * the queue here, otherwise the queue will get too long. */ - if (skb_queue_len(&wl->tx_queue) >= WL12XX_TX_QUEUE_MAX_LENGTH) { + if (skb_queue_len(&wl->tx_queue) >= WL1251_TX_QUEUE_MAX_LENGTH) { ieee80211_stop_queues(wl->hw); /* @@ -318,62 +395,62 @@ static int wl12xx_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) return NETDEV_TX_OK; } -static int wl12xx_op_start(struct ieee80211_hw *hw) +static int wl1251_op_start(struct ieee80211_hw *hw) { - struct wl12xx *wl = hw->priv; + struct wl1251 *wl = hw->priv; int ret = 0; - wl12xx_debug(DEBUG_MAC80211, "mac80211 start"); + wl1251_debug(DEBUG_MAC80211, "mac80211 start"); mutex_lock(&wl->mutex); - if (wl->state != WL12XX_STATE_OFF) { - wl12xx_error("cannot start because not in off state: %d", + if (wl->state != WL1251_STATE_OFF) { + wl1251_error("cannot start because not in off state: %d", wl->state); ret = -EBUSY; goto out; } - ret = wl12xx_chip_wakeup(wl); + ret = wl1251_chip_wakeup(wl); if (ret < 0) - return ret; + goto out; - ret = wl->chip.op_boot(wl); + ret = wl1251_boot(wl); if (ret < 0) goto out; - ret = wl->chip.op_hw_init(wl); + ret = wl1251_hw_init(wl); if (ret < 0) goto out; - ret = wl12xx_acx_station_id(wl); + ret = wl1251_acx_station_id(wl); if (ret < 0) goto out; - wl->state = WL12XX_STATE_ON; + wl->state = WL1251_STATE_ON; - wl12xx_info("firmware booted (%s)", wl->chip.fw_ver); + wl1251_info("firmware booted (%s)", wl->fw_ver); out: if (ret < 0) - wl12xx_power_off(wl); + wl1251_power_off(wl); mutex_unlock(&wl->mutex); return ret; } -static void wl12xx_op_stop(struct ieee80211_hw *hw) +static void wl1251_op_stop(struct ieee80211_hw *hw) { - struct wl12xx *wl = hw->priv; + struct wl1251 *wl = hw->priv; - wl12xx_info("down"); + wl1251_info("down"); - wl12xx_debug(DEBUG_MAC80211, "mac80211 stop"); + wl1251_debug(DEBUG_MAC80211, "mac80211 stop"); mutex_lock(&wl->mutex); - WARN_ON(wl->state != WL12XX_STATE_ON); + WARN_ON(wl->state != WL1251_STATE_ON); if (wl->scanning) { mutex_unlock(&wl->mutex); @@ -382,9 +459,9 @@ static void wl12xx_op_stop(struct ieee80211_hw *hw) wl->scanning = false; } - wl->state = WL12XX_STATE_OFF; + wl->state = WL1251_STATE_OFF; - wl12xx_disable_interrupts(wl); + wl1251_disable_interrupts(wl); mutex_unlock(&wl->mutex); @@ -395,9 +472,8 @@ static void wl12xx_op_stop(struct ieee80211_hw *hw) mutex_lock(&wl->mutex); /* let's notify MAC80211 about the remaining pending TX frames */ - wl12xx_tx_flush(wl); - - wl12xx_power_off(wl); + wl1251_tx_flush(wl); + wl1251_power_off(wl); memset(wl->bssid, 0, ETH_ALEN); wl->listen_int = 1; @@ -412,22 +488,22 @@ static void wl12xx_op_stop(struct ieee80211_hw *hw) wl->elp = false; wl->psm = 0; wl->tx_queue_stopped = false; - wl->power_level = WL12XX_DEFAULT_POWER_LEVEL; + wl->power_level = WL1251_DEFAULT_POWER_LEVEL; + wl->channel = WL1251_DEFAULT_CHANNEL; - wl12xx_debugfs_reset(wl); + wl1251_debugfs_reset(wl); mutex_unlock(&wl->mutex); } -static int wl12xx_op_add_interface(struct ieee80211_hw *hw, +static int wl1251_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf) { - struct wl12xx *wl = hw->priv; - DECLARE_MAC_BUF(mac); + struct wl1251 *wl = hw->priv; int ret = 0; - wl12xx_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %s", - conf->type, print_mac(mac, conf->mac_addr)); + wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", + conf->type, conf->mac_addr); mutex_lock(&wl->mutex); @@ -446,7 +522,7 @@ static int wl12xx_op_add_interface(struct ieee80211_hw *hw, if (memcmp(wl->mac_addr, conf->mac_addr, ETH_ALEN)) { memcpy(wl->mac_addr, conf->mac_addr, ETH_ALEN); SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); - ret = wl12xx_acx_station_id(wl); + ret = wl1251_acx_station_id(wl); if (ret < 0) goto out; } @@ -456,13 +532,13 @@ out: return ret; } -static void wl12xx_op_remove_interface(struct ieee80211_hw *hw, +static void wl1251_op_remove_interface(struct ieee80211_hw *hw, struct ieee80211_if_init_conf *conf) { - wl12xx_debug(DEBUG_MAC80211, "mac80211 remove interface"); + wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface"); } -static int wl12xx_build_null_data(struct wl12xx *wl) +static int wl1251_build_null_data(struct wl1251 *wl) { struct wl12xx_null_data_template template; @@ -478,12 +554,12 @@ static int wl12xx_build_null_data(struct wl12xx *wl) template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC); - return wl12xx_cmd_template_set(wl, CMD_NULL_DATA, &template, + return wl1251_cmd_template_set(wl, CMD_NULL_DATA, &template, sizeof(template)); } -static int wl12xx_build_ps_poll(struct wl12xx *wl, u16 aid) +static int wl1251_build_ps_poll(struct wl1251 *wl, u16 aid) { struct wl12xx_ps_poll_template template; @@ -492,41 +568,45 @@ static int wl12xx_build_ps_poll(struct wl12xx *wl, u16 aid) template.aid = aid; template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); - return wl12xx_cmd_template_set(wl, CMD_PS_POLL, &template, + return wl1251_cmd_template_set(wl, CMD_PS_POLL, &template, sizeof(template)); } -static int wl12xx_op_config(struct ieee80211_hw *hw, u32 changed) +static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) { - struct wl12xx *wl = hw->priv; + struct wl1251 *wl = hw->priv; struct ieee80211_conf *conf = &hw->conf; int channel, ret = 0; channel = ieee80211_frequency_to_channel(conf->channel->center_freq); - wl12xx_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d", + wl1251_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d", channel, conf->flags & IEEE80211_CONF_PS ? "on" : "off", conf->power_level); mutex_lock(&wl->mutex); - if (channel != wl->channel) { - /* FIXME: use beacon interval provided by mac80211 */ - ret = wl12xx_cmd_join(wl, wl->bss_type, 1, 100, 0); - if (ret < 0) - goto out; + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + if (channel != wl->channel) { wl->channel = channel; + + ret = wl1251_join(wl, wl->bss_type, wl->channel, + wl->beacon_int, wl->dtim_period); + if (ret < 0) + goto out_sleep; } - ret = wl12xx_build_null_data(wl); + ret = wl1251_build_null_data(wl); if (ret < 0) - goto out; + goto out_sleep; if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { - wl12xx_info("psm enabled"); + wl1251_debug(DEBUG_PSM, "psm enabled"); wl->psm_requested = true; @@ -535,49 +615,51 @@ static int wl12xx_op_config(struct ieee80211_hw *hw, u32 changed) * If we're not, we'll enter it when joining an SSID, * through the bss_info_changed() hook. */ - ret = wl12xx_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); } else if (!(conf->flags & IEEE80211_CONF_PS) && wl->psm_requested) { - wl12xx_info("psm disabled"); + wl1251_debug(DEBUG_PSM, "psm disabled"); wl->psm_requested = false; if (wl->psm) - ret = wl12xx_ps_set_mode(wl, STATION_ACTIVE_MODE); + ret = wl1251_ps_set_mode(wl, STATION_ACTIVE_MODE); } if (conf->power_level != wl->power_level) { - ret = wl12xx_acx_tx_power(wl, conf->power_level); + ret = wl1251_acx_tx_power(wl, conf->power_level); if (ret < 0) goto out; wl->power_level = conf->power_level; } +out_sleep: + wl1251_ps_elp_sleep(wl); + out: mutex_unlock(&wl->mutex); + return ret; } -#define WL12XX_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ +#define WL1251_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ FIF_ALLMULTI | \ FIF_FCSFAIL | \ FIF_BCN_PRBRESP_PROMISC | \ FIF_CONTROL | \ FIF_OTHER_BSS) -static void wl12xx_op_configure_filter(struct ieee80211_hw *hw, +static void wl1251_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, - unsigned int *total, - int mc_count, - struct dev_addr_list *mc_list) + unsigned int *total,u64 multicast) { - struct wl12xx *wl = hw->priv; + struct wl1251 *wl = hw->priv; - wl12xx_debug(DEBUG_MAC80211, "mac80211 configure filter"); + wl1251_debug(DEBUG_MAC80211, "mac80211 configure filter"); - *total &= WL12XX_SUPPORTED_FILTERS; - changed &= WL12XX_SUPPORTED_FILTERS; + *total &= WL1251_SUPPORTED_FILTERS; + changed &= WL1251_SUPPORTED_FILTERS; if (changed == 0) /* no filters which we support changed */ @@ -585,8 +667,8 @@ static void wl12xx_op_configure_filter(struct ieee80211_hw *hw, /* FIXME: wl->rx_config and wl->rx_filter are not protected */ - wl->rx_config = WL12XX_DEFAULT_RX_CONFIG; - wl->rx_filter = WL12XX_DEFAULT_RX_FILTER; + wl->rx_config = WL1251_DEFAULT_RX_CONFIG; + wl->rx_filter = WL1251_DEFAULT_RX_FILTER; if (*total & FIF_PROMISC_IN_BSS) { wl->rx_config |= CFG_BSSID_FILTER_EN; @@ -618,7 +700,8 @@ static void wl12xx_op_configure_filter(struct ieee80211_hw *hw, } /* HW encryption */ -static int wl12xx_set_key_type(struct wl12xx *wl, struct acx_set_key *key, +static int wl1251_set_key_type(struct wl1251 *wl, + struct wl1251_cmd_set_keys *key, enum set_key_cmd cmd, struct ieee80211_key_conf *mac80211_key, const u8 *addr) @@ -648,95 +731,116 @@ static int wl12xx_set_key_type(struct wl12xx *wl, struct acx_set_key *key, mac80211_key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; break; default: - wl12xx_error("Unknown key algo 0x%x", mac80211_key->alg); + wl1251_error("Unknown key algo 0x%x", mac80211_key->alg); return -EOPNOTSUPP; } return 0; } -static int wl12xx_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, +static int wl1251_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { - struct wl12xx *wl = hw->priv; - struct acx_set_key wl_key; + struct wl1251 *wl = hw->priv; + struct wl1251_cmd_set_keys *wl_cmd; const u8 *addr; int ret; static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - wl12xx_debug(DEBUG_MAC80211, "mac80211 set key"); + wl1251_debug(DEBUG_MAC80211, "mac80211 set key"); - memset(&wl_key, 0, sizeof(wl_key)); + wl_cmd = kzalloc(sizeof(*wl_cmd), GFP_KERNEL); + if (!wl_cmd) { + ret = -ENOMEM; + goto out; + } addr = sta ? sta->addr : bcast_addr; - wl12xx_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd); - wl12xx_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN); - wl12xx_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x", + wl1251_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd); + wl1251_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN); + wl1251_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x", key->alg, key->keyidx, key->keylen, key->flags); - wl12xx_dump(DEBUG_CRYPT, "KEY: ", key->key, key->keylen); + wl1251_dump(DEBUG_CRYPT, "KEY: ", key->key, key->keylen); + + if (is_zero_ether_addr(addr)) { + /* We dont support TX only encryption */ + ret = -EOPNOTSUPP; + goto out; + } mutex_lock(&wl->mutex); + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out_unlock; + switch (cmd) { case SET_KEY: - wl_key.key_action = KEY_ADD_OR_REPLACE; + wl_cmd->key_action = KEY_ADD_OR_REPLACE; break; case DISABLE_KEY: - wl_key.key_action = KEY_REMOVE; + wl_cmd->key_action = KEY_REMOVE; break; default: - wl12xx_error("Unsupported key cmd 0x%x", cmd); + wl1251_error("Unsupported key cmd 0x%x", cmd); break; } - ret = wl12xx_set_key_type(wl, &wl_key, cmd, key, addr); + ret = wl1251_set_key_type(wl, wl_cmd, cmd, key, addr); if (ret < 0) { - wl12xx_error("Set KEY type failed"); - goto out; + wl1251_error("Set KEY type failed"); + goto out_sleep; } - if (wl_key.key_type != KEY_WEP_DEFAULT) - memcpy(wl_key.addr, addr, ETH_ALEN); + if (wl_cmd->key_type != KEY_WEP_DEFAULT) + memcpy(wl_cmd->addr, addr, ETH_ALEN); - if ((wl_key.key_type == KEY_TKIP_MIC_GROUP) || - (wl_key.key_type == KEY_TKIP_MIC_PAIRWISE)) { + if ((wl_cmd->key_type == KEY_TKIP_MIC_GROUP) || + (wl_cmd->key_type == KEY_TKIP_MIC_PAIRWISE)) { /* * We get the key in the following form: * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes) * but the target is expecting: * TKIP - RX MIC - TX MIC */ - memcpy(wl_key.key, key->key, 16); - memcpy(wl_key.key + 16, key->key + 24, 8); - memcpy(wl_key.key + 24, key->key + 16, 8); + memcpy(wl_cmd->key, key->key, 16); + memcpy(wl_cmd->key + 16, key->key + 24, 8); + memcpy(wl_cmd->key + 24, key->key + 16, 8); } else { - memcpy(wl_key.key, key->key, key->keylen); + memcpy(wl_cmd->key, key->key, key->keylen); } - wl_key.key_size = key->keylen; + wl_cmd->key_size = key->keylen; - wl_key.id = key->keyidx; - wl_key.ssid_profile = 0; + wl_cmd->id = key->keyidx; + wl_cmd->ssid_profile = 0; - wl12xx_dump(DEBUG_CRYPT, "TARGET KEY: ", &wl_key, sizeof(wl_key)); + wl1251_dump(DEBUG_CRYPT, "TARGET KEY: ", wl_cmd, sizeof(*wl_cmd)); - if (wl12xx_cmd_send(wl, CMD_SET_KEYS, &wl_key, sizeof(wl_key)) < 0) { - wl12xx_error("Set KEY failed"); - ret = -EOPNOTSUPP; - goto out; + ret = wl1251_cmd_send(wl, CMD_SET_KEYS, wl_cmd, sizeof(*wl_cmd)); + if (ret < 0) { + wl1251_warning("could not set keys"); + goto out_sleep; } -out: +out_sleep: + wl1251_ps_elp_sleep(wl); + +out_unlock: mutex_unlock(&wl->mutex); + +out: + kfree(wl_cmd); + return ret; } -static int wl12xx_build_basic_rates(char *rates) +static int wl1251_build_basic_rates(char *rates) { u8 index = 0; @@ -748,7 +852,7 @@ static int wl12xx_build_basic_rates(char *rates) return index; } -static int wl12xx_build_extended_rates(char *rates) +static int wl1251_build_extended_rates(char *rates) { u8 index = 0; @@ -765,7 +869,7 @@ static int wl12xx_build_extended_rates(char *rates) } -static int wl12xx_build_probe_req(struct wl12xx *wl, u8 *ssid, size_t ssid_len) +static int wl1251_build_probe_req(struct wl1251 *wl, u8 *ssid, size_t ssid_len) { struct wl12xx_probe_req_template template; struct wl12xx_ie_rates *rates; @@ -792,31 +896,30 @@ static int wl12xx_build_probe_req(struct wl12xx *wl, u8 *ssid, size_t ssid_len) /* Basic Rates */ rates = (struct wl12xx_ie_rates *)ptr; rates->header.id = WLAN_EID_SUPP_RATES; - rates->header.len = wl12xx_build_basic_rates(rates->rates); + rates->header.len = wl1251_build_basic_rates(rates->rates); size += sizeof(struct wl12xx_ie_header) + rates->header.len; ptr += sizeof(struct wl12xx_ie_header) + rates->header.len; /* Extended rates */ rates = (struct wl12xx_ie_rates *)ptr; rates->header.id = WLAN_EID_EXT_SUPP_RATES; - rates->header.len = wl12xx_build_extended_rates(rates->rates); + rates->header.len = wl1251_build_extended_rates(rates->rates); size += sizeof(struct wl12xx_ie_header) + rates->header.len; - wl12xx_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size); + wl1251_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size); - return wl12xx_cmd_template_set(wl, CMD_PROBE_REQ, &template, + return wl1251_cmd_template_set(wl, CMD_PROBE_REQ, &template, size); } -static int wl12xx_hw_scan(struct wl12xx *wl, u8 *ssid, size_t len, +static int wl1251_hw_scan(struct wl1251 *wl, u8 *ssid, size_t len, u8 active_scan, u8 high_prio, u8 num_channels, u8 probe_requests) { + struct wl1251_cmd_trigger_scan_to *trigger = NULL; + struct cmd_scan *params = NULL; int i, ret; - u32 split_scan = 0; u16 scan_options = 0; - struct cmd_scan *params; - struct wl12xx_command *cmd_answer; if (wl->scanning) return -EINVAL; @@ -864,33 +967,38 @@ static int wl12xx_hw_scan(struct wl12xx *wl, u8 *ssid, size_t len, memset(params->params.ssid, 0, 32); } - ret = wl12xx_build_probe_req(wl, ssid, len); + ret = wl1251_build_probe_req(wl, ssid, len); if (ret < 0) { - wl12xx_error("PROBE request template failed"); + wl1251_error("PROBE request template failed"); goto out; } - ret = wl12xx_cmd_send(wl, CMD_TRIGGER_SCAN_TO, &split_scan, - sizeof(u32)); + trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); + if (!trigger) + goto out; + + trigger->timeout = 0; + + ret = wl1251_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, + sizeof(*trigger)); if (ret < 0) { - wl12xx_error("Split SCAN failed"); + wl1251_error("trigger scan to failed for hw scan"); goto out; } - wl12xx_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params)); + wl1251_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params)); wl->scanning = true; - ret = wl12xx_cmd_send(wl, CMD_SCAN, params, sizeof(*params)); + ret = wl1251_cmd_send(wl, CMD_SCAN, params, sizeof(*params)); if (ret < 0) - wl12xx_error("SCAN failed"); + wl1251_error("SCAN failed"); - wl12xx_spi_mem_read(wl, wl->cmd_box_addr, params, sizeof(*params)); + wl1251_mem_read(wl, wl->cmd_box_addr, params, sizeof(*params)); - cmd_answer = (struct wl12xx_command *) params; - if (cmd_answer->status != CMD_STATUS_SUCCESS) { - wl12xx_error("TEST command answer error: %d", - cmd_answer->status); + if (params->header.status != CMD_STATUS_SUCCESS) { + wl1251_error("TEST command answer error: %d", + params->header.status); wl->scanning = false; ret = -EIO; goto out; @@ -902,15 +1010,15 @@ out: } -static int wl12xx_op_hw_scan(struct ieee80211_hw *hw, +static int wl1251_op_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req) { - struct wl12xx *wl = hw->priv; + struct wl1251 *wl = hw->priv; int ret; u8 *ssid = NULL; size_t ssid_len = 0; - wl12xx_debug(DEBUG_MAC80211, "mac80211 hw scan"); + wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan"); if (req->n_ssids) { ssid = req->ssids[0].ssid; @@ -918,85 +1026,117 @@ static int wl12xx_op_hw_scan(struct ieee80211_hw *hw, } mutex_lock(&wl->mutex); - ret = wl12xx_hw_scan(hw->priv, ssid, ssid_len, 1, 0, 13, 3); + + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl1251_hw_scan(hw->priv, ssid, ssid_len, 1, 0, 13, 3); + + wl1251_ps_elp_sleep(wl); + +out: mutex_unlock(&wl->mutex); return ret; } -static int wl12xx_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +static int wl1251_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { - struct wl12xx *wl = hw->priv; + struct wl1251 *wl = hw->priv; int ret; - ret = wl12xx_acx_rts_threshold(wl, (u16) value); + mutex_lock(&wl->mutex); + + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + ret = wl1251_acx_rts_threshold(wl, (u16) value); if (ret < 0) - wl12xx_warning("wl12xx_op_set_rts_threshold failed: %d", ret); + wl1251_warning("wl1251_op_set_rts_threshold failed: %d", ret); + + wl1251_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); return ret; } -static void wl12xx_op_bss_info_changed(struct ieee80211_hw *hw, +static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { - enum acx_ps_mode mode; - struct wl12xx *wl = hw->priv; + enum wl1251_cmd_ps_mode mode; + struct wl1251 *wl = hw->priv; struct sk_buff *beacon; int ret; - wl12xx_debug(DEBUG_MAC80211, "mac80211 bss info changed"); + wl1251_debug(DEBUG_MAC80211, "mac80211 bss info changed"); mutex_lock(&wl->mutex); + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + if (changed & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { + wl->beacon_int = bss_conf->beacon_int; + wl->dtim_period = bss_conf->dtim_period; + + /* FIXME: call join */ + wl->aid = bss_conf->aid; - ret = wl12xx_build_ps_poll(wl, wl->aid); + ret = wl1251_build_ps_poll(wl, wl->aid); if (ret < 0) - goto out; + goto out_sleep; - ret = wl12xx_acx_aid(wl, wl->aid); + ret = wl1251_acx_aid(wl, wl->aid); if (ret < 0) - goto out; + goto out_sleep; /* If we want to go in PSM but we're not there yet */ if (wl->psm_requested && !wl->psm) { mode = STATION_POWER_SAVE_MODE; - ret = wl12xx_ps_set_mode(wl, mode); + ret = wl1251_ps_set_mode(wl, mode); if (ret < 0) - goto out; + goto out_sleep; } + } else { + /* use defaults when not associated */ + wl->beacon_int = WL1251_DEFAULT_BEACON_INT; + wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD; } } if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) - ret = wl12xx_acx_slot(wl, SLOT_TIME_SHORT); + ret = wl1251_acx_slot(wl, SLOT_TIME_SHORT); else - ret = wl12xx_acx_slot(wl, SLOT_TIME_LONG); + ret = wl1251_acx_slot(wl, SLOT_TIME_LONG); if (ret < 0) { - wl12xx_warning("Set slot time failed %d", ret); - goto out; + wl1251_warning("Set slot time failed %d", ret); + goto out_sleep; } } if (changed & BSS_CHANGED_ERP_PREAMBLE) { if (bss_conf->use_short_preamble) - wl12xx_acx_set_preamble(wl, ACX_PREAMBLE_SHORT); + wl1251_acx_set_preamble(wl, ACX_PREAMBLE_SHORT); else - wl12xx_acx_set_preamble(wl, ACX_PREAMBLE_LONG); + wl1251_acx_set_preamble(wl, ACX_PREAMBLE_LONG); } if (changed & BSS_CHANGED_ERP_CTS_PROT) { if (bss_conf->use_cts_prot) - ret = wl12xx_acx_cts_protect(wl, CTSPROTECT_ENABLE); + ret = wl1251_acx_cts_protect(wl, CTSPROTECT_ENABLE); else - ret = wl12xx_acx_cts_protect(wl, CTSPROTECT_DISABLE); + ret = wl1251_acx_cts_protect(wl, CTSPROTECT_DISABLE); if (ret < 0) { - wl12xx_warning("Set ctsprotect failed %d", ret); + wl1251_warning("Set ctsprotect failed %d", ret); goto out; } } @@ -1004,20 +1144,23 @@ static void wl12xx_op_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BSSID) { memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); - ret = wl12xx_build_null_data(wl); + ret = wl1251_build_null_data(wl); if (ret < 0) goto out; if (wl->bss_type != BSS_TYPE_IBSS) { - ret = wl12xx_cmd_join(wl, wl->bss_type, 5, 100, 1); + ret = wl1251_join(wl, wl->bss_type, wl->channel, + wl->beacon_int, wl->dtim_period); if (ret < 0) - goto out; + goto out_sleep; + wl1251_warning("Set ctsprotect failed %d", ret); + goto out_sleep; } } if (changed & BSS_CHANGED_BEACON) { beacon = ieee80211_beacon_get(hw, vif); - ret = wl12xx_cmd_template_set(wl, CMD_BEACON, beacon->data, + ret = wl1251_cmd_template_set(wl, CMD_BEACON, beacon->data, beacon->len); if (ret < 0) { @@ -1025,7 +1168,7 @@ static void wl12xx_op_bss_info_changed(struct ieee80211_hw *hw, goto out; } - ret = wl12xx_cmd_template_set(wl, CMD_PROBE_RESP, beacon->data, + ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, beacon->data, beacon->len); dev_kfree_skb(beacon); @@ -1033,19 +1176,23 @@ static void wl12xx_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) goto out; - ret = wl12xx_cmd_join(wl, wl->bss_type, 1, 100, 0); + ret = wl1251_join(wl, wl->bss_type, wl->beacon_int, + wl->channel, wl->dtim_period); if (ret < 0) goto out; } +out_sleep: + wl1251_ps_elp_sleep(wl); + out: mutex_unlock(&wl->mutex); } /* can't be const, mac80211 writes to this */ -static struct ieee80211_rate wl12xx_rates[] = { +static struct ieee80211_rate wl1251_rates[] = { { .bitrate = 10, .hw_value = 0x1, .hw_value_short = 0x1, }, @@ -1088,7 +1235,7 @@ static struct ieee80211_rate wl12xx_rates[] = { }; /* can't be const, mac80211 writes to this */ -static struct ieee80211_channel wl12xx_channels[] = { +static struct ieee80211_channel wl1251_channels[] = { { .hw_value = 1, .center_freq = 2412}, { .hw_value = 2, .center_freq = 2417}, { .hw_value = 3, .center_freq = 2422}, @@ -1105,28 +1252,28 @@ static struct ieee80211_channel wl12xx_channels[] = { }; /* can't be const, mac80211 writes to this */ -static struct ieee80211_supported_band wl12xx_band_2ghz = { - .channels = wl12xx_channels, - .n_channels = ARRAY_SIZE(wl12xx_channels), - .bitrates = wl12xx_rates, - .n_bitrates = ARRAY_SIZE(wl12xx_rates), +static struct ieee80211_supported_band wl1251_band_2ghz = { + .channels = wl1251_channels, + .n_channels = ARRAY_SIZE(wl1251_channels), + .bitrates = wl1251_rates, + .n_bitrates = ARRAY_SIZE(wl1251_rates), }; -static const struct ieee80211_ops wl12xx_ops = { - .start = wl12xx_op_start, - .stop = wl12xx_op_stop, - .add_interface = wl12xx_op_add_interface, - .remove_interface = wl12xx_op_remove_interface, - .config = wl12xx_op_config, - .configure_filter = wl12xx_op_configure_filter, - .tx = wl12xx_op_tx, - .set_key = wl12xx_op_set_key, - .hw_scan = wl12xx_op_hw_scan, - .bss_info_changed = wl12xx_op_bss_info_changed, - .set_rts_threshold = wl12xx_op_set_rts_threshold, +static const struct ieee80211_ops wl1251_ops = { + .start = wl1251_op_start, + .stop = wl1251_op_stop, + .add_interface = wl1251_op_add_interface, + .remove_interface = wl1251_op_remove_interface, + .config = wl1251_op_config, + .configure_filter = wl1251_op_configure_filter, + .tx = wl1251_op_tx, + .set_key = wl1251_op_set_key, + .hw_scan = wl1251_op_hw_scan, + .bss_info_changed = wl1251_op_bss_info_changed, + .set_rts_threshold = wl1251_op_set_rts_threshold, }; -static int wl12xx_register_hw(struct wl12xx *wl) +static int wl1251_register_hw(struct wl1251 *wl) { int ret; @@ -1137,22 +1284,24 @@ static int wl12xx_register_hw(struct wl12xx *wl) ret = ieee80211_register_hw(wl->hw); if (ret < 0) { - wl12xx_error("unable to register mac80211 hw: %d", ret); + wl1251_error("unable to register mac80211 hw: %d", ret); return ret; } wl->mac80211_registered = true; - wl12xx_notice("loaded"); + wl1251_notice("loaded"); return 0; } -static int wl12xx_init_ieee80211(struct wl12xx *wl) +int wl1251_init_ieee80211(struct wl1251 *wl) { + int ret; + /* The tx descriptor buffer and the TKIP space */ wl->hw->extra_tx_headroom = sizeof(struct tx_double_buffer_desc) - + WL12XX_TKIP_IV_SPACE; + + WL1251_TKIP_IV_SPACE; /* unit us */ /* FIXME: find a proper value */ @@ -1163,48 +1312,46 @@ static int wl12xx_init_ieee80211(struct wl12xx *wl) wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); wl->hw->wiphy->max_scan_ssids = 1; - wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl12xx_band_2ghz; + wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1251_band_2ghz; - SET_IEEE80211_DEV(wl->hw, &wl->spi->dev); + ret = wl1251_register_hw(wl); + if (ret) + goto out; - return 0; + wl1251_debugfs_init(wl); + wl1251_notice("initialized"); + + ret = 0; + +out: + return ret; } +EXPORT_SYMBOL_GPL(wl1251_init_ieee80211); -#define WL12XX_DEFAULT_CHANNEL 1 -static int __devinit wl12xx_probe(struct spi_device *spi) +struct ieee80211_hw *wl1251_alloc_hw(void) { - struct wl12xx_platform_data *pdata; struct ieee80211_hw *hw; - struct wl12xx *wl; - int ret, i; + struct wl1251 *wl; + int i; static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf}; - pdata = spi->dev.platform_data; - if (!pdata) { - wl12xx_error("no platform data"); - return -ENODEV; - } - - hw = ieee80211_alloc_hw(sizeof(*wl), &wl12xx_ops); + hw = ieee80211_alloc_hw(sizeof(*wl), &wl1251_ops); if (!hw) { - wl12xx_error("could not alloc ieee80211_hw"); - return -ENOMEM; + wl1251_error("could not alloc ieee80211_hw"); + return ERR_PTR(-ENOMEM); } wl = hw->priv; memset(wl, 0, sizeof(*wl)); wl->hw = hw; - dev_set_drvdata(&spi->dev, wl); - wl->spi = spi; wl->data_in_count = 0; skb_queue_head_init(&wl->tx_queue); - INIT_WORK(&wl->tx_work, wl12xx_tx_work); - INIT_WORK(&wl->filter_work, wl12xx_filter_work); - wl->channel = WL12XX_DEFAULT_CHANNEL; + INIT_WORK(&wl->filter_work, wl1251_filter_work); + wl->channel = WL1251_DEFAULT_CHANNEL; wl->scanning = false; wl->default_key = 0; wl->listen_int = 1; @@ -1212,23 +1359,24 @@ static int __devinit wl12xx_probe(struct spi_device *spi) wl->rx_handled = 0; wl->rx_current_buffer = 0; wl->rx_last_id = 0; - wl->rx_config = WL12XX_DEFAULT_RX_CONFIG; - wl->rx_filter = WL12XX_DEFAULT_RX_FILTER; + wl->rx_config = WL1251_DEFAULT_RX_CONFIG; + wl->rx_filter = WL1251_DEFAULT_RX_FILTER; wl->elp = false; wl->psm = 0; wl->psm_requested = false; wl->tx_queue_stopped = false; - wl->power_level = WL12XX_DEFAULT_POWER_LEVEL; - - /* We use the default power on sleep time until we know which chip - * we're using */ - wl->chip.power_on_sleep = WL12XX_DEFAULT_POWER_ON_SLEEP; + wl->power_level = WL1251_DEFAULT_POWER_LEVEL; + wl->beacon_int = WL1251_DEFAULT_BEACON_INT; + wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD; for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) wl->tx_frames[i] = NULL; wl->next_tx_complete = 0; + INIT_WORK(&wl->irq_work, wl1251_irq_work); + INIT_WORK(&wl->tx_work, wl1251_tx_work); + /* * In case our MAC address is not correctly set, * we use a random but Nokia MAC. @@ -1236,123 +1384,45 @@ static int __devinit wl12xx_probe(struct spi_device *spi) memcpy(wl->mac_addr, nokia_oui, 3); get_random_bytes(wl->mac_addr + 3, 3); - wl->state = WL12XX_STATE_OFF; + wl->state = WL1251_STATE_OFF; mutex_init(&wl->mutex); wl->tx_mgmt_frm_rate = DEFAULT_HW_GEN_TX_RATE; wl->tx_mgmt_frm_mod = DEFAULT_HW_GEN_MODULATION_TYPE; - /* This is the only SPI value that we need to set here, the rest - * comes from the board-peripherals file */ - spi->bits_per_word = 32; - - ret = spi_setup(spi); - if (ret < 0) { - wl12xx_error("spi_setup failed"); - goto out_free; - } - - wl->set_power = pdata->set_power; - if (!wl->set_power) { - wl12xx_error("set power function missing in platform data"); - return -ENODEV; - } - - wl->irq = spi->irq; - if (wl->irq < 0) { - wl12xx_error("irq missing in platform data"); - return -ENODEV; - } - - ret = request_irq(wl->irq, wl12xx_irq, 0, DRIVER_NAME, wl); - if (ret < 0) { - wl12xx_error("request_irq() failed: %d", ret); - goto out_free; + wl->rx_descriptor = kmalloc(sizeof(*wl->rx_descriptor), GFP_KERNEL); + if (!wl->rx_descriptor) { + wl1251_error("could not allocate memory for rx descriptor"); + ieee80211_free_hw(hw); + return ERR_PTR(-ENOMEM); } - set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); - - disable_irq(wl->irq); - - ret = wl12xx_init_ieee80211(wl); - if (ret) - goto out_irq; - - ret = wl12xx_register_hw(wl); - if (ret) - goto out_irq; - - wl12xx_debugfs_init(wl); - - wl12xx_notice("initialized"); - - return 0; - - out_irq: - free_irq(wl->irq, wl); - - out_free: - ieee80211_free_hw(hw); - - return ret; + return hw; } +EXPORT_SYMBOL_GPL(wl1251_alloc_hw); -static int __devexit wl12xx_remove(struct spi_device *spi) +int wl1251_free_hw(struct wl1251 *wl) { - struct wl12xx *wl = dev_get_drvdata(&spi->dev); - ieee80211_unregister_hw(wl->hw); - wl12xx_debugfs_exit(wl); + wl1251_debugfs_exit(wl); - free_irq(wl->irq, wl); kfree(wl->target_mem_map); kfree(wl->data_path); kfree(wl->fw); wl->fw = NULL; kfree(wl->nvs); wl->nvs = NULL; - ieee80211_free_hw(wl->hw); - - return 0; -} - -static struct spi_driver wl12xx_spi_driver = { - .driver = { - .name = "wl12xx", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - }, + kfree(wl->rx_descriptor); + wl->rx_descriptor = NULL; - .probe = wl12xx_probe, - .remove = __devexit_p(wl12xx_remove), -}; - -static int __init wl12xx_init(void) -{ - int ret; - - ret = spi_register_driver(&wl12xx_spi_driver); - if (ret < 0) { - wl12xx_error("failed to register spi driver: %d", ret); - goto out; - } - -out: - return ret; -} - -static void __exit wl12xx_exit(void) -{ - spi_unregister_driver(&wl12xx_spi_driver); + ieee80211_free_hw(wl->hw); - wl12xx_notice("unloaded"); + return 0; } +EXPORT_SYMBOL_GPL(wl1251_free_hw); -module_init(wl12xx_init); -module_exit(wl12xx_exit); - +MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core"); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kalle Valo <Kalle.Valo@nokia.com>, " - "Luciano Coelho <luciano.coelho@nokia.com>"); +MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>"); diff --git a/drivers/net/wireless/wl12xx/init.h b/drivers/net/wireless/wl12xx/wl1251_netlink.h index c8b6cd0b7c3e..ee36695e134e 100644 --- a/drivers/net/wireless/wl12xx/init.h +++ b/drivers/net/wireless/wl12xx/wl1251_netlink.h @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (C) 2009 Nokia Corporation * @@ -21,20 +21,10 @@ * */ -#ifndef __WL12XX_INIT_H__ -#define __WL12XX_INIT_H__ +#ifndef __WL1251_NETLINK_H__ +#define __WL1251_NETLINK_H__ -#include "wl12xx.h" +int wl1251_nl_register(void); +void wl1251_nl_unregister(void); -int wl12xx_hw_init_hwenc_config(struct wl12xx *wl); -int wl12xx_hw_init_templates_config(struct wl12xx *wl); -int wl12xx_hw_init_mem_config(struct wl12xx *wl); -int wl12xx_hw_init_rx_config(struct wl12xx *wl, u32 config, u32 filter); -int wl12xx_hw_init_phy_config(struct wl12xx *wl); -int wl12xx_hw_init_beacon_filter(struct wl12xx *wl); -int wl12xx_hw_init_pta(struct wl12xx *wl); -int wl12xx_hw_init_energy_detection(struct wl12xx *wl); -int wl12xx_hw_init_beacon_broadcast(struct wl12xx *wl); -int wl12xx_hw_init_power_auth(struct wl12xx *wl); - -#endif +#endif /* __WL1251_NETLINK_H__ */ diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/wl1251_ps.c index 83a10117330b..c53e28727ed4 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/wl1251_ps.c @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (C) 2008 Nokia Corporation * @@ -21,26 +21,27 @@ * */ -#include "reg.h" -#include "ps.h" -#include "spi.h" +#include "wl1251_reg.h" +#include "wl1251_ps.h" +#include "wl1251_cmd.h" +#include "wl1251_io.h" -#define WL12XX_WAKEUP_TIMEOUT 2000 +#define WL1251_WAKEUP_TIMEOUT 2000 /* Routines to toggle sleep mode while in ELP */ -void wl12xx_ps_elp_sleep(struct wl12xx *wl) +void wl1251_ps_elp_sleep(struct wl1251 *wl) { if (wl->elp || !wl->psm) return; - wl12xx_debug(DEBUG_PSM, "chip to elp"); + wl1251_debug(DEBUG_PSM, "chip to elp"); - wl12xx_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); + wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); wl->elp = true; } -int wl12xx_ps_elp_wakeup(struct wl12xx *wl) +int wl1251_ps_elp_wakeup(struct wl1251 *wl) { unsigned long timeout; u32 elp_reg; @@ -48,13 +49,13 @@ int wl12xx_ps_elp_wakeup(struct wl12xx *wl) if (!wl->elp) return 0; - wl12xx_debug(DEBUG_PSM, "waking up chip from elp"); + wl1251_debug(DEBUG_PSM, "waking up chip from elp"); - timeout = jiffies + msecs_to_jiffies(WL12XX_WAKEUP_TIMEOUT); + timeout = jiffies + msecs_to_jiffies(WL1251_WAKEUP_TIMEOUT); - wl12xx_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); + wl1251_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); - elp_reg = wl12xx_read32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); + elp_reg = wl1251_read32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); /* * FIXME: we should wait for irq from chip but, as a temporary @@ -62,40 +63,36 @@ int wl12xx_ps_elp_wakeup(struct wl12xx *wl) */ while (!(elp_reg & ELPCTRL_WLAN_READY)) { if (time_after(jiffies, timeout)) { - wl12xx_error("elp wakeup timeout"); + wl1251_error("elp wakeup timeout"); return -ETIMEDOUT; } msleep(1); - elp_reg = wl12xx_read32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); + elp_reg = wl1251_read32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR); } - wl12xx_debug(DEBUG_PSM, "wakeup time: %u ms", + wl1251_debug(DEBUG_PSM, "wakeup time: %u ms", jiffies_to_msecs(jiffies) - - (jiffies_to_msecs(timeout) - WL12XX_WAKEUP_TIMEOUT)); + (jiffies_to_msecs(timeout) - WL1251_WAKEUP_TIMEOUT)); wl->elp = false; return 0; } -static int wl12xx_ps_set_elp(struct wl12xx *wl, bool enable) +static int wl1251_ps_set_elp(struct wl1251 *wl, bool enable) { int ret; if (enable) { - wl12xx_debug(DEBUG_PSM, "sleep auth psm/elp"); + wl1251_debug(DEBUG_PSM, "sleep auth psm/elp"); - /* - * FIXME: we should PSM_ELP, but because of firmware wakeup - * problems let's use only PSM_PS - */ - ret = wl12xx_acx_sleep_auth(wl, WL12XX_PSM_PS); + ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_ELP); if (ret < 0) return ret; - wl12xx_ps_elp_sleep(wl); + wl1251_ps_elp_sleep(wl); } else { - wl12xx_debug(DEBUG_PSM, "sleep auth cam"); + wl1251_debug(DEBUG_PSM, "sleep auth cam"); /* * When the target is in ELP, we can only @@ -104,9 +101,9 @@ static int wl12xx_ps_set_elp(struct wl12xx *wl, bool enable) * changing the power authorization. */ - wl12xx_ps_elp_wakeup(wl); + wl1251_ps_elp_wakeup(wl); - ret = wl12xx_acx_sleep_auth(wl, WL12XX_PSM_CAM); + ret = wl1251_acx_sleep_auth(wl, WL1251_PSM_CAM); if (ret < 0) return ret; } @@ -114,18 +111,25 @@ static int wl12xx_ps_set_elp(struct wl12xx *wl, bool enable) return 0; } -int wl12xx_ps_set_mode(struct wl12xx *wl, enum acx_ps_mode mode) +int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode) { int ret; switch (mode) { case STATION_POWER_SAVE_MODE: - wl12xx_debug(DEBUG_PSM, "entering psm"); - ret = wl12xx_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE); + wl1251_debug(DEBUG_PSM, "entering psm"); + + ret = wl1251_acx_wake_up_conditions(wl, + WAKE_UP_EVENT_DTIM_BITMAP, + wl->listen_int); if (ret < 0) return ret; - ret = wl12xx_ps_set_elp(wl, true); + ret = wl1251_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE); + if (ret < 0) + return ret; + + ret = wl1251_ps_set_elp(wl, true); if (ret < 0) return ret; @@ -133,12 +137,18 @@ int wl12xx_ps_set_mode(struct wl12xx *wl, enum acx_ps_mode mode) break; case STATION_ACTIVE_MODE: default: - wl12xx_debug(DEBUG_PSM, "leaving psm"); - ret = wl12xx_ps_set_elp(wl, false); + wl1251_debug(DEBUG_PSM, "leaving psm"); + ret = wl1251_ps_set_elp(wl, false); + if (ret < 0) + return ret; + + ret = wl1251_acx_wake_up_conditions(wl, + WAKE_UP_EVENT_DTIM_BITMAP, + wl->listen_int); if (ret < 0) return ret; - ret = wl12xx_cmd_ps_mode(wl, STATION_ACTIVE_MODE); + ret = wl1251_cmd_ps_mode(wl, STATION_ACTIVE_MODE); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/ps.h b/drivers/net/wireless/wl12xx/wl1251_ps.h index 5d7c52553830..db036fe12f25 100644 --- a/drivers/net/wireless/wl12xx/ps.h +++ b/drivers/net/wireless/wl12xx/wl1251_ps.h @@ -1,8 +1,8 @@ -#ifndef __WL12XX_PS_H__ -#define __WL12XX_PS_H__ +#ifndef __WL1251_PS_H__ +#define __WL1251_PS_H__ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation @@ -25,12 +25,12 @@ * */ -#include "wl12xx.h" -#include "acx.h" +#include "wl1251.h" +#include "wl1251_acx.h" -int wl12xx_ps_set_mode(struct wl12xx *wl, enum acx_ps_mode mode); -void wl12xx_ps_elp_sleep(struct wl12xx *wl); -int wl12xx_ps_elp_wakeup(struct wl12xx *wl); +int wl1251_ps_set_mode(struct wl1251 *wl, enum wl1251_cmd_ps_mode mode); +void wl1251_ps_elp_sleep(struct wl1251 *wl); +int wl1251_ps_elp_wakeup(struct wl1251 *wl); -#endif /* __WL12XX_PS_H__ */ +#endif /* __WL1251_PS_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1251_reg.h b/drivers/net/wireless/wl12xx/wl1251_reg.h new file mode 100644 index 000000000000..06e1bd94a739 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1251_reg.h @@ -0,0 +1,644 @@ +/* + * This file is part of wl12xx + * + * Copyright (c) 1998-2007 Texas Instruments Incorporated + * Copyright (C) 2008 Nokia Corporation + * + * Contact: Kalle Valo <kalle.valo@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __REG_H__ +#define __REG_H__ + +#include <linux/bitops.h> + +#define REGISTERS_BASE 0x00300000 +#define DRPW_BASE 0x00310000 + +#define REGISTERS_DOWN_SIZE 0x00008800 +#define REGISTERS_WORK_SIZE 0x0000b000 + +#define HW_ACCESS_ELP_CTRL_REG_ADDR 0x1FFFC + +/* ELP register commands */ +#define ELPCTRL_WAKE_UP 0x1 +#define ELPCTRL_WAKE_UP_WLAN_READY 0x5 +#define ELPCTRL_SLEEP 0x0 +/* ELP WLAN_READY bit */ +#define ELPCTRL_WLAN_READY 0x2 + +/* Device Configuration registers*/ +#define SOR_CFG (REGISTERS_BASE + 0x0800) +#define ECPU_CTRL (REGISTERS_BASE + 0x0804) +#define HI_CFG (REGISTERS_BASE + 0x0808) +#define EE_START (REGISTERS_BASE + 0x080C) + +#define CHIP_ID_B (REGISTERS_BASE + 0x5674) + +#define CHIP_ID_1251_PG10 (0x7010101) +#define CHIP_ID_1251_PG11 (0x7020101) +#define CHIP_ID_1251_PG12 (0x7030101) + +#define ENABLE (REGISTERS_BASE + 0x5450) + +/* Power Management registers */ +#define ELP_CFG_MODE (REGISTERS_BASE + 0x5804) +#define ELP_CMD (REGISTERS_BASE + 0x5808) +#define PLL_CAL_TIME (REGISTERS_BASE + 0x5810) +#define CLK_REQ_TIME (REGISTERS_BASE + 0x5814) +#define CLK_BUF_TIME (REGISTERS_BASE + 0x5818) + +#define CFG_PLL_SYNC_CNT (REGISTERS_BASE + 0x5820) + +/* Scratch Pad registers*/ +#define SCR_PAD0 (REGISTERS_BASE + 0x5608) +#define SCR_PAD1 (REGISTERS_BASE + 0x560C) +#define SCR_PAD2 (REGISTERS_BASE + 0x5610) +#define SCR_PAD3 (REGISTERS_BASE + 0x5614) +#define SCR_PAD4 (REGISTERS_BASE + 0x5618) +#define SCR_PAD4_SET (REGISTERS_BASE + 0x561C) +#define SCR_PAD4_CLR (REGISTERS_BASE + 0x5620) +#define SCR_PAD5 (REGISTERS_BASE + 0x5624) +#define SCR_PAD5_SET (REGISTERS_BASE + 0x5628) +#define SCR_PAD5_CLR (REGISTERS_BASE + 0x562C) +#define SCR_PAD6 (REGISTERS_BASE + 0x5630) +#define SCR_PAD7 (REGISTERS_BASE + 0x5634) +#define SCR_PAD8 (REGISTERS_BASE + 0x5638) +#define SCR_PAD9 (REGISTERS_BASE + 0x563C) + +/* Spare registers*/ +#define SPARE_A1 (REGISTERS_BASE + 0x0994) +#define SPARE_A2 (REGISTERS_BASE + 0x0998) +#define SPARE_A3 (REGISTERS_BASE + 0x099C) +#define SPARE_A4 (REGISTERS_BASE + 0x09A0) +#define SPARE_A5 (REGISTERS_BASE + 0x09A4) +#define SPARE_A6 (REGISTERS_BASE + 0x09A8) +#define SPARE_A7 (REGISTERS_BASE + 0x09AC) +#define SPARE_A8 (REGISTERS_BASE + 0x09B0) +#define SPARE_B1 (REGISTERS_BASE + 0x5420) +#define SPARE_B2 (REGISTERS_BASE + 0x5424) +#define SPARE_B3 (REGISTERS_BASE + 0x5428) +#define SPARE_B4 (REGISTERS_BASE + 0x542C) +#define SPARE_B5 (REGISTERS_BASE + 0x5430) +#define SPARE_B6 (REGISTERS_BASE + 0x5434) +#define SPARE_B7 (REGISTERS_BASE + 0x5438) +#define SPARE_B8 (REGISTERS_BASE + 0x543C) + +enum wl12xx_acx_int_reg { + ACX_REG_INTERRUPT_TRIG, + ACX_REG_INTERRUPT_TRIG_H, + +/*============================================= + Host Interrupt Mask Register - 32bit (RW) + ------------------------------------------ + Setting a bit in this register masks the + corresponding interrupt to the host. + 0 - RX0 - Rx first dubble buffer Data Interrupt + 1 - TXD - Tx Data Interrupt + 2 - TXXFR - Tx Transfer Interrupt + 3 - RX1 - Rx second dubble buffer Data Interrupt + 4 - RXXFR - Rx Transfer Interrupt + 5 - EVENT_A - Event Mailbox interrupt + 6 - EVENT_B - Event Mailbox interrupt + 7 - WNONHST - Wake On Host Interrupt + 8 - TRACE_A - Debug Trace interrupt + 9 - TRACE_B - Debug Trace interrupt + 10 - CDCMP - Command Complete Interrupt + 11 - + 12 - + 13 - + 14 - ICOMP - Initialization Complete Interrupt + 16 - SG SE - Soft Gemini - Sense enable interrupt + 17 - SG SD - Soft Gemini - Sense disable interrupt + 18 - - + 19 - - + 20 - - + 21- - + Default: 0x0001 +*==============================================*/ + ACX_REG_INTERRUPT_MASK, + +/*============================================= + Host Interrupt Mask Set 16bit, (Write only) + ------------------------------------------ + Setting a bit in this register sets + the corresponding bin in ACX_HINT_MASK register + without effecting the mask + state of other bits (0 = no effect). +==============================================*/ + ACX_REG_HINT_MASK_SET, + +/*============================================= + Host Interrupt Mask Clear 16bit,(Write only) + ------------------------------------------ + Setting a bit in this register clears + the corresponding bin in ACX_HINT_MASK register + without effecting the mask + state of other bits (0 = no effect). +=============================================*/ + ACX_REG_HINT_MASK_CLR, + +/*============================================= + Host Interrupt Status Nondestructive Read + 16bit,(Read only) + ------------------------------------------ + The host can read this register to determine + which interrupts are active. + Reading this register doesn't + effect its content. +=============================================*/ + ACX_REG_INTERRUPT_NO_CLEAR, + +/*============================================= + Host Interrupt Status Clear on Read Register + 16bit,(Read only) + ------------------------------------------ + The host can read this register to determine + which interrupts are active. + Reading this register clears it, + thus making all interrupts inactive. +==============================================*/ + ACX_REG_INTERRUPT_CLEAR, + +/*============================================= + Host Interrupt Acknowledge Register + 16bit,(Write only) + ------------------------------------------ + The host can set individual bits in this + register to clear (acknowledge) the corresp. + interrupt status bits in the HINT_STS_CLR and + HINT_STS_ND registers, thus making the + assotiated interrupt inactive. (0-no effect) +==============================================*/ + ACX_REG_INTERRUPT_ACK, + +/*=============================================== + Host Software Reset - 32bit RW + ------------------------------------------ + [31:1] Reserved + 0 SOFT_RESET Soft Reset - When this bit is set, + it holds the Wlan hardware in a soft reset state. + This reset disables all MAC and baseband processor + clocks except the CardBus/PCI interface clock. + It also initializes all MAC state machines except + the host interface. It does not reload the + contents of the EEPROM. When this bit is cleared + (not self-clearing), the Wlan hardware + exits the software reset state. +===============================================*/ + ACX_REG_SLV_SOFT_RESET, + +/*=============================================== + EEPROM Burst Read Start - 32bit RW + ------------------------------------------ + [31:1] Reserved + 0 ACX_EE_START - EEPROM Burst Read Start 0 + Setting this bit starts a burst read from + the external EEPROM. + If this bit is set (after reset) before an EEPROM read/write, + the burst read starts at EEPROM address 0. + Otherwise, it starts at the address + following the address of the previous access. + TheWlan hardware hardware clears this bit automatically. + + Default: 0x00000000 +*================================================*/ + ACX_REG_EE_START, + +/* Embedded ARM CPU Control */ + +/*=============================================== + Halt eCPU - 32bit RW + ------------------------------------------ + 0 HALT_ECPU Halt Embedded CPU - This bit is the + compliment of bit 1 (MDATA2) in the SOR_CFG register. + During a hardware reset, this bit holds + the inverse of MDATA2. + When downloading firmware from the host, + set this bit (pull down MDATA2). + The host clears this bit after downloading the firmware into + zero-wait-state SSRAM. + When loading firmware from Flash, clear this bit (pull up MDATA2) + so that the eCPU can run the bootloader code in Flash + HALT_ECPU eCPU State + -------------------- + 1 halt eCPU + 0 enable eCPU + ===============================================*/ + ACX_REG_ECPU_CONTROL, + + ACX_REG_TABLE_LEN +}; + +#define ACX_SLV_SOFT_RESET_BIT BIT(0) +#define ACX_REG_EEPROM_START_BIT BIT(0) + +/* Command/Information Mailbox Pointers */ + +/*=============================================== + Command Mailbox Pointer - 32bit RW + ------------------------------------------ + This register holds the start address of + the command mailbox located in the Wlan hardware memory. + The host must read this pointer after a reset to + find the location of the command mailbox. + The Wlan hardware initializes the command mailbox + pointer with the default address of the command mailbox. + The command mailbox pointer is not valid until after + the host receives the Init Complete interrupt from + the Wlan hardware. + ===============================================*/ +#define REG_COMMAND_MAILBOX_PTR (SCR_PAD0) + +/*=============================================== + Information Mailbox Pointer - 32bit RW + ------------------------------------------ + This register holds the start address of + the information mailbox located in the Wlan hardware memory. + The host must read this pointer after a reset to find + the location of the information mailbox. + The Wlan hardware initializes the information mailbox pointer + with the default address of the information mailbox. + The information mailbox pointer is not valid + until after the host receives the Init Complete interrupt from + the Wlan hardware. + ===============================================*/ +#define REG_EVENT_MAILBOX_PTR (SCR_PAD1) + + +/* Misc */ + +#define REG_ENABLE_TX_RX (ENABLE) +/* + * Rx configuration (filter) information element + * --------------------------------------------- + */ +#define REG_RX_CONFIG (RX_CFG) +#define REG_RX_FILTER (RX_FILTER_CFG) + + +#define RX_CFG_ENABLE_PHY_HEADER_PLCP 0x0002 + +/* promiscuous - receives all valid frames */ +#define RX_CFG_PROMISCUOUS 0x0008 + +/* receives frames from any BSSID */ +#define RX_CFG_BSSID 0x0020 + +/* receives frames destined to any MAC address */ +#define RX_CFG_MAC 0x0010 + +#define RX_CFG_ENABLE_ONLY_MY_DEST_MAC 0x0010 +#define RX_CFG_ENABLE_ANY_DEST_MAC 0x0000 +#define RX_CFG_ENABLE_ONLY_MY_BSSID 0x0020 +#define RX_CFG_ENABLE_ANY_BSSID 0x0000 + +/* discards all broadcast frames */ +#define RX_CFG_DISABLE_BCAST 0x0200 + +#define RX_CFG_ENABLE_ONLY_MY_SSID 0x0400 +#define RX_CFG_ENABLE_RX_CMPLT_FCS_ERROR 0x0800 +#define RX_CFG_COPY_RX_STATUS 0x2000 +#define RX_CFG_TSF 0x10000 + +#define RX_CONFIG_OPTION_ANY_DST_MY_BSS (RX_CFG_ENABLE_ANY_DEST_MAC | \ + RX_CFG_ENABLE_ONLY_MY_BSSID) + +#define RX_CONFIG_OPTION_MY_DST_ANY_BSS (RX_CFG_ENABLE_ONLY_MY_DEST_MAC\ + | RX_CFG_ENABLE_ANY_BSSID) + +#define RX_CONFIG_OPTION_ANY_DST_ANY_BSS (RX_CFG_ENABLE_ANY_DEST_MAC | \ + RX_CFG_ENABLE_ANY_BSSID) + +#define RX_CONFIG_OPTION_MY_DST_MY_BSS (RX_CFG_ENABLE_ONLY_MY_DEST_MAC\ + | RX_CFG_ENABLE_ONLY_MY_BSSID) + +#define RX_CONFIG_OPTION_FOR_SCAN (RX_CFG_ENABLE_PHY_HEADER_PLCP \ + | RX_CFG_ENABLE_RX_CMPLT_FCS_ERROR \ + | RX_CFG_COPY_RX_STATUS | RX_CFG_TSF) + +#define RX_CONFIG_OPTION_FOR_MEASUREMENT (RX_CFG_ENABLE_ANY_DEST_MAC) + +#define RX_CONFIG_OPTION_FOR_JOIN (RX_CFG_ENABLE_ONLY_MY_BSSID | \ + RX_CFG_ENABLE_ONLY_MY_DEST_MAC) + +#define RX_CONFIG_OPTION_FOR_IBSS_JOIN (RX_CFG_ENABLE_ONLY_MY_SSID | \ + RX_CFG_ENABLE_ONLY_MY_DEST_MAC) + +#define RX_FILTER_OPTION_DEF (CFG_RX_MGMT_EN | CFG_RX_DATA_EN\ + | CFG_RX_CTL_EN | CFG_RX_BCN_EN\ + | CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN) + +#define RX_FILTER_OPTION_FILTER_ALL 0 + +#define RX_FILTER_OPTION_DEF_PRSP_BCN (CFG_RX_PRSP_EN | CFG_RX_MGMT_EN\ + | CFG_RX_RCTS_ACK | CFG_RX_BCN_EN) + +#define RX_FILTER_OPTION_JOIN (CFG_RX_MGMT_EN | CFG_RX_DATA_EN\ + | CFG_RX_BCN_EN | CFG_RX_AUTH_EN\ + | CFG_RX_ASSOC_EN | CFG_RX_RCTS_ACK\ + | CFG_RX_PRSP_EN) + + +/*=============================================== + EEPROM Read/Write Request 32bit RW + ------------------------------------------ + 1 EE_READ - EEPROM Read Request 1 - Setting this bit + loads a single byte of data into the EE_DATA + register from the EEPROM location specified in + the EE_ADDR register. + The Wlan hardware hardware clears this bit automatically. + EE_DATA is valid when this bit is cleared. + + 0 EE_WRITE - EEPROM Write Request - Setting this bit + writes a single byte of data from the EE_DATA register into the + EEPROM location specified in the EE_ADDR register. + The Wlan hardware hardware clears this bit automatically. +*===============================================*/ +#define ACX_EE_CTL_REG EE_CTL +#define EE_WRITE 0x00000001ul +#define EE_READ 0x00000002ul + +/*=============================================== + EEPROM Address - 32bit RW + ------------------------------------------ + This register specifies the address + within the EEPROM from/to which to read/write data. + ===============================================*/ +#define ACX_EE_ADDR_REG EE_ADDR + +/*=============================================== + EEPROM Data - 32bit RW + ------------------------------------------ + This register either holds the read 8 bits of + data from the EEPROM or the write data + to be written to the EEPROM. + ===============================================*/ +#define ACX_EE_DATA_REG EE_DATA + +/*=============================================== + EEPROM Base Address - 32bit RW + ------------------------------------------ + This register holds the upper nine bits + [23:15] of the 24-bit Wlan hardware memory + address for burst reads from EEPROM accesses. + The EEPROM provides the lower 15 bits of this address. + The MSB of the address from the EEPROM is ignored. + ===============================================*/ +#define ACX_EE_CFG EE_CFG + +/*=============================================== + GPIO Output Values -32bit, RW + ------------------------------------------ + [31:16] Reserved + [15: 0] Specify the output values (at the output driver inputs) for + GPIO[15:0], respectively. + ===============================================*/ +#define ACX_GPIO_OUT_REG GPIO_OUT +#define ACX_MAX_GPIO_LINES 15 + +/*=============================================== + Contention window -32bit, RW + ------------------------------------------ + [31:26] Reserved + [25:16] Max (0x3ff) + [15:07] Reserved + [06:00] Current contention window value - default is 0x1F + ===============================================*/ +#define ACX_CONT_WIND_CFG_REG CONT_WIND_CFG +#define ACX_CONT_WIND_MIN_MASK 0x0000007f +#define ACX_CONT_WIND_MAX 0x03ff0000 + +/*=============================================== + HI_CFG Interface Configuration Register Values + ------------------------------------------ + ===============================================*/ +#define HI_CFG_UART_ENABLE 0x00000004 +#define HI_CFG_RST232_ENABLE 0x00000008 +#define HI_CFG_CLOCK_REQ_SELECT 0x00000010 +#define HI_CFG_HOST_INT_ENABLE 0x00000020 +#define HI_CFG_VLYNQ_OUTPUT_ENABLE 0x00000040 +#define HI_CFG_HOST_INT_ACTIVE_LOW 0x00000080 +#define HI_CFG_UART_TX_OUT_GPIO_15 0x00000100 +#define HI_CFG_UART_TX_OUT_GPIO_14 0x00000200 +#define HI_CFG_UART_TX_OUT_GPIO_7 0x00000400 + +/* + * NOTE: USE_ACTIVE_HIGH compilation flag should be defined in makefile + * for platforms using active high interrupt level + */ +#ifdef USE_ACTIVE_HIGH +#define HI_CFG_DEF_VAL \ + (HI_CFG_UART_ENABLE | \ + HI_CFG_RST232_ENABLE | \ + HI_CFG_CLOCK_REQ_SELECT | \ + HI_CFG_HOST_INT_ENABLE) +#else +#define HI_CFG_DEF_VAL \ + (HI_CFG_UART_ENABLE | \ + HI_CFG_RST232_ENABLE | \ + HI_CFG_CLOCK_REQ_SELECT | \ + HI_CFG_HOST_INT_ENABLE) + +#endif + +#define REF_FREQ_19_2 0 +#define REF_FREQ_26_0 1 +#define REF_FREQ_38_4 2 +#define REF_FREQ_40_0 3 +#define REF_FREQ_33_6 4 +#define REF_FREQ_NUM 5 + +#define LUT_PARAM_INTEGER_DIVIDER 0 +#define LUT_PARAM_FRACTIONAL_DIVIDER 1 +#define LUT_PARAM_ATTN_BB 2 +#define LUT_PARAM_ALPHA_BB 3 +#define LUT_PARAM_STOP_TIME_BB 4 +#define LUT_PARAM_BB_PLL_LOOP_FILTER 5 +#define LUT_PARAM_NUM 6 + +#define ACX_EEPROMLESS_IND_REG (SCR_PAD4) +#define USE_EEPROM 0 +#define SOFT_RESET_MAX_TIME 1000000 +#define SOFT_RESET_STALL_TIME 1000 +#define NVS_DATA_BUNDARY_ALIGNMENT 4 + + +/* Firmware image load chunk size */ +#define CHUNK_SIZE 512 + +/* Firmware image header size */ +#define FW_HDR_SIZE 8 + +#define ECPU_CONTROL_HALT 0x00000101 + + +/****************************************************************************** + + CHANNELS, BAND & REG DOMAINS definitions + +******************************************************************************/ + + +enum { + RADIO_BAND_2_4GHZ = 0, /* 2.4 Ghz band */ + RADIO_BAND_5GHZ = 1, /* 5 Ghz band */ + RADIO_BAND_JAPAN_4_9_GHZ = 2, + DEFAULT_BAND = RADIO_BAND_2_4GHZ, + INVALID_BAND = 0xFE, + MAX_RADIO_BANDS = 0xFF +}; + +enum { + NO_RATE = 0, + RATE_1MBPS = 0x0A, + RATE_2MBPS = 0x14, + RATE_5_5MBPS = 0x37, + RATE_6MBPS = 0x0B, + RATE_9MBPS = 0x0F, + RATE_11MBPS = 0x6E, + RATE_12MBPS = 0x0A, + RATE_18MBPS = 0x0E, + RATE_22MBPS = 0xDC, + RATE_24MBPS = 0x09, + RATE_36MBPS = 0x0D, + RATE_48MBPS = 0x08, + RATE_54MBPS = 0x0C +}; + +enum { + RATE_INDEX_1MBPS = 0, + RATE_INDEX_2MBPS = 1, + RATE_INDEX_5_5MBPS = 2, + RATE_INDEX_6MBPS = 3, + RATE_INDEX_9MBPS = 4, + RATE_INDEX_11MBPS = 5, + RATE_INDEX_12MBPS = 6, + RATE_INDEX_18MBPS = 7, + RATE_INDEX_22MBPS = 8, + RATE_INDEX_24MBPS = 9, + RATE_INDEX_36MBPS = 10, + RATE_INDEX_48MBPS = 11, + RATE_INDEX_54MBPS = 12, + RATE_INDEX_MAX = RATE_INDEX_54MBPS, + MAX_RATE_INDEX, + INVALID_RATE_INDEX = MAX_RATE_INDEX, + RATE_INDEX_ENUM_MAX_SIZE = 0x7FFFFFFF +}; + +enum { + RATE_MASK_1MBPS = 0x1, + RATE_MASK_2MBPS = 0x2, + RATE_MASK_5_5MBPS = 0x4, + RATE_MASK_11MBPS = 0x20, +}; + +#define SHORT_PREAMBLE_BIT BIT(0) /* CCK or Barker depending on the rate */ +#define OFDM_RATE_BIT BIT(6) +#define PBCC_RATE_BIT BIT(7) + +enum { + CCK_LONG = 0, + CCK_SHORT = SHORT_PREAMBLE_BIT, + PBCC_LONG = PBCC_RATE_BIT, + PBCC_SHORT = PBCC_RATE_BIT | SHORT_PREAMBLE_BIT, + OFDM = OFDM_RATE_BIT +}; + +/****************************************************************************** + +Transmit-Descriptor RATE-SET field definitions... + +Define a new "Rate-Set" for TX path that incorporates the +Rate & Modulation info into a single 16-bit field. + +TxdRateSet_t: +b15 - Indicates Preamble type (1=SHORT, 0=LONG). + Notes: + Must be LONG (0) for 1Mbps rate. + Does not apply (set to 0) for RevG-OFDM rates. +b14 - Indicates PBCC encoding (1=PBCC, 0=not). + Notes: + Does not apply (set to 0) for rates 1 and 2 Mbps. + Does not apply (set to 0) for RevG-OFDM rates. +b13 - Unused (set to 0). +b12-b0 - Supported Rate indicator bits as defined below. + +******************************************************************************/ + + +/************************************************************************* + + Interrupt Trigger Register (Host -> WiLink) + +**************************************************************************/ + +/* Hardware to Embedded CPU Interrupts - first 32-bit register set */ + +/* + * Host Command Interrupt. Setting this bit masks + * the interrupt that the host issues to inform + * the FW that it has sent a command + * to the Wlan hardware Command Mailbox. + */ +#define INTR_TRIG_CMD BIT(0) + +/* + * Host Event Acknowlegde Interrupt. The host + * sets this bit to acknowledge that it received + * the unsolicited information from the event + * mailbox. + */ +#define INTR_TRIG_EVENT_ACK BIT(1) + +/* + * The host sets this bit to inform the Wlan + * FW that a TX packet is in the XFER + * Buffer #0. + */ +#define INTR_TRIG_TX_PROC0 BIT(2) + +/* + * The host sets this bit to inform the FW + * that it read a packet from RX XFER + * Buffer #0. + */ +#define INTR_TRIG_RX_PROC0 BIT(3) + +#define INTR_TRIG_DEBUG_ACK BIT(4) + +#define INTR_TRIG_STATE_CHANGED BIT(5) + + +/* Hardware to Embedded CPU Interrupts - second 32-bit register set */ + +/* + * The host sets this bit to inform the FW + * that it read a packet from RX XFER + * Buffer #1. + */ +#define INTR_TRIG_RX_PROC1 BIT(17) + +/* + * The host sets this bit to inform the Wlan + * hardware that a TX packet is in the XFER + * Buffer #1. + */ +#define INTR_TRIG_TX_PROC1 BIT(18) + +#endif diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/wl1251_rx.c index 981ea259eb89..17c54b59ef86 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/wl1251_rx.c @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation @@ -25,13 +25,15 @@ #include <linux/skbuff.h> #include <net/mac80211.h> -#include "wl12xx.h" -#include "reg.h" -#include "spi.h" -#include "rx.h" +#include "wl1251.h" +#include "wl1251_reg.h" +#include "wl1251_io.h" +#include "wl1251_rx.h" +#include "wl1251_cmd.h" +#include "wl1251_acx.h" -static void wl12xx_rx_header(struct wl12xx *wl, - struct wl12xx_rx_descriptor *desc) +static void wl1251_rx_header(struct wl1251 *wl, + struct wl1251_rx_descriptor *desc) { u32 rx_packet_ring_addr; @@ -39,15 +41,17 @@ static void wl12xx_rx_header(struct wl12xx *wl, if (wl->rx_current_buffer) rx_packet_ring_addr += wl->data_path->rx_packet_ring_chunk_size; - wl12xx_spi_mem_read(wl, rx_packet_ring_addr, desc, - sizeof(struct wl12xx_rx_descriptor)); + wl1251_mem_read(wl, rx_packet_ring_addr, desc, sizeof(*desc)); } -static void wl12xx_rx_status(struct wl12xx *wl, - struct wl12xx_rx_descriptor *desc, +static void wl1251_rx_status(struct wl1251 *wl, + struct wl1251_rx_descriptor *desc, struct ieee80211_rx_status *status, u8 beacon) { + u64 mactime; + int ret; + memset(status, 0, sizeof(struct ieee80211_rx_status)); status->band = IEEE80211_BAND_2GHZ; @@ -62,32 +66,14 @@ static void wl12xx_rx_status(struct wl12xx *wl, * this one must be atomic, while our SPI routines can sleep. */ if ((wl->bss_type == BSS_TYPE_IBSS) && beacon) { - u64 mactime; - int ret; - struct wl12xx_command cmd; - struct acx_tsf_info *tsf_info; - - memset(&cmd, 0, sizeof(cmd)); - - ret = wl12xx_cmd_interrogate(wl, ACX_TSF_INFO, - sizeof(struct acx_tsf_info), - &cmd); - if (ret < 0) { - wl12xx_warning("ACX_FW_REV interrogate failed"); - return; - } - - tsf_info = (struct acx_tsf_info *)&(cmd.parameters); - - mactime = tsf_info->current_tsf_lsb | - (tsf_info->current_tsf_msb << 31); - - status->mactime = mactime; + ret = wl1251_acx_tsf_info(wl, &mactime); + if (ret == 0) + status->mactime = mactime; } status->signal = desc->rssi; - status->qual = (desc->rssi - WL12XX_RX_MIN_RSSI) * 100 / - (WL12XX_RX_MAX_RSSI - WL12XX_RX_MIN_RSSI); + status->qual = (desc->rssi - WL1251_RX_MIN_RSSI) * 100 / + (WL1251_RX_MAX_RSSI - WL1251_RX_MIN_RSSI); status->qual = min(status->qual, 100); status->qual = max(status->qual, 0); @@ -118,8 +104,8 @@ static void wl12xx_rx_status(struct wl12xx *wl, /* FIXME: set status->rate_idx */ } -static void wl12xx_rx_body(struct wl12xx *wl, - struct wl12xx_rx_descriptor *desc) +static void wl1251_rx_body(struct wl1251 *wl, + struct wl1251_rx_descriptor *desc) { struct sk_buff *skb; struct ieee80211_rx_status status; @@ -127,12 +113,12 @@ static void wl12xx_rx_body(struct wl12xx *wl, u16 length, *fc; u32 curr_id, last_id_inc, rx_packet_ring_addr; - length = WL12XX_RX_ALIGN(desc->length - PLCP_HEADER_LENGTH); + length = WL1251_RX_ALIGN(desc->length - PLCP_HEADER_LENGTH); curr_id = (desc->flags & RX_DESC_SEQNUM_MASK) >> RX_DESC_PACKETID_SHIFT; last_id_inc = (wl->rx_last_id + 1) % (RX_MAX_PACKET_ID + 1); if (last_id_inc != curr_id) { - wl12xx_warning("curr ID:%d, last ID inc:%d", + wl1251_warning("curr ID:%d, last ID inc:%d", curr_id, last_id_inc); wl->rx_last_id = curr_id; } else { @@ -140,18 +126,18 @@ static void wl12xx_rx_body(struct wl12xx *wl, } rx_packet_ring_addr = wl->data_path->rx_packet_ring_addr + - sizeof(struct wl12xx_rx_descriptor) + 20; + sizeof(struct wl1251_rx_descriptor) + 20; if (wl->rx_current_buffer) rx_packet_ring_addr += wl->data_path->rx_packet_ring_chunk_size; skb = dev_alloc_skb(length); if (!skb) { - wl12xx_error("Couldn't allocate RX frame"); + wl1251_error("Couldn't allocate RX frame"); return; } rx_buffer = skb_put(skb, length); - wl12xx_spi_mem_read(wl, rx_packet_ring_addr, rx_buffer, length); + wl1251_mem_read(wl, rx_packet_ring_addr, rx_buffer, length); /* The actual lenght doesn't include the target's alignment */ skb->len = desc->length - PLCP_HEADER_LENGTH; @@ -161,15 +147,16 @@ static void wl12xx_rx_body(struct wl12xx *wl, if ((*fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) beacon = 1; - wl12xx_rx_status(wl, desc, &status, beacon); + wl1251_rx_status(wl, desc, &status, beacon); - wl12xx_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, skb->len, + wl1251_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, skb->len, beacon ? "beacon" : ""); - ieee80211_rx(wl->hw, skb, &status); + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + ieee80211_rx(wl->hw, skb); } -static void wl12xx_rx_ack(struct wl12xx *wl) +static void wl1251_rx_ack(struct wl1251 *wl) { u32 data, addr; @@ -181,28 +168,30 @@ static void wl12xx_rx_ack(struct wl12xx *wl) data = INTR_TRIG_RX_PROC0; } - wl12xx_reg_write32(wl, addr, data); + wl1251_reg_write32(wl, addr, data); /* Toggle buffer ring */ wl->rx_current_buffer = !wl->rx_current_buffer; } -void wl12xx_rx(struct wl12xx *wl) +void wl1251_rx(struct wl1251 *wl) { - struct wl12xx_rx_descriptor rx_desc; + struct wl1251_rx_descriptor *rx_desc; - if (wl->state != WL12XX_STATE_ON) + if (wl->state != WL1251_STATE_ON) return; + rx_desc = wl->rx_descriptor; + /* We first read the frame's header */ - wl12xx_rx_header(wl, &rx_desc); + wl1251_rx_header(wl, rx_desc); /* Now we can read the body */ - wl12xx_rx_body(wl, &rx_desc); + wl1251_rx_body(wl, rx_desc); /* Finally, we need to ACK the RX */ - wl12xx_rx_ack(wl); + wl1251_rx_ack(wl); return; } diff --git a/drivers/net/wireless/wl12xx/rx.h b/drivers/net/wireless/wl12xx/wl1251_rx.h index 8a23fdea5016..563a3fde40fb 100644 --- a/drivers/net/wireless/wl12xx/rx.h +++ b/drivers/net/wireless/wl12xx/wl1251_rx.h @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation @@ -22,11 +22,13 @@ * */ -#ifndef __WL12XX_RX_H__ -#define __WL12XX_RX_H__ +#ifndef __WL1251_RX_H__ +#define __WL1251_RX_H__ #include <linux/bitops.h> +#include "wl1251.h" + /* * RX PATH * @@ -43,12 +45,12 @@ * 4) The target prepares the next RX packet. */ -#define WL12XX_RX_MAX_RSSI -30 -#define WL12XX_RX_MIN_RSSI -95 +#define WL1251_RX_MAX_RSSI -30 +#define WL1251_RX_MIN_RSSI -95 -#define WL12XX_RX_ALIGN_TO 4 -#define WL12XX_RX_ALIGN(len) (((len) + WL12XX_RX_ALIGN_TO - 1) & \ - ~(WL12XX_RX_ALIGN_TO - 1)) +#define WL1251_RX_ALIGN_TO 4 +#define WL1251_RX_ALIGN(len) (((len) + WL1251_RX_ALIGN_TO - 1) & \ + ~(WL1251_RX_ALIGN_TO - 1)) #define SHORT_PREAMBLE_BIT BIT(0) #define OFDM_RATE_BIT BIT(6) @@ -72,7 +74,7 @@ #define RX_DESC_MIC_FAIL 0x2000 #define RX_DESC_DECRYPT_FAIL 0x4000 -struct wl12xx_rx_descriptor { +struct wl1251_rx_descriptor { u32 timestamp; /* In microseconds */ u16 length; /* Paylod length, including headers */ u16 flags; @@ -86,7 +88,7 @@ struct wl12xx_rx_descriptor { u8 type; /* - * Recevied Rate: + * Received Rate: * 0x0A - 1MBPS * 0x14 - 2MBPS * 0x37 - 5_5MBPS @@ -117,6 +119,6 @@ struct wl12xx_rx_descriptor { u8 snr; /* in dB */ } __attribute__ ((packed)); -void wl12xx_rx(struct wl12xx *wl); +void wl1251_rx(struct wl1251 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/wl1251_sdio.c b/drivers/net/wireless/wl12xx/wl1251_sdio.c new file mode 100644 index 000000000000..9423f22bdced --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1251_sdio.c @@ -0,0 +1,205 @@ +/* + * wl12xx SDIO routines + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Copyright (C) 2005 Texas Instruments Incorporated + * Copyright (C) 2008 Google Inc + * Copyright (C) 2009 Bob Copeland (me@bobcopeland.com) + */ +#include <linux/module.h> +#include <linux/crc7.h> +#include <linux/mod_devicetable.h> +#include <linux/irq.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio_ids.h> +#include <linux/platform_device.h> + +#include "wl1251.h" +#include "wl12xx_80211.h" +#include "wl1251_reg.h" +#include "wl1251_ps.h" +#include "wl1251_io.h" +#include "wl1251_tx.h" +#include "wl1251_debugfs.h" + +#ifndef SDIO_VENDOR_ID_TI +#define SDIO_VENDOR_ID_TI 0x104c +#endif + +#ifndef SDIO_DEVICE_ID_TI_WL1251 +#define SDIO_DEVICE_ID_TI_WL1251 0x9066 +#endif + +static struct sdio_func *wl_to_func(struct wl1251 *wl) +{ + return wl->if_priv; +} + +static void wl1251_sdio_interrupt(struct sdio_func *func) +{ + struct wl1251 *wl = sdio_get_drvdata(func); + + wl1251_debug(DEBUG_IRQ, "IRQ"); + + /* FIXME should be synchronous for sdio */ + ieee80211_queue_work(wl->hw, &wl->irq_work); +} + +static const struct sdio_device_id wl1251_devices[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1251) }, + {} +}; +MODULE_DEVICE_TABLE(sdio, wl1251_devices); + + +void wl1251_sdio_read(struct wl1251 *wl, int addr, void *buf, size_t len) +{ + int ret; + struct sdio_func *func = wl_to_func(wl); + + sdio_claim_host(func); + ret = sdio_memcpy_fromio(func, buf, addr, len); + if (ret) + wl1251_error("sdio read failed (%d)", ret); + sdio_release_host(func); +} + +void wl1251_sdio_write(struct wl1251 *wl, int addr, void *buf, size_t len) +{ + int ret; + struct sdio_func *func = wl_to_func(wl); + + sdio_claim_host(func); + ret = sdio_memcpy_toio(func, addr, buf, len); + if (ret) + wl1251_error("sdio write failed (%d)", ret); + sdio_release_host(func); +} + +void wl1251_sdio_reset(struct wl1251 *wl) +{ +} + +static void wl1251_sdio_enable_irq(struct wl1251 *wl) +{ + struct sdio_func *func = wl_to_func(wl); + + sdio_claim_host(func); + sdio_claim_irq(func, wl1251_sdio_interrupt); + sdio_release_host(func); +} + +static void wl1251_sdio_disable_irq(struct wl1251 *wl) +{ + struct sdio_func *func = wl_to_func(wl); + + sdio_claim_host(func); + sdio_release_irq(func); + sdio_release_host(func); +} + +void wl1251_sdio_set_power(bool enable) +{ +} + +struct wl1251_if_operations wl1251_sdio_ops = { + .read = wl1251_sdio_read, + .write = wl1251_sdio_write, + .reset = wl1251_sdio_reset, + .enable_irq = wl1251_sdio_enable_irq, + .disable_irq = wl1251_sdio_disable_irq, +}; + +int wl1251_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) +{ + int ret; + struct wl1251 *wl; + struct ieee80211_hw *hw; + + hw = wl1251_alloc_hw(); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + wl = hw->priv; + + sdio_claim_host(func); + ret = sdio_enable_func(func); + if (ret) + goto release; + + sdio_set_block_size(func, 512); + + SET_IEEE80211_DEV(hw, &func->dev); + wl->if_priv = func; + wl->if_ops = &wl1251_sdio_ops; + wl->set_power = wl1251_sdio_set_power; + + sdio_release_host(func); + ret = wl1251_init_ieee80211(wl); + if (ret) + goto disable; + + sdio_set_drvdata(func, wl); + return ret; + +disable: + sdio_claim_host(func); + sdio_disable_func(func); +release: + sdio_release_host(func); + return ret; +} + +static void __devexit wl1251_sdio_remove(struct sdio_func *func) +{ + struct wl1251 *wl = sdio_get_drvdata(func); + + wl1251_free_hw(wl); + + sdio_claim_host(func); + sdio_release_irq(func); + sdio_disable_func(func); + sdio_release_host(func); +} + +static struct sdio_driver wl1251_sdio_driver = { + .name = "wl1251_sdio", + .id_table = wl1251_devices, + .probe = wl1251_sdio_probe, + .remove = __devexit_p(wl1251_sdio_remove), +}; + +static int __init wl1251_sdio_init(void) +{ + int err; + + err = sdio_register_driver(&wl1251_sdio_driver); + if (err) + wl1251_error("failed to register sdio driver: %d", err); + return err; +} + +static void __exit wl1251_sdio_exit(void) +{ + sdio_unregister_driver(&wl1251_sdio_driver); + wl1251_notice("unloaded"); +} + +module_init(wl1251_sdio_init); +module_exit(wl1251_sdio_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>"); diff --git a/drivers/net/wireless/wl12xx/wl1251_spi.c b/drivers/net/wireless/wl12xx/wl1251_spi.c new file mode 100644 index 000000000000..14eff2b3d4c6 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1251_spi.c @@ -0,0 +1,344 @@ +/* + * This file is part of wl1251 + * + * Copyright (C) 2008 Nokia Corporation + * + * Contact: Kalle Valo <kalle.valo@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/crc7.h> +#include <linux/spi/spi.h> +#include <linux/spi/wl12xx.h> + +#include "wl1251.h" +#include "wl1251_reg.h" +#include "wl1251_spi.h" + +static irqreturn_t wl1251_irq(int irq, void *cookie) +{ + struct wl1251 *wl; + + wl1251_debug(DEBUG_IRQ, "IRQ"); + + wl = cookie; + + ieee80211_queue_work(wl->hw, &wl->irq_work); + + return IRQ_HANDLED; +} + +static struct spi_device *wl_to_spi(struct wl1251 *wl) +{ + return wl->if_priv; +} + +static void wl1251_spi_reset(struct wl1251 *wl) +{ + u8 *cmd; + struct spi_transfer t; + struct spi_message m; + + cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); + if (!cmd) { + wl1251_error("could not allocate cmd for spi reset"); + return; + } + + memset(&t, 0, sizeof(t)); + spi_message_init(&m); + + memset(cmd, 0xff, WSPI_INIT_CMD_LEN); + + t.tx_buf = cmd; + t.len = WSPI_INIT_CMD_LEN; + spi_message_add_tail(&t, &m); + + spi_sync(wl_to_spi(wl), &m); + + wl1251_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN); +} + +static void wl1251_spi_wake(struct wl1251 *wl) +{ + u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd; + struct spi_transfer t; + struct spi_message m; + + cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); + if (!cmd) { + wl1251_error("could not allocate cmd for spi init"); + return; + } + + memset(crc, 0, sizeof(crc)); + memset(&t, 0, sizeof(t)); + spi_message_init(&m); + + /* + * Set WSPI_INIT_COMMAND + * the data is being send from the MSB to LSB + */ + cmd[2] = 0xff; + cmd[3] = 0xff; + cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; + cmd[0] = 0; + cmd[7] = 0; + cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3; + cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; + + if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0) + cmd[5] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; + else + cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY; + + cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS + | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; + + crc[0] = cmd[1]; + crc[1] = cmd[0]; + crc[2] = cmd[7]; + crc[3] = cmd[6]; + crc[4] = cmd[5]; + + cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1; + cmd[4] |= WSPI_INIT_CMD_END; + + t.tx_buf = cmd; + t.len = WSPI_INIT_CMD_LEN; + spi_message_add_tail(&t, &m); + + spi_sync(wl_to_spi(wl), &m); + + wl1251_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN); +} + +static void wl1251_spi_reset_wake(struct wl1251 *wl) +{ + wl1251_spi_reset(wl); + wl1251_spi_wake(wl); +} + +static void wl1251_spi_read(struct wl1251 *wl, int addr, void *buf, + size_t len) +{ + struct spi_transfer t[3]; + struct spi_message m; + u8 *busy_buf; + u32 *cmd; + + cmd = &wl->buffer_cmd; + busy_buf = wl->buffer_busyword; + + *cmd = 0; + *cmd |= WSPI_CMD_READ; + *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; + *cmd |= addr & WSPI_CMD_BYTE_ADDR; + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = cmd; + t[0].len = 4; + spi_message_add_tail(&t[0], &m); + + /* Busy and non busy words read */ + t[1].rx_buf = busy_buf; + t[1].len = WL1251_BUSY_WORD_LEN; + spi_message_add_tail(&t[1], &m); + + t[2].rx_buf = buf; + t[2].len = len; + spi_message_add_tail(&t[2], &m); + + spi_sync(wl_to_spi(wl), &m); + + /* FIXME: check busy words */ + + wl1251_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd)); + wl1251_dump(DEBUG_SPI, "spi_read buf <- ", buf, len); +} + +static void wl1251_spi_write(struct wl1251 *wl, int addr, void *buf, + size_t len) +{ + struct spi_transfer t[2]; + struct spi_message m; + u32 *cmd; + + cmd = &wl->buffer_cmd; + + *cmd = 0; + *cmd |= WSPI_CMD_WRITE; + *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; + *cmd |= addr & WSPI_CMD_BYTE_ADDR; + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = cmd; + t[0].len = sizeof(*cmd); + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = buf; + t[1].len = len; + spi_message_add_tail(&t[1], &m); + + spi_sync(wl_to_spi(wl), &m); + + wl1251_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd)); + wl1251_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); +} + +static void wl1251_spi_enable_irq(struct wl1251 *wl) +{ + return enable_irq(wl->irq); +} + +static void wl1251_spi_disable_irq(struct wl1251 *wl) +{ + return disable_irq(wl->irq); +} + +static const struct wl1251_if_operations wl1251_spi_ops = { + .read = wl1251_spi_read, + .write = wl1251_spi_write, + .reset = wl1251_spi_reset_wake, + .enable_irq = wl1251_spi_enable_irq, + .disable_irq = wl1251_spi_disable_irq, +}; + +static int __devinit wl1251_spi_probe(struct spi_device *spi) +{ + struct wl12xx_platform_data *pdata; + struct ieee80211_hw *hw; + struct wl1251 *wl; + int ret; + + pdata = spi->dev.platform_data; + if (!pdata) { + wl1251_error("no platform data"); + return -ENODEV; + } + + hw = wl1251_alloc_hw(); + if (IS_ERR(hw)) + return PTR_ERR(hw); + + wl = hw->priv; + + SET_IEEE80211_DEV(hw, &spi->dev); + dev_set_drvdata(&spi->dev, wl); + wl->if_priv = spi; + wl->if_ops = &wl1251_spi_ops; + + /* This is the only SPI value that we need to set here, the rest + * comes from the board-peripherals file */ + spi->bits_per_word = 32; + + ret = spi_setup(spi); + if (ret < 0) { + wl1251_error("spi_setup failed"); + goto out_free; + } + + wl->set_power = pdata->set_power; + if (!wl->set_power) { + wl1251_error("set power function missing in platform data"); + return -ENODEV; + } + + wl->irq = spi->irq; + if (wl->irq < 0) { + wl1251_error("irq missing in platform data"); + return -ENODEV; + } + + ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl); + if (ret < 0) { + wl1251_error("request_irq() failed: %d", ret); + goto out_free; + } + + set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); + + disable_irq(wl->irq); + + ret = wl1251_init_ieee80211(wl); + if (ret) + goto out_irq; + + return 0; + + out_irq: + free_irq(wl->irq, wl); + + out_free: + ieee80211_free_hw(hw); + + return ret; +} + +static int __devexit wl1251_spi_remove(struct spi_device *spi) +{ + struct wl1251 *wl = dev_get_drvdata(&spi->dev); + + free_irq(wl->irq, wl); + wl1251_free_hw(wl); + + return 0; +} + +static struct spi_driver wl1251_spi_driver = { + .driver = { + .name = "wl12xx", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + + .probe = wl1251_spi_probe, + .remove = __devexit_p(wl1251_spi_remove), +}; + +static int __init wl1251_spi_init(void) +{ + int ret; + + ret = spi_register_driver(&wl1251_spi_driver); + if (ret < 0) { + wl1251_error("failed to register spi driver: %d", ret); + goto out; + } + +out: + return ret; +} + +static void __exit wl1251_spi_exit(void) +{ + spi_unregister_driver(&wl1251_spi_driver); + + wl1251_notice("unloaded"); +} + +module_init(wl1251_spi_init); +module_exit(wl1251_spi_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>"); diff --git a/drivers/net/wireless/wl12xx/wl1251_spi.h b/drivers/net/wireless/wl12xx/wl1251_spi.h new file mode 100644 index 000000000000..2e273a97e7f3 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1251_spi.h @@ -0,0 +1,61 @@ +/* + * This file is part of wl1251 + * + * Copyright (c) 1998-2007 Texas Instruments Incorporated + * Copyright (C) 2008 Nokia Corporation + * + * Contact: Kalle Valo <kalle.valo@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1251_SPI_H__ +#define __WL1251_SPI_H__ + +#include "wl1251_cmd.h" +#include "wl1251_acx.h" +#include "wl1251_reg.h" + +#define WSPI_CMD_READ 0x40000000 +#define WSPI_CMD_WRITE 0x00000000 +#define WSPI_CMD_FIXED 0x20000000 +#define WSPI_CMD_BYTE_LENGTH 0x1FFE0000 +#define WSPI_CMD_BYTE_LENGTH_OFFSET 17 +#define WSPI_CMD_BYTE_ADDR 0x0001FFFF + +#define WSPI_INIT_CMD_CRC_LEN 5 + +#define WSPI_INIT_CMD_START 0x00 +#define WSPI_INIT_CMD_TX 0x40 +/* the extra bypass bit is sampled by the TNET as '1' */ +#define WSPI_INIT_CMD_BYPASS_BIT 0x80 +#define WSPI_INIT_CMD_FIXEDBUSY_LEN 0x07 +#define WSPI_INIT_CMD_EN_FIXEDBUSY 0x80 +#define WSPI_INIT_CMD_DIS_FIXEDBUSY 0x00 +#define WSPI_INIT_CMD_IOD 0x40 +#define WSPI_INIT_CMD_IP 0x20 +#define WSPI_INIT_CMD_CS 0x10 +#define WSPI_INIT_CMD_WS 0x08 +#define WSPI_INIT_CMD_WSPI 0x01 +#define WSPI_INIT_CMD_END 0x01 + +#define WSPI_INIT_CMD_LEN 8 + +#define HW_ACCESS_WSPI_FIXED_BUSY_LEN \ + ((WL1251_BUSY_WORD_LEN - 4) / sizeof(u32)) +#define HW_ACCESS_WSPI_INIT_CMD_MASK 0 + +#endif /* __WL1251_SPI_H__ */ diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/wl1251_tx.c index 62145e205a8c..f85970615849 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/wl1251_tx.c @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation @@ -25,13 +25,13 @@ #include <linux/kernel.h> #include <linux/module.h> -#include "wl12xx.h" -#include "reg.h" -#include "spi.h" -#include "tx.h" -#include "ps.h" +#include "wl1251.h" +#include "wl1251_reg.h" +#include "wl1251_tx.h" +#include "wl1251_ps.h" +#include "wl1251_io.h" -static bool wl12xx_tx_double_buffer_busy(struct wl12xx *wl, u32 data_out_count) +static bool wl1251_tx_double_buffer_busy(struct wl1251 *wl, u32 data_out_count) { int used, data_in_count; @@ -52,15 +52,15 @@ static bool wl12xx_tx_double_buffer_busy(struct wl12xx *wl, u32 data_out_count) return false; } -static int wl12xx_tx_path_status(struct wl12xx *wl) +static int wl1251_tx_path_status(struct wl1251 *wl) { u32 status, addr, data_out_count; bool busy; addr = wl->data_path->tx_control_addr; - status = wl12xx_mem_read32(wl, addr); + status = wl1251_mem_read32(wl, addr); data_out_count = status & TX_STATUS_DATA_OUT_COUNT_MASK; - busy = wl12xx_tx_double_buffer_busy(wl, data_out_count); + busy = wl1251_tx_double_buffer_busy(wl, data_out_count); if (busy) return -EBUSY; @@ -68,7 +68,7 @@ static int wl12xx_tx_path_status(struct wl12xx *wl) return 0; } -static int wl12xx_tx_id(struct wl12xx *wl, struct sk_buff *skb) +static int wl1251_tx_id(struct wl1251 *wl, struct sk_buff *skb) { int i; @@ -81,7 +81,7 @@ static int wl12xx_tx_id(struct wl12xx *wl, struct sk_buff *skb) return -EBUSY; } -static void wl12xx_tx_control(struct tx_double_buffer_desc *tx_hdr, +static void wl1251_tx_control(struct tx_double_buffer_desc *tx_hdr, struct ieee80211_tx_info *control, u16 fc) { *(u16 *)&tx_hdr->control = 0; @@ -109,7 +109,7 @@ static void wl12xx_tx_control(struct tx_double_buffer_desc *tx_hdr, #define MAX_MPDU_HEADER_AND_SECURITY (MAX_MPDU_SECURITY_LENGTH + \ WLAN_QOS_HDR_LEN) #define HW_BLOCK_SIZE 252 -static void wl12xx_tx_frag_block_num(struct tx_double_buffer_desc *tx_hdr) +static void wl1251_tx_frag_block_num(struct tx_double_buffer_desc *tx_hdr) { u16 payload_len, frag_threshold, mem_blocks; u16 num_mpdus, mem_blocks_per_frag; @@ -142,7 +142,7 @@ static void wl12xx_tx_frag_block_num(struct tx_double_buffer_desc *tx_hdr) tx_hdr->num_mem_blocks = mem_blocks; } -static int wl12xx_tx_fill_hdr(struct wl12xx *wl, struct sk_buff *skb, +static int wl1251_tx_fill_hdr(struct wl1251 *wl, struct sk_buff *skb, struct ieee80211_tx_info *control) { struct tx_double_buffer_desc *tx_hdr; @@ -153,7 +153,7 @@ static int wl12xx_tx_fill_hdr(struct wl12xx *wl, struct sk_buff *skb, if (!skb) return -EINVAL; - id = wl12xx_tx_id(wl, skb); + id = wl1251_tx_id(wl, skb); if (id < 0) return id; @@ -170,14 +170,14 @@ static int wl12xx_tx_fill_hdr(struct wl12xx *wl, struct sk_buff *skb, /* FIXME: how to get the correct queue id? */ tx_hdr->xmit_queue = 0; - wl12xx_tx_control(tx_hdr, control, fc); - wl12xx_tx_frag_block_num(tx_hdr); + wl1251_tx_control(tx_hdr, control, fc); + wl1251_tx_frag_block_num(tx_hdr); return 0; } /* We copy the packet to the target */ -static int wl12xx_tx_send_packet(struct wl12xx *wl, struct sk_buff *skb, +static int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb, struct ieee80211_tx_info *control) { struct tx_double_buffer_desc *tx_hdr; @@ -196,12 +196,12 @@ static int wl12xx_tx_send_packet(struct wl12xx *wl, struct sk_buff *skb, u8 *pos; fc = *(u16 *)(skb->data + sizeof(*tx_hdr)); - tx_hdr->length += WL12XX_TKIP_IV_SPACE; + tx_hdr->length += WL1251_TKIP_IV_SPACE; hdrlen = ieee80211_hdrlen(fc); - pos = skb_push(skb, WL12XX_TKIP_IV_SPACE); - memmove(pos, pos + WL12XX_TKIP_IV_SPACE, + pos = skb_push(skb, WL1251_TKIP_IV_SPACE); + memmove(pos, pos + WL1251_TKIP_IV_SPACE, sizeof(*tx_hdr) + hdrlen); } @@ -211,7 +211,7 @@ static int wl12xx_tx_send_packet(struct wl12xx *wl, struct sk_buff *skb, */ if (unlikely((long)skb->data & 0x03)) { int offset = (4 - (long)skb->data) & 0x03; - wl12xx_debug(DEBUG_TX, "skb offset %d", offset); + wl1251_debug(DEBUG_TX, "skb offset %d", offset); /* check whether the current skb can be used */ if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) { @@ -221,13 +221,13 @@ static int wl12xx_tx_send_packet(struct wl12xx *wl, struct sk_buff *skb, skb_reserve(skb, offset); memmove(skb->data, src, skb->len); } else { - wl12xx_info("No handler, fixme!"); + wl1251_info("No handler, fixme!"); return -EINVAL; } } /* Our skb->data at this point includes the HW header */ - len = WL12XX_TX_ALIGN(skb->len); + len = WL1251_TX_ALIGN(skb->len); if (wl->data_in_count & 0x1) addr = wl->data_path->tx_packet_ring_addr + @@ -235,15 +235,15 @@ static int wl12xx_tx_send_packet(struct wl12xx *wl, struct sk_buff *skb, else addr = wl->data_path->tx_packet_ring_addr; - wl12xx_spi_mem_write(wl, addr, skb->data, len); + wl1251_mem_write(wl, addr, skb->data, len); - wl12xx_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x", + wl1251_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x", tx_hdr->id, skb, tx_hdr->length, tx_hdr->rate); return 0; } -static void wl12xx_tx_trigger(struct wl12xx *wl) +static void wl1251_tx_trigger(struct wl1251 *wl) { u32 data, addr; @@ -255,7 +255,7 @@ static void wl12xx_tx_trigger(struct wl12xx *wl) data = INTR_TRIG_TX_PROC0; } - wl12xx_reg_write32(wl, addr, data); + wl1251_reg_write32(wl, addr, data); /* Bumping data in */ wl->data_in_count = (wl->data_in_count + 1) & @@ -263,7 +263,7 @@ static void wl12xx_tx_trigger(struct wl12xx *wl) } /* caller must hold wl->mutex */ -static int wl12xx_tx_frame(struct wl12xx *wl, struct sk_buff *skb) +static int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb) { struct ieee80211_tx_info *info; int ret = 0; @@ -274,51 +274,53 @@ static int wl12xx_tx_frame(struct wl12xx *wl, struct sk_buff *skb) if (info->control.hw_key) { idx = info->control.hw_key->hw_key_idx; if (unlikely(wl->default_key != idx)) { - ret = wl12xx_acx_default_key(wl, idx); + ret = wl1251_acx_default_key(wl, idx); if (ret < 0) return ret; } } - ret = wl12xx_tx_path_status(wl); + ret = wl1251_tx_path_status(wl); if (ret < 0) return ret; - ret = wl12xx_tx_fill_hdr(wl, skb, info); + ret = wl1251_tx_fill_hdr(wl, skb, info); if (ret < 0) return ret; - ret = wl12xx_tx_send_packet(wl, skb, info); + ret = wl1251_tx_send_packet(wl, skb, info); if (ret < 0) return ret; - wl12xx_tx_trigger(wl); + wl1251_tx_trigger(wl); return ret; } -void wl12xx_tx_work(struct work_struct *work) +void wl1251_tx_work(struct work_struct *work) { - struct wl12xx *wl = container_of(work, struct wl12xx, tx_work); + struct wl1251 *wl = container_of(work, struct wl1251, tx_work); struct sk_buff *skb; bool woken_up = false; int ret; mutex_lock(&wl->mutex); - if (unlikely(wl->state == WL12XX_STATE_OFF)) + if (unlikely(wl->state == WL1251_STATE_OFF)) goto out; while ((skb = skb_dequeue(&wl->tx_queue))) { if (!woken_up) { - wl12xx_ps_elp_wakeup(wl); + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out; woken_up = true; } - ret = wl12xx_tx_frame(wl, skb); + ret = wl1251_tx_frame(wl, skb); if (ret == -EBUSY) { /* firmware buffer is full, stop queues */ - wl12xx_debug(DEBUG_TX, "tx_work: fw buffer full, " + wl1251_debug(DEBUG_TX, "tx_work: fw buffer full, " "stop queues"); ieee80211_stop_queues(wl->hw); wl->tx_queue_stopped = true; @@ -332,12 +334,12 @@ void wl12xx_tx_work(struct work_struct *work) out: if (woken_up) - wl12xx_ps_elp_sleep(wl); + wl1251_ps_elp_sleep(wl); mutex_unlock(&wl->mutex); } -static const char *wl12xx_tx_parse_status(u8 status) +static const char *wl1251_tx_parse_status(u8 status) { /* 8 bit status field, one character per bit plus null */ static char buf[9]; @@ -365,7 +367,7 @@ static const char *wl12xx_tx_parse_status(u8 status) return buf; } -static void wl12xx_tx_packet_cb(struct wl12xx *wl, +static void wl1251_tx_packet_cb(struct wl1251 *wl, struct tx_result *result) { struct ieee80211_tx_info *info; @@ -375,7 +377,7 @@ static void wl12xx_tx_packet_cb(struct wl12xx *wl, skb = wl->tx_frames[result->id]; if (skb == NULL) { - wl12xx_error("SKB for packet %d is NULL", result->id); + wl1251_error("SKB for packet %d is NULL", result->id); return; } @@ -396,14 +398,14 @@ static void wl12xx_tx_packet_cb(struct wl12xx *wl, if (info->control.hw_key && info->control.hw_key->alg == ALG_TKIP) { hdrlen = ieee80211_get_hdrlen_from_skb(skb); - memmove(frame + WL12XX_TKIP_IV_SPACE, frame, hdrlen); - skb_pull(skb, WL12XX_TKIP_IV_SPACE); + memmove(frame + WL1251_TKIP_IV_SPACE, frame, hdrlen); + skb_pull(skb, WL1251_TKIP_IV_SPACE); } - wl12xx_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" + wl1251_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" " status 0x%x (%s)", result->id, skb, result->ack_failures, result->rate, - result->status, wl12xx_tx_parse_status(result->status)); + result->status, wl1251_tx_parse_status(result->status)); ieee80211_tx_status(wl->hw, skb); @@ -411,7 +413,7 @@ static void wl12xx_tx_packet_cb(struct wl12xx *wl, wl->tx_frames[result->id] = NULL; if (wl->tx_queue_stopped) { - wl12xx_debug(DEBUG_TX, "cb: queue was stopped"); + wl1251_debug(DEBUG_TX, "cb: queue was stopped"); skb = skb_dequeue(&wl->tx_queue); @@ -420,10 +422,10 @@ static void wl12xx_tx_packet_cb(struct wl12xx *wl, queue empty */ if (skb) { - ret = wl12xx_tx_frame(wl, skb); + ret = wl1251_tx_frame(wl, skb); if (ret == -EBUSY) { /* firmware buffer is still full */ - wl12xx_debug(DEBUG_TX, "cb: fw buffer " + wl1251_debug(DEBUG_TX, "cb: fw buffer " "still full"); skb_queue_head(&wl->tx_queue, skb); return; @@ -433,23 +435,23 @@ static void wl12xx_tx_packet_cb(struct wl12xx *wl, } } - wl12xx_debug(DEBUG_TX, "cb: waking queues"); + wl1251_debug(DEBUG_TX, "cb: waking queues"); ieee80211_wake_queues(wl->hw); wl->tx_queue_stopped = false; } } /* Called upon reception of a TX complete interrupt */ -void wl12xx_tx_complete(struct wl12xx *wl) +void wl1251_tx_complete(struct wl1251 *wl) { int i, result_index, num_complete = 0; struct tx_result result[FW_TX_CMPLT_BLOCK_SIZE], *result_ptr; - if (unlikely(wl->state != WL12XX_STATE_ON)) + if (unlikely(wl->state != WL1251_STATE_ON)) return; /* First we read the result */ - wl12xx_spi_mem_read(wl, wl->data_path->tx_complete_addr, + wl1251_mem_read(wl, wl->data_path->tx_complete_addr, result, sizeof(result)); result_index = wl->next_tx_complete; @@ -459,7 +461,7 @@ void wl12xx_tx_complete(struct wl12xx *wl) if (result_ptr->done_1 == 1 && result_ptr->done_2 == 1) { - wl12xx_tx_packet_cb(wl, result_ptr); + wl1251_tx_packet_cb(wl, result_ptr); result_ptr->done_1 = 0; result_ptr->done_2 = 0; @@ -480,41 +482,41 @@ void wl12xx_tx_complete(struct wl12xx *wl) */ if (result_index > wl->next_tx_complete) { /* Only 1 write is needed */ - wl12xx_spi_mem_write(wl, - wl->data_path->tx_complete_addr + - (wl->next_tx_complete * - sizeof(struct tx_result)), - &result[wl->next_tx_complete], - num_complete * - sizeof(struct tx_result)); + wl1251_mem_write(wl, + wl->data_path->tx_complete_addr + + (wl->next_tx_complete * + sizeof(struct tx_result)), + &result[wl->next_tx_complete], + num_complete * + sizeof(struct tx_result)); } else if (result_index < wl->next_tx_complete) { /* 2 writes are needed */ - wl12xx_spi_mem_write(wl, - wl->data_path->tx_complete_addr + - (wl->next_tx_complete * - sizeof(struct tx_result)), - &result[wl->next_tx_complete], - (FW_TX_CMPLT_BLOCK_SIZE - - wl->next_tx_complete) * - sizeof(struct tx_result)); - - wl12xx_spi_mem_write(wl, - wl->data_path->tx_complete_addr, - result, - (num_complete - - FW_TX_CMPLT_BLOCK_SIZE + - wl->next_tx_complete) * - sizeof(struct tx_result)); + wl1251_mem_write(wl, + wl->data_path->tx_complete_addr + + (wl->next_tx_complete * + sizeof(struct tx_result)), + &result[wl->next_tx_complete], + (FW_TX_CMPLT_BLOCK_SIZE - + wl->next_tx_complete) * + sizeof(struct tx_result)); + + wl1251_mem_write(wl, + wl->data_path->tx_complete_addr, + result, + (num_complete - + FW_TX_CMPLT_BLOCK_SIZE + + wl->next_tx_complete) * + sizeof(struct tx_result)); } else { /* We have to write the whole array */ - wl12xx_spi_mem_write(wl, - wl->data_path->tx_complete_addr, - result, - FW_TX_CMPLT_BLOCK_SIZE * - sizeof(struct tx_result)); + wl1251_mem_write(wl, + wl->data_path->tx_complete_addr, + result, + FW_TX_CMPLT_BLOCK_SIZE * + sizeof(struct tx_result)); } } @@ -523,7 +525,7 @@ void wl12xx_tx_complete(struct wl12xx *wl) } /* caller must hold wl->mutex */ -void wl12xx_tx_flush(struct wl12xx *wl) +void wl1251_tx_flush(struct wl1251 *wl) { int i; struct sk_buff *skb; @@ -535,7 +537,7 @@ void wl12xx_tx_flush(struct wl12xx *wl) while ((skb = skb_dequeue(&wl->tx_queue))) { info = IEEE80211_SKB_CB(skb); - wl12xx_debug(DEBUG_TX, "flushing skb 0x%p", skb); + wl1251_debug(DEBUG_TX, "flushing skb 0x%p", skb); if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) continue; diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/wl1251_tx.h index dc82691f4c14..7c1c1665c810 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/wl1251_tx.h @@ -1,5 +1,5 @@ /* - * This file is part of wl12xx + * This file is part of wl1251 * * Copyright (c) 1998-2007 Texas Instruments Incorporated * Copyright (C) 2008 Nokia Corporation @@ -22,8 +22,8 @@ * */ -#ifndef __WL12XX_TX_H__ -#define __WL12XX_TX_H__ +#ifndef __WL1251_TX_H__ +#define __WL1251_TX_H__ #include <linux/bitops.h> @@ -73,10 +73,11 @@ #define TX_COMPLETE_REQUIRED_BIT 0x80 #define TX_STATUS_DATA_OUT_COUNT_MASK 0xf -#define WL12XX_TX_ALIGN_TO 4 -#define WL12XX_TX_ALIGN(len) (((len) + WL12XX_TX_ALIGN_TO - 1) & \ - ~(WL12XX_TX_ALIGN_TO - 1)) -#define WL12XX_TKIP_IV_SPACE 4 + +#define WL1251_TX_ALIGN_TO 4 +#define WL1251_TX_ALIGN(len) (((len) + WL1251_TX_ALIGN_TO - 1) & \ + ~(WL1251_TX_ALIGN_TO - 1)) +#define WL1251_TKIP_IV_SPACE 4 struct tx_control { /* Rate Policy (class) index */ @@ -208,8 +209,8 @@ struct tx_result { u8 done_2; } __attribute__ ((packed)); -void wl12xx_tx_work(struct work_struct *work); -void wl12xx_tx_complete(struct wl12xx *wl); -void wl12xx_tx_flush(struct wl12xx *wl); +void wl1251_tx_work(struct work_struct *work); +void wl1251_tx_complete(struct wl1251 *wl); +void wl1251_tx_flush(struct wl1251 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl1271.h index 48641437414b..55818f94017b 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -1,10 +1,10 @@ /* - * This file is part of wl12xx + * This file is part of wl1271 * - * Copyright (c) 1998-2007 Texas Instruments Incorporated + * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. * Copyright (C) 2008-2009 Nokia Corporation * - * Contact: Kalle Valo <kalle.valo@nokia.com> + * Contact: Luciano Coelho <luciano.coelho@nokia.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -22,15 +22,17 @@ * */ -#ifndef __WL12XX_H__ -#define __WL12XX_H__ +#ifndef __WL1271_H__ +#define __WL1271_H__ #include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/spinlock.h> #include <linux/list.h> #include <linux/bitops.h> #include <net/mac80211.h> -#define DRIVER_NAME "wl12xx" +#define DRIVER_NAME "wl1271" #define DRIVER_PREFIX DRIVER_NAME ": " enum { @@ -56,25 +58,25 @@ enum { #define DEBUG_DUMP_LIMIT 1024 -#define wl12xx_error(fmt, arg...) \ +#define wl1271_error(fmt, arg...) \ printk(KERN_ERR DRIVER_PREFIX "ERROR " fmt "\n", ##arg) -#define wl12xx_warning(fmt, arg...) \ +#define wl1271_warning(fmt, arg...) \ printk(KERN_WARNING DRIVER_PREFIX "WARNING " fmt "\n", ##arg) -#define wl12xx_notice(fmt, arg...) \ +#define wl1271_notice(fmt, arg...) \ printk(KERN_INFO DRIVER_PREFIX fmt "\n", ##arg) -#define wl12xx_info(fmt, arg...) \ +#define wl1271_info(fmt, arg...) \ printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg) -#define wl12xx_debug(level, fmt, arg...) \ +#define wl1271_debug(level, fmt, arg...) \ do { \ if (level & DEBUG_LEVEL) \ printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg); \ } while (0) -#define wl12xx_dump(level, prefix, buf, len) \ +#define wl1271_dump(level, prefix, buf, len) \ do { \ if (level & DEBUG_LEVEL) \ print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \ @@ -84,7 +86,7 @@ enum { 0); \ } while (0) -#define wl12xx_dump_ascii(level, prefix, buf, len) \ +#define wl1271_dump_ascii(level, prefix, buf, len) \ do { \ if (level & DEBUG_LEVEL) \ print_hex_dump(KERN_DEBUG, DRIVER_PREFIX prefix, \ @@ -94,35 +96,29 @@ enum { true); \ } while (0) -#define WL12XX_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \ +#define WL1271_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \ CFG_BSSID_FILTER_EN) -#define WL12XX_DEFAULT_RX_FILTER (CFG_RX_PRSP_EN | \ - CFG_RX_MGMT_EN | \ - CFG_RX_DATA_EN | \ - CFG_RX_CTL_EN | \ - CFG_RX_BCN_EN | \ - CFG_RX_AUTH_EN | \ - CFG_RX_ASSOC_EN) - - -struct boot_attr { - u32 radio_type; - u8 mac_clock; - u8 arm_clock; - int firmware_debug; - u32 minor; - u32 major; - u32 bugfix; -}; +#define WL1271_DEFAULT_RX_FILTER (CFG_RX_RCTS_ACK | CFG_RX_PRSP_EN | \ + CFG_RX_MGMT_EN | CFG_RX_DATA_EN | \ + CFG_RX_CTL_EN | CFG_RX_BCN_EN | \ + CFG_RX_AUTH_EN | CFG_RX_ASSOC_EN) + +#define WL1271_FW_NAME "wl1271-fw.bin" +#define WL1271_NVS_NAME "wl1271-nvs.bin" + +#define WL1271_BUSY_WORD_LEN 8 -enum wl12xx_state { - WL12XX_STATE_OFF, - WL12XX_STATE_ON, - WL12XX_STATE_PLT, +#define WL1271_ELP_HW_STATE_ASLEEP 0 +#define WL1271_ELP_HW_STATE_IRQ 1 + +enum wl1271_state { + WL1271_STATE_OFF, + WL1271_STATE_ON, + WL1271_STATE_PLT, }; -enum wl12xx_partition_type { +enum wl1271_partition_type { PART_DOWN, PART_WORK, PART_DRPW, @@ -130,44 +126,25 @@ enum wl12xx_partition_type { PART_TABLE_LEN }; -struct wl12xx_partition { +struct wl1271_partition { u32 size; u32 start; }; -struct wl12xx_partition_set { - struct wl12xx_partition mem; - struct wl12xx_partition reg; +struct wl1271_partition_set { + struct wl1271_partition mem; + struct wl1271_partition reg; }; -struct wl12xx; +struct wl1271; /* FIXME: I'm not sure about this structure name */ -struct wl12xx_chip { +struct wl1271_chip { u32 id; - - const char *fw_filename; - const char *nvs_filename; - char fw_ver[21]; - - unsigned int power_on_sleep; - int intr_cmd_complete; - int intr_init_complete; - - int (*op_upload_fw)(struct wl12xx *wl); - int (*op_upload_nvs)(struct wl12xx *wl); - int (*op_boot)(struct wl12xx *wl); - void (*op_set_ecpu_ctrl)(struct wl12xx *wl, u32 flag); - void (*op_target_enable_interrupts)(struct wl12xx *wl); - int (*op_hw_init)(struct wl12xx *wl); - int (*op_plt_init)(struct wl12xx *wl); - - struct wl12xx_partition_set *p_table; - enum wl12xx_acx_int_reg *acx_reg_table; }; -struct wl12xx_stats { +struct wl1271_stats { struct acx_statistics *fw_stats; unsigned long fw_stats_update; @@ -175,7 +152,7 @@ struct wl12xx_stats { unsigned int excessive_retries; }; -struct wl12xx_debugfs { +struct wl1271_debugfs { struct dentry *rootdir; struct dentry *fw_statistics; @@ -276,7 +253,28 @@ struct wl12xx_debugfs { struct dentry *excessive_retries; }; -struct wl12xx { +#define NUM_TX_QUEUES 4 +#define NUM_RX_PKT_DESC 8 + +/* FW status registers */ +struct wl1271_fw_status { + u32 intr; + u8 fw_rx_counter; + u8 drv_rx_counter; + u8 reserved; + u8 tx_results_counter; + u32 rx_pkt_descs[NUM_RX_PKT_DESC]; + u32 tx_released_blks[NUM_TX_QUEUES]; + u32 fw_localtime; + u32 padding[2]; +} __attribute__ ((packed)); + +struct wl1271_rx_mem_pool_addr { + u32 addr; + u32 addr_extra; +}; + +struct wl1271 { struct ieee80211_hw *hw; bool mac80211_registered; @@ -285,7 +283,9 @@ struct wl12xx { void (*set_power)(bool enable); int irq; - enum wl12xx_state state; + spinlock_t wl_lock; + + enum wl1271_state state; struct mutex mutex; int physical_mem_addr; @@ -293,11 +293,10 @@ struct wl12xx { int virtual_mem_addr; int virtual_reg_addr; - struct wl12xx_chip chip; + struct wl1271_chip chip; int cmd_box_addr; int event_box_addr; - struct boot_attr boot_attr; u8 *fw; size_t fw_len; @@ -307,14 +306,26 @@ struct wl12xx { u8 bssid[ETH_ALEN]; u8 mac_addr[ETH_ALEN]; u8 bss_type; + u8 ssid[IW_ESSID_MAX_SIZE + 1]; + u8 ssid_len; u8 listen_int; int channel; - void *target_mem_map; - struct acx_data_path_params_resp *data_path; + struct wl1271_acx_mem_map *target_mem_map; + + /* Accounting for allocated / available TX blocks on HW */ + u32 tx_blocks_freed[NUM_TX_QUEUES]; + u32 tx_blocks_available; + u8 tx_results_count; + + /* Transmitted TX packets counter for chipset interface */ + int tx_packets_count; - /* Number of TX packets transferred to the FW, modulo 16 */ - u32 data_in_count; + /* Time-offset between host and chipset clocks */ + int time_offset; + + /* Session counter for the chipset */ + int session_counter; /* Frames scheduled for transmission, not handled yet */ struct sk_buff_head tx_queue; @@ -326,25 +337,13 @@ struct wl12xx { /* Pending TX frames */ struct sk_buff *tx_frames[16]; - /* - * Index pointing to the next TX complete entry - * in the cyclic XT complete array we get from - * the FW. - */ - u32 next_tx_complete; - /* FW Rx counter */ u32 rx_counter; - /* Rx frames handled */ - u32 rx_handled; - - /* Current double buffer */ - u32 rx_current_buffer; - u32 rx_last_id; + /* Rx memory pool address */ + struct wl1271_rx_mem_pool_addr rx_mem_pool_addr; /* The target interrupt mask */ - u32 intr_mask; struct work_struct irq_work; /* The mbox event mask */ @@ -362,15 +361,14 @@ struct wl12xx { /* Default key (for WEP) */ u32 default_key; - unsigned int tx_mgmt_frm_rate; - unsigned int tx_mgmt_frm_mod; - unsigned int rx_config; unsigned int rx_filter; /* is firmware in elp mode */ bool elp; + struct completion *elp_compl; + /* we can be in psm, but not in elp, we have to differentiate */ bool psm; @@ -380,30 +378,30 @@ struct wl12xx { /* in dBm */ int power_level; - struct wl12xx_stats stats; - struct wl12xx_debugfs debugfs; + struct wl1271_stats stats; + struct wl1271_debugfs debugfs; + + u32 buffer_32; + u32 buffer_cmd; + u8 buffer_busyword[WL1271_BUSY_WORD_LEN]; + struct wl1271_rx_descriptor *rx_descriptor; + + struct wl1271_fw_status *fw_status; + struct wl1271_tx_hw_res_if *tx_res_if; }; -int wl12xx_plt_start(struct wl12xx *wl); -int wl12xx_plt_stop(struct wl12xx *wl); +int wl1271_plt_start(struct wl1271 *wl); +int wl1271_plt_stop(struct wl1271 *wl); -#define DEFAULT_HW_GEN_MODULATION_TYPE CCK_LONG /* Long Preamble */ -#define DEFAULT_HW_GEN_TX_RATE RATE_2MBPS #define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */ -#define WL12XX_DEFAULT_POWER_LEVEL 20 +#define SESSION_COUNTER_MAX 7 /* maximum value for the session counter */ -#define WL12XX_TX_QUEUE_MAX_LENGTH 20 +#define WL1271_DEFAULT_POWER_LEVEL 0 -/* Different chips need different sleep times after power on. WL1271 needs - * 200ms, WL1251 needs only 10ms. By default we use 200ms, but as soon as we - * know the chip ID, we change the sleep value in the wl12xx chip structure, - * so in subsequent power ons, we don't waste more time then needed. */ -#define WL12XX_DEFAULT_POWER_ON_SLEEP 200 +#define WL1271_TX_QUEUE_MAX_LENGTH 20 -#define CHIP_ID_1251_PG10 (0x7010101) -#define CHIP_ID_1251_PG11 (0x7020101) -#define CHIP_ID_1251_PG12 (0x7030101) -#define CHIP_ID_1271_PG10 (0x4030101) +/* WL1271 needs a 200ms sleep after power on */ +#define WL1271_POWER_ON_SLEEP 200 /* in miliseconds */ #endif diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c new file mode 100644 index 000000000000..f622a4092615 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -0,0 +1,961 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2008-2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "wl1271_acx.h" + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/crc7.h> +#include <linux/spi/spi.h> + +#include "wl1271.h" +#include "wl12xx_80211.h" +#include "wl1271_reg.h" +#include "wl1271_spi.h" +#include "wl1271_ps.h" + +int wl1271_acx_wake_up_conditions(struct wl1271 *wl, u8 wake_up_event, + u8 listen_interval) +{ + struct acx_wake_up_condition *wake_up; + int ret; + + wl1271_debug(DEBUG_ACX, "acx wake up conditions"); + + wake_up = kzalloc(sizeof(*wake_up), GFP_KERNEL); + if (!wake_up) { + ret = -ENOMEM; + goto out; + } + + wake_up->wake_up_event = wake_up_event; + wake_up->listen_interval = listen_interval; + + ret = wl1271_cmd_configure(wl, ACX_WAKE_UP_CONDITIONS, + wake_up, sizeof(*wake_up)); + if (ret < 0) { + wl1271_warning("could not set wake up conditions: %d", ret); + goto out; + } + +out: + kfree(wake_up); + return ret; +} + +int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth) +{ + struct acx_sleep_auth *auth; + int ret; + + wl1271_debug(DEBUG_ACX, "acx sleep auth"); + + auth = kzalloc(sizeof(*auth), GFP_KERNEL); + if (!auth) { + ret = -ENOMEM; + goto out; + } + + auth->sleep_auth = sleep_auth; + + ret = wl1271_cmd_configure(wl, ACX_SLEEP_AUTH, auth, sizeof(*auth)); + if (ret < 0) + return ret; + +out: + kfree(auth); + return ret; +} + +int wl1271_acx_fw_version(struct wl1271 *wl, char *buf, size_t len) +{ + struct acx_revision *rev; + int ret; + + wl1271_debug(DEBUG_ACX, "acx fw rev"); + + rev = kzalloc(sizeof(*rev), GFP_KERNEL); + if (!rev) { + ret = -ENOMEM; + goto out; + } + + ret = wl1271_cmd_interrogate(wl, ACX_FW_REV, rev, sizeof(*rev)); + if (ret < 0) { + wl1271_warning("ACX_FW_REV interrogate failed"); + goto out; + } + + /* be careful with the buffer sizes */ + strncpy(buf, rev->fw_version, min(len, sizeof(rev->fw_version))); + + /* + * if the firmware version string is exactly + * sizeof(rev->fw_version) long or fw_len is less than + * sizeof(rev->fw_version) it won't be null terminated + */ + buf[min(len, sizeof(rev->fw_version)) - 1] = '\0'; + +out: + kfree(rev); + return ret; +} + +int wl1271_acx_tx_power(struct wl1271 *wl, int power) +{ + struct acx_current_tx_power *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx dot11_cur_tx_pwr"); + + if (power < 0 || power > 25) + return -EINVAL; + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->current_tx_power = power * 10; + + ret = wl1271_cmd_configure(wl, DOT11_CUR_TX_PWR, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("configure of tx power failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1271_acx_feature_cfg(struct wl1271 *wl) +{ + struct acx_feature_config *feature; + int ret; + + wl1271_debug(DEBUG_ACX, "acx feature cfg"); + + feature = kzalloc(sizeof(*feature), GFP_KERNEL); + if (!feature) { + ret = -ENOMEM; + goto out; + } + + /* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE are disabled */ + feature->data_flow_options = 0; + feature->options = 0; + + ret = wl1271_cmd_configure(wl, ACX_FEATURE_CFG, + feature, sizeof(*feature)); + if (ret < 0) { + wl1271_error("Couldnt set HW encryption"); + goto out; + } + +out: + kfree(feature); + return ret; +} + +int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map, + size_t len) +{ + int ret; + + wl1271_debug(DEBUG_ACX, "acx mem map"); + + ret = wl1271_cmd_interrogate(wl, ACX_MEM_MAP, mem_map, len); + if (ret < 0) + return ret; + + return 0; +} + +int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time) +{ + struct acx_rx_msdu_lifetime *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx rx msdu life time"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->lifetime = life_time; + ret = wl1271_cmd_configure(wl, DOT11_RX_MSDU_LIFE_TIME, + acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("failed to set rx msdu life time: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter) +{ + struct acx_rx_config *rx_config; + int ret; + + wl1271_debug(DEBUG_ACX, "acx rx config"); + + rx_config = kzalloc(sizeof(*rx_config), GFP_KERNEL); + if (!rx_config) { + ret = -ENOMEM; + goto out; + } + + rx_config->config_options = config; + rx_config->filter_options = filter; + + ret = wl1271_cmd_configure(wl, ACX_RX_CFG, + rx_config, sizeof(*rx_config)); + if (ret < 0) { + wl1271_warning("failed to set rx config: %d", ret); + goto out; + } + +out: + kfree(rx_config); + return ret; +} + +int wl1271_acx_pd_threshold(struct wl1271 *wl) +{ + struct acx_packet_detection *pd; + int ret; + + wl1271_debug(DEBUG_ACX, "acx data pd threshold"); + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) { + ret = -ENOMEM; + goto out; + } + + /* FIXME: threshold value not set */ + + ret = wl1271_cmd_configure(wl, ACX_PD_THRESHOLD, pd, sizeof(*pd)); + if (ret < 0) { + wl1271_warning("failed to set pd threshold: %d", ret); + goto out; + } + +out: + kfree(pd); + return 0; +} + +int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time) +{ + struct acx_slot *slot; + int ret; + + wl1271_debug(DEBUG_ACX, "acx slot"); + + slot = kzalloc(sizeof(*slot), GFP_KERNEL); + if (!slot) { + ret = -ENOMEM; + goto out; + } + + slot->wone_index = STATION_WONE_INDEX; + slot->slot_time = slot_time; + + ret = wl1271_cmd_configure(wl, ACX_SLOT, slot, sizeof(*slot)); + if (ret < 0) { + wl1271_warning("failed to set slot time: %d", ret); + goto out; + } + +out: + kfree(slot); + return ret; +} + +int wl1271_acx_group_address_tbl(struct wl1271 *wl) +{ + struct acx_dot11_grp_addr_tbl *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx group address tbl"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + /* MAC filtering */ + acx->enabled = 0; + acx->num_groups = 0; + memset(acx->mac_table, 0, ADDRESS_GROUP_MAX_LEN); + + ret = wl1271_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL, + acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("failed to set group addr table: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1271_acx_service_period_timeout(struct wl1271 *wl) +{ + struct acx_rx_timeout *rx_timeout; + int ret; + + rx_timeout = kzalloc(sizeof(*rx_timeout), GFP_KERNEL); + if (!rx_timeout) { + ret = -ENOMEM; + goto out; + } + + wl1271_debug(DEBUG_ACX, "acx service period timeout"); + + rx_timeout->ps_poll_timeout = RX_TIMEOUT_PS_POLL_DEF; + rx_timeout->upsd_timeout = RX_TIMEOUT_UPSD_DEF; + + ret = wl1271_cmd_configure(wl, ACX_SERVICE_PERIOD_TIMEOUT, + rx_timeout, sizeof(*rx_timeout)); + if (ret < 0) { + wl1271_warning("failed to set service period timeout: %d", + ret); + goto out; + } + +out: + kfree(rx_timeout); + return ret; +} + +int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold) +{ + struct acx_rts_threshold *rts; + int ret; + + wl1271_debug(DEBUG_ACX, "acx rts threshold"); + + rts = kzalloc(sizeof(*rts), GFP_KERNEL); + if (!rts) { + ret = -ENOMEM; + goto out; + } + + rts->threshold = rts_threshold; + + ret = wl1271_cmd_configure(wl, DOT11_RTS_THRESHOLD, rts, sizeof(*rts)); + if (ret < 0) { + wl1271_warning("failed to set rts threshold: %d", ret); + goto out; + } + +out: + kfree(rts); + return ret; +} + +int wl1271_acx_beacon_filter_opt(struct wl1271 *wl) +{ + struct acx_beacon_filter_option *beacon_filter; + int ret; + + wl1271_debug(DEBUG_ACX, "acx beacon filter opt"); + + beacon_filter = kzalloc(sizeof(*beacon_filter), GFP_KERNEL); + if (!beacon_filter) { + ret = -ENOMEM; + goto out; + } + + beacon_filter->enable = 0; + beacon_filter->max_num_beacons = 0; + + ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_OPT, + beacon_filter, sizeof(*beacon_filter)); + if (ret < 0) { + wl1271_warning("failed to set beacon filter opt: %d", ret); + goto out; + } + +out: + kfree(beacon_filter); + return ret; +} + +int wl1271_acx_beacon_filter_table(struct wl1271 *wl) +{ + struct acx_beacon_filter_ie_table *ie_table; + int ret; + + wl1271_debug(DEBUG_ACX, "acx beacon filter table"); + + ie_table = kzalloc(sizeof(*ie_table), GFP_KERNEL); + if (!ie_table) { + ret = -ENOMEM; + goto out; + } + + ie_table->num_ie = 0; + memset(ie_table->table, 0, BEACON_FILTER_TABLE_MAX_SIZE); + + ret = wl1271_cmd_configure(wl, ACX_BEACON_FILTER_TABLE, + ie_table, sizeof(*ie_table)); + if (ret < 0) { + wl1271_warning("failed to set beacon filter table: %d", ret); + goto out; + } + +out: + kfree(ie_table); + return ret; +} + +int wl1271_acx_sg_enable(struct wl1271 *wl) +{ + struct acx_bt_wlan_coex *pta; + int ret; + + wl1271_debug(DEBUG_ACX, "acx sg enable"); + + pta = kzalloc(sizeof(*pta), GFP_KERNEL); + if (!pta) { + ret = -ENOMEM; + goto out; + } + + pta->enable = SG_ENABLE; + + ret = wl1271_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta)); + if (ret < 0) { + wl1271_warning("failed to set softgemini enable: %d", ret); + goto out; + } + +out: + kfree(pta); + return ret; +} + +int wl1271_acx_sg_cfg(struct wl1271 *wl) +{ + struct acx_bt_wlan_coex_param *param; + int ret; + + wl1271_debug(DEBUG_ACX, "acx sg cfg"); + + param = kzalloc(sizeof(*param), GFP_KERNEL); + if (!param) { + ret = -ENOMEM; + goto out; + } + + /* BT-WLAN coext parameters */ + param->min_rate = RATE_INDEX_24MBPS; + param->bt_hp_max_time = PTA_BT_HP_MAXTIME_DEF; + param->wlan_hp_max_time = PTA_WLAN_HP_MAX_TIME_DEF; + param->sense_disable_timer = PTA_SENSE_DISABLE_TIMER_DEF; + param->rx_time_bt_hp = PTA_PROTECTIVE_RX_TIME_DEF; + param->tx_time_bt_hp = PTA_PROTECTIVE_TX_TIME_DEF; + param->rx_time_bt_hp_fast = PTA_PROTECTIVE_RX_TIME_FAST_DEF; + param->tx_time_bt_hp_fast = PTA_PROTECTIVE_TX_TIME_FAST_DEF; + param->wlan_cycle_fast = PTA_CYCLE_TIME_FAST_DEF; + param->bt_anti_starvation_period = PTA_ANTI_STARVE_PERIOD_DEF; + param->next_bt_lp_packet = PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF; + param->wake_up_beacon = PTA_TIME_BEFORE_BEACON_DEF; + param->hp_dm_max_guard_time = PTA_HPDM_MAX_TIME_DEF; + param->next_wlan_packet = PTA_TIME_OUT_NEXT_WLAN_DEF; + param->antenna_type = PTA_ANTENNA_TYPE_DEF; + param->signal_type = PTA_SIGNALING_TYPE_DEF; + param->afh_leverage_on = PTA_AFH_LEVERAGE_ON_DEF; + param->quiet_cycle_num = PTA_NUMBER_QUIET_CYCLE_DEF; + param->max_cts = PTA_MAX_NUM_CTS_DEF; + param->wlan_packets_num = PTA_NUMBER_OF_WLAN_PACKETS_DEF; + param->bt_packets_num = PTA_NUMBER_OF_BT_PACKETS_DEF; + param->missed_rx_avalanche = PTA_RX_FOR_AVALANCHE_DEF; + param->wlan_elp_hp = PTA_ELP_HP_DEF; + param->bt_anti_starvation_cycles = PTA_ANTI_STARVE_NUM_CYCLE_DEF; + param->ack_mode_dual_ant = PTA_ACK_MODE_DEF; + param->pa_sd_enable = PTA_ALLOW_PA_SD_DEF; + param->pta_auto_mode_enable = PTA_AUTO_MODE_NO_CTS_DEF; + param->bt_hp_respected_num = PTA_BT_HP_RESPECTED_DEF; + + ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); + if (ret < 0) { + wl1271_warning("failed to set sg config: %d", ret); + goto out; + } + +out: + kfree(param); + return ret; +} + +int wl1271_acx_cca_threshold(struct wl1271 *wl) +{ + struct acx_energy_detection *detection; + int ret; + + wl1271_debug(DEBUG_ACX, "acx cca threshold"); + + detection = kzalloc(sizeof(*detection), GFP_KERNEL); + if (!detection) { + ret = -ENOMEM; + goto out; + } + + detection->rx_cca_threshold = CCA_THRSH_DISABLE_ENERGY_D; + detection->tx_energy_detection = 0; + + ret = wl1271_cmd_configure(wl, ACX_CCA_THRESHOLD, + detection, sizeof(*detection)); + if (ret < 0) { + wl1271_warning("failed to set cca threshold: %d", ret); + return ret; + } + +out: + kfree(detection); + return ret; +} + +int wl1271_acx_bcn_dtim_options(struct wl1271 *wl) +{ + struct acx_beacon_broadcast *bb; + int ret; + + wl1271_debug(DEBUG_ACX, "acx bcn dtim options"); + + bb = kzalloc(sizeof(*bb), GFP_KERNEL); + if (!bb) { + ret = -ENOMEM; + goto out; + } + + bb->beacon_rx_timeout = BCN_RX_TIMEOUT_DEF_VALUE; + bb->broadcast_timeout = BROADCAST_RX_TIMEOUT_DEF_VALUE; + bb->rx_broadcast_in_ps = RX_BROADCAST_IN_PS_DEF_VALUE; + bb->ps_poll_threshold = CONSECUTIVE_PS_POLL_FAILURE_DEF; + + ret = wl1271_cmd_configure(wl, ACX_BCN_DTIM_OPTIONS, bb, sizeof(*bb)); + if (ret < 0) { + wl1271_warning("failed to set rx config: %d", ret); + goto out; + } + +out: + kfree(bb); + return ret; +} + +int wl1271_acx_aid(struct wl1271 *wl, u16 aid) +{ + struct acx_aid *acx_aid; + int ret; + + wl1271_debug(DEBUG_ACX, "acx aid"); + + acx_aid = kzalloc(sizeof(*acx_aid), GFP_KERNEL); + if (!acx_aid) { + ret = -ENOMEM; + goto out; + } + + acx_aid->aid = aid; + + ret = wl1271_cmd_configure(wl, ACX_AID, acx_aid, sizeof(*acx_aid)); + if (ret < 0) { + wl1271_warning("failed to set aid: %d", ret); + goto out; + } + +out: + kfree(acx_aid); + return ret; +} + +int wl1271_acx_event_mbox_mask(struct wl1271 *wl, u32 event_mask) +{ + struct acx_event_mask *mask; + int ret; + + wl1271_debug(DEBUG_ACX, "acx event mbox mask"); + + mask = kzalloc(sizeof(*mask), GFP_KERNEL); + if (!mask) { + ret = -ENOMEM; + goto out; + } + + /* high event mask is unused */ + mask->high_event_mask = 0xffffffff; + + mask->event_mask = event_mask; + + ret = wl1271_cmd_configure(wl, ACX_EVENT_MBOX_MASK, + mask, sizeof(*mask)); + if (ret < 0) { + wl1271_warning("failed to set acx_event_mbox_mask: %d", ret); + goto out; + } + +out: + kfree(mask); + return ret; +} + +int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble) +{ + struct acx_preamble *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx_set_preamble"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->preamble = preamble; + + ret = wl1271_cmd_configure(wl, ACX_PREAMBLE_TYPE, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("Setting of preamble failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1271_acx_cts_protect(struct wl1271 *wl, + enum acx_ctsprotect_type ctsprotect) +{ + struct acx_ctsprotect *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx_set_ctsprotect"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->ctsprotect = ctsprotect; + + ret = wl1271_cmd_configure(wl, ACX_CTS_PROTECTION, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("Setting of ctsprotect failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats) +{ + int ret; + + wl1271_debug(DEBUG_ACX, "acx statistics"); + + ret = wl1271_cmd_interrogate(wl, ACX_STATISTICS, stats, + sizeof(*stats)); + if (ret < 0) { + wl1271_warning("acx statistics failed: %d", ret); + return -ENOMEM; + } + + return 0; +} + +int wl1271_acx_rate_policies(struct wl1271 *wl) +{ + struct acx_rate_policy *acx; + int ret = 0; + + wl1271_debug(DEBUG_ACX, "acx rate policies"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + + if (!acx) { + ret = -ENOMEM; + goto out; + } + + /* configure one default (one-size-fits-all) rate class */ + acx->rate_class_cnt = 1; + acx->rate_class[0].enabled_rates = ACX_RATE_MASK_ALL; + acx->rate_class[0].short_retry_limit = ACX_RATE_RETRY_LIMIT; + acx->rate_class[0].long_retry_limit = ACX_RATE_RETRY_LIMIT; + acx->rate_class[0].aflags = 0; + + ret = wl1271_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("Setting of rate policies failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1271_acx_ac_cfg(struct wl1271 *wl) +{ + struct acx_ac_cfg *acx; + int i, ret = 0; + + wl1271_debug(DEBUG_ACX, "acx access category config"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + + if (!acx) { + ret = -ENOMEM; + goto out; + } + + /* + * FIXME: Configure each AC with appropriate values (most suitable + * values will probably be different for each AC. + */ + for (i = 0; i < WL1271_ACX_AC_COUNT; i++) { + acx->ac = i; + + /* + * FIXME: The following default values originate from + * the TI reference driver. What do they mean? + */ + acx->cw_min = 15; + acx->cw_max = 63; + acx->aifsn = 3; + acx->reserved = 0; + acx->tx_op_limit = 0; + + ret = wl1271_cmd_configure(wl, ACX_AC_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("Setting of access category " + "config: %d", ret); + goto out; + } + } + +out: + kfree(acx); + return ret; +} + +int wl1271_acx_tid_cfg(struct wl1271 *wl) +{ + struct acx_tid_config *acx; + int i, ret = 0; + + wl1271_debug(DEBUG_ACX, "acx tid config"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + + if (!acx) { + ret = -ENOMEM; + goto out; + } + + /* FIXME: configure each TID with a different AC reference */ + for (i = 0; i < WL1271_ACX_TID_COUNT; i++) { + acx->queue_id = i; + acx->tsid = WL1271_ACX_AC_BE; + acx->ps_scheme = WL1271_ACX_PS_SCHEME_LEGACY; + acx->ack_policy = WL1271_ACX_ACK_POLICY_LEGACY; + + ret = wl1271_cmd_configure(wl, ACX_TID_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("Setting of tid config failed: %d", ret); + goto out; + } + } + +out: + kfree(acx); + return ret; +} + +int wl1271_acx_frag_threshold(struct wl1271 *wl) +{ + struct acx_frag_threshold *acx; + int ret = 0; + + wl1271_debug(DEBUG_ACX, "acx frag threshold"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD; + ret = wl1271_cmd_configure(wl, ACX_FRAG_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("Setting of frag threshold failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1271_acx_tx_config_options(struct wl1271 *wl) +{ + struct acx_tx_config_options *acx; + int ret = 0; + + wl1271_debug(DEBUG_ACX, "acx tx config options"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->tx_compl_timeout = WL1271_ACX_TX_COMPL_TIMEOUT; + acx->tx_compl_threshold = WL1271_ACX_TX_COMPL_THRESHOLD; + ret = wl1271_cmd_configure(wl, ACX_TX_CONFIG_OPT, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("Setting of tx options failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl1271_acx_mem_cfg(struct wl1271 *wl) +{ + struct wl1271_acx_config_memory *mem_conf; + int ret; + + wl1271_debug(DEBUG_ACX, "wl1271 mem cfg"); + + mem_conf = kzalloc(sizeof(*mem_conf), GFP_KERNEL); + if (!mem_conf) { + ret = -ENOMEM; + goto out; + } + + /* memory config */ + mem_conf->num_stations = cpu_to_le16(DEFAULT_NUM_STATIONS); + mem_conf->rx_mem_block_num = ACX_RX_MEM_BLOCKS; + mem_conf->tx_min_mem_block_num = ACX_TX_MIN_MEM_BLOCKS; + mem_conf->num_ssid_profiles = ACX_NUM_SSID_PROFILES; + mem_conf->total_tx_descriptors = ACX_TX_DESCRIPTORS; + + ret = wl1271_cmd_configure(wl, ACX_MEM_CFG, mem_conf, + sizeof(*mem_conf)); + if (ret < 0) { + wl1271_warning("wl1271 mem config failed: %d", ret); + goto out; + } + +out: + kfree(mem_conf); + return ret; +} + +int wl1271_acx_init_mem_config(struct wl1271 *wl) +{ + int ret; + + ret = wl1271_acx_mem_cfg(wl); + if (ret < 0) + return ret; + + wl->target_mem_map = kzalloc(sizeof(struct wl1271_acx_mem_map), + GFP_KERNEL); + if (!wl->target_mem_map) { + wl1271_error("couldn't allocate target memory map"); + return -ENOMEM; + } + + /* we now ask for the firmware built memory map */ + ret = wl1271_acx_mem_map(wl, (void *)wl->target_mem_map, + sizeof(struct wl1271_acx_mem_map)); + if (ret < 0) { + wl1271_error("couldn't retrieve firmware memory map"); + kfree(wl->target_mem_map); + wl->target_mem_map = NULL; + return ret; + } + + /* initialize TX block book keeping */ + wl->tx_blocks_available = wl->target_mem_map->num_tx_mem_blocks; + wl1271_debug(DEBUG_TX, "available tx blocks: %d", + wl->tx_blocks_available); + + return 0; +} + +int wl1271_acx_init_rx_interrupt(struct wl1271 *wl) +{ + struct wl1271_acx_rx_config_opt *rx_conf; + int ret; + + wl1271_debug(DEBUG_ACX, "wl1271 rx interrupt config"); + + rx_conf = kzalloc(sizeof(*rx_conf), GFP_KERNEL); + if (!rx_conf) { + ret = -ENOMEM; + goto out; + } + + rx_conf->threshold = WL1271_RX_INTR_THRESHOLD_DEF; + rx_conf->timeout = WL1271_RX_INTR_TIMEOUT_DEF; + rx_conf->mblk_threshold = USHORT_MAX; /* Disabled */ + rx_conf->queue_type = RX_QUEUE_TYPE_RX_LOW_PRIORITY; + + ret = wl1271_cmd_configure(wl, ACX_RX_CONFIG_OPT, rx_conf, + sizeof(*rx_conf)); + if (ret < 0) { + wl1271_warning("wl1271 rx config opt failed: %d", ret); + goto out; + } + +out: + kfree(rx_conf); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h new file mode 100644 index 000000000000..9068daaf0ddf --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -0,0 +1,1221 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. + * Copyright (C) 2008-2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1271_ACX_H__ +#define __WL1271_ACX_H__ + +#include "wl1271.h" +#include "wl1271_cmd.h" + +/************************************************************************* + + Host Interrupt Register (WiLink -> Host) + +**************************************************************************/ +/* HW Initiated interrupt Watchdog timer expiration */ +#define WL1271_ACX_INTR_WATCHDOG BIT(0) +/* Init sequence is done (masked interrupt, detection through polling only ) */ +#define WL1271_ACX_INTR_INIT_COMPLETE BIT(1) +/* Event was entered to Event MBOX #A*/ +#define WL1271_ACX_INTR_EVENT_A BIT(2) +/* Event was entered to Event MBOX #B*/ +#define WL1271_ACX_INTR_EVENT_B BIT(3) +/* Command processing completion*/ +#define WL1271_ACX_INTR_CMD_COMPLETE BIT(4) +/* Signaling the host on HW wakeup */ +#define WL1271_ACX_INTR_HW_AVAILABLE BIT(5) +/* The MISC bit is used for aggregation of RX, TxComplete and TX rate update */ +#define WL1271_ACX_INTR_DATA BIT(6) +/* Trace meassge on MBOX #A */ +#define WL1271_ACX_INTR_TRACE_A BIT(7) +/* Trace meassge on MBOX #B */ +#define WL1271_ACX_INTR_TRACE_B BIT(8) + +#define WL1271_ACX_INTR_ALL 0xFFFFFFFF +#define WL1271_ACX_ALL_EVENTS_VECTOR (WL1271_ACX_INTR_WATCHDOG | \ + WL1271_ACX_INTR_INIT_COMPLETE | \ + WL1271_ACX_INTR_EVENT_A | \ + WL1271_ACX_INTR_EVENT_B | \ + WL1271_ACX_INTR_CMD_COMPLETE | \ + WL1271_ACX_INTR_HW_AVAILABLE | \ + WL1271_ACX_INTR_DATA) + +#define WL1271_INTR_MASK (WL1271_ACX_INTR_EVENT_A | \ + WL1271_ACX_INTR_EVENT_B | \ + WL1271_ACX_INTR_DATA) + +/* Target's information element */ +struct acx_header { + struct wl1271_cmd_header cmd; + + /* acx (or information element) header */ + u16 id; + + /* payload length (not including headers */ + u16 len; +}; + +struct acx_error_counter { + struct acx_header header; + + /* The number of PLCP errors since the last time this */ + /* information element was interrogated. This field is */ + /* automatically cleared when it is interrogated.*/ + u32 PLCP_error; + + /* The number of FCS errors since the last time this */ + /* information element was interrogated. This field is */ + /* automatically cleared when it is interrogated.*/ + u32 FCS_error; + + /* The number of MPDUs without PLCP header errors received*/ + /* since the last time this information element was interrogated. */ + /* This field is automatically cleared when it is interrogated.*/ + u32 valid_frame; + + /* the number of missed sequence numbers in the squentially */ + /* values of frames seq numbers */ + u32 seq_num_miss; +} __attribute__ ((packed)); + +struct acx_revision { + struct acx_header header; + + /* + * The WiLink firmware version, an ASCII string x.x.x.x, + * that uniquely identifies the current firmware. + * The left most digit is incremented each time a + * significant change is made to the firmware, such as + * code redesign or new platform support. + * The second digit is incremented when major enhancements + * are added or major fixes are made. + * The third digit is incremented for each GA release. + * The fourth digit is incremented for each build. + * The first two digits identify a firmware release version, + * in other words, a unique set of features. + * The first three digits identify a GA release. + */ + char fw_version[20]; + + /* + * This 4 byte field specifies the WiLink hardware version. + * bits 0 - 15: Reserved. + * bits 16 - 23: Version ID - The WiLink version ID + * (1 = first spin, 2 = second spin, and so on). + * bits 24 - 31: Chip ID - The WiLink chip ID. + */ + u32 hw_version; +} __attribute__ ((packed)); + +enum wl1271_psm_mode { + /* Active mode */ + WL1271_PSM_CAM = 0, + + /* Power save mode */ + WL1271_PSM_PS = 1, + + /* Extreme low power */ + WL1271_PSM_ELP = 2, +}; + +struct acx_sleep_auth { + struct acx_header header; + + /* The sleep level authorization of the device. */ + /* 0 - Always active*/ + /* 1 - Power down mode: light / fast sleep*/ + /* 2 - ELP mode: Deep / Max sleep*/ + u8 sleep_auth; + u8 padding[3]; +} __attribute__ ((packed)); + +enum { + HOSTIF_PCI_MASTER_HOST_INDIRECT, + HOSTIF_PCI_MASTER_HOST_DIRECT, + HOSTIF_SLAVE, + HOSTIF_PKT_RING, + HOSTIF_DONTCARE = 0xFF +}; + +#define DEFAULT_UCAST_PRIORITY 0 +#define DEFAULT_RX_Q_PRIORITY 0 +#define DEFAULT_NUM_STATIONS 1 +#define DEFAULT_RXQ_PRIORITY 0 /* low 0 .. 15 high */ +#define DEFAULT_RXQ_TYPE 0x07 /* All frames, Data/Ctrl/Mgmt */ +#define TRACE_BUFFER_MAX_SIZE 256 + +#define DP_RX_PACKET_RING_CHUNK_SIZE 1600 +#define DP_TX_PACKET_RING_CHUNK_SIZE 1600 +#define DP_RX_PACKET_RING_CHUNK_NUM 2 +#define DP_TX_PACKET_RING_CHUNK_NUM 2 +#define DP_TX_COMPLETE_TIME_OUT 20 +#define FW_TX_CMPLT_BLOCK_SIZE 16 + +#define TX_MSDU_LIFETIME_MIN 0 +#define TX_MSDU_LIFETIME_MAX 3000 +#define TX_MSDU_LIFETIME_DEF 512 +#define RX_MSDU_LIFETIME_MIN 0 +#define RX_MSDU_LIFETIME_MAX 0xFFFFFFFF +#define RX_MSDU_LIFETIME_DEF 512000 + +struct acx_rx_msdu_lifetime { + struct acx_header header; + + /* + * The maximum amount of time, in TU, before the + * firmware discards the MSDU. + */ + u32 lifetime; +} __attribute__ ((packed)); + +/* + * RX Config Options Table + * Bit Definition + * === ========== + * 31:14 Reserved + * 13 Copy RX Status - when set, write three receive status words + * to top of rx'd MPDUs. + * When cleared, do not write three status words (added rev 1.5) + * 12 Reserved + * 11 RX Complete upon FCS error - when set, give rx complete + * interrupt for FCS errors, after the rx filtering, e.g. unicast + * frames not to us with FCS error will not generate an interrupt. + * 10 SSID Filter Enable - When set, the WiLink discards all beacon, + * probe request, and probe response frames with an SSID that does + * not match the SSID specified by the host in the START/JOIN + * command. + * When clear, the WiLink receives frames with any SSID. + * 9 Broadcast Filter Enable - When set, the WiLink discards all + * broadcast frames. When clear, the WiLink receives all received + * broadcast frames. + * 8:6 Reserved + * 5 BSSID Filter Enable - When set, the WiLink discards any frames + * with a BSSID that does not match the BSSID specified by the + * host. + * When clear, the WiLink receives frames from any BSSID. + * 4 MAC Addr Filter - When set, the WiLink discards any frames + * with a destination address that does not match the MAC address + * of the adaptor. + * When clear, the WiLink receives frames destined to any MAC + * address. + * 3 Promiscuous - When set, the WiLink receives all valid frames + * (i.e., all frames that pass the FCS check). + * When clear, only frames that pass the other filters specified + * are received. + * 2 FCS - When set, the WiLink includes the FCS with the received + * frame. + * When cleared, the FCS is discarded. + * 1 PLCP header - When set, write all data from baseband to frame + * buffer including PHY header. + * 0 Reserved - Always equal to 0. + * + * RX Filter Options Table + * Bit Definition + * === ========== + * 31:12 Reserved - Always equal to 0. + * 11 Association - When set, the WiLink receives all association + * related frames (association request/response, reassocation + * request/response, and disassociation). When clear, these frames + * are discarded. + * 10 Auth/De auth - When set, the WiLink receives all authentication + * and de-authentication frames. When clear, these frames are + * discarded. + * 9 Beacon - When set, the WiLink receives all beacon frames. + * When clear, these frames are discarded. + * 8 Contention Free - When set, the WiLink receives all contention + * free frames. + * When clear, these frames are discarded. + * 7 Control - When set, the WiLink receives all control frames. + * When clear, these frames are discarded. + * 6 Data - When set, the WiLink receives all data frames. + * When clear, these frames are discarded. + * 5 FCS Error - When set, the WiLink receives frames that have FCS + * errors. + * When clear, these frames are discarded. + * 4 Management - When set, the WiLink receives all management + * frames. + * When clear, these frames are discarded. + * 3 Probe Request - When set, the WiLink receives all probe request + * frames. + * When clear, these frames are discarded. + * 2 Probe Response - When set, the WiLink receives all probe + * response frames. + * When clear, these frames are discarded. + * 1 RTS/CTS/ACK - When set, the WiLink receives all RTS, CTS and ACK + * frames. + * When clear, these frames are discarded. + * 0 Rsvd Type/Sub Type - When set, the WiLink receives all frames + * that have reserved frame types and sub types as defined by the + * 802.11 specification. + * When clear, these frames are discarded. + */ +struct acx_rx_config { + struct acx_header header; + + u32 config_options; + u32 filter_options; +} __attribute__ ((packed)); + +struct acx_packet_detection { + struct acx_header header; + + u32 threshold; +} __attribute__ ((packed)); + + +enum acx_slot_type { + SLOT_TIME_LONG = 0, + SLOT_TIME_SHORT = 1, + DEFAULT_SLOT_TIME = SLOT_TIME_SHORT, + MAX_SLOT_TIMES = 0xFF +}; + +#define STATION_WONE_INDEX 0 + +struct acx_slot { + struct acx_header header; + + u8 wone_index; /* Reserved */ + u8 slot_time; + u8 reserved[6]; +} __attribute__ ((packed)); + + +#define ADDRESS_GROUP_MAX (8) +#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ADDRESS_GROUP_MAX) + +struct acx_dot11_grp_addr_tbl { + struct acx_header header; + + u8 enabled; + u8 num_groups; + u8 pad[2]; + u8 mac_table[ADDRESS_GROUP_MAX_LEN]; +} __attribute__ ((packed)); + + +#define RX_TIMEOUT_PS_POLL_MIN 0 +#define RX_TIMEOUT_PS_POLL_MAX (200000) +#define RX_TIMEOUT_PS_POLL_DEF (15) +#define RX_TIMEOUT_UPSD_MIN 0 +#define RX_TIMEOUT_UPSD_MAX (200000) +#define RX_TIMEOUT_UPSD_DEF (15) + +struct acx_rx_timeout { + struct acx_header header; + + /* + * The longest time the STA will wait to receive + * traffic from the AP after a PS-poll has been + * transmitted. + */ + u16 ps_poll_timeout; + + /* + * The longest time the STA will wait to receive + * traffic from the AP after a frame has been sent + * from an UPSD enabled queue. + */ + u16 upsd_timeout; +} __attribute__ ((packed)); + +#define RTS_THRESHOLD_MIN 0 +#define RTS_THRESHOLD_MAX 4096 +#define RTS_THRESHOLD_DEF 2347 + +struct acx_rts_threshold { + struct acx_header header; + + u16 threshold; + u8 pad[2]; +} __attribute__ ((packed)); + +struct acx_beacon_filter_option { + struct acx_header header; + + u8 enable; + + /* + * The number of beacons without the unicast TIM + * bit set that the firmware buffers before + * signaling the host about ready frames. + * When set to 0 and the filter is enabled, beacons + * without the unicast TIM bit set are dropped. + */ + u8 max_num_beacons; + u8 pad[2]; +} __attribute__ ((packed)); + +/* + * ACXBeaconFilterEntry (not 221) + * Byte Offset Size (Bytes) Definition + * =========== ============ ========== + * 0 1 IE identifier + * 1 1 Treatment bit mask + * + * ACXBeaconFilterEntry (221) + * Byte Offset Size (Bytes) Definition + * =========== ============ ========== + * 0 1 IE identifier + * 1 1 Treatment bit mask + * 2 3 OUI + * 5 1 Type + * 6 2 Version + * + * + * Treatment bit mask - The information element handling: + * bit 0 - The information element is compared and transferred + * in case of change. + * bit 1 - The information element is transferred to the host + * with each appearance or disappearance. + * Note that both bits can be set at the same time. + */ +#define BEACON_FILTER_TABLE_MAX_IE_NUM (32) +#define BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM (6) +#define BEACON_FILTER_TABLE_IE_ENTRY_SIZE (2) +#define BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE (6) +#define BEACON_FILTER_TABLE_MAX_SIZE ((BEACON_FILTER_TABLE_MAX_IE_NUM * \ + BEACON_FILTER_TABLE_IE_ENTRY_SIZE) + \ + (BEACON_FILTER_TABLE_MAX_VENDOR_SPECIFIC_IE_NUM * \ + BEACON_FILTER_TABLE_EXTRA_VENDOR_SPECIFIC_IE_SIZE)) + +struct acx_beacon_filter_ie_table { + struct acx_header header; + + u8 num_ie; + u8 table[BEACON_FILTER_TABLE_MAX_SIZE]; + u8 pad[3]; +} __attribute__ ((packed)); + +enum { + SG_ENABLE = 0, + SG_DISABLE, + SG_SENSE_NO_ACTIVITY, + SG_SENSE_ACTIVE +}; + +struct acx_bt_wlan_coex { + struct acx_header header; + + /* + * 0 -> PTA enabled + * 1 -> PTA disabled + * 2 -> sense no active mode, i.e. + * an interrupt is sent upon + * BT activity. + * 3 -> PTA is switched on in response + * to the interrupt sending. + */ + u8 enable; + u8 pad[3]; +} __attribute__ ((packed)); + +#define PTA_ANTENNA_TYPE_DEF (0) +#define PTA_BT_HP_MAXTIME_DEF (2000) +#define PTA_WLAN_HP_MAX_TIME_DEF (5000) +#define PTA_SENSE_DISABLE_TIMER_DEF (1350) +#define PTA_PROTECTIVE_RX_TIME_DEF (1500) +#define PTA_PROTECTIVE_TX_TIME_DEF (1500) +#define PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF (3000) +#define PTA_SIGNALING_TYPE_DEF (1) +#define PTA_AFH_LEVERAGE_ON_DEF (0) +#define PTA_NUMBER_QUIET_CYCLE_DEF (0) +#define PTA_MAX_NUM_CTS_DEF (3) +#define PTA_NUMBER_OF_WLAN_PACKETS_DEF (2) +#define PTA_NUMBER_OF_BT_PACKETS_DEF (2) +#define PTA_PROTECTIVE_RX_TIME_FAST_DEF (1500) +#define PTA_PROTECTIVE_TX_TIME_FAST_DEF (3000) +#define PTA_CYCLE_TIME_FAST_DEF (8700) +#define PTA_RX_FOR_AVALANCHE_DEF (5) +#define PTA_ELP_HP_DEF (0) +#define PTA_ANTI_STARVE_PERIOD_DEF (500) +#define PTA_ANTI_STARVE_NUM_CYCLE_DEF (4) +#define PTA_ALLOW_PA_SD_DEF (1) +#define PTA_TIME_BEFORE_BEACON_DEF (6300) +#define PTA_HPDM_MAX_TIME_DEF (1600) +#define PTA_TIME_OUT_NEXT_WLAN_DEF (2550) +#define PTA_AUTO_MODE_NO_CTS_DEF (0) +#define PTA_BT_HP_RESPECTED_DEF (3) +#define PTA_WLAN_RX_MIN_RATE_DEF (24) +#define PTA_ACK_MODE_DEF (1) + +struct acx_bt_wlan_coex_param { + struct acx_header header; + + /* + * The minimum rate of a received WLAN packet in the STA, + * during protective mode, of which a new BT-HP request + * during this Rx will always be respected and gain the antenna. + */ + u32 min_rate; + + /* Max time the BT HP will be respected. */ + u16 bt_hp_max_time; + + /* Max time the WLAN HP will be respected. */ + u16 wlan_hp_max_time; + + /* + * The time between the last BT activity + * and the moment when the sense mode returns + * to SENSE_INACTIVE. + */ + u16 sense_disable_timer; + + /* Time before the next BT HP instance */ + u16 rx_time_bt_hp; + u16 tx_time_bt_hp; + + /* range: 10-20000 default: 1500 */ + u16 rx_time_bt_hp_fast; + u16 tx_time_bt_hp_fast; + + /* range: 2000-65535 default: 8700 */ + u16 wlan_cycle_fast; + + /* range: 0 - 15000 (Msec) default: 1000 */ + u16 bt_anti_starvation_period; + + /* range 400-10000(Usec) default: 3000 */ + u16 next_bt_lp_packet; + + /* Deafult: worst case for BT DH5 traffic */ + u16 wake_up_beacon; + + /* range: 0-50000(Usec) default: 1050 */ + u16 hp_dm_max_guard_time; + + /* + * This is to prevent both BT & WLAN antenna + * starvation. + * Range: 100-50000(Usec) default:2550 + */ + u16 next_wlan_packet; + + /* 0 -> shared antenna */ + u8 antenna_type; + + /* + * 0 -> TI legacy + * 1 -> Palau + */ + u8 signal_type; + + /* + * BT AFH status + * 0 -> no AFH + * 1 -> from dedicated GPIO + * 2 -> AFH on (from host) + */ + u8 afh_leverage_on; + + /* + * The number of cycles during which no + * TX will be sent after 1 cycle of RX + * transaction in protective mode + */ + u8 quiet_cycle_num; + + /* + * The maximum number of CTSs that will + * be sent for receiving RX packet in + * protective mode + */ + u8 max_cts; + + /* + * The number of WLAN packets + * transferred in common mode before + * switching to BT. + */ + u8 wlan_packets_num; + + /* + * The number of BT packets + * transferred in common mode before + * switching to WLAN. + */ + u8 bt_packets_num; + + /* range: 1-255 default: 5 */ + u8 missed_rx_avalanche; + + /* range: 0-1 default: 1 */ + u8 wlan_elp_hp; + + /* range: 0 - 15 default: 4 */ + u8 bt_anti_starvation_cycles; + + u8 ack_mode_dual_ant; + + /* + * Allow PA_SD assertion/de-assertion + * during enabled BT activity. + */ + u8 pa_sd_enable; + + /* + * Enable/Disable PTA in auto mode: + * Support Both Active & P.S modes + */ + u8 pta_auto_mode_enable; + + /* range: 0 - 20 default: 1 */ + u8 bt_hp_respected_num; +} __attribute__ ((packed)); + +#define CCA_THRSH_ENABLE_ENERGY_D 0x140A +#define CCA_THRSH_DISABLE_ENERGY_D 0xFFEF + +struct acx_energy_detection { + struct acx_header header; + + /* The RX Clear Channel Assessment threshold in the PHY */ + u16 rx_cca_threshold; + u8 tx_energy_detection; + u8 pad; +} __attribute__ ((packed)); + +#define BCN_RX_TIMEOUT_DEF_VALUE 10000 +#define BROADCAST_RX_TIMEOUT_DEF_VALUE 20000 +#define RX_BROADCAST_IN_PS_DEF_VALUE 1 +#define CONSECUTIVE_PS_POLL_FAILURE_DEF 4 + +struct acx_beacon_broadcast { + struct acx_header header; + + u16 beacon_rx_timeout; + u16 broadcast_timeout; + + /* Enables receiving of broadcast packets in PS mode */ + u8 rx_broadcast_in_ps; + + /* Consecutive PS Poll failures before updating the host */ + u8 ps_poll_threshold; + u8 pad[2]; +} __attribute__ ((packed)); + +struct acx_event_mask { + struct acx_header header; + + u32 event_mask; + u32 high_event_mask; /* Unused */ +} __attribute__ ((packed)); + +#define CFG_RX_FCS BIT(2) +#define CFG_RX_ALL_GOOD BIT(3) +#define CFG_UNI_FILTER_EN BIT(4) +#define CFG_BSSID_FILTER_EN BIT(5) +#define CFG_MC_FILTER_EN BIT(6) +#define CFG_MC_ADDR0_EN BIT(7) +#define CFG_MC_ADDR1_EN BIT(8) +#define CFG_BC_REJECT_EN BIT(9) +#define CFG_SSID_FILTER_EN BIT(10) +#define CFG_RX_INT_FCS_ERROR BIT(11) +#define CFG_RX_INT_ENCRYPTED BIT(12) +#define CFG_RX_WR_RX_STATUS BIT(13) +#define CFG_RX_FILTER_NULTI BIT(14) +#define CFG_RX_RESERVE BIT(15) +#define CFG_RX_TIMESTAMP_TSF BIT(16) + +#define CFG_RX_RSV_EN BIT(0) +#define CFG_RX_RCTS_ACK BIT(1) +#define CFG_RX_PRSP_EN BIT(2) +#define CFG_RX_PREQ_EN BIT(3) +#define CFG_RX_MGMT_EN BIT(4) +#define CFG_RX_FCS_ERROR BIT(5) +#define CFG_RX_DATA_EN BIT(6) +#define CFG_RX_CTL_EN BIT(7) +#define CFG_RX_CF_EN BIT(8) +#define CFG_RX_BCN_EN BIT(9) +#define CFG_RX_AUTH_EN BIT(10) +#define CFG_RX_ASSOC_EN BIT(11) + +#define SCAN_PASSIVE BIT(0) +#define SCAN_5GHZ_BAND BIT(1) +#define SCAN_TRIGGERED BIT(2) +#define SCAN_PRIORITY_HIGH BIT(3) + +struct acx_feature_config { + struct acx_header header; + + u32 options; + u32 data_flow_options; +} __attribute__ ((packed)); + +struct acx_current_tx_power { + struct acx_header header; + + u8 current_tx_power; + u8 padding[3]; +} __attribute__ ((packed)); + +enum acx_wake_up_event { + WAKE_UP_EVENT_BEACON_BITMAP = 0x01, /* Wake on every Beacon*/ + WAKE_UP_EVENT_DTIM_BITMAP = 0x02, /* Wake on every DTIM*/ + WAKE_UP_EVENT_N_DTIM_BITMAP = 0x04, /* Wake on every Nth DTIM */ + WAKE_UP_EVENT_N_BEACONS_BITMAP = 0x08, /* Wake on every Nth Beacon */ + WAKE_UP_EVENT_BITS_MASK = 0x0F +}; + +struct acx_wake_up_condition { + struct acx_header header; + + u8 wake_up_event; /* Only one bit can be set */ + u8 listen_interval; + u8 pad[2]; +} __attribute__ ((packed)); + +struct acx_aid { + struct acx_header header; + + /* + * To be set when associated with an AP. + */ + u16 aid; + u8 pad[2]; +} __attribute__ ((packed)); + +enum acx_preamble_type { + ACX_PREAMBLE_LONG = 0, + ACX_PREAMBLE_SHORT = 1 +}; + +struct acx_preamble { + struct acx_header header; + + /* + * When set, the WiLink transmits the frames with a short preamble and + * when cleared, the WiLink transmits the frames with a long preamble. + */ + u8 preamble; + u8 padding[3]; +} __attribute__ ((packed)); + +enum acx_ctsprotect_type { + CTSPROTECT_DISABLE = 0, + CTSPROTECT_ENABLE = 1 +}; + +struct acx_ctsprotect { + struct acx_header header; + u8 ctsprotect; + u8 padding[3]; +} __attribute__ ((packed)); + +struct acx_tx_statistics { + u32 internal_desc_overflow; +} __attribute__ ((packed)); + +struct acx_rx_statistics { + u32 out_of_mem; + u32 hdr_overflow; + u32 hw_stuck; + u32 dropped; + u32 fcs_err; + u32 xfr_hint_trig; + u32 path_reset; + u32 reset_counter; +} __attribute__ ((packed)); + +struct acx_dma_statistics { + u32 rx_requested; + u32 rx_errors; + u32 tx_requested; + u32 tx_errors; +} __attribute__ ((packed)); + +struct acx_isr_statistics { + /* host command complete */ + u32 cmd_cmplt; + + /* fiqisr() */ + u32 fiqs; + + /* (INT_STS_ND & INT_TRIG_RX_HEADER) */ + u32 rx_headers; + + /* (INT_STS_ND & INT_TRIG_RX_CMPLT) */ + u32 rx_completes; + + /* (INT_STS_ND & INT_TRIG_NO_RX_BUF) */ + u32 rx_mem_overflow; + + /* (INT_STS_ND & INT_TRIG_S_RX_RDY) */ + u32 rx_rdys; + + /* irqisr() */ + u32 irqs; + + /* (INT_STS_ND & INT_TRIG_TX_PROC) */ + u32 tx_procs; + + /* (INT_STS_ND & INT_TRIG_DECRYPT_DONE) */ + u32 decrypt_done; + + /* (INT_STS_ND & INT_TRIG_DMA0) */ + u32 dma0_done; + + /* (INT_STS_ND & INT_TRIG_DMA1) */ + u32 dma1_done; + + /* (INT_STS_ND & INT_TRIG_TX_EXC_CMPLT) */ + u32 tx_exch_complete; + + /* (INT_STS_ND & INT_TRIG_COMMAND) */ + u32 commands; + + /* (INT_STS_ND & INT_TRIG_RX_PROC) */ + u32 rx_procs; + + /* (INT_STS_ND & INT_TRIG_PM_802) */ + u32 hw_pm_mode_changes; + + /* (INT_STS_ND & INT_TRIG_ACKNOWLEDGE) */ + u32 host_acknowledges; + + /* (INT_STS_ND & INT_TRIG_PM_PCI) */ + u32 pci_pm; + + /* (INT_STS_ND & INT_TRIG_ACM_WAKEUP) */ + u32 wakeups; + + /* (INT_STS_ND & INT_TRIG_LOW_RSSI) */ + u32 low_rssi; +} __attribute__ ((packed)); + +struct acx_wep_statistics { + /* WEP address keys configured */ + u32 addr_key_count; + + /* default keys configured */ + u32 default_key_count; + + u32 reserved; + + /* number of times that WEP key not found on lookup */ + u32 key_not_found; + + /* number of times that WEP key decryption failed */ + u32 decrypt_fail; + + /* WEP packets decrypted */ + u32 packets; + + /* WEP decrypt interrupts */ + u32 interrupt; +} __attribute__ ((packed)); + +#define ACX_MISSED_BEACONS_SPREAD 10 + +struct acx_pwr_statistics { + /* the amount of enters into power save mode (both PD & ELP) */ + u32 ps_enter; + + /* the amount of enters into ELP mode */ + u32 elp_enter; + + /* the amount of missing beacon interrupts to the host */ + u32 missing_bcns; + + /* the amount of wake on host-access times */ + u32 wake_on_host; + + /* the amount of wake on timer-expire */ + u32 wake_on_timer_exp; + + /* the number of packets that were transmitted with PS bit set */ + u32 tx_with_ps; + + /* the number of packets that were transmitted with PS bit clear */ + u32 tx_without_ps; + + /* the number of received beacons */ + u32 rcvd_beacons; + + /* the number of entering into PowerOn (power save off) */ + u32 power_save_off; + + /* the number of entries into power save mode */ + u16 enable_ps; + + /* + * the number of exits from power save, not including failed PS + * transitions + */ + u16 disable_ps; + + /* + * the number of times the TSF counter was adjusted because + * of drift + */ + u32 fix_tsf_ps; + + /* Gives statistics about the spread continuous missed beacons. + * The 16 LSB are dedicated for the PS mode. + * The 16 MSB are dedicated for the PS mode. + * cont_miss_bcns_spread[0] - single missed beacon. + * cont_miss_bcns_spread[1] - two continuous missed beacons. + * cont_miss_bcns_spread[2] - three continuous missed beacons. + * ... + * cont_miss_bcns_spread[9] - ten and more continuous missed beacons. + */ + u32 cont_miss_bcns_spread[ACX_MISSED_BEACONS_SPREAD]; + + /* the number of beacons in awake mode */ + u32 rcvd_awake_beacons; +} __attribute__ ((packed)); + +struct acx_mic_statistics { + u32 rx_pkts; + u32 calc_failure; +} __attribute__ ((packed)); + +struct acx_aes_statistics { + u32 encrypt_fail; + u32 decrypt_fail; + u32 encrypt_packets; + u32 decrypt_packets; + u32 encrypt_interrupt; + u32 decrypt_interrupt; +} __attribute__ ((packed)); + +struct acx_event_statistics { + u32 heart_beat; + u32 calibration; + u32 rx_mismatch; + u32 rx_mem_empty; + u32 rx_pool; + u32 oom_late; + u32 phy_transmit_error; + u32 tx_stuck; +} __attribute__ ((packed)); + +struct acx_ps_statistics { + u32 pspoll_timeouts; + u32 upsd_timeouts; + u32 upsd_max_sptime; + u32 upsd_max_apturn; + u32 pspoll_max_apturn; + u32 pspoll_utilization; + u32 upsd_utilization; +} __attribute__ ((packed)); + +struct acx_rxpipe_statistics { + u32 rx_prep_beacon_drop; + u32 descr_host_int_trig_rx_data; + u32 beacon_buffer_thres_host_int_trig_rx_data; + u32 missed_beacon_host_int_trig_rx_data; + u32 tx_xfr_host_int_trig_rx_data; +} __attribute__ ((packed)); + +struct acx_statistics { + struct acx_header header; + + struct acx_tx_statistics tx; + struct acx_rx_statistics rx; + struct acx_dma_statistics dma; + struct acx_isr_statistics isr; + struct acx_wep_statistics wep; + struct acx_pwr_statistics pwr; + struct acx_aes_statistics aes; + struct acx_mic_statistics mic; + struct acx_event_statistics event; + struct acx_ps_statistics ps; + struct acx_rxpipe_statistics rxpipe; +} __attribute__ ((packed)); + +#define ACX_MAX_RATE_CLASSES 8 +#define ACX_RATE_MASK_UNSPECIFIED 0 +#define ACX_RATE_MASK_ALL 0x1eff +#define ACX_RATE_RETRY_LIMIT 10 + +struct acx_rate_class { + u32 enabled_rates; + u8 short_retry_limit; + u8 long_retry_limit; + u8 aflags; + u8 reserved; +}; + +struct acx_rate_policy { + struct acx_header header; + + u32 rate_class_cnt; + struct acx_rate_class rate_class[ACX_MAX_RATE_CLASSES]; +} __attribute__ ((packed)); + +#define WL1271_ACX_AC_COUNT 4 + +struct acx_ac_cfg { + struct acx_header header; + u8 ac; + u8 cw_min; + u16 cw_max; + u8 aifsn; + u8 reserved; + u16 tx_op_limit; +} __attribute__ ((packed)); + +enum wl1271_acx_ac { + WL1271_ACX_AC_BE = 0, + WL1271_ACX_AC_BK = 1, + WL1271_ACX_AC_VI = 2, + WL1271_ACX_AC_VO = 3, + WL1271_ACX_AC_CTS2SELF = 4, + WL1271_ACX_AC_ANY_TID = 0x1F, + WL1271_ACX_AC_INVALID = 0xFF, +}; + +enum wl1271_acx_ps_scheme { + WL1271_ACX_PS_SCHEME_LEGACY = 0, + WL1271_ACX_PS_SCHEME_UPSD_TRIGGER = 1, + WL1271_ACX_PS_SCHEME_LEGACY_PSPOLL = 2, + WL1271_ACX_PS_SCHEME_SAPSD = 3, +}; + +enum wl1271_acx_ack_policy { + WL1271_ACX_ACK_POLICY_LEGACY = 0, + WL1271_ACX_ACK_POLICY_NO_ACK = 1, + WL1271_ACX_ACK_POLICY_BLOCK = 2, +}; + +#define WL1271_ACX_TID_COUNT 7 + +struct acx_tid_config { + struct acx_header header; + u8 queue_id; + u8 channel_type; + u8 tsid; + u8 ps_scheme; + u8 ack_policy; + u8 padding[3]; + u32 apsd_conf[2]; +} __attribute__ ((packed)); + +struct acx_frag_threshold { + struct acx_header header; + u16 frag_threshold; + u8 padding[2]; +} __attribute__ ((packed)); + +#define WL1271_ACX_TX_COMPL_TIMEOUT 5 +#define WL1271_ACX_TX_COMPL_THRESHOLD 5 + +struct acx_tx_config_options { + struct acx_header header; + u16 tx_compl_timeout; /* msec */ + u16 tx_compl_threshold; /* number of packets */ +} __attribute__ ((packed)); + +#define ACX_RX_MEM_BLOCKS 64 +#define ACX_TX_MIN_MEM_BLOCKS 64 +#define ACX_TX_DESCRIPTORS 32 +#define ACX_NUM_SSID_PROFILES 1 + +struct wl1271_acx_config_memory { + struct acx_header header; + + u8 rx_mem_block_num; + u8 tx_min_mem_block_num; + u8 num_stations; + u8 num_ssid_profiles; + u32 total_tx_descriptors; +} __attribute__ ((packed)); + +struct wl1271_acx_mem_map { + struct acx_header header; + + void *code_start; + void *code_end; + + void *wep_defkey_start; + void *wep_defkey_end; + + void *sta_table_start; + void *sta_table_end; + + void *packet_template_start; + void *packet_template_end; + + /* Address of the TX result interface (control block) */ + u32 tx_result; + u32 tx_result_queue_start; + + void *queue_memory_start; + void *queue_memory_end; + + u32 packet_memory_pool_start; + u32 packet_memory_pool_end; + + void *debug_buffer1_start; + void *debug_buffer1_end; + + void *debug_buffer2_start; + void *debug_buffer2_end; + + /* Number of blocks FW allocated for TX packets */ + u32 num_tx_mem_blocks; + + /* Number of blocks FW allocated for RX packets */ + u32 num_rx_mem_blocks; + + /* the following 4 fields are valid in SLAVE mode only */ + u8 *tx_cbuf; + u8 *rx_cbuf; + void *rx_ctrl; + void *tx_ctrl; +} __attribute__ ((packed)); + +enum wl1271_acx_rx_queue_type { + RX_QUEUE_TYPE_RX_LOW_PRIORITY, /* All except the high priority */ + RX_QUEUE_TYPE_RX_HIGH_PRIORITY, /* Management and voice packets */ + RX_QUEUE_TYPE_NUM, + RX_QUEUE_TYPE_MAX = USHORT_MAX +}; + +#define WL1271_RX_INTR_THRESHOLD_DEF 0 /* no pacing, send interrupt on + * every event */ +#define WL1271_RX_INTR_THRESHOLD_MIN 0 +#define WL1271_RX_INTR_THRESHOLD_MAX 15 + +#define WL1271_RX_INTR_TIMEOUT_DEF 5 +#define WL1271_RX_INTR_TIMEOUT_MIN 1 +#define WL1271_RX_INTR_TIMEOUT_MAX 100 + +struct wl1271_acx_rx_config_opt { + struct acx_header header; + + u16 mblk_threshold; + u16 threshold; + u16 timeout; + u8 queue_type; + u8 reserved; +} __attribute__ ((packed)); + +enum { + ACX_WAKE_UP_CONDITIONS = 0x0002, + ACX_MEM_CFG = 0x0003, + ACX_SLOT = 0x0004, + ACX_AC_CFG = 0x0007, + ACX_MEM_MAP = 0x0008, + ACX_AID = 0x000A, + /* ACX_FW_REV is missing in the ref driver, but seems to work */ + ACX_FW_REV = 0x000D, + ACX_MEDIUM_USAGE = 0x000F, + ACX_RX_CFG = 0x0010, + ACX_TX_QUEUE_CFG = 0x0011, /* FIXME: only used by wl1251 */ + ACX_STATISTICS = 0x0013, /* Debug API */ + ACX_PWR_CONSUMPTION_STATISTICS = 0x0014, + ACX_FEATURE_CFG = 0x0015, + ACX_TID_CFG = 0x001A, + ACX_PS_RX_STREAMING = 0x001B, + ACX_BEACON_FILTER_OPT = 0x001F, + ACX_NOISE_HIST = 0x0021, + ACX_HDK_VERSION = 0x0022, /* ??? */ + ACX_PD_THRESHOLD = 0x0023, + ACX_TX_CONFIG_OPT = 0x0024, + ACX_CCA_THRESHOLD = 0x0025, + ACX_EVENT_MBOX_MASK = 0x0026, + ACX_CONN_MONIT_PARAMS = 0x002D, + ACX_CONS_TX_FAILURE = 0x002F, + ACX_BCN_DTIM_OPTIONS = 0x0031, + ACX_SG_ENABLE = 0x0032, + ACX_SG_CFG = 0x0033, + ACX_BEACON_FILTER_TABLE = 0x0038, + ACX_ARP_IP_FILTER = 0x0039, + ACX_ROAMING_STATISTICS_TBL = 0x003B, + ACX_RATE_POLICY = 0x003D, + ACX_CTS_PROTECTION = 0x003E, + ACX_SLEEP_AUTH = 0x003F, + ACX_PREAMBLE_TYPE = 0x0040, + ACX_ERROR_CNT = 0x0041, + ACX_IBSS_FILTER = 0x0044, + ACX_SERVICE_PERIOD_TIMEOUT = 0x0045, + ACX_TSF_INFO = 0x0046, + ACX_CONFIG_PS_WMM = 0x0049, + ACX_ENABLE_RX_DATA_FILTER = 0x004A, + ACX_SET_RX_DATA_FILTER = 0x004B, + ACX_GET_DATA_FILTER_STATISTICS = 0x004C, + ACX_RX_CONFIG_OPT = 0x004E, + ACX_FRAG_CFG = 0x004F, + ACX_BET_ENABLE = 0x0050, + ACX_RSSI_SNR_TRIGGER = 0x0051, + ACX_RSSI_SNR_WEIGHTS = 0x0051, + ACX_KEEP_ALIVE_MODE = 0x0052, + ACX_SET_KEEP_ALIVE_CONFIG = 0x0054, + ACX_BA_SESSION_RESPONDER_POLICY = 0x0055, + ACX_BA_SESSION_INITIATOR_POLICY = 0x0056, + ACX_PEER_HT_CAP = 0x0057, + ACX_HT_BSS_OPERATION = 0x0058, + ACX_COEX_ACTIVITY = 0x0059, + DOT11_RX_MSDU_LIFE_TIME = 0x1004, + DOT11_CUR_TX_PWR = 0x100D, + DOT11_RX_DOT11_MODE = 0x1012, + DOT11_RTS_THRESHOLD = 0x1013, + DOT11_GROUP_ADDRESS_TBL = 0x1014, + + MAX_DOT11_IE = DOT11_GROUP_ADDRESS_TBL, + + MAX_IE = 0xFFFF +}; + + +int wl1271_acx_wake_up_conditions(struct wl1271 *wl, u8 wake_up_event, + u8 listen_interval); +int wl1271_acx_sleep_auth(struct wl1271 *wl, u8 sleep_auth); +int wl1271_acx_fw_version(struct wl1271 *wl, char *buf, size_t len); +int wl1271_acx_tx_power(struct wl1271 *wl, int power); +int wl1271_acx_feature_cfg(struct wl1271 *wl); +int wl1271_acx_mem_map(struct wl1271 *wl, + struct acx_header *mem_map, size_t len); +int wl1271_acx_rx_msdu_life_time(struct wl1271 *wl, u32 life_time); +int wl1271_acx_rx_config(struct wl1271 *wl, u32 config, u32 filter); +int wl1271_acx_pd_threshold(struct wl1271 *wl); +int wl1271_acx_slot(struct wl1271 *wl, enum acx_slot_type slot_time); +int wl1271_acx_group_address_tbl(struct wl1271 *wl); +int wl1271_acx_service_period_timeout(struct wl1271 *wl); +int wl1271_acx_rts_threshold(struct wl1271 *wl, u16 rts_threshold); +int wl1271_acx_beacon_filter_opt(struct wl1271 *wl); +int wl1271_acx_beacon_filter_table(struct wl1271 *wl); +int wl1271_acx_sg_enable(struct wl1271 *wl); +int wl1271_acx_sg_cfg(struct wl1271 *wl); +int wl1271_acx_cca_threshold(struct wl1271 *wl); +int wl1271_acx_bcn_dtim_options(struct wl1271 *wl); +int wl1271_acx_aid(struct wl1271 *wl, u16 aid); +int wl1271_acx_event_mbox_mask(struct wl1271 *wl, u32 event_mask); +int wl1271_acx_set_preamble(struct wl1271 *wl, enum acx_preamble_type preamble); +int wl1271_acx_cts_protect(struct wl1271 *wl, + enum acx_ctsprotect_type ctsprotect); +int wl1271_acx_statistics(struct wl1271 *wl, struct acx_statistics *stats); +int wl1271_acx_rate_policies(struct wl1271 *wl); +int wl1271_acx_ac_cfg(struct wl1271 *wl); +int wl1271_acx_tid_cfg(struct wl1271 *wl); +int wl1271_acx_frag_threshold(struct wl1271 *wl); +int wl1271_acx_tx_config_options(struct wl1271 *wl); +int wl1271_acx_mem_cfg(struct wl1271 *wl); +int wl1271_acx_init_mem_config(struct wl1271 *wl); +int wl1271_acx_init_rx_interrupt(struct wl1271 *wl); + +#endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c new file mode 100644 index 000000000000..8228ef474a7e --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_boot.c @@ -0,0 +1,541 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2008-2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/gpio.h> + +#include "wl1271_acx.h" +#include "wl1271_reg.h" +#include "wl1271_boot.h" +#include "wl1271_spi.h" +#include "wl1271_event.h" + +static struct wl1271_partition_set part_table[PART_TABLE_LEN] = { + [PART_DOWN] = { + .mem = { + .start = 0x00000000, + .size = 0x000177c0 + }, + .reg = { + .start = REGISTERS_BASE, + .size = 0x00008800 + }, + }, + + [PART_WORK] = { + .mem = { + .start = 0x00040000, + .size = 0x00014fc0 + }, + .reg = { + .start = REGISTERS_BASE, + .size = 0x0000b000 + }, + }, + + [PART_DRPW] = { + .mem = { + .start = 0x00040000, + .size = 0x00014fc0 + }, + .reg = { + .start = DRPW_BASE, + .size = 0x00006000 + } + } +}; + +static void wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) +{ + u32 cpu_ctrl; + + /* 10.5.0 run the firmware (I) */ + cpu_ctrl = wl1271_reg_read32(wl, ACX_REG_ECPU_CONTROL); + + /* 10.5.1 run the firmware (II) */ + cpu_ctrl |= flag; + wl1271_reg_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); +} + +static void wl1271_boot_fw_version(struct wl1271 *wl) +{ + struct wl1271_static_data static_data; + + wl1271_spi_mem_read(wl, wl->cmd_box_addr, + &static_data, sizeof(static_data)); + + strncpy(wl->chip.fw_ver, static_data.fw_version, + sizeof(wl->chip.fw_ver)); + + /* make sure the string is NULL-terminated */ + wl->chip.fw_ver[sizeof(wl->chip.fw_ver) - 1] = '\0'; +} + +static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, + size_t fw_data_len, u32 dest) +{ + int addr, chunk_num, partition_limit; + u8 *p; + + /* whal_FwCtrl_LoadFwImageSm() */ + + wl1271_debug(DEBUG_BOOT, "starting firmware upload"); + + wl1271_debug(DEBUG_BOOT, "fw_data_len %zd chunk_size %d", + fw_data_len, CHUNK_SIZE); + + + if ((fw_data_len % 4) != 0) { + wl1271_error("firmware length not multiple of four"); + return -EIO; + } + + wl1271_set_partition(wl, dest, + part_table[PART_DOWN].mem.size, + part_table[PART_DOWN].reg.start, + part_table[PART_DOWN].reg.size); + + /* 10.1 set partition limit and chunk num */ + chunk_num = 0; + partition_limit = part_table[PART_DOWN].mem.size; + + while (chunk_num < fw_data_len / CHUNK_SIZE) { + /* 10.2 update partition, if needed */ + addr = dest + (chunk_num + 2) * CHUNK_SIZE; + if (addr > partition_limit) { + addr = dest + chunk_num * CHUNK_SIZE; + partition_limit = chunk_num * CHUNK_SIZE + + part_table[PART_DOWN].mem.size; + + /* FIXME: Over 80 chars! */ + wl1271_set_partition(wl, + addr, + part_table[PART_DOWN].mem.size, + part_table[PART_DOWN].reg.start, + part_table[PART_DOWN].reg.size); + } + + /* 10.3 upload the chunk */ + addr = dest + chunk_num * CHUNK_SIZE; + p = buf + chunk_num * CHUNK_SIZE; + wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", + p, addr); + wl1271_spi_mem_write(wl, addr, p, CHUNK_SIZE); + + chunk_num++; + } + + /* 10.4 upload the last chunk */ + addr = dest + chunk_num * CHUNK_SIZE; + p = buf + chunk_num * CHUNK_SIZE; + wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%zd B) 0x%p to 0x%x", + fw_data_len % CHUNK_SIZE, p, addr); + wl1271_spi_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE); + + return 0; +} + +static int wl1271_boot_upload_firmware(struct wl1271 *wl) +{ + u32 chunks, addr, len; + u8 *fw; + + fw = wl->fw; + chunks = be32_to_cpup((u32 *) fw); + fw += sizeof(u32); + + wl1271_debug(DEBUG_BOOT, "firmware chunks to be uploaded: %u", chunks); + + while (chunks--) { + addr = be32_to_cpup((u32 *) fw); + fw += sizeof(u32); + len = be32_to_cpup((u32 *) fw); + fw += sizeof(u32); + + if (len > 300000) { + wl1271_info("firmware chunk too long: %u", len); + return -EINVAL; + } + wl1271_debug(DEBUG_BOOT, "chunk %d addr 0x%x len %u", + chunks, addr, len); + wl1271_boot_upload_firmware_chunk(wl, fw, len, addr); + fw += len; + } + + return 0; +} + +static int wl1271_boot_upload_nvs(struct wl1271 *wl) +{ + size_t nvs_len, burst_len; + int i; + u32 dest_addr, val; + u8 *nvs_ptr, *nvs, *nvs_aligned; + + nvs = wl->nvs; + if (nvs == NULL) + return -ENODEV; + + nvs_ptr = nvs; + + nvs_len = wl->nvs_len; + + /* Update the device MAC address into the nvs */ + nvs[11] = wl->mac_addr[0]; + nvs[10] = wl->mac_addr[1]; + nvs[6] = wl->mac_addr[2]; + nvs[5] = wl->mac_addr[3]; + nvs[4] = wl->mac_addr[4]; + nvs[3] = wl->mac_addr[5]; + + /* + * Layout before the actual NVS tables: + * 1 byte : burst length. + * 2 bytes: destination address. + * n bytes: data to burst copy. + * + * This is ended by a 0 length, then the NVS tables. + */ + + /* FIXME: Do we need to check here whether the LSB is 1? */ + while (nvs_ptr[0]) { + burst_len = nvs_ptr[0]; + dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8)); + + /* FIXME: Due to our new wl1271_translate_reg_addr function, + we need to add the REGISTER_BASE to the destination */ + dest_addr += REGISTERS_BASE; + + /* We move our pointer to the data */ + nvs_ptr += 3; + + for (i = 0; i < burst_len; i++) { + val = (nvs_ptr[0] | (nvs_ptr[1] << 8) + | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); + + wl1271_debug(DEBUG_BOOT, + "nvs burst write 0x%x: 0x%x", + dest_addr, val); + wl1271_reg_write32(wl, dest_addr, val); + + nvs_ptr += 4; + dest_addr += 4; + } + } + + /* + * We've reached the first zero length, the first NVS table + * is 7 bytes further. + */ + nvs_ptr += 7; + nvs_len -= nvs_ptr - nvs; + nvs_len = ALIGN(nvs_len, 4); + + /* FIXME: The driver sets the partition here, but this is not needed, + since it sets to the same one as currently in use */ + /* Now we must set the partition correctly */ + wl1271_set_partition(wl, + part_table[PART_WORK].mem.start, + part_table[PART_WORK].mem.size, + part_table[PART_WORK].reg.start, + part_table[PART_WORK].reg.size); + + /* Copy the NVS tables to a new block to ensure alignment */ + nvs_aligned = kmemdup(nvs_ptr, nvs_len, GFP_KERNEL); + + /* And finally we upload the NVS tables */ + /* FIXME: In wl1271, we upload everything at once. + No endianness handling needed here?! The ref driver doesn't do + anything about it at this point */ + wl1271_spi_mem_write(wl, CMD_MBOX_ADDRESS, nvs_aligned, nvs_len); + + kfree(nvs_aligned); + return 0; +} + +static void wl1271_boot_enable_interrupts(struct wl1271 *wl) +{ + enable_irq(wl->irq); + wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, + WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); + wl1271_reg_write32(wl, HI_CFG, HI_CFG_DEF_VAL); +} + +static int wl1271_boot_soft_reset(struct wl1271 *wl) +{ + unsigned long timeout; + u32 boot_data; + + /* perform soft reset */ + wl1271_reg_write32(wl, ACX_REG_SLV_SOFT_RESET, ACX_SLV_SOFT_RESET_BIT); + + /* SOFT_RESET is self clearing */ + timeout = jiffies + usecs_to_jiffies(SOFT_RESET_MAX_TIME); + while (1) { + boot_data = wl1271_reg_read32(wl, ACX_REG_SLV_SOFT_RESET); + wl1271_debug(DEBUG_BOOT, "soft reset bootdata 0x%x", boot_data); + if ((boot_data & ACX_SLV_SOFT_RESET_BIT) == 0) + break; + + if (time_after(jiffies, timeout)) { + /* 1.2 check pWhalBus->uSelfClearTime if the + * timeout was reached */ + wl1271_error("soft reset timeout"); + return -1; + } + + udelay(SOFT_RESET_STALL_TIME); + } + + /* disable Rx/Tx */ + wl1271_reg_write32(wl, ENABLE, 0x0); + + /* disable auto calibration on start*/ + wl1271_reg_write32(wl, SPARE_A2, 0xffff); + + return 0; +} + +static int wl1271_boot_run_firmware(struct wl1271 *wl) +{ + int loop, ret; + u32 chip_id, interrupt; + + wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); + + chip_id = wl1271_reg_read32(wl, CHIP_ID_B); + + wl1271_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); + + if (chip_id != wl->chip.id) { + wl1271_error("chip id doesn't match after firmware boot"); + return -EIO; + } + + /* wait for init to complete */ + loop = 0; + while (loop++ < INIT_LOOP) { + udelay(INIT_LOOP_DELAY); + interrupt = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + + if (interrupt == 0xffffffff) { + wl1271_error("error reading hardware complete " + "init indication"); + return -EIO; + } + /* check that ACX_INTR_INIT_COMPLETE is enabled */ + else if (interrupt & WL1271_ACX_INTR_INIT_COMPLETE) { + wl1271_reg_write32(wl, ACX_REG_INTERRUPT_ACK, + WL1271_ACX_INTR_INIT_COMPLETE); + break; + } + } + + if (loop >= INIT_LOOP) { + wl1271_error("timeout waiting for the hardware to " + "complete initialization"); + return -EIO; + } + + /* get hardware config command mail box */ + wl->cmd_box_addr = wl1271_reg_read32(wl, REG_COMMAND_MAILBOX_PTR); + + /* get hardware config event mail box */ + wl->event_box_addr = wl1271_reg_read32(wl, REG_EVENT_MAILBOX_PTR); + + /* set the working partition to its "running" mode offset */ + wl1271_set_partition(wl, + part_table[PART_WORK].mem.start, + part_table[PART_WORK].mem.size, + part_table[PART_WORK].reg.start, + part_table[PART_WORK].reg.size); + + wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x", + wl->cmd_box_addr, wl->event_box_addr); + + wl1271_boot_fw_version(wl); + + /* + * in case of full asynchronous mode the firmware event must be + * ready to receive event from the command mailbox + */ + + /* enable gpio interrupts */ + wl1271_boot_enable_interrupts(wl); + + /* unmask all mbox events */ + wl->event_mask = 0xffffffff; + + ret = wl1271_event_unmask(wl); + if (ret < 0) { + wl1271_error("EVENT mask setting failed"); + return ret; + } + + wl1271_event_mbox_config(wl); + + /* firmware startup completed */ + return 0; +} + +static int wl1271_boot_write_irq_polarity(struct wl1271 *wl) +{ + u32 polarity, status, i; + + wl1271_reg_write32(wl, OCP_POR_CTR, OCP_REG_POLARITY); + wl1271_reg_write32(wl, OCP_CMD, OCP_CMD_READ); + + /* Wait until the command is complete (ie. bit 18 is set) */ + for (i = 0; i < OCP_CMD_LOOP; i++) { + polarity = wl1271_reg_read32(wl, OCP_DATA_READ); + if (polarity & OCP_READY_MASK) + break; + } + if (i == OCP_CMD_LOOP) { + wl1271_error("OCP command timeout!"); + return -EIO; + } + + status = polarity & OCP_STATUS_MASK; + if (status != OCP_STATUS_OK) { + wl1271_error("OCP command failed (%d)", status); + return -EIO; + } + + /* We use HIGH polarity, so unset the LOW bit */ + polarity &= ~POLARITY_LOW; + + wl1271_reg_write32(wl, OCP_POR_CTR, OCP_REG_POLARITY); + wl1271_reg_write32(wl, OCP_DATA_WRITE, polarity); + wl1271_reg_write32(wl, OCP_CMD, OCP_CMD_WRITE); + + return 0; +} + +int wl1271_boot(struct wl1271 *wl) +{ + int ret = 0; + u32 tmp, clk, pause; + + if (REF_CLOCK == 0 || REF_CLOCK == 2) + /* ref clk: 19.2/38.4 */ + clk = 0x3; + else if (REF_CLOCK == 1 || REF_CLOCK == 3) + /* ref clk: 26/52 */ + clk = 0x5; + + wl1271_reg_write32(wl, PLL_PARAMETERS, clk); + + pause = wl1271_reg_read32(wl, PLL_PARAMETERS); + + wl1271_debug(DEBUG_BOOT, "pause1 0x%x", pause); + + pause &= ~(WU_COUNTER_PAUSE_VAL); /* FIXME: This should probably be + * WU_COUNTER_PAUSE_VAL instead of + * 0x3ff (magic number ). How does + * this work?! */ + pause |= WU_COUNTER_PAUSE_VAL; + wl1271_reg_write32(wl, WU_COUNTER_PAUSE, pause); + + /* Continue the ELP wake up sequence */ + wl1271_reg_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); + udelay(500); + + wl1271_set_partition(wl, + part_table[PART_DRPW].mem.start, + part_table[PART_DRPW].mem.size, + part_table[PART_DRPW].reg.start, + part_table[PART_DRPW].reg.size); + + /* Read-modify-write DRPW_SCRATCH_START register (see next state) + to be used by DRPw FW. The RTRIM value will be added by the FW + before taking DRPw out of reset */ + + wl1271_debug(DEBUG_BOOT, "DRPW_SCRATCH_START %08x", DRPW_SCRATCH_START); + clk = wl1271_reg_read32(wl, DRPW_SCRATCH_START); + + wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); + + /* 2 */ + clk |= (REF_CLOCK << 1) << 4; + wl1271_reg_write32(wl, DRPW_SCRATCH_START, clk); + + wl1271_set_partition(wl, + part_table[PART_WORK].mem.start, + part_table[PART_WORK].mem.size, + part_table[PART_WORK].reg.start, + part_table[PART_WORK].reg.size); + + /* Disable interrupts */ + wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + + ret = wl1271_boot_soft_reset(wl); + if (ret < 0) + goto out; + + /* 2. start processing NVS file */ + ret = wl1271_boot_upload_nvs(wl); + if (ret < 0) + goto out; + + /* write firmware's last address (ie. it's length) to + * ACX_EEPROMLESS_IND_REG */ + wl1271_debug(DEBUG_BOOT, "ACX_EEPROMLESS_IND_REG"); + + wl1271_reg_write32(wl, ACX_EEPROMLESS_IND_REG, ACX_EEPROMLESS_IND_REG); + + tmp = wl1271_reg_read32(wl, CHIP_ID_B); + + wl1271_debug(DEBUG_BOOT, "chip id 0x%x", tmp); + + /* 6. read the EEPROM parameters */ + tmp = wl1271_reg_read32(wl, SCR_PAD2); + + ret = wl1271_boot_write_irq_polarity(wl); + if (ret < 0) + goto out; + + /* FIXME: Need to check whether this is really what we want */ + wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, + WL1271_ACX_ALL_EVENTS_VECTOR); + + /* WL1271: The reference driver skips steps 7 to 10 (jumps directly + * to upload_fw) */ + + ret = wl1271_boot_upload_firmware(wl); + if (ret < 0) + goto out; + + /* 10.5 start firmware */ + ret = wl1271_boot_run_firmware(wl); + if (ret < 0) + goto out; + + /* set the wl1271 default filters */ + wl->rx_config = WL1271_DEFAULT_RX_CONFIG; + wl->rx_filter = WL1271_DEFAULT_RX_FILTER; + + wl1271_event_mbox_config(wl); + +out: + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.h b/drivers/net/wireless/wl12xx/wl1271_boot.h new file mode 100644 index 000000000000..b0d8fb46a439 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_boot.h @@ -0,0 +1,72 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2008-2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __BOOT_H__ +#define __BOOT_H__ + +#include "wl1271.h" + +int wl1271_boot(struct wl1271 *wl); + +#define WL1271_NO_SUBBANDS 8 +#define WL1271_NO_POWER_LEVELS 4 +#define WL1271_FW_VERSION_MAX_LEN 20 + +struct wl1271_static_data { + u8 mac_address[ETH_ALEN]; + u8 padding[2]; + u8 fw_version[WL1271_FW_VERSION_MAX_LEN]; + u32 hw_version; + u8 tx_power_table[WL1271_NO_SUBBANDS][WL1271_NO_POWER_LEVELS]; +}; + +/* number of times we try to read the INIT interrupt */ +#define INIT_LOOP 20000 + +/* delay between retries */ +#define INIT_LOOP_DELAY 50 + +#define REF_CLOCK 2 +#define WU_COUNTER_PAUSE_VAL 0x3FF +#define WELP_ARM_COMMAND_VAL 0x4 + +#define OCP_CMD_LOOP 32 + +#define OCP_CMD_WRITE 0x1 +#define OCP_CMD_READ 0x2 + +#define OCP_READY_MASK BIT(18) +#define OCP_STATUS_MASK (BIT(16) | BIT(17)) + +#define OCP_STATUS_NO_RESP 0x00000 +#define OCP_STATUS_OK 0x10000 +#define OCP_STATUS_REQ_FAILED 0x20000 +#define OCP_STATUS_RESP_ERROR 0x30000 + +#define OCP_REG_POLARITY 0x30032 + +#define CMD_MBOX_ADDRESS 0x407B4 + +#define POLARITY_LOW BIT(1) + +#endif diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c new file mode 100644 index 000000000000..2a4351ff54dc --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -0,0 +1,813 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/crc7.h> +#include <linux/spi/spi.h> +#include <linux/etherdevice.h> + +#include "wl1271.h" +#include "wl1271_reg.h" +#include "wl1271_spi.h" +#include "wl1271_acx.h" +#include "wl12xx_80211.h" +#include "wl1271_cmd.h" + +/* + * send command to firmware + * + * @wl: wl struct + * @id: command id + * @buf: buffer containing the command, must work with dma + * @len: length of the buffer + */ +int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len) +{ + struct wl1271_cmd_header *cmd; + unsigned long timeout; + u32 intr; + int ret = 0; + + cmd = buf; + cmd->id = id; + cmd->status = 0; + + WARN_ON(len % 4 != 0); + + wl1271_spi_mem_write(wl, wl->cmd_box_addr, buf, len); + + wl1271_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_CMD); + + timeout = jiffies + msecs_to_jiffies(WL1271_COMMAND_TIMEOUT); + + intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + while (!(intr & WL1271_ACX_INTR_CMD_COMPLETE)) { + if (time_after(jiffies, timeout)) { + wl1271_error("command complete timeout"); + ret = -ETIMEDOUT; + goto out; + } + + msleep(1); + + intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_NO_CLEAR); + } + + wl1271_reg_write32(wl, ACX_REG_INTERRUPT_ACK, + WL1271_ACX_INTR_CMD_COMPLETE); + +out: + return ret; +} + +int wl1271_cmd_cal_channel_tune(struct wl1271 *wl) +{ + struct wl1271_cmd_cal_channel_tune *cmd; + int ret = 0; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->test.id = TEST_CMD_CHANNEL_TUNE; + + cmd->band = WL1271_CHANNEL_TUNE_BAND_2_4; + /* set up any channel, 7 is in the middle of the range */ + cmd->channel = 7; + + ret = wl1271_cmd_test(wl, cmd, sizeof(*cmd), 0); + if (ret < 0) + wl1271_warning("TEST_CMD_CHANNEL_TUNE failed"); + + kfree(cmd); + return ret; +} + +int wl1271_cmd_cal_update_ref_point(struct wl1271 *wl) +{ + struct wl1271_cmd_cal_update_ref_point *cmd; + int ret = 0; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->test.id = TEST_CMD_UPDATE_PD_REFERENCE_POINT; + + /* FIXME: still waiting for the correct values */ + cmd->ref_power = 0; + cmd->ref_detector = 0; + + cmd->sub_band = WL1271_PD_REFERENCE_POINT_BAND_B_G; + + ret = wl1271_cmd_test(wl, cmd, sizeof(*cmd), 0); + if (ret < 0) + wl1271_warning("TEST_CMD_UPDATE_PD_REFERENCE_POINT failed"); + + kfree(cmd); + return ret; +} + +int wl1271_cmd_cal_p2g(struct wl1271 *wl) +{ + struct wl1271_cmd_cal_p2g *cmd; + int ret = 0; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->test.id = TEST_CMD_P2G_CAL; + + cmd->sub_band_mask = WL1271_CAL_P2G_BAND_B_G; + + ret = wl1271_cmd_test(wl, cmd, sizeof(*cmd), 0); + if (ret < 0) + wl1271_warning("TEST_CMD_P2G_CAL failed"); + + kfree(cmd); + return ret; +} + +int wl1271_cmd_cal(struct wl1271 *wl) +{ + /* + * FIXME: we must make sure that we're not sleeping when calibration + * is done + */ + int ret; + + wl1271_notice("performing tx calibration"); + + ret = wl1271_cmd_cal_channel_tune(wl); + if (ret < 0) + return ret; + + ret = wl1271_cmd_cal_update_ref_point(wl); + if (ret < 0) + return ret; + + ret = wl1271_cmd_cal_p2g(wl); + if (ret < 0) + return ret; + + return ret; +} + +int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval, + u16 beacon_interval, u8 wait) +{ + static bool do_cal = true; + unsigned long timeout; + struct wl1271_cmd_join *join; + int ret, i; + u8 *bssid; + + /* FIXME: remove when we get calibration from the factory */ + if (do_cal) { + ret = wl1271_cmd_cal(wl); + if (ret < 0) + wl1271_warning("couldn't calibrate"); + else + do_cal = false; + } + + + join = kzalloc(sizeof(*join), GFP_KERNEL); + if (!join) { + ret = -ENOMEM; + goto out; + } + + wl1271_debug(DEBUG_CMD, "cmd join"); + + /* Reverse order BSSID */ + bssid = (u8 *) &join->bssid_lsb; + for (i = 0; i < ETH_ALEN; i++) + bssid[i] = wl->bssid[ETH_ALEN - i - 1]; + + join->rx_config_options = wl->rx_config; + join->rx_filter_options = wl->rx_filter; + + join->basic_rate_set = RATE_MASK_1MBPS | RATE_MASK_2MBPS | + RATE_MASK_5_5MBPS | RATE_MASK_11MBPS; + + join->beacon_interval = beacon_interval; + join->dtim_interval = dtim_interval; + join->bss_type = bss_type; + join->channel = wl->channel; + join->ssid_len = wl->ssid_len; + memcpy(join->ssid, wl->ssid, wl->ssid_len); + join->ctrl = WL1271_JOIN_CMD_CTRL_TX_FLUSH; + + /* increment the session counter */ + wl->session_counter++; + if (wl->session_counter >= SESSION_COUNTER_MAX) + wl->session_counter = 0; + + join->ctrl |= wl->session_counter << WL1271_JOIN_CMD_TX_SESSION_OFFSET; + + + ret = wl1271_cmd_send(wl, CMD_START_JOIN, join, sizeof(*join)); + if (ret < 0) { + wl1271_error("failed to initiate cmd join"); + goto out_free; + } + + timeout = msecs_to_jiffies(JOIN_TIMEOUT); + + /* + * ugly hack: we should wait for JOIN_EVENT_COMPLETE_ID but to + * simplify locking we just sleep instead, for now + */ + if (wait) + msleep(10); + +out_free: + kfree(join); + +out: + return ret; +} + +/** + * send test command to firmware + * + * @wl: wl struct + * @buf: buffer containing the command, with all headers, must work with dma + * @len: length of the buffer + * @answer: is answer needed + */ +int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer) +{ + int ret; + + wl1271_debug(DEBUG_CMD, "cmd test"); + + ret = wl1271_cmd_send(wl, CMD_TEST, buf, buf_len); + + if (ret < 0) { + wl1271_warning("TEST command failed"); + return ret; + } + + if (answer) { + struct wl1271_command *cmd_answer; + + /* + * The test command got in, we can read the answer. + * The answer would be a wl1271_command, where the + * parameter array contains the actual answer. + */ + wl1271_spi_mem_read(wl, wl->cmd_box_addr, buf, buf_len); + + cmd_answer = buf; + + if (cmd_answer->header.status != CMD_STATUS_SUCCESS) + wl1271_error("TEST command answer error: %d", + cmd_answer->header.status); + } + + return 0; +} + +/** + * read acx from firmware + * + * @wl: wl struct + * @id: acx id + * @buf: buffer for the response, including all headers, must work with dma + * @len: lenght of buf + */ +int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len) +{ + struct acx_header *acx = buf; + int ret; + + wl1271_debug(DEBUG_CMD, "cmd interrogate"); + + acx->id = id; + + /* payload length, does not include any headers */ + acx->len = len - sizeof(*acx); + + ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_error("INTERROGATE command failed"); + goto out; + } + + /* the interrogate command got in, we can read the answer */ + wl1271_spi_mem_read(wl, wl->cmd_box_addr, buf, len); + + acx = buf; + if (acx->cmd.status != CMD_STATUS_SUCCESS) + wl1271_error("INTERROGATE command error: %d", + acx->cmd.status); + +out: + return ret; +} + +/** + * write acx value to firmware + * + * @wl: wl struct + * @id: acx id + * @buf: buffer containing acx, including all headers, must work with dma + * @len: length of buf + */ +int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) +{ + struct acx_header *acx = buf; + int ret; + + wl1271_debug(DEBUG_CMD, "cmd configure"); + + acx->id = id; + + /* payload length, does not include any headers */ + acx->len = len - sizeof(*acx); + + ret = wl1271_cmd_send(wl, CMD_CONFIGURE, acx, len); + if (ret < 0) { + wl1271_warning("CONFIGURE command NOK"); + return ret; + } + + return 0; +} + +int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable) +{ + struct cmd_enabledisable_path *cmd; + int ret; + u16 cmd_rx, cmd_tx; + + wl1271_debug(DEBUG_CMD, "cmd data path"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->channel = channel; + + if (enable) { + cmd_rx = CMD_ENABLE_RX; + cmd_tx = CMD_ENABLE_TX; + } else { + cmd_rx = CMD_DISABLE_RX; + cmd_tx = CMD_DISABLE_TX; + } + + ret = wl1271_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1271_error("rx %s cmd for channel %d failed", + enable ? "start" : "stop", channel); + goto out; + } + + wl1271_debug(DEBUG_BOOT, "rx %s cmd channel %d", + enable ? "start" : "stop", channel); + + ret = wl1271_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1271_error("tx %s cmd for channel %d failed", + enable ? "start" : "stop", channel); + return ret; + } + + wl1271_debug(DEBUG_BOOT, "tx %s cmd channel %d", + enable ? "start" : "stop", channel); + +out: + kfree(cmd); + return ret; +} + +int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode) +{ + struct wl1271_cmd_ps_params *ps_params = NULL; + int ret = 0; + + /* FIXME: this should be in ps.c */ + ret = wl1271_acx_wake_up_conditions(wl, WAKE_UP_EVENT_DTIM_BITMAP, + wl->listen_int); + if (ret < 0) { + wl1271_error("couldn't set wake up conditions"); + goto out; + } + + wl1271_debug(DEBUG_CMD, "cmd set ps mode"); + + ps_params = kzalloc(sizeof(*ps_params), GFP_KERNEL); + if (!ps_params) { + ret = -ENOMEM; + goto out; + } + + ps_params->ps_mode = ps_mode; + ps_params->send_null_data = 1; + ps_params->retries = 5; + ps_params->hang_over_period = 128; + ps_params->null_data_rate = 1; /* 1 Mbps */ + + ret = wl1271_cmd_send(wl, CMD_SET_PS_MODE, ps_params, + sizeof(*ps_params)); + if (ret < 0) { + wl1271_error("cmd set_ps_mode failed"); + goto out; + } + +out: + kfree(ps_params); + return ret; +} + +int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, + size_t len) +{ + struct cmd_read_write_memory *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd read memory"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + WARN_ON(len > MAX_READ_SIZE); + len = min_t(size_t, len, MAX_READ_SIZE); + + cmd->addr = addr; + cmd->size = len; + + ret = wl1271_cmd_send(wl, CMD_READ_MEMORY, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1271_error("read memory command failed: %d", ret); + goto out; + } + + /* the read command got in, we can now read the answer */ + wl1271_spi_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); + + if (cmd->header.status != CMD_STATUS_SUCCESS) + wl1271_error("error in read command result: %d", + cmd->header.status); + + memcpy(answer, cmd->value, len); + +out: + kfree(cmd); + return ret; +} + +int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, + u8 active_scan, u8 high_prio, u8 num_channels, + u8 probe_requests) +{ + + struct wl1271_cmd_trigger_scan_to *trigger = NULL; + struct wl1271_cmd_scan *params = NULL; + int i, ret; + u16 scan_options = 0; + + if (wl->scanning) + return -EINVAL; + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) + return -ENOMEM; + + params->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD); + params->params.rx_filter_options = + cpu_to_le32(CFG_RX_PRSP_EN | CFG_RX_MGMT_EN | CFG_RX_BCN_EN); + + if (!active_scan) + scan_options |= WL1271_SCAN_OPT_PASSIVE; + if (high_prio) + scan_options |= WL1271_SCAN_OPT_PRIORITY_HIGH; + params->params.scan_options = scan_options; + + params->params.num_channels = num_channels; + params->params.num_probe_requests = probe_requests; + params->params.tx_rate = cpu_to_le32(RATE_MASK_2MBPS); + params->params.tid_trigger = 0; + params->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; + + for (i = 0; i < num_channels; i++) { + params->channels[i].min_duration = + cpu_to_le32(WL1271_SCAN_CHAN_MIN_DURATION); + params->channels[i].max_duration = + cpu_to_le32(WL1271_SCAN_CHAN_MAX_DURATION); + memset(¶ms->channels[i].bssid_lsb, 0xff, 4); + memset(¶ms->channels[i].bssid_msb, 0xff, 2); + params->channels[i].early_termination = 0; + params->channels[i].tx_power_att = WL1271_SCAN_CURRENT_TX_PWR; + params->channels[i].channel = i + 1; + } + + if (len && ssid) { + params->params.ssid_len = len; + memcpy(params->params.ssid, ssid, len); + } + + ret = wl1271_cmd_build_probe_req(wl, ssid, len); + if (ret < 0) { + wl1271_error("PROBE request template failed"); + goto out; + } + + trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); + if (!trigger) { + ret = -ENOMEM; + goto out; + } + + /* disable the timeout */ + trigger->timeout = 0; + + ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, + sizeof(*trigger)); + if (ret < 0) { + wl1271_error("trigger scan to failed for hw scan"); + goto out; + } + + wl1271_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params)); + + wl->scanning = true; + + ret = wl1271_cmd_send(wl, CMD_SCAN, params, sizeof(*params)); + if (ret < 0) { + wl1271_error("SCAN failed"); + goto out; + } + + wl1271_spi_mem_read(wl, wl->cmd_box_addr, params, sizeof(*params)); + + if (params->header.status != CMD_STATUS_SUCCESS) { + wl1271_error("Scan command error: %d", + params->header.status); + wl->scanning = false; + ret = -EIO; + goto out; + } + +out: + kfree(params); + return ret; +} + +int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, + void *buf, size_t buf_len) +{ + struct wl1271_cmd_template_set *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd template_set %d", template_id); + + WARN_ON(buf_len > WL1271_CMD_TEMPL_MAX_SIZE); + buf_len = min_t(size_t, buf_len, WL1271_CMD_TEMPL_MAX_SIZE); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->len = cpu_to_le16(buf_len); + cmd->template_type = template_id; + cmd->enabled_rates = ACX_RATE_MASK_UNSPECIFIED; + cmd->short_retry_limit = ACX_RATE_RETRY_LIMIT; + cmd->long_retry_limit = ACX_RATE_RETRY_LIMIT; + + if (buf) + memcpy(cmd->template_data, buf, buf_len); + + ret = wl1271_cmd_send(wl, CMD_SET_TEMPLATE, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1271_warning("cmd set_template failed: %d", ret); + goto out_free; + } + +out_free: + kfree(cmd); + +out: + return ret; +} + +static int wl1271_build_basic_rates(char *rates) +{ + u8 index = 0; + + rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; + rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; + rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; + rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; + + return index; +} + +static int wl1271_build_extended_rates(char *rates) +{ + u8 index = 0; + + rates[index++] = IEEE80211_OFDM_RATE_6MB; + rates[index++] = IEEE80211_OFDM_RATE_9MB; + rates[index++] = IEEE80211_OFDM_RATE_12MB; + rates[index++] = IEEE80211_OFDM_RATE_18MB; + rates[index++] = IEEE80211_OFDM_RATE_24MB; + rates[index++] = IEEE80211_OFDM_RATE_36MB; + rates[index++] = IEEE80211_OFDM_RATE_48MB; + rates[index++] = IEEE80211_OFDM_RATE_54MB; + + return index; +} + +int wl1271_cmd_build_null_data(struct wl1271 *wl) +{ + struct wl12xx_null_data_template template; + + if (!is_zero_ether_addr(wl->bssid)) { + memcpy(template.header.da, wl->bssid, ETH_ALEN); + memcpy(template.header.bssid, wl->bssid, ETH_ALEN); + } else { + memset(template.header.da, 0xff, ETH_ALEN); + memset(template.header.bssid, 0xff, ETH_ALEN); + } + + memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); + template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC); + + return wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, &template, + sizeof(template)); + +} + +int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid) +{ + struct wl12xx_ps_poll_template template; + + memcpy(template.bssid, wl->bssid, ETH_ALEN); + memcpy(template.ta, wl->mac_addr, ETH_ALEN); + template.aid = aid; + template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); + + return wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, &template, + sizeof(template)); + +} + +int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len) +{ + struct wl12xx_probe_req_template template; + struct wl12xx_ie_rates *rates; + char *ptr; + u16 size; + + ptr = (char *)&template; + size = sizeof(struct ieee80211_header); + + memset(template.header.da, 0xff, ETH_ALEN); + memset(template.header.bssid, 0xff, ETH_ALEN); + memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); + template.header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); + + /* IEs */ + /* SSID */ + template.ssid.header.id = WLAN_EID_SSID; + template.ssid.header.len = ssid_len; + if (ssid_len && ssid) + memcpy(template.ssid.ssid, ssid, ssid_len); + size += sizeof(struct wl12xx_ie_header) + ssid_len; + ptr += size; + + /* Basic Rates */ + rates = (struct wl12xx_ie_rates *)ptr; + rates->header.id = WLAN_EID_SUPP_RATES; + rates->header.len = wl1271_build_basic_rates(rates->rates); + size += sizeof(struct wl12xx_ie_header) + rates->header.len; + ptr += sizeof(struct wl12xx_ie_header) + rates->header.len; + + /* Extended rates */ + rates = (struct wl12xx_ie_rates *)ptr; + rates->header.id = WLAN_EID_EXT_SUPP_RATES; + rates->header.len = wl1271_build_extended_rates(rates->rates); + size += sizeof(struct wl12xx_ie_header) + rates->header.len; + + wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size); + + return wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, + &template, size); +} + +int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id) +{ + struct wl1271_cmd_set_keys *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd set_default_wep_key %d", id); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->id = id; + cmd->key_action = KEY_SET_ID; + cmd->key_type = KEY_WEP; + + ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1271_warning("cmd set_default_wep_key failed: %d", ret); + goto out; + } + +out: + kfree(cmd); + + return ret; +} + +int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, + u8 key_size, const u8 *key, const u8 *addr) +{ + struct wl1271_cmd_set_keys *cmd; + int ret = 0; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + if (key_type != KEY_WEP) + memcpy(cmd->addr, addr, ETH_ALEN); + + cmd->key_action = action; + cmd->key_size = key_size; + cmd->key_type = key_type; + + /* we have only one SSID profile */ + cmd->ssid_profile = 0; + + cmd->id = id; + + /* FIXME: this is from wl1251, needs to be checked */ + if (key_type == KEY_TKIP) { + /* + * We get the key in the following form: + * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes) + * but the target is expecting: + * TKIP - RX MIC - TX MIC + */ + memcpy(cmd->key, key, 16); + memcpy(cmd->key + 16, key + 24, 8); + memcpy(cmd->key + 24, key + 16, 8); + + } else { + memcpy(cmd->key, key, key_size); + } + + wl1271_dump(DEBUG_CRYPT, "TARGET KEY: ", cmd, sizeof(*cmd)); + + ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1271_warning("could not set keys"); + goto out; + } + +out: + kfree(cmd); + + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h new file mode 100644 index 000000000000..951a8447a516 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h @@ -0,0 +1,464 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1271_CMD_H__ +#define __WL1271_CMD_H__ + +#include "wl1271.h" + +struct acx_header; + +int wl1271_cmd_send(struct wl1271 *wl, u16 type, void *buf, size_t buf_len); +int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type, u8 dtim_interval, + u16 beacon_interval, u8 wait); +int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); +int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len); +int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len); +int wl1271_cmd_data_path(struct wl1271 *wl, u8 channel, bool enable); +int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode); +int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, + size_t len); +int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, + u8 active_scan, u8 high_prio, u8 num_channels, + u8 probe_requests); +int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, + void *buf, size_t buf_len); +int wl1271_cmd_build_null_data(struct wl1271 *wl); +int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid); +int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len); +int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id); +int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, + u8 key_size, const u8 *key, const u8 *addr); + +enum wl1271_commands { + CMD_INTERROGATE = 1, /*use this to read information elements*/ + CMD_CONFIGURE = 2, /*use this to write information elements*/ + CMD_ENABLE_RX = 3, + CMD_ENABLE_TX = 4, + CMD_DISABLE_RX = 5, + CMD_DISABLE_TX = 6, + CMD_SCAN = 8, + CMD_STOP_SCAN = 9, + CMD_START_JOIN = 11, + CMD_SET_KEYS = 12, + CMD_READ_MEMORY = 13, + CMD_WRITE_MEMORY = 14, + CMD_SET_TEMPLATE = 19, + CMD_TEST = 23, + CMD_NOISE_HIST = 28, + CMD_LNA_CONTROL = 32, + CMD_SET_BCN_MODE = 33, + CMD_MEASUREMENT = 34, + CMD_STOP_MEASUREMENT = 35, + CMD_DISCONNECT = 36, + CMD_SET_PS_MODE = 37, + CMD_CHANNEL_SWITCH = 38, + CMD_STOP_CHANNEL_SWICTH = 39, + CMD_AP_DISCOVERY = 40, + CMD_STOP_AP_DISCOVERY = 41, + CMD_SPS_SCAN = 42, + CMD_STOP_SPS_SCAN = 43, + CMD_HEALTH_CHECK = 45, + CMD_DEBUG = 46, + CMD_TRIGGER_SCAN_TO = 47, + CMD_CONNECTION_SCAN_CFG = 48, + CMD_CONNECTION_SCAN_SSID_CFG = 49, + CMD_START_PERIODIC_SCAN = 50, + CMD_STOP_PERIODIC_SCAN = 51, + CMD_SET_STA_STATE = 52, + + NUM_COMMANDS, + MAX_COMMAND_ID = 0xFFFF, +}; + +#define MAX_CMD_PARAMS 572 + +enum cmd_templ { + CMD_TEMPL_NULL_DATA = 0, + CMD_TEMPL_BEACON, + CMD_TEMPL_CFG_PROBE_REQ_2_4, + CMD_TEMPL_CFG_PROBE_REQ_5, + CMD_TEMPL_PROBE_RESPONSE, + CMD_TEMPL_QOS_NULL_DATA, + CMD_TEMPL_PS_POLL, + CMD_TEMPL_KLV, + CMD_TEMPL_DISCONNECT, + CMD_TEMPL_PROBE_REQ_2_4, /* for firmware internal use only */ + CMD_TEMPL_PROBE_REQ_5, /* for firmware internal use only */ + CMD_TEMPL_BAR, /* for firmware internal use only */ + CMD_TEMPL_CTS, /* + * For CTS-to-self (FastCTS) mechanism + * for BT/WLAN coexistence (SoftGemini). */ + CMD_TEMPL_MAX = 0xff +}; + +/* unit ms */ +#define WL1271_COMMAND_TIMEOUT 2000 +#define WL1271_CMD_TEMPL_MAX_SIZE 252 + +struct wl1271_cmd_header { + u16 id; + u16 status; + /* payload */ + u8 data[0]; +} __attribute__ ((packed)); + +#define WL1271_CMD_MAX_PARAMS 572 + +struct wl1271_command { + struct wl1271_cmd_header header; + u8 parameters[WL1271_CMD_MAX_PARAMS]; +} __attribute__ ((packed)); + +enum { + CMD_MAILBOX_IDLE = 0, + CMD_STATUS_SUCCESS = 1, + CMD_STATUS_UNKNOWN_CMD = 2, + CMD_STATUS_UNKNOWN_IE = 3, + CMD_STATUS_REJECT_MEAS_SG_ACTIVE = 11, + CMD_STATUS_RX_BUSY = 13, + CMD_STATUS_INVALID_PARAM = 14, + CMD_STATUS_TEMPLATE_TOO_LARGE = 15, + CMD_STATUS_OUT_OF_MEMORY = 16, + CMD_STATUS_STA_TABLE_FULL = 17, + CMD_STATUS_RADIO_ERROR = 18, + CMD_STATUS_WRONG_NESTING = 19, + CMD_STATUS_TIMEOUT = 21, /* Driver internal use.*/ + CMD_STATUS_FW_RESET = 22, /* Driver internal use.*/ + MAX_COMMAND_STATUS = 0xff +}; + + +/* + * CMD_READ_MEMORY + * + * The host issues this command to read the WiLink device memory/registers. + * + * Note: The Base Band address has special handling (16 bits registers and + * addresses). For more information, see the hardware specification. + */ +/* + * CMD_WRITE_MEMORY + * + * The host issues this command to write the WiLink device memory/registers. + * + * The Base Band address has special handling (16 bits registers and + * addresses). For more information, see the hardware specification. + */ +#define MAX_READ_SIZE 256 + +struct cmd_read_write_memory { + struct wl1271_cmd_header header; + + /* The address of the memory to read from or write to.*/ + u32 addr; + + /* The amount of data in bytes to read from or write to the WiLink + * device.*/ + u32 size; + + /* The actual value read from or written to the Wilink. The source + of this field is the Host in WRITE command or the Wilink in READ + command. */ + u8 value[MAX_READ_SIZE]; +}; + +#define CMDMBOX_HEADER_LEN 4 +#define CMDMBOX_INFO_ELEM_HEADER_LEN 4 + +enum { + BSS_TYPE_IBSS = 0, + BSS_TYPE_STA_BSS = 2, + BSS_TYPE_AP_BSS = 3, + MAX_BSS_TYPE = 0xFF +}; + +#define WL1271_JOIN_CMD_CTRL_TX_FLUSH 0x80 /* Firmware flushes all Tx */ +#define WL1271_JOIN_CMD_TX_SESSION_OFFSET 1 + +struct wl1271_cmd_join { + struct wl1271_cmd_header header; + + u32 bssid_lsb; + u16 bssid_msb; + u16 beacon_interval; /* in TBTTs */ + u32 rx_config_options; + u32 rx_filter_options; + + /* + * The target uses this field to determine the rate at + * which to transmit control frame responses (such as + * ACK or CTS frames). + */ + u32 basic_rate_set; + u8 dtim_interval; + /* + * bits 0-2: This bitwise field specifies the type + * of BSS to start or join (BSS_TYPE_*). + * bit 4: Band - The radio band in which to join + * or start. + * 0 - 2.4GHz band + * 1 - 5GHz band + * bits 3, 5-7: Reserved + */ + u8 bss_type; + u8 channel; + u8 ssid_len; + u8 ssid[IW_ESSID_MAX_SIZE]; + u8 ctrl; /* JOIN_CMD_CTRL_* */ + u8 reserved[3]; +} __attribute__ ((packed)); + +struct cmd_enabledisable_path { + struct wl1271_cmd_header header; + + u8 channel; + u8 padding[3]; +} __attribute__ ((packed)); + +struct wl1271_cmd_template_set { + struct wl1271_cmd_header header; + + u16 len; + u8 template_type; + u8 index; /* relevant only for KLV_TEMPLATE type */ + u32 enabled_rates; + u8 short_retry_limit; + u8 long_retry_limit; + u8 aflags; + u8 reserved; + u8 template_data[WL1271_CMD_TEMPL_MAX_SIZE]; +} __attribute__ ((packed)); + +#define TIM_ELE_ID 5 +#define PARTIAL_VBM_MAX 251 + +struct wl1271_tim { + u8 identity; + u8 length; + u8 dtim_count; + u8 dtim_period; + u8 bitmap_ctrl; + u8 pvb_field[PARTIAL_VBM_MAX]; /* Partial Virtual Bitmap */ +} __attribute__ ((packed)); + +enum wl1271_cmd_ps_mode { + STATION_ACTIVE_MODE, + STATION_POWER_SAVE_MODE +}; + +struct wl1271_cmd_ps_params { + struct wl1271_cmd_header header; + + u8 ps_mode; /* STATION_* */ + u8 send_null_data; /* Do we have to send NULL data packet ? */ + u8 retries; /* Number of retires for the initial NULL data packet */ + + /* + * TUs during which the target stays awake after switching + * to power save mode. + */ + u8 hang_over_period; + u32 null_data_rate; +} __attribute__ ((packed)); + +/* HW encryption keys */ +#define NUM_ACCESS_CATEGORIES_COPY 4 +#define MAX_KEY_SIZE 32 + +/* When set, disable HW encryption */ +#define DF_ENCRYPTION_DISABLE 0x01 +/* When set, disable HW decryption */ +#define DF_SNIFF_MODE_ENABLE 0x80 + +enum wl1271_cmd_key_action { + KEY_ADD_OR_REPLACE = 1, + KEY_REMOVE = 2, + KEY_SET_ID = 3, + MAX_KEY_ACTION = 0xffff, +}; + +enum wl1271_cmd_key_type { + KEY_NONE = 0, + KEY_WEP = 1, + KEY_TKIP = 2, + KEY_AES = 3, + KEY_GEM = 4 +}; + +/* FIXME: Add description for key-types */ + +struct wl1271_cmd_set_keys { + struct wl1271_cmd_header header; + + /* Ignored for default WEP key */ + u8 addr[ETH_ALEN]; + + /* key_action_e */ + u16 key_action; + + u16 reserved_1; + + /* key size in bytes */ + u8 key_size; + + /* key_type_e */ + u8 key_type; + u8 ssid_profile; + + /* + * TKIP, AES: frame's key id field. + * For WEP default key: key id; + */ + u8 id; + u8 reserved_2[6]; + u8 key[MAX_KEY_SIZE]; + u16 ac_seq_num16[NUM_ACCESS_CATEGORIES_COPY]; + u32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY]; +} __attribute__ ((packed)); + + +#define WL1271_SCAN_MAX_CHANNELS 24 +#define WL1271_SCAN_DEFAULT_TAG 1 +#define WL1271_SCAN_CURRENT_TX_PWR 0 +#define WL1271_SCAN_OPT_ACTIVE 0 +#define WL1271_SCAN_OPT_PASSIVE 1 +#define WL1271_SCAN_OPT_PRIORITY_HIGH 4 +#define WL1271_SCAN_CHAN_MIN_DURATION 30000 /* TU */ +#define WL1271_SCAN_CHAN_MAX_DURATION 60000 /* TU */ + +struct basic_scan_params { + u32 rx_config_options; + u32 rx_filter_options; + /* Scan option flags (WL1271_SCAN_OPT_*) */ + u16 scan_options; + /* Number of scan channels in the list (maximum 30) */ + u8 num_channels; + /* This field indicates the number of probe requests to send + per channel for an active scan */ + u8 num_probe_requests; + /* Rate bit field for sending the probes */ + u32 tx_rate; + u8 tid_trigger; + u8 ssid_len; + /* in order to align */ + u8 padding1[2]; + u8 ssid[IW_ESSID_MAX_SIZE]; + /* Band to scan */ + u8 band; + u8 use_ssid_list; + u8 scan_tag; + u8 padding2; +} __attribute__ ((packed)); + +struct basic_scan_channel_params { + /* Duration in TU to wait for frames on a channel for active scan */ + u32 min_duration; + u32 max_duration; + u32 bssid_lsb; + u16 bssid_msb; + u8 early_termination; + u8 tx_power_att; + u8 channel; + /* FW internal use only! */ + u8 dfs_candidate; + u8 activity_detected; + u8 pad; +} __attribute__ ((packed)); + +struct wl1271_cmd_scan { + struct wl1271_cmd_header header; + + struct basic_scan_params params; + struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS]; +} __attribute__ ((packed)); + +struct wl1271_cmd_trigger_scan_to { + struct wl1271_cmd_header header; + + u32 timeout; +}; + +struct wl1271_cmd_test_header { + u8 id; + u8 padding[3]; +}; + +enum wl1271_channel_tune_bands { + WL1271_CHANNEL_TUNE_BAND_2_4, + WL1271_CHANNEL_TUNE_BAND_5, + WL1271_CHANNEL_TUNE_BAND_4_9 +}; + +#define WL1271_PD_REFERENCE_POINT_BAND_B_G 0 + +#define TEST_CMD_P2G_CAL 0x02 +#define TEST_CMD_CHANNEL_TUNE 0x0d +#define TEST_CMD_UPDATE_PD_REFERENCE_POINT 0x1d + +struct wl1271_cmd_cal_channel_tune { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + u8 band; + u8 channel; + + u16 radio_status; +} __attribute__ ((packed)); + +struct wl1271_cmd_cal_update_ref_point { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + s32 ref_power; + s32 ref_detector; + u8 sub_band; + u8 padding[3]; +} __attribute__ ((packed)); + +#define MAX_TLV_LENGTH 400 +#define MAX_NVS_VERSION_LENGTH 12 + +#define WL1271_CAL_P2G_BAND_B_G BIT(0) + +struct wl1271_cmd_cal_p2g { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + u16 len; + u8 buf[MAX_TLV_LENGTH]; + u8 type; + u8 padding; + + s16 radio_status; + u8 nvs_version[MAX_NVS_VERSION_LENGTH]; + + u8 sub_band_mask; + u8 padding2; +} __attribute__ ((packed)); + +#endif /* __WL1271_CMD_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_debugfs.c b/drivers/net/wireless/wl12xx/wl1271_debugfs.c new file mode 100644 index 000000000000..c1805e5f8964 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_debugfs.c @@ -0,0 +1,518 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "wl1271_debugfs.h" + +#include <linux/skbuff.h> + +#include "wl1271.h" +#include "wl1271_acx.h" +#include "wl1271_ps.h" + +/* ms */ +#define WL1271_DEBUGFS_STATS_LIFETIME 1000 + +/* debugfs macros idea from mac80211 */ + +#define DEBUGFS_READONLY_FILE(name, buflen, fmt, value...) \ +static ssize_t name## _read(struct file *file, char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + struct wl1271 *wl = file->private_data; \ + char buf[buflen]; \ + int res; \ + \ + res = scnprintf(buf, buflen, fmt "\n", ##value); \ + return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ +} \ + \ +static const struct file_operations name## _ops = { \ + .read = name## _read, \ + .open = wl1271_open_file_generic, \ +}; + +#define DEBUGFS_ADD(name, parent) \ + wl->debugfs.name = debugfs_create_file(#name, 0400, parent, \ + wl, &name## _ops); \ + if (IS_ERR(wl->debugfs.name)) { \ + ret = PTR_ERR(wl->debugfs.name); \ + wl->debugfs.name = NULL; \ + goto out; \ + } + +#define DEBUGFS_DEL(name) \ + do { \ + debugfs_remove(wl->debugfs.name); \ + wl->debugfs.name = NULL; \ + } while (0) + +#define DEBUGFS_FWSTATS_FILE(sub, name, buflen, fmt) \ +static ssize_t sub## _ ##name## _read(struct file *file, \ + char __user *userbuf, \ + size_t count, loff_t *ppos) \ +{ \ + struct wl1271 *wl = file->private_data; \ + char buf[buflen]; \ + int res; \ + \ + wl1271_debugfs_update_stats(wl); \ + \ + res = scnprintf(buf, buflen, fmt "\n", \ + wl->stats.fw_stats->sub.name); \ + return simple_read_from_buffer(userbuf, count, ppos, buf, res); \ +} \ + \ +static const struct file_operations sub## _ ##name## _ops = { \ + .read = sub## _ ##name## _read, \ + .open = wl1271_open_file_generic, \ +}; + +#define DEBUGFS_FWSTATS_ADD(sub, name) \ + DEBUGFS_ADD(sub## _ ##name, wl->debugfs.fw_statistics) + +#define DEBUGFS_FWSTATS_DEL(sub, name) \ + DEBUGFS_DEL(sub## _ ##name) + +static void wl1271_debugfs_update_stats(struct wl1271 *wl) +{ + int ret; + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + if (wl->state == WL1271_STATE_ON && + time_after(jiffies, wl->stats.fw_stats_update + + msecs_to_jiffies(WL1271_DEBUGFS_STATS_LIFETIME))) { + wl1271_acx_statistics(wl, wl->stats.fw_stats); + wl->stats.fw_stats_update = jiffies; + } + + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); +} + +static int wl1271_open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +DEBUGFS_FWSTATS_FILE(tx, internal_desc_overflow, 20, "%u"); + +DEBUGFS_FWSTATS_FILE(rx, out_of_mem, 20, "%u"); +DEBUGFS_FWSTATS_FILE(rx, hdr_overflow, 20, "%u"); +DEBUGFS_FWSTATS_FILE(rx, hw_stuck, 20, "%u"); +DEBUGFS_FWSTATS_FILE(rx, dropped, 20, "%u"); +DEBUGFS_FWSTATS_FILE(rx, fcs_err, 20, "%u"); +DEBUGFS_FWSTATS_FILE(rx, xfr_hint_trig, 20, "%u"); +DEBUGFS_FWSTATS_FILE(rx, path_reset, 20, "%u"); +DEBUGFS_FWSTATS_FILE(rx, reset_counter, 20, "%u"); + +DEBUGFS_FWSTATS_FILE(dma, rx_requested, 20, "%u"); +DEBUGFS_FWSTATS_FILE(dma, rx_errors, 20, "%u"); +DEBUGFS_FWSTATS_FILE(dma, tx_requested, 20, "%u"); +DEBUGFS_FWSTATS_FILE(dma, tx_errors, 20, "%u"); + +DEBUGFS_FWSTATS_FILE(isr, cmd_cmplt, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, fiqs, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, rx_headers, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, rx_mem_overflow, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, rx_rdys, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, irqs, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, tx_procs, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, decrypt_done, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, dma0_done, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, dma1_done, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, tx_exch_complete, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, commands, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, rx_procs, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, hw_pm_mode_changes, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, host_acknowledges, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, pci_pm, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, wakeups, 20, "%u"); +DEBUGFS_FWSTATS_FILE(isr, low_rssi, 20, "%u"); + +DEBUGFS_FWSTATS_FILE(wep, addr_key_count, 20, "%u"); +DEBUGFS_FWSTATS_FILE(wep, default_key_count, 20, "%u"); +/* skipping wep.reserved */ +DEBUGFS_FWSTATS_FILE(wep, key_not_found, 20, "%u"); +DEBUGFS_FWSTATS_FILE(wep, decrypt_fail, 20, "%u"); +DEBUGFS_FWSTATS_FILE(wep, packets, 20, "%u"); +DEBUGFS_FWSTATS_FILE(wep, interrupt, 20, "%u"); + +DEBUGFS_FWSTATS_FILE(pwr, ps_enter, 20, "%u"); +DEBUGFS_FWSTATS_FILE(pwr, elp_enter, 20, "%u"); +DEBUGFS_FWSTATS_FILE(pwr, missing_bcns, 20, "%u"); +DEBUGFS_FWSTATS_FILE(pwr, wake_on_host, 20, "%u"); +DEBUGFS_FWSTATS_FILE(pwr, wake_on_timer_exp, 20, "%u"); +DEBUGFS_FWSTATS_FILE(pwr, tx_with_ps, 20, "%u"); +DEBUGFS_FWSTATS_FILE(pwr, tx_without_ps, 20, "%u"); +DEBUGFS_FWSTATS_FILE(pwr, rcvd_beacons, 20, "%u"); +DEBUGFS_FWSTATS_FILE(pwr, power_save_off, 20, "%u"); +DEBUGFS_FWSTATS_FILE(pwr, enable_ps, 20, "%u"); +DEBUGFS_FWSTATS_FILE(pwr, disable_ps, 20, "%u"); +DEBUGFS_FWSTATS_FILE(pwr, fix_tsf_ps, 20, "%u"); +/* skipping cont_miss_bcns_spread for now */ +DEBUGFS_FWSTATS_FILE(pwr, rcvd_awake_beacons, 20, "%u"); + +DEBUGFS_FWSTATS_FILE(mic, rx_pkts, 20, "%u"); +DEBUGFS_FWSTATS_FILE(mic, calc_failure, 20, "%u"); + +DEBUGFS_FWSTATS_FILE(aes, encrypt_fail, 20, "%u"); +DEBUGFS_FWSTATS_FILE(aes, decrypt_fail, 20, "%u"); +DEBUGFS_FWSTATS_FILE(aes, encrypt_packets, 20, "%u"); +DEBUGFS_FWSTATS_FILE(aes, decrypt_packets, 20, "%u"); +DEBUGFS_FWSTATS_FILE(aes, encrypt_interrupt, 20, "%u"); +DEBUGFS_FWSTATS_FILE(aes, decrypt_interrupt, 20, "%u"); + +DEBUGFS_FWSTATS_FILE(event, heart_beat, 20, "%u"); +DEBUGFS_FWSTATS_FILE(event, calibration, 20, "%u"); +DEBUGFS_FWSTATS_FILE(event, rx_mismatch, 20, "%u"); +DEBUGFS_FWSTATS_FILE(event, rx_mem_empty, 20, "%u"); +DEBUGFS_FWSTATS_FILE(event, rx_pool, 20, "%u"); +DEBUGFS_FWSTATS_FILE(event, oom_late, 20, "%u"); +DEBUGFS_FWSTATS_FILE(event, phy_transmit_error, 20, "%u"); +DEBUGFS_FWSTATS_FILE(event, tx_stuck, 20, "%u"); + +DEBUGFS_FWSTATS_FILE(ps, pspoll_timeouts, 20, "%u"); +DEBUGFS_FWSTATS_FILE(ps, upsd_timeouts, 20, "%u"); +DEBUGFS_FWSTATS_FILE(ps, upsd_max_sptime, 20, "%u"); +DEBUGFS_FWSTATS_FILE(ps, upsd_max_apturn, 20, "%u"); +DEBUGFS_FWSTATS_FILE(ps, pspoll_max_apturn, 20, "%u"); +DEBUGFS_FWSTATS_FILE(ps, pspoll_utilization, 20, "%u"); +DEBUGFS_FWSTATS_FILE(ps, upsd_utilization, 20, "%u"); + +DEBUGFS_FWSTATS_FILE(rxpipe, rx_prep_beacon_drop, 20, "%u"); +DEBUGFS_FWSTATS_FILE(rxpipe, descr_host_int_trig_rx_data, 20, "%u"); +DEBUGFS_FWSTATS_FILE(rxpipe, beacon_buffer_thres_host_int_trig_rx_data, + 20, "%u"); +DEBUGFS_FWSTATS_FILE(rxpipe, missed_beacon_host_int_trig_rx_data, 20, "%u"); +DEBUGFS_FWSTATS_FILE(rxpipe, tx_xfr_host_int_trig_rx_data, 20, "%u"); + +DEBUGFS_READONLY_FILE(retry_count, 20, "%u", wl->stats.retry_count); +DEBUGFS_READONLY_FILE(excessive_retries, 20, "%u", + wl->stats.excessive_retries); + +static ssize_t tx_queue_len_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + u32 queue_len; + char buf[20]; + int res; + + queue_len = skb_queue_len(&wl->tx_queue); + + res = scnprintf(buf, sizeof(buf), "%u\n", queue_len); + return simple_read_from_buffer(userbuf, count, ppos, buf, res); +} + +static const struct file_operations tx_queue_len_ops = { + .read = tx_queue_len_read, + .open = wl1271_open_file_generic, +}; + +static void wl1271_debugfs_delete_files(struct wl1271 *wl) +{ + DEBUGFS_FWSTATS_DEL(tx, internal_desc_overflow); + + DEBUGFS_FWSTATS_DEL(rx, out_of_mem); + DEBUGFS_FWSTATS_DEL(rx, hdr_overflow); + DEBUGFS_FWSTATS_DEL(rx, hw_stuck); + DEBUGFS_FWSTATS_DEL(rx, dropped); + DEBUGFS_FWSTATS_DEL(rx, fcs_err); + DEBUGFS_FWSTATS_DEL(rx, xfr_hint_trig); + DEBUGFS_FWSTATS_DEL(rx, path_reset); + DEBUGFS_FWSTATS_DEL(rx, reset_counter); + + DEBUGFS_FWSTATS_DEL(dma, rx_requested); + DEBUGFS_FWSTATS_DEL(dma, rx_errors); + DEBUGFS_FWSTATS_DEL(dma, tx_requested); + DEBUGFS_FWSTATS_DEL(dma, tx_errors); + + DEBUGFS_FWSTATS_DEL(isr, cmd_cmplt); + DEBUGFS_FWSTATS_DEL(isr, fiqs); + DEBUGFS_FWSTATS_DEL(isr, rx_headers); + DEBUGFS_FWSTATS_DEL(isr, rx_mem_overflow); + DEBUGFS_FWSTATS_DEL(isr, rx_rdys); + DEBUGFS_FWSTATS_DEL(isr, irqs); + DEBUGFS_FWSTATS_DEL(isr, tx_procs); + DEBUGFS_FWSTATS_DEL(isr, decrypt_done); + DEBUGFS_FWSTATS_DEL(isr, dma0_done); + DEBUGFS_FWSTATS_DEL(isr, dma1_done); + DEBUGFS_FWSTATS_DEL(isr, tx_exch_complete); + DEBUGFS_FWSTATS_DEL(isr, commands); + DEBUGFS_FWSTATS_DEL(isr, rx_procs); + DEBUGFS_FWSTATS_DEL(isr, hw_pm_mode_changes); + DEBUGFS_FWSTATS_DEL(isr, host_acknowledges); + DEBUGFS_FWSTATS_DEL(isr, pci_pm); + DEBUGFS_FWSTATS_DEL(isr, wakeups); + DEBUGFS_FWSTATS_DEL(isr, low_rssi); + + DEBUGFS_FWSTATS_DEL(wep, addr_key_count); + DEBUGFS_FWSTATS_DEL(wep, default_key_count); + /* skipping wep.reserved */ + DEBUGFS_FWSTATS_DEL(wep, key_not_found); + DEBUGFS_FWSTATS_DEL(wep, decrypt_fail); + DEBUGFS_FWSTATS_DEL(wep, packets); + DEBUGFS_FWSTATS_DEL(wep, interrupt); + + DEBUGFS_FWSTATS_DEL(pwr, ps_enter); + DEBUGFS_FWSTATS_DEL(pwr, elp_enter); + DEBUGFS_FWSTATS_DEL(pwr, missing_bcns); + DEBUGFS_FWSTATS_DEL(pwr, wake_on_host); + DEBUGFS_FWSTATS_DEL(pwr, wake_on_timer_exp); + DEBUGFS_FWSTATS_DEL(pwr, tx_with_ps); + DEBUGFS_FWSTATS_DEL(pwr, tx_without_ps); + DEBUGFS_FWSTATS_DEL(pwr, rcvd_beacons); + DEBUGFS_FWSTATS_DEL(pwr, power_save_off); + DEBUGFS_FWSTATS_DEL(pwr, enable_ps); + DEBUGFS_FWSTATS_DEL(pwr, disable_ps); + DEBUGFS_FWSTATS_DEL(pwr, fix_tsf_ps); + /* skipping cont_miss_bcns_spread for now */ + DEBUGFS_FWSTATS_DEL(pwr, rcvd_awake_beacons); + + DEBUGFS_FWSTATS_DEL(mic, rx_pkts); + DEBUGFS_FWSTATS_DEL(mic, calc_failure); + + DEBUGFS_FWSTATS_DEL(aes, encrypt_fail); + DEBUGFS_FWSTATS_DEL(aes, decrypt_fail); + DEBUGFS_FWSTATS_DEL(aes, encrypt_packets); + DEBUGFS_FWSTATS_DEL(aes, decrypt_packets); + DEBUGFS_FWSTATS_DEL(aes, encrypt_interrupt); + DEBUGFS_FWSTATS_DEL(aes, decrypt_interrupt); + + DEBUGFS_FWSTATS_DEL(event, heart_beat); + DEBUGFS_FWSTATS_DEL(event, calibration); + DEBUGFS_FWSTATS_DEL(event, rx_mismatch); + DEBUGFS_FWSTATS_DEL(event, rx_mem_empty); + DEBUGFS_FWSTATS_DEL(event, rx_pool); + DEBUGFS_FWSTATS_DEL(event, oom_late); + DEBUGFS_FWSTATS_DEL(event, phy_transmit_error); + DEBUGFS_FWSTATS_DEL(event, tx_stuck); + + DEBUGFS_FWSTATS_DEL(ps, pspoll_timeouts); + DEBUGFS_FWSTATS_DEL(ps, upsd_timeouts); + DEBUGFS_FWSTATS_DEL(ps, upsd_max_sptime); + DEBUGFS_FWSTATS_DEL(ps, upsd_max_apturn); + DEBUGFS_FWSTATS_DEL(ps, pspoll_max_apturn); + DEBUGFS_FWSTATS_DEL(ps, pspoll_utilization); + DEBUGFS_FWSTATS_DEL(ps, upsd_utilization); + + DEBUGFS_FWSTATS_DEL(rxpipe, rx_prep_beacon_drop); + DEBUGFS_FWSTATS_DEL(rxpipe, descr_host_int_trig_rx_data); + DEBUGFS_FWSTATS_DEL(rxpipe, beacon_buffer_thres_host_int_trig_rx_data); + DEBUGFS_FWSTATS_DEL(rxpipe, missed_beacon_host_int_trig_rx_data); + DEBUGFS_FWSTATS_DEL(rxpipe, tx_xfr_host_int_trig_rx_data); + + DEBUGFS_DEL(tx_queue_len); + DEBUGFS_DEL(retry_count); + DEBUGFS_DEL(excessive_retries); +} + +static int wl1271_debugfs_add_files(struct wl1271 *wl) +{ + int ret = 0; + + DEBUGFS_FWSTATS_ADD(tx, internal_desc_overflow); + + DEBUGFS_FWSTATS_ADD(rx, out_of_mem); + DEBUGFS_FWSTATS_ADD(rx, hdr_overflow); + DEBUGFS_FWSTATS_ADD(rx, hw_stuck); + DEBUGFS_FWSTATS_ADD(rx, dropped); + DEBUGFS_FWSTATS_ADD(rx, fcs_err); + DEBUGFS_FWSTATS_ADD(rx, xfr_hint_trig); + DEBUGFS_FWSTATS_ADD(rx, path_reset); + DEBUGFS_FWSTATS_ADD(rx, reset_counter); + + DEBUGFS_FWSTATS_ADD(dma, rx_requested); + DEBUGFS_FWSTATS_ADD(dma, rx_errors); + DEBUGFS_FWSTATS_ADD(dma, tx_requested); + DEBUGFS_FWSTATS_ADD(dma, tx_errors); + + DEBUGFS_FWSTATS_ADD(isr, cmd_cmplt); + DEBUGFS_FWSTATS_ADD(isr, fiqs); + DEBUGFS_FWSTATS_ADD(isr, rx_headers); + DEBUGFS_FWSTATS_ADD(isr, rx_mem_overflow); + DEBUGFS_FWSTATS_ADD(isr, rx_rdys); + DEBUGFS_FWSTATS_ADD(isr, irqs); + DEBUGFS_FWSTATS_ADD(isr, tx_procs); + DEBUGFS_FWSTATS_ADD(isr, decrypt_done); + DEBUGFS_FWSTATS_ADD(isr, dma0_done); + DEBUGFS_FWSTATS_ADD(isr, dma1_done); + DEBUGFS_FWSTATS_ADD(isr, tx_exch_complete); + DEBUGFS_FWSTATS_ADD(isr, commands); + DEBUGFS_FWSTATS_ADD(isr, rx_procs); + DEBUGFS_FWSTATS_ADD(isr, hw_pm_mode_changes); + DEBUGFS_FWSTATS_ADD(isr, host_acknowledges); + DEBUGFS_FWSTATS_ADD(isr, pci_pm); + DEBUGFS_FWSTATS_ADD(isr, wakeups); + DEBUGFS_FWSTATS_ADD(isr, low_rssi); + + DEBUGFS_FWSTATS_ADD(wep, addr_key_count); + DEBUGFS_FWSTATS_ADD(wep, default_key_count); + /* skipping wep.reserved */ + DEBUGFS_FWSTATS_ADD(wep, key_not_found); + DEBUGFS_FWSTATS_ADD(wep, decrypt_fail); + DEBUGFS_FWSTATS_ADD(wep, packets); + DEBUGFS_FWSTATS_ADD(wep, interrupt); + + DEBUGFS_FWSTATS_ADD(pwr, ps_enter); + DEBUGFS_FWSTATS_ADD(pwr, elp_enter); + DEBUGFS_FWSTATS_ADD(pwr, missing_bcns); + DEBUGFS_FWSTATS_ADD(pwr, wake_on_host); + DEBUGFS_FWSTATS_ADD(pwr, wake_on_timer_exp); + DEBUGFS_FWSTATS_ADD(pwr, tx_with_ps); + DEBUGFS_FWSTATS_ADD(pwr, tx_without_ps); + DEBUGFS_FWSTATS_ADD(pwr, rcvd_beacons); + DEBUGFS_FWSTATS_ADD(pwr, power_save_off); + DEBUGFS_FWSTATS_ADD(pwr, enable_ps); + DEBUGFS_FWSTATS_ADD(pwr, disable_ps); + DEBUGFS_FWSTATS_ADD(pwr, fix_tsf_ps); + /* skipping cont_miss_bcns_spread for now */ + DEBUGFS_FWSTATS_ADD(pwr, rcvd_awake_beacons); + + DEBUGFS_FWSTATS_ADD(mic, rx_pkts); + DEBUGFS_FWSTATS_ADD(mic, calc_failure); + + DEBUGFS_FWSTATS_ADD(aes, encrypt_fail); + DEBUGFS_FWSTATS_ADD(aes, decrypt_fail); + DEBUGFS_FWSTATS_ADD(aes, encrypt_packets); + DEBUGFS_FWSTATS_ADD(aes, decrypt_packets); + DEBUGFS_FWSTATS_ADD(aes, encrypt_interrupt); + DEBUGFS_FWSTATS_ADD(aes, decrypt_interrupt); + + DEBUGFS_FWSTATS_ADD(event, heart_beat); + DEBUGFS_FWSTATS_ADD(event, calibration); + DEBUGFS_FWSTATS_ADD(event, rx_mismatch); + DEBUGFS_FWSTATS_ADD(event, rx_mem_empty); + DEBUGFS_FWSTATS_ADD(event, rx_pool); + DEBUGFS_FWSTATS_ADD(event, oom_late); + DEBUGFS_FWSTATS_ADD(event, phy_transmit_error); + DEBUGFS_FWSTATS_ADD(event, tx_stuck); + + DEBUGFS_FWSTATS_ADD(ps, pspoll_timeouts); + DEBUGFS_FWSTATS_ADD(ps, upsd_timeouts); + DEBUGFS_FWSTATS_ADD(ps, upsd_max_sptime); + DEBUGFS_FWSTATS_ADD(ps, upsd_max_apturn); + DEBUGFS_FWSTATS_ADD(ps, pspoll_max_apturn); + DEBUGFS_FWSTATS_ADD(ps, pspoll_utilization); + DEBUGFS_FWSTATS_ADD(ps, upsd_utilization); + + DEBUGFS_FWSTATS_ADD(rxpipe, rx_prep_beacon_drop); + DEBUGFS_FWSTATS_ADD(rxpipe, descr_host_int_trig_rx_data); + DEBUGFS_FWSTATS_ADD(rxpipe, beacon_buffer_thres_host_int_trig_rx_data); + DEBUGFS_FWSTATS_ADD(rxpipe, missed_beacon_host_int_trig_rx_data); + DEBUGFS_FWSTATS_ADD(rxpipe, tx_xfr_host_int_trig_rx_data); + + DEBUGFS_ADD(tx_queue_len, wl->debugfs.rootdir); + DEBUGFS_ADD(retry_count, wl->debugfs.rootdir); + DEBUGFS_ADD(excessive_retries, wl->debugfs.rootdir); + +out: + if (ret < 0) + wl1271_debugfs_delete_files(wl); + + return ret; +} + +void wl1271_debugfs_reset(struct wl1271 *wl) +{ + memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats)); + wl->stats.retry_count = 0; + wl->stats.excessive_retries = 0; +} + +int wl1271_debugfs_init(struct wl1271 *wl) +{ + int ret; + + wl->debugfs.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); + + if (IS_ERR(wl->debugfs.rootdir)) { + ret = PTR_ERR(wl->debugfs.rootdir); + wl->debugfs.rootdir = NULL; + goto err; + } + + wl->debugfs.fw_statistics = debugfs_create_dir("fw-statistics", + wl->debugfs.rootdir); + + if (IS_ERR(wl->debugfs.fw_statistics)) { + ret = PTR_ERR(wl->debugfs.fw_statistics); + wl->debugfs.fw_statistics = NULL; + goto err_root; + } + + wl->stats.fw_stats = kzalloc(sizeof(*wl->stats.fw_stats), + GFP_KERNEL); + + if (!wl->stats.fw_stats) { + ret = -ENOMEM; + goto err_fw; + } + + wl->stats.fw_stats_update = jiffies; + + ret = wl1271_debugfs_add_files(wl); + + if (ret < 0) + goto err_file; + + return 0; + +err_file: + kfree(wl->stats.fw_stats); + wl->stats.fw_stats = NULL; + +err_fw: + debugfs_remove(wl->debugfs.fw_statistics); + wl->debugfs.fw_statistics = NULL; + +err_root: + debugfs_remove(wl->debugfs.rootdir); + wl->debugfs.rootdir = NULL; + +err: + return ret; +} + +void wl1271_debugfs_exit(struct wl1271 *wl) +{ + wl1271_debugfs_delete_files(wl); + + kfree(wl->stats.fw_stats); + wl->stats.fw_stats = NULL; + + debugfs_remove(wl->debugfs.fw_statistics); + wl->debugfs.fw_statistics = NULL; + + debugfs_remove(wl->debugfs.rootdir); + wl->debugfs.rootdir = NULL; + +} diff --git a/drivers/net/wireless/wl12xx/wl1271_debugfs.h b/drivers/net/wireless/wl12xx/wl1271_debugfs.h new file mode 100644 index 000000000000..00a45b2669ad --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_debugfs.h @@ -0,0 +1,33 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef WL1271_DEBUGFS_H +#define WL1271_DEBUGFS_H + +#include "wl1271.h" + +int wl1271_debugfs_init(struct wl1271 *wl); +void wl1271_debugfs_exit(struct wl1271 *wl); +void wl1271_debugfs_reset(struct wl1271 *wl); + +#endif /* WL1271_DEBUGFS_H */ diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c new file mode 100644 index 000000000000..f3afd4a6ff33 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_event.c @@ -0,0 +1,125 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2008-2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "wl1271.h" +#include "wl1271_reg.h" +#include "wl1271_spi.h" +#include "wl1271_event.h" +#include "wl1271_ps.h" + +static int wl1271_event_scan_complete(struct wl1271 *wl, + struct event_mailbox *mbox) +{ + wl1271_debug(DEBUG_EVENT, "status: 0x%x", + mbox->scheduled_scan_status); + + if (wl->scanning) { + mutex_unlock(&wl->mutex); + ieee80211_scan_completed(wl->hw, false); + mutex_lock(&wl->mutex); + wl->scanning = false; + } + + return 0; +} + +static void wl1271_event_mbox_dump(struct event_mailbox *mbox) +{ + wl1271_debug(DEBUG_EVENT, "MBOX DUMP:"); + wl1271_debug(DEBUG_EVENT, "\tvector: 0x%x", mbox->events_vector); + wl1271_debug(DEBUG_EVENT, "\tmask: 0x%x", mbox->events_mask); +} + +static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) +{ + int ret; + u32 vector; + + wl1271_event_mbox_dump(mbox); + + vector = mbox->events_vector & ~(mbox->events_mask); + wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector); + + if (vector & SCAN_COMPLETE_EVENT_ID) { + ret = wl1271_event_scan_complete(wl, mbox); + if (ret < 0) + return ret; + } + + if (vector & BSS_LOSE_EVENT_ID) { + wl1271_debug(DEBUG_EVENT, "BSS_LOSE_EVENT"); + + if (wl->psm_requested && wl->psm) { + ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE); + if (ret < 0) + return ret; + } + } + + return 0; +} + +int wl1271_event_unmask(struct wl1271 *wl) +{ + int ret; + + ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask)); + if (ret < 0) + return ret; + + return 0; +} + +void wl1271_event_mbox_config(struct wl1271 *wl) +{ + wl->mbox_ptr[0] = wl1271_reg_read32(wl, REG_EVENT_MAILBOX_PTR); + wl->mbox_ptr[1] = wl->mbox_ptr[0] + sizeof(struct event_mailbox); + + wl1271_debug(DEBUG_EVENT, "MBOX ptrs: 0x%x 0x%x", + wl->mbox_ptr[0], wl->mbox_ptr[1]); +} + +int wl1271_event_handle(struct wl1271 *wl, u8 mbox_num) +{ + struct event_mailbox mbox; + int ret; + + wl1271_debug(DEBUG_EVENT, "EVENT on mbox %d", mbox_num); + + if (mbox_num > 1) + return -EINVAL; + + /* first we read the mbox descriptor */ + wl1271_spi_mem_read(wl, wl->mbox_ptr[mbox_num], &mbox, + sizeof(struct event_mailbox)); + + /* process the descriptor */ + ret = wl1271_event_process(wl, &mbox); + if (ret < 0) + return ret; + + /* then we let the firmware know it can go on...*/ + wl1271_reg_write32(wl, ACX_REG_INTERRUPT_TRIG, INTR_TRIG_EVENT_ACK); + + return 0; +} diff --git a/drivers/net/wireless/wl12xx/wl1271_event.h b/drivers/net/wireless/wl12xx/wl1271_event.h new file mode 100644 index 000000000000..2cdce7c34bf0 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_event.h @@ -0,0 +1,110 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. + * Copyright (C) 2008-2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1271_EVENT_H__ +#define __WL1271_EVENT_H__ + +/* + * Mbox events + * + * The event mechanism is based on a pair of event buffers (buffers A and + * B) at fixed locations in the target's memory. The host processes one + * buffer while the other buffer continues to collect events. If the host + * is not processing events, an interrupt is issued to signal that a buffer + * is ready. Once the host is done with processing events from one buffer, + * it signals the target (with an ACK interrupt) that the event buffer is + * free. + */ + +enum { + MEASUREMENT_START_EVENT_ID = BIT(8), + MEASUREMENT_COMPLETE_EVENT_ID = BIT(9), + SCAN_COMPLETE_EVENT_ID = BIT(10), + SCHEDULED_SCAN_COMPLETE_EVENT_ID = BIT(11), + AP_DISCOVERY_COMPLETE_EVENT_ID = BIT(12), + PS_REPORT_EVENT_ID = BIT(13), + PSPOLL_DELIVERY_FAILURE_EVENT_ID = BIT(14), + DISCONNECT_EVENT_COMPLETE_ID = BIT(15), + JOIN_EVENT_COMPLETE_ID = BIT(16), + CHANNEL_SWITCH_COMPLETE_EVENT_ID = BIT(17), + BSS_LOSE_EVENT_ID = BIT(18), + REGAINED_BSS_EVENT_ID = BIT(19), + ROAMING_TRIGGER_MAX_TX_RETRY_EVENT_ID = BIT(20), + SOFT_GEMINI_SENSE_EVENT_ID = BIT(22), + SOFT_GEMINI_PREDICTION_EVENT_ID = BIT(23), + SOFT_GEMINI_AVALANCHE_EVENT_ID = BIT(24), + PLT_RX_CALIBRATION_COMPLETE_EVENT_ID = BIT(25), + DBG_EVENT_ID = BIT(26), + HEALTH_CHECK_REPLY_EVENT_ID = BIT(27), + PERIODIC_SCAN_COMPLETE_EVENT_ID = BIT(28), + PERIODIC_SCAN_REPORT_EVENT_ID = BIT(29), + BA_SESSION_TEAR_DOWN_EVENT_ID = BIT(30), + EVENT_MBOX_ALL_EVENT_ID = 0x7fffffff, +}; + +struct event_debug_report { + u8 debug_event_id; + u8 num_params; + u16 pad; + u32 report_1; + u32 report_2; + u32 report_3; +} __attribute__ ((packed)); + +#define NUM_OF_RSSI_SNR_TRIGGERS 8 + +struct event_mailbox { + u32 events_vector; + u32 events_mask; + u32 reserved_1; + u32 reserved_2; + + u8 dbg_event_id; + u8 num_relevant_params; + u16 reserved_3; + u32 event_report_p1; + u32 event_report_p2; + u32 event_report_p3; + + u8 number_of_scan_results; + u8 scan_tag; + u8 reserved_4[2]; + u32 compl_scheduled_scan_status; + + u16 scheduled_scan_attended_channels; + u8 soft_gemini_sense_info; + u8 soft_gemini_protective_info; + s8 rssi_snr_trigger_metric[NUM_OF_RSSI_SNR_TRIGGERS]; + u8 channel_switch_status; + u8 scheduled_scan_status; + u8 ps_status; + + u8 reserved_5[29]; +} __attribute__ ((packed)); + +int wl1271_event_unmask(struct wl1271 *wl); +void wl1271_event_mbox_config(struct wl1271 *wl); +int wl1271_event_handle(struct wl1271 *wl, u8 mbox); + +#endif diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c new file mode 100644 index 000000000000..490df217605a --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -0,0 +1,397 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> + +#include "wl1271_init.h" +#include "wl12xx_80211.h" +#include "wl1271_acx.h" +#include "wl1271_cmd.h" +#include "wl1271_reg.h" + +static int wl1271_init_hwenc_config(struct wl1271 *wl) +{ + int ret; + + ret = wl1271_acx_feature_cfg(wl); + if (ret < 0) { + wl1271_warning("couldn't set feature config"); + return ret; + } + + ret = wl1271_cmd_set_default_wep_key(wl, wl->default_key); + if (ret < 0) { + wl1271_warning("couldn't set default key"); + return ret; + } + + return 0; +} + +static int wl1271_init_templates_config(struct wl1271 *wl) +{ + int ret; + + /* send empty templates for fw memory reservation */ + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, NULL, + sizeof(struct wl12xx_probe_req_template)); + if (ret < 0) + return ret; + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, NULL, + sizeof(struct wl12xx_null_data_template)); + if (ret < 0) + return ret; + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, NULL, + sizeof(struct wl12xx_ps_poll_template)); + if (ret < 0) + return ret; + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, NULL, + sizeof + (struct wl12xx_qos_null_data_template)); + if (ret < 0) + return ret; + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, NULL, + sizeof + (struct wl12xx_probe_resp_template)); + if (ret < 0) + return ret; + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, NULL, + sizeof + (struct wl12xx_beacon_template)); + if (ret < 0) + return ret; + + return 0; +} + +static int wl1271_init_rx_config(struct wl1271 *wl, u32 config, u32 filter) +{ + int ret; + + ret = wl1271_acx_rx_msdu_life_time(wl, RX_MSDU_LIFETIME_DEF); + if (ret < 0) + return ret; + + ret = wl1271_acx_rx_config(wl, config, filter); + if (ret < 0) + return ret; + + return 0; +} + +static int wl1271_init_phy_config(struct wl1271 *wl) +{ + int ret; + + ret = wl1271_acx_pd_threshold(wl); + if (ret < 0) + return ret; + + ret = wl1271_acx_slot(wl, DEFAULT_SLOT_TIME); + if (ret < 0) + return ret; + + ret = wl1271_acx_group_address_tbl(wl); + if (ret < 0) + return ret; + + ret = wl1271_acx_service_period_timeout(wl); + if (ret < 0) + return ret; + + ret = wl1271_acx_rts_threshold(wl, RTS_THRESHOLD_DEF); + if (ret < 0) + return ret; + + return 0; +} + +static int wl1271_init_beacon_filter(struct wl1271 *wl) +{ + int ret; + + ret = wl1271_acx_beacon_filter_opt(wl); + if (ret < 0) + return ret; + + ret = wl1271_acx_beacon_filter_table(wl); + if (ret < 0) + return ret; + + return 0; +} + +static int wl1271_init_pta(struct wl1271 *wl) +{ + int ret; + + ret = wl1271_acx_sg_enable(wl); + if (ret < 0) + return ret; + + ret = wl1271_acx_sg_cfg(wl); + if (ret < 0) + return ret; + + return 0; +} + +static int wl1271_init_energy_detection(struct wl1271 *wl) +{ + int ret; + + ret = wl1271_acx_cca_threshold(wl); + if (ret < 0) + return ret; + + return 0; +} + +static int wl1271_init_beacon_broadcast(struct wl1271 *wl) +{ + int ret; + + ret = wl1271_acx_bcn_dtim_options(wl); + if (ret < 0) + return ret; + + return 0; +} + +static int wl1271_init_general_parms(struct wl1271 *wl) +{ + struct wl1271_general_parms *gen_parms; + int ret; + + gen_parms = kzalloc(sizeof(*gen_parms), GFP_KERNEL); + if (!gen_parms) + return -ENOMEM; + + gen_parms->id = TEST_CMD_INI_FILE_GENERAL_PARAM; + + gen_parms->ref_clk = REF_CLK_38_4_E; + /* FIXME: magic numbers */ + gen_parms->settling_time = 5; + gen_parms->clk_valid_on_wakeup = 0; + gen_parms->dc2dcmode = 0; + gen_parms->single_dual_band = 0; + gen_parms->tx_bip_fem_autodetect = 1; + gen_parms->tx_bip_fem_manufacturer = 1; + gen_parms->settings = 1; + + ret = wl1271_cmd_test(wl, gen_parms, sizeof(*gen_parms), 0); + if (ret < 0) { + wl1271_warning("CMD_INI_FILE_GENERAL_PARAM failed"); + return ret; + } + + kfree(gen_parms); + return 0; +} + +static int wl1271_init_radio_parms(struct wl1271 *wl) +{ + /* + * FIXME: All these magic numbers should be moved to some place where + * they can be configured (separate file?) + */ + + struct wl1271_radio_parms *radio_parms; + int ret; + u8 compensation[] = { 0xec, 0xf6, 0x00, 0x0c, 0x18, 0xf8, 0xfc, 0x00, + 0x08, 0x10, 0xf0, 0xf8, 0x00, 0x0a, 0x14 }; + + u8 tx_rate_limits_normal[] = { 0x1e, 0x1f, 0x22, 0x24, 0x28, 0x29 }; + u8 tx_rate_limits_degraded[] = { 0x1b, 0x1c, 0x1e, 0x20, 0x24, 0x25 }; + + u8 tx_channel_limits_11b[] = { 0x22, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x22, 0x50, + 0x22, 0x50 }; + + u8 tx_channel_limits_ofdm[] = { 0x20, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x50, 0x50, + 0x50, 0x50, 0x20, 0x50, + 0x20, 0x50 }; + + u8 tx_pdv_rate_offsets[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + u8 tx_ibias[] = { 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x27 }; + + radio_parms = kzalloc(sizeof(*radio_parms), GFP_KERNEL); + if (!radio_parms) + return -ENOMEM; + + radio_parms->id = TEST_CMD_INI_FILE_RADIO_PARAM; + + /* Static radio parameters */ + radio_parms->rx_trace_loss = 10; + radio_parms->tx_trace_loss = 10; + memcpy(radio_parms->rx_rssi_and_proc_compens, compensation, + sizeof(compensation)); + + /* We don't set the 5GHz -- N/A */ + + /* Dynamic radio parameters */ + radio_parms->tx_ref_pd_voltage = cpu_to_le16(0x24e); + radio_parms->tx_ref_power = 0x78; + radio_parms->tx_offset_db = 0x0; + + memcpy(radio_parms->tx_rate_limits_normal, tx_rate_limits_normal, + sizeof(tx_rate_limits_normal)); + memcpy(radio_parms->tx_rate_limits_degraded, tx_rate_limits_degraded, + sizeof(tx_rate_limits_degraded)); + + memcpy(radio_parms->tx_channel_limits_11b, tx_channel_limits_11b, + sizeof(tx_channel_limits_11b)); + memcpy(radio_parms->tx_channel_limits_ofdm, tx_channel_limits_ofdm, + sizeof(tx_channel_limits_ofdm)); + memcpy(radio_parms->tx_pdv_rate_offsets, tx_pdv_rate_offsets, + sizeof(tx_pdv_rate_offsets)); + memcpy(radio_parms->tx_ibias, tx_ibias, + sizeof(tx_ibias)); + + radio_parms->rx_fem_insertion_loss = 0x14; + + ret = wl1271_cmd_test(wl, radio_parms, sizeof(*radio_parms), 0); + if (ret < 0) + wl1271_warning("CMD_INI_FILE_RADIO_PARAM failed"); + + kfree(radio_parms); + return ret; +} + +int wl1271_hw_init(struct wl1271 *wl) +{ + int ret; + + ret = wl1271_init_general_parms(wl); + if (ret < 0) + return ret; + + ret = wl1271_init_radio_parms(wl); + if (ret < 0) + return ret; + + /* Template settings */ + ret = wl1271_init_templates_config(wl); + if (ret < 0) + return ret; + + /* Default memory configuration */ + ret = wl1271_acx_init_mem_config(wl); + if (ret < 0) + return ret; + + /* RX config */ + ret = wl1271_init_rx_config(wl, + RX_CFG_PROMISCUOUS | RX_CFG_TSF, + RX_FILTER_OPTION_DEF); + /* RX_CONFIG_OPTION_ANY_DST_ANY_BSS, + RX_FILTER_OPTION_FILTER_ALL); */ + if (ret < 0) + goto out_free_memmap; + + /* PHY layer config */ + ret = wl1271_init_phy_config(wl); + if (ret < 0) + goto out_free_memmap; + + /* Beacon filtering */ + ret = wl1271_init_beacon_filter(wl); + if (ret < 0) + goto out_free_memmap; + + /* Configure TX patch complete interrupt behavior */ + ret = wl1271_acx_tx_config_options(wl); + if (ret < 0) + goto out_free_memmap; + + /* RX complete interrupt pacing */ + ret = wl1271_acx_init_rx_interrupt(wl); + if (ret < 0) + goto out_free_memmap; + + /* Bluetooth WLAN coexistence */ + ret = wl1271_init_pta(wl); + if (ret < 0) + goto out_free_memmap; + + /* Energy detection */ + ret = wl1271_init_energy_detection(wl); + if (ret < 0) + goto out_free_memmap; + + /* Beacons and boradcast settings */ + ret = wl1271_init_beacon_broadcast(wl); + if (ret < 0) + goto out_free_memmap; + + /* Default fragmentation threshold */ + ret = wl1271_acx_frag_threshold(wl); + if (ret < 0) + goto out_free_memmap; + + /* Default TID configuration */ + ret = wl1271_acx_tid_cfg(wl); + if (ret < 0) + goto out_free_memmap; + + /* Default AC configuration */ + ret = wl1271_acx_ac_cfg(wl); + if (ret < 0) + goto out_free_memmap; + + /* Configure TX rate classes */ + ret = wl1271_acx_rate_policies(wl); + if (ret < 0) + goto out_free_memmap; + + /* Enable data path */ + ret = wl1271_cmd_data_path(wl, wl->channel, 1); + if (ret < 0) + goto out_free_memmap; + + /* Configure for ELP power saving */ + ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); + if (ret < 0) + goto out_free_memmap; + + /* Configure HW encryption */ + ret = wl1271_init_hwenc_config(wl); + if (ret < 0) + goto out_free_memmap; + + return 0; + + out_free_memmap: + kfree(wl->target_mem_map); + + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1271_init.h b/drivers/net/wireless/wl12xx/wl1271_init.h new file mode 100644 index 000000000000..bd8ff0fa2272 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_init.h @@ -0,0 +1,115 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1271_INIT_H__ +#define __WL1271_INIT_H__ + +#include "wl1271.h" + +int wl1271_hw_init_power_auth(struct wl1271 *wl); +int wl1271_hw_init(struct wl1271 *wl); + +/* These are not really a TEST_CMD, but the ref driver uses them as such */ +#define TEST_CMD_INI_FILE_RADIO_PARAM 0x19 +#define TEST_CMD_INI_FILE_GENERAL_PARAM 0x1E + +struct wl1271_general_parms { + u8 id; + u8 padding[3]; + + u8 ref_clk; + u8 settling_time; + u8 clk_valid_on_wakeup; + u8 dc2dcmode; + u8 single_dual_band; + + u8 tx_bip_fem_autodetect; + u8 tx_bip_fem_manufacturer; + u8 settings; +} __attribute__ ((packed)); + +enum ref_clk_enum { + REF_CLK_19_2_E, + REF_CLK_26_E, + REF_CLK_38_4_E, + REF_CLK_52_E +}; + +#define RSSI_AND_PROCESS_COMPENSATION_SIZE 15 +#define NUMBER_OF_SUB_BANDS_5 7 +#define NUMBER_OF_RATE_GROUPS 6 +#define NUMBER_OF_CHANNELS_2_4 14 +#define NUMBER_OF_CHANNELS_5 35 + +struct wl1271_radio_parms { + u8 id; + u8 padding[3]; + + /* Static radio parameters */ + /* 2.4GHz */ + u8 rx_trace_loss; + u8 tx_trace_loss; + s8 rx_rssi_and_proc_compens[RSSI_AND_PROCESS_COMPENSATION_SIZE]; + + /* 5GHz */ + u8 rx_trace_loss_5[NUMBER_OF_SUB_BANDS_5]; + u8 tx_trace_loss_5[NUMBER_OF_SUB_BANDS_5]; + s8 rx_rssi_and_proc_compens_5[RSSI_AND_PROCESS_COMPENSATION_SIZE]; + + /* Dynamic radio parameters */ + /* 2.4GHz */ + s16 tx_ref_pd_voltage; + s8 tx_ref_power; + s8 tx_offset_db; + + s8 tx_rate_limits_normal[NUMBER_OF_RATE_GROUPS]; + s8 tx_rate_limits_degraded[NUMBER_OF_RATE_GROUPS]; + + s8 tx_channel_limits_11b[NUMBER_OF_CHANNELS_2_4]; + s8 tx_channel_limits_ofdm[NUMBER_OF_CHANNELS_2_4]; + s8 tx_pdv_rate_offsets[NUMBER_OF_RATE_GROUPS]; + + u8 tx_ibias[NUMBER_OF_RATE_GROUPS]; + u8 rx_fem_insertion_loss; + + u8 padding2; + + /* 5GHz */ + s16 tx_ref_pd_voltage_5[NUMBER_OF_SUB_BANDS_5]; + s8 tx_ref_power_5[NUMBER_OF_SUB_BANDS_5]; + s8 tx_offset_db_5[NUMBER_OF_SUB_BANDS_5]; + + s8 tx_rate_limits_normal_5[NUMBER_OF_RATE_GROUPS]; + s8 tx_rate_limits_degraded_5[NUMBER_OF_RATE_GROUPS]; + + s8 tx_channel_limits_ofdm_5[NUMBER_OF_CHANNELS_5]; + s8 tx_pdv_rate_offsets_5[NUMBER_OF_RATE_GROUPS]; + + /* FIXME: this is inconsistent with the types for 2.4GHz */ + s8 tx_ibias_5[NUMBER_OF_RATE_GROUPS]; + s8 rx_fem_insertion_loss_5[NUMBER_OF_SUB_BANDS_5]; + + u8 padding3[2]; +} __attribute__ ((packed)); + +#endif diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c new file mode 100644 index 000000000000..d9169b47ac42 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -0,0 +1,1394 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2008-2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/firmware.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/spi/spi.h> +#include <linux/crc32.h> +#include <linux/etherdevice.h> +#include <linux/spi/wl12xx.h> + +#include "wl1271.h" +#include "wl12xx_80211.h" +#include "wl1271_reg.h" +#include "wl1271_spi.h" +#include "wl1271_event.h" +#include "wl1271_tx.h" +#include "wl1271_rx.h" +#include "wl1271_ps.h" +#include "wl1271_init.h" +#include "wl1271_debugfs.h" +#include "wl1271_cmd.h" +#include "wl1271_boot.h" + +static int wl1271_plt_init(struct wl1271 *wl) +{ + int ret; + + ret = wl1271_acx_init_mem_config(wl); + if (ret < 0) + return ret; + + ret = wl1271_cmd_data_path(wl, wl->channel, 1); + if (ret < 0) + return ret; + + return 0; +} + +static void wl1271_disable_interrupts(struct wl1271 *wl) +{ + disable_irq(wl->irq); +} + +static void wl1271_power_off(struct wl1271 *wl) +{ + wl->set_power(false); +} + +static void wl1271_power_on(struct wl1271 *wl) +{ + wl->set_power(true); +} + +static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_status *status) +{ + u32 total = 0; + int i; + + /* + * FIXME: Reading the FW status directly from the registers seems to + * be the right thing to do, but it doesn't work. And in the + * reference driver, there is a workaround called + * USE_SDIO_24M_WORKAROUND, which reads the status from memory + * instead, so we do the same here. + */ + + wl1271_spi_mem_read(wl, STATUS_MEM_ADDRESS, status, sizeof(*status)); + + wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " + "drv_rx_counter = %d, tx_results_counter = %d)", + status->intr, + status->fw_rx_counter, + status->drv_rx_counter, + status->tx_results_counter); + + /* update number of available TX blocks */ + for (i = 0; i < NUM_TX_QUEUES; i++) { + u32 cnt = status->tx_released_blks[i] - wl->tx_blocks_freed[i]; + wl->tx_blocks_freed[i] = status->tx_released_blks[i]; + wl->tx_blocks_available += cnt; + total += cnt; + } + + /* if more blocks are available now, schedule some tx work */ + if (total && !skb_queue_empty(&wl->tx_queue)) + schedule_work(&wl->tx_work); + + /* update the host-chipset time offset */ + wl->time_offset = jiffies_to_usecs(jiffies) - status->fw_localtime; +} + +#define WL1271_IRQ_MAX_LOOPS 10 +static void wl1271_irq_work(struct work_struct *work) +{ + u32 intr, ctr = WL1271_IRQ_MAX_LOOPS; + int ret; + struct wl1271 *wl = + container_of(work, struct wl1271, irq_work); + + mutex_lock(&wl->mutex); + + wl1271_debug(DEBUG_IRQ, "IRQ work"); + + if (wl->state == WL1271_STATE_OFF) + goto out; + + ret = wl1271_ps_elp_wakeup(wl, true); + if (ret < 0) + goto out; + + wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); + + intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); + if (!intr) { + wl1271_debug(DEBUG_IRQ, "Zero interrupt received."); + goto out_sleep; + } + + intr &= WL1271_INTR_MASK; + + do { + wl1271_fw_status(wl, wl->fw_status); + + + if (intr & (WL1271_ACX_INTR_EVENT_A | + WL1271_ACX_INTR_EVENT_B)) { + wl1271_debug(DEBUG_IRQ, + "WL1271_ACX_INTR_EVENT (0x%x)", intr); + if (intr & WL1271_ACX_INTR_EVENT_A) + wl1271_event_handle(wl, 0); + else + wl1271_event_handle(wl, 1); + } + + if (intr & WL1271_ACX_INTR_INIT_COMPLETE) + wl1271_debug(DEBUG_IRQ, + "WL1271_ACX_INTR_INIT_COMPLETE"); + + if (intr & WL1271_ACX_INTR_HW_AVAILABLE) + wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE"); + + if (intr & WL1271_ACX_INTR_DATA) { + u8 tx_res_cnt = wl->fw_status->tx_results_counter - + wl->tx_results_count; + + wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); + + /* check for tx results */ + if (tx_res_cnt) + wl1271_tx_complete(wl, tx_res_cnt); + + wl1271_rx(wl, wl->fw_status); + } + + intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); + intr &= WL1271_INTR_MASK; + } while (intr && --ctr); + +out_sleep: + wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, + WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); +} + +static irqreturn_t wl1271_irq(int irq, void *cookie) +{ + struct wl1271 *wl; + unsigned long flags; + + wl1271_debug(DEBUG_IRQ, "IRQ"); + + wl = cookie; + + /* complete the ELP completion */ + spin_lock_irqsave(&wl->wl_lock, flags); + if (wl->elp_compl) { + complete(wl->elp_compl); + wl->elp_compl = NULL; + } + + schedule_work(&wl->irq_work); + spin_unlock_irqrestore(&wl->wl_lock, flags); + + return IRQ_HANDLED; +} + +static int wl1271_fetch_firmware(struct wl1271 *wl) +{ + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, WL1271_FW_NAME, &wl->spi->dev); + + if (ret < 0) { + wl1271_error("could not get firmware: %d", ret); + return ret; + } + + if (fw->size % 4) { + wl1271_error("firmware size is not multiple of 32 bits: %zu", + fw->size); + ret = -EILSEQ; + goto out; + } + + wl->fw_len = fw->size; + wl->fw = kmalloc(wl->fw_len, GFP_KERNEL); + + if (!wl->fw) { + wl1271_error("could not allocate memory for the firmware"); + ret = -ENOMEM; + goto out; + } + + memcpy(wl->fw, fw->data, wl->fw_len); + + ret = 0; + +out: + release_firmware(fw); + + return ret; +} + +static int wl1271_fetch_nvs(struct wl1271 *wl) +{ + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, WL1271_NVS_NAME, &wl->spi->dev); + + if (ret < 0) { + wl1271_error("could not get nvs file: %d", ret); + return ret; + } + + if (fw->size % 4) { + wl1271_error("nvs size is not multiple of 32 bits: %zu", + fw->size); + ret = -EILSEQ; + goto out; + } + + wl->nvs_len = fw->size; + wl->nvs = kmalloc(wl->nvs_len, GFP_KERNEL); + + if (!wl->nvs) { + wl1271_error("could not allocate memory for the nvs file"); + ret = -ENOMEM; + goto out; + } + + memcpy(wl->nvs, fw->data, wl->nvs_len); + + ret = 0; + +out: + release_firmware(fw); + + return ret; +} + +static void wl1271_fw_wakeup(struct wl1271 *wl) +{ + u32 elp_reg; + + elp_reg = ELPCTRL_WAKE_UP; + wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, elp_reg); +} + +static int wl1271_setup(struct wl1271 *wl) +{ + wl->fw_status = kmalloc(sizeof(*wl->fw_status), GFP_KERNEL); + if (!wl->fw_status) + return -ENOMEM; + + wl->tx_res_if = kmalloc(sizeof(*wl->tx_res_if), GFP_KERNEL); + if (!wl->tx_res_if) { + kfree(wl->fw_status); + return -ENOMEM; + } + + INIT_WORK(&wl->irq_work, wl1271_irq_work); + INIT_WORK(&wl->tx_work, wl1271_tx_work); + return 0; +} + +static int wl1271_chip_wakeup(struct wl1271 *wl) +{ + int ret = 0; + + wl1271_power_on(wl); + msleep(WL1271_POWER_ON_SLEEP); + wl1271_spi_reset(wl); + wl1271_spi_init(wl); + + /* We don't need a real memory partition here, because we only want + * to use the registers at this point. */ + wl1271_set_partition(wl, + 0x00000000, + 0x00000000, + REGISTERS_BASE, + REGISTERS_DOWN_SIZE); + + /* ELP module wake up */ + wl1271_fw_wakeup(wl); + + /* whal_FwCtrl_BootSm() */ + + /* 0. read chip id from CHIP_ID */ + wl->chip.id = wl1271_reg_read32(wl, CHIP_ID_B); + + /* 1. check if chip id is valid */ + + switch (wl->chip.id) { + case CHIP_ID_1271_PG10: + wl1271_warning("chip id 0x%x (1271 PG10) support is obsolete", + wl->chip.id); + + ret = wl1271_setup(wl); + if (ret < 0) + goto out; + break; + case CHIP_ID_1271_PG20: + wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1271 PG20)", + wl->chip.id); + + ret = wl1271_setup(wl); + if (ret < 0) + goto out; + break; + default: + wl1271_error("unsupported chip id: 0x%x", wl->chip.id); + ret = -ENODEV; + goto out; + } + + if (wl->fw == NULL) { + ret = wl1271_fetch_firmware(wl); + if (ret < 0) + goto out; + } + + /* No NVS from netlink, try to get it from the filesystem */ + if (wl->nvs == NULL) { + ret = wl1271_fetch_nvs(wl); + if (ret < 0) + goto out; + } + +out: + return ret; +} + +static void wl1271_filter_work(struct work_struct *work) +{ + struct wl1271 *wl = + container_of(work, struct wl1271, filter_work); + int ret; + + mutex_lock(&wl->mutex); + + if (wl->state == WL1271_STATE_OFF) + goto out; + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + /* FIXME: replace the magic numbers with proper definitions */ + ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); + if (ret < 0) + goto out_sleep; + +out_sleep: + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); +} + +int wl1271_plt_start(struct wl1271 *wl) +{ + int ret; + + mutex_lock(&wl->mutex); + + wl1271_notice("power up"); + + if (wl->state != WL1271_STATE_OFF) { + wl1271_error("cannot go into PLT state because not " + "in off state: %d", wl->state); + ret = -EBUSY; + goto out; + } + + wl->state = WL1271_STATE_PLT; + + ret = wl1271_chip_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl1271_boot(wl); + if (ret < 0) + goto out; + + wl1271_notice("firmware booted in PLT mode (%s)", wl->chip.fw_ver); + + ret = wl1271_plt_init(wl); + if (ret < 0) + goto out; + +out: + mutex_unlock(&wl->mutex); + + return ret; +} + +int wl1271_plt_stop(struct wl1271 *wl) +{ + int ret = 0; + + mutex_lock(&wl->mutex); + + wl1271_notice("power down"); + + if (wl->state != WL1271_STATE_PLT) { + wl1271_error("cannot power down because not in PLT " + "state: %d", wl->state); + ret = -EBUSY; + goto out; + } + + wl1271_disable_interrupts(wl); + wl1271_power_off(wl); + + wl->state = WL1271_STATE_OFF; + +out: + mutex_unlock(&wl->mutex); + + return ret; +} + + +static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct wl1271 *wl = hw->priv; + + skb_queue_tail(&wl->tx_queue, skb); + + /* + * The chip specific setup must run before the first TX packet - + * before that, the tx_work will not be initialized! + */ + + schedule_work(&wl->tx_work); + + /* + * The workqueue is slow to process the tx_queue and we need stop + * the queue here, otherwise the queue will get too long. + */ + if (skb_queue_len(&wl->tx_queue) >= WL1271_TX_QUEUE_MAX_LENGTH) { + ieee80211_stop_queues(wl->hw); + + /* + * FIXME: this is racy, the variable is not properly + * protected. Maybe fix this by removing the stupid + * variable altogether and checking the real queue state? + */ + wl->tx_queue_stopped = true; + } + + return NETDEV_TX_OK; +} + +static int wl1271_op_start(struct ieee80211_hw *hw) +{ + struct wl1271 *wl = hw->priv; + int ret = 0; + + wl1271_debug(DEBUG_MAC80211, "mac80211 start"); + + mutex_lock(&wl->mutex); + + if (wl->state != WL1271_STATE_OFF) { + wl1271_error("cannot start because not in off state: %d", + wl->state); + ret = -EBUSY; + goto out; + } + + ret = wl1271_chip_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl1271_boot(wl); + if (ret < 0) + goto out; + + ret = wl1271_hw_init(wl); + if (ret < 0) + goto out; + + wl->state = WL1271_STATE_ON; + + wl1271_info("firmware booted (%s)", wl->chip.fw_ver); + +out: + if (ret < 0) + wl1271_power_off(wl); + + mutex_unlock(&wl->mutex); + + return ret; +} + +static void wl1271_op_stop(struct ieee80211_hw *hw) +{ + struct wl1271 *wl = hw->priv; + int i; + + wl1271_info("down"); + + wl1271_debug(DEBUG_MAC80211, "mac80211 stop"); + + mutex_lock(&wl->mutex); + + WARN_ON(wl->state != WL1271_STATE_ON); + + if (wl->scanning) { + mutex_unlock(&wl->mutex); + ieee80211_scan_completed(wl->hw, true); + mutex_lock(&wl->mutex); + wl->scanning = false; + } + + wl->state = WL1271_STATE_OFF; + + wl1271_disable_interrupts(wl); + + mutex_unlock(&wl->mutex); + + cancel_work_sync(&wl->irq_work); + cancel_work_sync(&wl->tx_work); + cancel_work_sync(&wl->filter_work); + + mutex_lock(&wl->mutex); + + /* let's notify MAC80211 about the remaining pending TX frames */ + wl1271_tx_flush(wl); + wl1271_power_off(wl); + + memset(wl->bssid, 0, ETH_ALEN); + memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1); + wl->ssid_len = 0; + wl->listen_int = 1; + wl->bss_type = MAX_BSS_TYPE; + + wl->rx_counter = 0; + wl->elp = false; + wl->psm = 0; + wl->tx_queue_stopped = false; + wl->power_level = WL1271_DEFAULT_POWER_LEVEL; + wl->tx_blocks_available = 0; + wl->tx_results_count = 0; + wl->tx_packets_count = 0; + wl->time_offset = 0; + wl->session_counter = 0; + for (i = 0; i < NUM_TX_QUEUES; i++) + wl->tx_blocks_freed[i] = 0; + + wl1271_debugfs_reset(wl); + mutex_unlock(&wl->mutex); +} + +static int wl1271_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + struct wl1271 *wl = hw->priv; + int ret = 0; + + wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", + conf->type, conf->mac_addr); + + mutex_lock(&wl->mutex); + + switch (conf->type) { + case NL80211_IFTYPE_STATION: + wl->bss_type = BSS_TYPE_STA_BSS; + break; + case NL80211_IFTYPE_ADHOC: + wl->bss_type = BSS_TYPE_IBSS; + break; + default: + ret = -EOPNOTSUPP; + goto out; + } + + /* FIXME: what if conf->mac_addr changes? */ + +out: + mutex_unlock(&wl->mutex); + return ret; +} + +static void wl1271_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_if_init_conf *conf) +{ + wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); +} + +#if 0 +static int wl1271_op_config_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_if_conf *conf) +{ + struct wl1271 *wl = hw->priv; + struct sk_buff *beacon; + DECLARE_MAC_BUF(mac); + int ret; + + wl1271_debug(DEBUG_MAC80211, "mac80211 config_interface bssid %s", + print_mac(mac, conf->bssid)); + wl1271_dump_ascii(DEBUG_MAC80211, "ssid: ", conf->ssid, + conf->ssid_len); + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + memcpy(wl->bssid, conf->bssid, ETH_ALEN); + + ret = wl1271_cmd_build_null_data(wl); + if (ret < 0) + goto out_sleep; + + wl->ssid_len = conf->ssid_len; + if (wl->ssid_len) + memcpy(wl->ssid, conf->ssid, wl->ssid_len); + + if (wl->bss_type != BSS_TYPE_IBSS) { + /* FIXME: replace the magic numbers with proper definitions */ + ret = wl1271_cmd_join(wl, wl->bss_type, 5, 100, 1); + if (ret < 0) + goto out_sleep; + } + + if (conf->changed & IEEE80211_IFCC_BEACON) { + beacon = ieee80211_beacon_get(hw, vif); + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, + beacon->data, beacon->len); + + if (ret < 0) { + dev_kfree_skb(beacon); + goto out_sleep; + } + + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, + beacon->data, beacon->len); + + dev_kfree_skb(beacon); + + if (ret < 0) + goto out_sleep; + + /* FIXME: replace the magic numbers with proper definitions */ + ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); + + if (ret < 0) + goto out_sleep; + } + +out_sleep: + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + + return ret; +} +#endif + +static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) +{ + struct wl1271 *wl = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + int channel, ret = 0; + + channel = ieee80211_frequency_to_channel(conf->channel->center_freq); + + wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d", + channel, + conf->flags & IEEE80211_CONF_PS ? "on" : "off", + conf->power_level); + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + if (channel != wl->channel) { + u8 old_channel = wl->channel; + wl->channel = channel; + + /* FIXME: use beacon interval provided by mac80211 */ + ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); + if (ret < 0) { + wl->channel = old_channel; + goto out_sleep; + } + } + + ret = wl1271_cmd_build_null_data(wl); + if (ret < 0) + goto out_sleep; + + if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { + wl1271_info("psm enabled"); + + wl->psm_requested = true; + + /* + * We enter PSM only if we're already associated. + * If we're not, we'll enter it when joining an SSID, + * through the bss_info_changed() hook. + */ + ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + } else if (!(conf->flags & IEEE80211_CONF_PS) && + wl->psm_requested) { + wl1271_info("psm disabled"); + + wl->psm_requested = false; + + if (wl->psm) + ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE); + } + + if (conf->power_level != wl->power_level) { + ret = wl1271_acx_tx_power(wl, conf->power_level); + if (ret < 0) + goto out; + + wl->power_level = conf->power_level; + } + +out_sleep: + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + + return ret; +} + +#define WL1271_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ + FIF_ALLMULTI | \ + FIF_FCSFAIL | \ + FIF_BCN_PRBRESP_PROMISC | \ + FIF_CONTROL | \ + FIF_OTHER_BSS) + +static void wl1271_op_configure_filter(struct ieee80211_hw *hw, + unsigned int changed, + unsigned int *total,u64 multicast) +{ + struct wl1271 *wl = hw->priv; + + wl1271_debug(DEBUG_MAC80211, "mac80211 configure filter"); + + *total &= WL1271_SUPPORTED_FILTERS; + changed &= WL1271_SUPPORTED_FILTERS; + + if (changed == 0) + return; + + /* FIXME: wl->rx_config and wl->rx_filter are not protected */ + wl->rx_config = WL1271_DEFAULT_RX_CONFIG; + wl->rx_filter = WL1271_DEFAULT_RX_FILTER; + + /* + * FIXME: workqueues need to be properly cancelled on stop(), for + * now let's just disable changing the filter settings. They will + * be updated any on config(). + */ + /* schedule_work(&wl->filter_work); */ +} + +static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key_conf) +{ + struct wl1271 *wl = hw->priv; + const u8 *addr; + int ret; + u8 key_type; + + static const u8 bcast_addr[ETH_ALEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + wl1271_debug(DEBUG_MAC80211, "mac80211 set key"); + + addr = sta ? sta->addr : bcast_addr; + + wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd); + wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN); + wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x", + key_conf->alg, key_conf->keyidx, + key_conf->keylen, key_conf->flags); + wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen); + + if (is_zero_ether_addr(addr)) { + /* We dont support TX only encryption */ + ret = -EOPNOTSUPP; + goto out; + } + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out_unlock; + + switch (key_conf->alg) { + case ALG_WEP: + key_type = KEY_WEP; + + key_conf->hw_key_idx = key_conf->keyidx; + break; + case ALG_TKIP: + key_type = KEY_TKIP; + + key_conf->hw_key_idx = key_conf->keyidx; + break; + case ALG_CCMP: + key_type = KEY_AES; + + key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + break; + default: + wl1271_error("Unknown key algo 0x%x", key_conf->alg); + + ret = -EOPNOTSUPP; + goto out_sleep; + } + + switch (cmd) { + case SET_KEY: + ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE, + key_conf->keyidx, key_type, + key_conf->keylen, key_conf->key, + addr); + if (ret < 0) { + wl1271_error("Could not add or replace key"); + goto out_sleep; + } + break; + + case DISABLE_KEY: + ret = wl1271_cmd_set_key(wl, KEY_REMOVE, + key_conf->keyidx, key_type, + key_conf->keylen, key_conf->key, + addr); + if (ret < 0) { + wl1271_error("Could not remove key"); + goto out_sleep; + } + break; + + default: + wl1271_error("Unsupported key cmd 0x%x", cmd); + ret = -EOPNOTSUPP; + goto out_sleep; + + break; + } + +out_sleep: + wl1271_ps_elp_sleep(wl); + +out_unlock: + mutex_unlock(&wl->mutex); + +out: + return ret; +} + +static int wl1271_op_hw_scan(struct ieee80211_hw *hw, + struct cfg80211_scan_request *req) +{ + struct wl1271 *wl = hw->priv; + int ret; + u8 *ssid = NULL; + size_t ssid_len = 0; + + wl1271_debug(DEBUG_MAC80211, "mac80211 hw scan"); + + if (req->n_ssids) { + ssid = req->ssids[0].ssid; + ssid_len = req->ssids[0].ssid_len; + } + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + ret = wl1271_cmd_scan(hw->priv, ssid, ssid_len, 1, 0, 13, 3); + + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + + return ret; +} + +static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct wl1271 *wl = hw->priv; + int ret; + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + ret = wl1271_acx_rts_threshold(wl, (u16) value); + if (ret < 0) + wl1271_warning("wl1271_op_set_rts_threshold failed: %d", ret); + + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); + + return ret; +} + +static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + enum wl1271_cmd_ps_mode mode; + struct wl1271 *wl = hw->priv; + int ret; + + wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed"); + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + if (changed & BSS_CHANGED_ASSOC) { + if (bss_conf->assoc) { + wl->aid = bss_conf->aid; + + ret = wl1271_cmd_build_ps_poll(wl, wl->aid); + if (ret < 0) + goto out_sleep; + + ret = wl1271_acx_aid(wl, wl->aid); + if (ret < 0) + goto out_sleep; + + /* If we want to go in PSM but we're not there yet */ + if (wl->psm_requested && !wl->psm) { + mode = STATION_POWER_SAVE_MODE; + ret = wl1271_ps_set_mode(wl, mode); + if (ret < 0) + goto out_sleep; + } + } + } + if (changed & BSS_CHANGED_ERP_SLOT) { + if (bss_conf->use_short_slot) + ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT); + else + ret = wl1271_acx_slot(wl, SLOT_TIME_LONG); + if (ret < 0) { + wl1271_warning("Set slot time failed %d", ret); + goto out_sleep; + } + } + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + if (bss_conf->use_short_preamble) + wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT); + else + wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG); + } + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + if (bss_conf->use_cts_prot) + ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE); + else + ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE); + if (ret < 0) { + wl1271_warning("Set ctsprotect failed %d", ret); + goto out_sleep; + } + } + +out_sleep: + wl1271_ps_elp_sleep(wl); + +out: + mutex_unlock(&wl->mutex); +} + + +/* can't be const, mac80211 writes to this */ +static struct ieee80211_rate wl1271_rates[] = { + { .bitrate = 10, + .hw_value = 0x1, + .hw_value_short = 0x1, }, + { .bitrate = 20, + .hw_value = 0x2, + .hw_value_short = 0x2, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 55, + .hw_value = 0x4, + .hw_value_short = 0x4, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 110, + .hw_value = 0x20, + .hw_value_short = 0x20, + .flags = IEEE80211_RATE_SHORT_PREAMBLE }, + { .bitrate = 60, + .hw_value = 0x8, + .hw_value_short = 0x8, }, + { .bitrate = 90, + .hw_value = 0x10, + .hw_value_short = 0x10, }, + { .bitrate = 120, + .hw_value = 0x40, + .hw_value_short = 0x40, }, + { .bitrate = 180, + .hw_value = 0x80, + .hw_value_short = 0x80, }, + { .bitrate = 240, + .hw_value = 0x200, + .hw_value_short = 0x200, }, + { .bitrate = 360, + .hw_value = 0x400, + .hw_value_short = 0x400, }, + { .bitrate = 480, + .hw_value = 0x800, + .hw_value_short = 0x800, }, + { .bitrate = 540, + .hw_value = 0x1000, + .hw_value_short = 0x1000, }, +}; + +/* can't be const, mac80211 writes to this */ +static struct ieee80211_channel wl1271_channels[] = { + { .hw_value = 1, .center_freq = 2412}, + { .hw_value = 2, .center_freq = 2417}, + { .hw_value = 3, .center_freq = 2422}, + { .hw_value = 4, .center_freq = 2427}, + { .hw_value = 5, .center_freq = 2432}, + { .hw_value = 6, .center_freq = 2437}, + { .hw_value = 7, .center_freq = 2442}, + { .hw_value = 8, .center_freq = 2447}, + { .hw_value = 9, .center_freq = 2452}, + { .hw_value = 10, .center_freq = 2457}, + { .hw_value = 11, .center_freq = 2462}, + { .hw_value = 12, .center_freq = 2467}, + { .hw_value = 13, .center_freq = 2472}, +}; + +/* can't be const, mac80211 writes to this */ +static struct ieee80211_supported_band wl1271_band_2ghz = { + .channels = wl1271_channels, + .n_channels = ARRAY_SIZE(wl1271_channels), + .bitrates = wl1271_rates, + .n_bitrates = ARRAY_SIZE(wl1271_rates), +}; + +static const struct ieee80211_ops wl1271_ops = { + .start = wl1271_op_start, + .stop = wl1271_op_stop, + .add_interface = wl1271_op_add_interface, + .remove_interface = wl1271_op_remove_interface, + .config = wl1271_op_config, +/* .config_interface = wl1271_op_config_interface, */ + .configure_filter = wl1271_op_configure_filter, + .tx = wl1271_op_tx, + .set_key = wl1271_op_set_key, + .hw_scan = wl1271_op_hw_scan, + .bss_info_changed = wl1271_op_bss_info_changed, + .set_rts_threshold = wl1271_op_set_rts_threshold, +}; + +static int wl1271_register_hw(struct wl1271 *wl) +{ + int ret; + + if (wl->mac80211_registered) + return 0; + + SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); + + ret = ieee80211_register_hw(wl->hw); + if (ret < 0) { + wl1271_error("unable to register mac80211 hw: %d", ret); + return ret; + } + + wl->mac80211_registered = true; + + wl1271_notice("loaded"); + + return 0; +} + +static int wl1271_init_ieee80211(struct wl1271 *wl) +{ + /* + * The tx descriptor buffer and the TKIP space. + * + * FIXME: add correct 1271 descriptor size + */ + wl->hw->extra_tx_headroom = WL1271_TKIP_IV_SPACE; + + /* unit us */ + /* FIXME: find a proper value */ + wl->hw->channel_change_time = 10000; + + wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_NOISE_DBM; + + wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + wl->hw->wiphy->max_scan_ssids = 1; + wl->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl1271_band_2ghz; + + SET_IEEE80211_DEV(wl->hw, &wl->spi->dev); + + return 0; +} + +static void wl1271_device_release(struct device *dev) +{ + +} + +static struct platform_device wl1271_device = { + .name = "wl1271", + .id = -1, + + /* device model insists to have a release function */ + .dev = { + .release = wl1271_device_release, + }, +}; + +#define WL1271_DEFAULT_CHANNEL 0 +static int __devinit wl1271_probe(struct spi_device *spi) +{ + struct wl12xx_platform_data *pdata; + struct ieee80211_hw *hw; + struct wl1271 *wl; + int ret, i; + static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf}; + + pdata = spi->dev.platform_data; + if (!pdata) { + wl1271_error("no platform data"); + return -ENODEV; + } + + hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); + if (!hw) { + wl1271_error("could not alloc ieee80211_hw"); + return -ENOMEM; + } + + wl = hw->priv; + memset(wl, 0, sizeof(*wl)); + + wl->hw = hw; + dev_set_drvdata(&spi->dev, wl); + wl->spi = spi; + + skb_queue_head_init(&wl->tx_queue); + + INIT_WORK(&wl->filter_work, wl1271_filter_work); + wl->channel = WL1271_DEFAULT_CHANNEL; + wl->scanning = false; + wl->default_key = 0; + wl->listen_int = 1; + wl->rx_counter = 0; + wl->rx_config = WL1271_DEFAULT_RX_CONFIG; + wl->rx_filter = WL1271_DEFAULT_RX_FILTER; + wl->elp = false; + wl->psm = 0; + wl->psm_requested = false; + wl->tx_queue_stopped = false; + wl->power_level = WL1271_DEFAULT_POWER_LEVEL; + + /* We use the default power on sleep time until we know which chip + * we're using */ + for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) + wl->tx_frames[i] = NULL; + + spin_lock_init(&wl->wl_lock); + + /* + * In case our MAC address is not correctly set, + * we use a random but Nokia MAC. + */ + memcpy(wl->mac_addr, nokia_oui, 3); + get_random_bytes(wl->mac_addr + 3, 3); + + wl->state = WL1271_STATE_OFF; + mutex_init(&wl->mutex); + + wl->rx_descriptor = kmalloc(sizeof(*wl->rx_descriptor), GFP_KERNEL); + if (!wl->rx_descriptor) { + wl1271_error("could not allocate memory for rx descriptor"); + ret = -ENOMEM; + goto out_free; + } + + /* This is the only SPI value that we need to set here, the rest + * comes from the board-peripherals file */ + spi->bits_per_word = 32; + + ret = spi_setup(spi); + if (ret < 0) { + wl1271_error("spi_setup failed"); + goto out_free; + } + + wl->set_power = pdata->set_power; + if (!wl->set_power) { + wl1271_error("set power function missing in platform data"); + ret = -ENODEV; + goto out_free; + } + + wl->irq = spi->irq; + if (wl->irq < 0) { + wl1271_error("irq missing in platform data"); + ret = -ENODEV; + goto out_free; + } + + ret = request_irq(wl->irq, wl1271_irq, 0, DRIVER_NAME, wl); + if (ret < 0) { + wl1271_error("request_irq() failed: %d", ret); + goto out_free; + } + + set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); + + disable_irq(wl->irq); + + ret = platform_device_register(&wl1271_device); + if (ret) { + wl1271_error("couldn't register platform device"); + goto out_irq; + } + dev_set_drvdata(&wl1271_device.dev, wl); + + ret = wl1271_init_ieee80211(wl); + if (ret) + goto out_platform; + + ret = wl1271_register_hw(wl); + if (ret) + goto out_platform; + + wl1271_debugfs_init(wl); + + wl1271_notice("initialized"); + + return 0; + + out_platform: + platform_device_unregister(&wl1271_device); + + out_irq: + free_irq(wl->irq, wl); + + out_free: + kfree(wl->rx_descriptor); + wl->rx_descriptor = NULL; + + ieee80211_free_hw(hw); + + return ret; +} + +static int __devexit wl1271_remove(struct spi_device *spi) +{ + struct wl1271 *wl = dev_get_drvdata(&spi->dev); + + ieee80211_unregister_hw(wl->hw); + + wl1271_debugfs_exit(wl); + platform_device_unregister(&wl1271_device); + free_irq(wl->irq, wl); + kfree(wl->target_mem_map); + kfree(wl->fw); + wl->fw = NULL; + kfree(wl->nvs); + wl->nvs = NULL; + + kfree(wl->rx_descriptor); + wl->rx_descriptor = NULL; + + kfree(wl->fw_status); + kfree(wl->tx_res_if); + + ieee80211_free_hw(wl->hw); + + return 0; +} + + +static struct spi_driver wl1271_spi_driver = { + .driver = { + .name = "wl1271", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + + .probe = wl1271_probe, + .remove = __devexit_p(wl1271_remove), +}; + +static int __init wl1271_init(void) +{ + int ret; + + ret = spi_register_driver(&wl1271_spi_driver); + if (ret < 0) { + wl1271_error("failed to register spi driver: %d", ret); + goto out; + } + +out: + return ret; +} + +static void __exit wl1271_exit(void) +{ + spi_unregister_driver(&wl1271_spi_driver); + + wl1271_notice("unloaded"); +} + +module_init(wl1271_init); +module_exit(wl1271_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>"); diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.c b/drivers/net/wireless/wl12xx/wl1271_ps.c new file mode 100644 index 000000000000..1dc74b0c7736 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_ps.c @@ -0,0 +1,142 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2008-2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "wl1271_reg.h" +#include "wl1271_ps.h" +#include "wl1271_spi.h" + +#define WL1271_WAKEUP_TIMEOUT 500 + +/* Routines to toggle sleep mode while in ELP */ +void wl1271_ps_elp_sleep(struct wl1271 *wl) +{ + /* + * FIXME: due to a problem in the firmware (causing a firmware + * crash), ELP entry is prevented below. Remove the "true" to + * re-enable ELP entry. + */ + if (true || wl->elp || !wl->psm) + return; + + /* + * Go to ELP unless there is work already pending - pending work + * will immediately wakeup the chipset anyway. + */ + if (!work_pending(&wl->irq_work) && !work_pending(&wl->tx_work)) { + wl1271_debug(DEBUG_PSM, "chip to elp"); + wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_SLEEP); + wl->elp = true; + } +} + +int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake) +{ + DECLARE_COMPLETION_ONSTACK(compl); + unsigned long flags; + int ret; + u32 start_time = jiffies; + bool pending = false; + + if (!wl->elp) + return 0; + + wl1271_debug(DEBUG_PSM, "waking up chip from elp"); + + /* + * The spinlock is required here to synchronize both the work and + * the completion variable in one entity. + */ + spin_lock_irqsave(&wl->wl_lock, flags); + if (work_pending(&wl->irq_work) || chip_awake) + pending = true; + else + wl->elp_compl = &compl; + spin_unlock_irqrestore(&wl->wl_lock, flags); + + wl1271_write32(wl, HW_ACCESS_ELP_CTRL_REG_ADDR, ELPCTRL_WAKE_UP); + + if (!pending) { + ret = wait_for_completion_timeout( + &compl, msecs_to_jiffies(WL1271_WAKEUP_TIMEOUT)); + if (ret == 0) { + wl1271_error("ELP wakeup timeout!"); + ret = -ETIMEDOUT; + goto err; + } else if (ret < 0) { + wl1271_error("ELP wakeup completion error."); + goto err; + } + } + + wl->elp = false; + + wl1271_debug(DEBUG_PSM, "wakeup time: %u ms", + jiffies_to_msecs(jiffies - start_time)); + goto out; + +err: + spin_lock_irqsave(&wl->wl_lock, flags); + wl->elp_compl = NULL; + spin_unlock_irqrestore(&wl->wl_lock, flags); + return ret; + +out: + return 0; +} + +int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode) +{ + int ret; + + switch (mode) { + case STATION_POWER_SAVE_MODE: + wl1271_debug(DEBUG_PSM, "entering psm"); + ret = wl1271_cmd_ps_mode(wl, STATION_POWER_SAVE_MODE); + if (ret < 0) + return ret; + + wl1271_ps_elp_sleep(wl); + if (ret < 0) + return ret; + + wl->psm = 1; + break; + case STATION_ACTIVE_MODE: + default: + wl1271_debug(DEBUG_PSM, "leaving psm"); + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + return ret; + + ret = wl1271_cmd_ps_mode(wl, STATION_ACTIVE_MODE); + if (ret < 0) + return ret; + + wl->psm = 0; + break; + } + + return ret; +} + + diff --git a/drivers/net/wireless/wl12xx/wl1271_ps.h b/drivers/net/wireless/wl12xx/wl1271_ps.h new file mode 100644 index 000000000000..de2bd3c7dc9c --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_ps.h @@ -0,0 +1,35 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2008-2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1271_PS_H__ +#define __WL1271_PS_H__ + +#include "wl1271.h" +#include "wl1271_acx.h" + +int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode); +void wl1271_ps_elp_sleep(struct wl1271 *wl); +int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake); + + +#endif /* __WL1271_PS_H__ */ diff --git a/drivers/net/wireless/wl12xx/reg.h b/drivers/net/wireless/wl12xx/wl1271_reg.h index e421643215cd..f8ed4a4fc691 100644 --- a/drivers/net/wireless/wl12xx/reg.h +++ b/drivers/net/wireless/wl12xx/wl1271_reg.h @@ -1,10 +1,10 @@ /* * This file is part of wl12xx * - * Copyright (c) 1998-2007 Texas Instruments Incorporated - * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. + * Copyright (C) 2009 Nokia Corporation * - * Contact: Kalle Valo <kalle.valo@nokia.com> + * Contact: Luciano Coelho <luciano.coelho@nokia.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -26,7 +26,6 @@ #define __REG_H__ #include <linux/bitops.h> -#include "wl12xx.h" #define REGISTERS_BASE 0x00300000 #define DRPW_BASE 0x00310000 @@ -35,6 +34,7 @@ #define REGISTERS_WORK_SIZE 0x0000b000 #define HW_ACCESS_ELP_CTRL_REG_ADDR 0x1FFFC +#define STATUS_MEM_ADDRESS 0x40400 /* ELP register commands */ #define ELPCTRL_WAKE_UP 0x1 @@ -43,6 +43,25 @@ /* ELP WLAN_READY bit */ #define ELPCTRL_WLAN_READY 0x2 +/*=============================================== + Host Software Reset - 32bit RW + ------------------------------------------ + [31:1] Reserved + 0 SOFT_RESET Soft Reset - When this bit is set, + it holds the Wlan hardware in a soft reset state. + This reset disables all MAC and baseband processor + clocks except the CardBus/PCI interface clock. + It also initializes all MAC state machines except + the host interface. It does not reload the + contents of the EEPROM. When this bit is cleared + (not self-clearing), the Wlan hardware + exits the software reset state. +===============================================*/ +#define ACX_REG_SLV_SOFT_RESET (REGISTERS_BASE + 0x0000) + +#define WL1271_SLV_REG_DATA (REGISTERS_BASE + 0x0008) +#define WL1271_SLV_REG_ADATA (REGISTERS_BASE + 0x000c) +#define WL1271_SLV_MEM_DATA (REGISTERS_BASE + 0x0018) /* * Interrupt registers. * 64 bit interrupt sources registers ws ced. @@ -96,6 +115,9 @@ #define HOST_MASK_CLR_L (REGISTERS_BASE + 0x0440) #define HOST_MASK_CLR_H (REGISTERS_BASE + 0x0444) +#define ACX_REG_INTERRUPT_TRIG (REGISTERS_BASE + 0x0474) +#define ACX_REG_INTERRUPT_TRIG_H (REGISTERS_BASE + 0x0478) + /* Host Interrupts*/ #define HINT_MASK (REGISTERS_BASE + 0x0494) #define HINT_MASK_SET (REGISTERS_BASE + 0x0498) @@ -107,67 +129,6 @@ #define HINT_ACK (REGISTERS_BASE + 0x04A8) #define HINT_TRIG (REGISTERS_BASE + 0x04AC) -/* Device Configuration registers*/ -#define SOR_CFG (REGISTERS_BASE + 0x0800) -#define ECPU_CTRL (REGISTERS_BASE + 0x0804) -#define HI_CFG (REGISTERS_BASE + 0x0808) -#define EE_START (REGISTERS_BASE + 0x080C) - -#define CHIP_ID_B (REGISTERS_BASE + 0x5674) - -#define CHIP_ID_1251_PG10 (0x7010101) -#define CHIP_ID_1251_PG11 (0x7020101) -#define CHIP_ID_1251_PG12 (0x7030101) - -#define ENABLE (REGISTERS_BASE + 0x5450) - -/* Power Management registers */ -#define ELP_CFG_MODE (REGISTERS_BASE + 0x5804) -#define ELP_CMD (REGISTERS_BASE + 0x5808) -#define PLL_CAL_TIME (REGISTERS_BASE + 0x5810) -#define CLK_REQ_TIME (REGISTERS_BASE + 0x5814) -#define CLK_BUF_TIME (REGISTERS_BASE + 0x5818) - -#define CFG_PLL_SYNC_CNT (REGISTERS_BASE + 0x5820) - -/* Scratch Pad registers*/ -#define SCR_PAD0 (REGISTERS_BASE + 0x5608) -#define SCR_PAD1 (REGISTERS_BASE + 0x560C) -#define SCR_PAD2 (REGISTERS_BASE + 0x5610) -#define SCR_PAD3 (REGISTERS_BASE + 0x5614) -#define SCR_PAD4 (REGISTERS_BASE + 0x5618) -#define SCR_PAD4_SET (REGISTERS_BASE + 0x561C) -#define SCR_PAD4_CLR (REGISTERS_BASE + 0x5620) -#define SCR_PAD5 (REGISTERS_BASE + 0x5624) -#define SCR_PAD5_SET (REGISTERS_BASE + 0x5628) -#define SCR_PAD5_CLR (REGISTERS_BASE + 0x562C) -#define SCR_PAD6 (REGISTERS_BASE + 0x5630) -#define SCR_PAD7 (REGISTERS_BASE + 0x5634) -#define SCR_PAD8 (REGISTERS_BASE + 0x5638) -#define SCR_PAD9 (REGISTERS_BASE + 0x563C) - -/* Spare registers*/ -#define SPARE_A1 (REGISTERS_BASE + 0x0994) -#define SPARE_A2 (REGISTERS_BASE + 0x0998) -#define SPARE_A3 (REGISTERS_BASE + 0x099C) -#define SPARE_A4 (REGISTERS_BASE + 0x09A0) -#define SPARE_A5 (REGISTERS_BASE + 0x09A4) -#define SPARE_A6 (REGISTERS_BASE + 0x09A8) -#define SPARE_A7 (REGISTERS_BASE + 0x09AC) -#define SPARE_A8 (REGISTERS_BASE + 0x09B0) -#define SPARE_B1 (REGISTERS_BASE + 0x5420) -#define SPARE_B2 (REGISTERS_BASE + 0x5424) -#define SPARE_B3 (REGISTERS_BASE + 0x5428) -#define SPARE_B4 (REGISTERS_BASE + 0x542C) -#define SPARE_B5 (REGISTERS_BASE + 0x5430) -#define SPARE_B6 (REGISTERS_BASE + 0x5434) -#define SPARE_B7 (REGISTERS_BASE + 0x5438) -#define SPARE_B8 (REGISTERS_BASE + 0x543C) - -enum wl12xx_acx_int_reg { - ACX_REG_INTERRUPT_TRIG, - ACX_REG_INTERRUPT_TRIG_H, - /*============================================= Host Interrupt Mask Register - 32bit (RW) ------------------------------------------ @@ -196,7 +157,7 @@ enum wl12xx_acx_int_reg { 21- - Default: 0x0001 *==============================================*/ - ACX_REG_INTERRUPT_MASK, +#define ACX_REG_INTERRUPT_MASK (REGISTERS_BASE + 0x04DC) /*============================================= Host Interrupt Mask Set 16bit, (Write only) @@ -206,7 +167,7 @@ enum wl12xx_acx_int_reg { without effecting the mask state of other bits (0 = no effect). ==============================================*/ - ACX_REG_HINT_MASK_SET, +#define ACX_REG_HINT_MASK_SET (REGISTERS_BASE + 0x04E0) /*============================================= Host Interrupt Mask Clear 16bit,(Write only) @@ -216,7 +177,7 @@ enum wl12xx_acx_int_reg { without effecting the mask state of other bits (0 = no effect). =============================================*/ - ACX_REG_HINT_MASK_CLR, +#define ACX_REG_HINT_MASK_CLR (REGISTERS_BASE + 0x04E4) /*============================================= Host Interrupt Status Nondestructive Read @@ -227,7 +188,7 @@ enum wl12xx_acx_int_reg { Reading this register doesn't effect its content. =============================================*/ - ACX_REG_INTERRUPT_NO_CLEAR, +#define ACX_REG_INTERRUPT_NO_CLEAR (REGISTERS_BASE + 0x04E8) /*============================================= Host Interrupt Status Clear on Read Register @@ -238,7 +199,7 @@ enum wl12xx_acx_int_reg { Reading this register clears it, thus making all interrupts inactive. ==============================================*/ - ACX_REG_INTERRUPT_CLEAR, +#define ACX_REG_INTERRUPT_CLEAR (REGISTERS_BASE + 0x04F8) /*============================================= Host Interrupt Acknowledge Register @@ -250,40 +211,13 @@ enum wl12xx_acx_int_reg { HINT_STS_ND registers, thus making the assotiated interrupt inactive. (0-no effect) ==============================================*/ - ACX_REG_INTERRUPT_ACK, +#define ACX_REG_INTERRUPT_ACK (REGISTERS_BASE + 0x04F0) -/*=============================================== - Host Software Reset - 32bit RW - ------------------------------------------ - [31:1] Reserved - 0 SOFT_RESET Soft Reset - When this bit is set, - it holds the Wlan hardware in a soft reset state. - This reset disables all MAC and baseband processor - clocks except the CardBus/PCI interface clock. - It also initializes all MAC state machines except - the host interface. It does not reload the - contents of the EEPROM. When this bit is cleared - (not self-clearing), the Wlan hardware - exits the software reset state. -===============================================*/ - ACX_REG_SLV_SOFT_RESET, - -/*=============================================== - EEPROM Burst Read Start - 32bit RW - ------------------------------------------ - [31:1] Reserved - 0 ACX_EE_START - EEPROM Burst Read Start 0 - Setting this bit starts a burst read from - the external EEPROM. - If this bit is set (after reset) before an EEPROM read/write, - the burst read starts at EEPROM address 0. - Otherwise, it starts at the address - following the address of the previous access. - TheWlan hardware hardware clears this bit automatically. +#define RX_DRIVER_DUMMY_WRITE_ADDRESS (REGISTERS_BASE + 0x0534) +#define RX_DRIVER_COUNTER_ADDRESS (REGISTERS_BASE + 0x0538) - Default: 0x00000000 -*================================================*/ - ACX_REG_EE_START, +/* Device Configuration registers*/ +#define SOR_CFG (REGISTERS_BASE + 0x0800) /* Embedded ARM CPU Control */ @@ -305,10 +239,89 @@ enum wl12xx_acx_int_reg { 1 halt eCPU 0 enable eCPU ===============================================*/ - ACX_REG_ECPU_CONTROL, +#define ACX_REG_ECPU_CONTROL (REGISTERS_BASE + 0x0804) + +#define HI_CFG (REGISTERS_BASE + 0x0808) + +/*=============================================== + EEPROM Burst Read Start - 32bit RW + ------------------------------------------ + [31:1] Reserved + 0 ACX_EE_START - EEPROM Burst Read Start 0 + Setting this bit starts a burst read from + the external EEPROM. + If this bit is set (after reset) before an EEPROM read/write, + the burst read starts at EEPROM address 0. + Otherwise, it starts at the address + following the address of the previous access. + TheWlan hardware hardware clears this bit automatically. + + Default: 0x00000000 +*================================================*/ +#define ACX_REG_EE_START (REGISTERS_BASE + 0x080C) + +#define OCP_POR_CTR (REGISTERS_BASE + 0x09B4) +#define OCP_DATA_WRITE (REGISTERS_BASE + 0x09B8) +#define OCP_DATA_READ (REGISTERS_BASE + 0x09BC) +#define OCP_CMD (REGISTERS_BASE + 0x09C0) + +#define WL1271_HOST_WR_ACCESS (REGISTERS_BASE + 0x09F8) + +#define CHIP_ID_B (REGISTERS_BASE + 0x5674) + +#define CHIP_ID_1271_PG10 (0x4030101) +#define CHIP_ID_1271_PG20 (0x4030111) + +#define ENABLE (REGISTERS_BASE + 0x5450) + +/* Power Management registers */ +#define ELP_CFG_MODE (REGISTERS_BASE + 0x5804) +#define ELP_CMD (REGISTERS_BASE + 0x5808) +#define PLL_CAL_TIME (REGISTERS_BASE + 0x5810) +#define CLK_REQ_TIME (REGISTERS_BASE + 0x5814) +#define CLK_BUF_TIME (REGISTERS_BASE + 0x5818) + +#define CFG_PLL_SYNC_CNT (REGISTERS_BASE + 0x5820) + +/* Scratch Pad registers*/ +#define SCR_PAD0 (REGISTERS_BASE + 0x5608) +#define SCR_PAD1 (REGISTERS_BASE + 0x560C) +#define SCR_PAD2 (REGISTERS_BASE + 0x5610) +#define SCR_PAD3 (REGISTERS_BASE + 0x5614) +#define SCR_PAD4 (REGISTERS_BASE + 0x5618) +#define SCR_PAD4_SET (REGISTERS_BASE + 0x561C) +#define SCR_PAD4_CLR (REGISTERS_BASE + 0x5620) +#define SCR_PAD5 (REGISTERS_BASE + 0x5624) +#define SCR_PAD5_SET (REGISTERS_BASE + 0x5628) +#define SCR_PAD5_CLR (REGISTERS_BASE + 0x562C) +#define SCR_PAD6 (REGISTERS_BASE + 0x5630) +#define SCR_PAD7 (REGISTERS_BASE + 0x5634) +#define SCR_PAD8 (REGISTERS_BASE + 0x5638) +#define SCR_PAD9 (REGISTERS_BASE + 0x563C) + +/* Spare registers*/ +#define SPARE_A1 (REGISTERS_BASE + 0x0994) +#define SPARE_A2 (REGISTERS_BASE + 0x0998) +#define SPARE_A3 (REGISTERS_BASE + 0x099C) +#define SPARE_A4 (REGISTERS_BASE + 0x09A0) +#define SPARE_A5 (REGISTERS_BASE + 0x09A4) +#define SPARE_A6 (REGISTERS_BASE + 0x09A8) +#define SPARE_A7 (REGISTERS_BASE + 0x09AC) +#define SPARE_A8 (REGISTERS_BASE + 0x09B0) +#define SPARE_B1 (REGISTERS_BASE + 0x5420) +#define SPARE_B2 (REGISTERS_BASE + 0x5424) +#define SPARE_B3 (REGISTERS_BASE + 0x5428) +#define SPARE_B4 (REGISTERS_BASE + 0x542C) +#define SPARE_B5 (REGISTERS_BASE + 0x5430) +#define SPARE_B6 (REGISTERS_BASE + 0x5434) +#define SPARE_B7 (REGISTERS_BASE + 0x5438) +#define SPARE_B8 (REGISTERS_BASE + 0x543C) + +#define PLL_PARAMETERS (REGISTERS_BASE + 0x6040) +#define WU_COUNTER_PAUSE (REGISTERS_BASE + 0x6008) +#define WELP_ARM_COMMAND (REGISTERS_BASE + 0x6100) +#define DRPW_SCRATCH_START (DRPW_BASE + 0x002C) - ACX_REG_TABLE_LEN -}; #define ACX_SLV_SOFT_RESET_BIT BIT(1) #define ACX_REG_EEPROM_START_BIT BIT(1) diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.c b/drivers/net/wireless/wl12xx/wl1271_rx.c new file mode 100644 index 000000000000..ad8b6904c5eb --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_rx.c @@ -0,0 +1,200 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "wl1271.h" +#include "wl1271_acx.h" +#include "wl1271_reg.h" +#include "wl1271_rx.h" +#include "wl1271_spi.h" + +static u8 wl1271_rx_get_mem_block(struct wl1271_fw_status *status, + u32 drv_rx_counter) +{ + return status->rx_pkt_descs[drv_rx_counter] & RX_MEM_BLOCK_MASK; +} + +static u32 wl1271_rx_get_buf_size(struct wl1271_fw_status *status, + u32 drv_rx_counter) +{ + return (status->rx_pkt_descs[drv_rx_counter] & RX_BUF_SIZE_MASK) >> + RX_BUF_SIZE_SHIFT_DIV; +} + +/* The values of this table must match the wl1271_rates[] array */ +static u8 wl1271_rx_rate_to_idx[] = { + /* MCS rates are used only with 11n */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS7 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS6 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS5 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS4 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS3 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS2 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS1 */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_MCS0 */ + + 11, /* WL1271_RATE_54 */ + 10, /* WL1271_RATE_48 */ + 9, /* WL1271_RATE_36 */ + 8, /* WL1271_RATE_24 */ + + /* TI-specific rate */ + WL1271_RX_RATE_UNSUPPORTED, /* WL1271_RATE_22 */ + + 7, /* WL1271_RATE_18 */ + 6, /* WL1271_RATE_12 */ + 3, /* WL1271_RATE_11 */ + 5, /* WL1271_RATE_9 */ + 4, /* WL1271_RATE_6 */ + 2, /* WL1271_RATE_5_5 */ + 1, /* WL1271_RATE_2 */ + 0 /* WL1271_RATE_1 */ +}; + +static void wl1271_rx_status(struct wl1271 *wl, + struct wl1271_rx_descriptor *desc, + struct ieee80211_rx_status *status, + u8 beacon) +{ + memset(status, 0, sizeof(struct ieee80211_rx_status)); + + if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == WL1271_RX_DESC_BAND_BG) + status->band = IEEE80211_BAND_2GHZ; + else + wl1271_warning("unsupported band 0x%x", + desc->flags & WL1271_RX_DESC_BAND_MASK); + + /* + * FIXME: Add mactime handling. For IBSS (ad-hoc) we need to get the + * timestamp from the beacon (acx_tsf_info). In BSS mode (infra) we + * only need the mactime for monitor mode. For now the mactime is + * not valid, so RX_FLAG_TSFT should not be set + */ + status->signal = desc->rssi; + + /* FIXME: Should this be optimized? */ + status->qual = (desc->rssi - WL1271_RX_MIN_RSSI) * 100 / + (WL1271_RX_MAX_RSSI - WL1271_RX_MIN_RSSI); + status->qual = min(status->qual, 100); + status->qual = max(status->qual, 0); + + /* + * FIXME: In wl1251, the SNR should be divided by two. In wl1271 we + * need to divide by two for now, but TI has been discussing about + * changing it. This needs to be rechecked. + */ + status->noise = desc->rssi - (desc->snr >> 1); + + status->freq = ieee80211_channel_to_frequency(desc->channel); + + if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) { + status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; + + if (likely(!(desc->flags & WL1271_RX_DESC_DECRYPT_FAIL))) + status->flag |= RX_FLAG_DECRYPTED; + + if (unlikely(desc->flags & WL1271_RX_DESC_MIC_FAIL)) + status->flag |= RX_FLAG_MMIC_ERROR; + } + + status->rate_idx = wl1271_rx_rate_to_idx[desc->rate]; + + if (status->rate_idx == WL1271_RX_RATE_UNSUPPORTED) + wl1271_warning("unsupported rate"); +} + +static void wl1271_rx_handle_data(struct wl1271 *wl, u32 length) +{ + struct ieee80211_rx_status rx_status; + struct wl1271_rx_descriptor *desc; + struct sk_buff *skb; + u16 *fc; + u8 *buf; + u8 beacon = 0; + + skb = dev_alloc_skb(length); + if (!skb) { + wl1271_error("Couldn't allocate RX frame"); + return; + } + + buf = skb_put(skb, length); + wl1271_spi_reg_read(wl, WL1271_SLV_MEM_DATA, buf, length, true); + + /* the data read starts with the descriptor */ + desc = (struct wl1271_rx_descriptor *) buf; + + /* now we pull the descriptor out of the buffer */ + skb_pull(skb, sizeof(*desc)); + + fc = (u16 *)skb->data; + if ((*fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) + beacon = 1; + + wl1271_rx_status(wl, desc, &rx_status, beacon); + + wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, skb->len, + beacon ? "beacon" : ""); + + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); + ieee80211_rx(wl->hw, skb); +} + +void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status) +{ + struct wl1271_acx_mem_map *wl_mem_map = wl->target_mem_map; + u32 buf_size; + u32 fw_rx_counter = status->fw_rx_counter & NUM_RX_PKT_DESC_MOD_MASK; + u32 drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; + u32 mem_block; + + while (drv_rx_counter != fw_rx_counter) { + mem_block = wl1271_rx_get_mem_block(status, drv_rx_counter); + buf_size = wl1271_rx_get_buf_size(status, drv_rx_counter); + + if (buf_size == 0) { + wl1271_warning("received empty data"); + break; + } + + wl->rx_mem_pool_addr.addr = + (mem_block << 8) + wl_mem_map->packet_memory_pool_start; + wl->rx_mem_pool_addr.addr_extra = + wl->rx_mem_pool_addr.addr + 4; + + /* Choose the block we want to read */ + wl1271_spi_reg_write(wl, WL1271_SLV_REG_DATA, + &wl->rx_mem_pool_addr, + sizeof(wl->rx_mem_pool_addr), false); + + wl1271_rx_handle_data(wl, buf_size); + + wl->rx_counter++; + drv_rx_counter = wl->rx_counter & NUM_RX_PKT_DESC_MOD_MASK; + } + + wl1271_reg_write32(wl, RX_DRIVER_COUNTER_ADDRESS, wl->rx_counter); + + /* This is a workaround for some problems in the chip */ + wl1271_reg_write32(wl, RX_DRIVER_DUMMY_WRITE_ADDRESS, 0x1); + +} diff --git a/drivers/net/wireless/wl12xx/wl1271_rx.h b/drivers/net/wireless/wl12xx/wl1271_rx.h new file mode 100644 index 000000000000..d1ca60e43a25 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_rx.h @@ -0,0 +1,121 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. + * Copyright (C) 2008-2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1271_RX_H__ +#define __WL1271_RX_H__ + +#include <linux/bitops.h> + +#define WL1271_RX_MAX_RSSI -30 +#define WL1271_RX_MIN_RSSI -95 + +#define WL1271_RX_ALIGN_TO 4 +#define WL1271_RX_ALIGN(len) (((len) + WL1271_RX_ALIGN_TO - 1) & \ + ~(WL1271_RX_ALIGN_TO - 1)) + +#define SHORT_PREAMBLE_BIT BIT(0) +#define OFDM_RATE_BIT BIT(6) +#define PBCC_RATE_BIT BIT(7) + +#define PLCP_HEADER_LENGTH 8 +#define RX_DESC_PACKETID_SHIFT 11 +#define RX_MAX_PACKET_ID 3 + +#define NUM_RX_PKT_DESC_MOD_MASK 7 +#define WL1271_RX_RATE_UNSUPPORTED 0xFF + +#define RX_DESC_VALID_FCS 0x0001 +#define RX_DESC_MATCH_RXADDR1 0x0002 +#define RX_DESC_MCAST 0x0004 +#define RX_DESC_STAINTIM 0x0008 +#define RX_DESC_VIRTUAL_BM 0x0010 +#define RX_DESC_BCAST 0x0020 +#define RX_DESC_MATCH_SSID 0x0040 +#define RX_DESC_MATCH_BSSID 0x0080 +#define RX_DESC_ENCRYPTION_MASK 0x0300 +#define RX_DESC_MEASURMENT 0x0400 +#define RX_DESC_SEQNUM_MASK 0x1800 +#define RX_DESC_MIC_FAIL 0x2000 +#define RX_DESC_DECRYPT_FAIL 0x4000 + +/* + * RX Descriptor flags: + * + * Bits 0-1 - band + * Bit 2 - STBC + * Bit 3 - A-MPDU + * Bit 4 - HT + * Bits 5-7 - encryption + */ +#define WL1271_RX_DESC_BAND_MASK 0x03 +#define WL1271_RX_DESC_ENCRYPT_MASK 0xE0 + +#define WL1271_RX_DESC_BAND_BG 0x00 +#define WL1271_RX_DESC_BAND_J 0x01 +#define WL1271_RX_DESC_BAND_A 0x02 + +#define WL1271_RX_DESC_STBC BIT(2) +#define WL1271_RX_DESC_A_MPDU BIT(3) +#define WL1271_RX_DESC_HT BIT(4) + +#define WL1271_RX_DESC_ENCRYPT_WEP 0x20 +#define WL1271_RX_DESC_ENCRYPT_TKIP 0x40 +#define WL1271_RX_DESC_ENCRYPT_AES 0x60 +#define WL1271_RX_DESC_ENCRYPT_GEM 0x80 + +/* + * RX Descriptor status + * + * Bits 0-2 - status + * Bits 3-7 - reserved + */ +#define WL1271_RX_DESC_STATUS_MASK 0x07 + +#define WL1271_RX_DESC_SUCCESS 0x00 +#define WL1271_RX_DESC_DECRYPT_FAIL 0x01 +#define WL1271_RX_DESC_MIC_FAIL 0x02 +#define WL1271_RX_DESC_DRIVER_RX_Q_FAIL 0x03 + +#define RX_MEM_BLOCK_MASK 0xFF +#define RX_BUF_SIZE_MASK 0xFFF00 +#define RX_BUF_SIZE_SHIFT_DIV 6 + +struct wl1271_rx_descriptor { + u16 length; + u8 status; + u8 flags; + u8 rate; + u8 channel; + s8 rssi; + u8 snr; + u32 timestamp; + u8 packet_class; + u8 process_id; + u8 pad_len; + u8 reserved; +} __attribute__ ((packed)); + +void wl1271_rx(struct wl1271 *wl, struct wl1271_fw_status *status); + +#endif diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index abdf171a47e7..4a12880c16a8 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c @@ -1,9 +1,9 @@ /* - * This file is part of wl12xx + * This file is part of wl1271 * - * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 2008-2009 Nokia Corporation * - * Contact: Kalle Valo <kalle.valo@nokia.com> + * Contact: Luciano Coelho <luciano.coelho@nokia.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -22,39 +22,26 @@ */ #include <linux/module.h> +#include <linux/platform_device.h> #include <linux/crc7.h> #include <linux/spi/spi.h> -#include "wl12xx.h" +#include "wl1271.h" #include "wl12xx_80211.h" -#include "reg.h" -#include "spi.h" -#include "ps.h" +#include "wl1271_spi.h" -static int wl12xx_translate_reg_addr(struct wl12xx *wl, int addr) +static int wl1271_translate_reg_addr(struct wl1271 *wl, int addr) { - /* If the address is lower than REGISTERS_BASE, it means that this is - * a chip-specific register address, so look it up in the registers - * table */ - if (addr < REGISTERS_BASE) { - /* Make sure we don't go over the table */ - if (addr >= ACX_REG_TABLE_LEN) { - wl12xx_error("address out of range (%d)", addr); - return -EINVAL; - } - addr = wl->chip.acx_reg_table[addr]; - } - return addr - wl->physical_reg_addr + wl->virtual_reg_addr; } -static int wl12xx_translate_mem_addr(struct wl12xx *wl, int addr) +static int wl1271_translate_mem_addr(struct wl1271 *wl, int addr) { return addr - wl->physical_mem_addr + wl->virtual_mem_addr; } -void wl12xx_spi_reset(struct wl12xx *wl) +void wl1271_spi_reset(struct wl1271 *wl) { u8 *cmd; struct spi_transfer t; @@ -62,7 +49,7 @@ void wl12xx_spi_reset(struct wl12xx *wl) cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); if (!cmd) { - wl12xx_error("could not allocate cmd for spi reset"); + wl1271_error("could not allocate cmd for spi reset"); return; } @@ -77,10 +64,10 @@ void wl12xx_spi_reset(struct wl12xx *wl) spi_sync(wl->spi, &m); - wl12xx_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN); + wl1271_dump(DEBUG_SPI, "spi reset -> ", cmd, WSPI_INIT_CMD_LEN); } -void wl12xx_spi_init(struct wl12xx *wl) +void wl1271_spi_init(struct wl1271 *wl) { u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd; struct spi_transfer t; @@ -88,7 +75,7 @@ void wl12xx_spi_init(struct wl12xx *wl) cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); if (!cmd) { - wl12xx_error("could not allocate cmd for spi init"); + wl1271_error("could not allocate cmd for spi init"); return; } @@ -131,7 +118,7 @@ void wl12xx_spi_init(struct wl12xx *wl) spi_sync(wl->spi, &m); - wl12xx_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN); + wl1271_dump(DEBUG_SPI, "spi init -> ", cmd, WSPI_INIT_CMD_LEN); } /* Set the SPI partitions to access the chip addresses @@ -167,45 +154,47 @@ void wl12xx_spi_init(struct wl12xx *wl) * | | * */ -void wl12xx_set_partition(struct wl12xx *wl, +int wl1271_set_partition(struct wl1271 *wl, u32 mem_start, u32 mem_size, u32 reg_start, u32 reg_size) { - u8 tx_buf[sizeof(u32) + 2 * sizeof(struct wl12xx_partition)]; - struct wl12xx_partition *partition; + struct wl1271_partition *partition; struct spi_transfer t; struct spi_message m; + size_t len, cmd_len; u32 *cmd; - size_t len; int addr; + cmd_len = sizeof(u32) + 2 * sizeof(struct wl1271_partition); + cmd = kzalloc(cmd_len, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + spi_message_init(&m); memset(&t, 0, sizeof(t)); - memset(tx_buf, 0, sizeof(tx_buf)); - cmd = (u32 *) tx_buf; - partition = (struct wl12xx_partition *) (tx_buf + sizeof(u32)); + partition = (struct wl1271_partition *) (cmd + 1); addr = HW_ACCESS_PART0_SIZE_ADDR; - len = 2 * sizeof(struct wl12xx_partition); + len = 2 * sizeof(struct wl1271_partition); *cmd |= WSPI_CMD_WRITE; *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; *cmd |= addr & WSPI_CMD_BYTE_ADDR; - wl12xx_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", + wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", mem_start, mem_size); - wl12xx_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", + wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", reg_start, reg_size); /* Make sure that the two partitions together don't exceed the * address range */ if ((mem_size + reg_size) > HW_ACCESS_MEMORY_MAX_RANGE) { - wl12xx_debug(DEBUG_SPI, "Total size exceeds maximum virtual" + wl1271_debug(DEBUG_SPI, "Total size exceeds maximum virtual" " address range. Truncating partition[0]."); mem_size = HW_ACCESS_MEMORY_MAX_RANGE - reg_size; - wl12xx_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", + wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", mem_start, mem_size); - wl12xx_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", + wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", reg_start, reg_size); } @@ -213,23 +202,23 @@ void wl12xx_set_partition(struct wl12xx *wl, ((mem_start + mem_size) > reg_start)) { /* Guarantee that the memory partition doesn't overlap the * registers partition */ - wl12xx_debug(DEBUG_SPI, "End of partition[0] is " + wl1271_debug(DEBUG_SPI, "End of partition[0] is " "overlapping partition[1]. Adjusted."); mem_size = reg_start - mem_start; - wl12xx_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", + wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", mem_start, mem_size); - wl12xx_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", + wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", reg_start, reg_size); } else if ((reg_start < mem_start) && ((reg_start + reg_size) > mem_start)) { /* Guarantee that the register partition doesn't overlap the * memory partition */ - wl12xx_debug(DEBUG_SPI, "End of partition[1] is" + wl1271_debug(DEBUG_SPI, "End of partition[1] is" " overlapping partition[0]. Adjusted."); reg_size = mem_start - reg_start; - wl12xx_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", + wl1271_debug(DEBUG_SPI, "mem_start %08X mem_size %08X", mem_start, mem_size); - wl12xx_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", + wl1271_debug(DEBUG_SPI, "reg_start %08X reg_size %08X", reg_start, reg_size); } @@ -244,36 +233,46 @@ void wl12xx_set_partition(struct wl12xx *wl, wl->virtual_mem_addr = 0; wl->virtual_reg_addr = mem_size; - t.tx_buf = tx_buf; - t.len = sizeof(tx_buf); + t.tx_buf = cmd; + t.len = cmd_len; spi_message_add_tail(&t, &m); spi_sync(wl->spi, &m); + + kfree(cmd); + + return 0; } -void wl12xx_spi_read(struct wl12xx *wl, int addr, void *buf, - size_t len) +void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) { struct spi_transfer t[3]; struct spi_message m; - char busy_buf[TNETWIF_READ_OFFSET_BYTES]; - u32 cmd; + u8 *busy_buf; + u32 *cmd; - cmd = 0; - cmd |= WSPI_CMD_READ; - cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; - cmd |= addr & WSPI_CMD_BYTE_ADDR; + cmd = &wl->buffer_cmd; + busy_buf = wl->buffer_busyword; + + *cmd = 0; + *cmd |= WSPI_CMD_READ; + *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; + *cmd |= addr & WSPI_CMD_BYTE_ADDR; + + if (fixed) + *cmd |= WSPI_CMD_FIXED; spi_message_init(&m); memset(t, 0, sizeof(t)); - t[0].tx_buf = &cmd; + t[0].tx_buf = cmd; t[0].len = 4; spi_message_add_tail(&t[0], &m); /* Busy and non busy words read */ t[1].rx_buf = busy_buf; - t[1].len = TNETWIF_READ_OFFSET_BYTES; + t[1].len = WL1271_BUSY_WORD_LEN; spi_message_add_tail(&t[1], &m); t[2].rx_buf = buf; @@ -284,27 +283,32 @@ void wl12xx_spi_read(struct wl12xx *wl, int addr, void *buf, /* FIXME: check busy words */ - wl12xx_dump(DEBUG_SPI, "spi_read cmd -> ", &cmd, sizeof(cmd)); - wl12xx_dump(DEBUG_SPI, "spi_read buf <- ", buf, len); + wl1271_dump(DEBUG_SPI, "spi_read cmd -> ", cmd, sizeof(*cmd)); + wl1271_dump(DEBUG_SPI, "spi_read buf <- ", buf, len); } -void wl12xx_spi_write(struct wl12xx *wl, int addr, void *buf, - size_t len) +void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed) { struct spi_transfer t[2]; struct spi_message m; - u32 cmd; + u32 *cmd; - cmd = 0; - cmd |= WSPI_CMD_WRITE; - cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; - cmd |= addr & WSPI_CMD_BYTE_ADDR; + cmd = &wl->buffer_cmd; + + *cmd = 0; + *cmd |= WSPI_CMD_WRITE; + *cmd |= (len << WSPI_CMD_BYTE_LENGTH_OFFSET) & WSPI_CMD_BYTE_LENGTH; + *cmd |= addr & WSPI_CMD_BYTE_ADDR; + + if (fixed) + *cmd |= WSPI_CMD_FIXED; spi_message_init(&m); memset(t, 0, sizeof(t)); - t[0].tx_buf = &cmd; - t[0].len = sizeof(cmd); + t[0].tx_buf = cmd; + t[0].len = sizeof(*cmd); spi_message_add_tail(&t[0], &m); t[1].tx_buf = buf; @@ -313,46 +317,66 @@ void wl12xx_spi_write(struct wl12xx *wl, int addr, void *buf, spi_sync(wl->spi, &m); - wl12xx_dump(DEBUG_SPI, "spi_write cmd -> ", &cmd, sizeof(cmd)); - wl12xx_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); + wl1271_dump(DEBUG_SPI, "spi_write cmd -> ", cmd, sizeof(*cmd)); + wl1271_dump(DEBUG_SPI, "spi_write buf -> ", buf, len); } -void wl12xx_spi_mem_read(struct wl12xx *wl, int addr, void *buf, +void wl1271_spi_mem_read(struct wl1271 *wl, int addr, void *buf, size_t len) { int physical; - physical = wl12xx_translate_mem_addr(wl, addr); + physical = wl1271_translate_mem_addr(wl, addr); - wl12xx_spi_read(wl, physical, buf, len); + wl1271_spi_read(wl, physical, buf, len, false); } -void wl12xx_spi_mem_write(struct wl12xx *wl, int addr, void *buf, +void wl1271_spi_mem_write(struct wl1271 *wl, int addr, void *buf, size_t len) { int physical; - physical = wl12xx_translate_mem_addr(wl, addr); + physical = wl1271_translate_mem_addr(wl, addr); + + wl1271_spi_write(wl, physical, buf, len, false); +} + +void wl1271_spi_reg_read(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed) +{ + int physical; + + physical = wl1271_translate_reg_addr(wl, addr); + + wl1271_spi_read(wl, physical, buf, len, fixed); +} + +void wl1271_spi_reg_write(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed) +{ + int physical; + + physical = wl1271_translate_reg_addr(wl, addr); - wl12xx_spi_write(wl, physical, buf, len); + wl1271_spi_write(wl, physical, buf, len, fixed); } -u32 wl12xx_mem_read32(struct wl12xx *wl, int addr) +u32 wl1271_mem_read32(struct wl1271 *wl, int addr) { - return wl12xx_read32(wl, wl12xx_translate_mem_addr(wl, addr)); + return wl1271_read32(wl, wl1271_translate_mem_addr(wl, addr)); } -void wl12xx_mem_write32(struct wl12xx *wl, int addr, u32 val) +void wl1271_mem_write32(struct wl1271 *wl, int addr, u32 val) { - wl12xx_write32(wl, wl12xx_translate_mem_addr(wl, addr), val); + wl1271_write32(wl, wl1271_translate_mem_addr(wl, addr), val); } -u32 wl12xx_reg_read32(struct wl12xx *wl, int addr) +u32 wl1271_reg_read32(struct wl1271 *wl, int addr) { - return wl12xx_read32(wl, wl12xx_translate_reg_addr(wl, addr)); + return wl1271_read32(wl, wl1271_translate_reg_addr(wl, addr)); } -void wl12xx_reg_write32(struct wl12xx *wl, int addr, u32 val) +void wl1271_reg_write32(struct wl1271 *wl, int addr, u32 val) { - wl12xx_write32(wl, wl12xx_translate_reg_addr(wl, addr), val); + wl1271_write32(wl, wl1271_translate_reg_addr(wl, addr), val); } diff --git a/drivers/net/wireless/wl12xx/spi.h b/drivers/net/wireless/wl12xx/wl1271_spi.h index fd3227e904a8..2c9968458646 100644 --- a/drivers/net/wireless/wl12xx/spi.h +++ b/drivers/net/wireless/wl12xx/wl1271_spi.h @@ -1,10 +1,10 @@ /* - * This file is part of wl12xx + * This file is part of wl1271 * - * Copyright (c) 1998-2007 Texas Instruments Incorporated - * Copyright (C) 2008 Nokia Corporation + * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. + * Copyright (C) 2008-2009 Nokia Corporation * - * Contact: Kalle Valo <kalle.valo@nokia.com> + * Contact: Luciano Coelho <luciano.coelho@nokia.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -22,12 +22,10 @@ * */ -#ifndef __WL12XX_SPI_H__ -#define __WL12XX_SPI_H__ +#ifndef __WL1271_SPI_H__ +#define __WL1271_SPI_H__ -#include "cmd.h" -#include "acx.h" -#include "reg.h" +#include "wl1271_reg.h" #define HW_ACCESS_MEMORY_MAX_RANGE 0x1FFC0 @@ -65,45 +63,51 @@ #define WSPI_INIT_CMD_LEN 8 -#define TNETWIF_READ_OFFSET_BYTES 8 #define HW_ACCESS_WSPI_FIXED_BUSY_LEN \ - ((TNETWIF_READ_OFFSET_BYTES - 4) / sizeof(u32)) + ((WL1271_BUSY_WORD_LEN - 4) / sizeof(u32)) #define HW_ACCESS_WSPI_INIT_CMD_MASK 0 /* Raw target IO, address is not translated */ -void wl12xx_spi_read(struct wl12xx *wl, int addr, void *buf, size_t len); -void wl12xx_spi_write(struct wl12xx *wl, int addr, void *buf, size_t len); +void wl1271_spi_write(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed); +void wl1271_spi_read(struct wl1271 *wl, int addr, void *buf, + size_t len, bool fixed); /* Memory target IO, address is tranlated to partition 0 */ -void wl12xx_spi_mem_read(struct wl12xx *wl, int addr, void *buf, size_t len); -void wl12xx_spi_mem_write(struct wl12xx *wl, int addr, void *buf, size_t len); -u32 wl12xx_mem_read32(struct wl12xx *wl, int addr); -void wl12xx_mem_write32(struct wl12xx *wl, int addr, u32 val); +void wl1271_spi_mem_read(struct wl1271 *wl, int addr, void *buf, size_t len); +void wl1271_spi_mem_write(struct wl1271 *wl, int addr, void *buf, size_t len); +u32 wl1271_mem_read32(struct wl1271 *wl, int addr); +void wl1271_mem_write32(struct wl1271 *wl, int addr, u32 val); /* Registers IO */ -u32 wl12xx_reg_read32(struct wl12xx *wl, int addr); -void wl12xx_reg_write32(struct wl12xx *wl, int addr, u32 val); +void wl1271_spi_reg_read(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed); +void wl1271_spi_reg_write(struct wl1271 *wl, int addr, void *buf, size_t len, + bool fixed); +u32 wl1271_reg_read32(struct wl1271 *wl, int addr); +void wl1271_reg_write32(struct wl1271 *wl, int addr, u32 val); /* INIT and RESET words */ -void wl12xx_spi_reset(struct wl12xx *wl); -void wl12xx_spi_init(struct wl12xx *wl); -void wl12xx_set_partition(struct wl12xx *wl, - u32 part_start, u32 part_size, - u32 reg_start, u32 reg_size); +void wl1271_spi_reset(struct wl1271 *wl); +void wl1271_spi_init(struct wl1271 *wl); +int wl1271_set_partition(struct wl1271 *wl, + u32 part_start, u32 part_size, + u32 reg_start, u32 reg_size); -static inline u32 wl12xx_read32(struct wl12xx *wl, int addr) +static inline u32 wl1271_read32(struct wl1271 *wl, int addr) { - u32 response; + wl1271_spi_read(wl, addr, &wl->buffer_32, + sizeof(wl->buffer_32), false); - wl12xx_spi_read(wl, addr, &response, sizeof(u32)); - - return response; + return wl->buffer_32; } -static inline void wl12xx_write32(struct wl12xx *wl, int addr, u32 val) +static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val) { - wl12xx_spi_write(wl, addr, &val, sizeof(u32)); + wl->buffer_32 = val; + wl1271_spi_write(wl, addr, &wl->buffer_32, + sizeof(wl->buffer_32), false); } -#endif /* __WL12XX_SPI_H__ */ +#endif /* __WL1271_SPI_H__ */ diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c new file mode 100644 index 000000000000..ff221258b941 --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_tx.c @@ -0,0 +1,378 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> + +#include "wl1271.h" +#include "wl1271_spi.h" +#include "wl1271_reg.h" +#include "wl1271_ps.h" +#include "wl1271_tx.h" + +static int wl1271_tx_id(struct wl1271 *wl, struct sk_buff *skb) +{ + int i; + + for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) + if (wl->tx_frames[i] == NULL) { + wl->tx_frames[i] = skb; + return i; + } + + return -EBUSY; +} + +static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra) +{ + struct wl1271_tx_hw_descr *desc; + u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra; + u32 total_blocks, excluded; + int id, ret = -EBUSY; + + /* allocate free identifier for the packet */ + id = wl1271_tx_id(wl, skb); + if (id < 0) + return id; + + /* approximate the number of blocks required for this packet + in the firmware */ + /* FIXME: try to figure out what is done here and make it cleaner */ + total_blocks = (skb->len) >> TX_HW_BLOCK_SHIFT_DIV; + excluded = (total_blocks << 2) + (skb->len & 0xff) + 34; + total_blocks += (excluded > 252) ? 2 : 1; + total_blocks += TX_HW_BLOCK_SPARE; + + if (total_blocks <= wl->tx_blocks_available) { + desc = (struct wl1271_tx_hw_descr *)skb_push( + skb, total_len - skb->len); + + desc->extra_mem_blocks = TX_HW_BLOCK_SPARE; + desc->total_mem_blocks = total_blocks; + desc->id = id; + + wl->tx_blocks_available -= total_blocks; + + ret = 0; + + wl1271_debug(DEBUG_TX, + "tx_allocate: size: %d, blocks: %d, id: %d", + total_len, total_blocks, id); + } else + wl->tx_frames[id] = NULL; + + return ret; +} + +static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, + u32 extra, struct ieee80211_tx_info *control) +{ + struct wl1271_tx_hw_descr *desc; + int pad; + + desc = (struct wl1271_tx_hw_descr *) skb->data; + + /* configure packet life time */ + desc->start_time = jiffies_to_usecs(jiffies) - wl->time_offset; + desc->life_time = TX_HW_MGMT_PKT_LIFETIME_TU; + + /* configure the tx attributes */ + desc->tx_attr = wl->session_counter << TX_HW_ATTR_OFST_SESSION_COUNTER; + /* FIXME: do we know the packet priority? can we identify mgmt + packets, and use max prio for them at least? */ + desc->tid = 0; + desc->aid = TX_HW_DEFAULT_AID; + desc->reserved = 0; + + /* align the length (and store in terms of words) */ + pad = WL1271_TX_ALIGN(skb->len); + desc->length = pad >> 2; + + /* calculate number of padding bytes */ + pad = pad - skb->len; + desc->tx_attr |= pad << TX_HW_ATTR_OFST_LAST_WORD_PAD; + + wl1271_debug(DEBUG_TX, "tx_fill_hdr: pad: %d", pad); + return 0; +} + +static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb, + struct ieee80211_tx_info *control) +{ + + struct wl1271_tx_hw_descr *desc; + int len; + + /* FIXME: This is a workaround for getting non-aligned packets. + This happens at least with EAPOL packets from the user space. + Our DMA requires packets to be aligned on a 4-byte boundary. + */ + if (unlikely((long)skb->data & 0x03)) { + int offset = (4 - (long)skb->data) & 0x03; + wl1271_debug(DEBUG_TX, "skb offset %d", offset); + + /* check whether the current skb can be used */ + if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) { + unsigned char *src = skb->data; + + /* align the buffer on a 4-byte boundary */ + skb_reserve(skb, offset); + memmove(skb->data, src, skb->len); + } else { + wl1271_info("No handler, fixme!"); + return -EINVAL; + } + } + + len = WL1271_TX_ALIGN(skb->len); + + /* perform a fixed address block write with the packet */ + wl1271_spi_reg_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true); + + /* write packet new counter into the write access register */ + wl->tx_packets_count++; + wl1271_reg_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); + + desc = (struct wl1271_tx_hw_descr *) skb->data; + wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)", + desc->id, skb, len, desc->length); + + return 0; +} + +/* caller must hold wl->mutex */ +static int wl1271_tx_frame(struct wl1271 *wl, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info; + u32 extra = 0; + int ret = 0; + u8 idx; + + if (!skb) + return -EINVAL; + + info = IEEE80211_SKB_CB(skb); + + if (info->control.hw_key && + info->control.hw_key->alg == ALG_TKIP) + extra = WL1271_TKIP_IV_SPACE; + + if (info->control.hw_key) { + idx = info->control.hw_key->hw_key_idx; + + /* FIXME: do we have to do this if we're not using WEP? */ + if (unlikely(wl->default_key != idx)) { + ret = wl1271_cmd_set_default_wep_key(wl, idx); + if (ret < 0) + return ret; + } + } + + ret = wl1271_tx_allocate(wl, skb, extra); + if (ret < 0) + return ret; + + ret = wl1271_tx_fill_hdr(wl, skb, extra, info); + if (ret < 0) + return ret; + + ret = wl1271_tx_send_packet(wl, skb, info); + if (ret < 0) + return ret; + + return ret; +} + +void wl1271_tx_work(struct work_struct *work) +{ + struct wl1271 *wl = container_of(work, struct wl1271, tx_work); + struct sk_buff *skb; + bool woken_up = false; + int ret; + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state == WL1271_STATE_OFF)) + goto out; + + while ((skb = skb_dequeue(&wl->tx_queue))) { + if (!woken_up) { + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + woken_up = true; + } + + ret = wl1271_tx_frame(wl, skb); + if (ret == -EBUSY) { + /* firmware buffer is full, stop queues */ + wl1271_debug(DEBUG_TX, "tx_work: fw buffer full, " + "stop queues"); + ieee80211_stop_queues(wl->hw); + wl->tx_queue_stopped = true; + skb_queue_head(&wl->tx_queue, skb); + goto out; + } else if (ret < 0) { + dev_kfree_skb(skb); + goto out; + } else if (wl->tx_queue_stopped) { + /* firmware buffer has space, restart queues */ + wl1271_debug(DEBUG_TX, + "complete_packet: waking queues"); + ieee80211_wake_queues(wl->hw); + wl->tx_queue_stopped = false; + } + } + +out: + if (woken_up) + wl1271_ps_elp_sleep(wl); + + mutex_unlock(&wl->mutex); +} + +static void wl1271_tx_complete_packet(struct wl1271 *wl, + struct wl1271_tx_hw_res_descr *result) +{ + + struct ieee80211_tx_info *info; + struct sk_buff *skb; + u32 header_len; + int id = result->id; + + /* check for id legality */ + if (id >= TX_HW_RESULT_QUEUE_LEN || wl->tx_frames[id] == NULL) { + wl1271_warning("TX result illegal id: %d", id); + return; + } + + skb = wl->tx_frames[id]; + info = IEEE80211_SKB_CB(skb); + + /* update packet status */ + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { + if (result->status == TX_SUCCESS) + info->flags |= IEEE80211_TX_STAT_ACK; + if (result->status & TX_RETRY_EXCEEDED) { + /* FIXME */ + /* info->status.excessive_retries = 1; */ + wl->stats.excessive_retries++; + } + } + + /* FIXME */ + /* info->status.retry_count = result->ack_failures; */ + wl->stats.retry_count += result->ack_failures; + + /* get header len */ + if (info->control.hw_key && + info->control.hw_key->alg == ALG_TKIP) + header_len = WL1271_TKIP_IV_SPACE + + sizeof(struct wl1271_tx_hw_descr); + else + header_len = sizeof(struct wl1271_tx_hw_descr); + + wl1271_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" + " status 0x%x", + result->id, skb, result->ack_failures, + result->rate_class_index, result->status); + + /* remove private header from packet */ + skb_pull(skb, header_len); + + /* return the packet to the stack */ + ieee80211_tx_status(wl->hw, skb); + wl->tx_frames[result->id] = NULL; +} + +/* Called upon reception of a TX complete interrupt */ +void wl1271_tx_complete(struct wl1271 *wl, u32 count) +{ + struct wl1271_acx_mem_map *memmap = + (struct wl1271_acx_mem_map *)wl->target_mem_map; + u32 i; + + wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count); + + /* read the tx results from the chipset */ + wl1271_spi_mem_read(wl, memmap->tx_result, + wl->tx_res_if, sizeof(*wl->tx_res_if)); + + /* verify that the result buffer is not getting overrun */ + if (count > TX_HW_RESULT_QUEUE_LEN) { + wl1271_warning("TX result overflow from chipset: %d", count); + count = TX_HW_RESULT_QUEUE_LEN; + } + + /* process the results */ + for (i = 0; i < count; i++) { + struct wl1271_tx_hw_res_descr *result; + u8 offset = wl->tx_results_count & TX_HW_RESULT_QUEUE_LEN_MASK; + + /* process the packet */ + result = &(wl->tx_res_if->tx_results_queue[offset]); + wl1271_tx_complete_packet(wl, result); + + wl->tx_results_count++; + } + + /* write host counter to chipset (to ack) */ + wl1271_mem_write32(wl, memmap->tx_result + + offsetof(struct wl1271_tx_hw_res_if, + tx_result_host_counter), + wl->tx_res_if->tx_result_fw_counter); +} + +/* caller must hold wl->mutex */ +void wl1271_tx_flush(struct wl1271 *wl) +{ + int i; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + + /* TX failure */ +/* control->flags = 0; FIXME */ + + while ((skb = skb_dequeue(&wl->tx_queue))) { + info = IEEE80211_SKB_CB(skb); + + wl1271_debug(DEBUG_TX, "flushing skb 0x%p", skb); + + if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) + continue; + + ieee80211_tx_status(wl->hw, skb); + } + + for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) + if (wl->tx_frames[i] != NULL) { + skb = wl->tx_frames[i]; + info = IEEE80211_SKB_CB(skb); + + if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) + continue; + + ieee80211_tx_status(wl->hw, skb); + wl->tx_frames[i] = NULL; + } +} diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.h b/drivers/net/wireless/wl12xx/wl1271_tx.h new file mode 100644 index 000000000000..4a614067ddba --- /dev/null +++ b/drivers/net/wireless/wl12xx/wl1271_tx.h @@ -0,0 +1,130 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 1998-2009 Texas Instruments. All rights reserved. + * Copyright (C) 2009 Nokia Corporation + * + * Contact: Luciano Coelho <luciano.coelho@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 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 St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL1271_TX_H__ +#define __WL1271_TX_H__ + +#define TX_HW_BLOCK_SPARE 2 +#define TX_HW_BLOCK_SHIFT_DIV 8 + +#define TX_HW_MGMT_PKT_LIFETIME_TU 2000 +/* The chipset reference driver states, that the "aid" value 1 + * is for infra-BSS, but is still always used */ +#define TX_HW_DEFAULT_AID 1 + +#define TX_HW_ATTR_SAVE_RETRIES BIT(0) +#define TX_HW_ATTR_HEADER_PAD BIT(1) +#define TX_HW_ATTR_SESSION_COUNTER (BIT(2) | BIT(3) | BIT(4)) +#define TX_HW_ATTR_RATE_POLICY (BIT(5) | BIT(6) | BIT(7) | \ + BIT(8) | BIT(9)) +#define TX_HW_ATTR_LAST_WORD_PAD (BIT(10) | BIT(11)) +#define TX_HW_ATTR_TX_CMPLT_REQ BIT(12) + +#define TX_HW_ATTR_OFST_SAVE_RETRIES 0 +#define TX_HW_ATTR_OFST_HEADER_PAD 1 +#define TX_HW_ATTR_OFST_SESSION_COUNTER 2 +#define TX_HW_ATTR_OFST_RATE_POLICY 5 +#define TX_HW_ATTR_OFST_LAST_WORD_PAD 10 +#define TX_HW_ATTR_OFST_TX_CMPLT_REQ 12 + +#define TX_HW_RESULT_QUEUE_LEN 16 +#define TX_HW_RESULT_QUEUE_LEN_MASK 0xf + +#define WL1271_TX_ALIGN_TO 4 +#define WL1271_TX_ALIGN(len) (((len) + WL1271_TX_ALIGN_TO - 1) & \ + ~(WL1271_TX_ALIGN_TO - 1)) +#define WL1271_TKIP_IV_SPACE 4 + +struct wl1271_tx_hw_descr { + /* Length of packet in words, including descriptor+header+data */ + u16 length; + /* Number of extra memory blocks to allocate for this packet in + addition to the number of blocks derived from the packet length */ + u8 extra_mem_blocks; + /* Total number of memory blocks allocated by the host for this packet. + Must be equal or greater than the actual blocks number allocated by + HW!! */ + u8 total_mem_blocks; + /* Device time (in us) when the packet arrived to the driver */ + u32 start_time; + /* Max delay in TUs until transmission. The last device time the + packet can be transmitted is: startTime+(1024*LifeTime) */ + u16 life_time; + /* Bitwise fields - see TX_ATTR... definitions above. */ + u16 tx_attr; + /* Packet identifier used also in the Tx-Result. */ + u8 id; + /* The packet TID value (as User-Priority) */ + u8 tid; + /* Identifier of the remote STA in IBSS, 1 in infra-BSS */ + u8 aid; + u8 reserved; +} __attribute__ ((packed)); + +enum wl1271_tx_hw_res_status { + TX_SUCCESS = 0, + TX_HW_ERROR = 1, + TX_DISABLED = 2, + TX_RETRY_EXCEEDED = 3, + TX_TIMEOUT = 4, + TX_KEY_NOT_FOUND = 5, + TX_PEER_NOT_FOUND = 6, + TX_SESSION_MISMATCH = 7 +}; + +struct wl1271_tx_hw_res_descr { + /* Packet Identifier - same value used in the Tx descriptor.*/ + u8 id; + /* The status of the transmission, indicating success or one of + several possible reasons for failure. */ + u8 status; + /* Total air access duration including all retrys and overheads.*/ + u16 medium_usage; + /* The time passed from host xfer to Tx-complete.*/ + u32 fw_handling_time; + /* Total media delay + (from 1st EDCA AIFS counter until TX Complete). */ + u32 medium_delay; + /* LS-byte of last TKIP seq-num (saved per AC for recovery). */ + u8 lsb_security_sequence_number; + /* Retry count - number of transmissions without successful ACK.*/ + u8 ack_failures; + /* The rate that succeeded getting ACK + (Valid only if status=SUCCESS). */ + u8 rate_class_index; + /* for 4-byte alignment. */ + u8 spare; +} __attribute__ ((packed)); + +struct wl1271_tx_hw_res_if { + u32 tx_result_fw_counter; + u32 tx_result_host_counter; + struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN]; +} __attribute__ ((packed)); + +void wl1271_tx_work(struct work_struct *work); +void wl1271_tx_complete(struct wl1271 *wl, u32 count); +void wl1271_tx_flush(struct wl1271 *wl); + +#endif diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index e3e96bb2c246..4f1e0cfe609b 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -1333,7 +1333,8 @@ static void wl3501_tx_timeout(struct net_device *dev) * 1 - Could not transmit (dev_queue_xmit will queue it) * and try to sent it later */ -static int wl3501_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t wl3501_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) { int enabled, rc; struct wl3501_card *this = netdev_priv(dev); @@ -1348,7 +1349,6 @@ static int wl3501_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (rc) { ++dev->stats.tx_dropped; netif_stop_queue(dev); - rc = NETDEV_TX_OK; } else { ++dev->stats.tx_packets; dev->stats.tx_bytes += skb->len; @@ -1358,7 +1358,7 @@ static int wl3501_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); } spin_unlock_irqrestore(&this->lock, flags); - return rc; + return NETDEV_TX_OK; } static int wl3501_open(struct net_device *dev) diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index 4430b8d92e21..bc81974a2bc7 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -779,7 +779,8 @@ static int zd1201_net_stop(struct net_device *dev) (llc+snap+type+payload) zd 1 null byte, zd1201 packet type */ -static int zd1201_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t zd1201_hard_start_xmit(struct sk_buff *skb, + struct net_device *dev) { struct zd1201 *zd = netdev_priv(dev); unsigned char *txbuf = zd->txdata; @@ -789,7 +790,7 @@ static int zd1201_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (!zd->mac_enabled || zd->monitor) { dev->stats.tx_dropped++; kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } netif_stop_queue(dev); @@ -826,7 +827,7 @@ static int zd1201_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) } kfree_skb(skb); - return 0; + return NETDEV_TX_OK; } static void zd1201_tx_timeout(struct net_device *dev) diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c index 2c813d87092c..5e110a2328ae 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.c +++ b/drivers/net/wireless/zd1211rw/zd_chip.c @@ -1278,11 +1278,11 @@ int zd_chip_control_leds(struct zd_chip *chip, enum led_status status) other_led = chip->link_led == LED1 ? LED2 : LED1; switch (status) { - case LED_OFF: + case ZD_LED_OFF: ioreqs[0].value = FW_LINK_OFF; ioreqs[1].value = v[1] & ~(LED1|LED2); break; - case LED_SCANNING: + case ZD_LED_SCANNING: ioreqs[0].value = FW_LINK_OFF; ioreqs[1].value = v[1] & ~other_led; if (get_seconds() % 3 == 0) { @@ -1291,7 +1291,7 @@ int zd_chip_control_leds(struct zd_chip *chip, enum led_status status) ioreqs[1].value |= chip->link_led; } break; - case LED_ASSOCIATED: + case ZD_LED_ASSOCIATED: ioreqs[0].value = FW_LINK_TX; ioreqs[1].value = v[1] & ~other_led; ioreqs[1].value |= chip->link_led; diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h index ee42751d5cb0..678c139a840c 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.h +++ b/drivers/net/wireless/zd1211rw/zd_chip.h @@ -897,9 +897,9 @@ int zd_chip_lock_phy_regs(struct zd_chip *chip); int zd_chip_unlock_phy_regs(struct zd_chip *chip); enum led_status { - LED_OFF = 0, - LED_SCANNING = 1, - LED_ASSOCIATED = 2, + ZD_LED_OFF = 0, + ZD_LED_SCANNING = 1, + ZD_LED_ASSOCIATED = 2, }; int zd_chip_control_leds(struct zd_chip *chip, enum led_status status); diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 3bd3c779fff3..6d666359a42f 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -711,7 +711,8 @@ int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length) memcpy(skb_put(skb, length), buffer, length); - ieee80211_rx_irqsafe(hw, skb, &stats); + memcpy(IEEE80211_SKB_RXCB(skb), &stats, sizeof(stats)); + ieee80211_rx_irqsafe(hw, skb); return 0; } @@ -795,18 +796,40 @@ static void set_rx_filter_handler(struct work_struct *work) dev_err(zd_mac_dev(mac), "set_rx_filter_handler error %d\n", r); } +static u64 zd_op_prepare_multicast(struct ieee80211_hw *hw, + int mc_count, struct dev_addr_list *mclist) +{ + struct zd_mac *mac = zd_hw_mac(hw); + struct zd_mc_hash hash; + int i; + + zd_mc_clear(&hash); + + for (i = 0; i < mc_count; i++) { + if (!mclist) + break; + dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", mclist->dmi_addr); + zd_mc_add_addr(&hash, mclist->dmi_addr); + mclist = mclist->next; + } + + return hash.low | ((u64)hash.high << 32); +} + #define SUPPORTED_FIF_FLAGS \ (FIF_PROMISC_IN_BSS | FIF_ALLMULTI | FIF_FCSFAIL | FIF_CONTROL | \ FIF_OTHER_BSS | FIF_BCN_PRBRESP_PROMISC) static void zd_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *new_flags, - int mc_count, struct dev_mc_list *mclist) + u64 multicast) { - struct zd_mc_hash hash; + struct zd_mc_hash hash = { + .low = multicast, + .high = multicast >> 32, + }; struct zd_mac *mac = zd_hw_mac(hw); unsigned long flags; - int i; /* Only deal with supported flags */ changed_flags &= SUPPORTED_FIF_FLAGS; @@ -818,25 +841,16 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw, if (!changed_flags) return; - if (*new_flags & (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)) { + if (*new_flags & (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)) zd_mc_add_all(&hash); - } else { - zd_mc_clear(&hash); - for (i = 0; i < mc_count; i++) { - if (!mclist) - break; - dev_dbg_f(zd_mac_dev(mac), "mc addr %pM\n", - mclist->dmi_addr); - zd_mc_add_addr(&hash, mclist->dmi_addr); - mclist = mclist->next; - } - } spin_lock_irqsave(&mac->lock, flags); mac->pass_failed_fcs = !!(*new_flags & FIF_FCSFAIL); mac->pass_ctrl = !!(*new_flags & FIF_CONTROL); mac->multicast_hash = hash; spin_unlock_irqrestore(&mac->lock, flags); + + /* XXX: these can be called here now, can sleep now! */ queue_work(zd_workqueue, &mac->set_multicast_hash_work); if (changed_flags & FIF_CONTROL) @@ -939,6 +953,7 @@ static const struct ieee80211_ops zd_ops = { .add_interface = zd_op_add_interface, .remove_interface = zd_op_remove_interface, .config = zd_op_config, + .prepare_multicast = zd_op_prepare_multicast, .configure_filter = zd_op_configure_filter, .bss_info_changed = zd_op_bss_info_changed, .get_tsf = zd_op_get_tsf, @@ -1012,7 +1027,7 @@ static void link_led_handler(struct work_struct *work) spin_unlock_irq(&mac->lock); r = zd_chip_control_leds(chip, - is_associated ? LED_ASSOCIATED : LED_SCANNING); + is_associated ? ZD_LED_ASSOCIATED : ZD_LED_SCANNING); if (r) dev_dbg_f(zd_mac_dev(mac), "zd_chip_control_leds error %d\n", r); @@ -1037,5 +1052,5 @@ static void housekeeping_disable(struct zd_mac *mac) dev_dbg_f(zd_mac_dev(mac), "\n"); cancel_rearming_delayed_workqueue(zd_workqueue, &mac->housekeeping.link_led_work); - zd_chip_control_leds(&mac->chip, LED_OFF); + zd_chip_control_leds(&mac->chip, ZD_LED_OFF); } diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 0e6e44689cc6..38688847d568 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -36,58 +36,60 @@ static struct usb_device_id usb_ids[] = { /* ZD1211 */ + { USB_DEVICE(0x0105, 0x145f), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0586, 0x3402), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0586, 0x3407), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0586, 0x3409), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x079b, 0x004a), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0ace, 0x1211), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0ace, 0xa211), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0b05, 0x170c), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0b3b, 0x1630), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x0b3b, 0x5630), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0df6, 0x9071), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0df6, 0x9075), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x079b, 0x004a), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x1740, 0x2000), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x157e, 0x3204), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0586, 0x3402), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0b3b, 0x5630), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0b05, 0x170c), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x126f, 0xa006), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x129b, 0x1666), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x1435, 0x0711), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0586, 0x3409), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0b3b, 0x1630), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x14ea, 0xab13), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0586, 0x3407), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x129b, 0x1666), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x157e, 0x300a), .driver_info = DEVICE_ZD1211 }, - { USB_DEVICE(0x0105, 0x145f), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x157e, 0x300b), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x157e, 0x3204), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x1740, 0x2000), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x6891, 0xa727), .driver_info = DEVICE_ZD1211 }, /* ZD1211B */ + { USB_DEVICE(0x0053, 0x5301), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0411, 0x00da), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0471, 0x1237), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x050d, 0x705c), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x054c, 0x0257), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0ace, 0xb215), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x340a), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3413), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x1582, 0x6003), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x050d, 0x705c), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x07fa, 0x1196), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x083a, 0x4505), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x083a, 0xe501), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x083a, 0xe503), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x083a, 0xe506), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x083a, 0x4505), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0ace, 0xb215), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0b05, 0x171b), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0586, 0x3413), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0053, 0x5301), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0411, 0x00da), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x2019, 0x5303), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x129b, 0x1667), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0cde, 0x001a), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0586, 0x340a), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x0471, 0x1237), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x07fa, 0x1196), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0df6, 0x0036), .driver_info = DEVICE_ZD1211B }, - { USB_DEVICE(0x07b8, 0x6001), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x129b, 0x1667), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x1582, 0x6003), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x2019, 0x5303), .driver_info = DEVICE_ZD1211B }, /* "Driverless" devices that need ejecting */ { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, { USB_DEVICE(0x0ace, 0x20ff), .driver_info = DEVICE_INSTALLER }, |