]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi...
authorJohn W. Linville <linville@tuxdriver.com>
Mon, 24 Feb 2014 20:05:42 +0000 (15:05 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 24 Feb 2014 20:05:42 +0000 (15:05 -0500)
125 files changed:
Documentation/DocBook/80211.tmpl
Documentation/devices.txt
drivers/bluetooth/ath3k.c
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_vhci.c
drivers/net/wireless/ath/ar5523/ar5523.c
drivers/net/wireless/ath/ath.h
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/debug.h
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/htt_tx.c
drivers/net/wireless/ath/ath10k/hw.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/pci.c
drivers/net/wireless/ath/ath10k/txrx.c
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.h
drivers/net/wireless/ath/ath6kl/usb.c
drivers/net/wireless/ath/ath6kl/wmi.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/htc.h
drivers/net/wireless/ath/ath9k/htc_drv_init.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/regd.c
drivers/net/wireless/ath/wcn36xx/dxe.c
drivers/net/wireless/ath/wcn36xx/dxe.h
drivers/net/wireless/ath/wcn36xx/hal.h
drivers/net/wireless/ath/wcn36xx/main.c
drivers/net/wireless/ath/wcn36xx/smd.c
drivers/net/wireless/ath/wcn36xx/smd.h
drivers/net/wireless/ath/wcn36xx/txrx.c
drivers/net/wireless/ath/wcn36xx/wcn36xx.h
drivers/net/wireless/b43/main.h
drivers/net/wireless/b43/xmit.c
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mac80211_hwsim.h
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/rt2x00/rt2500pci.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rtl818x/rtl8180/dev.c
drivers/net/wireless/ti/wl12xx/main.c
drivers/net/wireless/ti/wl12xx/wl12xx.h
drivers/net/wireless/ti/wl18xx/main.c
drivers/net/wireless/ti/wl18xx/tx.c
drivers/net/wireless/ti/wl18xx/wl18xx.h
drivers/net/wireless/ti/wlcore/acx.c
drivers/net/wireless/ti/wlcore/acx.h
drivers/net/wireless/ti/wlcore/cmd.c
drivers/net/wireless/ti/wlcore/cmd.h
drivers/net/wireless/ti/wlcore/event.c
drivers/net/wireless/ti/wlcore/hw_ops.h
drivers/net/wireless/ti/wlcore/init.c
drivers/net/wireless/ti/wlcore/io.h
drivers/net/wireless/ti/wlcore/main.c
drivers/net/wireless/ti/wlcore/ps.c
drivers/net/wireless/ti/wlcore/rx.c
drivers/net/wireless/ti/wlcore/rx.h
drivers/net/wireless/ti/wlcore/tx.c
drivers/net/wireless/ti/wlcore/tx.h
drivers/net/wireless/ti/wlcore/wlcore.h
drivers/net/wireless/ti/wlcore/wlcore_i.h
include/linux/miscdevice.h
include/linux/tty.h
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/bluetooth/mgmt.h
include/net/bluetooth/rfcomm.h
include/net/cfg80211.h
include/net/mac80211.h
include/uapi/linux/nl80211.h
net/bluetooth/a2mp.c
net/bluetooth/af_bluetooth.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sock.c
net/bluetooth/hci_sysfs.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/mgmt.c
net/bluetooth/rfcomm/core.c
net/bluetooth/rfcomm/sock.c
net/bluetooth/rfcomm/tty.c
net/bluetooth/smp.c
net/bluetooth/smp.h
net/mac80211/cfg.c
net/mac80211/debugfs_netdev.c
net/mac80211/driver-ops.h
net/mac80211/ht.c
net/mac80211/ibss.c
net/mac80211/ieee80211_i.h
net/mac80211/iface.c
net/mac80211/main.c
net/mac80211/mlme.c
net/mac80211/rx.c
net/mac80211/scan.c
net/mac80211/tx.c
net/wireless/chan.c
net/wireless/core.c
net/wireless/core.h
net/wireless/genregdb.awk
net/wireless/ibss.c
net/wireless/mesh.c
net/wireless/nl80211.c
net/wireless/nl80211.h
net/wireless/rdev-ops.h
net/wireless/reg.c
net/wireless/scan.c
net/wireless/sme.c
net/wireless/trace.h
net/wireless/util.c

index 46ad6faee9ab73dfae2fa3e2b00bb6fb7f1c63a1..044b76436e8373ae601f9c60bd274754f3df6033 100644 (file)
@@ -98,6 +98,8 @@
 !Finclude/net/cfg80211.h priv_to_wiphy
 !Finclude/net/cfg80211.h set_wiphy_dev
 !Finclude/net/cfg80211.h wdev_priv
+!Finclude/net/cfg80211.h ieee80211_iface_limit
+!Finclude/net/cfg80211.h ieee80211_iface_combination
       </chapter>
       <chapter>
       <title>Actions and configuration</title>
index 10378cc48374cf8ffde068d4ce329ce5b5cbb205..04356f5bc3afbb8c91332aa1077a344526862af0 100644 (file)
@@ -353,6 +353,7 @@ Your cooperation is appreciated.
                133 = /dev/exttrp       External device trap
                134 = /dev/apm_bios     Advanced Power Management BIOS
                135 = /dev/rtc          Real Time Clock
+               137 = /dev/vhci         Bluetooth virtual HCI driver
                139 = /dev/openprom     SPARC OpenBoot PROM
                140 = /dev/relay8       Berkshire Products Octal relay card
                141 = /dev/relay16      Berkshire Products ISO-16 relay card
index 106d1d8e16ad4f87ac510f5d02b53dc36e8f23ef..4f78a9d39dc8fe960e194b1dc3e8b0bf703733e3 100644 (file)
@@ -62,50 +62,53 @@ static const struct usb_device_id ath3k_table[] = {
        { USB_DEVICE(0x0CF3, 0x3000) },
 
        /* Atheros AR3011 with sflash firmware*/
+       { USB_DEVICE(0x0489, 0xE027) },
+       { USB_DEVICE(0x0489, 0xE03D) },
+       { USB_DEVICE(0x0930, 0x0215) },
        { USB_DEVICE(0x0CF3, 0x3002) },
        { USB_DEVICE(0x0CF3, 0xE019) },
        { USB_DEVICE(0x13d3, 0x3304) },
-       { USB_DEVICE(0x0930, 0x0215) },
-       { USB_DEVICE(0x0489, 0xE03D) },
-       { USB_DEVICE(0x0489, 0xE027) },
 
        /* Atheros AR9285 Malbec with sflash firmware */
        { USB_DEVICE(0x03F0, 0x311D) },
 
        /* Atheros AR3012 with sflash firmware*/
-       { USB_DEVICE(0x0CF3, 0x0036) },
-       { USB_DEVICE(0x0CF3, 0x3004) },
-       { USB_DEVICE(0x0CF3, 0x3008) },
-       { USB_DEVICE(0x0CF3, 0x311D) },
-       { USB_DEVICE(0x0CF3, 0x817a) },
-       { USB_DEVICE(0x13d3, 0x3375) },
+       { USB_DEVICE(0x0489, 0xe04d) },
+       { USB_DEVICE(0x0489, 0xe04e) },
+       { USB_DEVICE(0x0489, 0xe057) },
+       { USB_DEVICE(0x0489, 0xe056) },
+       { USB_DEVICE(0x0489, 0xe05f) },
+       { USB_DEVICE(0x04c5, 0x1330) },
        { USB_DEVICE(0x04CA, 0x3004) },
        { USB_DEVICE(0x04CA, 0x3005) },
        { USB_DEVICE(0x04CA, 0x3006) },
        { USB_DEVICE(0x04CA, 0x3008) },
        { USB_DEVICE(0x04CA, 0x300b) },
-       { USB_DEVICE(0x13d3, 0x3362) },
-       { USB_DEVICE(0x0CF3, 0xE004) },
-       { USB_DEVICE(0x0CF3, 0xE005) },
        { USB_DEVICE(0x0930, 0x0219) },
        { USB_DEVICE(0x0930, 0x0220) },
-       { USB_DEVICE(0x0489, 0xe057) },
-       { USB_DEVICE(0x13d3, 0x3393) },
-       { USB_DEVICE(0x0489, 0xe04e) },
-       { USB_DEVICE(0x0489, 0xe056) },
-       { USB_DEVICE(0x0489, 0xe04d) },
-       { USB_DEVICE(0x04c5, 0x1330) },
-       { USB_DEVICE(0x13d3, 0x3402) },
+       { USB_DEVICE(0x0b05, 0x17d0) },
+       { USB_DEVICE(0x0CF3, 0x0036) },
+       { USB_DEVICE(0x0CF3, 0x3004) },
+       { USB_DEVICE(0x0CF3, 0x3008) },
+       { USB_DEVICE(0x0CF3, 0x311D) },
+       { USB_DEVICE(0x0CF3, 0x311E) },
+       { USB_DEVICE(0x0CF3, 0x311F) },
        { USB_DEVICE(0x0cf3, 0x3121) },
+       { USB_DEVICE(0x0CF3, 0x817a) },
        { USB_DEVICE(0x0cf3, 0xe003) },
-       { USB_DEVICE(0x0489, 0xe05f) },
+       { USB_DEVICE(0x0CF3, 0xE004) },
+       { USB_DEVICE(0x0CF3, 0xE005) },
+       { USB_DEVICE(0x13d3, 0x3362) },
+       { USB_DEVICE(0x13d3, 0x3375) },
+       { USB_DEVICE(0x13d3, 0x3393) },
+       { USB_DEVICE(0x13d3, 0x3402) },
 
        /* Atheros AR5BBU12 with sflash firmware */
        { USB_DEVICE(0x0489, 0xE02C) },
 
        /* Atheros AR5BBU22 with sflash firmware */
-       { USB_DEVICE(0x0489, 0xE03C) },
        { USB_DEVICE(0x0489, 0xE036) },
+       { USB_DEVICE(0x0489, 0xE03C) },
 
        { }     /* Terminating entry */
 };
@@ -118,36 +121,39 @@ MODULE_DEVICE_TABLE(usb, ath3k_table);
 static const struct usb_device_id ath3k_blist_tbl[] = {
 
        /* Atheros AR3012 with sflash firmware*/
-       { USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x311E), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x311F), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
 
        /* Atheros AR5BBU22 with sflash firmware */
-       { USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
 
        { }     /* Terminating entry */
 };
index baeaaed299e4e339ee455833c94c9d7aa24421c4..199b9d42489c834c0f244bf506961a352fed7a04 100644 (file)
@@ -101,21 +101,24 @@ static const struct usb_device_id btusb_table[] = {
        { USB_DEVICE(0x0c10, 0x0000) },
 
        /* Broadcom BCM20702A0 */
+       { USB_DEVICE(0x0489, 0xe042) },
+       { USB_DEVICE(0x04ca, 0x2003) },
        { USB_DEVICE(0x0b05, 0x17b5) },
        { USB_DEVICE(0x0b05, 0x17cb) },
-       { USB_DEVICE(0x04ca, 0x2003) },
-       { USB_DEVICE(0x0489, 0xe042) },
        { USB_DEVICE(0x413c, 0x8197) },
 
        /* Foxconn - Hon Hai */
        { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01) },
 
-       /*Broadcom devices with vendor specific id */
+       /* Broadcom devices with vendor specific id */
        { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01) },
 
        /* Belkin F8065bf - Broadcom based */
        { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) },
 
+       /* IMC Networks - Broadcom based */
+       { USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01) },
+
        { }     /* Terminating entry */
 };
 
@@ -129,55 +132,58 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x0a5c, 0x2033), .driver_info = BTUSB_IGNORE },
 
        /* Atheros 3011 with sflash firmware */
+       { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
+       { USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
+       { USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
        { USB_DEVICE(0x0cf3, 0x3002), .driver_info = BTUSB_IGNORE },
        { USB_DEVICE(0x0cf3, 0xe019), .driver_info = BTUSB_IGNORE },
        { USB_DEVICE(0x13d3, 0x3304), .driver_info = BTUSB_IGNORE },
-       { USB_DEVICE(0x0930, 0x0215), .driver_info = BTUSB_IGNORE },
-       { USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
-       { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
 
        /* Atheros AR9285 Malbec with sflash firmware */
        { USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
 
        /* Atheros 3012 with sflash firmware */
-       { USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0cf3, 0x817a), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x311e), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x311f), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0x817a), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
-       { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
 
        /* Atheros AR5BBU12 with sflash firmware */
        { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
 
        /* Atheros AR5BBU12 with sflash firmware */
-       { USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xe036), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
 
        /* Broadcom BCM2035 */
-       { USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
-       { USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
        { USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
+       { USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
+       { USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
 
        /* Broadcom BCM2045 */
        { USB_DEVICE(0x0a5c, 0x2039), .driver_info = BTUSB_WRONG_SCO_MTU },
index 1ef6990a5c7e7c0387b4d9b6aafcb7a5c6f5cdc2..add1c6a720637a4009e35bb2b0a9b4e605fb59b8 100644 (file)
@@ -359,7 +359,7 @@ static const struct file_operations vhci_fops = {
 static struct miscdevice vhci_miscdev= {
        .name   = "vhci",
        .fops   = &vhci_fops,
-       .minor  = MISC_DYNAMIC_MINOR,
+       .minor  = VHCI_MINOR,
 };
 
 static int __init vhci_init(void)
@@ -385,3 +385,4 @@ MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
 MODULE_VERSION(VERSION);
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("devname:vhci");
+MODULE_ALIAS_MISCDEV(VHCI_MINOR);
index 8aa20df55e50d854407d7c84faf28ac11b1576e4..507d9a9ee69ad4b61ece2d334434691801682efe 100644 (file)
@@ -1764,7 +1764,7 @@ static struct usb_device_id ar5523_id_table[] = {
        AR5523_DEVICE_UG(0x07d1, 0x3a07),       /* D-Link / WUA-2340 rev A1 */
        AR5523_DEVICE_UG(0x1690, 0x0712),       /* Gigaset / AR5523 */
        AR5523_DEVICE_UG(0x1690, 0x0710),       /* Gigaset / SMCWUSBTG */
-       AR5523_DEVICE_UG(0x129b, 0x160c),       /* Gigaset / USB stick 108
+       AR5523_DEVICE_UG(0x129b, 0x160b),       /* Gigaset / USB stick 108
                                                   (CyberTAN Technology) */
        AR5523_DEVICE_UG(0x16ab, 0x7801),       /* Globalsun / AR5523_1 */
        AR5523_DEVICE_UX(0x16ab, 0x7811),       /* Globalsun / AR5523_2 */
index 6260b834a86f0365c7e3db9f4d72fe2273964dac..56d559939cfce6681139dd6ca69efb468ecb1b1a 100644 (file)
@@ -63,7 +63,7 @@ enum ath_bus_type {
 };
 
 struct reg_dmn_pair_mapping {
-       u16 regDmnEnum;
+       u16 reg_domain;
        u16 reg_5ghz_ctl;
        u16 reg_2ghz_ctl;
 };
index 3b59af3bddf4a6e4506f027472c41832f41c43c9..ebc5fc2ede75cbac75da3a2f789a946d4e1274ad 100644 (file)
@@ -55,8 +55,7 @@ static void ath10k_send_suspend_complete(struct ath10k *ar)
 {
        ath10k_dbg(ATH10K_DBG_BOOT, "boot suspend complete\n");
 
-       ar->is_target_paused = true;
-       wake_up(&ar->event_queue);
+       complete(&ar->target_suspend);
 }
 
 static int ath10k_init_connect_htc(struct ath10k *ar)
@@ -470,8 +469,12 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name)
                                if (index == ie_len)
                                        break;
 
-                               if (data[index] & (1 << bit))
+                               if (data[index] & (1 << bit)) {
+                                       ath10k_dbg(ATH10K_DBG_BOOT,
+                                                  "Enabling feature bit: %i\n",
+                                                  i);
                                        __set_bit(i, ar->fw_features);
+                               }
                        }
 
                        ath10k_dbg_dump(ATH10K_DBG_BOOT, "features", "",
@@ -699,6 +702,7 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
        init_completion(&ar->scan.started);
        init_completion(&ar->scan.completed);
        init_completion(&ar->scan.on_channel);
+       init_completion(&ar->target_suspend);
 
        init_completion(&ar->install_key_done);
        init_completion(&ar->vdev_setup_done);
@@ -722,8 +726,6 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
        INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work);
        skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
 
-       init_waitqueue_head(&ar->event_queue);
-
        INIT_WORK(&ar->restart_work, ath10k_core_restart);
 
        return ar;
@@ -856,10 +858,34 @@ err:
 }
 EXPORT_SYMBOL(ath10k_core_start);
 
+int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt)
+{
+       int ret;
+
+       reinit_completion(&ar->target_suspend);
+
+       ret = ath10k_wmi_pdev_suspend_target(ar, suspend_opt);
+       if (ret) {
+               ath10k_warn("could not suspend target (%d)\n", ret);
+               return ret;
+       }
+
+       ret = wait_for_completion_timeout(&ar->target_suspend, 1 * HZ);
+
+       if (ret == 0) {
+               ath10k_warn("suspend timed out - target pause event never came\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
 void ath10k_core_stop(struct ath10k *ar)
 {
        lockdep_assert_held(&ar->conf_mutex);
 
+       /* try to suspend target */
+       ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR);
        ath10k_debug_stop(ar);
        ath10k_htc_stop(&ar->htc);
        ath10k_htt_detach(&ar->htt);
index ade1781c7186c5d033a0d5bc5dda2c36b4c6a4ac..1fc26fe057e80bd760d0ef9b6a132679353b41be 100644 (file)
 
 #define ATH10K_MAX_NUM_MGMT_PENDING 128
 
+/* number of failed packets */
+#define ATH10K_KICKOUT_THRESHOLD 50
+
+/*
+ * Use insanely high numbers to make sure that the firmware implementation
+ * won't start, we have the same functionality already in hostapd. Unit
+ * is seconds.
+ */
+#define ATH10K_KEEPALIVE_MIN_IDLE 3747
+#define ATH10K_KEEPALIVE_MAX_IDLE 3895
+#define ATH10K_KEEPALIVE_MAX_UNRESPONSIVE 3900
+
 struct ath10k;
 
 struct ath10k_skb_cb {
@@ -61,6 +73,11 @@ struct ath10k_skb_cb {
                u8 frag_len;
                u8 pad_len;
        } __packed htt;
+
+       struct {
+               bool dtim_zero;
+               bool deliver_cab;
+       } bcn;
 } __packed;
 
 static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb)
@@ -211,6 +228,18 @@ struct ath10k_peer {
        struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
 };
 
+struct ath10k_sta {
+       struct ath10k_vif *arvif;
+
+       /* the following are protected by ar->data_lock */
+       u32 changed; /* IEEE80211_RC_* */
+       u32 bw;
+       u32 nss;
+       u32 smps;
+
+       struct work_struct update_wk;
+};
+
 #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ)
 
 struct ath10k_vif {
@@ -222,10 +251,17 @@ struct ath10k_vif {
        u32 beacon_interval;
        u32 dtim_period;
        struct sk_buff *beacon;
+       /* protected by data_lock */
+       bool beacon_sent;
 
        struct ath10k *ar;
        struct ieee80211_vif *vif;
 
+       bool is_started;
+       bool is_up;
+       u32 aid;
+       u8 bssid[ETH_ALEN];
+
        struct work_struct wep_key_work;
        struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
        u8 def_wep_key_idx;
@@ -235,7 +271,6 @@ struct ath10k_vif {
 
        union {
                struct {
-                       u8 bssid[ETH_ALEN];
                        u32 uapsd;
                } sta;
                struct {
@@ -249,9 +284,6 @@ struct ath10k_vif {
                        u32 noa_len;
                        u8 *noa_data;
                } ap;
-               struct {
-                       u8 bssid[ETH_ALEN];
-               } ibss;
        } u;
 
        u8 fixed_rate;
@@ -355,8 +387,7 @@ struct ath10k {
                const struct ath10k_hif_ops *ops;
        } hif;
 
-       wait_queue_head_t event_queue;
-       bool is_target_paused;
+       struct completion target_suspend;
 
        struct ath10k_bmi bmi;
        struct ath10k_wmi wmi;
@@ -412,6 +443,9 @@ struct ath10k {
        /* valid during scan; needed for mgmt rx during scan */
        struct ieee80211_channel *scan_channel;
 
+       /* current operating channel definition */
+       struct cfg80211_chan_def chandef;
+
        int free_vdev_map;
        int monitor_vdev_id;
        bool monitor_enabled;
@@ -470,6 +504,7 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
 void ath10k_core_destroy(struct ath10k *ar);
 
 int ath10k_core_start(struct ath10k *ar);
+int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt);
 void ath10k_core_stop(struct ath10k *ar);
 int ath10k_core_register(struct ath10k *ar, u32 chip_id);
 void ath10k_core_unregister(struct ath10k *ar);
index 1773c36c71a01a179177b887a3485c9f05c505cf..a5824990bd2a8c789e69d18bb2e3f12f26548248 100644 (file)
@@ -92,7 +92,7 @@ static inline void ath10k_debug_read_target_stats(struct ath10k *ar,
 
 #ifdef CONFIG_ATH10K_DEBUG
 __printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask,
-                                     const char *fmt, ...);
+                              const char *fmt, ...);
 void ath10k_dbg_dump(enum ath10k_debug_mask mask,
                     const char *msg, const char *prefix,
                     const void *buf, size_t len);
index fe8bd1b59f0e2651ee27b742087aedeb5e96a56c..4767c24bf8194113b7e89ea77930efa93e3a701b 100644 (file)
@@ -324,7 +324,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
                                 msdu->len + skb_tailroom(msdu),
                                 DMA_FROM_DEVICE);
 
-               ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
+               ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx pop: ",
                                msdu->data, msdu->len + skb_tailroom(msdu));
 
                rx_desc = (struct htt_rx_desc *)msdu->data;
@@ -417,8 +417,8 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
                                         next->len + skb_tailroom(next),
                                         DMA_FROM_DEVICE);
 
-                       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ",
-                                       next->data,
+                       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL,
+                                       "htt rx chained: ", next->data,
                                        next->len + skb_tailroom(next));
 
                        skb_trim(next, 0);
@@ -430,12 +430,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
                        msdu_chaining = 1;
                }
 
-               if (msdu_len > 0) {
-                       /* This may suggest FW bug? */
-                       ath10k_warn("htt rx msdu len not consumed (%d)\n",
-                                   msdu_len);
-               }
-
                last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
                                RX_MSDU_END_INFO0_LAST_MSDU;
 
@@ -751,7 +745,7 @@ static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info)
 
        /* This shouldn't happen. If it does than it may be a FW bug. */
        if (skb->next) {
-               ath10k_warn("received chained non A-MSDU frame\n");
+               ath10k_warn("htt rx received chained non A-MSDU frame\n");
                ath10k_htt_rx_free_msdu_chain(skb->next);
                skb->next = NULL;
        }
@@ -937,6 +931,8 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                        }
 
                        if (ath10k_htt_rx_has_decrypt_err(msdu_head)) {
+                               ath10k_dbg(ATH10K_DBG_HTT,
+                                          "htt rx dropping due to decrypt-err\n");
                                ath10k_htt_rx_free_msdu_chain(msdu_head);
                                continue;
                        }
@@ -945,12 +941,14 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
 
                        /* Skip mgmt frames while we handle this in WMI */
                        if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL) {
+                               ath10k_dbg(ATH10K_DBG_HTT, "htt rx mgmt ctrl\n");
                                ath10k_htt_rx_free_msdu_chain(msdu_head);
                                continue;
                        }
 
                        if (status != HTT_RX_IND_MPDU_STATUS_OK &&
                            status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR &&
+                           status != HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER &&
                            !htt->ar->monitor_enabled) {
                                ath10k_dbg(ATH10K_DBG_HTT,
                                           "htt rx ignoring frame w/ status %d\n",
@@ -960,6 +958,8 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                        }
 
                        if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) {
+                               ath10k_dbg(ATH10K_DBG_HTT,
+                                          "htt rx CAC running\n");
                                ath10k_htt_rx_free_msdu_chain(msdu_head);
                                continue;
                        }
@@ -967,7 +967,7 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                        /* FIXME: we do not support chaining yet.
                         * this needs investigation */
                        if (msdu_chaining) {
-                               ath10k_warn("msdu_chaining is true\n");
+                               ath10k_warn("htt rx msdu_chaining is true\n");
                                ath10k_htt_rx_free_msdu_chain(msdu_head);
                                continue;
                        }
@@ -975,6 +975,15 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
                        info.skb     = msdu_head;
                        info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
                        info.mic_err = ath10k_htt_rx_has_mic_err(msdu_head);
+
+                       if (info.fcs_err)
+                               ath10k_dbg(ATH10K_DBG_HTT,
+                                          "htt rx has FCS err\n");
+
+                       if (info.mic_err)
+                               ath10k_dbg(ATH10K_DBG_HTT,
+                                          "htt rx has MIC err\n");
+
                        info.signal  = ATH10K_DEFAULT_NOISE_FLOOR;
                        info.signal += rx->ppdu.combined_rssi;
 
@@ -1095,7 +1104,7 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt,
 
        skb_trim(info.skb, info.skb->len - trim);
 
-       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt frag mpdu: ",
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ",
                        info.skb->data, info.skb->len);
        ath10k_process_rx(htt->ar, &info);
 
@@ -1116,7 +1125,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
        if (!IS_ALIGNED((unsigned long)skb->data, 4))
                ath10k_warn("unaligned htt message, expect trouble\n");
 
-       ath10k_dbg(ATH10K_DBG_HTT, "HTT RX, msg_type: 0x%0X\n",
+       ath10k_dbg(ATH10K_DBG_HTT, "htt rx, msg_type: 0x%0X\n",
                   resp->hdr.msg_type);
        switch (resp->hdr.msg_type) {
        case HTT_T2H_MSG_TYPE_VERSION_CONF: {
index f1d36d2d27235aeec979fed652d2925ec6e94513..acaa046dc93b3c37a738eb015a00104ec58eaee7 100644 (file)
@@ -460,9 +460,9 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
                                           DMA_TO_DEVICE);
        }
 
-       ath10k_dbg(ATH10K_DBG_HTT, "msdu 0x%llx\n",
+       ath10k_dbg(ATH10K_DBG_HTT, "tx-msdu 0x%llx\n",
                   (unsigned long long) ATH10K_SKB_CB(msdu)->paddr);
-       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "msdu: ",
+       ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "tx-msdu: ",
                        msdu->data, msdu->len);
 
        skb_put(txdesc, desc_len);
index f1505a25d8109b3cf1a315c548aad9348ea0e441..35fc44e281f57968171283d7d336cce5b20eddac 100644 (file)
@@ -205,8 +205,11 @@ enum ath10k_mcast2ucast_mode {
 #define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS     0x0006c000
 #define PCIE_LOCAL_BASE_ADDRESS                        0x00080000
 
+#define SOC_RESET_CONTROL_ADDRESS              0x00000000
 #define SOC_RESET_CONTROL_OFFSET               0x00000000
 #define SOC_RESET_CONTROL_SI0_RST_MASK         0x00000001
+#define SOC_RESET_CONTROL_CE_RST_MASK          0x00040000
+#define SOC_RESET_CONTROL_CPU_WARM_RST_MASK    0x00000040
 #define SOC_CPU_CLOCK_OFFSET                   0x00000020
 #define SOC_CPU_CLOCK_STANDARD_LSB             0
 #define SOC_CPU_CLOCK_STANDARD_MASK            0x00000003
@@ -216,6 +219,8 @@ enum ath10k_mcast2ucast_mode {
 #define SOC_LPO_CAL_OFFSET                     0x000000e0
 #define SOC_LPO_CAL_ENABLE_LSB                 20
 #define SOC_LPO_CAL_ENABLE_MASK                        0x00100000
+#define SOC_LF_TIMER_CONTROL0_ADDRESS          0x00000050
+#define SOC_LF_TIMER_CONTROL0_ENABLE_MASK      0x00000004
 
 #define SOC_CHIP_ID_ADDRESS                    0x000000ec
 #define SOC_CHIP_ID_REV_LSB                    8
@@ -273,6 +278,7 @@ enum ath10k_mcast2ucast_mode {
 #define PCIE_INTR_CAUSE_ADDRESS                        0x000c
 #define PCIE_INTR_CLR_ADDRESS                  0x0014
 #define SCRATCH_3_ADDRESS                      0x0030
+#define CPU_INTR_ADDRESS                       0x0010
 
 /* Firmware indications to the Host via SCRATCH_3 register. */
 #define FW_INDICATOR_ADDRESS   (SOC_CORE_BASE_ADDRESS + SCRATCH_3_ADDRESS)
index 776e364eadcd76c82ed37a64e20601f5bb1926ab..e17f5d732b5abf099eeaa590bca0be746aed073e 100644 (file)
@@ -339,6 +339,50 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
        return 0;
 }
 
+static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
+{
+       struct ath10k *ar = arvif->ar;
+       u32 param;
+       int ret;
+
+       param = ar->wmi.pdev_param->sta_kickout_th;
+       ret = ath10k_wmi_pdev_set_param(ar, param,
+                                       ATH10K_KICKOUT_THRESHOLD);
+       if (ret) {
+               ath10k_warn("Failed to set kickout threshold: %d\n", ret);
+               return ret;
+       }
+
+       param = ar->wmi.vdev_param->ap_keepalive_min_idle_inactive_time_secs;
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
+                                       ATH10K_KEEPALIVE_MIN_IDLE);
+       if (ret) {
+               ath10k_warn("Failed to set keepalive minimum idle time : %d\n",
+                           ret);
+               return ret;
+       }
+
+       param = ar->wmi.vdev_param->ap_keepalive_max_idle_inactive_time_secs;
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
+                                       ATH10K_KEEPALIVE_MAX_IDLE);
+       if (ret) {
+               ath10k_warn("Failed to set keepalive maximum idle time: %d\n",
+                           ret);
+               return ret;
+       }
+
+       param = ar->wmi.vdev_param->ap_keepalive_max_unresponsive_time_secs;
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param,
+                                       ATH10K_KEEPALIVE_MAX_UNRESPONSIVE);
+       if (ret) {
+               ath10k_warn("Failed to set keepalive maximum unresponsive time: %d\n",
+                           ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 static int  ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
 {
        struct ath10k *ar = arvif->ar;
@@ -444,8 +488,7 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar)
 static int ath10k_vdev_start(struct ath10k_vif *arvif)
 {
        struct ath10k *ar = arvif->ar;
-       struct ieee80211_conf *conf = &ar->hw->conf;
-       struct ieee80211_channel *channel = conf->chandef.chan;
+       struct cfg80211_chan_def *chandef = &ar->chandef;
        struct wmi_vdev_start_request_arg arg = {};
        int ret = 0;
 
@@ -457,16 +500,14 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif)
        arg.dtim_period = arvif->dtim_period;
        arg.bcn_intval = arvif->beacon_interval;
 
-       arg.channel.freq = channel->center_freq;
-
-       arg.channel.band_center_freq1 = conf->chandef.center_freq1;
-
-       arg.channel.mode = chan_to_phymode(&conf->chandef);
+       arg.channel.freq = chandef->chan->center_freq;
+       arg.channel.band_center_freq1 = chandef->center_freq1;
+       arg.channel.mode = chan_to_phymode(chandef);
 
        arg.channel.min_power = 0;
-       arg.channel.max_power = channel->max_power * 2;
-       arg.channel.max_reg_power = channel->max_reg_power * 2;
-       arg.channel.max_antenna_gain = channel->max_antenna_gain * 2;
+       arg.channel.max_power = chandef->chan->max_power * 2;
+       arg.channel.max_reg_power = chandef->chan->max_reg_power * 2;
+       arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2;
 
        if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
                arg.ssid = arvif->u.ap.ssid;
@@ -475,7 +516,7 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif)
 
                /* For now allow DFS for AP mode */
                arg.channel.chan_radar =
-                       !!(channel->flags & IEEE80211_CHAN_RADAR);
+                       !!(chandef->chan->flags & IEEE80211_CHAN_RADAR);
        } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
                arg.ssid = arvif->vif->bss_conf.ssid;
                arg.ssid_len = arvif->vif->bss_conf.ssid_len;
@@ -527,7 +568,8 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif)
 
 static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
 {
-       struct ieee80211_channel *channel = ar->hw->conf.chandef.chan;
+       struct cfg80211_chan_def *chandef = &ar->chandef;
+       struct ieee80211_channel *channel = chandef->chan;
        struct wmi_vdev_start_request_arg arg = {};
        int ret = 0;
 
@@ -540,11 +582,11 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id)
 
        arg.vdev_id = vdev_id;
        arg.channel.freq = channel->center_freq;
-       arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1;
+       arg.channel.band_center_freq1 = chandef->center_freq1;
 
        /* TODO setup this dynamically, what in case we
           don't have any vifs? */
-       arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef);
+       arg.channel.mode = chan_to_phymode(chandef);
        arg.channel.chan_radar =
                        !!(channel->flags & IEEE80211_CHAN_RADAR);
 
@@ -791,6 +833,20 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
 
        if (!info->enable_beacon) {
                ath10k_vdev_stop(arvif);
+
+               arvif->is_started = false;
+               arvif->is_up = false;
+
+               spin_lock_bh(&arvif->ar->data_lock);
+               if (arvif->beacon) {
+                       ath10k_skb_unmap(arvif->ar->dev, arvif->beacon);
+                       dev_kfree_skb_any(arvif->beacon);
+
+                       arvif->beacon = NULL;
+                       arvif->beacon_sent = false;
+               }
+               spin_unlock_bh(&arvif->ar->data_lock);
+
                return;
        }
 
@@ -800,12 +856,21 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif,
        if (ret)
                return;
 
-       ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, 0, info->bssid);
+       arvif->aid = 0;
+       memcpy(arvif->bssid, info->bssid, ETH_ALEN);
+
+       ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
+                                arvif->bssid);
        if (ret) {
                ath10k_warn("Failed to bring up VDEV: %d\n",
                            arvif->vdev_id);
+               ath10k_vdev_stop(arvif);
                return;
        }
+
+       arvif->is_started = true;
+       arvif->is_up = true;
+
        ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id);
 }
 
@@ -824,18 +889,18 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif,
                        ath10k_warn("Failed to delete IBSS self peer:%pM for VDEV:%d ret:%d\n",
                                    self_peer, arvif->vdev_id, ret);
 
-               if (is_zero_ether_addr(arvif->u.ibss.bssid))
+               if (is_zero_ether_addr(arvif->bssid))
                        return;
 
                ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id,
-                                        arvif->u.ibss.bssid);
+                                        arvif->bssid);
                if (ret) {
                        ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n",
-                                   arvif->u.ibss.bssid, arvif->vdev_id, ret);
+                                   arvif->bssid, arvif->vdev_id, ret);
                        return;
                }
 
-               memset(arvif->u.ibss.bssid, 0, ETH_ALEN);
+               memset(arvif->bssid, 0, ETH_ALEN);
 
                return;
        }
@@ -1017,7 +1082,6 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
                                   struct wmi_peer_assoc_complete_arg *arg)
 {
        const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
-       int smps;
        int i, n;
 
        lockdep_assert_held(&ar->conf_mutex);
@@ -1063,17 +1127,6 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
                arg->peer_flags |= WMI_PEER_STBC;
        }
 
-       smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS;
-       smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT;
-
-       if (smps == WLAN_HT_CAP_SM_PS_STATIC) {
-               arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
-               arg->peer_flags |= WMI_PEER_STATIC_MIMOPS;
-       } else if (smps == WLAN_HT_CAP_SM_PS_DYNAMIC) {
-               arg->peer_flags |= WMI_PEER_SPATIAL_MUX;
-               arg->peer_flags |= WMI_PEER_DYN_MIMOPS;
-       }
-
        if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2])
                arg->peer_rate_caps |= WMI_RC_TS_FLAG;
        else if (ht_cap->mcs.rx_mask[1])
@@ -1083,8 +1136,23 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
                if (ht_cap->mcs.rx_mask[i/8] & (1 << i%8))
                        arg->peer_ht_rates.rates[n++] = i;
 
-       arg->peer_ht_rates.num_rates = n;
-       arg->peer_num_spatial_streams = max((n+7) / 8, 1);
+       /*
+        * This is a workaround for HT-enabled STAs which break the spec
+        * and have no HT capabilities RX mask (no HT RX MCS map).
+        *
+        * As per spec, in section 20.3.5 Modulation and coding scheme (MCS),
+        * MCS 0 through 7 are mandatory in 20MHz with 800 ns GI at all STAs.
+        *
+        * Firmware asserts if such situation occurs.
+        */
+       if (n == 0) {
+               arg->peer_ht_rates.num_rates = 8;
+               for (i = 0; i < arg->peer_ht_rates.num_rates; i++)
+                       arg->peer_ht_rates.rates[i] = i;
+       } else {
+               arg->peer_ht_rates.num_rates = n;
+               arg->peer_num_spatial_streams = sta->rx_nss;
+       }
 
        ath10k_dbg(ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n",
                   arg->addr,
@@ -1092,27 +1160,20 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar,
                   arg->peer_num_spatial_streams);
 }
 
-static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
-                                      struct ath10k_vif *arvif,
-                                      struct ieee80211_sta *sta,
-                                      struct ieee80211_bss_conf *bss_conf,
-                                      struct wmi_peer_assoc_complete_arg *arg)
+static int ath10k_peer_assoc_qos_ap(struct ath10k *ar,
+                                   struct ath10k_vif *arvif,
+                                   struct ieee80211_sta *sta)
 {
        u32 uapsd = 0;
        u32 max_sp = 0;
+       int ret = 0;
 
        lockdep_assert_held(&ar->conf_mutex);
 
-       if (sta->wme)
-               arg->peer_flags |= WMI_PEER_QOS;
-
        if (sta->wme && sta->uapsd_queues) {
                ath10k_dbg(ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n",
                           sta->uapsd_queues, sta->max_sp);
 
-               arg->peer_flags |= WMI_PEER_APSD;
-               arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG;
-
                if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
                        uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN |
                                 WMI_AP_PS_UAPSD_AC3_TRIGGER_EN;
@@ -1130,35 +1191,40 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar,
                if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP)
                        max_sp = sta->max_sp;
 
-               ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
-                                          sta->addr,
-                                          WMI_AP_PS_PEER_PARAM_UAPSD,
-                                          uapsd);
+               ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+                                                sta->addr,
+                                                WMI_AP_PS_PEER_PARAM_UAPSD,
+                                                uapsd);
+               if (ret) {
+                       ath10k_warn("failed to set ap ps peer param uapsd: %d\n",
+                                   ret);
+                       return ret;
+               }
 
-               ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
-                                          sta->addr,
-                                          WMI_AP_PS_PEER_PARAM_MAX_SP,
-                                          max_sp);
+               ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
+                                                sta->addr,
+                                                WMI_AP_PS_PEER_PARAM_MAX_SP,
+                                                max_sp);
+               if (ret) {
+                       ath10k_warn("failed to set ap ps peer param max sp: %d\n",
+                                   ret);
+                       return ret;
+               }
 
                /* TODO setup this based on STA listen interval and
                   beacon interval. Currently we don't know
                   sta->listen_interval - mac80211 patch required.
                   Currently use 10 seconds */
-               ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id,
-                                          sta->addr,
-                                          WMI_AP_PS_PEER_PARAM_AGEOUT_TIME,
-                                          10);
+               ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, sta->addr,
+                                       WMI_AP_PS_PEER_PARAM_AGEOUT_TIME, 10);
+               if (ret) {
+                       ath10k_warn("failed to set ap ps peer param ageout time: %d\n",
+                                   ret);
+                       return ret;
+               }
        }
-}
 
-static void ath10k_peer_assoc_h_qos_sta(struct ath10k *ar,
-                                       struct ath10k_vif *arvif,
-                                       struct ieee80211_sta *sta,
-                                       struct ieee80211_bss_conf *bss_conf,
-                                       struct wmi_peer_assoc_complete_arg *arg)
-{
-       if (bss_conf->qos)
-               arg->peer_flags |= WMI_PEER_QOS;
+       return 0;
 }
 
 static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
@@ -1211,10 +1277,17 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar,
 {
        switch (arvif->vdev_type) {
        case WMI_VDEV_TYPE_AP:
-               ath10k_peer_assoc_h_qos_ap(ar, arvif, sta, bss_conf, arg);
+               if (sta->wme)
+                       arg->peer_flags |= WMI_PEER_QOS;
+
+               if (sta->wme && sta->uapsd_queues) {
+                       arg->peer_flags |= WMI_PEER_APSD;
+                       arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG;
+               }
                break;
        case WMI_VDEV_TYPE_STA:
-               ath10k_peer_assoc_h_qos_sta(ar, arvif, sta, bss_conf, arg);
+               if (bss_conf->qos)
+                       arg->peer_flags |= WMI_PEER_QOS;
                break;
        default:
                break;
@@ -1293,6 +1366,33 @@ static int ath10k_peer_assoc_prepare(struct ath10k *ar,
        return 0;
 }
 
+static const u32 ath10k_smps_map[] = {
+       [WLAN_HT_CAP_SM_PS_STATIC] = WMI_PEER_SMPS_STATIC,
+       [WLAN_HT_CAP_SM_PS_DYNAMIC] = WMI_PEER_SMPS_DYNAMIC,
+       [WLAN_HT_CAP_SM_PS_INVALID] = WMI_PEER_SMPS_PS_NONE,
+       [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE,
+};
+
+static int ath10k_setup_peer_smps(struct ath10k *ar, struct ath10k_vif *arvif,
+                                 const u8 *addr,
+                                 const struct ieee80211_sta_ht_cap *ht_cap)
+{
+       int smps;
+
+       if (!ht_cap->ht_supported)
+               return 0;
+
+       smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS;
+       smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT;
+
+       if (smps >= ARRAY_SIZE(ath10k_smps_map))
+               return -EINVAL;
+
+       return ath10k_wmi_peer_set_param(ar, arvif->vdev_id, addr,
+                                        WMI_PEER_SMPS_STATE,
+                                        ath10k_smps_map[smps]);
+}
+
 /* can be called only in mac80211 callbacks due to `key_count` usage */
 static void ath10k_bss_assoc(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
@@ -1300,6 +1400,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ieee80211_sta_ht_cap ht_cap;
        struct wmi_peer_assoc_complete_arg peer_arg;
        struct ieee80211_sta *ap_sta;
        int ret;
@@ -1316,6 +1417,10 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
                return;
        }
 
+       /* ap_sta must be accessed only within rcu section which must be left
+        * before calling ath10k_setup_peer_smps() which might sleep. */
+       ht_cap = ap_sta->ht_cap;
+
        ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta,
                                        bss_conf, &peer_arg);
        if (ret) {
@@ -1334,15 +1439,27 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw,
                return;
        }
 
+       ret = ath10k_setup_peer_smps(ar, arvif, bss_conf->bssid, &ht_cap);
+       if (ret) {
+               ath10k_warn("failed to setup peer SMPS: %d\n", ret);
+               return;
+       }
+
        ath10k_dbg(ATH10K_DBG_MAC,
                   "mac vdev %d up (associated) bssid %pM aid %d\n",
                   arvif->vdev_id, bss_conf->bssid, bss_conf->aid);
 
-       ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid,
-                                bss_conf->bssid);
-       if (ret)
+       arvif->aid = bss_conf->aid;
+       memcpy(arvif->bssid, bss_conf->bssid, ETH_ALEN);
+
+       ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid);
+       if (ret) {
                ath10k_warn("VDEV: %d up failed: ret %d\n",
                            arvif->vdev_id, ret);
+               return;
+       }
+
+       arvif->is_up = true;
 }
 
 /*
@@ -1382,6 +1499,9 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
        ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);
 
        arvif->def_wep_key_idx = 0;
+
+       arvif->is_started = false;
+       arvif->is_up = false;
 }
 
 static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
@@ -1406,12 +1526,25 @@ static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
                return ret;
        }
 
+       ret = ath10k_setup_peer_smps(ar, arvif, sta->addr, &sta->ht_cap);
+       if (ret) {
+               ath10k_warn("failed to setup peer SMPS: %d\n", ret);
+               return ret;
+       }
+
        ret = ath10k_install_peer_wep_keys(arvif, sta->addr);
        if (ret) {
                ath10k_warn("could not install peer wep keys (%d)\n", ret);
                return ret;
        }
 
+       ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta);
+       if (ret) {
+               ath10k_warn("could not set qos params for STA %pM, %d\n",
+                           sta->addr, ret);
+               return ret;
+       }
+
        return ret;
 }
 
@@ -1547,9 +1680,9 @@ static void ath10k_regd_update(struct ath10k *ar)
        /* Target allows setting up per-band regdomain but ath_common provides
         * a combined one only */
        ret = ath10k_wmi_pdev_set_regdomain(ar,
-                                           regpair->regDmnEnum,
-                                           regpair->regDmnEnum, /* 2ghz */
-                                           regpair->regDmnEnum, /* 5ghz */
+                                           regpair->reg_domain,
+                                           regpair->reg_domain, /* 2ghz */
+                                           regpair->reg_domain, /* 5ghz */
                                            regpair->reg_2ghz_ctl,
                                            regpair->reg_5ghz_ctl);
        if (ret)
@@ -2100,11 +2233,29 @@ static int ath10k_start(struct ieee80211_hw *hw)
                ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
                            ret);
 
+       /*
+        * By default FW set ARP frames ac to voice (6). In that case ARP
+        * exchange is not working properly for UAPSD enabled AP. ARP requests
+        * which arrives with access category 0 are processed by network stack
+        * and send back with access category 0, but FW changes access category
+        * to 6. Set ARP frames access category to best effort (0) solves
+        * this problem.
+        */
+
+       ret = ath10k_wmi_pdev_set_param(ar,
+                                       ar->wmi.pdev_param->arp_ac_override, 0);
+       if (ret) {
+               ath10k_warn("could not set arp ac override parameter: %d\n",
+                           ret);
+               goto exit;
+       }
+
        ath10k_regd_update(ar);
+       ret = 0;
 
 exit:
        mutex_unlock(&ar->conf_mutex);
-       return 0;
+       return ret;
 }
 
 static void ath10k_stop(struct ieee80211_hw *hw)
@@ -2145,6 +2296,98 @@ static int ath10k_config_ps(struct ath10k *ar)
        return ret;
 }
 
+static const char *chandef_get_width(enum nl80211_chan_width width)
+{
+       switch (width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               return "20 (noht)";
+       case NL80211_CHAN_WIDTH_20:
+               return "20";
+       case NL80211_CHAN_WIDTH_40:
+               return "40";
+       case NL80211_CHAN_WIDTH_80:
+               return "80";
+       case NL80211_CHAN_WIDTH_80P80:
+               return "80+80";
+       case NL80211_CHAN_WIDTH_160:
+               return "160";
+       case NL80211_CHAN_WIDTH_5:
+               return "5";
+       case NL80211_CHAN_WIDTH_10:
+               return "10";
+       }
+       return "?";
+}
+
+static void ath10k_config_chan(struct ath10k *ar)
+{
+       struct ath10k_vif *arvif;
+       bool monitor_was_enabled;
+       int ret;
+
+       lockdep_assert_held(&ar->conf_mutex);
+
+       ath10k_dbg(ATH10K_DBG_MAC,
+                  "mac config channel to %dMHz (cf1 %dMHz cf2 %dMHz width %s)\n",
+                  ar->chandef.chan->center_freq,
+                  ar->chandef.center_freq1,
+                  ar->chandef.center_freq2,
+                  chandef_get_width(ar->chandef.width));
+
+       /* First stop monitor interface. Some FW versions crash if there's a
+        * lone monitor interface. */
+       monitor_was_enabled = ar->monitor_enabled;
+
+       if (ar->monitor_enabled)
+               ath10k_monitor_stop(ar);
+
+       list_for_each_entry(arvif, &ar->arvifs, list) {
+               if (!arvif->is_started)
+                       continue;
+
+               if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+                       continue;
+
+               ret = ath10k_vdev_stop(arvif);
+               if (ret) {
+                       ath10k_warn("could not stop vdev %d (%d)\n",
+                                   arvif->vdev_id, ret);
+                       continue;
+               }
+       }
+
+       /* all vdevs are now stopped - now attempt to restart them */
+
+       list_for_each_entry(arvif, &ar->arvifs, list) {
+               if (!arvif->is_started)
+                       continue;
+
+               if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
+                       continue;
+
+               ret = ath10k_vdev_start(arvif);
+               if (ret) {
+                       ath10k_warn("could not start vdev %d (%d)\n",
+                                   arvif->vdev_id, ret);
+                       continue;
+               }
+
+               if (!arvif->is_up)
+                       continue;
+
+               ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid,
+                                        arvif->bssid);
+               if (ret) {
+                       ath10k_warn("could not bring vdev up %d (%d)\n",
+                                   arvif->vdev_id, ret);
+                       continue;
+               }
+       }
+
+       if (monitor_was_enabled)
+               ath10k_monitor_start(ar, ar->monitor_vdev_id);
+}
+
 static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct ath10k *ar = hw->priv;
@@ -2165,6 +2408,11 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
                spin_unlock_bh(&ar->data_lock);
 
                ath10k_config_radar_detection(ar);
+
+               if (!cfg80211_chandef_identical(&ar->chandef, &conf->chandef)) {
+                       ar->chandef = conf->chandef;
+                       ath10k_config_chan(ar);
+               }
        }
 
        if (changed & IEEE80211_CONF_CHANGE_POWER) {
@@ -2214,7 +2462,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        enum wmi_sta_powersave_param param;
        int ret = 0;
-       u32 value, param_id;
+       u32 value;
        int bit;
        u32 vdev_param;
 
@@ -2307,12 +2555,12 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                        goto err_vdev_delete;
                }
 
-               param_id = ar->wmi.pdev_param->sta_kickout_th;
-
-               /* Disable STA KICKOUT functionality in FW */
-               ret = ath10k_wmi_pdev_set_param(ar, param_id, 0);
-               if (ret)
-                       ath10k_warn("Failed to disable STA KICKOUT\n");
+               ret = ath10k_mac_set_kickout(arvif);
+               if (ret) {
+                       ath10k_warn("Failed to set kickout parameters: %d\n",
+                                   ret);
+                       goto err_peer_delete;
+               }
        }
 
        if (arvif->vdev_type == WMI_VDEV_TYPE_STA) {
@@ -2559,15 +2807,20 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                                 * this is never erased as we it for crypto key
                                 * clearing; this is FW requirement
                                 */
-                               memcpy(arvif->u.sta.bssid, info->bssid,
-                                      ETH_ALEN);
+                               memcpy(arvif->bssid, info->bssid, ETH_ALEN);
 
                                ath10k_dbg(ATH10K_DBG_MAC,
                                           "mac vdev %d start %pM\n",
                                           arvif->vdev_id, info->bssid);
 
-                               /* FIXME: check return value */
                                ret = ath10k_vdev_start(arvif);
+                               if (ret) {
+                                       ath10k_warn("failed to start vdev: %d\n",
+                                                   ret);
+                                       goto exit;
+                               }
+
+                               arvif->is_started = true;
                        }
 
                        /*
@@ -2576,7 +2829,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                         * IBSS in order to remove BSSID peer.
                         */
                        if (vif->type == NL80211_IFTYPE_ADHOC)
-                               memcpy(arvif->u.ibss.bssid, info->bssid,
+                               memcpy(arvif->bssid, info->bssid,
                                       ETH_ALEN);
                }
        }
@@ -2645,6 +2898,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw,
                        ath10k_bss_assoc(hw, vif, info);
        }
 
+exit:
        mutex_unlock(&ar->conf_mutex);
 }
 
@@ -2850,6 +3104,69 @@ exit:
        return ret;
 }
 
+static void ath10k_sta_rc_update_wk(struct work_struct *wk)
+{
+       struct ath10k *ar;
+       struct ath10k_vif *arvif;
+       struct ath10k_sta *arsta;
+       struct ieee80211_sta *sta;
+       u32 changed, bw, nss, smps;
+       int err;
+
+       arsta = container_of(wk, struct ath10k_sta, update_wk);
+       sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv);
+       arvif = arsta->arvif;
+       ar = arvif->ar;
+
+       spin_lock_bh(&ar->data_lock);
+
+       changed = arsta->changed;
+       arsta->changed = 0;
+
+       bw = arsta->bw;
+       nss = arsta->nss;
+       smps = arsta->smps;
+
+       spin_unlock_bh(&ar->data_lock);
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (changed & IEEE80211_RC_BW_CHANGED) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM peer bw %d\n",
+                          sta->addr, bw);
+
+               err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+                                               WMI_PEER_CHAN_WIDTH, bw);
+               if (err)
+                       ath10k_warn("failed to update STA %pM peer bw %d: %d\n",
+                                   sta->addr, bw, err);
+       }
+
+       if (changed & IEEE80211_RC_NSS_CHANGED) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM nss %d\n",
+                          sta->addr, nss);
+
+               err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+                                               WMI_PEER_NSS, nss);
+               if (err)
+                       ath10k_warn("failed to update STA %pM nss %d: %d\n",
+                                   sta->addr, nss, err);
+       }
+
+       if (changed & IEEE80211_RC_SMPS_CHANGED) {
+               ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM smps %d\n",
+                          sta->addr, smps);
+
+               err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr,
+                                               WMI_PEER_SMPS_STATE, smps);
+               if (err)
+                       ath10k_warn("failed to update STA %pM smps %d: %d\n",
+                                   sta->addr, smps, err);
+       }
+
+       mutex_unlock(&ar->conf_mutex);
+}
+
 static int ath10k_sta_state(struct ieee80211_hw *hw,
                            struct ieee80211_vif *vif,
                            struct ieee80211_sta *sta,
@@ -2858,9 +3175,15 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
        int max_num_peers;
        int ret = 0;
 
+       /* cancel must be done outside the mutex to avoid deadlock */
+       if ((old_state == IEEE80211_STA_NONE &&
+            new_state == IEEE80211_STA_NOTEXIST))
+               cancel_work_sync(&arsta->update_wk);
+
        mutex_lock(&ar->conf_mutex);
 
        if (old_state == IEEE80211_STA_NOTEXIST &&
@@ -2885,6 +3208,10 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                           "mac vdev %d peer create %pM (new sta) num_peers %d\n",
                           arvif->vdev_id, sta->addr, ar->num_peers);
 
+               memset(arsta, 0, sizeof(*arsta));
+               arsta->arvif = arvif;
+               INIT_WORK(&arsta->update_wk, ath10k_sta_rc_update_wk);
+
                ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
                if (ret)
                        ath10k_warn("Failed to add peer %pM for vdev %d when adding a new sta: %i\n",
@@ -3234,23 +3561,14 @@ static int ath10k_suspend(struct ieee80211_hw *hw,
        struct ath10k *ar = hw->priv;
        int ret;
 
-       ar->is_target_paused = false;
+       mutex_lock(&ar->conf_mutex);
 
-       ret = ath10k_wmi_pdev_suspend_target(ar);
+       ret = ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND);
        if (ret) {
-               ath10k_warn("could not suspend target (%d)\n", ret);
-               return 1;
-       }
-
-       ret = wait_event_interruptible_timeout(ar->event_queue,
-                                              ar->is_target_paused == true,
-                                              1 * HZ);
-       if (ret < 0) {
-               ath10k_warn("suspend interrupted (%d)\n", ret);
-               goto resume;
-       } else if (ret == 0) {
-               ath10k_warn("suspend timed out - target pause event never came\n");
-               goto resume;
+               if (ret == -ETIMEDOUT)
+                       goto resume;
+               ret = 1;
+               goto exit;
        }
 
        ret = ath10k_hif_suspend(ar);
@@ -3259,12 +3577,17 @@ static int ath10k_suspend(struct ieee80211_hw *hw,
                goto resume;
        }
 
-       return 0;
+       ret = 0;
+       goto exit;
 resume:
        ret = ath10k_wmi_pdev_resume_target(ar);
        if (ret)
                ath10k_warn("could not resume target (%d)\n", ret);
-       return 1;
+
+       ret = 1;
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
 }
 
 static int ath10k_resume(struct ieee80211_hw *hw)
@@ -3272,19 +3595,26 @@ static int ath10k_resume(struct ieee80211_hw *hw)
        struct ath10k *ar = hw->priv;
        int ret;
 
+       mutex_lock(&ar->conf_mutex);
+
        ret = ath10k_hif_resume(ar);
        if (ret) {
                ath10k_warn("could not resume hif (%d)\n", ret);
-               return 1;
+               ret = 1;
+               goto exit;
        }
 
        ret = ath10k_wmi_pdev_resume_target(ar);
        if (ret) {
                ath10k_warn("could not resume target (%d)\n", ret);
-               return 1;
+               ret = 1;
+               goto exit;
        }
 
-       return 0;
+       ret = 0;
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
 }
 #endif
 
@@ -3640,6 +3970,96 @@ static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw,
        return ath10k_set_fixed_rate_param(arvif, fixed_rate, fixed_nss);
 }
 
+static void ath10k_channel_switch_beacon(struct ieee80211_hw *hw,
+                                        struct ieee80211_vif *vif,
+                                        struct cfg80211_chan_def *chandef)
+{
+       /* there's no need to do anything here. vif->csa_active is enough */
+       return;
+}
+
+static void ath10k_sta_rc_update(struct ieee80211_hw *hw,
+                                struct ieee80211_vif *vif,
+                                struct ieee80211_sta *sta,
+                                u32 changed)
+{
+       struct ath10k *ar = hw->priv;
+       struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+       u32 bw, smps;
+
+       spin_lock_bh(&ar->data_lock);
+
+       ath10k_dbg(ATH10K_DBG_MAC,
+                  "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n",
+                  sta->addr, changed, sta->bandwidth, sta->rx_nss,
+                  sta->smps_mode);
+
+       if (changed & IEEE80211_RC_BW_CHANGED) {
+               bw = WMI_PEER_CHWIDTH_20MHZ;
+
+               switch (sta->bandwidth) {
+               case IEEE80211_STA_RX_BW_20:
+                       bw = WMI_PEER_CHWIDTH_20MHZ;
+                       break;
+               case IEEE80211_STA_RX_BW_40:
+                       bw = WMI_PEER_CHWIDTH_40MHZ;
+                       break;
+               case IEEE80211_STA_RX_BW_80:
+                       bw = WMI_PEER_CHWIDTH_80MHZ;
+                       break;
+               case IEEE80211_STA_RX_BW_160:
+                       ath10k_warn("mac sta rc update for %pM: invalid bw %d\n",
+                                   sta->addr, sta->bandwidth);
+                       bw = WMI_PEER_CHWIDTH_20MHZ;
+                       break;
+               }
+
+               arsta->bw = bw;
+       }
+
+       if (changed & IEEE80211_RC_NSS_CHANGED)
+               arsta->nss = sta->rx_nss;
+
+       if (changed & IEEE80211_RC_SMPS_CHANGED) {
+               smps = WMI_PEER_SMPS_PS_NONE;
+
+               switch (sta->smps_mode) {
+               case IEEE80211_SMPS_AUTOMATIC:
+               case IEEE80211_SMPS_OFF:
+                       smps = WMI_PEER_SMPS_PS_NONE;
+                       break;
+               case IEEE80211_SMPS_STATIC:
+                       smps = WMI_PEER_SMPS_STATIC;
+                       break;
+               case IEEE80211_SMPS_DYNAMIC:
+                       smps = WMI_PEER_SMPS_DYNAMIC;
+                       break;
+               case IEEE80211_SMPS_NUM_MODES:
+                       ath10k_warn("mac sta rc update for %pM: invalid smps: %d\n",
+                                   sta->addr, sta->smps_mode);
+                       smps = WMI_PEER_SMPS_PS_NONE;
+                       break;
+               }
+
+               arsta->smps = smps;
+       }
+
+       if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
+               /* FIXME: Not implemented. Probably the only way to do it would
+                * be to re-assoc the peer. */
+               changed &= ~IEEE80211_RC_SUPP_RATES_CHANGED;
+               ath10k_dbg(ATH10K_DBG_MAC,
+                          "mac sta rc update for %pM: changing supported rates not implemented\n",
+                          sta->addr);
+       }
+
+       arsta->changed |= changed;
+
+       spin_unlock_bh(&ar->data_lock);
+
+       ieee80211_queue_work(hw, &arsta->update_wk);
+}
+
 static const struct ieee80211_ops ath10k_ops = {
        .tx                             = ath10k_tx,
        .start                          = ath10k_start,
@@ -3663,6 +4083,8 @@ static const struct ieee80211_ops ath10k_ops = {
        .restart_complete               = ath10k_restart_complete,
        .get_survey                     = ath10k_get_survey,
        .set_bitrate_mask               = ath10k_set_bitrate_mask,
+       .channel_switch_beacon          = ath10k_channel_switch_beacon,
+       .sta_rc_update                  = ath10k_sta_rc_update,
 #ifdef CONFIG_PM
        .suspend                        = ath10k_suspend,
        .resume                         = ath10k_resume,
@@ -4038,10 +4460,12 @@ int ath10k_mac_register(struct ath10k *ar)
        ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN;
 
        ar->hw->vif_data_size = sizeof(struct ath10k_vif);
+       ar->hw->sta_data_size = sizeof(struct ath10k_sta);
 
        ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;
 
        ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+       ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
        ar->hw->wiphy->max_remain_on_channel_duration = 5000;
 
        ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
index 29fd197d1fd8b3b7e8da66b212d0452c4e3ec3de..34f09106f423f1bf45654468bdcdefafda81d97a 100644 (file)
@@ -64,7 +64,8 @@ static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info,
                                             int num);
 static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info);
 static void ath10k_pci_stop_ce(struct ath10k *ar);
-static int ath10k_pci_device_reset(struct ath10k *ar);
+static int ath10k_pci_cold_reset(struct ath10k *ar);
+static int ath10k_pci_warm_reset(struct ath10k *ar);
 static int ath10k_pci_wait_for_target_init(struct ath10k *ar);
 static int ath10k_pci_init_irq(struct ath10k *ar);
 static int ath10k_pci_deinit_irq(struct ath10k *ar);
@@ -833,9 +834,7 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar)
        ath10k_err("firmware crashed!\n");
        ath10k_err("hardware name %s version 0x%x\n",
                   ar->hw_params.name, ar->target_version);
-       ath10k_err("firmware version: %u.%u.%u.%u\n", ar->fw_version_major,
-                  ar->fw_version_minor, ar->fw_version_release,
-                  ar->fw_version_build);
+       ath10k_err("firmware version: %s\n", ar->hw->wiphy->fw_version);
 
        host_addr = host_interest_item_address(HI_ITEM(hi_failure_state));
        ret = ath10k_pci_diag_read_mem(ar, host_addr,
@@ -1502,7 +1501,7 @@ static void ath10k_pci_hif_stop(struct ath10k *ar)
         * configuration during init. If ringbuffers are freed and the device
         * were to access them this could lead to memory corruption on the
         * host. */
-       ath10k_pci_device_reset(ar);
+       ath10k_pci_warm_reset(ar);
 
        ar_pci->started = 0;
 }
@@ -1993,7 +1992,94 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar)
        ath10k_pci_sleep(ar);
 }
 
-static int ath10k_pci_hif_power_up(struct ath10k *ar)
+static int ath10k_pci_warm_reset(struct ath10k *ar)
+{
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       int ret = 0;
+       u32 val;
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot performing warm chip reset\n");
+
+       ret = ath10k_do_pci_wake(ar);
+       if (ret) {
+               ath10k_err("failed to wake up target: %d\n", ret);
+               return ret;
+       }
+
+       /* debug */
+       val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+                               PCIE_INTR_CAUSE_ADDRESS);
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val);
+
+       val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+                               CPU_INTR_ADDRESS);
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
+                  val);
+
+       /* disable pending irqs */
+       ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
+                          PCIE_INTR_ENABLE_ADDRESS, 0);
+
+       ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS +
+                          PCIE_INTR_CLR_ADDRESS, ~0);
+
+       msleep(100);
+
+       /* clear fw indicator */
+       ath10k_pci_write32(ar, ar_pci->fw_indicator_address, 0);
+
+       /* clear target LF timer interrupts */
+       val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+                               SOC_LF_TIMER_CONTROL0_ADDRESS);
+       ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS +
+                          SOC_LF_TIMER_CONTROL0_ADDRESS,
+                          val & ~SOC_LF_TIMER_CONTROL0_ENABLE_MASK);
+
+       /* reset CE */
+       val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+                               SOC_RESET_CONTROL_ADDRESS);
+       ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+                          val | SOC_RESET_CONTROL_CE_RST_MASK);
+       val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+                               SOC_RESET_CONTROL_ADDRESS);
+       msleep(10);
+
+       /* unreset CE */
+       ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+                          val & ~SOC_RESET_CONTROL_CE_RST_MASK);
+       val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+                               SOC_RESET_CONTROL_ADDRESS);
+       msleep(10);
+
+       /* debug */
+       val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+                               PCIE_INTR_CAUSE_ADDRESS);
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val);
+
+       val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
+                               CPU_INTR_ADDRESS);
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n",
+                  val);
+
+       /* CPU warm reset */
+       val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+                               SOC_RESET_CONTROL_ADDRESS);
+       ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS,
+                          val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK);
+
+       val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS +
+                               SOC_RESET_CONTROL_ADDRESS);
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot target reset state: 0x%08x\n", val);
+
+       msleep(100);
+
+       ath10k_dbg(ATH10K_DBG_BOOT, "boot warm reset complete\n");
+
+       ath10k_do_pci_sleep(ar);
+       return ret;
+}
+
+static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        const char *irq_mode;
@@ -2009,7 +2095,11 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar)
         * is in an unexpected state. We try to catch that here in order to
         * reset the Target and retry the probe.
         */
-       ret = ath10k_pci_device_reset(ar);
+       if (cold_reset)
+               ret = ath10k_pci_cold_reset(ar);
+       else
+               ret = ath10k_pci_warm_reset(ar);
+
        if (ret) {
                ath10k_err("failed to reset target: %d\n", ret);
                goto err;
@@ -2079,7 +2169,7 @@ err_deinit_irq:
        ath10k_pci_deinit_irq(ar);
 err_ce:
        ath10k_pci_ce_deinit(ar);
-       ath10k_pci_device_reset(ar);
+       ath10k_pci_warm_reset(ar);
 err_ps:
        if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
                ath10k_do_pci_sleep(ar);
@@ -2087,6 +2177,34 @@ err:
        return ret;
 }
 
+static int ath10k_pci_hif_power_up(struct ath10k *ar)
+{
+       int ret;
+
+       /*
+        * Hardware CUS232 version 2 has some issues with cold reset and the
+        * preferred (and safer) way to perform a device reset is through a
+        * warm reset.
+        *
+        * Warm reset doesn't always work though (notably after a firmware
+        * crash) so fall back to cold reset if necessary.
+        */
+       ret = __ath10k_pci_hif_power_up(ar, false);
+       if (ret) {
+               ath10k_warn("failed to power up target using warm reset (%d), trying cold reset\n",
+                           ret);
+
+               ret = __ath10k_pci_hif_power_up(ar, true);
+               if (ret) {
+                       ath10k_err("failed to power up target using cold reset too (%d)\n",
+                                  ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
 static void ath10k_pci_hif_power_down(struct ath10k *ar)
 {
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
@@ -2094,7 +2212,7 @@ static void ath10k_pci_hif_power_down(struct ath10k *ar)
        ath10k_pci_free_early_irq(ar);
        ath10k_pci_kill_tasklet(ar);
        ath10k_pci_deinit_irq(ar);
-       ath10k_pci_device_reset(ar);
+       ath10k_pci_warm_reset(ar);
 
        ath10k_pci_ce_deinit(ar);
        if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
@@ -2411,11 +2529,10 @@ static int ath10k_pci_init_irq(struct ath10k *ar)
        /* Try MSI-X */
        if (ath10k_pci_irq_mode == ATH10K_PCI_IRQ_AUTO && msix_supported) {
                ar_pci->num_msi_intrs = MSI_NUM_REQUEST;
-               ret = pci_enable_msi_block(ar_pci->pdev, ar_pci->num_msi_intrs);
-               if (ret == 0)
-                       return 0;
+               ret = pci_enable_msi_range(ar_pci->pdev, ar_pci->num_msi_intrs,
+                                                        ar_pci->num_msi_intrs);
                if (ret > 0)
-                       pci_disable_msi(ar_pci->pdev);
+                       return 0;
 
                /* fall-through */
        }
@@ -2482,6 +2599,8 @@ static int ath10k_pci_deinit_irq(struct ath10k *ar)
        case MSI_NUM_REQUEST:
                pci_disable_msi(ar_pci->pdev);
                return 0;
+       default:
+               pci_disable_msi(ar_pci->pdev);
        }
 
        ath10k_warn("unknown irq configuration upon deinit\n");
@@ -2523,7 +2642,7 @@ out:
        return ret;
 }
 
-static int ath10k_pci_device_reset(struct ath10k *ar)
+static int ath10k_pci_cold_reset(struct ath10k *ar)
 {
        int i, ret;
        u32 val;
index 27f20e0510f7a1dc0574d746f90e3bcaa624f99c..ec6f82521b0ea6eb69e255d1b97bb6f99bfb78d7 100644 (file)
@@ -259,7 +259,7 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
        status->freq = ch->center_freq;
 
        ath10k_dbg(ATH10K_DBG_DATA,
-                  "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u\n",
+                  "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i\n",
                   info->skb,
                   info->skb->len,
                   status->flag == 0 ? "legacy" : "",
@@ -271,7 +271,7 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
                   status->rate_idx,
                   status->vht_nss,
                   status->freq,
-                  status->band);
+                  status->band, status->flag, info->fcs_err);
        ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ",
                        info->skb->data, info->skb->len);
 
index 712a606a080a02617fe839737ce4c2f5084a5881..91e501b5499ef0497b3bb8b22e718c691d00f315 100644 (file)
@@ -213,7 +213,7 @@ static struct wmi_cmd_map wmi_10x_cmd_map = {
        .p2p_go_set_beacon_ie = WMI_10X_P2P_GO_SET_BEACON_IE,
        .p2p_go_set_probe_resp_ie = WMI_10X_P2P_GO_SET_PROBE_RESP_IE,
        .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED,
-       .ap_ps_peer_param_cmdid = WMI_CMD_UNSUPPORTED,
+       .ap_ps_peer_param_cmdid = WMI_10X_AP_PS_PEER_PARAM_CMDID,
        .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED,
        .peer_rate_retry_sched_cmdid = WMI_10X_PEER_RATE_RETRY_SCHED_CMDID,
        .wlan_profile_trigger_cmdid = WMI_10X_WLAN_PROFILE_TRIGGER_CMDID,
@@ -420,7 +420,6 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = {
        .bcnflt_stats_update_period = WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
        .pmf_qos = WMI_PDEV_PARAM_PMF_QOS,
        .arp_ac_override = WMI_PDEV_PARAM_ARP_AC_OVERRIDE,
-       .arpdhcp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED,
        .dcs = WMI_PDEV_PARAM_DCS,
        .ani_enable = WMI_PDEV_PARAM_ANI_ENABLE,
        .ani_poll_period = WMI_PDEV_PARAM_ANI_POLL_PERIOD,
@@ -472,8 +471,7 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = {
        .bcnflt_stats_update_period =
                                WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD,
        .pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS,
-       .arp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED,
-       .arpdhcp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE,
+       .arp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE,
        .dcs = WMI_10X_PDEV_PARAM_DCS,
        .ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE,
        .ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD,
@@ -561,7 +559,6 @@ err_pull:
 
 static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
 {
-       struct wmi_bcn_tx_arg arg = {0};
        int ret;
 
        lockdep_assert_held(&arvif->ar->data_lock);
@@ -569,18 +566,16 @@ static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif)
        if (arvif->beacon == NULL)
                return;
 
-       arg.vdev_id = arvif->vdev_id;
-       arg.tx_rate = 0;
-       arg.tx_power = 0;
-       arg.bcn = arvif->beacon->data;
-       arg.bcn_len = arvif->beacon->len;
+       if (arvif->beacon_sent)
+               return;
 
-       ret = ath10k_wmi_beacon_send_nowait(arvif->ar, &arg);
+       ret = ath10k_wmi_beacon_send_ref_nowait(arvif);
        if (ret)
                return;
 
-       dev_kfree_skb_any(arvif->beacon);
-       arvif->beacon = NULL;
+       /* We need to retain the arvif->beacon reference for DMA unmapping and
+        * freeing the skbuff later. */
+       arvif->beacon_sent = true;
 }
 
 static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac,
@@ -1116,7 +1111,27 @@ static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar,
 static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar,
                                              struct sk_buff *skb)
 {
-       ath10k_dbg(ATH10K_DBG_WMI, "WMI_PEER_STA_KICKOUT_EVENTID\n");
+       struct wmi_peer_sta_kickout_event *ev;
+       struct ieee80211_sta *sta;
+
+       ev = (struct wmi_peer_sta_kickout_event *)skb->data;
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi event peer sta kickout %pM\n",
+                  ev->peer_macaddr.addr);
+
+       rcu_read_lock();
+
+       sta = ieee80211_find_sta_by_ifaddr(ar->hw, ev->peer_macaddr.addr, NULL);
+       if (!sta) {
+               ath10k_warn("Spurious quick kickout for STA %pM\n",
+                           ev->peer_macaddr.addr);
+               goto exit;
+       }
+
+       ieee80211_report_low_ack(sta, 10);
+
+exit:
+       rcu_read_unlock();
 }
 
 /*
@@ -1217,6 +1232,13 @@ static void ath10k_wmi_update_tim(struct ath10k *ar,
        tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast);
        memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len);
 
+       if (tim->dtim_count == 0) {
+               ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true;
+
+               if (__le32_to_cpu(bcn_info->tim_info.tim_mcast) == 1)
+                       ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true;
+       }
+
        ath10k_dbg(ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n",
                   tim->dtim_count, tim->dtim_period,
                   tim->bitmap_ctrl, pvm_len);
@@ -1385,6 +1407,17 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
                        continue;
                }
 
+               /* There are no completions for beacons so wait for next SWBA
+                * before telling mac80211 to decrement CSA counter
+                *
+                * Once CSA counter is completed stop sending beacons until
+                * actual channel switch is done */
+               if (arvif->vif->csa_active &&
+                   ieee80211_csa_is_complete(arvif->vif)) {
+                       ieee80211_csa_finish(arvif->vif);
+                       continue;
+               }
+
                bcn = ieee80211_beacon_get(ar->hw, arvif->vif);
                if (!bcn) {
                        ath10k_warn("could not get mac80211 beacon\n");
@@ -1396,13 +1429,20 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb)
                ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info);
 
                spin_lock_bh(&ar->data_lock);
+
                if (arvif->beacon) {
-                       ath10k_warn("SWBA overrun on vdev %d\n",
-                                   arvif->vdev_id);
+                       if (!arvif->beacon_sent)
+                               ath10k_warn("SWBA overrun on vdev %d\n",
+                                           arvif->vdev_id);
+
+                       ath10k_skb_unmap(ar->dev, arvif->beacon);
                        dev_kfree_skb_any(arvif->beacon);
                }
 
+               ath10k_skb_map(ar->dev, bcn);
+
                arvif->beacon = bcn;
+               arvif->beacon_sent = false;
 
                ath10k_wmi_tx_beacon_nowait(arvif);
                spin_unlock_bh(&ar->data_lock);
@@ -2031,11 +2071,11 @@ static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb)
        memcpy(ar->mac_addr, ev->mac_addr.addr, ETH_ALEN);
 
        ath10k_dbg(ATH10K_DBG_WMI,
-                  "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n",
+                  "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d skb->len %i ev-sz %zu\n",
                   __le32_to_cpu(ev->sw_version),
                   __le32_to_cpu(ev->abi_version),
                   ev->mac_addr.addr,
-                  __le32_to_cpu(ev->status));
+                  __le32_to_cpu(ev->status), skb->len, sizeof(*ev));
 
        complete(&ar->wmi.unified_ready);
        return 0;
@@ -2403,7 +2443,7 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
                                   ar->wmi.cmd->pdev_set_channel_cmdid);
 }
 
-int ath10k_wmi_pdev_suspend_target(struct ath10k *ar)
+int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt)
 {
        struct wmi_pdev_suspend_cmd *cmd;
        struct sk_buff *skb;
@@ -2413,7 +2453,7 @@ int ath10k_wmi_pdev_suspend_target(struct ath10k *ar)
                return -ENOMEM;
 
        cmd = (struct wmi_pdev_suspend_cmd *)skb->data;
-       cmd->suspend_opt = WMI_PDEV_SUSPEND;
+       cmd->suspend_opt = __cpu_to_le32(suspend_opt);
 
        return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid);
 }
@@ -3411,25 +3451,41 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar,
        return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid);
 }
 
-int ath10k_wmi_beacon_send_nowait(struct ath10k *ar,
-                                 const struct wmi_bcn_tx_arg *arg)
+/* This function assumes the beacon is already DMA mapped */
+int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif)
 {
-       struct wmi_bcn_tx_cmd *cmd;
+       struct wmi_bcn_tx_ref_cmd *cmd;
        struct sk_buff *skb;
+       struct sk_buff *beacon = arvif->beacon;
+       struct ath10k *ar = arvif->ar;
+       struct ieee80211_hdr *hdr;
        int ret;
+       u16 fc;
 
-       skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->bcn_len);
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
        if (!skb)
                return -ENOMEM;
 
-       cmd = (struct wmi_bcn_tx_cmd *)skb->data;
-       cmd->hdr.vdev_id  = __cpu_to_le32(arg->vdev_id);
-       cmd->hdr.tx_rate  = __cpu_to_le32(arg->tx_rate);
-       cmd->hdr.tx_power = __cpu_to_le32(arg->tx_power);
-       cmd->hdr.bcn_len  = __cpu_to_le32(arg->bcn_len);
-       memcpy(cmd->bcn, arg->bcn, arg->bcn_len);
+       hdr = (struct ieee80211_hdr *)beacon->data;
+       fc = le16_to_cpu(hdr->frame_control);
+
+       cmd = (struct wmi_bcn_tx_ref_cmd *)skb->data;
+       cmd->vdev_id = __cpu_to_le32(arvif->vdev_id);
+       cmd->data_len = __cpu_to_le32(beacon->len);
+       cmd->data_ptr = __cpu_to_le32(ATH10K_SKB_CB(beacon)->paddr);
+       cmd->msdu_id = 0;
+       cmd->frame_control = __cpu_to_le32(fc);
+       cmd->flags = 0;
+
+       if (ATH10K_SKB_CB(beacon)->bcn.dtim_zero)
+               cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO);
+
+       if (ATH10K_SKB_CB(beacon)->bcn.deliver_cab)
+               cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB);
+
+       ret = ath10k_wmi_cmd_send_nowait(ar, skb,
+                                        ar->wmi.cmd->pdev_send_bcn_cmdid);
 
-       ret = ath10k_wmi_cmd_send_nowait(ar, skb, ar->wmi.cmd->bcn_tx_cmdid);
        if (ret)
                dev_kfree_skb(skb);
 
index 4b5e7d3d32b62ff221992c6175db1d53f718b91e..4fcc96aa9513b89a45e11fd9c2d8da79414204c9 100644 (file)
@@ -2277,7 +2277,6 @@ struct wmi_pdev_param_map {
        u32 bcnflt_stats_update_period;
        u32 pmf_qos;
        u32 arp_ac_override;
-       u32 arpdhcp_ac_override;
        u32 dcs;
        u32 ani_enable;
        u32 ani_poll_period;
@@ -3403,6 +3402,24 @@ struct wmi_bcn_tx_arg {
        const void *bcn;
 };
 
+enum wmi_bcn_tx_ref_flags {
+       WMI_BCN_TX_REF_FLAG_DTIM_ZERO = 0x1,
+       WMI_BCN_TX_REF_FLAG_DELIVER_CAB = 0x2,
+};
+
+struct wmi_bcn_tx_ref_cmd {
+       __le32 vdev_id;
+       __le32 data_len;
+       /* physical address of the frame - dma pointer */
+       __le32 data_ptr;
+       /* id for host to track */
+       __le32 msdu_id;
+       /* frame ctrl to setup PPDU desc */
+       __le32 frame_control;
+       /* to control CABQ traffic: WMI_BCN_TX_REF_FLAG_ */
+       __le32 flags;
+} __packed;
+
 /* Beacon filter */
 #define WMI_BCN_FILTER_ALL   0 /* Filter all beacons */
 #define WMI_BCN_FILTER_NONE  1 /* Pass all beacons */
@@ -3859,6 +3876,12 @@ enum wmi_peer_smps_state {
        WMI_PEER_SMPS_DYNAMIC = 0x2
 };
 
+enum wmi_peer_chwidth {
+       WMI_PEER_CHWIDTH_20MHZ = 0,
+       WMI_PEER_CHWIDTH_40MHZ = 1,
+       WMI_PEER_CHWIDTH_80MHZ = 2,
+};
+
 enum wmi_peer_param {
        WMI_PEER_SMPS_STATE = 0x1, /* see %wmi_peer_smps_state */
        WMI_PEER_AMPDU      = 0x2,
@@ -4039,6 +4062,10 @@ struct wmi_chan_info_event {
        __le32 cycle_count;
 } __packed;
 
+struct wmi_peer_sta_kickout_event {
+       struct wmi_mac_addr peer_macaddr;
+} __packed;
+
 #define WMI_CHAN_INFO_FLAG_COMPLETE BIT(0)
 
 /* FIXME: empirically extrapolated */
@@ -4172,7 +4199,7 @@ int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar);
 int ath10k_wmi_connect_htc_service(struct ath10k *ar);
 int ath10k_wmi_pdev_set_channel(struct ath10k *ar,
                                const struct wmi_channel_arg *);
-int ath10k_wmi_pdev_suspend_target(struct ath10k *ar);
+int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt);
 int ath10k_wmi_pdev_resume_target(struct ath10k *ar);
 int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g,
                                  u16 rd5g, u16 ctl2g, u16 ctl5g);
@@ -4219,8 +4246,7 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac,
                               enum wmi_ap_ps_peer_param param_id, u32 value);
 int ath10k_wmi_scan_chan_list(struct ath10k *ar,
                              const struct wmi_scan_chan_list_arg *arg);
-int ath10k_wmi_beacon_send_nowait(struct ath10k *ar,
-                                 const struct wmi_bcn_tx_arg *arg);
+int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif);
 int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar,
                        const struct wmi_pdev_set_wmm_params_arg *arg);
 int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
index f38ff6a6255e7be58c4687a2ff85115c8250225c..56c3fd5cef65a07e915d63d8c0dc24727a87406b 100644 (file)
@@ -24,7 +24,7 @@
 /* constants */
 #define TX_URB_COUNT            32
 #define RX_URB_COUNT            32
-#define ATH6KL_USB_RX_BUFFER_SIZE  1700
+#define ATH6KL_USB_RX_BUFFER_SIZE  4096
 
 /* tx/rx pipes for usb */
 enum ATH6KL_USB_PIPE_ID {
@@ -481,8 +481,8 @@ static void ath6kl_usb_start_recv_pipes(struct ath6kl_usb *ar_usb)
         *              ATH6KL_USB_RX_BUFFER_SIZE);
         */
 
-       ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_cnt_thresh =
-           ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_alloc / 2;
+       ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_cnt_thresh = 1;
+
        ath6kl_usb_post_recv_transfers(&ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA],
                                       ATH6KL_USB_RX_BUFFER_SIZE);
 }
index 4f16d79c9eb187566ff878e9ac42c163cd58312a..8b4ce28e3ce8f51f7cda070364394a117d5aef66 100644 (file)
@@ -914,7 +914,7 @@ ath6kl_get_regpair(u16 regdmn)
                return NULL;
 
        for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
-               if (regDomainPairs[i].regDmnEnum == regdmn)
+               if (regDomainPairs[i].reg_domain == regdmn)
                        return &regDomainPairs[i];
        }
 
@@ -954,7 +954,7 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len)
                country = ath6kl_regd_find_country_by_rd((u16) reg_code);
                if (regpair)
                        ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n",
-                                  regpair->regDmnEnum);
+                                  regpair->reg_domain);
                else
                        ath6kl_warn("Regpair not found reg_code 0x%0x\n",
                                    reg_code);
index 25243cbc07f0ba4ed9c27a6779ff2ea3be7be77d..b8daff78b9d124ed8790223d49ee8871d831b4f1 100644 (file)
@@ -5065,6 +5065,10 @@ static u16 ar9003_hw_get_max_edge_power(struct ar9300_eeprom *eep,
                        break;
                }
        }
+
+       if (is2GHz && !twiceMaxEdgePower)
+               twiceMaxEdgePower = 60;
+
        return twiceMaxEdgePower;
 }
 
index c75493f4236a652bf9f0109c35cb8fdca411715b..ba83f582bf4a71c74b676a38f82e209125d1cec2 100644 (file)
@@ -262,6 +262,8 @@ enum tid_aggr_state {
 struct ath9k_htc_sta {
        u8 index;
        enum tid_aggr_state tid_state[ATH9K_HTC_MAX_TID];
+       struct work_struct rc_update_work;
+       struct ath9k_htc_priv *htc_priv;
 };
 
 #define ATH9K_HTC_RXBUF 256
index 9db8aefb86006d38560236ee4c1a79cdb6a29105..8d0b9bcb47b465059399bfaea0589406751e906b 100644 (file)
@@ -34,6 +34,10 @@ static int ath9k_htc_btcoex_enable;
 module_param_named(btcoex_enable, ath9k_htc_btcoex_enable, int, 0444);
 MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence");
 
+static int ath9k_ps_enable;
+module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
+MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
+
 #define CHAN2G(_freq, _idx)  { \
        .center_freq = (_freq), \
        .hw_value = (_idx), \
@@ -726,12 +730,14 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
                IEEE80211_HW_SPECTRUM_MGMT |
                IEEE80211_HW_HAS_RATE_CONTROL |
                IEEE80211_HW_RX_INCLUDES_FCS |
-               IEEE80211_HW_SUPPORTS_PS |
                IEEE80211_HW_PS_NULLFUNC_STACK |
                IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                IEEE80211_HW_MFP_CAPABLE |
                IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING;
 
+       if (ath9k_ps_enable)
+               hw->flags |= IEEE80211_HW_SUPPORTS_PS;
+
        hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_STATION) |
                BIT(NL80211_IFTYPE_ADHOC) |
index 228549a65ab9d17de7fd83f40777abc0975d86db..90dad4172b0a04a1c61ce7805f1c07e7ed146bd7 100644 (file)
@@ -1270,18 +1270,50 @@ static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
        mutex_unlock(&priv->mutex);
 }
 
+static void ath9k_htc_sta_rc_update_work(struct work_struct *work)
+{
+       struct ath9k_htc_sta *ista =
+           container_of(work, struct ath9k_htc_sta, rc_update_work);
+       struct ieee80211_sta *sta =
+           container_of((void *)ista, struct ieee80211_sta, drv_priv);
+       struct ath9k_htc_priv *priv = ista->htc_priv;
+       struct ath_common *common = ath9k_hw_common(priv->ah);
+       struct ath9k_htc_target_rate trate;
+
+       mutex_lock(&priv->mutex);
+       ath9k_htc_ps_wakeup(priv);
+
+       memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
+       ath9k_htc_setup_rate(priv, sta, &trate);
+       if (!ath9k_htc_send_rate_cmd(priv, &trate))
+               ath_dbg(common, CONFIG,
+                       "Supported rates for sta: %pM updated, rate caps: 0x%X\n",
+                       sta->addr, be32_to_cpu(trate.capflags));
+       else
+               ath_dbg(common, CONFIG,
+                       "Unable to update supported rates for sta: %pM\n",
+                       sta->addr);
+
+       ath9k_htc_ps_restore(priv);
+       mutex_unlock(&priv->mutex);
+}
+
 static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
                             struct ieee80211_vif *vif,
                             struct ieee80211_sta *sta)
 {
        struct ath9k_htc_priv *priv = hw->priv;
+       struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
        int ret;
 
        mutex_lock(&priv->mutex);
        ath9k_htc_ps_wakeup(priv);
        ret = ath9k_htc_add_station(priv, vif, sta);
-       if (!ret)
+       if (!ret) {
+               INIT_WORK(&ista->rc_update_work, ath9k_htc_sta_rc_update_work);
+               ista->htc_priv = priv;
                ath9k_htc_init_rate(priv, sta);
+       }
        ath9k_htc_ps_restore(priv);
        mutex_unlock(&priv->mutex);
 
@@ -1293,12 +1325,13 @@ static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
                                struct ieee80211_sta *sta)
 {
        struct ath9k_htc_priv *priv = hw->priv;
-       struct ath9k_htc_sta *ista;
+       struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
        int ret;
 
+       cancel_work_sync(&ista->rc_update_work);
+
        mutex_lock(&priv->mutex);
        ath9k_htc_ps_wakeup(priv);
-       ista = (struct ath9k_htc_sta *) sta->drv_priv;
        htc_sta_drain(priv->htc, ista->index);
        ret = ath9k_htc_remove_station(priv, vif, sta);
        ath9k_htc_ps_restore(priv);
@@ -1311,28 +1344,12 @@ static void ath9k_htc_sta_rc_update(struct ieee80211_hw *hw,
                                    struct ieee80211_vif *vif,
                                    struct ieee80211_sta *sta, u32 changed)
 {
-       struct ath9k_htc_priv *priv = hw->priv;
-       struct ath_common *common = ath9k_hw_common(priv->ah);
-       struct ath9k_htc_target_rate trate;
-
-       mutex_lock(&priv->mutex);
-       ath9k_htc_ps_wakeup(priv);
+       struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
 
-       if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) {
-               memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
-               ath9k_htc_setup_rate(priv, sta, &trate);
-               if (!ath9k_htc_send_rate_cmd(priv, &trate))
-                       ath_dbg(common, CONFIG,
-                               "Supported rates for sta: %pM updated, rate caps: 0x%X\n",
-                               sta->addr, be32_to_cpu(trate.capflags));
-               else
-                       ath_dbg(common, CONFIG,
-                               "Unable to update supported rates for sta: %pM\n",
-                               sta->addr);
-       }
+       if (!(changed & IEEE80211_RC_SUPP_RATES_CHANGED))
+               return;
 
-       ath9k_htc_ps_restore(priv);
-       mutex_unlock(&priv->mutex);
+       schedule_work(&ista->rc_update_work);
 }
 
 static int ath9k_htc_conf_tx(struct ieee80211_hw *hw,
index 15b8e783d1a7dd472bea73e3874212ce99043b01..5db01b4212c8201383473bfdf886137f02b41491 100644 (file)
@@ -1315,7 +1315,7 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
        if (AR_SREV_9300_20_OR_LATER(ah))
                udelay(50);
        else if (AR_SREV_9100(ah))
-               udelay(10000);
+               mdelay(10);
        else
                udelay(100);
 
@@ -2050,9 +2050,8 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah)
 
        REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
                    AR_RTC_FORCE_WAKE_EN);
-
        if (AR_SREV_9100(ah))
-               udelay(10000);
+               mdelay(10);
        else
                udelay(50);
 
index 67411d21c9a5a1c965f68582bccca058de0aa383..07a0315dd2f64baff254e18da191c273c32e4173 100644 (file)
@@ -57,6 +57,10 @@ static int ath9k_bt_ant_diversity;
 module_param_named(bt_ant_diversity, ath9k_bt_ant_diversity, int, 0444);
 MODULE_PARM_DESC(bt_ant_diversity, "Enable WLAN/BT RX antenna diversity");
 
+static int ath9k_ps_enable;
+module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
+MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
+
 bool is_ath9k_unloaded;
 /* We use the hw_value as an index into our private channel structure */
 
@@ -903,13 +907,15 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
        hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
                IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
                IEEE80211_HW_SIGNAL_DBM |
-               IEEE80211_HW_SUPPORTS_PS |
                IEEE80211_HW_PS_NULLFUNC_STACK |
                IEEE80211_HW_SPECTRUM_MGMT |
                IEEE80211_HW_REPORTS_TX_ACK_STATUS |
                IEEE80211_HW_SUPPORTS_RC_TABLE |
                IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
 
+       if (ath9k_ps_enable)
+               hw->flags |= IEEE80211_HW_SUPPORTS_PS;
+
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
                hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION;
 
index e5e905910db49c569c8287de38141fc4a4b3d244..415393dfb6fc17b51d33f4d24e70c6700620d4b5 100644 (file)
@@ -222,7 +222,7 @@ static const struct ieee80211_regdomain *ath_default_world_regdomain(void)
 static const struct
 ieee80211_regdomain *ath_world_regdomain(struct ath_regulatory *reg)
 {
-       switch (reg->regpair->regDmnEnum) {
+       switch (reg->regpair->reg_domain) {
        case 0x60:
        case 0x61:
        case 0x62:
@@ -431,7 +431,7 @@ static void ath_reg_apply_world_flags(struct wiphy *wiphy,
                                      enum nl80211_reg_initiator initiator,
                                      struct ath_regulatory *reg)
 {
-       switch (reg->regpair->regDmnEnum) {
+       switch (reg->regpair->reg_domain) {
        case 0x60:
        case 0x63:
        case 0x66:
@@ -560,7 +560,7 @@ static bool ath_regd_is_eeprom_valid(struct ath_regulatory *reg)
                        printk(KERN_DEBUG "ath: EEPROM indicates we "
                               "should expect a direct regpair map\n");
                for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++)
-                       if (regDomainPairs[i].regDmnEnum == rd)
+                       if (regDomainPairs[i].reg_domain == rd)
                                return true;
        }
        printk(KERN_DEBUG
@@ -617,7 +617,7 @@ ath_get_regpair(int regdmn)
        if (regdmn == NO_ENUMRD)
                return NULL;
        for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) {
-               if (regDomainPairs[i].regDmnEnum == regdmn)
+               if (regDomainPairs[i].reg_domain == regdmn)
                        return &regDomainPairs[i];
        }
        return NULL;
@@ -741,7 +741,7 @@ static int __ath_regd_init(struct ath_regulatory *reg)
        printk(KERN_DEBUG "ath: Country alpha2 being used: %c%c\n",
                reg->alpha2[0], reg->alpha2[1]);
        printk(KERN_DEBUG "ath: Regpair used: 0x%0x\n",
-               reg->regpair->regDmnEnum);
+               reg->regpair->reg_domain);
 
        return 0;
 }
index ee25786b44478fd094df1c3b165c9caf0d0bc73b..73f12f196f14ec6ee14368dee20673f67b8045a3 100644 (file)
@@ -44,6 +44,14 @@ static void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data)
        writel(data, wcn->mmio + addr);
 }
 
+#define wcn36xx_dxe_write_register_x(wcn, reg, reg_data)                \
+do {                                                                    \
+       if (wcn->chip_version == WCN36XX_CHIP_3680)                      \
+               wcn36xx_dxe_write_register(wcn, reg ## _3680, reg_data); \
+       else                                                             \
+               wcn36xx_dxe_write_register(wcn, reg ## _3660, reg_data); \
+} while (0)                                                             \
+
 static void wcn36xx_dxe_read_register(struct wcn36xx *wcn, int addr, int *data)
 {
        *data = readl(wcn->mmio + addr);
@@ -680,7 +688,7 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn)
 
        /* Setting interrupt path */
        reg_data = WCN36XX_DXE_CCU_INT;
-       wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data);
+       wcn36xx_dxe_write_register_x(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data);
 
        /***************************************/
        /* Init descriptors for TX LOW channel */
index c88562f85de1c21a30c80a884cc36f85c7c6cfc9..35ee7e966bd2789f8ea9bb283598d13be4b35e62 100644 (file)
@@ -28,11 +28,11 @@ H2H_TEST_RX_TX = DMA2
 */
 
 /* DXE registers */
-#define WCN36XX_DXE_MEM_BASE                   0x03000000
 #define WCN36XX_DXE_MEM_REG                    0x202000
 
 #define WCN36XX_DXE_CCU_INT                    0xA0011
-#define WCN36XX_DXE_REG_CCU_INT                        0x200b10
+#define WCN36XX_DXE_REG_CCU_INT_3660           0x200b10
+#define WCN36XX_DXE_REG_CCU_INT_3680           0x2050dc
 
 /* TODO This must calculated properly but not hardcoded */
 #define WCN36XX_DXE_CTRL_TX_L                  0x328a44
index 3c2ef0c32f72c7ef37b9b032e3b66ec3ab9619d6..a1f1127d7808d739e4f6571fa0f071491ca289bc 100644 (file)
@@ -4384,11 +4384,13 @@ enum place_holder_in_cap_bitmap {
        MAX_FEATURE_SUPPORTED = 128,
 };
 
+#define WCN36XX_HAL_CAPS_SIZE 4
+
 struct wcn36xx_hal_feat_caps_msg {
 
        struct wcn36xx_hal_msg_header header;
 
-       u32 feat_caps[4];
+       u32 feat_caps[WCN36XX_HAL_CAPS_SIZE];
 } __packed;
 
 /* status codes to help debug rekey failures */
index e64a6784079e20446ad57078f607e0f01b7565a1..4ab5370ab7a6ff5df3d5e4b1525b7f852cc55cb6 100644 (file)
@@ -17,6 +17,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/module.h>
+#include <linux/firmware.h>
 #include <linux/platform_device.h>
 #include "wcn36xx.h"
 
@@ -177,6 +178,60 @@ static inline u8 get_sta_index(struct ieee80211_vif *vif,
               sta_priv->sta_index;
 }
 
+static const char * const wcn36xx_caps_names[] = {
+       "MCC",                          /* 0 */
+       "P2P",                          /* 1 */
+       "DOT11AC",                      /* 2 */
+       "SLM_SESSIONIZATION",           /* 3 */
+       "DOT11AC_OPMODE",               /* 4 */
+       "SAP32STA",                     /* 5 */
+       "TDLS",                         /* 6 */
+       "P2P_GO_NOA_DECOUPLE_INIT_SCAN",/* 7 */
+       "WLANACTIVE_OFFLOAD",           /* 8 */
+       "BEACON_OFFLOAD",               /* 9 */
+       "SCAN_OFFLOAD",                 /* 10 */
+       "ROAM_OFFLOAD",                 /* 11 */
+       "BCN_MISS_OFFLOAD",             /* 12 */
+       "STA_POWERSAVE",                /* 13 */
+       "STA_ADVANCED_PWRSAVE",         /* 14 */
+       "AP_UAPSD",                     /* 15 */
+       "AP_DFS",                       /* 16 */
+       "BLOCKACK",                     /* 17 */
+       "PHY_ERR",                      /* 18 */
+       "BCN_FILTER",                   /* 19 */
+       "RTT",                          /* 20 */
+       "RATECTRL",                     /* 21 */
+       "WOW"                           /* 22 */
+};
+
+static const char *wcn36xx_get_cap_name(enum place_holder_in_cap_bitmap x)
+{
+       if (x >= ARRAY_SIZE(wcn36xx_caps_names))
+               return "UNKNOWN";
+       return wcn36xx_caps_names[x];
+}
+
+static void wcn36xx_feat_caps_info(struct wcn36xx *wcn)
+{
+       int i;
+
+       for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) {
+               if (get_feat_caps(wcn->fw_feat_caps, i))
+                       wcn36xx_info("FW Cap %s\n", wcn36xx_get_cap_name(i));
+       }
+}
+
+static void wcn36xx_detect_chip_version(struct wcn36xx *wcn)
+{
+       if (get_feat_caps(wcn->fw_feat_caps, DOT11AC)) {
+               wcn36xx_info("Chip is 3680\n");
+               wcn->chip_version = WCN36XX_CHIP_3680;
+       } else {
+               wcn36xx_info("Chip is 3660\n");
+               wcn->chip_version = WCN36XX_CHIP_3660;
+       }
+}
+
 static int wcn36xx_start(struct ieee80211_hw *hw)
 {
        struct wcn36xx *wcn = hw->priv;
@@ -223,6 +278,16 @@ static int wcn36xx_start(struct ieee80211_hw *hw)
                goto out_free_smd_buf;
        }
 
+       if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
+               ret = wcn36xx_smd_feature_caps_exchange(wcn);
+               if (ret)
+                       wcn36xx_warn("Exchange feature caps failed\n");
+               else
+                       wcn36xx_feat_caps_info(wcn);
+       }
+
+       wcn36xx_detect_chip_version(wcn);
+
        /* DMA channel initialization */
        ret = wcn36xx_dxe_init(wcn);
        if (ret) {
@@ -232,11 +297,6 @@ static int wcn36xx_start(struct ieee80211_hw *hw)
 
        wcn36xx_debugfs_init(wcn);
 
-       if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) {
-               ret = wcn36xx_smd_feature_caps_exchange(wcn);
-               if (ret)
-                       wcn36xx_warn("Exchange feature caps failed\n");
-       }
        INIT_LIST_HEAD(&wcn->vif_list);
        return 0;
 
@@ -648,6 +708,7 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
                            bss_conf->enable_beacon);
 
                if (bss_conf->enable_beacon) {
+                       vif_priv->dtim_period = bss_conf->dtim_period;
                        vif_priv->bss_index = 0xff;
                        wcn36xx_smd_config_bss(wcn, vif, NULL,
                                               vif->addr, false);
@@ -992,6 +1053,7 @@ static int wcn36xx_remove(struct platform_device *pdev)
        struct wcn36xx *wcn = hw->priv;
        wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n");
 
+       release_firmware(wcn->nv);
        mutex_destroy(&wcn->hal_mutex);
 
        ieee80211_unregister_hw(hw);
index 750626b0e22d528d6d76fbe4ad402a2c175316be..7bf0ef8a1f56f176e14cf0a7905410a0593362a3 100644 (file)
@@ -195,9 +195,11 @@ static void wcn36xx_smd_set_sta_params(struct wcn36xx *wcn,
 static int wcn36xx_smd_send_and_wait(struct wcn36xx *wcn, size_t len)
 {
        int ret = 0;
+       unsigned long start;
        wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "HAL >>> ", wcn->hal_buf, len);
 
        init_completion(&wcn->hal_rsp_compl);
+       start = jiffies;
        ret = wcn->ctrl_ops->tx(wcn->hal_buf, len);
        if (ret) {
                wcn36xx_err("HAL TX failed\n");
@@ -205,10 +207,13 @@ static int wcn36xx_smd_send_and_wait(struct wcn36xx *wcn, size_t len)
        }
        if (wait_for_completion_timeout(&wcn->hal_rsp_compl,
                msecs_to_jiffies(HAL_MSG_TIMEOUT)) <= 0) {
-               wcn36xx_err("Timeout while waiting SMD response\n");
+               wcn36xx_err("Timeout! No SMD response in %dms\n",
+                           HAL_MSG_TIMEOUT);
                ret = -ETIME;
                goto out;
        }
+       wcn36xx_dbg(WCN36XX_DBG_SMD, "SMD command completed in %dms",
+                   jiffies_to_msecs(jiffies - start));
 out:
        return ret;
 }
@@ -246,21 +251,22 @@ static int wcn36xx_smd_rsp_status_check(void *buf, size_t len)
 
 int wcn36xx_smd_load_nv(struct wcn36xx *wcn)
 {
-       const struct firmware *nv;
        struct nv_data *nv_d;
        struct wcn36xx_hal_nv_img_download_req_msg msg_body;
        int fw_bytes_left;
        int ret;
        u16 fm_offset = 0;
 
-       ret = request_firmware(&nv, WLAN_NV_FILE, wcn->dev);
-       if (ret) {
-               wcn36xx_err("Failed to load nv file %s: %d\n",
-                             WLAN_NV_FILE, ret);
-               goto out_free_nv;
+       if (!wcn->nv) {
+               ret = request_firmware(&wcn->nv, WLAN_NV_FILE, wcn->dev);
+               if (ret) {
+                       wcn36xx_err("Failed to load nv file %s: %d\n",
+                                     WLAN_NV_FILE, ret);
+                       goto out;
+               }
        }
 
-       nv_d = (struct nv_data *)nv->data;
+       nv_d = (struct nv_data *)wcn->nv->data;
        INIT_HAL_MSG(msg_body, WCN36XX_HAL_DOWNLOAD_NV_REQ);
 
        msg_body.header.len += WCN36XX_NV_FRAGMENT_SIZE;
@@ -270,7 +276,7 @@ int wcn36xx_smd_load_nv(struct wcn36xx *wcn)
        mutex_lock(&wcn->hal_mutex);
 
        do {
-               fw_bytes_left = nv->size - fm_offset - 4;
+               fw_bytes_left = wcn->nv->size - fm_offset - 4;
                if (fw_bytes_left > WCN36XX_NV_FRAGMENT_SIZE) {
                        msg_body.last_fragment = 0;
                        msg_body.nv_img_buffer_size = WCN36XX_NV_FRAGMENT_SIZE;
@@ -308,10 +314,7 @@ int wcn36xx_smd_load_nv(struct wcn36xx *wcn)
 
 out_unlock:
        mutex_unlock(&wcn->hal_mutex);
-out_free_nv:
-       release_firmware(nv);
-
-       return ret;
+out:   return ret;
 }
 
 static int wcn36xx_smd_start_rsp(struct wcn36xx *wcn, void *buf, size_t len)
@@ -899,11 +902,12 @@ static int wcn36xx_smd_config_sta_rsp(struct wcn36xx *wcn,
 
        sta_priv->sta_index = params->sta_index;
        sta_priv->dpu_desc_index = params->dpu_index;
+       sta_priv->ucast_dpu_sign = params->uc_ucast_sig;
 
        wcn36xx_dbg(WCN36XX_DBG_HAL,
-                   "hal config sta rsp status %d sta_index %d bssid_index %d p2p %d\n",
+                   "hal config sta rsp status %d sta_index %d bssid_index %d uc_ucast_sig %d p2p %d\n",
                    params->status, params->sta_index, params->bssid_index,
-                   params->p2p);
+                   params->uc_ucast_sig, params->p2p);
 
        return 0;
 }
@@ -1118,7 +1122,7 @@ static int wcn36xx_smd_config_bss_rsp(struct wcn36xx *wcn,
                priv_vif->sta->bss_dpu_desc_index = params->dpu_desc_index;
        }
 
-       priv_vif->ucast_dpu_signature = params->ucast_dpu_signature;
+       priv_vif->self_ucast_dpu_sign = params->ucast_dpu_signature;
 
        return 0;
 }
@@ -1637,12 +1641,12 @@ int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn,
 
        ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len);
        if (ret) {
-               wcn36xx_err("Sending hal_exit_bmps failed\n");
+               wcn36xx_err("Sending hal_keep_alive failed\n");
                goto out;
        }
        ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
        if (ret) {
-               wcn36xx_err("hal_exit_bmps response failed err=%d\n", ret);
+               wcn36xx_err("hal_keep_alive response failed err=%d\n", ret);
                goto out;
        }
 out:
@@ -1682,8 +1686,7 @@ out:
        return ret;
 }
 
-static inline void set_feat_caps(u32 *bitmap,
-                                enum place_holder_in_cap_bitmap cap)
+void set_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap)
 {
        int arr_idx, bit_idx;
 
@@ -1697,8 +1700,7 @@ static inline void set_feat_caps(u32 *bitmap,
        bitmap[arr_idx] |= (1 << bit_idx);
 }
 
-static inline int get_feat_caps(u32 *bitmap,
-                               enum place_holder_in_cap_bitmap cap)
+int get_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap)
 {
        int arr_idx, bit_idx;
        int ret = 0;
@@ -1714,8 +1716,7 @@ static inline int get_feat_caps(u32 *bitmap,
        return ret;
 }
 
-static inline void clear_feat_caps(u32 *bitmap,
-                               enum place_holder_in_cap_bitmap cap)
+void clear_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap)
 {
        int arr_idx, bit_idx;
 
@@ -1731,8 +1732,8 @@ static inline void clear_feat_caps(u32 *bitmap,
 
 int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn)
 {
-       struct wcn36xx_hal_feat_caps_msg msg_body;
-       int ret = 0;
+       struct wcn36xx_hal_feat_caps_msg msg_body, *rsp;
+       int ret = 0, i;
 
        mutex_lock(&wcn->hal_mutex);
        INIT_HAL_MSG(msg_body, WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_REQ);
@@ -1746,12 +1747,15 @@ int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn)
                wcn36xx_err("Sending hal_feature_caps_exchange failed\n");
                goto out;
        }
-       ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len);
-       if (ret) {
-               wcn36xx_err("hal_feature_caps_exchange response failed err=%d\n",
-                           ret);
+       if (wcn->hal_rsp_len != sizeof(*rsp)) {
+               wcn36xx_err("Invalid hal_feature_caps_exchange response");
                goto out;
        }
+
+       rsp = (struct wcn36xx_hal_feat_caps_msg *) wcn->hal_buf;
+
+       for (i = 0; i < WCN36XX_HAL_CAPS_SIZE; i++)
+               wcn->fw_feat_caps[i] = rsp->feat_caps[i];
 out:
        mutex_unlock(&wcn->hal_mutex);
        return ret;
index e7c39019c6f1aece7d149b410a77bc42ae05093d..008d03423dbf460fc3316924147a872552d47a94 100644 (file)
@@ -24,7 +24,7 @@
 
 #define WCN36XX_HAL_BUF_SIZE                           4096
 
-#define HAL_MSG_TIMEOUT 200
+#define HAL_MSG_TIMEOUT 500
 #define WCN36XX_SMSM_WLAN_TX_ENABLE                    0x00000400
 #define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY               0x00000200
 /* The PNO version info be contained in the rsp msg */
@@ -112,6 +112,9 @@ int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn,
 int wcn36xx_smd_dump_cmd_req(struct wcn36xx *wcn, u32 arg1, u32 arg2,
                             u32 arg3, u32 arg4, u32 arg5);
 int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn);
+void set_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap);
+int get_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap);
+void clear_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap);
 
 int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn,
                struct ieee80211_sta *sta,
index 6846f858ef62323556cf5b5dfbef60a25b639be3..32bb26a0db2abf429de627c32aabac9f453b7227 100644 (file)
@@ -131,6 +131,7 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
                                   struct ieee80211_vif,
                                   drv_priv);
 
+               bd->dpu_sign = sta_priv->ucast_dpu_sign;
                if (vif->type == NL80211_IFTYPE_STATION) {
                        bd->sta_index = sta_priv->bss_sta_index;
                        bd->dpu_desc_idx = sta_priv->bss_dpu_desc_index;
@@ -144,10 +145,9 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
                __vif_priv = get_vif_by_addr(wcn, hdr->addr2);
                bd->sta_index = __vif_priv->self_sta_index;
                bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index;
+               bd->dpu_sign = __vif_priv->self_ucast_dpu_sign;
        }
 
-       bd->dpu_sign = __vif_priv->ucast_dpu_signature;
-
        if (ieee80211_is_nullfunc(hdr->frame_control) ||
           (sta_priv && !sta_priv->is_data_encrypted))
                bd->dpu_ne = 1;
index 8fa5cbace5abba1e027954a64b312bdc37f7b13c..f0fb81dfd17b9d8547f2925d85cf007207f305f8 100644 (file)
@@ -125,10 +125,10 @@ struct wcn36xx_vif {
        enum wcn36xx_power_state pw_state;
 
        u8 bss_index;
-       u8 ucast_dpu_signature;
        /* Returned from WCN36XX_HAL_ADD_STA_SELF_RSP */
        u8 self_sta_index;
        u8 self_dpu_desc_index;
+       u8 self_ucast_dpu_sign;
 };
 
 /**
@@ -159,6 +159,7 @@ struct wcn36xx_sta {
        u16 tid;
        u8 sta_index;
        u8 dpu_desc_index;
+       u8 ucast_dpu_sign;
        u8 bss_sta_index;
        u8 bss_dpu_desc_index;
        bool is_data_encrypted;
@@ -171,10 +172,14 @@ struct wcn36xx {
        struct device           *dev;
        struct list_head        vif_list;
 
+       const struct firmware   *nv;
+
        u8                      fw_revision;
        u8                      fw_version;
        u8                      fw_minor;
        u8                      fw_major;
+       u32                     fw_feat_caps[WCN36XX_HAL_CAPS_SIZE];
+       u32                     chip_version;
 
        /* extra byte for the NULL termination */
        u8                      crm_version[WCN36XX_HAL_VERSION_LENGTH + 1];
@@ -222,6 +227,9 @@ struct wcn36xx {
 
 };
 
+#define WCN36XX_CHIP_3660      0
+#define WCN36XX_CHIP_3680      1
+
 static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn,
                                         u8 major,
                                         u8 minor,
index abac25ee958dad54f140c550ef62ba05bf2bc3ae..f476fc337d64c8098ff7397bff7fedd2a9bcc4b0 100644 (file)
@@ -58,41 +58,6 @@ enum b43_verbosity {
 #endif
 };
 
-
-/* Lightweight function to convert a frequency (in Mhz) to a channel number. */
-static inline u8 b43_freq_to_channel_5ghz(int freq)
-{
-       return ((freq - 5000) / 5);
-}
-static inline u8 b43_freq_to_channel_2ghz(int freq)
-{
-       u8 channel;
-
-       if (freq == 2484)
-               channel = 14;
-       else
-               channel = (freq - 2407) / 5;
-
-       return channel;
-}
-
-/* Lightweight function to convert a channel number to a frequency (in Mhz). */
-static inline int b43_channel_to_freq_5ghz(u8 channel)
-{
-       return (5000 + (5 * channel));
-}
-static inline int b43_channel_to_freq_2ghz(u8 channel)
-{
-       int freq;
-
-       if (channel == 14)
-               freq = 2484;
-       else
-               freq = 2407 + (5 * channel);
-
-       return freq;
-}
-
 static inline int b43_is_cck_rate(int rate)
 {
        return (rate == B43_CCK_RATE_1MB ||
index 50e5ddb12fb3f6bf97217819a862c5d508ec0bd4..218a0f37af46627570ba4e1e1a8e359edb470ddb 100644 (file)
@@ -806,7 +806,8 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
                B43_WARN_ON(1);
                /* FIXME: We don't really know which value the "chanid" contains.
                 *        So the following assignment might be wrong. */
-               status.freq = b43_channel_to_freq_5ghz(chanid);
+               status.freq =
+                       ieee80211_channel_to_frequency(chanid, status.band);
                break;
        case B43_PHYTYPE_G:
                status.band = IEEE80211_BAND_2GHZ;
@@ -819,13 +820,12 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr)
        case B43_PHYTYPE_HT:
                /* chanid is the SHM channel cookie. Which is the plain
                 * channel number in b43. */
-               if (chanstat & B43_RX_CHAN_5GHZ) {
+               if (chanstat & B43_RX_CHAN_5GHZ)
                        status.band = IEEE80211_BAND_5GHZ;
-                       status.freq = b43_channel_to_freq_5ghz(chanid);
-               } else {
+               else
                        status.band = IEEE80211_BAND_2GHZ;
-                       status.freq = b43_channel_to_freq_2ghz(chanid);
-               }
+               status.freq =
+                       ieee80211_channel_to_frequency(chanid, status.band);
                break;
        default:
                B43_WARN_ON(1);
index eeb3a838c1bd1be79eb49789e2a9c3a6c452897b..2f962ec0b75065b7387787a9444383c7e38094d7 100644 (file)
@@ -215,6 +215,11 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
 
        for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
                ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
+
+               if (ch_idx >= NUM_2GHZ_CHANNELS &&
+                   !data->sku_cap_band_52GHz_enable)
+                       ch_flags &= ~NVM_CHANNEL_VALID;
+
                if (!(ch_flags & NVM_CHANNEL_VALID)) {
                        IWL_DEBUG_EEPROM(dev,
                                         "Ch. %d Flags %x [%sGHz] - No traffic\n",
index 73cbba7424f2be42e5278f85c4e3f54289d06280..9426905de6b283dc0230cf51d5a694478da7797a 100644 (file)
@@ -504,6 +504,7 @@ struct iwl_scan_offload_profile {
  * @match_notify:      clients waiting for match found notification
  * @pass_match:                clients waiting for the results
  * @active_clients:    active clients bitmap - enum scan_framework_client
+ * @any_beacon_notify: clients waiting for match notification without match
  */
 struct iwl_scan_offload_profile_cfg {
        struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES];
@@ -512,7 +513,8 @@ struct iwl_scan_offload_profile_cfg {
        u8 match_notify;
        u8 pass_match;
        u8 active_clients;
-       u8 reserved[3];
+       u8 any_beacon_notify;
+       u8 reserved[2];
 } __packed;
 
 /**
index 83da68c8b4a927d78b5f8840a37e901c42229d7b..d74cc43ca593575dbc1f87bfbc75f23d21a66783 100644 (file)
@@ -365,7 +365,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
        else
                hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) {
+       if (0 && mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) {
                hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
                hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
                hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
@@ -1747,14 +1747,16 @@ out:
        return ret;
 }
 
-static void iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
-                                       struct ieee80211_vif *vif)
+static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif)
 {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
 
        mutex_lock(&mvm->mutex);
        iwl_mvm_sched_scan_stop(mvm);
        mutex_unlock(&mvm->mutex);
+
+       return 0;
 }
 
 static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
index cea935b9afa634d61e03175e0b2451f9b2a6173b..713efd71efe26b6eb4f545c4a7f306813f77c1dc 100644 (file)
@@ -344,7 +344,8 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
 
        iwl_mvm_scan_fill_ssids(cmd, req, basic_ssid ? 1 : 0);
 
-       cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL);
+       cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
+                                          TX_CMD_FLG_BT_DIS);
        cmd->tx_cmd.sta_id = mvm->aux_sta.sta_id;
        cmd->tx_cmd.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
        cmd->tx_cmd.rate_n_flags =
@@ -818,6 +819,8 @@ int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
        profile_cfg->active_clients = SCAN_CLIENT_SCHED_SCAN;
        profile_cfg->pass_match = SCAN_CLIENT_SCHED_SCAN;
        profile_cfg->match_notify = SCAN_CLIENT_SCHED_SCAN;
+       if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len)
+               profile_cfg->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN;
 
        for (i = 0; i < req->n_match_sets; i++) {
                profile = &profile_cfg->profiles[i];
index d1da93b79cc6fb3e575278884f2241f2965a294c..2677d1c0e1a1b69a46b533cb722d7f386974b8a2 100644 (file)
@@ -669,7 +669,7 @@ int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
-       static const u8 *baddr = _baddr;
+       const u8 *baddr = _baddr;
 
        lockdep_assert_held(&mvm->mutex);
 
index b83ef6ad1242d9a304f3458d9d9a2e69c785d9c9..6cdbf7b2171433bcf9c457d5f060e5eeb80a074d 100644 (file)
@@ -677,8 +677,14 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
        rcu_read_lock();
 
        sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+       /*
+        * sta can't be NULL otherwise it'd mean that the sta has been freed in
+        * the firmware while we still have packets for it in the Tx queues.
+        */
+       if (WARN_ON_ONCE(!sta))
+               goto out;
 
-       if (!IS_ERR_OR_NULL(sta)) {
+       if (!IS_ERR(sta)) {
                mvmsta = iwl_mvm_sta_from_mac80211(sta);
 
                if (tid != IWL_TID_NON_QOS) {
@@ -698,7 +704,6 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
                        ieee80211_sta_eosp(sta);
                }
        } else {
-               sta = NULL;
                mvmsta = NULL;
        }
 
@@ -706,42 +711,38 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
         * If the txq is not an AMPDU queue, there is no chance we freed
         * several skbs. Check that out...
         */
-       if (txq_id < mvm->first_agg_queue && !WARN_ON(skb_freed > 1) &&
-           atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) {
-               if (mvmsta) {
-                       /*
-                        * If there are no pending frames for this STA, notify
-                        * mac80211 that this station can go to sleep in its
-                        * STA table.
-                        */
-                       if (mvmsta->vif->type == NL80211_IFTYPE_AP)
-                               ieee80211_sta_block_awake(mvm->hw, sta, false);
-                       /*
-                        * We might very well have taken mvmsta pointer while
-                        * the station was being removed. The remove flow might
-                        * have seen a pending_frame (because we didn't take
-                        * the lock) even if now the queues are drained. So make
-                        * really sure now that this the station is not being
-                        * removed. If it is, run the drain worker to remove it.
-                        */
-                       spin_lock_bh(&mvmsta->lock);
-                       sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
-                       if (!sta || PTR_ERR(sta) == -EBUSY) {
-                               /*
-                                * Station disappeared in the meantime:
-                                * so we are draining.
-                                */
-                               set_bit(sta_id, mvm->sta_drained);
-                               schedule_work(&mvm->sta_drained_wk);
-                       }
-                       spin_unlock_bh(&mvmsta->lock);
-               } else if (!mvmsta && PTR_ERR(sta) == -EBUSY) {
-                       /* Tx response without STA, so we are draining */
-                       set_bit(sta_id, mvm->sta_drained);
-                       schedule_work(&mvm->sta_drained_wk);
-               }
+       if (txq_id >= mvm->first_agg_queue)
+               goto out;
+
+       /* We can't free more than one frame at once on a shared queue */
+       WARN_ON(skb_freed > 1);
+
+       /* If we have still frames from this STA nothing to do here */
+       if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id]))
+               goto out;
+
+       if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) {
+               /*
+                * If there are no pending frames for this STA, notify
+                * mac80211 that this station can go to sleep in its
+                * STA table.
+                * If mvmsta is not NULL, sta is valid.
+                */
+               ieee80211_sta_block_awake(mvm->hw, sta, false);
+       }
+
+       if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) {
+               /*
+                * We are draining and this was the last packet - pre_rcu_remove
+                * has been called already. We might be after the
+                * synchronize_net already.
+                * Don't rely on iwl_mvm_rm_sta to see the empty Tx queues.
+                */
+               set_bit(sta_id, mvm->sta_drained);
+               schedule_work(&mvm->sta_drained_wk);
        }
 
+out:
        rcu_read_unlock();
 }
 
index c81a7fb6276c8ab672eff7cbde0f6d60dcc5ca48..bbfe529d7b64e8d5b3789fd7cc4b53adcc882d0f 100644 (file)
@@ -469,6 +469,8 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
                        mvm->status, table.valid);
        }
 
+       IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version);
+
        trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
                                      table.data1, table.data2, table.data3,
                                      table.blink1, table.blink2, table.ilink1,
index 85779390c4448aeebdb89189bddee9ce2d508d06..0f52e961a5a514f069e80b139a3e5df484b5e76e 100644 (file)
@@ -360,20 +360,25 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
 /* 7265 Series */
        {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7265_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x095A, 0x5112, iwl7265_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x095A, 0x5100, iwl7265_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x095A, 0x510A, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)},
-       {IWL_PCI_DEVICE(0x095A, 0x500A, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7265_2n_cfg)},
+       {IWL_PCI_DEVICE(0x095A, 0x500A, iwl7265_2n_cfg)},
        {IWL_PCI_DEVICE(0x095B, 0x5200, iwl7265_2n_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x5002, iwl7265_n_cfg)},
        {IWL_PCI_DEVICE(0x095B, 0x5202, iwl7265_n_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9010, iwl7265_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x095A, 0x9012, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)},
+       {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9210, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)},
        {IWL_PCI_DEVICE(0x095A, 0x9310, iwl7265_2ac_cfg)},
index f7e3562542feae86fdf4ad62cc06a86aac47f67b..9d7a52f5a4102abedd2dbebc03c26c3866da2a64 100644 (file)
@@ -411,6 +411,7 @@ struct mac80211_hwsim_data {
 
        struct mac_address addresses[2];
        int channels, idx;
+       bool use_chanctx;
 
        struct ieee80211_channel *tmp_chan;
        struct delayed_work roc_done;
@@ -1088,7 +1089,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
                return;
        }
 
-       if (data->channels == 1) {
+       if (!data->use_chanctx) {
                channel = data->channel;
        } else if (txi->hw_queue == 4) {
                channel = data->tmp_chan;
@@ -1354,7 +1355,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed)
 
        data->channel = conf->chandef.chan;
 
-       WARN_ON(data->channel && data->channels > 1);
+       WARN_ON(data->channel && data->use_chanctx);
 
        data->power_level = conf->power_level;
        if (!data->started || !data->beacon_int)
@@ -1940,7 +1941,8 @@ static struct ieee80211_ops mac80211_hwsim_mchan_ops;
 
 static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
                                       const struct ieee80211_regdomain *regd,
-                                      bool reg_strict, bool p2p_device)
+                                      bool reg_strict, bool p2p_device,
+                                      bool use_chanctx)
 {
        int err;
        u8 addr[ETH_ALEN];
@@ -1950,11 +1952,14 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
        const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
        int idx;
 
+       if (WARN_ON(channels > 1 && !use_chanctx))
+               return -EINVAL;
+
        spin_lock_bh(&hwsim_radio_lock);
        idx = hwsim_radio_idx++;
        spin_unlock_bh(&hwsim_radio_lock);
 
-       if (channels > 1)
+       if (use_chanctx)
                ops = &mac80211_hwsim_mchan_ops;
        hw = ieee80211_alloc_hw(sizeof(*data), ops);
        if (!hw) {
@@ -1995,20 +2000,21 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
        hw->wiphy->addresses = data->addresses;
 
        data->channels = channels;
+       data->use_chanctx = use_chanctx;
        data->idx = idx;
 
-       if (data->channels > 1) {
+       if (data->use_chanctx) {
                hw->wiphy->max_scan_ssids = 255;
                hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
                hw->wiphy->max_remain_on_channel_duration = 1000;
                /* For channels > 1 DFS is not allowed */
                hw->wiphy->n_iface_combinations = 1;
                hw->wiphy->iface_combinations = &data->if_combination;
-               data->if_combination.num_different_channels = data->channels;
                if (p2p_device)
                        data->if_combination = hwsim_if_comb_p2p_dev[0];
                else
                        data->if_combination = hwsim_if_comb[0];
+               data->if_combination.num_different_channels = data->channels;
        } else if (p2p_device) {
                hw->wiphy->iface_combinations = hwsim_if_comb_p2p_dev;
                hw->wiphy->n_iface_combinations =
@@ -2156,7 +2162,7 @@ static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2,
        debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps);
        debugfs_create_file("group", 0666, data->debugfs, data,
                            &hwsim_fops_group);
-       if (data->channels == 1)
+       if (!data->use_chanctx)
                debugfs_create_file("dfs_simulate_radar", 0222,
                                    data->debugfs,
                                    data, &hwsim_simulate_radar);
@@ -2423,10 +2429,16 @@ static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info)
        const struct ieee80211_regdomain *regd = NULL;
        bool reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
        bool p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
+       bool use_chanctx;
 
        if (info->attrs[HWSIM_ATTR_CHANNELS])
                chans = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]);
 
+       if (info->attrs[HWSIM_ATTR_USE_CHANCTX])
+               use_chanctx = true;
+       else
+               use_chanctx = (chans > 1);
+
        if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2])
                alpha2 = nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]);
 
@@ -2439,7 +2451,7 @@ static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info)
        }
 
        return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict,
-                                          p2p_device);
+                                          p2p_device, use_chanctx);
 }
 
 static int hwsim_destroy_radio_nl(struct sk_buff *msg, struct genl_info *info)
@@ -2658,7 +2670,8 @@ static int __init init_mac80211_hwsim(void)
 
                err = mac80211_hwsim_create_radio(channels, reg_alpha2,
                                                  regd, reg_strict,
-                                                 support_p2p_device);
+                                                 support_p2p_device,
+                                                 channels > 1);
                if (err < 0)
                        goto out_free_radios;
        }
index 6e72996ec8c1f1dae6386a5e9755fa6b3a5041ba..c9d0315575bab27035378378d396a46db828bf4c 100644 (file)
@@ -108,6 +108,9 @@ enum {
  * @HWSIM_ATTR_REG_CUSTOM_REG: custom regulatory domain index (u32 attribute)
  * @HWSIM_ATTR_REG_STRICT_REG: request REGULATORY_STRICT_REG (flag attribute)
  * @HWSIM_ATTR_SUPPORT_P2P_DEVICE: support P2P Device virtual interface (flag)
+ * @HWSIM_ATTR_USE_CHANCTX: used with the %HWSIM_CMD_CREATE_RADIO
+ *     command to force use of channel contexts even when only a
+ *     single channel is supported
  * @__HWSIM_ATTR_MAX: enum limit
  */
 
@@ -128,6 +131,7 @@ enum {
        HWSIM_ATTR_REG_CUSTOM_REG,
        HWSIM_ATTR_REG_STRICT_REG,
        HWSIM_ATTR_SUPPORT_P2P_DEVICE,
+       HWSIM_ATTR_USE_CHANCTX,
        __HWSIM_ATTR_MAX,
 };
 #define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
index 436ba437a4ba198f2c469cad6545818e46cba730..6948a97af8390433fa41c5de5888eb96747eb943 100644 (file)
@@ -2600,8 +2600,8 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
 static int
 mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
                           u8 *peer, u8 action_code, u8 dialog_token,
-                          u16 status_code, const u8 *extra_ies,
-                          size_t extra_ies_len)
+                          u16 status_code, u32 peer_capability,
+                          const u8 *extra_ies, size_t extra_ies_len)
 {
        struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
        int ret;
index abc5f56f29fe1c96c3dc585af30f662c138c1cd7..2f1cd929c6f6d004b35ddf61197d83b2a57d7b2a 100644 (file)
@@ -1876,6 +1876,11 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
                                rt2x00_eeprom_addr(rt2x00dev,
                                                   EEPROM_MAC_ADDR_0));
 
+       /*
+        * Disable powersaving as default.
+        */
+       rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
        /*
         * Initialize hw_mode information.
         */
index 9f16824cd1bccf80407633fa0864d1d7f0d6d257..d849d590de250b915ddda53ce64c703beef3215e 100644 (file)
@@ -1706,6 +1706,11 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
            IEEE80211_HW_SUPPORTS_PS |
            IEEE80211_HW_PS_NULLFUNC_STACK;
 
+       /*
+        * Disable powersaving as default.
+        */
+       rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+
        SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
        SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
                                rt2x00_eeprom_addr(rt2x00dev,
index b8f5b06006c4393358d9be88cb11ef4f389476ef..7f8b5d156c8c91dde72791d439aa5bbc9e66b7cc 100644 (file)
@@ -7458,10 +7458,9 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
        u32 reg;
 
        /*
-        * Disable powersaving as default on PCI devices.
+        * Disable powersaving as default.
         */
-       if (rt2x00_is_pci(rt2x00dev) || rt2x00_is_soc(rt2x00dev))
-               rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+       rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
 
        /*
         * Initialize all hw fields.
index 9bc843e9f4b49156acc5cec17e943918e0e4627c..7980ab1f9eca3032a7af8b6c4993798895640c66 100644 (file)
@@ -107,6 +107,7 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
        struct rtl8180_priv *priv = dev->priv;
        unsigned int count = 32;
        u8 signal, agc, sq;
+       dma_addr_t mapping;
 
        while (count--) {
                struct rtl8180_rx_desc *entry = &priv->rx_ring[priv->rx_idx];
@@ -128,6 +129,17 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
                        if (unlikely(!new_skb))
                                goto done;
 
+                       mapping = pci_map_single(priv->pdev,
+                                              skb_tail_pointer(new_skb),
+                                              MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
+
+                       if (pci_dma_mapping_error(priv->pdev, mapping)) {
+                               kfree_skb(new_skb);
+                               dev_err(&priv->pdev->dev, "RX DMA map error\n");
+
+                               goto done;
+                       }
+
                        pci_unmap_single(priv->pdev,
                                         *((dma_addr_t *)skb->cb),
                                         MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
@@ -158,9 +170,7 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
 
                        skb = new_skb;
                        priv->rx_buf[priv->rx_idx] = skb;
-                       *((dma_addr_t *) skb->cb) =
-                               pci_map_single(priv->pdev, skb_tail_pointer(skb),
-                                              MAX_RX_SIZE, PCI_DMA_FROMDEVICE);
+                       *((dma_addr_t *) skb->cb) = mapping;
                }
 
        done:
@@ -266,6 +276,13 @@ static void rtl8180_tx(struct ieee80211_hw *dev,
        mapping = pci_map_single(priv->pdev, skb->data,
                                 skb->len, PCI_DMA_TODEVICE);
 
+       if (pci_dma_mapping_error(priv->pdev, mapping)) {
+               kfree_skb(skb);
+               dev_err(&priv->pdev->dev, "TX DMA mapping error\n");
+               return;
+
+       }
+
        tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS |
                   RTL818X_TX_DESC_FLAG_LS |
                   (ieee80211_get_tx_rate(dev, info)->hw_value << 24) |
index be7129ba16ad651524910c1897a33b7d48570007..d50dfac91631ebcbd4f685497ca237d3eb22893f 100644 (file)
@@ -1378,7 +1378,7 @@ static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data,
 
 static int wl12xx_tx_delayed_compl(struct wl1271 *wl)
 {
-       if (wl->fw_status_1->tx_results_counter ==
+       if (wl->fw_status->tx_results_counter ==
            (wl->tx_results_count & 0xff))
                return 0;
 
@@ -1438,6 +1438,37 @@ out:
        return ret;
 }
 
+static void wl12xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
+                                    struct wl_fw_status *fw_status)
+{
+       struct wl12xx_fw_status *int_fw_status = raw_fw_status;
+
+       fw_status->intr = le32_to_cpu(int_fw_status->intr);
+       fw_status->fw_rx_counter = int_fw_status->fw_rx_counter;
+       fw_status->drv_rx_counter = int_fw_status->drv_rx_counter;
+       fw_status->tx_results_counter = int_fw_status->tx_results_counter;
+       fw_status->rx_pkt_descs = int_fw_status->rx_pkt_descs;
+
+       fw_status->fw_localtime = le32_to_cpu(int_fw_status->fw_localtime);
+       fw_status->link_ps_bitmap = le32_to_cpu(int_fw_status->link_ps_bitmap);
+       fw_status->link_fast_bitmap =
+                       le32_to_cpu(int_fw_status->link_fast_bitmap);
+       fw_status->total_released_blks =
+                       le32_to_cpu(int_fw_status->total_released_blks);
+       fw_status->tx_total = le32_to_cpu(int_fw_status->tx_total);
+
+       fw_status->counters.tx_released_pkts =
+                       int_fw_status->counters.tx_released_pkts;
+       fw_status->counters.tx_lnk_free_pkts =
+                       int_fw_status->counters.tx_lnk_free_pkts;
+       fw_status->counters.tx_voice_released_blks =
+                       int_fw_status->counters.tx_voice_released_blks;
+       fw_status->counters.tx_last_rate =
+                       int_fw_status->counters.tx_last_rate;
+
+       fw_status->log_start_addr = le32_to_cpu(int_fw_status->log_start_addr);
+}
+
 static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl,
                                       struct wl12xx_vif *wlvif)
 {
@@ -1677,6 +1708,7 @@ static struct wlcore_ops wl12xx_ops = {
        .tx_delayed_compl       = wl12xx_tx_delayed_compl,
        .hw_init                = wl12xx_hw_init,
        .init_vif               = NULL,
+       .convert_fw_status      = wl12xx_convert_fw_status,
        .sta_get_ap_rate_mask   = wl12xx_sta_get_ap_rate_mask,
        .get_pg_ver             = wl12xx_get_pg_ver,
        .get_mac                = wl12xx_get_mac,
@@ -1711,22 +1743,53 @@ static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
                },
 };
 
+static const struct ieee80211_iface_limit wl12xx_iface_limits[] = {
+       {
+               .max = 3,
+               .types = BIT(NL80211_IFTYPE_STATION),
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_AP) |
+                        BIT(NL80211_IFTYPE_P2P_GO) |
+                        BIT(NL80211_IFTYPE_P2P_CLIENT),
+       },
+};
+
+static const struct ieee80211_iface_combination
+wl12xx_iface_combinations[] = {
+       {
+               .max_interfaces = 3,
+               .limits = wl12xx_iface_limits,
+               .n_limits = ARRAY_SIZE(wl12xx_iface_limits),
+               .num_different_channels = 1,
+       },
+};
+
 static int wl12xx_setup(struct wl1271 *wl)
 {
        struct wl12xx_priv *priv = wl->priv;
        struct wlcore_platdev_data *pdev_data = dev_get_platdata(&wl->pdev->dev);
        struct wl12xx_platform_data *pdata = pdev_data->pdata;
 
+       BUILD_BUG_ON(WL12XX_MAX_LINKS > WLCORE_MAX_LINKS);
+       BUILD_BUG_ON(WL12XX_MAX_AP_STATIONS > WL12XX_MAX_LINKS);
+
        wl->rtable = wl12xx_rtable;
        wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS;
        wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS;
-       wl->num_channels = 1;
+       wl->num_links = WL12XX_MAX_LINKS;
+       wl->max_ap_stations = WL12XX_MAX_AP_STATIONS;
+       wl->iface_combinations = wl12xx_iface_combinations;
+       wl->n_iface_combinations = ARRAY_SIZE(wl12xx_iface_combinations);
        wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES;
        wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
        wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
        wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0;
+       wl->fw_status_len = sizeof(struct wl12xx_fw_status);
        wl->fw_status_priv_len = 0;
        wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics);
+       wl->ofdm_only_ap = true;
        wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl12xx_ht_cap);
        wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl12xx_ht_cap);
        wl12xx_conf_init(wl);
index 9e5484a7366704b55ff360ae2c0b86ac12eb2d6d..75c92658bfeaecb3ccd356303fde6dc17efc92c4 100644 (file)
@@ -65,6 +65,9 @@
 
 #define WL12XX_RX_BA_MAX_SESSIONS 3
 
+#define WL12XX_MAX_AP_STATIONS 8
+#define WL12XX_MAX_LINKS 12
+
 struct wl127x_rx_mem_pool_addr {
        u32 addr;
        u32 addr_extra;
@@ -79,4 +82,54 @@ struct wl12xx_priv {
        struct wl127x_rx_mem_pool_addr *rx_mem_addr;
 };
 
+struct wl12xx_fw_packet_counters {
+       /* Cumulative counter of released packets per AC */
+       u8 tx_released_pkts[NUM_TX_QUEUES];
+
+       /* Cumulative counter of freed packets per HLID */
+       u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS];
+
+       /* Cumulative counter of released Voice memory blocks */
+       u8 tx_voice_released_blks;
+
+       /* Tx rate of the last transmitted packet */
+       u8 tx_last_rate;
+
+       u8 padding[2];
+} __packed;
+
+/* FW status registers */
+struct wl12xx_fw_status {
+       __le32 intr;
+       u8  fw_rx_counter;
+       u8  drv_rx_counter;
+       u8  reserved;
+       u8  tx_results_counter;
+       __le32 rx_pkt_descs[WL12XX_NUM_RX_DESCRIPTORS];
+
+       __le32 fw_localtime;
+
+       /*
+        * A bitmap (where each bit represents a single HLID)
+        * to indicate if the station is in PS mode.
+        */
+       __le32 link_ps_bitmap;
+
+       /*
+        * A bitmap (where each bit represents a single HLID) to indicate
+        * if the station is in Fast mode
+        */
+       __le32 link_fast_bitmap;
+
+       /* Cumulative counter of total released mem blocks since FW-reset */
+       __le32 total_released_blks;
+
+       /* Size (in Memory Blocks) of TX pool */
+       __le32 tx_total;
+
+       struct wl12xx_fw_packet_counters counters;
+
+       __le32 log_start_addr;
+} __packed;
+
 #endif /* __WL12XX_PRIV_H__ */
index ec37b16585df939938fb1a75ef060ef0ea3d73fd..de5b4fa5d1666b9a5af57b8312da487a037ebdf4 100644 (file)
@@ -648,7 +648,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = {
 };
 
 /* TODO: maybe move to a new header file? */
-#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-2.bin"
+#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-3.bin"
 
 static int wl18xx_identify_chip(struct wl1271 *wl)
 {
@@ -1133,6 +1133,39 @@ static int wl18xx_hw_init(struct wl1271 *wl)
        return ret;
 }
 
+static void wl18xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
+                                    struct wl_fw_status *fw_status)
+{
+       struct wl18xx_fw_status *int_fw_status = raw_fw_status;
+
+       fw_status->intr = le32_to_cpu(int_fw_status->intr);
+       fw_status->fw_rx_counter = int_fw_status->fw_rx_counter;
+       fw_status->drv_rx_counter = int_fw_status->drv_rx_counter;
+       fw_status->tx_results_counter = int_fw_status->tx_results_counter;
+       fw_status->rx_pkt_descs = int_fw_status->rx_pkt_descs;
+
+       fw_status->fw_localtime = le32_to_cpu(int_fw_status->fw_localtime);
+       fw_status->link_ps_bitmap = le32_to_cpu(int_fw_status->link_ps_bitmap);
+       fw_status->link_fast_bitmap =
+                       le32_to_cpu(int_fw_status->link_fast_bitmap);
+       fw_status->total_released_blks =
+                       le32_to_cpu(int_fw_status->total_released_blks);
+       fw_status->tx_total = le32_to_cpu(int_fw_status->tx_total);
+
+       fw_status->counters.tx_released_pkts =
+                       int_fw_status->counters.tx_released_pkts;
+       fw_status->counters.tx_lnk_free_pkts =
+                       int_fw_status->counters.tx_lnk_free_pkts;
+       fw_status->counters.tx_voice_released_blks =
+                       int_fw_status->counters.tx_voice_released_blks;
+       fw_status->counters.tx_last_rate =
+                       int_fw_status->counters.tx_last_rate;
+
+       fw_status->log_start_addr = le32_to_cpu(int_fw_status->log_start_addr);
+
+       fw_status->priv = &int_fw_status->priv;
+}
+
 static void wl18xx_set_tx_desc_csum(struct wl1271 *wl,
                                    struct wl1271_tx_hw_descr *desc,
                                    struct sk_buff *skb)
@@ -1572,7 +1605,7 @@ static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid,
 {
        u8 thold;
        struct wl18xx_fw_status_priv *status_priv =
-               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+               (struct wl18xx_fw_status_priv *)wl->fw_status->priv;
        u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
 
        /* suspended links are never high priority */
@@ -1594,7 +1627,7 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid,
 {
        u8 thold;
        struct wl18xx_fw_status_priv *status_priv =
-               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+               (struct wl18xx_fw_status_priv *)wl->fw_status->priv;
        u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap);
 
        if (test_bit(hlid, (unsigned long *)&suspend_bitmap))
@@ -1632,6 +1665,7 @@ static struct wlcore_ops wl18xx_ops = {
        .tx_immediate_compl = wl18xx_tx_immediate_completion,
        .tx_delayed_compl = NULL,
        .hw_init        = wl18xx_hw_init,
+       .convert_fw_status = wl18xx_convert_fw_status,
        .set_tx_desc_csum = wl18xx_set_tx_desc_csum,
        .get_pg_ver     = wl18xx_get_pg_ver,
        .set_rx_csum = wl18xx_set_rx_csum,
@@ -1713,19 +1747,62 @@ static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = {
                },
 };
 
+static const struct ieee80211_iface_limit wl18xx_iface_limits[] = {
+       {
+               .max = 3,
+               .types = BIT(NL80211_IFTYPE_STATION),
+       },
+       {
+               .max = 1,
+               .types = BIT(NL80211_IFTYPE_AP) |
+                        BIT(NL80211_IFTYPE_P2P_GO) |
+                        BIT(NL80211_IFTYPE_P2P_CLIENT),
+       },
+};
+
+static const struct ieee80211_iface_limit wl18xx_iface_ap_limits[] = {
+       {
+               .max = 2,
+               .types = BIT(NL80211_IFTYPE_AP),
+       },
+};
+
+static const struct ieee80211_iface_combination
+wl18xx_iface_combinations[] = {
+       {
+               .max_interfaces = 3,
+               .limits = wl18xx_iface_limits,
+               .n_limits = ARRAY_SIZE(wl18xx_iface_limits),
+               .num_different_channels = 2,
+       },
+       {
+               .max_interfaces = 2,
+               .limits = wl18xx_iface_ap_limits,
+               .n_limits = ARRAY_SIZE(wl18xx_iface_ap_limits),
+               .num_different_channels = 1,
+       }
+};
+
 static int wl18xx_setup(struct wl1271 *wl)
 {
        struct wl18xx_priv *priv = wl->priv;
        int ret;
 
+       BUILD_BUG_ON(WL18XX_MAX_LINKS > WLCORE_MAX_LINKS);
+       BUILD_BUG_ON(WL18XX_MAX_AP_STATIONS > WL18XX_MAX_LINKS);
+
        wl->rtable = wl18xx_rtable;
        wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS;
        wl->num_rx_desc = WL18XX_NUM_RX_DESCRIPTORS;
-       wl->num_channels = 2;
+       wl->num_links = WL18XX_MAX_LINKS;
+       wl->max_ap_stations = WL18XX_MAX_AP_STATIONS;
+       wl->iface_combinations = wl18xx_iface_combinations;
+       wl->n_iface_combinations = ARRAY_SIZE(wl18xx_iface_combinations);
        wl->num_mac_addr = WL18XX_NUM_MAC_ADDRESSES;
        wl->band_rate_to_idx = wl18xx_band_rate_to_idx;
        wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX;
        wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0;
+       wl->fw_status_len = sizeof(struct wl18xx_fw_status);
        wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv);
        wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics);
        wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv);
index 57c694396647f71adaea386cd4465df00a5611a8..be1ebd55ac88e8f7f04be16e6e7bb02436468cf6 100644 (file)
@@ -32,7 +32,7 @@ static
 void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif,
                             struct ieee80211_tx_rate *rate)
 {
-       u8 fw_rate = wl->fw_status_2->counters.tx_last_rate;
+       u8 fw_rate = wl->fw_status->counters.tx_last_rate;
 
        if (fw_rate > CONF_HW_RATE_INDEX_MAX) {
                wl1271_error("last Tx rate invalid: %d", fw_rate);
@@ -139,7 +139,7 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
 void wl18xx_tx_immediate_complete(struct wl1271 *wl)
 {
        struct wl18xx_fw_status_priv *status_priv =
-               (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv;
+               (struct wl18xx_fw_status_priv *)wl->fw_status->priv;
        struct wl18xx_priv *priv = wl->priv;
        u8 i;
 
index 9204e07ee432fe483a343f008ee1ce91f2752149..eb7cfe8170104ab5eb273c84f2c49209a0d9e1a3 100644 (file)
 
 /* minimum FW required for driver */
 #define WL18XX_CHIP_VER                8
-#define WL18XX_IFTYPE_VER      5
+#define WL18XX_IFTYPE_VER      8
 #define WL18XX_MAJOR_VER       WLCORE_FW_VER_IGNORE
 #define WL18XX_SUBTYPE_VER     WLCORE_FW_VER_IGNORE
-#define WL18XX_MINOR_VER       39
+#define WL18XX_MINOR_VER       13
 
 #define WL18XX_CMD_MAX_SIZE          740
 
 
 #define WL18XX_NUM_MAC_ADDRESSES 3
 
-#define WL18XX_RX_BA_MAX_SESSIONS 5
+#define WL18XX_RX_BA_MAX_SESSIONS 13
+
+#define WL18XX_MAX_AP_STATIONS 10
+#define WL18XX_MAX_LINKS 16
 
 struct wl18xx_priv {
        /* buffer for sending commands to FW */
@@ -109,6 +112,59 @@ struct wl18xx_fw_status_priv {
        u8 padding[3];
 };
 
+struct wl18xx_fw_packet_counters {
+       /* Cumulative counter of released packets per AC */
+       u8 tx_released_pkts[NUM_TX_QUEUES];
+
+       /* Cumulative counter of freed packets per HLID */
+       u8 tx_lnk_free_pkts[WL18XX_MAX_LINKS];
+
+       /* Cumulative counter of released Voice memory blocks */
+       u8 tx_voice_released_blks;
+
+       /* Tx rate of the last transmitted packet */
+       u8 tx_last_rate;
+
+       u8 padding[2];
+} __packed;
+
+/* FW status registers */
+struct wl18xx_fw_status {
+       __le32 intr;
+       u8  fw_rx_counter;
+       u8  drv_rx_counter;
+       u8  reserved;
+       u8  tx_results_counter;
+       __le32 rx_pkt_descs[WL18XX_NUM_RX_DESCRIPTORS];
+
+       __le32 fw_localtime;
+
+       /*
+        * A bitmap (where each bit represents a single HLID)
+        * to indicate if the station is in PS mode.
+        */
+       __le32 link_ps_bitmap;
+
+       /*
+        * A bitmap (where each bit represents a single HLID) to indicate
+        * if the station is in Fast mode
+        */
+       __le32 link_fast_bitmap;
+
+       /* Cumulative counter of total released mem blocks since FW-reset */
+       __le32 total_released_blks;
+
+       /* Size (in Memory Blocks) of TX pool */
+       __le32 tx_total;
+
+       struct wl18xx_fw_packet_counters counters;
+
+       __le32 log_start_addr;
+
+       /* Private status to be used by the lower drivers */
+       struct wl18xx_fw_status_priv priv;
+} __packed;
+
 #define WL18XX_PHY_VERSION_MAX_LEN 20
 
 struct wl18xx_static_data_priv {
index ec83675a244697537afd791613cfec9e87d13caa..b924ceadc02c4a5fcfd96530c986e65d9fc89f2b 100644 (file)
@@ -358,7 +358,8 @@ int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        struct acx_beacon_filter_option *beacon_filter = NULL;
        int ret = 0;
 
-       wl1271_debug(DEBUG_ACX, "acx beacon filter opt");
+       wl1271_debug(DEBUG_ACX, "acx beacon filter opt enable=%d",
+                    enable_filter);
 
        if (enable_filter &&
            wl->conf.conn.bcn_filt_mode == CONF_BCN_FILT_MODE_DISABLED)
@@ -1591,7 +1592,8 @@ out:
        return ret;
 }
 
-int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr)
+int wl1271_acx_set_inconnection_sta(struct wl1271 *wl,
+                                   struct wl12xx_vif *wlvif, u8 *addr)
 {
        struct wl1271_acx_inconnection_sta *acx = NULL;
        int ret;
@@ -1603,6 +1605,7 @@ int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr)
                return -ENOMEM;
 
        memcpy(acx->addr, addr, ETH_ALEN);
+       acx->role_id = wlvif->role_id;
 
        ret = wl1271_cmd_configure(wl, ACX_UPDATE_INCONNECTION_STA_LIST,
                                   acx, sizeof(*acx));
index 6dcfad9b04729a44d60044d41bb1ac0a9ac042cb..954d57ec98f45cc358c0753dab590205322a8178 100644 (file)
@@ -824,7 +824,8 @@ struct wl1271_acx_inconnection_sta {
        struct acx_header header;
 
        u8 addr[ETH_ALEN];
-       u8 padding1[2];
+       u8 role_id;
+       u8 padding;
 } __packed;
 
 /*
@@ -1118,7 +1119,8 @@ int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                               bool enable);
 int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl, struct wl12xx_vif *wlvif);
 int wl12xx_acx_config_ps(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr);
+int wl1271_acx_set_inconnection_sta(struct wl1271 *wl,
+                                   struct wl12xx_vif *wlvif, u8 *addr);
 int wl1271_acx_fm_coex(struct wl1271 *wl);
 int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl);
 int wl12xx_acx_config_hangover(struct wl1271 *wl);
index 9b2ecf52449faa911f25bea176ed12683fe1836e..40dc30f4faaab2b2be20c724abe54c7807f0871a 100644 (file)
@@ -60,8 +60,8 @@ static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf,
        u16 status;
        u16 poll_count = 0;
 
-       if (WARN_ON(wl->state == WLCORE_STATE_RESTARTING &&
-                   id != CMD_STOP_FWLOGGER))
+       if (unlikely(wl->state == WLCORE_STATE_RESTARTING &&
+                    id != CMD_STOP_FWLOGGER))
                return -EIO;
 
        cmd = buf;
@@ -312,8 +312,8 @@ static int wlcore_get_new_session_id(struct wl1271 *wl, u8 hlid)
 int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
 {
        unsigned long flags;
-       u8 link = find_first_zero_bit(wl->links_map, WL12XX_MAX_LINKS);
-       if (link >= WL12XX_MAX_LINKS)
+       u8 link = find_first_zero_bit(wl->links_map, wl->num_links);
+       if (link >= wl->num_links)
                return -EBUSY;
 
        wl->session_ids[link] = wlcore_get_new_session_id(wl, link);
@@ -324,9 +324,14 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid)
        __set_bit(link, wlvif->links_map);
        spin_unlock_irqrestore(&wl->wl_lock, flags);
 
-       /* take the last "freed packets" value from the current FW status */
-       wl->links[link].prev_freed_pkts =
-                       wl->fw_status_2->counters.tx_lnk_free_pkts[link];
+       /*
+        * take the last "freed packets" value from the current FW status.
+        * on recovery, we might not have fw_status yet, and
+        * tx_lnk_free_pkts will be NULL. check for it.
+        */
+       if (wl->fw_status->counters.tx_lnk_free_pkts)
+               wl->links[link].prev_freed_pkts =
+                       wl->fw_status->counters.tx_lnk_free_pkts[link];
        wl->links[link].wlvif = wlvif;
 
        /*
@@ -1527,6 +1532,7 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        cmd->sp_len = sta->max_sp;
        cmd->wmm = sta->wme ? 1 : 0;
        cmd->session_id = wl->session_ids[hlid];
+       cmd->role_id = wlvif->role_id;
 
        for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++)
                if (sta->wme && (sta->uapsd_queues & BIT(i)))
@@ -1563,7 +1569,8 @@ out:
        return ret;
 }
 
-int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid)
+int wl12xx_cmd_remove_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                          u8 hlid)
 {
        struct wl12xx_cmd_remove_peer *cmd;
        int ret;
@@ -1581,6 +1588,7 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid)
        /* We never send a deauth, mac80211 is in charge of this */
        cmd->reason_opcode = 0;
        cmd->send_deauth_flag = 0;
+       cmd->role_id = wlvif->role_id;
 
        ret = wl1271_cmd_send(wl, CMD_REMOVE_PEER, cmd, sizeof(*cmd), 0);
        if (ret < 0) {
index 323d4a856e4ba80d37f52fadf0832ad09066996e..b084830a61cf51adbe40b8461fc28510dd13bd5a 100644 (file)
@@ -88,7 +88,8 @@ int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id,
 int wl12xx_croc(struct wl1271 *wl, u8 role_id);
 int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                        struct ieee80211_sta *sta, u8 hlid);
-int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid);
+int wl12xx_cmd_remove_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+                          u8 hlid);
 void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel,
                                     enum ieee80211_band band);
 int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl);
@@ -206,7 +207,7 @@ enum cmd_templ {
 #define WL1271_COMMAND_TIMEOUT     2000
 #define WL1271_CMD_TEMPL_DFLT_SIZE 252
 #define WL1271_CMD_TEMPL_MAX_SIZE  512
-#define WL1271_EVENT_TIMEOUT       1500
+#define WL1271_EVENT_TIMEOUT       5000
 
 struct wl1271_cmd_header {
        __le16 id;
@@ -594,6 +595,8 @@ struct wl12xx_cmd_add_peer {
        u8 sp_len;
        u8 wmm;
        u8 session_id;
+       u8 role_id;
+       u8 padding[3];
 } __packed;
 
 struct wl12xx_cmd_remove_peer {
@@ -602,7 +605,7 @@ struct wl12xx_cmd_remove_peer {
        u8 hlid;
        u8 reason_opcode;
        u8 send_deauth_flag;
-       u8 padding1;
+       u8 role_id;
 } __packed;
 
 /*
index 8d3b34965db3475f64178eeb6463ec2589434c3f..1f9a36031b06d42785352fa3e7666bda3c9f17ce 100644 (file)
@@ -67,7 +67,7 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                u8 hlid;
                struct wl1271_link *lnk;
                for_each_set_bit(hlid, wlvif->ap.sta_hlid_map,
-                                WL12XX_MAX_LINKS) {
+                                wl->num_links) {
                        lnk = &wl->links[hlid];
                        if (!lnk->ba_bitmap)
                                continue;
@@ -172,7 +172,7 @@ static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap)
        const u8 *addr;
        int h;
 
-       for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) {
+       for_each_set_bit(h, &sta_bitmap, wl->num_links) {
                bool found = false;
                /* find the ap vif connected to this sta */
                wl12xx_for_each_wlvif_ap(wl, wlvif) {
index 51f8d634d32f43274d2eaccb679a6dff732468f3..1555ff9700509186e43996ffb4a7c5bbe106522f 100644 (file)
@@ -106,6 +106,15 @@ wlcore_hw_init_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        return 0;
 }
 
+static inline void
+wlcore_hw_convert_fw_status(struct wl1271 *wl, void *raw_fw_status,
+                           struct wl_fw_status *fw_status)
+{
+       BUG_ON(!wl->ops->convert_fw_status);
+
+       wl->ops->convert_fw_status(wl, raw_fw_status, fw_status);
+}
+
 static inline u32
 wlcore_hw_sta_get_ap_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif)
 {
index 7699f9d07e2636e3528fa7e3c28f260bfaa63e3f..199e941208644864e16479c4eb586afe8df69e86 100644 (file)
@@ -287,8 +287,8 @@ static int wl1271_init_sta_beacon_filter(struct wl1271 *wl,
        if (ret < 0)
                return ret;
 
-       /* enable beacon filtering */
-       ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
+       /* disable beacon filtering until we get the first beacon */
+       ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
        if (ret < 0)
                return ret;
 
@@ -462,7 +462,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif)
         * If the basic rates contain OFDM rates, use OFDM only
         * rates for unicast TX as well. Else use all supported rates.
         */
-       if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
+       if (wl->ofdm_only_ap && (wlvif->basic_rate_set & CONF_TX_OFDM_RATES))
                supported_rates = CONF_TX_OFDM_RATES;
        else
                supported_rates = CONF_TX_ENABLED_RATES;
index 07e3d6a049adf33d40dc586c27ef66c4428b83d1..0305729d09868230b2d75a5d5ce3b546b858ced6 100644 (file)
@@ -60,7 +60,9 @@ static inline int __must_check wlcore_raw_write(struct wl1271 *wl, int addr,
 {
        int ret;
 
-       if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags))
+       if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags) ||
+           WARN_ON((test_bit(WL1271_FLAG_IN_ELP, &wl->flags) &&
+                    addr != HW_ACCESS_ELP_CTRL_REG)))
                return -EIO;
 
        ret = wl->if_ops->write(wl->dev, addr, buf, len, fixed);
@@ -76,7 +78,9 @@ static inline int __must_check wlcore_raw_read(struct wl1271 *wl, int addr,
 {
        int ret;
 
-       if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags))
+       if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags) ||
+           WARN_ON((test_bit(WL1271_FLAG_IN_ELP, &wl->flags) &&
+                    addr != HW_ACCESS_ELP_CTRL_REG)))
                return -EIO;
 
        ret = wl->if_ops->read(wl->dev, addr, buf, len, fixed);
index b46b3116cc55c1cf129534af6ecf788996ae0cc0..4175a57ac9f537d1f16e886fd7b071d3c1dfb50c 100644 (file)
@@ -345,24 +345,24 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl,
         * Start high-level PS if the STA is asleep with enough blocks in FW.
         * Make an exception if this is the only connected link. In this
         * case FW-memory congestion is less of a problem.
-        * Note that a single connected STA means 3 active links, since we must
-        * account for the global and broadcast AP links. The "fw_ps" check
-        * assures us the third link is a STA connected to the AP. Otherwise
-        * the FW would not set the PSM bit.
+        * Note that a single connected STA means 2*ap_count + 1 active links,
+        * since we must account for the global and broadcast AP links
+        * for each AP. The "fw_ps" check assures us the other link is a STA
+        * connected to the AP. Otherwise the FW would not set the PSM bit.
         */
-       else if (wl->active_link_count > 3 && fw_ps &&
+       else if (wl->active_link_count > (wl->ap_count*2 + 1) && fw_ps &&
                 tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
 
 static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                                           struct wl12xx_vif *wlvif,
-                                          struct wl_fw_status_2 *status)
+                                          struct wl_fw_status *status)
 {
        u32 cur_fw_ps_map;
        u8 hlid;
 
-       cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap);
+       cur_fw_ps_map = status->link_ps_bitmap;
        if (wl->ap_fw_ps_map != cur_fw_ps_map) {
                wl1271_debug(DEBUG_PSM,
                             "link ps prev 0x%x cur 0x%x changed 0x%x",
@@ -372,77 +372,73 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl,
                wl->ap_fw_ps_map = cur_fw_ps_map;
        }
 
-       for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS)
+       for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, wl->num_links)
                wl12xx_irq_ps_regulate_link(wl, wlvif, hlid,
                                            wl->links[hlid].allocated_pkts);
 }
 
-static int wlcore_fw_status(struct wl1271 *wl,
-                           struct wl_fw_status_1 *status_1,
-                           struct wl_fw_status_2 *status_2)
+static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status)
 {
        struct wl12xx_vif *wlvif;
        struct timespec ts;
        u32 old_tx_blk_count = wl->tx_blocks_available;
        int avail, freed_blocks;
        int i;
-       size_t status_len;
        int ret;
        struct wl1271_link *lnk;
 
-       status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
-               sizeof(*status_2) + wl->fw_status_priv_len;
-
-       ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1,
-                                  status_len, false);
+       ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR,
+                                  wl->raw_fw_status,
+                                  wl->fw_status_len, false);
        if (ret < 0)
                return ret;
 
+       wlcore_hw_convert_fw_status(wl, wl->raw_fw_status, wl->fw_status);
+
        wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, "
                     "drv_rx_counter = %d, tx_results_counter = %d)",
-                    status_1->intr,
-                    status_1->fw_rx_counter,
-                    status_1->drv_rx_counter,
-                    status_1->tx_results_counter);
+                    status->intr,
+                    status->fw_rx_counter,
+                    status->drv_rx_counter,
+                    status->tx_results_counter);
 
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                /* prevent wrap-around in freed-packets counter */
                wl->tx_allocated_pkts[i] -=
-                               (status_2->counters.tx_released_pkts[i] -
+                               (status->counters.tx_released_pkts[i] -
                                wl->tx_pkts_freed[i]) & 0xff;
 
-               wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i];
+               wl->tx_pkts_freed[i] = status->counters.tx_released_pkts[i];
        }
 
 
-       for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) {
+       for_each_set_bit(i, wl->links_map, wl->num_links) {
                u8 diff;
                lnk = &wl->links[i];
 
                /* prevent wrap-around in freed-packets counter */
-               diff = (status_2->counters.tx_lnk_free_pkts[i] -
+               diff = (status->counters.tx_lnk_free_pkts[i] -
                       lnk->prev_freed_pkts) & 0xff;
 
                if (diff == 0)
                        continue;
 
                lnk->allocated_pkts -= diff;
-               lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i];
+               lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[i];
 
                /* accumulate the prev_freed_pkts counter */
                lnk->total_freed_pkts += diff;
        }
 
        /* prevent wrap-around in total blocks counter */
-       if (likely(wl->tx_blocks_freed <=
-                  le32_to_cpu(status_2->total_released_blks)))
-               freed_blocks = le32_to_cpu(status_2->total_released_blks) -
+       if (likely(wl->tx_blocks_freed <= status->total_released_blks))
+               freed_blocks = status->total_released_blks -
                               wl->tx_blocks_freed;
        else
                freed_blocks = 0x100000000LL - wl->tx_blocks_freed +
-                              le32_to_cpu(status_2->total_released_blks);
+                              status->total_released_blks;
 
-       wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks);
+       wl->tx_blocks_freed = status->total_released_blks;
 
        wl->tx_allocated_blocks -= freed_blocks;
 
@@ -458,7 +454,7 @@ static int wlcore_fw_status(struct wl1271 *wl,
                        cancel_delayed_work(&wl->tx_watchdog_work);
        }
 
-       avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks;
+       avail = status->tx_total - wl->tx_allocated_blocks;
 
        /*
         * The FW might change the total number of TX memblocks before
@@ -477,15 +473,15 @@ static int wlcore_fw_status(struct wl1271 *wl,
 
        /* for AP update num of allocated TX blocks per link and ps status */
        wl12xx_for_each_wlvif_ap(wl, wlvif) {
-               wl12xx_irq_update_links_status(wl, wlvif, status_2);
+               wl12xx_irq_update_links_status(wl, wlvif, status);
        }
 
        /* update the host-chipset time offset */
        getnstimeofday(&ts);
        wl->time_offset = (timespec_to_ns(&ts) >> 10) -
-               (s64)le32_to_cpu(status_2->fw_localtime);
+               (s64)(status->fw_localtime);
 
-       wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap);
+       wl->fw_fast_lnk_map = status->link_fast_bitmap;
 
        return 0;
 }
@@ -549,13 +545,13 @@ static int wlcore_irq_locked(struct wl1271 *wl)
                clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags);
                smp_mb__after_clear_bit();
 
-               ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
+               ret = wlcore_fw_status(wl, wl->fw_status);
                if (ret < 0)
                        goto out;
 
                wlcore_hw_tx_immediate_compl(wl);
 
-               intr = le32_to_cpu(wl->fw_status_1->intr);
+               intr = wl->fw_status->intr;
                intr &= WLCORE_ALL_INTR_MASK;
                if (!intr) {
                        done = true;
@@ -584,7 +580,7 @@ static int wlcore_irq_locked(struct wl1271 *wl)
                if (likely(intr & WL1271_ACX_INTR_DATA)) {
                        wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA");
 
-                       ret = wlcore_rx(wl, wl->fw_status_1);
+                       ret = wlcore_rx(wl, wl->fw_status);
                        if (ret < 0)
                                goto out;
 
@@ -786,10 +782,11 @@ out:
 
 void wl12xx_queue_recovery_work(struct wl1271 *wl)
 {
-       WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
-
        /* Avoid a recursive recovery */
        if (wl->state == WLCORE_STATE_ON) {
+               WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY,
+                                 &wl->flags));
+
                wl->state = WLCORE_STATE_RESTARTING;
                set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
                wl1271_ps_elp_wakeup(wl);
@@ -843,11 +840,11 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl)
                wl12xx_cmd_stop_fwlog(wl);
 
        /* Read the first memory block address */
-       ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2);
+       ret = wlcore_fw_status(wl, wl->fw_status);
        if (ret < 0)
                goto out;
 
-       addr = le32_to_cpu(wl->fw_status_2->log_start_addr);
+       addr = wl->fw_status->log_start_addr;
        if (!addr)
                goto out;
 
@@ -990,23 +987,23 @@ static int wlcore_fw_wakeup(struct wl1271 *wl)
 
 static int wl1271_setup(struct wl1271 *wl)
 {
-       wl->fw_status_1 = kzalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) +
-                                 sizeof(*wl->fw_status_2) +
-                                 wl->fw_status_priv_len, GFP_KERNEL);
-       if (!wl->fw_status_1)
-               return -ENOMEM;
+       wl->raw_fw_status = kzalloc(wl->fw_status_len, GFP_KERNEL);
+       if (!wl->raw_fw_status)
+               goto err;
 
-       wl->fw_status_2 = (struct wl_fw_status_2 *)
-                               (((u8 *) wl->fw_status_1) +
-                               WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc));
+       wl->fw_status = kzalloc(sizeof(*wl->fw_status), GFP_KERNEL);
+       if (!wl->fw_status)
+               goto err;
 
        wl->tx_res_if = kzalloc(sizeof(*wl->tx_res_if), GFP_KERNEL);
-       if (!wl->tx_res_if) {
-               kfree(wl->fw_status_1);
-               return -ENOMEM;
-       }
+       if (!wl->tx_res_if)
+               goto err;
 
        return 0;
+err:
+       kfree(wl->fw_status);
+       kfree(wl->raw_fw_status);
+       return -ENOMEM;
 }
 
 static int wl12xx_set_power_on(struct wl1271 *wl)
@@ -1767,6 +1764,12 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
        flush_work(&wl->tx_work);
        flush_delayed_work(&wl->elp_work);
 
+       /*
+        * Cancel the watchdog even if above tx_flush failed. We will detect
+        * it on resume anyway.
+        */
+       cancel_delayed_work(&wl->tx_watchdog_work);
+
        return 0;
 }
 
@@ -1824,6 +1827,13 @@ static int wl1271_op_resume(struct ieee80211_hw *hw)
 
 out:
        wl->wow_enabled = false;
+
+       /*
+        * Set a flag to re-init the watchdog on the first Tx after resume.
+        * That way we avoid possible conditions where Tx-complete interrupts
+        * fail to arrive and we perform a spurious recovery.
+        */
+       set_bit(WL1271_FLAG_REINIT_TX_WDOG, &wl->flags);
        mutex_unlock(&wl->mutex);
 
        return 0;
@@ -1914,6 +1924,7 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
        memset(wl->links_map, 0, sizeof(wl->links_map));
        memset(wl->roc_map, 0, sizeof(wl->roc_map));
        memset(wl->session_ids, 0, sizeof(wl->session_ids));
+       memset(wl->rx_filter_enabled, 0, sizeof(wl->rx_filter_enabled));
        wl->active_sta_count = 0;
        wl->active_link_count = 0;
 
@@ -1938,9 +1949,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
 
        wl1271_debugfs_reset(wl);
 
-       kfree(wl->fw_status_1);
-       wl->fw_status_1 = NULL;
-       wl->fw_status_2 = NULL;
+       kfree(wl->raw_fw_status);
+       wl->raw_fw_status = NULL;
+       kfree(wl->fw_status);
+       wl->fw_status = NULL;
        kfree(wl->tx_res_if);
        wl->tx_res_if = NULL;
        kfree(wl->target_mem_map);
@@ -2571,10 +2583,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
                ieee80211_scan_completed(wl->hw, true);
        }
 
-       if (wl->sched_vif == wlvif) {
-               ieee80211_sched_scan_stopped(wl->hw);
+       if (wl->sched_vif == wlvif)
                wl->sched_vif = NULL;
-       }
 
        if (wl->roc_vif == vif) {
                wl->roc_vif = NULL;
@@ -2931,6 +2941,11 @@ static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif)
                ret = wl1271_acx_keep_alive_mode(wl, wlvif, false);
                if (ret < 0)
                        return ret;
+
+               /* disable beacon filtering */
+               ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
+               if (ret < 0)
+                       return ret;
        }
 
        if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) {
@@ -3463,6 +3478,10 @@ static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw,
        wl1271_debug(DEBUG_MAC80211, "mac80211 set default key idx %d",
                     key_idx);
 
+       /* we don't handle unsetting of default key */
+       if (key_idx == -1)
+               return;
+
        mutex_lock(&wl->mutex);
 
        if (unlikely(wl->state != WLCORE_STATE_ON)) {
@@ -3649,8 +3668,8 @@ out:
        return ret;
 }
 
-static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
-                                     struct ieee80211_vif *vif)
+static int wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif)
 {
        struct wl1271 *wl = hw->priv;
        struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
@@ -3672,6 +3691,8 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
        wl1271_ps_elp_sleep(wl);
 out:
        mutex_unlock(&wl->mutex);
+
+       return 0;
 }
 
 static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
@@ -4298,6 +4319,13 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl,
                }
        }
 
+       if ((changed & BSS_CHANGED_BEACON_INFO) && bss_conf->dtim_period) {
+               /* enable beacon filtering */
+               ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true);
+               if (ret < 0)
+                       goto out;
+       }
+
        ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed);
        if (ret < 0)
                goto out;
@@ -4651,7 +4679,7 @@ static int wl1271_allocate_sta(struct wl1271 *wl,
        int ret;
 
 
-       if (wl->active_sta_count >= AP_MAX_STATIONS) {
+       if (wl->active_sta_count >= wl->max_ap_stations) {
                wl1271_warning("could not allocate HLID - too much stations");
                return -EBUSY;
        }
@@ -4754,7 +4782,7 @@ static int wl12xx_sta_remove(struct wl1271 *wl,
        if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map)))
                return -EINVAL;
 
-       ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid);
+       ret = wl12xx_cmd_remove_peer(wl, wlvif, wl_sta->hlid);
        if (ret < 0)
                return ret;
 
@@ -5679,28 +5707,6 @@ static void wl1271_unregister_hw(struct wl1271 *wl)
 
 }
 
-static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
-       {
-               .max = 3,
-               .types = BIT(NL80211_IFTYPE_STATION),
-       },
-       {
-               .max = 1,
-               .types = BIT(NL80211_IFTYPE_AP) |
-                        BIT(NL80211_IFTYPE_P2P_GO) |
-                        BIT(NL80211_IFTYPE_P2P_CLIENT),
-       },
-};
-
-static struct ieee80211_iface_combination
-wlcore_iface_combinations[] = {
-       {
-         .max_interfaces = 3,
-         .limits = wlcore_iface_limits,
-         .n_limits = ARRAY_SIZE(wlcore_iface_limits),
-       },
-};
-
 static int wl1271_init_ieee80211(struct wl1271 *wl)
 {
        int i;
@@ -5733,7 +5739,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
                IEEE80211_HW_AP_LINK_PS |
                IEEE80211_HW_AMPDU_AGGREGATION |
                IEEE80211_HW_TX_AMPDU_SETUP_IN_HW |
-               IEEE80211_HW_QUEUE_CONTROL;
+               IEEE80211_HW_QUEUE_CONTROL |
+               IEEE80211_HW_CHANCTX_STA_CSA;
 
        wl->hw->wiphy->cipher_suites = cipher_suites;
        wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
@@ -5821,10 +5828,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
                NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
 
        /* allowed interface combinations */
-       wlcore_iface_combinations[0].num_different_channels = wl->num_channels;
-       wl->hw->wiphy->iface_combinations = wlcore_iface_combinations;
-       wl->hw->wiphy->n_iface_combinations =
-               ARRAY_SIZE(wlcore_iface_combinations);
+       wl->hw->wiphy->iface_combinations = wl->iface_combinations;
+       wl->hw->wiphy->n_iface_combinations = wl->n_iface_combinations;
 
        SET_IEEE80211_DEV(wl->hw, wl->dev);
 
@@ -5844,8 +5849,6 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
        int i, j, ret;
        unsigned int order;
 
-       BUILD_BUG_ON(AP_MAX_STATIONS > WL12XX_MAX_LINKS);
-
        hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
        if (!hw) {
                wl1271_error("could not alloc ieee80211_hw");
@@ -5867,8 +5870,12 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size,
 
        wl->hw = hw;
 
+       /*
+        * wl->num_links is not configured yet, so just use WLCORE_MAX_LINKS.
+        * we don't allocate any additional resource here, so that's fine.
+        */
        for (i = 0; i < NUM_TX_QUEUES; i++)
-               for (j = 0; j < WL12XX_MAX_LINKS; j++)
+               for (j = 0; j < WLCORE_MAX_LINKS; j++)
                        skb_queue_head_init(&wl->links[j].tx_queue[i]);
 
        skb_queue_head_init(&wl->deferred_rx_queue);
@@ -6011,7 +6018,8 @@ int wlcore_free_hw(struct wl1271 *wl)
        kfree(wl->nvs);
        wl->nvs = NULL;
 
-       kfree(wl->fw_status_1);
+       kfree(wl->raw_fw_status);
+       kfree(wl->fw_status);
        kfree(wl->tx_res_if);
        destroy_workqueue(wl->freezable_wq);
 
index 26bfc365ba70bf03e215ddc0be7df0d01c13512b..b52516eed7b20302b618fcbe4e7934651e12646e 100644 (file)
@@ -280,7 +280,11 @@ void wl12xx_ps_link_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
        struct ieee80211_sta *sta;
        struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
 
-       if (test_bit(hlid, &wl->ap_ps_map))
+       if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS))
+               return;
+
+       if (!test_bit(hlid, wlvif->ap.sta_hlid_map) ||
+           test_bit(hlid, &wl->ap_ps_map))
                return;
 
        wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d pkts %d "
index 6791a1a6afba06702b434b1ae11a196377a0d229..e125974285cc890e0b40671886815c996ef37a11 100644 (file)
@@ -203,9 +203,9 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
        return is_data;
 }
 
-int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status)
+int wlcore_rx(struct wl1271 *wl, struct wl_fw_status *status)
 {
-       unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
+       unsigned long active_hlids[BITS_TO_LONGS(WLCORE_MAX_LINKS)] = {0};
        u32 buf_size;
        u32 fw_rx_counter = status->fw_rx_counter % wl->num_rx_desc;
        u32 drv_rx_counter = wl->rx_counter % wl->num_rx_desc;
@@ -263,12 +263,12 @@ int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status)
                                                  wl->aggr_buf + pkt_offset,
                                                  pkt_len, rx_align,
                                                  &hlid) == 1) {
-                               if (hlid < WL12XX_MAX_LINKS)
+                               if (hlid < wl->num_links)
                                        __set_bit(hlid, active_hlids);
                                else
                                        WARN(1,
-                                            "hlid exceeded WL12XX_MAX_LINKS "
-                                            "(%d)\n", hlid);
+                                            "hlid (%d) exceeded MAX_LINKS\n",
+                                            hlid);
                        }
 
                        wl->rx_counter++;
@@ -302,7 +302,7 @@ int wl1271_rx_filter_enable(struct wl1271 *wl,
 {
        int ret;
 
-       if (wl->rx_filter_enabled[index] == enable) {
+       if (!!test_bit(index, wl->rx_filter_enabled) == enable) {
                wl1271_warning("Request to enable an already "
                             "enabled rx filter %d", index);
                return 0;
@@ -316,7 +316,10 @@ int wl1271_rx_filter_enable(struct wl1271 *wl,
                return ret;
        }
 
-       wl->rx_filter_enabled[index] = enable;
+       if (enable)
+               __set_bit(index, wl->rx_filter_enabled);
+       else
+               __clear_bit(index, wl->rx_filter_enabled);
 
        return 0;
 }
@@ -326,7 +329,7 @@ int wl1271_rx_filter_clear_all(struct wl1271 *wl)
        int i, ret = 0;
 
        for (i = 0; i < WL1271_MAX_RX_FILTERS; i++) {
-               if (!wl->rx_filter_enabled[i])
+               if (!test_bit(i, wl->rx_filter_enabled))
                        continue;
                ret = wl1271_rx_filter_enable(wl, i, 0, NULL);
                if (ret)
index 3363f60fb7da6dfb05ad672b359ac5d037fbb401..a3b1618db27c202db4377c8e529aee4751c8c32a 100644 (file)
@@ -142,7 +142,7 @@ struct wl1271_rx_descriptor {
        u8  reserved;
 } __packed;
 
-int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status);
+int wlcore_rx(struct wl1271 *wl, struct wl_fw_status *status);
 u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band);
 int wl1271_rx_filter_enable(struct wl1271 *wl,
                            int index, bool enable,
index 87cd707affa240390f6b34ea0d1b28caf23ffb83..40b43115f83590b6a6cb4a38d8283e7b78460187 100644 (file)
@@ -101,7 +101,7 @@ static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl,
         * authentication response. this way it won't get de-authed by FW
         * when transmitting too soon.
         */
-       wl1271_acx_set_inconnection_sta(wl, hdr->addr1);
+       wl1271_acx_set_inconnection_sta(wl, wlvif, hdr->addr1);
 
        /*
         * ROC for 1 second on the AP channel for completing the connection.
@@ -134,12 +134,12 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl,
         * into high-level PS and clean out its TX queues.
         * Make an exception if this is the only connected link. In this
         * case FW-memory congestion is less of a problem.
-        * Note that a single connected STA means 3 active links, since we must
-        * account for the global and broadcast AP links. The "fw_ps" check
-        * assures us the third link is a STA connected to the AP. Otherwise
-        * the FW would not set the PSM bit.
+        * Note that a single connected STA means 2*ap_count + 1 active links,
+        * since we must account for the global and broadcast AP links
+        * for each AP. The "fw_ps" check assures us the other link is a STA
+        * connected to the AP. Otherwise the FW would not set the PSM bit.
         */
-       if (wl->active_link_count > 3 && fw_ps &&
+       if (wl->active_link_count > (wl->ap_count*2 + 1) && fw_ps &&
            tx_pkts >= WL1271_PS_STA_MAX_PACKETS)
                wl12xx_ps_link_start(wl, wlvif, hlid, true);
 }
@@ -234,8 +234,13 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                wl->tx_blocks_available -= total_blocks;
                wl->tx_allocated_blocks += total_blocks;
 
-               /* If the FW was empty before, arm the Tx watchdog */
-               if (wl->tx_allocated_blocks == total_blocks)
+               /*
+                * If the FW was empty before, arm the Tx watchdog. Also do
+                * this on the first Tx after resume, as we always cancel the
+                * watchdog on suspend.
+                */
+               if (wl->tx_allocated_blocks == total_blocks ||
+                   test_and_clear_bit(WL1271_FLAG_REINIT_TX_WDOG, &wl->flags))
                        wl12xx_rearm_tx_watchdog_locked(wl);
 
                ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
@@ -357,6 +362,10 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
            ieee80211_has_protected(frame_control))
                tx_attr |= TX_HW_ATTR_HOST_ENCRYPT;
 
+       /* send EAPOL frames as voice */
+       if (control->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)
+               tx_attr |= TX_HW_ATTR_EAPOL_FRAME;
+
        desc->tx_attr = cpu_to_le16(tx_attr);
 
        wlcore_hw_set_tx_desc_csum(wl, desc, skb);
@@ -560,11 +569,11 @@ static struct sk_buff *wlcore_vif_dequeue_high_prio(struct wl1271 *wl,
        int i, h, start_hlid;
 
        /* start from the link after the last one */
-       start_hlid = (wlvif->last_tx_hlid + 1) % WL12XX_MAX_LINKS;
+       start_hlid = (wlvif->last_tx_hlid + 1) % wl->num_links;
 
        /* dequeue according to AC, round robin on each link */
-       for (i = 0; i < WL12XX_MAX_LINKS; i++) {
-               h = (start_hlid + i) % WL12XX_MAX_LINKS;
+       for (i = 0; i < wl->num_links; i++) {
+               h = (start_hlid + i) % wl->num_links;
 
                /* only consider connected stations */
                if (!test_bit(h, wlvif->links_map))
@@ -688,8 +697,8 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
                skb_queue_head(&wl->links[hlid].tx_queue[q], skb);
 
                /* make sure we dequeue the same packet next time */
-               wlvif->last_tx_hlid = (hlid + WL12XX_MAX_LINKS - 1) %
-                                     WL12XX_MAX_LINKS;
+               wlvif->last_tx_hlid = (hlid + wl->num_links - 1) %
+                                     wl->num_links;
        }
 
        spin_lock_irqsave(&wl->wl_lock, flags);
@@ -722,7 +731,7 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids)
        timeout = wl->conf.rx_streaming.duration;
        wl12xx_for_each_wlvif_sta(wl, wlvif) {
                bool found = false;
-               for_each_set_bit(hlid, active_hlids, WL12XX_MAX_LINKS) {
+               for_each_set_bit(hlid, active_hlids, wl->num_links) {
                        if (test_bit(hlid, wlvif->links_map)) {
                                found  = true;
                                break;
@@ -759,7 +768,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
        struct wl1271_tx_hw_descr *desc;
        u32 buf_offset = 0, last_len = 0;
        bool sent_packets = false;
-       unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
+       unsigned long active_hlids[BITS_TO_LONGS(WLCORE_MAX_LINKS)] = {0};
        int ret = 0;
        int bus_ret = 0;
        u8 hlid;
@@ -1061,7 +1070,7 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)
        int i;
 
        /* TX failure */
-       for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) {
+       for_each_set_bit(i, wlvif->links_map, wl->num_links) {
                if (wlvif->bss_type == BSS_TYPE_AP_BSS &&
                    i != wlvif->ap.bcast_hlid && i != wlvif->ap.global_hlid) {
                        /* this calls wl12xx_free_link */
@@ -1085,7 +1094,7 @@ void wl12xx_tx_reset(struct wl1271 *wl)
 
        /* only reset the queues if something bad happened */
        if (wl1271_tx_total_queue_count(wl) != 0) {
-               for (i = 0; i < WL12XX_MAX_LINKS; i++)
+               for (i = 0; i < wl->num_links; i++)
                        wl1271_tx_reset_link_queues(wl, i);
 
                for (i = 0; i < NUM_TX_QUEUES; i++)
@@ -1178,7 +1187,7 @@ void wl1271_tx_flush(struct wl1271 *wl)
                       WL1271_TX_FLUSH_TIMEOUT / 1000);
 
        /* forcibly flush all Tx buffers on our queues */
-       for (i = 0; i < WL12XX_MAX_LINKS; i++)
+       for (i = 0; i < wl->num_links; i++)
                wl1271_tx_reset_link_queues(wl, i);
 
 out_wake:
index 35489c300da17bfefe3b35fc7515dc58aeeaa196..79cb3ff8b71f576aef0913f0a8828a95e3ede130 100644 (file)
@@ -37,6 +37,7 @@
 #define TX_HW_ATTR_TX_CMPLT_REQ          BIT(12)
 #define TX_HW_ATTR_TX_DUMMY_REQ          BIT(13)
 #define TX_HW_ATTR_HOST_ENCRYPT          BIT(14)
+#define TX_HW_ATTR_EAPOL_FRAME           BIT(15)
 
 #define TX_HW_ATTR_OFST_SAVE_RETRIES     0
 #define TX_HW_ATTR_OFST_HEADER_PAD       1
index 06efc12a39e5175dfde449843ffbb5a7c50b157d..95a54504f0cc3815831d212906002faf6e0e8904 100644 (file)
@@ -73,6 +73,8 @@ struct wlcore_ops {
        void (*tx_immediate_compl)(struct wl1271 *wl);
        int (*hw_init)(struct wl1271 *wl);
        int (*init_vif)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
+       void (*convert_fw_status)(struct wl1271 *wl, void *raw_fw_status,
+                                 struct wl_fw_status *fw_status);
        u32 (*sta_get_ap_rate_mask)(struct wl1271 *wl,
                                    struct wl12xx_vif *wlvif);
        int (*get_pg_ver)(struct wl1271 *wl, s8 *ver);
@@ -220,7 +222,7 @@ struct wl1271 {
        int channel;
        u8 system_hlid;
 
-       unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
+       unsigned long links_map[BITS_TO_LONGS(WLCORE_MAX_LINKS)];
        unsigned long roles_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
        unsigned long roc_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)];
        unsigned long rate_policies_map[
@@ -228,7 +230,7 @@ struct wl1271 {
        unsigned long klv_templates_map[
                        BITS_TO_LONGS(WLCORE_MAX_KLV_TEMPLATES)];
 
-       u8 session_ids[WL12XX_MAX_LINKS];
+       u8 session_ids[WLCORE_MAX_LINKS];
 
        struct list_head wlvif_list;
 
@@ -346,8 +348,8 @@ struct wl1271 {
        u32 buffer_cmd;
        u32 buffer_busyword[WL1271_BUSY_WORD_CNT];
 
-       struct wl_fw_status_1 *fw_status_1;
-       struct wl_fw_status_2 *fw_status_2;
+       void *raw_fw_status;
+       struct wl_fw_status *fw_status;
        struct wl1271_tx_hw_res_if *tx_res_if;
 
        /* Current chipset configuration */
@@ -376,7 +378,7 @@ struct wl1271 {
         * AP-mode - links indexed by HLID. The global and broadcast links
         * are always active.
         */
-       struct wl1271_link links[WL12XX_MAX_LINKS];
+       struct wl1271_link links[WLCORE_MAX_LINKS];
 
        /* number of currently active links */
        int active_link_count;
@@ -405,6 +407,9 @@ struct wl1271 {
        /* AP-mode - number of currently connected stations */
        int active_sta_count;
 
+       /* Flag determining whether AP should broadcast OFDM-only rates */
+       bool ofdm_only_ap;
+
        /* last wlvif we transmitted from */
        struct wl12xx_vif *last_wlvif;
 
@@ -434,6 +439,10 @@ struct wl1271 {
        u32 num_tx_desc;
        /* number of RX descriptors the HW supports. */
        u32 num_rx_desc;
+       /* number of links the HW supports */
+       u8 num_links;
+       /* max stations a single AP can support */
+       u8 max_ap_stations;
 
        /* translate HW Tx rates to standard rate-indices */
        const u8 **band_rate_to_idx;
@@ -448,10 +457,11 @@ struct wl1271 {
        struct ieee80211_sta_ht_cap ht_cap[WLCORE_NUM_BANDS];
 
        /* size of the private FW status data */
+       size_t fw_status_len;
        size_t fw_status_priv_len;
 
        /* RX Data filter rule state - enabled/disabled */
-       bool rx_filter_enabled[WL1271_MAX_RX_FILTERS];
+       unsigned long rx_filter_enabled[BITS_TO_LONGS(WL1271_MAX_RX_FILTERS)];
 
        /* size of the private static data */
        size_t static_data_priv_len;
@@ -476,8 +486,9 @@ struct wl1271 {
 
        struct completion nvs_loading_complete;
 
-       /* number of concurrent channels the HW supports */
-       u32 num_channels;
+       /* interface combinations supported by the hw */
+       const struct ieee80211_iface_combination *iface_combinations;
+       u8 n_iface_combinations;
 };
 
 int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
index ce7261ce8b59a244837618a07ecf174762f04946..756e890bc5ee2be0a5f68ef3e37de397ccc8dd52 100644 (file)
 #define WL1271_DEFAULT_DTIM_PERIOD 1
 
 #define WL12XX_MAX_ROLES           4
-#define WL12XX_MAX_LINKS           12
 #define WL12XX_INVALID_ROLE_ID     0xff
 #define WL12XX_INVALID_LINK_ID     0xff
 
+/*
+ * max number of links allowed by all HWs.
+ * this is NOT the actual max links supported by the current hw.
+ */
+#define WLCORE_MAX_LINKS 16
+
 /* the driver supports the 2.4Ghz and 5Ghz bands */
 #define WLCORE_NUM_BANDS           2
 
@@ -118,72 +123,58 @@ struct wl1271_chip {
 
 #define NUM_TX_QUEUES              4
 
-#define AP_MAX_STATIONS            8
-
-struct wl_fw_packet_counters {
-       /* Cumulative counter of released packets per AC */
-       u8 tx_released_pkts[NUM_TX_QUEUES];
-
-       /* Cumulative counter of freed packets per HLID */
-       u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS];
-
-       /* Cumulative counter of released Voice memory blocks */
-       u8 tx_voice_released_blks;
-
-       /* Tx rate of the last transmitted packet */
-       u8 tx_last_rate;
-
-       u8 padding[2];
-} __packed;
-
-/* FW status registers */
-struct wl_fw_status_1 {
-       __le32 intr;
+struct wl_fw_status {
+       u32 intr;
        u8  fw_rx_counter;
        u8  drv_rx_counter;
-       u8  reserved;
        u8  tx_results_counter;
-       __le32 rx_pkt_descs[0];
-} __packed;
-
-/*
- * Each HW arch has a different number of Rx descriptors.
- * The length of the status depends on it, since it holds an array
- * of descriptors.
- */
-#define WLCORE_FW_STATUS_1_LEN(num_rx_desc) \
-               (sizeof(struct wl_fw_status_1) + \
-               (sizeof(((struct wl_fw_status_1 *)0)->rx_pkt_descs[0])) * \
-               num_rx_desc)
+       __le32 *rx_pkt_descs;
 
-struct wl_fw_status_2 {
-       __le32 fw_localtime;
+       u32 fw_localtime;
 
        /*
         * A bitmap (where each bit represents a single HLID)
         * to indicate if the station is in PS mode.
         */
-       __le32 link_ps_bitmap;
+       u32 link_ps_bitmap;
 
        /*
         * A bitmap (where each bit represents a single HLID) to indicate
         * if the station is in Fast mode
         */
-       __le32 link_fast_bitmap;
+       u32 link_fast_bitmap;
 
        /* Cumulative counter of total released mem blocks since FW-reset */
-       __le32 total_released_blks;
+       u32 total_released_blks;
 
        /* Size (in Memory Blocks) of TX pool */
-       __le32 tx_total;
+       u32 tx_total;
+
+       struct {
+               /*
+                * Cumulative counter of released packets per AC
+                * (length of the array is NUM_TX_QUEUES)
+                */
+               u8 *tx_released_pkts;
 
-       struct wl_fw_packet_counters counters;
+               /*
+                * Cumulative counter of freed packets per HLID
+                * (length of the array is wl->num_links)
+                */
+               u8 *tx_lnk_free_pkts;
+
+               /* Cumulative counter of released Voice memory blocks */
+               u8 tx_voice_released_blks;
 
-       __le32 log_start_addr;
+               /* Tx rate of the last transmitted packet */
+               u8 tx_last_rate;
+       } counters;
+
+       u32 log_start_addr;
 
        /* Private status to be used by the lower drivers */
-       u8 priv[0];
-} __packed;
+       void *priv;
+};
 
 #define WL1271_MAX_CHANNELS 64
 struct wl1271_scan {
@@ -240,6 +231,7 @@ enum wl12xx_flags {
        WL1271_FLAG_VIF_CHANGE_IN_PROGRESS,
        WL1271_FLAG_INTENDED_FW_RECOVERY,
        WL1271_FLAG_IO_FAILED,
+       WL1271_FLAG_REINIT_TX_WDOG,
 };
 
 enum wl12xx_vif_flags {
@@ -368,7 +360,7 @@ struct wl12xx_vif {
 
                        /* HLIDs bitmap of associated stations */
                        unsigned long sta_hlid_map[BITS_TO_LONGS(
-                                                       WL12XX_MAX_LINKS)];
+                                                       WLCORE_MAX_LINKS)];
 
                        /* recoreded keys - set here before AP startup */
                        struct wl1271_ap_key *recorded_keys[MAX_NUM_KEYS];
@@ -385,7 +377,7 @@ struct wl12xx_vif {
        /* counters of packets per AC, across all links in the vif */
        int tx_queue_count[NUM_TX_QUEUES];
 
-       unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)];
+       unsigned long links_map[BITS_TO_LONGS(WLCORE_MAX_LINKS)];
 
        u8 ssid[IEEE80211_MAX_SSID_LEN + 1];
        u8 ssid_len;
index 3737f7218f51362ea9cf1991a66c0328143741b8..7bb6148d990fbdd89303d6edd5e5491c02095f39 100644 (file)
@@ -23,6 +23,7 @@
 #define TEMP_MINOR             131     /* Temperature Sensor */
 #define RTC_MINOR              135
 #define EFI_RTC_MINOR          136     /* EFI Time services */
+#define VHCI_MINOR             137
 #define SUN_OPENPROM_MINOR     139
 #define DMAPI_MINOR            140     /* DMAPI */
 #define NVRAM_MINOR            144
index 90b4fdc8a61f14fcd8753bc5d7d55d89924e9302..4781d7b27dd39d9930164592d008aa1ac22772c3 100644 (file)
@@ -518,9 +518,9 @@ extern void tty_port_put(struct tty_port *port);
 
 static inline struct tty_port *tty_port_get(struct tty_port *port)
 {
-       if (port)
-               kref_get(&port->kref);
-       return port;
+       if (port && kref_get_unless_zero(&port->kref))
+               return port;
+       return NULL;
 }
 
 /* If the cts flow control is enabled, return true. */
index f4f9ee466791a9b9fc0b0d7f74f22e2cf3660ac2..904777c1cd2420486a3df25636f61811ab35c29e 100644 (file)
@@ -65,6 +65,7 @@ struct bt_security {
 #define BT_SECURITY_LOW                1
 #define BT_SECURITY_MEDIUM     2
 #define BT_SECURITY_HIGH       3
+#define BT_SECURITY_FIPS       4
 
 #define BT_DEFER_SETUP 7
 
index 66c1cd87bfe7f9b9d117db340f736884f9d0c4c1..fe4b06bfc150f5a5a8dcd4a421dac8d8fdbb4d54 100644 (file)
@@ -117,11 +117,16 @@ enum {
        HCI_SERVICE_CACHE,
        HCI_DEBUG_KEYS,
        HCI_DUT_MODE,
+       HCI_FORCE_SC,
+       HCI_FORCE_STATIC_ADDR,
        HCI_UNREGISTER,
        HCI_USER_CHANNEL,
 
        HCI_LE_SCAN,
        HCI_SSP_ENABLED,
+       HCI_SC_ENABLED,
+       HCI_SC_ONLY,
+       HCI_RPA_RESOLVING,
        HCI_HS_ENABLED,
        HCI_LE_ENABLED,
        HCI_ADVERTISING,
@@ -282,10 +287,14 @@ enum {
 #define LMP_SYNC_TRAIN 0x04
 #define LMP_SYNC_SCAN  0x08
 
+#define LMP_SC         0x01
+#define LMP_PING       0x02
+
 /* Host features */
 #define LMP_HOST_SSP           0x01
 #define LMP_HOST_LE            0x02
 #define LMP_HOST_LE_BREDR      0x04
+#define LMP_HOST_SC            0x08
 
 /* Connection modes */
 #define HCI_CM_ACTIVE  0x0000
@@ -307,6 +316,7 @@ enum {
 #define HCI_LM_TRUSTED 0x0008
 #define HCI_LM_RELIABLE        0x0010
 #define HCI_LM_SECURE  0x0020
+#define HCI_LM_FIPS    0x0040
 
 /* Authentication types */
 #define HCI_AT_NO_BONDING              0x00
@@ -327,15 +337,21 @@ enum {
 #define HCI_LK_LOCAL_UNIT              0x01
 #define HCI_LK_REMOTE_UNIT             0x02
 #define HCI_LK_DEBUG_COMBINATION       0x03
-#define HCI_LK_UNAUTH_COMBINATION      0x04
-#define HCI_LK_AUTH_COMBINATION                0x05
+#define HCI_LK_UNAUTH_COMBINATION_P192 0x04
+#define HCI_LK_AUTH_COMBINATION_P192   0x05
 #define HCI_LK_CHANGED_COMBINATION     0x06
+#define HCI_LK_UNAUTH_COMBINATION_P256 0x07
+#define HCI_LK_AUTH_COMBINATION_P256   0x08
 /* The spec doesn't define types for SMP keys, the _MASTER suffix is implied */
 #define HCI_SMP_STK                    0x80
 #define HCI_SMP_STK_SLAVE              0x81
 #define HCI_SMP_LTK                    0x82
 #define HCI_SMP_LTK_SLAVE              0x83
 
+/* Long Term Key types */
+#define HCI_LTK_UNAUTH                 0x00
+#define HCI_LTK_AUTH                   0x01
+
 /* ---- HCI Error Codes ---- */
 #define HCI_ERROR_AUTH_FAILURE         0x05
 #define HCI_ERROR_CONNECTION_TIMEOUT   0x08
@@ -660,6 +676,15 @@ struct hci_rp_set_csb {
 
 #define HCI_OP_START_SYNC_TRAIN                0x0443
 
+#define HCI_OP_REMOTE_OOB_EXT_DATA_REPLY       0x0445
+struct hci_cp_remote_oob_ext_data_reply {
+       bdaddr_t bdaddr;
+       __u8     hash192[16];
+       __u8     randomizer192[16];
+       __u8     hash256[16];
+       __u8     randomizer256[16];
+} __packed;
+
 #define HCI_OP_SNIFF_MODE              0x0803
 struct hci_cp_sniff_mode {
        __le16   handle;
@@ -933,6 +958,26 @@ struct hci_rp_write_sync_train_params {
        __le16  sync_train_int;
 } __packed;
 
+#define HCI_OP_READ_SC_SUPPORT         0x0c79
+struct hci_rp_read_sc_support {
+       __u8    status;
+       __u8    support;
+} __packed;
+
+#define HCI_OP_WRITE_SC_SUPPORT                0x0c7a
+struct hci_cp_write_sc_support {
+       __u8    support;
+} __packed;
+
+#define HCI_OP_READ_LOCAL_OOB_EXT_DATA 0x0c7d
+struct hci_rp_read_local_oob_ext_data {
+       __u8     status;
+       __u8     hash192[16];
+       __u8     randomizer192[16];
+       __u8     hash256[16];
+       __u8     randomizer256[16];
+} __packed;
+
 #define HCI_OP_READ_LOCAL_VERSION      0x1001
 struct hci_rp_read_local_version {
        __u8     status;
index f2f0cf5865c40a9b92f7a98eb4c53c6834a34d29..c0fcc041fbb5e36b70306a4872de05207ad193c1 100644 (file)
@@ -101,7 +101,15 @@ struct smp_ltk {
        __le16 ediv;
        u8 rand[8];
        u8 val[16];
-} __packed;
+};
+
+struct smp_irk {
+       struct list_head list;
+       bdaddr_t rpa;
+       bdaddr_t bdaddr;
+       u8 addr_type;
+       u8 val[16];
+};
 
 struct link_key {
        struct list_head list;
@@ -114,8 +122,10 @@ struct link_key {
 struct oob_data {
        struct list_head list;
        bdaddr_t bdaddr;
-       u8 hash[16];
-       u8 randomizer[16];
+       u8 hash192[16];
+       u8 randomizer192[16];
+       u8 hash256[16];
+       u8 randomizer256[16];
 };
 
 #define HCI_MAX_SHORT_NAME_LENGTH      10
@@ -141,6 +151,7 @@ struct hci_dev {
        __u8            bus;
        __u8            dev_type;
        bdaddr_t        bdaddr;
+       bdaddr_t        random_addr;
        bdaddr_t        static_addr;
        __u8            own_addr_type;
        __u8            dev_name[HCI_MAX_NAME_LENGTH];
@@ -167,6 +178,7 @@ struct hci_dev {
        __u16           page_scan_interval;
        __u16           page_scan_window;
        __u8            page_scan_type;
+       __u8            le_adv_channel_map;
        __u16           le_scan_interval;
        __u16           le_scan_window;
        __u16           le_conn_min_interval;
@@ -257,19 +269,19 @@ struct hci_dev {
        __u32                   req_status;
        __u32                   req_result;
 
-       struct list_head        mgmt_pending;
+       struct crypto_blkcipher *tfm_aes;
 
        struct discovery_state  discovery;
        struct hci_conn_hash    conn_hash;
-       struct list_head        blacklist;
 
+       struct list_head        mgmt_pending;
+       struct list_head        blacklist;
        struct list_head        uuids;
-
        struct list_head        link_keys;
-
        struct list_head        long_term_keys;
-
+       struct list_head        identity_resolving_keys;
        struct list_head        remote_oob_data;
+       struct list_head        le_conn_params;
 
        struct hci_dev_stats    stat;
 
@@ -332,6 +344,8 @@ struct hci_conn {
        __u8            passkey_entered;
        __u16           disc_timeout;
        __u16           setting;
+       __u16           le_conn_min_interval;
+       __u16           le_conn_max_interval;
        unsigned long   flags;
 
        __u8            remote_cap;
@@ -372,6 +386,16 @@ struct hci_chan {
        __u8            state;
 };
 
+struct hci_conn_params {
+       struct list_head list;
+
+       bdaddr_t addr;
+       u8 addr_type;
+
+       u16 conn_min_interval;
+       u16 conn_max_interval;
+};
+
 extern struct list_head hci_dev_list;
 extern struct list_head hci_cb_list;
 extern rwlock_t hci_dev_list_lock;
@@ -446,6 +470,8 @@ enum {
        HCI_CONN_LE_SMP_PEND,
        HCI_CONN_MGMT_CONNECTED,
        HCI_CONN_SSP_ENABLED,
+       HCI_CONN_SC_ENABLED,
+       HCI_CONN_AES_CCM,
        HCI_CONN_POWER_SAVE,
        HCI_CONN_REMOTE_OOB,
        HCI_CONN_6LOWPAN,
@@ -458,6 +484,13 @@ static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
               test_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
 }
 
+static inline bool hci_conn_sc_enabled(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+       return test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
+              test_bit(HCI_CONN_SC_ENABLED, &conn->flags);
+}
+
 static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
 {
        struct hci_conn_hash *h = &hdev->conn_hash;
@@ -737,31 +770,50 @@ int hci_inquiry(void __user *arg);
 
 struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
                                         bdaddr_t *bdaddr, u8 type);
-int hci_blacklist_clear(struct hci_dev *hdev);
+void hci_blacklist_clear(struct hci_dev *hdev);
 int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
 int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
 
-int hci_uuids_clear(struct hci_dev *hdev);
+struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev,
+                                              bdaddr_t *addr, u8 addr_type);
+void hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
+                        u16 conn_min_interval, u16 conn_max_interval);
+void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type);
+void hci_conn_params_clear(struct hci_dev *hdev);
+
+void hci_uuids_clear(struct hci_dev *hdev);
 
-int hci_link_keys_clear(struct hci_dev *hdev);
+void hci_link_keys_clear(struct hci_dev *hdev);
 struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
 int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
                     bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len);
-struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]);
-int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type,
-               int new_key, u8 authenticated, u8 tk[16], u8 enc_size,
-               __le16 ediv, u8 rand[8]);
+struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8],
+                            bool master);
+struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                           u8 addr_type, u8 type, u8 authenticated,
+                           u8 tk[16], u8 enc_size, __le16 ediv, u8 rand[8]);
 struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                    u8 addr_type);
-int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr);
-int hci_smp_ltks_clear(struct hci_dev *hdev);
+                                    u8 addr_type, bool master);
+int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type);
+void hci_smp_ltks_clear(struct hci_dev *hdev);
 int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
 
-int hci_remote_oob_data_clear(struct hci_dev *hdev);
+struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa);
+struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                    u8 addr_type);
+struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                           u8 addr_type, u8 val[16], bdaddr_t *rpa);
+void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type);
+void hci_smp_irks_clear(struct hci_dev *hdev);
+
+void hci_remote_oob_data_clear(struct hci_dev *hdev);
 struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
-                                                       bdaddr_t *bdaddr);
-int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
-                                                               u8 *randomizer);
+                                         bdaddr_t *bdaddr);
+int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                           u8 *hash, u8 *randomizer);
+int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                               u8 *hash192, u8 *randomizer192,
+                               u8 *hash256, u8 *randomizer256);
 int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr);
 
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
@@ -803,9 +855,12 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
 #define lmp_csb_slave_capable(dev)  ((dev)->features[2][0] & LMP_CSB_SLAVE)
 #define lmp_sync_train_capable(dev) ((dev)->features[2][0] & LMP_SYNC_TRAIN)
 #define lmp_sync_scan_capable(dev)  ((dev)->features[2][0] & LMP_SYNC_SCAN)
+#define lmp_sc_capable(dev)         ((dev)->features[2][1] & LMP_SC)
+#define lmp_ping_capable(dev)       ((dev)->features[2][1] & LMP_PING)
 
 /* ----- Host capabilities ----- */
 #define lmp_host_ssp_capable(dev)  ((dev)->features[1][0] & LMP_HOST_SSP)
+#define lmp_host_sc_capable(dev)   ((dev)->features[1][0] & LMP_HOST_SC)
 #define lmp_host_le_capable(dev)   (!!((dev)->features[1][0] & LMP_HOST_LE))
 #define lmp_host_le_br_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE_BREDR))
 
@@ -1019,6 +1074,26 @@ static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type)
        return false;
 }
 
+static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type)
+{
+       if (addr_type != 0x01)
+               return false;
+
+       if ((bdaddr->b[5] & 0xc0) == 0x40)
+              return true;
+
+       return false;
+}
+
+static inline struct smp_irk *hci_get_irk(struct hci_dev *hdev,
+                                         bdaddr_t *bdaddr, u8 addr_type)
+{
+       if (!hci_bdaddr_is_rpa(bdaddr, addr_type))
+               return NULL;
+
+       return hci_find_irk_by_rpa(hdev, bdaddr);
+}
+
 int hci_register_cb(struct hci_cb *hcb);
 int hci_unregister_cb(struct hci_cb *hcb);
 
@@ -1122,11 +1197,13 @@ void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                      u8 addr_type, u8 status);
 void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status);
 void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
+void mgmt_sc_enable_complete(struct hci_dev *hdev, u8 enable, u8 status);
 void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
                                    u8 status);
 void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status);
-void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
-                                            u8 *randomizer, u8 status);
+void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
+                                      u8 *randomizer192, u8 *hash256,
+                                      u8 *randomizer256, u8 status);
 void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
                       u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name,
                       u8 ssp, u8 *eir, u16 eir_len);
@@ -1135,8 +1212,10 @@ void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 void mgmt_discovering(struct hci_dev *hdev, u8 discovering);
 int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
 int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
-void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent);
+void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key);
+void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk);
 void mgmt_reenable_advertising(struct hci_dev *hdev);
+void mgmt_smp_complete(struct hci_conn *conn, bool complete);
 
 /* HCI info for socket */
 #define hci_pi(sk) ((struct hci_pinfo *) sk)
index dbc4a89984ca67287715a8279b2d9793e7d2eebe..4abdcb220e3ac7a0558a9a0115b873a5312f3f07 100644 (file)
@@ -91,6 +91,7 @@ struct l2cap_conninfo {
 #define L2CAP_LM_TRUSTED       0x0008
 #define L2CAP_LM_RELIABLE      0x0010
 #define L2CAP_LM_SECURE                0x0020
+#define L2CAP_LM_FIPS          0x0040
 
 /* L2CAP command codes */
 #define L2CAP_COMMAND_REJ      0x01
@@ -623,6 +624,9 @@ struct l2cap_conn {
        __u32                   rx_len;
        __u8                    tx_ident;
 
+       struct sk_buff_head     pending_rx;
+       struct work_struct      pending_rx_work;
+
        __u8                    disc_reason;
 
        struct delayed_work     security_timer;
@@ -647,7 +651,7 @@ struct l2cap_user {
 #define L2CAP_CHAN_RAW                 1
 #define L2CAP_CHAN_CONN_LESS           2
 #define L2CAP_CHAN_CONN_ORIENTED       3
-#define L2CAP_CHAN_CONN_FIX_A2MP       4
+#define L2CAP_CHAN_FIXED               4
 
 /* ----- L2CAP socket info ----- */
 #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
@@ -853,7 +857,6 @@ static inline long l2cap_chan_no_get_sndtimeo(struct l2cap_chan *chan)
 }
 
 extern bool disable_ertm;
-extern bool enable_lecoc;
 
 int l2cap_init_sockets(void);
 void l2cap_cleanup_sockets(void);
@@ -878,6 +881,7 @@ int l2cap_ertm_init(struct l2cap_chan *chan);
 void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
 void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan);
 void l2cap_chan_del(struct l2cap_chan *chan, int err);
+void l2cap_conn_update_id_addr(struct hci_conn *hcon);
 void l2cap_send_conn_req(struct l2cap_chan *chan);
 void l2cap_move_start(struct l2cap_chan *chan);
 void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
index 518c5c84e39a67ef4c9789eacd8d88117bf6ccbd..2e46251e8aec9a81a17833327e7352b6ba265321 100644 (file)
@@ -94,6 +94,8 @@ struct mgmt_rp_read_index_list {
 #define MGMT_SETTING_HS                        0x00000100
 #define MGMT_SETTING_LE                        0x00000200
 #define MGMT_SETTING_ADVERTISING       0x00000400
+#define MGMT_SETTING_SECURE_CONN       0x00000800
+#define MGMT_SETTING_DEBUG_KEYS                0x00001000
 
 #define MGMT_OP_READ_INFO              0x0004
 #define MGMT_READ_INFO_SIZE            0
@@ -180,7 +182,7 @@ struct mgmt_cp_load_link_keys {
 
 struct mgmt_ltk_info {
        struct mgmt_addr_info addr;
-       __u8    authenticated;
+       __u8    type;
        __u8    master;
        __u8    enc_size;
        __le16  ediv;
@@ -294,6 +296,12 @@ struct mgmt_rp_read_local_oob_data {
        __u8    hash[16];
        __u8    randomizer[16];
 } __packed;
+struct mgmt_rp_read_local_oob_ext_data {
+       __u8    hash192[16];
+       __u8    randomizer192[16];
+       __u8    hash256[16];
+       __u8    randomizer256[16];
+} __packed;
 
 #define MGMT_OP_ADD_REMOTE_OOB_DATA    0x0021
 struct mgmt_cp_add_remote_oob_data {
@@ -302,6 +310,14 @@ struct mgmt_cp_add_remote_oob_data {
        __u8    randomizer[16];
 } __packed;
 #define MGMT_ADD_REMOTE_OOB_DATA_SIZE  (MGMT_ADDR_INFO_SIZE + 32)
+struct mgmt_cp_add_remote_oob_ext_data {
+       struct mgmt_addr_info addr;
+       __u8    hash192[16];
+       __u8    randomizer192[16];
+       __u8    hash256[16];
+       __u8    randomizer256[16];
+} __packed;
+#define MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE (MGMT_ADDR_INFO_SIZE + 64)
 
 #define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x0022
 struct mgmt_cp_remove_remote_oob_data {
@@ -369,6 +385,22 @@ struct mgmt_cp_set_scan_params {
 } __packed;
 #define MGMT_SET_SCAN_PARAMS_SIZE      4
 
+#define MGMT_OP_SET_SECURE_CONN                0x002D
+
+#define MGMT_OP_SET_DEBUG_KEYS         0x002E
+
+struct mgmt_irk_info {
+       struct mgmt_addr_info addr;
+       __u8 val[16];
+} __packed;
+
+#define MGMT_OP_LOAD_IRKS              0x0030
+struct mgmt_cp_load_irks {
+       __le16 irk_count;
+       struct mgmt_irk_info irks[0];
+} __packed;
+#define MGMT_LOAD_IRKS_SIZE            2
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16  opcode;
@@ -504,3 +536,10 @@ struct mgmt_ev_passkey_notify {
        __le32  passkey;
        __u8    entered;
 } __packed;
+
+#define MGMT_EV_NEW_IRK                        0x0018
+struct mgmt_ev_new_irk {
+       __u8     store_hint;
+       bdaddr_t rpa;
+       struct mgmt_irk_info irk;
+} __packed;
index 486213a1aed8d07ad63aaba57957e7a651657696..2611cc389d7d65d03cf298061bf3e4ed32c304cd 100644 (file)
@@ -238,9 +238,11 @@ int  rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
                                                                u8 channel);
 int  rfcomm_dlc_close(struct rfcomm_dlc *d, int reason);
 int  rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb);
+void rfcomm_dlc_send_noerror(struct rfcomm_dlc *d, struct sk_buff *skb);
 int  rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig);
 int  rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig);
 void rfcomm_dlc_accept(struct rfcomm_dlc *d);
+struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel);
 
 #define rfcomm_dlc_lock(d)     spin_lock(&d->lock)
 #define rfcomm_dlc_unlock(d)   spin_unlock(&d->lock)
@@ -295,6 +297,7 @@ struct rfcomm_conninfo {
 #define RFCOMM_LM_TRUSTED      0x0008
 #define RFCOMM_LM_RELIABLE     0x0010
 #define RFCOMM_LM_SECURE       0x0020
+#define RFCOMM_LM_FIPS         0x0040
 
 #define rfcomm_pi(sk) ((struct rfcomm_pinfo *) sk)
 
@@ -323,11 +326,16 @@ int  rfcomm_connect_ind(struct rfcomm_session *s, u8 channel,
 #define RFCOMMGETDEVINFO       _IOR('R', 211, int)
 #define RFCOMMSTEALDLC         _IOW('R', 220, int)
 
+/* rfcomm_dev.flags bit definitions */
 #define RFCOMM_REUSE_DLC      0
 #define RFCOMM_RELEASE_ONHUP  1
 #define RFCOMM_HANGUP_NOW     2
 #define RFCOMM_TTY_ATTACHED   3
-#define RFCOMM_TTY_RELEASED   4
+#define RFCOMM_DEFUNCT_BIT4   4          /* don't reuse this bit - userspace visible */
+
+/* rfcomm_dev.status bit definitions */
+#define RFCOMM_DEV_RELEASED   0
+#define RFCOMM_TTY_OWNED      1
 
 struct rfcomm_dev_req {
        s16      dev_id;
index 9f90554e88c48a1342c0d1116e1c47ba0b875d81..8c9ba44fb7cf37a3505226094dd022e1859086a5 100644 (file)
@@ -2206,7 +2206,12 @@ struct cfg80211_qos_map {
  * @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.
+ * @sched_scan_stop: Tell the driver to stop an ongoing scheduled scan. This
+ *     call must stop the scheduled scan and be ready for starting a new one
+ *     before it returns, i.e. @sched_scan_start may be called immediately
+ *     after that again and should not fail in that case. The driver should
+ *     not call cfg80211_sched_scan_stopped() for a requested stop (when this
+ *     method returns 0.)
  *
  * @mgmt_frame_register: Notify driver that a management frame type was
  *     registered. Note that this callback may not sleep, and cannot run
@@ -2465,7 +2470,8 @@ struct cfg80211_ops {
 
        int     (*tdls_mgmt)(struct wiphy *wiphy, struct net_device *dev,
                             u8 *peer, u8 action_code,  u8 dialog_token,
-                            u16 status_code, const u8 *buf, size_t len);
+                            u16 status_code, u32 peer_capability,
+                            const u8 *buf, size_t len);
        int     (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev,
                             u8 *peer, enum nl80211_tdls_operation oper);
 
@@ -2610,9 +2616,12 @@ struct ieee80211_iface_limit {
  *     only in special cases.
  * @radar_detect_widths: bitmap of channel widths supported for radar detection
  *
- * These examples can be expressed as follows:
+ * With this structure the driver can describe which interface
+ * combinations it supports concurrently.
  *
- * Allow #STA <= 1, #AP <= 1, matching BI, channels = 1, 2 total:
+ * Examples:
+ *
+ * 1. Allow #STA <= 1, #AP <= 1, matching BI, channels = 1, 2 total:
  *
  *  struct ieee80211_iface_limit limits1[] = {
  *     { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
@@ -2626,7 +2635,7 @@ struct ieee80211_iface_limit {
  *  };
  *
  *
- * Allow #{AP, P2P-GO} <= 8, channels = 1, 8 total:
+ * 2. Allow #{AP, P2P-GO} <= 8, channels = 1, 8 total:
  *
  *  struct ieee80211_iface_limit limits2[] = {
  *     { .max = 8, .types = BIT(NL80211_IFTYPE_AP) |
@@ -2640,7 +2649,8 @@ struct ieee80211_iface_limit {
  *  };
  *
  *
- * Allow #STA <= 1, #{P2P-client,P2P-GO} <= 3 on two channels, 4 total.
+ * 3. Allow #STA <= 1, #{P2P-client,P2P-GO} <= 3 on two channels, 4 total.
+ *
  * This allows for an infrastructure connection and three P2P connections.
  *
  *  struct ieee80211_iface_limit limits3[] = {
@@ -2790,7 +2800,7 @@ struct wiphy_vendor_command {
  * @perm_addr: permanent MAC address of this device
  * @addr_mask: If the device supports multiple MAC addresses by masking,
  *     set this to a mask with variable bits set to 1, e.g. if the last
- *     four bits are variable then set it to 00:...:00:0f. The actual
+ *     four bits are variable then set it to 00-00-00-00-00-0f. The actual
  *     variable bits shall be determined by the interfaces added, with
  *     interfaces not matching the mask being rejected to be brought up.
  * @n_addresses: number of addresses in @addresses.
index 4f0f29dce0aae64c98ffd9cf2cf6e26859e9c4d3..86faa413b37d043979cd5983164a65060f4f2965 100644 (file)
  *
  * Secondly, when the hardware handles fragmentation, the frame handed to
  * the driver from mac80211 is the MSDU, not the MPDU.
- *
- * Finally, for received frames, the driver is able to indicate that it has
- * filled a radiotap header and put that in front of the frame; if it does
- * not do so then mac80211 may add this under certain circumstances.
  */
 
 /**
@@ -1507,8 +1503,6 @@ struct ieee80211_tx_control {
  * @IEEE80211_HW_CONNECTION_MONITOR:
  *     The hardware performs its own connection monitoring, including
  *     periodic keep-alives to the AP and probing the AP on beacon loss.
- *     When this flag is set, signaling beacon-loss will cause an immediate
- *     change to disassociated state.
  *
  * @IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC:
  *     This device needs to get data from beacon before association (i.e.
@@ -1644,10 +1638,6 @@ enum ieee80211_hw_flags {
  *     the hw can report back.
  * @max_rate_tries: maximum number of tries for each stage
  *
- * @napi_weight: weight used for NAPI polling.  You must specify an
- *     appropriate value here if a napi_poll operation is provided
- *     by your driver.
- *
  * @max_rx_aggregation_subframes: maximum buffer size (number of
  *     sub-frames) to be used for A-MPDU block ack receiver
  *     aggregation.
@@ -1701,7 +1691,6 @@ struct ieee80211_hw {
        int vif_data_size;
        int sta_data_size;
        int chanctx_data_size;
-       int napi_weight;
        u16 queues;
        u16 max_listen_interval;
        s8 max_signal;
@@ -2471,6 +2460,7 @@ enum ieee80211_roc_type {
  *     This process will continue until sched_scan_stop is called.
  *
  * @sched_scan_stop: Tell the hardware to stop an ongoing scheduled scan.
+ *     In this case, ieee80211_sched_scan_stopped() must not be called.
  *
  * @sw_scan_start: Notifier function that is called just before a software scan
  *     is started. Can be NULL, if the driver doesn't need this notification.
@@ -2624,8 +2614,6 @@ enum ieee80211_roc_type {
  *     callback. They must then call ieee80211_chswitch_done() to indicate
  *     completion of the channel switch.
  *
- * @napi_poll: Poll Rx queue for incoming data frames.
- *
  * @set_antenna: Set antenna configuration (tx_ant, rx_ant) on the device.
  *     Parameters are bitmaps of allowed antennas to use for TX/RX. Drivers may
  *     reject TX/RX mask combinations they cannot support by returning -EINVAL
@@ -2820,7 +2808,7 @@ struct ieee80211_ops {
                                struct ieee80211_vif *vif,
                                struct cfg80211_sched_scan_request *req,
                                struct ieee80211_sched_scan_ies *ies);
-       void (*sched_scan_stop)(struct ieee80211_hw *hw,
+       int (*sched_scan_stop)(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif);
        void (*sw_scan_start)(struct ieee80211_hw *hw);
        void (*sw_scan_complete)(struct ieee80211_hw *hw);
@@ -2884,7 +2872,6 @@ struct ieee80211_ops {
        void (*flush)(struct ieee80211_hw *hw, u32 queues, bool drop);
        void (*channel_switch)(struct ieee80211_hw *hw,
                               struct ieee80211_channel_switch *ch_switch);
-       int (*napi_poll)(struct ieee80211_hw *hw, int budget);
        int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
        int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
 
@@ -3166,21 +3153,21 @@ void ieee80211_free_hw(struct ieee80211_hw *hw);
  */
 void ieee80211_restart_hw(struct ieee80211_hw *hw);
 
-/** ieee80211_napi_schedule - schedule NAPI poll
- *
- * Use this function to schedule NAPI polling on a device.
- *
- * @hw: the hardware to start polling
- */
-void ieee80211_napi_schedule(struct ieee80211_hw *hw);
-
-/** ieee80211_napi_complete - complete NAPI polling
- *
- * Use this function to finish NAPI polling on a device.
+/**
+ * ieee80211_napi_add - initialize mac80211 NAPI context
+ * @hw: the hardware to initialize the NAPI context on
+ * @napi: the NAPI context to initialize
+ * @napi_dev: dummy NAPI netdevice, here to not waste the space if the
+ *     driver doesn't use NAPI
+ * @poll: poll function
+ * @weight: default weight
  *
- * @hw: the hardware to stop polling
+ * See also netif_napi_add().
  */
-void ieee80211_napi_complete(struct ieee80211_hw *hw);
+void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi,
+                       struct net_device *napi_dev,
+                       int (*poll)(struct napi_struct *, int),
+                       int weight);
 
 /**
  * ieee80211_rx - receive frame
index a12e6cae5132569815271c352508c48001c1b51e..ff72cab3cd3a1355b03a1e5e54aff51d65802086 100644 (file)
  *     passed, all channels allowed for the current regulatory domain
  *     are used.  Extra IEs can also be passed from the userspace by
  *     using the %NL80211_ATTR_IE attribute.
- * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan.  Returns -ENOENT
- *     if scheduled scan is not running.
+ * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
+ *     scheduled scan is not running. The caller may assume that as soon
+ *     as the call returns, it is safe to start a new scheduled scan again.
  * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan
  *     results available.
  * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has
@@ -1575,6 +1576,9 @@ enum nl80211_commands {
  *     advertise values that cannot always be met. In such cases, an attempt
  *     to add a new station entry with @NL80211_CMD_NEW_STATION may fail.
  *
+ * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
+ *     As specified in the &enum nl80211_tdls_peer_capability.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1908,6 +1912,8 @@ enum nl80211_attrs {
 
        NL80211_ATTR_MAX_AP_ASSOC_STA,
 
+       NL80211_ATTR_TDLS_PEER_CAPABILITY,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2437,10 +2443,7 @@ enum nl80211_reg_type {
  *     in KHz. This is not a center a frequency but an actual regulatory
  *     band edge.
  * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this
- *     frequency range, in KHz. If not present or 0, maximum available
- *     bandwidth should be calculated base on contiguous rules and wider
- *     channels will be allowed to cross multiple contiguous/overlapping
- *     frequency ranges.
+ *     frequency range, in KHz.
  * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain
  *     for a given frequency range. The value is in mBi (100 * dBi).
  *     If you don't have one then don't send this.
@@ -2511,6 +2514,9 @@ enum nl80211_sched_scan_match_attr {
  * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed,
  *     this includes probe requests or modes of operation that require
  *     beaconing.
+ * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
+ *     base on contiguous rules and wider channels will be allowed to cross
+ *     multiple contiguous/overlapping frequency ranges.
  */
 enum nl80211_reg_rule_flags {
        NL80211_RRF_NO_OFDM             = 1<<0,
@@ -2522,6 +2528,7 @@ enum nl80211_reg_rule_flags {
        NL80211_RRF_PTMP_ONLY           = 1<<6,
        NL80211_RRF_NO_IR               = 1<<7,
        __NL80211_RRF_NO_IBSS           = 1<<8,
+       NL80211_RRF_AUTO_BW             = 1<<11,
 };
 
 #define NL80211_RRF_PASSIVE_SCAN       NL80211_RRF_NO_IR
@@ -3843,11 +3850,6 @@ enum nl80211_ap_sme_features {
  * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
  *     to work properly to suppport receiving regulatory hints from
  *     cellular base stations.
- * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: If this is set, an active
- *     P2P Device (%NL80211_IFTYPE_P2P_DEVICE) requires its own channel
- *     in the interface combinations, even when it's only used for scan
- *     and remain-on-channel. This could be due to, for example, the
- *     remain-on-channel implementation requiring a channel context.
  * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of
  *     equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station
  *     mode
@@ -3889,7 +3891,7 @@ enum nl80211_feature_flags {
        NL80211_FEATURE_HT_IBSS                         = 1 << 1,
        NL80211_FEATURE_INACTIVITY_TIMER                = 1 << 2,
        NL80211_FEATURE_CELL_BASE_REG_HINTS             = 1 << 3,
-       NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL        = 1 << 4,
+       /* bit 4 is reserved - don't use */
        NL80211_FEATURE_SAE                             = 1 << 5,
        NL80211_FEATURE_LOW_PRIORITY_SCAN               = 1 << 6,
        NL80211_FEATURE_SCAN_FLUSH                      = 1 << 7,
@@ -4079,4 +4081,20 @@ struct nl80211_vendor_cmd_info {
        __u32 subcmd;
 };
 
+/**
+ * enum nl80211_tdls_peer_capability - TDLS peer flags.
+ *
+ * Used by tdls_mgmt() to determine which conditional elements need
+ * to be added to TDLS Setup frames.
+ *
+ * @NL80211_TDLS_PEER_HT: TDLS peer is HT capable.
+ * @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable.
+ * @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable.
+ */
+enum nl80211_tdls_peer_capability {
+       NL80211_TDLS_PEER_HT = 1<<0,
+       NL80211_TDLS_PEER_VHT = 1<<1,
+       NL80211_TDLS_PEER_WMM = 1<<2,
+};
+
 #endif /* __LINUX_NL80211_H */
index efcd108822c43134e3d64755e24c16410f0598d5..f986b9968bdb8845c6747e9f29205e03090627f7 100644 (file)
@@ -235,7 +235,7 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
                        BT_DBG("chan %p state %s", chan,
                               state_to_string(chan->state));
 
-                       if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP)
+                       if (chan->scid == L2CAP_CID_A2MP)
                                continue;
 
                        l2cap_chan_lock(chan);
@@ -726,7 +726,11 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
 
        BT_DBG("chan %p", chan);
 
-       chan->chan_type = L2CAP_CHAN_CONN_FIX_A2MP;
+       chan->chan_type = L2CAP_CHAN_FIXED;
+       chan->scid = L2CAP_CID_A2MP;
+       chan->dcid = L2CAP_CID_A2MP;
+       chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
+       chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
        chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
 
        chan->ops = &a2mp_chan_ops;
index 0c5866bb49b6fc8933b01d0e5ed0c9d3f8564c63..2021c481cdb657a8011938f5e8486c4c93f0c116 100644 (file)
@@ -31,7 +31,7 @@
 #include <net/bluetooth/bluetooth.h>
 #include <linux/proc_fs.h>
 
-#define VERSION "2.18"
+#define VERSION "2.19"
 
 /* Bluetooth sockets */
 #define BT_MAX_PROTO   8
index ba5366c320dacc7d4db4659aa144051baf1b035a..bd66c52eff955e19f7d3fe5c6adc0b581374fcf6 100644 (file)
@@ -225,8 +225,8 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
        cp.conn_interval_max    = cpu_to_le16(max);
        cp.conn_latency         = cpu_to_le16(latency);
        cp.supervision_timeout  = cpu_to_le16(to_multiplier);
-       cp.min_ce_len           = __constant_cpu_to_le16(0x0001);
-       cp.max_ce_len           = __constant_cpu_to_le16(0x0001);
+       cp.min_ce_len           = __constant_cpu_to_le16(0x0000);
+       cp.max_ce_len           = __constant_cpu_to_le16(0x0000);
 
        hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp);
 }
@@ -514,6 +514,21 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
 }
 EXPORT_SYMBOL(hci_get_route);
 
+/* This function requires the caller holds hdev->lock */
+static void le_conn_failed(struct hci_conn *conn, u8 status)
+{
+       struct hci_dev *hdev = conn->hdev;
+
+       conn->state = BT_CLOSED;
+
+       mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type,
+                           status);
+
+       hci_proto_connect_cfm(conn, status);
+
+       hci_conn_del(conn);
+}
+
 static void create_le_conn_complete(struct hci_dev *hdev, u8 status)
 {
        struct hci_conn *conn;
@@ -530,14 +545,7 @@ static void create_le_conn_complete(struct hci_dev *hdev, u8 status)
        if (!conn)
                goto done;
 
-       conn->state = BT_CLOSED;
-
-       mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type,
-                           status);
-
-       hci_proto_connect_cfm(conn, status);
-
-       hci_conn_del(conn);
+       le_conn_failed(conn, status);
 
 done:
        hci_dev_unlock(hdev);
@@ -558,8 +566,8 @@ static int hci_create_le_conn(struct hci_conn *conn)
        bacpy(&cp.peer_addr, &conn->dst);
        cp.peer_addr_type = conn->dst_type;
        cp.own_address_type = conn->src_type;
-       cp.conn_interval_min = cpu_to_le16(hdev->le_conn_min_interval);
-       cp.conn_interval_max = cpu_to_le16(hdev->le_conn_max_interval);
+       cp.conn_interval_min = cpu_to_le16(conn->le_conn_min_interval);
+       cp.conn_interval_max = cpu_to_le16(conn->le_conn_max_interval);
        cp.supervision_timeout = __constant_cpu_to_le16(0x002a);
        cp.min_ce_len = __constant_cpu_to_le16(0x0000);
        cp.max_ce_len = __constant_cpu_to_le16(0x0000);
@@ -578,7 +586,9 @@ static int hci_create_le_conn(struct hci_conn *conn)
 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_params *params;
        struct hci_conn *conn;
+       struct smp_irk *irk;
        int err;
 
        if (test_bit(HCI_ADVERTISING, &hdev->flags))
@@ -607,15 +617,36 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
        if (conn)
                return ERR_PTR(-EBUSY);
 
+       /* Convert from L2CAP channel address type to HCI address type */
+       if (dst_type == BDADDR_LE_PUBLIC)
+               dst_type = ADDR_LE_DEV_PUBLIC;
+       else
+               dst_type = ADDR_LE_DEV_RANDOM;
+
+       /* When given an identity address with existing identity
+        * resolving key, the connection needs to be established
+        * to a resolvable random address.
+        *
+        * This uses the cached random resolvable address from
+        * a previous scan. When no cached address is available,
+        * try connecting to the identity address instead.
+        *
+        * Storing the resolvable random address is required here
+        * to handle connection failures. The address will later
+        * be resolved back into the original identity address
+        * from the connect request.
+        */
+       irk = hci_find_irk_by_addr(hdev, dst, dst_type);
+       if (irk && bacmp(&irk->rpa, BDADDR_ANY)) {
+               dst = &irk->rpa;
+               dst_type = ADDR_LE_DEV_RANDOM;
+       }
+
        conn = hci_conn_add(hdev, LE_LINK, dst);
        if (!conn)
                return ERR_PTR(-ENOMEM);
 
-       if (dst_type == BDADDR_LE_PUBLIC)
-               conn->dst_type = ADDR_LE_DEV_PUBLIC;
-       else
-               conn->dst_type = ADDR_LE_DEV_RANDOM;
-
+       conn->dst_type = dst_type;
        conn->src_type = hdev->own_addr_type;
 
        conn->state = BT_CONNECT;
@@ -625,6 +656,15 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
        conn->pending_sec_level = sec_level;
        conn->auth_type = auth_type;
 
+       params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
+       if (params) {
+               conn->le_conn_min_interval = params->conn_min_interval;
+               conn->le_conn_max_interval = params->conn_max_interval;
+       } else {
+               conn->le_conn_min_interval = hdev->le_conn_min_interval;
+               conn->le_conn_max_interval = hdev->le_conn_max_interval;
+       }
+
        err = hci_create_le_conn(conn);
        if (err)
                return ERR_PTR(err);
@@ -800,14 +840,23 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
        if (!(conn->link_mode & HCI_LM_AUTH))
                goto auth;
 
-       /* An authenticated combination key has sufficient security for any
-          security level. */
-       if (conn->key_type == HCI_LK_AUTH_COMBINATION)
+       /* An authenticated FIPS approved combination key has sufficient
+        * security for security level 4. */
+       if (conn->key_type == HCI_LK_AUTH_COMBINATION_P256 &&
+           sec_level == BT_SECURITY_FIPS)
+               goto encrypt;
+
+       /* An authenticated combination key has sufficient security for
+          security level 3. */
+       if ((conn->key_type == HCI_LK_AUTH_COMBINATION_P192 ||
+            conn->key_type == HCI_LK_AUTH_COMBINATION_P256) &&
+           sec_level == BT_SECURITY_HIGH)
                goto encrypt;
 
        /* An unauthenticated combination key has sufficient security for
           security level 1 and 2. */
-       if (conn->key_type == HCI_LK_UNAUTH_COMBINATION &&
+       if ((conn->key_type == HCI_LK_UNAUTH_COMBINATION_P192 ||
+            conn->key_type == HCI_LK_UNAUTH_COMBINATION_P256) &&
            (sec_level == BT_SECURITY_MEDIUM || sec_level == BT_SECURITY_LOW))
                goto encrypt;
 
@@ -816,7 +865,8 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
           is generated using maximum PIN code length (16).
           For pre 2.1 units. */
        if (conn->key_type == HCI_LK_COMBINATION &&
-           (sec_level != BT_SECURITY_HIGH || conn->pin_length == 16))
+           (sec_level == BT_SECURITY_MEDIUM || sec_level == BT_SECURITY_LOW ||
+            conn->pin_length == 16))
                goto encrypt;
 
 auth:
@@ -840,13 +890,17 @@ int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level)
 {
        BT_DBG("hcon %p", conn);
 
-       if (sec_level != BT_SECURITY_HIGH)
-               return 1; /* Accept if non-secure is required */
+       /* Accept if non-secure or higher security level is required */
+       if (sec_level != BT_SECURITY_HIGH && sec_level != BT_SECURITY_FIPS)
+               return 1;
 
-       if (conn->sec_level == BT_SECURITY_HIGH)
+       /* Accept if secure or higher security level is already present */
+       if (conn->sec_level == BT_SECURITY_HIGH ||
+           conn->sec_level == BT_SECURITY_FIPS)
                return 1;
 
-       return 0; /* Reject not secure link */
+       /* Reject not secure link */
+       return 0;
 }
 EXPORT_SYMBOL(hci_conn_check_secure);
 
index 5e8663c194c18e3c29ab221efe667dd311944294..964aa8deb0092af756d8eb163dfef5d42a2e56c8 100644 (file)
 #include <linux/idr.h>
 #include <linux/rfkill.h>
 #include <linux/debugfs.h>
+#include <linux/crypto.h>
 #include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
+#include "smp.h"
+
 static void hci_rx_work(struct work_struct *work);
 static void hci_cmd_work(struct work_struct *work);
 static void hci_tx_work(struct work_struct *work);
@@ -285,24 +288,6 @@ static const struct file_operations link_keys_fops = {
        .release        = single_release,
 };
 
-static ssize_t use_debug_keys_read(struct file *file, char __user *user_buf,
-                                  size_t count, loff_t *ppos)
-{
-       struct hci_dev *hdev = file->private_data;
-       char buf[3];
-
-       buf[0] = test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) ? 'Y': 'N';
-       buf[1] = '\n';
-       buf[2] = '\0';
-       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static const struct file_operations use_debug_keys_fops = {
-       .open           = simple_open,
-       .read           = use_debug_keys_read,
-       .llseek         = default_llseek,
-};
-
 static int dev_class_show(struct seq_file *f, void *ptr)
 {
        struct hci_dev *hdev = f->private;
@@ -415,6 +400,70 @@ static int ssp_debug_mode_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(ssp_debug_mode_fops, ssp_debug_mode_get,
                        ssp_debug_mode_set, "%llu\n");
 
+static ssize_t force_sc_support_read(struct file *file, char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_FORCE_SC, &hdev->dev_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_sc_support_write(struct file *file,
+                                     const char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[32];
+       size_t buf_size = min(count, (sizeof(buf)-1));
+       bool enable;
+
+       if (test_bit(HCI_UP, &hdev->flags))
+               return -EBUSY;
+
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+
+       if (enable == test_bit(HCI_FORCE_SC, &hdev->dev_flags))
+               return -EALREADY;
+
+       change_bit(HCI_FORCE_SC, &hdev->dev_flags);
+
+       return count;
+}
+
+static const struct file_operations force_sc_support_fops = {
+       .open           = simple_open,
+       .read           = force_sc_support_read,
+       .write          = force_sc_support_write,
+       .llseek         = default_llseek,
+};
+
+static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
+                                size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_SC_ONLY, &hdev->dev_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations sc_only_mode_fops = {
+       .open           = simple_open,
+       .read           = sc_only_mode_read,
+       .llseek         = default_llseek,
+};
+
 static int idle_timeout_set(void *data, u64 val)
 {
        struct hci_dev *hdev = data;
@@ -499,6 +548,29 @@ static int sniff_max_interval_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
                        sniff_max_interval_set, "%llu\n");
 
+static int random_address_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+
+       hci_dev_lock(hdev);
+       seq_printf(f, "%pMR\n", &hdev->random_addr);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int random_address_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, random_address_show, inode->i_private);
+}
+
+static const struct file_operations random_address_fops = {
+       .open           = random_address_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
 static int static_address_show(struct seq_file *f, void *p)
 {
        struct hci_dev *hdev = f->private;
@@ -522,33 +594,82 @@ static const struct file_operations static_address_fops = {
        .release        = single_release,
 };
 
-static int own_address_type_set(void *data, u64 val)
+static ssize_t force_static_address_read(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
 {
-       struct hci_dev *hdev = data;
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
 
-       if (val != 0 && val != 1)
+       buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_static_address_write(struct file *file,
+                                         const char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[32];
+       size_t buf_size = min(count, (sizeof(buf)-1));
+       bool enable;
+
+       if (test_bit(HCI_UP, &hdev->flags))
+               return -EBUSY;
+
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+       if (strtobool(buf, &enable))
                return -EINVAL;
 
-       hci_dev_lock(hdev);
-       hdev->own_addr_type = val;
-       hci_dev_unlock(hdev);
+       if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags))
+               return -EALREADY;
 
-       return 0;
+       change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags);
+
+       return count;
 }
 
-static int own_address_type_get(void *data, u64 *val)
+static const struct file_operations force_static_address_fops = {
+       .open           = simple_open,
+       .read           = force_static_address_read,
+       .write          = force_static_address_write,
+       .llseek         = default_llseek,
+};
+
+static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
 {
-       struct hci_dev *hdev = data;
+       struct hci_dev *hdev = f->private;
+       struct list_head *p, *n;
 
        hci_dev_lock(hdev);
-       *val = hdev->own_addr_type;
+       list_for_each_safe(p, n, &hdev->identity_resolving_keys) {
+               struct smp_irk *irk = list_entry(p, struct smp_irk, list);
+               seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
+                          &irk->bdaddr, irk->addr_type,
+                          16, irk->val, &irk->rpa);
+       }
        hci_dev_unlock(hdev);
 
        return 0;
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(own_address_type_fops, own_address_type_get,
-                       own_address_type_set, "%llu\n");
+static int identity_resolving_keys_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, identity_resolving_keys_show,
+                          inode->i_private);
+}
+
+static const struct file_operations identity_resolving_keys_fops = {
+       .open           = identity_resolving_keys_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
 
 static int long_term_keys_show(struct seq_file *f, void *ptr)
 {
@@ -556,9 +677,9 @@ static int long_term_keys_show(struct seq_file *f, void *ptr)
        struct list_head *p, *n;
 
        hci_dev_lock(hdev);
-       list_for_each_safe(p, n, &hdev->link_keys) {
+       list_for_each_safe(p, n, &hdev->long_term_keys) {
                struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list);
-               seq_printf(f, "%pMR (type %u) %u %u %u %.4x %*phN %*phN\\n",
+               seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %*phN %*phN\n",
                           &ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
                           ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
                           8, ltk->rand, 16, ltk->val);
@@ -636,6 +757,34 @@ static int conn_max_interval_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
                        conn_max_interval_set, "%llu\n");
 
+static int adv_channel_map_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x01 || val > 0x07)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_adv_channel_map = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int adv_channel_map_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_adv_channel_map;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get,
+                       adv_channel_map_set, "%llu\n");
+
 static ssize_t lowpan_read(struct file *file, char __user *user_buf,
                           size_t count, loff_t *ppos)
 {
@@ -1288,6 +1437,10 @@ static void hci_set_event_mask_page_2(struct hci_request *req)
                events[2] |= 0x08;      /* Truncated Page Complete */
        }
 
+       /* Enable Authenticated Payload Timeout Expired event if supported */
+       if (lmp_ping_capable(hdev))
+               events[2] |= 0x80;
+
        hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2, sizeof(events), events);
 }
 
@@ -1323,17 +1476,19 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
                hci_setup_link_policy(req);
 
        if (lmp_le_capable(hdev)) {
-               if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
-                       /* If the controller has a public BD_ADDR, then
-                        * by default use that one. If this is a LE only
-                        * controller without a public address, default
-                        * to the random address.
-                        */
-                       if (bacmp(&hdev->bdaddr, BDADDR_ANY))
-                               hdev->own_addr_type = ADDR_LE_DEV_PUBLIC;
-                       else
-                               hdev->own_addr_type = ADDR_LE_DEV_RANDOM;
-               }
+               /* If the controller has a public BD_ADDR, then by default
+                * use that one. If this is a LE only controller without
+                * a public address, default to the random address.
+                *
+                * For debugging purposes it is possible to force
+                * controllers with a public address to use the
+                * random address instead.
+                */
+               if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) ||
+                   !bacmp(&hdev->bdaddr, BDADDR_ANY))
+                       hdev->own_addr_type = ADDR_LE_DEV_RANDOM;
+               else
+                       hdev->own_addr_type = ADDR_LE_DEV_PUBLIC;
 
                hci_set_le_support(req);
        }
@@ -1359,6 +1514,15 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt)
        /* Check for Synchronization Train support */
        if (lmp_sync_train_capable(hdev))
                hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL);
+
+       /* Enable Secure Connections if supported and configured */
+       if ((lmp_sc_capable(hdev) ||
+            test_bit(HCI_FORCE_SC, &hdev->dev_flags)) &&
+           test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
+               u8 support = 0x01;
+               hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
+                           sizeof(support), &support);
+       }
 }
 
 static int __hci_init(struct hci_dev *hdev)
@@ -1417,8 +1581,6 @@ static int __hci_init(struct hci_dev *hdev)
                                    hdev, &inquiry_cache_fops);
                debugfs_create_file("link_keys", 0400, hdev->debugfs,
                                    hdev, &link_keys_fops);
-               debugfs_create_file("use_debug_keys", 0444, hdev->debugfs,
-                                   hdev, &use_debug_keys_fops);
                debugfs_create_file("dev_class", 0444, hdev->debugfs,
                                    hdev, &dev_class_fops);
                debugfs_create_file("voice_setting", 0444, hdev->debugfs,
@@ -1430,6 +1592,10 @@ static int __hci_init(struct hci_dev *hdev)
                                    hdev, &auto_accept_delay_fops);
                debugfs_create_file("ssp_debug_mode", 0644, hdev->debugfs,
                                    hdev, &ssp_debug_mode_fops);
+               debugfs_create_file("force_sc_support", 0644, hdev->debugfs,
+                                   hdev, &force_sc_support_fops);
+               debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
+                                   hdev, &sc_only_mode_fops);
        }
 
        if (lmp_sniff_capable(hdev)) {
@@ -1442,18 +1608,33 @@ static int __hci_init(struct hci_dev *hdev)
        }
 
        if (lmp_le_capable(hdev)) {
+               debugfs_create_file("random_address", 0444, hdev->debugfs,
+                                   hdev, &random_address_fops);
+               debugfs_create_file("static_address", 0444, hdev->debugfs,
+                                   hdev, &static_address_fops);
+
+               /* For controllers with a public address, provide a debug
+                * option to force the usage of the configured static
+                * address. By default the public address is used.
+                */
+               if (bacmp(&hdev->bdaddr, BDADDR_ANY))
+                       debugfs_create_file("force_static_address", 0644,
+                                           hdev->debugfs, hdev,
+                                           &force_static_address_fops);
+
                debugfs_create_u8("white_list_size", 0444, hdev->debugfs,
                                  &hdev->le_white_list_size);
-               debugfs_create_file("static_address", 0444, hdev->debugfs,
-                                  hdev, &static_address_fops);
-               debugfs_create_file("own_address_type", 0644, hdev->debugfs,
-                                   hdev, &own_address_type_fops);
+               debugfs_create_file("identity_resolving_keys", 0400,
+                                   hdev->debugfs, hdev,
+                                   &identity_resolving_keys_fops);
                debugfs_create_file("long_term_keys", 0400, hdev->debugfs,
                                    hdev, &long_term_keys_fops);
                debugfs_create_file("conn_min_interval", 0644, hdev->debugfs,
                                    hdev, &conn_min_interval_fops);
                debugfs_create_file("conn_max_interval", 0644, hdev->debugfs,
                                    hdev, &conn_max_interval_fops);
+               debugfs_create_file("adv_channel_map", 0644, hdev->debugfs,
+                                   hdev, &adv_channel_map_fops);
                debugfs_create_file("6lowpan", 0644, hdev->debugfs, hdev,
                                    &lowpan_debugfs_fops);
        }
@@ -1876,10 +2057,15 @@ static int hci_dev_do_open(struct hci_dev *hdev)
                 * be able to determine if there is a public address
                 * or not.
                 *
+                * In case of user channel usage, it is not important
+                * if a public address or static random address is
+                * available.
+                *
                 * This check is only valid for BR/EDR controllers
                 * since AMP controllers do not have an address.
                 */
-               if (hdev->dev_type == HCI_BREDR &&
+               if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) &&
+                   hdev->dev_type == HCI_BREDR &&
                    !bacmp(&hdev->bdaddr, BDADDR_ANY) &&
                    !bacmp(&hdev->static_addr, BDADDR_ANY)) {
                        ret = -EADDRNOTAVAIL;
@@ -2074,6 +2260,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
 
        memset(hdev->eir, 0, sizeof(hdev->eir));
        memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
+       bacpy(&hdev->random_addr, BDADDR_ANY);
 
        hci_req_unlock(hdev);
 
@@ -2437,7 +2624,7 @@ static void hci_discov_off(struct work_struct *work)
        mgmt_discoverable_timeout(hdev);
 }
 
-int hci_uuids_clear(struct hci_dev *hdev)
+void hci_uuids_clear(struct hci_dev *hdev)
 {
        struct bt_uuid *uuid, *tmp;
 
@@ -2445,11 +2632,9 @@ int hci_uuids_clear(struct hci_dev *hdev)
                list_del(&uuid->list);
                kfree(uuid);
        }
-
-       return 0;
 }
 
-int hci_link_keys_clear(struct hci_dev *hdev)
+void hci_link_keys_clear(struct hci_dev *hdev)
 {
        struct list_head *p, *n;
 
@@ -2461,11 +2646,9 @@ int hci_link_keys_clear(struct hci_dev *hdev)
                list_del(p);
                kfree(key);
        }
-
-       return 0;
 }
 
-int hci_smp_ltks_clear(struct hci_dev *hdev)
+void hci_smp_ltks_clear(struct hci_dev *hdev)
 {
        struct smp_ltk *k, *tmp;
 
@@ -2473,8 +2656,16 @@ int hci_smp_ltks_clear(struct hci_dev *hdev)
                list_del(&k->list);
                kfree(k);
        }
+}
 
-       return 0;
+void hci_smp_irks_clear(struct hci_dev *hdev)
+{
+       struct smp_irk *k, *tmp;
+
+       list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
+               list_del(&k->list);
+               kfree(k);
+       }
 }
 
 struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
@@ -2524,7 +2715,16 @@ static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn,
        return false;
 }
 
-struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8])
+static bool ltk_type_master(u8 type)
+{
+       if (type == HCI_SMP_STK || type == HCI_SMP_LTK)
+               return true;
+
+       return false;
+}
+
+struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8],
+                            bool master)
 {
        struct smp_ltk *k;
 
@@ -2533,6 +2733,9 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8])
                    memcmp(rand, k->rand, sizeof(k->rand)))
                        continue;
 
+               if (ltk_type_master(k->type) != master)
+                       continue;
+
                return k;
        }
 
@@ -2540,18 +2743,56 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8])
 }
 
 struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
-                                    u8 addr_type)
+                                    u8 addr_type, bool master)
 {
        struct smp_ltk *k;
 
        list_for_each_entry(k, &hdev->long_term_keys, list)
                if (addr_type == k->bdaddr_type &&
-                   bacmp(bdaddr, &k->bdaddr) == 0)
+                   bacmp(bdaddr, &k->bdaddr) == 0 &&
+                   ltk_type_master(k->type) == master)
                        return k;
 
        return NULL;
 }
 
+struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa)
+{
+       struct smp_irk *irk;
+
+       list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
+               if (!bacmp(&irk->rpa, rpa))
+                       return irk;
+       }
+
+       list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
+               if (smp_irk_matches(hdev->tfm_aes, irk->val, rpa)) {
+                       bacpy(&irk->rpa, rpa);
+                       return irk;
+               }
+       }
+
+       return NULL;
+}
+
+struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                                    u8 addr_type)
+{
+       struct smp_irk *irk;
+
+       /* Identity Address must be public or static random */
+       if (addr_type == ADDR_LE_DEV_RANDOM && (bdaddr->b[5] & 0xc0) != 0xc0)
+               return NULL;
+
+       list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
+               if (addr_type == irk->addr_type &&
+                   bacmp(bdaddr, &irk->bdaddr) == 0)
+                       return irk;
+       }
+
+       return NULL;
+}
+
 int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
                     bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len)
 {
@@ -2565,7 +2806,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
                key = old_key;
        } else {
                old_key_type = conn ? conn->key_type : 0xff;
-               key = kzalloc(sizeof(*key), GFP_ATOMIC);
+               key = kzalloc(sizeof(*key), GFP_KERNEL);
                if (!key)
                        return -ENOMEM;
                list_add(&key->list, &hdev->link_keys);
@@ -2605,22 +2846,20 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
        return 0;
 }
 
-int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type,
-               int new_key, u8 authenticated, u8 tk[16], u8 enc_size, __le16
-               ediv, u8 rand[8])
+struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                           u8 addr_type, u8 type, u8 authenticated,
+                           u8 tk[16], u8 enc_size, __le16 ediv, u8 rand[8])
 {
        struct smp_ltk *key, *old_key;
+       bool master = ltk_type_master(type);
 
-       if (!(type & HCI_SMP_STK) && !(type & HCI_SMP_LTK))
-               return 0;
-
-       old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type);
+       old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, master);
        if (old_key)
                key = old_key;
        else {
-               key = kzalloc(sizeof(*key), GFP_ATOMIC);
+               key = kzalloc(sizeof(*key), GFP_KERNEL);
                if (!key)
-                       return -ENOMEM;
+                       return NULL;
                list_add(&key->list, &hdev->long_term_keys);
        }
 
@@ -2633,13 +2872,30 @@ int hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type, u8 type,
        key->type = type;
        memcpy(key->rand, rand, sizeof(key->rand));
 
-       if (!new_key)
-               return 0;
+       return key;
+}
 
-       if (type & HCI_SMP_LTK)
-               mgmt_new_ltk(hdev, key, 1);
+struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                           u8 addr_type, u8 val[16], bdaddr_t *rpa)
+{
+       struct smp_irk *irk;
 
-       return 0;
+       irk = hci_find_irk_by_addr(hdev, bdaddr, addr_type);
+       if (!irk) {
+               irk = kzalloc(sizeof(*irk), GFP_KERNEL);
+               if (!irk)
+                       return NULL;
+
+               bacpy(&irk->bdaddr, bdaddr);
+               irk->addr_type = addr_type;
+
+               list_add(&irk->list, &hdev->identity_resolving_keys);
+       }
+
+       memcpy(irk->val, val, 16);
+       bacpy(&irk->rpa, rpa);
+
+       return irk;
 }
 
 int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
@@ -2658,21 +2914,38 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
        return 0;
 }
 
-int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)
 {
        struct smp_ltk *k, *tmp;
+       int removed = 0;
 
        list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
-               if (bacmp(bdaddr, &k->bdaddr))
+               if (bacmp(bdaddr, &k->bdaddr) || k->bdaddr_type != bdaddr_type)
                        continue;
 
                BT_DBG("%s removing %pMR", hdev->name, bdaddr);
 
                list_del(&k->list);
                kfree(k);
+               removed++;
        }
 
-       return 0;
+       return removed ? 0 : -ENOENT;
+}
+
+void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type)
+{
+       struct smp_irk *k, *tmp;
+
+       list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
+               if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type)
+                       continue;
+
+               BT_DBG("%s removing %pMR", hdev->name, bdaddr);
+
+               list_del(&k->list);
+               kfree(k);
+       }
 }
 
 /* HCI command timer function */
@@ -2721,7 +2994,7 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr)
        return 0;
 }
 
-int hci_remote_oob_data_clear(struct hci_dev *hdev)
+void hci_remote_oob_data_clear(struct hci_dev *hdev)
 {
        struct oob_data *data, *n;
 
@@ -2729,19 +3002,43 @@ int hci_remote_oob_data_clear(struct hci_dev *hdev)
                list_del(&data->list);
                kfree(data);
        }
+}
+
+int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                           u8 *hash, u8 *randomizer)
+{
+       struct oob_data *data;
+
+       data = hci_find_remote_oob_data(hdev, bdaddr);
+       if (!data) {
+               data = kmalloc(sizeof(*data), GFP_KERNEL);
+               if (!data)
+                       return -ENOMEM;
+
+               bacpy(&data->bdaddr, bdaddr);
+               list_add(&data->list, &hdev->remote_oob_data);
+       }
+
+       memcpy(data->hash192, hash, sizeof(data->hash192));
+       memcpy(data->randomizer192, randomizer, sizeof(data->randomizer192));
+
+       memset(data->hash256, 0, sizeof(data->hash256));
+       memset(data->randomizer256, 0, sizeof(data->randomizer256));
+
+       BT_DBG("%s for %pMR", hdev->name, bdaddr);
 
        return 0;
 }
 
-int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
-                           u8 *randomizer)
+int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
+                               u8 *hash192, u8 *randomizer192,
+                               u8 *hash256, u8 *randomizer256)
 {
        struct oob_data *data;
 
        data = hci_find_remote_oob_data(hdev, bdaddr);
-
        if (!data) {
-               data = kmalloc(sizeof(*data), GFP_ATOMIC);
+               data = kmalloc(sizeof(*data), GFP_KERNEL);
                if (!data)
                        return -ENOMEM;
 
@@ -2749,8 +3046,11 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
                list_add(&data->list, &hdev->remote_oob_data);
        }
 
-       memcpy(data->hash, hash, sizeof(data->hash));
-       memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
+       memcpy(data->hash192, hash192, sizeof(data->hash192));
+       memcpy(data->randomizer192, randomizer192, sizeof(data->randomizer192));
+
+       memcpy(data->hash256, hash256, sizeof(data->hash256));
+       memcpy(data->randomizer256, randomizer256, sizeof(data->randomizer256));
 
        BT_DBG("%s for %pMR", hdev->name, bdaddr);
 
@@ -2770,7 +3070,7 @@ struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev,
        return NULL;
 }
 
-int hci_blacklist_clear(struct hci_dev *hdev)
+void hci_blacklist_clear(struct hci_dev *hdev)
 {
        struct list_head *p, *n;
 
@@ -2780,8 +3080,6 @@ int hci_blacklist_clear(struct hci_dev *hdev)
                list_del(p);
                kfree(b);
        }
-
-       return 0;
 }
 
 int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
@@ -2810,8 +3108,10 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
 {
        struct bdaddr_list *entry;
 
-       if (!bacmp(bdaddr, BDADDR_ANY))
-               return hci_blacklist_clear(hdev);
+       if (!bacmp(bdaddr, BDADDR_ANY)) {
+               hci_blacklist_clear(hdev);
+               return 0;
+       }
 
        entry = hci_blacklist_lookup(hdev, bdaddr, type);
        if (!entry)
@@ -2823,6 +3123,81 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
        return mgmt_device_unblocked(hdev, bdaddr, type);
 }
 
+/* This function requires the caller holds hdev->lock */
+struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev,
+                                              bdaddr_t *addr, u8 addr_type)
+{
+       struct hci_conn_params *params;
+
+       list_for_each_entry(params, &hdev->le_conn_params, list) {
+               if (bacmp(&params->addr, addr) == 0 &&
+                   params->addr_type == addr_type) {
+                       return params;
+               }
+       }
+
+       return NULL;
+}
+
+/* This function requires the caller holds hdev->lock */
+void hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
+                        u16 conn_min_interval, u16 conn_max_interval)
+{
+       struct hci_conn_params *params;
+
+       params = hci_conn_params_lookup(hdev, addr, addr_type);
+       if (params) {
+               params->conn_min_interval = conn_min_interval;
+               params->conn_max_interval = conn_max_interval;
+               return;
+       }
+
+       params = kzalloc(sizeof(*params), GFP_KERNEL);
+       if (!params) {
+               BT_ERR("Out of memory");
+               return;
+       }
+
+       bacpy(&params->addr, addr);
+       params->addr_type = addr_type;
+       params->conn_min_interval = conn_min_interval;
+       params->conn_max_interval = conn_max_interval;
+
+       list_add(&params->list, &hdev->le_conn_params);
+
+       BT_DBG("addr %pMR (type %u) conn_min_interval 0x%.4x "
+              "conn_max_interval 0x%.4x", addr, addr_type, conn_min_interval,
+              conn_max_interval);
+}
+
+/* This function requires the caller holds hdev->lock */
+void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type)
+{
+       struct hci_conn_params *params;
+
+       params = hci_conn_params_lookup(hdev, addr, addr_type);
+       if (!params)
+               return;
+
+       list_del(&params->list);
+       kfree(params);
+
+       BT_DBG("addr %pMR (type %u)", addr, addr_type);
+}
+
+/* This function requires the caller holds hdev->lock */
+void hci_conn_params_clear(struct hci_dev *hdev)
+{
+       struct hci_conn_params *params, *tmp;
+
+       list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) {
+               list_del(&params->list);
+               kfree(params);
+       }
+
+       BT_DBG("All LE connection parameters were removed");
+}
+
 static void inquiry_complete(struct hci_dev *hdev, u8 status)
 {
        if (status) {
@@ -2919,6 +3294,7 @@ struct hci_dev *hci_alloc_dev(void)
        hdev->sniff_max_interval = 800;
        hdev->sniff_min_interval = 80;
 
+       hdev->le_adv_channel_map = 0x07;
        hdev->le_scan_interval = 0x0060;
        hdev->le_scan_window = 0x0030;
        hdev->le_conn_min_interval = 0x0028;
@@ -2932,7 +3308,9 @@ struct hci_dev *hci_alloc_dev(void)
        INIT_LIST_HEAD(&hdev->uuids);
        INIT_LIST_HEAD(&hdev->link_keys);
        INIT_LIST_HEAD(&hdev->long_term_keys);
+       INIT_LIST_HEAD(&hdev->identity_resolving_keys);
        INIT_LIST_HEAD(&hdev->remote_oob_data);
+       INIT_LIST_HEAD(&hdev->le_conn_params);
        INIT_LIST_HEAD(&hdev->conn_hash.list);
 
        INIT_WORK(&hdev->rx_work, hci_rx_work);
@@ -3017,9 +3395,18 @@ int hci_register_dev(struct hci_dev *hdev)
 
        dev_set_name(&hdev->dev, "%s", hdev->name);
 
+       hdev->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0,
+                                              CRYPTO_ALG_ASYNC);
+       if (IS_ERR(hdev->tfm_aes)) {
+               BT_ERR("Unable to create crypto context");
+               error = PTR_ERR(hdev->tfm_aes);
+               hdev->tfm_aes = NULL;
+               goto err_wqueue;
+       }
+
        error = device_add(&hdev->dev);
        if (error < 0)
-               goto err_wqueue;
+               goto err_tfm;
 
        hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
                                    RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops,
@@ -3055,6 +3442,8 @@ int hci_register_dev(struct hci_dev *hdev)
 
        return id;
 
+err_tfm:
+       crypto_free_blkcipher(hdev->tfm_aes);
 err_wqueue:
        destroy_workqueue(hdev->workqueue);
        destroy_workqueue(hdev->req_workqueue);
@@ -3105,6 +3494,9 @@ void hci_unregister_dev(struct hci_dev *hdev)
                rfkill_destroy(hdev->rfkill);
        }
 
+       if (hdev->tfm_aes)
+               crypto_free_blkcipher(hdev->tfm_aes);
+
        device_del(&hdev->dev);
 
        debugfs_remove_recursive(hdev->debugfs);
@@ -3117,7 +3509,9 @@ void hci_unregister_dev(struct hci_dev *hdev)
        hci_uuids_clear(hdev);
        hci_link_keys_clear(hdev);
        hci_smp_ltks_clear(hdev);
+       hci_smp_irks_clear(hdev);
        hci_remote_oob_data_clear(hdev);
+       hci_conn_params_clear(hdev);
        hci_dev_unlock(hdev);
 
        hci_dev_put(hdev);
index 5f812455a4504260927cb2dd5a3bdc018e0e20d4..4327b129d38e2d4d7c6d92f3d274538ecf3fa6ea 100644 (file)
@@ -461,6 +461,34 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
        }
 }
 
+static void hci_cc_write_sc_support(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       u8 status = *((u8 *) skb->data);
+       struct hci_cp_write_sc_support *sent;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+       sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SC_SUPPORT);
+       if (!sent)
+               return;
+
+       if (!status) {
+               if (sent->support)
+                       hdev->features[1][0] |= LMP_HOST_SC;
+               else
+                       hdev->features[1][0] &= ~LMP_HOST_SC;
+       }
+
+       if (test_bit(HCI_MGMT, &hdev->dev_flags))
+               mgmt_sc_enable_complete(hdev, sent->support, status);
+       else if (!status) {
+               if (sent->support)
+                       set_bit(HCI_SC_ENABLED, &hdev->dev_flags);
+               else
+                       clear_bit(HCI_SC_ENABLED, &hdev->dev_flags);
+       }
+}
+
 static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_rp_read_local_version *rp = (void *) skb->data;
@@ -904,16 +932,50 @@ static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev,
        hci_dev_unlock(hdev);
 }
 
-static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
-                                            struct sk_buff *skb)
+static void hci_cc_read_local_oob_data(struct hci_dev *hdev,
+                                      struct sk_buff *skb)
 {
        struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
 
        BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
 
        hci_dev_lock(hdev);
-       mgmt_read_local_oob_data_reply_complete(hdev, rp->hash,
-                                               rp->randomizer, rp->status);
+       mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->randomizer,
+                                         NULL, NULL, rp->status);
+       hci_dev_unlock(hdev);
+}
+
+static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev,
+                                          struct sk_buff *skb)
+{
+       struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+       hci_dev_lock(hdev);
+       mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->randomizer192,
+                                         rp->hash256, rp->randomizer256,
+                                         rp->status);
+       hci_dev_unlock(hdev);
+}
+
+
+static void hci_cc_le_set_random_addr(struct hci_dev *hdev, struct sk_buff *skb)
+{
+       __u8 status = *((__u8 *) skb->data);
+       bdaddr_t *sent;
+
+       BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+       sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_RANDOM_ADDR);
+       if (!sent)
+               return;
+
+       hci_dev_lock(hdev);
+
+       if (!status)
+               bacpy(&hdev->random_addr, sent);
+
        hci_dev_unlock(hdev);
 }
 
@@ -1185,9 +1247,12 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev,
                return 0;
 
        /* Only request authentication for SSP connections or non-SSP
-        * devices with sec_level HIGH or if MITM protection is requested */
+        * devices with sec_level MEDIUM or HIGH or if MITM protection
+        * is requested.
+        */
        if (!hci_conn_ssp_enabled(conn) && !(conn->auth_type & 0x01) &&
-           conn->pending_sec_level != BT_SECURITY_HIGH)
+           conn->pending_sec_level != BT_SECURITY_HIGH &&
+           conn->pending_sec_level != BT_SECURITY_MEDIUM)
                return 0;
 
        return 1;
@@ -1659,7 +1724,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
        } else {
                conn->state = BT_CLOSED;
                if (conn->type == ACL_LINK)
-                       mgmt_connect_failed(hdev, &ev->bdaddr, conn->type,
+                       mgmt_connect_failed(hdev, &conn->dst, conn->type,
                                            conn->dst_type, ev->status);
        }
 
@@ -1943,35 +2008,46 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
        hci_dev_lock(hdev);
 
        conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
-       if (conn) {
-               if (!ev->status) {
-                       if (ev->encrypt) {
-                               /* Encryption implies authentication */
-                               conn->link_mode |= HCI_LM_AUTH;
-                               conn->link_mode |= HCI_LM_ENCRYPT;
-                               conn->sec_level = conn->pending_sec_level;
-                       } else
-                               conn->link_mode &= ~HCI_LM_ENCRYPT;
-               }
+       if (!conn)
+               goto unlock;
+
+       if (!ev->status) {
+               if (ev->encrypt) {
+                       /* Encryption implies authentication */
+                       conn->link_mode |= HCI_LM_AUTH;
+                       conn->link_mode |= HCI_LM_ENCRYPT;
+                       conn->sec_level = conn->pending_sec_level;
 
-               clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
+                       /* P-256 authentication key implies FIPS */
+                       if (conn->key_type == HCI_LK_AUTH_COMBINATION_P256)
+                               conn->link_mode |= HCI_LM_FIPS;
 
-               if (ev->status && conn->state == BT_CONNECTED) {
-                       hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE);
-                       hci_conn_drop(conn);
-                       goto unlock;
+                       if ((conn->type == ACL_LINK && ev->encrypt == 0x02) ||
+                           conn->type == LE_LINK)
+                               set_bit(HCI_CONN_AES_CCM, &conn->flags);
+               } else {
+                       conn->link_mode &= ~HCI_LM_ENCRYPT;
+                       clear_bit(HCI_CONN_AES_CCM, &conn->flags);
                }
+       }
 
-               if (conn->state == BT_CONFIG) {
-                       if (!ev->status)
-                               conn->state = BT_CONNECTED;
+       clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
 
-                       hci_proto_connect_cfm(conn, ev->status);
-                       hci_conn_drop(conn);
-               } else
-                       hci_encrypt_cfm(conn, ev->status, ev->encrypt);
+       if (ev->status && conn->state == BT_CONNECTED) {
+               hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE);
+               hci_conn_drop(conn);
+               goto unlock;
        }
 
+       if (conn->state == BT_CONFIG) {
+               if (!ev->status)
+                       conn->state = BT_CONNECTED;
+
+               hci_proto_connect_cfm(conn, ev->status);
+               hci_conn_drop(conn);
+       } else
+               hci_encrypt_cfm(conn, ev->status, ev->encrypt);
+
 unlock:
        hci_dev_unlock(hdev);
 }
@@ -2144,6 +2220,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_write_ssp_mode(hdev, skb);
                break;
 
+       case HCI_OP_WRITE_SC_SUPPORT:
+               hci_cc_write_sc_support(hdev, skb);
+               break;
+
        case HCI_OP_READ_LOCAL_VERSION:
                hci_cc_read_local_version(hdev, skb);
                break;
@@ -2213,7 +2293,11 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                break;
 
        case HCI_OP_READ_LOCAL_OOB_DATA:
-               hci_cc_read_local_oob_data_reply(hdev, skb);
+               hci_cc_read_local_oob_data(hdev, skb);
+               break;
+
+       case HCI_OP_READ_LOCAL_OOB_EXT_DATA:
+               hci_cc_read_local_oob_ext_data(hdev, skb);
                break;
 
        case HCI_OP_LE_READ_BUFFER_SIZE:
@@ -2244,6 +2328,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                hci_cc_user_passkey_neg_reply(hdev, skb);
                break;
 
+       case HCI_OP_LE_SET_RANDOM_ADDR:
+               hci_cc_le_set_random_addr(hdev, skb);
+               break;
+
        case HCI_OP_LE_SET_ADV_ENABLE:
                hci_cc_le_set_adv_enable(hdev, skb);
                break;
@@ -2630,7 +2718,8 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
 
        conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
        if (conn) {
-               if (key->type == HCI_LK_UNAUTH_COMBINATION &&
+               if ((key->type == HCI_LK_UNAUTH_COMBINATION_P192 ||
+                    key->type == HCI_LK_UNAUTH_COMBINATION_P256) &&
                    conn->auth_type != 0xff && (conn->auth_type & 0x01)) {
                        BT_DBG("%s ignoring unauthenticated key", hdev->name);
                        goto not_found;
@@ -2844,6 +2933,9 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev,
                         * features do not indicate SSP support */
                        clear_bit(HCI_CONN_SSP_ENABLED, &conn->flags);
                }
+
+               if (ev->features[0] & LMP_HOST_SC)
+                       set_bit(HCI_CONN_SC_ENABLED, &conn->flags);
        }
 
        if (conn->state != BT_CONFIG)
@@ -3337,20 +3429,36 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
 
        data = hci_find_remote_oob_data(hdev, &ev->bdaddr);
        if (data) {
-               struct hci_cp_remote_oob_data_reply cp;
+               if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
+                       struct hci_cp_remote_oob_ext_data_reply cp;
 
-               bacpy(&cp.bdaddr, &ev->bdaddr);
-               memcpy(cp.hash, data->hash, sizeof(cp.hash));
-               memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer));
+                       bacpy(&cp.bdaddr, &ev->bdaddr);
+                       memcpy(cp.hash192, data->hash192, sizeof(cp.hash192));
+                       memcpy(cp.randomizer192, data->randomizer192,
+                              sizeof(cp.randomizer192));
+                       memcpy(cp.hash256, data->hash256, sizeof(cp.hash256));
+                       memcpy(cp.randomizer256, data->randomizer256,
+                              sizeof(cp.randomizer256));
+
+                       hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY,
+                                    sizeof(cp), &cp);
+               } else {
+                       struct hci_cp_remote_oob_data_reply cp;
 
-               hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY, sizeof(cp),
-                            &cp);
+                       bacpy(&cp.bdaddr, &ev->bdaddr);
+                       memcpy(cp.hash, data->hash192, sizeof(cp.hash));
+                       memcpy(cp.randomizer, data->randomizer192,
+                              sizeof(cp.randomizer));
+
+                       hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY,
+                                    sizeof(cp), &cp);
+               }
        } else {
                struct hci_cp_remote_oob_data_neg_reply cp;
 
                bacpy(&cp.bdaddr, &ev->bdaddr);
-               hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY, sizeof(cp),
-                            &cp);
+               hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY,
+                            sizeof(cp), &cp);
        }
 
 unlock:
@@ -3484,6 +3592,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 {
        struct hci_ev_le_conn_complete *ev = (void *) skb->data;
        struct hci_conn *conn;
+       struct smp_irk *irk;
 
        BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
 
@@ -3516,6 +3625,21 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
                }
        }
 
+       /* Lookup the identity address from the stored connection
+        * address and address type.
+        *
+        * When establishing connections to an identity address, the
+        * connection procedure will store the resolvable random
+        * address first. Now if it can be converted back into the
+        * identity address, start using the identity address from
+        * now on.
+        */
+       irk = hci_get_irk(hdev, &conn->dst, conn->dst_type);
+       if (irk) {
+               bacpy(&conn->dst, &irk->bdaddr);
+               conn->dst_type = irk->addr_type;
+       }
+
        if (ev->status) {
                mgmt_connect_failed(hdev, &conn->dst, conn->type,
                                    conn->dst_type, ev->status);
@@ -3526,7 +3650,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
        }
 
        if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
-               mgmt_device_connected(hdev, &ev->bdaddr, conn->type,
+               mgmt_device_connected(hdev, &conn->dst, conn->type,
                                      conn->dst_type, 0, NULL, 0, NULL);
 
        conn->sec_level = BT_SECURITY_LOW;
@@ -3577,7 +3701,7 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
        if (conn == NULL)
                goto not_found;
 
-       ltk = hci_find_ltk(hdev, ev->ediv, ev->random);
+       ltk = hci_find_ltk(hdev, ev->ediv, ev->random, conn->out);
        if (ltk == NULL)
                goto not_found;
 
index 7552f9e3089ce790040268f1ce0a067d6ef728cb..68e51a84e72ddb4f2eeba3fc76458d6a8f8a1c06 100644 (file)
@@ -716,6 +716,7 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
                err = hci_dev_open(hdev->id);
                if (err) {
                        clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags);
+                       mgmt_index_added(hdev);
                        hci_dev_put(hdev);
                        goto done;
                }
index 0b61250cfdf90c9e3a488c9ca3cce41ac79d6a84..555982a78a585513431722a7f65e1934dc081f86 100644 (file)
@@ -49,14 +49,7 @@ static struct attribute *bt_link_attrs[] = {
        NULL
 };
 
-static struct attribute_group bt_link_group = {
-       .attrs = bt_link_attrs,
-};
-
-static const struct attribute_group *bt_link_groups[] = {
-       &bt_link_group,
-       NULL
-};
+ATTRIBUTE_GROUPS(bt_link);
 
 static void bt_link_release(struct device *dev)
 {
@@ -182,14 +175,7 @@ static struct attribute *bt_host_attrs[] = {
        NULL
 };
 
-static struct attribute_group bt_host_group = {
-       .attrs = bt_host_attrs,
-};
-
-static const struct attribute_group *bt_host_groups[] = {
-       &bt_host_group,
-       NULL
-};
+ATTRIBUTE_GROUPS(bt_host);
 
 static void bt_host_release(struct device *dev)
 {
index b0ad2c752d738039cff8ec32c028dd109a10388b..6ace116f3b394ec8b83e5751694318f736254da1 100644 (file)
@@ -42,6 +42,8 @@
 #include "amp.h"
 #include "6lowpan.h"
 
+#define LE_FLOWCTL_MAX_CREDITS 65535
+
 bool disable_ertm;
 
 static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
@@ -330,44 +332,20 @@ static inline bool l2cap_seq_list_contains(struct l2cap_seq_list *seq_list,
        return seq_list->list[seq & seq_list->mask] != L2CAP_SEQ_LIST_CLEAR;
 }
 
-static u16 l2cap_seq_list_remove(struct l2cap_seq_list *seq_list, u16 seq)
+static inline u16 l2cap_seq_list_pop(struct l2cap_seq_list *seq_list)
 {
+       u16 seq = seq_list->head;
        u16 mask = seq_list->mask;
 
-       if (seq_list->head == L2CAP_SEQ_LIST_CLEAR) {
-               /* In case someone tries to pop the head of an empty list */
-               return L2CAP_SEQ_LIST_CLEAR;
-       } else if (seq_list->head == seq) {
-               /* Head can be removed in constant time */
-               seq_list->head = seq_list->list[seq & mask];
-               seq_list->list[seq & mask] = L2CAP_SEQ_LIST_CLEAR;
-
-               if (seq_list->head == L2CAP_SEQ_LIST_TAIL) {
-                       seq_list->head = L2CAP_SEQ_LIST_CLEAR;
-                       seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
-               }
-       } else {
-               /* Walk the list to find the sequence number */
-               u16 prev = seq_list->head;
-               while (seq_list->list[prev & mask] != seq) {
-                       prev = seq_list->list[prev & mask];
-                       if (prev == L2CAP_SEQ_LIST_TAIL)
-                               return L2CAP_SEQ_LIST_CLEAR;
-               }
+       seq_list->head = seq_list->list[seq & mask];
+       seq_list->list[seq & mask] = L2CAP_SEQ_LIST_CLEAR;
 
-               /* Unlink the number from the list and clear it */
-               seq_list->list[prev & mask] = seq_list->list[seq & mask];
-               seq_list->list[seq & mask] = L2CAP_SEQ_LIST_CLEAR;
-               if (seq_list->tail == seq)
-                       seq_list->tail = prev;
+       if (seq_list->head == L2CAP_SEQ_LIST_TAIL) {
+               seq_list->head = L2CAP_SEQ_LIST_CLEAR;
+               seq_list->tail = L2CAP_SEQ_LIST_CLEAR;
        }
-       return seq;
-}
 
-static inline u16 l2cap_seq_list_pop(struct l2cap_seq_list *seq_list)
-{
-       /* Remove the head in constant time */
-       return l2cap_seq_list_remove(seq_list, seq_list->head);
+       return seq;
 }
 
 static void l2cap_seq_list_clear(struct l2cap_seq_list *seq_list)
@@ -506,7 +484,7 @@ static void l2cap_le_flowctl_init(struct l2cap_chan *chan)
        chan->sdu_len = 0;
        chan->tx_credits = 0;
        chan->rx_credits = le_max_credits;
-       chan->mps = min_t(u16, chan->imtu, L2CAP_LE_DEFAULT_MPS);
+       chan->mps = min_t(u16, chan->imtu, le_default_mps);
 
        skb_queue_head_init(&chan->tx_q);
 }
@@ -522,18 +500,10 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
 
        switch (chan->chan_type) {
        case L2CAP_CHAN_CONN_ORIENTED:
-               if (conn->hcon->type == LE_LINK) {
-                       if (chan->dcid == L2CAP_CID_ATT) {
-                               chan->omtu = L2CAP_DEFAULT_MTU;
-                               chan->scid = L2CAP_CID_ATT;
-                       } else {
-                               chan->scid = l2cap_alloc_cid(conn);
-                       }
-               } else {
-                       /* Alloc CID for connection-oriented socket */
-                       chan->scid = l2cap_alloc_cid(conn);
+               /* Alloc CID for connection-oriented socket */
+               chan->scid = l2cap_alloc_cid(conn);
+               if (conn->hcon->type == ACL_LINK)
                        chan->omtu = L2CAP_DEFAULT_MTU;
-               }
                break;
 
        case L2CAP_CHAN_CONN_LESS:
@@ -543,11 +513,8 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
                chan->omtu = L2CAP_DEFAULT_MTU;
                break;
 
-       case L2CAP_CHAN_CONN_FIX_A2MP:
-               chan->scid = L2CAP_CID_A2MP;
-               chan->dcid = L2CAP_CID_A2MP;
-               chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
-               chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
+       case L2CAP_CHAN_FIXED:
+               /* Caller will set CID and CID specific MTU values */
                break;
 
        default:
@@ -595,7 +562,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
 
                chan->conn = NULL;
 
-               if (chan->chan_type != L2CAP_CHAN_CONN_FIX_A2MP)
+               if (chan->scid != L2CAP_CID_A2MP)
                        hci_conn_drop(conn->hcon);
 
                if (mgr && mgr->bredr_chan == chan)
@@ -642,6 +609,23 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
        return;
 }
 
+void l2cap_conn_update_id_addr(struct hci_conn *hcon)
+{
+       struct l2cap_conn *conn = hcon->l2cap_data;
+       struct l2cap_chan *chan;
+
+       mutex_lock(&conn->chan_lock);
+
+       list_for_each_entry(chan, &conn->chan_l, list) {
+               l2cap_chan_lock(chan);
+               bacpy(&chan->dst, &hcon->dst);
+               chan->dst_type = bdaddr_type(hcon, hcon->dst_type);
+               l2cap_chan_unlock(chan);
+       }
+
+       mutex_unlock(&conn->chan_lock);
+}
+
 static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
 {
        struct l2cap_conn *conn = chan->conn;
@@ -699,10 +683,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
 
        case BT_CONNECTED:
        case BT_CONFIG:
-               /* ATT uses L2CAP_CHAN_CONN_ORIENTED so we must also
-                * check for chan->psm.
-                */
-               if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && chan->psm) {
+               if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) {
                        __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
                        l2cap_send_disconn_req(chan, reason);
                } else
@@ -737,6 +718,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
        case L2CAP_CHAN_RAW:
                switch (chan->sec_level) {
                case BT_SECURITY_HIGH:
+               case BT_SECURITY_FIPS:
                        return HCI_AT_DEDICATED_BONDING_MITM;
                case BT_SECURITY_MEDIUM:
                        return HCI_AT_DEDICATED_BONDING;
@@ -749,7 +731,8 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
                        if (chan->sec_level == BT_SECURITY_LOW)
                                chan->sec_level = BT_SECURITY_SDP;
                }
-               if (chan->sec_level == BT_SECURITY_HIGH)
+               if (chan->sec_level == BT_SECURITY_HIGH ||
+                   chan->sec_level == BT_SECURITY_FIPS)
                        return HCI_AT_NO_BONDING_MITM;
                else
                        return HCI_AT_NO_BONDING;
@@ -759,7 +742,8 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
                        if (chan->sec_level == BT_SECURITY_LOW)
                                chan->sec_level = BT_SECURITY_SDP;
 
-                       if (chan->sec_level == BT_SECURITY_HIGH)
+                       if (chan->sec_level == BT_SECURITY_HIGH ||
+                           chan->sec_level == BT_SECURITY_FIPS)
                                return HCI_AT_NO_BONDING_MITM;
                        else
                                return HCI_AT_NO_BONDING;
@@ -768,6 +752,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan)
        default:
                switch (chan->sec_level) {
                case BT_SECURITY_HIGH:
+               case BT_SECURITY_FIPS:
                        return HCI_AT_GENERAL_BONDING_MITM;
                case BT_SECURITY_MEDIUM:
                        return HCI_AT_GENERAL_BONDING;
@@ -1330,7 +1315,7 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
                __clear_ack_timer(chan);
        }
 
-       if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+       if (chan->scid == L2CAP_CID_A2MP) {
                l2cap_state_change(chan, BT_DISCONN);
                return;
        }
@@ -1493,8 +1478,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
        if (!chan)
                goto clean;
 
-       chan->dcid = L2CAP_CID_ATT;
-
        bacpy(&chan->src, &hcon->src);
        bacpy(&chan->dst, &hcon->dst);
        chan->src_type = bdaddr_type(hcon, hcon->src_type);
@@ -1528,7 +1511,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
 
                l2cap_chan_lock(chan);
 
-               if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+               if (chan->scid == L2CAP_CID_A2MP) {
                        l2cap_chan_unlock(chan);
                        continue;
                }
@@ -1546,6 +1529,8 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
        }
 
        mutex_unlock(&conn->chan_lock);
+
+       queue_work(hcon->hdev->workqueue, &conn->pending_rx_work);
 }
 
 /* Notify sockets that we cannot guaranty reliability anymore */
@@ -1671,6 +1656,9 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
 
        kfree_skb(conn->rx_skb);
 
+       skb_queue_purge(&conn->pending_rx);
+       flush_work(&conn->pending_rx_work);
+
        l2cap_unregister_all_users(conn);
 
        mutex_lock(&conn->chan_lock);
@@ -1718,66 +1706,6 @@ static void security_timeout(struct work_struct *work)
        }
 }
 
-static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
-{
-       struct l2cap_conn *conn = hcon->l2cap_data;
-       struct hci_chan *hchan;
-
-       if (conn)
-               return conn;
-
-       hchan = hci_chan_create(hcon);
-       if (!hchan)
-               return NULL;
-
-       conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL);
-       if (!conn) {
-               hci_chan_del(hchan);
-               return NULL;
-       }
-
-       kref_init(&conn->ref);
-       hcon->l2cap_data = conn;
-       conn->hcon = hcon;
-       hci_conn_get(conn->hcon);
-       conn->hchan = hchan;
-
-       BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
-
-       switch (hcon->type) {
-       case LE_LINK:
-               if (hcon->hdev->le_mtu) {
-                       conn->mtu = hcon->hdev->le_mtu;
-                       break;
-               }
-               /* fall through */
-       default:
-               conn->mtu = hcon->hdev->acl_mtu;
-               break;
-       }
-
-       conn->feat_mask = 0;
-
-       if (hcon->type == ACL_LINK)
-               conn->hs_enabled = test_bit(HCI_HS_ENABLED,
-                                           &hcon->hdev->dev_flags);
-
-       spin_lock_init(&conn->lock);
-       mutex_init(&conn->chan_lock);
-
-       INIT_LIST_HEAD(&conn->chan_l);
-       INIT_LIST_HEAD(&conn->users);
-
-       if (hcon->type == LE_LINK)
-               INIT_DELAYED_WORK(&conn->security_timer, security_timeout);
-       else
-               INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
-
-       conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
-
-       return conn;
-}
-
 static void l2cap_conn_free(struct kref *ref)
 {
        struct l2cap_conn *conn = container_of(ref, struct l2cap_conn, ref);
@@ -1848,154 +1776,6 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
        return c1;
 }
 
-static bool is_valid_psm(u16 psm, u8 dst_type)
-{
-       if (!psm)
-               return false;
-
-       if (bdaddr_type_is_le(dst_type))
-               return (psm <= 0x00ff);
-
-       /* PSM must be odd and lsb of upper byte must be 0 */
-       return ((psm & 0x0101) == 0x0001);
-}
-
-int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
-                      bdaddr_t *dst, u8 dst_type)
-{
-       struct l2cap_conn *conn;
-       struct hci_conn *hcon;
-       struct hci_dev *hdev;
-       __u8 auth_type;
-       int err;
-
-       BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
-              dst_type, __le16_to_cpu(psm));
-
-       hdev = hci_get_route(dst, &chan->src);
-       if (!hdev)
-               return -EHOSTUNREACH;
-
-       hci_dev_lock(hdev);
-
-       l2cap_chan_lock(chan);
-
-       if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
-           chan->chan_type != L2CAP_CHAN_RAW) {
-               err = -EINVAL;
-               goto done;
-       }
-
-       if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !(psm || cid)) {
-               err = -EINVAL;
-               goto done;
-       }
-
-       switch (chan->mode) {
-       case L2CAP_MODE_BASIC:
-               break;
-       case L2CAP_MODE_LE_FLOWCTL:
-               l2cap_le_flowctl_init(chan);
-               break;
-       case L2CAP_MODE_ERTM:
-       case L2CAP_MODE_STREAMING:
-               if (!disable_ertm)
-                       break;
-               /* fall through */
-       default:
-               err = -ENOTSUPP;
-               goto done;
-       }
-
-       switch (chan->state) {
-       case BT_CONNECT:
-       case BT_CONNECT2:
-       case BT_CONFIG:
-               /* Already connecting */
-               err = 0;
-               goto done;
-
-       case BT_CONNECTED:
-               /* Already connected */
-               err = -EISCONN;
-               goto done;
-
-       case BT_OPEN:
-       case BT_BOUND:
-               /* Can connect */
-               break;
-
-       default:
-               err = -EBADFD;
-               goto done;
-       }
-
-       /* Set destination address and psm */
-       bacpy(&chan->dst, dst);
-       chan->dst_type = dst_type;
-
-       chan->psm = psm;
-       chan->dcid = cid;
-
-       auth_type = l2cap_get_auth_type(chan);
-
-       if (bdaddr_type_is_le(dst_type))
-               hcon = hci_connect(hdev, LE_LINK, dst, dst_type,
-                                  chan->sec_level, auth_type);
-       else
-               hcon = hci_connect(hdev, ACL_LINK, dst, dst_type,
-                                  chan->sec_level, auth_type);
-
-       if (IS_ERR(hcon)) {
-               err = PTR_ERR(hcon);
-               goto done;
-       }
-
-       conn = l2cap_conn_add(hcon);
-       if (!conn) {
-               hci_conn_drop(hcon);
-               err = -ENOMEM;
-               goto done;
-       }
-
-       if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
-               hci_conn_drop(hcon);
-               err = -EBUSY;
-               goto done;
-       }
-
-       /* Update source addr of the socket */
-       bacpy(&chan->src, &hcon->src);
-       chan->src_type = bdaddr_type(hcon, hcon->src_type);
-
-       l2cap_chan_unlock(chan);
-       l2cap_chan_add(conn, chan);
-       l2cap_chan_lock(chan);
-
-       /* l2cap_chan_add takes its own ref so we can drop this one */
-       hci_conn_drop(hcon);
-
-       l2cap_state_change(chan, BT_CONNECT);
-       __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
-
-       if (hcon->state == BT_CONNECTED) {
-               if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
-                       __clear_chan_timer(chan);
-                       if (l2cap_chan_check_security(chan))
-                               l2cap_state_change(chan, BT_CONNECTED);
-               } else
-                       l2cap_do_start(chan);
-       }
-
-       err = 0;
-
-done:
-       l2cap_chan_unlock(chan);
-       hci_dev_unlock(hdev);
-       hci_dev_put(hdev);
-       return err;
-}
-
 static void l2cap_monitor_timeout(struct work_struct *work)
 {
        struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
@@ -5709,7 +5489,7 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
 {
        struct l2cap_le_credits *pkt;
        struct l2cap_chan *chan;
-       u16 cid, credits;
+       u16 cid, credits, max_credits;
 
        if (cmd_len != sizeof(*pkt))
                return -EPROTO;
@@ -5724,6 +5504,17 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
        if (!chan)
                return -EBADSLT;
 
+       max_credits = LE_FLOWCTL_MAX_CREDITS - chan->tx_credits;
+       if (credits > max_credits) {
+               BT_ERR("LE credits overflow");
+               l2cap_send_disconn_req(chan, ECONNRESET);
+
+               /* Return 0 so that we don't trigger an unnecessary
+                * command reject packet.
+                */
+               return 0;
+       }
+
        chan->tx_credits += credits;
 
        while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
@@ -5770,17 +5561,6 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
 {
        int err = 0;
 
-       if (!enable_lecoc) {
-               switch (cmd->code) {
-               case L2CAP_LE_CONN_REQ:
-               case L2CAP_LE_CONN_RSP:
-               case L2CAP_LE_CREDITS:
-               case L2CAP_DISCONN_REQ:
-               case L2CAP_DISCONN_RSP:
-                       return -EINVAL;
-               }
-       }
-
        switch (cmd->code) {
        case L2CAP_COMMAND_REJ:
                l2cap_le_command_rej(conn, cmd, cmd_len, data);
@@ -6871,6 +6651,7 @@ static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
 
        if (!chan->rx_credits) {
                BT_ERR("No credits to receive LE L2CAP data");
+               l2cap_send_disconn_req(chan, ECONNRESET);
                return -ENOBUFS;
        }
 
@@ -6995,8 +6776,10 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
                 * But we don't have any other choice. L2CAP doesn't
                 * provide flow control mechanism. */
 
-               if (chan->imtu < skb->len)
+               if (chan->imtu < skb->len) {
+                       BT_ERR("Dropping L2CAP data: receive buffer overflow");
                        goto drop;
+               }
 
                if (!chan->ops->recv(chan, skb))
                        goto done;
@@ -7084,9 +6867,16 @@ drop:
 static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct l2cap_hdr *lh = (void *) skb->data;
+       struct hci_conn *hcon = conn->hcon;
        u16 cid, len;
        __le16 psm;
 
+       if (hcon->state != BT_CONNECTED) {
+               BT_DBG("queueing pending rx skb");
+               skb_queue_tail(&conn->pending_rx, skb);
+               return;
+       }
+
        skb_pull(skb, L2CAP_HDR_SIZE);
        cid = __le16_to_cpu(lh->cid);
        len = __le16_to_cpu(lh->len);
@@ -7132,6 +6922,240 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
        }
 }
 
+static void process_pending_rx(struct work_struct *work)
+{
+       struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
+                                              pending_rx_work);
+       struct sk_buff *skb;
+
+       BT_DBG("");
+
+       while ((skb = skb_dequeue(&conn->pending_rx)))
+               l2cap_recv_frame(conn, skb);
+}
+
+static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
+{
+       struct l2cap_conn *conn = hcon->l2cap_data;
+       struct hci_chan *hchan;
+
+       if (conn)
+               return conn;
+
+       hchan = hci_chan_create(hcon);
+       if (!hchan)
+               return NULL;
+
+       conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL);
+       if (!conn) {
+               hci_chan_del(hchan);
+               return NULL;
+       }
+
+       kref_init(&conn->ref);
+       hcon->l2cap_data = conn;
+       conn->hcon = hcon;
+       hci_conn_get(conn->hcon);
+       conn->hchan = hchan;
+
+       BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
+
+       switch (hcon->type) {
+       case LE_LINK:
+               if (hcon->hdev->le_mtu) {
+                       conn->mtu = hcon->hdev->le_mtu;
+                       break;
+               }
+               /* fall through */
+       default:
+               conn->mtu = hcon->hdev->acl_mtu;
+               break;
+       }
+
+       conn->feat_mask = 0;
+
+       if (hcon->type == ACL_LINK)
+               conn->hs_enabled = test_bit(HCI_HS_ENABLED,
+                                           &hcon->hdev->dev_flags);
+
+       spin_lock_init(&conn->lock);
+       mutex_init(&conn->chan_lock);
+
+       INIT_LIST_HEAD(&conn->chan_l);
+       INIT_LIST_HEAD(&conn->users);
+
+       if (hcon->type == LE_LINK)
+               INIT_DELAYED_WORK(&conn->security_timer, security_timeout);
+       else
+               INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
+
+       skb_queue_head_init(&conn->pending_rx);
+       INIT_WORK(&conn->pending_rx_work, process_pending_rx);
+
+       conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
+
+       return conn;
+}
+
+static bool is_valid_psm(u16 psm, u8 dst_type) {
+       if (!psm)
+               return false;
+
+       if (bdaddr_type_is_le(dst_type))
+               return (psm <= 0x00ff);
+
+       /* PSM must be odd and lsb of upper byte must be 0 */
+       return ((psm & 0x0101) == 0x0001);
+}
+
+int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
+                      bdaddr_t *dst, u8 dst_type)
+{
+       struct l2cap_conn *conn;
+       struct hci_conn *hcon;
+       struct hci_dev *hdev;
+       __u8 auth_type;
+       int err;
+
+       BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst,
+              dst_type, __le16_to_cpu(psm));
+
+       hdev = hci_get_route(dst, &chan->src);
+       if (!hdev)
+               return -EHOSTUNREACH;
+
+       hci_dev_lock(hdev);
+
+       l2cap_chan_lock(chan);
+
+       if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
+           chan->chan_type != L2CAP_CHAN_RAW) {
+               err = -EINVAL;
+               goto done;
+       }
+
+       if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && !psm) {
+               err = -EINVAL;
+               goto done;
+       }
+
+       if (chan->chan_type == L2CAP_CHAN_FIXED && !cid) {
+               err = -EINVAL;
+               goto done;
+       }
+
+       switch (chan->mode) {
+       case L2CAP_MODE_BASIC:
+               break;
+       case L2CAP_MODE_LE_FLOWCTL:
+               l2cap_le_flowctl_init(chan);
+               break;
+       case L2CAP_MODE_ERTM:
+       case L2CAP_MODE_STREAMING:
+               if (!disable_ertm)
+                       break;
+               /* fall through */
+       default:
+               err = -ENOTSUPP;
+               goto done;
+       }
+
+       switch (chan->state) {
+       case BT_CONNECT:
+       case BT_CONNECT2:
+       case BT_CONFIG:
+               /* Already connecting */
+               err = 0;
+               goto done;
+
+       case BT_CONNECTED:
+               /* Already connected */
+               err = -EISCONN;
+               goto done;
+
+       case BT_OPEN:
+       case BT_BOUND:
+               /* Can connect */
+               break;
+
+       default:
+               err = -EBADFD;
+               goto done;
+       }
+
+       /* Set destination address and psm */
+       bacpy(&chan->dst, dst);
+       chan->dst_type = dst_type;
+
+       chan->psm = psm;
+       chan->dcid = cid;
+
+       auth_type = l2cap_get_auth_type(chan);
+
+       if (bdaddr_type_is_le(dst_type))
+               hcon = hci_connect(hdev, LE_LINK, dst, dst_type,
+                                  chan->sec_level, auth_type);
+       else
+               hcon = hci_connect(hdev, ACL_LINK, dst, dst_type,
+                                  chan->sec_level, auth_type);
+
+       if (IS_ERR(hcon)) {
+               err = PTR_ERR(hcon);
+               goto done;
+       }
+
+       conn = l2cap_conn_add(hcon);
+       if (!conn) {
+               hci_conn_drop(hcon);
+               err = -ENOMEM;
+               goto done;
+       }
+
+       if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
+               hci_conn_drop(hcon);
+               err = -EBUSY;
+               goto done;
+       }
+
+       /* Update source addr of the socket */
+       bacpy(&chan->src, &hcon->src);
+       chan->src_type = bdaddr_type(hcon, hcon->src_type);
+
+       l2cap_chan_unlock(chan);
+       l2cap_chan_add(conn, chan);
+       l2cap_chan_lock(chan);
+
+       /* l2cap_chan_add takes its own ref so we can drop this one */
+       hci_conn_drop(hcon);
+
+       l2cap_state_change(chan, BT_CONNECT);
+       __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
+
+       /* Release chan->sport so that it can be reused by other
+        * sockets (as it's only used for listening sockets).
+        */
+       write_lock(&chan_list_lock);
+       chan->sport = 0;
+       write_unlock(&chan_list_lock);
+
+       if (hcon->state == BT_CONNECTED) {
+               if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
+                       __clear_chan_timer(chan);
+                       if (l2cap_chan_check_security(chan))
+                               l2cap_state_change(chan, BT_CONNECTED);
+               } else
+                       l2cap_do_start(chan);
+       }
+
+       err = 0;
+
+done:
+       l2cap_chan_unlock(chan);
+       hci_dev_unlock(hdev);
+       hci_dev_put(hdev);
+       return err;
+}
+
 /* ---- L2CAP interface with lower layer (HCI) ---- */
 
 int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
@@ -7206,7 +7230,8 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
        if (encrypt == 0x00) {
                if (chan->sec_level == BT_SECURITY_MEDIUM) {
                        __set_chan_timer(chan, L2CAP_ENC_TIMEOUT);
-               } else if (chan->sec_level == BT_SECURITY_HIGH)
+               } else if (chan->sec_level == BT_SECURITY_HIGH ||
+                          chan->sec_level == BT_SECURITY_FIPS)
                        l2cap_chan_close(chan, ECONNREFUSED);
        } else {
                if (chan->sec_level == BT_SECURITY_MEDIUM)
@@ -7238,7 +7263,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
                BT_DBG("chan %p scid 0x%4.4x state %s", chan, chan->scid,
                       state_to_string(chan->state));
 
-               if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
+               if (chan->scid == L2CAP_CID_A2MP) {
                        l2cap_chan_unlock(chan);
                        continue;
                }
index d58f76bcebd1e6866183360f778881f8e84d49b3..b247f9d27fed96dd1e3355265ed4ba6e0861138c 100644 (file)
@@ -36,8 +36,6 @@
 
 #include "smp.h"
 
-bool enable_lecoc;
-
 static struct bt_sock_list l2cap_sk_list = {
        .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock)
 };
@@ -101,9 +99,16 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
        if (!bdaddr_type_is_valid(la.l2_bdaddr_type))
                return -EINVAL;
 
+       if (la.l2_cid) {
+               /* When the socket gets created it defaults to
+                * CHAN_CONN_ORIENTED, so we need to overwrite the
+                * default here.
+                */
+               chan->chan_type = L2CAP_CHAN_FIXED;
+               chan->omtu = L2CAP_DEFAULT_MTU;
+       }
+
        if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
-               if (!enable_lecoc && la.l2_psm)
-                       return -EINVAL;
                /* We only allow ATT user space socket */
                if (la.l2_cid &&
                    la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
@@ -220,8 +225,6 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
                return -EINVAL;
 
        if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
-               if (!enable_lecoc && la.l2_psm)
-                       return -EINVAL;
                /* We only allow ATT user space socket */
                if (la.l2_cid &&
                    la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
@@ -357,17 +360,20 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
 
        BT_DBG("sock %p, sk %p", sock, sk);
 
+       if (peer && sk->sk_state != BT_CONNECTED)
+               return -ENOTCONN;
+
        memset(la, 0, sizeof(struct sockaddr_l2));
        addr->sa_family = AF_BLUETOOTH;
        *len = sizeof(struct sockaddr_l2);
 
+       la->l2_psm = chan->psm;
+
        if (peer) {
-               la->l2_psm = chan->psm;
                bacpy(&la->l2_bdaddr, &chan->dst);
                la->l2_cid = cpu_to_le16(chan->dcid);
                la->l2_bdaddr_type = chan->dst_type;
        } else {
-               la->l2_psm = chan->sport;
                bacpy(&la->l2_bdaddr, &chan->src);
                la->l2_cid = cpu_to_le16(chan->scid);
                la->l2_bdaddr_type = chan->src_type;
@@ -432,6 +438,10 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
                        opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
                              L2CAP_LM_SECURE;
                        break;
+               case BT_SECURITY_FIPS:
+                       opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
+                             L2CAP_LM_SECURE | L2CAP_LM_FIPS;
+                       break;
                default:
                        opt = 0;
                        break;
@@ -445,6 +455,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
 
                if (put_user(opt, (u32 __user *) optval))
                        err = -EFAULT;
+
                break;
 
        case L2CAP_CONNINFO:
@@ -499,6 +510,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
        switch (optname) {
        case BT_SECURITY:
                if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
+                   chan->chan_type != L2CAP_CHAN_FIXED &&
                    chan->chan_type != L2CAP_CHAN_RAW) {
                        err = -EINVAL;
                        break;
@@ -560,11 +572,6 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
                break;
 
        case BT_SNDMTU:
-               if (!enable_lecoc) {
-                       err = -EPROTONOSUPPORT;
-                       break;
-               }
-
                if (!bdaddr_type_is_le(chan->src_type)) {
                        err = -EINVAL;
                        break;
@@ -580,11 +587,6 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
                break;
 
        case BT_RCVMTU:
-               if (!enable_lecoc) {
-                       err = -EPROTONOSUPPORT;
-                       break;
-               }
-
                if (!bdaddr_type_is_le(chan->src_type)) {
                        err = -EINVAL;
                        break;
@@ -699,6 +701,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
                        break;
                }
 
+               if (opt & L2CAP_LM_FIPS) {
+                       err = -EINVAL;
+                       break;
+               }
+
                if (opt & L2CAP_LM_AUTH)
                        chan->sec_level = BT_SECURITY_LOW;
                if (opt & L2CAP_LM_ENCRYPT)
@@ -750,6 +757,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
        switch (optname) {
        case BT_SECURITY:
                if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
+                   chan->chan_type != L2CAP_CHAN_FIXED &&
                    chan->chan_type != L2CAP_CHAN_RAW) {
                        err = -EINVAL;
                        break;
@@ -895,11 +903,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
                break;
 
        case BT_SNDMTU:
-               if (!enable_lecoc) {
-                       err = -EPROTONOSUPPORT;
-                       break;
-               }
-
                if (!bdaddr_type_is_le(chan->src_type)) {
                        err = -EINVAL;
                        break;
@@ -912,11 +915,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
                break;
 
        case BT_RCVMTU:
-               if (!enable_lecoc) {
-                       err = -EPROTONOSUPPORT;
-                       break;
-               }
-
                if (!bdaddr_type_is_le(chan->src_type)) {
                        err = -EINVAL;
                        break;
@@ -1449,6 +1447,11 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
                chan->tx_credits = pchan->tx_credits;
                chan->rx_credits = pchan->rx_credits;
 
+               if (chan->chan_type == L2CAP_CHAN_FIXED) {
+                       chan->scid = pchan->scid;
+                       chan->dcid = pchan->scid;
+               }
+
                security_sk_clone(parent, sk);
        } else {
                switch (sk->sk_type) {
@@ -1614,6 +1617,3 @@ void l2cap_cleanup_sockets(void)
        bt_sock_unregister(BTPROTO_L2CAP);
        proto_unregister(&l2cap_proto);
 }
-
-module_param(enable_lecoc, bool, 0644);
-MODULE_PARM_DESC(enable_lecoc, "Enable support for LE CoC");
index a03ca3ca91bfa77e2663a09f90ce2271addb2331..12fa6399c796fed1afbf6344fc1d0899731a5840 100644 (file)
@@ -34,7 +34,7 @@
 #include "smp.h"
 
 #define MGMT_VERSION   1
-#define MGMT_REVISION  4
+#define MGMT_REVISION  5
 
 static const u16 mgmt_commands[] = {
        MGMT_OP_READ_INDEX_LIST,
@@ -79,6 +79,9 @@ static const u16 mgmt_commands[] = {
        MGMT_OP_SET_BREDR,
        MGMT_OP_SET_STATIC_ADDRESS,
        MGMT_OP_SET_SCAN_PARAMS,
+       MGMT_OP_SET_SECURE_CONN,
+       MGMT_OP_SET_DEBUG_KEYS,
+       MGMT_OP_LOAD_IRKS,
 };
 
 static const u16 mgmt_events[] = {
@@ -127,7 +130,7 @@ static u8 mgmt_status_table[] = {
        MGMT_STATUS_FAILED,             /* Hardware Failure */
        MGMT_STATUS_CONNECT_FAILED,     /* Page Timeout */
        MGMT_STATUS_AUTH_FAILED,        /* Authentication Failed */
-       MGMT_STATUS_NOT_PAIRED,         /* PIN or Key Missing */
+       MGMT_STATUS_AUTH_FAILED,        /* PIN or Key Missing */
        MGMT_STATUS_NO_RESOURCES,       /* Memory Full */
        MGMT_STATUS_TIMEOUT,            /* Connection Timeout */
        MGMT_STATUS_NO_RESOURCES,       /* Max Number of Connections */
@@ -363,6 +366,7 @@ static u32 get_supported_settings(struct hci_dev *hdev)
 
        settings |= MGMT_SETTING_POWERED;
        settings |= MGMT_SETTING_PAIRABLE;
+       settings |= MGMT_SETTING_DEBUG_KEYS;
 
        if (lmp_bredr_capable(hdev)) {
                settings |= MGMT_SETTING_CONNECTABLE;
@@ -376,6 +380,10 @@ static u32 get_supported_settings(struct hci_dev *hdev)
                        settings |= MGMT_SETTING_SSP;
                        settings |= MGMT_SETTING_HS;
                }
+
+               if (lmp_sc_capable(hdev) ||
+                   test_bit(HCI_FORCE_SC, &hdev->dev_flags))
+                       settings |= MGMT_SETTING_SECURE_CONN;
        }
 
        if (lmp_le_capable(hdev)) {
@@ -423,6 +431,12 @@ static u32 get_current_settings(struct hci_dev *hdev)
        if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
                settings |= MGMT_SETTING_ADVERTISING;
 
+       if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags))
+               settings |= MGMT_SETTING_SECURE_CONN;
+
+       if (test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags))
+               settings |= MGMT_SETTING_DEBUG_KEYS;
+
        return settings;
 }
 
@@ -629,14 +643,8 @@ static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr)
 
        flags |= get_adv_discov_flags(hdev);
 
-       if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
-               if (lmp_le_br_capable(hdev))
-                       flags |= LE_AD_SIM_LE_BREDR_CTRL;
-               if (lmp_host_le_br_capable(hdev))
-                       flags |= LE_AD_SIM_LE_BREDR_HOST;
-       } else {
+       if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
                flags |= LE_AD_NO_BREDR;
-       }
 
        if (flags) {
                BT_DBG("adv flags 0x%02x", flags);
@@ -1366,7 +1374,7 @@ static void enable_advertising(struct hci_request *req)
        cp.max_interval = __constant_cpu_to_le16(0x0800);
        cp.type = get_adv_type(hdev);
        cp.own_address_type = hdev->own_addr_type;
-       cp.channel_map = 0x07;
+       cp.channel_map = hdev->le_adv_channel_map;
 
        hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
 
@@ -2065,7 +2073,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
        }
 
        if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
-               err = hci_uuids_clear(hdev);
+               hci_uuids_clear(hdev);
 
                if (enable_service_cache(hdev)) {
                        err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID,
@@ -2205,6 +2213,7 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
 {
        struct mgmt_cp_load_link_keys *cp = data;
        u16 key_count, expected_len;
+       bool changed;
        int i;
 
        BT_DBG("request for %s", hdev->name);
@@ -2234,7 +2243,7 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
        for (i = 0; i < key_count; i++) {
                struct mgmt_link_key_info *key = &cp->keys[i];
 
-               if (key->addr.type != BDADDR_BREDR)
+               if (key->addr.type != BDADDR_BREDR || key->type > 0x08)
                        return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
                                          MGMT_STATUS_INVALID_PARAMS);
        }
@@ -2244,9 +2253,12 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
        hci_link_keys_clear(hdev);
 
        if (cp->debug_keys)
-               set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+               changed = !test_and_set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
        else
-               clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+               changed = test_and_clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+
+       if (changed)
+               new_settings(hdev, NULL);
 
        for (i = 0; i < key_count; i++) {
                struct mgmt_link_key_info *key = &cp->keys[i];
@@ -2306,10 +2318,20 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
                goto unlock;
        }
 
-       if (cp->addr.type == BDADDR_BREDR)
+       if (cp->addr.type == BDADDR_BREDR) {
                err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
-       else
-               err = hci_remove_ltk(hdev, &cp->addr.bdaddr);
+       } else {
+               u8 addr_type;
+
+               if (cp->addr.type == BDADDR_LE_PUBLIC)
+                       addr_type = ADDR_LE_DEV_PUBLIC;
+               else
+                       addr_type = ADDR_LE_DEV_RANDOM;
+
+               hci_remove_irk(hdev, &cp->addr.bdaddr, addr_type);
+
+               err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type);
+       }
 
        if (err < 0) {
                err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
@@ -2633,6 +2655,16 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
        mgmt_pending_remove(cmd);
 }
 
+void mgmt_smp_complete(struct hci_conn *conn, bool complete)
+{
+       u8 status = complete ? MGMT_STATUS_SUCCESS : MGMT_STATUS_FAILED;
+       struct pending_cmd *cmd;
+
+       cmd = find_pairing(conn);
+       if (cmd)
+               pairing_complete(cmd, status);
+}
+
 static void pairing_complete_cb(struct hci_conn *conn, u8 status)
 {
        struct pending_cmd *cmd;
@@ -2646,7 +2678,7 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status)
                pairing_complete(cmd, mgmt_status(status));
 }
 
-static void le_connect_complete_cb(struct hci_conn *conn, u8 status)
+static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
 {
        struct pending_cmd *cmd;
 
@@ -2733,13 +2765,16 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
        }
 
        /* For LE, just connecting isn't a proof that the pairing finished */
-       if (cp->addr.type == BDADDR_BREDR)
+       if (cp->addr.type == BDADDR_BREDR) {
                conn->connect_cfm_cb = pairing_complete_cb;
-       else
-               conn->connect_cfm_cb = le_connect_complete_cb;
+               conn->security_cfm_cb = pairing_complete_cb;
+               conn->disconn_cfm_cb = pairing_complete_cb;
+       } else {
+               conn->connect_cfm_cb = le_pairing_complete_cb;
+               conn->security_cfm_cb = le_pairing_complete_cb;
+               conn->disconn_cfm_cb = le_pairing_complete_cb;
+       }
 
-       conn->security_cfm_cb = pairing_complete_cb;
-       conn->disconn_cfm_cb = pairing_complete_cb;
        conn->io_capability = cp->io_cap;
        cmd->user_data = conn;
 
@@ -3071,7 +3106,12 @@ static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
                goto unlock;
        }
 
-       err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+       if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags))
+               err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_EXT_DATA,
+                                  0, NULL);
+       else
+               err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+
        if (err < 0)
                mgmt_pending_remove(cmd);
 
@@ -3083,23 +3123,46 @@ unlock:
 static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
                               void *data, u16 len)
 {
-       struct mgmt_cp_add_remote_oob_data *cp = data;
-       u8 status;
        int err;
 
        BT_DBG("%s ", hdev->name);
 
        hci_dev_lock(hdev);
 
-       err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
-                                     cp->randomizer);
-       if (err < 0)
-               status = MGMT_STATUS_FAILED;
-       else
-               status = MGMT_STATUS_SUCCESS;
+       if (len == MGMT_ADD_REMOTE_OOB_DATA_SIZE) {
+               struct mgmt_cp_add_remote_oob_data *cp = data;
+               u8 status;
 
-       err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status,
-                          &cp->addr, sizeof(cp->addr));
+               err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr,
+                                             cp->hash, cp->randomizer);
+               if (err < 0)
+                       status = MGMT_STATUS_FAILED;
+               else
+                       status = MGMT_STATUS_SUCCESS;
+
+               err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
+                                  status, &cp->addr, sizeof(cp->addr));
+       } else if (len == MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE) {
+               struct mgmt_cp_add_remote_oob_ext_data *cp = data;
+               u8 status;
+
+               err = hci_add_remote_oob_ext_data(hdev, &cp->addr.bdaddr,
+                                                 cp->hash192,
+                                                 cp->randomizer192,
+                                                 cp->hash256,
+                                                 cp->randomizer256);
+               if (err < 0)
+                       status = MGMT_STATUS_FAILED;
+               else
+                       status = MGMT_STATUS_SUCCESS;
+
+               err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
+                                  status, &cp->addr, sizeof(cp->addr));
+       } else {
+               BT_ERR("add_remote_oob_data: invalid length of %u bytes", len);
+               err = cmd_status(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
+                                MGMT_STATUS_INVALID_PARAMS);
+       }
 
        hci_dev_unlock(hdev);
        return err;
@@ -3999,15 +4062,219 @@ unlock:
        return err;
 }
 
+static int set_secure_conn(struct sock *sk, struct hci_dev *hdev,
+                          void *data, u16 len)
+{
+       struct mgmt_mode *cp = data;
+       struct pending_cmd *cmd;
+       u8 val, status;
+       int err;
+
+       BT_DBG("request for %s", hdev->name);
+
+       status = mgmt_bredr_support(hdev);
+       if (status)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
+                                 status);
+
+       if (!lmp_sc_capable(hdev) &&
+           !test_bit(HCI_FORCE_SC, &hdev->dev_flags))
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
+
+       if (!hdev_is_powered(hdev)) {
+               bool changed;
+
+               if (cp->val) {
+                       changed = !test_and_set_bit(HCI_SC_ENABLED,
+                                                   &hdev->dev_flags);
+                       if (cp->val == 0x02)
+                               set_bit(HCI_SC_ONLY, &hdev->dev_flags);
+                       else
+                               clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+               } else {
+                       changed = test_and_clear_bit(HCI_SC_ENABLED,
+                                                    &hdev->dev_flags);
+                       clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+               }
+
+               err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev);
+               if (err < 0)
+                       goto failed;
+
+               if (changed)
+                       err = new_settings(hdev, sk);
+
+               goto failed;
+       }
+
+       if (mgmt_pending_find(MGMT_OP_SET_SECURE_CONN, hdev)) {
+               err = cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
+                                MGMT_STATUS_BUSY);
+               goto failed;
+       }
+
+       val = !!cp->val;
+
+       if (val == test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
+           (cp->val == 0x02) == test_bit(HCI_SC_ONLY, &hdev->dev_flags)) {
+               err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev);
+               goto failed;
+       }
+
+       cmd = mgmt_pending_add(sk, MGMT_OP_SET_SECURE_CONN, hdev, data, len);
+       if (!cmd) {
+               err = -ENOMEM;
+               goto failed;
+       }
+
+       err = hci_send_cmd(hdev, HCI_OP_WRITE_SC_SUPPORT, 1, &val);
+       if (err < 0) {
+               mgmt_pending_remove(cmd);
+               goto failed;
+       }
+
+       if (cp->val == 0x02)
+               set_bit(HCI_SC_ONLY, &hdev->dev_flags);
+       else
+               clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+
+failed:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+static int set_debug_keys(struct sock *sk, struct hci_dev *hdev,
+                         void *data, u16 len)
+{
+       struct mgmt_mode *cp = data;
+       bool changed;
+       int err;
+
+       BT_DBG("request for %s", hdev->name);
+
+       if (cp->val != 0x00 && cp->val != 0x01)
+               return cmd_status(sk, hdev->id, MGMT_OP_SET_DEBUG_KEYS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+
+       hci_dev_lock(hdev);
+
+       if (cp->val)
+               changed = !test_and_set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+       else
+               changed = test_and_clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
+
+       err = send_settings_rsp(sk, MGMT_OP_SET_DEBUG_KEYS, hdev);
+       if (err < 0)
+               goto unlock;
+
+       if (changed)
+               err = new_settings(hdev, sk);
+
+unlock:
+       hci_dev_unlock(hdev);
+       return err;
+}
+
+static bool irk_is_valid(struct mgmt_irk_info *irk)
+{
+       switch (irk->addr.type) {
+       case BDADDR_LE_PUBLIC:
+               return true;
+
+       case BDADDR_LE_RANDOM:
+               /* Two most significant bits shall be set */
+               if ((irk->addr.bdaddr.b[5] & 0xc0) != 0xc0)
+                       return false;
+               return true;
+       }
+
+       return false;
+}
+
+static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data,
+                    u16 len)
+{
+       struct mgmt_cp_load_irks *cp = cp_data;
+       u16 irk_count, expected_len;
+       int i, err;
+
+       BT_DBG("request for %s", hdev->name);
+
+       if (!lmp_le_capable(hdev))
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
+                                 MGMT_STATUS_NOT_SUPPORTED);
+
+       irk_count = __le16_to_cpu(cp->irk_count);
+
+       expected_len = sizeof(*cp) + irk_count * sizeof(struct mgmt_irk_info);
+       if (expected_len != len) {
+               BT_ERR("load_irks: expected %u bytes, got %u bytes",
+                      len, expected_len);
+               return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS,
+                                 MGMT_STATUS_INVALID_PARAMS);
+       }
+
+       BT_DBG("%s irk_count %u", hdev->name, irk_count);
+
+       for (i = 0; i < irk_count; i++) {
+               struct mgmt_irk_info *key = &cp->irks[i];
+
+               if (!irk_is_valid(key))
+                       return cmd_status(sk, hdev->id,
+                                         MGMT_OP_LOAD_IRKS,
+                                         MGMT_STATUS_INVALID_PARAMS);
+       }
+
+       hci_dev_lock(hdev);
+
+       hci_smp_irks_clear(hdev);
+
+       for (i = 0; i < irk_count; i++) {
+               struct mgmt_irk_info *irk = &cp->irks[i];
+               u8 addr_type;
+
+               if (irk->addr.type == BDADDR_LE_PUBLIC)
+                       addr_type = ADDR_LE_DEV_PUBLIC;
+               else
+                       addr_type = ADDR_LE_DEV_RANDOM;
+
+               hci_add_irk(hdev, &irk->addr.bdaddr, addr_type, irk->val,
+                           BDADDR_ANY);
+       }
+
+       set_bit(HCI_RPA_RESOLVING, &hdev->dev_flags);
+
+       err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_IRKS, 0, NULL, 0);
+
+       hci_dev_unlock(hdev);
+
+       return err;
+}
+
 static bool ltk_is_valid(struct mgmt_ltk_info *key)
 {
-       if (key->authenticated != 0x00 && key->authenticated != 0x01)
-               return false;
        if (key->master != 0x00 && key->master != 0x01)
                return false;
-       if (!bdaddr_type_is_le(key->addr.type))
-               return false;
-       return true;
+
+       switch (key->addr.type) {
+       case BDADDR_LE_PUBLIC:
+               return true;
+
+       case BDADDR_LE_RANDOM:
+               /* Two most significant bits shall be set */
+               if ((key->addr.bdaddr.b[5] & 0xc0) != 0xc0)
+                       return false;
+               return true;
+       }
+
+       return false;
 }
 
 static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
@@ -4063,9 +4330,9 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
                else
                        type = HCI_SMP_LTK_SLAVE;
 
-               hci_add_ltk(hdev, &key->addr.bdaddr, addr_type,
-                           type, 0, key->authenticated, key->val,
-                           key->enc_size, key->ediv, key->rand);
+               hci_add_ltk(hdev, &key->addr.bdaddr, addr_type, type,
+                           key->type, key->val, key->enc_size, key->ediv,
+                           key->rand);
        }
 
        err = cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0,
@@ -4115,7 +4382,7 @@ static const struct mgmt_handler {
        { user_passkey_reply,     false, MGMT_USER_PASSKEY_REPLY_SIZE },
        { user_passkey_neg_reply, false, MGMT_USER_PASSKEY_NEG_REPLY_SIZE },
        { read_local_oob_data,    false, MGMT_READ_LOCAL_OOB_DATA_SIZE },
-       { add_remote_oob_data,    false, MGMT_ADD_REMOTE_OOB_DATA_SIZE },
+       { add_remote_oob_data,    true,  MGMT_ADD_REMOTE_OOB_DATA_SIZE },
        { remove_remote_oob_data, false, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE },
        { start_discovery,        false, MGMT_START_DISCOVERY_SIZE },
        { stop_discovery,         false, MGMT_STOP_DISCOVERY_SIZE },
@@ -4127,6 +4394,10 @@ static const struct mgmt_handler {
        { set_bredr,              false, MGMT_SETTING_SIZE },
        { set_static_address,     false, MGMT_SET_STATIC_ADDRESS_SIZE },
        { set_scan_params,        false, MGMT_SET_SCAN_PARAMS_SIZE },
+       { set_secure_conn,        false, MGMT_SETTING_SIZE },
+       { set_debug_keys,         false, MGMT_SETTING_SIZE },
+       { },
+       { load_irks,              true,  MGMT_LOAD_IRKS_SIZE },
 };
 
 
@@ -4494,16 +4765,32 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
        mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
 }
 
-void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
+void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key)
 {
        struct mgmt_ev_new_long_term_key ev;
 
        memset(&ev, 0, sizeof(ev));
 
-       ev.store_hint = persistent;
+       /* Devices using resolvable or non-resolvable random addresses
+        * without providing an indentity resolving key don't require
+        * to store long term keys. Their addresses will change the
+        * next time around.
+        *
+        * Only when a remote device provides an identity address
+        * make sure the long term key is stored. If the remote
+        * identity is known, the long term keys are internally
+        * mapped to the identity address. So allow static random
+        * and public addresses here.
+        */
+       if (key->bdaddr_type == ADDR_LE_DEV_RANDOM &&
+           (key->bdaddr.b[5] & 0xc0) != 0xc0)
+               ev.store_hint = 0x00;
+       else
+               ev.store_hint = 0x01;
+
        bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
        ev.key.addr.type = link_to_bdaddr(LE_LINK, key->bdaddr_type);
-       ev.key.authenticated = key->authenticated;
+       ev.key.type = key->authenticated;
        ev.key.enc_size = key->enc_size;
        ev.key.ediv = key->ediv;
 
@@ -4516,6 +4803,36 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
        mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL);
 }
 
+void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk)
+{
+       struct mgmt_ev_new_irk ev;
+
+       memset(&ev, 0, sizeof(ev));
+
+       /* For identity resolving keys from devices that are already
+        * using a public address or static random address, do not
+        * ask for storing this key. The identity resolving key really
+        * is only mandatory for devices using resovlable random
+        * addresses.
+        *
+        * Storing all identity resolving keys has the downside that
+        * they will be also loaded on next boot of they system. More
+        * identity resolving keys, means more time during scanning is
+        * needed to actually resolve these addresses.
+        */
+       if (bacmp(&irk->rpa, BDADDR_ANY))
+               ev.store_hint = 0x01;
+       else
+               ev.store_hint = 0x00;
+
+       bacpy(&ev.rpa, &irk->rpa);
+       bacpy(&ev.irk.addr.bdaddr, &irk->bdaddr);
+       ev.irk.addr.type = link_to_bdaddr(LE_LINK, irk->addr_type);
+       memcpy(ev.irk.val, irk->val, sizeof(irk->val));
+
+       mgmt_event(MGMT_EV_NEW_IRK, hdev, &ev, sizeof(ev), NULL);
+}
+
 static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
                                  u8 data_len)
 {
@@ -4910,6 +5227,43 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
        hci_req_run(&req, NULL);
 }
 
+void mgmt_sc_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
+{
+       struct cmd_lookup match = { NULL, hdev };
+       bool changed = false;
+
+       if (status) {
+               u8 mgmt_err = mgmt_status(status);
+
+               if (enable) {
+                       if (test_and_clear_bit(HCI_SC_ENABLED,
+                                              &hdev->dev_flags))
+                               new_settings(hdev, NULL);
+                       clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+               }
+
+               mgmt_pending_foreach(MGMT_OP_SET_SECURE_CONN, hdev,
+                                    cmd_status_rsp, &mgmt_err);
+               return;
+       }
+
+       if (enable) {
+               changed = !test_and_set_bit(HCI_SC_ENABLED, &hdev->dev_flags);
+       } else {
+               changed = test_and_clear_bit(HCI_SC_ENABLED, &hdev->dev_flags);
+               clear_bit(HCI_SC_ONLY, &hdev->dev_flags);
+       }
+
+       mgmt_pending_foreach(MGMT_OP_SET_SECURE_CONN, hdev,
+                            settings_rsp, &match);
+
+       if (changed)
+               new_settings(hdev, match.sk);
+
+       if (match.sk)
+               sock_put(match.sk);
+}
+
 static void sk_lookup(struct pending_cmd *cmd, void *data)
 {
        struct cmd_lookup *match = data;
@@ -4964,8 +5318,9 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
                   cmd ? cmd->sk : NULL);
 }
 
-void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
-                                            u8 *randomizer, u8 status)
+void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
+                                      u8 *randomizer192, u8 *hash256,
+                                      u8 *randomizer256, u8 status)
 {
        struct pending_cmd *cmd;
 
@@ -4979,13 +5334,32 @@ void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
                cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
                           mgmt_status(status));
        } else {
-               struct mgmt_rp_read_local_oob_data rp;
+               if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
+                   hash256 && randomizer256) {
+                       struct mgmt_rp_read_local_oob_ext_data rp;
+
+                       memcpy(rp.hash192, hash192, sizeof(rp.hash192));
+                       memcpy(rp.randomizer192, randomizer192,
+                              sizeof(rp.randomizer192));
 
-               memcpy(rp.hash, hash, sizeof(rp.hash));
-               memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
+                       memcpy(rp.hash256, hash256, sizeof(rp.hash256));
+                       memcpy(rp.randomizer256, randomizer256,
+                              sizeof(rp.randomizer256));
+
+                       cmd_complete(cmd->sk, hdev->id,
+                                    MGMT_OP_READ_LOCAL_OOB_DATA, 0,
+                                    &rp, sizeof(rp));
+               } else {
+                       struct mgmt_rp_read_local_oob_data rp;
 
-               cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
-                            0, &rp, sizeof(rp));
+                       memcpy(rp.hash, hash192, sizeof(rp.hash));
+                       memcpy(rp.randomizer, randomizer192,
+                              sizeof(rp.randomizer));
+
+                       cmd_complete(cmd->sk, hdev->id,
+                                    MGMT_OP_READ_LOCAL_OOB_DATA, 0,
+                                    &rp, sizeof(rp));
+               }
        }
 
        mgmt_pending_remove(cmd);
@@ -4997,6 +5371,7 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 {
        char buf[512];
        struct mgmt_ev_device_found *ev = (void *) buf;
+       struct smp_irk *irk;
        size_t ev_size;
 
        if (!hci_discovery_active(hdev))
@@ -5008,8 +5383,15 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
 
        memset(buf, 0, sizeof(buf));
 
-       bacpy(&ev->addr.bdaddr, bdaddr);
-       ev->addr.type = link_to_bdaddr(link_type, addr_type);
+       irk = hci_get_irk(hdev, bdaddr, addr_type);
+       if (irk) {
+               bacpy(&ev->addr.bdaddr, &irk->bdaddr);
+               ev->addr.type = link_to_bdaddr(link_type, irk->addr_type);
+       } else {
+               bacpy(&ev->addr.bdaddr, bdaddr);
+               ev->addr.type = link_to_bdaddr(link_type, addr_type);
+       }
+
        ev->rssi = rssi;
        if (cfm_name)
                ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME);
index facd8a79c0383eb8898fca8dc905c87ed623202a..21e15318937c31e8d7b6c8f286036dfa2ec22300 100644 (file)
@@ -216,6 +216,7 @@ static int rfcomm_check_security(struct rfcomm_dlc *d)
 
        switch (d->sec_level) {
        case BT_SECURITY_HIGH:
+       case BT_SECURITY_FIPS:
                auth_type = HCI_AT_GENERAL_BONDING_MITM;
                break;
        case BT_SECURITY_MEDIUM:
@@ -359,6 +360,11 @@ static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci)
        return NULL;
 }
 
+static int rfcomm_check_channel(u8 channel)
+{
+       return channel < 1 || channel > 30;
+}
+
 static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
 {
        struct rfcomm_session *s;
@@ -368,7 +374,7 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
        BT_DBG("dlc %p state %ld %pMR -> %pMR channel %d",
               d, d->state, src, dst, channel);
 
-       if (channel < 1 || channel > 30)
+       if (rfcomm_check_channel(channel))
                return -EINVAL;
 
        if (d->state != BT_OPEN && d->state != BT_CLOSED)
@@ -425,6 +431,20 @@ int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 chann
        return r;
 }
 
+static void __rfcomm_dlc_disconn(struct rfcomm_dlc *d)
+{
+       struct rfcomm_session *s = d->session;
+
+       d->state = BT_DISCONN;
+       if (skb_queue_empty(&d->tx_queue)) {
+               rfcomm_send_disc(s, d->dlci);
+               rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT);
+       } else {
+               rfcomm_queue_disc(d);
+               rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2);
+       }
+}
+
 static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
 {
        struct rfcomm_session *s = d->session;
@@ -437,32 +457,29 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
        switch (d->state) {
        case BT_CONNECT:
        case BT_CONFIG:
+       case BT_OPEN:
+       case BT_CONNECT2:
                if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
                        set_bit(RFCOMM_AUTH_REJECT, &d->flags);
                        rfcomm_schedule();
-                       break;
+                       return 0;
                }
-               /* Fall through */
+       }
 
+       switch (d->state) {
+       case BT_CONNECT:
        case BT_CONNECTED:
-               d->state = BT_DISCONN;
-               if (skb_queue_empty(&d->tx_queue)) {
-                       rfcomm_send_disc(s, d->dlci);
-                       rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT);
-               } else {
-                       rfcomm_queue_disc(d);
-                       rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2);
-               }
+               __rfcomm_dlc_disconn(d);
                break;
 
-       case BT_OPEN:
-       case BT_CONNECT2:
-               if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
-                       set_bit(RFCOMM_AUTH_REJECT, &d->flags);
-                       rfcomm_schedule();
+       case BT_CONFIG:
+               if (s->state != BT_BOUND) {
+                       __rfcomm_dlc_disconn(d);
                        break;
                }
-               /* Fall through */
+               /* if closing a dlc in a session that hasn't been started,
+                * just close and unlink the dlc
+                */
 
        default:
                rfcomm_dlc_clear_timer(d);
@@ -513,6 +530,25 @@ no_session:
        return r;
 }
 
+struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel)
+{
+       struct rfcomm_session *s;
+       struct rfcomm_dlc *dlc = NULL;
+       u8 dlci;
+
+       if (rfcomm_check_channel(channel))
+               return ERR_PTR(-EINVAL);
+
+       rfcomm_lock();
+       s = rfcomm_session_get(src, dst);
+       if (s) {
+               dlci = __dlci(!s->initiator, channel);
+               dlc = rfcomm_dlc_get(s, dlci);
+       }
+       rfcomm_unlock();
+       return dlc;
+}
+
 int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
 {
        int len = skb->len;
@@ -533,6 +569,20 @@ int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
        return len;
 }
 
+void rfcomm_dlc_send_noerror(struct rfcomm_dlc *d, struct sk_buff *skb)
+{
+       int len = skb->len;
+
+       BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len);
+
+       rfcomm_make_uih(skb, d->addr);
+       skb_queue_tail(&d->tx_queue, skb);
+
+       if (d->state == BT_CONNECTED &&
+           !test_bit(RFCOMM_TX_THROTTLED, &d->flags))
+               rfcomm_schedule();
+}
+
 void __rfcomm_dlc_throttle(struct rfcomm_dlc *d)
 {
        BT_DBG("dlc %p state %ld", d, d->state);
@@ -1943,12 +1993,11 @@ static void rfcomm_process_sessions(void)
                        continue;
                }
 
-               if (s->state == BT_LISTEN) {
+               switch (s->state) {
+               case BT_LISTEN:
                        rfcomm_accept_connection(s);
                        continue;
-               }
 
-               switch (s->state) {
                case BT_BOUND:
                        s = rfcomm_check_connection(s);
                        break;
@@ -2085,7 +2134,8 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
                                set_bit(RFCOMM_SEC_PENDING, &d->flags);
                                rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
                                continue;
-                       } else if (d->sec_level == BT_SECURITY_HIGH) {
+                       } else if (d->sec_level == BT_SECURITY_HIGH ||
+                                  d->sec_level == BT_SECURITY_FIPS) {
                                set_bit(RFCOMM_ENC_DROP, &d->flags);
                                continue;
                        }
index 3c2d3e4aa2f58a271bea6632451edd21b92668a9..c024e715512fdff242616c4ac403fa8315b13289 100644 (file)
@@ -105,13 +105,18 @@ static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err)
 }
 
 /* ---- Socket functions ---- */
-static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src)
+static struct sock *__rfcomm_get_listen_sock_by_addr(u8 channel, bdaddr_t *src)
 {
        struct sock *sk = NULL;
 
        sk_for_each(sk, &rfcomm_sk_list.head) {
-               if (rfcomm_pi(sk)->channel == channel &&
-                               !bacmp(&rfcomm_pi(sk)->src, src))
+               if (rfcomm_pi(sk)->channel != channel)
+                       continue;
+
+               if (bacmp(&rfcomm_pi(sk)->src, src))
+                       continue;
+
+               if (sk->sk_state == BT_BOUND || sk->sk_state == BT_LISTEN)
                        break;
        }
 
@@ -331,6 +336,7 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr
 {
        struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
        struct sock *sk = sock->sk;
+       int chan = sa->rc_channel;
        int err = 0;
 
        BT_DBG("sk %p %pMR", sk, &sa->rc_bdaddr);
@@ -352,12 +358,12 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr
 
        write_lock(&rfcomm_sk_list.lock);
 
-       if (sa->rc_channel && __rfcomm_get_sock_by_addr(sa->rc_channel, &sa->rc_bdaddr)) {
+       if (chan && __rfcomm_get_listen_sock_by_addr(chan, &sa->rc_bdaddr)) {
                err = -EADDRINUSE;
        } else {
                /* Save source address */
                bacpy(&rfcomm_pi(sk)->src, &sa->rc_bdaddr);
-               rfcomm_pi(sk)->channel = sa->rc_channel;
+               rfcomm_pi(sk)->channel = chan;
                sk->sk_state = BT_BOUND;
        }
 
@@ -439,7 +445,7 @@ static int rfcomm_sock_listen(struct socket *sock, int backlog)
                write_lock(&rfcomm_sk_list.lock);
 
                for (channel = 1; channel < 31; channel++)
-                       if (!__rfcomm_get_sock_by_addr(channel, src)) {
+                       if (!__rfcomm_get_listen_sock_by_addr(channel, src)) {
                                rfcomm_pi(sk)->channel = channel;
                                err = 0;
                                break;
@@ -528,6 +534,9 @@ static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *
 
        BT_DBG("sock %p, sk %p", sock, sk);
 
+       if (peer && sk->sk_state != BT_CONNECTED)
+               return -ENOTCONN;
+
        memset(sa, 0, sizeof(*sa));
        sa->rc_family  = AF_BLUETOOTH;
        sa->rc_channel = rfcomm_pi(sk)->channel;
@@ -648,6 +657,11 @@ static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __u
                        break;
                }
 
+               if (opt & RFCOMM_LM_FIPS) {
+                       err = -EINVAL;
+                       break;
+               }
+
                if (opt & RFCOMM_LM_AUTH)
                        rfcomm_pi(sk)->sec_level = BT_SECURITY_LOW;
                if (opt & RFCOMM_LM_ENCRYPT)
@@ -762,7 +776,11 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
                        break;
                case BT_SECURITY_HIGH:
                        opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT |
-                                                       RFCOMM_LM_SECURE;
+                             RFCOMM_LM_SECURE;
+                       break;
+               case BT_SECURITY_FIPS:
+                       opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT |
+                             RFCOMM_LM_SECURE | RFCOMM_LM_FIPS;
                        break;
                default:
                        opt = 0;
@@ -774,6 +792,7 @@ static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __u
 
                if (put_user(opt, (u32 __user *) optval))
                        err = -EFAULT;
+
                break;
 
        case RFCOMM_CONNINFO:
index f9c0980abeeac9eaf1d94d70f3e001fd6e1f0870..403ec09f480a2a72983b0b1b9db0222fd6e86742 100644 (file)
@@ -40,6 +40,7 @@
 #define RFCOMM_TTY_MAJOR 216           /* device node major id of the usb/bluetooth.c driver */
 #define RFCOMM_TTY_MINOR 0
 
+static DEFINE_MUTEX(rfcomm_ioctl_mutex);
 static struct tty_driver *rfcomm_tty_driver;
 
 struct rfcomm_dev {
@@ -51,6 +52,8 @@ struct rfcomm_dev {
        unsigned long           flags;
        int                     err;
 
+       unsigned long           status;         /* don't export to userspace */
+
        bdaddr_t                src;
        bdaddr_t                dst;
        u8                      channel;
@@ -58,7 +61,6 @@ struct rfcomm_dev {
        uint                    modem_status;
 
        struct rfcomm_dlc       *dlc;
-       wait_queue_head_t       conn_wait;
 
        struct device           *tty_dev;
 
@@ -83,10 +85,6 @@ static void rfcomm_dev_destruct(struct tty_port *port)
 
        BT_DBG("dev %p dlc %p", dev, dlc);
 
-       spin_lock(&rfcomm_dev_lock);
-       list_del(&dev->list);
-       spin_unlock(&rfcomm_dev_lock);
-
        rfcomm_dlc_lock(dlc);
        /* Detach DLC if it's owned by this dev */
        if (dlc->owner == dev)
@@ -95,7 +93,12 @@ static void rfcomm_dev_destruct(struct tty_port *port)
 
        rfcomm_dlc_put(dlc);
 
-       tty_unregister_device(rfcomm_tty_driver, dev->id);
+       if (dev->tty_dev)
+               tty_unregister_device(rfcomm_tty_driver, dev->id);
+
+       spin_lock(&rfcomm_dev_lock);
+       list_del(&dev->list);
+       spin_unlock(&rfcomm_dev_lock);
 
        kfree(dev);
 
@@ -104,60 +107,24 @@ static void rfcomm_dev_destruct(struct tty_port *port)
        module_put(THIS_MODULE);
 }
 
-static struct device *rfcomm_get_device(struct rfcomm_dev *dev)
-{
-       struct hci_dev *hdev;
-       struct hci_conn *conn;
-
-       hdev = hci_get_route(&dev->dst, &dev->src);
-       if (!hdev)
-               return NULL;
-
-       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &dev->dst);
-
-       hci_dev_put(hdev);
-
-       return conn ? &conn->dev : NULL;
-}
-
 /* device-specific initialization: open the dlc */
 static int rfcomm_dev_activate(struct tty_port *port, struct tty_struct *tty)
 {
        struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
-       DEFINE_WAIT(wait);
        int err;
 
        err = rfcomm_dlc_open(dev->dlc, &dev->src, &dev->dst, dev->channel);
        if (err)
-               return err;
-
-       while (1) {
-               prepare_to_wait(&dev->conn_wait, &wait, TASK_INTERRUPTIBLE);
-
-               if (dev->dlc->state == BT_CLOSED) {
-                       err = -dev->err;
-                       break;
-               }
-
-               if (dev->dlc->state == BT_CONNECTED)
-                       break;
-
-               if (signal_pending(current)) {
-                       err = -ERESTARTSYS;
-                       break;
-               }
-
-               tty_unlock(tty);
-               schedule();
-               tty_lock(tty);
-       }
-       finish_wait(&dev->conn_wait, &wait);
+               set_bit(TTY_IO_ERROR, &tty->flags);
+       return err;
+}
 
-       if (!err)
-               device_move(dev->tty_dev, rfcomm_get_device(dev),
-                           DPM_ORDER_DEV_AFTER_PARENT);
+/* we block the open until the dlc->state becomes BT_CONNECTED */
+static int rfcomm_dev_carrier_raised(struct tty_port *port)
+{
+       struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
 
-       return err;
+       return (dev->dlc->state == BT_CONNECTED);
 }
 
 /* device-specific cleanup: close the dlc */
@@ -176,9 +143,10 @@ static const struct tty_port_operations rfcomm_port_ops = {
        .destruct = rfcomm_dev_destruct,
        .activate = rfcomm_dev_activate,
        .shutdown = rfcomm_dev_shutdown,
+       .carrier_raised = rfcomm_dev_carrier_raised,
 };
 
-static struct rfcomm_dev *__rfcomm_dev_get(int id)
+static struct rfcomm_dev *__rfcomm_dev_lookup(int id)
 {
        struct rfcomm_dev *dev;
 
@@ -195,20 +163,41 @@ static struct rfcomm_dev *rfcomm_dev_get(int id)
 
        spin_lock(&rfcomm_dev_lock);
 
-       dev = __rfcomm_dev_get(id);
+       dev = __rfcomm_dev_lookup(id);
 
-       if (dev) {
-               if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
-                       dev = NULL;
-               else
-                       tty_port_get(&dev->port);
-       }
+       if (dev && !tty_port_get(&dev->port))
+               dev = NULL;
 
        spin_unlock(&rfcomm_dev_lock);
 
        return dev;
 }
 
+static void rfcomm_reparent_device(struct rfcomm_dev *dev)
+{
+       struct hci_dev *hdev;
+       struct hci_conn *conn;
+
+       hdev = hci_get_route(&dev->dst, &dev->src);
+       if (!hdev)
+               return;
+
+       /* The lookup results are unsafe to access without the
+        * hci device lock (FIXME: why is this not documented?)
+        */
+       hci_dev_lock(hdev);
+       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &dev->dst);
+
+       /* Just because the acl link is in the hash table is no
+        * guarantee the sysfs device has been added ...
+        */
+       if (conn && device_is_registered(&conn->dev))
+               device_move(dev->tty_dev, &conn->dev, DPM_ORDER_DEV_AFTER_PARENT);
+
+       hci_dev_unlock(hdev);
+       hci_dev_put(hdev);
+}
+
 static ssize_t show_address(struct device *tty_dev, struct device_attribute *attr, char *buf)
 {
        struct rfcomm_dev *dev = dev_get_drvdata(tty_dev);
@@ -224,17 +213,16 @@ static ssize_t show_channel(struct device *tty_dev, struct device_attribute *att
 static DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
 static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL);
 
-static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
+static struct rfcomm_dev *__rfcomm_dev_add(struct rfcomm_dev_req *req,
+                                          struct rfcomm_dlc *dlc)
 {
        struct rfcomm_dev *dev, *entry;
        struct list_head *head = &rfcomm_dev_list;
        int err = 0;
 
-       BT_DBG("id %d channel %d", req->dev_id, req->channel);
-
        dev = kzalloc(sizeof(struct rfcomm_dev), GFP_KERNEL);
        if (!dev)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        spin_lock(&rfcomm_dev_lock);
 
@@ -282,7 +270,6 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
 
        tty_port_init(&dev->port);
        dev->port.ops = &rfcomm_port_ops;
-       init_waitqueue_head(&dev->conn_wait);
 
        skb_queue_head_init(&dev->pending);
 
@@ -318,22 +305,37 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
           holds reference to this module. */
        __module_get(THIS_MODULE);
 
+       spin_unlock(&rfcomm_dev_lock);
+       return dev;
+
 out:
        spin_unlock(&rfcomm_dev_lock);
+       kfree(dev);
+       return ERR_PTR(err);
+}
+
+static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
+{
+       struct rfcomm_dev *dev;
+       struct device *tty;
+
+       BT_DBG("id %d channel %d", req->dev_id, req->channel);
 
-       if (err < 0)
-               goto free;
+       dev = __rfcomm_dev_add(req, dlc);
+       if (IS_ERR(dev)) {
+               rfcomm_dlc_put(dlc);
+               return PTR_ERR(dev);
+       }
 
-       dev->tty_dev = tty_port_register_device(&dev->port, rfcomm_tty_driver,
+       tty = tty_port_register_device(&dev->port, rfcomm_tty_driver,
                        dev->id, NULL);
-       if (IS_ERR(dev->tty_dev)) {
-               err = PTR_ERR(dev->tty_dev);
-               spin_lock(&rfcomm_dev_lock);
-               list_del(&dev->list);
-               spin_unlock(&rfcomm_dev_lock);
-               goto free;
+       if (IS_ERR(tty)) {
+               tty_port_put(&dev->port);
+               return PTR_ERR(tty);
        }
 
+       dev->tty_dev = tty;
+       rfcomm_reparent_device(dev);
        dev_set_drvdata(dev->tty_dev, dev);
 
        if (device_create_file(dev->tty_dev, &dev_attr_address) < 0)
@@ -343,24 +345,23 @@ out:
                BT_ERR("Failed to create channel attribute");
 
        return dev->id;
-
-free:
-       kfree(dev);
-       return err;
 }
 
 /* ---- Send buffer ---- */
-static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc)
+static inline unsigned int rfcomm_room(struct rfcomm_dev *dev)
 {
-       /* We can't let it be zero, because we don't get a callback
-          when tx_credits becomes nonzero, hence we'd never wake up */
-       return dlc->mtu * (dlc->tx_credits?:1);
+       struct rfcomm_dlc *dlc = dev->dlc;
+
+       /* Limit the outstanding number of packets not yet sent to 40 */
+       int pending = 40 - atomic_read(&dev->wmem_alloc);
+
+       return max(0, pending) * dlc->mtu;
 }
 
 static void rfcomm_wfree(struct sk_buff *skb)
 {
        struct rfcomm_dev *dev = (void *) skb->sk;
-       atomic_sub(skb->truesize, &dev->wmem_alloc);
+       atomic_dec(&dev->wmem_alloc);
        if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
                tty_port_tty_wakeup(&dev->port);
        tty_port_put(&dev->port);
@@ -369,28 +370,24 @@ static void rfcomm_wfree(struct sk_buff *skb)
 static void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev)
 {
        tty_port_get(&dev->port);
-       atomic_add(skb->truesize, &dev->wmem_alloc);
+       atomic_inc(&dev->wmem_alloc);
        skb->sk = (void *) dev;
        skb->destructor = rfcomm_wfree;
 }
 
 static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, gfp_t priority)
 {
-       if (atomic_read(&dev->wmem_alloc) < rfcomm_room(dev->dlc)) {
-               struct sk_buff *skb = alloc_skb(size, priority);
-               if (skb) {
-                       rfcomm_set_owner_w(skb, dev);
-                       return skb;
-               }
-       }
-       return NULL;
+       struct sk_buff *skb = alloc_skb(size, priority);
+       if (skb)
+               rfcomm_set_owner_w(skb, dev);
+       return skb;
 }
 
 /* ---- Device IOCTLs ---- */
 
 #define NOCAP_FLAGS ((1 << RFCOMM_REUSE_DLC) | (1 << RFCOMM_RELEASE_ONHUP))
 
-static int rfcomm_create_dev(struct sock *sk, void __user *arg)
+static int __rfcomm_create_dev(struct sock *sk, void __user *arg)
 {
        struct rfcomm_dev_req req;
        struct rfcomm_dlc *dlc;
@@ -412,16 +409,22 @@ static int rfcomm_create_dev(struct sock *sk, void __user *arg)
                dlc = rfcomm_pi(sk)->dlc;
                rfcomm_dlc_hold(dlc);
        } else {
+               /* Validate the channel is unused */
+               dlc = rfcomm_dlc_exists(&req.src, &req.dst, req.channel);
+               if (IS_ERR(dlc))
+                       return PTR_ERR(dlc);
+               else if (dlc) {
+                       rfcomm_dlc_put(dlc);
+                       return -EBUSY;
+               }
                dlc = rfcomm_dlc_alloc(GFP_KERNEL);
                if (!dlc)
                        return -ENOMEM;
        }
 
        id = rfcomm_dev_add(&req, dlc);
-       if (id < 0) {
-               rfcomm_dlc_put(dlc);
+       if (id < 0)
                return id;
-       }
 
        if (req.flags & (1 << RFCOMM_REUSE_DLC)) {
                /* DLC is now used by device.
@@ -432,7 +435,7 @@ static int rfcomm_create_dev(struct sock *sk, void __user *arg)
        return id;
 }
 
-static int rfcomm_release_dev(void __user *arg)
+static int __rfcomm_release_dev(void __user *arg)
 {
        struct rfcomm_dev_req req;
        struct rfcomm_dev *dev;
@@ -452,6 +455,12 @@ static int rfcomm_release_dev(void __user *arg)
                return -EPERM;
        }
 
+       /* only release once */
+       if (test_and_set_bit(RFCOMM_DEV_RELEASED, &dev->status)) {
+               tty_port_put(&dev->port);
+               return -EALREADY;
+       }
+
        if (req.flags & (1 << RFCOMM_HANGUP_NOW))
                rfcomm_dlc_close(dev->dlc, 0);
 
@@ -462,14 +471,35 @@ static int rfcomm_release_dev(void __user *arg)
                tty_kref_put(tty);
        }
 
-       if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) &&
-           !test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags))
+       if (!test_bit(RFCOMM_TTY_OWNED, &dev->status))
                tty_port_put(&dev->port);
 
        tty_port_put(&dev->port);
        return 0;
 }
 
+static int rfcomm_create_dev(struct sock *sk, void __user *arg)
+{
+       int ret;
+
+       mutex_lock(&rfcomm_ioctl_mutex);
+       ret = __rfcomm_create_dev(sk, arg);
+       mutex_unlock(&rfcomm_ioctl_mutex);
+
+       return ret;
+}
+
+static int rfcomm_release_dev(void __user *arg)
+{
+       int ret;
+
+       mutex_lock(&rfcomm_ioctl_mutex);
+       ret = __rfcomm_release_dev(arg);
+       mutex_unlock(&rfcomm_ioctl_mutex);
+
+       return ret;
+}
+
 static int rfcomm_get_dev_list(void __user *arg)
 {
        struct rfcomm_dev *dev;
@@ -497,7 +527,7 @@ static int rfcomm_get_dev_list(void __user *arg)
        spin_lock(&rfcomm_dev_lock);
 
        list_for_each_entry(dev, &rfcomm_dev_list, list) {
-               if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags))
+               if (!tty_port_get(&dev->port))
                        continue;
                (di + n)->id      = dev->id;
                (di + n)->flags   = dev->flags;
@@ -505,6 +535,7 @@ static int rfcomm_get_dev_list(void __user *arg)
                (di + n)->channel = dev->channel;
                bacpy(&(di + n)->src, &dev->src);
                bacpy(&(di + n)->dst, &dev->dst);
+               tty_port_put(&dev->port);
                if (++n >= dev_num)
                        break;
        }
@@ -601,9 +632,11 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
        BT_DBG("dlc %p dev %p err %d", dlc, dev, err);
 
        dev->err = err;
-       wake_up_interruptible(&dev->conn_wait);
+       if (dlc->state == BT_CONNECTED) {
+               rfcomm_reparent_device(dev);
 
-       if (dlc->state == BT_CLOSED)
+               wake_up_interruptible(&dev->port.open_wait);
+       } else if (dlc->state == BT_CLOSED)
                tty_port_tty_hangup(&dev->port, false);
 }
 
@@ -703,8 +736,10 @@ static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
         * when the last process closes the tty. The behaviour is expected by
         * userspace.
         */
-       if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
+       if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
+               set_bit(RFCOMM_TTY_OWNED, &dev->status);
                tty_port_put(&dev->port);
+       }
 
        return 0;
 }
@@ -750,7 +785,7 @@ static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, in
        struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
        struct rfcomm_dlc *dlc = dev->dlc;
        struct sk_buff *skb;
-       int err = 0, sent = 0, size;
+       int sent = 0, size;
 
        BT_DBG("tty %p count %d", tty, count);
 
@@ -758,7 +793,6 @@ static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, in
                size = min_t(uint, count, dlc->mtu);
 
                skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC);
-
                if (!skb)
                        break;
 
@@ -766,32 +800,24 @@ static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, in
 
                memcpy(skb_put(skb, size), buf + sent, size);
 
-               err = rfcomm_dlc_send(dlc, skb);
-               if (err < 0) {
-                       kfree_skb(skb);
-                       break;
-               }
+               rfcomm_dlc_send_noerror(dlc, skb);
 
                sent  += size;
                count -= size;
        }
 
-       return sent ? sent : err;
+       return sent;
 }
 
 static int rfcomm_tty_write_room(struct tty_struct *tty)
 {
        struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
-       int room;
+       int room = 0;
 
-       BT_DBG("tty %p", tty);
-
-       if (!dev || !dev->dlc)
-               return 0;
+       if (dev && dev->dlc)
+               room = rfcomm_room(dev);
 
-       room = rfcomm_room(dev->dlc) - atomic_read(&dev->wmem_alloc);
-       if (room < 0)
-               room = 0;
+       BT_DBG("tty %p room %d", tty, room);
 
        return room;
 }
@@ -1125,7 +1151,7 @@ int __init rfcomm_init_ttys(void)
        rfcomm_tty_driver->subtype      = SERIAL_TYPE_NORMAL;
        rfcomm_tty_driver->flags        = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
        rfcomm_tty_driver->init_termios = tty_std_termios;
-       rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
        rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
        tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
 
index 45007362683b4e4cdfcf8131d79ff38aa95f5733..f06068072bdd78c9d542da4d18d75e556c061ce4 100644 (file)
@@ -78,6 +78,52 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
        return err;
 }
 
+static int smp_ah(struct crypto_blkcipher *tfm, u8 irk[16], u8 r[3], u8 res[3])
+{
+       u8 _res[16], k[16];
+       int err;
+
+       /* r' = padding || r */
+       memset(_res, 0, 13);
+       _res[13] = r[2];
+       _res[14] = r[1];
+       _res[15] = r[0];
+
+       swap128(irk, k);
+       err = smp_e(tfm, k, _res);
+       if (err) {
+               BT_ERR("Encrypt error");
+               return err;
+       }
+
+       /* The output of the random address function ah is:
+        *      ah(h, r) = e(k, r') mod 2^24
+        * The output of the security function e is then truncated to 24 bits
+        * by taking the least significant 24 bits of the output of e as the
+        * result of ah.
+        */
+       res[0] = _res[15];
+       res[1] = _res[14];
+       res[2] = _res[13];
+
+       return 0;
+}
+
+bool smp_irk_matches(struct crypto_blkcipher *tfm, u8 irk[16],
+                    bdaddr_t *bdaddr)
+{
+       u8 hash[3];
+       int err;
+
+       BT_DBG("RPA %pMR IRK %*phN", bdaddr, 16, irk);
+
+       err = smp_ah(tfm, irk, &bdaddr->b[3], hash);
+       if (err)
+               return false;
+
+       return !memcmp(bdaddr->b, hash, 3);
+}
+
 static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16],
                  u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia,
                  u8 _rat, bdaddr_t *ra, u8 res[16])
@@ -203,31 +249,42 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
                              struct smp_cmd_pairing *req,
                              struct smp_cmd_pairing *rsp, __u8 authreq)
 {
-       u8 dist_keys = 0;
+       struct smp_chan *smp = conn->smp_chan;
+       struct hci_conn *hcon = conn->hcon;
+       struct hci_dev *hdev = hcon->hdev;
+       u8 local_dist = 0, remote_dist = 0;
 
        if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->dev_flags)) {
-               dist_keys = SMP_DIST_ENC_KEY;
+               local_dist = SMP_DIST_ENC_KEY;
+               remote_dist = SMP_DIST_ENC_KEY;
                authreq |= SMP_AUTH_BONDING;
        } else {
                authreq &= ~SMP_AUTH_BONDING;
        }
 
+       if (test_bit(HCI_RPA_RESOLVING, &hdev->dev_flags))
+               remote_dist |= SMP_DIST_ID_KEY;
+
        if (rsp == NULL) {
                req->io_capability = conn->hcon->io_capability;
                req->oob_flag = SMP_OOB_NOT_PRESENT;
                req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
-               req->init_key_dist = 0;
-               req->resp_key_dist = dist_keys;
+               req->init_key_dist = local_dist;
+               req->resp_key_dist = remote_dist;
                req->auth_req = (authreq & AUTH_REQ_MASK);
+
+               smp->remote_key_dist = remote_dist;
                return;
        }
 
        rsp->io_capability = conn->hcon->io_capability;
        rsp->oob_flag = SMP_OOB_NOT_PRESENT;
        rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
-       rsp->init_key_dist = 0;
-       rsp->resp_key_dist = req->resp_key_dist & dist_keys;
+       rsp->init_key_dist = req->init_key_dist & remote_dist;
+       rsp->resp_key_dist = req->resp_key_dist & local_dist;
        rsp->auth_req = (authreq & AUTH_REQ_MASK);
+
+       smp->remote_key_dist = rsp->init_key_dist;
 }
 
 static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
@@ -356,20 +413,16 @@ static void confirm_work(struct work_struct *work)
 {
        struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
        struct l2cap_conn *conn = smp->conn;
-       struct crypto_blkcipher *tfm;
+       struct hci_dev *hdev = conn->hcon->hdev;
+       struct crypto_blkcipher *tfm = hdev->tfm_aes;
        struct smp_cmd_pairing_confirm cp;
        int ret;
        u8 res[16], reason;
 
        BT_DBG("conn %p", conn);
 
-       tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
-       if (IS_ERR(tfm)) {
-               reason = SMP_UNSPECIFIED;
-               goto error;
-       }
-
-       smp->tfm = tfm;
+       /* Prevent mutual access to hdev->tfm_aes */
+       hci_dev_lock(hdev);
 
        if (conn->hcon->out)
                ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
@@ -379,6 +432,9 @@ static void confirm_work(struct work_struct *work)
                ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp,
                             conn->hcon->dst_type, &conn->hcon->dst,
                             conn->hcon->src_type, &conn->hcon->src, res);
+
+       hci_dev_unlock(hdev);
+
        if (ret) {
                reason = SMP_UNSPECIFIED;
                goto error;
@@ -400,7 +456,8 @@ static void random_work(struct work_struct *work)
        struct smp_chan *smp = container_of(work, struct smp_chan, random);
        struct l2cap_conn *conn = smp->conn;
        struct hci_conn *hcon = conn->hcon;
-       struct crypto_blkcipher *tfm = smp->tfm;
+       struct hci_dev *hdev = hcon->hdev;
+       struct crypto_blkcipher *tfm = hdev->tfm_aes;
        u8 reason, confirm[16], res[16], key[16];
        int ret;
 
@@ -411,6 +468,9 @@ static void random_work(struct work_struct *work)
 
        BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
 
+       /* Prevent mutual access to hdev->tfm_aes */
+       hci_dev_lock(hdev);
+
        if (hcon->out)
                ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
                             hcon->src_type, &hcon->src,
@@ -419,6 +479,9 @@ static void random_work(struct work_struct *work)
                ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp,
                             hcon->dst_type, &hcon->dst,
                             hcon->src_type, &hcon->src, res);
+
+       hci_dev_unlock(hdev);
+
        if (ret) {
                reason = SMP_UNSPECIFIED;
                goto error;
@@ -469,7 +532,7 @@ static void random_work(struct work_struct *work)
                       SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
 
                hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
-                           HCI_SMP_STK_SLAVE, 0, 0, stk, smp->enc_key_size,
+                           HCI_SMP_STK_SLAVE, 0, stk, smp->enc_key_size,
                            ediv, rand);
        }
 
@@ -502,11 +565,12 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
 void smp_chan_destroy(struct l2cap_conn *conn)
 {
        struct smp_chan *smp = conn->smp_chan;
+       bool complete;
 
        BUG_ON(!smp);
 
-       if (smp->tfm)
-               crypto_free_blkcipher(smp->tfm);
+       complete = test_bit(SMP_FLAG_COMPLETE, &smp->smp_flags);
+       mgmt_smp_complete(conn->hcon, complete);
 
        kfree(smp);
        conn->smp_chan = NULL;
@@ -565,6 +629,9 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
 
        BT_DBG("conn %p", conn);
 
+       if (skb->len < sizeof(*req))
+               return SMP_UNSPECIFIED;
+
        if (conn->hcon->link_mode & HCI_LM_MASTER)
                return SMP_CMD_NOTSUPP;
 
@@ -617,6 +684,9 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
 
        BT_DBG("conn %p", conn);
 
+       if (skb->len < sizeof(*rsp))
+               return SMP_UNSPECIFIED;
+
        if (!(conn->hcon->link_mode & HCI_LM_MASTER))
                return SMP_CMD_NOTSUPP;
 
@@ -661,6 +731,9 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
 
        BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
 
+       if (skb->len < sizeof(smp->pcnf))
+               return SMP_UNSPECIFIED;
+
        memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
        skb_pull(skb, sizeof(smp->pcnf));
 
@@ -686,6 +759,9 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
 
        BT_DBG("conn %p", conn);
 
+       if (skb->len < sizeof(smp->rrnd))
+               return SMP_UNSPECIFIED;
+
        swap128(skb->data, smp->rrnd);
        skb_pull(skb, sizeof(smp->rrnd));
 
@@ -699,7 +775,8 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
        struct smp_ltk *key;
        struct hci_conn *hcon = conn->hcon;
 
-       key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type);
+       key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
+                                  hcon->out);
        if (!key)
                return 0;
 
@@ -724,6 +801,9 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
 
        BT_DBG("conn %p", conn);
 
+       if (skb->len < sizeof(*rp))
+               return SMP_UNSPECIFIED;
+
        if (!(conn->hcon->link_mode & HCI_LM_MASTER))
                return SMP_CMD_NOTSUPP;
 
@@ -813,6 +893,15 @@ static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
        struct smp_cmd_encrypt_info *rp = (void *) skb->data;
        struct smp_chan *smp = conn->smp_chan;
 
+       BT_DBG("conn %p", conn);
+
+       if (skb->len < sizeof(*rp))
+               return SMP_UNSPECIFIED;
+
+       /* Ignore this PDU if it wasn't requested */
+       if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY))
+               return 0;
+
        skb_pull(skb, sizeof(*rp));
 
        memcpy(smp->tk, rp->ltk, sizeof(smp->tk));
@@ -826,21 +915,95 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
        struct smp_chan *smp = conn->smp_chan;
        struct hci_dev *hdev = conn->hcon->hdev;
        struct hci_conn *hcon = conn->hcon;
+       struct smp_ltk *ltk;
        u8 authenticated;
 
+       BT_DBG("conn %p", conn);
+
+       if (skb->len < sizeof(*rp))
+               return SMP_UNSPECIFIED;
+
+       /* Ignore this PDU if it wasn't requested */
+       if (!(smp->remote_key_dist & SMP_DIST_ENC_KEY))
+               return 0;
+
        skb_pull(skb, sizeof(*rp));
 
        hci_dev_lock(hdev);
        authenticated = (hcon->sec_level == BT_SECURITY_HIGH);
-       hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK, 1,
-                   authenticated, smp->tk, smp->enc_key_size,
-                   rp->ediv, rp->rand);
-       smp_distribute_keys(conn, 1);
+       ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK,
+                         authenticated, smp->tk, smp->enc_key_size,
+                         rp->ediv, rp->rand);
+       smp->ltk = ltk;
+       if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
+               smp_distribute_keys(conn, 1);
        hci_dev_unlock(hdev);
 
        return 0;
 }
 
+static int smp_cmd_ident_info(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+       struct smp_cmd_ident_info *info = (void *) skb->data;
+       struct smp_chan *smp = conn->smp_chan;
+
+       BT_DBG("");
+
+       if (skb->len < sizeof(*info))
+               return SMP_UNSPECIFIED;
+
+       /* Ignore this PDU if it wasn't requested */
+       if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
+               return 0;
+
+       skb_pull(skb, sizeof(*info));
+
+       memcpy(smp->irk, info->irk, 16);
+
+       return 0;
+}
+
+static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
+                                  struct sk_buff *skb)
+{
+       struct smp_cmd_ident_addr_info *info = (void *) skb->data;
+       struct smp_chan *smp = conn->smp_chan;
+       struct hci_conn *hcon = conn->hcon;
+       bdaddr_t rpa;
+
+       BT_DBG("");
+
+       if (skb->len < sizeof(*info))
+               return SMP_UNSPECIFIED;
+
+       /* Ignore this PDU if it wasn't requested */
+       if (!(smp->remote_key_dist & SMP_DIST_ID_KEY))
+               return 0;
+
+       skb_pull(skb, sizeof(*info));
+
+       bacpy(&smp->id_addr, &info->bdaddr);
+       smp->id_addr_type = info->addr_type;
+
+       if (hci_bdaddr_is_rpa(&hcon->dst, hcon->dst_type))
+               bacpy(&rpa, &hcon->dst);
+       else
+               bacpy(&rpa, BDADDR_ANY);
+
+       smp->remote_irk = hci_add_irk(conn->hcon->hdev, &smp->id_addr,
+                                     smp->id_addr_type, smp->irk, &rpa);
+
+       /* Track the connection based on the Identity Address from now on */
+       bacpy(&hcon->dst, &smp->id_addr);
+       hcon->dst_type = smp->id_addr_type;
+
+       l2cap_conn_update_id_addr(hcon);
+
+       smp_distribute_keys(conn, 1);
+
+       return 0;
+}
+
 int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
 {
        struct hci_conn *hcon = conn->hcon;
@@ -915,7 +1078,13 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
                break;
 
        case SMP_CMD_IDENT_INFO:
+               reason = smp_cmd_ident_info(conn, skb);
+               break;
+
        case SMP_CMD_IDENT_ADDR_INFO:
+               reason = smp_cmd_ident_addr_info(conn, skb);
+               break;
+
        case SMP_CMD_SIGN_INFO:
                /* Just ignored */
                reason = 0;
@@ -937,6 +1106,28 @@ done:
        return err;
 }
 
+static void smp_notify_keys(struct l2cap_conn *conn)
+{
+       struct smp_chan *smp = conn->smp_chan;
+       struct hci_conn *hcon = conn->hcon;
+       struct hci_dev *hdev = hcon->hdev;
+
+       if (smp->remote_irk)
+               mgmt_new_irk(hdev, smp->remote_irk);
+
+       if (smp->ltk) {
+               smp->ltk->bdaddr_type = hcon->dst_type;
+               bacpy(&smp->ltk->bdaddr, &hcon->dst);
+               mgmt_new_ltk(hdev, smp->ltk);
+       }
+
+       if (smp->slave_ltk) {
+               smp->slave_ltk->bdaddr_type = hcon->dst_type;
+               bacpy(&smp->slave_ltk->bdaddr, &hcon->dst);
+               mgmt_new_ltk(hdev, smp->slave_ltk);
+       }
+}
+
 int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
 {
        struct smp_cmd_pairing *req, *rsp;
@@ -964,13 +1155,13 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
                *keydist &= req->resp_key_dist;
        }
 
-
        BT_DBG("keydist 0x%x", *keydist);
 
        if (*keydist & SMP_DIST_ENC_KEY) {
                struct smp_cmd_encrypt_info enc;
                struct smp_cmd_master_ident ident;
                struct hci_conn *hcon = conn->hcon;
+               struct smp_ltk *ltk;
                u8 authenticated;
                __le16 ediv;
 
@@ -981,9 +1172,10 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
                smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
 
                authenticated = hcon->sec_level == BT_SECURITY_HIGH;
-               hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
-                           HCI_SMP_LTK_SLAVE, 1, authenticated,
-                           enc.ltk, smp->enc_key_size, ediv, ident.rand);
+               ltk = hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
+                                 HCI_SMP_LTK_SLAVE, authenticated, enc.ltk,
+                                 smp->enc_key_size, ediv, ident.rand);
+               smp->slave_ltk = ltk;
 
                ident.ediv = ediv;
 
@@ -1022,9 +1214,11 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
                *keydist &= ~SMP_DIST_SIGN;
        }
 
-       if (conn->hcon->out || force) {
+       if (conn->hcon->out || force || !(rsp->init_key_dist & 0x07)) {
                clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags);
                cancel_delayed_work_sync(&conn->security_timer);
+               set_bit(SMP_FLAG_COMPLETE, &smp->smp_flags);
+               smp_notify_keys(conn);
                smp_chan_destroy(conn);
        }
 
index a700bcb490d7bedb24c55b0b97b58bc6280c5552..d8cc543f523ce77f460eeb42bd7e0be53d8d89ae 100644 (file)
@@ -118,6 +118,7 @@ struct smp_cmd_security_req {
 #define SMP_FLAG_TK_VALID      1
 #define SMP_FLAG_CFM_PENDING   2
 #define SMP_FLAG_MITM_AUTH     3
+#define SMP_FLAG_COMPLETE      4
 
 struct smp_chan {
        struct l2cap_conn *conn;
@@ -128,11 +129,16 @@ struct smp_chan {
        u8              pcnf[16]; /* SMP Pairing Confirm */
        u8              tk[16]; /* SMP Temporary Key */
        u8              enc_key_size;
+       u8              remote_key_dist;
+       bdaddr_t        id_addr;
+       u8              id_addr_type;
+       u8              irk[16];
+       struct smp_ltk  *ltk;
+       struct smp_ltk  *slave_ltk;
+       struct smp_irk  *remote_irk;
        unsigned long   smp_flags;
-       struct crypto_blkcipher *tfm;
        struct work_struct confirm;
        struct work_struct random;
-
 };
 
 /* SMP Commands */
@@ -144,4 +150,7 @@ int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
 
 void smp_chan_destroy(struct l2cap_conn *conn);
 
+bool smp_irk_matches(struct crypto_blkcipher *tfm, u8 irk[16],
+                    bdaddr_t *bdaddr);
+
 #endif /* __SMP_H */
index 6973ccdd230b88942ba71ceae911fb4d58424279..1acb29109b45dfc9d76d83b0506d8ed47932b10d 100644 (file)
@@ -1021,8 +1021,10 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
                                        IEEE80211_P2P_OPPPS_ENABLE_BIT;
 
        err = ieee80211_assign_beacon(sdata, &params->beacon);
-       if (err < 0)
+       if (err < 0) {
+               ieee80211_vif_release_channel(sdata);
                return err;
+       }
        changed |= err;
 
        err = drv_start_ap(sdata->local, sdata);
@@ -1032,6 +1034,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
                if (old)
                        kfree_rcu(old, rcu_head);
                RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+               ieee80211_vif_release_channel(sdata);
                return err;
        }
 
@@ -1093,8 +1096,6 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        kfree(sdata->u.ap.next_beacon);
        sdata->u.ap.next_beacon = NULL;
 
-       cancel_work_sync(&sdata->u.ap.request_smps_work);
-
        /* turn off carrier for this interface and dependent VLANs */
        list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
                netif_carrier_off(vlan->dev);
@@ -1106,6 +1107,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
        kfree_rcu(old_beacon, rcu_head);
        if (old_probe_resp)
                kfree_rcu(old_probe_resp, rcu_head);
+       sdata->u.ap.driver_smps_mode = IEEE80211_SMPS_OFF;
 
        __sta_info_flush(sdata, true);
        ieee80211_free_keys(sdata, true);
@@ -1345,9 +1347,6 @@ static int sta_apply_parameters(struct ieee80211_local *local,
                                                    params->vht_capa, sta);
 
        if (params->opmode_notif_used) {
-               enum ieee80211_band band =
-                       ieee80211_get_sdata_band(sdata);
-
                /* returned value is only needed for rc update, but the
                 * rc isn't initialized here yet, so ignore it
                 */
@@ -2665,6 +2664,24 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
        INIT_LIST_HEAD(&roc->dependents);
 
+       /*
+        * cookie is either the roc cookie (for normal roc)
+        * or the SKB (for mgmt TX)
+        */
+       if (!txskb) {
+               /* local->mtx protects this */
+               local->roc_cookie_counter++;
+               roc->cookie = local->roc_cookie_counter;
+               /* wow, you wrapped 64 bits ... more likely a bug */
+               if (WARN_ON(roc->cookie == 0)) {
+                       roc->cookie = 1;
+                       local->roc_cookie_counter++;
+               }
+               *cookie = roc->cookie;
+       } else {
+               *cookie = (unsigned long)txskb;
+       }
+
        /* if there's one pending or we're scanning, queue this one */
        if (!list_empty(&local->roc_list) ||
            local->scanning || local->radar_detect_enabled)
@@ -2787,24 +2804,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
        if (!queued)
                list_add_tail(&roc->list, &local->roc_list);
 
-       /*
-        * cookie is either the roc cookie (for normal roc)
-        * or the SKB (for mgmt TX)
-        */
-       if (!txskb) {
-               /* local->mtx protects this */
-               local->roc_cookie_counter++;
-               roc->cookie = local->roc_cookie_counter;
-               /* wow, you wrapped 64 bits ... more likely a bug */
-               if (WARN_ON(roc->cookie == 0)) {
-                       roc->cookie = 1;
-                       local->roc_cookie_counter++;
-               }
-               *cookie = roc->cookie;
-       } else {
-               *cookie = (unsigned long)txskb;
-       }
-
        return 0;
 }
 
@@ -3645,8 +3644,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
 
 static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
                               u8 *peer, u8 action_code, u8 dialog_token,
-                              u16 status_code, const u8 *extra_ies,
-                              size_t extra_ies_len)
+                              u16 status_code, u32 peer_capability,
+                              const u8 *extra_ies, size_t extra_ies_len)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
index ebf80f3abd83fe1af18ce94863a0fe1dd090ea19..40a648938985db178b6997f1541bb47a3247b5a8 100644 (file)
@@ -358,6 +358,18 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
 }
 IEEE80211_IF_FILE_W(tkip_mic_test);
 
+static ssize_t ieee80211_if_parse_beacon_loss(
+       struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
+{
+       if (!ieee80211_sdata_running(sdata) || !sdata->vif.bss_conf.assoc)
+               return -ENOTCONN;
+
+       ieee80211_beacon_loss(&sdata->vif);
+
+       return buflen;
+}
+IEEE80211_IF_FILE_W(beacon_loss);
+
 static ssize_t ieee80211_if_fmt_uapsd_queues(
        const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
 {
@@ -569,6 +581,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
        DEBUGFS_ADD(beacon_timeout);
        DEBUGFS_ADD_MODE(smps, 0600);
        DEBUGFS_ADD_MODE(tkip_mic_test, 0200);
+       DEBUGFS_ADD_MODE(beacon_loss, 0200);
        DEBUGFS_ADD_MODE(uapsd_queues, 0600);
        DEBUGFS_ADD_MODE(uapsd_max_sp_len, 0600);
 }
index ef8b385eff04e4c7a279a92722fedc3f84163f6c..fc689f5d971e259381f0a26e13079f1a727fe704 100644 (file)
@@ -354,16 +354,20 @@ drv_sched_scan_start(struct ieee80211_local *local,
        return ret;
 }
 
-static inline void drv_sched_scan_stop(struct ieee80211_local *local,
-                                      struct ieee80211_sub_if_data *sdata)
+static inline int drv_sched_scan_stop(struct ieee80211_local *local,
+                                     struct ieee80211_sub_if_data *sdata)
 {
+       int ret;
+
        might_sleep();
 
        check_sdata_in_driver(sdata);
 
        trace_drv_sched_scan_stop(local, sdata);
-       local->ops->sched_scan_stop(&local->hw, &sdata->vif);
-       trace_drv_return_void(local);
+       ret = local->ops->sched_scan_stop(&local->hw, &sdata->vif);
+       trace_drv_return_int(local, ret);
+
+       return ret;
 }
 
 static inline void drv_sw_scan_start(struct ieee80211_local *local)
index dc3c28002e3e2d99ee20f6d3809fe594d3796430..c150b68436d78ada5bfbb0825d128d8e89f916e3 100644 (file)
@@ -466,7 +466,9 @@ void ieee80211_request_smps_ap_work(struct work_struct *work)
                             u.ap.request_smps_work);
 
        sdata_lock(sdata);
-       __ieee80211_request_smps_ap(sdata, sdata->u.ap.driver_smps_mode);
+       if (sdata_dereference(sdata->u.ap.beacon, sdata))
+               __ieee80211_request_smps_ap(sdata,
+                                           sdata->u.ap.driver_smps_mode);
        sdata_unlock(sdata);
 }
 
@@ -480,8 +482,6 @@ void ieee80211_request_smps(struct ieee80211_vif *vif,
                return;
 
        if (vif->type == NL80211_IFTYPE_STATION) {
-               if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF))
-                       smps_mode = IEEE80211_SMPS_AUTOMATIC;
                if (sdata->u.mgd.driver_smps_mode == smps_mode)
                        return;
                sdata->u.mgd.driver_smps_mode = smps_mode;
index 9c84b75f3de8a58dc4f4e3cbf8a0c0c72be63ab3..e458ca0dffec12fcaca7e614eb14204760ca9b17 100644 (file)
@@ -283,6 +283,11 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
 
        err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
                                            &chandef);
+       if (err < 0) {
+               sdata_info(sdata,
+                          "Failed to join IBSS, invalid chandef\n");
+               return;
+       }
        if (err > 0) {
                if (!ifibss->userspace_handles_dfs) {
                        sdata_info(sdata,
@@ -684,12 +689,9 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
        struct cfg80211_bss *cbss;
        struct beacon_data *presp;
        struct sta_info *sta;
-       int active_ibss;
        u16 capability;
 
-       active_ibss = ieee80211_sta_active_ibss(sdata);
-
-       if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
+       if (!is_zero_ether_addr(ifibss->bssid)) {
                capability = WLAN_CAPABILITY_IBSS;
 
                if (ifibss->privacy)
index 0014b5396ce5a4212a978af3f6f9f7594a9de60f..8603dfb52b3a90ab604390b987eaba1a882197a4 100644 (file)
@@ -1242,6 +1242,8 @@ struct ieee80211_local {
 
        struct ieee80211_sub_if_data __rcu *p2p_sdata;
 
+       struct napi_struct *napi;
+
        /* virtual monitor interface */
        struct ieee80211_sub_if_data __rcu *monitor_sdata;
        struct cfg80211_chan_def monitor_chandef;
index 8880bc8fce0dc9d20a118efd9667218ea62f4b1e..be198f42f1f7ce6b8f47aedef24032fe0f04c4fe 100644 (file)
@@ -101,9 +101,8 @@ static u32 __ieee80211_idle_on(struct ieee80211_local *local)
 static u32 __ieee80211_recalc_idle(struct ieee80211_local *local,
                                   bool force_active)
 {
-       bool working = false, scanning, active;
+       bool working, scanning, active;
        unsigned int led_trig_start = 0, led_trig_stop = 0;
-       struct ieee80211_roc_work *roc;
 
        lockdep_assert_held(&local->mtx);
 
@@ -111,12 +110,8 @@ static u32 __ieee80211_recalc_idle(struct ieee80211_local *local,
                 !list_empty(&local->chanctx_list) ||
                 local->monitors;
 
-       if (!local->ops->remain_on_channel) {
-               list_for_each_entry(roc, &local->roc_list, list) {
-                       working = true;
-                       break;
-               }
-       }
+       working = !local->ops->remain_on_channel &&
+                 !list_empty(&local->roc_list);
 
        scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
                   test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning);
@@ -418,20 +413,24 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
                return ret;
        }
 
+       mutex_lock(&local->iflist_mtx);
+       rcu_assign_pointer(local->monitor_sdata, sdata);
+       mutex_unlock(&local->iflist_mtx);
+
        mutex_lock(&local->mtx);
        ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef,
                                        IEEE80211_CHANCTX_EXCLUSIVE);
        mutex_unlock(&local->mtx);
        if (ret) {
+               mutex_lock(&local->iflist_mtx);
+               rcu_assign_pointer(local->monitor_sdata, NULL);
+               mutex_unlock(&local->iflist_mtx);
+               synchronize_net();
                drv_remove_interface(local, sdata);
                kfree(sdata);
                return ret;
        }
 
-       mutex_lock(&local->iflist_mtx);
-       rcu_assign_pointer(local->monitor_sdata, sdata);
-       mutex_unlock(&local->iflist_mtx);
-
        return 0;
 }
 
@@ -770,12 +769,19 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_roc_purge(local, sdata);
 
-       if (sdata->vif.type == NL80211_IFTYPE_STATION)
+       switch (sdata->vif.type) {
+       case NL80211_IFTYPE_STATION:
                ieee80211_mgd_stop(sdata);
-
-       if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
+               break;
+       case NL80211_IFTYPE_ADHOC:
                ieee80211_ibss_stop(sdata);
-
+               break;
+       case NL80211_IFTYPE_AP:
+               cancel_work_sync(&sdata->u.ap.request_smps_work);
+               break;
+       default:
+               break;
+       }
 
        /*
         * Remove all stations associated with this interface.
index 1f7d8422d62d865e1d6b2f84021681e0718c898c..b055f6a55c68e231c5bc73393a7817309a0ed10d 100644 (file)
@@ -1076,6 +1076,18 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL(ieee80211_register_hw);
 
+void ieee80211_napi_add(struct ieee80211_hw *hw, struct napi_struct *napi,
+                       struct net_device *napi_dev,
+                       int (*poll)(struct napi_struct *, int),
+                       int weight)
+{
+       struct ieee80211_local *local = hw_to_local(hw);
+
+       netif_napi_add(napi_dev, napi, poll, weight);
+       local->napi = napi;
+}
+EXPORT_SYMBOL_GPL(ieee80211_napi_add);
+
 void ieee80211_unregister_hw(struct ieee80211_hw *hw)
 {
        struct ieee80211_local *local = hw_to_local(hw);
index 61604834b9146c0b1c6d7360570120d432c1c2c2..46b62bb3677ccbad6b29446a82f2413e97b9ad29 100644 (file)
@@ -131,13 +131,13 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata)
        if (unlikely(!sdata->u.mgd.associated))
                return;
 
+       ifmgd->probe_send_count = 0;
+
        if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
                return;
 
        mod_timer(&sdata->u.mgd.conn_mon_timer,
                  round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
-
-       ifmgd->probe_send_count = 0;
 }
 
 static int ecw2cw(int ecw)
@@ -2249,6 +2249,62 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
        /* ignore frame -- wait for timeout */
 }
 
+#define case_WLAN(type) \
+       case WLAN_REASON_##type: return #type
+
+static const char *ieee80211_get_reason_code_string(u16 reason_code)
+{
+       switch (reason_code) {
+       case_WLAN(UNSPECIFIED);
+       case_WLAN(PREV_AUTH_NOT_VALID);
+       case_WLAN(DEAUTH_LEAVING);
+       case_WLAN(DISASSOC_DUE_TO_INACTIVITY);
+       case_WLAN(DISASSOC_AP_BUSY);
+       case_WLAN(CLASS2_FRAME_FROM_NONAUTH_STA);
+       case_WLAN(CLASS3_FRAME_FROM_NONASSOC_STA);
+       case_WLAN(DISASSOC_STA_HAS_LEFT);
+       case_WLAN(STA_REQ_ASSOC_WITHOUT_AUTH);
+       case_WLAN(DISASSOC_BAD_POWER);
+       case_WLAN(DISASSOC_BAD_SUPP_CHAN);
+       case_WLAN(INVALID_IE);
+       case_WLAN(MIC_FAILURE);
+       case_WLAN(4WAY_HANDSHAKE_TIMEOUT);
+       case_WLAN(GROUP_KEY_HANDSHAKE_TIMEOUT);
+       case_WLAN(IE_DIFFERENT);
+       case_WLAN(INVALID_GROUP_CIPHER);
+       case_WLAN(INVALID_PAIRWISE_CIPHER);
+       case_WLAN(INVALID_AKMP);
+       case_WLAN(UNSUPP_RSN_VERSION);
+       case_WLAN(INVALID_RSN_IE_CAP);
+       case_WLAN(IEEE8021X_FAILED);
+       case_WLAN(CIPHER_SUITE_REJECTED);
+       case_WLAN(DISASSOC_UNSPECIFIED_QOS);
+       case_WLAN(DISASSOC_QAP_NO_BANDWIDTH);
+       case_WLAN(DISASSOC_LOW_ACK);
+       case_WLAN(DISASSOC_QAP_EXCEED_TXOP);
+       case_WLAN(QSTA_LEAVE_QBSS);
+       case_WLAN(QSTA_NOT_USE);
+       case_WLAN(QSTA_REQUIRE_SETUP);
+       case_WLAN(QSTA_TIMEOUT);
+       case_WLAN(QSTA_CIPHER_NOT_SUPP);
+       case_WLAN(MESH_PEER_CANCELED);
+       case_WLAN(MESH_MAX_PEERS);
+       case_WLAN(MESH_CONFIG);
+       case_WLAN(MESH_CLOSE);
+       case_WLAN(MESH_MAX_RETRIES);
+       case_WLAN(MESH_CONFIRM_TIMEOUT);
+       case_WLAN(MESH_INVALID_GTK);
+       case_WLAN(MESH_INCONSISTENT_PARAM);
+       case_WLAN(MESH_INVALID_SECURITY);
+       case_WLAN(MESH_PATH_ERROR);
+       case_WLAN(MESH_PATH_NOFORWARD);
+       case_WLAN(MESH_PATH_DEST_UNREACHABLE);
+       case_WLAN(MAC_EXISTS_IN_MBSS);
+       case_WLAN(MESH_CHAN_REGULATORY);
+       case_WLAN(MESH_CHAN);
+       default: return "<unknown>";
+       }
+}
 
 static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
                                     struct ieee80211_mgmt *mgmt, size_t len)
@@ -2270,8 +2326,8 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 
        reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
 
-       sdata_info(sdata, "deauthenticated from %pM (Reason: %u)\n",
-                  bssid, reason_code);
+       sdata_info(sdata, "deauthenticated from %pM (Reason: %u=%s)\n",
+                  bssid, reason_code, ieee80211_get_reason_code_string(reason_code));
 
        ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
 
@@ -4340,8 +4396,8 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
        bool report_frame = false;
 
        sdata_info(sdata,
-                  "deauthenticating from %pM by local choice (reason=%d)\n",
-                  req->bssid, req->reason_code);
+                  "deauthenticating from %pM by local choice (Reason: %u=%s)\n",
+                  req->bssid, req->reason_code, ieee80211_get_reason_code_string(req->reason_code));
 
        if (ifmgd->auth_data) {
                drv_mgd_prepare_tx(sdata->local, sdata);
@@ -4387,8 +4443,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
                return -ENOLINK;
 
        sdata_info(sdata,
-                  "disassociating from %pM by local choice (reason=%d)\n",
-                  req->bss->bssid, req->reason_code);
+                  "disassociating from %pM by local choice (Reason: %u=%s)\n",
+                  req->bss->bssid, req->reason_code, ieee80211_get_reason_code_string(req->reason_code));
 
        memcpy(bssid, req->bss->bssid, ETH_ALEN);
        ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC,
index 593062109c50231d7d131e2165ddb96d7359a35d..58e4b7052d1764ad9ed20e9293aa0b7eee80ae99 100644 (file)
@@ -1954,7 +1954,10 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
                /* deliver to local stack */
                skb->protocol = eth_type_trans(skb, dev);
                memset(skb->cb, 0, sizeof(skb->cb));
-               netif_receive_skb(skb);
+               if (rx->local->napi)
+                       napi_gro_receive(rx->local->napi, skb);
+               else
+                       netif_receive_skb(skb);
        }
 
        if (xmit_skb) {
index 88c81616f8f758595e90cced1d503448ce6ba969..836f500dfbf3c989f1f4d21fa281da0d506468fa 100644 (file)
@@ -472,9 +472,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
        if (local->ops->hw_scan) {
                u8 *ies;
 
-               local->hw_scan_ies_bufsize = 2 + IEEE80211_MAX_SSID_LEN +
-                                            local->scan_ies_len +
-                                            req->ie_len;
+               local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len;
                local->hw_scan_req = kmalloc(
                                sizeof(*local->hw_scan_req) +
                                req->n_channels * sizeof(req->channels[0]) +
@@ -979,8 +977,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
        struct cfg80211_chan_def chandef;
        int ret, i, iebufsz;
 
-       iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
-                 local->scan_ies_len + req->ie_len;
+       iebufsz = local->scan_ies_len + req->ie_len;
 
        lockdep_assert_held(&local->mtx);
 
@@ -1059,7 +1056,7 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
        local->sched_scan_req = NULL;
 
        if (rcu_access_pointer(local->sched_scan_sdata))
-               drv_sched_scan_stop(local, sdata);
+               ret = drv_sched_scan_stop(local, sdata);
 
 out:
        mutex_unlock(&local->mtx);
index 5476a69b45c9244123dd21dbd60882b3f98b6e70..722151fa5dced6aaccee5fe8dab0986d2a78bde2 100644 (file)
@@ -874,7 +874,7 @@ static int ieee80211_fragment(struct ieee80211_tx_data *tx,
        }
 
        /* adjust first fragment's length */
-       skb->len = hdrlen + per_fragm;
+       skb_trim(skb, hdrlen + per_fragm);
        return 0;
 }
 
index f8ab7df1ab0dc8f0a3bfadf13a6dfd08caa5dd54..5946450c540676b75b726e20b016395ae08e6b7c 100644 (file)
@@ -705,12 +705,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
        case NL80211_IFTYPE_MONITOR:
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_WDS:
-               /* these interface types don't really have a channel */
-               return;
        case NL80211_IFTYPE_P2P_DEVICE:
-               if (wdev->wiphy->features &
-                               NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL)
-                       *chanmode = CHAN_MODE_EXCLUSIVE;
+               /* these interface types don't really have a channel */
                return;
        case NL80211_IFTYPE_UNSPECIFIED:
        case NUM_NL80211_IFTYPES:
index b5ff39a6f6ed9949a828b2391ad5bdf284ab54b9..76ae6a605abb3366ab757bda121606d1b4747f10 100644 (file)
@@ -203,8 +203,11 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
 
        rdev->opencount--;
 
-       WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev &&
-               !rdev->scan_req->notified);
+       if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
+               if (WARN_ON(!rdev->scan_req->notified))
+                       rdev->scan_req->aborted = true;
+               ___cfg80211_scan_done(rdev, false);
+       }
 }
 
 static int cfg80211_rfkill_set_block(void *data, bool blocked)
@@ -440,9 +443,6 @@ int wiphy_register(struct wiphy *wiphy)
        int i;
        u16 ifmodes = wiphy->interface_modes;
 
-       /* support for 5/10 MHz is broken due to nl80211 API mess - disable */
-       wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_5_10_MHZ;
-
        /*
         * There are major locking problems in nl80211/mac80211 for CSA,
         * disable for all drivers until this has been reworked.
@@ -859,8 +859,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                break;
        case NETDEV_DOWN:
                cfg80211_update_iface_num(rdev, wdev->iftype, -1);
-               WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev &&
-                       !rdev->scan_req->notified);
+               if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
+                       if (WARN_ON(!rdev->scan_req->notified))
+                               rdev->scan_req->aborted = true;
+                       ___cfg80211_scan_done(rdev, false);
+               }
 
                if (WARN_ON(rdev->sched_scan_req &&
                            rdev->sched_scan_req->dev == wdev->netdev)) {
index 9895ab16c0510d2fcbf631d8120ec535beed7db8..40683004d52338fa8830a11cd38fdd42c74c156a 100644 (file)
@@ -62,6 +62,7 @@ struct cfg80211_registered_device {
        struct rb_root bss_tree;
        u32 bss_generation;
        struct cfg80211_scan_request *scan_req; /* protected by RTNL */
+       struct sk_buff *scan_msg;
        struct cfg80211_sched_scan_request *sched_scan_req;
        unsigned long suspend_at;
        struct work_struct scan_done_wk;
@@ -363,7 +364,8 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
                                   struct key_params *params, int key_idx,
                                   bool pairwise, const u8 *mac_addr);
 void __cfg80211_scan_done(struct work_struct *wk);
-void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev);
+void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
+                          bool send_message);
 void __cfg80211_sched_scan_results(struct work_struct *wk);
 int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
                               bool driver_initiated);
index 9a8217d2a90882f872451833cabe30159a64953f..fdfd3f063a9bcad326377d4da0b2f6c5354c5cb5 100644 (file)
@@ -105,6 +105,8 @@ function parse_reg_rule()
                        flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
                } else if (flagarray[arg] == "NO-IR") {
                        flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
+               } else if (flagarray[arg] == "AUTO-BW") {
+                       flags = flags "\n\t\t\tNL80211_RRF_AUTO_BW | "
                }
 
        }
index 1470b90e438f4bf6306be3591fdf6d376676ec5f..349db9ddc0d13737eb84f874bc3b556b61b38026 100644 (file)
@@ -128,12 +128,11 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
 #endif
        check_chan = params->chandef.chan;
        if (params->userspace_handles_dfs) {
-               /* use channel NULL to check for radar even if the current
-                * channel is not a radar channel - it might decide to change
-                * to DFS channel later.
+               /* Check for radar even if the current channel is not
+                * a radar channel - it might decide to change to DFS
+                * channel later.
                 */
                radar_detect_width = BIT(params->chandef.width);
-               check_chan = NULL;
        }
 
        err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
index d42a3fcb2f67ae0fb7a563f07ee74f96fa17fee7..5af5cc6b2c4c2406475a3063a69eef80cc14691f 100644 (file)
@@ -236,6 +236,12 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
                if (!netif_running(wdev->netdev))
                        return -ENETDOWN;
 
+               /* cfg80211_can_use_chan() calls
+                * cfg80211_can_use_iftype_chan() with no radar
+                * detection, so if we're trying to use a radar
+                * channel here, something is wrong.
+                */
+               WARN_ON_ONCE(chandef->chan->flags & IEEE80211_CHAN_RADAR);
                err = cfg80211_can_use_chan(rdev, wdev, chandef->chan,
                                            CHAN_MODE_SHARED);
                if (err)
index ebea1a197afb368faadfc90d1f97f2ffa6e1bcff..2c38b28a85b9b1d800caaf617be18f06517b63cd 100644 (file)
@@ -384,6 +384,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
                                   .len = IEEE80211_QOS_MAP_LEN_MAX },
        [NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
        [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
+       [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
 };
 
 /* policy for the key attributes */
@@ -1740,9 +1741,10 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
                                 * We can then retry with the larger buffer.
                                 */
                                if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
-                                   !skb->len &&
+                                   !skb->len && !state->split &&
                                    cb->min_dump_alloc < 4096) {
                                        cb->min_dump_alloc = 4096;
+                                       state->split_start = 0;
                                        rtnl_unlock();
                                        return 1;
                                }
@@ -4626,6 +4628,8 @@ static int parse_reg_rule(struct nlattr *tb[],
                return -EINVAL;
        if (!tb[NL80211_ATTR_FREQ_RANGE_END])
                return -EINVAL;
+       if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
+               return -EINVAL;
        if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
                return -EINVAL;
 
@@ -4635,9 +4639,8 @@ static int parse_reg_rule(struct nlattr *tb[],
                nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
        freq_range->end_freq_khz =
                nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
-       if (tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
-               freq_range->max_bandwidth_khz =
-                       nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
+       freq_range->max_bandwidth_khz =
+               nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
 
        power_rule->max_eirp =
                nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
@@ -5274,7 +5277,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
        if (!rdev->ops->scan)
                return -EOPNOTSUPP;
 
-       if (rdev->scan_req) {
+       if (rdev->scan_req || rdev->scan_msg) {
                err = -EBUSY;
                goto unlock;
        }
@@ -5709,8 +5712,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
                request->min_rssi_thold = NL80211_SCAN_RSSI_THOLD_OFF;
        }
 
-       if (info->attrs[NL80211_ATTR_IE]) {
-               request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+       if (ie_len) {
+               request->ie_len = ie_len;
                memcpy((void *)request->ie,
                       nla_data(info->attrs[NL80211_ATTR_IE]),
                       request->ie_len);
@@ -5910,17 +5913,22 @@ skip_beacons:
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
                return -EINVAL;
 
-       if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
-           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO ||
-           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC) {
+       switch (dev->ieee80211_ptr->iftype) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_P2P_GO:
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
                err = cfg80211_chandef_dfs_required(wdev->wiphy,
                                                    &params.chandef);
-               if (err < 0) {
+               if (err < 0)
                        return err;
-               } else if (err) {
+               if (err) {
                        radar_detect_width = BIT(params.chandef.width);
                        params.radar_required = true;
                }
+               break;
+       default:
+               break;
        }
 
        err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
@@ -7268,6 +7276,7 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct net_device *dev = info->user_ptr[1];
        u8 action_code, dialog_token;
+       u32 peer_capability = 0;
        u16 status_code;
        u8 *peer;
 
@@ -7286,9 +7295,12 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
        action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
        status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
        dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
+       if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY])
+               peer_capability =
+                       nla_get_u32(info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]);
 
        return rdev_tdls_mgmt(rdev, dev, peer, action_code,
-                             dialog_token, status_code,
+                             dialog_token, status_code, peer_capability,
                              nla_data(info->attrs[NL80211_ATTR_IE]),
                              nla_len(info->attrs[NL80211_ATTR_IE]));
 }
@@ -10116,40 +10128,31 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
                                NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
 
-void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
-                           struct wireless_dev *wdev)
+struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
+                                      struct wireless_dev *wdev, bool aborted)
 {
        struct sk_buff *msg;
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
-               return;
+               return NULL;
 
        if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
-                                 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
+                                 aborted ? NL80211_CMD_SCAN_ABORTED :
+                                           NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
                nlmsg_free(msg);
-               return;
+               return NULL;
        }
 
-       genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
-                               NL80211_MCGRP_SCAN, GFP_KERNEL);
+       return msg;
 }
 
-void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
-                              struct wireless_dev *wdev)
+void nl80211_send_scan_result(struct cfg80211_registered_device *rdev,
+                             struct sk_buff *msg)
 {
-       struct sk_buff *msg;
-
-       msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
        if (!msg)
                return;
 
-       if (nl80211_send_scan_msg(msg, rdev, wdev, 0, 0, 0,
-                                 NL80211_CMD_SCAN_ABORTED) < 0) {
-               nlmsg_free(msg);
-               return;
-       }
-
        genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
                                NL80211_MCGRP_SCAN, GFP_KERNEL);
 }
index cb0216e1a0041b1c66b2a4a3e55405492b7743f7..1e6df9630f42f11f815578a4bbc73fc7b037017d 100644 (file)
@@ -8,10 +8,10 @@ void nl80211_exit(void);
 void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev);
 void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
                             struct wireless_dev *wdev);
-void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
-                           struct wireless_dev *wdev);
-void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
-                              struct wireless_dev *wdev);
+struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev,
+                                      struct wireless_dev *wdev, bool aborted);
+void nl80211_send_scan_result(struct cfg80211_registered_device *rdev,
+                             struct sk_buff *msg);
 void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
                             struct net_device *netdev, u32 cmd);
 void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
index c8e225947adb2601d31dbb0f4a1aa5a4392d8bc5..74d97d33c938e8250ef300c2c3fb82d2a39b63b6 100644 (file)
@@ -769,13 +769,16 @@ static inline int rdev_set_rekey_data(struct cfg80211_registered_device *rdev,
 static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev,
                                 struct net_device *dev, u8 *peer,
                                 u8 action_code, u8 dialog_token,
-                                u16 status_code, const u8 *buf, size_t len)
+                                u16 status_code, u32 peer_capability,
+                                const u8 *buf, size_t len)
 {
        int ret;
        trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
-                            dialog_token, status_code, buf, len);
+                            dialog_token, status_code, peer_capability,
+                            buf, len);
        ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
-                                  dialog_token, status_code, buf, len);
+                                  dialog_token, status_code, peer_capability,
+                                  buf, len);
        trace_rdev_return_int(&rdev->wiphy, ret);
        return ret;
 }
index 27c5253e7a610b706ac99402727eda9bc66de755..6b6f33ad78f2b25be6b1d1318cb237705c17e6ba 100644 (file)
@@ -563,9 +563,6 @@ unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
                if (freq_range_tmp->end_freq_khz < freq_range->start_freq_khz)
                        break;
 
-               if (freq_range_tmp->max_bandwidth_khz)
-                       break;
-
                freq_range = freq_range_tmp;
        }
 
@@ -582,9 +579,6 @@ unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
                if (freq_range_tmp->start_freq_khz > freq_range->end_freq_khz)
                        break;
 
-               if (freq_range_tmp->max_bandwidth_khz)
-                       break;
-
                freq_range = freq_range_tmp;
        }
 
@@ -729,21 +723,29 @@ static int reg_rules_intersect(const struct ieee80211_regdomain *rd1,
        max_bandwidth1 = freq_range1->max_bandwidth_khz;
        max_bandwidth2 = freq_range2->max_bandwidth_khz;
 
-       /*
-        * In case max_bandwidth1 == 0 and max_bandwith2 == 0 set
-        * output bandwidth as 0 (auto calculation). Next we will
-        * calculate this correctly in handle_channel function.
-        * In other case calculate output bandwidth here.
-        */
-       if (max_bandwidth1 || max_bandwidth2) {
-               if (!max_bandwidth1)
-                       max_bandwidth1 = reg_get_max_bandwidth(rd1, rule1);
-               if (!max_bandwidth2)
-                       max_bandwidth2 = reg_get_max_bandwidth(rd2, rule2);
-       }
+       if (rule1->flags & NL80211_RRF_AUTO_BW)
+               max_bandwidth1 = reg_get_max_bandwidth(rd1, rule1);
+       if (rule2->flags & NL80211_RRF_AUTO_BW)
+               max_bandwidth2 = reg_get_max_bandwidth(rd2, rule2);
 
        freq_range->max_bandwidth_khz = min(max_bandwidth1, max_bandwidth2);
 
+       intersected_rule->flags = rule1->flags | rule2->flags;
+
+       /*
+        * In case NL80211_RRF_AUTO_BW requested for both rules
+        * set AUTO_BW in intersected rule also. Next we will
+        * calculate BW correctly in handle_channel function.
+        * In other case remove AUTO_BW flag while we calculate
+        * maximum bandwidth correctly and auto calculation is
+        * not required.
+        */
+       if ((rule1->flags & NL80211_RRF_AUTO_BW) &&
+           (rule2->flags & NL80211_RRF_AUTO_BW))
+               intersected_rule->flags |= NL80211_RRF_AUTO_BW;
+       else
+               intersected_rule->flags &= ~NL80211_RRF_AUTO_BW;
+
        freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz;
        if (freq_range->max_bandwidth_khz > freq_diff)
                freq_range->max_bandwidth_khz = freq_diff;
@@ -753,8 +755,6 @@ static int reg_rules_intersect(const struct ieee80211_regdomain *rd1,
        power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain,
                power_rule2->max_antenna_gain);
 
-       intersected_rule->flags = rule1->flags | rule2->flags;
-
        if (!is_valid_reg_rule(intersected_rule))
                return -EINVAL;
 
@@ -938,31 +938,42 @@ const char *reg_initiator_name(enum nl80211_reg_initiator initiator)
 EXPORT_SYMBOL(reg_initiator_name);
 
 #ifdef CONFIG_CFG80211_REG_DEBUG
-static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
+static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
+                                   struct ieee80211_channel *chan,
                                    const struct ieee80211_reg_rule *reg_rule)
 {
        const struct ieee80211_power_rule *power_rule;
        const struct ieee80211_freq_range *freq_range;
-       char max_antenna_gain[32];
+       char max_antenna_gain[32], bw[32];
 
        power_rule = &reg_rule->power_rule;
        freq_range = &reg_rule->freq_range;
 
        if (!power_rule->max_antenna_gain)
-               snprintf(max_antenna_gain, 32, "N/A");
+               snprintf(max_antenna_gain, sizeof(max_antenna_gain), "N/A");
        else
-               snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain);
+               snprintf(max_antenna_gain, sizeof(max_antenna_gain), "%d",
+                        power_rule->max_antenna_gain);
+
+       if (reg_rule->flags & NL80211_RRF_AUTO_BW)
+               snprintf(bw, sizeof(bw), "%d KHz, %d KHz AUTO",
+                        freq_range->max_bandwidth_khz,
+                        reg_get_max_bandwidth(regd, reg_rule));
+       else
+               snprintf(bw, sizeof(bw), "%d KHz",
+                        freq_range->max_bandwidth_khz);
 
        REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n",
                      chan->center_freq);
 
-       REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n",
+       REG_DBG_PRINT("%d KHz - %d KHz @ %s), (%s mBi, %d mBm)\n",
                      freq_range->start_freq_khz, freq_range->end_freq_khz,
-                     freq_range->max_bandwidth_khz, max_antenna_gain,
+                     bw, max_antenna_gain,
                      power_rule->max_eirp);
 }
 #else
-static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan,
+static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd,
+                                   struct ieee80211_channel *chan,
                                    const struct ieee80211_reg_rule *reg_rule)
 {
        return;
@@ -1022,17 +1033,16 @@ static void handle_channel(struct wiphy *wiphy,
                return;
        }
 
-       chan_reg_rule_print_dbg(chan, reg_rule);
+       regd = reg_get_regdomain(wiphy);
+       chan_reg_rule_print_dbg(regd, chan, reg_rule);
 
        power_rule = &reg_rule->power_rule;
        freq_range = &reg_rule->freq_range;
 
        max_bandwidth_khz = freq_range->max_bandwidth_khz;
        /* Check if auto calculation requested */
-       if (!max_bandwidth_khz) {
-               regd = reg_get_regdomain(wiphy);
+       if (reg_rule->flags & NL80211_RRF_AUTO_BW)
                max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
-       }
 
        if (max_bandwidth_khz < MHZ_TO_KHZ(40))
                bw_flags = IEEE80211_CHAN_NO_HT40;
@@ -1437,14 +1447,14 @@ static void handle_channel_custom(struct wiphy *wiphy,
                return;
        }
 
-       chan_reg_rule_print_dbg(chan, reg_rule);
+       chan_reg_rule_print_dbg(regd, chan, reg_rule);
 
        power_rule = &reg_rule->power_rule;
        freq_range = &reg_rule->freq_range;
 
        max_bandwidth_khz = freq_range->max_bandwidth_khz;
        /* Check if auto calculation requested */
-       if (!max_bandwidth_khz)
+       if (reg_rule->flags & NL80211_RRF_AUTO_BW)
                max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule);
 
        if (max_bandwidth_khz < MHZ_TO_KHZ(40))
@@ -2254,11 +2264,12 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
                freq_range = &reg_rule->freq_range;
                power_rule = &reg_rule->power_rule;
 
-               if (!freq_range->max_bandwidth_khz)
-                       snprintf(bw, 32, "%d KHz, AUTO",
+               if (reg_rule->flags & NL80211_RRF_AUTO_BW)
+                       snprintf(bw, sizeof(bw), "%d KHz, %d KHz AUTO",
+                                freq_range->max_bandwidth_khz,
                                 reg_get_max_bandwidth(rd, reg_rule));
                else
-                       snprintf(bw, 32, "%d KHz",
+                       snprintf(bw, sizeof(bw), "%d KHz",
                                 freq_range->max_bandwidth_khz);
 
                /*
index b528e31da2cfc07ccf5826ce02f3014a61b808e4..d1ed4aebbbb7dcc6dca3fccea4222e3eb7eb2fb0 100644 (file)
@@ -161,18 +161,25 @@ static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev,
                dev->bss_generation++;
 }
 
-void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev)
+void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev,
+                          bool send_message)
 {
        struct cfg80211_scan_request *request;
        struct wireless_dev *wdev;
+       struct sk_buff *msg;
 #ifdef CONFIG_CFG80211_WEXT
        union iwreq_data wrqu;
 #endif
 
        ASSERT_RTNL();
 
-       request = rdev->scan_req;
+       if (rdev->scan_msg) {
+               nl80211_send_scan_result(rdev, rdev->scan_msg);
+               rdev->scan_msg = NULL;
+               return;
+       }
 
+       request = rdev->scan_req;
        if (!request)
                return;
 
@@ -186,18 +193,16 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev)
        if (wdev->netdev)
                cfg80211_sme_scan_done(wdev->netdev);
 
-       if (request->aborted) {
-               nl80211_send_scan_aborted(rdev, wdev);
-       } else {
-               if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
-                       /* flush entries from previous scans */
-                       spin_lock_bh(&rdev->bss_lock);
-                       __cfg80211_bss_expire(rdev, request->scan_start);
-                       spin_unlock_bh(&rdev->bss_lock);
-               }
-               nl80211_send_scan_done(rdev, wdev);
+       if (!request->aborted &&
+           request->flags & NL80211_SCAN_FLAG_FLUSH) {
+               /* flush entries from previous scans */
+               spin_lock_bh(&rdev->bss_lock);
+               __cfg80211_bss_expire(rdev, request->scan_start);
+               spin_unlock_bh(&rdev->bss_lock);
        }
 
+       msg = nl80211_build_scan_msg(rdev, wdev, request->aborted);
+
 #ifdef CONFIG_CFG80211_WEXT
        if (wdev->netdev && !request->aborted) {
                memset(&wrqu, 0, sizeof(wrqu));
@@ -211,6 +216,11 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev)
 
        rdev->scan_req = NULL;
        kfree(request);
+
+       if (!send_message)
+               rdev->scan_msg = msg;
+       else
+               nl80211_send_scan_result(rdev, msg);
 }
 
 void __cfg80211_scan_done(struct work_struct *wk)
@@ -221,7 +231,7 @@ void __cfg80211_scan_done(struct work_struct *wk)
                            scan_done_wk);
 
        rtnl_lock();
-       ___cfg80211_scan_done(rdev);
+       ___cfg80211_scan_done(rdev, true);
        rtnl_unlock();
 }
 
@@ -1079,7 +1089,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,
        if (IS_ERR(rdev))
                return PTR_ERR(rdev);
 
-       if (rdev->scan_req) {
+       if (rdev->scan_req || rdev->scan_msg) {
                err = -EBUSY;
                goto out;
        }
@@ -1481,7 +1491,7 @@ int cfg80211_wext_giwscan(struct net_device *dev,
        if (IS_ERR(rdev))
                return PTR_ERR(rdev);
 
-       if (rdev->scan_req)
+       if (rdev->scan_req || rdev->scan_msg)
                return -EAGAIN;
 
        res = ieee80211_scan_results(rdev, info, extra, data->length);
index a6350911850890dc40fdcd6326345866ffc342a9..f04d4c32e96e144d37b49de5d1be69add8a55b2c 100644 (file)
@@ -67,7 +67,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
        ASSERT_RDEV_LOCK(rdev);
        ASSERT_WDEV_LOCK(wdev);
 
-       if (rdev->scan_req)
+       if (rdev->scan_req || rdev->scan_msg)
                return -EBUSY;
 
        if (wdev->conn->params.channel)
index 5eaeed59db07651de6305320187f127c9fdd8b65..aabccf13e07b6860ef92ddc637a7879a8f961aab 100644 (file)
@@ -1468,9 +1468,10 @@ TRACE_EVENT(rdev_sched_scan_start,
 TRACE_EVENT(rdev_tdls_mgmt,
        TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
                 u8 *peer, u8 action_code, u8 dialog_token,
-                u16 status_code, const u8 *buf, size_t len),
+                u16 status_code, u32 peer_capability,
+                const u8 *buf, size_t len),
        TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code,
-               buf, len),
+               peer_capability, buf, len),
        TP_STRUCT__entry(
                WIPHY_ENTRY
                NETDEV_ENTRY
@@ -1478,6 +1479,7 @@ TRACE_EVENT(rdev_tdls_mgmt,
                __field(u8, action_code)
                __field(u8, dialog_token)
                __field(u16, status_code)
+               __field(u32, peer_capability)
                __dynamic_array(u8, buf, len)
        ),
        TP_fast_assign(
@@ -1487,13 +1489,15 @@ TRACE_EVENT(rdev_tdls_mgmt,
                __entry->action_code = action_code;
                __entry->dialog_token = dialog_token;
                __entry->status_code = status_code;
+               __entry->peer_capability = peer_capability;
                memcpy(__get_dynamic_array(buf), buf, len);
        ),
        TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, "
-                 "dialog_token: %u, status_code: %u, buf: %#.2x ",
+                 "dialog_token: %u, status_code: %u, peer_capability: %u buf: %#.2x ",
                  WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer),
                  __entry->action_code, __entry->dialog_token,
-                 __entry->status_code, ((u8 *)__get_dynamic_array(buf))[0])
+                 __entry->status_code, __entry->peer_capability,
+                 ((u8 *)__get_dynamic_array(buf))[0])
 );
 
 TRACE_EVENT(rdev_dump_survey,
index 780b4546c9c74c133d2de69ddcc352ca0979b8fe..57b3ce7a6b9255df15b5064ac5fd177414a662cc 100644 (file)
@@ -1269,7 +1269,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        enum cfg80211_chan_mode chmode;
        int num_different_channels = 0;
        int total = 1;
-       bool radar_required = false;
        int i, j;
 
        ASSERT_RTNL();
@@ -1277,35 +1276,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
        if (WARN_ON(hweight32(radar_detect) > 1))
                return -EINVAL;
 
-       switch (iftype) {
-       case NL80211_IFTYPE_ADHOC:
-       case NL80211_IFTYPE_AP:
-       case NL80211_IFTYPE_AP_VLAN:
-       case NL80211_IFTYPE_MESH_POINT:
-       case NL80211_IFTYPE_P2P_GO:
-       case NL80211_IFTYPE_WDS:
-               /* if the interface could potentially choose a DFS channel,
-                * then mark DFS as required.
-                */
-               if (!chan) {
-                       if (chanmode != CHAN_MODE_UNDEFINED && radar_detect)
-                               radar_required = true;
-                       break;
-               }
-               radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR);
-               break;
-       case NL80211_IFTYPE_P2P_CLIENT:
-       case NL80211_IFTYPE_STATION:
-       case NL80211_IFTYPE_P2P_DEVICE:
-       case NL80211_IFTYPE_MONITOR:
-               break;
-       case NUM_NL80211_IFTYPES:
-       case NL80211_IFTYPE_UNSPECIFIED:
-       default:
-               return -EINVAL;
-       }
-
-       if (radar_required && !radar_detect)
+       if (WARN_ON(iftype >= NUM_NL80211_IFTYPES))
                return -EINVAL;
 
        /* Always allow software iftypes */