diff options
Diffstat (limited to 'drivers')
84 files changed, 1846 insertions, 2269 deletions
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index f7b0af7100cd..b6b4b5ebd4c2 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -273,6 +273,7 @@ static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4358) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) }, { 0, }, diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index 9ea4627dc0c2..0d546b64be34 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -507,7 +507,9 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus) /* for these chips OTP is always available */ present = true; break; + case BCMA_CHIP_ID_BCM43227: case BCMA_CHIP_ID_BCM43228: + case BCMA_CHIP_ID_BCM43428: present = chip_status & BCMA_CC_CHIPST_43228_OTP_PRESENT; break; default: diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 0c0838d9b56c..0d26851d6e49 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -681,7 +681,7 @@ static int bluecard_hci_send_frame(struct sk_buff *skb) case HCI_SCODATA_PKT: hdev->stat.sco_tx++; break; - }; + } /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 03b3acba6143..3f4bfc814dc7 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -600,8 +600,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv) exit: if (ret) { hdev->stat.err_rx++; - if (skb) - kfree_skb(skb); + kfree_skb(skb); } return ret; diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 2f510a87b28f..35a553a90616 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -446,7 +446,7 @@ static int btuart_hci_send_frame(struct sk_buff *skb) case HCI_SCODATA_PKT: hdev->stat.sco_tx++; break; - }; + } /* Prepend skb with frame type */ memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index e5921d681ddb..debda27df9b0 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -96,11 +96,12 @@ static struct usb_device_id btusb_table[] = { { USB_DEVICE(0x0c10, 0x0000) }, /* Broadcom BCM20702A0 */ + { USB_DEVICE(0x04ca, 0x2003) }, { USB_DEVICE(0x0489, 0xe042) }, { USB_DEVICE(0x413c, 0x8197) }, /* Foxconn - Hon Hai */ - { USB_DEVICE(0x0489, 0xe033) }, + { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) }, /*Broadcom devices with vendor specific id */ { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) }, diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c index 4ad7b35cfc0e..60abf596f60e 100644 --- a/drivers/bluetooth/btwilink.c +++ b/drivers/bluetooth/btwilink.c @@ -358,21 +358,7 @@ static struct platform_driver btwilink_driver = { }, }; -/* ------- Module Init/Exit interfaces ------ */ -static int __init btwilink_init(void) -{ - BT_INFO("Bluetooth Driver for TI WiLink - Version %s", VERSION); - - return platform_driver_register(&btwilink_driver); -} - -static void __exit btwilink_exit(void) -{ - platform_driver_unregister(&btwilink_driver); -} - -module_init(btwilink_init); -module_exit(btwilink_exit); +module_platform_driver(btwilink_driver); /* ------ Module Info ------ */ diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 74e0966b3ead..c8abce3d2d9c 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -531,7 +531,7 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, default: err = n_tty_ioctl_helper(tty, file, cmd, arg); break; - }; + } return err; } diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index ff6d589c34a5..cfc767938589 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -481,7 +481,7 @@ static int ll_recv(struct hci_uart *hu, void *data, int count) hu->hdev->stat.err_rx++; ptr++; count--; continue; - }; + } ptr++; count--; diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 3f72595a6017..d8b7aed6e4a9 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -156,7 +156,7 @@ static inline ssize_t vhci_put_user(struct vhci_data *data, case HCI_SCODATA_PKT: data->hdev->stat.sco_tx++; break; - }; + } return total; } diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index 6169fbd23ed1..4521342c62cc 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -159,6 +159,7 @@ struct ath_common { bool btcoex_enabled; bool disable_ani; + bool antenna_diversity; }; struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index a0a202de1109..9fd6d9a9942e 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -2446,6 +2446,7 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_MFP_CAPABLE | IEEE80211_HW_REPORTS_TX_ACK_STATUS; hw->wiphy->interface_modes = diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index df61a09adb6d..7a28538e6e05 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -489,6 +489,9 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (ath5k_modparam_nohwcrypt) return -EOPNOTSUPP; + if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) + return -EOPNOTSUPP; + if (vif->type == NL80211_IFTYPE_ADHOC && (key->cipher == WLAN_CIPHER_SUITE_TKIP || key->cipher == WLAN_CIPHER_SUITE_CCMP) && @@ -523,7 +526,7 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (key->cipher == WLAN_CIPHER_SUITE_TKIP) key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; if (key->cipher == WLAN_CIPHER_SUITE_CCMP) - key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; ret = 0; } break; diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index 01c90ed58453..ab363f34b4df 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -1975,11 +1975,13 @@ ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah, spur_delta_phase = (spur_offset << 18) / 25; spur_freq_sigma_delta = (spur_delta_phase >> 10); symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 2; + break; case AR5K_BWMODE_5MHZ: /* Both sample_freq and chip_freq are 10MHz (?) */ spur_delta_phase = (spur_offset << 19) / 25; spur_freq_sigma_delta = (spur_delta_phase >> 10); symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz / 4; + break; default: if (channel->band == IEEE80211_BAND_5GHZ) { /* Both sample_freq and chip_freq are 40MHz */ diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 86aeef4b9d7e..7089f8160ad5 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1488,7 +1488,7 @@ static int ath6kl_cfg80211_set_power_mgmt(struct wiphy *wiphy, } static struct wireless_dev *ath6kl_cfg80211_add_iface(struct wiphy *wiphy, - char *name, + const char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params) @@ -3477,7 +3477,7 @@ void ath6kl_cfg80211_vif_cleanup(struct ath6kl_vif *vif) ar->num_vif--; } -struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, char *name, +struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, enum nl80211_iftype type, u8 fw_vif_idx, u8 nw_type) { diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h index 56b1ebe79812..780f77775a91 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.h +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h @@ -25,7 +25,7 @@ enum ath6kl_cfg_suspend_mode { ATH6KL_CFG_SUSPEND_SCHED_SCAN, }; -struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, char *name, +struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, enum nl80211_iftype type, u8 fw_vif_idx, u8 nw_type); void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, diff --git a/drivers/net/wireless/ath/ath9k/antenna.c b/drivers/net/wireless/ath/ath9k/antenna.c index bbcfeb3b2a60..664844c5d3d5 100644 --- a/drivers/net/wireless/ath/ath9k/antenna.c +++ b/drivers/net/wireless/ath/ath9k/antenna.c @@ -311,6 +311,9 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, struct ath_ant_comb *antcomb, int alt_ratio) { + ant_conf->main_gaintb = 0; + ant_conf->alt_gaintb = 0; + if (ant_conf->div_group == 0) { /* Adjust the fast_div_bias based on main and alt lna conf */ switch ((ant_conf->main_lna_conf << 4) | @@ -360,18 +363,12 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->alt_lna_conf) { case 0x01: /* A-B LNA2 */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x02: /* A-B LNA1 */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x03: /* A-B A+B */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x10: /* LNA2 A-B */ if (!(antcomb->scan) && @@ -379,13 +376,9 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->fast_div_bias = 0x3f; else ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x12: /* LNA2 LNA1 */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x13: /* LNA2 A+B */ if (!(antcomb->scan) && @@ -393,8 +386,6 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->fast_div_bias = 0x3f; else ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x20: /* LNA1 A-B */ if (!(antcomb->scan) && @@ -402,13 +393,9 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->fast_div_bias = 0x3f; else ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x21: /* LNA1 LNA2 */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x23: /* LNA1 A+B */ if (!(antcomb->scan) && @@ -416,23 +403,15 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->fast_div_bias = 0x3f; else ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x30: /* A+B A-B */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x31: /* A+B LNA2 */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x32: /* A+B LNA1 */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; default: break; @@ -443,18 +422,12 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->alt_lna_conf) { case 0x01: /* A-B LNA2 */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x02: /* A-B LNA1 */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x03: /* A-B A+B */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x10: /* LNA2 A-B */ if (!(antcomb->scan) && @@ -462,13 +435,9 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->fast_div_bias = 0x1; else ant_conf->fast_div_bias = 0x2; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x12: /* LNA2 LNA1 */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x13: /* LNA2 A+B */ if (!(antcomb->scan) && @@ -476,8 +445,6 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->fast_div_bias = 0x1; else ant_conf->fast_div_bias = 0x2; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x20: /* LNA1 A-B */ if (!(antcomb->scan) && @@ -485,13 +452,9 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->fast_div_bias = 0x1; else ant_conf->fast_div_bias = 0x2; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x21: /* LNA1 LNA2 */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x23: /* LNA1 A+B */ if (!(antcomb->scan) && @@ -499,23 +462,77 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->fast_div_bias = 0x1; else ant_conf->fast_div_bias = 0x2; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x30: /* A+B A-B */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x31: /* A+B LNA2 */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; break; case 0x32: /* A+B LNA1 */ ant_conf->fast_div_bias = 0x1; - ant_conf->main_gaintb = 0; - ant_conf->alt_gaintb = 0; + break; + default: + break; + } + } else if (ant_conf->div_group == 3) { + switch ((ant_conf->main_lna_conf << 4) | + ant_conf->alt_lna_conf) { + case 0x01: /* A-B LNA2 */ + ant_conf->fast_div_bias = 0x1; + break; + case 0x02: /* A-B LNA1 */ + ant_conf->fast_div_bias = 0x39; + break; + case 0x03: /* A-B A+B */ + ant_conf->fast_div_bias = 0x1; + break; + case 0x10: /* LNA2 A-B */ + if ((antcomb->scan == 0) && + (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { + ant_conf->fast_div_bias = 0x3f; + } else { + ant_conf->fast_div_bias = 0x1; + } + break; + case 0x12: /* LNA2 LNA1 */ + ant_conf->fast_div_bias = 0x39; + break; + case 0x13: /* LNA2 A+B */ + if ((antcomb->scan == 0) && + (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { + ant_conf->fast_div_bias = 0x3f; + } else { + ant_conf->fast_div_bias = 0x1; + } + break; + case 0x20: /* LNA1 A-B */ + if ((antcomb->scan == 0) && + (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { + ant_conf->fast_div_bias = 0x3f; + } else { + ant_conf->fast_div_bias = 0x4; + } + break; + case 0x21: /* LNA1 LNA2 */ + ant_conf->fast_div_bias = 0x6; + break; + case 0x23: /* LNA1 A+B */ + if ((antcomb->scan == 0) && + (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { + ant_conf->fast_div_bias = 0x3f; + } else { + ant_conf->fast_div_bias = 0x6; + } + break; + case 0x30: /* A+B A-B */ + ant_conf->fast_div_bias = 0x1; + break; + case 0x31: /* A+B LNA2 */ + ant_conf->fast_div_bias = 0x6; + break; + case 0x32: /* A+B LNA1 */ + ant_conf->fast_div_bias = 0x1; break; default: break; @@ -759,6 +776,7 @@ div_comb_done: void ath_ant_comb_update(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ath_hw_antcomb_conf div_ant_conf; u8 lna_conf; @@ -773,4 +791,7 @@ void ath_ant_comb_update(struct ath_softc *sc) div_ant_conf.alt_lna_conf = lna_conf; ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf); + + if (common->antenna_diversity) + ath9k_hw_antctrl_shared_chain_lnadiv(ah, true); } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 884f9f0014ae..5bbe5057ba18 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3566,9 +3566,9 @@ static u16 ar9003_hw_ant_ctrl_chain_get(struct ath_hw *ah, int chain, static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) { + struct ath9k_hw_capabilities *pCap = &ah->caps; int chain; u32 regval; - u32 ant_div_ctl1; static const u32 switch_chain_reg[AR9300_MAX_CHAINS] = { AR_PHY_SWITCH_CHAIN_0, AR_PHY_SWITCH_CHAIN_1, @@ -3633,6 +3633,16 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) /* enable_lnadiv */ regval &= (~AR_PHY_ANT_DIV_LNADIV); regval |= ((value >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S; + + if (AR_SREV_9565(ah)) { + if (ah->shared_chain_lnadiv) { + regval |= (1 << AR_PHY_ANT_SW_RX_PROT_S); + } else { + regval &= ~(1 << AR_PHY_ANT_DIV_LNADIV_S); + regval &= ~(1 << AR_PHY_ANT_SW_RX_PROT_S); + } + } + REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); /*enable fast_div */ @@ -3640,9 +3650,8 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) regval &= (~AR_FAST_DIV_ENABLE); regval |= ((value >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S; REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); - ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1); - /* check whether antenna diversity is enabled */ - if ((ant_div_ctl1 >> 0x6) == 0x3) { + + if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) { regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL); /* * clear bits 25-30 main_lnaconf, alt_lnaconf, @@ -3659,10 +3668,7 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) AR_PHY_ANT_DIV_ALT_LNACONF_S); REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); } - - } - } static void ar9003_hw_drive_strength_apply(struct ath_hw *ah) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index b2e39e8a21b5..8dbb60b53f1a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -1027,6 +1027,7 @@ void ar9003_mci_2g5g_switch(struct ath_hw *ah, bool force) if (!(mci->config & ATH_MCI_CONFIG_DISABLE_OSLA)) ar9003_mci_osla_setup(ah, true); + REG_WRITE(ah, AR_SELFGEN_MASK, 0x02); } else { ar9003_mci_send_lna_take(ah, true); udelay(5); @@ -1235,6 +1236,10 @@ u32 ar9003_mci_state(struct ath_hw *ah, u32 state_type) case MCI_STATE_NEED_FTP_STOMP: value = !(mci->config & ATH_MCI_CONFIG_DISABLE_FTP_STOMP); break; + case MCI_STATE_NEED_FLUSH_BT_INFO: + value = (!mci->unhalt_bt_gpm && mci->need_flush_btinfo) ? 1 : 0; + mci->need_flush_btinfo = false; + break; default: break; } @@ -1284,7 +1289,7 @@ void ar9003_mci_set_power_awake(struct ath_hw *ah) } REG_WRITE(ah, AR_DIAG_SW, (diag_sw | BIT(27) | BIT(19) | BIT(18))); lna_ctrl = REG_READ(ah, AR_OBS_BUS_CTRL) & 0x3; - bt_sleep = REG_READ(ah, AR_MCI_RX_STATUS) & AR_MCI_RX_REMOTE_SLEEP; + bt_sleep = MS(REG_READ(ah, AR_MCI_RX_STATUS), AR_MCI_RX_REMOTE_SLEEP); REG_WRITE(ah, AR_BTCOEX_CTRL2, btcoex_ctrl2); REG_WRITE(ah, AR_DIAG_SW, diag_sw); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.h b/drivers/net/wireless/ath/ath9k/ar9003_mci.h index f3bef8d69edd..30acf2869aa4 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.h @@ -200,6 +200,7 @@ enum mci_state_type { MCI_STATE_RECOVER_RX, MCI_STATE_NEED_FTP_STOMP, MCI_STATE_DEBUG, + MCI_STATE_NEED_FLUSH_BT_INFO, MCI_STATE_MAX }; @@ -211,7 +212,8 @@ enum mci_gpm_coex_opcode { MCI_GPM_COEX_WLAN_CHANNELS, MCI_GPM_COEX_BT_PROFILE_INFO, MCI_GPM_COEX_BT_STATUS_UPDATE, - MCI_GPM_COEX_BT_UPDATE_FLAGS + MCI_GPM_COEX_BT_UPDATE_FLAGS, + MCI_GPM_COEX_NOOP, }; #define MCI_GPM_NOMORE 0 diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 0d800c62e227..fc67844a1430 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -605,9 +605,6 @@ static void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx) if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && (tx == 0x7)) REG_WRITE(ah, AR_SELFGEN_MASK, 0x3); - else if (AR_SREV_9462(ah)) - /* xxx only when MCI support is enabled */ - REG_WRITE(ah, AR_SELFGEN_MASK, 0x3); else REG_WRITE(ah, AR_SELFGEN_MASK, tx); @@ -1294,6 +1291,9 @@ static void ar9003_hw_antdiv_comb_conf_get(struct ath_hw *ah, } else if (AR_SREV_9485(ah)) { antconf->lna1_lna2_delta = -9; antconf->div_group = 2; + } else if (AR_SREV_9565(ah)) { + antconf->lna1_lna2_delta = -3; + antconf->div_group = 3; } else { antconf->lna1_lna2_delta = -3; antconf->div_group = 0; @@ -1325,6 +1325,65 @@ static void ar9003_hw_antdiv_comb_conf_set(struct ath_hw *ah, REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); } +static void ar9003_hw_antctrl_shared_chain_lnadiv(struct ath_hw *ah, + bool enable) +{ + u8 ant_div_ctl1; + u32 regval; + + if (!AR_SREV_9565(ah)) + return; + + ah->shared_chain_lnadiv = enable; + ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1); + + regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL); + regval &= (~AR_ANT_DIV_CTRL_ALL); + regval |= (ant_div_ctl1 & 0x3f) << AR_ANT_DIV_CTRL_ALL_S; + regval &= ~AR_PHY_ANT_DIV_LNADIV; + regval |= ((ant_div_ctl1 >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S; + + if (enable) + regval |= AR_ANT_DIV_ENABLE; + + REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); + + regval = REG_READ(ah, AR_PHY_CCK_DETECT); + regval &= ~AR_FAST_DIV_ENABLE; + regval |= ((ant_div_ctl1 >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S; + + if (enable) + regval |= AR_FAST_DIV_ENABLE; + + REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); + + if (enable) { + REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL, + (1 << AR_PHY_ANT_SW_RX_PROT_S)); + if (IS_CHAN_2GHZ(ah->curchan)) + REG_SET_BIT(ah, AR_PHY_RESTART, + AR_PHY_RESTART_ENABLE_DIV_M2FLAG); + REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, + AR_BTCOEX_WL_LNADIV_FORCE_ON); + } else { + REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, AR_ANT_DIV_ENABLE); + REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, + (1 << AR_PHY_ANT_SW_RX_PROT_S)); + REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, AR_FAST_DIV_ENABLE); + REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV, + AR_BTCOEX_WL_LNADIV_FORCE_ON); + + regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL); + regval &= ~(AR_PHY_ANT_DIV_MAIN_LNACONF | + AR_PHY_ANT_DIV_ALT_LNACONF | + AR_PHY_ANT_DIV_MAIN_GAINTB | + AR_PHY_ANT_DIV_ALT_GAINTB); + regval |= (AR_PHY_ANT_DIV_LNA1 << AR_PHY_ANT_DIV_MAIN_LNACONF_S); + regval |= (AR_PHY_ANT_DIV_LNA2 << AR_PHY_ANT_DIV_ALT_LNACONF_S); + REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval); + } +} + static int ar9003_hw_fast_chan_change(struct ath_hw *ah, struct ath9k_channel *chan, u8 *ini_reloaded) @@ -1423,6 +1482,7 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah) ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get; ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set; + ops->antctrl_shared_chain_lnadiv = ar9003_hw_antctrl_shared_chain_lnadiv; ar9003_hw_set_nf_limits(ah); ar9003_hw_set_radar_conf(ah); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index fdabc9a28a96..9a48e3d2f231 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -282,6 +282,8 @@ #define AR_PHY_ANT_FAST_DIV_BIAS 0x00007e00 #define AR_PHY_ANT_FAST_DIV_BIAS_S 9 +#define AR_PHY_ANT_SW_RX_PROT 0x00800000 +#define AR_PHY_ANT_SW_RX_PROT_S 23 #define AR_PHY_ANT_DIV_LNADIV 0x01000000 #define AR_PHY_ANT_DIV_LNADIV_S 24 #define AR_PHY_ANT_DIV_ALT_LNACONF 0x06000000 @@ -422,6 +424,8 @@ #define AR_PHY_FIND_SIG_RELSTEP 0x1f #define AR_PHY_FIND_SIG_RELSTEP_S 0 #define AR_PHY_FIND_SIG_RELSTEP_SIGN_BIT 5 +#define AR_PHY_RESTART_ENABLE_DIV_M2FLAG 0x00200000 +#define AR_PHY_RESTART_ENABLE_DIV_M2FLAG_S 21 #define AR_PHY_RESTART_DIV_GC 0x001C0000 #define AR_PHY_RESTART_DIV_GC_S 18 #define AR_PHY_RESTART_ENA 0x01 @@ -1261,4 +1265,24 @@ #define AR_PHY_CL_TAB_CL_GAIN_MOD 0x1f #define AR_PHY_CL_TAB_CL_GAIN_MOD_S 0 +#define AR_BTCOEX_WL_LNADIV 0x1a64 +#define AR_BTCOEX_WL_LNADIV_PREDICTED_PERIOD 0x00003FFF +#define AR_BTCOEX_WL_LNADIV_PREDICTED_PERIOD_S 0 +#define AR_BTCOEX_WL_LNADIV_DPDT_IGNORE_PRIORITY 0x00004000 +#define AR_BTCOEX_WL_LNADIV_DPDT_IGNORE_PRIORITY_S 14 +#define AR_BTCOEX_WL_LNADIV_FORCE_ON 0x00008000 +#define AR_BTCOEX_WL_LNADIV_FORCE_ON_S 15 +#define AR_BTCOEX_WL_LNADIV_MODE_OPTION 0x00030000 +#define AR_BTCOEX_WL_LNADIV_MODE_OPTION_S 16 +#define AR_BTCOEX_WL_LNADIV_MODE 0x007c0000 +#define AR_BTCOEX_WL_LNADIV_MODE_S 18 +#define AR_BTCOEX_WL_LNADIV_ALLOWED_TX_ANTDIV_WL_TX_REQ 0x00800000 +#define AR_BTCOEX_WL_LNADIV_ALLOWED_TX_ANTDIV_WL_TX_REQ_S 23 +#define AR_BTCOEX_WL_LNADIV_DISABLE_TX_ANTDIV_ENABLE 0x01000000 +#define AR_BTCOEX_WL_LNADIV_DISABLE_TX_ANTDIV_ENABLE_S 24 +#define AR_BTCOEX_WL_LNADIV_CONTINUOUS_BT_ACTIVE_PROTECT 0x02000000 +#define AR_BTCOEX_WL_LNADIV_CONTINUOUS_BT_ACTIVE_PROTECT_S 25 +#define AR_BTCOEX_WL_LNADIV_BT_INACTIVE_THRESHOLD 0xFC000000 +#define AR_BTCOEX_WL_LNADIV_BT_INACTIVE_THRESHOLD_S 26 + #endif /* AR9003_PHY_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h index fa9e0932769c..843e79f67ff2 100644 --- a/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h @@ -58,8 +58,6 @@ static const u32 ar9565_1p0_mac_core[][2] = { {0x00008040, 0x00000000}, {0x00008044, 0x00000000}, {0x00008048, 0x00000000}, - {0x0000804c, 0xffffffff}, - {0x00008050, 0xffffffff}, {0x00008054, 0x00000000}, {0x00008058, 0x00000000}, {0x0000805c, 0x000fc78f}, @@ -246,7 +244,7 @@ static const u32 ar9565_1p0_baseband_core[][2] = { {0x00009e50, 0x00ff03f1}, {0x00009e54, 0xe4c355c7}, {0x00009e5c, 0xe9198724}, - {0x00009fc0, 0x823e4788}, + {0x00009fc0, 0x823e4fc8}, {0x00009fc4, 0x0001efb5}, {0x00009fcc, 0x40000014}, {0x0000a20c, 0x00000000}, @@ -291,7 +289,7 @@ static const u32 ar9565_1p0_baseband_core[][2] = { {0x0000a3ec, 0x20202020}, {0x0000a3f0, 0x00000000}, {0x0000a3f4, 0x00000006}, - {0x0000a3f8, 0x0cdbd380}, + {0x0000a3f8, 0x0c9bd380}, {0x0000a3fc, 0x000f0f01}, {0x0000a400, 0x8fa91f01}, {0x0000a404, 0x00000000}, @@ -355,11 +353,11 @@ static const u32 ar9565_1p0_baseband_postamble[][5] = { {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, - {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222}, + {0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946222, 0xcf946222}, {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, - {0x0000a204, 0x033187c0, 0x033187c4, 0x033187c4, 0x033187c0}, + {0x0000a204, 0x07318fc0, 0x07318fc4, 0x07318fc4, 0x07318fc0}, {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, {0x0000a22c, 0x01026a2f, 0x01026a27, 0x01026a2f, 0x01026a2f}, {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b}, @@ -375,9 +373,9 @@ static const u32 ar9565_1p0_baseband_postamble[][5] = { {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, {0x0000a288, 0x00100510, 0x00100510, 0x00100510, 0x00100510}, {0x0000a28c, 0x00021551, 0x00021551, 0x00021551, 0x00021551}, - {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, + {0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18}, {0x0000a2d0, 0x00071982, 0x00071982, 0x00071982, 0x00071982}, - {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a}, + {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae04, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -417,7 +415,7 @@ static const u32 ar9565_1p0_radio_core[][2] = { {0x00016144, 0x02084080}, {0x00016148, 0x000080c0}, {0x00016280, 0x050a0001}, - {0x00016284, 0x3d841400}, + {0x00016284, 0x3d841440}, {0x00016288, 0x00000000}, {0x0001628c, 0xe3000000}, {0x00016290, 0xa1004080}, @@ -840,27 +838,27 @@ static const u32 ar9565_1p0_common_wo_xlna_rx_gain_table[][2] = { {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x301f3000}, - {0x0000a0c4, 0x41004101}, - {0x0000a0c8, 0x411e411f}, - {0x0000a0cc, 0x411c411d}, - {0x0000a0d0, 0x42434244}, - {0x0000a0d4, 0x42414242}, - {0x0000a0d8, 0x425f4240}, - {0x0000a0dc, 0x5342425e}, - {0x0000a0e0, 0x53405341}, - {0x0000a0e4, 0x535e535f}, - {0x0000a0e8, 0x7402535d}, - {0x0000a0ec, 0x74007401}, - {0x0000a0f0, 0x741e741f}, - {0x0000a0f4, 0x7522741d}, - {0x0000a0f8, 0x75207521}, - {0x0000a0fc, 0x753e753f}, - {0x0000a100, 0x76617662}, - {0x0000a104, 0x767f7660}, - {0x0000a108, 0x767d767e}, - {0x0000a10c, 0x77e277e3}, - {0x0000a110, 0x77e077e1}, + {0x0000a0c0, 0x00bf00a0}, + {0x0000a0c4, 0x11a011a1}, + {0x0000a0c8, 0x11be11bf}, + {0x0000a0cc, 0x11bc11bd}, + {0x0000a0d0, 0x22632264}, + {0x0000a0d4, 0x22612262}, + {0x0000a0d8, 0x227f2260}, + {0x0000a0dc, 0x4322227e}, + {0x0000a0e0, 0x43204321}, + {0x0000a0e4, 0x433e433f}, + {0x0000a0e8, 0x4462433d}, + {0x0000a0ec, 0x44604461}, + {0x0000a0f0, 0x447e447f}, + {0x0000a0f4, 0x5582447d}, + {0x0000a0f8, 0x55805581}, + {0x0000a0fc, 0x559e559f}, + {0x0000a100, 0x66816682}, + {0x0000a104, 0x669f6680}, + {0x0000a108, 0x669d669e}, + {0x0000a10c, 0x77627763}, + {0x0000a110, 0x77607761}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, @@ -872,27 +870,27 @@ static const u32 ar9565_1p0_common_wo_xlna_rx_gain_table[][2] = { {0x0000a134, 0x00000000}, {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, - {0x0000a140, 0x301f3000}, - {0x0000a144, 0x41004101}, - {0x0000a148, 0x411e411f}, - {0x0000a14c, 0x411c411d}, - {0x0000a150, 0x42434244}, - {0x0000a154, 0x42414242}, - {0x0000a158, 0x425f4240}, - {0x0000a15c, 0x5342425e}, - {0x0000a160, 0x53405341}, - {0x0000a164, 0x535e535f}, - {0x0000a168, 0x7402535d}, - {0x0000a16c, 0x74007401}, - {0x0000a170, 0x741e741f}, - {0x0000a174, 0x7522741d}, - {0x0000a178, 0x75207521}, - {0x0000a17c, 0x753e753f}, - {0x0000a180, 0x76617662}, - {0x0000a184, 0x767f7660}, - {0x0000a188, 0x767d767e}, - {0x0000a18c, 0x77e277e3}, - {0x0000a190, 0x77e077e1}, + {0x0000a140, 0x00bf00a0}, + {0x0000a144, 0x11a011a1}, + {0x0000a148, 0x11be11bf}, + {0x0000a14c, 0x11bc11bd}, + {0x0000a150, 0x22632264}, + {0x0000a154, 0x22612262}, + {0x0000a158, 0x227f2260}, + {0x0000a15c, 0x4322227e}, + {0x0000a160, 0x43204321}, + {0x0000a164, 0x433e433f}, + {0x0000a168, 0x4462433d}, + {0x0000a16c, 0x44604461}, + {0x0000a170, 0x447e447f}, + {0x0000a174, 0x5582447d}, + {0x0000a178, 0x55805581}, + {0x0000a17c, 0x559e559f}, + {0x0000a180, 0x66816682}, + {0x0000a184, 0x669f6680}, + {0x0000a188, 0x669d669e}, + {0x0000a18c, 0x77627763}, + {0x0000a190, 0x77607761}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 96b8331ef9e7..8e27f4fb21fe 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -537,6 +537,7 @@ struct ath9k_wow_pattern { #ifdef CONFIG_MAC80211_LEDS void ath_init_leds(struct ath_softc *sc); void ath_deinit_leds(struct ath_softc *sc); +void ath_fill_led_pin(struct ath_softc *sc); #else static inline void ath_init_leds(struct ath_softc *sc) { @@ -545,6 +546,9 @@ static inline void ath_init_leds(struct ath_softc *sc) static inline void ath_deinit_leds(struct ath_softc *sc) { } +static inline void ath_fill_led_pin(struct ath_softc *sc) +{ +} #endif /*******************************/ @@ -596,8 +600,6 @@ struct ath_ant_comb { int main_conf; enum ath9k_ant_div_comb_lna_conf first_quick_scan_conf; enum ath9k_ant_div_comb_lna_conf second_quick_scan_conf; - int first_bias; - int second_bias; bool first_ratio; bool second_ratio; unsigned long scan_start_time; diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 45f24220b16e..bf7d29ec1a87 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -44,25 +44,6 @@ void ath_init_leds(struct ath_softc *sc) if (AR_SREV_9100(sc->sc_ah)) return; - if (sc->sc_ah->led_pin < 0) { - if (AR_SREV_9287(sc->sc_ah)) - sc->sc_ah->led_pin = ATH_LED_PIN_9287; - else if (AR_SREV_9485(sc->sc_ah)) - sc->sc_ah->led_pin = ATH_LED_PIN_9485; - else if (AR_SREV_9300(sc->sc_ah)) - sc->sc_ah->led_pin = ATH_LED_PIN_9300; - else if (AR_SREV_9462(sc->sc_ah) || AR_SREV_9565(sc->sc_ah)) - sc->sc_ah->led_pin = ATH_LED_PIN_9462; - else - sc->sc_ah->led_pin = ATH_LED_PIN_DEF; - } - - /* Configure gpio 1 for output */ - 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, sc->sc_ah->led_pin, 1); - if (!led_blink) sc->led_cdev.default_trigger = ieee80211_get_radio_led_name(sc->hw); @@ -78,6 +59,31 @@ void ath_init_leds(struct ath_softc *sc) sc->led_registered = true; } + +void ath_fill_led_pin(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + + if (AR_SREV_9100(ah) || (ah->led_pin >= 0)) + return; + + if (AR_SREV_9287(ah)) + ah->led_pin = ATH_LED_PIN_9287; + else if (AR_SREV_9485(sc->sc_ah)) + ah->led_pin = ATH_LED_PIN_9485; + else if (AR_SREV_9300(sc->sc_ah)) + ah->led_pin = ATH_LED_PIN_9300; + else if (AR_SREV_9462(sc->sc_ah) || AR_SREV_9565(sc->sc_ah)) + ah->led_pin = ATH_LED_PIN_9462; + else + ah->led_pin = ATH_LED_PIN_DEF; + + /* Configure gpio 1 for output */ + ath9k_hw_cfg_output(ah, ah->led_pin, AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + + /* LED off, active low */ + ath9k_hw_set_gpio(ah, ah->led_pin, 1); +} #endif /*******************/ @@ -314,8 +320,10 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc) ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n"); /* make sure duty cycle timer is also stopped when resuming */ - if (btcoex->hw_timer_enabled) + if (btcoex->hw_timer_enabled) { ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer); + btcoex->hw_timer_enabled = false; + } btcoex->bt_priority_cnt = 0; btcoex->bt_priority_time = jiffies; @@ -336,18 +344,20 @@ void ath9k_btcoex_timer_pause(struct ath_softc *sc) del_timer_sync(&btcoex->period_timer); - if (btcoex->hw_timer_enabled) + if (btcoex->hw_timer_enabled) { ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); - - btcoex->hw_timer_enabled = false; + btcoex->hw_timer_enabled = false; + } } void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; - if (btcoex->hw_timer_enabled) + if (btcoex->hw_timer_enabled) { ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer); + btcoex->hw_timer_enabled = false; + } } u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen) diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index ee6e50aebf8d..924c4616c3d9 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1072,14 +1072,15 @@ static void ath9k_hif_usb_dev_deinit(struct hif_device_usb *hif_dev) */ static void ath9k_hif_usb_firmware_fail(struct hif_device_usb *hif_dev) { - struct device *parent = hif_dev->udev->dev.parent; + struct device *dev = &hif_dev->udev->dev; + struct device *parent = dev->parent; complete(&hif_dev->fw_done); if (parent) device_lock(parent); - device_release_driver(&hif_dev->udev->dev); + device_release_driver(dev); if (parent) device_unlock(parent); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 61d096e3596f..ca78e33ca23e 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1445,7 +1445,7 @@ static int ath9k_htc_set_key(struct ieee80211_hw *hw, key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; if (priv->ah->sw_mgmt_crypto && key->cipher == WLAN_CIPHER_SUITE_CCMP) - key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; ret = 0; } break; diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 265bf77598a2..0f2b97f6b739 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -78,6 +78,13 @@ static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah, ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf); } +static inline void ath9k_hw_antctrl_shared_chain_lnadiv(struct ath_hw *ah, + bool enable) +{ + if (ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv) + ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv(ah, enable); +} + /* Private hardware call ops */ /* PHY ops */ diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 40f57aa2564e..f9a6ec5cf470 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -24,6 +24,7 @@ #include "rc.h" #include "ar9003_mac.h" #include "ar9003_mci.h" +#include "ar9003_phy.h" #include "debug.h" #include "ath9k.h" @@ -1733,12 +1734,12 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) if (!ret) goto fail; - ath9k_hw_loadnf(ah, ah->curchan); - ath9k_hw_start_nfcal(ah, true); - if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_2g5g_switch(ah, false); + ath9k_hw_loadnf(ah, ah->curchan); + ath9k_hw_start_nfcal(ah, true); + if (AR_SREV_9271(ah)) ar9002_hw_load_ani_reg(ah, chan); @@ -2025,6 +2026,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_apply_gpio_override(ah); + if (AR_SREV_9565(ah) && ah->shared_chain_lnadiv) + REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON); + return 0; } EXPORT_SYMBOL(ath9k_hw_reset); @@ -2535,7 +2539,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) } - if (AR_SREV_9330(ah) || AR_SREV_9485(ah)) { + if (AR_SREV_9330(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah)) { ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1); /* * enable the diversity-combining algorithm only when diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index f0798cc50dc1..566a4ce4f156 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -686,7 +686,7 @@ struct ath_hw_ops { struct ath_hw_antcomb_conf *antconf); void (*antdiv_comb_conf_set)(struct ath_hw *ah, struct ath_hw_antcomb_conf *antconf); - + void (*antctrl_shared_chain_lnadiv)(struct ath_hw *hw, bool enable); }; struct ath_nf_limits { @@ -730,6 +730,7 @@ struct ath_hw { bool aspm_enabled; bool is_monitoring; bool need_an_top2_fixup; + bool shared_chain_lnadiv; u16 tx_trig_level; u32 nf_regs[6]; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index f3ce5ca2f1d3..fad3ccd5cd91 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -46,6 +46,10 @@ static int ath9k_btcoex_enable; module_param_named(btcoex_enable, ath9k_btcoex_enable, int, 0444); MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence"); +static int ath9k_enable_diversity; +module_param_named(enable_diversity, ath9k_enable_diversity, int, 0444); +MODULE_PARM_DESC(enable_diversity, "Enable Antenna diversity for AR9565"); + bool is_ath9k_unloaded; /* We use the hw_value as an index into our private channel structure */ @@ -546,6 +550,14 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, common->debug_mask = ath9k_debug; common->btcoex_enabled = ath9k_btcoex_enable == 1; common->disable_ani = false; + + /* + * Enable Antenna diversity only when BTCOEX is disabled + * and the user manually requests the feature. + */ + if (!common->btcoex_enabled && ath9k_enable_diversity) + common->antenna_diversity = 1; + spin_lock_init(&common->cc_lock); spin_lock_init(&sc->sc_serial_rw); @@ -597,6 +609,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ath9k_cmn_init_crypto(sc->sc_ah); ath9k_init_misc(sc); + ath_fill_led_pin(sc); if (common->bus_ops->aspm_init) common->bus_ops->aspm_init(common); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 3923ad933aef..31ab82e3ba85 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1406,7 +1406,7 @@ static int ath9k_set_key(struct ieee80211_hw *hw, key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; if (sc->sc_ah->sw_mgmt_crypto && key->cipher == WLAN_CIPHER_SUITE_CCMP) - key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; ret = 0; } break; diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 8f0e8d9c2054..8f51e9e358fd 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -80,6 +80,7 @@ void ath_mci_flush_profile(struct ath_mci_profile *mci) struct ath_mci_profile_info *info, *tinfo; mci->aggr_limit = 0; + mci->num_mgmt = 0; if (list_empty(&mci->info)) return; @@ -120,7 +121,14 @@ static void ath_mci_update_scheme(struct ath_softc *sc) if (mci_hw->config & ATH_MCI_CONFIG_DISABLE_TUNING) goto skip_tuning; + mci->aggr_limit = 0; btcoex->duty_cycle = ath_mci_duty_cycle[num_profile]; + btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD; + if (NUM_PROF(mci)) + btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; + else + btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL : + ATH_BTCOEX_STOMP_LOW; if (num_profile == 1) { info = list_first_entry(&mci->info, @@ -132,7 +140,8 @@ static void ath_mci_update_scheme(struct ath_softc *sc) else if (info->T == 6) { mci->aggr_limit = 6; btcoex->duty_cycle = 30; - } + } else + mci->aggr_limit = 6; ath_dbg(common, MCI, "Single SCO, aggregation limit %d 1/4 ms\n", mci->aggr_limit); @@ -241,8 +250,8 @@ static void ath9k_mci_work(struct work_struct *work) ath_mci_update_scheme(sc); } -static void ath_mci_process_profile(struct ath_softc *sc, - struct ath_mci_profile_info *info) +static u8 ath_mci_process_profile(struct ath_softc *sc, + struct ath_mci_profile_info *info) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_btcoex *btcoex = &sc->btcoex; @@ -268,25 +277,15 @@ static void ath_mci_process_profile(struct ath_softc *sc, if (info->start) { if (!entry && !ath_mci_add_profile(common, mci, info)) - return; + return 0; } else ath_mci_del_profile(common, mci, entry); - btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD; - mci->aggr_limit = mci->num_sco ? 6 : 0; - - btcoex->duty_cycle = ath_mci_duty_cycle[NUM_PROF(mci)]; - if (NUM_PROF(mci)) - btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; - else - btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL : - ATH_BTCOEX_STOMP_LOW; - - ieee80211_queue_work(sc->hw, &sc->mci_work); + return 1; } -static void ath_mci_process_status(struct ath_softc *sc, - struct ath_mci_profile_status *status) +static u8 ath_mci_process_status(struct ath_softc *sc, + struct ath_mci_profile_status *status) { struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &btcoex->mci; @@ -295,14 +294,14 @@ static void ath_mci_process_status(struct ath_softc *sc, /* Link status type are not handled */ if (status->is_link) - return; + return 0; info.conn_handle = status->conn_handle; if (ath_mci_find_profile(mci, &info)) - return; + return 0; if (status->conn_handle >= ATH_MCI_MAX_PROFILE) - return; + return 0; if (status->is_critical) __set_bit(status->conn_handle, mci->status); @@ -316,7 +315,9 @@ static void ath_mci_process_status(struct ath_softc *sc, } while (++i < ATH_MCI_MAX_PROFILE); if (old_num_mgmt != mci->num_mgmt) - ieee80211_queue_work(sc->hw, &sc->mci_work); + return 1; + + return 0; } static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) @@ -325,9 +326,16 @@ static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) struct ath_mci_profile_info profile_info; struct ath_mci_profile_status profile_status; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - u8 major, minor; + u8 major, minor, update_scheme = 0; u32 seq_num; + if (ar9003_mci_state(ah, MCI_STATE_NEED_FLUSH_BT_INFO) && + ar9003_mci_state(ah, MCI_STATE_ENABLE)) { + ath_dbg(common, MCI, "(MCI) Need to flush BT profiles\n"); + ath_mci_flush_profile(&sc->btcoex.mci); + ar9003_mci_state(ah, MCI_STATE_SEND_STATUS_QUERY); + } + switch (opcode) { case MCI_GPM_COEX_VERSION_QUERY: ar9003_mci_state(ah, MCI_STATE_SEND_WLAN_COEX_VERSION); @@ -353,7 +361,7 @@ static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) break; } - ath_mci_process_profile(sc, &profile_info); + update_scheme += ath_mci_process_profile(sc, &profile_info); break; case MCI_GPM_COEX_BT_STATUS_UPDATE: profile_status.is_link = *(rx_payload + @@ -369,12 +377,14 @@ static void ath_mci_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) profile_status.is_link, profile_status.conn_handle, profile_status.is_critical, seq_num); - ath_mci_process_status(sc, &profile_status); + update_scheme += ath_mci_process_status(sc, &profile_status); break; default: ath_dbg(common, MCI, "Unknown GPM COEX message = 0x%02x\n", opcode); break; } + if (update_scheme) + ieee80211_queue_work(sc->hw, &sc->mci_work); } int ath_mci_setup(struct ath_softc *sc) @@ -568,9 +578,11 @@ void ath_mci_intr(struct ath_softc *sc) } if ((mci_int & AR_MCI_INTERRUPT_RX_INVALID_HDR) || - (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) + (mci_int & AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT)) { mci_int &= ~(AR_MCI_INTERRUPT_RX_INVALID_HDR | AR_MCI_INTERRUPT_CONT_INFO_TIMEOUT); + ath_mci_msg(sc, MCI_GPM_COEX_NOOP, NULL); + } } void ath_mci_enable(struct ath_softc *sc) diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index a8f6126f6b2d..c0c599673eeb 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -128,8 +128,9 @@ static void ath_pci_aspm_init(struct ath_common *common) if (!parent) return; - if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) { - /* Bluetooth coexistance requires disabling ASPM. */ + if ((ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) && + (AR_SREV_9285(ah))) { + /* Bluetooth coexistance requires disabling ASPM for AR9285. */ pci_read_config_byte(pdev, pos + PCI_EXP_LNKCTL, &aspm); aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); pci_write_config_byte(pdev, pos + PCI_EXP_LNKCTL, aspm); diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 4b12c347d188..27ed80b54881 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1222,11 +1222,14 @@ static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta) caps |= WLAN_RC_TS_FLAG | WLAN_RC_DS_FLAG; else if (sta->ht_cap.mcs.rx_mask[1]) caps |= WLAN_RC_DS_FLAG; - if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { caps |= WLAN_RC_40_FLAG; - if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40 || - sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) - caps |= WLAN_RC_SGI_FLAG; + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) + caps |= WLAN_RC_SGI_FLAG; + } else { + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) + caps |= WLAN_RC_SGI_FLAG; + } } return caps; diff --git a/drivers/net/wireless/ath/carl9170/mac.c b/drivers/net/wireless/ath/carl9170/mac.c index f8676280dc36..e3b1b6e87760 100644 --- a/drivers/net/wireless/ath/carl9170/mac.c +++ b/drivers/net/wireless/ath/carl9170/mac.c @@ -304,7 +304,8 @@ int carl9170_set_operating_mode(struct ar9170 *ar) struct ath_common *common = &ar->common; u8 *mac_addr, *bssid; u32 cam_mode = AR9170_MAC_CAM_DEFAULTS; - u32 enc_mode = AR9170_MAC_ENCRYPTION_DEFAULTS; + u32 enc_mode = AR9170_MAC_ENCRYPTION_DEFAULTS | + AR9170_MAC_ENCRYPTION_MGMT_RX_SOFTWARE; u32 rx_ctrl = AR9170_MAC_RX_CTRL_DEAGG | AR9170_MAC_RX_CTRL_SHORT_FILTER; u32 sniffer = AR9170_MAC_SNIFFER_DEFAULTS; diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 18554ab76733..67997b39aba7 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1149,6 +1149,7 @@ static int carl9170_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, break; case WLAN_CIPHER_SUITE_CCMP: ktype = AR9170_ENC_ALG_AESCCMP; + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; break; default: return -EOPNOTSUPP; @@ -1780,6 +1781,7 @@ void *carl9170_alloc(size_t priv_size) hw->wiphy->interface_modes = 0; hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_MFP_CAPABLE | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 8e7e6928c936..3b2c4c20e7fc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -185,7 +185,7 @@ brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) return err; } -static int +int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, void *data, bool write) { @@ -249,7 +249,9 @@ u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) int retval; brcmf_dbg(INFO, "addr:0x%08x\n", addr); + sdio_claim_host(sdiodev->func[1]); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); + sdio_release_host(sdiodev->func[1]); brcmf_dbg(INFO, "data:0x%02x\n", data); if (ret) @@ -264,7 +266,9 @@ u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) int retval; brcmf_dbg(INFO, "addr:0x%08x\n", addr); + sdio_claim_host(sdiodev->func[1]); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); + sdio_release_host(sdiodev->func[1]); brcmf_dbg(INFO, "data:0x%08x\n", data); if (ret) @@ -279,7 +283,9 @@ void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, int retval; brcmf_dbg(INFO, "addr:0x%08x, data:0x%02x\n", addr, data); + sdio_claim_host(sdiodev->func[1]); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); + sdio_release_host(sdiodev->func[1]); if (ret) *ret = retval; @@ -291,7 +297,9 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, int retval; brcmf_dbg(INFO, "addr:0x%08x, data:0x%08x\n", addr, data); + sdio_claim_host(sdiodev->func[1]); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); + sdio_release_host(sdiodev->func[1]); if (ret) *ret = retval; @@ -356,15 +364,20 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pkt->len); + sdio_claim_host(sdiodev->func[1]); + width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr); if (err) - return err; + goto done; incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_READ, fn, addr, pkt); +done: + sdio_release_host(sdiodev->func[1]); + return err; } @@ -378,15 +391,20 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pktq->qlen); + sdio_claim_host(sdiodev->func[1]); + width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr); if (err) - return err; + goto done; incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; err = brcmf_sdioh_request_chain(sdiodev, incr_fix, SDIOH_READ, fn, addr, pktq); +done: + sdio_release_host(sdiodev->func[1]); + return err; } @@ -428,10 +446,12 @@ brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, if (flags & SDIO_REQ_ASYNC) return -ENOTSUPP; + sdio_claim_host(sdiodev->func[1]); + if (bar0 != sdiodev->sbwad) { err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); if (err) - return err; + goto done; sdiodev->sbwad = bar0; } @@ -443,8 +463,13 @@ brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, if (width == 4) addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; - return brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn, - addr, pkt); + err = brcmf_sdioh_request_buffer(sdiodev, incr_fix, SDIOH_WRITE, fn, + addr, pkt); + +done: + sdio_release_host(sdiodev->func[1]); + + return err; } int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr, @@ -485,8 +510,10 @@ int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn) brcmf_dbg(TRACE, "Enter\n"); /* issue abort cmd52 command through F0 */ + sdio_claim_host(sdiodev->func[1]); brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0, SDIO_CCCR_ABORT, &t_func); + sdio_release_host(sdiodev->func[1]); brcmf_dbg(TRACE, "Exit\n"); return 0; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c index 372f74eac1e7..c3247d5b3c22 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -103,7 +103,6 @@ static inline int brcmf_sdioh_f0_write_byte(struct brcmf_sdio_dev *sdiodev, if (regaddr == SDIO_CCCR_IOEx) { sdfunc = sdiodev->func[2]; if (sdfunc) { - sdio_claim_host(sdfunc); if (*byte & SDIO_FUNC_ENABLE_2) { /* Enable Function 2 */ err_ret = sdio_enable_func(sdfunc); @@ -119,7 +118,6 @@ static inline int brcmf_sdioh_f0_write_byte(struct brcmf_sdio_dev *sdiodev, "Disable F2 failed:%d\n", err_ret); } - sdio_release_host(sdfunc); } } else if ((regaddr == SDIO_CCCR_ABORT) || (regaddr == SDIO_CCCR_IENx)) { @@ -128,17 +126,13 @@ static inline int brcmf_sdioh_f0_write_byte(struct brcmf_sdio_dev *sdiodev, if (!sdfunc) return -ENOMEM; sdfunc->num = 0; - sdio_claim_host(sdfunc); sdio_writeb(sdfunc, *byte, regaddr, &err_ret); - sdio_release_host(sdfunc); kfree(sdfunc); } else if (regaddr < 0xF0) { brcmf_dbg(ERROR, "F0 Wr:0x%02x: write disallowed\n", regaddr); err_ret = -EPERM; } else { - sdio_claim_host(sdfunc); sdio_f0_writeb(sdfunc, *byte, regaddr, &err_ret); - sdio_release_host(sdfunc); } return err_ret; @@ -159,7 +153,6 @@ int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint func, /* handle F0 separately */ err_ret = brcmf_sdioh_f0_write_byte(sdiodev, regaddr, byte); } else { - sdio_claim_host(sdiodev->func[func]); if (rw) /* CMD52 Write */ sdio_writeb(sdiodev->func[func], *byte, regaddr, &err_ret); @@ -170,7 +163,6 @@ int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint func, *byte = sdio_readb(sdiodev->func[func], regaddr, &err_ret); } - sdio_release_host(sdiodev->func[func]); } if (err_ret) @@ -197,8 +189,6 @@ int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, brcmf_pm_resume_wait(sdiodev, &sdiodev->request_word_wait); if (brcmf_pm_resume_error(sdiodev)) return -EIO; - /* Claim host controller */ - sdio_claim_host(sdiodev->func[func]); if (rw) { /* CMD52 Write */ if (nbytes == 4) @@ -219,9 +209,6 @@ int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, brcmf_dbg(ERROR, "Invalid nbytes: %d\n", nbytes); } - /* Release host controller */ - sdio_release_host(sdiodev->func[func]); - if (err_ret) brcmf_dbg(ERROR, "Failed to %s word, Err: 0x%08x\n", rw ? "write" : "read", err_ret); @@ -275,9 +262,6 @@ brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc, if (brcmf_pm_resume_error(sdiodev)) return -EIO; - /* Claim host controller */ - sdio_claim_host(sdiodev->func[func]); - skb_queue_walk(pktq, pkt) { uint pkt_len = pkt->len; pkt_len += 3; @@ -300,9 +284,6 @@ brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc, SGCount++; } - /* Release host controller */ - sdio_release_host(sdiodev->func[func]); - brcmf_dbg(TRACE, "Exit\n"); return err_ret; } @@ -328,9 +309,6 @@ int brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev, if (brcmf_pm_resume_error(sdiodev)) return -EIO; - /* Claim host controller */ - sdio_claim_host(sdiodev->func[func]); - pkt_len += 3; pkt_len &= (uint)~3; @@ -344,9 +322,6 @@ int brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev, write ? "TX" : "RX", pkt, addr, pkt_len); } - /* Release host controller */ - sdio_release_host(sdiodev->func[func]); - return status; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 4766d9f35696..55e489d2147d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -682,10 +682,6 @@ extern int brcmf_c_host_event(struct brcmf_pub *drvr, int *idx, extern void brcmf_del_if(struct brcmf_pub *drvr, int ifidx); -/* Send packet to dongle via data channel */ -extern int brcmf_sendpkt(struct brcmf_pub *drvr, int ifidx,\ - struct sk_buff *pkt); - extern void brcmf_c_pktfilter_offload_set(struct brcmf_pub *drvr, char *arg); extern void brcmf_c_pktfilter_offload_enable(struct brcmf_pub *drvr, char *arg, int enable, int master_mode); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 8121dbabbff6..aa4f719a51a9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -80,8 +80,10 @@ brcmf_c_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen) strncpy(buf, name, buflen); /* append data onto the end of the name string */ - memcpy(&buf[len], data, datalen); - len += datalen; + if (data && datalen) { + memcpy(&buf[len], data, datalen); + len += datalen; + } return len; } @@ -431,13 +433,7 @@ brcmf_c_show_host_event(struct brcmf_event_msg *event, void *event_data) } /* show any appended data */ - if (datalen) { - buf = (unsigned char *) event_data; - brcmf_dbg(EVENT, " data (%d) : ", datalen); - for (i = 0; i < datalen; i++) - brcmf_dbg(EVENT, " 0x%02x ", *buf++); - brcmf_dbg(EVENT, "\n"); - } + brcmf_dbg_hex_dump(datalen, event_data, datalen, "Received data"); } #endif /* DEBUG */ @@ -528,8 +524,9 @@ brcmf_c_host_event(struct brcmf_pub *drvr, int *ifidx, void *pktdata, } #ifdef DEBUG - brcmf_c_show_host_event(event, event_data); -#endif /* DEBUG */ + if (BRCMF_EVENT_ON()) + brcmf_c_show_host_event(event, event_data); +#endif /* DEBUG */ return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index b784920532d3..fb508c2256dd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -55,6 +55,7 @@ do { \ #define BRCMF_HDRS_ON() (brcmf_msg_level & BRCMF_HDRS_VAL) #define BRCMF_BYTES_ON() (brcmf_msg_level & BRCMF_BYTES_VAL) #define BRCMF_GLOM_ON() (brcmf_msg_level & BRCMF_GLOM_VAL) +#define BRCMF_EVENT_ON() (brcmf_msg_level & BRCMF_EVENT_VAL) #else /* (defined DEBUG) || (defined DEBUG) */ @@ -65,6 +66,7 @@ do { \ #define BRCMF_HDRS_ON() 0 #define BRCMF_BYTES_ON() 0 #define BRCMF_GLOM_ON() 0 +#define BRCMF_EVENT_ON() 0 #endif /* defined(DEBUG) */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index b08f3474d8e7..d7c76ce9d8cb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -272,30 +272,6 @@ static void brcmf_netdev_set_multicast_list(struct net_device *ndev) schedule_work(&drvr->multicast_work); } -int brcmf_sendpkt(struct brcmf_pub *drvr, int ifidx, struct sk_buff *pktbuf) -{ - /* Reject if down */ - if (!drvr->bus_if->drvr_up || (drvr->bus_if->state == BRCMF_BUS_DOWN)) - return -ENODEV; - - /* Update multicast statistic */ - if (pktbuf->len >= ETH_ALEN) { - u8 *pktdata = (u8 *) (pktbuf->data); - struct ethhdr *eh = (struct ethhdr *)pktdata; - - if (is_multicast_ether_addr(eh->h_dest)) - drvr->tx_multicast++; - if (ntohs(eh->h_proto) == ETH_P_PAE) - atomic_inc(&drvr->pend_8021x_cnt); - } - - /* If the protocol uses a data header, apply it */ - brcmf_proto_hdrpush(drvr, ifidx, pktbuf); - - /* Use bus module to send data frame */ - return drvr->bus_if->brcmf_bus_txdata(drvr->dev, pktbuf); -} - static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) { int ret; @@ -338,7 +314,22 @@ static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) } } - ret = brcmf_sendpkt(drvr, ifp->idx, skb); + /* Update multicast statistic */ + if (skb->len >= ETH_ALEN) { + u8 *pktdata = (u8 *)(skb->data); + struct ethhdr *eh = (struct ethhdr *)pktdata; + + if (is_multicast_ether_addr(eh->h_dest)) + drvr->tx_multicast++; + if (ntohs(eh->h_proto) == ETH_P_PAE) + atomic_inc(&drvr->pend_8021x_cnt); + } + + /* If the protocol uses a data header, apply it */ + brcmf_proto_hdrpush(drvr, ifp->idx, skb); + + /* Use bus module to send data frame */ + ret = drvr->bus_if->brcmf_bus_txdata(drvr->dev, skb); done: if (ret) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 4580ff34c2d0..3564686add9a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -482,6 +482,15 @@ struct sdpcm_shared_le { __le32 brpt_addr; }; +/* SDIO read frame info */ +struct brcmf_sdio_read { + u8 seq_num; + u8 channel; + u16 len; + u16 len_left; + u16 len_nxtfrm; + u8 dat_offset; +}; /* misc chip info needed by some of the routines */ /* Private data for SDIO bus interaction */ @@ -494,9 +503,8 @@ struct brcmf_sdio { u32 ramsize; /* Size of RAM in SOCRAM (bytes) */ u32 hostintmask; /* Copy of Host Interrupt Mask */ - u32 intstatus; /* Intstatus bits (events) pending */ - bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */ - bool fcstate; /* State of dongle flow-control */ + atomic_t intstatus; /* Intstatus bits (events) pending */ + atomic_t fcstate; /* State of dongle flow-control */ uint blocksize; /* Block size of SDIO transfers */ uint roundup; /* Max roundup limit */ @@ -508,9 +516,11 @@ struct brcmf_sdio { u8 hdrbuf[MAX_HDR_READ + BRCMF_SDALIGN]; u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */ - u16 nextlen; /* Next Read Len from last header */ u8 rx_seq; /* Receive sequence number (expected) */ + struct brcmf_sdio_read cur_read; + /* info of current read frame */ bool rxskip; /* Skip receive (awaiting NAK ACK) */ + bool rxpending; /* Data frame pending in dongle */ uint rxbound; /* Rx frames to read before resched */ uint txbound; /* Tx frames to send before resched */ @@ -531,7 +541,7 @@ struct brcmf_sdio { bool intr; /* Use interrupts */ bool poll; /* Use polling */ - bool ipend; /* Device interrupt is pending */ + atomic_t ipend; /* Device interrupt is pending */ uint spurious; /* Count of spurious interrupts */ uint pollrate; /* Ticks between device polls */ uint polltick; /* Tick counter */ @@ -549,12 +559,9 @@ struct brcmf_sdio { s32 idleclock; /* How to set bus driver when idle */ s32 sd_rxchain; bool use_rxchain; /* If brcmf should use PKT chains */ - bool sleeping; /* Is SDIO bus sleeping? */ bool rxflow_mode; /* Rx flow control mode */ bool rxflow; /* Is rx flow control on */ bool alp_only; /* Don't use HT clock (ALP only) */ -/* Field to decide if rx of control frames happen in rxbuf or lb-pool */ - bool usebufpool; u8 *ctrl_frame_buf; u32 ctrl_frame_len; @@ -570,8 +577,8 @@ struct brcmf_sdio { bool wd_timer_valid; uint save_ms; - struct task_struct *dpc_tsk; - struct completion dpc_wait; + struct workqueue_struct *brcmf_wq; + struct work_struct datawork; struct list_head dpc_tsklst; spinlock_t dpc_tl_lock; @@ -657,15 +664,6 @@ w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) #define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) -/* Packet free applicable unconditionally for sdio and sdspi. - * Conditional if bufpool was present for gspi bus. - */ -static void brcmf_sdbrcm_pktfree2(struct brcmf_sdio *bus, struct sk_buff *pkt) -{ - if (bus->usebufpool) - brcmu_pkt_buf_free_skb(pkt); -} - /* Turn backplane clock on or off */ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) { @@ -853,81 +851,6 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) return 0; } -static int brcmf_sdbrcm_bussleep(struct brcmf_sdio *bus, bool sleep) -{ - int ret; - - brcmf_dbg(INFO, "request %s (currently %s)\n", - sleep ? "SLEEP" : "WAKE", - bus->sleeping ? "SLEEP" : "WAKE"); - - /* Done if we're already in the requested state */ - if (sleep == bus->sleeping) - return 0; - - /* Going to sleep: set the alarm and turn off the lights... */ - if (sleep) { - /* Don't sleep if something is pending */ - if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq)) - return -EBUSY; - - /* Make sure the controller has the bus up */ - brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); - - /* Tell device to start using OOB wakeup */ - ret = w_sdreg32(bus, SMB_USE_OOB, - offsetof(struct sdpcmd_regs, tosbmailbox)); - if (ret != 0) - brcmf_dbg(ERROR, "CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"); - - /* Turn off our contribution to the HT clock request */ - brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); - - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - SBSDIO_FORCE_HW_CLKREQ_OFF, NULL); - - /* Isolate the bus */ - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - SBSDIO_DEVCTL_PADS_ISO, NULL); - - /* Change state */ - bus->sleeping = true; - - } else { - /* Waking up: bus power up is ok, set local state */ - - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - 0, NULL); - - /* Make sure the controller has the bus up */ - brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); - - /* Send misc interrupt to indicate OOB not needed */ - ret = w_sdreg32(bus, 0, - offsetof(struct sdpcmd_regs, tosbmailboxdata)); - if (ret == 0) - ret = w_sdreg32(bus, SMB_DEV_INT, - offsetof(struct sdpcmd_regs, tosbmailbox)); - - if (ret != 0) - brcmf_dbg(ERROR, "CANNOT SIGNAL CHIP TO CLEAR OOB!!\n"); - - /* Make sure we have SD bus access */ - brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); - - /* Change state */ - bus->sleeping = false; - } - - return 0; -} - -static void bus_wake(struct brcmf_sdio *bus) -{ - if (bus->sleeping) - brcmf_sdbrcm_bussleep(bus, false); -} - static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) { u32 intstatus = 0; @@ -1056,7 +979,7 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) } /* Clear partial in any case */ - bus->nextlen = 0; + bus->cur_read.len = 0; /* If we can't reach the device, signal failure */ if (err) @@ -1108,6 +1031,96 @@ static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus) } } +static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, + struct brcmf_sdio_read *rd) +{ + u16 len, checksum; + u8 rx_seq, fc, tx_seq_max; + + /* + * 4 bytes hardware header (frame tag) + * Byte 0~1: Frame length + * Byte 2~3: Checksum, bit-wise inverse of frame length + */ + len = get_unaligned_le16(header); + checksum = get_unaligned_le16(header + sizeof(u16)); + /* All zero means no more to read */ + if (!(len | checksum)) { + bus->rxpending = false; + return false; + } + if ((u16)(~(len ^ checksum))) { + brcmf_dbg(ERROR, "HW header checksum error\n"); + bus->sdcnt.rx_badhdr++; + brcmf_sdbrcm_rxfail(bus, false, false); + return false; + } + if (len < SDPCM_HDRLEN) { + brcmf_dbg(ERROR, "HW header length error\n"); + return false; + } + rd->len = len; + + /* + * 8 bytes hardware header + * Byte 0: Rx sequence number + * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag + * Byte 2: Length of next data frame + * Byte 3: Data offset + * Byte 4: Flow control bits + * Byte 5: Maximum Sequence number allow for Tx + * Byte 6~7: Reserved + */ + rx_seq = SDPCM_PACKET_SEQUENCE(&header[SDPCM_FRAMETAG_LEN]); + rd->channel = SDPCM_PACKET_CHANNEL(&header[SDPCM_FRAMETAG_LEN]); + if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL) { + brcmf_dbg(ERROR, "HW header length too long\n"); + bus->sdiodev->bus_if->dstats.rx_errors++; + bus->sdcnt.rx_toolong++; + brcmf_sdbrcm_rxfail(bus, false, false); + rd->len = 0; + return false; + } + rd->dat_offset = SDPCM_DOFFSET_VALUE(&header[SDPCM_FRAMETAG_LEN]); + if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) { + brcmf_dbg(ERROR, "seq %d: bad data offset\n", rx_seq); + bus->sdcnt.rx_badhdr++; + brcmf_sdbrcm_rxfail(bus, false, false); + rd->len = 0; + return false; + } + if (rd->seq_num != rx_seq) { + brcmf_dbg(ERROR, "seq %d: sequence number error, expect %d\n", + rx_seq, rd->seq_num); + bus->sdcnt.rx_badseq++; + rd->seq_num = rx_seq; + } + rd->len_nxtfrm = header[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; + if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) { + /* only warm for NON glom packet */ + if (rd->channel != SDPCM_GLOM_CHANNEL) + brcmf_dbg(ERROR, "seq %d: next length error\n", rx_seq); + rd->len_nxtfrm = 0; + } + fc = SDPCM_FCMASK_VALUE(&header[SDPCM_FRAMETAG_LEN]); + if (bus->flowcontrol != fc) { + if (~bus->flowcontrol & fc) + bus->sdcnt.fc_xoff++; + if (bus->flowcontrol & ~fc) + bus->sdcnt.fc_xon++; + bus->sdcnt.fc_rcvd++; + bus->flowcontrol = fc; + } + tx_seq_max = SDPCM_WINDOW_VALUE(&header[SDPCM_FRAMETAG_LEN]); + if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) { + brcmf_dbg(ERROR, "seq %d: max tx seq number error\n", rx_seq); + tx_seq_max = bus->tx_seq + 2; + } + bus->tx_max = tx_seq_max; + + return true; +} + static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) { u16 dlen, totlen; @@ -1122,6 +1135,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) int ifidx = 0; bool usechain = bus->use_rxchain; + u16 next_len; /* If packets, issue read(s) and send up packet chain */ /* Return sequence numbers consumed? */ @@ -1185,10 +1199,10 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) if (pnext) { brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n", totlen, num); - if (BRCMF_GLOM_ON() && bus->nextlen && - totlen != bus->nextlen) { + if (BRCMF_GLOM_ON() && bus->cur_read.len && + totlen != bus->cur_read.len) { brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n", - bus->nextlen, totlen, rxseq); + bus->cur_read.len, totlen, rxseq); } pfirst = pnext = NULL; } else { @@ -1199,7 +1213,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) /* Done with descriptor packet */ brcmu_pkt_buf_free_skb(bus->glomd); bus->glomd = NULL; - bus->nextlen = 0; + bus->cur_read.len = 0; } /* Ok -- either we just generated a packet chain, @@ -1272,12 +1286,13 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]); - bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; - if ((bus->nextlen << 4) > MAX_RX_DATASZ) { + next_len = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; + if ((next_len << 4) > MAX_RX_DATASZ) { brcmf_dbg(INFO, "nextlen too large (%d) seq %d\n", - bus->nextlen, seq); - bus->nextlen = 0; + next_len, seq); + next_len = 0; } + bus->cur_read.len = next_len << 4; doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); @@ -1378,7 +1393,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) bus->sdcnt.rxglomfail++; brcmf_sdbrcm_free_glom(bus); } - bus->nextlen = 0; + bus->cur_read.len = 0; return 0; } @@ -1573,422 +1588,166 @@ static void brcmf_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) } } -static void -brcmf_alloc_pkt_and_read(struct brcmf_sdio *bus, u16 rdlen, - struct sk_buff **pkt, u8 **rxbuf) +static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) { - int sdret; /* Return code from calls */ - - *pkt = brcmu_pkt_buf_get_skb(rdlen + BRCMF_SDALIGN); - if (*pkt == NULL) - return; - - pkt_align(*pkt, rdlen, BRCMF_SDALIGN); - *rxbuf = (u8 *) ((*pkt)->data); - /* Read the entire frame */ - sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, *pkt); - bus->sdcnt.f2rxdata++; - - if (sdret < 0) { - brcmf_dbg(ERROR, "(nextlen): read %d bytes failed: %d\n", - rdlen, sdret); - brcmu_pkt_buf_free_skb(*pkt); - bus->sdiodev->bus_if->dstats.rx_errors++; - /* Force retry w/normal header read. - * Don't attempt NAK for - * gSPI - */ - brcmf_sdbrcm_rxfail(bus, true, true); - *pkt = NULL; - } -} - -/* Checks the header */ -static int -brcmf_check_rxbuf(struct brcmf_sdio *bus, struct sk_buff *pkt, u8 *rxbuf, - u8 rxseq, u16 nextlen, u16 *len) -{ - u16 check; - bool len_consistent; /* Result of comparing readahead len and - len from hw-hdr */ - - memcpy(bus->rxhdr, rxbuf, SDPCM_HDRLEN); - - /* Extract hardware header fields */ - *len = get_unaligned_le16(bus->rxhdr); - check = get_unaligned_le16(bus->rxhdr + sizeof(u16)); - - /* All zeros means readahead info was bad */ - if (!(*len | check)) { - brcmf_dbg(INFO, "(nextlen): read zeros in HW header???\n"); - goto fail; - } - - /* Validate check bytes */ - if ((u16)~(*len ^ check)) { - brcmf_dbg(ERROR, "(nextlen): HW hdr error: nextlen/len/check 0x%04x/0x%04x/0x%04x\n", - nextlen, *len, check); - bus->sdcnt.rx_badhdr++; - brcmf_sdbrcm_rxfail(bus, false, false); - goto fail; - } - - /* Validate frame length */ - if (*len < SDPCM_HDRLEN) { - brcmf_dbg(ERROR, "(nextlen): HW hdr length invalid: %d\n", - *len); - goto fail; - } - - /* Check for consistency with readahead info */ - len_consistent = (nextlen != (roundup(*len, 16) >> 4)); - if (len_consistent) { - /* Mismatch, force retry w/normal - header (may be >4K) */ - brcmf_dbg(ERROR, "(nextlen): mismatch, nextlen %d len %d rnd %d; expected rxseq %d\n", - nextlen, *len, roundup(*len, 16), - rxseq); - brcmf_sdbrcm_rxfail(bus, true, true); - goto fail; - } - - return 0; - -fail: - brcmf_sdbrcm_pktfree2(bus, pkt); - return -EINVAL; -} - -/* Return true if there may be more frames to read */ -static uint -brcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished) -{ - u16 len, check; /* Extracted hardware header fields */ - u8 chan, seq, doff; /* Extracted software header fields */ - u8 fcbits; /* Extracted fcbits from software header */ - struct sk_buff *pkt; /* Packet for event or data frames */ u16 pad; /* Number of pad bytes to read */ - u16 rdlen; /* Total number of bytes to read */ - u8 rxseq; /* Next sequence number to expect */ uint rxleft = 0; /* Remaining number of frames allowed */ int sdret; /* Return code from calls */ - u8 txmax; /* Maximum tx sequence offered */ - u8 *rxbuf; int ifidx = 0; uint rxcount = 0; /* Total frames read */ + struct brcmf_sdio_read *rd = &bus->cur_read, rd_new; + u8 head_read = 0; brcmf_dbg(TRACE, "Enter\n"); /* Not finished unless we encounter no more frames indication */ - *finished = false; + bus->rxpending = true; - for (rxseq = bus->rx_seq, rxleft = maxframes; + for (rd->seq_num = bus->rx_seq, rxleft = maxframes; !bus->rxskip && rxleft && bus->sdiodev->bus_if->state != BRCMF_BUS_DOWN; - rxseq++, rxleft--) { + rd->seq_num++, rxleft--) { /* Handle glomming separately */ if (bus->glomd || !skb_queue_empty(&bus->glom)) { u8 cnt; brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n", bus->glomd, skb_peek(&bus->glom)); - cnt = brcmf_sdbrcm_rxglom(bus, rxseq); + cnt = brcmf_sdbrcm_rxglom(bus, rd->seq_num); brcmf_dbg(GLOM, "rxglom returned %d\n", cnt); - rxseq += cnt - 1; + rd->seq_num += cnt - 1; rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1; continue; } - /* Try doing single read if we can */ - if (bus->nextlen) { - u16 nextlen = bus->nextlen; - bus->nextlen = 0; - - rdlen = len = nextlen << 4; - brcmf_pad(bus, &pad, &rdlen); - - /* - * After the frame is received we have to - * distinguish whether it is data - * or non-data frame. - */ - brcmf_alloc_pkt_and_read(bus, rdlen, &pkt, &rxbuf); - if (pkt == NULL) { - /* Give up on data, request rtx of events */ - brcmf_dbg(ERROR, "(nextlen): brcmf_alloc_pkt_and_read failed: len %d rdlen %d expected rxseq %d\n", - len, rdlen, rxseq); - continue; - } - - if (brcmf_check_rxbuf(bus, pkt, rxbuf, rxseq, nextlen, - &len) < 0) + rd->len_left = rd->len; + /* read header first for unknow frame length */ + if (!rd->len) { + sdret = brcmf_sdcard_recv_buf(bus->sdiodev, + bus->sdiodev->sbwad, + SDIO_FUNC_2, F2SYNC, + bus->rxhdr, + BRCMF_FIRSTREAD); + bus->sdcnt.f2rxhdrs++; + if (sdret < 0) { + brcmf_dbg(ERROR, "RXHEADER FAILED: %d\n", + sdret); + bus->sdcnt.rx_hdrfail++; + brcmf_sdbrcm_rxfail(bus, true, true); continue; - - /* Extract software header fields */ - chan = SDPCM_PACKET_CHANNEL( - &bus->rxhdr[SDPCM_FRAMETAG_LEN]); - seq = SDPCM_PACKET_SEQUENCE( - &bus->rxhdr[SDPCM_FRAMETAG_LEN]); - doff = SDPCM_DOFFSET_VALUE( - &bus->rxhdr[SDPCM_FRAMETAG_LEN]); - txmax = SDPCM_WINDOW_VALUE( - &bus->rxhdr[SDPCM_FRAMETAG_LEN]); - - bus->nextlen = - bus->rxhdr[SDPCM_FRAMETAG_LEN + - SDPCM_NEXTLEN_OFFSET]; - if ((bus->nextlen << 4) > MAX_RX_DATASZ) { - brcmf_dbg(INFO, "(nextlen): got frame w/nextlen too large (%d), seq %d\n", - bus->nextlen, seq); - bus->nextlen = 0; } - bus->sdcnt.rx_readahead_cnt++; - - /* Handle Flow Control */ - fcbits = SDPCM_FCMASK_VALUE( - &bus->rxhdr[SDPCM_FRAMETAG_LEN]); - - if (bus->flowcontrol != fcbits) { - if (~bus->flowcontrol & fcbits) - bus->sdcnt.fc_xoff++; - - if (bus->flowcontrol & ~fcbits) - bus->sdcnt.fc_xon++; - - bus->sdcnt.fc_rcvd++; - bus->flowcontrol = fcbits; - } - - /* Check and update sequence number */ - if (rxseq != seq) { - brcmf_dbg(INFO, "(nextlen): rx_seq %d, expected %d\n", - seq, rxseq); - bus->sdcnt.rx_badseq++; - rxseq = seq; - } - - /* Check window for sanity */ - if ((u8) (txmax - bus->tx_seq) > 0x40) { - brcmf_dbg(ERROR, "got unlikely tx max %d with tx_seq %d\n", - txmax, bus->tx_seq); - txmax = bus->tx_seq + 2; - } - bus->tx_max = txmax; - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), - rxbuf, len, "Rx Data:\n"); - brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && - BRCMF_DATA_ON()) && - BRCMF_HDRS_ON(), + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(), bus->rxhdr, SDPCM_HDRLEN, "RxHdr:\n"); - if (chan == SDPCM_CONTROL_CHANNEL) { - brcmf_dbg(ERROR, "(nextlen): readahead on control packet %d?\n", - seq); - /* Force retry w/normal header read */ - bus->nextlen = 0; - brcmf_sdbrcm_rxfail(bus, false, true); - brcmf_sdbrcm_pktfree2(bus, pkt); - continue; + if (!brcmf_sdio_hdparser(bus, bus->rxhdr, rd)) { + if (!bus->rxpending) + break; + else + continue; } - /* Validate data offset */ - if ((doff < SDPCM_HDRLEN) || (doff > len)) { - brcmf_dbg(ERROR, "(nextlen): bad data offset %d: HW len %d min %d\n", - doff, len, SDPCM_HDRLEN); - brcmf_sdbrcm_rxfail(bus, false, false); - brcmf_sdbrcm_pktfree2(bus, pkt); + if (rd->channel == SDPCM_CONTROL_CHANNEL) { + brcmf_sdbrcm_read_control(bus, bus->rxhdr, + rd->len, + rd->dat_offset); + /* prepare the descriptor for the next read */ + rd->len = rd->len_nxtfrm << 4; + rd->len_nxtfrm = 0; + /* treat all packet as event if we don't know */ + rd->channel = SDPCM_EVENT_CHANNEL; continue; } - - /* All done with this one -- now deliver the packet */ - goto deliver; - } - - /* Read frame header (hardware and software) */ - sdret = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, bus->rxhdr, - BRCMF_FIRSTREAD); - bus->sdcnt.f2rxhdrs++; - - if (sdret < 0) { - brcmf_dbg(ERROR, "RXHEADER FAILED: %d\n", sdret); - bus->sdcnt.rx_hdrfail++; - brcmf_sdbrcm_rxfail(bus, true, true); - continue; - } - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(), - bus->rxhdr, SDPCM_HDRLEN, "RxHdr:\n"); - - - /* Extract hardware header fields */ - len = get_unaligned_le16(bus->rxhdr); - check = get_unaligned_le16(bus->rxhdr + sizeof(u16)); - - /* All zeros means no more frames */ - if (!(len | check)) { - *finished = true; - break; + rd->len_left = rd->len > BRCMF_FIRSTREAD ? + rd->len - BRCMF_FIRSTREAD : 0; + head_read = BRCMF_FIRSTREAD; } - /* Validate check bytes */ - if ((u16) ~(len ^ check)) { - brcmf_dbg(ERROR, "HW hdr err: len/check 0x%04x/0x%04x\n", - len, check); - bus->sdcnt.rx_badhdr++; - brcmf_sdbrcm_rxfail(bus, false, false); - continue; - } + brcmf_pad(bus, &pad, &rd->len_left); - /* Validate frame length */ - if (len < SDPCM_HDRLEN) { - brcmf_dbg(ERROR, "HW hdr length invalid: %d\n", len); - continue; - } - - /* Extract software header fields */ - chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); - seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); - doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); - txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); - - /* Validate data offset */ - if ((doff < SDPCM_HDRLEN) || (doff > len)) { - brcmf_dbg(ERROR, "Bad data offset %d: HW len %d, min %d seq %d\n", - doff, len, SDPCM_HDRLEN, seq); - bus->sdcnt.rx_badhdr++; - brcmf_sdbrcm_rxfail(bus, false, false); - continue; - } - - /* Save the readahead length if there is one */ - bus->nextlen = - bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; - if ((bus->nextlen << 4) > MAX_RX_DATASZ) { - brcmf_dbg(INFO, "(nextlen): got frame w/nextlen too large (%d), seq %d\n", - bus->nextlen, seq); - bus->nextlen = 0; - } - - /* Handle Flow Control */ - fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]); - - if (bus->flowcontrol != fcbits) { - if (~bus->flowcontrol & fcbits) - bus->sdcnt.fc_xoff++; - - if (bus->flowcontrol & ~fcbits) - bus->sdcnt.fc_xon++; - - bus->sdcnt.fc_rcvd++; - bus->flowcontrol = fcbits; - } - - /* Check and update sequence number */ - if (rxseq != seq) { - brcmf_dbg(INFO, "rx_seq %d, expected %d\n", seq, rxseq); - bus->sdcnt.rx_badseq++; - rxseq = seq; - } - - /* Check window for sanity */ - if ((u8) (txmax - bus->tx_seq) > 0x40) { - brcmf_dbg(ERROR, "unlikely tx max %d with tx_seq %d\n", - txmax, bus->tx_seq); - txmax = bus->tx_seq + 2; - } - bus->tx_max = txmax; - - /* Call a separate function for control frames */ - if (chan == SDPCM_CONTROL_CHANNEL) { - brcmf_sdbrcm_read_control(bus, bus->rxhdr, len, doff); - continue; - } - - /* precondition: chan is either SDPCM_DATA_CHANNEL, - SDPCM_EVENT_CHANNEL, SDPCM_TEST_CHANNEL or - SDPCM_GLOM_CHANNEL */ - - /* Length to read */ - rdlen = (len > BRCMF_FIRSTREAD) ? (len - BRCMF_FIRSTREAD) : 0; - - /* May pad read to blocksize for efficiency */ - if (bus->roundup && bus->blocksize && - (rdlen > bus->blocksize)) { - pad = bus->blocksize - (rdlen % bus->blocksize); - if ((pad <= bus->roundup) && (pad < bus->blocksize) && - ((rdlen + pad + BRCMF_FIRSTREAD) < MAX_RX_DATASZ)) - rdlen += pad; - } else if (rdlen % BRCMF_SDALIGN) { - rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN); - } - - /* Satisfy length-alignment requirements */ - if (rdlen & (ALIGNMENT - 1)) - rdlen = roundup(rdlen, ALIGNMENT); - - if ((rdlen + BRCMF_FIRSTREAD) > MAX_RX_DATASZ) { - /* Too long -- skip this frame */ - brcmf_dbg(ERROR, "too long: len %d rdlen %d\n", - len, rdlen); - bus->sdiodev->bus_if->dstats.rx_errors++; - bus->sdcnt.rx_toolong++; - brcmf_sdbrcm_rxfail(bus, false, false); - continue; - } - - pkt = brcmu_pkt_buf_get_skb(rdlen + - BRCMF_FIRSTREAD + BRCMF_SDALIGN); + pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read + + BRCMF_SDALIGN); if (!pkt) { /* Give up on data, request rtx of events */ - brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: rdlen %d chan %d\n", - rdlen, chan); + brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed\n"); bus->sdiodev->bus_if->dstats.rx_dropped++; - brcmf_sdbrcm_rxfail(bus, false, RETRYCHAN(chan)); + brcmf_sdbrcm_rxfail(bus, false, + RETRYCHAN(rd->channel)); continue; } + skb_pull(pkt, head_read); + pkt_align(pkt, rd->len_left, BRCMF_SDALIGN); - /* Leave room for what we already read, and align remainder */ - skb_pull(pkt, BRCMF_FIRSTREAD); - pkt_align(pkt, rdlen, BRCMF_SDALIGN); - - /* Read the remaining frame data */ sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, pkt); bus->sdcnt.f2rxdata++; if (sdret < 0) { - brcmf_dbg(ERROR, "read %d %s bytes failed: %d\n", rdlen, - ((chan == SDPCM_EVENT_CHANNEL) ? "event" - : ((chan == SDPCM_DATA_CHANNEL) ? "data" - : "test")), sdret); + brcmf_dbg(ERROR, "read %d bytes from channel %d failed: %d\n", + rd->len, rd->channel, sdret); brcmu_pkt_buf_free_skb(pkt); bus->sdiodev->bus_if->dstats.rx_errors++; - brcmf_sdbrcm_rxfail(bus, true, RETRYCHAN(chan)); + brcmf_sdbrcm_rxfail(bus, true, + RETRYCHAN(rd->channel)); continue; } - /* Copy the already-read portion */ - skb_push(pkt, BRCMF_FIRSTREAD); - memcpy(pkt->data, bus->rxhdr, BRCMF_FIRSTREAD); + if (head_read) { + skb_push(pkt, head_read); + memcpy(pkt->data, bus->rxhdr, head_read); + head_read = 0; + } else { + memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN); + rd_new.seq_num = rd->seq_num; + if (!brcmf_sdio_hdparser(bus, bus->rxhdr, &rd_new)) { + rd->len = 0; + brcmu_pkt_buf_free_skb(pkt); + } + bus->sdcnt.rx_readahead_cnt++; + if (rd->len != roundup(rd_new.len, 16)) { + brcmf_dbg(ERROR, "frame length mismatch:read %d, should be %d\n", + rd->len, + roundup(rd_new.len, 16) >> 4); + rd->len = 0; + brcmf_sdbrcm_rxfail(bus, true, true); + brcmu_pkt_buf_free_skb(pkt); + continue; + } + rd->len_nxtfrm = rd_new.len_nxtfrm; + rd->channel = rd_new.channel; + rd->dat_offset = rd_new.dat_offset; + + brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && + BRCMF_DATA_ON()) && + BRCMF_HDRS_ON(), + bus->rxhdr, SDPCM_HDRLEN, + "RxHdr:\n"); + + if (rd_new.channel == SDPCM_CONTROL_CHANNEL) { + brcmf_dbg(ERROR, "readahead on control packet %d?\n", + rd_new.seq_num); + /* Force retry w/normal header read */ + rd->len = 0; + brcmf_sdbrcm_rxfail(bus, false, true); + brcmu_pkt_buf_free_skb(pkt); + continue; + } + } brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), - pkt->data, len, "Rx Data:\n"); + pkt->data, rd->len, "Rx Data:\n"); -deliver: /* Save superframe descriptor and allocate packet frame */ - if (chan == SDPCM_GLOM_CHANNEL) { + if (rd->channel == SDPCM_GLOM_CHANNEL) { if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) { brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n", - len); + rd->len); brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), - pkt->data, len, + pkt->data, rd->len, "Glom Data:\n"); - __skb_trim(pkt, len); + __skb_trim(pkt, rd->len); skb_pull(pkt, SDPCM_HDRLEN); bus->glomd = pkt; } else { @@ -1996,12 +1755,23 @@ deliver: "descriptor!\n", __func__); brcmf_sdbrcm_rxfail(bus, false, false); } + /* prepare the descriptor for the next read */ + rd->len = rd->len_nxtfrm << 4; + rd->len_nxtfrm = 0; + /* treat all packet as event if we don't know */ + rd->channel = SDPCM_EVENT_CHANNEL; continue; } /* Fill in packet len and prio, deliver upward */ - __skb_trim(pkt, len); - skb_pull(pkt, doff); + __skb_trim(pkt, rd->len); + skb_pull(pkt, rd->dat_offset); + + /* prepare the descriptor for the next read */ + rd->len = rd->len_nxtfrm << 4; + rd->len_nxtfrm = 0; + /* treat all packet as event if we don't know */ + rd->channel = SDPCM_EVENT_CHANNEL; if (pkt->len == 0) { brcmu_pkt_buf_free_skb(pkt); @@ -2019,17 +1789,17 @@ deliver: brcmf_rx_packet(bus->sdiodev->dev, ifidx, pkt); down(&bus->sdsem); } + rxcount = maxframes - rxleft; /* Message if we hit the limit */ if (!rxleft) - brcmf_dbg(DATA, "hit rx limit of %d frames\n", - maxframes); + brcmf_dbg(DATA, "hit rx limit of %d frames\n", maxframes); else brcmf_dbg(DATA, "processed %d frames\n", rxcount); /* Back off rxseq if awaiting rtx, update rx_seq */ if (bus->rxskip) - rxseq--; - bus->rx_seq = rxseq; + rd->seq_num--; + bus->rx_seq = rd->seq_num; return rxcount; } @@ -2227,7 +1997,7 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) if (ret != 0) break; if (intstatus & bus->hostintmask) - bus->ipend = true; + atomic_set(&bus->ipend, 1); } } @@ -2259,16 +2029,8 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev) bus->watchdog_tsk = NULL; } - if (bus->dpc_tsk && bus->dpc_tsk != current) { - send_sig(SIGTERM, bus->dpc_tsk, 1); - kthread_stop(bus->dpc_tsk); - bus->dpc_tsk = NULL; - } - down(&bus->sdsem); - bus_wake(bus); - /* Enable clock for device interrupts */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); @@ -2327,7 +2089,7 @@ static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) unsigned long flags; spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags); - if (!bus->sdiodev->irq_en && !bus->ipend) { + if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) { enable_irq(bus->sdiodev->irq); bus->sdiodev->irq_en = true; } @@ -2339,21 +2101,69 @@ static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) } #endif /* CONFIG_BRCMFMAC_SDIO_OOB */ -static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) +static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus) +{ + struct list_head *new_hd; + unsigned long flags; + + if (in_interrupt()) + new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC); + else + new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL); + if (new_hd == NULL) + return; + + spin_lock_irqsave(&bus->dpc_tl_lock, flags); + list_add_tail(new_hd, &bus->dpc_tsklst); + spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); +} + +static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) { - u32 intstatus, newstatus = 0; + u8 idx; + u32 addr; + unsigned long val; + int n, ret; + + idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); + addr = bus->ci->c_inf[idx].base + + offsetof(struct sdpcmd_regs, intstatus); + + ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, false); + bus->sdcnt.f1regdata++; + if (ret != 0) + val = 0; + + val &= bus->hostintmask; + atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); + + /* Clear interrupts */ + if (val) { + ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, true); + bus->sdcnt.f1regdata++; + } + + if (ret) { + atomic_set(&bus->intstatus, 0); + } else if (val) { + for_each_set_bit(n, &val, 32) + set_bit(n, (unsigned long *)&bus->intstatus.counter); + } + + return ret; +} + +static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) +{ + u32 newstatus = 0; + unsigned long intstatus; uint rxlimit = bus->rxbound; /* Rx frames to read before resched */ uint txlimit = bus->txbound; /* Tx frames to send before resched */ uint framecnt = 0; /* Temporary counter of tx/rx frames */ - bool rxdone = true; /* Flag for no more read data */ - bool resched = false; /* Flag indicating resched wanted */ - int err; + int err = 0, n; brcmf_dbg(TRACE, "Enter\n"); - /* Start with leftover status bits */ - intstatus = bus->intstatus; - down(&bus->sdsem); /* If waiting for HTAVAIL, check status */ @@ -2399,39 +2209,22 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; } bus->clkstate = CLK_AVAIL; - } else { - goto clkwait; } } - bus_wake(bus); - /* Make sure backplane clock is on */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, true); - if (bus->clkstate == CLK_PENDING) - goto clkwait; /* Pending interrupt indicates new device status */ - if (bus->ipend) { - bus->ipend = false; - err = r_sdreg32(bus, &newstatus, - offsetof(struct sdpcmd_regs, intstatus)); - bus->sdcnt.f1regdata++; - if (err != 0) - newstatus = 0; - newstatus &= bus->hostintmask; - bus->fcstate = !!(newstatus & I_HMB_FC_STATE); - if (newstatus) { - err = w_sdreg32(bus, newstatus, - offsetof(struct sdpcmd_regs, - intstatus)); - bus->sdcnt.f1regdata++; - } + if (atomic_read(&bus->ipend) > 0) { + atomic_set(&bus->ipend, 0); + sdio_claim_host(bus->sdiodev->func[1]); + err = brcmf_sdio_intr_rstatus(bus); + sdio_release_host(bus->sdiodev->func[1]); } - /* Merge new bits with previous */ - intstatus |= newstatus; - bus->intstatus = 0; + /* Start with leftover status bits */ + intstatus = atomic_xchg(&bus->intstatus, 0); /* Handle flow-control change: read new state in case our ack * crossed another change interrupt. If change still set, assume @@ -2445,8 +2238,8 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) err = r_sdreg32(bus, &newstatus, offsetof(struct sdpcmd_regs, intstatus)); bus->sdcnt.f1regdata += 2; - bus->fcstate = - !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE)); + atomic_set(&bus->fcstate, + !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE))); intstatus |= (newstatus & bus->hostintmask); } @@ -2483,32 +2276,34 @@ static bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) intstatus &= ~I_HMB_FRAME_IND; /* On frame indication, read available frames */ - if (PKT_AVAILABLE()) { - framecnt = brcmf_sdbrcm_readframes(bus, rxlimit, &rxdone); - if (rxdone || bus->rxskip) + if (PKT_AVAILABLE() && bus->clkstate == CLK_AVAIL) { + framecnt = brcmf_sdio_readframes(bus, rxlimit); + if (!bus->rxpending) intstatus &= ~I_HMB_FRAME_IND; rxlimit -= min(framecnt, rxlimit); } /* Keep still-pending events for next scheduling */ - bus->intstatus = intstatus; + if (intstatus) { + for_each_set_bit(n, &intstatus, 32) + set_bit(n, (unsigned long *)&bus->intstatus.counter); + } -clkwait: brcmf_sdbrcm_clrintr(bus); if (data_ok(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) { - int ret, i; + int i; - ret = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, + err = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, bus->ctrl_frame_buf, (u32) bus->ctrl_frame_len); - if (ret < 0) { + if (err < 0) { /* On failure, abort the command and terminate the frame */ brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", - ret); + err); bus->sdcnt.tx_sderrs++; brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); @@ -2530,42 +2325,34 @@ clkwait: break; } - } - if (ret == 0) + } else { bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; - - brcmf_dbg(INFO, "Return_dpc value is : %d\n", ret); + } bus->ctrl_frame_stat = false; brcmf_sdbrcm_wait_event_wakeup(bus); } /* Send queued frames (limit 1 if rx may still be pending) */ - else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate && + else if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && data_ok(bus)) { - framecnt = rxdone ? txlimit : min(txlimit, bus->txminmax); + framecnt = bus->rxpending ? min(txlimit, bus->txminmax) : + txlimit; framecnt = brcmf_sdbrcm_sendfromq(bus, framecnt); txlimit -= framecnt; } - /* Resched if events or tx frames are pending, - else await next interrupt */ - /* On failed register access, all bets are off: - no resched or interrupts */ if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) || (err != 0)) { brcmf_dbg(ERROR, "failed backplane access over SDIO, halting operation\n"); bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; - bus->intstatus = 0; - } else if (bus->clkstate == CLK_PENDING) { - brcmf_dbg(INFO, "rescheduled due to CLK_PENDING awaiting I_CHIPACTIVE interrupt\n"); - resched = true; - } else if (bus->intstatus || bus->ipend || - (!bus->fcstate && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) - && data_ok(bus)) || PKT_AVAILABLE()) { - resched = true; + atomic_set(&bus->intstatus, 0); + } else if (atomic_read(&bus->intstatus) || + atomic_read(&bus->ipend) > 0 || + (!atomic_read(&bus->fcstate) && + brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && + data_ok(bus)) || PKT_AVAILABLE()) { + brcmf_sdbrcm_adddpctsk(bus); } - bus->dpc_sched = resched; - /* If we're done for now, turn off clock request. */ if ((bus->clkstate != CLK_PENDING) && bus->idletime == BRCMF_IDLE_IMMEDIATE) { @@ -2574,65 +2361,6 @@ clkwait: } up(&bus->sdsem); - - return resched; -} - -static inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus) -{ - struct list_head *new_hd; - unsigned long flags; - - if (in_interrupt()) - new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC); - else - new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL); - if (new_hd == NULL) - return; - - spin_lock_irqsave(&bus->dpc_tl_lock, flags); - list_add_tail(new_hd, &bus->dpc_tsklst); - spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); -} - -static int brcmf_sdbrcm_dpc_thread(void *data) -{ - struct brcmf_sdio *bus = (struct brcmf_sdio *) data; - struct list_head *cur_hd, *tmp_hd; - unsigned long flags; - - allow_signal(SIGTERM); - /* Run until signal received */ - while (1) { - if (kthread_should_stop()) - break; - - if (list_empty(&bus->dpc_tsklst)) - if (wait_for_completion_interruptible(&bus->dpc_wait)) - break; - - spin_lock_irqsave(&bus->dpc_tl_lock, flags); - list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) { - spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); - - if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { - /* after stopping the bus, exit thread */ - brcmf_sdbrcm_bus_stop(bus->sdiodev->dev); - bus->dpc_tsk = NULL; - spin_lock_irqsave(&bus->dpc_tl_lock, flags); - break; - } - - if (brcmf_sdbrcm_dpc(bus)) - brcmf_sdbrcm_adddpctsk(bus); - - spin_lock_irqsave(&bus->dpc_tl_lock, flags); - list_del(cur_hd); - kfree(cur_hd); - } - spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); - } - return 0; } static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) @@ -2642,6 +2370,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; + unsigned long flags; brcmf_dbg(TRACE, "Enter\n"); @@ -2680,13 +2409,15 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) if (pktq_plen(&bus->txq, prec) > qcount[prec]) qcount[prec] = pktq_plen(&bus->txq, prec); #endif - /* Schedule DPC if needed to send queued packet(s) */ - if (!bus->dpc_sched) { - bus->dpc_sched = true; - if (bus->dpc_tsk) { - brcmf_sdbrcm_adddpctsk(bus); - complete(&bus->dpc_wait); - } + + spin_lock_irqsave(&bus->dpc_tl_lock, flags); + if (list_empty(&bus->dpc_tsklst)) { + spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); + + brcmf_sdbrcm_adddpctsk(bus); + queue_work(bus->brcmf_wq, &bus->datawork); + } else { + spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); } return ret; @@ -2707,6 +2438,8 @@ brcmf_sdbrcm_membytes(struct brcmf_sdio *bus, bool write, u32 address, u8 *data, else dsize = size; + sdio_claim_host(bus->sdiodev->func[1]); + /* Set the backplane window to include the start address */ bcmerror = brcmf_sdcard_set_sbaddr_window(bus->sdiodev, address); if (bcmerror) { @@ -2748,6 +2481,8 @@ xfer_done: brcmf_dbg(ERROR, "FAILED to set window back to 0x%x\n", bus->sdiodev->sbwad); + sdio_release_host(bus->sdiodev->func[1]); + return bcmerror; } @@ -2882,6 +2617,7 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; + unsigned long flags; brcmf_dbg(TRACE, "Enter\n"); @@ -2918,8 +2654,6 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) /* Need to lock here to protect txseq and SDIO tx calls */ down(&bus->sdsem); - bus_wake(bus); - /* Make sure backplane clock is on */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); @@ -2967,9 +2701,15 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) } while (ret < 0 && retries++ < TXRETRIES); } - if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && !bus->dpc_sched) { + spin_lock_irqsave(&bus->dpc_tl_lock, flags); + if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && + list_empty(&bus->dpc_tsklst)) { + spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); + bus->activity = false; brcmf_sdbrcm_clkctl(bus, CLK_NONE, true); + } else { + spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); } up(&bus->sdsem); @@ -3774,23 +3514,20 @@ void brcmf_sdbrcm_isr(void *arg) } /* Count the interrupt call */ bus->sdcnt.intrcount++; - bus->ipend = true; - - /* Shouldn't get this interrupt if we're sleeping? */ - if (bus->sleeping) { - brcmf_dbg(ERROR, "INTERRUPT WHILE SLEEPING??\n"); - return; - } + if (in_interrupt()) + atomic_set(&bus->ipend, 1); + else + if (brcmf_sdio_intr_rstatus(bus)) { + brcmf_dbg(ERROR, "failed backplane access\n"); + bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; + } /* Disable additional interrupts (is this needed now)? */ if (!bus->intr) brcmf_dbg(ERROR, "isr w/o interrupt configured!\n"); - bus->dpc_sched = true; - if (bus->dpc_tsk) { - brcmf_sdbrcm_adddpctsk(bus); - complete(&bus->dpc_wait); - } + brcmf_sdbrcm_adddpctsk(bus); + queue_work(bus->brcmf_wq, &bus->datawork); } static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) @@ -3798,13 +3535,10 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) #ifdef DEBUG struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev); #endif /* DEBUG */ + unsigned long flags; brcmf_dbg(TIMER, "Enter\n"); - /* Ignore the timer if simulating bus down */ - if (bus->sleeping) - return false; - down(&bus->sdsem); /* Poll period: check device if appropriate. */ @@ -3818,27 +3552,30 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) if (!bus->intr || (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) { - if (!bus->dpc_sched) { + spin_lock_irqsave(&bus->dpc_tl_lock, flags); + if (list_empty(&bus->dpc_tsklst)) { u8 devpend; + spin_unlock_irqrestore(&bus->dpc_tl_lock, + flags); devpend = brcmf_sdio_regrb(bus->sdiodev, SDIO_CCCR_INTx, NULL); intstatus = devpend & (INTR_STATUS_FUNC1 | INTR_STATUS_FUNC2); + } else { + spin_unlock_irqrestore(&bus->dpc_tl_lock, + flags); } /* If there is something, make like the ISR and schedule the DPC */ if (intstatus) { bus->sdcnt.pollcnt++; - bus->ipend = true; + atomic_set(&bus->ipend, 1); - bus->dpc_sched = true; - if (bus->dpc_tsk) { - brcmf_sdbrcm_adddpctsk(bus); - complete(&bus->dpc_wait); - } + brcmf_sdbrcm_adddpctsk(bus); + queue_work(bus->brcmf_wq, &bus->datawork); } } @@ -3876,7 +3613,7 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) up(&bus->sdsem); - return bus->ipend; + return (atomic_read(&bus->ipend) > 0); } static bool brcmf_sdbrcm_chipmatch(u16 chipid) @@ -3892,6 +3629,26 @@ static bool brcmf_sdbrcm_chipmatch(u16 chipid) return false; } +static void brcmf_sdio_dataworker(struct work_struct *work) +{ + struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio, + datawork); + struct list_head *cur_hd, *tmp_hd; + unsigned long flags; + + spin_lock_irqsave(&bus->dpc_tl_lock, flags); + list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) { + spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); + + brcmf_sdbrcm_dpc(bus); + + spin_lock_irqsave(&bus->dpc_tl_lock, flags); + list_del(cur_hd); + kfree(cur_hd); + } + spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); +} + static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); @@ -4024,7 +3781,6 @@ static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus) SDIO_FUNC_ENABLE_1, NULL); bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; - bus->sleeping = false; bus->rxflow = false; /* Done with backplane-dependent accesses, can drop clock... */ @@ -4105,6 +3861,9 @@ static void brcmf_sdbrcm_release(struct brcmf_sdio *bus) /* De-register interrupt handler */ brcmf_sdio_intr_unregister(bus->sdiodev); + cancel_work_sync(&bus->datawork); + destroy_workqueue(bus->brcmf_wq); + if (bus->sdiodev->bus_if->drvr) { brcmf_detach(bus->sdiodev->dev); brcmf_sdbrcm_release_dongle(bus); @@ -4144,8 +3903,6 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) bus->rxbound = BRCMF_RXBOUND; bus->txminmax = BRCMF_TXMINMAX; bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1; - bus->usebufpool = false; /* Use bufpool if allocated, - else use locally malloced rxbuf */ /* attempt to attach to the dongle */ if (!(brcmf_sdbrcm_probe_attach(bus, regsva))) { @@ -4157,6 +3914,13 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) init_waitqueue_head(&bus->ctrl_wait); init_waitqueue_head(&bus->dcmd_resp_wait); + bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq"); + if (bus->brcmf_wq == NULL) { + brcmf_dbg(ERROR, "insufficient memory to create txworkqueue\n"); + goto fail; + } + INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); + /* Set up the watchdog timer */ init_timer(&bus->timer); bus->timer.data = (unsigned long)bus; @@ -4174,15 +3938,8 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) bus->watchdog_tsk = NULL; } /* Initialize DPC thread */ - init_completion(&bus->dpc_wait); INIT_LIST_HEAD(&bus->dpc_tsklst); spin_lock_init(&bus->dpc_tl_lock); - bus->dpc_tsk = kthread_run(brcmf_sdbrcm_dpc_thread, - bus, "brcmf_dpc"); - if (IS_ERR(bus->dpc_tsk)) { - pr_warn("brcmf_dpc thread failed to start\n"); - bus->dpc_tsk = NULL; - } /* Assign bus interface call back */ bus->sdiodev->bus_if->brcmf_bus_stop = brcmf_sdbrcm_bus_stop; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 29bf78d264e0..0d30afd8c672 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -174,6 +174,8 @@ extern void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data, int *ret); extern void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data, int *ret); +extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, + void *data, bool write); /* Buffer transfer to/from device (client) core via cmd53. * fn: function number diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index c6d5aeb27a02..5fe6ec7f838e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -81,10 +81,12 @@ enum usbdev_suspend_state { }; struct brcmf_usb_image { - void *data; - u32 len; + struct list_head list; + s8 *fwname; + u8 *image; + int image_len; }; -static struct brcmf_usb_image g_image = { NULL, 0 }; +static struct list_head fw_image_list; struct intr_transfer_buf { u32 notification; @@ -132,8 +134,6 @@ struct brcmf_usbdev_info { wait_queue_head_t ctrl_wait; ulong ctl_op; - bool rxctl_deferrespok; - struct urb *bulk_urb; /* used for FW download */ struct urb *intr_urb; /* URB for interrupt endpoint */ int intr_size; /* Size of interrupt message */ @@ -299,17 +299,9 @@ brcmf_usb_recv_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len) devinfo->ctl_read.wLength = cpu_to_le16p(&size); devinfo->ctl_urb->transfer_buffer_length = size; - if (devinfo->rxctl_deferrespok) { - /* BMAC model */ - devinfo->ctl_read.bRequestType = USB_DIR_IN - | USB_TYPE_VENDOR | USB_RECIP_INTERFACE; - devinfo->ctl_read.bRequest = DL_DEFER_RESP_OK; - } else { - /* full dongle model */ - devinfo->ctl_read.bRequestType = USB_DIR_IN - | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - devinfo->ctl_read.bRequest = 1; - } + devinfo->ctl_read.bRequestType = USB_DIR_IN + | USB_TYPE_CLASS | USB_RECIP_INTERFACE; + devinfo->ctl_read.bRequest = 1; usb_fill_control_urb(devinfo->ctl_urb, devinfo->usbdev, @@ -345,6 +337,7 @@ static int brcmf_usb_tx_ctlpkt(struct device *dev, u8 *buf, u32 len) err = brcmf_usb_send_ctl(devinfo, buf, len); if (err) { brcmf_dbg(ERROR, "fail %d bytes: %d\n", err, len); + clear_bit(0, &devinfo->ctl_op); return err; } @@ -375,6 +368,7 @@ static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len) err = brcmf_usb_recv_ctl(devinfo, buf, len); if (err) { brcmf_dbg(ERROR, "fail %d bytes: %d\n", err, len); + clear_bit(0, &devinfo->ctl_op); return err; } devinfo->ctl_completed = false; @@ -1152,10 +1146,6 @@ static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo) { brcmf_dbg(TRACE, "devinfo %p\n", devinfo); - /* store the image globally */ - g_image.data = devinfo->image; - g_image.len = devinfo->image_len; - /* free the URBS */ brcmf_usb_free_q(&devinfo->rx_freeq, false); brcmf_usb_free_q(&devinfo->tx_freeq, false); @@ -1207,17 +1197,9 @@ static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo) { s8 *fwname; const struct firmware *fw; + struct brcmf_usb_image *fw_image; int err; - devinfo->image = g_image.data; - devinfo->image_len = g_image.len; - - /* - * if we have an image we can leave here. - */ - if (devinfo->image) - return 0; - switch (devinfo->bus_pub.devid) { case 43143: fwname = BRCMF_USB_43143_FW_NAME; @@ -1235,6 +1217,14 @@ static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo) break; } + list_for_each_entry(fw_image, &fw_image_list, list) { + if (fw_image->fwname == fwname) { + devinfo->image = fw_image->image; + devinfo->image_len = fw_image->image_len; + return 0; + } + } + /* fw image not yet loaded. Load it now and add to list */ err = request_firmware(&fw, fwname, devinfo->dev); if (!fw) { brcmf_dbg(ERROR, "fail to request firmware %s\n", fwname); @@ -1245,14 +1235,24 @@ static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo) return -EINVAL; } - devinfo->image = vmalloc(fw->size); /* plus nvram */ - if (!devinfo->image) + fw_image = kzalloc(sizeof(*fw_image), GFP_ATOMIC); + if (!fw_image) + return -ENOMEM; + INIT_LIST_HEAD(&fw_image->list); + list_add_tail(&fw_image->list, &fw_image_list); + fw_image->fwname = fwname; + fw_image->image = vmalloc(fw->size); + if (!fw_image->image) return -ENOMEM; - memcpy(devinfo->image, fw->data, fw->size); - devinfo->image_len = fw->size; + memcpy(fw_image->image, fw->data, fw->size); + fw_image->image_len = fw->size; release_firmware(fw); + + devinfo->image = fw_image->image; + devinfo->image_len = fw_image->image_len; + return 0; } @@ -1304,8 +1304,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, brcmf_dbg(ERROR, "usb_alloc_urb (ctl) failed\n"); goto error; } - devinfo->rxctl_deferrespok = 0; - devinfo->bulk_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!devinfo->bulk_urb) { brcmf_dbg(ERROR, "usb_alloc_urb (bulk) failed\n"); @@ -1340,10 +1338,8 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo, struct device *dev = devinfo->dev; bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ); - if (!bus_pub) { - ret = -ENODEV; - goto fail; - } + if (!bus_pub) + return -ENODEV; bus = kzalloc(sizeof(struct brcmf_bus), GFP_ATOMIC); if (!bus) { @@ -1596,15 +1592,25 @@ static struct usb_driver brcmf_usbdrvr = { .disable_hub_initiated_lpm = 1, }; +static void brcmf_release_fw(struct list_head *q) +{ + struct brcmf_usb_image *fw_image, *next; + + list_for_each_entry_safe(fw_image, next, q, list) { + vfree(fw_image->image); + list_del_init(&fw_image->list); + } +} + + void brcmf_usb_exit(void) { usb_deregister(&brcmf_usbdrvr); - vfree(g_image.data); - g_image.data = NULL; - g_image.len = 0; + brcmf_release_fw(&fw_image_list); } void brcmf_usb_init(void) { + INIT_LIST_HEAD(&fw_image_list); usb_register(&brcmf_usbdrvr); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 32ee05297ccf..af396e4ab977 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -36,6 +36,18 @@ #include "dhd.h" #include "wl_cfg80211.h" +#define BRCMF_SCAN_IE_LEN_MAX 2048 +#define BRCMF_PNO_VERSION 2 +#define BRCMF_PNO_TIME 30 +#define BRCMF_PNO_REPEAT 4 +#define BRCMF_PNO_FREQ_EXPO_MAX 3 +#define BRCMF_PNO_MAX_PFN_COUNT 16 +#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6 +#define BRCMF_PNO_HIDDEN_BIT 2 +#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF +#define BRCMF_PNO_SCAN_COMPLETE 1 +#define BRCMF_PNO_SCAN_INCOMPLETE 0 + #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) @@ -700,11 +712,11 @@ static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le, u32 n_channels; s32 i; s32 offset; - __le16 chanspec; + u16 chanspec; u16 channel; struct ieee80211_channel *req_channel; char *ptr; - struct brcmf_ssid ssid; + struct brcmf_ssid_le ssid_le; memcpy(params_le->bssid, ether_bcast, ETH_ALEN); params_le->bss_type = DOT11_BSSTYPE_ANY; @@ -747,13 +759,10 @@ static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le, chanspec |= WL_CHANSPEC_CTL_SB_UPPER; } - params_le->channel_list[i] = - (channel & WL_CHANSPEC_CHAN_MASK) | - chanspec; + chanspec |= (channel & WL_CHANSPEC_CHAN_MASK); WL_SCAN("Chan : %d, Channel spec: %x\n", - channel, params_le->channel_list[i]); - params_le->channel_list[i] = - cpu_to_le16(params_le->channel_list[i]); + channel, chanspec); + params_le->channel_list[i] = cpu_to_le16(chanspec); } } else { WL_SCAN("Scanning all channels\n"); @@ -766,17 +775,18 @@ static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le, offset = roundup(offset, sizeof(u32)); ptr = (char *)params_le + offset; for (i = 0; i < n_ssids; i++) { - memset(&ssid, 0, sizeof(ssid)); - ssid.SSID_len = cpu_to_le32(request->ssids[i].ssid_len); - memcpy(ssid.SSID, request->ssids[i].ssid, - request->ssids[i].ssid_len); - if (!ssid.SSID_len) + memset(&ssid_le, 0, sizeof(ssid_le)); + ssid_le.SSID_len = + cpu_to_le32(request->ssids[i].ssid_len); + memcpy(ssid_le.SSID, request->ssids[i].ssid, + request->ssids[i].ssid_len); + if (!ssid_le.SSID_len) WL_SCAN("%d: Broadcast scan\n", i); else WL_SCAN("%d: scan for %s size =%d\n", i, - ssid.SSID, ssid.SSID_len); - memcpy(ptr, &ssid, sizeof(ssid)); - ptr += sizeof(ssid); + ssid_le.SSID, ssid_le.SSID_len); + memcpy(ptr, &ssid_le, sizeof(ssid_le)); + ptr += sizeof(ssid_le); } } else { WL_SCAN("Broadcast scan %p\n", request->ssids); @@ -834,7 +844,17 @@ brcmf_notify_escan_complete(struct brcmf_cfg80211_priv *cfg_priv, if (err) WL_ERR("Scan abort failed\n"); } - if (scan_request) { + /* + * e-scan can be initiated by scheduled scan + * which takes precedence. + */ + if (cfg_priv->sched_escan) { + WL_SCAN("scheduled scan completed\n"); + cfg_priv->sched_escan = false; + if (!aborted) + cfg80211_sched_scan_results(cfg_to_wiphy(cfg_priv)); + brcmf_set_mpc(ndev, 1); + } else if (scan_request) { WL_SCAN("ESCAN Completed scan: %s\n", aborted ? "Aborted" : "Done"); cfg80211_scan_done(scan_request, aborted); @@ -2593,11 +2613,13 @@ update_bss_info_out: return err; } -static void brcmf_term_iscan(struct brcmf_cfg80211_priv *cfg_priv) +static void brcmf_abort_scanning(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv); + struct escan_info *escan = &cfg_priv->escan_info; struct brcmf_ssid ssid; + set_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status); if (cfg_priv->iscan_on) { iscan->state = WL_ISCAN_STATE_IDLE; @@ -2611,7 +2633,20 @@ static void brcmf_term_iscan(struct brcmf_cfg80211_priv *cfg_priv) /* Abort iscan running in FW */ memset(&ssid, 0, sizeof(ssid)); brcmf_run_iscan(iscan, &ssid, WL_SCAN_ACTION_ABORT); + + if (cfg_priv->scan_request) { + /* Indidate scan abort to cfg80211 layer */ + WL_INFO("Terminating scan in progress\n"); + cfg80211_scan_done(cfg_priv->scan_request, true); + cfg_priv->scan_request = NULL; + } } + if (cfg_priv->escan_on && cfg_priv->scan_request) { + escan->escan_state = WL_ESCAN_STATE_IDLE; + brcmf_notify_escan_complete(cfg_priv, escan->ndev, true, true); + } + clear_bit(WL_STATUS_SCANNING, &cfg_priv->status); + clear_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status); } static void brcmf_notify_iscan_complete(struct brcmf_cfg80211_iscan_ctrl *iscan, @@ -2842,10 +2877,13 @@ brcmf_compare_update_same_bss(struct brcmf_bss_info_le *bss, !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) { if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) == (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL)) { + s16 bss_rssi = le16_to_cpu(bss->RSSI); + s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI); + /* preserve max RSSI if the measurements are * both on-channel or both off-channel */ - if (bss_info_le->RSSI > bss->RSSI) + if (bss_info_rssi > bss_rssi) bss->RSSI = bss_info_le->RSSI; } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) && (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) { @@ -2873,6 +2911,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_cfg80211_priv *cfg_priv, u32 bi_length; struct brcmf_scan_results *list; u32 i; + bool aborted; status = be32_to_cpu(e->status); @@ -2945,16 +2984,9 @@ brcmf_cfg80211_escan_handler(struct brcmf_cfg80211_priv *cfg_priv, cfg_priv->bss_list = (struct brcmf_scan_results *) cfg_priv->escan_info.escan_buf; brcmf_inform_bss(cfg_priv); - if (status == BRCMF_E_STATUS_SUCCESS) { - WL_SCAN("ESCAN Completed\n"); - brcmf_notify_escan_complete(cfg_priv, ndev, - false, false); - } else { - WL_ERR("ESCAN Aborted, Event 0x%x\n", status); - brcmf_notify_escan_complete(cfg_priv, ndev, - true, false); - } - brcmf_set_mpc(ndev, 1); + aborted = status != BRCMF_E_STATUS_SUCCESS; + brcmf_notify_escan_complete(cfg_priv, ndev, aborted, + false); } else WL_ERR("Unexpected scan result 0x%x\n", status); } @@ -3039,18 +3071,10 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, brcmf_delay(500); } - set_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status); if (test_bit(WL_STATUS_READY, &cfg_priv->status)) - brcmf_term_iscan(cfg_priv); - - if (cfg_priv->scan_request) { - /* Indidate scan abort to cfg80211 layer */ - WL_INFO("Terminating scan in progress\n"); - cfg80211_scan_done(cfg_priv->scan_request, true); - cfg_priv->scan_request = NULL; - } - clear_bit(WL_STATUS_SCANNING, &cfg_priv->status); - clear_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status); + brcmf_abort_scanning(cfg_priv); + else + clear_bit(WL_STATUS_SCANNING, &cfg_priv->status); /* Turn off watchdog timer */ if (test_bit(WL_STATUS_READY, &cfg_priv->status)) @@ -3229,6 +3253,269 @@ brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) } +/* + * PFN result doesn't have all the info which are + * required by the supplicant + * (For e.g IEs) Do a target Escan so that sched scan results are reported + * via wl_inform_single_bss in the required format. Escan does require the + * scan request in the form of cfg80211_scan_request. For timebeing, create + * cfg80211_scan_request one out of the received PNO event. + */ +static s32 +brcmf_notify_sched_scan_results(struct brcmf_cfg80211_priv *cfg_priv, + struct net_device *ndev, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_pno_net_info_le *netinfo, *netinfo_start; + struct cfg80211_scan_request *request = NULL; + struct cfg80211_ssid *ssid = NULL; + struct ieee80211_channel *channel = NULL; + struct wiphy *wiphy = cfg_to_wiphy(cfg_priv); + int err = 0; + int channel_req = 0; + int band = 0; + struct brcmf_pno_scanresults_le *pfn_result; + u32 result_count; + u32 status; + + WL_SCAN("Enter\n"); + + if (e->event_type == cpu_to_be32(BRCMF_E_PFN_NET_LOST)) { + WL_SCAN("PFN NET LOST event. Do Nothing\n"); + return 0; + } + + pfn_result = (struct brcmf_pno_scanresults_le *)data; + result_count = le32_to_cpu(pfn_result->count); + status = le32_to_cpu(pfn_result->status); + + /* + * PFN event is limited to fit 512 bytes so we may get + * multiple NET_FOUND events. For now place a warning here. + */ + WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE); + WL_SCAN("PFN NET FOUND event. count: %d\n", result_count); + if (result_count > 0) { + int i; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + ssid = kzalloc(sizeof(*ssid) * result_count, GFP_KERNEL); + channel = kzalloc(sizeof(*channel) * result_count, GFP_KERNEL); + if (!request || !ssid || !channel) { + err = -ENOMEM; + goto out_err; + } + + request->wiphy = wiphy; + data += sizeof(struct brcmf_pno_scanresults_le); + netinfo_start = (struct brcmf_pno_net_info_le *)data; + + for (i = 0; i < result_count; i++) { + netinfo = &netinfo_start[i]; + if (!netinfo) { + WL_ERR("Invalid netinfo ptr. index: %d\n", i); + err = -EINVAL; + goto out_err; + } + + WL_SCAN("SSID:%s Channel:%d\n", + netinfo->SSID, netinfo->channel); + memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len); + ssid[i].ssid_len = netinfo->SSID_len; + request->n_ssids++; + + channel_req = netinfo->channel; + if (channel_req <= CH_MAX_2G_CHANNEL) + band = NL80211_BAND_2GHZ; + else + band = NL80211_BAND_5GHZ; + channel[i].center_freq = + ieee80211_channel_to_frequency(channel_req, + band); + channel[i].band = band; + channel[i].flags |= IEEE80211_CHAN_NO_HT40; + request->channels[i] = &channel[i]; + request->n_channels++; + } + + /* assign parsed ssid array */ + if (request->n_ssids) + request->ssids = &ssid[0]; + + if (test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) { + /* Abort any on-going scan */ + brcmf_abort_scanning(cfg_priv); + } + + set_bit(WL_STATUS_SCANNING, &cfg_priv->status); + err = brcmf_do_escan(cfg_priv, wiphy, ndev, request); + if (err) { + clear_bit(WL_STATUS_SCANNING, &cfg_priv->status); + goto out_err; + } + cfg_priv->sched_escan = true; + cfg_priv->scan_request = request; + } else { + WL_ERR("FALSE PNO Event. (pfn_count == 0)\n"); + goto out_err; + } + + kfree(ssid); + kfree(channel); + kfree(request); + return 0; + +out_err: + kfree(ssid); + kfree(channel); + kfree(request); + cfg80211_sched_scan_stopped(wiphy); + return err; +} + +#ifndef CONFIG_BRCMISCAN +static int brcmf_dev_pno_clean(struct net_device *ndev) +{ + char iovbuf[128]; + int ret; + + /* Disable pfn */ + ret = brcmf_dev_intvar_set(ndev, "pfn", 0); + if (ret == 0) { + /* clear pfn */ + ret = brcmf_dev_iovar_setbuf(ndev, "pfnclear", NULL, 0, + iovbuf, sizeof(iovbuf)); + } + if (ret < 0) + WL_ERR("failed code %d\n", ret); + + return ret; +} + +static int brcmf_dev_pno_config(struct net_device *ndev) +{ + struct brcmf_pno_param_le pfn_param; + char iovbuf[128]; + + memset(&pfn_param, 0, sizeof(pfn_param)); + pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); + + /* set extra pno params */ + pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); + pfn_param.repeat = BRCMF_PNO_REPEAT; + pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; + + /* set up pno scan fr */ + pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); + + return brcmf_dev_iovar_setbuf(ndev, "pfn_set", + &pfn_param, sizeof(pfn_param), + iovbuf, sizeof(iovbuf)); +} + +static int +brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_sched_scan_request *request) +{ + char iovbuf[128]; + struct brcmf_cfg80211_priv *cfg_priv = wiphy_priv(wiphy); + struct brcmf_pno_net_param_le pfn; + int i; + int ret = 0; + + WL_SCAN("Enter n_match_sets:%d n_ssids:%d\n", + request->n_match_sets, request->n_ssids); + if (test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) { + WL_ERR("Scanning already : status (%lu)\n", cfg_priv->status); + return -EAGAIN; + } + + if (!request || !request->n_ssids || !request->n_match_sets) { + WL_ERR("Invalid sched scan req!! n_ssids:%d\n", + request->n_ssids); + return -EINVAL; + } + + if (request->n_ssids > 0) { + for (i = 0; i < request->n_ssids; i++) { + /* Active scan req for ssids */ + WL_SCAN(">>> Active scan req for ssid (%s)\n", + request->ssids[i].ssid); + + /* + * match_set ssids is a supert set of n_ssid list, + * so we need not add these set seperately. + */ + } + } + + if (request->n_match_sets > 0) { + /* clean up everything */ + ret = brcmf_dev_pno_clean(ndev); + if (ret < 0) { + WL_ERR("failed error=%d\n", ret); + return ret; + } + + /* configure pno */ + ret = brcmf_dev_pno_config(ndev); + if (ret < 0) { + WL_ERR("PNO setup failed!! ret=%d\n", ret); + return -EINVAL; + } + + /* configure each match set */ + for (i = 0; i < request->n_match_sets; i++) { + struct cfg80211_ssid *ssid; + u32 ssid_len; + + ssid = &request->match_sets[i].ssid; + ssid_len = ssid->ssid_len; + + if (!ssid_len) { + WL_ERR("skip broadcast ssid\n"); + continue; + } + pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); + pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); + pfn.wsec = cpu_to_le32(0); + pfn.infra = cpu_to_le32(1); + pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); + pfn.ssid.SSID_len = cpu_to_le32(ssid_len); + memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len); + ret = brcmf_dev_iovar_setbuf(ndev, "pfn_add", + &pfn, sizeof(pfn), + iovbuf, sizeof(iovbuf)); + WL_SCAN(">>> PNO filter %s for ssid (%s)\n", + ret == 0 ? "set" : "failed", + ssid->ssid); + } + /* Enable the PNO */ + if (brcmf_dev_intvar_set(ndev, "pfn", 1) < 0) { + WL_ERR("PNO enable failed!! ret=%d\n", ret); + return -EINVAL; + } + } else { + return -EINVAL; + } + + return 0; +} + +static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *ndev) +{ + struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy); + + WL_SCAN("enter\n"); + brcmf_dev_pno_clean(ndev); + if (cfg_priv->sched_escan) + brcmf_notify_escan_complete(cfg_priv, ndev, true, true); + return 0; +} +#endif /* CONFIG_BRCMISCAN */ + #ifdef CONFIG_NL80211_TESTMODE static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len) { @@ -3271,6 +3558,11 @@ static struct cfg80211_ops wl_cfg80211_ops = { .set_pmksa = brcmf_cfg80211_set_pmksa, .del_pmksa = brcmf_cfg80211_del_pmksa, .flush_pmksa = brcmf_cfg80211_flush_pmksa, +#ifndef CONFIG_BRCMISCAN + /* scheduled scan need e-scan, which is mutual exclusive with i-scan */ + .sched_scan_start = brcmf_cfg80211_sched_scan_start, + .sched_scan_stop = brcmf_cfg80211_sched_scan_stop, +#endif #ifdef CONFIG_NL80211_TESTMODE .testmode_cmd = brcmf_cfg80211_testmode #endif @@ -3292,6 +3584,17 @@ static s32 brcmf_mode_to_nl80211_iftype(s32 mode) return err; } +static void brcmf_wiphy_pno_params(struct wiphy *wiphy) +{ +#ifndef CONFIG_BRCMFISCAN + /* scheduled scan settings */ + wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; + wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; + wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; + wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +#endif +} + static struct wireless_dev *brcmf_alloc_wdev(s32 sizeof_iface, struct device *ndev) { @@ -3330,6 +3633,7 @@ static struct wireless_dev *brcmf_alloc_wdev(s32 sizeof_iface, * save mode * by default */ + brcmf_wiphy_pno_params(wdev->wiphy); err = wiphy_register(wdev->wiphy); if (err < 0) { WL_ERR("Could not register wiphy device (%d)\n", err); @@ -3732,6 +4036,7 @@ static void brcmf_init_eloop_handler(struct brcmf_cfg80211_event_loop *el) el->handler[BRCMF_E_ROAM] = brcmf_notify_roaming_status; el->handler[BRCMF_E_MIC_ERROR] = brcmf_notify_mic_status; el->handler[BRCMF_E_SET_SSID] = brcmf_notify_connect_status; + el->handler[BRCMF_E_PFN_NET_FOUND] = brcmf_notify_sched_scan_results; } static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_priv *cfg_priv) @@ -3957,13 +4262,13 @@ static void wl_deinit_priv(struct brcmf_cfg80211_priv *cfg_priv) cfg_priv->dongle_up = false; /* dongle down */ brcmf_flush_eq(cfg_priv); brcmf_link_down(cfg_priv); - brcmf_term_iscan(cfg_priv); + brcmf_abort_scanning(cfg_priv); brcmf_deinit_priv_mem(cfg_priv); } struct brcmf_cfg80211_dev *brcmf_cfg80211_attach(struct net_device *ndev, struct device *busdev, - void *data) + struct brcmf_pub *drvr) { struct wireless_dev *wdev; struct brcmf_cfg80211_priv *cfg_priv; @@ -3988,7 +4293,7 @@ struct brcmf_cfg80211_dev *brcmf_cfg80211_attach(struct net_device *ndev, wdev->iftype = brcmf_mode_to_nl80211_iftype(WL_MODE_BSS); cfg_priv = wdev_to_cfg(wdev); cfg_priv->wdev = wdev; - cfg_priv->pub = data; + cfg_priv->pub = drvr; ci = (struct brcmf_cfg80211_iface *)&cfg_priv->ci; ci->cfg_priv = cfg_priv; ndev->ieee80211_ptr = wdev; @@ -4103,6 +4408,7 @@ static s32 brcmf_dongle_eventmsg(struct net_device *ndev) setbit(eventmask, BRCMF_E_JOIN_START); setbit(eventmask, BRCMF_E_SCAN_COMPLETE); setbit(eventmask, BRCMF_E_ESCAN_RESULT); + setbit(eventmask, BRCMF_E_PFN_NET_FOUND); brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); @@ -4235,7 +4541,7 @@ static s32 wl_update_wiphybands(struct brcmf_cfg80211_priv *cfg_priv) return err; } - phy = ((char *)&phy_list)[1]; + phy = ((char *)&phy_list)[0]; WL_INFO("%c phy\n", phy); if (phy == 'n' || phy == 'a') { wiphy = cfg_to_wiphy(cfg_priv); @@ -4368,17 +4674,8 @@ static s32 __brcmf_cfg80211_down(struct brcmf_cfg80211_priv *cfg_priv) brcmf_delay(500); } - set_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status); - brcmf_term_iscan(cfg_priv); - if (cfg_priv->scan_request) { - cfg80211_scan_done(cfg_priv->scan_request, true); - /* May need to perform this to cover rmmod */ - /* wl_set_mpc(cfg_to_ndev(wl), 1); */ - cfg_priv->scan_request = NULL; - } + brcmf_abort_scanning(cfg_priv); clear_bit(WL_STATUS_READY, &cfg_priv->status); - clear_bit(WL_STATUS_SCANNING, &cfg_priv->status); - clear_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status); brcmf_debugfs_remove_netdev(cfg_priv); @@ -4411,20 +4708,3 @@ s32 brcmf_cfg80211_down(struct brcmf_cfg80211_dev *cfg_dev) return err; } -static __used s32 brcmf_add_ie(struct brcmf_cfg80211_priv *cfg_priv, - u8 t, u8 l, u8 *v) -{ - struct brcmf_cfg80211_ie *ie = &cfg_priv->ie; - s32 err = 0; - - if (ie->offset + l + 2 > WL_TLV_INFO_MAX) { - WL_ERR("ei crosses buffer boundary\n"); - return -ENOSPC; - } - ie->buf[ie->offset] = t; - ie->buf[ie->offset + 1] = l; - memcpy(&ie->buf[ie->offset + 2], v, l); - ie->offset += l + 2; - - return err; -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index 3b2129738d30..52e408ed6f41 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -295,50 +295,168 @@ struct escan_info { struct net_device *ndev; }; -/* dongle private data of cfg80211 interface */ +/** + * struct brcmf_pno_param_le - PNO scan configuration parameters + * + * @version: PNO parameters version. + * @scan_freq: scan frequency. + * @lost_network_timeout: #sec. to declare discovered network as lost. + * @flags: Bit field to control features of PFN such as sort criteria auto + * enable switch and background scan. + * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort + * criteria. + * @bestn: number of best networks in each scan. + * @mscan: number of scans recorded. + * @repeat: minimum number of scan intervals before scan frequency changes + * in adaptive scan. + * @exp: exponent of 2 for maximum scan interval. + * @slow_freq: slow scan period. + */ +struct brcmf_pno_param_le { + __le32 version; + __le32 scan_freq; + __le32 lost_network_timeout; + __le16 flags; + __le16 rssi_margin; + u8 bestn; + u8 mscan; + u8 repeat; + u8 exp; + __le32 slow_freq; +}; + +/** + * struct brcmf_pno_net_param_le - scan parameters per preferred network. + * + * @ssid: ssid name and its length. + * @flags: bit2: hidden. + * @infra: BSS vs IBSS. + * @auth: Open vs Closed. + * @wpa_auth: WPA type. + * @wsec: wsec value. + */ +struct brcmf_pno_net_param_le { + struct brcmf_ssid_le ssid; + __le32 flags; + __le32 infra; + __le32 auth; + __le32 wpa_auth; + __le32 wsec; +}; + +/** + * struct brcmf_pno_net_info_le - information per found network. + * + * @bssid: BSS network identifier. + * @channel: channel number only. + * @SSID_len: length of ssid. + * @SSID: ssid characters. + * @RSSI: receive signal strength (in dBm). + * @timestamp: age in seconds. + */ +struct brcmf_pno_net_info_le { + u8 bssid[ETH_ALEN]; + u8 channel; + u8 SSID_len; + u8 SSID[32]; + __le16 RSSI; + __le16 timestamp; +}; + +/** + * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event. + * + * @version: PNO version identifier. + * @status: indicates completion status of PNO scan. + * @count: amount of brcmf_pno_net_info_le entries appended. + */ +struct brcmf_pno_scanresults_le { + __le32 version; + __le32 status; + __le32 count; +}; + +/** + * struct brcmf_cfg80211_priv - dongle private data of cfg80211 interface + * + * @wdev: representing wl cfg80211 device. + * @conf: dongle configuration. + * @scan_request: cfg80211 scan request object. + * @el: main event loop. + * @evt_q_list: used for event queue. + * @evt_q_lock: for event queue synchronization. + * @usr_sync: mainly for dongle up/down synchronization. + * @bss_list: bss_list holding scanned ap information. + * @scan_results: results of the last scan. + * @scan_req_int: internal scan request object. + * @bss_info: bss information for cfg80211 layer. + * @ie: information element object for internal purpose. + * @profile: holding dongle profile. + * @iscan: iscan controller information. + * @conn_info: association info. + * @pmk_list: wpa2 pmk list. + * @event_work: event handler work struct. + * @status: current dongle status. + * @pub: common driver information. + * @channel: current channel. + * @iscan_on: iscan on/off switch. + * @iscan_kickstart: indicate iscan already started. + * @active_scan: current scan mode. + * @sched_escan: e-scan for scheduled scan support running. + * @ibss_starter: indicates this sta is ibss starter. + * @link_up: link/connection up flag. + * @pwr_save: indicate whether dongle to support power save mode. + * @dongle_up: indicate whether dongle up or not. + * @roam_on: on/off switch for dongle self-roaming. + * @scan_tried: indicates if first scan attempted. + * @dcmd_buf: dcmd buffer. + * @extra_buf: mainly to grab assoc information. + * @debugfsdir: debugfs folder for this device. + * @escan_on: escan on/off switch. + * @escan_info: escan information. + * @escan_timeout: Timer for catch scan timeout. + * @escan_timeout_work: scan timeout worker. + * @escan_ioctl_buf: dongle command buffer for escan commands. + * @ci: used to link this structure to netdev private data. + */ struct brcmf_cfg80211_priv { - struct wireless_dev *wdev; /* representing wl cfg80211 device */ - struct brcmf_cfg80211_conf *conf; /* dongle configuration */ - struct cfg80211_scan_request *scan_request; /* scan request - object */ - struct brcmf_cfg80211_event_loop el; /* main event loop */ - struct list_head evt_q_list; /* used for event queue */ - spinlock_t evt_q_lock; /* for event queue synchronization */ - struct mutex usr_sync; /* maily for dongle up/down synchronization */ - struct brcmf_scan_results *bss_list; /* bss_list holding scanned - ap information */ + struct wireless_dev *wdev; + struct brcmf_cfg80211_conf *conf; + struct cfg80211_scan_request *scan_request; + struct brcmf_cfg80211_event_loop el; + struct list_head evt_q_list; + spinlock_t evt_q_lock; + struct mutex usr_sync; + struct brcmf_scan_results *bss_list; struct brcmf_scan_results *scan_results; - struct brcmf_cfg80211_scan_req *scan_req_int; /* scan request object - for internal purpose */ - struct wl_cfg80211_bss_info *bss_info; /* bss information for - cfg80211 layer */ - struct brcmf_cfg80211_ie ie; /* information element object for - internal purpose */ - struct brcmf_cfg80211_profile *profile; /* holding dongle profile */ - struct brcmf_cfg80211_iscan_ctrl *iscan; /* iscan controller */ - struct brcmf_cfg80211_connect_info conn_info; /* association info */ - struct brcmf_cfg80211_pmk_list *pmk_list; /* wpa2 pmk list */ - struct work_struct event_work; /* event handler work struct */ - unsigned long status; /* current dongle status */ - void *pub; - u32 channel; /* current channel */ - bool iscan_on; /* iscan on/off switch */ - bool iscan_kickstart; /* indicate iscan already started */ - bool active_scan; /* current scan mode */ - bool ibss_starter; /* indicates this sta is ibss starter */ - bool link_up; /* link/connection up flag */ - bool pwr_save; /* indicate whether dongle to support - power save mode */ - bool dongle_up; /* indicate whether dongle up or not */ - bool roam_on; /* on/off switch for dongle self-roaming */ - bool scan_tried; /* indicates if first scan attempted */ - u8 *dcmd_buf; /* dcmd buffer */ - u8 *extra_buf; /* maily to grab assoc information */ + struct brcmf_cfg80211_scan_req *scan_req_int; + struct wl_cfg80211_bss_info *bss_info; + struct brcmf_cfg80211_ie ie; + struct brcmf_cfg80211_profile *profile; + struct brcmf_cfg80211_iscan_ctrl *iscan; + struct brcmf_cfg80211_connect_info conn_info; + struct brcmf_cfg80211_pmk_list *pmk_list; + struct work_struct event_work; + unsigned long status; + struct brcmf_pub *pub; + u32 channel; + bool iscan_on; + bool iscan_kickstart; + bool active_scan; + bool sched_escan; + bool ibss_starter; + bool link_up; + bool pwr_save; + bool dongle_up; + bool roam_on; + bool scan_tried; + u8 *dcmd_buf; + u8 *extra_buf; struct dentry *debugfsdir; - bool escan_on; /* escan on/off switch */ - struct escan_info escan_info; /* escan information */ - struct timer_list escan_timeout; /* Timer for catch scan timeout */ - struct work_struct escan_timeout_work; /* scan timeout worker */ + bool escan_on; + struct escan_info escan_info; + struct timer_list escan_timeout; + struct work_struct escan_timeout_work; u8 *escan_ioctl_buf; u8 ci[0] __aligned(NETDEV_ALIGN); }; @@ -379,7 +497,7 @@ brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_priv *cfg) extern struct brcmf_cfg80211_dev *brcmf_cfg80211_attach(struct net_device *ndev, struct device *busdev, - void *data); + struct brcmf_pub *drvr); extern void brcmf_cfg80211_detach(struct brcmf_cfg80211_dev *cfg); /* event handler from dongle */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 718da8d6d658..a744ea5a9559 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -304,7 +304,10 @@ static int brcms_ops_start(struct ieee80211_hw *hw) wl->mute_tx = true; if (!wl->pub->up) - err = brcms_up(wl); + if (!blocked) + err = brcms_up(wl); + else + err = -ERFKILL; else err = -ENODEV; spin_unlock_bh(&wl->lock); diff --git a/drivers/net/wireless/ipw2x00/libipw_wx.c b/drivers/net/wireless/ipw2x00/libipw_wx.c index 1571505b1a38..54aba4744438 100644 --- a/drivers/net/wireless/ipw2x00/libipw_wx.c +++ b/drivers/net/wireless/ipw2x00/libipw_wx.c @@ -675,7 +675,7 @@ int libipw_wx_set_encodeext(struct libipw_device *ieee, } done: if (ieee->set_security) - ieee->set_security(ieee->dev, &sec); + ieee->set_security(dev, &sec); return ret; } diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index eb9987520d61..318ed3c9fe74 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -1586,9 +1586,9 @@ il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame, return 0; frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); - memcpy(frame->da, il_bcast_addr, ETH_ALEN); + eth_broadcast_addr(frame->da); memcpy(frame->sa, ta, ETH_ALEN); - memcpy(frame->bssid, il_bcast_addr, ETH_ALEN); + eth_broadcast_addr(frame->bssid); frame->seq_ctrl = 0; len += 24; diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c index e3467fa86899..bb9f6252d28f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -612,9 +612,9 @@ static u16 iwl_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, return 0; frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); - memcpy(frame->da, iwl_bcast_addr, ETH_ALEN); + eth_broadcast_addr(frame->da); memcpy(frame->sa, ta, ETH_ALEN); - memcpy(frame->bssid, iwl_bcast_addr, ETH_ALEN); + eth_broadcast_addr(frame->bssid); frame->seq_ctrl = 0; len += 24; diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c index fe36a38f3505..cd9b6de4273e 100644 --- a/drivers/net/wireless/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/iwlwifi/dvm/sta.c @@ -128,10 +128,11 @@ int iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_addsta_cmd *addsta = - (struct iwl_addsta_cmd *) cmd->payload; - return iwl_process_add_sta_resp(priv, addsta, pkt); + if (!cmd) + return 0; + + return iwl_process_add_sta_resp(priv, (void *)cmd->payload, pkt); } int iwl_send_add_sta(struct iwl_priv *priv, diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 6d8d6dd7943f..2cb1efbc5ed1 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -295,7 +295,7 @@ static int iwl_alive_notify(struct iwl_priv *priv) static int iwl_verify_sec_sparse(struct iwl_priv *priv, const struct fw_desc *fw_desc) { - __le32 *image = (__le32 *)fw_desc->v_addr; + __le32 *image = (__le32 *)fw_desc->data; u32 len = fw_desc->len; u32 val; u32 i; @@ -319,7 +319,7 @@ static int iwl_verify_sec_sparse(struct iwl_priv *priv, static void iwl_print_mismatch_sec(struct iwl_priv *priv, const struct fw_desc *fw_desc) { - __le32 *image = (__le32 *)fw_desc->v_addr; + __le32 *image = (__le32 *)fw_desc->data; u32 len = fw_desc->len; u32 val; u32 offs; diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 48d6d44c16d0..198634b75ed0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -64,6 +64,7 @@ #include <linux/dma-mapping.h> #include <linux/firmware.h> #include <linux/module.h> +#include <linux/vmalloc.h> #include "iwl-drv.h" #include "iwl-debug.h" @@ -164,10 +165,8 @@ struct fw_sec { static void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc) { - if (desc->v_addr) - dma_free_coherent(drv->trans->dev, desc->len, - desc->v_addr, desc->p_addr); - desc->v_addr = NULL; + vfree(desc->data); + desc->data = NULL; desc->len = 0; } @@ -186,21 +185,24 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv) } static int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc, - struct fw_sec *sec) + struct fw_sec *sec) { - if (!sec || !sec->size) { - desc->v_addr = NULL; + void *data; + + desc->data = NULL; + + if (!sec || !sec->size) return -EINVAL; - } - desc->v_addr = dma_alloc_coherent(drv->trans->dev, sec->size, - &desc->p_addr, GFP_KERNEL); - if (!desc->v_addr) + data = vmalloc(sec->size); + if (!data) return -ENOMEM; desc->len = sec->size; desc->offset = sec->offset; - memcpy(desc->v_addr, sec->data, sec->size); + memcpy(data, sec->data, desc->len); + desc->data = data; + return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 2153e4cc5572..d1a86b66bc51 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -124,8 +124,7 @@ struct iwl_ucode_capabilities { /* one for each uCode image (inst/data, init/runtime/wowlan) */ struct fw_desc { - dma_addr_t p_addr; /* hardware address */ - void *v_addr; /* software address */ + const void *data; /* vmalloc'ed data */ u32 len; /* size in bytes */ u32 offset; /* offset in the device */ }; diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 89bfb43f4946..2a4675396707 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -263,8 +263,6 @@ MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); /* PCI registers */ #define PCI_CFG_RETRY_TIMEOUT 0x041 -#ifndef CONFIG_IWLWIFI_IDI - static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { const struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data); @@ -307,8 +305,6 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); } -#endif /* CONFIG_IWLWIFI_IDI */ - #ifdef CONFIG_PM_SLEEP static int iwl_pci_suspend(struct device *device) @@ -353,15 +349,6 @@ static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume); #endif -#ifdef CONFIG_IWLWIFI_IDI -/* - * Defined externally in iwl-idi.c - */ -int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); -void __devexit iwl_pci_remove(struct pci_dev *pdev); - -#endif /* CONFIG_IWLWIFI_IDI */ - static struct pci_driver iwl_pci_driver = { .name = DRV_NAME, .id_table = iwl_hw_card_ids, diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 71c79943e633..401178f44a3b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -311,7 +311,7 @@ void iwl_trans_pcie_free(struct iwl_trans *trans); ******************************************************/ void iwl_bg_rx_replenish(struct work_struct *data); void iwl_irq_tasklet(struct iwl_trans *trans); -void iwlagn_rx_replenish(struct iwl_trans *trans); +void iwl_rx_replenish(struct iwl_trans *trans); void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, struct iwl_rx_queue *q); diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 498372008810..17c8e5d82681 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -35,10 +35,6 @@ #include "internal.h" #include "iwl-op-mode.h" -#ifdef CONFIG_IWLWIFI_IDI -#include "iwl-amfh.h" -#endif - /****************************************************************************** * * RX path functions @@ -181,15 +177,15 @@ void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, } /** - * iwlagn_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr + * iwl_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr */ -static inline __le32 iwlagn_dma_addr2rbd_ptr(dma_addr_t dma_addr) +static inline __le32 iwl_dma_addr2rbd_ptr(dma_addr_t dma_addr) { return cpu_to_le32((u32)(dma_addr >> 8)); } /** - * iwlagn_rx_queue_restock - refill RX queue from pre-allocated pool + * iwl_rx_queue_restock - refill RX queue from pre-allocated pool * * If there are slots in the RX queue that need to be restocked, * and we have free pre-allocated buffers, fill the ranks as much @@ -199,7 +195,7 @@ static inline __le32 iwlagn_dma_addr2rbd_ptr(dma_addr_t dma_addr) * also updates the memory address in the firmware to reference the new * target buffer. */ -static void iwlagn_rx_queue_restock(struct iwl_trans *trans) +static void iwl_rx_queue_restock(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; @@ -207,6 +203,17 @@ static void iwlagn_rx_queue_restock(struct iwl_trans *trans) struct iwl_rx_mem_buffer *rxb; unsigned long flags; + /* + * If the device isn't enabled - not need to try to add buffers... + * This can happen when we stop the device and still have an interrupt + * pending. We stop the APM before we sync the interrupts / tasklets + * because we have to (see comment there). On the other hand, since + * the APM is stopped, we cannot access the HW (in particular not prph). + * So don't try to restock if the APM has been already stopped. + */ + if (!test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) + return; + spin_lock_irqsave(&rxq->lock, flags); while ((iwl_rx_queue_space(rxq) > 0) && (rxq->free_count)) { /* The overwritten rxb must be a used one */ @@ -219,7 +226,7 @@ static void iwlagn_rx_queue_restock(struct iwl_trans *trans) list_del(element); /* Point to Rx buffer via next RBD in circular buffer */ - rxq->bd[rxq->write] = iwlagn_dma_addr2rbd_ptr(rxb->page_dma); + rxq->bd[rxq->write] = iwl_dma_addr2rbd_ptr(rxb->page_dma); rxq->queue[rxq->write] = rxb; rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; rxq->free_count--; @@ -230,7 +237,6 @@ static void iwlagn_rx_queue_restock(struct iwl_trans *trans) if (rxq->free_count <= RX_LOW_WATERMARK) schedule_work(&trans_pcie->rx_replenish); - /* If we've added more space for the firmware to place data, tell it. * Increment device's write pointer in multiples of 8. */ if (rxq->write_actual != (rxq->write & ~0x7)) { @@ -241,15 +247,16 @@ static void iwlagn_rx_queue_restock(struct iwl_trans *trans) } } -/** - * iwlagn_rx_replenish - Move all used packet from rx_used to rx_free - * - * When moving to rx_free an SKB is allocated for the slot. +/* + * iwl_rx_allocate - allocate a page for each used RBD * - * Also restock the Rx queue via iwl_rx_queue_restock. - * This is called as a scheduled work item (except for during initialization) + * A used RBD is an Rx buffer that has been given to the stack. To use it again + * a page must be allocated and the RBD must point to the page. This function + * doesn't change the HW pointer but handles the list of pages that is used by + * iwl_rx_queue_restock. The latter function will update the HW to use the newly + * allocated buffers. */ -static void iwlagn_rx_allocate(struct iwl_trans *trans, gfp_t priority) +static void iwl_rx_allocate(struct iwl_trans *trans, gfp_t priority) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; @@ -328,23 +335,31 @@ static void iwlagn_rx_allocate(struct iwl_trans *trans, gfp_t priority) } } -void iwlagn_rx_replenish(struct iwl_trans *trans) +/* + * iwl_rx_replenish - Move all used buffers from rx_used to rx_free + * + * When moving to rx_free an page is allocated for the slot. + * + * Also restock the Rx queue via iwl_rx_queue_restock. + * This is called as a scheduled work item (except for during initialization) + */ +void iwl_rx_replenish(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); unsigned long flags; - iwlagn_rx_allocate(trans, GFP_KERNEL); + iwl_rx_allocate(trans, GFP_KERNEL); spin_lock_irqsave(&trans_pcie->irq_lock, flags); - iwlagn_rx_queue_restock(trans); + iwl_rx_queue_restock(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); } -static void iwlagn_rx_replenish_now(struct iwl_trans *trans) +static void iwl_rx_replenish_now(struct iwl_trans *trans) { - iwlagn_rx_allocate(trans, GFP_ATOMIC); + iwl_rx_allocate(trans, GFP_ATOMIC); - iwlagn_rx_queue_restock(trans); + iwl_rx_queue_restock(trans); } void iwl_bg_rx_replenish(struct work_struct *data) @@ -352,7 +367,7 @@ void iwl_bg_rx_replenish(struct work_struct *data) struct iwl_trans_pcie *trans_pcie = container_of(data, struct iwl_trans_pcie, rx_replenish); - iwlagn_rx_replenish(trans_pcie->trans); + iwl_rx_replenish(trans_pcie->trans); } static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, @@ -530,7 +545,7 @@ static void iwl_rx_handle(struct iwl_trans *trans) count++; if (count >= 8) { rxq->read = i; - iwlagn_rx_replenish_now(trans); + iwl_rx_replenish_now(trans); count = 0; } } @@ -539,9 +554,9 @@ static void iwl_rx_handle(struct iwl_trans *trans) /* Backtrack one entry */ rxq->read = i; if (fill_rx) - iwlagn_rx_replenish_now(trans); + iwl_rx_replenish_now(trans); else - iwlagn_rx_queue_restock(trans); + iwl_rx_queue_restock(trans); } /** @@ -723,11 +738,9 @@ void iwl_irq_tasklet(struct iwl_trans *trans) /* Disable periodic interrupt; we use it as just a one-shot. */ iwl_write8(trans, CSR_INT_PERIODIC_REG, CSR_INT_PERIODIC_DIS); -#ifdef CONFIG_IWLWIFI_IDI - iwl_amfh_rx_handler(); -#else + iwl_rx_handle(trans); -#endif + /* * Enable periodic interrupt in 8 msec only if we received * real RX interrupt (instead of just periodic int), to catch diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 76aa5031f7ba..34aee0688e0d 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -216,7 +216,7 @@ static int iwl_rx_init(struct iwl_trans *trans) rxq->free_count = 0; spin_unlock_irqrestore(&rxq->lock, flags); - iwlagn_rx_replenish(trans); + iwl_rx_replenish(trans); iwl_trans_rx_hw_init(trans, rxq); @@ -855,10 +855,8 @@ static int iwl_nic_init(struct iwl_trans *trans) iwl_op_mode_nic_config(trans->op_mode); -#ifndef CONFIG_IWLWIFI_IDI /* Allocate the RX queue, or reset if it is already allocated */ iwl_rx_init(trans); -#endif /* Allocate or reset and init all Tx and Command queues */ if (iwl_tx_init(trans)) @@ -925,13 +923,10 @@ static int iwl_prepare_card_hw(struct iwl_trans *trans) /* * ucode */ -static int iwl_load_section(struct iwl_trans *trans, u8 section_num, - const struct fw_desc *section) +static int iwl_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr, + dma_addr_t phy_addr, u32 byte_cnt) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - dma_addr_t phy_addr = section->p_addr; - u32 byte_cnt = section->len; - u32 dst_addr = section->offset; int ret; trans_pcie->ucode_write_complete = false; @@ -945,8 +940,8 @@ static int iwl_load_section(struct iwl_trans *trans, u8 section_num, dst_addr); iwl_write_direct32(trans, - FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), - phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); + FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), + phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); iwl_write_direct32(trans, FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), @@ -965,33 +960,64 @@ static int iwl_load_section(struct iwl_trans *trans, u8 section_num, FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); - IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", - section_num); ret = wait_event_timeout(trans_pcie->ucode_write_waitq, trans_pcie->ucode_write_complete, 5 * HZ); if (!ret) { - IWL_ERR(trans, "Could not load the [%d] uCode section\n", - section_num); + IWL_ERR(trans, "Failed to load firmware chunk!\n"); return -ETIMEDOUT; } return 0; } -static int iwl_load_given_ucode(struct iwl_trans *trans, - const struct fw_img *image) +static int iwl_load_section(struct iwl_trans *trans, u8 section_num, + const struct fw_desc *section) { + u8 *v_addr; + dma_addr_t p_addr; + u32 offset; int ret = 0; - int i; - for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) { - if (!image->sec[i].p_addr) - break; + IWL_DEBUG_FW(trans, "[%d] uCode section being loaded...\n", + section_num); - ret = iwl_load_section(trans, i, &image->sec[i]); - if (ret) - return ret; + v_addr = dma_alloc_coherent(trans->dev, PAGE_SIZE, &p_addr, GFP_KERNEL); + if (!v_addr) + return -ENOMEM; + + for (offset = 0; offset < section->len; offset += PAGE_SIZE) { + u32 copy_size; + + copy_size = min_t(u32, PAGE_SIZE, section->len - offset); + + memcpy(v_addr, (u8 *)section->data + offset, copy_size); + ret = iwl_load_firmware_chunk(trans, section->offset + offset, + p_addr, copy_size); + if (ret) { + IWL_ERR(trans, + "Could not load the [%d] uCode section\n", + section_num); + break; } + } + + dma_free_coherent(trans->dev, PAGE_SIZE, v_addr, p_addr); + return ret; +} + +static int iwl_load_given_ucode(struct iwl_trans *trans, + const struct fw_img *image) +{ + int i, ret = 0; + + for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) { + if (!image->sec[i].data) + break; + + ret = iwl_load_section(trans, i, &image->sec[i]); + if (ret) + return ret; + } /* Remove all resets to allow NIC to operate */ iwl_write32(trans, CSR_RESET, 0); @@ -1184,9 +1210,8 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) */ if (test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) { iwl_trans_tx_stop(trans); -#ifndef CONFIG_IWLWIFI_IDI iwl_trans_rx_stop(trans); -#endif + /* Power-down device's busmaster DMA clocks */ iwl_write_prph(trans, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); @@ -1457,14 +1482,16 @@ static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans, bool hw_rfkill; unsigned long flags; + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + iwl_disable_interrupts(trans); + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + iwl_apm_stop(trans); spin_lock_irqsave(&trans_pcie->irq_lock, flags); iwl_disable_interrupts(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - iwl_write32(trans, CSR_INT, 0xFFFFFFFF); - if (!op_mode_leaving) { /* * Even if we stop the HW, we still want the RF kill @@ -1552,9 +1579,8 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); iwl_trans_pcie_tx_free(trans); -#ifndef CONFIG_IWLWIFI_IDI iwl_trans_pcie_rx_free(trans); -#endif + if (trans_pcie->irq_requested == true) { free_irq(trans_pcie->irq, trans); iwl_free_isr_ict(trans); diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 9d45b3bb974c..429ca3215fdb 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -2056,7 +2056,7 @@ failed: mac80211_hwsim_free(); return err; } - +module_init(init_mac80211_hwsim); static void __exit exit_mac80211_hwsim(void) { @@ -2067,7 +2067,4 @@ static void __exit exit_mac80211_hwsim(void) mac80211_hwsim_free(); unregister_netdev(hwsim_mon); } - - -module_init(init_mac80211_hwsim); module_exit(exit_mac80211_hwsim); diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index b9f7b3e6912d..c24824f8c8a1 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1502,6 +1502,12 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, wiphy_dbg(wiphy, "info: received scan request on %s\n", dev->name); + if (atomic_read(&priv->wmm.tx_pkts_queued) >= + MWIFIEX_MIN_TX_PENDING_TO_CANCEL_SCAN) { + dev_dbg(priv->adapter->dev, "scan rejected due to traffic\n"); + return -EBUSY; + } + priv->scan_request = request; priv->user_scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), @@ -1630,7 +1636,7 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, * create a new virtual interface with the given name */ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, - char *name, + const char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params) diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c index 1d8dd003e396..fa3a80fb8c01 100644 --- a/drivers/net/wireless/mwifiex/ie.c +++ b/drivers/net/wireless/mwifiex/ie.c @@ -160,7 +160,7 @@ mwifiex_update_uap_custom_ie(struct mwifiex_private *priv, u16 len; int ret; - ap_custom_ie = kzalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); + ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL); if (!ap_custom_ie) return -ENOMEM; diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 9c1549ee4c09..b2ba262f8a13 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -118,6 +118,7 @@ static void scan_delay_timer_fn(unsigned long data) mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); + queue_work(adapter->workqueue, &adapter->main_work); goto done; } } else { diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 12ceea47b4b4..90b64b015447 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -91,6 +91,8 @@ enum { #define MWIFIEX_MAX_EMPTY_TX_Q_CNT 10 #define MWIFIEX_SCAN_DELAY_MSEC 20 +#define MWIFIEX_MIN_TX_PENDING_TO_CANCEL_SCAN 2 + #define RSN_GTK_OUI_OFFSET 2 #define MWIFIEX_OUI_NOT_PRESENT 0 @@ -1031,7 +1033,7 @@ int mwifiex_check_network_compatibility(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc); struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, - char *name, + const char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index d7ad2d4a069f..731562f026f5 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -26,6 +26,9 @@ #include "11n.h" #include "cfg80211.h" +static int disconnect_on_suspend = 1; +module_param(disconnect_on_suspend, int, 0644); + /* * Copies the multicast address list from device to driver. * @@ -448,6 +451,16 @@ EXPORT_SYMBOL_GPL(mwifiex_cancel_hs); int mwifiex_enable_hs(struct mwifiex_adapter *adapter) { struct mwifiex_ds_hs_cfg hscfg; + struct mwifiex_private *priv; + int i; + + if (disconnect_on_suspend) { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv) + mwifiex_deauthenticate(priv, NULL); + } + } if (adapter->hs_activated) { dev_dbg(adapter->dev, "cmd: HS Already actived\n"); diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 2969d5321ca6..aadda99989c0 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -515,6 +515,17 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, if (modparam_nohwcrypt) return -EOPNOTSUPP; + if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) { + /* + * Unfortunately most/all firmwares are trying to decrypt + * incoming management frames if a suitable key can be found. + * However, in doing so the data in these frames gets + * corrupted. So, we can't have firmware supported crypto + * offload in this case. + */ + return -EOPNOTSUPP; + } + mutex_lock(&priv->conf_mutex); if (cmd == SET_KEY) { switch (key->cipher) { @@ -738,6 +749,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | + IEEE80211_HW_MFP_CAPABLE | IEEE80211_HW_REPORTS_TX_ACK_STATUS; dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index 6458ab87717b..e3a2d9070cf6 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1789,7 +1789,6 @@ static const struct data_queue_desc rt2400pci_queue_atim = { static const struct rt2x00_ops rt2400pci_ops = { .name = KBUILD_MODNAME, - .max_sta_intf = 1, .max_ap_intf = 1, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index 68bca1456cda..479d756e275b 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -2081,7 +2081,6 @@ static const struct data_queue_desc rt2500pci_queue_atim = { static const struct rt2x00_ops rt2500pci_ops = { .name = KBUILD_MODNAME, - .max_sta_intf = 1, .max_ap_intf = 1, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index f95b5516c50a..a12e84f892be 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -1896,7 +1896,6 @@ static const struct data_queue_desc rt2500usb_queue_atim = { static const struct rt2x00_ops rt2500usb_ops = { .name = KBUILD_MODNAME, - .max_sta_intf = 1, .max_ap_intf = 1, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 9e09367c9739..540c94f8505a 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -1763,36 +1763,15 @@ static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev, rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0); + rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, + rt2x00dev->default_ant.rx_chain_num <= 1); + rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, + rt2x00dev->default_ant.rx_chain_num <= 2); rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0); - if (rt2x00_rt(rt2x00dev, RT3390)) { - rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, - rt2x00dev->default_ant.rx_chain_num == 1); - rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, - rt2x00dev->default_ant.tx_chain_num == 1); - } else { - rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); - rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); - - switch (rt2x00dev->default_ant.tx_chain_num) { - case 1: - rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1); - /* fall through */ - case 2: - rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1); - break; - } - - switch (rt2x00dev->default_ant.rx_chain_num) { - case 1: - rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1); - /* fall through */ - case 2: - rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1); - break; - } - } + rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, + rt2x00dev->default_ant.tx_chain_num <= 1); + rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, + rt2x00dev->default_ant.tx_chain_num <= 2); rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); @@ -2896,23 +2875,32 @@ EXPORT_SYMBOL_GPL(rt2800_link_stats); static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev) { + u8 vgc; + if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) { if (rt2x00_rt(rt2x00dev, RT3070) || rt2x00_rt(rt2x00dev, RT3071) || rt2x00_rt(rt2x00dev, RT3090) || rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3390) || + rt2x00_rt(rt2x00dev, RT3572) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392)) - return 0x1c + (2 * rt2x00dev->lna_gain); + vgc = 0x1c + (2 * rt2x00dev->lna_gain); else - return 0x2e + rt2x00dev->lna_gain; + vgc = 0x2e + rt2x00dev->lna_gain; + } else { /* 5GHZ band */ + if (rt2x00_rt(rt2x00dev, RT3572)) + vgc = 0x22 + (rt2x00dev->lna_gain * 5) / 3; + else { + if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) + vgc = 0x32 + (rt2x00dev->lna_gain * 5) / 3; + else + vgc = 0x3a + (rt2x00dev->lna_gain * 5) / 3; + } } - if (!test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) - return 0x32 + (rt2x00dev->lna_gain * 5) / 3; - else - return 0x3a + (rt2x00dev->lna_gain * 5) / 3; + return vgc; } static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev, @@ -3081,7 +3069,7 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev) rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); } else if (rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392)) { + rt2x00_rt(rt2x00dev, RT5392)) { rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000404); rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606); rt2800_register_write(rt2x00dev, TX_SW_CFG2, 0x00000000); @@ -3526,6 +3514,11 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) } else if (rt2800_is_305x_soc(rt2x00dev)) { rt2800_bbp_write(rt2x00dev, 78, 0x0e); rt2800_bbp_write(rt2x00dev, 80, 0x08); + } else if (rt2x00_rt(rt2x00dev, RT3290)) { + rt2800_bbp_write(rt2x00dev, 74, 0x0b); + rt2800_bbp_write(rt2x00dev, 79, 0x18); + rt2800_bbp_write(rt2x00dev, 80, 0x09); + rt2800_bbp_write(rt2x00dev, 81, 0x33); } else if (rt2x00_rt(rt2x00dev, RT3352)) { rt2800_bbp_write(rt2x00dev, 78, 0x0e); rt2800_bbp_write(rt2x00dev, 80, 0x08); @@ -3534,13 +3527,6 @@ static int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev) rt2800_bbp_write(rt2x00dev, 81, 0x37); } - if (rt2x00_rt(rt2x00dev, RT3290)) { - rt2800_bbp_write(rt2x00dev, 74, 0x0b); - rt2800_bbp_write(rt2x00dev, 79, 0x18); - rt2800_bbp_write(rt2x00dev, 80, 0x09); - rt2800_bbp_write(rt2x00dev, 81, 0x33); - } - rt2800_bbp_write(rt2x00dev, 82, 0x62); if (rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT5390) || diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 391e08fa054b..27829e1e2e38 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -1088,7 +1088,6 @@ static const struct data_queue_desc rt2800pci_queue_bcn = { static const struct rt2x00_ops rt2800pci_ops = { .name = KBUILD_MODNAME, .drv_data_size = sizeof(struct rt2800_drv_data), - .max_sta_intf = 1, .max_ap_intf = 8, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 603b65d6f28b..c9e9370eb789 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -870,7 +870,6 @@ static const struct data_queue_desc rt2800usb_queue_bcn = { static const struct rt2x00_ops rt2800usb_ops = { .name = KBUILD_MODNAME, .drv_data_size = sizeof(struct rt2800_drv_data), - .max_sta_intf = 1, .max_ap_intf = 8, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 49375c86c334..0751b35ef6dc 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -656,7 +656,6 @@ struct rt2x00lib_ops { struct rt2x00_ops { const char *name; const unsigned int drv_data_size; - const unsigned int max_sta_intf; const unsigned int max_ap_intf; const unsigned int eeprom_size; const unsigned int rf_size; @@ -742,6 +741,14 @@ enum rt2x00_capability_flags { }; /* + * Interface combinations + */ +enum { + IF_COMB_AP = 0, + NUM_IF_COMB, +}; + +/* * rt2x00 device structure. */ struct rt2x00_dev { @@ -868,6 +875,12 @@ struct rt2x00_dev { unsigned int intf_beaconing; /* + * Interface combinations + */ + struct ieee80211_iface_limit if_limits_ap; + struct ieee80211_iface_combination if_combinations[NUM_IF_COMB]; + + /* * Link quality */ struct link link; diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 10cf67267775..69097d1faeb6 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1118,6 +1118,34 @@ void rt2x00lib_stop(struct rt2x00_dev *rt2x00dev) rt2x00dev->intf_associated = 0; } +static inline void rt2x00lib_set_if_combinations(struct rt2x00_dev *rt2x00dev) +{ + struct ieee80211_iface_limit *if_limit; + struct ieee80211_iface_combination *if_combination; + + /* + * Build up AP interface limits structure. + */ + if_limit = &rt2x00dev->if_limits_ap; + if_limit->max = rt2x00dev->ops->max_ap_intf; + if_limit->types = BIT(NL80211_IFTYPE_AP); + + /* + * Build up AP interface combinations structure. + */ + if_combination = &rt2x00dev->if_combinations[IF_COMB_AP]; + if_combination->limits = if_limit; + if_combination->n_limits = 1; + if_combination->max_interfaces = if_limit->max; + if_combination->num_different_channels = 1; + + /* + * Finally, specify the possible combinations to mac80211. + */ + rt2x00dev->hw->wiphy->iface_combinations = rt2x00dev->if_combinations; + rt2x00dev->hw->wiphy->n_iface_combinations = 1; +} + /* * driver allocation handlers. */ @@ -1126,6 +1154,11 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) int retval = -ENOMEM; /* + * Set possible interface combinations. + */ + rt2x00lib_set_if_combinations(rt2x00dev); + + /* * Allocate the driver data memory, if necessary. */ if (rt2x00dev->ops->drv_data_size > 0) { diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index c3d0f2f87b69..98a9e48f8e4a 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -214,46 +214,6 @@ int rt2x00mac_add_interface(struct ieee80211_hw *hw, !test_bit(DEVICE_STATE_STARTED, &rt2x00dev->flags)) return -ENODEV; - switch (vif->type) { - case NL80211_IFTYPE_AP: - /* - * We don't support mixed combinations of - * sta and ap interfaces. - */ - if (rt2x00dev->intf_sta_count) - return -ENOBUFS; - - /* - * Check if we exceeded the maximum amount - * of supported interfaces. - */ - if (rt2x00dev->intf_ap_count >= rt2x00dev->ops->max_ap_intf) - return -ENOBUFS; - - break; - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_MESH_POINT: - case NL80211_IFTYPE_WDS: - /* - * We don't support mixed combinations of - * sta and ap interfaces. - */ - if (rt2x00dev->intf_ap_count) - return -ENOBUFS; - - /* - * Check if we exceeded the maximum amount - * of supported interfaces. - */ - if (rt2x00dev->intf_sta_count >= rt2x00dev->ops->max_sta_intf) - return -ENOBUFS; - - break; - default: - return -EINVAL; - } - /* * Loop through all beacon queues to find a free * entry. Since there are as much beacon entries diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 2673e058caaf..d6582a2fa353 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -3045,7 +3045,6 @@ static const struct data_queue_desc rt61pci_queue_bcn = { static const struct rt2x00_ops rt61pci_ops = { .name = KBUILD_MODNAME, - .max_sta_intf = 1, .max_ap_intf = 4, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index cfa9f37cccc2..e5eb43b3eee7 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2382,7 +2382,6 @@ static const struct data_queue_desc rt73usb_queue_bcn = { static const struct rt2x00_ops rt73usb_ops = { .name = KBUILD_MODNAME, - .max_sta_intf = 1, .max_ap_intf = 4, .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 3b20b73ee649..ec857676c39f 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -5,21 +5,9 @@ menu "Near Field Communication (NFC) devices" depends on NFC -config PN544_NFC - tristate "PN544 NFC driver" - depends on I2C - select CRC_CCITT - default n - ---help--- - Say yes if you want PN544 Near Field Communication driver. - This is for i2c connected version. If unsure, say N here. - - To compile this driver as a module, choose m here. The module will - be called pn544. - config PN544_HCI_NFC tristate "HCI PN544 NFC driver" - depends on I2C && NFC_SHDLC + depends on I2C && NFC_HCI && NFC_SHDLC select CRC_CCITT default n ---help--- diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index 473e44cef612..bf05831fdf09 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -2,7 +2,6 @@ # Makefile for nfc devices # -obj-$(CONFIG_PN544_NFC) += pn544.o obj-$(CONFIG_PN544_HCI_NFC) += pn544_hci.o obj-$(CONFIG_NFC_PN533) += pn533.o obj-$(CONFIG_NFC_WILINK) += nfcwilink.o diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c index e7fd4938f9bc..50b1ee41afc6 100644 --- a/drivers/nfc/nfcwilink.c +++ b/drivers/nfc/nfcwilink.c @@ -352,8 +352,6 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb) struct nfcwilink *drv = priv_data; int rc; - nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len); - if (!skb) return -EFAULT; @@ -362,6 +360,8 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb) return -EFAULT; } + nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len); + /* strip the ST header (apart for the chnl byte, which is not received in the hdr) */ skb_pull(skb, (NFCWILINK_HDR_LEN-1)); @@ -604,21 +604,7 @@ static struct platform_driver nfcwilink_driver = { }, }; -/* ------- Module Init/Exit interfaces ------ */ -static int __init nfcwilink_init(void) -{ - printk(KERN_INFO "NFC Driver for TI WiLink"); - - return platform_driver_register(&nfcwilink_driver); -} - -static void __exit nfcwilink_exit(void) -{ - platform_driver_unregister(&nfcwilink_driver); -} - -module_init(nfcwilink_init); -module_exit(nfcwilink_exit); +module_platform_driver(nfcwilink_driver); /* ------ Module Info ------ */ diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index d606f52fec84..97c440a8cd61 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -356,6 +356,7 @@ struct pn533 { struct workqueue_struct *wq; struct work_struct cmd_work; + struct work_struct cmd_complete_work; struct work_struct poll_work; struct work_struct mi_work; struct work_struct tg_work; @@ -383,6 +384,19 @@ struct pn533 { u8 tgt_mode; u32 device_type; + + struct list_head cmd_queue; + u8 cmd_pending; +}; + +struct pn533_cmd { + struct list_head queue; + struct pn533_frame *out_frame; + struct pn533_frame *in_frame; + int in_frame_len; + pn533_cmd_complete_t cmd_complete; + void *arg; + gfp_t flags; }; struct pn533_frame { @@ -487,7 +501,7 @@ static bool pn533_rx_frame_is_cmd_response(struct pn533_frame *frame, u8 cmd) static void pn533_wq_cmd_complete(struct work_struct *work) { - struct pn533 *dev = container_of(work, struct pn533, cmd_work); + struct pn533 *dev = container_of(work, struct pn533, cmd_complete_work); struct pn533_frame *in_frame; int rc; @@ -502,7 +516,7 @@ static void pn533_wq_cmd_complete(struct work_struct *work) PN533_FRAME_CMD_PARAMS_LEN(in_frame)); if (rc != -EINPROGRESS) - mutex_unlock(&dev->cmd_lock); + queue_work(dev->wq, &dev->cmd_work); } static void pn533_recv_response(struct urb *urb) @@ -550,7 +564,7 @@ static void pn533_recv_response(struct urb *urb) dev->wq_in_frame = in_frame; sched_wq: - queue_work(dev->wq, &dev->cmd_work); + queue_work(dev->wq, &dev->cmd_complete_work); } static int pn533_submit_urb_for_response(struct pn533 *dev, gfp_t flags) @@ -606,7 +620,7 @@ static void pn533_recv_ack(struct urb *urb) sched_wq: dev->wq_in_frame = NULL; - queue_work(dev->wq, &dev->cmd_work); + queue_work(dev->wq, &dev->cmd_complete_work); } static int pn533_submit_urb_for_ack(struct pn533 *dev, gfp_t flags) @@ -669,6 +683,31 @@ error: return rc; } +static void pn533_wq_cmd(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, cmd_work); + struct pn533_cmd *cmd; + + mutex_lock(&dev->cmd_lock); + + if (list_empty(&dev->cmd_queue)) { + dev->cmd_pending = 0; + mutex_unlock(&dev->cmd_lock); + return; + } + + cmd = list_first_entry(&dev->cmd_queue, struct pn533_cmd, queue); + + mutex_unlock(&dev->cmd_lock); + + __pn533_send_cmd_frame_async(dev, cmd->out_frame, cmd->in_frame, + cmd->in_frame_len, cmd->cmd_complete, + cmd->arg, cmd->flags); + + list_del(&cmd->queue); + kfree(cmd); +} + static int pn533_send_cmd_frame_async(struct pn533 *dev, struct pn533_frame *out_frame, struct pn533_frame *in_frame, @@ -676,21 +715,44 @@ static int pn533_send_cmd_frame_async(struct pn533 *dev, pn533_cmd_complete_t cmd_complete, void *arg, gfp_t flags) { - int rc; + struct pn533_cmd *cmd; + int rc = 0; nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - if (!mutex_trylock(&dev->cmd_lock)) - return -EBUSY; + mutex_lock(&dev->cmd_lock); - rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame, - in_frame_len, cmd_complete, arg, flags); - if (rc) - goto error; + if (!dev->cmd_pending) { + rc = __pn533_send_cmd_frame_async(dev, out_frame, in_frame, + in_frame_len, cmd_complete, + arg, flags); + if (!rc) + dev->cmd_pending = 1; - return 0; -error: + goto unlock; + } + + nfc_dev_dbg(&dev->interface->dev, "%s Queueing command", __func__); + + cmd = kzalloc(sizeof(struct pn533_cmd), flags); + if (!cmd) { + rc = -ENOMEM; + goto unlock; + } + + INIT_LIST_HEAD(&cmd->queue); + cmd->out_frame = out_frame; + cmd->in_frame = in_frame; + cmd->in_frame_len = in_frame_len; + cmd->cmd_complete = cmd_complete; + cmd->arg = arg; + cmd->flags = flags; + + list_add_tail(&cmd->queue, &dev->cmd_queue); + +unlock: mutex_unlock(&dev->cmd_lock); + return rc; } @@ -1305,8 +1367,6 @@ static void pn533_listen_mode_timer(unsigned long data) dev->cancel_listen = 1; - mutex_unlock(&dev->cmd_lock); - pn533_poll_next_mod(dev); queue_work(dev->wq, &dev->poll_work); @@ -2131,7 +2191,7 @@ error_cmd: kfree(arg); - mutex_unlock(&dev->cmd_lock); + queue_work(dev->wq, &dev->cmd_work); } static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata, @@ -2330,13 +2390,12 @@ static int pn533_probe(struct usb_interface *interface, NULL, 0, pn533_send_complete, dev); - INIT_WORK(&dev->cmd_work, pn533_wq_cmd_complete); + INIT_WORK(&dev->cmd_work, pn533_wq_cmd); + INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete); INIT_WORK(&dev->mi_work, pn533_wq_mi_recv); INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data); INIT_WORK(&dev->poll_work, pn533_wq_poll); - dev->wq = alloc_workqueue("pn533", - WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM, - 1); + dev->wq = alloc_ordered_workqueue("pn533", 0); if (dev->wq == NULL) goto error; @@ -2346,6 +2405,8 @@ static int pn533_probe(struct usb_interface *interface, skb_queue_head_init(&dev->resp_q); + INIT_LIST_HEAD(&dev->cmd_queue); + usb_set_intfdata(interface, dev); pn533_tx_frame_init(dev->out_frame, PN533_CMD_GET_FIRMWARE_VERSION); @@ -2417,6 +2478,7 @@ error: static void pn533_disconnect(struct usb_interface *interface) { struct pn533 *dev; + struct pn533_cmd *cmd, *n; dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); @@ -2433,6 +2495,11 @@ static void pn533_disconnect(struct usb_interface *interface) del_timer(&dev->listen_timer); + list_for_each_entry_safe(cmd, n, &dev->cmd_queue, queue) { + list_del(&cmd->queue); + kfree(cmd); + } + kfree(dev->in_frame); usb_free_urb(dev->in_urb); kfree(dev->out_frame); diff --git a/drivers/nfc/pn544.c b/drivers/nfc/pn544.c deleted file mode 100644 index 724f65d8f9e4..000000000000 --- a/drivers/nfc/pn544.c +++ /dev/null @@ -1,893 +0,0 @@ -/* - * Driver for the PN544 NFC chip. - * - * Copyright (C) Nokia Corporation - * - * Author: Jari Vanhala <ext-jari.vanhala@nokia.com> - * Contact: Matti Aaltonen <matti.j.aaltonen@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/completion.h> -#include <linux/crc-ccitt.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/miscdevice.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/nfc/pn544.h> -#include <linux/poll.h> -#include <linux/regulator/consumer.h> -#include <linux/serial_core.h> /* for TCGETS */ -#include <linux/slab.h> - -#define DRIVER_CARD "PN544 NFC" -#define DRIVER_DESC "NFC driver for PN544" - -static struct i2c_device_id pn544_id_table[] = { - { PN544_DRIVER_NAME, 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, pn544_id_table); - -#define HCI_MODE 0 -#define FW_MODE 1 - -enum pn544_state { - PN544_ST_COLD, - PN544_ST_FW_READY, - PN544_ST_READY, -}; - -enum pn544_irq { - PN544_NONE, - PN544_INT, -}; - -struct pn544_info { - struct miscdevice miscdev; - struct i2c_client *i2c_dev; - struct regulator_bulk_data regs[3]; - - enum pn544_state state; - wait_queue_head_t read_wait; - loff_t read_offset; - enum pn544_irq read_irq; - struct mutex read_mutex; /* Serialize read_irq access */ - struct mutex mutex; /* Serialize info struct access */ - u8 *buf; - size_t buflen; -}; - -static const char reg_vdd_io[] = "Vdd_IO"; -static const char reg_vbat[] = "VBat"; -static const char reg_vsim[] = "VSim"; - -/* sysfs interface */ -static ssize_t pn544_test(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pn544_info *info = dev_get_drvdata(dev); - struct i2c_client *client = info->i2c_dev; - struct pn544_nfc_platform_data *pdata = client->dev.platform_data; - - return snprintf(buf, PAGE_SIZE, "%d\n", pdata->test()); -} - -static int pn544_enable(struct pn544_info *info, int mode) -{ - struct pn544_nfc_platform_data *pdata; - struct i2c_client *client = info->i2c_dev; - - int r; - - r = regulator_bulk_enable(ARRAY_SIZE(info->regs), info->regs); - if (r < 0) - return r; - - pdata = client->dev.platform_data; - info->read_irq = PN544_NONE; - if (pdata->enable) - pdata->enable(mode); - - if (mode) { - info->state = PN544_ST_FW_READY; - dev_dbg(&client->dev, "now in FW-mode\n"); - } else { - info->state = PN544_ST_READY; - dev_dbg(&client->dev, "now in HCI-mode\n"); - } - - usleep_range(10000, 15000); - - return 0; -} - -static void pn544_disable(struct pn544_info *info) -{ - struct pn544_nfc_platform_data *pdata; - struct i2c_client *client = info->i2c_dev; - - pdata = client->dev.platform_data; - if (pdata->disable) - pdata->disable(); - - info->state = PN544_ST_COLD; - - dev_dbg(&client->dev, "Now in OFF-mode\n"); - - msleep(PN544_RESETVEN_TIME); - - info->read_irq = PN544_NONE; - regulator_bulk_disable(ARRAY_SIZE(info->regs), info->regs); -} - -static int check_crc(u8 *buf, int buflen) -{ - u8 len; - u16 crc; - - len = buf[0] + 1; - if (len < 4 || len != buflen || len > PN544_MSG_MAX_SIZE) { - pr_err(PN544_DRIVER_NAME - ": CRC; corrupt packet len %u (%d)\n", len, buflen); - print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE, - 16, 2, buf, buflen, false); - return -EPERM; - } - crc = crc_ccitt(0xffff, buf, len - 2); - crc = ~crc; - - if (buf[len-2] != (crc & 0xff) || buf[len-1] != (crc >> 8)) { - pr_err(PN544_DRIVER_NAME ": CRC error 0x%x != 0x%x 0x%x\n", - crc, buf[len-1], buf[len-2]); - - print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE, - 16, 2, buf, buflen, false); - return -EPERM; - } - return 0; -} - -static int pn544_i2c_write(struct i2c_client *client, u8 *buf, int len) -{ - int r; - - if (len < 4 || len != (buf[0] + 1)) { - dev_err(&client->dev, "%s: Illegal message length: %d\n", - __func__, len); - return -EINVAL; - } - - if (check_crc(buf, len)) - return -EINVAL; - - usleep_range(3000, 6000); - - r = i2c_master_send(client, buf, len); - dev_dbg(&client->dev, "send: %d\n", r); - - if (r == -EREMOTEIO) { /* Retry, chip was in standby */ - usleep_range(6000, 10000); - r = i2c_master_send(client, buf, len); - dev_dbg(&client->dev, "send2: %d\n", r); - } - - if (r != len) - return -EREMOTEIO; - - return r; -} - -static int pn544_i2c_read(struct i2c_client *client, u8 *buf, int buflen) -{ - int r; - u8 len; - - /* - * You could read a packet in one go, but then you'd need to read - * max size and rest would be 0xff fill, so we do split reads. - */ - r = i2c_master_recv(client, &len, 1); - dev_dbg(&client->dev, "recv1: %d\n", r); - - if (r != 1) - return -EREMOTEIO; - - if (len < PN544_LLC_HCI_OVERHEAD) - len = PN544_LLC_HCI_OVERHEAD; - else if (len > (PN544_MSG_MAX_SIZE - 1)) - len = PN544_MSG_MAX_SIZE - 1; - - if (1 + len > buflen) /* len+(data+crc16) */ - return -EMSGSIZE; - - buf[0] = len; - - r = i2c_master_recv(client, buf + 1, len); - dev_dbg(&client->dev, "recv2: %d\n", r); - - if (r != len) - return -EREMOTEIO; - - usleep_range(3000, 6000); - - return r + 1; -} - -static int pn544_fw_write(struct i2c_client *client, u8 *buf, int len) -{ - int r; - - dev_dbg(&client->dev, "%s\n", __func__); - - if (len < PN544_FW_HEADER_SIZE || - (PN544_FW_HEADER_SIZE + (buf[1] << 8) + buf[2]) != len) - return -EINVAL; - - r = i2c_master_send(client, buf, len); - dev_dbg(&client->dev, "fw send: %d\n", r); - - if (r == -EREMOTEIO) { /* Retry, chip was in standby */ - usleep_range(6000, 10000); - r = i2c_master_send(client, buf, len); - dev_dbg(&client->dev, "fw send2: %d\n", r); - } - - if (r != len) - return -EREMOTEIO; - - return r; -} - -static int pn544_fw_read(struct i2c_client *client, u8 *buf, int buflen) -{ - int r, len; - - if (buflen < PN544_FW_HEADER_SIZE) - return -EINVAL; - - r = i2c_master_recv(client, buf, PN544_FW_HEADER_SIZE); - dev_dbg(&client->dev, "FW recv1: %d\n", r); - - if (r < 0) - return r; - - if (r < PN544_FW_HEADER_SIZE) - return -EINVAL; - - len = (buf[1] << 8) + buf[2]; - if (len == 0) /* just header, no additional data */ - return r; - - if (len > buflen - PN544_FW_HEADER_SIZE) - return -EMSGSIZE; - - r = i2c_master_recv(client, buf + PN544_FW_HEADER_SIZE, len); - dev_dbg(&client->dev, "fw recv2: %d\n", r); - - if (r != len) - return -EINVAL; - - return r + PN544_FW_HEADER_SIZE; -} - -static irqreturn_t pn544_irq_thread_fn(int irq, void *dev_id) -{ - struct pn544_info *info = dev_id; - struct i2c_client *client = info->i2c_dev; - - BUG_ON(!info); - BUG_ON(irq != info->i2c_dev->irq); - - dev_dbg(&client->dev, "IRQ\n"); - - mutex_lock(&info->read_mutex); - info->read_irq = PN544_INT; - mutex_unlock(&info->read_mutex); - - wake_up_interruptible(&info->read_wait); - - return IRQ_HANDLED; -} - -static enum pn544_irq pn544_irq_state(struct pn544_info *info) -{ - enum pn544_irq irq; - - mutex_lock(&info->read_mutex); - irq = info->read_irq; - mutex_unlock(&info->read_mutex); - /* - * XXX: should we check GPIO-line status directly? - * return pdata->irq_status() ? PN544_INT : PN544_NONE; - */ - - return irq; -} - -static ssize_t pn544_read(struct file *file, char __user *buf, - size_t count, loff_t *offset) -{ - struct pn544_info *info = container_of(file->private_data, - struct pn544_info, miscdev); - struct i2c_client *client = info->i2c_dev; - enum pn544_irq irq; - size_t len; - int r = 0; - - dev_dbg(&client->dev, "%s: info: %p, count: %zu\n", __func__, - info, count); - - mutex_lock(&info->mutex); - - if (info->state == PN544_ST_COLD) { - r = -ENODEV; - goto out; - } - - irq = pn544_irq_state(info); - if (irq == PN544_NONE) { - if (file->f_flags & O_NONBLOCK) { - r = -EAGAIN; - goto out; - } - - if (wait_event_interruptible(info->read_wait, - (info->read_irq == PN544_INT))) { - r = -ERESTARTSYS; - goto out; - } - } - - if (info->state == PN544_ST_FW_READY) { - len = min(count, info->buflen); - - mutex_lock(&info->read_mutex); - r = pn544_fw_read(info->i2c_dev, info->buf, len); - info->read_irq = PN544_NONE; - mutex_unlock(&info->read_mutex); - - if (r < 0) { - dev_err(&info->i2c_dev->dev, "FW read failed: %d\n", r); - goto out; - } - - print_hex_dump(KERN_DEBUG, "FW read: ", DUMP_PREFIX_NONE, - 16, 2, info->buf, r, false); - - *offset += r; - if (copy_to_user(buf, info->buf, r)) { - r = -EFAULT; - goto out; - } - } else { - len = min(count, info->buflen); - - mutex_lock(&info->read_mutex); - r = pn544_i2c_read(info->i2c_dev, info->buf, len); - info->read_irq = PN544_NONE; - mutex_unlock(&info->read_mutex); - - if (r < 0) { - dev_err(&info->i2c_dev->dev, "read failed (%d)\n", r); - goto out; - } - print_hex_dump(KERN_DEBUG, "read: ", DUMP_PREFIX_NONE, - 16, 2, info->buf, r, false); - - *offset += r; - if (copy_to_user(buf, info->buf, r)) { - r = -EFAULT; - goto out; - } - } - -out: - mutex_unlock(&info->mutex); - - return r; -} - -static unsigned int pn544_poll(struct file *file, poll_table *wait) -{ - struct pn544_info *info = container_of(file->private_data, - struct pn544_info, miscdev); - struct i2c_client *client = info->i2c_dev; - int r = 0; - - dev_dbg(&client->dev, "%s: info: %p\n", __func__, info); - - mutex_lock(&info->mutex); - - if (info->state == PN544_ST_COLD) { - r = -ENODEV; - goto out; - } - - poll_wait(file, &info->read_wait, wait); - - if (pn544_irq_state(info) == PN544_INT) { - r = POLLIN | POLLRDNORM; - goto out; - } -out: - mutex_unlock(&info->mutex); - - return r; -} - -static ssize_t pn544_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct pn544_info *info = container_of(file->private_data, - struct pn544_info, miscdev); - struct i2c_client *client = info->i2c_dev; - ssize_t len; - int r; - - dev_dbg(&client->dev, "%s: info: %p, count %zu\n", __func__, - info, count); - - mutex_lock(&info->mutex); - - if (info->state == PN544_ST_COLD) { - r = -ENODEV; - goto out; - } - - /* - * XXX: should we detect rset-writes and clean possible - * read_irq state - */ - if (info->state == PN544_ST_FW_READY) { - size_t fw_len; - - if (count < PN544_FW_HEADER_SIZE) { - r = -EINVAL; - goto out; - } - - len = min(count, info->buflen); - if (copy_from_user(info->buf, buf, len)) { - r = -EFAULT; - goto out; - } - - print_hex_dump(KERN_DEBUG, "FW write: ", DUMP_PREFIX_NONE, - 16, 2, info->buf, len, false); - - fw_len = PN544_FW_HEADER_SIZE + (info->buf[1] << 8) + - info->buf[2]; - - if (len > fw_len) /* 1 msg at a time */ - len = fw_len; - - r = pn544_fw_write(info->i2c_dev, info->buf, len); - } else { - if (count < PN544_LLC_MIN_SIZE) { - r = -EINVAL; - goto out; - } - - len = min(count, info->buflen); - if (copy_from_user(info->buf, buf, len)) { - r = -EFAULT; - goto out; - } - - print_hex_dump(KERN_DEBUG, "write: ", DUMP_PREFIX_NONE, - 16, 2, info->buf, len, false); - - if (len > (info->buf[0] + 1)) /* 1 msg at a time */ - len = info->buf[0] + 1; - - r = pn544_i2c_write(info->i2c_dev, info->buf, len); - } -out: - mutex_unlock(&info->mutex); - - return r; - -} - -static long pn544_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct pn544_info *info = container_of(file->private_data, - struct pn544_info, miscdev); - struct i2c_client *client = info->i2c_dev; - struct pn544_nfc_platform_data *pdata; - unsigned int val; - int r = 0; - - dev_dbg(&client->dev, "%s: info: %p, cmd: 0x%x\n", __func__, info, cmd); - - mutex_lock(&info->mutex); - - if (info->state == PN544_ST_COLD) { - r = -ENODEV; - goto out; - } - - pdata = info->i2c_dev->dev.platform_data; - switch (cmd) { - case PN544_GET_FW_MODE: - dev_dbg(&client->dev, "%s: PN544_GET_FW_MODE\n", __func__); - - val = (info->state == PN544_ST_FW_READY); - if (copy_to_user((void __user *)arg, &val, sizeof(val))) { - r = -EFAULT; - goto out; - } - - break; - - case PN544_SET_FW_MODE: - dev_dbg(&client->dev, "%s: PN544_SET_FW_MODE\n", __func__); - - if (copy_from_user(&val, (void __user *)arg, sizeof(val))) { - r = -EFAULT; - goto out; - } - - if (val) { - if (info->state == PN544_ST_FW_READY) - break; - - pn544_disable(info); - r = pn544_enable(info, FW_MODE); - if (r < 0) - goto out; - } else { - if (info->state == PN544_ST_READY) - break; - pn544_disable(info); - r = pn544_enable(info, HCI_MODE); - if (r < 0) - goto out; - } - file->f_pos = info->read_offset; - break; - - case TCGETS: - dev_dbg(&client->dev, "%s: TCGETS\n", __func__); - - r = -ENOIOCTLCMD; - break; - - default: - dev_err(&client->dev, "Unknown ioctl 0x%x\n", cmd); - r = -ENOIOCTLCMD; - break; - } - -out: - mutex_unlock(&info->mutex); - - return r; -} - -static int pn544_open(struct inode *inode, struct file *file) -{ - struct pn544_info *info = container_of(file->private_data, - struct pn544_info, miscdev); - struct i2c_client *client = info->i2c_dev; - int r = 0; - - dev_dbg(&client->dev, "%s: info: %p, client %p\n", __func__, - info, info->i2c_dev); - - mutex_lock(&info->mutex); - - /* - * Only 1 at a time. - * XXX: maybe user (counter) would work better - */ - if (info->state != PN544_ST_COLD) { - r = -EBUSY; - goto out; - } - - file->f_pos = info->read_offset; - r = pn544_enable(info, HCI_MODE); - -out: - mutex_unlock(&info->mutex); - return r; -} - -static int pn544_close(struct inode *inode, struct file *file) -{ - struct pn544_info *info = container_of(file->private_data, - struct pn544_info, miscdev); - struct i2c_client *client = info->i2c_dev; - - dev_dbg(&client->dev, "%s: info: %p, client %p\n", - __func__, info, info->i2c_dev); - - mutex_lock(&info->mutex); - pn544_disable(info); - mutex_unlock(&info->mutex); - - return 0; -} - -static const struct file_operations pn544_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = pn544_read, - .write = pn544_write, - .poll = pn544_poll, - .open = pn544_open, - .release = pn544_close, - .unlocked_ioctl = pn544_ioctl, -}; - -#ifdef CONFIG_PM -static int pn544_suspend(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pn544_info *info; - int r = 0; - - dev_info(&client->dev, "***\n%s: client %p\n***\n", __func__, client); - - info = i2c_get_clientdata(client); - dev_info(&client->dev, "%s: info: %p, client %p\n", __func__, - info, client); - - mutex_lock(&info->mutex); - - switch (info->state) { - case PN544_ST_FW_READY: - /* Do not suspend while upgrading FW, please! */ - r = -EPERM; - break; - - case PN544_ST_READY: - /* - * CHECK: Device should be in standby-mode. No way to check? - * Allowing low power mode for the regulator is potentially - * dangerous if pn544 does not go to suspension. - */ - break; - - case PN544_ST_COLD: - break; - }; - - mutex_unlock(&info->mutex); - return r; -} - -static int pn544_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pn544_info *info = i2c_get_clientdata(client); - int r = 0; - - dev_dbg(&client->dev, "%s: info: %p, client %p\n", __func__, - info, client); - - mutex_lock(&info->mutex); - - switch (info->state) { - case PN544_ST_READY: - /* - * CHECK: If regulator low power mode is allowed in - * pn544_suspend, we should go back to normal mode - * here. - */ - break; - - case PN544_ST_COLD: - break; - - case PN544_ST_FW_READY: - break; - }; - - mutex_unlock(&info->mutex); - - return r; -} - -static SIMPLE_DEV_PM_OPS(pn544_pm_ops, pn544_suspend, pn544_resume); -#endif - -static struct device_attribute pn544_attr = - __ATTR(nfc_test, S_IRUGO, pn544_test, NULL); - -static int __devinit pn544_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct pn544_info *info; - struct pn544_nfc_platform_data *pdata; - int r = 0; - - dev_dbg(&client->dev, "%s\n", __func__); - dev_dbg(&client->dev, "IRQ: %d\n", client->irq); - - /* private data allocation */ - info = kzalloc(sizeof(struct pn544_info), GFP_KERNEL); - if (!info) { - dev_err(&client->dev, - "Cannot allocate memory for pn544_info.\n"); - r = -ENOMEM; - goto err_info_alloc; - } - - info->buflen = max(PN544_MSG_MAX_SIZE, PN544_MAX_I2C_TRANSFER); - info->buf = kzalloc(info->buflen, GFP_KERNEL); - if (!info->buf) { - dev_err(&client->dev, - "Cannot allocate memory for pn544_info->buf.\n"); - r = -ENOMEM; - goto err_buf_alloc; - } - - info->regs[0].supply = reg_vdd_io; - info->regs[1].supply = reg_vbat; - info->regs[2].supply = reg_vsim; - r = regulator_bulk_get(&client->dev, ARRAY_SIZE(info->regs), - info->regs); - if (r < 0) - goto err_kmalloc; - - info->i2c_dev = client; - info->state = PN544_ST_COLD; - info->read_irq = PN544_NONE; - mutex_init(&info->read_mutex); - mutex_init(&info->mutex); - init_waitqueue_head(&info->read_wait); - i2c_set_clientdata(client, info); - pdata = client->dev.platform_data; - if (!pdata) { - dev_err(&client->dev, "No platform data\n"); - r = -EINVAL; - goto err_reg; - } - - if (!pdata->request_resources) { - dev_err(&client->dev, "request_resources() missing\n"); - r = -EINVAL; - goto err_reg; - } - - r = pdata->request_resources(client); - if (r) { - dev_err(&client->dev, "Cannot get platform resources\n"); - goto err_reg; - } - - r = request_threaded_irq(client->irq, NULL, pn544_irq_thread_fn, - IRQF_TRIGGER_RISING, PN544_DRIVER_NAME, - info); - if (r < 0) { - dev_err(&client->dev, "Unable to register IRQ handler\n"); - goto err_res; - } - - /* If we don't have the test we don't need the sysfs file */ - if (pdata->test) { - r = device_create_file(&client->dev, &pn544_attr); - if (r) { - dev_err(&client->dev, - "sysfs registration failed, error %d\n", r); - goto err_irq; - } - } - - info->miscdev.minor = MISC_DYNAMIC_MINOR; - info->miscdev.name = PN544_DRIVER_NAME; - info->miscdev.fops = &pn544_fops; - info->miscdev.parent = &client->dev; - r = misc_register(&info->miscdev); - if (r < 0) { - dev_err(&client->dev, "Device registration failed\n"); - goto err_sysfs; - } - - dev_dbg(&client->dev, "%s: info: %p, pdata %p, client %p\n", - __func__, info, pdata, client); - - return 0; - -err_sysfs: - if (pdata->test) - device_remove_file(&client->dev, &pn544_attr); -err_irq: - free_irq(client->irq, info); -err_res: - if (pdata->free_resources) - pdata->free_resources(); -err_reg: - regulator_bulk_free(ARRAY_SIZE(info->regs), info->regs); -err_kmalloc: - kfree(info->buf); -err_buf_alloc: - kfree(info); -err_info_alloc: - return r; -} - -static __devexit int pn544_remove(struct i2c_client *client) -{ - struct pn544_info *info = i2c_get_clientdata(client); - struct pn544_nfc_platform_data *pdata = client->dev.platform_data; - - dev_dbg(&client->dev, "%s\n", __func__); - - misc_deregister(&info->miscdev); - if (pdata->test) - device_remove_file(&client->dev, &pn544_attr); - - if (info->state != PN544_ST_COLD) { - if (pdata->disable) - pdata->disable(); - - info->read_irq = PN544_NONE; - } - - free_irq(client->irq, info); - if (pdata->free_resources) - pdata->free_resources(); - - regulator_bulk_free(ARRAY_SIZE(info->regs), info->regs); - kfree(info->buf); - kfree(info); - - return 0; -} - -static struct i2c_driver pn544_driver = { - .driver = { - .name = PN544_DRIVER_NAME, -#ifdef CONFIG_PM - .pm = &pn544_pm_ops, -#endif - }, - .probe = pn544_probe, - .id_table = pn544_id_table, - .remove = __devexit_p(pn544_remove), -}; - -static int __init pn544_init(void) -{ - int r; - - pr_debug(DRIVER_DESC ": %s\n", __func__); - - r = i2c_add_driver(&pn544_driver); - if (r) { - pr_err(PN544_DRIVER_NAME ": driver registration failed\n"); - return r; - } - - return 0; -} - -static void __exit pn544_exit(void) -{ - i2c_del_driver(&pn544_driver); - pr_info(DRIVER_DESC ", Exiting.\n"); -} - -module_init(pn544_init); -module_exit(pn544_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/nfc/pn544_hci.c b/drivers/nfc/pn544_hci.c index aa71807189ba..c9c8570273ab 100644 --- a/drivers/nfc/pn544_hci.c +++ b/drivers/nfc/pn544_hci.c @@ -29,7 +29,7 @@ #include <linux/nfc.h> #include <net/nfc/hci.h> -#include <net/nfc/shdlc.h> +#include <net/nfc/llc.h> #include <linux/nfc/pn544.h> @@ -128,10 +128,12 @@ static struct nfc_hci_gate pn544_gates[] = { /* Largest headroom needed for outgoing custom commands */ #define PN544_CMDS_HEADROOM 2 +#define PN544_FRAME_HEADROOM 1 +#define PN544_FRAME_TAILROOM 2 struct pn544_hci_info { struct i2c_client *i2c_dev; - struct nfc_shdlc *shdlc; + struct nfc_hci_dev *hdev; enum pn544_state state; @@ -146,6 +148,9 @@ struct pn544_hci_info { * < 0 if hardware error occured (e.g. i2c err) * and prevents normal operation. */ + int async_cb_type; + data_exchange_cb_t async_cb; + void *async_cb_context; }; static void pn544_hci_platform_init(struct pn544_hci_info *info) @@ -230,8 +235,12 @@ static int pn544_hci_i2c_write(struct i2c_client *client, u8 *buf, int len) r = i2c_master_send(client, buf, len); } - if (r >= 0 && r != len) - r = -EREMOTEIO; + if (r >= 0) { + if (r != len) + return -EREMOTEIO; + else + return 0; + } return r; } @@ -341,13 +350,16 @@ flush: static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id) { struct pn544_hci_info *info = dev_id; - struct i2c_client *client = info->i2c_dev; + struct i2c_client *client; struct sk_buff *skb = NULL; int r; - BUG_ON(!info); - BUG_ON(irq != info->i2c_dev->irq); + if (!info || irq != info->i2c_dev->irq) { + WARN_ON_ONCE(1); + return IRQ_NONE; + } + client = info->i2c_dev; dev_dbg(&client->dev, "IRQ\n"); if (info->hard_fault != 0) @@ -357,21 +369,21 @@ static irqreturn_t pn544_hci_irq_thread_fn(int irq, void *dev_id) if (r == -EREMOTEIO) { info->hard_fault = r; - nfc_shdlc_recv_frame(info->shdlc, NULL); + nfc_hci_recv_frame(info->hdev, NULL); return IRQ_HANDLED; } else if ((r == -ENOMEM) || (r == -EBADMSG)) { return IRQ_HANDLED; } - nfc_shdlc_recv_frame(info->shdlc, skb); + nfc_hci_recv_frame(info->hdev, skb); return IRQ_HANDLED; } -static int pn544_hci_open(struct nfc_shdlc *shdlc) +static int pn544_hci_open(struct nfc_hci_dev *hdev) { - struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc); + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); int r = 0; mutex_lock(&info->info_lock); @@ -391,9 +403,9 @@ out: return r; } -static void pn544_hci_close(struct nfc_shdlc *shdlc) +static void pn544_hci_close(struct nfc_hci_dev *hdev) { - struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc); + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); mutex_lock(&info->info_lock); @@ -408,9 +420,8 @@ out: mutex_unlock(&info->info_lock); } -static int pn544_hci_ready(struct nfc_shdlc *shdlc) +static int pn544_hci_ready(struct nfc_hci_dev *hdev) { - struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); struct sk_buff *skb; static struct hw_config { u8 adr[2]; @@ -576,21 +587,45 @@ static int pn544_hci_ready(struct nfc_shdlc *shdlc) return 0; } -static int pn544_hci_xmit(struct nfc_shdlc *shdlc, struct sk_buff *skb) +static void pn544_hci_add_len_crc(struct sk_buff *skb) { - struct pn544_hci_info *info = nfc_shdlc_get_clientdata(shdlc); + u16 crc; + int len; + + len = skb->len + 2; + *skb_push(skb, 1) = len; + + crc = crc_ccitt(0xffff, skb->data, skb->len); + crc = ~crc; + *skb_put(skb, 1) = crc & 0xff; + *skb_put(skb, 1) = crc >> 8; +} + +static void pn544_hci_remove_len_crc(struct sk_buff *skb) +{ + skb_pull(skb, PN544_FRAME_HEADROOM); + skb_trim(skb, PN544_FRAME_TAILROOM); +} + +static int pn544_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb) +{ + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); struct i2c_client *client = info->i2c_dev; + int r; if (info->hard_fault != 0) return info->hard_fault; - return pn544_hci_i2c_write(client, skb->data, skb->len); + pn544_hci_add_len_crc(skb); + r = pn544_hci_i2c_write(client, skb->data, skb->len); + pn544_hci_remove_len_crc(skb); + + return r; } -static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, +static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, u32 im_protocols, u32 tm_protocols) { - struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); u8 phases = 0; int r; u8 duration[2]; @@ -641,7 +676,7 @@ static int pn544_hci_start_poll(struct nfc_shdlc *shdlc, return r; } -static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate, +static int pn544_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate, struct nfc_target *target) { switch (gate) { @@ -659,11 +694,10 @@ static int pn544_hci_target_from_gate(struct nfc_shdlc *shdlc, u8 gate, return 0; } -static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc, +static int pn544_hci_complete_target_discovered(struct nfc_hci_dev *hdev, u8 gate, struct nfc_target *target) { - struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); struct sk_buff *uid_skb; int r = 0; @@ -704,6 +738,26 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc, return r; } +#define PN544_CB_TYPE_READER_F 1 + +static void pn544_hci_data_exchange_cb(void *context, struct sk_buff *skb, + int err) +{ + struct pn544_hci_info *info = context; + + switch (info->async_cb_type) { + case PN544_CB_TYPE_READER_F: + if (err == 0) + skb_pull(skb, 1); + info->async_cb(info->async_cb_context, skb, err); + break; + default: + if (err == 0) + kfree_skb(skb); + break; + } +} + #define MIFARE_CMD_AUTH_KEY_A 0x60 #define MIFARE_CMD_AUTH_KEY_B 0x61 #define MIFARE_CMD_HEADER 2 @@ -715,13 +769,12 @@ static int pn544_hci_complete_target_discovered(struct nfc_shdlc *shdlc, * <= 0: driver handled the data exchange * 1: driver doesn't especially handle, please do standard processing */ -static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc, +static int pn544_hci_data_exchange(struct nfc_hci_dev *hdev, struct nfc_target *target, - struct sk_buff *skb, - struct sk_buff **res_skb) + struct sk_buff *skb, data_exchange_cb_t cb, + void *cb_context) { - struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); - int r; + struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev); pr_info(DRIVER_DESC ": %s for gate=%d\n", __func__, target->hci_reader_gate); @@ -746,41 +799,43 @@ static int pn544_hci_data_exchange(struct nfc_shdlc *shdlc, memcpy(data, uid, MIFARE_UID_LEN); } - return nfc_hci_send_cmd(hdev, target->hci_reader_gate, - PN544_MIFARE_CMD, - skb->data, skb->len, res_skb); + return nfc_hci_send_cmd_async(hdev, + target->hci_reader_gate, + PN544_MIFARE_CMD, + skb->data, skb->len, + cb, cb_context); } else return 1; case PN544_RF_READER_F_GATE: *skb_push(skb, 1) = 0; *skb_push(skb, 1) = 0; - r = nfc_hci_send_cmd(hdev, target->hci_reader_gate, - PN544_FELICA_RAW, - skb->data, skb->len, res_skb); - if (r == 0) - skb_pull(*res_skb, 1); - return r; + info->async_cb_type = PN544_CB_TYPE_READER_F; + info->async_cb = cb; + info->async_cb_context = cb_context; + + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + PN544_FELICA_RAW, skb->data, + skb->len, + pn544_hci_data_exchange_cb, info); case PN544_RF_READER_JEWEL_GATE: - return nfc_hci_send_cmd(hdev, target->hci_reader_gate, - PN544_JEWEL_RAW_CMD, - skb->data, skb->len, res_skb); + return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate, + PN544_JEWEL_RAW_CMD, skb->data, + skb->len, cb, cb_context); default: return 1; } } -static int pn544_hci_check_presence(struct nfc_shdlc *shdlc, +static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, struct nfc_target *target) { - struct nfc_hci_dev *hdev = nfc_shdlc_get_hci_dev(shdlc); - return nfc_hci_send_cmd(hdev, target->hci_reader_gate, PN544_RF_READER_CMD_PRESENCE_CHECK, NULL, 0, NULL); } -static struct nfc_shdlc_ops pn544_shdlc_ops = { +static struct nfc_hci_ops pn544_hci_ops = { .open = pn544_hci_open, .close = pn544_hci_close, .hci_ready = pn544_hci_ready, @@ -848,8 +903,8 @@ static int __devinit pn544_hci_probe(struct i2c_client *client, pn544_hci_platform_init(info); r = request_threaded_irq(client->irq, NULL, pn544_hci_irq_thread_fn, - IRQF_TRIGGER_RISING, PN544_HCI_DRIVER_NAME, - info); + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + PN544_HCI_DRIVER_NAME, info); if (r < 0) { dev_err(&client->dev, "Unable to register IRQ handler\n"); goto err_rti; @@ -872,22 +927,30 @@ static int __devinit pn544_hci_probe(struct i2c_client *client, NFC_PROTO_ISO14443_B_MASK | NFC_PROTO_NFC_DEP_MASK; - info->shdlc = nfc_shdlc_allocate(&pn544_shdlc_ops, - &init_data, protocols, - PN544_CMDS_HEADROOM, 0, - PN544_HCI_LLC_MAX_PAYLOAD, - dev_name(&client->dev)); - if (!info->shdlc) { - dev_err(&client->dev, "Cannot allocate nfc shdlc.\n"); + info->hdev = nfc_hci_allocate_device(&pn544_hci_ops, &init_data, + protocols, LLC_SHDLC_NAME, + PN544_FRAME_HEADROOM + + PN544_CMDS_HEADROOM, + PN544_FRAME_TAILROOM, + PN544_HCI_LLC_MAX_PAYLOAD); + if (!info->hdev) { + dev_err(&client->dev, "Cannot allocate nfc hdev.\n"); r = -ENOMEM; - goto err_allocshdlc; + goto err_alloc_hdev; } - nfc_shdlc_set_clientdata(info->shdlc, info); + nfc_hci_set_clientdata(info->hdev, info); + + r = nfc_hci_register_device(info->hdev); + if (r) + goto err_regdev; return 0; -err_allocshdlc: +err_regdev: + nfc_hci_free_device(info->hdev); + +err_alloc_hdev: free_irq(client->irq, info); err_rti: @@ -908,7 +971,7 @@ static __devexit int pn544_hci_remove(struct i2c_client *client) dev_dbg(&client->dev, "%s\n", __func__); - nfc_shdlc_free(info->shdlc); + nfc_hci_free_device(info->hdev); if (info->state != PN544_ST_COLD) { if (pdata->disable) |