]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
authorJohn W. Linville <linville@tuxdriver.com>
Fri, 28 Sep 2012 15:11:16 +0000 (11:11 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 28 Sep 2012 15:11:16 +0000 (11:11 -0400)
Conflicts:
net/nfc/netlink.c

Signed-off-by: John W. Linville <linville@tuxdriver.com>
148 files changed:
Documentation/feature-removal-schedule.txt
MAINTAINERS
drivers/bcma/host_pci.c
drivers/bcma/sprom.c
drivers/bluetooth/bluecard_cs.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btuart_cs.c
drivers/bluetooth/btusb.c
drivers/bluetooth/btwilink.c
drivers/bluetooth/hci_ldisc.c
drivers/bluetooth/hci_ll.c
drivers/bluetooth/hci_vhci.c
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath5k/mac80211-ops.c
drivers/net/wireless/ath/ath5k/phy.c
drivers/net/wireless/ath/ath6kl/cfg80211.c
drivers/net/wireless/ath/ath6kl/cfg80211.h
drivers/net/wireless/ath/ath9k/antenna.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_mci.c
drivers/net/wireless/ath/ath9k/ar9003_mci.h
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/gpio.c
drivers/net/wireless/ath/ath9k/hif_usb.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/hw-ops.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/mci.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/ath/ath9k/rc.c
drivers/net/wireless/ath/carl9170/mac.c
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
drivers/net/wireless/brcm80211/brcmfmac/dhd.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/ipw2x00/libipw_wx.c
drivers/net/wireless/iwlegacy/common.c
drivers/net/wireless/iwlwifi/dvm/scan.c
drivers/net/wireless/iwlwifi/dvm/sta.c
drivers/net/wireless/iwlwifi/dvm/ucode.c
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/internal.h
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/ie.c
drivers/net/wireless/mwifiex/init.c
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/p54/main.c
drivers/net/wireless/rt2x00/rt2400pci.c
drivers/net/wireless/rt2x00/rt2500pci.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800pci.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00.h
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt2x00mac.c
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/nfcwilink.c
drivers/nfc/pn533.c
drivers/nfc/pn544.c [deleted file]
drivers/nfc/pn544_hci.c
include/linux/ieee80211.h
include/linux/nfc.h
include/linux/nl80211.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/bluetooth/mgmt.h
include/net/cfg80211.h
include/net/mac80211.h
include/net/nfc/hci.h
include/net/nfc/llc.h [new file with mode: 0644]
include/net/nfc/nci.h
include/net/nfc/nci_core.h
include/net/nfc/nfc.h
include/net/nfc/shdlc.h [deleted file]
net/bluetooth/af_bluetooth.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/l2cap_core.c
net/bluetooth/mgmt.c
net/mac80211/agg-tx.c
net/mac80211/cfg.c
net/mac80211/chan.c
net/mac80211/debugfs.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/key.c
net/mac80211/main.c
net/mac80211/mesh_plink.c
net/mac80211/mlme.c
net/mac80211/offchannel.c
net/mac80211/scan.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c
net/mac80211/tx.c
net/mac80211/util.c
net/nfc/core.c
net/nfc/hci/Makefile
net/nfc/hci/command.c
net/nfc/hci/core.c
net/nfc/hci/hci.h
net/nfc/hci/hcp.c
net/nfc/hci/llc.c [new file with mode: 0644]
net/nfc/hci/llc.h [new file with mode: 0644]
net/nfc/hci/llc_nop.c [new file with mode: 0644]
net/nfc/hci/llc_shdlc.c [moved from net/nfc/hci/shdlc.c with 54% similarity]
net/nfc/llcp/commands.c
net/nfc/llcp/llcp.c
net/nfc/llcp/llcp.h
net/nfc/llcp/sock.c
net/nfc/nci/core.c
net/nfc/nci/ntf.c
net/nfc/nci/rsp.c
net/nfc/netlink.c
net/rfkill/core.c
net/wireless/mlme.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/reg.c

index e45fa5c0aa20affadaf0487f6ac6509e028d23d3..e824c1e742023ee021fa0843ff93d7990eaaf8a3 100644 (file)
@@ -508,18 +508,6 @@ Who:       Kees Cook <keescook@chromium.org>
 
 ----------------------------
 
-What:  Removing the pn544 raw driver.
-When:  3.6
-Why:   With the introduction of the NFC HCI and SHDL kernel layers, pn544.c
-       is being replaced by pn544_hci.c which is accessible through the netlink
-       and socket NFC APIs. Moreover, pn544.c is outdated and does not seem to
-       work properly with the latest Android stacks.
-       Having 2 drivers for the same hardware is confusing and as such we
-       should only keep the one following the kernel NFC APIs.
-Who:   Samuel Ortiz <sameo@linux.intel.com>
-
-----------------------------
-
 What:  setitimer accepts user NULL pointer (value)
 When:  3.6
 Why:   setitimer is not returning -EFAULT if user pointer is NULL. This
index 53cc13c82cb1d74646c310e78d01a3509841a669..eb30279763c1e26da00eb97867d2187eb8e64045 100644 (file)
@@ -4797,6 +4797,7 @@ M:        Lauro Ramos Venancio <lauro.venancio@openbossa.org>
 M:     Aloisio Almeida Jr <aloisio.almeida@openbossa.org>
 M:     Samuel Ortiz <sameo@linux.intel.com>
 L:     linux-wireless@vger.kernel.org
+L:     linux-nfc@lists.01.org (moderated for non-subscribers)
 S:     Maintained
 F:     net/nfc/
 F:     include/linux/nfc.h
index f7b0af7100cdf5cc0c074a09ddd842c0b459b715..b6b4b5ebd4c2560255b3a53bbceae0a814723e96 100644 (file)
@@ -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, },
index 9ea4627dc0c233a808f322816c2560fd9c145d3d..0d546b64be341239a5ee405970f166be47f83081 100644 (file)
@@ -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:
index 0c0838d9b56c2ec266c9ad92448787576a5170d6..0d26851d6e495a8624e611394709a1bf1246ef90 100644 (file)
@@ -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);
index 03b3acba61431a4cf7f0bfc0e5cadab2ef9b76ce..3f4bfc814dc7d5a0382635dbe16a41af413e59fe 100644 (file)
@@ -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;
index 2f510a87b28f90ae1c01370cb1df21882108868e..35a553a90616d85f07bf5c03d5d24e5373494ac6 100644 (file)
@@ -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);
index e5921d681ddb551677a0900d4c1d82d4e6fdc53a..debda27df9b0452e3d7f3e5d2b06855c1b7af1f4 100644 (file)
@@ -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) },
index 4ad7b35cfc0e1b46d0285e64d77aca460ae8b2bd..60abf596f60ea21f9354ae1b3d3608bb04e4142e 100644 (file)
@@ -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 ------ */
 
index 74e0966b3ead0bbcf3678ebea52ea6886a65baee..c8abce3d2d9c0618f2092c94398e3197d0396b27 100644 (file)
@@ -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;
 }
index ff6d589c34a5e900eff0eb498782f5597a6a8bee..cfc7679385890b6e0feaadee1462a1e85330cc59 100644 (file)
@@ -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--;
 
index 3f72595a60178a7f23ca45ac6418c6e5cb53faa6..d8b7aed6e4a96f6d6997ab9fac2a1ac84fbc4700 100644 (file)
@@ -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;
 }
index 6169fbd23ed10a527e26973c2e00f2bfe6bd82af..4521342c62cc37654ee1889b9ee177e395706f27 100644 (file)
@@ -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,
index a0a202de110953c0808539667f0f3bfacd57173b..9fd6d9a9942ec298b81be9a8e62697df1d13c037 100644 (file)
@@ -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 =
index df61a09adb6d8dfa1143ea1230319de8c4979b27..7a28538e6e05ba6c001e9b04aa913955a339ac44 100644 (file)
@@ -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;
index 01c90ed58453af446a76344b4d72558a6af702de..ab363f34b4df71c76f1fa9198c245e0bac28da7a 100644 (file)
@@ -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 */
index 86aeef4b9d7ee9295fe04c5533d3959a19474c9c..7089f8160ad5bb7f2a7377bbc0adae68229274b2 100644 (file)
@@ -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)
 {
index 56b1ebe79812d0d90b2fc6292592809fae9637c1..780f77775a9152ca078922cbd2a7754c9a019d24 100644 (file)
@@ -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,
index bbcfeb3b2a60ac90d046110d59161dd118b15e4a..664844c5d3d51ae8752514bb3976fa2074ba0afe 100644 (file)
@@ -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);
 }
index b5659cb688fe4fa4f9c016df68744a19bd180180..e65aad07d9eec1d9d2ed9d620f7656070766cdf9 100644 (file)
@@ -3562,9 +3562,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,
@@ -3629,6 +3629,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 */
@@ -3636,9 +3646,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,
@@ -3655,10 +3664,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)
index b2e39e8a21b540dd57606844d4ac384dc0eb8cbf..8dbb60b53f1aae5349e536e7759147c5d6f7ea07 100644 (file)
@@ -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);
index f3bef8d69edd81684664dbcf55ddb581760a37a8..30acf2869aa422c018651a0d850071c1aadc3b10 100644 (file)
@@ -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
index 0d800c62e227c4b10993228f3ec37bceb717a077..fc67844a14305898bfece85e7c9273b5f5780c6c 100644 (file)
@@ -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);
index fdabc9a28a9664f462c2b9a0b9c37feee25f9e56..9a48e3d2f231eadefcf15dc2604d891150b389fb 100644 (file)
 
 #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
 #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
 #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 */
index fa9e0932769c7d443e25712c304ea01cab836650..843e79f67ff25740745c16a09002100019bb13d6 100644 (file)
@@ -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},
index 96b8331ef9e72ab478fc72f689068264e8ba0144..8e27f4fb21fe06867467ed4123827b8d43dcbbd5 100644 (file)
@@ -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;
index 45f24220b16ea39059842226df42f0ca0136c08e..bf7d29ec1a87cac0f501354a2c9d18b553441c88 100644 (file)
@@ -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)
index ee6e50aebf8d3fe997803262d3dd9d6b80ff1277..924c4616c3d990dc7f03e46ed299bdc94396d765 100644 (file)
@@ -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);
index 61d096e3596f2dff4c392aa2f921e7848f5923be..ca78e33ca23ec1393dd6fa9b109a7443cf865cf8 100644 (file)
@@ -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;
index 265bf77598a268c60a4392165db2a12fde95324c..0f2b97f6b7390e32a920e0d645449e820d9d195a 100644 (file)
@@ -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 */
index 99cab44d2312afcd614fb317723f90735ef269a3..2cc08a7912abae9471fd92ef59a24b1a61c94acf 100644 (file)
@@ -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);
@@ -2539,7 +2543,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
index 0d17ce0b0ff42f0de8ac8e7370135a7089323cf7..17203b527507d412ec9ab01a5236382954912eae 100644 (file)
@@ -687,7 +687,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 {
@@ -731,6 +731,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];
index f3ce5ca2f1d318250e55d2262b0695bad2758069..fad3ccd5cd91aa8ab5b96603701275303a3f6392 100644 (file)
@@ -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);
index 3923ad933aefbcd27b45e978754341fd873bd391..31ab82e3ba85fdee932f1c8b2037c262037b1f24 100644 (file)
@@ -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;
index 8f0e8d9c2054a0de94db4b964641145e29149a80..8f51e9e358fdd17de8505697cd7fb76bede311aa 100644 (file)
@@ -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)
index a8f6126f6b2daf562beb7d84df7d5f6db6cc85cb..c0c599673eebef0a54eb14890fc40340a0b7223c 100644 (file)
@@ -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);
index 4b12c347d18828714484890539c99a81cec2a709..27ed80b5488133175a0b348a3f9752ebe69ed016 100644 (file)
@@ -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;
index f8676280dc36669fcd5775468beefc8378823753..e3b1b6e87760ed0e70ca77dfcb23ae3e4b9df982 100644 (file)
@@ -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;
index 18554ab76733cb591fccbebc8200feceeff2eac7..67997b39aba79f0d14c47ffdbe4e85248bece20b 100644 (file)
@@ -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 |
index 8e7e6928c93699bf9b7df35f9efc4c9b26928481..3b2c4c20e7fcfcccaa2a6706108473f805c0f13b 100644 (file)
@@ -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;
index e0b313c7f5ce48ced1512cc7a3c6c2fc85260515..70f21a4355329b9526b2fe45e3969d9b37d5b7b3 100644 (file)
@@ -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;
 }
 
index 4766d9f356966224d526aee1273848964bca0dca..55e489d2147d90cafadad708e7c5bdf228983ea5 100644 (file)
@@ -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);
index f6b862d779867f211b997c9eee4c3f1cbeadc696..fbecde73f9046047230125739f0cc362ee787df8 100644 (file)
@@ -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;
 }
index b784920532d31b3ae9deb1ca0453244efb010b39..fb508c2256ddc8a7244b61ad1e6b3fd13b41bf30 100644 (file)
@@ -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) */
 
index b08f3474d8e74e9e82b7d6c6998b4ff4d5c364cb..d7c76ce9d8cb3d74dccb79c863e5db1efd9a7446 100644 (file)
@@ -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)
index 4580ff34c2d0dc13deea12497d7b8bf605e3bb78..3564686add9a1099aa048c5ba4526f5052288abd 100644 (file)
@@ -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;
index 29bf78d264e096d9b81cb2efd3ac527a34d80d61..0d30afd8c672affe82ec26cac18ba5e0138d6ac2 100644 (file)
@@ -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
index c6d5aeb27a0211c444b8768edc7a6885f2602c2a..5fe6ec7f838e229667664d426ce7042e19cba6e4 100644 (file)
@@ -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);
 }
index 65cf8f92cb3e1407b1ec26389d74335efc66f0ee..e40cfe846a48903c976bebda15b8aea990f4ffd7 100644 (file)
 #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))
 
@@ -698,11 +710,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;
@@ -745,13 +757,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");
@@ -764,17 +773,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);
@@ -832,7 +842,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);
@@ -2591,11 +2611,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;
 
@@ -2609,7 +2631,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,
@@ -2840,10 +2875,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) {
@@ -2871,6 +2909,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);
 
@@ -2943,16 +2982,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);
        }
@@ -3037,18 +3069,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))
@@ -3227,6 +3251,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)
 {
@@ -3269,6 +3556,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
@@ -3290,6 +3582,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)
 {
@@ -3328,6 +3631,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);
@@ -3730,6 +4034,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)
@@ -3955,13 +4260,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;
@@ -3986,7 +4291,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;
@@ -4101,6 +4406,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));
@@ -4233,7 +4539,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);
@@ -4366,17 +4672,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);
 
@@ -4409,20 +4706,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;
-}
index 3b2129738d30cd112897e7d8e41a8d0d9643a75d..52e408ed6f41e35418a14c14ec1f77a2fd288363 100644 (file)
@@ -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 */
index 718da8d6d6583f1e148e9f927cbf912322007a0b..a744ea5a95599797023a0040401ab1816855f77f 100644 (file)
@@ -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);
index 1571505b1a38ba4903f6664cb0a25ada4e9a6e04..54aba474443867f463714e117f14a605ba251803 100644 (file)
@@ -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;
 }
index eb9987520d611f5e4a1d3851b1f57186fa8ab948..318ed3c9fe7499899d08fc3de1de917438690109 100644 (file)
@@ -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;
index e3467fa868996264f88e681edd8b5f6114fa5fe4..bb9f6252d28fad2d25340845f8f3a324e5d0fc56 100644 (file)
@@ -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;
index fe36a38f3505bd1232ac5aab3cbdfe9f7fca5ef4..cd9b6de4273e8c035a8bcdd39bf715316825db90 100644 (file)
@@ -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,
index 6d8d6dd7943fc3cd2e58e872071078be8f4bfc0b..2cb1efbc5ed1f3d80127f678e4c82e42046eb7c1 100644 (file)
@@ -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;
index 48d6d44c16d03053e36f5f04aa9e3baf1db8eaf7..198634b75ed0e0cb5cb732525b6c9049f1a4b422 100644 (file)
@@ -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;
 }
 
index 2153e4cc5572ede3f15c1bb8a7eb5c3f279b24a1..d1a86b66bc51ee6fab8e797e8aa458b9db3812ea 100644 (file)
@@ -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 */
 };
index 89bfb43f4946dc4a923c7fba4a06c8fba319db45..2a4675396707474fc0a0ced64009eddb967b4bec 100644 (file)
@@ -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,
index 71c79943e633782de0b47d0c9fb5e6a61d340d1d..401178f44a3b130a9de6832bf10bd3e9b6e4b529 100644 (file)
@@ -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);
 
index 498372008810b09b9e2b105a04c697c66b4fafce..17c8e5d82681022383d657da550924d48d0d9289 100644 (file)
 #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
index 848851177e7e1edbfee81c867c6dc2a767851408..d0c0fe7edc2f1f61b245ce7ebbfe49380139a641 100644 (file)
@@ -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);
@@ -1456,14 +1481,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
@@ -1551,9 +1578,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);
index 9d45b3bb974c2efad968af4827b40ce1b2218c2e..429ca3215fdbf3e0d48f82e3c1eb69381938fd46 100644 (file)
@@ -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);
index b9f7b3e6912db71e186680513cada3d79dc60109..c24824f8c8a123a3dcc5f8a0c751598aac5b42c5 100644 (file)
@@ -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)
index 1d8dd003e39617124ff87d92d2d3437391b7ba84..fa3a80fb8c01e5b4c194aacd32d66dec934307bb 100644 (file)
@@ -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;
 
index 9c1549ee4c09dee56452d385da2fd0f4e1d41233..b2ba262f8a139774d94432bc2f64855630856020 100644 (file)
@@ -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 {
index 12ceea47b4b447e7986a93f9d672e1ea47d01f4a..90b64b015447b7d28a439be80dc99efac03f5588 100644 (file)
@@ -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);
index d7ad2d4a069f31832a9bd404351b84075eef4096..731562f026f5df1dc9c64221d29ec8703fb8e99d 100644 (file)
@@ -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");
index 2969d5321ca6b08a67ecf5cbea6c92f300c3ef97..aadda99989c007838faf7eb5ca816d18904ea91c 100644 (file)
@@ -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) |
index 6458ab87717b625669074e9a637bf138e8e57af0..e3a2d9070cf655acdfb9758a525c5cc7a71575a9 100644 (file)
@@ -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,
index 68bca1456cdaf39433d361a767e78010100e29ce..479d756e275b388fc5d17b77bde4303e27117e48 100644 (file)
@@ -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,
index f95b5516c50aadaa5172bf25e0b41ba31b4e6b03..a12e84f892be1d9b13d1762a0369a7841af32645 100644 (file)
@@ -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,
index 9e09367c973922c11ffea6312c8877b63312bbc0..540c94f8505a9b734b9b09ef6ab814ca04b17bd0 100644 (file)
@@ -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) ||
index 391e08fa054bc1ea2bc48710a69287f175231a34..27829e1e2e38964b2085dc84e30f618b3f72f97e 100644 (file)
@@ -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,
index 603b65d6f28bfcb25b9a57222595a0fa6cbfff70..c9e9370eb789c04ec91fd97a2a85d8d21977a144 100644 (file)
@@ -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,
index 49375c86c33414cc132cc0cc099bb1636a4a9fc6..0751b35ef6dcd536ba51c3554dc4b58e359a9628 100644 (file)
@@ -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;
@@ -741,6 +740,14 @@ enum rt2x00_capability_flags {
        CAPABILITY_VCO_RECALIBRATION,
 };
 
+/*
+ * Interface combinations
+ */
+enum {
+       IF_COMB_AP = 0,
+       NUM_IF_COMB,
+};
+
 /*
  * rt2x00 device structure.
  */
@@ -867,6 +874,12 @@ struct rt2x00_dev {
        unsigned int intf_associated;
        unsigned int intf_beaconing;
 
+       /*
+        * Interface combinations
+        */
+       struct ieee80211_iface_limit if_limits_ap;
+       struct ieee80211_iface_combination if_combinations[NUM_IF_COMB];
+
        /*
         * Link quality
         */
index 10cf67267775ed93487183d24a9facc2bc9aa423..69097d1faeb676d97ddd27c7ba7dc3f575cd2f6f 100644 (file)
@@ -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.
  */
@@ -1125,6 +1153,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.
         */
index c3d0f2f87b6987ba0df7989e1516c007a9ef6479..98a9e48f8e4a38e852c54e8dbdef0c2369a753a4 100644 (file)
@@ -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
index 2673e058caaf62b7a3540f6f4b2156ba2a5e1ecf..d6582a2fa3534879614a23dd29435e747a0cca0c 100644 (file)
@@ -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,
index cfa9f37cccc20917f4b9be9fff67890d9e611287..e5eb43b3eee74f94db85349436f0ba1e15925059 100644 (file)
@@ -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,
index 3b20b73ee649bf46d62cc27f81c7a6f78366a148..ec857676c39ffaffcb6d55c25917ec9beb1192ea 100644 (file)
@@ -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---
index 473e44cef6122fdc4530fbdf9d796e29afc3f85a..bf05831fdf091d372f154c8f7b3ea9e023cdba64 100644 (file)
@@ -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
index e7fd4938f9bc2e36191de06f38fa30fd807eb307..50b1ee41afc60e2a3789f1a4e26ab12ff811ce4e 100644 (file)
@@ -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 ------ */
 
index d606f52fec842d5a613cff3f63719a97827178b4..97c440a8cd615798a1e61250628e0030ede37694 100644 (file)
@@ -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 (file)
index 724f65d..0000000
+++ /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);
index aa71807189ba3235a0720fc1660049507bf19039..c9c8570273ab5a58c782e9fe953339e091042d2a 100644 (file)
@@ -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)
index e02fc682bb6850600258b622da1a80ef52260dd7..2385119f8bb016c003f5bf2b638b412a6ddde7f9 100644 (file)
@@ -1934,36 +1934,6 @@ static inline bool ieee80211_is_public_action(struct ieee80211_hdr *hdr,
        return mgmt->u.action.category == WLAN_CATEGORY_PUBLIC;
 }
 
-/**
- * ieee80211_fhss_chan_to_freq - get channel frequency
- * @channel: the FHSS channel
- *
- * Convert IEEE802.11 FHSS channel to frequency (MHz)
- * Ref IEEE 802.11-2007 section 14.6
- */
-static inline int ieee80211_fhss_chan_to_freq(int channel)
-{
-       if ((channel > 1) && (channel < 96))
-               return channel + 2400;
-       else
-               return -1;
-}
-
-/**
- * ieee80211_freq_to_fhss_chan - get channel
- * @freq: the channels frequency
- *
- * Convert frequency (MHz) to IEEE802.11 FHSS channel
- * Ref IEEE 802.11-2007 section 14.6
- */
-static inline int ieee80211_freq_to_fhss_chan(int freq)
-{
-       if ((freq > 2401) && (freq < 2496))
-               return freq - 2400;
-       else
-               return -1;
-}
-
 /**
  * ieee80211_dsss_chan_to_freq - get channel center frequency
  * @channel: the DSSS channel
@@ -2000,56 +1970,6 @@ static inline int ieee80211_freq_to_dsss_chan(int freq)
                return -1;
 }
 
-/* Convert IEEE802.11 HR DSSS channel to frequency (MHz) and back
- * Ref IEEE 802.11-2007 section 18.4.6.2
- *
- * The channels and frequencies are the same as those defined for DSSS
- */
-#define ieee80211_hr_chan_to_freq(chan) ieee80211_dsss_chan_to_freq(chan)
-#define ieee80211_freq_to_hr_chan(freq) ieee80211_freq_to_dsss_chan(freq)
-
-/* Convert IEEE802.11 ERP channel to frequency (MHz) and back
- * Ref IEEE 802.11-2007 section 19.4.2
- */
-#define ieee80211_erp_chan_to_freq(chan) ieee80211_hr_chan_to_freq(chan)
-#define ieee80211_freq_to_erp_chan(freq) ieee80211_freq_to_hr_chan(freq)
-
-/**
- * ieee80211_ofdm_chan_to_freq - get channel center frequency
- * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz
- * @channel: the OFDM channel
- *
- * Convert IEEE802.11 OFDM channel to center frequency (MHz)
- * Ref IEEE 802.11-2007 section 17.3.8.3.2
- */
-static inline int ieee80211_ofdm_chan_to_freq(int s_freq, int channel)
-{
-       if ((channel > 0) && (channel <= 200) &&
-           (s_freq >= 4000))
-               return s_freq + (channel * 5);
-       else
-               return -1;
-}
-
-/**
- * ieee80211_freq_to_ofdm_channel - get channel
- * @s_freq: starting frequency == (dotChannelStartingFactor/2) MHz
- * @freq: the frequency
- *
- * Convert frequency (MHz) to IEEE802.11 OFDM channel
- * Ref IEEE 802.11-2007 section 17.3.8.3.2
- *
- * This routine selects the channel with the closest center frequency.
- */
-static inline int ieee80211_freq_to_ofdm_chan(int s_freq, int freq)
-{
-       if ((freq > (s_freq + 2)) && (freq <= (s_freq + 1202)) &&
-           (s_freq >= 4000))
-               return (freq + 2 - s_freq) / 5;
-       else
-               return -1;
-}
-
 /**
  * ieee80211_tu_to_usec - convert time units (TU) to microseconds
  * @tu: the TUs
index 6189f27e305b551a3d47bbbc8d9935041917e246..d908d17da56d677e62a07011c852cc38cdf824cf 100644 (file)
@@ -183,4 +183,15 @@ struct sockaddr_nfc_llcp {
 
 #define NFC_HEADER_SIZE 1
 
+/**
+ * Pseudo-header info for raw socket packets
+ * First byte is the adapter index
+ * Second byte contains flags
+ *  - 0x01 - Direction (0=RX, 1=TX)
+ *  - 0x02-0x80 - Reserved
+ **/
+#define NFC_LLCP_RAW_HEADER_SIZE       2
+#define NFC_LLCP_DIRECTION_RX          0x00
+#define NFC_LLCP_DIRECTION_TX          0x01
+
 #endif /*__LINUX_NFC_H */
index 458416279347f054e1c0e7b1b6a50be768b391a2..7df9b500c80493e944591a2192b07253f1b42669 100644 (file)
  * @NL80211_CMD_STOP_P2P_DEVICE: Stop the given P2P Device, identified by
  *     its %NL80211_ATTR_WDEV identifier.
  *
+ * @NL80211_CMD_CONN_FAILED: connection request to an AP failed; used to
+ *     notify userspace that AP has rejected the connection request from a
+ *     station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON
+ *     is used for this.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -719,6 +724,8 @@ enum nl80211_commands {
        NL80211_CMD_START_P2P_DEVICE,
        NL80211_CMD_STOP_P2P_DEVICE,
 
+       NL80211_CMD_CONN_FAILED,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -1262,6 +1269,10 @@ enum nl80211_commands {
  *     was used to provide the hint. For the different types of
  *     allowed user regulatory hints see nl80211_user_reg_hint_type.
  *
+ * @NL80211_ATTR_CONN_FAILED_REASON: The reason for which AP has rejected
+ *     the connection request from a station. nl80211_connect_failed_reason
+ *     enum has different reasons of connection failure.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1517,6 +1528,8 @@ enum nl80211_attrs {
 
        NL80211_ATTR_USER_REG_HINT_TYPE,
 
+       NL80211_ATTR_CONN_FAILED_REASON,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -3045,4 +3058,15 @@ enum nl80211_probe_resp_offload_support_attr {
        NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U =     1<<3,
 };
 
+/**
+ * enum nl80211_connect_failed_reason - connection request failed reasons
+ * @NL80211_CONN_FAIL_MAX_CLIENTS: Maximum number of clients that can be
+ *     handled by the AP is reached.
+ * @NL80211_CONN_FAIL_BLOCKED_CLIENT: Client's MAC is in the AP's blocklist.
+ */
+enum nl80211_connect_failed_reason {
+       NL80211_CONN_FAIL_MAX_CLIENTS,
+       NL80211_CONN_FAIL_BLOCKED_CLIENT,
+};
+
 #endif /* __LINUX_NL80211_H */
index 23cf413e2acf3d72073c183fb2dc4dab3d16fa45..76b2b6bdcf36a281d558925a2a7a707ca2e1dddf 100644 (file)
@@ -302,8 +302,11 @@ enum {
 
 /* ---- HCI Error Codes ---- */
 #define HCI_ERROR_AUTH_FAILURE         0x05
+#define HCI_ERROR_CONNECTION_TIMEOUT   0x08
 #define HCI_ERROR_REJ_BAD_ADDR         0x0f
 #define HCI_ERROR_REMOTE_USER_TERM     0x13
+#define HCI_ERROR_REMOTE_LOW_RESOURCES 0x14
+#define HCI_ERROR_REMOTE_POWER_OFF     0x15
 #define HCI_ERROR_LOCAL_HOST_TERM      0x16
 #define HCI_ERROR_PAIRING_NOT_ALLOWED  0x18
 
@@ -1246,6 +1249,24 @@ struct hci_ev_simple_pair_complete {
        bdaddr_t bdaddr;
 } __packed;
 
+#define HCI_EV_USER_PASSKEY_NOTIFY     0x3b
+struct hci_ev_user_passkey_notify {
+       bdaddr_t        bdaddr;
+       __le32          passkey;
+} __packed;
+
+#define HCI_KEYPRESS_STARTED           0
+#define HCI_KEYPRESS_ENTERED           1
+#define HCI_KEYPRESS_ERASED            2
+#define HCI_KEYPRESS_CLEARED           3
+#define HCI_KEYPRESS_COMPLETED         4
+
+#define HCI_EV_KEYPRESS_NOTIFY         0x3c
+struct hci_ev_keypress_notify {
+       bdaddr_t        bdaddr;
+       __u8            type;
+} __packed;
+
 #define HCI_EV_REMOTE_HOST_FEATURES    0x3d
 struct hci_ev_remote_host_features {
        bdaddr_t bdaddr;
index 41d943926d2c3aa063ed5297b8ffef69ab1dabcd..e7d454609881a30d929ec8aa613390df157c8b43 100644 (file)
@@ -303,6 +303,8 @@ struct hci_conn {
        __u8            pin_length;
        __u8            enc_key_size;
        __u8            io_capability;
+       __u32           passkey_notify;
+       __u8            passkey_entered;
        __u16           disc_timeout;
        unsigned long   flags;
 
@@ -428,15 +430,6 @@ static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
               test_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
 }
 
-static inline void hci_conn_hash_init(struct hci_dev *hdev)
-{
-       struct hci_conn_hash *h = &hdev->conn_hash;
-       INIT_LIST_HEAD(&h->list);
-       h->acl_num = 0;
-       h->sco_num = 0;
-       h->le_num = 0;
-}
-
 static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
 {
        struct hci_conn_hash *h = &hdev->conn_hash;
@@ -551,9 +544,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
        return NULL;
 }
 
-void hci_acl_connect(struct hci_conn *conn);
 void hci_acl_disconn(struct hci_conn *conn, __u8 reason);
-void hci_add_sco(struct hci_conn *conn, __u16 handle);
 void hci_setup_sync(struct hci_conn *conn, __u16 handle);
 void hci_sco_setup(struct hci_conn *conn, __u8 status);
 
@@ -563,7 +554,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev);
 void hci_conn_check_pending(struct hci_dev *hdev);
 
 struct hci_chan *hci_chan_create(struct hci_conn *conn);
-int hci_chan_del(struct hci_chan *chan);
+void hci_chan_del(struct hci_chan *chan);
 void hci_chan_list_flush(struct hci_conn *conn);
 
 struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
@@ -614,11 +605,17 @@ static inline void hci_conn_put(struct hci_conn *conn)
 /* ----- HCI Devices ----- */
 static inline void hci_dev_put(struct hci_dev *d)
 {
+       BT_DBG("%s orig refcnt %d", d->name,
+              atomic_read(&d->dev.kobj.kref.refcount));
+
        put_device(&d->dev);
 }
 
 static inline struct hci_dev *hci_dev_hold(struct hci_dev *d)
 {
+       BT_DBG("%s orig refcnt %d", d->name,
+              atomic_read(&d->dev.kobj.kref.refcount));
+
        get_device(&d->dev);
        return d;
 }
@@ -1004,7 +1001,7 @@ int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                          u8 addr_type, u32 flags, u8 *name, u8 name_len,
                          u8 *dev_class);
 int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                            u8 link_type, u8 addr_type);
+                            u8 link_type, u8 addr_type, u8 reason);
 int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
                           u8 link_type, u8 addr_type, u8 status);
 int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
@@ -1027,6 +1024,9 @@ int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                     u8 link_type, u8 addr_type, u8 status);
 int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                         u8 link_type, u8 addr_type, u8 status);
+int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                            u8 link_type, u8 addr_type, u32 passkey,
+                            u8 entered);
 int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                     u8 addr_type, u8 status);
 int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
index d206296137e2b4fb50aa49bcee18dae9f0d687fb..7ed8e356425a16dc33c5afd5e4a80eaafdd6ea64 100644 (file)
@@ -433,11 +433,10 @@ struct l2cap_chan {
        struct sock *sk;
 
        struct l2cap_conn       *conn;
+       struct kref     kref;
 
        __u8            state;
 
-       atomic_t        refcnt;
-
        __le16          psm;
        __u16           dcid;
        __u16           scid;
index 4348ee8bda6993a15193a4e1e6adb640c9512e1f..22980a7c38730f53305b181e338dc29009cf62d6 100644 (file)
@@ -405,7 +405,16 @@ struct mgmt_ev_device_connected {
        __u8    eir[0];
 } __packed;
 
+#define MGMT_DEV_DISCONN_UNKNOWN       0x00
+#define MGMT_DEV_DISCONN_TIMEOUT       0x01
+#define MGMT_DEV_DISCONN_LOCAL_HOST    0x02
+#define MGMT_DEV_DISCONN_REMOTE                0x03
+
 #define MGMT_EV_DEVICE_DISCONNECTED    0x000C
+struct mgmt_ev_device_disconnected {
+       struct mgmt_addr_info addr;
+       __u8    reason;
+} __packed;
 
 #define MGMT_EV_CONNECT_FAILED         0x000D
 struct mgmt_ev_connect_failed {
@@ -469,3 +478,10 @@ struct mgmt_ev_device_unblocked {
 struct mgmt_ev_device_unpaired {
        struct mgmt_addr_info addr;
 } __packed;
+
+#define MGMT_EV_PASSKEY_NOTIFY         0x0017
+struct mgmt_ev_passkey_notify {
+       struct mgmt_addr_info addr;
+       __le32  passkey;
+       __u8    entered;
+} __packed;
index a9a2be78e73ca856ab99e36e5f964cf4bd3ed739..1b49890822449df661d576219181b10c64d57a6c 100644 (file)
@@ -1580,9 +1580,7 @@ struct cfg80211_gtk_rekey_data {
  * @set_cqm_txe_config: Configure connection quality monitor TX error
  *     thresholds.
  * @sched_scan_start: Tell the driver to start a scheduled scan.
- * @sched_scan_stop: Tell the driver to stop an ongoing scheduled
- *     scan.  The driver_initiated flag specifies whether the driver
- *     itself has informed that the scan has stopped.
+ * @sched_scan_stop: Tell the driver to stop an ongoing scheduled scan.
  *
  * @mgmt_frame_register: Notify driver that a management frame type was
  *     registered. Note that this callback may not sleep, and cannot run
@@ -1630,7 +1628,7 @@ struct cfg80211_ops {
        void    (*set_wakeup)(struct wiphy *wiphy, bool enabled);
 
        struct wireless_dev * (*add_virtual_intf)(struct wiphy *wiphy,
-                                                 char *name,
+                                                 const char *name,
                                                  enum nl80211_iftype type,
                                                  u32 *flags,
                                                  struct vif_params *params);
@@ -3362,6 +3360,25 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
  */
 void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp);
 
+/**
+ * cfg80211_conn_failed - connection request failed notification
+ *
+ * @dev: the netdev
+ * @mac_addr: the station's address
+ * @reason: the reason for connection failure
+ * @gfp: allocation flags
+ *
+ * Whenever a station tries to connect to an AP and if the station
+ * could not connect to the AP as the AP has rejected the connection
+ * for some reasons, this function is called.
+ *
+ * The reason for connection failure can be any of the value from
+ * nl80211_connect_failed_reason enum
+ */
+void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
+                         enum nl80211_connect_failed_reason reason,
+                         gfp_t gfp);
+
 /**
  * cfg80211_rx_mgmt - notification of received, unprocessed management frame
  * @wdev: wireless device receiving the frame
index 71f8262fc1dfc2705861527e1409002cd8ea64af..82558c8decf86e7cf720d6b7a190455c31e60be3 100644 (file)
@@ -973,21 +973,29 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
  *     generation in software.
  * @IEEE80211_KEY_FLAG_PAIRWISE: Set by mac80211, this flag indicates
  *     that the key is pairwise rather then a shared key.
- * @IEEE80211_KEY_FLAG_SW_MGMT: This flag should be set by the driver for a
+ * @IEEE80211_KEY_FLAG_SW_MGMT_TX: This flag should be set by the driver for a
  *     CCMP key if it requires CCMP encryption of management frames (MFP) to
  *     be done in software.
  * @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver
  *     if space should be prepared for the IV, but the IV
  *     itself should not be generated. Do not set together with
  *     @IEEE80211_KEY_FLAG_GENERATE_IV on the same key.
+ * @IEEE80211_KEY_FLAG_RX_MGMT: This key will be used to decrypt received
+ *     management frames. The flag can help drivers that have a hardware
+ *     crypto implementation that doesn't deal with management frames
+ *     properly by allowing them to not upload the keys to hardware and
+ *     fall back to software crypto. Note that this flag deals only with
+ *     RX, if your crypto engine can't deal with TX you can also set the
+ *     %IEEE80211_KEY_FLAG_SW_MGMT_TX flag to encrypt such frames in SW.
  */
 enum ieee80211_key_flags {
        IEEE80211_KEY_FLAG_WMM_STA      = 1<<0,
        IEEE80211_KEY_FLAG_GENERATE_IV  = 1<<1,
        IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2,
        IEEE80211_KEY_FLAG_PAIRWISE     = 1<<3,
-       IEEE80211_KEY_FLAG_SW_MGMT      = 1<<4,
+       IEEE80211_KEY_FLAG_SW_MGMT_TX   = 1<<4,
        IEEE80211_KEY_FLAG_PUT_IV_SPACE = 1<<5,
+       IEEE80211_KEY_FLAG_RX_MGMT      = 1<<6,
 };
 
 /**
index f5169b04f0829aa10a438fcfb33e336b4cc26f33..e900072950cb8cf5635e4a106389e34bd5df21f1 100644 (file)
@@ -30,6 +30,11 @@ struct nfc_hci_ops {
        int (*open) (struct nfc_hci_dev *hdev);
        void (*close) (struct nfc_hci_dev *hdev);
        int (*hci_ready) (struct nfc_hci_dev *hdev);
+       /*
+        * xmit must always send the complete buffer before
+        * returning. Returned result must be 0 for success
+        * or negative for failure.
+        */
        int (*xmit) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
        int (*start_poll) (struct nfc_hci_dev *hdev,
                           u32 im_protocols, u32 tm_protocols);
@@ -38,8 +43,8 @@ struct nfc_hci_ops {
        int (*complete_target_discovered) (struct nfc_hci_dev *hdev, u8 gate,
                                           struct nfc_target *target);
        int (*data_exchange) (struct nfc_hci_dev *hdev,
-                             struct nfc_target *target,
-                             struct sk_buff *skb, struct sk_buff **res_skb);
+                             struct nfc_target *target, struct sk_buff *skb,
+                             data_exchange_cb_t cb, void *cb_context);
        int (*check_presence)(struct nfc_hci_dev *hdev,
                              struct nfc_target *target);
 };
@@ -74,7 +79,6 @@ struct nfc_hci_dev {
 
        struct list_head msg_tx_queue;
 
-       struct workqueue_struct *msg_tx_wq;
        struct work_struct msg_tx_work;
 
        struct timer_list cmd_timer;
@@ -82,13 +86,14 @@ struct nfc_hci_dev {
 
        struct sk_buff_head rx_hcp_frags;
 
-       struct workqueue_struct *msg_rx_wq;
        struct work_struct msg_rx_work;
 
        struct sk_buff_head msg_rx_queue;
 
        struct nfc_hci_ops *ops;
 
+       struct nfc_llc *llc;
+
        struct nfc_hci_init_data init_data;
 
        void *clientdata;
@@ -105,12 +110,17 @@ struct nfc_hci_dev {
        u8 hw_mpw;
        u8 hw_software;
        u8 hw_bsid;
+
+       int async_cb_type;
+       data_exchange_cb_t async_cb;
+       void *async_cb_context;
 };
 
 /* hci device allocation */
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
                                            u32 protocols,
+                                           const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
                                            int max_link_payload);
@@ -202,6 +212,9 @@ int nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
                      const u8 *param, size_t param_len);
 int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
                     const u8 *param, size_t param_len, struct sk_buff **skb);
+int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
+                          const u8 *param, size_t param_len,
+                          data_exchange_cb_t cb, void *cb_context);
 int nfc_hci_send_response(struct nfc_hci_dev *hdev, u8 gate, u8 response,
                          const u8 *param, size_t param_len);
 int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
diff --git a/include/net/nfc/llc.h b/include/net/nfc/llc.h
new file mode 100644 (file)
index 0000000..400ab7a
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Link Layer Control manager public interface
+ *
+ * Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions 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.
+ */
+
+#ifndef __NFC_LLC_H_
+#define __NFC_LLC_H_
+
+#include <net/nfc/hci.h>
+#include <linux/skbuff.h>
+
+#define LLC_NOP_NAME "nop"
+#define LLC_SHDLC_NAME "shdlc"
+
+typedef void (*rcv_to_hci_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
+typedef int (*xmit_to_drv_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
+typedef void (*llc_failure_t) (struct nfc_hci_dev *hdev, int err);
+
+struct nfc_llc;
+
+struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev,
+                                xmit_to_drv_t xmit_to_drv,
+                                rcv_to_hci_t rcv_to_hci, int tx_headroom,
+                                int tx_tailroom, llc_failure_t llc_failure);
+void nfc_llc_free(struct nfc_llc *llc);
+
+void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom,
+                                  int *rx_tailroom);
+
+
+int nfc_llc_start(struct nfc_llc *llc);
+int nfc_llc_stop(struct nfc_llc *llc);
+void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb);
+int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb);
+
+int nfc_llc_init(void);
+void nfc_llc_exit(void);
+
+#endif /* __NFC_LLC_H_ */
index 276094b91d7ced880477730fd487f162cf5b4a6e..88785e5c6b2cf6d2c32a8af553259a9d21678025 100644 (file)
@@ -32,6 +32,7 @@
 #define NCI_MAX_NUM_MAPPING_CONFIGS                            10
 #define NCI_MAX_NUM_RF_CONFIGS                                 10
 #define NCI_MAX_NUM_CONN                                       10
+#define NCI_MAX_PARAM_LEN                                      251
 
 /* NCI Status Codes */
 #define NCI_STATUS_OK                                          0x00
 #define NCI_RF_INTERFACE_ISO_DEP                               0x02
 #define NCI_RF_INTERFACE_NFC_DEP                               0x03
 
+/* NCI Configuration Parameter Tags */
+#define NCI_PN_ATR_REQ_GEN_BYTES                               0x29
+
 /* NCI Reset types */
 #define NCI_RESET_TYPE_KEEP_CONFIG                             0x00
 #define NCI_RESET_TYPE_RESET_CONFIG                            0x01
@@ -188,6 +192,18 @@ struct nci_core_reset_cmd {
 
 #define NCI_OP_CORE_INIT_CMD           nci_opcode_pack(NCI_GID_CORE, 0x01)
 
+#define NCI_OP_CORE_SET_CONFIG_CMD     nci_opcode_pack(NCI_GID_CORE, 0x02)
+struct set_config_param {
+       __u8    id;
+       __u8    len;
+       __u8    val[NCI_MAX_PARAM_LEN];
+} __packed;
+
+struct nci_core_set_config_cmd {
+       __u8    num_params;
+       struct  set_config_param param; /* support 1 param per cmd is enough */
+} __packed;
+
 #define NCI_OP_RF_DISCOVER_MAP_CMD     nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
 struct disc_map_config {
        __u8    rf_protocol;
@@ -252,6 +268,13 @@ struct nci_core_init_rsp_2 {
        __le32  manufact_specific_info;
 } __packed;
 
+#define NCI_OP_CORE_SET_CONFIG_RSP     nci_opcode_pack(NCI_GID_CORE, 0x02)
+struct nci_core_set_config_rsp {
+       __u8    status;
+       __u8    num_params;
+       __u8    params_id[0];   /* variable size array */
+} __packed;
+
 #define NCI_OP_RF_DISCOVER_MAP_RSP     nci_opcode_pack(NCI_GID_RF_MGMT, 0x00)
 
 #define NCI_OP_RF_DISCOVER_RSP         nci_opcode_pack(NCI_GID_RF_MGMT, 0x03)
@@ -328,6 +351,11 @@ struct activation_params_nfcb_poll_iso_dep {
        __u8    attrib_res[50];
 };
 
+struct activation_params_poll_nfc_dep {
+       __u8    atr_res_len;
+       __u8    atr_res[63];
+};
+
 struct nci_rf_intf_activated_ntf {
        __u8    rf_discovery_id;
        __u8    rf_interface;
@@ -351,6 +379,7 @@ struct nci_rf_intf_activated_ntf {
        union {
                struct activation_params_nfca_poll_iso_dep nfca_poll_iso_dep;
                struct activation_params_nfcb_poll_iso_dep nfcb_poll_iso_dep;
+               struct activation_params_poll_nfc_dep poll_nfc_dep;
        } activation_params;
 
 } __packed;
index feba74027ff8bc18674c68625f8f6e5a9cd6562f..d705d867494987b30da4272fe4c1967a7884412a 100644 (file)
@@ -54,6 +54,7 @@ enum nci_state {
 /* NCI timeouts */
 #define NCI_RESET_TIMEOUT                      5000
 #define NCI_INIT_TIMEOUT                       5000
+#define NCI_SET_CONFIG_TIMEOUT                 5000
 #define NCI_RF_DISC_TIMEOUT                    5000
 #define NCI_RF_DISC_SELECT_TIMEOUT             5000
 #define NCI_RF_DEACTIVATE_TIMEOUT              30000
@@ -137,6 +138,10 @@ struct nci_dev {
        data_exchange_cb_t      data_exchange_cb;
        void                    *data_exchange_cb_context;
        struct sk_buff          *rx_data_reassembly;
+
+       /* stored during intf_activated_ntf */
+       __u8 remote_gb[NFC_MAX_GT_LEN];
+       __u8 remote_gb_len;
 };
 
 /* ----- NCI Devices ----- */
index 6735909f826d565b51c4e29a62add003561ed026..f05b10682c9d9bbe0aa65579fc73bd8adf1747df 100644 (file)
@@ -72,6 +72,7 @@ struct nfc_ops {
 
 #define NFC_TARGET_IDX_ANY -1
 #define NFC_MAX_GT_LEN 48
+#define NFC_ATR_RES_GT_OFFSET 15
 
 struct nfc_target {
        u32 idx;
@@ -112,7 +113,6 @@ struct nfc_dev {
        int tx_tailroom;
 
        struct timer_list check_pres_timer;
-       struct workqueue_struct *check_pres_wq;
        struct work_struct check_pres_work;
 
        struct nfc_ops *ops;
diff --git a/include/net/nfc/shdlc.h b/include/net/nfc/shdlc.h
deleted file mode 100644 (file)
index 35e930d..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2012  Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the
- * Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef __NFC_SHDLC_H
-#define __NFC_SHDLC_H
-
-struct nfc_shdlc;
-
-struct nfc_shdlc_ops {
-       int (*open) (struct nfc_shdlc *shdlc);
-       void (*close) (struct nfc_shdlc *shdlc);
-       int (*hci_ready) (struct nfc_shdlc *shdlc);
-       int (*xmit) (struct nfc_shdlc *shdlc, struct sk_buff *skb);
-       int (*start_poll) (struct nfc_shdlc *shdlc,
-                          u32 im_protocols, u32 tm_protocols);
-       int (*target_from_gate) (struct nfc_shdlc *shdlc, u8 gate,
-                                struct nfc_target *target);
-       int (*complete_target_discovered) (struct nfc_shdlc *shdlc, u8 gate,
-                                          struct nfc_target *target);
-       int (*data_exchange) (struct nfc_shdlc *shdlc,
-                             struct nfc_target *target,
-                             struct sk_buff *skb, struct sk_buff **res_skb);
-       int (*check_presence)(struct nfc_shdlc *shdlc,
-                             struct nfc_target *target);
-};
-
-enum shdlc_state {
-       SHDLC_DISCONNECTED = 0,
-       SHDLC_CONNECTING = 1,
-       SHDLC_NEGOCIATING = 2,
-       SHDLC_CONNECTED = 3
-};
-
-struct nfc_shdlc {
-       struct mutex state_mutex;
-       enum shdlc_state state;
-       int hard_fault;
-
-       struct nfc_hci_dev *hdev;
-
-       wait_queue_head_t *connect_wq;
-       int connect_tries;
-       int connect_result;
-       struct timer_list connect_timer;/* aka T3 in spec 10.6.1 */
-
-       u8 w;                           /* window size */
-       bool srej_support;
-
-       struct timer_list t1_timer;     /* send ack timeout */
-       bool t1_active;
-
-       struct timer_list t2_timer;     /* guard/retransmit timeout */
-       bool t2_active;
-
-       int ns;                         /* next seq num for send */
-       int nr;                         /* next expected seq num for receive */
-       int dnr;                        /* oldest sent unacked seq num */
-
-       struct sk_buff_head rcv_q;
-
-       struct sk_buff_head send_q;
-       bool rnr;                       /* other side is not ready to receive */
-
-       struct sk_buff_head ack_pending_q;
-
-       struct workqueue_struct *sm_wq;
-       struct work_struct sm_work;
-
-       struct nfc_shdlc_ops *ops;
-
-       int client_headroom;
-       int client_tailroom;
-
-       void *clientdata;
-};
-
-void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb);
-
-struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops,
-                                    struct nfc_hci_init_data *init_data,
-                                    u32 protocols,
-                                    int tx_headroom, int tx_tailroom,
-                                    int max_link_payload, const char *devname);
-
-void nfc_shdlc_free(struct nfc_shdlc *shdlc);
-
-void nfc_shdlc_set_clientdata(struct nfc_shdlc *shdlc, void *clientdata);
-void *nfc_shdlc_get_clientdata(struct nfc_shdlc *shdlc);
-struct nfc_hci_dev *nfc_shdlc_get_hci_dev(struct nfc_shdlc *shdlc);
-
-#endif /* __NFC_SHDLC_H */
index 58f9762b339aa7809554e9a7990970fff9a22231..9d49ee6d72190c8f9b727ed98ee1ba9f0c256f48 100644 (file)
@@ -567,8 +567,6 @@ static void bt_seq_stop(struct seq_file *seq, void *v)
 
 static int bt_seq_show(struct seq_file *seq, void *v)
 {
-       struct sock *sk;
-       struct bt_sock *bt;
        struct bt_seq_state *s = seq->private;
        struct bt_sock_list *l = s->l;
        bdaddr_t src_baswapped, dst_baswapped;
@@ -583,8 +581,8 @@ static int bt_seq_show(struct seq_file *seq, void *v)
 
                seq_putc(seq, '\n');
        } else {
-               sk = sk_entry(v);
-               bt = bt_sk(sk);
+               struct sock *sk = sk_entry(v);
+               struct bt_sock *bt = bt_sk(sk);
                baswap(&src_baswapped, &bt->src);
                baswap(&dst_baswapped, &bt->dst);
 
@@ -624,7 +622,7 @@ static int bt_seq_open(struct inode *inode, struct file *file)
        sk_list = PDE(inode)->data;
        s = __seq_open_private(file, &bt_seq_ops,
                               sizeof(struct bt_seq_state));
-       if (s == NULL)
+       if (!s)
                return -ENOMEM;
 
        s->l = sk_list;
@@ -646,7 +644,7 @@ int bt_procfs_init(struct module* module, struct net *net, const char *name,
        sk_list->fops.release   = seq_release_private;
 
        pde = proc_net_fops_create(net, name, 0, &sk_list->fops);
-       if (pde == NULL)
+       if (!pde)
                return -ENOMEM;
 
        pde->data = sk_list;
index 3c094e78dde98cafed3ac893abd3b2fa86b76a92..b9196a44f7598bf33b0c2bff6d0764eeeba8fc11 100644 (file)
@@ -31,7 +31,7 @@
 #include <net/bluetooth/a2mp.h>
 #include <net/bluetooth/smp.h>
 
-static void hci_le_connect(struct hci_conn *conn)
+static void hci_le_create_connection(struct hci_conn *conn)
 {
        struct hci_dev *hdev = conn->hdev;
        struct hci_cp_le_create_conn cp;
@@ -55,12 +55,12 @@ static void hci_le_connect(struct hci_conn *conn)
        hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
 }
 
-static void hci_le_connect_cancel(struct hci_conn *conn)
+static void hci_le_create_connection_cancel(struct hci_conn *conn)
 {
        hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
 }
 
-void hci_acl_connect(struct hci_conn *conn)
+static void hci_acl_create_connection(struct hci_conn *conn)
 {
        struct hci_dev *hdev = conn->hdev;
        struct inquiry_entry *ie;
@@ -104,7 +104,7 @@ void hci_acl_connect(struct hci_conn *conn)
        hci_send_cmd(hdev, HCI_OP_CREATE_CONN, sizeof(cp), &cp);
 }
 
-static void hci_acl_connect_cancel(struct hci_conn *conn)
+static void hci_acl_create_connection_cancel(struct hci_conn *conn)
 {
        struct hci_cp_create_conn_cancel cp;
 
@@ -130,7 +130,7 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason)
        hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
 }
 
-void hci_add_sco(struct hci_conn *conn, __u16 handle)
+static void hci_add_sco(struct hci_conn *conn, __u16 handle)
 {
        struct hci_dev *hdev = conn->hdev;
        struct hci_cp_add_sco cp;
@@ -246,9 +246,9 @@ static void hci_conn_timeout(struct work_struct *work)
        case BT_CONNECT2:
                if (conn->out) {
                        if (conn->type == ACL_LINK)
-                               hci_acl_connect_cancel(conn);
+                               hci_acl_create_connection_cancel(conn);
                        else if (conn->type == LE_LINK)
-                               hci_le_connect_cancel(conn);
+                               hci_le_create_connection_cancel(conn);
                }
                break;
        case BT_CONFIG:
@@ -471,40 +471,37 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
 }
 EXPORT_SYMBOL(hci_get_route);
 
-/* Create SCO, ACL or LE connection.
- * Device _must_ be locked */
-struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
-                            __u8 dst_type, __u8 sec_level, __u8 auth_type)
+static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
+                                   u8 dst_type, u8 sec_level, u8 auth_type)
 {
-       struct hci_conn *acl;
-       struct hci_conn *sco;
        struct hci_conn *le;
 
-       BT_DBG("%s dst %s", hdev->name, batostr(dst));
+       le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
+       if (!le) {
+               le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
+               if (le)
+                       return ERR_PTR(-EBUSY);
 
-       if (type == LE_LINK) {
-               le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
-               if (!le) {
-                       le = hci_conn_hash_lookup_state(hdev, LE_LINK,
-                                                       BT_CONNECT);
-                       if (le)
-                               return ERR_PTR(-EBUSY);
+               le = hci_conn_add(hdev, LE_LINK, dst);
+               if (!le)
+                       return ERR_PTR(-ENOMEM);
 
-                       le = hci_conn_add(hdev, LE_LINK, dst);
-                       if (!le)
-                               return ERR_PTR(-ENOMEM);
+               le->dst_type = bdaddr_to_le(dst_type);
+               hci_le_create_connection(le);
+       }
 
-                       le->dst_type = bdaddr_to_le(dst_type);
-                       hci_le_connect(le);
-               }
+       le->pending_sec_level = sec_level;
+       le->auth_type = auth_type;
 
-               le->pending_sec_level = sec_level;
-               le->auth_type = auth_type;
+       hci_conn_hold(le);
 
-               hci_conn_hold(le);
+       return le;
+}
 
-               return le;
-       }
+static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
+                                               u8 sec_level, u8 auth_type)
+{
+       struct hci_conn *acl;
 
        acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
        if (!acl) {
@@ -519,10 +516,20 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
                acl->sec_level = BT_SECURITY_LOW;
                acl->pending_sec_level = sec_level;
                acl->auth_type = auth_type;
-               hci_acl_connect(acl);
+               hci_acl_create_connection(acl);
        }
 
-       if (type == ACL_LINK)
+       return acl;
+}
+
+static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type,
+                               bdaddr_t *dst, u8 sec_level, u8 auth_type)
+{
+       struct hci_conn *acl;
+       struct hci_conn *sco;
+
+       acl = hci_connect_acl(hdev, dst, sec_level, auth_type);
+       if (IS_ERR(acl))
                return acl;
 
        sco = hci_conn_hash_lookup_ba(hdev, type, dst);
@@ -556,6 +563,25 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
        return sco;
 }
 
+/* Create SCO, ACL or LE connection. */
+struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
+                            __u8 dst_type, __u8 sec_level, __u8 auth_type)
+{
+       BT_DBG("%s dst %s type 0x%x", hdev->name, batostr(dst), type);
+
+       switch (type) {
+       case LE_LINK:
+               return hci_connect_le(hdev, dst, dst_type, sec_level, auth_type);
+       case ACL_LINK:
+               return hci_connect_acl(hdev, dst, sec_level, auth_type);
+       case SCO_LINK:
+       case ESCO_LINK:
+               return hci_connect_sco(hdev, type, dst, sec_level, auth_type);
+       }
+
+       return ERR_PTR(-EINVAL);
+}
+
 /* Check link security requirement */
 int hci_conn_check_link_mode(struct hci_conn *conn)
 {
@@ -775,7 +801,7 @@ void hci_conn_check_pending(struct hci_dev *hdev)
 
        conn = hci_conn_hash_lookup_state(hdev, ACL_LINK, BT_CONNECT2);
        if (conn)
-               hci_acl_connect(conn);
+               hci_acl_create_connection(conn);
 
        hci_dev_unlock(hdev);
 }
@@ -913,7 +939,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
        return chan;
 }
 
-int hci_chan_del(struct hci_chan *chan)
+void hci_chan_del(struct hci_chan *chan)
 {
        struct hci_conn *conn = chan->conn;
        struct hci_dev *hdev = conn->hdev;
@@ -926,8 +952,6 @@ int hci_chan_del(struct hci_chan *chan)
 
        skb_queue_purge(&chan->data_q);
        kfree(chan);
-
-       return 0;
 }
 
 void hci_chan_list_flush(struct hci_conn *conn)
index fa974a19d365e78031cc53977871a1e2b8df2016..e4070517ff3b3decf3b61a5194874c7e2a039e7a 100644 (file)
@@ -231,6 +231,9 @@ static void amp_init(struct hci_dev *hdev)
 
        /* Read Local AMP Info */
        hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
+
+       /* Read Data Blk size */
+       hci_send_cmd(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL);
 }
 
 static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
@@ -268,7 +271,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
                BT_ERR("Unknown device type %d", hdev->dev_type);
                break;
        }
-
 }
 
 static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt)
@@ -1652,6 +1654,7 @@ struct hci_dev *hci_alloc_dev(void)
        INIT_LIST_HEAD(&hdev->link_keys);
        INIT_LIST_HEAD(&hdev->long_term_keys);
        INIT_LIST_HEAD(&hdev->remote_oob_data);
+       INIT_LIST_HEAD(&hdev->conn_hash.list);
 
        INIT_WORK(&hdev->rx_work, hci_rx_work);
        INIT_WORK(&hdev->cmd_work, hci_cmd_work);
@@ -1674,7 +1677,6 @@ struct hci_dev *hci_alloc_dev(void)
 
        hci_init_sysfs(hdev);
        discovery_init(hdev);
-       hci_conn_hash_init(hdev);
 
        return hdev;
 }
index 4fd2cf3bcd0577f418d622d3d81007867b52b283..2022b43c7353ee98d7546d6c9e0ef67c43811d3f 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/mgmt.h>
 
 /* Handle HCI Event packets */
 
@@ -303,7 +304,7 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
 
        hci_dev_lock(hdev);
 
-       if (status != 0) {
+       if (status) {
                mgmt_write_scan_failed(hdev, param, status);
                hdev->discov_timeout = 0;
                goto done;
@@ -925,7 +926,7 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb)
        if (test_bit(HCI_MGMT, &hdev->dev_flags))
                mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status);
 
-       if (rp->status != 0)
+       if (rp->status)
                goto unlock;
 
        cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY);
@@ -1891,6 +1892,22 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
        }
 }
 
+static u8 hci_to_mgmt_reason(u8 err)
+{
+       switch (err) {
+       case HCI_ERROR_CONNECTION_TIMEOUT:
+               return MGMT_DEV_DISCONN_TIMEOUT;
+       case HCI_ERROR_REMOTE_USER_TERM:
+       case HCI_ERROR_REMOTE_LOW_RESOURCES:
+       case HCI_ERROR_REMOTE_POWER_OFF:
+               return MGMT_DEV_DISCONN_REMOTE;
+       case HCI_ERROR_LOCAL_HOST_TERM:
+               return MGMT_DEV_DISCONN_LOCAL_HOST;
+       default:
+               return MGMT_DEV_DISCONN_UNKNOWN;
+       }
+}
+
 static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_disconn_complete *ev = (void *) skb->data;
@@ -1909,12 +1926,15 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
        if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) &&
            (conn->type == ACL_LINK || conn->type == LE_LINK)) {
-               if (ev->status != 0)
+               if (ev->status) {
                        mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
                                               conn->dst_type, ev->status);
-               else
+               } else {
+                       u8 reason = hci_to_mgmt_reason(ev->reason);
+
                        mgmt_device_disconnected(hdev, &conn->dst, conn->type,
-                                                conn->dst_type);
+                                                conn->dst_type, reason);
+               }
        }
 
        if (ev->status == 0) {
@@ -3259,6 +3279,65 @@ static void hci_user_passkey_request_evt(struct hci_dev *hdev,
                mgmt_user_passkey_request(hdev, &ev->bdaddr, ACL_LINK, 0);
 }
 
+static void hci_user_passkey_notify_evt(struct hci_dev *hdev,
+                                       struct sk_buff *skb)
+{
+       struct hci_ev_user_passkey_notify *ev = (void *) skb->data;
+       struct hci_conn *conn;
+
+       BT_DBG("%s", hdev->name);
+
+       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
+       if (!conn)
+               return;
+
+       conn->passkey_notify = __le32_to_cpu(ev->passkey);
+       conn->passkey_entered = 0;
+
+       if (test_bit(HCI_MGMT, &hdev->dev_flags))
+               mgmt_user_passkey_notify(hdev, &conn->dst, conn->type,
+                                        conn->dst_type, conn->passkey_notify,
+                                        conn->passkey_entered);
+}
+
+static void hci_keypress_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hci_ev_keypress_notify *ev = (void *) skb->data;
+       struct hci_conn *conn;
+
+       BT_DBG("%s", hdev->name);
+
+       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
+       if (!conn)
+               return;
+
+       switch (ev->type) {
+       case HCI_KEYPRESS_STARTED:
+               conn->passkey_entered = 0;
+               return;
+
+       case HCI_KEYPRESS_ENTERED:
+               conn->passkey_entered++;
+               break;
+
+       case HCI_KEYPRESS_ERASED:
+               conn->passkey_entered--;
+               break;
+
+       case HCI_KEYPRESS_CLEARED:
+               conn->passkey_entered = 0;
+               break;
+
+       case HCI_KEYPRESS_COMPLETED:
+               return;
+       }
+
+       if (test_bit(HCI_MGMT, &hdev->dev_flags))
+               mgmt_user_passkey_notify(hdev, &conn->dst, conn->type,
+                                        conn->dst_type, conn->passkey_notify,
+                                        conn->passkey_entered);
+}
+
 static void hci_simple_pair_complete_evt(struct hci_dev *hdev,
                                         struct sk_buff *skb)
 {
@@ -3278,7 +3357,7 @@ static void hci_simple_pair_complete_evt(struct hci_dev *hdev,
         * initiated the authentication. A traditional auth_complete
         * event gets always produced as initiator and is also mapped to
         * the mgmt_auth_failed event */
-       if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status != 0)
+       if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status)
                mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
                                 ev->status);
 
@@ -3623,6 +3702,14 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                hci_user_passkey_request_evt(hdev, skb);
                break;
 
+       case HCI_EV_USER_PASSKEY_NOTIFY:
+               hci_user_passkey_notify_evt(hdev, skb);
+               break;
+
+       case HCI_EV_KEYPRESS_NOTIFY:
+               hci_keypress_notify_evt(hdev, skb);
+               break;
+
        case HCI_EV_SIMPLE_PAIR_COMPLETE:
                hci_simple_pair_complete_evt(hdev, skb);
                break;
index e0abaf3cb6a59976c8a5b814d9ae088211ffe741..7a59e929febcfbc5522beac888e6682324a698d4 100644 (file)
@@ -406,7 +406,7 @@ struct l2cap_chan *l2cap_chan_create(void)
 
        chan->state = BT_OPEN;
 
-       atomic_set(&chan->refcnt, 1);
+       kref_init(&chan->kref);
 
        /* This flag is cleared in l2cap_chan_ready() */
        set_bit(CONF_NOT_COMPLETE, &chan->conf_state);
@@ -416,8 +416,10 @@ struct l2cap_chan *l2cap_chan_create(void)
        return chan;
 }
 
-static void l2cap_chan_destroy(struct l2cap_chan *chan)
+static void l2cap_chan_destroy(struct kref *kref)
 {
+       struct l2cap_chan *chan = container_of(kref, struct l2cap_chan, kref);
+
        BT_DBG("chan %p", chan);
 
        write_lock(&chan_list_lock);
@@ -429,17 +431,16 @@ static void l2cap_chan_destroy(struct l2cap_chan *chan)
 
 void l2cap_chan_hold(struct l2cap_chan *c)
 {
-       BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt));
+       BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->kref.refcount));
 
-       atomic_inc(&c->refcnt);
+       kref_get(&c->kref);
 }
 
 void l2cap_chan_put(struct l2cap_chan *c)
 {
-       BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->refcnt));
+       BT_DBG("chan %p orig refcnt %d", c, atomic_read(&c->kref.refcount));
 
-       if (atomic_dec_and_test(&c->refcnt))
-               l2cap_chan_destroy(c);
+       kref_put(&c->kref, l2cap_chan_destroy);
 }
 
 void l2cap_chan_set_defaults(struct l2cap_chan *chan)
@@ -1448,7 +1449,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
        int err;
 
        BT_DBG("%s -> %s (type %u) psm 0x%2.2x", batostr(src), batostr(dst),
-              dst_type, __le16_to_cpu(chan->psm));
+              dst_type, __le16_to_cpu(psm));
 
        hdev = hci_get_route(dst, src);
        if (!hdev)
index a3329cbd3e4da52dc36987b40c9102262cea8e6b..8934343be0ea57f443a4445c78428967047c5332 100644 (file)
@@ -35,7 +35,7 @@
 bool enable_hs;
 
 #define MGMT_VERSION   1
-#define MGMT_REVISION  1
+#define MGMT_REVISION  2
 
 static const u16 mgmt_commands[] = {
        MGMT_OP_READ_INDEX_LIST,
@@ -99,6 +99,7 @@ static const u16 mgmt_events[] = {
        MGMT_EV_DEVICE_BLOCKED,
        MGMT_EV_DEVICE_UNBLOCKED,
        MGMT_EV_DEVICE_UNPAIRED,
+       MGMT_EV_PASSKEY_NOTIFY,
 };
 
 /*
@@ -3077,16 +3078,17 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
 }
 
 int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                            u8 link_type, u8 addr_type)
+                            u8 link_type, u8 addr_type, u8 reason)
 {
-       struct mgmt_addr_info ev;
+       struct mgmt_ev_device_disconnected ev;
        struct sock *sk = NULL;
        int err;
 
        mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
 
-       bacpy(&ev.bdaddr, bdaddr);
-       ev.type = link_to_bdaddr(link_type, addr_type);
+       bacpy(&ev.addr.bdaddr, bdaddr);
+       ev.addr.type = link_to_bdaddr(link_type, addr_type);
+       ev.reason = reason;
 
        err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
                         sk);
@@ -3275,6 +3277,22 @@ int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
                                          MGMT_OP_USER_PASSKEY_NEG_REPLY);
 }
 
+int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                            u8 link_type, u8 addr_type, u32 passkey,
+                            u8 entered)
+{
+       struct mgmt_ev_passkey_notify ev;
+
+       BT_DBG("%s", hdev->name);
+
+       bacpy(&ev.addr.bdaddr, bdaddr);
+       ev.addr.type = link_to_bdaddr(link_type, addr_type);
+       ev.passkey = __cpu_to_le32(passkey);
+       ev.entered = entered;
+
+       return mgmt_event(MGMT_EV_PASSKEY_NOTIFY, hdev, &ev, sizeof(ev), NULL);
+}
+
 int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                     u8 addr_type, u8 status)
 {
index d0deb3edae21fe4a1fc3cbbc7773e742945197f4..3195a6307f50eeb5e6715a6db0fbecc4fee2f4d5 100644 (file)
@@ -869,7 +869,7 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
 
        } else {
                ___ieee80211_stop_tx_ba_session(sta, tid, WLAN_BACK_INITIATOR,
-                                               true);
+                                               false);
        }
 
  out:
index 03fe6d1cff4214b0d9dde329a831ecb172ba3e42..05f3a313db8852b36c677cad188fd7154d6564ef 100644 (file)
@@ -20,7 +20,8 @@
 #include "rate.h"
 #include "mesh.h"
 
-static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, char *name,
+static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
+                                               const char *name,
                                                enum nl80211_iftype type,
                                                u32 *flags,
                                                struct vif_params *params)
@@ -170,6 +171,38 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
                }
        }
 
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_STATION:
+               if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
+                       key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
+               break;
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+               /* Keys without a station are used for TX only */
+               if (key->sta && test_sta_flag(key->sta, WLAN_STA_MFP))
+                       key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               /* no MFP (yet) */
+               break;
+       case NL80211_IFTYPE_MESH_POINT:
+#ifdef CONFIG_MAC80211_MESH
+               if (sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE)
+                       key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
+               break;
+#endif
+       case NL80211_IFTYPE_WDS:
+       case NL80211_IFTYPE_MONITOR:
+       case NL80211_IFTYPE_P2P_DEVICE:
+       case NL80211_IFTYPE_UNSPECIFIED:
+       case NUM_NL80211_IFTYPES:
+       case NL80211_IFTYPE_P2P_CLIENT:
+       case NL80211_IFTYPE_P2P_GO:
+               /* shouldn't happen */
+               WARN_ON_ONCE(1);
+               break;
+       }
+
        err = ieee80211_key_link(key, sdata, sta);
        if (err)
                ieee80211_key_free(sdata->local, key);
@@ -2038,9 +2071,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
         */
        if (!sdata->u.mgd.associated ||
            sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
-               mutex_lock(&sdata->local->iflist_mtx);
                ieee80211_recalc_smps(sdata->local);
-               mutex_unlock(&sdata->local->iflist_mtx);
                return 0;
        }
 
index f0f87e5a1d354eef6a705deba7a15c21b491093f..0bfc914ddd1504d16a2ebf8ad74655d6deb60e53 100644 (file)
@@ -68,16 +68,14 @@ ieee80211_get_channel_mode(struct ieee80211_local *local,
        return mode;
 }
 
-bool ieee80211_set_channel_type(struct ieee80211_local *local,
-                               struct ieee80211_sub_if_data *sdata,
-                               enum nl80211_channel_type chantype)
+static enum nl80211_channel_type
+ieee80211_get_superchan(struct ieee80211_local *local,
+                       struct ieee80211_sub_if_data *sdata)
 {
-       struct ieee80211_sub_if_data *tmp;
        enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
-       bool result;
+       struct ieee80211_sub_if_data *tmp;
 
        mutex_lock(&local->iflist_mtx);
-
        list_for_each_entry(tmp, &local->interfaces, list) {
                if (tmp == sdata)
                        continue;
@@ -103,39 +101,70 @@ bool ieee80211_set_channel_type(struct ieee80211_local *local,
                        break;
                }
        }
+       mutex_unlock(&local->iflist_mtx);
 
-       switch (superchan) {
+       return superchan;
+}
+
+static bool
+ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
+                                      enum nl80211_channel_type chantype2,
+                                      enum nl80211_channel_type *compat)
+{
+       /*
+        * start out with chantype1 being the result,
+        * overwriting later if needed
+        */
+       if (compat)
+               *compat = chantype1;
+
+       switch (chantype1) {
        case NL80211_CHAN_NO_HT:
+               if (compat)
+                       *compat = chantype2;
+               break;
        case NL80211_CHAN_HT20:
                /*
                 * allow any change that doesn't go to no-HT
                 * (if it already is no-HT no change is needed)
                 */
-               if (chantype == NL80211_CHAN_NO_HT)
+               if (chantype2 == NL80211_CHAN_NO_HT)
                        break;
-               superchan = chantype;
+               if (compat)
+                       *compat = chantype2;
                break;
        case NL80211_CHAN_HT40PLUS:
        case NL80211_CHAN_HT40MINUS:
                /* allow smaller bandwidth and same */
-               if (chantype == NL80211_CHAN_NO_HT)
+               if (chantype2 == NL80211_CHAN_NO_HT)
                        break;
-               if (chantype == NL80211_CHAN_HT20)
+               if (chantype2 == NL80211_CHAN_HT20)
                        break;
-               if (superchan == chantype)
+               if (chantype2 == chantype1)
                        break;
-               result = false;
-               goto out;
+               return false;
        }
 
-       local->_oper_channel_type = superchan;
+       return true;
+}
+
+bool ieee80211_set_channel_type(struct ieee80211_local *local,
+                               struct ieee80211_sub_if_data *sdata,
+                               enum nl80211_channel_type chantype)
+{
+       enum nl80211_channel_type superchan;
+       enum nl80211_channel_type compatchan;
+
+       superchan = ieee80211_get_superchan(local, sdata);
+       if (!ieee80211_channel_types_are_compatible(superchan, chantype,
+                                                   &compatchan))
+               return false;
+
+       local->_oper_channel_type = compatchan;
 
        if (sdata)
                sdata->vif.bss_conf.channel_type = chantype;
 
-       result = true;
- out:
-       mutex_unlock(&local->iflist_mtx);
+       return true;
 
-       return result;
 }
index 97173f8144d4af58adfa4714cad0be24f5e3e4c3..466f4b45dd94fdf72517d60a5a71e5ba8bbc9daf 100644 (file)
@@ -70,6 +70,7 @@ DEBUGFS_READONLY_FILE(wep_iv, "%#08x",
 DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s",
        local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver");
 
+#ifdef CONFIG_PM
 static ssize_t reset_write(struct file *file, const char __user *user_buf,
                           size_t count, loff_t *ppos)
 {
@@ -88,6 +89,7 @@ static const struct file_operations reset_ops = {
        .open = simple_open,
        .llseek = noop_llseek,
 };
+#endif
 
 static ssize_t hwflags_read(struct file *file, char __user *user_buf,
                            size_t count, loff_t *ppos)
@@ -245,7 +247,9 @@ void debugfs_hw_add(struct ieee80211_local *local)
        DEBUGFS_ADD(total_ps_buffered);
        DEBUGFS_ADD(wep_iv);
        DEBUGFS_ADD(queues);
+#ifdef CONFIG_PM
        DEBUGFS_ADD_MODE(reset, 0200);
+#endif
        DEBUGFS_ADD(hwflags);
        DEBUGFS_ADD(user_power);
        DEBUGFS_ADD(power);
index a9d93285dba75b1a9f6ed78fe804f1d1fc20e178..5f3620f0bc0a651257aa53e28b91c4b2114be637 100644 (file)
@@ -278,7 +278,7 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
        if (auth && !sdata->u.ibss.auth_frame_registrations) {
                ibss_dbg(sdata,
                         "TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n",
-                        sdata->vif.addr, sdata->u.ibss.bssid, addr);
+                        sdata->vif.addr, addr, sdata->u.ibss.bssid);
                ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, NULL, 0,
                                    addr, sdata->u.ibss.bssid, NULL, 0, 0);
        }
@@ -332,11 +332,27 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
        return ieee80211_ibss_finish_sta(sta, auth);
 }
 
+static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
+                                         struct ieee80211_mgmt *mgmt,
+                                         size_t len)
+{
+       u16 reason = le16_to_cpu(mgmt->u.deauth.reason_code);
+
+       if (len < IEEE80211_DEAUTH_FRAME_LEN)
+               return;
+
+       ibss_dbg(sdata, "RX DeAuth SA=%pM DA=%pM BSSID=%pM (reason: %d)\n",
+                mgmt->sa, mgmt->da, mgmt->bssid, reason);
+       sta_info_destroy_addr(sdata, mgmt->sa);
+}
+
 static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
                                        struct ieee80211_mgmt *mgmt,
                                        size_t len)
 {
        u16 auth_alg, auth_transaction;
+       struct sta_info *sta;
+       u8 deauth_frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
        lockdep_assert_held(&sdata->u.ibss.mtx);
 
@@ -352,9 +368,21 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
                 "RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n",
                 mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction);
        sta_info_destroy_addr(sdata, mgmt->sa);
-       ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
+       sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
        rcu_read_unlock();
 
+       /*
+        * if we have any problem in allocating the new station, we reply with a
+        * DEAUTH frame to tell the other end that we had a problem
+        */
+       if (!sta) {
+               ieee80211_send_deauth_disassoc(sdata, sdata->u.ibss.bssid,
+                                              IEEE80211_STYPE_DEAUTH,
+                                              WLAN_REASON_UNSPECIFIED, true,
+                                              deauth_frame_buf);
+               return;
+       }
+
        /*
         * IEEE 802.11 standard does not require authentication in IBSS
         * networks and most implementations do not seem to use it.
@@ -902,6 +930,9 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
        case IEEE80211_STYPE_AUTH:
                ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len);
                break;
+       case IEEE80211_STYPE_DEAUTH:
+               ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len);
+               break;
        }
 
  mgmt_out:
index 204bfedba30682e6b6c9a7a7138c4fde692606cb..8c804550465b37857d6dc50b082881ec5bd4ac35 100644 (file)
@@ -68,6 +68,8 @@ struct ieee80211_local;
 #define IEEE80211_DEFAULT_MAX_SP_LEN           \
        IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL
 
+#define IEEE80211_DEAUTH_FRAME_LEN     (24 /* hdr */ + 2 /* reason */)
+
 struct ieee80211_fragment_entry {
        unsigned long first_frag_time;
        unsigned int seq;
@@ -411,6 +413,7 @@ struct ieee80211_if_managed {
        struct work_struct monitor_work;
        struct work_struct chswitch_work;
        struct work_struct beacon_connection_loss_work;
+       struct work_struct csa_connection_drop_work;
 
        unsigned long beacon_timeout;
        unsigned long probe_timeout;
@@ -970,7 +973,6 @@ struct ieee80211_local {
        int scan_channel_idx;
        int scan_ies_len;
 
-       struct ieee80211_sched_scan_ies sched_scan_ies;
        struct work_struct sched_scan_stopped_work;
        struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
 
@@ -1057,7 +1059,7 @@ struct ieee80211_local {
        bool disable_dynamic_ps;
 
        int user_power_level; /* in dBm */
-       int power_constr_level; /* in dBm */
+       int ap_power_level; /* in dBm */
 
        enum ieee80211_smps_mode smps_mode;
 
@@ -1165,7 +1167,6 @@ struct ieee802_11_elems {
        u8 prep_len;
        u8 perr_len;
        u8 country_elem_len;
-       u8 pwr_constr_elem_len;
        u8 quiet_elem_len;
        u8 num_of_quiet_elem;   /* can be more the one */
        u8 timeout_int_len;
@@ -1367,7 +1368,6 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
 int ieee80211_reconfig(struct ieee80211_local *local);
 void ieee80211_stop_device(struct ieee80211_local *local);
 
-#ifdef CONFIG_PM
 int __ieee80211_suspend(struct ieee80211_hw *hw,
                        struct cfg80211_wowlan *wowlan);
 
@@ -1381,18 +1381,6 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
 
        return ieee80211_reconfig(hw_to_local(hw));
 }
-#else
-static inline int __ieee80211_suspend(struct ieee80211_hw *hw,
-                                     struct cfg80211_wowlan *wowlan)
-{
-       return 0;
-}
-
-static inline int __ieee80211_resume(struct ieee80211_hw *hw)
-{
-       return 0;
-}
-#endif
 
 /* utility functions/constants */
 extern void *mac80211_wiphy_privid; /* for wiphy privid */
@@ -1459,6 +1447,9 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
                         u16 transaction, u16 auth_alg,
                         u8 *extra, size_t extra_len, const u8 *bssid,
                         const u8 *da, const u8 *key, u8 key_len, u8 key_idx);
+void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+                                   const u8 *bssid, u16 stype, u16 reason,
+                                   bool send_frame, u8 *frame_buf);
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                             const u8 *ie, size_t ie_len,
                             enum ieee80211_band band, u32 rate_mask,
index d747da5417471b220bb472ca95030e453cbe6f64..6f8a73c64fb31bde831609fa88a9b4e0425f02b9 100644 (file)
@@ -793,11 +793,20 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
                flush_work(&sdata->work);
                /*
                 * When we get here, the interface is marked down.
-                * Call synchronize_rcu() to wait for the RX path
+                * Call rcu_barrier() to wait both for the RX path
                 * should it be using the interface and enqueuing
-                * frames at this very time on another CPU.
+                * frames at this very time on another CPU, and
+                * for the sta free call_rcu callbacks.
                 */
-               synchronize_rcu();
+               rcu_barrier();
+
+               /*
+                * free_sta_rcu() enqueues a work for the actual
+                * sta cleanup, so we need to flush it while
+                * sdata is still valid.
+                */
+               flush_workqueue(local->workqueue);
+
                skb_queue_purge(&sdata->skb_queue);
 
                /*
index 7ae678ba5d679dbd40fc7c199c499ed82228757f..d27e61aaa71bd7200c527606820f5d5d82c39ff0 100644 (file)
@@ -402,7 +402,7 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)
         * Synchronize so the TX path can no longer be using
         * this key before we free/remove it.
         */
-       synchronize_rcu();
+       synchronize_net();
 
        if (key->local)
                ieee80211_key_disable_hw_accel(key);
index bd7529363193ae5cb6ab55f7f1fc997c90e202b9..c80c4490351ce54fb75c41cdb9be1a313b0ffef5 100644 (file)
@@ -150,13 +150,11 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
 
        if (test_bit(SCAN_SW_SCANNING, &local->scanning) ||
            test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
-           test_bit(SCAN_HW_SCANNING, &local->scanning))
+           test_bit(SCAN_HW_SCANNING, &local->scanning) ||
+           !local->ap_power_level)
                power = chan->max_power;
        else
-               power = local->power_constr_level ?
-                       min(chan->max_power,
-                               (chan->max_reg_power  - local->power_constr_level)) :
-                       chan->max_power;
+               power = min(chan->max_power, local->ap_power_level);
 
        if (local->user_power_level >= 0)
                power = min(power, local->user_power_level);
@@ -366,9 +364,7 @@ static void ieee80211_recalc_smps_work(struct work_struct *work)
        struct ieee80211_local *local =
                container_of(work, struct ieee80211_local, recalc_smps);
 
-       mutex_lock(&local->iflist_mtx);
        ieee80211_recalc_smps(local);
-       mutex_unlock(&local->iflist_mtx);
 }
 
 #ifdef CONFIG_INET
index 9d7ad366ef09a37cb9031c0b4eeb754ec7bb1217..3ab34d81689753e0beaf7af4a9c7c806f9da6915 100644 (file)
@@ -537,7 +537,8 @@ int mesh_plink_open(struct sta_info *sta)
        spin_lock_bh(&sta->lock);
        get_random_bytes(&llid, 2);
        sta->llid = llid;
-       if (sta->plink_state != NL80211_PLINK_LISTEN) {
+       if (sta->plink_state != NL80211_PLINK_LISTEN &&
+           sta->plink_state != NL80211_PLINK_BLOCKED) {
                spin_unlock_bh(&sta->lock);
                return -EBUSY;
        }
index 5d77650d4363a668ed2d3241b0c499f8c31f4db9..e714ed8bb198727c6738c1e3ae7650243dd2a07e 100644 (file)
@@ -88,8 +88,6 @@ MODULE_PARM_DESC(probe_wait_ms,
 #define TMR_RUNNING_TIMER      0
 #define TMR_RUNNING_CHANSW     1
 
-#define DEAUTH_DISASSOC_LEN    (24 /* hdr */ + 2 /* reason */)
-
 /*
  * All cfg80211 functions have to be called outside a locked
  * section so that they can acquire a lock themselves... This
@@ -574,46 +572,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
        ieee80211_tx_skb(sdata, skb);
 }
 
-static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
-                                          const u8 *bssid, u16 stype,
-                                          u16 reason, bool send_frame,
-                                          u8 *frame_buf)
-{
-       struct ieee80211_local *local = sdata->local;
-       struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct sk_buff *skb;
-       struct ieee80211_mgmt *mgmt = (void *)frame_buf;
-
-       /* build frame */
-       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
-       mgmt->duration = 0; /* initialize only */
-       mgmt->seq_ctrl = 0; /* initialize only */
-       memcpy(mgmt->da, bssid, ETH_ALEN);
-       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
-       memcpy(mgmt->bssid, bssid, ETH_ALEN);
-       /* u.deauth.reason_code == u.disassoc.reason_code */
-       mgmt->u.deauth.reason_code = cpu_to_le16(reason);
-
-       if (send_frame) {
-               skb = dev_alloc_skb(local->hw.extra_tx_headroom +
-                                   DEAUTH_DISASSOC_LEN);
-               if (!skb)
-                       return;
-
-               skb_reserve(skb, local->hw.extra_tx_headroom);
-
-               /* copy in frame */
-               memcpy(skb_put(skb, DEAUTH_DISASSOC_LEN),
-                      mgmt, DEAUTH_DISASSOC_LEN);
-
-               if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
-                       IEEE80211_SKB_CB(skb)->flags |=
-                               IEEE80211_TX_INTFL_DONT_ENCRYPT;
-
-               ieee80211_tx_skb(sdata, skb);
-       }
-}
-
 void ieee80211_send_pspoll(struct ieee80211_local *local,
                           struct ieee80211_sub_if_data *sdata)
 {
@@ -730,16 +688,13 @@ void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
 
        trace_api_chswitch_done(sdata, success);
        if (!success) {
-               /*
-                * If the channel switch was not successful, stay
-                * around on the old channel. We currently lack
-                * good handling of this situation, possibly we
-                * should just drop the association.
-                */
-               sdata->local->csa_channel = sdata->local->oper_channel;
+               sdata_info(sdata,
+                          "driver channel switch failed, disconnecting\n");
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &ifmgd->csa_connection_drop_work);
+       } else {
+               ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
        }
-
-       ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
 }
 EXPORT_SYMBOL(ieee80211_chswitch_done);
 
@@ -784,8 +739,14 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                return;
 
        new_ch = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
-       if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED)
+       if (!new_ch || new_ch->flags & IEEE80211_CHAN_DISABLED) {
+               sdata_info(sdata,
+                          "AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
+                          ifmgd->associated->bssid, new_freq);
+               ieee80211_queue_work(&sdata->local->hw,
+                                    &ifmgd->csa_connection_drop_work);
                return;
+       }
 
        sdata->local->csa_channel = new_ch;
 
@@ -818,23 +779,71 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 }
 
 static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
-                                       u16 capab_info, u8 *pwr_constr_elem,
-                                       u8 pwr_constr_elem_len)
+                                       struct ieee80211_channel *channel,
+                                       const u8 *country_ie, u8 country_ie_len,
+                                       const u8 *pwr_constr_elem)
 {
-       struct ieee80211_conf *conf = &sdata->local->hw.conf;
+       struct ieee80211_country_ie_triplet *triplet;
+       int chan = ieee80211_frequency_to_channel(channel->center_freq);
+       int i, chan_pwr, chan_increment, new_ap_level;
+       bool have_chan_pwr = false;
 
-       if (!(capab_info & WLAN_CAPABILITY_SPECTRUM_MGMT))
+       /* Invalid IE */
+       if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
                return;
 
-       /* Power constraint IE length should be 1 octet */
-       if (pwr_constr_elem_len != 1)
-               return;
+       triplet = (void *)(country_ie + 3);
+       country_ie_len -= 3;
 
-       if ((*pwr_constr_elem <= conf->channel->max_reg_power) &&
-           (*pwr_constr_elem != sdata->local->power_constr_level)) {
-               sdata->local->power_constr_level = *pwr_constr_elem;
-               ieee80211_hw_config(sdata->local, 0);
+       switch (channel->band) {
+       default:
+               WARN_ON_ONCE(1);
+               /* fall through */
+       case IEEE80211_BAND_2GHZ:
+       case IEEE80211_BAND_60GHZ:
+               chan_increment = 1;
+               break;
+       case IEEE80211_BAND_5GHZ:
+               chan_increment = 4;
+               break;
        }
+
+       /* find channel */
+       while (country_ie_len >= 3) {
+               u8 first_channel = triplet->chans.first_channel;
+
+               if (first_channel >= IEEE80211_COUNTRY_EXTENSION_ID)
+                       goto next;
+
+               for (i = 0; i < triplet->chans.num_channels; i++) {
+                       if (first_channel + i * chan_increment == chan) {
+                               have_chan_pwr = true;
+                               chan_pwr = triplet->chans.max_power;
+                               break;
+                       }
+               }
+               if (have_chan_pwr)
+                       break;
+
+ next:
+               triplet++;
+               country_ie_len -= 3;
+       }
+
+       if (!have_chan_pwr)
+               return;
+
+       new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem);
+
+       if (sdata->local->ap_power_level == new_ap_level)
+               return;
+
+       sdata_info(sdata,
+                  "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
+                  new_ap_level, chan_pwr, *pwr_constr_elem,
+                  sdata->u.mgd.bssid);
+       sdata->local->ap_power_level = new_ap_level;
+       ieee80211_hw_config(sdata->local, 0);
 }
 
 void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif)
@@ -1339,9 +1348,9 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
 
        mutex_lock(&local->iflist_mtx);
        ieee80211_recalc_ps(local, -1);
-       ieee80211_recalc_smps(local);
        mutex_unlock(&local->iflist_mtx);
 
+       ieee80211_recalc_smps(local);
        ieee80211_recalc_ps_vif(sdata);
 
        netif_tx_start_all_queues(sdata->dev);
@@ -1390,7 +1399,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        sta = sta_info_get(sdata, ifmgd->bssid);
        if (sta) {
                set_sta_flag(sta, WLAN_STA_BLOCK_BA);
-               ieee80211_sta_tear_down_BA_sessions(sta, tx);
+               ieee80211_sta_tear_down_BA_sessions(sta, false);
        }
        mutex_unlock(&local->sta_mtx);
 
@@ -1438,7 +1447,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
        memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
 
-       local->power_constr_level = 0;
+       local->ap_power_level = 0;
 
        del_timer_sync(&local->dynamic_ps_timer);
        cancel_work_sync(&local->dynamic_ps_enable_work);
@@ -1692,11 +1701,12 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(ieee80211_ap_probereq_get);
 
-static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
+static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata,
+                                  bool transmit_frame)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
-       u8 frame_buf[DEAUTH_DISASSOC_LEN];
+       u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
        mutex_lock(&ifmgd->mtx);
        if (!ifmgd->associated) {
@@ -1704,19 +1714,17 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
                return;
        }
 
-       sdata_info(sdata, "Connection to AP %pM lost\n",
-                  ifmgd->associated->bssid);
-
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
                               WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
-                              false, frame_buf);
+                              transmit_frame, frame_buf);
+       ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
        mutex_unlock(&ifmgd->mtx);
 
        /*
         * must be outside lock due to cfg80211,
         * but that's not a problem.
         */
-       cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
+       cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
 
        mutex_lock(&local->mtx);
        ieee80211_recalc_idle(local);
@@ -1739,10 +1747,24 @@ static void ieee80211_beacon_connection_loss_work(struct work_struct *work)
                rcu_read_unlock();
        }
 
-       if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
-               __ieee80211_connection_loss(sdata);
-       else
+       if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) {
+               sdata_info(sdata, "Connection to AP %pM lost\n",
+                          ifmgd->bssid);
+               __ieee80211_disconnect(sdata, false);
+       } else {
                ieee80211_mgd_probe_ap(sdata, true);
+       }
+}
+
+static void ieee80211_csa_connection_drop_work(struct work_struct *work)
+{
+       struct ieee80211_sub_if_data *sdata =
+               container_of(work, struct ieee80211_sub_if_data,
+                            u.mgd.csa_connection_drop_work);
+
+       ieee80211_wake_queues_by_reason(&sdata->local->hw,
+                                       IEEE80211_QUEUE_STOP_REASON_CSA);
+       __ieee80211_disconnect(sdata, true);
 }
 
 void ieee80211_beacon_loss(struct ieee80211_vif *vif)
@@ -2530,15 +2552,13 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
                                                  bssid, true);
        }
 
-       /* Note: country IE parsing is done for us by cfg80211 */
-       if (elems.country_elem) {
-               /* TODO: IBSS also needs this */
-               if (elems.pwr_constr_elem)
-                       ieee80211_handle_pwr_constr(sdata,
-                               le16_to_cpu(mgmt->u.probe_resp.capab_info),
-                               elems.pwr_constr_elem,
-                               elems.pwr_constr_elem_len);
-       }
+       if (elems.country_elem && elems.pwr_constr_elem &&
+           mgmt->u.probe_resp.capab_info &
+                               cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT))
+               ieee80211_handle_pwr_constr(sdata, local->oper_channel,
+                                           elems.country_elem,
+                                           elems.country_elem_len,
+                                           elems.pwr_constr_elem);
 
        ieee80211_bss_info_change_notify(sdata, changed);
 }
@@ -2635,7 +2655,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       u8 frame_buf[DEAUTH_DISASSOC_LEN];
+       u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
                               false, frame_buf);
@@ -2645,7 +2665,7 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
         * must be outside lock due to cfg80211,
         * but that's not a problem.
         */
-       cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
+       cfg80211_send_deauth(sdata->dev, frame_buf, IEEE80211_DEAUTH_FRAME_LEN);
 
        mutex_lock(&local->mtx);
        ieee80211_recalc_idle(local);
@@ -2929,6 +2949,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
 
        cancel_work_sync(&ifmgd->monitor_work);
        cancel_work_sync(&ifmgd->beacon_connection_loss_work);
+       cancel_work_sync(&ifmgd->csa_connection_drop_work);
        if (del_timer_sync(&ifmgd->timer))
                set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
 
@@ -2985,6 +3006,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
        INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
        INIT_WORK(&ifmgd->beacon_connection_loss_work,
                  ieee80211_beacon_connection_loss_work);
+       INIT_WORK(&ifmgd->csa_connection_drop_work,
+                 ieee80211_csa_connection_drop_work);
        INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work);
        setup_timer(&ifmgd->timer, ieee80211_sta_timer,
                    (unsigned long) sdata);
@@ -3525,7 +3548,7 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                         struct cfg80211_deauth_request *req)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       u8 frame_buf[DEAUTH_DISASSOC_LEN];
+       u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
        mutex_lock(&ifmgd->mtx);
 
@@ -3553,7 +3576,8 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
 
        mutex_unlock(&ifmgd->mtx);
 
-       __cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
+       __cfg80211_send_deauth(sdata->dev, frame_buf,
+                              IEEE80211_DEAUTH_FRAME_LEN);
 
        mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
@@ -3567,7 +3591,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        u8 bssid[ETH_ALEN];
-       u8 frame_buf[DEAUTH_DISASSOC_LEN];
+       u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
 
        mutex_lock(&ifmgd->mtx);
 
@@ -3592,7 +3616,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
                               frame_buf);
        mutex_unlock(&ifmgd->mtx);
 
-       __cfg80211_send_disassoc(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
+       __cfg80211_send_disassoc(sdata->dev, frame_buf,
+                                IEEE80211_DEAUTH_FRAME_LEN);
 
        mutex_lock(&sdata->local->mtx);
        ieee80211_recalc_idle(sdata->local);
index 507121dad082b9b2e9ce2302c8243834ef195903..83608ac167801f1c06fc55dd3ab53370d947cbc7 100644 (file)
@@ -233,8 +233,7 @@ static void ieee80211_hw_roc_start(struct work_struct *work)
                        u32 dur = dep->duration;
                        dep->duration = dur - roc->duration;
                        roc->duration = dur;
-                       list_del(&dep->list);
-                       list_add(&dep->list, &roc->list);
+                       list_move(&dep->list, &roc->list);
                }
        }
  out_unlock:
index 740e414d44f46fe58c5f6fb1c2f73e5404de10f4..c4cdbde24fd3a70db1141c9460f617710daf98e2 100644 (file)
@@ -407,7 +407,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
        enum ieee80211_band band = local->hw.conf.channel->band;
 
        sdata = rcu_dereference_protected(local->scan_sdata,
-                                         lockdep_is_held(&local->mtx));;
+                                         lockdep_is_held(&local->mtx));
 
        for (i = 0; i < local->scan_req->n_ssids; i++)
                ieee80211_send_probe_req(
@@ -917,6 +917,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
                                       struct cfg80211_sched_scan_request *req)
 {
        struct ieee80211_local *local = sdata->local;
+       struct ieee80211_sched_scan_ies sched_scan_ies;
        int ret, i;
 
        mutex_lock(&local->mtx);
@@ -935,33 +936,28 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
                if (!local->hw.wiphy->bands[i])
                        continue;
 
-               local->sched_scan_ies.ie[i] = kzalloc(2 +
-                                                     IEEE80211_MAX_SSID_LEN +
-                                                     local->scan_ies_len +
-                                                     req->ie_len,
-                                                     GFP_KERNEL);
-               if (!local->sched_scan_ies.ie[i]) {
+               sched_scan_ies.ie[i] = kzalloc(2 + IEEE80211_MAX_SSID_LEN +
+                                              local->scan_ies_len +
+                                              req->ie_len,
+                                              GFP_KERNEL);
+               if (!sched_scan_ies.ie[i]) {
                        ret = -ENOMEM;
                        goto out_free;
                }
 
-               local->sched_scan_ies.len[i] =
-                       ieee80211_build_preq_ies(local,
-                                                local->sched_scan_ies.ie[i],
+               sched_scan_ies.len[i] =
+                       ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
                                                 req->ie, req->ie_len, i,
                                                 (u32) -1, 0);
        }
 
-       ret = drv_sched_scan_start(local, sdata, req,
-                                  &local->sched_scan_ies);
-       if (ret == 0) {
+       ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
+       if (ret == 0)
                rcu_assign_pointer(local->sched_scan_sdata, sdata);
-               goto out;
-       }
 
 out_free:
        while (i > 0)
-               kfree(local->sched_scan_ies.ie[--i]);
+               kfree(sched_scan_ies.ie[--i]);
 out:
        mutex_unlock(&local->mtx);
        return ret;
@@ -970,7 +966,7 @@ out:
 int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_local *local = sdata->local;
-       int ret = 0, i;
+       int ret = 0;
 
        mutex_lock(&local->mtx);
 
@@ -979,12 +975,9 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
                goto out;
        }
 
-       if (rcu_access_pointer(local->sched_scan_sdata)) {
-               for (i = 0; i < IEEE80211_NUM_BANDS; i++)
-                       kfree(local->sched_scan_ies.ie[i]);
-
+       if (rcu_access_pointer(local->sched_scan_sdata))
                drv_sched_scan_stop(local, sdata);
-       }
+
 out:
        mutex_unlock(&local->mtx);
 
@@ -1006,7 +999,6 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
        struct ieee80211_local *local =
                container_of(work, struct ieee80211_local,
                             sched_scan_stopped_work);
-       int i;
 
        mutex_lock(&local->mtx);
 
@@ -1015,9 +1007,6 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work)
                return;
        }
 
-       for (i = 0; i < IEEE80211_NUM_BANDS; i++)
-               kfree(local->sched_scan_ies.ie[i]);
-
        rcu_assign_pointer(local->sched_scan_sdata, NULL);
 
        mutex_unlock(&local->mtx);
index 06fa75ceb0251e6064d90661bf7b5e61f979b72c..797dd36a220d92ac549067a9cd4d59a7ebfc09a3 100644 (file)
@@ -91,6 +91,70 @@ static int sta_info_hash_del(struct ieee80211_local *local,
        return -ENOENT;
 }
 
+static void free_sta_work(struct work_struct *wk)
+{
+       struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk);
+       int ac, i;
+       struct tid_ampdu_tx *tid_tx;
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
+       struct ieee80211_local *local = sdata->local;
+
+       /*
+        * At this point, when being called as call_rcu callback,
+        * neither mac80211 nor the driver can reference this
+        * sta struct any more except by still existing timers
+        * associated with this station that we clean up below.
+        */
+
+       if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
+               BUG_ON(!sdata->bss);
+
+               clear_sta_flag(sta, WLAN_STA_PS_STA);
+
+               atomic_dec(&sdata->bss->num_sta_ps);
+               sta_info_recalc_tim(sta);
+       }
+
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+               local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
+               __skb_queue_purge(&sta->ps_tx_buf[ac]);
+               __skb_queue_purge(&sta->tx_filtered[ac]);
+       }
+
+#ifdef CONFIG_MAC80211_MESH
+       if (ieee80211_vif_is_mesh(&sdata->vif)) {
+               mesh_accept_plinks_update(sdata);
+               mesh_plink_deactivate(sta);
+               del_timer_sync(&sta->plink_timer);
+       }
+#endif
+
+       cancel_work_sync(&sta->drv_unblock_wk);
+
+       /*
+        * Destroy aggregation state here. It would be nice to wait for the
+        * driver to finish aggregation stop and then clean up, but for now
+        * drivers have to handle aggregation stop being requested, followed
+        * directly by station destruction.
+        */
+       for (i = 0; i < STA_TID_NUM; i++) {
+               tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
+               if (!tid_tx)
+                       continue;
+               __skb_queue_purge(&tid_tx->pending);
+               kfree(tid_tx);
+       }
+
+       sta_info_free(local, sta);
+}
+
+static void free_sta_rcu(struct rcu_head *h)
+{
+       struct sta_info *sta = container_of(h, struct sta_info, rcu_head);
+
+       ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk);
+}
+
 /* protected by RCU */
 struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
                              const u8 *addr)
@@ -241,6 +305,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
 
        spin_lock_init(&sta->lock);
        INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
+       INIT_WORK(&sta->free_sta_wk, free_sta_work);
        INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
        mutex_init(&sta->ampdu_mlme.mtx);
 
@@ -654,8 +719,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
 {
        struct ieee80211_local *local;
        struct ieee80211_sub_if_data *sdata;
-       int ret, i, ac;
-       struct tid_ampdu_tx *tid_tx;
+       int ret, i;
 
        might_sleep();
 
@@ -674,7 +738,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
         * will be sufficient.
         */
        set_sta_flag(sta, WLAN_STA_BLOCK_BA);
-       ieee80211_sta_tear_down_BA_sessions(sta, true);
+       ieee80211_sta_tear_down_BA_sessions(sta, false);
 
        ret = sta_info_hash_del(local, sta);
        if (ret)
@@ -711,65 +775,14 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
                WARN_ON_ONCE(ret != 0);
        }
 
-       /*
-        * At this point, after we wait for an RCU grace period,
-        * neither mac80211 nor the driver can reference this
-        * sta struct any more except by still existing timers
-        * associated with this station that we clean up below.
-        */
-       synchronize_rcu();
-
-       if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
-               BUG_ON(!sdata->bss);
-
-               clear_sta_flag(sta, WLAN_STA_PS_STA);
-
-               atomic_dec(&sdata->bss->num_sta_ps);
-               sta_info_recalc_tim(sta);
-       }
-
-       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
-               local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
-               __skb_queue_purge(&sta->ps_tx_buf[ac]);
-               __skb_queue_purge(&sta->tx_filtered[ac]);
-       }
-
-#ifdef CONFIG_MAC80211_MESH
-       if (ieee80211_vif_is_mesh(&sdata->vif))
-               mesh_accept_plinks_update(sdata);
-#endif
-
        sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr);
 
-       cancel_work_sync(&sta->drv_unblock_wk);
-
        cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL);
 
        rate_control_remove_sta_debugfs(sta);
        ieee80211_sta_debugfs_remove(sta);
 
-#ifdef CONFIG_MAC80211_MESH
-       if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
-               mesh_plink_deactivate(sta);
-               del_timer_sync(&sta->plink_timer);
-       }
-#endif
-
-       /*
-        * Destroy aggregation state here. It would be nice to wait for the
-        * driver to finish aggregation stop and then clean up, but for now
-        * drivers have to handle aggregation stop being requested, followed
-        * directly by station destruction.
-        */
-       for (i = 0; i < STA_TID_NUM; i++) {
-               tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
-               if (!tid_tx)
-                       continue;
-               __skb_queue_purge(&tid_tx->pending);
-               kfree(tid_tx);
-       }
-
-       sta_info_free(local, sta);
+       call_rcu(&sta->rcu_head, free_sta_rcu);
 
        return 0;
 }
index a470e1123a5576ed5e14b779ed4a9213cda407b7..c88f161f81185a678335fe3df6201fb8e64c8d80 100644 (file)
@@ -287,6 +287,7 @@ struct sta_ampdu_mlme {
 struct sta_info {
        /* General information, mostly static */
        struct list_head list;
+       struct rcu_head rcu_head;
        struct sta_info __rcu *hnext;
        struct ieee80211_local *local;
        struct ieee80211_sub_if_data *sdata;
@@ -297,6 +298,7 @@ struct sta_info {
        spinlock_t lock;
 
        struct work_struct drv_unblock_wk;
+       struct work_struct free_sta_wk;
 
        u16 listen_interval;
 
index b0801b7d572dfd71ad2f50b1b6dba4429aaa0894..2ce89732d0f21755939699b55e3382ac98baeaca 100644 (file)
@@ -517,29 +517,41 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
 
        if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
                u64 cookie = (unsigned long)skb;
+               bool found = false;
+
                acked = info->flags & IEEE80211_TX_STAT_ACK;
 
-               if (ieee80211_is_nullfunc(hdr->frame_control) ||
-                   ieee80211_is_qos_nullfunc(hdr->frame_control)) {
-                       cfg80211_probe_status(skb->dev, hdr->addr1,
-                                             cookie, acked, GFP_ATOMIC);
-               } else if (skb->dev) {
-                       cfg80211_mgmt_tx_status(
-                               skb->dev->ieee80211_ptr, cookie, skb->data,
-                               skb->len, acked, GFP_ATOMIC);
-               } else {
-                       struct ieee80211_sub_if_data *p2p_sdata;
+               rcu_read_lock();
+
+               list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+                       if (!sdata->dev)
+                               continue;
 
-                       rcu_read_lock();
+                       if (skb->dev != sdata->dev)
+                               continue;
 
-                       p2p_sdata = rcu_dereference(local->p2p_sdata);
-                       if (p2p_sdata) {
-                               cfg80211_mgmt_tx_status(
-                                       &p2p_sdata->wdev, cookie, skb->data,
-                                       skb->len, acked, GFP_ATOMIC);
-                       }
-                       rcu_read_unlock();
+                       found = true;
+                       break;
+               }
+
+               if (!skb->dev) {
+                       sdata = rcu_dereference(local->p2p_sdata);
+                       if (sdata)
+                               found = true;
+               }
+
+               if (!found)
+                       skb->dev = NULL;
+               else if (ieee80211_is_nullfunc(hdr->frame_control) ||
+                        ieee80211_is_qos_nullfunc(hdr->frame_control)) {
+                       cfg80211_probe_status(sdata->dev, hdr->addr1,
+                                             cookie, acked, GFP_ATOMIC);
+               } else {
+                       cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
+                                               skb->len, acked, GFP_ATOMIC);
                }
+
+               rcu_read_unlock();
        }
 
        if (unlikely(info->ack_frame_id)) {
index 29eb4e678235eff5fcfe48d9304f0db5b84259f1..e0e0d1d0e8301d4a8803b7af811e24d752347a88 100644 (file)
@@ -580,7 +580,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
                                tx->key = NULL;
                        else
                                skip_hw = (tx->key->conf.flags &
-                                          IEEE80211_KEY_FLAG_SW_MGMT) &&
+                                          IEEE80211_KEY_FLAG_SW_MGMT_TX) &&
                                        ieee80211_is_mgmt(hdr->frame_control);
                        break;
                case WLAN_CIPHER_SUITE_AES_CMAC:
index 471fb0516c99453b3543a2c47b82476594e83f34..22ca35054dd065753b9e7d6c4f3e5ab990903e83 100644 (file)
@@ -792,8 +792,11 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
                        elems->country_elem_len = elen;
                        break;
                case WLAN_EID_PWR_CONSTRAINT:
+                       if (elen != 1) {
+                               elem_parse_failed = true;
+                               break;
+                       }
                        elems->pwr_constr_elem = pos;
-                       elems->pwr_constr_elem_len = elen;
                        break;
                case WLAN_EID_TIMEOUT_INTERVAL:
                        elems->timeout_int = pos;
@@ -1004,6 +1007,45 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
        ieee80211_tx_skb(sdata, skb);
 }
 
+void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
+                                   const u8 *bssid, u16 stype, u16 reason,
+                                   bool send_frame, u8 *frame_buf)
+{
+       struct ieee80211_local *local = sdata->local;
+       struct sk_buff *skb;
+       struct ieee80211_mgmt *mgmt = (void *)frame_buf;
+
+       /* build frame */
+       mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
+       mgmt->duration = 0; /* initialize only */
+       mgmt->seq_ctrl = 0; /* initialize only */
+       memcpy(mgmt->da, bssid, ETH_ALEN);
+       memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+       memcpy(mgmt->bssid, bssid, ETH_ALEN);
+       /* u.deauth.reason_code == u.disassoc.reason_code */
+       mgmt->u.deauth.reason_code = cpu_to_le16(reason);
+
+       if (send_frame) {
+               skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+                                   IEEE80211_DEAUTH_FRAME_LEN);
+               if (!skb)
+                       return;
+
+               skb_reserve(skb, local->hw.extra_tx_headroom);
+
+               /* copy in frame */
+               memcpy(skb_put(skb, IEEE80211_DEAUTH_FRAME_LEN),
+                      mgmt, IEEE80211_DEAUTH_FRAME_LEN);
+
+               if (sdata->vif.type != NL80211_IFTYPE_STATION ||
+                   !(sdata->u.mgd.flags & IEEE80211_STA_MFP_ENABLED))
+                       IEEE80211_SKB_CB(skb)->flags |=
+                               IEEE80211_TX_INTFL_DONT_ENCRYPT;
+
+               ieee80211_tx_skb(sdata, skb);
+       }
+}
+
 int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
                             const u8 *ie, size_t ie_len,
                             enum ieee80211_band band, u32 rate_mask,
@@ -1564,14 +1606,13 @@ static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
        return 0;
 }
 
-/* must hold iflist_mtx */
 void ieee80211_recalc_smps(struct ieee80211_local *local)
 {
        struct ieee80211_sub_if_data *sdata;
        enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF;
        int count = 0;
 
-       lockdep_assert_held(&local->iflist_mtx);
+       mutex_lock(&local->iflist_mtx);
 
        /*
         * This function could be improved to handle multiple
@@ -1600,12 +1641,14 @@ void ieee80211_recalc_smps(struct ieee80211_local *local)
        }
 
        if (smps_mode == local->smps_mode)
-               return;
+               goto unlock;
 
  set:
        local->smps_mode = smps_mode;
        /* changed flag is auto-detected for this */
        ieee80211_hw_config(local, 0);
+ unlock:
+       mutex_unlock(&local->iflist_mtx);
 }
 
 static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
index ff749794bc5b87d0b72e36e48cf92caa9487d130..c9eacc1f145f8e6da7990c56822b44dfedc80fe2 100644 (file)
@@ -679,7 +679,7 @@ static void nfc_release(struct device *d)
 
        if (dev->ops->check_presence) {
                del_timer_sync(&dev->check_pres_timer);
-               destroy_workqueue(dev->check_pres_wq);
+               cancel_work_sync(&dev->check_pres_work);
        }
 
        nfc_genl_data_exit(&dev->genl_data);
@@ -715,7 +715,7 @@ static void nfc_check_pres_timeout(unsigned long data)
 {
        struct nfc_dev *dev = (struct nfc_dev *)data;
 
-       queue_work(dev->check_pres_wq, &dev->check_pres_work);
+       queue_work(system_nrt_wq, &dev->check_pres_work);
 }
 
 struct class nfc_class = {
@@ -784,20 +784,11 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
        dev->targets_generation = 1;
 
        if (ops->check_presence) {
-               char name[32];
                init_timer(&dev->check_pres_timer);
                dev->check_pres_timer.data = (unsigned long)dev;
                dev->check_pres_timer.function = nfc_check_pres_timeout;
 
                INIT_WORK(&dev->check_pres_work, nfc_check_pres_work);
-               snprintf(name, sizeof(name), "nfc%d_check_pres_wq", dev->idx);
-               dev->check_pres_wq = alloc_workqueue(name, WQ_NON_REENTRANT |
-                                                    WQ_UNBOUND |
-                                                    WQ_MEM_RECLAIM, 1);
-               if (dev->check_pres_wq == NULL) {
-                       kfree(dev);
-                       return NULL;
-               }
        }
 
        return dev;
index f9c44b2fb065de5b7f753390caeb4b3ab5466b68..c5dbb6891b24bcce74821a648b04345d02dfcc39 100644 (file)
@@ -4,5 +4,5 @@
 
 obj-$(CONFIG_NFC_HCI) += hci.o
 
-hci-y                  := core.o hcp.o command.o
-hci-$(CONFIG_NFC_SHDLC)        += shdlc.o
+hci-y                  := core.o hcp.o command.o llc.o llc_nop.o
+hci-$(CONFIG_NFC_SHDLC) += llc_shdlc.o
index 46362ef979db1ca88b4ad69d4b6adb7bd440f844..71c6a7086b8f04b4e6264969d21f1ba5a362fcb8 100644 (file)
 
 #include "hci.h"
 
-static void nfc_hci_execute_cb(struct nfc_hci_dev *hdev, int err,
-                              struct sk_buff *skb, void *cb_data)
+static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
+                              const u8 *param, size_t param_len,
+                              data_exchange_cb_t cb, void *cb_context)
 {
-       struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)cb_data;
+       pr_debug("exec cmd async through pipe=%d, cmd=%d, plen=%zd\n", pipe,
+                cmd, param_len);
+
+       /* TODO: Define hci cmd execution delay. Should it be the same
+        * for all commands?
+        */
+       return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd,
+                                     param, param_len, cb, cb_context, 3000);
+}
+
+/*
+ * HCI command execution completion callback.
+ * err will be a standard linux error (may be converted from HCI response)
+ * skb contains the response data and must be disposed, or may be NULL if
+ * an error occured
+ */
+static void nfc_hci_execute_cb(void *context, struct sk_buff *skb, int err)
+{
+       struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)context;
 
        pr_debug("HCI Cmd completed with result=%d\n", err);
 
@@ -55,7 +74,8 @@ static int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
        hcp_ew.exec_complete = false;
        hcp_ew.result_skb = NULL;
 
-       pr_debug("through pipe=%d, cmd=%d, plen=%zd\n", pipe, cmd, param_len);
+       pr_debug("exec cmd sync through pipe=%d, cmd=%d, plen=%zd\n", pipe,
+                cmd, param_len);
 
        /* TODO: Define hci cmd execution delay. Should it be the same
         * for all commands?
@@ -133,6 +153,23 @@ int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
 }
 EXPORT_SYMBOL(nfc_hci_send_cmd);
 
+int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
+                          const u8 *param, size_t param_len,
+                          data_exchange_cb_t cb, void *cb_context)
+{
+       u8 pipe;
+
+       pr_debug("\n");
+
+       pipe = hdev->gate2pipe[gate];
+       if (pipe == NFC_HCI_INVALID_PIPE)
+               return -EADDRNOTAVAIL;
+
+       return nfc_hci_execute_cmd_async(hdev, pipe, cmd, param, param_len,
+                                        cb, cb_context);
+}
+EXPORT_SYMBOL(nfc_hci_send_cmd_async);
+
 int nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
                      const u8 *param, size_t param_len)
 {
index 1ac7b3fac6c9bb9e12030efb67d540821f690988..d378d93de62e8825d31166c567ececbfd1564153 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <net/nfc/nfc.h>
 #include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
 
 #include "hci.h"
 
@@ -57,12 +58,11 @@ static void nfc_hci_msg_tx_work(struct work_struct *work)
        if (hdev->cmd_pending_msg) {
                if (timer_pending(&hdev->cmd_timer) == 0) {
                        if (hdev->cmd_pending_msg->cb)
-                               hdev->cmd_pending_msg->cb(hdev,
-                                                         -ETIME,
-                                                         NULL,
-                                                         hdev->
+                               hdev->cmd_pending_msg->cb(hdev->
                                                          cmd_pending_msg->
-                                                         cb_context);
+                                                         cb_context,
+                                                         NULL,
+                                                         -ETIME);
                        kfree(hdev->cmd_pending_msg);
                        hdev->cmd_pending_msg = NULL;
                } else
@@ -78,12 +78,12 @@ next_msg:
 
        pr_debug("msg_tx_queue has a cmd to send\n");
        while ((skb = skb_dequeue(&msg->msg_frags)) != NULL) {
-               r = hdev->ops->xmit(hdev, skb);
+               r = nfc_llc_xmit_from_hci(hdev->llc, skb);
                if (r < 0) {
                        kfree_skb(skb);
                        skb_queue_purge(&msg->msg_frags);
                        if (msg->cb)
-                               msg->cb(hdev, r, NULL, msg->cb_context);
+                               msg->cb(msg->cb_context, NULL, r);
                        kfree(msg);
                        break;
                }
@@ -133,15 +133,15 @@ static void __nfc_hci_cmd_completion(struct nfc_hci_dev *hdev, int err,
        del_timer_sync(&hdev->cmd_timer);
 
        if (hdev->cmd_pending_msg->cb)
-               hdev->cmd_pending_msg->cb(hdev, err, skb,
-                                         hdev->cmd_pending_msg->cb_context);
+               hdev->cmd_pending_msg->cb(hdev->cmd_pending_msg->cb_context,
+                                         skb, err);
        else
                kfree_skb(skb);
 
        kfree(hdev->cmd_pending_msg);
        hdev->cmd_pending_msg = NULL;
 
-       queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
+       queue_work(system_nrt_wq, &hdev->msg_tx_work);
 }
 
 void nfc_hci_resp_received(struct nfc_hci_dev *hdev, u8 result,
@@ -326,7 +326,7 @@ static void nfc_hci_cmd_timeout(unsigned long data)
 {
        struct nfc_hci_dev *hdev = (struct nfc_hci_dev *)data;
 
-       queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
+       queue_work(system_nrt_wq, &hdev->msg_tx_work);
 }
 
 static int hci_dev_connect_gates(struct nfc_hci_dev *hdev, u8 gate_count,
@@ -398,8 +398,7 @@ disconnect_all:
        nfc_hci_disconnect_all_gates(hdev);
 
 exit:
-       if (skb)
-               kfree_skb(skb);
+       kfree_skb(skb);
 
        return r;
 }
@@ -470,29 +469,38 @@ static int hci_dev_up(struct nfc_dev *nfc_dev)
                        return r;
        }
 
+       r = nfc_llc_start(hdev->llc);
+       if (r < 0)
+               goto exit_close;
+
        r = hci_dev_session_init(hdev);
        if (r < 0)
-               goto exit;
+               goto exit_llc;
 
        r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
                               NFC_HCI_EVT_END_OPERATION, NULL, 0);
        if (r < 0)
-               goto exit;
+               goto exit_llc;
 
        if (hdev->ops->hci_ready) {
                r = hdev->ops->hci_ready(hdev);
                if (r < 0)
-                       goto exit;
+                       goto exit_llc;
        }
 
        r = hci_dev_version(hdev);
        if (r < 0)
-               goto exit;
+               goto exit_llc;
+
+       return 0;
+
+exit_llc:
+       nfc_llc_stop(hdev->llc);
+
+exit_close:
+       if (hdev->ops->close)
+               hdev->ops->close(hdev);
 
-exit:
-       if (r < 0)
-               if (hdev->ops->close)
-                       hdev->ops->close(hdev);
        return r;
 }
 
@@ -500,6 +508,8 @@ static int hci_dev_down(struct nfc_dev *nfc_dev)
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
 
+       nfc_llc_stop(hdev->llc);
+
        if (hdev->ops->close)
                hdev->ops->close(hdev);
 
@@ -539,13 +549,37 @@ static void hci_deactivate_target(struct nfc_dev *nfc_dev,
 {
 }
 
+#define HCI_CB_TYPE_TRANSCEIVE 1
+
+static void hci_transceive_cb(void *context, struct sk_buff *skb, int err)
+{
+       struct nfc_hci_dev *hdev = context;
+
+       switch (hdev->async_cb_type) {
+       case HCI_CB_TYPE_TRANSCEIVE:
+               /*
+                * TODO: Check RF Error indicator to make sure data is valid.
+                * It seems that HCI cmd can complete without error, but data
+                * can be invalid if an RF error occured? Ignore for now.
+                */
+               if (err == 0)
+                       skb_trim(skb, skb->len - 1); /* RF Err ind */
+
+               hdev->async_cb(hdev->async_cb_context, skb, err);
+               break;
+       default:
+               if (err == 0)
+                       kfree_skb(skb);
+               break;
+       }
+}
+
 static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
                          struct sk_buff *skb, data_exchange_cb_t cb,
                          void *cb_context)
 {
        struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
        int r;
-       struct sk_buff *res_skb = NULL;
 
        pr_debug("target_idx=%d\n", target->idx);
 
@@ -553,40 +587,37 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
        case NFC_HCI_RF_READER_A_GATE:
        case NFC_HCI_RF_READER_B_GATE:
                if (hdev->ops->data_exchange) {
-                       r = hdev->ops->data_exchange(hdev, target, skb,
-                                                    &res_skb);
+                       r = hdev->ops->data_exchange(hdev, target, skb, cb,
+                                                    cb_context);
                        if (r <= 0)     /* handled */
                                break;
                }
 
                *skb_push(skb, 1) = 0;  /* CTR, see spec:10.2.2.1 */
-               r = nfc_hci_send_cmd(hdev, target->hci_reader_gate,
-                                    NFC_HCI_WR_XCHG_DATA,
-                                    skb->data, skb->len, &res_skb);
-               /*
-                * TODO: Check RF Error indicator to make sure data is valid.
-                * It seems that HCI cmd can complete without error, but data
-                * can be invalid if an RF error occured? Ignore for now.
-                */
-               if (r == 0)
-                       skb_trim(res_skb, res_skb->len - 1); /* RF Err ind */
+
+               hdev->async_cb_type = HCI_CB_TYPE_TRANSCEIVE;
+               hdev->async_cb = cb;
+               hdev->async_cb_context = cb_context;
+
+               r = nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
+                                          NFC_HCI_WR_XCHG_DATA, skb->data,
+                                          skb->len, hci_transceive_cb, hdev);
                break;
        default:
                if (hdev->ops->data_exchange) {
-                       r = hdev->ops->data_exchange(hdev, target, skb,
-                                                    &res_skb);
+                       r = hdev->ops->data_exchange(hdev, target, skb, cb,
+                                                    cb_context);
                        if (r == 1)
                                r = -ENOTSUPP;
                }
                else
                        r = -ENOTSUPP;
+               break;
        }
 
        kfree_skb(skb);
 
-       cb(cb_context, res_skb, r);
-
-       return 0;
+       return r;
 }
 
 static int hci_check_presence(struct nfc_dev *nfc_dev,
@@ -600,6 +631,93 @@ static int hci_check_presence(struct nfc_dev *nfc_dev,
        return 0;
 }
 
+static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err)
+{
+       mutex_lock(&hdev->msg_tx_mutex);
+
+       if (hdev->cmd_pending_msg == NULL) {
+               nfc_driver_failure(hdev->ndev, err);
+               goto exit;
+       }
+
+       __nfc_hci_cmd_completion(hdev, err, NULL);
+
+exit:
+       mutex_unlock(&hdev->msg_tx_mutex);
+}
+
+static void nfc_hci_llc_failure(struct nfc_hci_dev *hdev, int err)
+{
+       nfc_hci_failure(hdev, err);
+}
+
+static void nfc_hci_recv_from_llc(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+{
+       struct hcp_packet *packet;
+       u8 type;
+       u8 instruction;
+       struct sk_buff *hcp_skb;
+       u8 pipe;
+       struct sk_buff *frag_skb;
+       int msg_len;
+
+       packet = (struct hcp_packet *)skb->data;
+       if ((packet->header & ~NFC_HCI_FRAGMENT) == 0) {
+               skb_queue_tail(&hdev->rx_hcp_frags, skb);
+               return;
+       }
+
+       /* it's the last fragment. Does it need re-aggregation? */
+       if (skb_queue_len(&hdev->rx_hcp_frags)) {
+               pipe = packet->header & NFC_HCI_FRAGMENT;
+               skb_queue_tail(&hdev->rx_hcp_frags, skb);
+
+               msg_len = 0;
+               skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
+                       msg_len += (frag_skb->len -
+                                   NFC_HCI_HCP_PACKET_HEADER_LEN);
+               }
+
+               hcp_skb = nfc_alloc_recv_skb(NFC_HCI_HCP_PACKET_HEADER_LEN +
+                                            msg_len, GFP_KERNEL);
+               if (hcp_skb == NULL) {
+                       nfc_hci_failure(hdev, -ENOMEM);
+                       return;
+               }
+
+               *skb_put(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN) = pipe;
+
+               skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
+                       msg_len = frag_skb->len - NFC_HCI_HCP_PACKET_HEADER_LEN;
+                       memcpy(skb_put(hcp_skb, msg_len),
+                              frag_skb->data + NFC_HCI_HCP_PACKET_HEADER_LEN,
+                              msg_len);
+               }
+
+               skb_queue_purge(&hdev->rx_hcp_frags);
+       } else {
+               packet->header &= NFC_HCI_FRAGMENT;
+               hcp_skb = skb;
+       }
+
+       /* if this is a response, dispatch immediately to
+        * unblock waiting cmd context. Otherwise, enqueue to dispatch
+        * in separate context where handler can also execute command.
+        */
+       packet = (struct hcp_packet *)hcp_skb->data;
+       type = HCP_MSG_GET_TYPE(packet->message.header);
+       if (type == NFC_HCI_HCP_RESPONSE) {
+               pipe = packet->header;
+               instruction = HCP_MSG_GET_CMD(packet->message.header);
+               skb_pull(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN +
+                        NFC_HCI_HCP_MESSAGE_HEADER_LEN);
+               nfc_hci_hcp_message_rx(hdev, pipe, type, instruction, hcp_skb);
+       } else {
+               skb_queue_tail(&hdev->msg_rx_queue, hcp_skb);
+               queue_work(system_nrt_wq, &hdev->msg_rx_work);
+       }
+}
+
 static struct nfc_ops hci_nfc_ops = {
        .dev_up = hci_dev_up,
        .dev_down = hci_dev_down,
@@ -614,6 +732,7 @@ static struct nfc_ops hci_nfc_ops = {
 struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
                                            struct nfc_hci_init_data *init_data,
                                            u32 protocols,
+                                           const char *llc_name,
                                            int tx_headroom,
                                            int tx_tailroom,
                                            int max_link_payload)
@@ -630,10 +749,19 @@ struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
        if (hdev == NULL)
                return NULL;
 
+       hdev->llc = nfc_llc_allocate(llc_name, hdev, ops->xmit,
+                                    nfc_hci_recv_from_llc, tx_headroom,
+                                    tx_tailroom, nfc_hci_llc_failure);
+       if (hdev->llc == NULL) {
+               kfree(hdev);
+               return NULL;
+       }
+
        hdev->ndev = nfc_allocate_device(&hci_nfc_ops, protocols,
                                         tx_headroom + HCI_CMDS_HEADROOM,
                                         tx_tailroom);
        if (!hdev->ndev) {
+               nfc_llc_free(hdev->llc);
                kfree(hdev);
                return NULL;
        }
@@ -653,29 +781,18 @@ EXPORT_SYMBOL(nfc_hci_allocate_device);
 void nfc_hci_free_device(struct nfc_hci_dev *hdev)
 {
        nfc_free_device(hdev->ndev);
+       nfc_llc_free(hdev->llc);
        kfree(hdev);
 }
 EXPORT_SYMBOL(nfc_hci_free_device);
 
 int nfc_hci_register_device(struct nfc_hci_dev *hdev)
 {
-       struct device *dev = &hdev->ndev->dev;
-       const char *devname = dev_name(dev);
-       char name[32];
-       int r = 0;
-
        mutex_init(&hdev->msg_tx_mutex);
 
        INIT_LIST_HEAD(&hdev->msg_tx_queue);
 
        INIT_WORK(&hdev->msg_tx_work, nfc_hci_msg_tx_work);
-       snprintf(name, sizeof(name), "%s_hci_msg_tx_wq", devname);
-       hdev->msg_tx_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
-                                         WQ_MEM_RECLAIM, 1);
-       if (hdev->msg_tx_wq == NULL) {
-               r = -ENOMEM;
-               goto exit;
-       }
 
        init_timer(&hdev->cmd_timer);
        hdev->cmd_timer.data = (unsigned long)hdev;
@@ -684,27 +801,10 @@ int nfc_hci_register_device(struct nfc_hci_dev *hdev)
        skb_queue_head_init(&hdev->rx_hcp_frags);
 
        INIT_WORK(&hdev->msg_rx_work, nfc_hci_msg_rx_work);
-       snprintf(name, sizeof(name), "%s_hci_msg_rx_wq", devname);
-       hdev->msg_rx_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
-                                         WQ_MEM_RECLAIM, 1);
-       if (hdev->msg_rx_wq == NULL) {
-               r = -ENOMEM;
-               goto exit;
-       }
 
        skb_queue_head_init(&hdev->msg_rx_queue);
 
-       r = nfc_register_device(hdev->ndev);
-
-exit:
-       if (r < 0) {
-               if (hdev->msg_tx_wq)
-                       destroy_workqueue(hdev->msg_tx_wq);
-               if (hdev->msg_rx_wq)
-                       destroy_workqueue(hdev->msg_rx_wq);
-       }
-
-       return r;
+       return nfc_register_device(hdev->ndev);
 }
 EXPORT_SYMBOL(nfc_hci_register_device);
 
@@ -725,9 +825,8 @@ void nfc_hci_unregister_device(struct nfc_hci_dev *hdev)
 
        nfc_unregister_device(hdev->ndev);
 
-       destroy_workqueue(hdev->msg_tx_wq);
-
-       destroy_workqueue(hdev->msg_rx_wq);
+       cancel_work_sync(&hdev->msg_tx_work);
+       cancel_work_sync(&hdev->msg_rx_work);
 }
 EXPORT_SYMBOL(nfc_hci_unregister_device);
 
@@ -743,93 +842,30 @@ void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev)
 }
 EXPORT_SYMBOL(nfc_hci_get_clientdata);
 
-static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err)
-{
-       mutex_lock(&hdev->msg_tx_mutex);
-
-       if (hdev->cmd_pending_msg == NULL) {
-               nfc_driver_failure(hdev->ndev, err);
-               goto exit;
-       }
-
-       __nfc_hci_cmd_completion(hdev, err, NULL);
-
-exit:
-       mutex_unlock(&hdev->msg_tx_mutex);
-}
-
 void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err)
 {
        nfc_hci_failure(hdev, err);
 }
 EXPORT_SYMBOL(nfc_hci_driver_failure);
 
-void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+void inline nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
 {
-       struct hcp_packet *packet;
-       u8 type;
-       u8 instruction;
-       struct sk_buff *hcp_skb;
-       u8 pipe;
-       struct sk_buff *frag_skb;
-       int msg_len;
-
-       packet = (struct hcp_packet *)skb->data;
-       if ((packet->header & ~NFC_HCI_FRAGMENT) == 0) {
-               skb_queue_tail(&hdev->rx_hcp_frags, skb);
-               return;
-       }
-
-       /* it's the last fragment. Does it need re-aggregation? */
-       if (skb_queue_len(&hdev->rx_hcp_frags)) {
-               pipe = packet->header & NFC_HCI_FRAGMENT;
-               skb_queue_tail(&hdev->rx_hcp_frags, skb);
-
-               msg_len = 0;
-               skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
-                       msg_len += (frag_skb->len -
-                                   NFC_HCI_HCP_PACKET_HEADER_LEN);
-               }
-
-               hcp_skb = nfc_alloc_recv_skb(NFC_HCI_HCP_PACKET_HEADER_LEN +
-                                            msg_len, GFP_KERNEL);
-               if (hcp_skb == NULL) {
-                       nfc_hci_failure(hdev, -ENOMEM);
-                       return;
-               }
-
-               *skb_put(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN) = pipe;
-
-               skb_queue_walk(&hdev->rx_hcp_frags, frag_skb) {
-                       msg_len = frag_skb->len - NFC_HCI_HCP_PACKET_HEADER_LEN;
-                       memcpy(skb_put(hcp_skb, msg_len),
-                              frag_skb->data + NFC_HCI_HCP_PACKET_HEADER_LEN,
-                              msg_len);
-               }
+       nfc_llc_rcv_from_drv(hdev->llc, skb);
+}
+EXPORT_SYMBOL(nfc_hci_recv_frame);
 
-               skb_queue_purge(&hdev->rx_hcp_frags);
-       } else {
-               packet->header &= NFC_HCI_FRAGMENT;
-               hcp_skb = skb;
-       }
+static int __init nfc_hci_init(void)
+{
+       return nfc_llc_init();
+}
 
-       /* if this is a response, dispatch immediately to
-        * unblock waiting cmd context. Otherwise, enqueue to dispatch
-        * in separate context where handler can also execute command.
-        */
-       packet = (struct hcp_packet *)hcp_skb->data;
-       type = HCP_MSG_GET_TYPE(packet->message.header);
-       if (type == NFC_HCI_HCP_RESPONSE) {
-               pipe = packet->header;
-               instruction = HCP_MSG_GET_CMD(packet->message.header);
-               skb_pull(hcp_skb, NFC_HCI_HCP_PACKET_HEADER_LEN +
-                        NFC_HCI_HCP_MESSAGE_HEADER_LEN);
-               nfc_hci_hcp_message_rx(hdev, pipe, type, instruction, hcp_skb);
-       } else {
-               skb_queue_tail(&hdev->msg_rx_queue, hcp_skb);
-               queue_work(hdev->msg_rx_wq, &hdev->msg_rx_work);
-       }
+static void __exit nfc_hci_exit(void)
+{
+       nfc_llc_exit();
 }
-EXPORT_SYMBOL(nfc_hci_recv_frame);
+
+subsys_initcall(nfc_hci_init);
+module_exit(nfc_hci_exit);
 
 MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("NFC HCI Core");
index fa9a21e922396bd396dc11a3b9dd916282b39ef9..b274d12c18ac5d7b5ac3a6aa4e4fc943738d6eea 100644 (file)
@@ -20,6 +20,8 @@
 #ifndef __LOCAL_HCI_H
 #define __LOCAL_HCI_H
 
+#include <net/nfc/hci.h>
+
 struct gate_pipe_map {
        u8 gate;
        u8 pipe;
@@ -35,15 +37,6 @@ struct hcp_packet {
        struct hcp_message message;
 } __packed;
 
-/*
- * HCI command execution completion callback.
- * result will be a standard linux error (may be converted from HCI response)
- * skb contains the response data and must be disposed, or may be NULL if
- * an error occured
- */
-typedef void (*hci_cmd_cb_t) (struct nfc_hci_dev *hdev, int result,
-                             struct sk_buff *skb, void *cb_data);
-
 struct hcp_exec_waiter {
        wait_queue_head_t *wq;
        bool exec_complete;
@@ -55,7 +48,7 @@ struct hci_msg {
        struct list_head msg_l;
        struct sk_buff_head msg_frags;
        bool wait_response;
-       hci_cmd_cb_t cb;
+       data_exchange_cb_t cb;
        void *cb_context;
        unsigned long completion_delay;
 };
@@ -83,7 +76,7 @@ struct hci_create_pipe_resp {
 int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
                           u8 type, u8 instruction,
                           const u8 *payload, size_t payload_len,
-                          hci_cmd_cb_t cb, void *cb_data,
+                          data_exchange_cb_t cb, void *cb_context,
                           unsigned long completion_delay);
 
 u8 nfc_hci_pipe2gate(struct nfc_hci_dev *hdev, u8 pipe);
index f4dad1a8974078864fd43f650e52598a6b46c566..208eedd07ee39d1e56aa3d919362979cf71d3bbe 100644 (file)
@@ -35,7 +35,7 @@
 int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
                           u8 type, u8 instruction,
                           const u8 *payload, size_t payload_len,
-                          hci_cmd_cb_t cb, void *cb_data,
+                          data_exchange_cb_t cb, void *cb_context,
                           unsigned long completion_delay)
 {
        struct nfc_dev *ndev = hdev->ndev;
@@ -52,7 +52,7 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
        skb_queue_head_init(&cmd->msg_frags);
        cmd->wait_response = (type == NFC_HCI_HCP_COMMAND) ? true : false;
        cmd->cb = cb;
-       cmd->cb_context = cb_data;
+       cmd->cb_context = cb_context;
        cmd->completion_delay = completion_delay;
 
        hci_len = payload_len + 1;
@@ -108,7 +108,7 @@ int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
        list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue);
        mutex_unlock(&hdev->msg_tx_mutex);
 
-       queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work);
+       queue_work(system_nrt_wq, &hdev->msg_tx_work);
 
        return 0;
 
diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c
new file mode 100644 (file)
index 0000000..ae1205d
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Link Layer Control manager
+ *
+ * Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions 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 <net/nfc/llc.h>
+
+#include "llc.h"
+
+static struct list_head llc_engines;
+
+int nfc_llc_init(void)
+{
+       int r;
+
+       INIT_LIST_HEAD(&llc_engines);
+
+       r = nfc_llc_nop_register();
+       if (r)
+               goto exit;
+
+       r = nfc_llc_shdlc_register();
+       if (r)
+               goto exit;
+
+       return 0;
+
+exit:
+       nfc_llc_exit();
+       return r;
+}
+
+void nfc_llc_exit(void)
+{
+       struct nfc_llc_engine *llc_engine, *n;
+
+       list_for_each_entry_safe(llc_engine, n, &llc_engines, entry) {
+               list_del(&llc_engine->entry);
+               kfree(llc_engine->name);
+               kfree(llc_engine);
+       }
+}
+
+int nfc_llc_register(const char *name, struct nfc_llc_ops *ops)
+{
+       struct nfc_llc_engine *llc_engine;
+
+       llc_engine = kzalloc(sizeof(struct nfc_llc_engine), GFP_KERNEL);
+       if (llc_engine == NULL)
+               return -ENOMEM;
+
+       llc_engine->name = kstrdup(name, GFP_KERNEL);
+       if (llc_engine->name == NULL) {
+               kfree(llc_engine);
+               return -ENOMEM;
+       }
+       llc_engine->ops = ops;
+
+       INIT_LIST_HEAD(&llc_engine->entry);
+       list_add_tail (&llc_engine->entry, &llc_engines);
+
+       return 0;
+}
+
+static struct nfc_llc_engine *nfc_llc_name_to_engine(const char *name)
+{
+       struct nfc_llc_engine *llc_engine;
+
+       list_for_each_entry(llc_engine, &llc_engines, entry) {
+               if (strcmp(llc_engine->name, name) == 0)
+                       return llc_engine;
+       }
+
+       return NULL;
+}
+
+void nfc_llc_unregister(const char *name)
+{
+       struct nfc_llc_engine *llc_engine;
+
+       llc_engine = nfc_llc_name_to_engine(name);
+       if (llc_engine == NULL)
+               return;
+
+       list_del(&llc_engine->entry);
+       kfree(llc_engine->name);
+       kfree(llc_engine);
+}
+
+struct nfc_llc *nfc_llc_allocate(const char *name, struct nfc_hci_dev *hdev,
+                                xmit_to_drv_t xmit_to_drv,
+                                rcv_to_hci_t rcv_to_hci, int tx_headroom,
+                                int tx_tailroom, llc_failure_t llc_failure)
+{
+       struct nfc_llc_engine *llc_engine;
+       struct nfc_llc *llc;
+
+       llc_engine = nfc_llc_name_to_engine(name);
+       if (llc_engine == NULL)
+               return NULL;
+
+       llc = kzalloc(sizeof(struct nfc_llc), GFP_KERNEL);
+       if (llc == NULL)
+               return NULL;
+
+       llc->data = llc_engine->ops->init(hdev, xmit_to_drv, rcv_to_hci,
+                                         tx_headroom, tx_tailroom,
+                                         &llc->rx_headroom, &llc->rx_tailroom,
+                                         llc_failure);
+       if (llc->data == NULL) {
+               kfree(llc);
+               return NULL;
+       }
+       llc->ops = llc_engine->ops;
+
+       return llc;
+}
+
+void nfc_llc_free(struct nfc_llc *llc)
+{
+       llc->ops->deinit(llc);
+       kfree(llc);
+}
+
+inline void nfc_llc_get_rx_head_tail_room(struct nfc_llc *llc, int *rx_headroom,
+                                         int *rx_tailroom)
+{
+       *rx_headroom = llc->rx_headroom;
+       *rx_tailroom = llc->rx_tailroom;
+}
+
+inline int nfc_llc_start(struct nfc_llc *llc)
+{
+       return llc->ops->start(llc);
+}
+
+inline int nfc_llc_stop(struct nfc_llc *llc)
+{
+       return llc->ops->stop(llc);
+}
+
+inline void nfc_llc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
+{
+       llc->ops->rcv_from_drv(llc, skb);
+}
+
+inline int nfc_llc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
+{
+       return llc->ops->xmit_from_hci(llc, skb);
+}
+
+inline void *nfc_llc_get_data(struct nfc_llc *llc)
+{
+       return llc->data;
+}
diff --git a/net/nfc/hci/llc.h b/net/nfc/hci/llc.h
new file mode 100644 (file)
index 0000000..7be0b7f
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Link Layer Control manager
+ *
+ * Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions 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.
+ */
+
+#ifndef __LOCAL_LLC_H_
+#define __LOCAL_LLC_H_
+
+#include <net/nfc/hci.h>
+#include <net/nfc/llc.h>
+#include <linux/skbuff.h>
+
+struct nfc_llc_ops {
+       void *(*init) (struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+                      rcv_to_hci_t rcv_to_hci, int tx_headroom,
+                      int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+                      llc_failure_t llc_failure);
+       void (*deinit) (struct nfc_llc *llc);
+       int (*start) (struct nfc_llc *llc);
+       int (*stop) (struct nfc_llc *llc);
+       void (*rcv_from_drv) (struct nfc_llc *llc, struct sk_buff *skb);
+       int (*xmit_from_hci) (struct nfc_llc *llc, struct sk_buff *skb);
+};
+
+struct nfc_llc_engine {
+       const char *name;
+       struct nfc_llc_ops *ops;
+       struct list_head entry;
+};
+
+struct nfc_llc {
+       void *data;
+       struct nfc_llc_ops *ops;
+       int rx_headroom;
+       int rx_tailroom;
+};
+
+void *nfc_llc_get_data(struct nfc_llc *llc);
+
+int nfc_llc_register(const char *name, struct nfc_llc_ops *ops);
+void nfc_llc_unregister(const char *name);
+
+int nfc_llc_nop_register(void);
+
+#if defined(CONFIG_NFC_SHDLC)
+int nfc_llc_shdlc_register(void);
+#else
+static inline int nfc_llc_shdlc_register(void)
+{
+       return 0;
+}
+#endif
+
+#endif /* __LOCAL_LLC_H_ */
diff --git a/net/nfc/hci/llc_nop.c b/net/nfc/hci/llc_nop.c
new file mode 100644 (file)
index 0000000..87b1029
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * nop (passthrough) Link Layer Control
+ *
+ * Copyright (C) 2012  Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions 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/types.h>
+
+#include "llc.h"
+
+struct llc_nop {
+       struct nfc_hci_dev *hdev;
+       xmit_to_drv_t xmit_to_drv;
+       rcv_to_hci_t rcv_to_hci;
+       int tx_headroom;
+       int tx_tailroom;
+       llc_failure_t llc_failure;
+};
+
+static void *llc_nop_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+                         rcv_to_hci_t rcv_to_hci, int tx_headroom,
+                         int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+                         llc_failure_t llc_failure)
+{
+       struct llc_nop *llc_nop;
+
+       *rx_headroom = 0;
+       *rx_tailroom = 0;
+
+       llc_nop = kzalloc(sizeof(struct llc_nop), GFP_KERNEL);
+       if (llc_nop == NULL)
+               return NULL;
+
+       llc_nop->hdev = hdev;
+       llc_nop->xmit_to_drv = xmit_to_drv;
+       llc_nop->rcv_to_hci = rcv_to_hci;
+       llc_nop->tx_headroom = tx_headroom;
+       llc_nop->tx_tailroom = tx_tailroom;
+       llc_nop->llc_failure = llc_failure;
+
+       return llc_nop;
+}
+
+static void llc_nop_deinit(struct nfc_llc *llc)
+{
+       kfree(nfc_llc_get_data(llc));
+}
+
+static int llc_nop_start(struct nfc_llc *llc)
+{
+       return 0;
+}
+
+static int llc_nop_stop(struct nfc_llc *llc)
+{
+       return 0;
+}
+
+static void llc_nop_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
+{
+       struct llc_nop *llc_nop = nfc_llc_get_data(llc);
+
+       llc_nop->rcv_to_hci(llc_nop->hdev, skb);
+}
+
+static int llc_nop_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
+{
+       struct llc_nop *llc_nop = nfc_llc_get_data(llc);
+
+       return llc_nop->xmit_to_drv(llc_nop->hdev, skb);
+}
+
+static struct nfc_llc_ops llc_nop_ops = {
+       .init = llc_nop_init,
+       .deinit = llc_nop_deinit,
+       .start = llc_nop_start,
+       .stop = llc_nop_stop,
+       .rcv_from_drv = llc_nop_rcv_from_drv,
+       .xmit_from_hci = llc_nop_xmit_from_hci,
+};
+
+int nfc_llc_nop_register(void)
+{
+       return nfc_llc_register(LLC_NOP_NAME, &llc_nop_ops);
+}
similarity index 54%
rename from net/nfc/hci/shdlc.c
rename to net/nfc/hci/llc_shdlc.c
index 6f840c18c892e351adc19f30e3a16513751d4f77..8f69d791dcb38f20a386f36654298b4f92c4a275 100644 (file)
@@ -1,10 +1,11 @@
 /*
+ * shdlc Link Layer Control
+ *
  * Copyright (C) 2012  Intel Corporation. All rights reserved.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions 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
 
 #define pr_fmt(fmt) "shdlc: %s: " fmt, __func__
 
+#include <linux/types.h>
 #include <linux/sched.h>
-#include <linux/export.h>
 #include <linux/wait.h>
-#include <linux/crc-ccitt.h>
 #include <linux/slab.h>
 #include <linux/skbuff.h>
 
-#include <net/nfc/hci.h>
-#include <net/nfc/shdlc.h>
+#include "llc.h"
+
+enum shdlc_state {
+       SHDLC_DISCONNECTED = 0,
+       SHDLC_CONNECTING = 1,
+       SHDLC_NEGOTIATING = 2,
+       SHDLC_HALF_CONNECTED = 3,
+       SHDLC_CONNECTED = 4
+};
+
+struct llc_shdlc {
+       struct nfc_hci_dev *hdev;
+       xmit_to_drv_t xmit_to_drv;
+       rcv_to_hci_t rcv_to_hci;
+
+       struct mutex state_mutex;
+       enum shdlc_state state;
+       int hard_fault;
+
+       wait_queue_head_t *connect_wq;
+       int connect_tries;
+       int connect_result;
+       struct timer_list connect_timer;/* aka T3 in spec 10.6.1 */
+
+       u8 w;                           /* window size */
+       bool srej_support;
+
+       struct timer_list t1_timer;     /* send ack timeout */
+       bool t1_active;
+
+       struct timer_list t2_timer;     /* guard/retransmit timeout */
+       bool t2_active;
+
+       int ns;                         /* next seq num for send */
+       int nr;                         /* next expected seq num for receive */
+       int dnr;                        /* oldest sent unacked seq num */
+
+       struct sk_buff_head rcv_q;
+
+       struct sk_buff_head send_q;
+       bool rnr;                       /* other side is not ready to receive */
+
+       struct sk_buff_head ack_pending_q;
+
+       struct work_struct sm_work;
+
+       int tx_headroom;
+       int tx_tailroom;
+
+       llc_failure_t llc_failure;
+};
 
 #define SHDLC_LLC_HEAD_ROOM    2
-#define SHDLC_LLC_TAIL_ROOM    2
 
 #define SHDLC_MAX_WINDOW       4
 #define SHDLC_SREJ_SUPPORT     false
@@ -71,7 +119,7 @@ do {                                                           \
 } while (0)
 
 /* checks x < y <= z modulo 8 */
-static bool nfc_shdlc_x_lt_y_lteq_z(int x, int y, int z)
+static bool llc_shdlc_x_lt_y_lteq_z(int x, int y, int z)
 {
        if (x < z)
                return ((x < y) && (y <= z)) ? true : false;
@@ -80,7 +128,7 @@ static bool nfc_shdlc_x_lt_y_lteq_z(int x, int y, int z)
 }
 
 /* checks x <= y < z modulo 8 */
-static bool nfc_shdlc_x_lteq_y_lt_z(int x, int y, int z)
+static bool llc_shdlc_x_lteq_y_lt_z(int x, int y, int z)
 {
        if (x <= z)
                return ((x <= y) && (y < z)) ? true : false;
@@ -88,36 +136,21 @@ static bool nfc_shdlc_x_lteq_y_lt_z(int x, int y, int z)
                return ((y >= x) || (y < z)) ? true : false;
 }
 
-static struct sk_buff *nfc_shdlc_alloc_skb(struct nfc_shdlc *shdlc,
+static struct sk_buff *llc_shdlc_alloc_skb(struct llc_shdlc *shdlc,
                                           int payload_len)
 {
        struct sk_buff *skb;
 
-       skb = alloc_skb(shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM +
-                       shdlc->client_tailroom + SHDLC_LLC_TAIL_ROOM +
-                       payload_len, GFP_KERNEL);
+       skb = alloc_skb(shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM +
+                       shdlc->tx_tailroom + payload_len, GFP_KERNEL);
        if (skb)
-               skb_reserve(skb, shdlc->client_headroom + SHDLC_LLC_HEAD_ROOM);
+               skb_reserve(skb, shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM);
 
        return skb;
 }
 
-static void nfc_shdlc_add_len_crc(struct sk_buff *skb)
-{
-       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;
-}
-
 /* immediately sends an S frame. */
-static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc,
+static int llc_shdlc_send_s_frame(struct llc_shdlc *shdlc,
                                  enum sframe_type sframe_type, int nr)
 {
        int r;
@@ -125,15 +158,13 @@ static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc,
 
        pr_debug("sframe_type=%d nr=%d\n", sframe_type, nr);
 
-       skb = nfc_shdlc_alloc_skb(shdlc, 0);
+       skb = llc_shdlc_alloc_skb(shdlc, 0);
        if (skb == NULL)
                return -ENOMEM;
 
        *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_S | (sframe_type << 3) | nr;
 
-       nfc_shdlc_add_len_crc(skb);
-
-       r = shdlc->ops->xmit(shdlc, skb);
+       r = shdlc->xmit_to_drv(shdlc->hdev, skb);
 
        kfree_skb(skb);
 
@@ -141,7 +172,7 @@ static int nfc_shdlc_send_s_frame(struct nfc_shdlc *shdlc,
 }
 
 /* immediately sends an U frame. skb may contain optional payload */
-static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc,
+static int llc_shdlc_send_u_frame(struct llc_shdlc *shdlc,
                                  struct sk_buff *skb,
                                  enum uframe_modifier uframe_modifier)
 {
@@ -151,9 +182,7 @@ static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc,
 
        *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_U | uframe_modifier;
 
-       nfc_shdlc_add_len_crc(skb);
-
-       r = shdlc->ops->xmit(shdlc, skb);
+       r = shdlc->xmit_to_drv(shdlc->hdev, skb);
 
        kfree_skb(skb);
 
@@ -164,7 +193,7 @@ static int nfc_shdlc_send_u_frame(struct nfc_shdlc *shdlc,
  * Free ack_pending frames until y_nr - 1, and reset t2 according to
  * the remaining oldest ack_pending frame sent time
  */
-static void nfc_shdlc_reset_t2(struct nfc_shdlc *shdlc, int y_nr)
+static void llc_shdlc_reset_t2(struct llc_shdlc *shdlc, int y_nr)
 {
        struct sk_buff *skb;
        int dnr = shdlc->dnr;   /* MUST initially be < y_nr */
@@ -204,7 +233,7 @@ static void nfc_shdlc_reset_t2(struct nfc_shdlc *shdlc, int y_nr)
  * Receive validated frames from lower layer. skb contains HCI payload only.
  * Handle according to algorithm at spec:10.8.2
  */
-static void nfc_shdlc_rcv_i_frame(struct nfc_shdlc *shdlc,
+static void llc_shdlc_rcv_i_frame(struct llc_shdlc *shdlc,
                                  struct sk_buff *skb, int ns, int nr)
 {
        int x_ns = ns;
@@ -216,66 +245,64 @@ static void nfc_shdlc_rcv_i_frame(struct nfc_shdlc *shdlc,
                goto exit;
 
        if (x_ns != shdlc->nr) {
-               nfc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr);
+               llc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr);
                goto exit;
        }
 
        if (shdlc->t1_active == false) {
                shdlc->t1_active = true;
-               mod_timer(&shdlc->t1_timer,
+               mod_timer(&shdlc->t1_timer, jiffies +
                          msecs_to_jiffies(SHDLC_T1_VALUE_MS(shdlc->w)));
                pr_debug("(re)Start T1(send ack)\n");
        }
 
        if (skb->len) {
-               nfc_hci_recv_frame(shdlc->hdev, skb);
+               shdlc->rcv_to_hci(shdlc->hdev, skb);
                skb = NULL;
        }
 
        shdlc->nr = (shdlc->nr + 1) % 8;
 
-       if (nfc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
-               nfc_shdlc_reset_t2(shdlc, y_nr);
+       if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
+               llc_shdlc_reset_t2(shdlc, y_nr);
 
                shdlc->dnr = y_nr;
        }
 
 exit:
-       if (skb)
-               kfree_skb(skb);
+       kfree_skb(skb);
 }
 
-static void nfc_shdlc_rcv_ack(struct nfc_shdlc *shdlc, int y_nr)
+static void llc_shdlc_rcv_ack(struct llc_shdlc *shdlc, int y_nr)
 {
        pr_debug("remote acked up to frame %d excluded\n", y_nr);
 
-       if (nfc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
-               nfc_shdlc_reset_t2(shdlc, y_nr);
+       if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) {
+               llc_shdlc_reset_t2(shdlc, y_nr);
                shdlc->dnr = y_nr;
        }
 }
 
-static void nfc_shdlc_requeue_ack_pending(struct nfc_shdlc *shdlc)
+static void llc_shdlc_requeue_ack_pending(struct llc_shdlc *shdlc)
 {
        struct sk_buff *skb;
 
        pr_debug("ns reset to %d\n", shdlc->dnr);
 
        while ((skb = skb_dequeue_tail(&shdlc->ack_pending_q))) {
-               skb_pull(skb, 2);       /* remove len+control */
-               skb_trim(skb, skb->len - 2);    /* remove crc */
+               skb_pull(skb, 1);       /* remove control field */
                skb_queue_head(&shdlc->send_q, skb);
        }
        shdlc->ns = shdlc->dnr;
 }
 
-static void nfc_shdlc_rcv_rej(struct nfc_shdlc *shdlc, int y_nr)
+static void llc_shdlc_rcv_rej(struct llc_shdlc *shdlc, int y_nr)
 {
        struct sk_buff *skb;
 
        pr_debug("remote asks retransmition from frame %d\n", y_nr);
 
-       if (nfc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) {
+       if (llc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) {
                if (shdlc->t2_active) {
                        del_timer_sync(&shdlc->t2_timer);
                        shdlc->t2_active = false;
@@ -289,12 +316,12 @@ static void nfc_shdlc_rcv_rej(struct nfc_shdlc *shdlc, int y_nr)
                        }
                }
 
-               nfc_shdlc_requeue_ack_pending(shdlc);
+               llc_shdlc_requeue_ack_pending(shdlc);
        }
 }
 
 /* See spec RR:10.8.3 REJ:10.8.4 */
-static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc,
+static void llc_shdlc_rcv_s_frame(struct llc_shdlc *shdlc,
                                  enum sframe_type s_frame_type, int nr)
 {
        struct sk_buff *skb;
@@ -304,21 +331,21 @@ static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc,
 
        switch (s_frame_type) {
        case S_FRAME_RR:
-               nfc_shdlc_rcv_ack(shdlc, nr);
+               llc_shdlc_rcv_ack(shdlc, nr);
                if (shdlc->rnr == true) {       /* see SHDLC 10.7.7 */
                        shdlc->rnr = false;
                        if (shdlc->send_q.qlen == 0) {
-                               skb = nfc_shdlc_alloc_skb(shdlc, 0);
+                               skb = llc_shdlc_alloc_skb(shdlc, 0);
                                if (skb)
                                        skb_queue_tail(&shdlc->send_q, skb);
                        }
                }
                break;
        case S_FRAME_REJ:
-               nfc_shdlc_rcv_rej(shdlc, nr);
+               llc_shdlc_rcv_rej(shdlc, nr);
                break;
        case S_FRAME_RNR:
-               nfc_shdlc_rcv_ack(shdlc, nr);
+               llc_shdlc_rcv_ack(shdlc, nr);
                shdlc->rnr = true;
                break;
        default:
@@ -326,7 +353,7 @@ static void nfc_shdlc_rcv_s_frame(struct nfc_shdlc *shdlc,
        }
 }
 
-static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r)
+static void llc_shdlc_connect_complete(struct llc_shdlc *shdlc, int r)
 {
        pr_debug("result=%d\n", r);
 
@@ -337,7 +364,7 @@ static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r)
                shdlc->nr = 0;
                shdlc->dnr = 0;
 
-               shdlc->state = SHDLC_CONNECTED;
+               shdlc->state = SHDLC_HALF_CONNECTED;
        } else {
                shdlc->state = SHDLC_DISCONNECTED;
        }
@@ -347,36 +374,36 @@ static void nfc_shdlc_connect_complete(struct nfc_shdlc *shdlc, int r)
        wake_up(shdlc->connect_wq);
 }
 
-static int nfc_shdlc_connect_initiate(struct nfc_shdlc *shdlc)
+static int llc_shdlc_connect_initiate(struct llc_shdlc *shdlc)
 {
        struct sk_buff *skb;
 
        pr_debug("\n");
 
-       skb = nfc_shdlc_alloc_skb(shdlc, 2);
+       skb = llc_shdlc_alloc_skb(shdlc, 2);
        if (skb == NULL)
                return -ENOMEM;
 
        *skb_put(skb, 1) = SHDLC_MAX_WINDOW;
        *skb_put(skb, 1) = SHDLC_SREJ_SUPPORT ? 1 : 0;
 
-       return nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET);
+       return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET);
 }
 
-static int nfc_shdlc_connect_send_ua(struct nfc_shdlc *shdlc)
+static int llc_shdlc_connect_send_ua(struct llc_shdlc *shdlc)
 {
        struct sk_buff *skb;
 
        pr_debug("\n");
 
-       skb = nfc_shdlc_alloc_skb(shdlc, 0);
+       skb = llc_shdlc_alloc_skb(shdlc, 0);
        if (skb == NULL)
                return -ENOMEM;
 
-       return nfc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA);
+       return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA);
 }
 
-static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
+static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc,
                                  struct sk_buff *skb,
                                  enum uframe_modifier u_frame_modifier)
 {
@@ -388,8 +415,13 @@ static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
 
        switch (u_frame_modifier) {
        case U_FRAME_RSET:
-               if (shdlc->state == SHDLC_NEGOCIATING) {
-                       /* we sent RSET, but chip wants to negociate */
+               switch (shdlc->state) {
+               case SHDLC_NEGOTIATING:
+               case SHDLC_CONNECTING:
+                       /*
+                        * We sent RSET, but chip wants to negociate or we
+                        * got RSET before we managed to send out our.
+                        */
                        if (skb->len > 0)
                                w = skb->data[0];
 
@@ -401,22 +433,34 @@ static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
                            (SHDLC_SREJ_SUPPORT || (srej_support == false))) {
                                shdlc->w = w;
                                shdlc->srej_support = srej_support;
-                               r = nfc_shdlc_connect_send_ua(shdlc);
-                               nfc_shdlc_connect_complete(shdlc, r);
+                               r = llc_shdlc_connect_send_ua(shdlc);
+                               llc_shdlc_connect_complete(shdlc, r);
                        }
-               } else if (shdlc->state == SHDLC_CONNECTED) {
+                       break;
+               case SHDLC_HALF_CONNECTED:
+                       /*
+                        * Chip resent RSET due to its timeout - Ignote it
+                        * as we already sent UA.
+                        */
+                       break;
+               case SHDLC_CONNECTED:
                        /*
                         * Chip wants to reset link. This is unexpected and
                         * unsupported.
                         */
                        shdlc->hard_fault = -ECONNRESET;
+                       break;
+               default:
+                       break;
                }
                break;
        case U_FRAME_UA:
                if ((shdlc->state == SHDLC_CONNECTING &&
                     shdlc->connect_tries > 0) ||
-                   (shdlc->state == SHDLC_NEGOCIATING))
-                       nfc_shdlc_connect_complete(shdlc, 0);
+                   (shdlc->state == SHDLC_NEGOTIATING)) {
+                       llc_shdlc_connect_complete(shdlc, 0);
+                       shdlc->state = SHDLC_CONNECTED;
+               }
                break;
        default:
                break;
@@ -425,7 +469,7 @@ static void nfc_shdlc_rcv_u_frame(struct nfc_shdlc *shdlc,
        kfree_skb(skb);
 }
 
-static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc)
+static void llc_shdlc_handle_rcv_queue(struct llc_shdlc *shdlc)
 {
        struct sk_buff *skb;
        u8 control;
@@ -443,19 +487,25 @@ static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc)
                switch (control & SHDLC_CONTROL_HEAD_MASK) {
                case SHDLC_CONTROL_HEAD_I:
                case SHDLC_CONTROL_HEAD_I2:
+                       if (shdlc->state == SHDLC_HALF_CONNECTED)
+                               shdlc->state = SHDLC_CONNECTED;
+
                        ns = (control & SHDLC_CONTROL_NS_MASK) >> 3;
                        nr = control & SHDLC_CONTROL_NR_MASK;
-                       nfc_shdlc_rcv_i_frame(shdlc, skb, ns, nr);
+                       llc_shdlc_rcv_i_frame(shdlc, skb, ns, nr);
                        break;
                case SHDLC_CONTROL_HEAD_S:
+                       if (shdlc->state == SHDLC_HALF_CONNECTED)
+                               shdlc->state = SHDLC_CONNECTED;
+
                        s_frame_type = (control & SHDLC_CONTROL_TYPE_MASK) >> 3;
                        nr = control & SHDLC_CONTROL_NR_MASK;
-                       nfc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr);
+                       llc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr);
                        kfree_skb(skb);
                        break;
                case SHDLC_CONTROL_HEAD_U:
                        u_frame_modifier = control & SHDLC_CONTROL_M_MASK;
-                       nfc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier);
+                       llc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier);
                        break;
                default:
                        pr_err("UNKNOWN Control=%d\n", control);
@@ -465,7 +515,7 @@ static void nfc_shdlc_handle_rcv_queue(struct nfc_shdlc *shdlc)
        }
 }
 
-static int nfc_shdlc_w_used(int ns, int dnr)
+static int llc_shdlc_w_used(int ns, int dnr)
 {
        int unack_count;
 
@@ -478,7 +528,7 @@ static int nfc_shdlc_w_used(int ns, int dnr)
 }
 
 /* Send frames according to algorithm at spec:10.8.1 */
-static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
+static void llc_shdlc_handle_send_queue(struct llc_shdlc *shdlc)
 {
        struct sk_buff *skb;
        int r;
@@ -489,7 +539,7 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
                    ("sendQlen=%d ns=%d dnr=%d rnr=%s w_room=%d unackQlen=%d\n",
                     shdlc->send_q.qlen, shdlc->ns, shdlc->dnr,
                     shdlc->rnr == false ? "false" : "true",
-                    shdlc->w - nfc_shdlc_w_used(shdlc->ns, shdlc->dnr),
+                    shdlc->w - llc_shdlc_w_used(shdlc->ns, shdlc->dnr),
                     shdlc->ack_pending_q.qlen);
 
        while (shdlc->send_q.qlen && shdlc->ack_pending_q.qlen < shdlc->w &&
@@ -508,11 +558,9 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
 
                pr_debug("Sending I-Frame %d, waiting to rcv %d\n", shdlc->ns,
                         shdlc->nr);
-       /*      SHDLC_DUMP_SKB("shdlc frame written", skb); */
-
-               nfc_shdlc_add_len_crc(skb);
+               SHDLC_DUMP_SKB("shdlc frame written", skb);
 
-               r = shdlc->ops->xmit(shdlc, skb);
+               r = shdlc->xmit_to_drv(shdlc->hdev, skb);
                if (r < 0) {
                        shdlc->hard_fault = r;
                        break;
@@ -534,36 +582,36 @@ static void nfc_shdlc_handle_send_queue(struct nfc_shdlc *shdlc)
        }
 }
 
-static void nfc_shdlc_connect_timeout(unsigned long data)
+static void llc_shdlc_connect_timeout(unsigned long data)
 {
-       struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
+       struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
 
        pr_debug("\n");
 
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
+       queue_work(system_nrt_wq, &shdlc->sm_work);
 }
 
-static void nfc_shdlc_t1_timeout(unsigned long data)
+static void llc_shdlc_t1_timeout(unsigned long data)
 {
-       struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
+       struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
 
        pr_debug("SoftIRQ: need to send ack\n");
 
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
+       queue_work(system_nrt_wq, &shdlc->sm_work);
 }
 
-static void nfc_shdlc_t2_timeout(unsigned long data)
+static void llc_shdlc_t2_timeout(unsigned long data)
 {
-       struct nfc_shdlc *shdlc = (struct nfc_shdlc *)data;
+       struct llc_shdlc *shdlc = (struct llc_shdlc *)data;
 
        pr_debug("SoftIRQ: need to retransmit\n");
 
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
+       queue_work(system_nrt_wq, &shdlc->sm_work);
 }
 
-static void nfc_shdlc_sm_work(struct work_struct *work)
+static void llc_shdlc_sm_work(struct work_struct *work)
 {
-       struct nfc_shdlc *shdlc = container_of(work, struct nfc_shdlc, sm_work);
+       struct llc_shdlc *shdlc = container_of(work, struct llc_shdlc, sm_work);
        int r;
 
        pr_debug("\n");
@@ -578,46 +626,47 @@ static void nfc_shdlc_sm_work(struct work_struct *work)
                break;
        case SHDLC_CONNECTING:
                if (shdlc->hard_fault) {
-                       nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
+                       llc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
                        break;
                }
 
                if (shdlc->connect_tries++ < 5)
-                       r = nfc_shdlc_connect_initiate(shdlc);
+                       r = llc_shdlc_connect_initiate(shdlc);
                else
                        r = -ETIME;
                if (r < 0)
-                       nfc_shdlc_connect_complete(shdlc, r);
+                       llc_shdlc_connect_complete(shdlc, r);
                else {
                        mod_timer(&shdlc->connect_timer, jiffies +
                                  msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS));
 
-                       shdlc->state = SHDLC_NEGOCIATING;
+                       shdlc->state = SHDLC_NEGOTIATING;
                }
                break;
-       case SHDLC_NEGOCIATING:
+       case SHDLC_NEGOTIATING:
                if (timer_pending(&shdlc->connect_timer) == 0) {
                        shdlc->state = SHDLC_CONNECTING;
-                       queue_work(shdlc->sm_wq, &shdlc->sm_work);
+                       queue_work(system_nrt_wq, &shdlc->sm_work);
                }
 
-               nfc_shdlc_handle_rcv_queue(shdlc);
+               llc_shdlc_handle_rcv_queue(shdlc);
 
                if (shdlc->hard_fault) {
-                       nfc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
+                       llc_shdlc_connect_complete(shdlc, shdlc->hard_fault);
                        break;
                }
                break;
+       case SHDLC_HALF_CONNECTED:
        case SHDLC_CONNECTED:
-               nfc_shdlc_handle_rcv_queue(shdlc);
-               nfc_shdlc_handle_send_queue(shdlc);
+               llc_shdlc_handle_rcv_queue(shdlc);
+               llc_shdlc_handle_send_queue(shdlc);
 
                if (shdlc->t1_active && timer_pending(&shdlc->t1_timer) == 0) {
                        pr_debug
                            ("Handle T1(send ack) elapsed (T1 now inactive)\n");
 
                        shdlc->t1_active = false;
-                       r = nfc_shdlc_send_s_frame(shdlc, S_FRAME_RR,
+                       r = llc_shdlc_send_s_frame(shdlc, S_FRAME_RR,
                                                   shdlc->nr);
                        if (r < 0)
                                shdlc->hard_fault = r;
@@ -629,12 +678,12 @@ static void nfc_shdlc_sm_work(struct work_struct *work)
 
                        shdlc->t2_active = false;
 
-                       nfc_shdlc_requeue_ack_pending(shdlc);
-                       nfc_shdlc_handle_send_queue(shdlc);
+                       llc_shdlc_requeue_ack_pending(shdlc);
+                       llc_shdlc_handle_send_queue(shdlc);
                }
 
                if (shdlc->hard_fault) {
-                       nfc_hci_driver_failure(shdlc->hdev, shdlc->hard_fault);
+                       shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault);
                }
                break;
        default:
@@ -647,7 +696,7 @@ static void nfc_shdlc_sm_work(struct work_struct *work)
  * Called from syscall context to establish shdlc link. Sleeps until
  * link is ready or failure.
  */
-static int nfc_shdlc_connect(struct nfc_shdlc *shdlc)
+static int llc_shdlc_connect(struct llc_shdlc *shdlc)
 {
        DECLARE_WAIT_QUEUE_HEAD_ONSTACK(connect_wq);
 
@@ -662,14 +711,14 @@ static int nfc_shdlc_connect(struct nfc_shdlc *shdlc)
 
        mutex_unlock(&shdlc->state_mutex);
 
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
+       queue_work(system_nrt_wq, &shdlc->sm_work);
 
        wait_event(connect_wq, shdlc->connect_result != 1);
 
        return shdlc->connect_result;
 }
 
-static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc)
+static void llc_shdlc_disconnect(struct llc_shdlc *shdlc)
 {
        pr_debug("\n");
 
@@ -679,7 +728,7 @@ static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc)
 
        mutex_unlock(&shdlc->state_mutex);
 
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
+       queue_work(system_nrt_wq, &shdlc->sm_work);
 }
 
 /*
@@ -687,7 +736,7 @@ static void nfc_shdlc_disconnect(struct nfc_shdlc *shdlc)
  * skb contains only LLC header and payload.
  * If skb == NULL, it is a notification that the link below is dead.
  */
-void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb)
+static void llc_shdlc_recv_frame(struct llc_shdlc *shdlc, struct sk_buff *skb)
 {
        if (skb == NULL) {
                pr_err("NULL Frame -> link is dead\n");
@@ -697,176 +746,37 @@ void nfc_shdlc_recv_frame(struct nfc_shdlc *shdlc, struct sk_buff *skb)
                skb_queue_tail(&shdlc->rcv_q, skb);
        }
 
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
-}
-EXPORT_SYMBOL(nfc_shdlc_recv_frame);
-
-static int nfc_shdlc_open(struct nfc_hci_dev *hdev)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-       int r;
-
-       pr_debug("\n");
-
-       if (shdlc->ops->open) {
-               r = shdlc->ops->open(shdlc);
-               if (r < 0)
-                       return r;
-       }
-
-       r = nfc_shdlc_connect(shdlc);
-       if (r < 0 && shdlc->ops->close)
-               shdlc->ops->close(shdlc);
-
-       return r;
-}
-
-static void nfc_shdlc_close(struct nfc_hci_dev *hdev)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       pr_debug("\n");
-
-       nfc_shdlc_disconnect(shdlc);
-
-       if (shdlc->ops->close)
-               shdlc->ops->close(shdlc);
+       queue_work(system_nrt_wq, &shdlc->sm_work);
 }
 
-static int nfc_shdlc_hci_ready(struct nfc_hci_dev *hdev)
+static void *llc_shdlc_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv,
+                           rcv_to_hci_t rcv_to_hci, int tx_headroom,
+                           int tx_tailroom, int *rx_headroom, int *rx_tailroom,
+                           llc_failure_t llc_failure)
 {
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-       int r = 0;
-
-       pr_debug("\n");
+       struct llc_shdlc *shdlc;
 
-       if (shdlc->ops->hci_ready)
-               r = shdlc->ops->hci_ready(shdlc);
-
-       return r;
-}
-
-static int nfc_shdlc_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       SHDLC_DUMP_SKB("queuing HCP packet to shdlc", skb);
-
-       skb_queue_tail(&shdlc->send_q, skb);
+       *rx_headroom = SHDLC_LLC_HEAD_ROOM;
+       *rx_tailroom = 0;
 
-       queue_work(shdlc->sm_wq, &shdlc->sm_work);
-
-       return 0;
-}
-
-static int nfc_shdlc_start_poll(struct nfc_hci_dev *hdev,
-                               u32 im_protocols, u32 tm_protocols)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       pr_debug("\n");
-
-       if (shdlc->ops->start_poll)
-               return shdlc->ops->start_poll(shdlc,
-                                             im_protocols, tm_protocols);
-
-       return 0;
-}
-
-static int nfc_shdlc_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
-                                     struct nfc_target *target)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       if (shdlc->ops->target_from_gate)
-               return shdlc->ops->target_from_gate(shdlc, gate, target);
-
-       return -EPERM;
-}
-
-static int nfc_shdlc_complete_target_discovered(struct nfc_hci_dev *hdev,
-                                               u8 gate,
-                                               struct nfc_target *target)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       pr_debug("\n");
-
-       if (shdlc->ops->complete_target_discovered)
-               return shdlc->ops->complete_target_discovered(shdlc, gate,
-                                                             target);
-
-       return 0;
-}
-
-static int nfc_shdlc_data_exchange(struct nfc_hci_dev *hdev,
-                                  struct nfc_target *target,
-                                  struct sk_buff *skb,
-                                  struct sk_buff **res_skb)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       if (shdlc->ops->data_exchange)
-               return shdlc->ops->data_exchange(shdlc, target, skb, res_skb);
-
-       return -EPERM;
-}
-
-static int nfc_shdlc_check_presence(struct nfc_hci_dev *hdev,
-                                   struct nfc_target *target)
-{
-       struct nfc_shdlc *shdlc = nfc_hci_get_clientdata(hdev);
-
-       if (shdlc->ops->check_presence)
-               return shdlc->ops->check_presence(shdlc, target);
-
-       return 0;
-}
-
-static struct nfc_hci_ops shdlc_ops = {
-       .open = nfc_shdlc_open,
-       .close = nfc_shdlc_close,
-       .hci_ready = nfc_shdlc_hci_ready,
-       .xmit = nfc_shdlc_xmit,
-       .start_poll = nfc_shdlc_start_poll,
-       .target_from_gate = nfc_shdlc_target_from_gate,
-       .complete_target_discovered = nfc_shdlc_complete_target_discovered,
-       .data_exchange = nfc_shdlc_data_exchange,
-       .check_presence = nfc_shdlc_check_presence,
-};
-
-struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops,
-                                    struct nfc_hci_init_data *init_data,
-                                    u32 protocols,
-                                    int tx_headroom, int tx_tailroom,
-                                    int max_link_payload, const char *devname)
-{
-       struct nfc_shdlc *shdlc;
-       int r;
-       char name[32];
-
-       if (ops->xmit == NULL)
-               return NULL;
-
-       shdlc = kzalloc(sizeof(struct nfc_shdlc), GFP_KERNEL);
+       shdlc = kzalloc(sizeof(struct llc_shdlc), GFP_KERNEL);
        if (shdlc == NULL)
                return NULL;
 
        mutex_init(&shdlc->state_mutex);
-       shdlc->ops = ops;
        shdlc->state = SHDLC_DISCONNECTED;
 
        init_timer(&shdlc->connect_timer);
        shdlc->connect_timer.data = (unsigned long)shdlc;
-       shdlc->connect_timer.function = nfc_shdlc_connect_timeout;
+       shdlc->connect_timer.function = llc_shdlc_connect_timeout;
 
        init_timer(&shdlc->t1_timer);
        shdlc->t1_timer.data = (unsigned long)shdlc;
-       shdlc->t1_timer.function = nfc_shdlc_t1_timeout;
+       shdlc->t1_timer.function = llc_shdlc_t1_timeout;
 
        init_timer(&shdlc->t2_timer);
        shdlc->t2_timer.data = (unsigned long)shdlc;
-       shdlc->t2_timer.function = nfc_shdlc_t2_timeout;
+       shdlc->t2_timer.function = llc_shdlc_t2_timeout;
 
        shdlc->w = SHDLC_MAX_WINDOW;
        shdlc->srej_support = SHDLC_SREJ_SUPPORT;
@@ -875,77 +785,73 @@ struct nfc_shdlc *nfc_shdlc_allocate(struct nfc_shdlc_ops *ops,
        skb_queue_head_init(&shdlc->send_q);
        skb_queue_head_init(&shdlc->ack_pending_q);
 
-       INIT_WORK(&shdlc->sm_work, nfc_shdlc_sm_work);
-       snprintf(name, sizeof(name), "%s_shdlc_sm_wq", devname);
-       shdlc->sm_wq = alloc_workqueue(name, WQ_NON_REENTRANT | WQ_UNBOUND |
-                                      WQ_MEM_RECLAIM, 1);
-       if (shdlc->sm_wq == NULL)
-               goto err_allocwq;
+       INIT_WORK(&shdlc->sm_work, llc_shdlc_sm_work);
 
-       shdlc->client_headroom = tx_headroom;
-       shdlc->client_tailroom = tx_tailroom;
-
-       shdlc->hdev = nfc_hci_allocate_device(&shdlc_ops, init_data, protocols,
-                                             tx_headroom + SHDLC_LLC_HEAD_ROOM,
-                                             tx_tailroom + SHDLC_LLC_TAIL_ROOM,
-                                             max_link_payload);
-       if (shdlc->hdev == NULL)
-               goto err_allocdev;
-
-       nfc_hci_set_clientdata(shdlc->hdev, shdlc);
-
-       r = nfc_hci_register_device(shdlc->hdev);
-       if (r < 0)
-               goto err_regdev;
+       shdlc->hdev = hdev;
+       shdlc->xmit_to_drv = xmit_to_drv;
+       shdlc->rcv_to_hci = rcv_to_hci;
+       shdlc->tx_headroom = tx_headroom;
+       shdlc->tx_tailroom = tx_tailroom;
+       shdlc->llc_failure = llc_failure;
 
        return shdlc;
+}
 
-err_regdev:
-       nfc_hci_free_device(shdlc->hdev);
+static void llc_shdlc_deinit(struct nfc_llc *llc)
+{
+       struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
 
-err_allocdev:
-       destroy_workqueue(shdlc->sm_wq);
+       skb_queue_purge(&shdlc->rcv_q);
+       skb_queue_purge(&shdlc->send_q);
+       skb_queue_purge(&shdlc->ack_pending_q);
 
-err_allocwq:
        kfree(shdlc);
-
-       return NULL;
 }
-EXPORT_SYMBOL(nfc_shdlc_allocate);
 
-void nfc_shdlc_free(struct nfc_shdlc *shdlc)
+static int llc_shdlc_start(struct nfc_llc *llc)
 {
-       pr_debug("\n");
+       struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
 
-       nfc_hci_unregister_device(shdlc->hdev);
-       nfc_hci_free_device(shdlc->hdev);
+       return llc_shdlc_connect(shdlc);
+}
 
-       destroy_workqueue(shdlc->sm_wq);
+static int llc_shdlc_stop(struct nfc_llc *llc)
+{
+       struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
 
-       skb_queue_purge(&shdlc->rcv_q);
-       skb_queue_purge(&shdlc->send_q);
-       skb_queue_purge(&shdlc->ack_pending_q);
+       llc_shdlc_disconnect(shdlc);
 
-       kfree(shdlc);
+       return 0;
 }
-EXPORT_SYMBOL(nfc_shdlc_free);
 
-void nfc_shdlc_set_clientdata(struct nfc_shdlc *shdlc, void *clientdata)
+static void llc_shdlc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb)
 {
-       pr_debug("\n");
+       struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
 
-       shdlc->clientdata = clientdata;
+       llc_shdlc_recv_frame(shdlc, skb);
 }
-EXPORT_SYMBOL(nfc_shdlc_set_clientdata);
 
-void *nfc_shdlc_get_clientdata(struct nfc_shdlc *shdlc)
+static int llc_shdlc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb)
 {
-       return shdlc->clientdata;
+       struct llc_shdlc *shdlc = nfc_llc_get_data(llc);
+
+       skb_queue_tail(&shdlc->send_q, skb);
+
+       queue_work(system_nrt_wq, &shdlc->sm_work);
+
+       return 0;
 }
-EXPORT_SYMBOL(nfc_shdlc_get_clientdata);
 
-struct nfc_hci_dev *nfc_shdlc_get_hci_dev(struct nfc_shdlc *shdlc)
+static struct nfc_llc_ops llc_shdlc_ops = {
+       .init = llc_shdlc_init,
+       .deinit = llc_shdlc_deinit,
+       .start = llc_shdlc_start,
+       .stop = llc_shdlc_stop,
+       .rcv_from_drv = llc_shdlc_rcv_from_drv,
+       .xmit_from_hci = llc_shdlc_xmit_from_hci,
+};
+
+int nfc_llc_shdlc_register(void)
 {
-       return shdlc->hdev;
+       return nfc_llc_register(LLC_SHDLC_NAME, &llc_shdlc_ops);
 }
-EXPORT_SYMBOL(nfc_shdlc_get_hci_dev);
index b982b5b890d73da30a315567851d5d91e7ec9285..c45ccd6c094c5b16f258be4ffd7a1cff4433f6dc 100644 (file)
@@ -312,6 +312,8 @@ int nfc_llcp_send_symm(struct nfc_dev *dev)
 
        skb = llcp_add_header(skb, 0, 0, LLCP_PDU_SYMM);
 
+       nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX);
+
        return nfc_data_exchange(dev, local->target_idx, skb,
                                 nfc_llcp_recv, local);
 }
index 82f0f7588b463d8ba0a8e1931124c03f94de29ed..c12c5ef3d036e468e3dc4905dfd00531a8406b64 100644 (file)
@@ -56,7 +56,7 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
        sk_for_each_safe(sk, node, tmp, &local->sockets.head) {
                llcp_sock = nfc_llcp_sock(sk);
 
-               lock_sock(sk);
+               bh_lock_sock(sk);
 
                if (sk->sk_state == LLCP_CONNECTED)
                        nfc_put_device(llcp_sock->dev);
@@ -68,26 +68,26 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
                        list_for_each_entry_safe(lsk, n, &llcp_sock->accept_queue,
                                                 accept_queue) {
                                accept_sk = &lsk->sk;
-                               lock_sock(accept_sk);
+                               bh_lock_sock(accept_sk);
 
                                nfc_llcp_accept_unlink(accept_sk);
 
                                accept_sk->sk_state = LLCP_CLOSED;
 
-                               release_sock(accept_sk);
+                               bh_unlock_sock(accept_sk);
 
                                sock_orphan(accept_sk);
                        }
 
                        if (listen == true) {
-                               release_sock(sk);
+                               bh_unlock_sock(sk);
                                continue;
                        }
                }
 
                sk->sk_state = LLCP_CLOSED;
 
-               release_sock(sk);
+               bh_unlock_sock(sk);
 
                sock_orphan(sk);
 
@@ -114,9 +114,9 @@ static void local_release(struct kref *ref)
        nfc_llcp_socket_release(local, false);
        del_timer_sync(&local->link_timer);
        skb_queue_purge(&local->tx_queue);
-       destroy_workqueue(local->tx_wq);
-       destroy_workqueue(local->rx_wq);
-       destroy_workqueue(local->timeout_wq);
+       cancel_work_sync(&local->tx_work);
+       cancel_work_sync(&local->rx_work);
+       cancel_work_sync(&local->timeout_work);
        kfree_skb(local->rx_pending);
        kfree(local);
 }
@@ -181,7 +181,7 @@ static void nfc_llcp_symm_timer(unsigned long data)
 
        pr_err("SYMM timeout\n");
 
-       queue_work(local->timeout_wq, &local->timeout_work);
+       queue_work(system_nrt_wq, &local->timeout_work);
 }
 
 struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
@@ -426,6 +426,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
        u8 *miux_tlv, miux_length;
        __be16 miux;
        u8 gb_len = 0;
+       int ret = 0;
 
        version = LLCP_VERSION_11;
        version_tlv = nfc_llcp_build_tlv(LLCP_TLV_VERSION, &version,
@@ -450,8 +451,8 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
        gb_len += ARRAY_SIZE(llcp_magic);
 
        if (gb_len > NFC_MAX_GT_LEN) {
-               kfree(version_tlv);
-               return -EINVAL;
+               ret = -EINVAL;
+               goto out;
        }
 
        gb_cur = local->gb;
@@ -471,12 +472,15 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
        memcpy(gb_cur, miux_tlv, miux_length);
        gb_cur += miux_length;
 
+       local->gb_len = gb_len;
+
+out:
        kfree(version_tlv);
        kfree(lto_tlv);
+       kfree(wks_tlv);
+       kfree(miux_tlv);
 
-       local->gb_len = gb_len;
-
-       return 0;
+       return ret;
 }
 
 u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len)
@@ -554,6 +558,46 @@ static void nfc_llcp_set_nrns(struct nfc_llcp_sock *sock, struct sk_buff *pdu)
        sock->recv_ack_n = (sock->recv_n - 1) % 16;
 }
 
+void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,
+                              struct sk_buff *skb, u8 direction)
+{
+       struct hlist_node *node;
+       struct sk_buff *skb_copy = NULL, *nskb;
+       struct sock *sk;
+       u8 *data;
+
+       read_lock(&local->raw_sockets.lock);
+
+       sk_for_each(sk, node, &local->raw_sockets.head) {
+               if (sk->sk_state != LLCP_BOUND)
+                       continue;
+
+               if (skb_copy == NULL) {
+                       skb_copy = __pskb_copy(skb, NFC_LLCP_RAW_HEADER_SIZE,
+                                              GFP_ATOMIC);
+
+                       if (skb_copy == NULL)
+                               continue;
+
+                       data = skb_push(skb_copy, NFC_LLCP_RAW_HEADER_SIZE);
+
+                       data[0] = local->dev ? local->dev->idx : 0xFF;
+                       data[1] = direction;
+               }
+
+               nskb = skb_clone(skb_copy, GFP_ATOMIC);
+               if (!nskb)
+                       continue;
+
+               if (sock_queue_rcv_skb(sk, nskb))
+                       kfree_skb(nskb);
+       }
+
+       read_unlock(&local->raw_sockets.lock);
+
+       kfree_skb(skb_copy);
+}
+
 static void nfc_llcp_tx_work(struct work_struct *work)
 {
        struct nfc_llcp_local *local = container_of(work, struct nfc_llcp_local,
@@ -574,6 +618,9 @@ static void nfc_llcp_tx_work(struct work_struct *work)
                                       DUMP_PREFIX_OFFSET, 16, 1,
                                       skb->data, skb->len, true);
 
+                       nfc_llcp_send_to_raw_sock(local, skb,
+                                                 NFC_LLCP_DIRECTION_TX);
+
                        ret = nfc_data_exchange(local->dev, local->target_idx,
                                                skb, nfc_llcp_recv, local);
 
@@ -1018,6 +1065,8 @@ static void nfc_llcp_rx_work(struct work_struct *work)
                print_hex_dump(KERN_DEBUG, "LLCP Rx: ", DUMP_PREFIX_OFFSET,
                               16, 1, skb->data, skb->len, true);
 
+       nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_RX);
+
        switch (ptype) {
        case LLCP_PDU_SYMM:
                pr_debug("SYMM\n");
@@ -1052,7 +1101,7 @@ static void nfc_llcp_rx_work(struct work_struct *work)
 
        }
 
-       queue_work(local->tx_wq, &local->tx_work);
+       queue_work(system_nrt_wq, &local->tx_work);
        kfree_skb(local->rx_pending);
        local->rx_pending = NULL;
 
@@ -1071,7 +1120,7 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
 
        local->rx_pending = skb_get(skb);
        del_timer(&local->link_timer);
-       queue_work(local->rx_wq, &local->rx_work);
+       queue_work(system_nrt_wq, &local->rx_work);
 
        return;
 }
@@ -1086,7 +1135,7 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
 
        local->rx_pending = skb_get(skb);
        del_timer(&local->link_timer);
-       queue_work(local->rx_wq, &local->rx_work);
+       queue_work(system_nrt_wq, &local->rx_work);
 
        return 0;
 }
@@ -1121,7 +1170,7 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
        if (rf_mode == NFC_RF_INITIATOR) {
                pr_debug("Queueing Tx work\n");
 
-               queue_work(local->tx_wq, &local->tx_work);
+               queue_work(system_nrt_wq, &local->tx_work);
        } else {
                mod_timer(&local->link_timer,
                          jiffies + msecs_to_jiffies(local->remote_lto));
@@ -1130,10 +1179,7 @@ void nfc_llcp_mac_is_up(struct nfc_dev *dev, u32 target_idx,
 
 int nfc_llcp_register_device(struct nfc_dev *ndev)
 {
-       struct device *dev = &ndev->dev;
        struct nfc_llcp_local *local;
-       char name[32];
-       int err;
 
        local = kzalloc(sizeof(struct nfc_llcp_local), GFP_KERNEL);
        if (local == NULL)
@@ -1149,41 +1195,15 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
 
        skb_queue_head_init(&local->tx_queue);
        INIT_WORK(&local->tx_work, nfc_llcp_tx_work);
-       snprintf(name, sizeof(name), "%s_llcp_tx_wq", dev_name(dev));
-       local->tx_wq =
-               alloc_workqueue(name,
-                               WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
-                               1);
-       if (local->tx_wq == NULL) {
-               err = -ENOMEM;
-               goto err_local;
-       }
 
        local->rx_pending = NULL;
        INIT_WORK(&local->rx_work, nfc_llcp_rx_work);
-       snprintf(name, sizeof(name), "%s_llcp_rx_wq", dev_name(dev));
-       local->rx_wq =
-               alloc_workqueue(name,
-                               WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
-                               1);
-       if (local->rx_wq == NULL) {
-               err = -ENOMEM;
-               goto err_tx_wq;
-       }
 
        INIT_WORK(&local->timeout_work, nfc_llcp_timeout_work);
-       snprintf(name, sizeof(name), "%s_llcp_timeout_wq", dev_name(dev));
-       local->timeout_wq =
-               alloc_workqueue(name,
-                               WQ_NON_REENTRANT | WQ_UNBOUND | WQ_MEM_RECLAIM,
-                               1);
-       if (local->timeout_wq == NULL) {
-               err = -ENOMEM;
-               goto err_rx_wq;
-       }
 
-       local->sockets.lock = __RW_LOCK_UNLOCKED(local->sockets.lock);
-       local->connecting_sockets.lock = __RW_LOCK_UNLOCKED(local->connecting_sockets.lock);
+       rwlock_init(&local->sockets.lock);
+       rwlock_init(&local->connecting_sockets.lock);
+       rwlock_init(&local->raw_sockets.lock);
 
        nfc_llcp_build_gb(local);
 
@@ -1192,17 +1212,6 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
 
        list_add(&llcp_devices, &local->list);
 
-       return 0;
-
-err_rx_wq:
-       destroy_workqueue(local->rx_wq);
-
-err_tx_wq:
-       destroy_workqueue(local->tx_wq);
-
-err_local:
-       kfree(local);
-
        return 0;
 }
 
index 83b8bba5a2803d2cc4bafb05d7e553221434ae0e..fdb2d24e60bda5fe85d8225c38b25a1caed7c0de 100644 (file)
@@ -56,12 +56,9 @@ struct nfc_llcp_local {
 
        struct timer_list link_timer;
        struct sk_buff_head tx_queue;
-       struct workqueue_struct *tx_wq;
        struct work_struct       tx_work;
-       struct workqueue_struct *rx_wq;
        struct work_struct       rx_work;
        struct sk_buff *rx_pending;
-       struct workqueue_struct *timeout_wq;
        struct work_struct       timeout_work;
 
        u32 target_idx;
@@ -89,6 +86,7 @@ struct nfc_llcp_local {
        /* sockets array */
        struct llcp_sock_list sockets;
        struct llcp_sock_list connecting_sockets;
+       struct llcp_sock_list raw_sockets;
 };
 
 struct nfc_llcp_sock {
@@ -187,6 +185,8 @@ u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local,
 u8 nfc_llcp_get_local_ssap(struct nfc_llcp_local *local);
 void nfc_llcp_put_ssap(struct nfc_llcp_local *local, u8 ssap);
 int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock);
+void nfc_llcp_send_to_raw_sock(struct nfc_llcp_local *local,
+                              struct sk_buff *skb, u8 direction);
 
 /* Sock API */
 struct sock *nfc_llcp_sock_alloc(struct socket *sock, int type, gfp_t gfp);
index ddeb9aa398f0ced280fed93b853319923cf43c69..40f056debf9aaecd1fc7993a186a21230a111856 100644 (file)
@@ -142,6 +142,60 @@ error:
        return ret;
 }
 
+static int llcp_raw_sock_bind(struct socket *sock, struct sockaddr *addr,
+                             int alen)
+{
+       struct sock *sk = sock->sk;
+       struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
+       struct nfc_llcp_local *local;
+       struct nfc_dev *dev;
+       struct sockaddr_nfc_llcp llcp_addr;
+       int len, ret = 0;
+
+       if (!addr || addr->sa_family != AF_NFC)
+               return -EINVAL;
+
+       pr_debug("sk %p addr %p family %d\n", sk, addr, addr->sa_family);
+
+       memset(&llcp_addr, 0, sizeof(llcp_addr));
+       len = min_t(unsigned int, sizeof(llcp_addr), alen);
+       memcpy(&llcp_addr, addr, len);
+
+       lock_sock(sk);
+
+       if (sk->sk_state != LLCP_CLOSED) {
+               ret = -EBADFD;
+               goto error;
+       }
+
+       dev = nfc_get_device(llcp_addr.dev_idx);
+       if (dev == NULL) {
+               ret = -ENODEV;
+               goto error;
+       }
+
+       local = nfc_llcp_find_local(dev);
+       if (local == NULL) {
+               ret = -ENODEV;
+               goto put_dev;
+       }
+
+       llcp_sock->dev = dev;
+       llcp_sock->local = nfc_llcp_local_get(local);
+       llcp_sock->nfc_protocol = llcp_addr.nfc_protocol;
+
+       nfc_llcp_sock_link(&local->raw_sockets, sk);
+
+       sk->sk_state = LLCP_BOUND;
+
+put_dev:
+       nfc_put_device(dev);
+
+error:
+       release_sock(sk);
+       return ret;
+}
+
 static int llcp_sock_listen(struct socket *sock, int backlog)
 {
        struct sock *sk = sock->sk;
@@ -300,9 +354,6 @@ static int llcp_sock_getname(struct socket *sock, struct sockaddr *uaddr,
        pr_debug("%p %d %d %d\n", sk, llcp_sock->target_idx,
                 llcp_sock->dsap, llcp_sock->ssap);
 
-       if (llcp_sock == NULL || llcp_sock->dev == NULL)
-               return -EBADFD;
-
        uaddr->sa_family = AF_NFC;
 
        *len = sizeof(struct sockaddr_nfc_llcp);
@@ -421,7 +472,10 @@ static int llcp_sock_release(struct socket *sock)
 
        release_sock(sk);
 
-       nfc_llcp_sock_unlink(&local->sockets, sk);
+       if (sock->type == SOCK_RAW)
+               nfc_llcp_sock_unlink(&local->raw_sockets, sk);
+       else
+               nfc_llcp_sock_unlink(&local->sockets, sk);
 
 out:
        sock_orphan(sk);
@@ -617,7 +671,7 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
        if (!(flags & MSG_PEEK)) {
 
                /* SOCK_STREAM: re-queue skb if it contains unreceived data */
-               if (sk->sk_type == SOCK_STREAM) {
+               if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_RAW) {
                        skb_pull(skb, copied);
                        if (skb->len) {
                                skb_queue_head(&sk->sk_receive_queue, skb);
@@ -658,6 +712,26 @@ static const struct proto_ops llcp_sock_ops = {
        .mmap           = sock_no_mmap,
 };
 
+static const struct proto_ops llcp_rawsock_ops = {
+       .family         = PF_NFC,
+       .owner          = THIS_MODULE,
+       .bind           = llcp_raw_sock_bind,
+       .connect        = sock_no_connect,
+       .release        = llcp_sock_release,
+       .socketpair     = sock_no_socketpair,
+       .accept         = sock_no_accept,
+       .getname        = llcp_sock_getname,
+       .poll           = llcp_sock_poll,
+       .ioctl          = sock_no_ioctl,
+       .listen         = sock_no_listen,
+       .shutdown       = sock_no_shutdown,
+       .setsockopt     = sock_no_setsockopt,
+       .getsockopt     = sock_no_getsockopt,
+       .sendmsg        = sock_no_sendmsg,
+       .recvmsg        = llcp_sock_recvmsg,
+       .mmap           = sock_no_mmap,
+};
+
 static void llcp_sock_destruct(struct sock *sk)
 {
        struct nfc_llcp_sock *llcp_sock = nfc_llcp_sock(sk);
@@ -735,10 +809,15 @@ static int llcp_sock_create(struct net *net, struct socket *sock,
 
        pr_debug("%p\n", sock);
 
-       if (sock->type != SOCK_STREAM && sock->type != SOCK_DGRAM)
+       if (sock->type != SOCK_STREAM &&
+           sock->type != SOCK_DGRAM &&
+           sock->type != SOCK_RAW)
                return -ESOCKTNOSUPPORT;
 
-       sock->ops = &llcp_sock_ops;
+       if (sock->type == SOCK_RAW)
+               sock->ops = &llcp_rawsock_ops;
+       else
+               sock->ops = &llcp_sock_ops;
 
        sk = nfc_llcp_sock_alloc(sock, sock->type, GFP_ATOMIC);
        if (sk == NULL)
index f81efe13985a71e2bd9c3bc16e0799a750ccee3d..acf9abb7d99badc592592f83b42f4acbb14ef4de 100644 (file)
@@ -176,6 +176,27 @@ static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt)
                     (1 + ((*num) * sizeof(struct disc_map_config))), &cmd);
 }
 
+struct nci_set_config_param {
+       __u8    id;
+       size_t  len;
+       __u8    *val;
+};
+
+static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt)
+{
+       struct nci_set_config_param *param = (struct nci_set_config_param *)opt;
+       struct nci_core_set_config_cmd cmd;
+
+       BUG_ON(param->len > NCI_MAX_PARAM_LEN);
+
+       cmd.num_params = 1;
+       cmd.param.id = param->id;
+       cmd.param.len = param->len;
+       memcpy(cmd.param.val, param->val, param->len);
+
+       nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd);
+}
+
 static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
 {
        struct nci_rf_disc_cmd cmd;
@@ -388,6 +409,32 @@ static int nci_dev_down(struct nfc_dev *nfc_dev)
        return nci_close_device(ndev);
 }
 
+static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+       struct nci_set_config_param param;
+       __u8 local_gb[NFC_MAX_GT_LEN];
+       int i, rc = 0;
+
+       param.val = nfc_get_local_general_bytes(nfc_dev, &param.len);
+       if ((param.val == NULL) || (param.len == 0))
+               return rc;
+
+       if (param.len > NCI_MAX_PARAM_LEN)
+               return -EINVAL;
+
+       for (i = 0; i < param.len; i++)
+               local_gb[param.len-1-i] = param.val[i];
+
+       param.id = NCI_PN_ATR_REQ_GEN_BYTES;
+       param.val = local_gb;
+
+       rc = nci_request(ndev, nci_set_config_req, (unsigned long)&param,
+                        msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
+
+       return rc;
+}
+
 static int nci_start_poll(struct nfc_dev *nfc_dev,
                          __u32 im_protocols, __u32 tm_protocols)
 {
@@ -415,6 +462,14 @@ static int nci_start_poll(struct nfc_dev *nfc_dev,
                        return -EBUSY;
        }
 
+       if (im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+               rc = nci_set_local_general_bytes(nfc_dev);
+               if (rc) {
+                       pr_err("failed to set local general bytes\n");
+                       return rc;
+               }
+       }
+
        rc = nci_request(ndev, nci_rf_discover_req, im_protocols,
                         msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
 
@@ -509,7 +564,7 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
 {
        struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
 
-       pr_debug("target_idx %d\n", target->idx);
+       pr_debug("entry\n");
 
        if (!ndev->target_active_prot) {
                pr_err("unable to deactivate target, no active target\n");
@@ -524,6 +579,38 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
        }
 }
 
+
+static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
+                          __u8 comm_mode, __u8 *gb, size_t gb_len)
+{
+       struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+       int rc;
+
+       pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode);
+
+       rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP);
+       if (rc)
+               return rc;
+
+       rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb,
+                                         ndev->remote_gb_len);
+       if (!rc)
+               rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE,
+                                       NFC_RF_INITIATOR);
+
+       return rc;
+}
+
+static int nci_dep_link_down(struct nfc_dev *nfc_dev)
+{
+       pr_debug("entry\n");
+
+       nci_deactivate_target(nfc_dev, NULL);
+
+       return 0;
+}
+
+
 static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
                          struct sk_buff *skb,
                          data_exchange_cb_t cb, void *cb_context)
@@ -557,6 +644,8 @@ static struct nfc_ops nci_nfc_ops = {
        .dev_down = nci_dev_down,
        .start_poll = nci_start_poll,
        .stop_poll = nci_stop_poll,
+       .dep_link_up = nci_dep_link_up,
+       .dep_link_down = nci_dep_link_down,
        .activate_target = nci_activate_target,
        .deactivate_target = nci_deactivate_target,
        .im_transceive = nci_transceive,
index af7a93b04393a1ff3cbc088390f4972592dd3b1c..b2aa98ef0927cb5760f72fb40d016a0b1dcf5575 100644 (file)
@@ -176,6 +176,8 @@ static int nci_add_new_protocol(struct nci_dev *ndev,
                        protocol = NFC_PROTO_ISO14443_B_MASK;
        else if (rf_protocol == NCI_RF_PROTOCOL_T3T)
                protocol = NFC_PROTO_FELICA_MASK;
+       else if (rf_protocol == NCI_RF_PROTOCOL_NFC_DEP)
+               protocol = NFC_PROTO_NFC_DEP_MASK;
        else
                protocol = 0;
 
@@ -361,6 +363,33 @@ static int nci_extract_activation_params_iso_dep(struct nci_dev *ndev,
        return NCI_STATUS_OK;
 }
 
+static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
+                       struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
+{
+       struct activation_params_poll_nfc_dep *poll;
+       int i;
+
+       switch (ntf->activation_rf_tech_and_mode) {
+       case NCI_NFC_A_PASSIVE_POLL_MODE:
+       case NCI_NFC_F_PASSIVE_POLL_MODE:
+               poll = &ntf->activation_params.poll_nfc_dep;
+               poll->atr_res_len = min_t(__u8, *data++, 63);
+               pr_debug("atr_res_len %d\n", poll->atr_res_len);
+               if (poll->atr_res_len > 0) {
+                       for (i = 0; i < poll->atr_res_len; i++)
+                               poll->atr_res[poll->atr_res_len-1-i] = data[i];
+               }
+               break;
+
+       default:
+               pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
+                      ntf->activation_rf_tech_and_mode);
+               return NCI_STATUS_RF_PROTOCOL_ERROR;
+       }
+
+       return NCI_STATUS_OK;
+}
+
 static void nci_target_auto_activated(struct nci_dev *ndev,
                                      struct nci_rf_intf_activated_ntf *ntf)
 {
@@ -454,6 +483,11 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
                                                                    &ntf, data);
                        break;
 
+               case NCI_RF_INTERFACE_NFC_DEP:
+                       err = nci_extract_activation_params_nfc_dep(ndev,
+                                                                   &ntf, data);
+                       break;
+
                case NCI_RF_INTERFACE_FRAME:
                        /* no activation params */
                        break;
@@ -473,6 +507,24 @@ exit:
 
                /* set the available credits to initial value */
                atomic_set(&ndev->credits_cnt, ndev->initial_num_credits);
+
+               /* store general bytes to be reported later in dep_link_up */
+               if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) {
+                       ndev->remote_gb_len = 0;
+
+                       if (ntf.activation_params_len > 0) {
+                               /* ATR_RES general bytes at offset 15 */
+                               ndev->remote_gb_len = min_t(__u8,
+                                       (ntf.activation_params
+                                       .poll_nfc_dep.atr_res_len
+                                       - NFC_ATR_RES_GT_OFFSET),
+                                       NFC_MAX_GT_LEN);
+                               memcpy(ndev->remote_gb,
+                                      (ntf.activation_params.poll_nfc_dep
+                                      .atr_res + NFC_ATR_RES_GT_OFFSET),
+                                      ndev->remote_gb_len);
+                       }
+               }
        }
 
        if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
index 3003c3390e492c18907cc7f5076949439dd7273d..dd072f38ad00f5241d2cdc1299965d8bcc3966b9 100644 (file)
@@ -119,6 +119,16 @@ exit:
        nci_req_complete(ndev, rsp_1->status);
 }
 
+static void nci_core_set_config_rsp_packet(struct nci_dev *ndev,
+                                          struct sk_buff *skb)
+{
+       struct nci_core_set_config_rsp *rsp = (void *) skb->data;
+
+       pr_debug("status 0x%x\n", rsp->status);
+
+       nci_req_complete(ndev, rsp->status);
+}
+
 static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev,
                                       struct sk_buff *skb)
 {
@@ -194,6 +204,10 @@ void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb)
                nci_core_init_rsp_packet(ndev, skb);
                break;
 
+       case NCI_OP_CORE_SET_CONFIG_RSP:
+               nci_core_set_config_rsp_packet(ndev, skb);
+               break;
+
        case NCI_OP_RF_DISCOVER_MAP_RSP:
                nci_rf_disc_map_rsp_packet(ndev, skb);
                break;
index 4bbb70e32d1e93e2a3ae122caed6d42c1d293fe0..c1b5285cbde79fa6c861106649d57728373c4ba4 100644 (file)
@@ -761,31 +761,63 @@ static struct genl_ops nfc_genl_ops[] = {
        },
 };
 
-static int nfc_genl_rcv_nl_event(struct notifier_block *this,
-                                unsigned long event, void *ptr)
+
+struct urelease_work {
+       struct  work_struct w;
+       int     portid;
+};
+
+static void nfc_urelease_event_work(struct work_struct *work)
 {
-       struct netlink_notify *n = ptr;
+       struct urelease_work *w = container_of(work, struct urelease_work, w);
        struct class_dev_iter iter;
        struct nfc_dev *dev;
 
-       if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
-               goto out;
+       pr_debug("portid %d\n", w->portid);
 
-       pr_debug("NETLINK_URELEASE event from id %d\n", n->portid);
+       mutex_lock(&nfc_devlist_mutex);
 
        nfc_device_iter_init(&iter);
        dev = nfc_device_iter_next(&iter);
 
        while (dev) {
-               if (dev->genl_data.poll_req_portid == n->portid) {
+               mutex_lock(&dev->genl_data.genl_data_mutex);
+
+               if (dev->genl_data.poll_req_portid == w->portid) {
                        nfc_stop_poll(dev);
                        dev->genl_data.poll_req_portid = 0;
                }
+
+               mutex_unlock(&dev->genl_data.genl_data_mutex);
+
                dev = nfc_device_iter_next(&iter);
        }
 
        nfc_device_iter_exit(&iter);
 
+       mutex_unlock(&nfc_devlist_mutex);
+
+       kfree(w);
+}
+
+static int nfc_genl_rcv_nl_event(struct notifier_block *this,
+                                unsigned long event, void *ptr)
+{
+       struct netlink_notify *n = ptr;
+       struct urelease_work *w;
+
+       if (event != NETLINK_URELEASE || n->protocol != NETLINK_GENERIC)
+               goto out;
+
+       pr_debug("NETLINK_URELEASE event from id %d\n", n->portid);
+
+       w = kmalloc(sizeof(*w), GFP_ATOMIC);
+       if (w) {
+               INIT_WORK((struct work_struct *) w, nfc_urelease_event_work);
+               w->portid = n->portid;
+               schedule_work((struct work_struct *) w);
+       }
+
 out:
        return NOTIFY_DONE;
 }
index c275bad12068b28801d5b31fc79614a61bc14c4c..a5c95274127990b34de5af72f82e9135cf73aa85 100644 (file)
@@ -270,6 +270,7 @@ static bool __rfkill_set_hw_state(struct rfkill *rfkill,
 static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
 {
        unsigned long flags;
+       bool prev, curr;
        int err;
 
        if (unlikely(rfkill->dev.power.power_state.event & PM_EVENT_SLEEP))
@@ -284,6 +285,8 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
                rfkill->ops->query(rfkill, rfkill->data);
 
        spin_lock_irqsave(&rfkill->lock, flags);
+       prev = rfkill->state & RFKILL_BLOCK_SW;
+
        if (rfkill->state & RFKILL_BLOCK_SW)
                rfkill->state |= RFKILL_BLOCK_SW_PREV;
        else
@@ -313,10 +316,13 @@ static void rfkill_set_block(struct rfkill *rfkill, bool blocked)
        }
        rfkill->state &= ~RFKILL_BLOCK_SW_SETCALL;
        rfkill->state &= ~RFKILL_BLOCK_SW_PREV;
+       curr = rfkill->state & RFKILL_BLOCK_SW;
        spin_unlock_irqrestore(&rfkill->lock, flags);
 
        rfkill_led_trigger_event(rfkill);
-       rfkill_event(rfkill);
+
+       if (prev != curr)
+               rfkill_event(rfkill);
 }
 
 #ifdef CONFIG_RFKILL_INPUT
index ec7fcee5bad65b2ed8a24de5a2d97d850926d882..8016fee0752b0325a20409b7b1b93b5433c73872 100644 (file)
@@ -612,6 +612,17 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
 }
 EXPORT_SYMBOL(cfg80211_del_sta);
 
+void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
+                         enum nl80211_connect_failed_reason reason,
+                         gfp_t gfp)
+{
+       struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
+       struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+       nl80211_send_conn_failed_event(rdev, dev, mac_addr, reason, gfp);
+}
+EXPORT_SYMBOL(cfg80211_conn_failed);
+
 struct cfg80211_mgmt_registration {
        struct list_head list;
 
index 139946dc80209bb958f87b26c689a64ceab6a6b9..0418a6d5c1a683f95542c64628e66f487ddea196 100644 (file)
@@ -8364,6 +8364,40 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
        nlmsg_free(msg);
 }
 
+void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
+                                   struct net_device *dev, const u8 *mac_addr,
+                                   enum nl80211_connect_failed_reason reason,
+                                   gfp_t gfp)
+{
+       struct sk_buff *msg;
+       void *hdr;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+       if (!msg)
+               return;
+
+       hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONN_FAILED);
+       if (!hdr) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
+           nla_put_u32(msg, NL80211_ATTR_CONN_FAILED_REASON, reason))
+               goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+
+       genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+                               nl80211_mlme_mcgrp.id, gfp);
+       return;
+
+ nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       nlmsg_free(msg);
+}
+
 static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
                                       const u8 *addr, gfp_t gfp)
 {
index 9f2616fffb4001958600d72dac222ff7edc7a9b4..f6153516068c30dce59e4283d9abfc0591adebfb 100644 (file)
@@ -91,6 +91,11 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
                                struct net_device *dev, const u8 *mac_addr,
                                gfp_t gfp);
 
+void nl80211_send_conn_failed_event(struct cfg80211_registered_device *rdev,
+                                   struct net_device *dev, const u8 *mac_addr,
+                                   enum nl80211_connect_failed_reason reason,
+                                   gfp_t gfp);
+
 int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
                      struct wireless_dev *wdev, u32 nlpid,
                      int freq, int sig_dbm,
index 1ad04e54014cac47134f5e082f4eb5f258d1e826..844823973daf44dd493346ce140faff63cbd47d1 100644 (file)
@@ -504,9 +504,11 @@ static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range,
  *
  * This lets us know if a specific frequency rule is or is not relevant to
  * a specific frequency's band. Bands are device specific and artificial
- * definitions (the "2.4 GHz band" and the "5 GHz band"), however it is
- * safe for now to assume that a frequency rule should not be part of a
- * frequency's band if the start freq or end freq are off by more than 2 GHz.
+ * definitions (the "2.4 GHz band", the "5 GHz band" and the "60GHz band"),
+ * however it is safe for now to assume that a frequency rule should not be
+ * part of a frequency's band if the start freq or end freq are off by more
+ * than 2 GHz for the 2.4 and 5 GHz bands, and by more than 10 GHz for the
+ * 60 GHz band.
  * This resolution can be lowered and should be considered as we add
  * regulatory rule support for other "bands".
  **/
@@ -514,9 +516,16 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
        u32 freq_khz)
 {
 #define ONE_GHZ_IN_KHZ 1000000
-       if (abs(freq_khz - freq_range->start_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
+       /*
+        * From 802.11ad: directional multi-gigabit (DMG):
+        * Pertaining to operation in a frequency band containing a channel
+        * with the Channel starting frequency above 45 GHz.
+        */
+       u32 limit = freq_khz > 45 * ONE_GHZ_IN_KHZ ?
+                       10 * ONE_GHZ_IN_KHZ : 2 * ONE_GHZ_IN_KHZ;
+       if (abs(freq_khz - freq_range->start_freq_khz) <= limit)
                return true;
-       if (abs(freq_khz - freq_range->end_freq_khz) <= (2 * ONE_GHZ_IN_KHZ))
+       if (abs(freq_khz - freq_range->end_freq_khz) <= limit)
                return true;
        return false;
 #undef ONE_GHZ_IN_KHZ
@@ -2193,7 +2202,6 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd)
 static int __set_regdom(const struct ieee80211_regdomain *rd)
 {
        const struct ieee80211_regdomain *intersected_rd = NULL;
-       struct cfg80211_registered_device *rdev = NULL;
        struct wiphy *request_wiphy;
        /* Some basic sanity checks first */
 
@@ -2305,24 +2313,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
                return 0;
        }
 
-       if (!intersected_rd)
-               return -EINVAL;
-
-       rdev = wiphy_to_dev(request_wiphy);
-
-       rdev->country_ie_alpha2[0] = rd->alpha2[0];
-       rdev->country_ie_alpha2[1] = rd->alpha2[1];
-       rdev->env = last_request->country_ie_env;
-
-       BUG_ON(intersected_rd == rd);
-
-       kfree(rd);
-       rd = NULL;
-
-       reset_regdomains(false);
-       cfg80211_regdomain = intersected_rd;
-
-       return 0;
+       return -EINVAL;
 }