diff options
152 files changed, 6347 insertions, 3581 deletions
diff --git a/arch/alpha/include/asm/socket.h b/arch/alpha/include/asm/socket.h index 06edfefc3373..082355f159e6 100644 --- a/arch/alpha/include/asm/socket.h +++ b/arch/alpha/include/asm/socket.h @@ -69,6 +69,9 @@ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + /* O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. */ diff --git a/arch/arm/include/asm/socket.h b/arch/arm/include/asm/socket.h index 90ffd04b8e74..dec6f9afb3cf 100644 --- a/arch/arm/include/asm/socket.h +++ b/arch/arm/include/asm/socket.h @@ -62,4 +62,7 @@ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #endif /* _ASM_SOCKET_H */ diff --git a/arch/avr32/include/asm/socket.h b/arch/avr32/include/asm/socket.h index c8d1fae49476..247b88c760be 100644 --- a/arch/avr32/include/asm/socket.h +++ b/arch/avr32/include/asm/socket.h @@ -62,4 +62,7 @@ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #endif /* __ASM_AVR32_SOCKET_H */ diff --git a/arch/cris/include/asm/socket.h b/arch/cris/include/asm/socket.h index 1a4a61909ca8..e269264df7c4 100644 --- a/arch/cris/include/asm/socket.h +++ b/arch/cris/include/asm/socket.h @@ -64,6 +64,9 @@ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #endif /* _ASM_SOCKET_H */ diff --git a/arch/frv/include/asm/socket.h b/arch/frv/include/asm/socket.h index a6b26880c1ec..ce80fdadcce5 100644 --- a/arch/frv/include/asm/socket.h +++ b/arch/frv/include/asm/socket.h @@ -62,5 +62,8 @@ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #endif /* _ASM_SOCKET_H */ diff --git a/arch/h8300/include/asm/socket.h b/arch/h8300/include/asm/socket.h index 04c0f4596eb5..cf1daab6f27e 100644 --- a/arch/h8300/include/asm/socket.h +++ b/arch/h8300/include/asm/socket.h @@ -62,4 +62,7 @@ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #endif /* _ASM_SOCKET_H */ diff --git a/arch/ia64/include/asm/socket.h b/arch/ia64/include/asm/socket.h index 51427eaa51ba..4b03664e3fb5 100644 --- a/arch/ia64/include/asm/socket.h +++ b/arch/ia64/include/asm/socket.h @@ -71,4 +71,7 @@ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #endif /* _ASM_IA64_SOCKET_H */ diff --git a/arch/m32r/include/asm/socket.h b/arch/m32r/include/asm/socket.h index 469787c30098..e8b8c5bb053c 100644 --- a/arch/m32r/include/asm/socket.h +++ b/arch/m32r/include/asm/socket.h @@ -62,4 +62,7 @@ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #endif /* _ASM_M32R_SOCKET_H */ diff --git a/arch/m68k/include/asm/socket.h b/arch/m68k/include/asm/socket.h index 9bf49c87d954..d4708ce466e0 100644 --- a/arch/m68k/include/asm/socket.h +++ b/arch/m68k/include/asm/socket.h @@ -62,4 +62,7 @@ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #endif /* _ASM_SOCKET_H */ diff --git a/arch/mips/include/asm/socket.h b/arch/mips/include/asm/socket.h index 9de5190f2487..ad5c0a7a02a7 100644 --- a/arch/mips/include/asm/socket.h +++ b/arch/mips/include/asm/socket.h @@ -82,6 +82,9 @@ To add: #define SO_REUSEPORT 0x0200 /* Allow local address and port reuse. */ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #ifdef __KERNEL__ /** sock_type - Socket types diff --git a/arch/mn10300/include/asm/socket.h b/arch/mn10300/include/asm/socket.h index 4e60c4281288..876356d78522 100644 --- a/arch/mn10300/include/asm/socket.h +++ b/arch/mn10300/include/asm/socket.h @@ -62,4 +62,7 @@ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #endif /* _ASM_SOCKET_H */ diff --git a/arch/parisc/include/asm/socket.h b/arch/parisc/include/asm/socket.h index 225b7d6a1a0a..d28c51b61067 100644 --- a/arch/parisc/include/asm/socket.h +++ b/arch/parisc/include/asm/socket.h @@ -61,6 +61,9 @@ #define SO_RXQ_OVFL 0x4021 +#define SO_WIFI_STATUS 0x4022 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + /* O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. */ diff --git a/arch/powerpc/include/asm/socket.h b/arch/powerpc/include/asm/socket.h index 866f7606da68..2fc2af8fbf59 100644 --- a/arch/powerpc/include/asm/socket.h +++ b/arch/powerpc/include/asm/socket.h @@ -69,4 +69,7 @@ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/arch/s390/include/asm/socket.h b/arch/s390/include/asm/socket.h index fdff1e995c73..67b5c1b14b51 100644 --- a/arch/s390/include/asm/socket.h +++ b/arch/s390/include/asm/socket.h @@ -70,4 +70,7 @@ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #endif /* _ASM_SOCKET_H */ diff --git a/arch/sparc/include/asm/socket.h b/arch/sparc/include/asm/socket.h index 9d3fefcff2f5..8af1b64168b3 100644 --- a/arch/sparc/include/asm/socket.h +++ b/arch/sparc/include/asm/socket.h @@ -58,6 +58,9 @@ #define SO_RXQ_OVFL 0x0024 +#define SO_WIFI_STATUS 0x0025 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 #define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002 diff --git a/arch/xtensa/include/asm/socket.h b/arch/xtensa/include/asm/socket.h index cbdf2ffaacff..bb06968be227 100644 --- a/arch/xtensa/include/asm/socket.h +++ b/arch/xtensa/include/asm/socket.h @@ -73,4 +73,7 @@ #define SO_RXQ_OVFL 40 +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS + #endif /* _XTENSA_SOCKET_H */ diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 106beb194f3c..1622772f802d 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -30,6 +30,7 @@ #include <net/bluetooth/bluetooth.h> #define VERSION "1.0" +#define ATH3K_FIRMWARE "ath3k-1.fw" #define ATH3K_DNLOAD 0x01 #define ATH3K_GETSTATE 0x05 @@ -400,9 +401,15 @@ static int ath3k_probe(struct usb_interface *intf, return 0; } - if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) { - BT_ERR("Error loading firmware"); - return -EIO; + ret = request_firmware(&firmware, ATH3K_FIRMWARE, &udev->dev); + if (ret < 0) { + if (ret == -ENOENT) + BT_ERR("Firmware file \"%s\" not found", + ATH3K_FIRMWARE); + else + BT_ERR("Firmware file \"%s\" request failed (err=%d)", + ATH3K_FIRMWARE, ret); + return ret; } ret = ath3k_load_firmware(udev, firmware); @@ -441,4 +448,4 @@ MODULE_AUTHOR("Atheros Communications"); MODULE_DESCRIPTION("Atheros AR30xx firmware driver"); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("ath3k-1.fw"); +MODULE_FIRMWARE(ATH3K_FIRMWARE); diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 61b591470a90..a936763b8c3d 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -751,9 +751,7 @@ static void bfusb_disconnect(struct usb_interface *intf) bfusb_close(hdev); - if (hci_unregister_dev(hdev) < 0) - BT_ERR("Can't unregister HCI device %s", hdev->name); - + hci_unregister_dev(hdev); hci_free_dev(hdev); } diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index aed1904ea67b..c6a0c6103743 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -844,9 +844,7 @@ static int bluecard_close(bluecard_info_t *info) /* Turn FPGA off */ outb(0x80, iobase + 0x30); - if (hci_unregister_dev(hdev) < 0) - BT_ERR("Can't unregister HCI device %s", hdev->name); - + hci_unregister_dev(hdev); hci_free_dev(hdev); return 0; diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 4fc01949d399..0c97e5d514b6 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -636,9 +636,7 @@ static int bt3c_close(bt3c_info_t *info) bt3c_hci_close(hdev); - if (hci_unregister_dev(hdev) < 0) - BT_ERR("Can't unregister HCI device %s", hdev->name); - + hci_unregister_dev(hdev); hci_free_dev(hdev); return 0; diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 526b61807d94..200b3a2877d6 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -565,9 +565,7 @@ static int btuart_close(btuart_info_t *info) spin_unlock_irqrestore(&(info->lock), flags); - if (hci_unregister_dev(hdev) < 0) - BT_ERR("Can't unregister HCI device %s", hdev->name); - + hci_unregister_dev(hdev); hci_free_dev(hdev); return 0; diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index eabc437ce500..ea5ad1cbbd3d 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -315,7 +315,8 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags) err = usb_submit_urb(urb, mem_flags); if (err < 0) { - BT_ERR("%s urb %p submission failed (%d)", + if (err != -EPERM && err != -ENODEV) + BT_ERR("%s urb %p submission failed (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } @@ -400,7 +401,8 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags) err = usb_submit_urb(urb, mem_flags); if (err < 0) { - BT_ERR("%s urb %p submission failed (%d)", + if (err != -EPERM && err != -ENODEV) + BT_ERR("%s urb %p submission failed (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } @@ -523,7 +525,8 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags) err = usb_submit_urb(urb, mem_flags); if (err < 0) { - BT_ERR("%s urb %p submission failed (%d)", + if (err != -EPERM && err != -ENODEV) + BT_ERR("%s urb %p submission failed (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } @@ -727,6 +730,9 @@ static int btusb_send_frame(struct sk_buff *skb) usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len, btusb_tx_complete, skb); + if (skb->priority >= HCI_PRIO_MAX - 1) + urb->transfer_flags = URB_ISO_ASAP; + hdev->stat.acl_tx++; break; @@ -770,7 +776,9 @@ skip_waking: err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { - BT_ERR("%s urb %p submission failed", hdev->name, urb); + if (err != -EPERM && err != -ENODEV) + BT_ERR("%s urb %p submission failed (%d)", + hdev->name, urb, -err); kfree(urb->setup_packet); usb_unanchor_urb(urb); } else { diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 5e4c2de9fc3f..969bb22e493f 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -551,9 +551,7 @@ static int dtl1_close(dtl1_info_t *info) spin_unlock_irqrestore(&(info->lock), flags); - if (hci_unregister_dev(hdev) < 0) - BT_ERR("Can't unregister HCI device %s", hdev->name); - + hci_unregister_dev(hdev); hci_free_dev(hdev); return 0; diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 67c180c2c1e0..2ed6ab1c6e1b 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -41,6 +41,8 @@ #define VERSION "1.3" +static bool amp; + struct vhci_data { struct hci_dev *hdev; @@ -239,6 +241,9 @@ static int vhci_open(struct inode *inode, struct file *file) hdev->bus = HCI_VIRTUAL; hdev->driver_data = data; + if (amp) + hdev->dev_type = HCI_AMP; + hdev->open = vhci_open_dev; hdev->close = vhci_close_dev; hdev->flush = vhci_flush; @@ -264,10 +269,7 @@ static int vhci_release(struct inode *inode, struct file *file) struct vhci_data *data = file->private_data; struct hci_dev *hdev = data->hdev; - if (hci_unregister_dev(hdev) < 0) { - BT_ERR("Can't unregister HCI device %s", hdev->name); - } - + hci_unregister_dev(hdev); hci_free_dev(hdev); file->private_data = NULL; @@ -306,6 +308,9 @@ static void __exit vhci_exit(void) module_init(vhci_init); module_exit(vhci_exit); +module_param(amp, bool, 0644); +MODULE_PARM_DESC(amp, "Create AMP controller device"); + MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION); MODULE_VERSION(VERSION); diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 0a304b060b6c..c1c0678b1fb6 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -58,6 +58,6 @@ obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx/ obj-$(CONFIG_IWM) += iwmc3200wifi/ obj-$(CONFIG_MWIFIEX) += mwifiex/ -obj-$(CONFIG_BRCMFMAC) += brcm80211/ -obj-$(CONFIG_BRCMUMAC) += brcm80211/ -obj-$(CONFIG_BRCMSMAC) += brcm80211/ + +obj-$(CONFIG_BRCMFMAC) += brcm80211/ +obj-$(CONFIG_BRCMSMAC) += brcm80211/ diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index 908fdbc3e0ee..fe4bf4da255f 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -240,6 +240,7 @@ enum ATH_DEBUG { ATH_DBG_BTCOEX = 0x00002000, ATH_DBG_WMI = 0x00004000, ATH_DBG_BSTUCK = 0x00008000, + ATH_DBG_MCI = 0x00010000, ATH_DBG_ANY = 0xffffffff }; diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 3aff36bad5d3..daf444bf8d48 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1732,7 +1732,8 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, u64 *cookie) + const u8 *buf, size_t len, bool no_cck, + bool dont_wait_for_ack, u64 *cookie) { struct ath6kl *ar = ath6kl_priv(dev); u32 id; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index c1d2366704b5..81e0031012ca 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1548,7 +1548,8 @@ static int ath6kl_init(struct net_device *dev) ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; - ar->wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; + ar->wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM | + WIPHY_FLAG_HAVE_AP_SME; status = ath6kl_target_config_wlan_params(ar); if (!status) diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 36ed3c46fec6..49d3f25f509d 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -4,6 +4,7 @@ ath9k-y += beacon.o \ main.o \ recv.o \ xmit.o \ + mci.o \ ath9k-$(CONFIG_ATH9K_RATE_CONTROL) += rc.o ath9k-$(CONFIG_ATH9K_PCI) += pci.o diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index fe96997921d3..04b060af5087 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -198,12 +198,14 @@ static void ar9003_hw_spur_mitigate_mrc_cck(struct ath_hw *ah, synth_freq = chan->channel; } } else { - range = 10; + range = AR_SREV_9462(ah) ? 5 : 10; max_spur_cnts = 4; synth_freq = chan->channel; } for (i = 0; i < max_spur_cnts; i++) { + if (AR_SREV_9462(ah) && (i == 0 || i == 3)) + continue; negative = 0; if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah)) cur_bb_spur = FBIN2FREQ(spur_fbin_ptr[i], diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h index 9c51b395b4ff..259a6f312afb 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h @@ -43,16 +43,16 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, - {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, - {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, + {0x00009824, 0x5ac640de, 0x5ac640d0, 0x5ac640d0, 0x5ac640de}, + {0x00009828, 0x0796be89, 0x0696b081, 0x0696b881, 0x0796be89}, {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, - {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e}, - {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3039605e, 0x33795d5e}, + {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x92c84d2e}, + {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, @@ -688,8 +688,8 @@ static const u32 ar9462_2p0_mac_postamble_emulation[][5] = { static const u32 ar9462_2p0_radio_postamble_sys3ant[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24645808, 0x24645808}, - {0x00016140, 0x10804008, 0x10804008, 0x90804008, 0x90804008}, - {0x00016540, 0x10804008, 0x10804008, 0x90804008, 0x90804008}, + {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, + {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, }; static const u32 ar9462_2p0_baseband_postamble_emulation[][5] = { @@ -717,8 +717,8 @@ static const u32 ar9462_2p0_baseband_postamble_emulation[][5] = { static const u32 ar9462_2p0_radio_postamble_sys2ant[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24645808, 0x24645808}, - {0x00016140, 0x10804008, 0x10804008, 0x90804008, 0x90804008}, - {0x00016540, 0x10804008, 0x10804008, 0x90804008, 0x90804008}, + {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, + {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, }; static const u32 ar9462_common_wo_xlna_rx_gain_table_2p0[][2] = { @@ -1059,7 +1059,7 @@ static const u32 ar9462_modes_low_ob_db_tx_gain_table_2p0[][5] = { static const u32 ar9462_2p0_soc_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00007010, 0x00002233, 0x00002233, 0x00002233, 0x00002233}, + {0x00007010, 0x00000033, 0x00000033, 0x00000033, 0x00000033}, }; static const u32 ar9462_2p0_baseband_core[][2] = { @@ -1257,8 +1257,8 @@ static const u32 ar9462_modes_high_ob_db_tx_gain_table_2p0[][5] = { {0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660}, {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861}, {0x0000a548, 0x53025eb2, 0x53025eb2, 0x3e001a81, 0x3e001a81}, - {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83}, - {0x0000a550, 0x5f025ef6, 0x5f025ef6, 0x44001c84, 0x44001c84}, + {0x0000a54c, 0x59025eb6, 0x59025eb6, 0x42001a83, 0x42001a83}, + {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x44001c84, 0x44001c84}, {0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3}, {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5}, {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9}, @@ -1850,8 +1850,8 @@ static const u32 ar9462_modes_green_ob_db_tx_gain_table_2p0[][5] = { {0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660}, {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861}, {0x0000a548, 0x53025eb2, 0x53025eb2, 0x3e001a81, 0x3e001a81}, - {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83}, - {0x0000a550, 0x5f025ef6, 0x5f025ef6, 0x44001c84, 0x44001c84}, + {0x0000a54c, 0x59025eb6, 0x59025eb6, 0x42001a83, 0x42001a83}, + {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x44001c84, 0x44001c84}, {0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3}, {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5}, {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9}, diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 1c269f50822b..93b45b4b3033 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -25,6 +25,7 @@ #include "debug.h" #include "common.h" +#include "mci.h" /* * Header for the ath9k.ko driver core *only* -- hw code nor any other driver @@ -252,6 +253,7 @@ struct ath_node { #ifdef CONFIG_ATH9K_DEBUGFS struct list_head list; /* for sc->nodes */ struct ieee80211_sta *sta; /* station struct we're part of */ + struct ieee80211_vif *vif; /* interface with which we're associated */ #endif struct ath_atx_tid tid[WME_NUM_TID]; struct ath_atx_ac ac[WME_NUM_AC]; @@ -443,7 +445,9 @@ struct ath_btcoex { u32 btcoex_no_stomp; /* in usec */ u32 btcoex_period; /* in usec */ u32 btscan_no_stomp; /* in usec */ + u32 duty_cycle; struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */ + struct ath_mci_profile mci; }; int ath_init_btcoex_timer(struct ath_softc *sc); diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 327aa28f6030..8e7e57ccbe9a 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -708,24 +708,29 @@ static ssize_t read_file_stations(struct file *file, char __user *user_buf, len += snprintf(buf + len, size - len, "Stations:\n" - " tid: addr sched paused buf_q-empty an ac\n" + " tid: addr sched paused buf_q-empty an ac baw\n" " ac: addr sched tid_q-empty txq\n"); spin_lock(&sc->nodes_lock); list_for_each_entry(an, &sc->nodes, list) { + unsigned short ma = an->maxampdu; + if (ma == 0) + ma = 65535; /* see ath_lookup_rate */ len += snprintf(buf + len, size - len, - "%pM\n", an->sta->addr); + "iface: %pM sta: %pM max-ampdu: %hu mpdu-density: %uus\n", + an->vif->addr, an->sta->addr, ma, + (unsigned int)(an->mpdudensity)); if (len >= size) goto done; for (q = 0; q < WME_NUM_TID; q++) { struct ath_atx_tid *tid = &(an->tid[q]); len += snprintf(buf + len, size - len, - " tid: %p %s %s %i %p %p\n", + " tid: %p %s %s %i %p %p %hu\n", tid, tid->sched ? "sched" : "idle", tid->paused ? "paused" : "running", skb_queue_empty(&tid->buf_q), - tid->an, tid->ac); + tid->an, tid->ac, tid->baw_size); if (len >= size) goto done; } diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 655576c8fdab..2c279dcaf4ba 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -189,8 +189,8 @@ static void ath_btcoex_period_timer(unsigned long data) bool is_btscan; ath9k_ps_wakeup(sc); - ath_detect_bt_priority(sc); - + if (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) + ath_detect_bt_priority(sc); is_btscan = sc->sc_flags & SC_OP_BT_SCAN; spin_lock_bh(&btcoex->btcoex_lock); @@ -212,8 +212,9 @@ static void ath_btcoex_period_timer(unsigned long data) } ath9k_ps_restore(sc); + timer_period = btcoex->btcoex_period / 1000; mod_timer(&btcoex->period_timer, jiffies + - msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD)); + msecs_to_jiffies(timer_period)); } /* diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index b479160dc262..27471f80d8b2 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2334,7 +2334,7 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) ah->enabled_cals |= TX_IQ_ON_AGC_CAL; } if (AR_SREV_9462(ah)) - pCap->hw_caps |= ATH9K_HW_CAP_RTT; + pCap->hw_caps |= ATH9K_HW_CAP_RTT | ATH9K_HW_CAP_MCI; return 0; } @@ -2582,7 +2582,7 @@ void ath9k_hw_set_txpowerlimit(struct ath_hw *ah, u32 limit, bool test) struct ath9k_channel *chan = ah->curchan; struct ieee80211_channel *channel = chan->chan; - reg->power_limit = min_t(int, limit, MAX_RATE_POWER); + reg->power_limit = min_t(u32, limit, MAX_RATE_POWER); if (test) channel->max_power = MAX_RATE_POWER / 2; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index f389b3c93cf3..33e8f2f9d425 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -203,6 +203,7 @@ enum ath9k_hw_caps { ATH9K_HW_CAP_5GHZ = BIT(14), ATH9K_HW_CAP_APM = BIT(15), ATH9K_HW_CAP_RTT = BIT(16), + ATH9K_HW_CAP_MCI = BIT(17), }; struct ath9k_hw_capabilities { @@ -419,6 +420,16 @@ enum ath9k_rx_qtype { ATH9K_RX_QUEUE_MAX, }; +enum ath_mci_gpm_coex_profile_type { + MCI_GPM_COEX_PROFILE_UNKNOWN, + MCI_GPM_COEX_PROFILE_RFCOMM, + MCI_GPM_COEX_PROFILE_A2DP, + MCI_GPM_COEX_PROFILE_HID, + MCI_GPM_COEX_PROFILE_BNEP, + MCI_GPM_COEX_PROFILE_VOICE, + MCI_GPM_COEX_PROFILE_MAX +}; + struct ath9k_beacon_state { u32 bs_nexttbtt; u32 bs_nextdtim; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index af1b32549531..5cb0599b01c2 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -423,6 +423,8 @@ static int ath9k_init_btcoex(struct ath_softc *sc) txq = sc->tx.txq_map[WME_AC_BE]; ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum); sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; + sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE; + INIT_LIST_HEAD(&sc->btcoex.mci.info); break; default: WARN_ON(1); @@ -694,6 +696,7 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; hw->queues = 4; hw->max_rates = 4; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 93fbe6f40898..e43c41cff25b 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -630,7 +630,8 @@ set_timer: } } -static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta) +static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, + struct ieee80211_vif *vif) { struct ath_node *an; an = (struct ath_node *)sta->drv_priv; @@ -640,6 +641,7 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta) list_add(&an->list, &sc->nodes); spin_unlock(&sc->nodes_lock); an->sta = sta; + an->vif = vif; #endif if (sc->sc_flags & SC_OP_TXAGGR) { ath_tx_node_init(sc, an); @@ -1133,8 +1135,9 @@ static int ath9k_start(struct ieee80211_hw *hw) if ((ah->btcoex_hw.scheme != ATH_BTCOEX_CFG_NONE) && !ah->btcoex_hw.enabled) { - ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, - AR_STOMP_LOW_WLAN_WGHT); + if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI)) + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_LOW_WLAN_WGHT); ath9k_hw_btcoex_enable(ah); if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) @@ -1237,6 +1240,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath9k_hw_btcoex_disable(ah); if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) ath9k_btcoex_timer_pause(sc); + ath_mci_flush_profile(&sc->btcoex.mci); } spin_lock_bh(&sc->sc_pcu_lock); @@ -1798,7 +1802,7 @@ static int ath9k_sta_add(struct ieee80211_hw *hw, struct ath_node *an = (struct ath_node *) sta->drv_priv; struct ieee80211_key_conf ps_key = { }; - ath_node_attach(sc, sta); + ath_node_attach(sc, sta, vif); if (vif->type != NL80211_IFTYPE_AP && vif->type != NL80211_IFTYPE_AP_VLAN) diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c new file mode 100644 index 000000000000..0fbb141bc302 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ath9k.h" +#include "mci.h" + +u8 ath_mci_duty_cycle[] = { 0, 50, 60, 70, 80, 85, 90, 95, 98 }; + +static struct ath_mci_profile_info* +ath_mci_find_profile(struct ath_mci_profile *mci, + struct ath_mci_profile_info *info) +{ + struct ath_mci_profile_info *entry; + + list_for_each_entry(entry, &mci->info, list) { + if (entry->conn_handle == info->conn_handle) + break; + } + return entry; +} + +static bool ath_mci_add_profile(struct ath_common *common, + struct ath_mci_profile *mci, + struct ath_mci_profile_info *info) +{ + struct ath_mci_profile_info *entry; + + if ((mci->num_sco == ATH_MCI_MAX_SCO_PROFILE) && + (info->type == MCI_GPM_COEX_PROFILE_VOICE)) { + ath_dbg(common, ATH_DBG_MCI, + "Too many SCO profile, failed to add new profile\n"); + return false; + } + + if (((NUM_PROF(mci) - mci->num_sco) == ATH_MCI_MAX_ACL_PROFILE) && + (info->type != MCI_GPM_COEX_PROFILE_VOICE)) { + ath_dbg(common, ATH_DBG_MCI, + "Too many ACL profile, failed to add new profile\n"); + return false; + } + + entry = ath_mci_find_profile(mci, info); + + if (entry) + memcpy(entry, info, 10); + else { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return false; + + memcpy(entry, info, 10); + INC_PROF(mci, info); + list_add_tail(&info->list, &mci->info); + } + return true; +} + +static void ath_mci_del_profile(struct ath_common *common, + struct ath_mci_profile *mci, + struct ath_mci_profile_info *info) +{ + struct ath_mci_profile_info *entry; + + entry = ath_mci_find_profile(mci, info); + + if (!entry) { + ath_dbg(common, ATH_DBG_MCI, + "Profile to be deleted not found\n"); + return; + } + DEC_PROF(mci, entry); + list_del(&entry->list); + kfree(entry); +} + +void ath_mci_flush_profile(struct ath_mci_profile *mci) +{ + struct ath_mci_profile_info *info, *tinfo; + + list_for_each_entry_safe(info, tinfo, &mci->info, list) { + list_del(&info->list); + DEC_PROF(mci, info); + kfree(info); + } + mci->aggr_limit = 0; +} + +static void ath_mci_adjust_aggr_limit(struct ath_btcoex *btcoex) +{ + struct ath_mci_profile *mci = &btcoex->mci; + u32 wlan_airtime = btcoex->btcoex_period * + (100 - btcoex->duty_cycle) / 100; + + /* + * Scale: wlan_airtime is in ms, aggr_limit is in 0.25 ms. + * When wlan_airtime is less than 4ms, aggregation limit has to be + * adjusted half of wlan_airtime to ensure that the aggregation can fit + * without collision with BT traffic. + */ + if ((wlan_airtime <= 4) && + (!mci->aggr_limit || (mci->aggr_limit > (2 * wlan_airtime)))) + mci->aggr_limit = 2 * wlan_airtime; +} + +static void ath_mci_update_scheme(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_mci_profile *mci = &btcoex->mci; + struct ath_mci_profile_info *info; + u32 num_profile = NUM_PROF(mci); + + if (num_profile == 1) { + info = list_first_entry(&mci->info, + struct ath_mci_profile_info, + list); + if (mci->num_sco && info->T == 12) { + mci->aggr_limit = 8; + ath_dbg(common, ATH_DBG_MCI, + "Single SCO, aggregation limit 2 ms\n"); + } else if ((info->type == MCI_GPM_COEX_PROFILE_BNEP) && + !info->master) { + btcoex->btcoex_period = 60; + ath_dbg(common, ATH_DBG_MCI, + "Single slave PAN/FTP, bt period 60 ms\n"); + } else if ((info->type == MCI_GPM_COEX_PROFILE_HID) && + (info->T > 0 && info->T < 50) && + (info->A > 1 || info->W > 1)) { + btcoex->duty_cycle = 30; + mci->aggr_limit = 8; + ath_dbg(common, ATH_DBG_MCI, + "Multiple attempt/timeout single HID " + "aggregation limit 2 ms dutycycle 30%%\n"); + } + } else if ((num_profile == 2) && (mci->num_hid == 2)) { + btcoex->duty_cycle = 30; + mci->aggr_limit = 8; + ath_dbg(common, ATH_DBG_MCI, + "Two HIDs aggregation limit 2 ms dutycycle 30%%\n"); + } else if (num_profile > 3) { + mci->aggr_limit = 6; + ath_dbg(common, ATH_DBG_MCI, + "Three or more profiles aggregation limit 1.5 ms\n"); + } + + if (IS_CHAN_2GHZ(sc->sc_ah->curchan)) { + if (IS_CHAN_HT(sc->sc_ah->curchan)) + ath_mci_adjust_aggr_limit(btcoex); + else + btcoex->btcoex_period >>= 1; + } + + ath9k_hw_btcoex_disable(sc->sc_ah); + ath9k_btcoex_timer_pause(sc); + + if (IS_CHAN_5GHZ(sc->sc_ah->curchan)) + return; + + btcoex->duty_cycle += (mci->num_bdr ? ATH_MCI_MAX_DUTY_CYCLE : 0); + if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE) + btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE; + + btcoex->btcoex_period *= 1000; + btcoex->btcoex_no_stomp = btcoex->btcoex_period * + (100 - btcoex->duty_cycle) / 100; + + ath9k_hw_btcoex_enable(sc->sc_ah); + ath9k_btcoex_timer_resume(sc); +} + +void ath_mci_process_profile(struct ath_softc *sc, + struct ath_mci_profile_info *info) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_mci_profile *mci = &btcoex->mci; + + if (info->start) { + if (!ath_mci_add_profile(common, mci, info)) + return; + } else + ath_mci_del_profile(common, mci, info); + + btcoex->btcoex_period = ATH_MCI_DEF_BT_PERIOD; + mci->aggr_limit = mci->num_sco ? 6 : 0; + if (NUM_PROF(mci)) { + btcoex->bt_stomp_type = ATH_BTCOEX_STOMP_LOW; + btcoex->duty_cycle = ath_mci_duty_cycle[NUM_PROF(mci)]; + } else { + btcoex->bt_stomp_type = mci->num_mgmt ? ATH_BTCOEX_STOMP_ALL : + ATH_BTCOEX_STOMP_LOW; + btcoex->duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE; + } + + ath_mci_update_scheme(sc); +} + +void ath_mci_process_status(struct ath_softc *sc, + struct ath_mci_profile_status *status) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_mci_profile *mci = &btcoex->mci; + struct ath_mci_profile_info info; + int i = 0, old_num_mgmt = mci->num_mgmt; + + /* Link status type are not handled */ + if (status->is_link) { + ath_dbg(common, ATH_DBG_MCI, + "Skip link type status update\n"); + return; + } + + memset(&info, 0, sizeof(struct ath_mci_profile_info)); + + info.conn_handle = status->conn_handle; + if (ath_mci_find_profile(mci, &info)) { + ath_dbg(common, ATH_DBG_MCI, + "Skip non link state update for existing profile %d\n", + status->conn_handle); + return; + } + if (status->conn_handle >= ATH_MCI_MAX_PROFILE) { + ath_dbg(common, ATH_DBG_MCI, + "Ignore too many non-link update\n"); + return; + } + if (status->is_critical) + __set_bit(status->conn_handle, mci->status); + else + __clear_bit(status->conn_handle, mci->status); + + mci->num_mgmt = 0; + do { + if (test_bit(i, mci->status)) + mci->num_mgmt++; + } while (++i < ATH_MCI_MAX_PROFILE); + + if (old_num_mgmt != mci->num_mgmt) + ath_mci_update_scheme(sc); +} diff --git a/drivers/net/wireless/ath/ath9k/mci.h b/drivers/net/wireless/ath/ath9k/mci.h new file mode 100644 index 000000000000..9590c61822d1 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/mci.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MCI_H +#define MCI_H + +#define ATH_MCI_DEF_BT_PERIOD 40 +#define ATH_MCI_BDR_DUTY_CYCLE 20 +#define ATH_MCI_MAX_DUTY_CYCLE 90 + +#define ATH_MCI_DEF_AGGR_LIMIT 6 /* in 0.24 ms */ +#define ATH_MCI_MAX_ACL_PROFILE 7 +#define ATH_MCI_MAX_SCO_PROFILE 1 +#define ATH_MCI_MAX_PROFILE (ATH_MCI_MAX_ACL_PROFILE +\ + ATH_MCI_MAX_SCO_PROFILE) + +#define INC_PROF(_mci, _info) do { \ + switch (_info->type) { \ + case MCI_GPM_COEX_PROFILE_RFCOMM:\ + _mci->num_other_acl++; \ + break; \ + case MCI_GPM_COEX_PROFILE_A2DP: \ + _mci->num_a2dp++; \ + if (!_info->edr) \ + _mci->num_bdr++; \ + break; \ + case MCI_GPM_COEX_PROFILE_HID: \ + _mci->num_hid++; \ + break; \ + case MCI_GPM_COEX_PROFILE_BNEP: \ + _mci->num_pan++; \ + break; \ + case MCI_GPM_COEX_PROFILE_VOICE: \ + _mci->num_sco++; \ + break; \ + default: \ + break; \ + } \ + } while (0) + +#define DEC_PROF(_mci, _info) do { \ + switch (_info->type) { \ + case MCI_GPM_COEX_PROFILE_RFCOMM:\ + _mci->num_other_acl--; \ + break; \ + case MCI_GPM_COEX_PROFILE_A2DP: \ + _mci->num_a2dp--; \ + if (!_info->edr) \ + _mci->num_bdr--; \ + break; \ + case MCI_GPM_COEX_PROFILE_HID: \ + _mci->num_hid--; \ + break; \ + case MCI_GPM_COEX_PROFILE_BNEP: \ + _mci->num_pan--; \ + break; \ + case MCI_GPM_COEX_PROFILE_VOICE: \ + _mci->num_sco--; \ + break; \ + default: \ + break; \ + } \ + } while (0) + +#define NUM_PROF(_mci) (_mci->num_other_acl + _mci->num_a2dp + \ + _mci->num_hid + _mci->num_pan + _mci->num_sco) + +struct ath_mci_profile_info { + u8 type; + u8 conn_handle; + bool start; + bool master; + bool edr; + u8 voice_type; + u16 T; /* Voice: Tvoice, HID: Tsniff, in slots */ + u8 W; /* Voice: Wvoice, HID: Sniff timeout, in slots */ + u8 A; /* HID: Sniff attempt, in slots */ + struct list_head list; +}; + +struct ath_mci_profile_status { + bool is_critical; + bool is_link; + u8 conn_handle; +}; + +struct ath_mci_profile { + struct list_head info; + DECLARE_BITMAP(status, ATH_MCI_MAX_PROFILE); + u16 aggr_limit; + u8 num_mgmt; + u8 num_sco; + u8 num_a2dp; + u8 num_hid; + u8 num_pan; + u8 num_other_acl; + u8 num_bdr; +}; + +void ath_mci_flush_profile(struct ath_mci_profile *mci); +void ath_mci_process_profile(struct ath_softc *sc, + struct ath_mci_profile_info *info); +void ath_mci_process_status(struct ath_softc *sc, + struct ath_mci_profile_status *status); +#endif diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 03b0a651a591..55d077e7135d 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -601,6 +601,7 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, struct sk_buff *skb; struct ieee80211_tx_info *tx_info; struct ieee80211_tx_rate *rates; + struct ath_mci_profile *mci = &sc->btcoex.mci; u32 max_4ms_framelen, frmlen; u16 aggr_limit, legacy = 0; int i; @@ -645,7 +646,9 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, if (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE || legacy) return 0; - if (sc->sc_flags & SC_OP_BT_PRIORITY_DETECTED) + if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && mci->aggr_limit) + aggr_limit = (max_4ms_framelen * mci->aggr_limit) >> 4; + else if (sc->sc_flags & SC_OP_BT_PRIORITY_DETECTED) aggr_limit = min((max_4ms_framelen * 3) / 8, (u32)ATH_AMPDU_LIMIT_MAX); else diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 028310f263c8..f1be57f0f5bb 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -253,6 +253,8 @@ ath_reg_apply_active_scan_flags(struct wiphy *wiphy, int r; sband = wiphy->bands[IEEE80211_BAND_2GHZ]; + if (!sband) + return; /* * If no country IE has been received always enable active scan diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 58ea0e5fabfd..5f77cbe0b6aa 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -175,6 +175,7 @@ void b43_generate_plcp_hdr(struct b43_plcp_hdr4 *plcp, } } +/* TODO: verify if needed for SSLPN or LCN */ static u16 b43_generate_tx_phy_ctl1(struct b43_wldev *dev, u8 bitrate) { const struct b43_phy *phy = &dev->phy; @@ -256,6 +257,9 @@ int b43_generate_txhdr(struct b43_wldev *dev, unsigned int plcp_fragment_len; u32 mac_ctl = 0; u16 phy_ctl = 0; + bool fill_phy_ctl1 = (phy->type == B43_PHYTYPE_LP || + phy->type == B43_PHYTYPE_N || + phy->type == B43_PHYTYPE_HT); u8 extra_ft = 0; struct ieee80211_rate *txrate; struct ieee80211_tx_rate *rates; @@ -531,7 +535,7 @@ int b43_generate_txhdr(struct b43_wldev *dev, extra_ft |= B43_TXH_EFT_RTSFB_CCK; if (rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS && - phy->type == B43_PHYTYPE_N) { + fill_phy_ctl1) { txhdr->phy_ctl1_rts = cpu_to_le16( b43_generate_tx_phy_ctl1(dev, rts_rate)); txhdr->phy_ctl1_rts_fb = cpu_to_le16( @@ -552,7 +556,7 @@ int b43_generate_txhdr(struct b43_wldev *dev, break; } - if (phy->type == B43_PHYTYPE_N) { + if (fill_phy_ctl1) { txhdr->phy_ctl1 = cpu_to_le16(b43_generate_tx_phy_ctl1(dev, rate)); txhdr->phy_ctl1_fb = @@ -736,7 +740,14 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) /* Link quality statistics */ switch (chanstat & B43_RX_CHAN_PHYTYPE) { + case B43_PHYTYPE_HT: + /* TODO: is max the right choice? */ + status.signal = max_t(__s8, + max(rxhdr->phy_ht_power0, rxhdr->phy_ht_power1), + rxhdr->phy_ht_power2); + break; case B43_PHYTYPE_N: + /* Broadcom has code for min and avg, but always uses max */ if (rxhdr->power0 == 16 || rxhdr->power0 == 32) status.signal = max(rxhdr->power1, rxhdr->power2); else diff --git a/drivers/net/wireless/b43/xmit.h b/drivers/net/wireless/b43/xmit.h index 16c514d54afa..98d90747836a 100644 --- a/drivers/net/wireless/b43/xmit.h +++ b/drivers/net/wireless/b43/xmit.h @@ -249,6 +249,12 @@ struct b43_rxhdr_fw4 { } __packed; } __packed; union { + /* HT-PHY */ + struct { + PAD_BYTES(1); + __s8 phy_ht_power0; + } __packed; + /* RSSI for N-PHYs */ struct { __s8 power2; @@ -257,7 +263,15 @@ struct b43_rxhdr_fw4 { __le16 phy_status2; /* PHY RX Status 2 */ } __packed; - __le16 phy_status3; /* PHY RX Status 3 */ + union { + /* HT-PHY */ + struct { + __s8 phy_ht_power1; + __s8 phy_ht_power2; + } __packed; + + __le16 phy_status3; /* PHY RX Status 3 */ + } __packed; union { /* Tested with 598.314, 644.1001 and 666.2 */ struct { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index b44e3094588a..d58aa1b0a932 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -26,7 +26,8 @@ DHDOFILES = \ dhd_sdio.o \ dhd_linux.o \ bcmsdh.o \ - bcmsdh_sdmmc.o + bcmsdh_sdmmc.o \ + sdio_chip.o obj-$(CONFIG_BRCMFMAC) += brcmfmac.o brcmfmac-objs += $(DHDOFILES) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmchip.h b/drivers/net/wireless/brcm80211/brcmfmac/bcmchip.h index d7d3afd5a10f..cecb5e5f412b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmchip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmchip.h @@ -18,13 +18,6 @@ #define _bcmchip_h_ /* bcm4329 */ -/* SDIO device core, ID 0x829 */ -#define BCM4329_CORE_BUS_BASE 0x18011000 -/* internal memory core, ID 0x80e */ -#define BCM4329_CORE_SOCRAM_BASE 0x18003000 -/* ARM Cortex M3 core, ID 0x82a */ -#define BCM4329_CORE_ARM_BASE 0x18002000 -#define BCM4329_RAMSIZE 0x48000 /* firmware name */ #define BCM4329_FW_NAME "brcm/bcm4329-fullmac-4.bin" #define BCM4329_NV_NAME "brcm/bcm4329-fullmac-4.txt" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 4645766b4070..6da519e7578f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -87,7 +87,7 @@ #define TOE_TX_CSUM_OL 0x00000001 #define TOE_RX_CSUM_OL 0x00000002 -#define BRCMF_BSS_INFO_VERSION 108 /* current ver of brcmf_bss_info struct */ +#define BRCMF_BSS_INFO_VERSION 108 /* curr ver of brcmf_bss_info_le struct */ /* size of brcmf_scan_params not including variable length array */ #define BRCMF_SCAN_PARAMS_FIXED_SIZE 64 @@ -122,8 +122,6 @@ /* For supporting multiple interfaces */ #define BRCMF_MAX_IFS 16 -#define BRCMF_DEL_IF -0xe -#define BRCMF_BAD_IF -0xf #define DOT11_BSSTYPE_ANY 2 #define DOT11_MAX_DEFAULT_KEYS 4 @@ -365,7 +363,7 @@ struct brcmf_pkt_filter_enable_le { * Applications MUST CHECK ie_offset field and length field to access IEs and * next bss_info structure in a vector (in struct brcmf_scan_results) */ -struct brcmf_bss_info { +struct brcmf_bss_info_le { __le32 version; /* version field */ __le32 length; /* byte length of data in this record, * starting at version and including IEs @@ -466,14 +464,13 @@ struct brcmf_scan_results { u32 buflen; u32 version; u32 count; - struct brcmf_bss_info bss_info[1]; + struct brcmf_bss_info_le bss_info_le[]; }; struct brcmf_scan_results_le { __le32 buflen; __le32 version; __le32 count; - struct brcmf_bss_info bss_info[1]; }; /* used for association with a specific BSSID and chanspec list */ @@ -493,10 +490,6 @@ struct brcmf_join_params { struct brcmf_assoc_params_le params_le; }; -/* size of brcmf_scan_results not including variable length array */ -#define BRCMF_SCAN_RESULTS_FIXED_SIZE \ - (sizeof(struct brcmf_scan_results) - sizeof(struct brcmf_bss_info)) - /* incremental scan results struct */ struct brcmf_iscan_results { union { @@ -511,7 +504,7 @@ struct brcmf_iscan_results { /* size of brcmf_iscan_results not including variable length array */ #define BRCMF_ISCAN_RESULTS_FIXED_SIZE \ - (BRCMF_SCAN_RESULTS_FIXED_SIZE + \ + (sizeof(struct brcmf_scan_results) + \ offsetof(struct brcmf_iscan_results, results)) struct brcmf_wsec_key { @@ -734,8 +727,7 @@ extern int brcmf_c_host_event(struct brcmf_info *drvr_priv, int *idx, extern void brcmf_c_init(void); extern int brcmf_add_if(struct brcmf_info *drvr_priv, int ifidx, - struct net_device *ndev, char *name, u8 *mac_addr, - u32 flags, u8 bssidx); + char *name, u8 *mac_addr); extern void brcmf_del_if(struct brcmf_info *drvr_priv, int ifidx); /* Send packet to dongle via data channel */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 891826197f96..40928e58b6a6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -488,10 +488,9 @@ brcmf_c_host_event(struct brcmf_info *drvr_priv, int *ifidx, void *pktdata, if (ifevent->ifidx > 0 && ifevent->ifidx < BRCMF_MAX_IFS) { if (ifevent->action == BRCMF_E_IF_ADD) - brcmf_add_if(drvr_priv, ifevent->ifidx, NULL, + brcmf_add_if(drvr_priv, ifevent->ifidx, event->ifname, - pvt_data->eth.h_dest, - ifevent->flags, ifevent->bssidx); + pvt_data->eth.h_dest); else brcmf_del_if(drvr_priv, ifevent->ifidx); } else { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 4acbac5a74c6..719fd9397eb6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -58,7 +58,6 @@ struct brcmf_if { struct net_device *ndev; struct net_device_stats stats; int idx; /* iface idx in dongle */ - int state; /* interface state */ u8 mac_addr[ETH_ALEN]; /* assigned MAC address */ }; @@ -80,20 +79,6 @@ struct brcmf_info { /* Error bits */ module_param(brcmf_msg_level, int, 0); - -static int brcmf_net2idx(struct brcmf_info *drvr_priv, struct net_device *ndev) -{ - int i = 0; - - while (i < BRCMF_MAX_IFS) { - if (drvr_priv->iflist[i] && drvr_priv->iflist[i]->ndev == ndev) - return i; - i++; - } - - return BRCMF_BAD_IF; -} - int brcmf_ifname2idx(struct brcmf_info *drvr_priv, char *name) { int i = BRCMF_MAX_IFS; @@ -285,14 +270,9 @@ _brcmf_set_mac_address(struct work_struct *work) static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr) { - struct brcmf_info *drvr_priv = *(struct brcmf_info **) - netdev_priv(ndev); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_info *drvr_priv = ifp->info; struct sockaddr *sa = (struct sockaddr *)addr; - int ifidx; - - ifidx = brcmf_net2idx(drvr_priv, ndev); - if (ifidx == BRCMF_BAD_IF) - return -1; memcpy(&drvr_priv->macvalue, sa->sa_data, ETH_ALEN); schedule_work(&drvr_priv->setmacaddr_work); @@ -301,13 +281,8 @@ static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr) static void brcmf_netdev_set_multicast_list(struct net_device *ndev) { - struct brcmf_info *drvr_priv = *(struct brcmf_info **) - netdev_priv(ndev); - int ifidx; - - ifidx = brcmf_net2idx(drvr_priv, ndev); - if (ifidx == BRCMF_BAD_IF) - return; + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_info *drvr_priv = ifp->info; schedule_work(&drvr_priv->multicast_work); } @@ -341,9 +316,8 @@ int brcmf_sendpkt(struct brcmf_pub *drvr, int ifidx, struct sk_buff *pktbuf) static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) { int ret; - struct brcmf_info *drvr_priv = *(struct brcmf_info **) - netdev_priv(ndev); - int ifidx; + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_info *drvr_priv = ifp->info; brcmf_dbg(TRACE, "Enter\n"); @@ -355,9 +329,8 @@ static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) return -ENODEV; } - ifidx = brcmf_net2idx(drvr_priv, ndev); - if (ifidx == BRCMF_BAD_IF) { - brcmf_dbg(ERROR, "bad ifidx %d\n", ifidx); + if (!drvr_priv->iflist[ifp->idx]) { + brcmf_dbg(ERROR, "bad ifidx %d\n", ifp->idx); netif_stop_queue(ndev); return -ENODEV; } @@ -367,20 +340,20 @@ static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) struct sk_buff *skb2; brcmf_dbg(INFO, "%s: insufficient headroom\n", - brcmf_ifname(&drvr_priv->pub, ifidx)); + brcmf_ifname(&drvr_priv->pub, ifp->idx)); drvr_priv->pub.tx_realloc++; skb2 = skb_realloc_headroom(skb, drvr_priv->pub.hdrlen); dev_kfree_skb(skb); skb = skb2; if (skb == NULL) { brcmf_dbg(ERROR, "%s: skb_realloc_headroom failed\n", - brcmf_ifname(&drvr_priv->pub, ifidx)); + brcmf_ifname(&drvr_priv->pub, ifp->idx)); ret = -ENOMEM; goto done; } } - ret = brcmf_sendpkt(&drvr_priv->pub, ifidx, skb); + ret = brcmf_sendpkt(&drvr_priv->pub, ifp->idx, skb); done: if (ret) @@ -482,12 +455,10 @@ void brcmf_rx_frame(struct brcmf_pub *drvr, int ifidx, struct sk_buff *skb, skb_mac_header(skb), &event, &data); - if (drvr_priv->iflist[ifidx] && - !drvr_priv->iflist[ifidx]->state) + if (drvr_priv->iflist[ifidx]) { ifp = drvr_priv->iflist[ifidx]; - - if (ifp->ndev) ifp->ndev->last_rx = jiffies; + } drvr->dstats.rx_bytes += skb->len; drvr->rx_packets++; /* Local count */ @@ -524,19 +495,11 @@ void brcmf_txcomplete(struct brcmf_pub *drvr, struct sk_buff *txp, bool success) static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev) { - struct brcmf_info *drvr_priv = *(struct brcmf_info **) - netdev_priv(ndev); - struct brcmf_if *ifp; - int ifidx; + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_info *drvr_priv = ifp->info; brcmf_dbg(TRACE, "Enter\n"); - ifidx = brcmf_net2idx(drvr_priv, ndev); - if (ifidx == BRCMF_BAD_IF) - return NULL; - - ifp = drvr_priv->iflist[ifidx]; - if (drvr_priv->pub.up) /* Use the protocol to get dongle stats */ brcmf_proto_dstats(&drvr_priv->pub); @@ -637,8 +600,8 @@ static int brcmf_toe_set(struct brcmf_info *drvr_priv, int ifidx, u32 toe_ol) static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { - struct brcmf_info *drvr_priv = *(struct brcmf_info **) - netdev_priv(ndev); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_info *drvr_priv = ifp->info; sprintf(info->driver, KBUILD_MODNAME); sprintf(info->version, "%lu", drvr_priv->pub.drv_version); @@ -765,14 +728,12 @@ static int brcmf_ethtool(struct brcmf_info *drvr_priv, void __user *uaddr) static int brcmf_netdev_ioctl_entry(struct net_device *ndev, struct ifreq *ifr, int cmd) { - struct brcmf_info *drvr_priv = *(struct brcmf_info **) - netdev_priv(ndev); - int ifidx; + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_info *drvr_priv = ifp->info; - ifidx = brcmf_net2idx(drvr_priv, ndev); - brcmf_dbg(TRACE, "ifidx %d, cmd 0x%04x\n", ifidx, cmd); + brcmf_dbg(TRACE, "ifidx %d, cmd 0x%04x\n", ifp->idx, cmd); - if (ifidx == BRCMF_BAD_IF) + if (!drvr_priv->iflist[ifp->idx]) return -1; if (cmd == SIOCETHTOOL) @@ -788,17 +749,14 @@ s32 brcmf_exec_dcmd(struct net_device *ndev, u32 cmd, void *arg, u32 len) s32 err = 0; int buflen = 0; bool is_set_key_cmd; - struct brcmf_info *drvr_priv = *(struct brcmf_info **) - netdev_priv(ndev); - int ifidx; + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_info *drvr_priv = ifp->info; memset(&dcmd, 0, sizeof(dcmd)); dcmd.cmd = cmd; dcmd.buf = arg; dcmd.len = len; - ifidx = brcmf_net2idx(drvr_priv, ndev); - if (dcmd.buf != NULL) buflen = min_t(uint, dcmd.len, BRCMF_DCMD_MAXLEN); @@ -826,7 +784,7 @@ s32 brcmf_exec_dcmd(struct net_device *ndev, u32 cmd, void *arg, u32 len) if (is_set_key_cmd) brcmf_netdev_wait_pend8021x(ndev); - err = brcmf_proto_dcmd(&drvr_priv->pub, ifidx, &dcmd, buflen); + err = brcmf_proto_dcmd(&drvr_priv->pub, ifp->idx, &dcmd, buflen); done: if (err > 0) @@ -837,7 +795,8 @@ done: static int brcmf_netdev_stop(struct net_device *ndev) { - struct brcmf_pub *drvr = *(struct brcmf_pub **) netdev_priv(ndev); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = &ifp->info->pub; brcmf_dbg(TRACE, "Enter\n"); brcmf_cfg80211_down(drvr->config); @@ -853,16 +812,14 @@ static int brcmf_netdev_stop(struct net_device *ndev) static int brcmf_netdev_open(struct net_device *ndev) { - struct brcmf_info *drvr_priv = *(struct brcmf_info **) - netdev_priv(ndev); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_info *drvr_priv = ifp->info; u32 toe_ol; - int ifidx = brcmf_net2idx(drvr_priv, ndev); s32 ret = 0; - brcmf_dbg(TRACE, "ifidx %d\n", ifidx); - - if (ifidx == 0) { /* do it only for primary eth0 */ + brcmf_dbg(TRACE, "ifidx %d\n", ifp->idx); + if (ifp->idx == 0) { /* do it only for primary eth0 */ /* try to bring up bus */ ret = brcmf_bus_start(&drvr_priv->pub); if (ret != 0) { @@ -874,12 +831,12 @@ static int brcmf_netdev_open(struct net_device *ndev) memcpy(ndev->dev_addr, drvr_priv->pub.mac, ETH_ALEN); /* Get current TOE mode from dongle */ - if (brcmf_toe_get(drvr_priv, ifidx, &toe_ol) >= 0 + if (brcmf_toe_get(drvr_priv, ifp->idx, &toe_ol) >= 0 && (toe_ol & TOE_TX_CSUM_OL) != 0) - drvr_priv->iflist[ifidx]->ndev->features |= + drvr_priv->iflist[ifp->idx]->ndev->features |= NETIF_F_IP_CSUM; else - drvr_priv->iflist[ifidx]->ndev->features &= + drvr_priv->iflist[ifp->idx]->ndev->features &= ~NETIF_F_IP_CSUM; } /* Allow transmit calls */ @@ -893,75 +850,62 @@ static int brcmf_netdev_open(struct net_device *ndev) return ret; } +static const struct net_device_ops brcmf_netdev_ops_pri = { + .ndo_open = brcmf_netdev_open, + .ndo_stop = brcmf_netdev_stop, + .ndo_get_stats = brcmf_netdev_get_stats, + .ndo_do_ioctl = brcmf_netdev_ioctl_entry, + .ndo_start_xmit = brcmf_netdev_start_xmit, + .ndo_set_mac_address = brcmf_netdev_set_mac_address, + .ndo_set_rx_mode = brcmf_netdev_set_multicast_list +}; + int -brcmf_add_if(struct brcmf_info *drvr_priv, int ifidx, struct net_device *ndev, - char *name, u8 *mac_addr, u32 flags, u8 bssidx) +brcmf_add_if(struct brcmf_info *drvr_priv, int ifidx, char *name, u8 *mac_addr) { struct brcmf_if *ifp; - int ret = 0, err = 0; + struct net_device *ndev; - brcmf_dbg(TRACE, "idx %d, handle->%p\n", ifidx, ndev); + brcmf_dbg(TRACE, "idx %d\n", ifidx); ifp = drvr_priv->iflist[ifidx]; - if (!ifp) { - ifp = kmalloc(sizeof(struct brcmf_if), GFP_ATOMIC); - if (!ifp) - return -ENOMEM; + /* + * Delete the existing interface before overwriting it + * in case we missed the BRCMF_E_IF_DEL event. + */ + if (ifp) { + brcmf_dbg(ERROR, "ERROR: netdev:%s already exists, try free & unregister\n", + ifp->ndev->name); + netif_stop_queue(ifp->ndev); + unregister_netdev(ifp->ndev); + free_netdev(ifp->ndev); + drvr_priv->iflist[ifidx] = NULL; + } + + /* Allocate netdev, including space for private structure */ + ndev = alloc_netdev(sizeof(struct brcmf_if), name, ether_setup); + if (!ndev) { + brcmf_dbg(ERROR, "OOM - alloc_netdev\n"); + return -ENOMEM; } - memset(ifp, 0, sizeof(struct brcmf_if)); + ifp = netdev_priv(ndev); + ifp->ndev = ndev; ifp->info = drvr_priv; drvr_priv->iflist[ifidx] = ifp; + ifp->idx = ifidx; if (mac_addr != NULL) memcpy(&ifp->mac_addr, mac_addr, ETH_ALEN); - if (ndev == NULL) { - ifp->state = BRCMF_E_IF_ADD; - ifp->idx = ifidx; - /* - * Delete the existing interface before overwriting it - * in case we missed the BRCMF_E_IF_DEL event. - */ - if (ifp->ndev != NULL) { - brcmf_dbg(ERROR, "ERROR: netdev:%s already exists, try free & unregister\n", - ifp->ndev->name); - netif_stop_queue(ifp->ndev); - unregister_netdev(ifp->ndev); - free_netdev(ifp->ndev); - } - - /* Allocate netdev, including space for private structure */ - ifp->ndev = alloc_netdev(sizeof(drvr_priv), "wlan%d", - ether_setup); - if (!ifp->ndev) { - brcmf_dbg(ERROR, "OOM - alloc_netdev\n"); - ret = -ENOMEM; - } - - if (ret == 0) { - memcpy(netdev_priv(ifp->ndev), &drvr_priv, - sizeof(drvr_priv)); - err = brcmf_net_attach(&drvr_priv->pub, ifp->idx); - if (err != 0) { - brcmf_dbg(ERROR, "brcmf_net_attach failed, err %d\n", - err); - ret = -EOPNOTSUPP; - } else { - brcmf_dbg(TRACE, " ==== pid:%x, net_device for if:%s created ===\n", - current->pid, ifp->ndev->name); - ifp->state = 0; - } - } - - if (ret < 0) { - if (ifp->ndev) - free_netdev(ifp->ndev); + if (brcmf_net_attach(&drvr_priv->pub, ifp->idx)) { + brcmf_dbg(ERROR, "brcmf_net_attach failed"); + free_netdev(ifp->ndev); + drvr_priv->iflist[ifidx] = NULL; + return -EOPNOTSUPP; + } - drvr_priv->iflist[ifp->idx] = NULL; - kfree(ifp); - } - } else - ifp->ndev = ndev; + brcmf_dbg(TRACE, " ==== pid:%x, net_device for if:%s created ===\n", + current->pid, ifp->ndev->name); return 0; } @@ -977,47 +921,36 @@ void brcmf_del_if(struct brcmf_info *drvr_priv, int ifidx) brcmf_dbg(ERROR, "Null interface\n"); return; } + if (ifp->ndev) { + if (ifidx == 0) { + if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { + rtnl_lock(); + brcmf_netdev_stop(ifp->ndev); + rtnl_unlock(); + } + } else { + netif_stop_queue(ifp->ndev); + } - ifp->state = BRCMF_E_IF_DEL; - ifp->idx = ifidx; - if (ifp->ndev != NULL) { - netif_stop_queue(ifp->ndev); unregister_netdev(ifp->ndev); - free_netdev(ifp->ndev); drvr_priv->iflist[ifidx] = NULL; - kfree(ifp); + if (ifidx == 0) + brcmf_cfg80211_detach(drvr_priv->pub.config); + free_netdev(ifp->ndev); } } struct brcmf_pub *brcmf_attach(struct brcmf_bus *bus, uint bus_hdrlen) { struct brcmf_info *drvr_priv = NULL; - struct net_device *ndev; brcmf_dbg(TRACE, "Enter\n"); - /* Allocate netdev, including space for private structure */ - ndev = alloc_netdev(sizeof(drvr_priv), "wlan%d", ether_setup); - if (!ndev) { - brcmf_dbg(ERROR, "OOM - alloc_netdev\n"); - goto fail; - } - /* Allocate primary brcmf_info */ drvr_priv = kzalloc(sizeof(struct brcmf_info), GFP_ATOMIC); if (!drvr_priv) goto fail; - /* - * Save the brcmf_info into the priv - */ - memcpy(netdev_priv(ndev), &drvr_priv, sizeof(drvr_priv)); - - if (brcmf_add_if(drvr_priv, 0, ndev, ndev->name, NULL, 0, 0) == - BRCMF_BAD_IF) - goto fail; - - ndev->netdev_ops = NULL; mutex_init(&drvr_priv->proto_block); /* Link to info module */ @@ -1033,29 +966,12 @@ struct brcmf_pub *brcmf_attach(struct brcmf_bus *bus, uint bus_hdrlen) goto fail; } - /* Attach and link in the cfg80211 */ - drvr_priv->pub.config = - brcmf_cfg80211_attach(ndev, - brcmf_bus_get_device(bus), - &drvr_priv->pub); - if (drvr_priv->pub.config == NULL) { - brcmf_dbg(ERROR, "wl_cfg80211_attach failed\n"); - goto fail; - } - INIT_WORK(&drvr_priv->setmacaddr_work, _brcmf_set_mac_address); INIT_WORK(&drvr_priv->multicast_work, _brcmf_set_multicast_list); - /* - * Save the brcmf_info into the priv - */ - memcpy(netdev_priv(ndev), &drvr_priv, sizeof(drvr_priv)); - return &drvr_priv->pub; fail: - if (ndev) - free_netdev(ndev); if (drvr_priv) brcmf_detach(&drvr_priv->pub); @@ -1123,16 +1039,6 @@ int brcmf_bus_start(struct brcmf_pub *drvr) return 0; } -static struct net_device_ops brcmf_netdev_ops_pri = { - .ndo_open = brcmf_netdev_open, - .ndo_stop = brcmf_netdev_stop, - .ndo_get_stats = brcmf_netdev_get_stats, - .ndo_do_ioctl = brcmf_netdev_ioctl_entry, - .ndo_start_xmit = brcmf_netdev_start_xmit, - .ndo_set_mac_address = brcmf_netdev_set_mac_address, - .ndo_set_rx_mode = brcmf_netdev_set_multicast_list -}; - int brcmf_net_attach(struct brcmf_pub *drvr, int ifidx) { struct brcmf_info *drvr_priv = drvr->info; @@ -1169,6 +1075,18 @@ int brcmf_net_attach(struct brcmf_pub *drvr, int ifidx) memcpy(ndev->dev_addr, temp_addr, ETH_ALEN); + /* attach to cfg80211 for primary interface */ + if (!ifidx) { + drvr->config = + brcmf_cfg80211_attach(ndev, + brcmf_bus_get_device(drvr->bus), + drvr); + if (drvr->config == NULL) { + brcmf_dbg(ERROR, "wl_cfg80211_attach failed\n"); + goto fail; + } + } + if (register_netdev(ndev) != 0) { brcmf_dbg(ERROR, "couldn't register the net device\n"); goto fail; @@ -1210,21 +1128,13 @@ void brcmf_detach(struct brcmf_pub *drvr) if (drvr) { drvr_priv = drvr->info; if (drvr_priv) { - struct brcmf_if *ifp; int i; - for (i = 1; i < BRCMF_MAX_IFS; i++) + /* make sure primary interface removed last */ + for (i = BRCMF_MAX_IFS-1; i > -1; i--) if (drvr_priv->iflist[i]) brcmf_del_if(drvr_priv, i); - ifp = drvr_priv->iflist[0]; - if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { - rtnl_lock(); - brcmf_netdev_stop(ifp->ndev); - rtnl_unlock(); - unregister_netdev(ifp->ndev); - } - cancel_work_sync(&drvr_priv->setmacaddr_work); cancel_work_sync(&drvr_priv->multicast_work); @@ -1233,10 +1143,6 @@ void brcmf_detach(struct brcmf_pub *drvr) if (drvr->prot) brcmf_proto_detach(drvr); - brcmf_cfg80211_detach(drvr->config); - - free_netdev(ifp->ndev); - kfree(ifp); kfree(drvr_priv); } } @@ -1302,7 +1208,8 @@ static int brcmf_get_pend_8021x_cnt(struct brcmf_info *drvr_priv) int brcmf_netdev_wait_pend8021x(struct net_device *ndev) { - struct brcmf_info *drvr_priv = *(struct brcmf_info **)netdev_priv(ndev); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_info *drvr_priv = ifp->info; int timeout = 10 * HZ / 1000; int ntimes = MAX_WAIT_FOR_8021X_TX; int pend = brcmf_get_pend_8021x_cnt(drvr_priv); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 313b8bf592d1..43b44961a41a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -28,6 +28,7 @@ #include <linux/semaphore.h> #include <linux/firmware.h> #include <linux/module.h> +#include <linux/bcma/bcma.h> #include <asm/unaligned.h> #include <defs.h> #include <brcmu_wifi.h> @@ -35,6 +36,7 @@ #include <brcm_hw_ids.h> #include <soc.h> #include "sdio_host.h" +#include "sdio_chip.h" #define DCMD_RESP_TIMEOUT 2000 /* In milli second */ @@ -134,33 +136,6 @@ struct rte_console { /* Force no backplane reset */ #define SBSDIO_DEVCTL_RST_NOBPRESET 0x20 -/* SBSDIO_FUNC1_CHIPCLKCSR */ - -/* Force ALP request to backplane */ -#define SBSDIO_FORCE_ALP 0x01 -/* Force HT request to backplane */ -#define SBSDIO_FORCE_HT 0x02 -/* Force ILP request to backplane */ -#define SBSDIO_FORCE_ILP 0x04 -/* Make ALP ready (power up xtal) */ -#define SBSDIO_ALP_AVAIL_REQ 0x08 -/* Make HT ready (power up PLL) */ -#define SBSDIO_HT_AVAIL_REQ 0x10 -/* Squelch clock requests from HW */ -#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 -/* Status: ALP is ready */ -#define SBSDIO_ALP_AVAIL 0x40 -/* Status: HT is ready */ -#define SBSDIO_HT_AVAIL 0x80 - -#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) -#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) -#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) -#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) - -#define SBSDIO_CLKAV(regval, alponly) \ - (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) - /* direct(mapped) cis space */ /* MAPPED common CIS address */ @@ -335,50 +310,6 @@ struct rte_console { /* Flags for SDH calls */ #define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) -/* sbimstate */ -#define SBIM_IBE 0x20000 /* inbanderror */ -#define SBIM_TO 0x40000 /* timeout */ -#define SBIM_BY 0x01800000 /* busy (sonics >= 2.3) */ -#define SBIM_RJ 0x02000000 /* reject (sonics >= 2.3) */ - -/* sbtmstatelow */ - -/* reset */ -#define SBTML_RESET 0x0001 -/* reject field */ -#define SBTML_REJ_MASK 0x0006 -/* reject */ -#define SBTML_REJ 0x0002 -/* temporary reject, for error recovery */ -#define SBTML_TMPREJ 0x0004 - -/* Shift to locate the SI control flags in sbtml */ -#define SBTML_SICF_SHIFT 16 - -/* sbtmstatehigh */ -#define SBTMH_SERR 0x0001 /* serror */ -#define SBTMH_INT 0x0002 /* interrupt */ -#define SBTMH_BUSY 0x0004 /* busy */ -#define SBTMH_TO 0x0020 /* timeout (sonics >= 2.3) */ - -/* Shift to locate the SI status flags in sbtmh */ -#define SBTMH_SISF_SHIFT 16 - -/* sbidlow */ -#define SBIDL_INIT 0x80 /* initiator */ - -/* sbidhigh */ -#define SBIDH_RC_MASK 0x000f /* revision code */ -#define SBIDH_RCE_MASK 0x7000 /* revision code extension field */ -#define SBIDH_RCE_SHIFT 8 -#define SBCOREREV(sbidh) \ - ((((sbidh) & SBIDH_RCE_MASK) >> SBIDH_RCE_SHIFT) | \ - ((sbidh) & SBIDH_RC_MASK)) -#define SBIDH_CC_MASK 0x8ff0 /* core code */ -#define SBIDH_CC_SHIFT 4 -#define SBIDH_VC_MASK 0xffff0000 /* vendor code */ -#define SBIDH_VC_SHIFT 16 - /* * Conversion of 802.1D priority to precedence level */ @@ -388,17 +319,6 @@ static uint prio2prec(u32 prio) (prio^2) : prio; } -/* - * Core reg address translation. - * Both macro's returns a 32 bits byte address on the backplane bus. - */ -#define CORE_CC_REG(base, field) \ - (base + offsetof(struct chipcregs, field)) -#define CORE_BUS_REG(base, field) \ - (base + offsetof(struct sdpcmd_regs, field)) -#define CORE_SB(base, field) \ - (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) - /* core registers */ struct sdpcmd_regs { u32 corecontrol; /* 0x00, rev8 */ @@ -524,21 +444,6 @@ struct sdpcm_shared_le { /* misc chip info needed by some of the routines */ -struct chip_info { - u32 chip; - u32 chiprev; - u32 cccorebase; - u32 ccrev; - u32 cccaps; - u32 buscorebase; /* 32 bits backplane bus address */ - u32 buscorerev; - u32 buscoretype; - u32 ramcorebase; - u32 armcorebase; - u32 pmurev; - u32 ramsize; -}; - /* Private data for SDIO bus interaction */ struct brcmf_bus { struct brcmf_pub *drvr; @@ -574,7 +479,7 @@ struct brcmf_bus { uint txminmax; struct sk_buff *glomd; /* Packet containing glomming descriptor */ - struct sk_buff *glom; /* Packet chain for glommed superframe */ + struct sk_buff_head glom; /* Packet list for glommed superframe */ uint glomerr; /* Glom packet read errors */ u8 *rxbuf; /* Buffer for receiving control packets */ @@ -663,46 +568,6 @@ struct brcmf_bus { u32 fw_ptr; }; -struct sbconfig { - u32 PAD[2]; - u32 sbipsflag; /* initiator port ocp slave flag */ - u32 PAD[3]; - u32 sbtpsflag; /* target port ocp slave flag */ - u32 PAD[11]; - u32 sbtmerrloga; /* (sonics >= 2.3) */ - u32 PAD; - u32 sbtmerrlog; /* (sonics >= 2.3) */ - u32 PAD[3]; - u32 sbadmatch3; /* address match3 */ - u32 PAD; - u32 sbadmatch2; /* address match2 */ - u32 PAD; - u32 sbadmatch1; /* address match1 */ - u32 PAD[7]; - u32 sbimstate; /* initiator agent state */ - u32 sbintvec; /* interrupt mask */ - u32 sbtmstatelow; /* target state */ - u32 sbtmstatehigh; /* target state */ - u32 sbbwa0; /* bandwidth allocation table0 */ - u32 PAD; - u32 sbimconfiglow; /* initiator configuration */ - u32 sbimconfighigh; /* initiator configuration */ - u32 sbadmatch0; /* address match0 */ - u32 PAD; - u32 sbtmconfiglow; /* target configuration */ - u32 sbtmconfighigh; /* target configuration */ - u32 sbbconfig; /* broadcast configuration */ - u32 PAD; - u32 sbbstate; /* broadcast state */ - u32 PAD[3]; - u32 sbactcnfg; /* activate configuration */ - u32 PAD[3]; - u32 sbflagst; /* current sbflags */ - u32 PAD[3]; - u32 sbidlow; /* identification */ - u32 sbidhigh; /* identification */ -}; - /* clkstate */ #define CLK_NONE 0 #define CLK_SDONLY 1 @@ -750,10 +615,12 @@ static bool data_ok(struct brcmf_bus *bus) static void r_sdreg32(struct brcmf_bus *bus, u32 *regvar, u32 reg_offset, u32 *retryvar) { + u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); *retryvar = 0; do { *regvar = brcmf_sdcard_reg_read(bus->sdiodev, - bus->ci->buscorebase + reg_offset, sizeof(u32)); + bus->ci->c_inf[idx].base + reg_offset, + sizeof(u32)); } while (brcmf_sdcard_regfail(bus->sdiodev) && (++(*retryvar) <= retry_limit)); if (*retryvar) { @@ -768,10 +635,11 @@ r_sdreg32(struct brcmf_bus *bus, u32 *regvar, u32 reg_offset, u32 *retryvar) static void w_sdreg32(struct brcmf_bus *bus, u32 regval, u32 reg_offset, u32 *retryvar) { + u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); *retryvar = 0; do { brcmf_sdcard_reg_write(bus->sdiodev, - bus->ci->buscorebase + reg_offset, + bus->ci->c_inf[idx].base + reg_offset, sizeof(u32), regval); } while (brcmf_sdcard_regfail(bus->sdiodev) && (++(*retryvar) <= retry_limit)); @@ -812,10 +680,6 @@ static int brcmf_sdbrcm_htclk(struct brcmf_bus *bus, bool on, bool pendok) clkreq = bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; - if ((bus->ci->chip == BCM4329_CHIP_ID) - && (bus->ci->chiprev == 0)) - clkreq |= SBSDIO_FORCE_ALP; - brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err); if (err) { @@ -823,8 +687,8 @@ static int brcmf_sdbrcm_htclk(struct brcmf_bus *bus, bool on, bool pendok) return -EBADE; } - if (pendok && ((bus->ci->buscoretype == PCMCIA_CORE_ID) - && (bus->ci->buscorerev == 9))) { + if (pendok && ((bus->ci->c_inf[1].id == PCMCIA_CORE_ID) + && (bus->ci->c_inf[1].rev == 9))) { u32 dummy, retries; r_sdreg32(bus, &dummy, offsetof(struct sdpcmd_regs, clockctlstatus), @@ -1034,11 +898,9 @@ static int brcmf_sdbrcm_bussleep(struct brcmf_bus *bus, bool sleep) SBSDIO_FORCE_HW_CLKREQ_OFF, NULL); /* Isolate the bus */ - if (bus->ci->chip != BCM4329_CHIP_ID) { - brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1, - SBSDIO_DEVICE_CTL, - SBSDIO_DEVCTL_PADS_ISO, NULL); - } + brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1, + SBSDIO_DEVICE_CTL, + SBSDIO_DEVCTL_PADS_ISO, NULL); /* Change state */ bus->sleeping = true; @@ -1051,8 +913,8 @@ static int brcmf_sdbrcm_bussleep(struct brcmf_bus *bus, bool sleep) /* Force pad isolation off if possible (in case power never toggled) */ - if ((bus->ci->buscoretype == PCMCIA_CORE_ID) - && (bus->ci->buscorerev >= 10)) + if ((bus->ci->c_inf[1].id == PCMCIA_CORE_ID) + && (bus->ci->c_inf[1].rev >= 10)) brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, 0, NULL); @@ -1222,6 +1084,29 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_bus *bus, bool abort, bool rtx) bus->drvr->busstate = BRCMF_BUS_DOWN; } +/* copy a buffer into a pkt buffer chain */ +static uint brcmf_sdbrcm_glom_from_buf(struct brcmf_bus *bus, uint len) +{ + uint n, ret = 0; + struct sk_buff *p; + u8 *buf; + + buf = bus->dataptr; + + /* copy the data */ + skb_queue_walk(&bus->glom, p) { + n = min_t(uint, p->len, len); + memcpy(p->data, buf, n); + buf += n; + len -= n; + ret += n; + if (!len) + break; + } + + return ret; +} + static u8 brcmf_sdbrcm_rxglom(struct brcmf_bus *bus, u8 rxseq) { u16 dlen, totlen; @@ -1240,7 +1125,8 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_bus *bus, u8 rxseq) /* If packets, issue read(s) and send up packet chain */ /* Return sequence numbers consumed? */ - brcmf_dbg(TRACE, "start: glomd %p glom %p\n", bus->glomd, bus->glom); + brcmf_dbg(TRACE, "start: glomd %p glom %p\n", + bus->glomd, skb_peek(&bus->glom)); /* If there's a descriptor, generate the packet chain */ if (bus->glomd) { @@ -1287,12 +1173,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_bus *bus, u8 rxseq) num, sublen); break; } - if (!pfirst) { - pfirst = plast = pnext; - } else { - plast->next = pnext; - plast = pnext; - } + skb_queue_tail(&bus->glom, pnext); /* Adhere to start alignment requirements */ pkt_align(pnext, sublen, BRCMF_SDALIGN); @@ -1308,12 +1189,13 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_bus *bus, u8 rxseq) brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n", bus->nextlen, totlen, rxseq); } - bus->glom = pfirst; pfirst = pnext = NULL; } else { - if (pfirst) - brcmu_pkt_buf_free_skb(pfirst); - bus->glom = NULL; + if (!skb_queue_empty(&bus->glom)) + skb_queue_walk_safe(&bus->glom, pfirst, pnext) { + skb_unlink(pfirst, &bus->glom); + brcmu_pkt_buf_free_skb(pfirst); + } num = 0; } @@ -1325,17 +1207,17 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_bus *bus, u8 rxseq) /* Ok -- either we just generated a packet chain, or had one from before */ - if (bus->glom) { + if (!skb_queue_empty(&bus->glom)) { if (BRCMF_GLOM_ON()) { brcmf_dbg(GLOM, "try superframe read, packet chain:\n"); - for (pnext = bus->glom; pnext; pnext = pnext->next) { + skb_queue_walk(&bus->glom, pnext) { brcmf_dbg(GLOM, " %p: %p len 0x%04x (%d)\n", pnext, (u8 *) (pnext->data), pnext->len, pnext->len); } } - pfirst = bus->glom; + pfirst = skb_peek(&bus->glom); dlen = (u16) brcmu_pkttotlen(pfirst); /* Do an SDIO read for the superframe. Configurable iovar to @@ -1354,8 +1236,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_bus *bus, u8 rxseq) SDIO_FUNC_2, F2SYNC, bus->dataptr, dlen, NULL); - sublen = (u16) brcmu_pktfrombuf(pfirst, 0, dlen, - bus->dataptr); + sublen = (u16) brcmf_sdbrcm_glom_from_buf(bus, dlen); if (sublen != dlen) { brcmf_dbg(ERROR, "FAILED TO COPY, dlen %d sublen %d\n", dlen, sublen); @@ -1380,9 +1261,11 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_bus *bus, u8 rxseq) } else { bus->glomerr = 0; brcmf_sdbrcm_rxfail(bus, true, false); - brcmu_pkt_buf_free_skb(bus->glom); bus->rxglomfail++; - bus->glom = NULL; + skb_queue_walk_safe(&bus->glom, pfirst, pnext) { + skb_unlink(pfirst, &bus->glom); + brcmu_pkt_buf_free_skb(pfirst); + } } return 0; } @@ -1503,9 +1386,11 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_bus *bus, u8 rxseq) } else { bus->glomerr = 0; brcmf_sdbrcm_rxfail(bus, true, false); - brcmu_pkt_buf_free_skb(bus->glom); bus->rxglomfail++; - bus->glom = NULL; + skb_queue_walk_safe(&bus->glom, pfirst, pnext) { + skb_unlink(pfirst, &bus->glom); + brcmu_pkt_buf_free_skb(pfirst); + } } bus->nextlen = 0; return 0; @@ -1513,7 +1398,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_bus *bus, u8 rxseq) /* Basic SD framing looks ok - process each packet (header) */ save_pfirst = pfirst; - bus->glom = NULL; plast = NULL; for (num = 0; pfirst; rxseq++, pfirst = pnext) { @@ -1850,10 +1734,10 @@ brcmf_sdbrcm_readframes(struct brcmf_bus *bus, uint maxframes, bool *finished) rxseq++, rxleft--) { /* Handle glomming separately */ - if (bus->glom || bus->glomd) { + if (bus->glomd || !skb_queue_empty(&bus->glom)) { u8 cnt; brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n", - bus->glomd, bus->glom); + bus->glomd, skb_peek(&bus->glom)); cnt = brcmf_sdbrcm_rxglom(bus, rxseq); brcmf_dbg(GLOM, "rxglom returned %d\n", cnt); rxseq += cnt - 1; @@ -3210,135 +3094,12 @@ static int brcmf_sdbrcm_write_vars(struct brcmf_bus *bus) return bcmerror; } -static void -brcmf_sdbrcm_chip_disablecore(struct brcmf_sdio_dev *sdiodev, u32 corebase) -{ - u32 regdata; - - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbtmstatelow), 4); - if (regdata & SBTML_RESET) - return; - - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbtmstatelow), 4); - if ((regdata & (SICF_CLOCK_EN << SBTML_SICF_SHIFT)) != 0) { - /* - * set target reject and spin until busy is clear - * (preserve core-specific bits) - */ - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbtmstatelow), 4); - brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), - 4, regdata | SBTML_REJ); - - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbtmstatelow), 4); - udelay(1); - SPINWAIT((brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbtmstatehigh), 4) & - SBTMH_BUSY), 100000); - - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbtmstatehigh), 4); - if (regdata & SBTMH_BUSY) - brcmf_dbg(ERROR, "ARM core still busy\n"); - - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbidlow), 4); - if (regdata & SBIDL_INIT) { - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbimstate), 4) | - SBIM_RJ; - brcmf_sdcard_reg_write(sdiodev, - CORE_SB(corebase, sbimstate), 4, - regdata); - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbimstate), 4); - udelay(1); - SPINWAIT((brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbimstate), 4) & - SBIM_BY), 100000); - } - - /* set reset and reject while enabling the clocks */ - brcmf_sdcard_reg_write(sdiodev, - CORE_SB(corebase, sbtmstatelow), 4, - (((SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) | - SBTML_REJ | SBTML_RESET)); - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbtmstatelow), 4); - udelay(10); - - /* clear the initiator reject bit */ - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbidlow), 4); - if (regdata & SBIDL_INIT) { - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbimstate), 4) & - ~SBIM_RJ; - brcmf_sdcard_reg_write(sdiodev, - CORE_SB(corebase, sbimstate), 4, - regdata); - } - } - - /* leave reset and reject asserted */ - brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4, - (SBTML_REJ | SBTML_RESET)); - udelay(1); -} - -static void -brcmf_sdbrcm_chip_resetcore(struct brcmf_sdio_dev *sdiodev, u32 corebase) -{ - u32 regdata; - - /* - * Must do the disable sequence first to work for - * arbitrary current core state. - */ - brcmf_sdbrcm_chip_disablecore(sdiodev, corebase); - - /* - * Now do the initialization sequence. - * set reset while enabling the clock and - * forcing them on throughout the core - */ - brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4, - ((SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) | - SBTML_RESET); - udelay(1); - - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbtmstatehigh), 4); - if (regdata & SBTMH_SERR) - brcmf_sdcard_reg_write(sdiodev, - CORE_SB(corebase, sbtmstatehigh), 4, 0); - - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(corebase, sbimstate), 4); - if (regdata & (SBIM_IBE | SBIM_TO)) - brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbimstate), 4, - regdata & ~(SBIM_IBE | SBIM_TO)); - - /* clear reset and allow it to propagate throughout the core */ - brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4, - (SICF_FGC << SBTML_SICF_SHIFT) | - (SICF_CLOCK_EN << SBTML_SICF_SHIFT)); - udelay(1); - - /* leave clock enabled */ - brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4, - (SICF_CLOCK_EN << SBTML_SICF_SHIFT)); - udelay(1); -} - static int brcmf_sdbrcm_download_state(struct brcmf_bus *bus, bool enter) { uint retries; - u32 regdata; int bcmerror = 0; + u8 idx; + struct chip_info *ci = bus->ci; /* To enter download state, disable ARM and reset SOCRAM. * To exit download state, simply reset ARM (default is RAM boot). @@ -3346,10 +3107,11 @@ static int brcmf_sdbrcm_download_state(struct brcmf_bus *bus, bool enter) if (enter) { bus->alp_only = true; - brcmf_sdbrcm_chip_disablecore(bus->sdiodev, - bus->ci->armcorebase); + idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); + brcmf_sdio_chip_coredisable(bus->sdiodev, ci->c_inf[idx].base); - brcmf_sdbrcm_chip_resetcore(bus->sdiodev, bus->ci->ramcorebase); + idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_INTERNAL_MEM); + brcmf_sdio_chip_resetcore(bus->sdiodev, ci->c_inf[idx].base); /* Clear the top bit of memory */ if (bus->ramsize) { @@ -3358,11 +3120,9 @@ static int brcmf_sdbrcm_download_state(struct brcmf_bus *bus, bool enter) (u8 *)&zeros, 4); } } else { - regdata = brcmf_sdcard_reg_read(bus->sdiodev, - CORE_SB(bus->ci->ramcorebase, sbtmstatelow), 4); - regdata &= (SBTML_RESET | SBTML_REJ_MASK | - (SICF_CLOCK_EN << SBTML_SICF_SHIFT)); - if ((SICF_CLOCK_EN << SBTML_SICF_SHIFT) != regdata) { + idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_INTERNAL_MEM); + if (!brcmf_sdio_chip_iscoreup(bus->sdiodev, + ci->c_inf[idx].base)) { brcmf_dbg(ERROR, "SOCRAM core is down after reset?\n"); bcmerror = -EBADE; goto fail; @@ -3377,7 +3137,8 @@ static int brcmf_sdbrcm_download_state(struct brcmf_bus *bus, bool enter) w_sdreg32(bus, 0xFFFFFFFF, offsetof(struct sdpcmd_regs, intstatus), &retries); - brcmf_sdbrcm_chip_resetcore(bus->sdiodev, bus->ci->armcorebase); + idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); + brcmf_sdio_chip_resetcore(bus->sdiodev, ci->c_inf[idx].base); /* Allow HT Clock now that the ARM is running. */ bus->alp_only = false; @@ -3602,6 +3363,8 @@ void brcmf_sdbrcm_bus_stop(struct brcmf_bus *bus) u8 saveclk; uint retries; int err; + struct sk_buff *cur; + struct sk_buff *next; brcmf_dbg(TRACE, "Enter\n"); @@ -3661,11 +3424,11 @@ void brcmf_sdbrcm_bus_stop(struct brcmf_bus *bus) /* Clear any held glomming stuff */ if (bus->glomd) brcmu_pkt_buf_free_skb(bus->glomd); - - if (bus->glom) - brcmu_pkt_buf_free_skb(bus->glom); - - bus->glom = bus->glomd = NULL; + if (!skb_queue_empty(&bus->glom)) + skb_queue_walk_safe(&bus->glom, cur, next) { + skb_unlink(cur, &bus->glom); + brcmu_pkt_buf_free_skb(cur); + } /* Clear rx control and wake any waiters */ bus->rxlen = 0; @@ -3950,269 +3713,6 @@ fail: return false; } -/* SDIO Pad drive strength to select value mappings */ -struct sdiod_drive_str { - u8 strength; /* Pad Drive Strength in mA */ - u8 sel; /* Chip-specific select value */ -}; - -/* SDIO Drive Strength to sel value table for PMU Rev 1 */ -static const struct sdiod_drive_str sdiod_drive_strength_tab1[] = { - { - 4, 0x2}, { - 2, 0x3}, { - 1, 0x0}, { - 0, 0x0} - }; - -/* SDIO Drive Strength to sel value table for PMU Rev 2, 3 */ -static const struct sdiod_drive_str sdiod_drive_strength_tab2[] = { - { - 12, 0x7}, { - 10, 0x6}, { - 8, 0x5}, { - 6, 0x4}, { - 4, 0x2}, { - 2, 0x1}, { - 0, 0x0} - }; - -/* SDIO Drive Strength to sel value table for PMU Rev 8 (1.8V) */ -static const struct sdiod_drive_str sdiod_drive_strength_tab3[] = { - { - 32, 0x7}, { - 26, 0x6}, { - 22, 0x5}, { - 16, 0x4}, { - 12, 0x3}, { - 8, 0x2}, { - 4, 0x1}, { - 0, 0x0} - }; - -#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) - -static char *brcmf_chipname(uint chipid, char *buf, uint len) -{ - const char *fmt; - - fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; - snprintf(buf, len, fmt, chipid); - return buf; -} - -static void brcmf_sdbrcm_sdiod_drive_strength_init(struct brcmf_bus *bus, - u32 drivestrength) { - struct sdiod_drive_str *str_tab = NULL; - u32 str_mask = 0; - u32 str_shift = 0; - char chn[8]; - - if (!(bus->ci->cccaps & CC_CAP_PMU)) - return; - - switch (SDIOD_DRVSTR_KEY(bus->ci->chip, bus->ci->pmurev)) { - case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 1): - str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab1; - str_mask = 0x30000000; - str_shift = 28; - break; - case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 2): - case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 3): - str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab2; - str_mask = 0x00003800; - str_shift = 11; - break; - case SDIOD_DRVSTR_KEY(BCM4336_CHIP_ID, 8): - str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab3; - str_mask = 0x00003800; - str_shift = 11; - break; - default: - brcmf_dbg(ERROR, "No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", - brcmf_chipname(bus->ci->chip, chn, 8), - bus->ci->chiprev, bus->ci->pmurev); - break; - } - - if (str_tab != NULL) { - u32 drivestrength_sel = 0; - u32 cc_data_temp; - int i; - - for (i = 0; str_tab[i].strength != 0; i++) { - if (drivestrength >= str_tab[i].strength) { - drivestrength_sel = str_tab[i].sel; - break; - } - } - - brcmf_sdcard_reg_write(bus->sdiodev, - CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr), - 4, 1); - cc_data_temp = brcmf_sdcard_reg_read(bus->sdiodev, - CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr), 4); - cc_data_temp &= ~str_mask; - drivestrength_sel <<= str_shift; - cc_data_temp |= drivestrength_sel; - brcmf_sdcard_reg_write(bus->sdiodev, - CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr), - 4, cc_data_temp); - - brcmf_dbg(INFO, "SDIO: %dmA drive strength selected, set to 0x%08x\n", - drivestrength, cc_data_temp); - } -} - -static int -brcmf_sdbrcm_chip_recognition(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u32 regs) -{ - u32 regdata; - - /* - * Get CC core rev - * Chipid is assume to be at offset 0 from regs arg - * For different chiptypes or old sdio hosts w/o chipcommon, - * other ways of recognition should be added here. - */ - ci->cccorebase = regs; - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_CC_REG(ci->cccorebase, chipid), 4); - ci->chip = regdata & CID_ID_MASK; - ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; - - brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev); - - /* Address of cores for new chips should be added here */ - switch (ci->chip) { - case BCM4329_CHIP_ID: - ci->buscorebase = BCM4329_CORE_BUS_BASE; - ci->ramcorebase = BCM4329_CORE_SOCRAM_BASE; - ci->armcorebase = BCM4329_CORE_ARM_BASE; - ci->ramsize = BCM4329_RAMSIZE; - break; - default: - brcmf_dbg(ERROR, "chipid 0x%x is not supported\n", ci->chip); - return -ENODEV; - } - - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(ci->cccorebase, sbidhigh), 4); - ci->ccrev = SBCOREREV(regdata); - - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_CC_REG(ci->cccorebase, pmucapabilities), 4); - ci->pmurev = regdata & PCAP_REV_MASK; - - regdata = brcmf_sdcard_reg_read(sdiodev, - CORE_SB(ci->buscorebase, sbidhigh), 4); - ci->buscorerev = SBCOREREV(regdata); - ci->buscoretype = (regdata & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT; - - brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n", - ci->ccrev, ci->pmurev, ci->buscorerev, ci->buscoretype); - - /* get chipcommon capabilites */ - ci->cccaps = brcmf_sdcard_reg_read(sdiodev, - CORE_CC_REG(ci->cccorebase, capabilities), 4); - - return 0; -} - -static int -brcmf_sdbrcm_chip_attach(struct brcmf_bus *bus, u32 regs) -{ - struct chip_info *ci; - int err; - u8 clkval, clkset; - - brcmf_dbg(TRACE, "Enter\n"); - - /* alloc chip_info_t */ - ci = kzalloc(sizeof(struct chip_info), GFP_ATOMIC); - if (NULL == ci) - return -ENOMEM; - - /* bus/core/clk setup for register access */ - /* Try forcing SDIO core to do ALPAvail request only */ - clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; - brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1, - SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); - if (err) { - brcmf_dbg(ERROR, "error writing for HT off\n"); - goto fail; - } - - /* If register supported, wait for ALPAvail and then force ALP */ - /* This may take up to 15 milliseconds */ - clkval = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1, - SBSDIO_FUNC1_CHIPCLKCSR, NULL); - if ((clkval & ~SBSDIO_AVBITS) == clkset) { - SPINWAIT(((clkval = - brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1, - SBSDIO_FUNC1_CHIPCLKCSR, - NULL)), - !SBSDIO_ALPAV(clkval)), - PMU_MAX_TRANSITION_DLY); - if (!SBSDIO_ALPAV(clkval)) { - brcmf_dbg(ERROR, "timeout on ALPAV wait, clkval 0x%02x\n", - clkval); - err = -EBUSY; - goto fail; - } - clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | - SBSDIO_FORCE_ALP; - brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1, - SBSDIO_FUNC1_CHIPCLKCSR, - clkset, &err); - udelay(65); - } else { - brcmf_dbg(ERROR, "ChipClkCSR access: wrote 0x%02x read 0x%02x\n", - clkset, clkval); - err = -EACCES; - goto fail; - } - - /* Also, disable the extra SDIO pull-ups */ - brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1, - SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); - - err = brcmf_sdbrcm_chip_recognition(bus->sdiodev, ci, regs); - if (err) - goto fail; - - /* - * Make sure any on-chip ARM is off (in case strapping is wrong), - * or downloaded code was already running. - */ - brcmf_sdbrcm_chip_disablecore(bus->sdiodev, ci->armcorebase); - - brcmf_sdcard_reg_write(bus->sdiodev, - CORE_CC_REG(ci->cccorebase, gpiopullup), 4, 0); - brcmf_sdcard_reg_write(bus->sdiodev, - CORE_CC_REG(ci->cccorebase, gpiopulldown), 4, 0); - - /* Disable F2 to clear any intermediate frame state on the dongle */ - brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_0, SDIO_CCCR_IOEx, - SDIO_FUNC_ENABLE_1, NULL); - - /* WAR: cmd52 backplane read so core HW will drop ALPReq */ - clkval = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1, - 0, NULL); - - /* Done with backplane-dependent accesses, can drop clock... */ - brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1, - SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); - - bus->ci = ci; - return 0; -fail: - bus->ci = NULL; - kfree(ci); - return err; -} - static bool brcmf_sdbrcm_probe_attach(struct brcmf_bus *bus, u32 regsva) { @@ -4220,6 +3720,7 @@ brcmf_sdbrcm_probe_attach(struct brcmf_bus *bus, u32 regsva) int err = 0; int reg_addr; u32 reg_val; + u8 idx; bus->alp_only = true; @@ -4234,7 +3735,7 @@ brcmf_sdbrcm_probe_attach(struct brcmf_bus *bus, u32 regsva) #endif /* BCMDBG */ /* - * Force PLL off until brcmf_sdbrcm_chip_attach() + * Force PLL off until brcmf_sdio_chip_attach() * programs PLL control regs */ @@ -4252,8 +3753,8 @@ brcmf_sdbrcm_probe_attach(struct brcmf_bus *bus, u32 regsva) goto fail; } - if (brcmf_sdbrcm_chip_attach(bus, regsva)) { - brcmf_dbg(ERROR, "brcmf_sdbrcm_chip_attach failed!\n"); + if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci, regsva)) { + brcmf_dbg(ERROR, "brcmf_sdio_chip_attach failed!\n"); goto fail; } @@ -4262,11 +3763,10 @@ brcmf_sdbrcm_probe_attach(struct brcmf_bus *bus, u32 regsva) goto fail; } - brcmf_sdbrcm_sdiod_drive_strength_init(bus, SDIO_DRIVE_STRENGTH); + brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci, + SDIO_DRIVE_STRENGTH); - /* Get info on the ARM and SOCRAM cores... */ - brcmf_sdcard_reg_read(bus->sdiodev, - CORE_SB(bus->ci->armcorebase, sbidhigh), 4); + /* Get info on the SOCRAM cores... */ bus->ramsize = bus->ci->ramsize; if (!(bus->ramsize)) { brcmf_dbg(ERROR, "failed to find SOCRAM memory!\n"); @@ -4274,7 +3774,8 @@ brcmf_sdbrcm_probe_attach(struct brcmf_bus *bus, u32 regsva) } /* Set core control so an SDIO reset does a backplane reset */ - reg_addr = bus->ci->buscorebase + + idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); + reg_addr = bus->ci->c_inf[idx].base + offsetof(struct sdpcmd_regs, corecontrol); reg_val = brcmf_sdcard_reg_read(bus->sdiodev, reg_addr, sizeof(u32)); brcmf_sdcard_reg_write(bus->sdiodev, reg_addr, sizeof(u32), @@ -4364,15 +3865,6 @@ brcmf_sdbrcm_watchdog(unsigned long data) } } -static void -brcmf_sdbrcm_chip_detach(struct brcmf_bus *bus) -{ - brcmf_dbg(TRACE, "Enter\n"); - - kfree(bus->ci); - bus->ci = NULL; -} - static void brcmf_sdbrcm_release_dongle(struct brcmf_bus *bus) { brcmf_dbg(TRACE, "Enter\n"); @@ -4380,7 +3872,7 @@ static void brcmf_sdbrcm_release_dongle(struct brcmf_bus *bus) if (bus->ci) { brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); - brcmf_sdbrcm_chip_detach(bus); + brcmf_sdio_chip_detach(&bus->ci); if (bus->vars && bus->varsz) kfree(bus->vars); bus->vars = NULL; @@ -4440,6 +3932,7 @@ void *brcmf_sdbrcm_probe(u16 bus_no, u16 slot, u16 func, uint bustype, bus->sdiodev = sdiodev; sdiodev->bus = bus; + skb_queue_head_init(&bus->glom); bus->txbound = BRCMF_TXBOUND; bus->rxbound = BRCMF_RXBOUND; bus->txminmax = BRCMF_TXMINMAX; @@ -4521,9 +4014,10 @@ void *brcmf_sdbrcm_probe(u16 bus_no, u16 slot, u16 func, uint bustype, goto fail; } } - /* Ok, have the per-port tell the stack we're open for business */ - if (brcmf_net_attach(bus->drvr, 0) != 0) { - brcmf_dbg(ERROR, "Net attach failed!!\n"); + + /* add interface and open for business */ + if (brcmf_add_if((struct brcmf_info *)bus->drvr, 0, "wlan%d", NULL)) { + brcmf_dbg(ERROR, "Add primary net device interface failed!!\n"); goto fail; } @@ -4554,10 +4048,6 @@ struct device *brcmf_bus_get_device(struct brcmf_bus *bus) void brcmf_sdbrcm_wd_timer(struct brcmf_bus *bus, uint wdtick) { - /* don't start the wd until fw is loaded */ - if (bus->drvr->busstate == BRCMF_BUS_DOWN) - return; - /* Totally stop the timer */ if (!wdtick && bus->wd_timer_valid == true) { del_timer_sync(&bus->timer); @@ -4566,6 +4056,10 @@ brcmf_sdbrcm_wd_timer(struct brcmf_bus *bus, uint wdtick) return; } + /* don't start the wd until fw is loaded */ + if (bus->drvr->busstate == BRCMF_BUS_DOWN) + return; + if (wdtick) { if (bus->save_ms != BRCMF_WD_POLL_MS) { if (bus->wd_timer_valid == true) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c new file mode 100644 index 000000000000..99d00ddc1639 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2011 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* ***** SDIO interface chip backplane handle functions ***** */ + +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/mmc/card.h> +#include <linux/ssb/ssb_regs.h> +#include <linux/bcma/bcma.h> + +#include <chipcommon.h> +#include <brcm_hw_ids.h> +#include <brcmu_wifi.h> +#include <brcmu_utils.h> +#include <soc.h> +#include "dhd.h" +#include "dhd_dbg.h" +#include "sdio_host.h" +#include "sdio_chip.h" + +/* chip core base & ramsize */ +/* bcm4329 */ +/* SDIO device core, ID 0x829 */ +#define BCM4329_CORE_BUS_BASE 0x18011000 +/* internal memory core, ID 0x80e */ +#define BCM4329_CORE_SOCRAM_BASE 0x18003000 +/* ARM Cortex M3 core, ID 0x82a */ +#define BCM4329_CORE_ARM_BASE 0x18002000 +#define BCM4329_RAMSIZE 0x48000 + +#define SBCOREREV(sbidh) \ + ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \ + ((sbidh) & SSB_IDHIGH_RCLO)) + +#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) +/* SDIO Pad drive strength to select value mappings */ +struct sdiod_drive_str { + u8 strength; /* Pad Drive Strength in mA */ + u8 sel; /* Chip-specific select value */ +}; +/* SDIO Drive Strength to sel value table for PMU Rev 1 */ +static const struct sdiod_drive_str sdiod_drive_strength_tab1[] = { + { + 4, 0x2}, { + 2, 0x3}, { + 1, 0x0}, { + 0, 0x0} + }; +/* SDIO Drive Strength to sel value table for PMU Rev 2, 3 */ +static const struct sdiod_drive_str sdiod_drive_strength_tab2[] = { + { + 12, 0x7}, { + 10, 0x6}, { + 8, 0x5}, { + 6, 0x4}, { + 4, 0x2}, { + 2, 0x1}, { + 0, 0x0} + }; +/* SDIO Drive Strength to sel value table for PMU Rev 8 (1.8V) */ +static const struct sdiod_drive_str sdiod_drive_strength_tab3[] = { + { + 32, 0x7}, { + 26, 0x6}, { + 22, 0x5}, { + 16, 0x4}, { + 12, 0x3}, { + 8, 0x2}, { + 4, 0x1}, { + 0, 0x0} + }; + +u8 +brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid) +{ + u8 idx; + + for (idx = 0; idx < BRCMF_MAX_CORENUM; idx++) + if (coreid == ci->c_inf[idx].id) + return idx; + + return BRCMF_MAX_CORENUM; +} + +static u32 +brcmf_sdio_chip_corerev(struct brcmf_sdio_dev *sdiodev, + u32 corebase) +{ + u32 regdata; + + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbidhigh), 4); + return SBCOREREV(regdata); +} + +bool +brcmf_sdio_chip_iscoreup(struct brcmf_sdio_dev *sdiodev, + u32 corebase) +{ + u32 regdata; + + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbtmstatelow), 4); + regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT | + SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK); + return (SSB_TMSLOW_CLOCK == regdata); +} + +void +brcmf_sdio_chip_coredisable(struct brcmf_sdio_dev *sdiodev, u32 corebase) +{ + u32 regdata; + + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbtmstatelow), 4); + if (regdata & SSB_TMSLOW_RESET) + return; + + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbtmstatelow), 4); + if ((regdata & SSB_TMSLOW_CLOCK) != 0) { + /* + * set target reject and spin until busy is clear + * (preserve core-specific bits) + */ + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbtmstatelow), 4); + brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), + 4, regdata | SSB_TMSLOW_REJECT); + + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbtmstatelow), 4); + udelay(1); + SPINWAIT((brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbtmstatehigh), 4) & + SSB_TMSHIGH_BUSY), 100000); + + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbtmstatehigh), 4); + if (regdata & SSB_TMSHIGH_BUSY) + brcmf_dbg(ERROR, "core state still busy\n"); + + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbidlow), 4); + if (regdata & SSB_IDLOW_INITIATOR) { + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbimstate), 4) | + SSB_IMSTATE_REJECT; + brcmf_sdcard_reg_write(sdiodev, + CORE_SB(corebase, sbimstate), 4, + regdata); + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbimstate), 4); + udelay(1); + SPINWAIT((brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbimstate), 4) & + SSB_IMSTATE_BUSY), 100000); + } + + /* set reset and reject while enabling the clocks */ + brcmf_sdcard_reg_write(sdiodev, + CORE_SB(corebase, sbtmstatelow), 4, + (SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | + SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET)); + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbtmstatelow), 4); + udelay(10); + + /* clear the initiator reject bit */ + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbidlow), 4); + if (regdata & SSB_IDLOW_INITIATOR) { + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbimstate), 4) & + ~SSB_IMSTATE_REJECT; + brcmf_sdcard_reg_write(sdiodev, + CORE_SB(corebase, sbimstate), 4, + regdata); + } + } + + /* leave reset and reject asserted */ + brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4, + (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET)); + udelay(1); +} + +void +brcmf_sdio_chip_resetcore(struct brcmf_sdio_dev *sdiodev, u32 corebase) +{ + u32 regdata; + + /* + * Must do the disable sequence first to work for + * arbitrary current core state. + */ + brcmf_sdio_chip_coredisable(sdiodev, corebase); + + /* + * Now do the initialization sequence. + * set reset while enabling the clock and + * forcing them on throughout the core + */ + brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4, + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET); + udelay(1); + + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbtmstatehigh), 4); + if (regdata & SSB_TMSHIGH_SERR) + brcmf_sdcard_reg_write(sdiodev, + CORE_SB(corebase, sbtmstatehigh), 4, 0); + + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(corebase, sbimstate), 4); + if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) + brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbimstate), 4, + regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO)); + + /* clear reset and allow it to propagate throughout the core */ + brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), 4, + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK); + udelay(1); + + /* leave clock enabled */ + brcmf_sdcard_reg_write(sdiodev, CORE_SB(corebase, sbtmstatelow), + 4, SSB_TMSLOW_CLOCK); + udelay(1); +} + +static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, + struct chip_info *ci, u32 regs) +{ + u32 regdata; + + /* + * Get CC core rev + * Chipid is assume to be at offset 0 from regs arg + * For different chiptypes or old sdio hosts w/o chipcommon, + * other ways of recognition should be added here. + */ + ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; + ci->c_inf[0].base = regs; + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_CC_REG(ci->c_inf[0].base, chipid), 4); + ci->chip = regdata & CID_ID_MASK; + ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; + + brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev); + + /* Address of cores for new chips should be added here */ + switch (ci->chip) { + case BCM4329_CHIP_ID: + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = BCM4329_CORE_BUS_BASE; + ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; + ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE; + ci->c_inf[3].id = BCMA_CORE_ARM_CM3; + ci->c_inf[3].base = BCM4329_CORE_ARM_BASE; + ci->ramsize = BCM4329_RAMSIZE; + break; + default: + brcmf_dbg(ERROR, "chipid 0x%x is not supported\n", ci->chip); + return -ENODEV; + } + + return 0; +} + +static int +brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev) +{ + int err = 0; + u8 clkval, clkset; + + /* Try forcing SDIO core to do ALPAvail request only */ + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; + brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + if (err) { + brcmf_dbg(ERROR, "error writing for HT off\n"); + return err; + } + + /* If register supported, wait for ALPAvail and then force ALP */ + /* This may take up to 15 milliseconds */ + clkval = brcmf_sdcard_cfg_read(sdiodev, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, NULL); + + if ((clkval & ~SBSDIO_AVBITS) != clkset) { + brcmf_dbg(ERROR, "ChipClkCSR access: wrote 0x%02x read 0x%02x\n", + clkset, clkval); + return -EACCES; + } + + SPINWAIT(((clkval = brcmf_sdcard_cfg_read(sdiodev, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, NULL)), + !SBSDIO_ALPAV(clkval)), + PMU_MAX_TRANSITION_DLY); + if (!SBSDIO_ALPAV(clkval)) { + brcmf_dbg(ERROR, "timeout on ALPAV wait, clkval 0x%02x\n", + clkval); + return -EBUSY; + } + + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; + brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, + SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + udelay(65); + + /* Also, disable the extra SDIO pull-ups */ + brcmf_sdcard_cfg_write(sdiodev, SDIO_FUNC_1, + SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); + + return 0; +} + +static void +brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, + struct chip_info *ci) +{ + u32 regdata; + u8 idx; + + /* get chipcommon rev */ + ci->c_inf[0].rev = + brcmf_sdio_chip_corerev(sdiodev, ci->c_inf[0].base); + + /* get chipcommon capabilites */ + ci->c_inf[0].caps = + brcmf_sdcard_reg_read(sdiodev, + CORE_CC_REG(ci->c_inf[0].base, capabilities), 4); + + /* get pmu caps & rev */ + if (ci->c_inf[0].caps & CC_CAP_PMU) { + ci->pmucaps = brcmf_sdcard_reg_read(sdiodev, + CORE_CC_REG(ci->c_inf[0].base, pmucapabilities), 4); + ci->pmurev = ci->pmucaps & PCAP_REV_MASK; + } + + ci->c_inf[1].rev = brcmf_sdio_chip_corerev(sdiodev, ci->c_inf[1].base); + regdata = brcmf_sdcard_reg_read(sdiodev, + CORE_SB(ci->c_inf[1].base, sbidhigh), 4); + ci->c_inf[1].id = (regdata & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT; + + brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n", + ci->c_inf[0].rev, ci->pmurev, + ci->c_inf[1].rev, ci->c_inf[1].id); + + /* + * Make sure any on-chip ARM is off (in case strapping is wrong), + * or downloaded code was already running. + */ + idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); + brcmf_sdio_chip_coredisable(sdiodev, ci->c_inf[idx].base); +} + +int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, + struct chip_info **ci_ptr, u32 regs) +{ + int ret; + struct chip_info *ci; + + brcmf_dbg(TRACE, "Enter\n"); + + /* alloc chip_info_t */ + ci = kzalloc(sizeof(struct chip_info), GFP_ATOMIC); + if (!ci) + return -ENOMEM; + + ret = brcmf_sdio_chip_buscoreprep(sdiodev); + if (ret != 0) + goto err; + + ret = brcmf_sdio_chip_recognition(sdiodev, ci, regs); + if (ret != 0) + goto err; + + brcmf_sdio_chip_buscoresetup(sdiodev, ci); + + brcmf_sdcard_reg_write(sdiodev, + CORE_CC_REG(ci->c_inf[0].base, gpiopullup), 4, 0); + brcmf_sdcard_reg_write(sdiodev, + CORE_CC_REG(ci->c_inf[0].base, gpiopulldown), 4, 0); + + *ci_ptr = ci; + return 0; + +err: + kfree(ci); + return ret; +} + +void +brcmf_sdio_chip_detach(struct chip_info **ci_ptr) +{ + brcmf_dbg(TRACE, "Enter\n"); + + kfree(*ci_ptr); + *ci_ptr = NULL; +} + +static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len) +{ + const char *fmt; + + fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; + snprintf(buf, len, fmt, chipid); + return buf; +} + +void +brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, + struct chip_info *ci, u32 drivestrength) +{ + struct sdiod_drive_str *str_tab = NULL; + u32 str_mask = 0; + u32 str_shift = 0; + char chn[8]; + + if (!(ci->c_inf[0].caps & CC_CAP_PMU)) + return; + + switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { + case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 1): + str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab1; + str_mask = 0x30000000; + str_shift = 28; + break; + case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 2): + case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 3): + str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab2; + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM4336_CHIP_ID, 8): + str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab3; + str_mask = 0x00003800; + str_shift = 11; + break; + default: + brcmf_dbg(ERROR, "No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", + brcmf_sdio_chip_name(ci->chip, chn, 8), + ci->chiprev, ci->pmurev); + break; + } + + if (str_tab != NULL) { + u32 drivestrength_sel = 0; + u32 cc_data_temp; + int i; + + for (i = 0; str_tab[i].strength != 0; i++) { + if (drivestrength >= str_tab[i].strength) { + drivestrength_sel = str_tab[i].sel; + break; + } + } + + brcmf_sdcard_reg_write(sdiodev, + CORE_CC_REG(ci->c_inf[0].base, chipcontrol_addr), + 4, 1); + cc_data_temp = brcmf_sdcard_reg_read(sdiodev, + CORE_CC_REG(ci->c_inf[0].base, chipcontrol_addr), 4); + cc_data_temp &= ~str_mask; + drivestrength_sel <<= str_shift; + cc_data_temp |= drivestrength_sel; + brcmf_sdcard_reg_write(sdiodev, + CORE_CC_REG(ci->c_inf[0].base, chipcontrol_addr), + 4, cc_data_temp); + + brcmf_dbg(INFO, "SDIO: %dmA drive strength selected, set to 0x%08x\n", + drivestrength, cc_data_temp); + } +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h new file mode 100644 index 000000000000..0ee37ae4c958 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2011 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _BRCMFMAC_SDIO_CHIP_H_ +#define _BRCMFMAC_SDIO_CHIP_H_ + +/* + * Core reg address translation. + * Both macro's returns a 32 bits byte address on the backplane bus. + */ +#define CORE_CC_REG(base, field) \ + (base + offsetof(struct chipcregs, field)) +#define CORE_BUS_REG(base, field) \ + (base + offsetof(struct sdpcmd_regs, field)) +#define CORE_SB(base, field) \ + (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) + +/* SDIO function 1 register CHIPCLKCSR */ +/* Force ALP request to backplane */ +#define SBSDIO_FORCE_ALP 0x01 +/* Force HT request to backplane */ +#define SBSDIO_FORCE_HT 0x02 +/* Force ILP request to backplane */ +#define SBSDIO_FORCE_ILP 0x04 +/* Make ALP ready (power up xtal) */ +#define SBSDIO_ALP_AVAIL_REQ 0x08 +/* Make HT ready (power up PLL) */ +#define SBSDIO_HT_AVAIL_REQ 0x10 +/* Squelch clock requests from HW */ +#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 +/* Status: ALP is ready */ +#define SBSDIO_ALP_AVAIL 0x40 +/* Status: HT is ready */ +#define SBSDIO_HT_AVAIL 0x80 +#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) +#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) +#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) +#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) +#define SBSDIO_CLKAV(regval, alponly) \ + (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) + +#define BRCMF_MAX_CORENUM 6 + +struct chip_core_info { + u16 id; + u16 rev; + u32 base; + u32 wrapbase; + u32 caps; +}; + +struct chip_info { + u32 chip; + u32 chiprev; + /* core info */ + /* always put chipcommon core at 0, bus core at 1 */ + struct chip_core_info c_inf[BRCMF_MAX_CORENUM]; + u32 pmurev; + u32 pmucaps; + u32 ramsize; +}; + +struct sbconfig { + u32 PAD[2]; + u32 sbipsflag; /* initiator port ocp slave flag */ + u32 PAD[3]; + u32 sbtpsflag; /* target port ocp slave flag */ + u32 PAD[11]; + u32 sbtmerrloga; /* (sonics >= 2.3) */ + u32 PAD; + u32 sbtmerrlog; /* (sonics >= 2.3) */ + u32 PAD[3]; + u32 sbadmatch3; /* address match3 */ + u32 PAD; + u32 sbadmatch2; /* address match2 */ + u32 PAD; + u32 sbadmatch1; /* address match1 */ + u32 PAD[7]; + u32 sbimstate; /* initiator agent state */ + u32 sbintvec; /* interrupt mask */ + u32 sbtmstatelow; /* target state */ + u32 sbtmstatehigh; /* target state */ + u32 sbbwa0; /* bandwidth allocation table0 */ + u32 PAD; + u32 sbimconfiglow; /* initiator configuration */ + u32 sbimconfighigh; /* initiator configuration */ + u32 sbadmatch0; /* address match0 */ + u32 PAD; + u32 sbtmconfiglow; /* target configuration */ + u32 sbtmconfighigh; /* target configuration */ + u32 sbbconfig; /* broadcast configuration */ + u32 PAD; + u32 sbbstate; /* broadcast state */ + u32 PAD[3]; + u32 sbactcnfg; /* activate configuration */ + u32 PAD[3]; + u32 sbflagst; /* current sbflags */ + u32 PAD[3]; + u32 sbidlow; /* identification */ + u32 sbidhigh; /* identification */ +}; + +extern void brcmf_sdio_chip_resetcore(struct brcmf_sdio_dev *sdiodev, + u32 corebase); +extern bool brcmf_sdio_chip_iscoreup(struct brcmf_sdio_dev *sdiodev, + u32 corebase); +extern void brcmf_sdio_chip_coredisable(struct brcmf_sdio_dev *sdiodev, + u32 corebase); +extern int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, + struct chip_info **ci_ptr, u32 regs); +extern void brcmf_sdio_chip_detach(struct chip_info **ci_ptr); +extern void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, + struct chip_info *ci, + u32 drivestrength); +extern u8 brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid); + + +#endif /* _BRCMFMAC_SDIO_CHIP_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 5eddabe5228a..73be2c8d4cee 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -1997,7 +1997,7 @@ done: } static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_priv *cfg_priv, - struct brcmf_bss_info *bi) + struct brcmf_bss_info_le *bi) { struct wiphy *wiphy = cfg_to_wiphy(cfg_priv); struct ieee80211_channel *notify_channel; @@ -2057,10 +2057,19 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_priv *cfg_priv, return err; } +static struct brcmf_bss_info_le * +next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss) +{ + if (bss == NULL) + return list->bss_info_le; + return (struct brcmf_bss_info_le *)((unsigned long)bss + + le32_to_cpu(bss->length)); +} + static s32 brcmf_inform_bss(struct brcmf_cfg80211_priv *cfg_priv) { struct brcmf_scan_results *bss_list; - struct brcmf_bss_info *bi = NULL; /* must be initialized */ + struct brcmf_bss_info_le *bi = NULL; /* must be initialized */ s32 err = 0; int i; @@ -2072,7 +2081,7 @@ static s32 brcmf_inform_bss(struct brcmf_cfg80211_priv *cfg_priv) } WL_SCAN("scanned AP count (%d)\n", bss_list->count); for (i = 0; i < bss_list->count && i < WL_AP_MAX; i++) { - bi = next_bss(bss_list, bi); + bi = next_bss_le(bss_list, bi); err = brcmf_inform_single_bss(cfg_priv, bi); if (err) break; @@ -2085,7 +2094,7 @@ static s32 wl_inform_ibss(struct brcmf_cfg80211_priv *cfg_priv, { struct wiphy *wiphy = cfg_to_wiphy(cfg_priv); struct ieee80211_channel *notify_channel; - struct brcmf_bss_info *bi = NULL; + struct brcmf_bss_info_le *bi = NULL; struct ieee80211_supported_band *band; u8 *buf = NULL; s32 err = 0; @@ -2114,7 +2123,7 @@ static s32 wl_inform_ibss(struct brcmf_cfg80211_priv *cfg_priv, goto CleanUp; } - bi = (struct brcmf_bss_info *)(buf + 4); + bi = (struct brcmf_bss_info_le *)(buf + 4); channel = bi->ctl_ch ? bi->ctl_ch : CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec)); @@ -2188,7 +2197,7 @@ static struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key) static s32 brcmf_update_bss_info(struct brcmf_cfg80211_priv *cfg_priv) { - struct brcmf_bss_info *bi; + struct brcmf_bss_info_le *bi; struct brcmf_ssid *ssid; struct brcmf_tlv *tim; u16 beacon_interval; @@ -2211,7 +2220,7 @@ static s32 brcmf_update_bss_info(struct brcmf_cfg80211_priv *cfg_priv) goto update_bss_info_out; } - bi = (struct brcmf_bss_info *)(cfg_priv->extra_buf + 4); + bi = (struct brcmf_bss_info_le *)(cfg_priv->extra_buf + 4); err = brcmf_inform_single_bss(cfg_priv, bi); if (err) goto update_bss_info_out; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index 62dc46144ede..a613b49cb13f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -352,15 +352,6 @@ brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_priv *cfg) return &cfg->conn_info; } -static inline struct brcmf_bss_info *next_bss(struct brcmf_scan_results *list, - struct brcmf_bss_info *bss) -{ - return bss = bss ? - (struct brcmf_bss_info *)((unsigned long)bss + - le32_to_cpu(bss->length)) : - list->bss_info; -} - extern struct brcmf_cfg80211_dev *brcmf_cfg80211_attach(struct net_device *ndev, struct device *busdev, void *data); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h index 106a7424a7cd..b51d1e421e24 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h @@ -38,88 +38,12 @@ /* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), high 32 bits */ #define SI_PCIE_DMA_H32 0x80000000 -/* core codes */ -#define NODEV_CORE_ID 0x700 /* Invalid coreid */ -#define CC_CORE_ID 0x800 /* chipcommon core */ -#define ILINE20_CORE_ID 0x801 /* iline20 core */ -#define SRAM_CORE_ID 0x802 /* sram core */ -#define SDRAM_CORE_ID 0x803 /* sdram core */ -#define PCI_CORE_ID 0x804 /* pci core */ -#define MIPS_CORE_ID 0x805 /* mips core */ -#define ENET_CORE_ID 0x806 /* enet mac core */ -#define CODEC_CORE_ID 0x807 /* v90 codec core */ -#define USB_CORE_ID 0x808 /* usb 1.1 host/device core */ -#define ADSL_CORE_ID 0x809 /* ADSL core */ -#define ILINE100_CORE_ID 0x80a /* iline100 core */ -#define IPSEC_CORE_ID 0x80b /* ipsec core */ -#define UTOPIA_CORE_ID 0x80c /* utopia core */ -#define PCMCIA_CORE_ID 0x80d /* pcmcia core */ -#define SOCRAM_CORE_ID 0x80e /* internal memory core */ -#define MEMC_CORE_ID 0x80f /* memc sdram core */ -#define OFDM_CORE_ID 0x810 /* OFDM phy core */ -#define EXTIF_CORE_ID 0x811 /* external interface core */ -#define D11_CORE_ID 0x812 /* 802.11 MAC core */ -#define APHY_CORE_ID 0x813 /* 802.11a phy core */ -#define BPHY_CORE_ID 0x814 /* 802.11b phy core */ -#define GPHY_CORE_ID 0x815 /* 802.11g phy core */ -#define MIPS33_CORE_ID 0x816 /* mips3302 core */ -#define USB11H_CORE_ID 0x817 /* usb 1.1 host core */ -#define USB11D_CORE_ID 0x818 /* usb 1.1 device core */ -#define USB20H_CORE_ID 0x819 /* usb 2.0 host core */ -#define USB20D_CORE_ID 0x81a /* usb 2.0 device core */ -#define SDIOH_CORE_ID 0x81b /* sdio host core */ -#define ROBO_CORE_ID 0x81c /* roboswitch core */ -#define ATA100_CORE_ID 0x81d /* parallel ATA core */ -#define SATAXOR_CORE_ID 0x81e /* serial ATA & XOR DMA core */ -#define GIGETH_CORE_ID 0x81f /* gigabit ethernet core */ -#define PCIE_CORE_ID 0x820 /* pci express core */ -#define NPHY_CORE_ID 0x821 /* 802.11n 2x2 phy core */ -#define SRAMC_CORE_ID 0x822 /* SRAM controller core */ -#define MINIMAC_CORE_ID 0x823 /* MINI MAC/phy core */ -#define ARM11_CORE_ID 0x824 /* ARM 1176 core */ -#define ARM7S_CORE_ID 0x825 /* ARM7tdmi-s core */ -#define LPPHY_CORE_ID 0x826 /* 802.11a/b/g phy core */ -#define PMU_CORE_ID 0x827 /* PMU core */ -#define SSNPHY_CORE_ID 0x828 /* 802.11n single-stream phy core */ -#define SDIOD_CORE_ID 0x829 /* SDIO device core */ -#define ARMCM3_CORE_ID 0x82a /* ARM Cortex M3 core */ -#define HTPHY_CORE_ID 0x82b /* 802.11n 4x4 phy core */ -#define MIPS74K_CORE_ID 0x82c /* mips 74k core */ -#define GMAC_CORE_ID 0x82d /* Gigabit MAC core */ -#define DMEMC_CORE_ID 0x82e /* DDR1/2 memory controller core */ -#define PCIERC_CORE_ID 0x82f /* PCIE Root Complex core */ -#define OCP_CORE_ID 0x830 /* OCP2OCP bridge core */ -#define SC_CORE_ID 0x831 /* shared common core */ -#define AHB_CORE_ID 0x832 /* OCP2AHB bridge core */ -#define SPIH_CORE_ID 0x833 /* SPI host core */ -#define I2S_CORE_ID 0x834 /* I2S core */ -#define DMEMS_CORE_ID 0x835 /* SDR/DDR1 memory controller core */ -#define DEF_SHIM_COMP 0x837 /* SHIM component in ubus/6362 */ -#define OOB_ROUTER_CORE_ID 0x367 /* OOB router core ID */ -#define DEF_AI_COMP 0xfff /* Default component, in ai chips it - * maps all unused address ranges - */ - /* chipcommon being the first core: */ #define SI_CC_IDX 0 /* SOC Interconnect types (aka chip types) */ #define SOCI_AI 1 -/* Common core control flags */ -#define SICF_BIST_EN 0x8000 -#define SICF_PME_EN 0x4000 -#define SICF_CORE_BITS 0x3ffc -#define SICF_FGC 0x0002 -#define SICF_CLOCK_EN 0x0001 - -/* Common core status flags */ -#define SISF_BIST_DONE 0x8000 -#define SISF_BIST_ERROR 0x4000 -#define SISF_GATED_CLK 0x2000 -#define SISF_DMA64 0x1000 -#define SISF_CORE_BITS 0x0fff - /* A register that is common to all cores to * communicate w/PMU regarding clock control. */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/brcm80211/brcmsmac/dma.c index b56a30297c26..e286fb4d4813 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/dma.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.c @@ -14,7 +14,6 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <linux/slab.h> -#include <linux/skbuff.h> #include <linux/delay.h> #include <linux/pci.h> @@ -22,6 +21,7 @@ #include <aiutils.h> #include "types.h" #include "dma.h" +#include "soc.h" /* * DMA hardware requires each descriptor ring to be 8kB aligned, and fit within @@ -358,13 +358,14 @@ static uint nrxdactive(struct dma_info *di, uint h, uint t) static uint _dma_ctrlflags(struct dma_info *di, uint mask, uint flags) { - uint dmactrlflags = di->dma.dmactrlflags; + uint dmactrlflags; if (di == NULL) { - DMA_ERROR(("%s: _dma_ctrlflags: NULL dma handle\n", di->name)); + DMA_ERROR(("_dma_ctrlflags: NULL dma handle\n")); return 0; } + dmactrlflags = di->dma.dmactrlflags; dmactrlflags &= ~mask; dmactrlflags |= flags; @@ -900,7 +901,7 @@ static struct sk_buff *_dma_getnextrxp(struct dma_info *di, bool forceall) /* * !! rx entry routine - * returns a pointer to the next frame received, or NULL if there are no more + * returns the number packages in the next frame, or 0 if there are no more * if DMA_CTRL_RXMULTI is defined, DMA scattering(multiple buffers) is * supported with pkts chain * otherwise, it's treated as giant pkt and will be tossed. @@ -908,38 +909,40 @@ static struct sk_buff *_dma_getnextrxp(struct dma_info *di, bool forceall) * buffer data. After it reaches the max size of buffer, the data continues * in next DMA descriptor buffer WITHOUT DMA header */ -struct sk_buff *dma_rx(struct dma_pub *pub) +int dma_rx(struct dma_pub *pub, struct sk_buff_head *skb_list) { struct dma_info *di = (struct dma_info *)pub; - struct sk_buff *p, *head, *tail; + struct sk_buff_head dma_frames; + struct sk_buff *p, *next; uint len; uint pkt_len; int resid = 0; + int pktcnt = 1; + skb_queue_head_init(&dma_frames); next_frame: - head = _dma_getnextrxp(di, false); - if (head == NULL) - return NULL; + p = _dma_getnextrxp(di, false); + if (p == NULL) + return 0; - len = le16_to_cpu(*(__le16 *) (head->data)); + len = le16_to_cpu(*(__le16 *) (p->data)); DMA_TRACE(("%s: dma_rx len %d\n", di->name, len)); - dma_spin_for_len(len, head); + dma_spin_for_len(len, p); /* set actual length */ pkt_len = min((di->rxoffset + len), di->rxbufsize); - __skb_trim(head, pkt_len); + __skb_trim(p, pkt_len); + skb_queue_tail(&dma_frames, p); resid = len - (di->rxbufsize - di->rxoffset); /* check for single or multi-buffer rx */ if (resid > 0) { - tail = head; while ((resid > 0) && (p = _dma_getnextrxp(di, false))) { - tail->next = p; pkt_len = min_t(uint, resid, di->rxbufsize); __skb_trim(p, pkt_len); - - tail = p; + skb_queue_tail(&dma_frames, p); resid -= di->rxbufsize; + pktcnt++; } #ifdef BCMDBG @@ -958,13 +961,18 @@ struct sk_buff *dma_rx(struct dma_pub *pub) if ((di->dma.dmactrlflags & DMA_CTRL_RXMULTI) == 0) { DMA_ERROR(("%s: dma_rx: bad frame length (%d)\n", di->name, len)); - brcmu_pkt_buf_free_skb(head); + skb_queue_walk_safe(&dma_frames, p, next) { + skb_unlink(p, &dma_frames); + brcmu_pkt_buf_free_skb(p); + } di->dma.rxgiants++; + pktcnt = 1; goto next_frame; } } - return head; + skb_queue_splice_tail(&dma_frames, skb_list); + return pktcnt; } static bool dma64_rxidle(struct dma_info *di) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.h b/drivers/net/wireless/brcm80211/brcmsmac/dma.h index ebc5bc546f3b..d317c7c12f91 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/dma.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.h @@ -18,6 +18,7 @@ #define _BRCM_DMA_H_ #include <linux/delay.h> +#include <linux/skbuff.h> #include "types.h" /* forward structure declarations */ /* map/unmap direction */ @@ -80,7 +81,7 @@ extern struct dma_pub *dma_attach(char *name, struct si_pub *sih, uint nrxpost, uint rxoffset, uint *msg_level); void dma_rxinit(struct dma_pub *pub); -struct sk_buff *dma_rx(struct dma_pub *pub); +int dma_rx(struct dma_pub *pub, struct sk_buff_head *skb_list); bool dma_rxfill(struct dma_pub *pub); bool dma_rxreset(struct dma_pub *pub); bool dma_txreset(struct dma_pub *pub); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index ac8d02bd34f2..8e35c62d0bb1 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -215,8 +215,7 @@ static const struct ieee80211_supported_band brcms_band_2GHz_nphy_template = { .ht_cap = { /* from include/linux/ieee80211.h */ .cap = IEEE80211_HT_CAP_GRN_FLD | - IEEE80211_HT_CAP_SGI_20 | - IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT, + IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40, .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, .ampdu_density = AMPDU_DEF_MPDU_DENSITY, @@ -237,8 +236,7 @@ static const struct ieee80211_supported_band brcms_band_5GHz_nphy_template = { BRCMS_LEGACY_5G_RATE_OFFSET, .ht_cap = { .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | - IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_40MHZ_INTOLERANT, /* No 40 mhz yet */ + IEEE80211_HT_CAP_SGI_40, .ht_supported = true, .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, .ampdu_density = AMPDU_DEF_MPDU_DENSITY, @@ -286,6 +284,7 @@ static int brcms_ops_start(struct ieee80211_hw *hw) { struct brcms_info *wl = hw->priv; bool blocked; + int err; ieee80211_wake_queues(hw); spin_lock_bh(&wl->lock); @@ -294,57 +293,69 @@ static int brcms_ops_start(struct ieee80211_hw *hw) if (!blocked) wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy); - return 0; + spin_lock_bh(&wl->lock); + /* avoid acknowledging frames before a non-monitor device is added */ + wl->mute_tx = true; + + if (!wl->pub->up) + err = brcms_up(wl); + else + err = -ENODEV; + spin_unlock_bh(&wl->lock); + + if (err != 0) + wiphy_err(hw->wiphy, "%s: brcms_up() returned %d\n", __func__, + err); + return err; } static void brcms_ops_stop(struct ieee80211_hw *hw) { + struct brcms_info *wl = hw->priv; + int status; + ieee80211_stop_queues(hw); + + if (wl->wlc == NULL) + return; + + spin_lock_bh(&wl->lock); + status = brcms_c_chipmatch(wl->wlc->hw->vendorid, + wl->wlc->hw->deviceid); + spin_unlock_bh(&wl->lock); + if (!status) { + wiphy_err(wl->wiphy, + "wl: brcms_ops_stop: chipmatch failed\n"); + return; + } + + /* put driver in down state */ + spin_lock_bh(&wl->lock); + brcms_down(wl); + spin_unlock_bh(&wl->lock); } static int brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { - struct brcms_info *wl; - int err; + struct brcms_info *wl = hw->priv; /* Just STA for now */ - if (vif->type != NL80211_IFTYPE_AP && - vif->type != NL80211_IFTYPE_MESH_POINT && - vif->type != NL80211_IFTYPE_STATION && - vif->type != NL80211_IFTYPE_WDS && - vif->type != NL80211_IFTYPE_ADHOC) { + if (vif->type != NL80211_IFTYPE_STATION) { wiphy_err(hw->wiphy, "%s: Attempt to add type %d, only" " STA for now\n", __func__, vif->type); return -EOPNOTSUPP; } - wl = hw->priv; - spin_lock_bh(&wl->lock); - if (!wl->pub->up) - err = brcms_up(wl); - else - err = -ENODEV; - spin_unlock_bh(&wl->lock); - - if (err != 0) - wiphy_err(hw->wiphy, "%s: brcms_up() returned %d\n", __func__, - err); + wl->mute_tx = false; + brcms_c_mute(wl->wlc, false); - return err; + return 0; } static void brcms_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { - struct brcms_info *wl; - - wl = hw->priv; - - /* put driver in down state */ - spin_lock_bh(&wl->lock); - brcms_down(wl); - spin_unlock_bh(&wl->lock); } static int brcms_ops_config(struct ieee80211_hw *hw, u32 changed) @@ -876,37 +887,18 @@ static void brcms_free(struct brcms_info *wl) } /* -* called from both kernel as from this kernel module. +* called from both kernel as from this kernel module (error flow on attach) * precondition: perimeter lock is not acquired. */ static void brcms_remove(struct pci_dev *pdev) { - struct brcms_info *wl; - struct ieee80211_hw *hw; - int status; - - hw = pci_get_drvdata(pdev); - wl = hw->priv; - if (!wl) { - pr_err("wl: brcms_remove: pci_get_drvdata failed\n"); - return; - } + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct brcms_info *wl = hw->priv; - spin_lock_bh(&wl->lock); - status = brcms_c_chipmatch(pdev->vendor, pdev->device); - spin_unlock_bh(&wl->lock); - if (!status) { - wiphy_err(wl->wiphy, "wl: brcms_remove: chipmatch " - "failed\n"); - return; - } if (wl->wlc) { wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false); wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy); ieee80211_unregister_hw(hw); - spin_lock_bh(&wl->lock); - brcms_down(wl); - spin_unlock_bh(&wl->lock); } pci_disable_device(pdev); @@ -1080,9 +1072,6 @@ static struct brcms_info *brcms_attach(u16 vendor, u16 device, wl->pub->ieee_hw = hw; - /* disable mpc */ - brcms_c_set_radio_mpc(wl->wlc, false); - /* register our interrupt handler */ if (request_irq(irq, brcms_isr, IRQF_SHARED, KBUILD_MODNAME, wl)) { wiphy_err(wl->wiphy, "wl%d: request_irq() failed\n", unit); @@ -1318,8 +1307,7 @@ void brcms_init(struct brcms_info *wl) { BCMMSG(wl->pub->ieee_hw->wiphy, "wl%d\n", wl->pub->unit); brcms_reset(wl); - - brcms_c_init(wl->wlc); + brcms_c_init(wl->wlc, wl->mute_tx); } /* @@ -1336,6 +1324,14 @@ uint brcms_reset(struct brcms_info *wl) return 0; } +void brcms_fatal_error(struct brcms_info *wl) +{ + wiphy_err(wl->wlc->wiphy, "wl%d: fatal error, reinitializing\n", + wl->wlc->pub->unit); + brcms_reset(wl); + ieee80211_restart_hw(wl->pub->ieee_hw); +} + /* * These are interrupt on/off entry points. Disable interrupts * during interrupt state transition. diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h index 177f0e44e4b6..6242f188b717 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h @@ -80,6 +80,7 @@ struct brcms_info { struct brcms_firmware fw; struct wiphy *wiphy; struct brcms_ucode ucode; + bool mute_tx; }; /* misc callbacks */ @@ -104,5 +105,6 @@ extern bool brcms_del_timer(struct brcms_timer *timer); extern void brcms_msleep(struct brcms_info *wl, uint ms); extern void brcms_dpc(unsigned long data); extern void brcms_timer(struct brcms_timer *t); +extern void brcms_fatal_error(struct brcms_info *wl); #endif /* _BRCM_MAC80211_IF_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 510e9bb52287..f193fab675dc 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -30,44 +30,21 @@ #include "mac80211_if.h" #include "ucode_loader.h" #include "main.h" +#include "soc.h" /* * Indication for txflowcontrol that all priority bits in * TXQ_STOP_FOR_PRIOFC_MASK are to be considered. */ -#define ALLPRIO -1 - -/* - * 32 SSID chars, max of 4 chars for each SSID char "\xFF", plus NULL. - */ -#define SSID_FMT_BUF_LEN ((4 * IEEE80211_MAX_SSID_LEN) + 1) +#define ALLPRIO -1 /* watchdog timer, in unit of ms */ -#define TIMER_INTERVAL_WATCHDOG 1000 +#define TIMER_INTERVAL_WATCHDOG 1000 /* radio monitor timer, in unit of ms */ -#define TIMER_INTERVAL_RADIOCHK 800 - -/* Max MPC timeout, in unit of watchdog */ -#ifndef BRCMS_MPC_MAX_DELAYCNT -#define BRCMS_MPC_MAX_DELAYCNT 10 -#endif +#define TIMER_INTERVAL_RADIOCHK 800 -/* Min MPC timeout, in unit of watchdog */ -#define BRCMS_MPC_MIN_DELAYCNT 1 -#define BRCMS_MPC_THRESHOLD 3 /* MPC count threshold level */ - -/* beacon interval, in unit of 1024TU */ -#define BEACON_INTERVAL_DEFAULT 100 -/* DTIM interval, in unit of beacon interval */ -#define DTIM_INTERVAL_DEFAULT 3 - -/* Scale down delays to accommodate QT slow speed */ /* beacon interval, in unit of 1024TU */ -#define BEACON_INTERVAL_DEF_QT 20 -/* DTIM interval, in unit of beacon interval */ -#define DTIM_INTERVAL_DEF_QT 1 - -#define TBTT_ALIGN_LEEWAY_US 100 /* min leeway before first TBTT in us */ +#define BEACON_INTERVAL_DEFAULT 100 /* n-mode support capability */ /* 2x2 includes both 1x1 & 2x2 devices @@ -78,113 +55,76 @@ #define WL_11N_3x3 3 #define WL_11N_4x4 4 -/* define 11n feature disable flags */ -#define WLFEATURE_DISABLE_11N 0x00000001 -#define WLFEATURE_DISABLE_11N_STBC_TX 0x00000002 -#define WLFEATURE_DISABLE_11N_STBC_RX 0x00000004 -#define WLFEATURE_DISABLE_11N_SGI_TX 0x00000008 -#define WLFEATURE_DISABLE_11N_SGI_RX 0x00000010 -#define WLFEATURE_DISABLE_11N_AMPDU_TX 0x00000020 -#define WLFEATURE_DISABLE_11N_AMPDU_RX 0x00000040 -#define WLFEATURE_DISABLE_11N_GF 0x00000080 - -#define EDCF_ACI_MASK 0x60 -#define EDCF_ACI_SHIFT 5 -#define EDCF_ECWMIN_MASK 0x0f -#define EDCF_ECWMAX_SHIFT 4 -#define EDCF_AIFSN_MASK 0x0f -#define EDCF_AIFSN_MAX 15 -#define EDCF_ECWMAX_MASK 0xf0 - -#define EDCF_AC_BE_TXOP_STA 0x0000 -#define EDCF_AC_BK_TXOP_STA 0x0000 -#define EDCF_AC_VO_ACI_STA 0x62 -#define EDCF_AC_VO_ECW_STA 0x32 -#define EDCF_AC_VI_ACI_STA 0x42 -#define EDCF_AC_VI_ECW_STA 0x43 -#define EDCF_AC_BK_ECW_STA 0xA4 -#define EDCF_AC_VI_TXOP_STA 0x005e -#define EDCF_AC_VO_TXOP_STA 0x002f -#define EDCF_AC_BE_ACI_STA 0x03 -#define EDCF_AC_BE_ECW_STA 0xA4 -#define EDCF_AC_BK_ACI_STA 0x27 -#define EDCF_AC_VO_TXOP_AP 0x002f - -#define EDCF_TXOP2USEC(txop) ((txop) << 5) -#define EDCF_ECW2CW(exp) ((1 << (exp)) - 1) - -#define APHY_SYMBOL_TIME 4 -#define APHY_PREAMBLE_TIME 16 -#define APHY_SIGNAL_TIME 4 -#define APHY_SIFS_TIME 16 -#define APHY_SERVICE_NBITS 16 -#define APHY_TAIL_NBITS 6 -#define BPHY_SIFS_TIME 10 -#define BPHY_PLCP_SHORT_TIME 96 - -#define PREN_PREAMBLE 24 -#define PREN_MM_EXT 12 -#define PREN_PREAMBLE_EXT 4 +#define EDCF_ACI_MASK 0x60 +#define EDCF_ACI_SHIFT 5 +#define EDCF_ECWMIN_MASK 0x0f +#define EDCF_ECWMAX_SHIFT 4 +#define EDCF_AIFSN_MASK 0x0f +#define EDCF_AIFSN_MAX 15 +#define EDCF_ECWMAX_MASK 0xf0 + +#define EDCF_AC_BE_TXOP_STA 0x0000 +#define EDCF_AC_BK_TXOP_STA 0x0000 +#define EDCF_AC_VO_ACI_STA 0x62 +#define EDCF_AC_VO_ECW_STA 0x32 +#define EDCF_AC_VI_ACI_STA 0x42 +#define EDCF_AC_VI_ECW_STA 0x43 +#define EDCF_AC_BK_ECW_STA 0xA4 +#define EDCF_AC_VI_TXOP_STA 0x005e +#define EDCF_AC_VO_TXOP_STA 0x002f +#define EDCF_AC_BE_ACI_STA 0x03 +#define EDCF_AC_BE_ECW_STA 0xA4 +#define EDCF_AC_BK_ACI_STA 0x27 +#define EDCF_AC_VO_TXOP_AP 0x002f + +#define EDCF_TXOP2USEC(txop) ((txop) << 5) +#define EDCF_ECW2CW(exp) ((1 << (exp)) - 1) + +#define APHY_SYMBOL_TIME 4 +#define APHY_PREAMBLE_TIME 16 +#define APHY_SIGNAL_TIME 4 +#define APHY_SIFS_TIME 16 +#define APHY_SERVICE_NBITS 16 +#define APHY_TAIL_NBITS 6 +#define BPHY_SIFS_TIME 10 +#define BPHY_PLCP_SHORT_TIME 96 + +#define PREN_PREAMBLE 24 +#define PREN_MM_EXT 12 +#define PREN_PREAMBLE_EXT 4 #define DOT11_MAC_HDR_LEN 24 -#define DOT11_ACK_LEN 10 -#define DOT11_BA_LEN 4 +#define DOT11_ACK_LEN 10 +#define DOT11_BA_LEN 4 #define DOT11_OFDM_SIGNAL_EXTENSION 6 #define DOT11_MIN_FRAG_LEN 256 -#define DOT11_RTS_LEN 16 -#define DOT11_CTS_LEN 10 +#define DOT11_RTS_LEN 16 +#define DOT11_CTS_LEN 10 #define DOT11_BA_BITMAP_LEN 128 #define DOT11_MIN_BEACON_PERIOD 1 #define DOT11_MAX_BEACON_PERIOD 0xFFFF -#define DOT11_MAXNUMFRAGS 16 +#define DOT11_MAXNUMFRAGS 16 #define DOT11_MAX_FRAG_LEN 2346 -#define BPHY_PLCP_TIME 192 -#define RIFS_11N_TIME 2 +#define BPHY_PLCP_TIME 192 +#define RIFS_11N_TIME 2 -#define WME_VER 1 -#define WME_SUBTYPE_PARAM_IE 1 -#define WME_TYPE 2 -#define WME_OUI "\x00\x50\xf2" +#define AC_BE 0 +#define AC_BK 1 +#define AC_VI 2 +#define AC_VO 3 -#define AC_BE 0 -#define AC_BK 1 -#define AC_VI 2 -#define AC_VO 3 - -#define BCN_TMPL_LEN 512 /* length of the BCN template area */ +/* length of the BCN template area */ +#define BCN_TMPL_LEN 512 /* brcms_bss_info flag bit values */ -#define BRCMS_BSS_HT 0x0020 /* BSS is HT (MIMO) capable */ - -/* Flags used in brcms_c_txq_info.stopped */ -/* per prio flow control bits */ -#define TXQ_STOP_FOR_PRIOFC_MASK 0x000000FF -/* stop txq enqueue for packet drain */ -#define TXQ_STOP_FOR_PKT_DRAIN 0x00000100 -/* stop txq enqueue for ampdu flow control */ -#define TXQ_STOP_FOR_AMPDU_FLOW_CNTRL 0x00000200 - -#define BRCMS_HWRXOFF 38 /* chip rx buffer offset */ - -/* Find basic rate for a given rate */ -static u8 brcms_basic_rate(struct brcms_c_info *wlc, u32 rspec) -{ - if (is_mcs_rate(rspec)) - return wlc->band->basic_rate[mcs_table[rspec & RSPEC_RATE_MASK] - .leg_ofdm]; - return wlc->band->basic_rate[rspec & RSPEC_RATE_MASK]; -} +#define BRCMS_BSS_HT 0x0020 /* BSS is HT (MIMO) capable */ -static u16 frametype(u32 rspec, u8 mimoframe) -{ - if (is_mcs_rate(rspec)) - return mimoframe; - return is_cck_rate(rspec) ? FT_CCK : FT_OFDM; -} +/* chip rx buffer offset */ +#define BRCMS_HWRXOFF 38 /* rfdisable delay timer 500 ms, runs of ALP clock */ -#define RFDISABLE_DEFAULT 10000000 +#define RFDISABLE_DEFAULT 10000000 #define BRCMS_TEMPSENSE_PERIOD 10 /* 10 second timeout */ @@ -194,87 +134,83 @@ static u16 frametype(u32 rspec, u8 mimoframe) * These constants are used ONLY by wlc_prio2prec_map. Do not use them * elsewhere. */ -#define _BRCMS_PREC_NONE 0 /* None = - */ -#define _BRCMS_PREC_BK 2 /* BK - Background */ -#define _BRCMS_PREC_BE 4 /* BE - Best-effort */ -#define _BRCMS_PREC_EE 6 /* EE - Excellent-effort */ -#define _BRCMS_PREC_CL 8 /* CL - Controlled Load */ -#define _BRCMS_PREC_VI 10 /* Vi - Video */ -#define _BRCMS_PREC_VO 12 /* Vo - Voice */ -#define _BRCMS_PREC_NC 14 /* NC - Network Control */ - -/* The BSS is generating beacons in HW */ -#define BRCMS_BSSCFG_HW_BCN 0x20 - -#define SYNTHPU_DLY_APHY_US 3700 /* a phy synthpu_dly time in us */ -#define SYNTHPU_DLY_BPHY_US 1050 /* b/g phy synthpu_dly time in us */ -#define SYNTHPU_DLY_NPHY_US 2048 /* n phy REV3 synthpu_dly time in us */ -#define SYNTHPU_DLY_LPPHY_US 300 /* lpphy synthpu_dly time in us */ - -#define SYNTHPU_DLY_PHY_US_QT 100 /* QT synthpu_dly time in us */ - -#define ANTCNT 10 /* vanilla M_MAX_ANTCNT value */ +#define _BRCMS_PREC_NONE 0 /* None = - */ +#define _BRCMS_PREC_BK 2 /* BK - Background */ +#define _BRCMS_PREC_BE 4 /* BE - Best-effort */ +#define _BRCMS_PREC_EE 6 /* EE - Excellent-effort */ +#define _BRCMS_PREC_CL 8 /* CL - Controlled Load */ +#define _BRCMS_PREC_VI 10 /* Vi - Video */ +#define _BRCMS_PREC_VO 12 /* Vo - Voice */ +#define _BRCMS_PREC_NC 14 /* NC - Network Control */ + +/* synthpu_dly times in us */ +#define SYNTHPU_DLY_APHY_US 3700 +#define SYNTHPU_DLY_BPHY_US 1050 +#define SYNTHPU_DLY_NPHY_US 2048 +#define SYNTHPU_DLY_LPPHY_US 300 + +#define ANTCNT 10 /* vanilla M_MAX_ANTCNT val */ /* Per-AC retry limit register definitions; uses defs.h bitfield macros */ -#define EDCF_SHORT_S 0 -#define EDCF_SFB_S 4 -#define EDCF_LONG_S 8 -#define EDCF_LFB_S 12 -#define EDCF_SHORT_M BITFIELD_MASK(4) -#define EDCF_SFB_M BITFIELD_MASK(4) -#define EDCF_LONG_M BITFIELD_MASK(4) -#define EDCF_LFB_M BITFIELD_MASK(4) +#define EDCF_SHORT_S 0 +#define EDCF_SFB_S 4 +#define EDCF_LONG_S 8 +#define EDCF_LFB_S 12 +#define EDCF_SHORT_M BITFIELD_MASK(4) +#define EDCF_SFB_M BITFIELD_MASK(4) +#define EDCF_LONG_M BITFIELD_MASK(4) +#define EDCF_LFB_M BITFIELD_MASK(4) -#define RETRY_SHORT_DEF 7 /* Default Short retry Limit */ -#define RETRY_SHORT_MAX 255 /* Maximum Short retry Limit */ -#define RETRY_LONG_DEF 4 /* Default Long retry count */ -#define RETRY_SHORT_FB 3 /* Short count for fallback rate */ -#define RETRY_LONG_FB 2 /* Long count for fallback rate */ +#define RETRY_SHORT_DEF 7 /* Default Short retry Limit */ +#define RETRY_SHORT_MAX 255 /* Maximum Short retry Limit */ +#define RETRY_LONG_DEF 4 /* Default Long retry count */ +#define RETRY_SHORT_FB 3 /* Short count for fb rate */ +#define RETRY_LONG_FB 2 /* Long count for fb rate */ -#define APHY_CWMIN 15 -#define PHY_CWMAX 1023 +#define APHY_CWMIN 15 +#define PHY_CWMAX 1023 -#define EDCF_AIFSN_MIN 1 +#define EDCF_AIFSN_MIN 1 -#define FRAGNUM_MASK 0xF +#define FRAGNUM_MASK 0xF -#define APHY_SLOT_TIME 9 -#define BPHY_SLOT_TIME 20 +#define APHY_SLOT_TIME 9 +#define BPHY_SLOT_TIME 20 -#define WL_SPURAVOID_OFF 0 -#define WL_SPURAVOID_ON1 1 -#define WL_SPURAVOID_ON2 2 +#define WL_SPURAVOID_OFF 0 +#define WL_SPURAVOID_ON1 1 +#define WL_SPURAVOID_ON2 2 /* invalid core flags, use the saved coreflags */ -#define BRCMS_USE_COREFLAGS 0xffffffff +#define BRCMS_USE_COREFLAGS 0xffffffff /* values for PLCPHdr_override */ -#define BRCMS_PLCP_AUTO -1 -#define BRCMS_PLCP_SHORT 0 -#define BRCMS_PLCP_LONG 1 +#define BRCMS_PLCP_AUTO -1 +#define BRCMS_PLCP_SHORT 0 +#define BRCMS_PLCP_LONG 1 /* values for g_protection_override and n_protection_override */ #define BRCMS_PROTECTION_AUTO -1 #define BRCMS_PROTECTION_OFF 0 #define BRCMS_PROTECTION_ON 1 #define BRCMS_PROTECTION_MMHDR_ONLY 2 -#define BRCMS_PROTECTION_CTS_ONLY 3 +#define BRCMS_PROTECTION_CTS_ONLY 3 /* values for g_protection_control and n_protection_control */ -#define BRCMS_PROTECTION_CTL_OFF 0 +#define BRCMS_PROTECTION_CTL_OFF 0 #define BRCMS_PROTECTION_CTL_LOCAL 1 #define BRCMS_PROTECTION_CTL_OVERLAP 2 /* values for n_protection */ #define BRCMS_N_PROTECTION_OFF 0 #define BRCMS_N_PROTECTION_OPTIONAL 1 -#define BRCMS_N_PROTECTION_20IN40 2 +#define BRCMS_N_PROTECTION_20IN40 2 #define BRCMS_N_PROTECTION_MIXEDMODE 3 /* values for band specific 40MHz capabilities */ -#define BRCMS_N_BW_20ALL 0 -#define BRCMS_N_BW_40ALL 1 -#define BRCMS_N_BW_20IN2G_40IN5G 2 +#define BRCMS_N_BW_20ALL 0 +#define BRCMS_N_BW_40ALL 1 +#define BRCMS_N_BW_20IN2G_40IN5G 2 /* bitflags for SGI support (sgi_rx iovar) */ #define BRCMS_N_SGI_20 0x01 @@ -282,48 +218,42 @@ static u16 frametype(u32 rspec, u8 mimoframe) /* defines used by the nrate iovar */ /* MSC in use,indicates b0-6 holds an mcs */ -#define NRATE_MCS_INUSE 0x00000080 +#define NRATE_MCS_INUSE 0x00000080 /* rate/mcs value */ -#define NRATE_RATE_MASK 0x0000007f +#define NRATE_RATE_MASK 0x0000007f /* stf mode mask: siso, cdd, stbc, sdm */ -#define NRATE_STF_MASK 0x0000ff00 +#define NRATE_STF_MASK 0x0000ff00 /* stf mode shift */ -#define NRATE_STF_SHIFT 8 -/* bit indicates override both rate & mode */ -#define NRATE_OVERRIDE 0x80000000 +#define NRATE_STF_SHIFT 8 /* bit indicate to override mcs only */ -#define NRATE_OVERRIDE_MCS_ONLY 0x40000000 -#define NRATE_SGI_MASK 0x00800000 /* sgi mode */ -#define NRATE_SGI_SHIFT 23 /* sgi mode */ -#define NRATE_LDPC_CODING 0x00400000 /* bit indicates adv coding in use */ -#define NRATE_LDPC_SHIFT 22 /* ldpc shift */ +#define NRATE_OVERRIDE_MCS_ONLY 0x40000000 +#define NRATE_SGI_MASK 0x00800000 /* sgi mode */ +#define NRATE_SGI_SHIFT 23 /* sgi mode */ +#define NRATE_LDPC_CODING 0x00400000 /* adv coding in use */ +#define NRATE_LDPC_SHIFT 22 /* ldpc shift */ -#define NRATE_STF_SISO 0 /* stf mode SISO */ -#define NRATE_STF_CDD 1 /* stf mode CDD */ -#define NRATE_STF_STBC 2 /* stf mode STBC */ -#define NRATE_STF_SDM 3 /* stf mode SDM */ +#define NRATE_STF_SISO 0 /* stf mode SISO */ +#define NRATE_STF_CDD 1 /* stf mode CDD */ +#define NRATE_STF_STBC 2 /* stf mode STBC */ +#define NRATE_STF_SDM 3 /* stf mode SDM */ -#define MAX_DMA_SEGS 4 +#define MAX_DMA_SEGS 4 /* Max # of entries in Tx FIFO based on 4kb page size */ -#define NTXD 256 +#define NTXD 256 /* Max # of entries in Rx FIFO based on 4kb page size */ -#define NRXD 256 +#define NRXD 256 /* try to keep this # rbufs posted to the chip */ -#define NRXBUFPOST 32 +#define NRXBUFPOST 32 /* data msg txq hiwat mark */ -#define BRCMS_DATAHIWAT 50 - -/* bounded rx loops */ -#define RXBND 8 /* max # frames to process in brcms_c_recv() */ -#define TXSBND 8 /* max # tx status to process in wlc_txstatus() */ +#define BRCMS_DATAHIWAT 50 -/* - * 32 SSID chars, max of 4 chars for each SSID char "\xFF", plus NULL. - */ -#define SSID_FMT_BUF_LEN ((4 * IEEE80211_MAX_SSID_LEN) + 1) +/* max # frames to process in brcms_c_recv() */ +#define RXBND 8 +/* max # tx status to process in wlc_txstatus() */ +#define TXSBND 8 /* brcmu_format_flags() bit description structure */ struct brcms_c_bit_desc { @@ -405,13 +335,6 @@ static const u16 xmtfifo_sz[][NFIFO] = { {9, 58, 22, 14, 14, 5}, }; -static const u8 acbitmap2maxprio[] = { - PRIO_8021D_BE, PRIO_8021D_BE, PRIO_8021D_BK, PRIO_8021D_BK, - PRIO_8021D_VI, PRIO_8021D_VI, PRIO_8021D_VI, PRIO_8021D_VI, - PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO, - PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO -}; - #ifdef BCMDBG static const char * const fifo_names[] = { "AC_BK", "AC_BE", "AC_VI", "AC_VO", "BCMC", "ATIM" }; @@ -424,6 +347,22 @@ static const char fifo_names[6][0]; static struct brcms_c_info *wlc_info_dbg = (struct brcms_c_info *) (NULL); #endif +/* Find basic rate for a given rate */ +static u8 brcms_basic_rate(struct brcms_c_info *wlc, u32 rspec) +{ + if (is_mcs_rate(rspec)) + return wlc->band->basic_rate[mcs_table[rspec & RSPEC_RATE_MASK] + .leg_ofdm]; + return wlc->band->basic_rate[rspec & RSPEC_RATE_MASK]; +} + +static u16 frametype(u32 rspec, u8 mimoframe) +{ + if (is_mcs_rate(rspec)) + return mimoframe; + return is_cck_rate(rspec) ? FT_CCK : FT_OFDM; +} + /* currently the best mechanism for determining SIFS is the band in use */ static u16 get_sifs(struct brcms_band *band) { @@ -470,20 +409,6 @@ static int brcms_chspec_bw(u16 chanspec) return BRCMS_10_MHZ; } -/* - * return true if Minimum Power Consumption should - * be entered, false otherwise - */ -static bool brcms_c_is_non_delay_mpc(struct brcms_c_info *wlc) -{ - return false; -} - -static bool brcms_c_ismpc(struct brcms_c_info *wlc) -{ - return (wlc->mpc_delay_off == 0) && (brcms_c_is_non_delay_mpc(wlc)); -} - static void brcms_c_bsscfg_mfree(struct brcms_bss_cfg *cfg) { if (cfg == NULL) @@ -669,9 +594,8 @@ static void brcms_b_update_slot_timing(struct brcms_hardware *wlc_hw, * calculate frame duration of a given rate and length, return * time in usec unit */ -uint -brcms_c_calc_frame_time(struct brcms_c_info *wlc, u32 ratespec, - u8 preamble_type, uint mac_len) +static uint brcms_c_calc_frame_time(struct brcms_c_info *wlc, u32 ratespec, + u8 preamble_type, uint mac_len) { uint nsyms, dur = 0, Ndps, kNdps; uint rate = rspec2rate(ratespec); @@ -2352,13 +2276,6 @@ void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type) wlc_phy_antsel_type_set(wlc_hw->band->pi, antsel_type); } -static void brcms_c_fatal_error(struct brcms_c_info *wlc) -{ - wiphy_err(wlc->wiphy, "wl%d: fatal error, reinitializing\n", - wlc->pub->unit); - brcms_init(wlc->wl); -} - static void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw) { bool fatal = false; @@ -2414,7 +2331,7 @@ static void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw) } if (fatal) { - brcms_c_fatal_error(wlc_hw->wlc); /* big hammer */ + brcms_fatal_error(wlc_hw->wlc->wl); /* big hammer */ break; } else W_REG(®s->intctrlregs[idx].intstatus, @@ -2479,6 +2396,7 @@ void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask) W_REG(&wlc_hw->regs->macintmask, wlc->macintmask); } +/* assumes that the d11 MAC is enabled */ static void brcms_b_tx_fifo_suspend(struct brcms_hardware *wlc_hw, uint tx_fifo) { @@ -2535,11 +2453,12 @@ static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw, } } -static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool on, u32 flags) +/* precondition: requires the mac core to be enabled */ +static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx) { static const u8 null_ether_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; - if (on) { + if (mute_tx) { /* suspend tx fifos */ brcms_b_tx_fifo_suspend(wlc_hw, TX_DATA_FIFO); brcms_b_tx_fifo_suspend(wlc_hw, TX_CTL_FIFO); @@ -2561,14 +2480,20 @@ static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool on, u32 flags) wlc_hw->etheraddr); } - wlc_phy_mute_upd(wlc_hw->band->pi, on, flags); + wlc_phy_mute_upd(wlc_hw->band->pi, mute_tx, 0); - if (on) + if (mute_tx) brcms_c_ucode_mute_override_set(wlc_hw); else brcms_c_ucode_mute_override_clear(wlc_hw); } +void +brcms_c_mute(struct brcms_c_info *wlc, bool mute_tx) +{ + brcms_b_mute(wlc->hw, mute_tx); +} + /* * Read and clear macintmask and macintstatus and intstatus registers. * This routine should be called with interrupts off @@ -3437,8 +3362,7 @@ static void brcms_b_coreinit(struct brcms_c_info *wlc) } void -static brcms_b_init(struct brcms_hardware *wlc_hw, u16 chanspec, - bool mute) { +static brcms_b_init(struct brcms_hardware *wlc_hw, u16 chanspec) { u32 macintmask; bool fastclk; struct brcms_c_info *wlc = wlc_hw->wlc; @@ -3463,10 +3387,6 @@ static brcms_b_init(struct brcms_hardware *wlc_hw, u16 chanspec, /* core-specific initialization */ brcms_b_coreinit(wlc); - /* suspend the tx fifos and mute the phy for preism cac time */ - if (mute) - brcms_b_mute(wlc_hw, ON, PHY_MUTE_FOR_PREISM); - /* band-specific inits */ brcms_b_bsinit(wlc, chanspec); @@ -3979,7 +3899,7 @@ static void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, u16 chanspec) void brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec, - bool mute, struct txpwr_limits *txpwr) + bool mute_tx, struct txpwr_limits *txpwr) { uint bandunit; @@ -4005,7 +3925,7 @@ brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec, } } - wlc_phy_initcal_enable(wlc_hw->band->pi, !mute); + wlc_phy_initcal_enable(wlc_hw->band->pi, !mute_tx); if (!wlc_hw->up) { if (wlc_hw->clk) @@ -4017,7 +3937,7 @@ brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec, wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr, chanspec); /* Update muting of the channel */ - brcms_b_mute(wlc_hw, mute, 0); + brcms_b_mute(wlc_hw, mute_tx); } } @@ -4242,7 +4162,7 @@ void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, } } -void brcms_c_edcf_setparams(struct brcms_c_info *wlc, bool suspend) +static void brcms_c_edcf_setparams(struct brcms_c_info *wlc, bool suspend) { u16 aci; int i_ac; @@ -4277,17 +4197,6 @@ void brcms_c_edcf_setparams(struct brcms_c_info *wlc, bool suspend) } } -/* maintain LED behavior in down state */ -static void brcms_c_down_led_upd(struct brcms_c_info *wlc) -{ - /* - * maintain LEDs while in down state, turn on sbclk if - * not available yet. Turn on sbclk if necessary - */ - brcms_b_pllreq(wlc->hw, true, BRCMS_PLLREQ_FLIP); - brcms_b_pllreq(wlc->hw, false, BRCMS_PLLREQ_FLIP); -} - static void brcms_c_radio_monitor_start(struct brcms_c_info *wlc) { /* Don't start the timer if HWRADIO feature is disabled */ @@ -4299,28 +4208,6 @@ static void brcms_c_radio_monitor_start(struct brcms_c_info *wlc) brcms_add_timer(wlc->radio_timer, TIMER_INTERVAL_RADIOCHK, true); } -static void brcms_c_radio_disable(struct brcms_c_info *wlc) -{ - if (!wlc->pub->up) { - brcms_c_down_led_upd(wlc); - return; - } - - brcms_c_radio_monitor_start(wlc); - brcms_down(wlc->wl); -} - -static void brcms_c_radio_enable(struct brcms_c_info *wlc) -{ - if (wlc->pub->up) - return; - - if (brcms_deviceremoved(wlc)) - return; - - brcms_up(wlc->wl); -} - static bool brcms_c_radio_monitor_stop(struct brcms_c_info *wlc) { if (!wlc->radio_monitor) @@ -4343,18 +4230,6 @@ static void brcms_c_radio_hwdisable_upd(struct brcms_c_info *wlc) mboolclr(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE); } -/* - * centralized radio disable/enable function, - * invoke radio enable/disable after updating hwradio status - */ -static void brcms_c_radio_upd(struct brcms_c_info *wlc) -{ - if (wlc->pub->radio_disabled) - brcms_c_radio_disable(wlc); - else - brcms_c_radio_enable(wlc); -} - /* update hwradio status and return it */ bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc) { @@ -4376,12 +4251,7 @@ static void brcms_c_radio_timer(void *arg) return; } - /* cap mpc off count */ - if (wlc->mpc_offcnt < BRCMS_MPC_MAX_DELAYCNT) - wlc->mpc_offcnt++; - brcms_c_radio_hwdisable_upd(wlc); - brcms_c_radio_upd(wlc); } /* common low-level watchdog code */ @@ -4407,60 +4277,6 @@ static void brcms_b_watchdog(void *arg) wlc_phy_watchdog(wlc_hw->band->pi); } -static void brcms_c_radio_mpc_upd(struct brcms_c_info *wlc) -{ - bool mpc_radio, radio_state; - - /* - * Clear the WL_RADIO_MPC_DISABLE bit when mpc feature is disabled - * in case the WL_RADIO_MPC_DISABLE bit was set. Stop the radio - * monitor also when WL_RADIO_MPC_DISABLE is the only reason that - * the radio is going down. - */ - if (!wlc->mpc) { - if (!wlc->pub->radio_disabled) - return; - mboolclr(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE); - brcms_c_radio_upd(wlc); - if (!wlc->pub->radio_disabled) - brcms_c_radio_monitor_stop(wlc); - return; - } - - /* - * sync ismpc logic with WL_RADIO_MPC_DISABLE bit in - * wlc->pub->radio_disabled to go ON, always call radio_upd - * synchronously to go OFF, postpone radio_upd to later when - * context is safe(e.g. watchdog) - */ - radio_state = - (mboolisset(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE) ? OFF : - ON); - mpc_radio = (brcms_c_ismpc(wlc) == true) ? OFF : ON; - - if (radio_state == ON && mpc_radio == OFF) - wlc->mpc_delay_off = wlc->mpc_dlycnt; - else if (radio_state == OFF && mpc_radio == ON) { - mboolclr(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE); - brcms_c_radio_upd(wlc); - if (wlc->mpc_offcnt < BRCMS_MPC_THRESHOLD) - wlc->mpc_dlycnt = BRCMS_MPC_MAX_DELAYCNT; - else - wlc->mpc_dlycnt = BRCMS_MPC_MIN_DELAYCNT; - } - /* - * Below logic is meant to capture the transition from mpc off - * to mpc on for reasons other than wlc->mpc_delay_off keeping - * the mpc off. In that case reset wlc->mpc_delay_off to - * wlc->mpc_dlycnt, so that we restart the countdown of mpc_delay_off - */ - if ((wlc->prev_non_delay_mpc == false) && - (brcms_c_is_non_delay_mpc(wlc) == true) && wlc->mpc_delay_off) - wlc->mpc_delay_off = wlc->mpc_dlycnt; - - wlc->prev_non_delay_mpc = brcms_c_is_non_delay_mpc(wlc); -} - /* common watchdog code */ static void brcms_c_watchdog(void *arg) { @@ -4481,21 +4297,7 @@ static void brcms_c_watchdog(void *arg) /* increment second count */ wlc->pub->now++; - /* delay radio disable */ - if (wlc->mpc_delay_off) { - if (--wlc->mpc_delay_off == 0) { - mboolset(wlc->pub->radio_disabled, - WL_RADIO_MPC_DISABLE); - if (wlc->mpc && brcms_c_ismpc(wlc)) - wlc->mpc_offcnt = 0; - } - } - - /* mpc sync */ - brcms_c_radio_mpc_upd(wlc); - /* radio sync: sw/hw/mpc --> radio_disable/radio_enable */ brcms_c_radio_hwdisable_upd(wlc); - brcms_c_radio_upd(wlc); /* if radio is disable, driver may be down, quit here */ if (wlc->pub->radio_disabled) return; @@ -4599,9 +4401,6 @@ static void brcms_c_info_init(struct brcms_c_info *wlc, int unit) /* WME QoS mode is Auto by default */ wlc->pub->_ampdu = AMPDU_AGG_HOST; wlc->pub->bcmerror = 0; - - /* initialize mpc delay */ - wlc->mpc_delay_off = wlc->mpc_dlycnt = BRCMS_MPC_MIN_DELAYCNT; } static uint brcms_c_attach_module(struct brcms_c_info *wlc) @@ -5259,9 +5058,6 @@ static void brcms_c_ap_upd(struct brcms_c_info *wlc) { /* STA-BSS; short capable */ wlc->PLCPHdr_override = BRCMS_PLCP_SHORT; - - /* fixup mpc */ - wlc->mpc = true; } /* Initialize just the hardware when coming out of POR or S3/S5 system states */ @@ -5575,7 +5371,6 @@ uint brcms_c_down(struct brcms_c_info *wlc) if (!wlc->pub->up) return callbacks; - /* in between, mpc could try to bring down again.. */ wlc->going_down = true; callbacks += brcms_b_bmac_down_prep(wlc->hw); @@ -6103,7 +5898,6 @@ void brcms_c_print_txdesc(struct d11txh *txh) u8 *rtsph = txh->RTSPhyHeader; struct ieee80211_rts rts = txh->rts_frame; - char hexbuf[256]; /* add plcp header along with txh descriptor */ printk(KERN_DEBUG "Raw TxDesc + plcp header:\n"); @@ -6124,17 +5918,16 @@ void brcms_c_print_txdesc(struct d11txh *txh) printk(KERN_DEBUG "XtraFrameTypes: %04x ", xtraft); printk(KERN_DEBUG "\n"); - brcmu_format_hex(hexbuf, iv, sizeof(txh->IV)); - printk(KERN_DEBUG "SecIV: %s\n", hexbuf); - brcmu_format_hex(hexbuf, ra, sizeof(txh->TxFrameRA)); - printk(KERN_DEBUG "RA: %s\n", hexbuf); + print_hex_dump_bytes("SecIV:", DUMP_PREFIX_OFFSET, iv, sizeof(txh->IV)); + print_hex_dump_bytes("RA:", DUMP_PREFIX_OFFSET, + ra, sizeof(txh->TxFrameRA)); printk(KERN_DEBUG "Fb FES Time: %04x ", tfestfb); - brcmu_format_hex(hexbuf, rtspfb, sizeof(txh->RTSPLCPFallback)); - printk(KERN_DEBUG "RTS PLCP: %s ", hexbuf); + print_hex_dump_bytes("Fb RTS PLCP:", DUMP_PREFIX_OFFSET, + rtspfb, sizeof(txh->RTSPLCPFallback)); printk(KERN_DEBUG "RTS DUR: %04x ", rtsdfb); - brcmu_format_hex(hexbuf, fragpfb, sizeof(txh->FragPLCPFallback)); - printk(KERN_DEBUG "PLCP: %s ", hexbuf); + print_hex_dump_bytes("PLCP:", DUMP_PREFIX_OFFSET, + fragpfb, sizeof(txh->FragPLCPFallback)); printk(KERN_DEBUG "DUR: %04x", fragdfb); printk(KERN_DEBUG "\n"); @@ -6149,18 +5942,18 @@ void brcms_c_print_txdesc(struct d11txh *txh) printk(KERN_DEBUG "MaxAggbyte_fb: %04x\n", mabyte_f); printk(KERN_DEBUG "MinByte: %04x\n", mmbyte); - brcmu_format_hex(hexbuf, rtsph, sizeof(txh->RTSPhyHeader)); - printk(KERN_DEBUG "RTS PLCP: %s ", hexbuf); - brcmu_format_hex(hexbuf, (u8 *) &rts, sizeof(txh->rts_frame)); - printk(KERN_DEBUG "RTS Frame: %s", hexbuf); + print_hex_dump_bytes("RTS PLCP:", DUMP_PREFIX_OFFSET, + rtsph, sizeof(txh->RTSPhyHeader)); + print_hex_dump_bytes("RTS Frame:", DUMP_PREFIX_OFFSET, + (u8 *)&rts, sizeof(txh->rts_frame)); printk(KERN_DEBUG "\n"); } #endif /* defined(BCMDBG) */ #if defined(BCMDBG) -int +static int brcms_c_format_flags(const struct brcms_c_bit_desc *bd, u32 flags, char *buf, - int len) + int len) { int i; char *p = buf; @@ -8253,12 +8046,6 @@ int brcms_c_get_tx_power(struct brcms_c_info *wlc) return (int)(qdbm / BRCMS_TXPWR_DB_FACTOR); } -void brcms_c_set_radio_mpc(struct brcms_c_info *wlc, bool mpc) -{ - wlc->mpc = mpc; - brcms_c_radio_mpc_upd(wlc); -} - /* Process received frames */ /* * Return true if more frames need to be processed. false otherwise. @@ -8328,21 +8115,17 @@ static bool brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound) { struct sk_buff *p; - struct sk_buff *head = NULL; - struct sk_buff *tail = NULL; + struct sk_buff *next = NULL; + struct sk_buff_head recv_frames; + uint n = 0; uint bound_limit = bound ? RXBND : -1; BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); - /* gather received frames */ - while ((p = dma_rx(wlc_hw->di[fifo]))) { + skb_queue_head_init(&recv_frames); - if (!tail) - head = tail = p; - else { - tail->prev = p; - tail = p; - } + /* gather received frames */ + while (dma_rx(wlc_hw->di[fifo], &recv_frames)) { /* !give others some time to run! */ if (++n >= bound_limit) @@ -8353,12 +8136,11 @@ brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound) dma_rxfill(wlc_hw->di[fifo]); /* process each frame */ - while ((p = head) != NULL) { + skb_queue_walk_safe(&recv_frames, p, next) { struct d11rxhdr_le *rxh_le; struct d11rxhdr *rxh; - head = head->prev; - p->prev = NULL; + skb_unlink(p, &recv_frames); rxh_le = (struct d11rxhdr_le *)p->data; rxh = (struct d11rxhdr *)p->data; @@ -8448,8 +8230,7 @@ bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) printk_once("%s : PSM Watchdog, chipid 0x%x, chiprev 0x%x\n", __func__, wlc_hw->sih->chip, wlc_hw->sih->chiprev); - /* big hammer */ - brcms_init(wlc->wl); + brcms_fatal_error(wlc_hw->wlc->wl); } /* gptimer timeout */ @@ -8470,15 +8251,14 @@ bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) return wlc->macintstatus != 0; fatal: - brcms_init(wlc->wl); + brcms_fatal_error(wlc_hw->wlc->wl); return wlc->macintstatus != 0; } -void brcms_c_init(struct brcms_c_info *wlc) +void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx) { struct d11regs __iomem *regs; u16 chanspec; - bool mute = false; BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); @@ -8494,7 +8274,7 @@ void brcms_c_init(struct brcms_c_info *wlc) else chanspec = brcms_c_init_chanspec(wlc); - brcms_b_init(wlc->hw, chanspec, mute); + brcms_b_init(wlc->hw, chanspec); /* update beacon listen interval */ brcms_c_bcn_li_upd(wlc); @@ -8560,15 +8340,16 @@ void brcms_c_init(struct brcms_c_info *wlc) /* ..now really unleash hell (allow the MAC out of suspend) */ brcms_c_enable_mac(wlc); + /* suspend the tx fifos and mute the phy for preism cac time */ + if (mute_tx) + brcms_b_mute(wlc->hw, true); + /* clear tx flow control */ brcms_c_txflowcontrol_reset(wlc); /* enable the RF Disable Delay timer */ W_REG(&wlc->regs->rfdisabledly, RFDISABLE_DEFAULT); - /* initialize mpc delay */ - wlc->mpc_delay_off = wlc->mpc_dlycnt = BRCMS_MPC_MIN_DELAYCNT; - /* * Initialize WME parameters; if they haven't been set by some other * mechanism (IOVar, etc) then read them from the hardware. @@ -8754,8 +8535,6 @@ brcms_c_attach(struct brcms_info *wl, u16 vendor, u16 device, uint unit, brcms_c_ht_update_sgi_rx(wlc, 0); } - /* initialize radio_mpc_disable according to wlc->mpc */ - brcms_c_radio_mpc_upd(wlc); brcms_b_antsel_set(wlc->hw, wlc->asi->antsel_avail); if (perr) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.h b/drivers/net/wireless/brcm80211/brcmsmac/main.h index c0e0fcfdfaf8..9a7535d3838d 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.h @@ -427,11 +427,6 @@ struct brcms_txq_info { * bandinit_pending: track band init in auto band. * radio_monitor: radio timer is running. * going_down: down path intermediate variable. - * mpc: enable minimum power consumption. - * mpc_dlycnt: # of watchdog cnt before turn disable radio. - * mpc_offcnt: # of watchdog cnt that radio is disabled. - * mpc_delay_off: delay radio disable by # of watchdog cnt. - * prev_non_delay_mpc: prev state brcms_c_is_non_delay_mpc. * wdtimer: timer for watchdog routine. * radio_timer: timer for hw radio button monitor routine. * monitor: monitor (MPDU sniffing) mode. @@ -522,12 +517,6 @@ struct brcms_c_info { bool radio_monitor; bool going_down; - bool mpc; - u8 mpc_dlycnt; - u8 mpc_offcnt; - u8 mpc_delay_off; - u8 prev_non_delay_mpc; - struct brcms_timer *wdtimer; struct brcms_timer *radio_timer; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c index a3149254cbcd..2faea509f017 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c @@ -112,7 +112,7 @@ static const struct chan_info_basic chan_info_all[] = { {216, 50800} }; -const u8 ofdm_rate_lookup[] = { +static const u8 ofdm_rate_lookup[] = { BRCM_RATE_48M, BRCM_RATE_24M, diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h index bea85241a244..5f9478b1c993 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h @@ -774,11 +774,6 @@ struct brcms_phy { s16 nphy_noise_win[PHY_CORE_MAX][PHY_NOISE_WINDOW_SZ]; u8 nphy_noise_index; - u8 nphy_txpid2g[PHY_CORE_NUM_2]; - u8 nphy_txpid5g[PHY_CORE_NUM_2]; - u8 nphy_txpid5gl[PHY_CORE_NUM_2]; - u8 nphy_txpid5gh[PHY_CORE_NUM_2]; - bool nphy_gain_boost; bool nphy_elna_gain_config; u16 old_bphy_test; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c index cd19c2f7a347..ec9b56639d54 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c @@ -29,6 +29,7 @@ #include "phy_radio.h" #include "phyreg_n.h" #include "phytbl_n.h" +#include "soc.h" #define READ_RADIO_REG2(pi, radio_type, jspace, core, reg_name) \ read_radio_reg(pi, radio_type##_##jspace##_##reg_name | \ @@ -14417,12 +14418,6 @@ static void wlc_phy_txpwr_srom_read_ppr_nphy(struct brcms_phy *pi) switch (band_num) { case 0: - pi->nphy_txpid2g[PHY_CORE_0] = - (u8) wlapi_getintvar(shim, - BRCMS_SROM_TXPID2GA0); - pi->nphy_txpid2g[PHY_CORE_1] = - (u8) wlapi_getintvar(shim, - BRCMS_SROM_TXPID2GA1); pi->nphy_pwrctrl_info[PHY_CORE_0].max_pwr_2g = (s8) wlapi_getintvar(shim, BRCMS_SROM_MAXP2GA0); @@ -14486,12 +14481,6 @@ static void wlc_phy_txpwr_srom_read_ppr_nphy(struct brcms_phy *pi) break; case 1: - pi->nphy_txpid5g[PHY_CORE_0] = - (u8) wlapi_getintvar(shim, - BRCMS_SROM_TXPID5GA0); - pi->nphy_txpid5g[PHY_CORE_1] = - (u8) wlapi_getintvar(shim, - BRCMS_SROM_TXPID5GA1); pi->nphy_pwrctrl_info[PHY_CORE_0].max_pwr_5gm = (s8) wlapi_getintvar(shim, BRCMS_SROM_MAXP5GA0); pi->nphy_pwrctrl_info[PHY_CORE_1].max_pwr_5gm = @@ -14551,12 +14540,6 @@ static void wlc_phy_txpwr_srom_read_ppr_nphy(struct brcms_phy *pi) break; case 2: - pi->nphy_txpid5gl[0] = - (u8) wlapi_getintvar(shim, - BRCMS_SROM_TXPID5GLA0); - pi->nphy_txpid5gl[1] = - (u8) wlapi_getintvar(shim, - BRCMS_SROM_TXPID5GLA1); pi->nphy_pwrctrl_info[0].max_pwr_5gl = (s8) wlapi_getintvar(shim, BRCMS_SROM_MAXP5GLA0); @@ -14615,12 +14598,6 @@ static void wlc_phy_txpwr_srom_read_ppr_nphy(struct brcms_phy *pi) break; case 3: - pi->nphy_txpid5gh[0] = - (u8) wlapi_getintvar(shim, - BRCMS_SROM_TXPID5GHA0); - pi->nphy_txpid5gh[1] = - (u8) wlapi_getintvar(shim, - BRCMS_SROM_TXPID5GHA1); pi->nphy_pwrctrl_info[0].max_pwr_5gh = (s8) wlapi_getintvar(shim, BRCMS_SROM_MAXP5GHA0); @@ -27994,20 +27971,11 @@ void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi) chan_freq_range = wlc_phy_get_chan_freq_range_nphy(pi, 0); switch (chan_freq_range) { case WL_CHAN_FREQ_RANGE_2G: - txpi[0] = pi->nphy_txpid2g[0]; - txpi[1] = pi->nphy_txpid2g[1]; - break; case WL_CHAN_FREQ_RANGE_5GL: - txpi[0] = pi->nphy_txpid5gl[0]; - txpi[1] = pi->nphy_txpid5gl[1]; - break; case WL_CHAN_FREQ_RANGE_5GM: - txpi[0] = pi->nphy_txpid5g[0]; - txpi[1] = pi->nphy_txpid5g[1]; - break; case WL_CHAN_FREQ_RANGE_5GH: - txpi[0] = pi->nphy_txpid5gh[0]; - txpi[1] = pi->nphy_txpid5gh[1]; + txpi[0] = 0; + txpi[1] = 0; break; default: txpi[0] = txpi[1] = 91; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pmu.c b/drivers/net/wireless/brcm80211/brcmsmac/pmu.c index 3b36e3acfd74..12ba575f5785 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/pmu.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/pmu.c @@ -23,6 +23,7 @@ #include "pub.h" #include "aiutils.h" #include "pmu.h" +#include "soc.h" /* * external LPO crystal frequency diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pub.h b/drivers/net/wireless/brcm80211/brcmsmac/pub.h index 37bb2dcc113f..022523a5a532 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/pub.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/pub.h @@ -170,22 +170,6 @@ enum brcms_srom_id { BRCMS_SROM_TSSIPOS2G, BRCMS_SROM_TSSIPOS5G, BRCMS_SROM_TXCHAIN, - BRCMS_SROM_TXPID2GA0, - BRCMS_SROM_TXPID2GA1, - BRCMS_SROM_TXPID2GA2, - BRCMS_SROM_TXPID2GA3, - BRCMS_SROM_TXPID5GA0, - BRCMS_SROM_TXPID5GA1, - BRCMS_SROM_TXPID5GA2, - BRCMS_SROM_TXPID5GA3, - BRCMS_SROM_TXPID5GHA0, - BRCMS_SROM_TXPID5GHA1, - BRCMS_SROM_TXPID5GHA2, - BRCMS_SROM_TXPID5GHA3, - BRCMS_SROM_TXPID5GLA0, - BRCMS_SROM_TXPID5GLA1, - BRCMS_SROM_TXPID5GLA2, - BRCMS_SROM_TXPID5GLA3, /* * per-path identifiers (see srom.c) */ @@ -225,10 +209,6 @@ enum brcms_srom_id { BRCMS_SROM_PA2GW2A1, BRCMS_SROM_PA2GW2A2, BRCMS_SROM_PA2GW2A3, - BRCMS_SROM_PA2GW3A0, - BRCMS_SROM_PA2GW3A1, - BRCMS_SROM_PA2GW3A2, - BRCMS_SROM_PA2GW3A3, BRCMS_SROM_PA5GHW0A0, BRCMS_SROM_PA5GHW0A1, BRCMS_SROM_PA5GHW0A2, @@ -241,10 +221,6 @@ enum brcms_srom_id { BRCMS_SROM_PA5GHW2A1, BRCMS_SROM_PA5GHW2A2, BRCMS_SROM_PA5GHW2A3, - BRCMS_SROM_PA5GHW3A0, - BRCMS_SROM_PA5GHW3A1, - BRCMS_SROM_PA5GHW3A2, - BRCMS_SROM_PA5GHW3A3, BRCMS_SROM_PA5GLW0A0, BRCMS_SROM_PA5GLW0A1, BRCMS_SROM_PA5GLW0A2, @@ -257,10 +233,6 @@ enum brcms_srom_id { BRCMS_SROM_PA5GLW2A1, BRCMS_SROM_PA5GLW2A2, BRCMS_SROM_PA5GLW2A3, - BRCMS_SROM_PA5GLW3A0, - BRCMS_SROM_PA5GLW3A1, - BRCMS_SROM_PA5GLW3A2, - BRCMS_SROM_PA5GLW3A3, BRCMS_SROM_PA5GW0A0, BRCMS_SROM_PA5GW0A1, BRCMS_SROM_PA5GW0A2, @@ -273,10 +245,6 @@ enum brcms_srom_id { BRCMS_SROM_PA5GW2A1, BRCMS_SROM_PA5GW2A2, BRCMS_SROM_PA5GW2A3, - BRCMS_SROM_PA5GW3A0, - BRCMS_SROM_PA5GW3A1, - BRCMS_SROM_PA5GW3A2, - BRCMS_SROM_PA5GW3A3, }; #define BRCMS_NUMRATES 16 /* max # of rates in a rateset */ @@ -572,7 +540,7 @@ extern int brcms_c_up(struct brcms_c_info *wlc); extern uint brcms_c_down(struct brcms_c_info *wlc); extern bool brcms_c_chipmatch(u16 vendor, u16 device); -extern void brcms_c_init(struct brcms_c_info *wlc); +extern void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx); extern void brcms_c_reset(struct brcms_c_info *wlc); extern void brcms_c_intrson(struct brcms_c_info *wlc); @@ -628,7 +596,7 @@ extern void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval); extern int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr); extern int brcms_c_get_tx_power(struct brcms_c_info *wlc); -extern void brcms_c_set_radio_mpc(struct brcms_c_info *wlc, bool mpc); extern bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc); +extern void brcms_c_mute(struct brcms_c_info *wlc, bool on); #endif /* _BRCM_PUB_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/rate.h b/drivers/net/wireless/brcm80211/brcmsmac/rate.h index e7b9dc2f2731..980d578825cc 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/rate.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/rate.h @@ -19,6 +19,7 @@ #include "types.h" #include "d11.h" +#include "phy_hal.h" extern const u8 rate_info[]; extern const struct brcms_c_rateset cck_ofdm_mimo_rates; @@ -198,11 +199,9 @@ static inline u8 cck_rspec(u8 cck) /* Convert encoded rate value in plcp header to numerical rates in 500 KHz * increments */ -extern const u8 ofdm_rate_lookup[]; - static inline u8 ofdm_phy2mac_rate(u8 rlpt) { - return ofdm_rate_lookup[rlpt & 0x7]; + return wlc_phy_get_ofdm_rate_lookup()[rlpt & 0x7]; } static inline u8 cck_phy2mac_rate(u8 signal) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/srom.c b/drivers/net/wireless/brcm80211/brcmsmac/srom.c index 99f791048e84..0539a6a831c5 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/srom.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/srom.c @@ -28,6 +28,7 @@ #include "aiutils.h" #include "otp.h" #include "srom.h" +#include "soc.h" /* * SROM CRC8 polynomial value: @@ -62,9 +63,6 @@ #define SROM_MACHI_ET1 42 #define SROM_MACMID_ET1 43 #define SROM_MACLO_ET1 44 -#define SROM3_MACHI 37 -#define SROM3_MACMID 38 -#define SROM3_MACLO 39 #define SROM_BXARSSI2G 40 #define SROM_BXARSSI5G 41 @@ -101,7 +99,6 @@ #define SROM_BFL 57 #define SROM_BFL2 28 -#define SROM3_BFL2 61 #define SROM_AG10 58 @@ -109,99 +106,16 @@ #define SROM_OPO 60 -#define SROM3_LEDDC 62 - #define SROM_CRCREV 63 -/* SROM Rev 4: Reallocate the software part of the srom to accommodate - * MIMO features. It assumes up to two PCIE functions and 440 bytes - * of usable srom i.e. the usable storage in chips with OTP that - * implements hardware redundancy. - */ - #define SROM4_WORDS 220 -#define SROM4_SIGN 32 -#define SROM4_SIGNATURE 0x5372 - -#define SROM4_BREV 33 - -#define SROM4_BFL0 34 -#define SROM4_BFL1 35 -#define SROM4_BFL2 36 -#define SROM4_BFL3 37 -#define SROM5_BFL0 37 -#define SROM5_BFL1 38 -#define SROM5_BFL2 39 -#define SROM5_BFL3 40 - -#define SROM4_MACHI 38 -#define SROM4_MACMID 39 -#define SROM4_MACLO 40 -#define SROM5_MACHI 41 -#define SROM5_MACMID 42 -#define SROM5_MACLO 43 - -#define SROM4_CCODE 41 -#define SROM4_REGREV 42 -#define SROM5_CCODE 34 -#define SROM5_REGREV 35 - -#define SROM4_LEDBH10 43 -#define SROM4_LEDBH32 44 -#define SROM5_LEDBH10 59 -#define SROM5_LEDBH32 60 - -#define SROM4_LEDDC 45 -#define SROM5_LEDDC 45 - -#define SROM4_AA 46 - -#define SROM4_AG10 47 -#define SROM4_AG32 48 - -#define SROM4_TXPID2G 49 -#define SROM4_TXPID5G 51 -#define SROM4_TXPID5GL 53 -#define SROM4_TXPID5GH 55 - -#define SROM4_TXRXC 61 #define SROM4_TXCHAIN_MASK 0x000f -#define SROM4_TXCHAIN_SHIFT 0 #define SROM4_RXCHAIN_MASK 0x00f0 -#define SROM4_RXCHAIN_SHIFT 4 #define SROM4_SWITCH_MASK 0xff00 -#define SROM4_SWITCH_SHIFT 8 /* Per-path fields */ #define MAX_PATH_SROM 4 -#define SROM4_PATH0 64 -#define SROM4_PATH1 87 -#define SROM4_PATH2 110 -#define SROM4_PATH3 133 - -#define SROM4_2G_ITT_MAXP 0 -#define SROM4_2G_PA 1 -#define SROM4_5G_ITT_MAXP 5 -#define SROM4_5GLH_MAXP 6 -#define SROM4_5G_PA 7 -#define SROM4_5GL_PA 11 -#define SROM4_5GH_PA 15 - -/* All the miriad power offsets */ -#define SROM4_2G_CCKPO 156 -#define SROM4_2G_OFDMPO 157 -#define SROM4_5G_OFDMPO 159 -#define SROM4_5GL_OFDMPO 161 -#define SROM4_5GH_OFDMPO 163 -#define SROM4_2G_MCSPO 165 -#define SROM4_5G_MCSPO 173 -#define SROM4_5GL_MCSPO 181 -#define SROM4_5GH_MCSPO 189 -#define SROM4_CDDPO 197 -#define SROM4_STBCPO 198 -#define SROM4_BW40PO 199 -#define SROM4_BWDUPPO 200 #define SROM4_CRCREV 219 @@ -424,103 +338,32 @@ struct brcms_varbuf { static const struct brcms_sromvar pci_sromvars[] = { {BRCMS_SROM_DEVID, 0xffffff00, SRFL_PRHEX | SRFL_NOVAR, PCI_F0DEVID, 0xffff}, - {BRCMS_SROM_BOARDREV, 0x0000000e, SRFL_PRHEX, SROM_AABREV, - SROM_BR_MASK}, - {BRCMS_SROM_BOARDREV, 0x000000f0, SRFL_PRHEX, SROM4_BREV, 0xffff}, {BRCMS_SROM_BOARDREV, 0xffffff00, SRFL_PRHEX, SROM8_BREV, 0xffff}, - {BRCMS_SROM_BOARDFLAGS, 0x00000002, SRFL_PRHEX, SROM_BFL, 0xffff}, - {BRCMS_SROM_BOARDFLAGS, 0x00000004, SRFL_PRHEX | SRFL_MORE, SROM_BFL, - 0xffff}, - {BRCMS_SROM_CONT, 0, 0, SROM_BFL2, 0xffff}, - {BRCMS_SROM_BOARDFLAGS, 0x00000008, SRFL_PRHEX | SRFL_MORE, SROM_BFL, - 0xffff}, - {BRCMS_SROM_CONT, 0, 0, SROM3_BFL2, 0xffff}, - {BRCMS_SROM_BOARDFLAGS, 0x00000010, SRFL_PRHEX | SRFL_MORE, SROM4_BFL0, - 0xffff}, - {BRCMS_SROM_CONT, 0, 0, SROM4_BFL1, 0xffff}, - {BRCMS_SROM_BOARDFLAGS, 0x000000e0, SRFL_PRHEX | SRFL_MORE, SROM5_BFL0, - 0xffff}, - {BRCMS_SROM_CONT, 0, 0, SROM5_BFL1, 0xffff}, {BRCMS_SROM_BOARDFLAGS, 0xffffff00, SRFL_PRHEX | SRFL_MORE, SROM8_BFL0, 0xffff}, {BRCMS_SROM_CONT, 0, 0, SROM8_BFL1, 0xffff}, - {BRCMS_SROM_BOARDFLAGS2, 0x00000010, SRFL_PRHEX | SRFL_MORE, SROM4_BFL2, - 0xffff}, - {BRCMS_SROM_CONT, 0, 0, SROM4_BFL3, 0xffff}, - {BRCMS_SROM_BOARDFLAGS2, 0x000000e0, SRFL_PRHEX | SRFL_MORE, SROM5_BFL2, - 0xffff}, - {BRCMS_SROM_CONT, 0, 0, SROM5_BFL3, 0xffff}, {BRCMS_SROM_BOARDFLAGS2, 0xffffff00, SRFL_PRHEX | SRFL_MORE, SROM8_BFL2, 0xffff}, {BRCMS_SROM_CONT, 0, 0, SROM8_BFL3, 0xffff}, {BRCMS_SROM_BOARDTYPE, 0xfffffffc, SRFL_PRHEX, SROM_SSID, 0xffff}, - {BRCMS_SROM_BOARDNUM, 0x00000006, 0, SROM_MACLO_IL0, 0xffff}, - {BRCMS_SROM_BOARDNUM, 0x00000008, 0, SROM3_MACLO, 0xffff}, - {BRCMS_SROM_BOARDNUM, 0x00000010, 0, SROM4_MACLO, 0xffff}, - {BRCMS_SROM_BOARDNUM, 0x000000e0, 0, SROM5_MACLO, 0xffff}, {BRCMS_SROM_BOARDNUM, 0xffffff00, 0, SROM8_MACLO, 0xffff}, - {BRCMS_SROM_CC, 0x00000002, 0, SROM_AABREV, SROM_CC_MASK}, - {BRCMS_SROM_REGREV, 0x00000008, 0, SROM_OPO, 0xff00}, - {BRCMS_SROM_REGREV, 0x00000010, 0, SROM4_REGREV, 0x00ff}, - {BRCMS_SROM_REGREV, 0x000000e0, 0, SROM5_REGREV, 0x00ff}, {BRCMS_SROM_REGREV, 0xffffff00, 0, SROM8_REGREV, 0x00ff}, - {BRCMS_SROM_LEDBH0, 0x0000000e, SRFL_NOFFS, SROM_LEDBH10, 0x00ff}, - {BRCMS_SROM_LEDBH1, 0x0000000e, SRFL_NOFFS, SROM_LEDBH10, 0xff00}, - {BRCMS_SROM_LEDBH2, 0x0000000e, SRFL_NOFFS, SROM_LEDBH32, 0x00ff}, - {BRCMS_SROM_LEDBH3, 0x0000000e, SRFL_NOFFS, SROM_LEDBH32, 0xff00}, - {BRCMS_SROM_LEDBH0, 0x00000010, SRFL_NOFFS, SROM4_LEDBH10, 0x00ff}, - {BRCMS_SROM_LEDBH1, 0x00000010, SRFL_NOFFS, SROM4_LEDBH10, 0xff00}, - {BRCMS_SROM_LEDBH2, 0x00000010, SRFL_NOFFS, SROM4_LEDBH32, 0x00ff}, - {BRCMS_SROM_LEDBH3, 0x00000010, SRFL_NOFFS, SROM4_LEDBH32, 0xff00}, - {BRCMS_SROM_LEDBH0, 0x000000e0, SRFL_NOFFS, SROM5_LEDBH10, 0x00ff}, - {BRCMS_SROM_LEDBH1, 0x000000e0, SRFL_NOFFS, SROM5_LEDBH10, 0xff00}, - {BRCMS_SROM_LEDBH2, 0x000000e0, SRFL_NOFFS, SROM5_LEDBH32, 0x00ff}, - {BRCMS_SROM_LEDBH3, 0x000000e0, SRFL_NOFFS, SROM5_LEDBH32, 0xff00}, {BRCMS_SROM_LEDBH0, 0xffffff00, SRFL_NOFFS, SROM8_LEDBH10, 0x00ff}, {BRCMS_SROM_LEDBH1, 0xffffff00, SRFL_NOFFS, SROM8_LEDBH10, 0xff00}, {BRCMS_SROM_LEDBH2, 0xffffff00, SRFL_NOFFS, SROM8_LEDBH32, 0x00ff}, {BRCMS_SROM_LEDBH3, 0xffffff00, SRFL_NOFFS, SROM8_LEDBH32, 0xff00}, - {BRCMS_SROM_PA0B0, 0x0000000e, SRFL_PRHEX, SROM_WL0PAB0, 0xffff}, - {BRCMS_SROM_PA0B1, 0x0000000e, SRFL_PRHEX, SROM_WL0PAB1, 0xffff}, - {BRCMS_SROM_PA0B2, 0x0000000e, SRFL_PRHEX, SROM_WL0PAB2, 0xffff}, - {BRCMS_SROM_PA0ITSSIT, 0x0000000e, 0, SROM_ITT, 0x00ff}, - {BRCMS_SROM_PA0MAXPWR, 0x0000000e, 0, SROM_WL10MAXP, 0x00ff}, {BRCMS_SROM_PA0B0, 0xffffff00, SRFL_PRHEX, SROM8_W0_PAB0, 0xffff}, {BRCMS_SROM_PA0B1, 0xffffff00, SRFL_PRHEX, SROM8_W0_PAB1, 0xffff}, {BRCMS_SROM_PA0B2, 0xffffff00, SRFL_PRHEX, SROM8_W0_PAB2, 0xffff}, {BRCMS_SROM_PA0ITSSIT, 0xffffff00, 0, SROM8_W0_ITTMAXP, 0xff00}, {BRCMS_SROM_PA0MAXPWR, 0xffffff00, 0, SROM8_W0_ITTMAXP, 0x00ff}, - {BRCMS_SROM_OPO, 0x0000000c, 0, SROM_OPO, 0x00ff}, {BRCMS_SROM_OPO, 0xffffff00, 0, SROM8_2G_OFDMPO, 0x00ff}, - {BRCMS_SROM_AA2G, 0x0000000e, 0, SROM_AABREV, SROM_AA0_MASK}, - {BRCMS_SROM_AA2G, 0x000000f0, 0, SROM4_AA, 0x00ff}, {BRCMS_SROM_AA2G, 0xffffff00, 0, SROM8_AA, 0x00ff}, - {BRCMS_SROM_AA5G, 0x0000000e, 0, SROM_AABREV, SROM_AA1_MASK}, - {BRCMS_SROM_AA5G, 0x000000f0, 0, SROM4_AA, 0xff00}, {BRCMS_SROM_AA5G, 0xffffff00, 0, SROM8_AA, 0xff00}, - {BRCMS_SROM_AG0, 0x0000000e, 0, SROM_AG10, 0x00ff}, - {BRCMS_SROM_AG1, 0x0000000e, 0, SROM_AG10, 0xff00}, - {BRCMS_SROM_AG0, 0x000000f0, 0, SROM4_AG10, 0x00ff}, - {BRCMS_SROM_AG1, 0x000000f0, 0, SROM4_AG10, 0xff00}, - {BRCMS_SROM_AG2, 0x000000f0, 0, SROM4_AG32, 0x00ff}, - {BRCMS_SROM_AG3, 0x000000f0, 0, SROM4_AG32, 0xff00}, {BRCMS_SROM_AG0, 0xffffff00, 0, SROM8_AG10, 0x00ff}, {BRCMS_SROM_AG1, 0xffffff00, 0, SROM8_AG10, 0xff00}, {BRCMS_SROM_AG2, 0xffffff00, 0, SROM8_AG32, 0x00ff}, {BRCMS_SROM_AG3, 0xffffff00, 0, SROM8_AG32, 0xff00}, - {BRCMS_SROM_PA1B0, 0x0000000e, SRFL_PRHEX, SROM_WL1PAB0, 0xffff}, - {BRCMS_SROM_PA1B1, 0x0000000e, SRFL_PRHEX, SROM_WL1PAB1, 0xffff}, - {BRCMS_SROM_PA1B2, 0x0000000e, SRFL_PRHEX, SROM_WL1PAB2, 0xffff}, - {BRCMS_SROM_PA1LOB0, 0x0000000c, SRFL_PRHEX, SROM_WL1LPAB0, 0xffff}, - {BRCMS_SROM_PA1LOB1, 0x0000000c, SRFL_PRHEX, SROM_WL1LPAB1, 0xffff}, - {BRCMS_SROM_PA1LOB2, 0x0000000c, SRFL_PRHEX, SROM_WL1LPAB2, 0xffff}, - {BRCMS_SROM_PA1HIB0, 0x0000000c, SRFL_PRHEX, SROM_WL1HPAB0, 0xffff}, - {BRCMS_SROM_PA1HIB1, 0x0000000c, SRFL_PRHEX, SROM_WL1HPAB1, 0xffff}, - {BRCMS_SROM_PA1HIB2, 0x0000000c, SRFL_PRHEX, SROM_WL1HPAB2, 0xffff}, - {BRCMS_SROM_PA1ITSSIT, 0x0000000e, 0, SROM_ITT, 0xff00}, - {BRCMS_SROM_PA1MAXPWR, 0x0000000e, 0, SROM_WL10MAXP, 0xff00}, - {BRCMS_SROM_PA1LOMAXPWR, 0x0000000c, 0, SROM_WL1LHMAXP, 0xff00}, - {BRCMS_SROM_PA1HIMAXPWR, 0x0000000c, 0, SROM_WL1LHMAXP, 0x00ff}, {BRCMS_SROM_PA1B0, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB0, 0xffff}, {BRCMS_SROM_PA1B1, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB1, 0xffff}, {BRCMS_SROM_PA1B2, 0xffffff00, SRFL_PRHEX, SROM8_W1_PAB2, 0xffff}, @@ -534,40 +377,20 @@ static const struct brcms_sromvar pci_sromvars[] = { {BRCMS_SROM_PA1MAXPWR, 0xffffff00, 0, SROM8_W1_ITTMAXP, 0x00ff}, {BRCMS_SROM_PA1LOMAXPWR, 0xffffff00, 0, SROM8_W1_MAXP_LCHC, 0xff00}, {BRCMS_SROM_PA1HIMAXPWR, 0xffffff00, 0, SROM8_W1_MAXP_LCHC, 0x00ff}, - {BRCMS_SROM_BXA2G, 0x00000008, 0, SROM_BXARSSI2G, 0x1800}, - {BRCMS_SROM_RSSISAV2G, 0x00000008, 0, SROM_BXARSSI2G, 0x0700}, - {BRCMS_SROM_RSSISMC2G, 0x00000008, 0, SROM_BXARSSI2G, 0x00f0}, - {BRCMS_SROM_RSSISMF2G, 0x00000008, 0, SROM_BXARSSI2G, 0x000f}, {BRCMS_SROM_BXA2G, 0xffffff00, 0, SROM8_BXARSSI2G, 0x1800}, {BRCMS_SROM_RSSISAV2G, 0xffffff00, 0, SROM8_BXARSSI2G, 0x0700}, {BRCMS_SROM_RSSISMC2G, 0xffffff00, 0, SROM8_BXARSSI2G, 0x00f0}, {BRCMS_SROM_RSSISMF2G, 0xffffff00, 0, SROM8_BXARSSI2G, 0x000f}, - {BRCMS_SROM_BXA5G, 0x00000008, 0, SROM_BXARSSI5G, 0x1800}, - {BRCMS_SROM_RSSISAV5G, 0x00000008, 0, SROM_BXARSSI5G, 0x0700}, - {BRCMS_SROM_RSSISMC5G, 0x00000008, 0, SROM_BXARSSI5G, 0x00f0}, - {BRCMS_SROM_RSSISMF5G, 0x00000008, 0, SROM_BXARSSI5G, 0x000f}, {BRCMS_SROM_BXA5G, 0xffffff00, 0, SROM8_BXARSSI5G, 0x1800}, {BRCMS_SROM_RSSISAV5G, 0xffffff00, 0, SROM8_BXARSSI5G, 0x0700}, {BRCMS_SROM_RSSISMC5G, 0xffffff00, 0, SROM8_BXARSSI5G, 0x00f0}, {BRCMS_SROM_RSSISMF5G, 0xffffff00, 0, SROM8_BXARSSI5G, 0x000f}, - {BRCMS_SROM_TRI2G, 0x00000008, 0, SROM_TRI52G, 0x00ff}, - {BRCMS_SROM_TRI5G, 0x00000008, 0, SROM_TRI52G, 0xff00}, - {BRCMS_SROM_TRI5GL, 0x00000008, 0, SROM_TRI5GHL, 0x00ff}, - {BRCMS_SROM_TRI5GH, 0x00000008, 0, SROM_TRI5GHL, 0xff00}, {BRCMS_SROM_TRI2G, 0xffffff00, 0, SROM8_TRI52G, 0x00ff}, {BRCMS_SROM_TRI5G, 0xffffff00, 0, SROM8_TRI52G, 0xff00}, {BRCMS_SROM_TRI5GL, 0xffffff00, 0, SROM8_TRI5GHL, 0x00ff}, {BRCMS_SROM_TRI5GH, 0xffffff00, 0, SROM8_TRI5GHL, 0xff00}, - {BRCMS_SROM_RXPO2G, 0x00000008, SRFL_PRSIGN, SROM_RXPO52G, 0x00ff}, - {BRCMS_SROM_RXPO5G, 0x00000008, SRFL_PRSIGN, SROM_RXPO52G, 0xff00}, {BRCMS_SROM_RXPO2G, 0xffffff00, SRFL_PRSIGN, SROM8_RXPO52G, 0x00ff}, {BRCMS_SROM_RXPO5G, 0xffffff00, SRFL_PRSIGN, SROM8_RXPO52G, 0xff00}, - {BRCMS_SROM_TXCHAIN, 0x000000f0, SRFL_NOFFS, SROM4_TXRXC, - SROM4_TXCHAIN_MASK}, - {BRCMS_SROM_RXCHAIN, 0x000000f0, SRFL_NOFFS, SROM4_TXRXC, - SROM4_RXCHAIN_MASK}, - {BRCMS_SROM_ANTSWITCH, 0x000000f0, SRFL_NOFFS, SROM4_TXRXC, - SROM4_SWITCH_MASK}, {BRCMS_SROM_TXCHAIN, 0xffffff00, SRFL_NOFFS, SROM8_TXRXC, SROM4_TXCHAIN_MASK}, {BRCMS_SROM_RXCHAIN, 0xffffff00, SRFL_NOFFS, SROM8_TXRXC, @@ -594,43 +417,11 @@ static const struct brcms_sromvar pci_sromvars[] = { SROM8_FEM_ANTSWLUT_MASK}, {BRCMS_SROM_TEMPTHRESH, 0xffffff00, 0, SROM8_THERMAL, 0xff00}, {BRCMS_SROM_TEMPOFFSET, 0xffffff00, 0, SROM8_THERMAL, 0x00ff}, - {BRCMS_SROM_TXPID2GA0, 0x000000f0, 0, SROM4_TXPID2G, 0x00ff}, - {BRCMS_SROM_TXPID2GA1, 0x000000f0, 0, SROM4_TXPID2G, 0xff00}, - {BRCMS_SROM_TXPID2GA2, 0x000000f0, 0, SROM4_TXPID2G + 1, 0x00ff}, - {BRCMS_SROM_TXPID2GA3, 0x000000f0, 0, SROM4_TXPID2G + 1, 0xff00}, - {BRCMS_SROM_TXPID5GA0, 0x000000f0, 0, SROM4_TXPID5G, 0x00ff}, - {BRCMS_SROM_TXPID5GA1, 0x000000f0, 0, SROM4_TXPID5G, 0xff00}, - {BRCMS_SROM_TXPID5GA2, 0x000000f0, 0, SROM4_TXPID5G + 1, 0x00ff}, - {BRCMS_SROM_TXPID5GA3, 0x000000f0, 0, SROM4_TXPID5G + 1, 0xff00}, - {BRCMS_SROM_TXPID5GLA0, 0x000000f0, 0, SROM4_TXPID5GL, 0x00ff}, - {BRCMS_SROM_TXPID5GLA1, 0x000000f0, 0, SROM4_TXPID5GL, 0xff00}, - {BRCMS_SROM_TXPID5GLA2, 0x000000f0, 0, SROM4_TXPID5GL + 1, 0x00ff}, - {BRCMS_SROM_TXPID5GLA3, 0x000000f0, 0, SROM4_TXPID5GL + 1, 0xff00}, - {BRCMS_SROM_TXPID5GHA0, 0x000000f0, 0, SROM4_TXPID5GH, 0x00ff}, - {BRCMS_SROM_TXPID5GHA1, 0x000000f0, 0, SROM4_TXPID5GH, 0xff00}, - {BRCMS_SROM_TXPID5GHA2, 0x000000f0, 0, SROM4_TXPID5GH + 1, 0x00ff}, - {BRCMS_SROM_TXPID5GHA3, 0x000000f0, 0, SROM4_TXPID5GH + 1, 0xff00}, - - {BRCMS_SROM_CCODE, 0x0000000f, SRFL_CCODE, SROM_CCODE, 0xffff}, - {BRCMS_SROM_CCODE, 0x00000010, SRFL_CCODE, SROM4_CCODE, 0xffff}, - {BRCMS_SROM_CCODE, 0x000000e0, SRFL_CCODE, SROM5_CCODE, 0xffff}, + {BRCMS_SROM_CCODE, 0xffffff00, SRFL_CCODE, SROM8_CCODE, 0xffff}, {BRCMS_SROM_MACADDR, 0xffffff00, SRFL_ETHADDR, SROM8_MACHI, 0xffff}, - {BRCMS_SROM_MACADDR, 0x000000e0, SRFL_ETHADDR, SROM5_MACHI, 0xffff}, - {BRCMS_SROM_MACADDR, 0x00000010, SRFL_ETHADDR, SROM4_MACHI, 0xffff}, - {BRCMS_SROM_MACADDR, 0x00000008, SRFL_ETHADDR, SROM3_MACHI, 0xffff}, - {BRCMS_SROM_IL0MACADDR, 0x00000007, SRFL_ETHADDR, SROM_MACHI_IL0, - 0xffff}, - {BRCMS_SROM_ET1MACADDR, 0x00000007, SRFL_ETHADDR, SROM_MACHI_ET1, - 0xffff}, {BRCMS_SROM_LEDDC, 0xffffff00, SRFL_NOFFS | SRFL_LEDDC, SROM8_LEDDC, 0xffff}, - {BRCMS_SROM_LEDDC, 0x000000e0, SRFL_NOFFS | SRFL_LEDDC, SROM5_LEDDC, - 0xffff}, - {BRCMS_SROM_LEDDC, 0x00000010, SRFL_NOFFS | SRFL_LEDDC, SROM4_LEDDC, - 0xffff}, - {BRCMS_SROM_LEDDC, 0x00000008, SRFL_NOFFS | SRFL_LEDDC, SROM3_LEDDC, - 0xffff}, {BRCMS_SROM_RAWTEMPSENSE, 0xffffff00, SRFL_PRHEX, SROM8_MPWR_RAWTS, 0x01ff}, {BRCMS_SROM_MEASPOWER, 0xffffff00, SRFL_PRHEX, SROM8_MPWR_RAWTS, @@ -650,16 +441,7 @@ static const struct brcms_sromvar pci_sromvars[] = { {BRCMS_SROM_PHYCAL_TEMPDELTA, 0xffffff00, 0, SROM8_PHYCAL_TEMPDELTA, 0x00ff}, - {BRCMS_SROM_CCK2GPO, 0x000000f0, 0, SROM4_2G_CCKPO, 0xffff}, {BRCMS_SROM_CCK2GPO, 0x00000100, 0, SROM8_2G_CCKPO, 0xffff}, - {BRCMS_SROM_OFDM2GPO, 0x000000f0, SRFL_MORE, SROM4_2G_OFDMPO, 0xffff}, - {BRCMS_SROM_CONT, 0, 0, SROM4_2G_OFDMPO + 1, 0xffff}, - {BRCMS_SROM_OFDM5GPO, 0x000000f0, SRFL_MORE, SROM4_5G_OFDMPO, 0xffff}, - {BRCMS_SROM_CONT, 0, 0, SROM4_5G_OFDMPO + 1, 0xffff}, - {BRCMS_SROM_OFDM5GLPO, 0x000000f0, SRFL_MORE, SROM4_5GL_OFDMPO, 0xffff}, - {BRCMS_SROM_CONT, 0, 0, SROM4_5GL_OFDMPO + 1, 0xffff}, - {BRCMS_SROM_OFDM5GHPO, 0x000000f0, SRFL_MORE, SROM4_5GH_OFDMPO, 0xffff}, - {BRCMS_SROM_CONT, 0, 0, SROM4_5GH_OFDMPO + 1, 0xffff}, {BRCMS_SROM_OFDM2GPO, 0x00000100, SRFL_MORE, SROM8_2G_OFDMPO, 0xffff}, {BRCMS_SROM_CONT, 0, 0, SROM8_2G_OFDMPO + 1, 0xffff}, {BRCMS_SROM_OFDM5GPO, 0x00000100, SRFL_MORE, SROM8_5G_OFDMPO, 0xffff}, @@ -668,38 +450,6 @@ static const struct brcms_sromvar pci_sromvars[] = { {BRCMS_SROM_CONT, 0, 0, SROM8_5GL_OFDMPO + 1, 0xffff}, {BRCMS_SROM_OFDM5GHPO, 0x00000100, SRFL_MORE, SROM8_5GH_OFDMPO, 0xffff}, {BRCMS_SROM_CONT, 0, 0, SROM8_5GH_OFDMPO + 1, 0xffff}, - {BRCMS_SROM_MCS2GPO0, 0x000000f0, 0, SROM4_2G_MCSPO, 0xffff}, - {BRCMS_SROM_MCS2GPO1, 0x000000f0, 0, SROM4_2G_MCSPO + 1, 0xffff}, - {BRCMS_SROM_MCS2GPO2, 0x000000f0, 0, SROM4_2G_MCSPO + 2, 0xffff}, - {BRCMS_SROM_MCS2GPO3, 0x000000f0, 0, SROM4_2G_MCSPO + 3, 0xffff}, - {BRCMS_SROM_MCS2GPO4, 0x000000f0, 0, SROM4_2G_MCSPO + 4, 0xffff}, - {BRCMS_SROM_MCS2GPO5, 0x000000f0, 0, SROM4_2G_MCSPO + 5, 0xffff}, - {BRCMS_SROM_MCS2GPO6, 0x000000f0, 0, SROM4_2G_MCSPO + 6, 0xffff}, - {BRCMS_SROM_MCS2GPO7, 0x000000f0, 0, SROM4_2G_MCSPO + 7, 0xffff}, - {BRCMS_SROM_MCS5GPO0, 0x000000f0, 0, SROM4_5G_MCSPO, 0xffff}, - {BRCMS_SROM_MCS5GPO1, 0x000000f0, 0, SROM4_5G_MCSPO + 1, 0xffff}, - {BRCMS_SROM_MCS5GPO2, 0x000000f0, 0, SROM4_5G_MCSPO + 2, 0xffff}, - {BRCMS_SROM_MCS5GPO3, 0x000000f0, 0, SROM4_5G_MCSPO + 3, 0xffff}, - {BRCMS_SROM_MCS5GPO4, 0x000000f0, 0, SROM4_5G_MCSPO + 4, 0xffff}, - {BRCMS_SROM_MCS5GPO5, 0x000000f0, 0, SROM4_5G_MCSPO + 5, 0xffff}, - {BRCMS_SROM_MCS5GPO6, 0x000000f0, 0, SROM4_5G_MCSPO + 6, 0xffff}, - {BRCMS_SROM_MCS5GPO7, 0x000000f0, 0, SROM4_5G_MCSPO + 7, 0xffff}, - {BRCMS_SROM_MCS5GLPO0, 0x000000f0, 0, SROM4_5GL_MCSPO, 0xffff}, - {BRCMS_SROM_MCS5GLPO1, 0x000000f0, 0, SROM4_5GL_MCSPO + 1, 0xffff}, - {BRCMS_SROM_MCS5GLPO2, 0x000000f0, 0, SROM4_5GL_MCSPO + 2, 0xffff}, - {BRCMS_SROM_MCS5GLPO3, 0x000000f0, 0, SROM4_5GL_MCSPO + 3, 0xffff}, - {BRCMS_SROM_MCS5GLPO4, 0x000000f0, 0, SROM4_5GL_MCSPO + 4, 0xffff}, - {BRCMS_SROM_MCS5GLPO5, 0x000000f0, 0, SROM4_5GL_MCSPO + 5, 0xffff}, - {BRCMS_SROM_MCS5GLPO6, 0x000000f0, 0, SROM4_5GL_MCSPO + 6, 0xffff}, - {BRCMS_SROM_MCS5GLPO7, 0x000000f0, 0, SROM4_5GL_MCSPO + 7, 0xffff}, - {BRCMS_SROM_MCS5GHPO0, 0x000000f0, 0, SROM4_5GH_MCSPO, 0xffff}, - {BRCMS_SROM_MCS5GHPO1, 0x000000f0, 0, SROM4_5GH_MCSPO + 1, 0xffff}, - {BRCMS_SROM_MCS5GHPO2, 0x000000f0, 0, SROM4_5GH_MCSPO + 2, 0xffff}, - {BRCMS_SROM_MCS5GHPO3, 0x000000f0, 0, SROM4_5GH_MCSPO + 3, 0xffff}, - {BRCMS_SROM_MCS5GHPO4, 0x000000f0, 0, SROM4_5GH_MCSPO + 4, 0xffff}, - {BRCMS_SROM_MCS5GHPO5, 0x000000f0, 0, SROM4_5GH_MCSPO + 5, 0xffff}, - {BRCMS_SROM_MCS5GHPO6, 0x000000f0, 0, SROM4_5GH_MCSPO + 6, 0xffff}, - {BRCMS_SROM_MCS5GHPO7, 0x000000f0, 0, SROM4_5GH_MCSPO + 7, 0xffff}, {BRCMS_SROM_MCS2GPO0, 0x00000100, 0, SROM8_2G_MCSPO, 0xffff}, {BRCMS_SROM_MCS2GPO1, 0x00000100, 0, SROM8_2G_MCSPO + 1, 0xffff}, {BRCMS_SROM_MCS2GPO2, 0x00000100, 0, SROM8_2G_MCSPO + 2, 0xffff}, @@ -732,10 +482,6 @@ static const struct brcms_sromvar pci_sromvars[] = { {BRCMS_SROM_MCS5GHPO5, 0x00000100, 0, SROM8_5GH_MCSPO + 5, 0xffff}, {BRCMS_SROM_MCS5GHPO6, 0x00000100, 0, SROM8_5GH_MCSPO + 6, 0xffff}, {BRCMS_SROM_MCS5GHPO7, 0x00000100, 0, SROM8_5GH_MCSPO + 7, 0xffff}, - {BRCMS_SROM_CDDPO, 0x000000f0, 0, SROM4_CDDPO, 0xffff}, - {BRCMS_SROM_STBCPO, 0x000000f0, 0, SROM4_STBCPO, 0xffff}, - {BRCMS_SROM_BW40PO, 0x000000f0, 0, SROM4_BW40PO, 0xffff}, - {BRCMS_SROM_BWDUPPO, 0x000000f0, 0, SROM4_BWDUPPO, 0xffff}, {BRCMS_SROM_CDDPO, 0x00000100, 0, SROM8_CDDPO, 0xffff}, {BRCMS_SROM_STBCPO, 0x00000100, 0, SROM8_STBCPO, 0xffff}, {BRCMS_SROM_BW40PO, 0x00000100, 0, SROM8_BW40PO, 0xffff}, @@ -811,34 +557,6 @@ static const struct brcms_sromvar pci_sromvars[] = { }; static const struct brcms_sromvar perpath_pci_sromvars[] = { - {BRCMS_SROM_MAXP2GA0, 0x000000f0, 0, SROM4_2G_ITT_MAXP, 0x00ff}, - {BRCMS_SROM_ITT2GA0, 0x000000f0, 0, SROM4_2G_ITT_MAXP, 0xff00}, - {BRCMS_SROM_ITT5GA0, 0x000000f0, 0, SROM4_5G_ITT_MAXP, 0xff00}, - {BRCMS_SROM_PA2GW0A0, 0x000000f0, SRFL_PRHEX, SROM4_2G_PA, 0xffff}, - {BRCMS_SROM_PA2GW1A0, 0x000000f0, SRFL_PRHEX, SROM4_2G_PA + 1, 0xffff}, - {BRCMS_SROM_PA2GW2A0, 0x000000f0, SRFL_PRHEX, SROM4_2G_PA + 2, 0xffff}, - {BRCMS_SROM_PA2GW3A0, 0x000000f0, SRFL_PRHEX, SROM4_2G_PA + 3, 0xffff}, - {BRCMS_SROM_MAXP5GA0, 0x000000f0, 0, SROM4_5G_ITT_MAXP, 0x00ff}, - {BRCMS_SROM_MAXP5GHA0, 0x000000f0, 0, SROM4_5GLH_MAXP, 0x00ff}, - {BRCMS_SROM_MAXP5GLA0, 0x000000f0, 0, SROM4_5GLH_MAXP, 0xff00}, - {BRCMS_SROM_PA5GW0A0, 0x000000f0, SRFL_PRHEX, SROM4_5G_PA, 0xffff}, - {BRCMS_SROM_PA5GW1A0, 0x000000f0, SRFL_PRHEX, SROM4_5G_PA + 1, 0xffff}, - {BRCMS_SROM_PA5GW2A0, 0x000000f0, SRFL_PRHEX, SROM4_5G_PA + 2, 0xffff}, - {BRCMS_SROM_PA5GW3A0, 0x000000f0, SRFL_PRHEX, SROM4_5G_PA + 3, 0xffff}, - {BRCMS_SROM_PA5GLW0A0, 0x000000f0, SRFL_PRHEX, SROM4_5GL_PA, 0xffff}, - {BRCMS_SROM_PA5GLW1A0, 0x000000f0, SRFL_PRHEX, SROM4_5GL_PA + 1, - 0xffff}, - {BRCMS_SROM_PA5GLW2A0, 0x000000f0, SRFL_PRHEX, SROM4_5GL_PA + 2, - 0xffff}, - {BRCMS_SROM_PA5GLW3A0, 0x000000f0, SRFL_PRHEX, SROM4_5GL_PA + 3, - 0xffff}, - {BRCMS_SROM_PA5GHW0A0, 0x000000f0, SRFL_PRHEX, SROM4_5GH_PA, 0xffff}, - {BRCMS_SROM_PA5GHW1A0, 0x000000f0, SRFL_PRHEX, SROM4_5GH_PA + 1, - 0xffff}, - {BRCMS_SROM_PA5GHW2A0, 0x000000f0, SRFL_PRHEX, SROM4_5GH_PA + 2, - 0xffff}, - {BRCMS_SROM_PA5GHW3A0, 0x000000f0, SRFL_PRHEX, SROM4_5GH_PA + 3, - 0xffff}, {BRCMS_SROM_MAXP2GA0, 0xffffff00, 0, SROM8_2G_ITT_MAXP, 0x00ff}, {BRCMS_SROM_ITT2GA0, 0xffffff00, 0, SROM8_2G_ITT_MAXP, 0xff00}, {BRCMS_SROM_ITT5GA0, 0xffffff00, 0, SROM8_5G_ITT_MAXP, 0xff00}, @@ -868,24 +586,17 @@ static const struct brcms_sromvar perpath_pci_sromvars[] = { * shared between devices. */ static u8 brcms_srom_crc8_table[CRC8_TABLE_SIZE]; -static u16 __iomem * +static u8 __iomem * srom_window_address(struct si_pub *sih, u8 __iomem *curmap) { if (sih->ccrev < 32) - return (u16 __iomem *)(curmap + PCI_BAR0_SPROM_OFFSET); + return curmap + PCI_BAR0_SPROM_OFFSET; if (sih->cccaps & CC_CAP_SROM) - return (u16 __iomem *) - (curmap + PCI_16KB0_CCREGS_OFFSET + CC_SROM_OTP); + return curmap + PCI_16KB0_CCREGS_OFFSET + CC_SROM_OTP; return NULL; } -/* Parse SROM and create name=value pairs. 'srom' points to - * the SROM word array. 'off' specifies the offset of the - * first word 'srom' points to, which should be either 0 or - * SROM3_SWRG_OFF (full SROM or software region). - */ - static uint mask_shift(u16 mask) { uint i; @@ -906,18 +617,16 @@ static uint mask_width(u16 mask) return 0; } -static inline void ltoh16_buf(u16 *buf, unsigned int size) +static inline void le16_to_cpu_buf(u16 *buf, uint nwords) { - size /= 2; - while (size--) - *(buf + size) = le16_to_cpu(*(__le16 *)(buf + size)); + while (nwords--) + *(buf + nwords) = le16_to_cpu(*(__le16 *)(buf + nwords)); } -static inline void htol16_buf(u16 *buf, unsigned int size) +static inline void cpu_to_le16_buf(u16 *buf, uint nwords) { - size /= 2; - while (size--) - *(__le16 *)(buf + size) = cpu_to_le16(*(buf + size)); + while (nwords--) + *(__le16 *)(buf + nwords) = cpu_to_le16(*(buf + nwords)); } /* @@ -934,6 +643,9 @@ _initvars_srom_pci(u8 sromrev, u16 *srom, struct list_head *var_list) uint width; uint flags; u32 sr = (1 << sromrev); + uint p; + uint pb = SROM8_PATH0; + const uint psz = SROM8_PATH1 - SROM8_PATH0; /* first store the srom revision */ entry = kzalloc(sizeof(struct brcms_srom_list_head), GFP_KERNEL); @@ -1031,47 +743,34 @@ _initvars_srom_pci(u8 sromrev, u16 *srom, struct list_head *var_list) list_add(&entry->var_list, var_list); } - if (sromrev >= 4) { - /* Do per-path variables */ - uint p, pb, psz; - - if (sromrev >= 8) { - pb = SROM8_PATH0; - psz = SROM8_PATH1 - SROM8_PATH0; - } else { - pb = SROM4_PATH0; - psz = SROM4_PATH1 - SROM4_PATH0; - } - - for (p = 0; p < MAX_PATH_SROM; p++) { - for (srv = perpath_pci_sromvars; - srv->varid != BRCMS_SROM_NULL; srv++) { - if ((srv->revmask & sr) == 0) - continue; + for (p = 0; p < MAX_PATH_SROM; p++) { + for (srv = perpath_pci_sromvars; + srv->varid != BRCMS_SROM_NULL; srv++) { + if ((srv->revmask & sr) == 0) + continue; - if (srv->flags & SRFL_NOVAR) - continue; + if (srv->flags & SRFL_NOVAR) + continue; - w = srom[pb + srv->off]; - val = (w & srv->mask) >> mask_shift(srv->mask); - width = mask_width(srv->mask); + w = srom[pb + srv->off]; + val = (w & srv->mask) >> mask_shift(srv->mask); + width = mask_width(srv->mask); - /* Cheating: no per-path var is more than - * 1 word */ - if ((srv->flags & SRFL_NOFFS) - && ((int)val == (1 << width) - 1)) - continue; + /* Cheating: no per-path var is more than + * 1 word */ + if ((srv->flags & SRFL_NOFFS) + && ((int)val == (1 << width) - 1)) + continue; - entry = - kzalloc(sizeof(struct brcms_srom_list_head), - GFP_KERNEL); - entry->varid = srv->varid+p; - entry->var_type = BRCMS_SROM_UNUMBER; - entry->uval = val; - list_add(&entry->var_list, var_list); - } - pb += psz; + entry = + kzalloc(sizeof(struct brcms_srom_list_head), + GFP_KERNEL); + entry->varid = srv->varid+p; + entry->var_type = BRCMS_SROM_UNUMBER; + entry->uval = val; + list_add(&entry->var_list, var_list); } + pb += psz; } } @@ -1080,41 +779,38 @@ _initvars_srom_pci(u8 sromrev, u16 *srom, struct list_head *var_list) * Return 0 on success, nonzero on error. */ static int -sprom_read_pci(struct si_pub *sih, u16 __iomem *sprom, uint wordoff, +sprom_read_pci(struct si_pub *sih, u8 __iomem *sprom, uint wordoff, u16 *buf, uint nwords, bool check_crc) { int err = 0; uint i; + u8 *bbuf = (u8 *)buf; /* byte buffer */ + uint nbytes = nwords << 1; - /* read the sprom */ - for (i = 0; i < nwords; i++) - buf[i] = R_REG(&sprom[wordoff + i]); - - if (check_crc) { + /* read the sprom in bytes */ + for (i = 0; i < nbytes; i++) + bbuf[i] = readb(sprom+i); - if (buf[0] == 0xffff) - /* - * The hardware thinks that an srom that starts with - * 0xffff is blank, regardless of the rest of the - * content, so declare it bad. - */ - return -ENODATA; - - /* fixup the endianness so crc8 will pass */ - htol16_buf(buf, nwords * 2); - if (crc8(brcms_srom_crc8_table, (u8 *) buf, nwords * 2, - CRC8_INIT_VALUE) != - CRC8_GOOD_VALUE(brcms_srom_crc8_table)) - /* DBG only pci always read srom4 first, then srom8/9 */ - err = -EIO; + if (buf[0] == 0xffff) + /* + * The hardware thinks that an srom that starts with + * 0xffff is blank, regardless of the rest of the + * content, so declare it bad. + */ + return -ENODATA; + if (check_crc && + crc8(brcms_srom_crc8_table, bbuf, nbytes, CRC8_INIT_VALUE) != + CRC8_GOOD_VALUE(brcms_srom_crc8_table)) + err = -EIO; + else /* now correct the endianness of the byte array */ - ltoh16_buf(buf, nwords * 2); - } + le16_to_cpu_buf(buf, nwords); + return err; } -static int otp_read_pci(struct si_pub *sih, u16 *buf, uint bufsz) +static int otp_read_pci(struct si_pub *sih, u16 *buf, uint nwords) { u8 *otp; uint sz = OTP_SZ_MAX / 2; /* size in words */ @@ -1126,7 +822,8 @@ static int otp_read_pci(struct si_pub *sih, u16 *buf, uint bufsz) err = otp_read_region(sih, OTP_HW_RGN, (u16 *) otp, &sz); - memcpy(buf, otp, bufsz); + sz = min_t(uint, sz, nwords); + memcpy(buf, otp, sz * 2); kfree(otp); @@ -1138,14 +835,12 @@ static int otp_read_pci(struct si_pub *sih, u16 *buf, uint bufsz) */ return -ENODATA; - /* fixup the endianness so crc8 will pass */ - htol16_buf(buf, bufsz); - if (crc8(brcms_srom_crc8_table, (u8 *) buf, SROM4_WORDS * 2, + if (crc8(brcms_srom_crc8_table, (u8 *) buf, sz * 2, CRC8_INIT_VALUE) != CRC8_GOOD_VALUE(brcms_srom_crc8_table)) err = -EIO; - - /* now correct the endianness of the byte array */ - ltoh16_buf(buf, bufsz); + else + /* now correct the endianness of the byte array */ + le16_to_cpu_buf(buf, sz); return err; } @@ -1157,7 +852,7 @@ static int otp_read_pci(struct si_pub *sih, u16 *buf, uint bufsz) static int initvars_srom_pci(struct si_pub *sih, void __iomem *curmap) { u16 *srom; - u16 __iomem *sromwindow; + u8 __iomem *sromwindow; u8 sromrev = 0; u32 sr; int err = 0; @@ -1173,29 +868,16 @@ static int initvars_srom_pci(struct si_pub *sih, void __iomem *curmap) crc8_populate_lsb(brcms_srom_crc8_table, SROM_CRC8_POLY); if (ai_is_sprom_available(sih)) { - err = sprom_read_pci(sih, sromwindow, 0, srom, SROM_WORDS, - true); - - if ((srom[SROM4_SIGN] == SROM4_SIGNATURE) || - (((sih->buscoretype == PCIE_CORE_ID) - && (sih->buscorerev >= 6)) - || ((sih->buscoretype == PCI_CORE_ID) - && (sih->buscorerev >= 0xe)))) { - /* sromrev >= 4, read more */ - err = sprom_read_pci(sih, sromwindow, 0, srom, - SROM4_WORDS, true); - sromrev = srom[SROM4_CRCREV] & 0xff; - } else if (err == 0) { - /* srom is good and is rev < 4 */ + err = sprom_read_pci(sih, sromwindow, 0, srom, + SROM4_WORDS, true); + + if (err == 0) + /* srom read and passed crc */ /* top word of sprom contains version and crc8 */ - sromrev = srom[SROM_CRCREV] & 0xff; - /* bcm4401 sroms misprogrammed */ - if (sromrev == 0x10) - sromrev = 1; - } + sromrev = srom[SROM4_CRCREV] & 0xff; } else { /* Use OTP if SPROM not available */ - err = otp_read_pci(sih, srom, SROM_MAX); + err = otp_read_pci(sih, srom, SROM4_WORDS); if (err == 0) /* OTP only contain SROM rev8/rev9 for now */ sromrev = srom[SROM4_CRCREV] & 0xff; @@ -1208,10 +890,9 @@ static int initvars_srom_pci(struct si_pub *sih, void __iomem *curmap) sr = 1 << sromrev; /* - * srom version check: Current valid versions: 1, 2, 3, 4, 5, 8, - * 9 + * srom version check: Current valid versions: 8, 9 */ - if ((sr & 0x33e) == 0) { + if ((sr & 0x300) == 0) { err = -EINVAL; goto errout; } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/srom.h b/drivers/net/wireless/brcm80211/brcmsmac/srom.h index 708c43ff51cc..c81df9798e50 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/srom.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/srom.h @@ -26,9 +26,4 @@ extern void srom_free_vars(struct si_pub *sih); extern int srom_read(struct si_pub *sih, uint bus, void *curmap, uint byteoff, uint nbytes, u16 *buf, bool check_crc); -/* parse standard PCMCIA cis, normally used by SB/PCMCIA/SDIO/SPI/OTP - * and extract from it into name=value pairs - */ -extern int srom_parsecis(u8 **pcis, uint ciscnt, - char **vars, uint *count); #endif /* _BRCM_SROM_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmutil/utils.c b/drivers/net/wireless/brcm80211/brcmutil/utils.c index f27c48910827..12b795ffd369 100644 --- a/drivers/net/wireless/brcm80211/brcmutil/utils.c +++ b/drivers/net/wireless/brcm80211/brcmutil/utils.c @@ -16,6 +16,7 @@ #include <linux/netdevice.h> #include <linux/module.h> + #include <brcmu_utils.h> MODULE_AUTHOR("Broadcom Corporation"); @@ -66,36 +67,6 @@ void brcmu_pkt_buf_free_skb(struct sk_buff *skb) EXPORT_SYMBOL(brcmu_pkt_buf_free_skb); -/* copy a buffer into a pkt buffer chain */ -uint brcmu_pktfrombuf(struct sk_buff *p, uint offset, int len, - unsigned char *buf) -{ - uint n, ret = 0; - - /* skip 'offset' bytes */ - for (; p && offset; p = p->next) { - if (offset < (uint) (p->len)) - break; - offset -= p->len; - } - - if (!p) - return 0; - - /* copy the data */ - for (; p && len; p = p->next) { - n = min((uint) (p->len) - offset, (uint) len); - memcpy(p->data + offset, buf, n); - buf += n; - len -= n; - ret += n; - offset = 0; - } - - return ret; -} -EXPORT_SYMBOL(brcmu_pktfrombuf); - /* return total length of buffer chain */ uint brcmu_pkttotlen(struct sk_buff *p) { @@ -364,23 +335,3 @@ void brcmu_prpkt(const char *msg, struct sk_buff *p0) } EXPORT_SYMBOL(brcmu_prpkt); #endif /* defined(BCMDBG) */ - -#if defined(BCMDBG) -/* - * print bytes formatted as hex to a string. return the resulting - * string length - */ -int brcmu_format_hex(char *str, const void *bytes, int len) -{ - int i; - char *p = str; - const u8 *src = (const u8 *)bytes; - - for (i = 0; i < len; i++) { - p += snprintf(p, 3, "%02X", *src); - src++; - } - return (int)(p - str); -} -EXPORT_SYMBOL(brcmu_format_hex); -#endif /* defined(BCMDBG) */ diff --git a/drivers/net/wireless/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/brcm80211/include/brcmu_utils.h index 7d0f46e0eb95..ccf60151953c 100644 --- a/drivers/net/wireless/brcm80211/include/brcmu_utils.h +++ b/drivers/net/wireless/brcm80211/include/brcmu_utils.h @@ -173,23 +173,18 @@ extern void brcmu_pktq_flush(struct pktq *pq, bool dir, /* externs */ /* packet */ -extern uint brcmu_pktfrombuf(struct sk_buff *p, - uint offset, int len, unsigned char *buf); extern uint brcmu_pkttotlen(struct sk_buff *p); /* ip address */ struct ipv4_addr; + +/* externs */ +/* format/print */ #ifdef BCMDBG extern void brcmu_prpkt(const char *msg, struct sk_buff *p0); #else #define brcmu_prpkt(a, b) #endif /* BCMDBG */ -/* externs */ -/* format/print */ -#if defined(BCMDBG) -extern int brcmu_format_hex(char *str, const void *bytes, int len); -#endif - #endif /* _BRCMU_UTILS_H_ */ diff --git a/drivers/net/wireless/brcm80211/include/defs.h b/drivers/net/wireless/brcm80211/include/defs.h index 1e5f310af1e7..f0d8c04a9c8c 100644 --- a/drivers/net/wireless/brcm80211/include/defs.h +++ b/drivers/net/wireless/brcm80211/include/defs.h @@ -62,7 +62,6 @@ #define WL_RADIO_SW_DISABLE (1<<0) #define WL_RADIO_HW_DISABLE (1<<1) -#define WL_RADIO_MPC_DISABLE (1<<2) /* some countries don't support any channel */ #define WL_RADIO_COUNTRY_DISABLE (1<<3) diff --git a/drivers/net/wireless/brcm80211/include/soc.h b/drivers/net/wireless/brcm80211/include/soc.h index 4fcb956ad9e0..4e9b7e4827ea 100644 --- a/drivers/net/wireless/brcm80211/include/soc.h +++ b/drivers/net/wireless/brcm80211/include/soc.h @@ -77,8 +77,9 @@ #define DMEMS_CORE_ID 0x835 /* SDR/DDR1 memory controller core */ #define DEF_SHIM_COMP 0x837 /* SHIM component in ubus/6362 */ #define OOB_ROUTER_CORE_ID 0x367 /* OOB router core ID */ -/* Default component, in ai chips it maps all unused address ranges */ -#define DEF_AI_COMP 0xfff +#define DEF_AI_COMP 0xfff /* Default component, in ai chips it + * maps all unused address ranges + */ /* Common core control flags */ #define SICF_BIST_EN 0x8000 @@ -87,4 +88,11 @@ #define SICF_FGC 0x0002 #define SICF_CLOCK_EN 0x0001 +/* Common core status flags */ +#define SISF_BIST_DONE 0x8000 +#define SISF_BIST_ERROR 0x4000 +#define SISF_GATED_CLK 0x2000 +#define SISF_DMA64 0x1000 +#define SISF_CORE_BITS 0x0fff + #endif /* _BRCM_SOC_H */ diff --git a/drivers/net/wireless/ipw2x00/libipw.h b/drivers/net/wireless/ipw2x00/libipw.h index 70f5586d96bd..3d5821eeb054 100644 --- a/drivers/net/wireless/ipw2x00/libipw.h +++ b/drivers/net/wireless/ipw2x00/libipw.h @@ -66,16 +66,8 @@ extern u32 libipw_debug_level; do { if (libipw_debug_level & (level)) \ printk(KERN_DEBUG "libipw: %c %s " fmt, \ in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) -static inline bool libipw_ratelimit_debug(u32 level) -{ - return (libipw_debug_level & level) && net_ratelimit(); -} #else #define LIBIPW_DEBUG(level, fmt, args...) do {} while (0) -static inline bool libipw_ratelimit_debug(u32 level) -{ - return false; -} #endif /* CONFIG_LIBIPW_DEBUG */ /* diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index ccba69b7f8a7..9d463cf40380 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2028,6 +2028,7 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw, .tkip = &tkip_cmd, .use_tkip = false, }; + struct iwlagn_d3_config_cmd d3_cfg_cmd = {}; int ret, i; u16 seq; @@ -2085,13 +2086,14 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw, if (wowlan->four_way_handshake) wakeup_filter_cmd.enabled |= cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE); - if (wowlan->rfkill_release) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_RFKILL); if (wowlan->n_patterns) wakeup_filter_cmd.enabled |= cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH); + if (wowlan->rfkill_release) + d3_cfg_cmd.wakeup_flags |= + cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL); + iwl_scan_cancel_timeout(priv, 200); memcpy(&rxon, &ctx->active, sizeof(rxon)); @@ -2179,6 +2181,11 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw, } } + ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_D3_CONFIG, CMD_SYNC, + sizeof(d3_cfg_cmd), &d3_cfg_cmd); + if (ret) + goto error; + ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_WOWLAN_WAKEUP_FILTER, CMD_SYNC, sizeof(wakeup_filter_cmd), &wakeup_filter_cmd); @@ -3171,7 +3178,7 @@ static int iwl_set_hw_params(struct iwl_priv *priv) } /* This function both allocates and initializes hw and priv. */ -static struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg) +static struct ieee80211_hw *iwl_alloc_all(void) { struct iwl_priv *priv; /* mac80211 allocates memory for this device instance, including @@ -3179,11 +3186,8 @@ static struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg) struct ieee80211_hw *hw; hw = ieee80211_alloc_hw(sizeof(struct iwl_priv), &iwlagn_hw_ops); - if (hw == NULL) { - pr_err("%s: Can not allocate network device\n", - cfg->name); + if (!hw) goto out; - } priv = hw->priv; priv->hw = hw; @@ -3204,8 +3208,9 @@ int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops, /************************ * 1. Allocating HW data ************************/ - hw = iwl_alloc_all(cfg); + hw = iwl_alloc_all(); if (!hw) { + pr_err("%s: Cannot allocate network device\n", cfg->name); err = -ENOMEM; goto out; } diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 69d5f85d11e2..f4eccf583775 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -198,6 +198,7 @@ enum { REPLY_WOWLAN_TKIP_PARAMS = 0xe3, REPLY_WOWLAN_KEK_KCK_MATERIAL = 0xe4, REPLY_WOWLAN_GET_STATUS = 0xe5, + REPLY_D3_CONFIG = 0xd3, REPLY_MAX = 0xff }; @@ -3801,6 +3802,19 @@ struct iwl_bt_coex_prot_env_cmd { } __attribute__((packed)); /* + * REPLY_D3_CONFIG + */ +enum iwlagn_d3_wakeup_filters { + IWLAGN_D3_WAKEUP_RFKILL = BIT(0), + IWLAGN_D3_WAKEUP_SYSASSERT = BIT(1), +}; + +struct iwlagn_d3_config_cmd { + __le32 min_sleep_time; + __le32 wakeup_flags; +} __packed; + +/* * REPLY_WOWLAN_PATTERNS */ #define IWLAGN_WOWLAN_MIN_PATTERN_LEN 16 @@ -3830,19 +3844,16 @@ enum iwlagn_wowlan_wakeup_filters { IWLAGN_WOWLAN_WAKEUP_BEACON_MISS = BIT(2), IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3), IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4), - IWLAGN_WOWLAN_WAKEUP_RFKILL = BIT(5), - IWLAGN_WOWLAN_WAKEUP_UCODE_ERROR = BIT(6), - IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(7), - IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(8), - IWLAGN_WOWLAN_WAKEUP_ALWAYS = BIT(9), - IWLAGN_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(10), + IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(5), + IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(6), + IWLAGN_WOWLAN_WAKEUP_ALWAYS = BIT(7), + IWLAGN_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(8), }; struct iwlagn_wowlan_wakeup_filter_cmd { __le32 enabled; __le16 non_qos_seq; - u8 min_sleep_seconds; - u8 reserved; + __le16 reserved; __le16 qos_seq[8]; }; diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index b9f3267e720c..fbc3095c7b44 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -284,8 +284,8 @@ #define CSR_HW_REV_TYPE_6x35 CSR_HW_REV_TYPE_6x05 #define CSR_HW_REV_TYPE_2x30 (0x00000C0) #define CSR_HW_REV_TYPE_2x00 (0x0000100) -#define CSR_HW_REV_TYPE_200 (0x0000110) -#define CSR_HW_REV_TYPE_230 (0x0000120) +#define CSR_HW_REV_TYPE_105 (0x0000110) +#define CSR_HW_REV_TYPE_135 (0x0000120) #define CSR_HW_REV_TYPE_NONE (0x00001F0) /* EEPROM REG */ diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/iwlwifi/iwl-devtrace.c index a635a7e75447..2a2c8de64a04 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.c +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.c @@ -28,7 +28,7 @@ /* sparse doesn't like tracepoint macros */ #ifndef __CHECKER__ -#include "iwl-dev.h" +#include "iwl-trans.h" #define CREATE_TRACE_POINTS #include "iwl-devtrace.h" diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index 8a51c5ccda1e..f9d3319ecad5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -29,7 +29,6 @@ #include <linux/tracepoint.h> -struct iwl_priv; #if !defined(CONFIG_IWLWIFI_DEVICE_TRACING) || defined(__CHECKER__) #undef TRACE_EVENT @@ -37,14 +36,14 @@ struct iwl_priv; static inline void trace_ ## name(proto) {} #endif -#define PRIV_ENTRY __field(struct iwl_priv *, priv) +#define PRIV_ENTRY __field(void *, priv) #define PRIV_ASSIGN __entry->priv = priv #undef TRACE_SYSTEM #define TRACE_SYSTEM iwlwifi_io TRACE_EVENT(iwlwifi_dev_ioread32, - TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val), + TP_PROTO(void *priv, u32 offs, u32 val), TP_ARGS(priv, offs, val), TP_STRUCT__entry( PRIV_ENTRY @@ -60,7 +59,7 @@ TRACE_EVENT(iwlwifi_dev_ioread32, ); TRACE_EVENT(iwlwifi_dev_iowrite8, - TP_PROTO(struct iwl_priv *priv, u32 offs, u8 val), + TP_PROTO(void *priv, u32 offs, u8 val), TP_ARGS(priv, offs, val), TP_STRUCT__entry( PRIV_ENTRY @@ -76,7 +75,7 @@ TRACE_EVENT(iwlwifi_dev_iowrite8, ); TRACE_EVENT(iwlwifi_dev_iowrite32, - TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val), + TP_PROTO(void *priv, u32 offs, u32 val), TP_ARGS(priv, offs, val), TP_STRUCT__entry( PRIV_ENTRY @@ -95,7 +94,7 @@ TRACE_EVENT(iwlwifi_dev_iowrite32, #define TRACE_SYSTEM iwlwifi_ucode TRACE_EVENT(iwlwifi_dev_ucode_cont_event, - TP_PROTO(struct iwl_priv *priv, u32 time, u32 data, u32 ev), + TP_PROTO(void *priv, u32 time, u32 data, u32 ev), TP_ARGS(priv, time, data, ev), TP_STRUCT__entry( PRIV_ENTRY @@ -115,7 +114,7 @@ TRACE_EVENT(iwlwifi_dev_ucode_cont_event, ); TRACE_EVENT(iwlwifi_dev_ucode_wrap_event, - TP_PROTO(struct iwl_priv *priv, u32 wraps, u32 n_entry, u32 p_entry), + TP_PROTO(void *priv, u32 wraps, u32 n_entry, u32 p_entry), TP_ARGS(priv, wraps, n_entry, p_entry), TP_STRUCT__entry( PRIV_ENTRY @@ -139,7 +138,7 @@ TRACE_EVENT(iwlwifi_dev_ucode_wrap_event, #define TRACE_SYSTEM iwlwifi TRACE_EVENT(iwlwifi_dev_hcmd, - TP_PROTO(struct iwl_priv *priv, u32 flags, + TP_PROTO(void *priv, u32 flags, const void *hcmd0, size_t len0, const void *hcmd1, size_t len1, const void *hcmd2, size_t len2), @@ -164,7 +163,7 @@ TRACE_EVENT(iwlwifi_dev_hcmd, ); TRACE_EVENT(iwlwifi_dev_rx, - TP_PROTO(struct iwl_priv *priv, void *rxbuf, size_t len), + TP_PROTO(void *priv, void *rxbuf, size_t len), TP_ARGS(priv, rxbuf, len), TP_STRUCT__entry( PRIV_ENTRY @@ -179,7 +178,7 @@ TRACE_EVENT(iwlwifi_dev_rx, ); TRACE_EVENT(iwlwifi_dev_tx, - TP_PROTO(struct iwl_priv *priv, void *tfd, size_t tfdlen, + TP_PROTO(void *priv, void *tfd, size_t tfdlen, void *buf0, size_t buf0_len, void *buf1, size_t buf1_len), TP_ARGS(priv, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len), @@ -211,7 +210,7 @@ TRACE_EVENT(iwlwifi_dev_tx, ); TRACE_EVENT(iwlwifi_dev_ucode_error, - TP_PROTO(struct iwl_priv *priv, u32 desc, u32 tsf_low, + TP_PROTO(void *priv, u32 desc, u32 tsf_low, u32 data1, u32 data2, u32 line, u32 blink1, u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time, u32 gp1, u32 gp2, u32 gp3, u32 ucode_ver, u32 hw_ver, @@ -271,7 +270,7 @@ TRACE_EVENT(iwlwifi_dev_ucode_error, ); TRACE_EVENT(iwlwifi_dev_ucode_event, - TP_PROTO(struct iwl_priv *priv, u32 time, u32 data, u32 ev), + TP_PROTO(void *priv, u32 time, u32 data, u32 ev), TP_ARGS(priv, time, data, ev), TP_STRUCT__entry( PRIV_ENTRY diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index e5d727f537d0..a26fbd33a5d9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -678,7 +678,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) priv->contexts[IWL_RXON_CTX_BSS].active.flags & RXON_FLG_CHANNEL_MODE_MSK) >> RXON_FLG_CHANNEL_MODE_POS; - if (chan_mod == CHANNEL_MODE_PURE_40) { + if ((priv->scan_request && priv->scan_request->no_cck) || + chan_mod == CHANNEL_MODE_PURE_40) { rate = IWL_RATE_6M_PLCP; } else { rate = IWL_RATE_1M_PLCP; diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c index c42be81e979e..48e8218fd23b 100644 --- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c +++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c @@ -165,11 +165,15 @@ static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, struct key_params *params) { struct iwm_priv *iwm = ndev_to_iwm(ndev); - struct iwm_key *key = &iwm->keys[key_index]; + struct iwm_key *key; int ret; IWM_DBG_WEXT(iwm, DBG, "Adding key for %pM\n", mac_addr); + if (key_index >= IWM_NUM_KEYS) + return -ENOENT; + + key = &iwm->keys[key_index]; memset(key, 0, sizeof(struct iwm_key)); ret = iwm_key_init(key, key_index, mac_addr, params); if (ret < 0) { @@ -214,8 +218,12 @@ static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_index, bool pairwise, const u8 *mac_addr) { struct iwm_priv *iwm = ndev_to_iwm(ndev); - struct iwm_key *key = &iwm->keys[key_index]; + struct iwm_key *key; + if (key_index >= IWM_NUM_KEYS) + return -ENOENT; + + key = &iwm->keys[key_index]; if (!iwm->keys[key_index].key_len) { IWM_DBG_WEXT(iwm, DBG, "Key %d not used\n", key_index); return 0; @@ -236,6 +244,9 @@ static int iwm_cfg80211_set_default_key(struct wiphy *wiphy, IWM_DBG_WEXT(iwm, DBG, "Default key index is: %d\n", key_index); + if (key_index >= IWM_NUM_KEYS) + return -ENOENT; + if (!iwm->keys[key_index].key_len) { IWM_ERR(iwm, "Key %d not used\n", key_index); return -EINVAL; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 68455a2307cb..477100d0b117 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1747,6 +1747,8 @@ static int __init init_mac80211_hwsim(void) IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | IEEE80211_HW_AMPDU_AGGREGATION; + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; + /* ask mac80211 to reserve space for magic */ hw->vif_data_size = sizeof(struct hwsim_vif_priv); hw->sta_data_size = sizeof(struct hwsim_sta_priv); diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index d34acf082d3a..a2f32008f9a8 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -386,7 +386,6 @@ static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter) card->txbd_ring_vbase = kzalloc(card->txbd_ring_size, GFP_KERNEL); if (!card->txbd_ring_vbase) { dev_err(adapter->dev, "Unable to allocate buffer for txbd ring.\n"); - kfree(card->txbd_ring_vbase); return -1; } card->txbd_ring_pbase = virt_to_phys(card->txbd_ring_vbase); @@ -1229,9 +1228,12 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, if (!skb) return 0; - if (rdptr >= MWIFIEX_MAX_EVT_BD) + if (rdptr >= MWIFIEX_MAX_EVT_BD) { dev_err(adapter->dev, "event_complete: Invalid rdptr 0x%x\n", rdptr); + ret = -EINVAL; + goto done; + } /* Read the event ring write pointer set by firmware */ if (mwifiex_read_reg(adapter, REG_EVTBD_WRPTR, &wrptr)) { @@ -1672,9 +1674,8 @@ static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type, struct sk_buff *skb, struct mwifiex_tx_param *tx_param) { - if (!adapter || !skb) { - dev_err(adapter->dev, "Invalid parameter in %s <%p, %p>\n", - __func__, adapter, skb); + if (!skb) { + dev_err(adapter->dev, "Passed NULL skb to %s\n", __func__); return -1; } diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index dae8dbb24a03..8a3f9598ad33 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1469,7 +1469,7 @@ mwifiex_update_curr_bss_params(struct mwifiex_private *priv, u8 *bssid, s32 rssi, const u8 *ie_buf, size_t ie_len, u16 beacon_period, u16 cap_info_bitmap, u8 band) { - struct mwifiex_bssdescriptor *bss_desc = NULL; + struct mwifiex_bssdescriptor *bss_desc; int ret; unsigned long flags; u8 *beacon_ie; @@ -1484,6 +1484,7 @@ mwifiex_update_curr_bss_params(struct mwifiex_private *priv, u8 *bssid, beacon_ie = kmemdup(ie_buf, ie_len, GFP_KERNEL); if (!beacon_ie) { + kfree(bss_desc); dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n"); return -ENOMEM; } diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 283171bbcedf..ffaf3f3a57df 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -1630,14 +1630,14 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) card->mpa_tx.pkt_cnt = 0; card->mpa_tx.start_port = 0; - card->mpa_tx.enabled = 0; + card->mpa_tx.enabled = 1; card->mpa_tx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; card->mpa_rx.buf_len = 0; card->mpa_rx.pkt_cnt = 0; card->mpa_rx.start_port = 0; - card->mpa_rx.enabled = 0; + card->mpa_rx.enabled = 1; card->mpa_rx.pkt_aggr_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; /* Allocate buffers for SDIO MP-A */ diff --git a/drivers/net/wireless/orinoco/scan.c b/drivers/net/wireless/orinoco/scan.c index e99ca1c1e0d8..96e39edfec77 100644 --- a/drivers/net/wireless/orinoco/scan.c +++ b/drivers/net/wireless/orinoco/scan.c @@ -76,6 +76,7 @@ static void orinoco_add_hostscan_result(struct orinoco_private *priv, { struct wiphy *wiphy = priv_to_wiphy(priv); struct ieee80211_channel *channel; + struct cfg80211_bss *cbss; u8 *ie; u8 ie_buf[46]; u64 timestamp; @@ -121,9 +122,10 @@ static void orinoco_add_hostscan_result(struct orinoco_private *priv, beacon_interval = le16_to_cpu(bss->a.beacon_interv); signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level)); - cfg80211_inform_bss(wiphy, channel, bss->a.bssid, timestamp, - capability, beacon_interval, ie_buf, ie_len, - signal, GFP_KERNEL); + cbss = cfg80211_inform_bss(wiphy, channel, bss->a.bssid, timestamp, + capability, beacon_interval, ie_buf, ie_len, + signal, GFP_KERNEL); + cfg80211_put_bss(cbss); } void orinoco_add_extscan_result(struct orinoco_private *priv, @@ -132,6 +134,7 @@ void orinoco_add_extscan_result(struct orinoco_private *priv, { struct wiphy *wiphy = priv_to_wiphy(priv); struct ieee80211_channel *channel; + struct cfg80211_bss *cbss; const u8 *ie; u64 timestamp; s32 signal; @@ -152,9 +155,10 @@ void orinoco_add_extscan_result(struct orinoco_private *priv, ie = bss->data; signal = SIGNAL_TO_MBM(bss->level); - cfg80211_inform_bss(wiphy, channel, bss->bssid, timestamp, - capability, beacon_interval, ie, ie_len, - signal, GFP_KERNEL); + cbss = cfg80211_inform_bss(wiphy, channel, bss->bssid, timestamp, + capability, beacon_interval, ie, ie_len, + signal, GFP_KERNEL); + cfg80211_put_bss(cbss); } void orinoco_add_hostscan_results(struct orinoco_private *priv, diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 0c13840a7de5..620e3c0e88e0 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -414,6 +414,7 @@ struct ndis_80211_pmkid { #define RNDIS_WLAN_ALG_TKIP (1<<1) #define RNDIS_WLAN_ALG_CCMP (1<<2) +#define RNDIS_WLAN_NUM_KEYS 4 #define RNDIS_WLAN_KEY_MGMT_NONE 0 #define RNDIS_WLAN_KEY_MGMT_802_1X (1<<0) #define RNDIS_WLAN_KEY_MGMT_PSK (1<<1) @@ -516,7 +517,7 @@ struct rndis_wlan_private { /* encryption stuff */ int encr_tx_key_index; - struct rndis_wlan_encr_key encr_keys[4]; + struct rndis_wlan_encr_key encr_keys[RNDIS_WLAN_NUM_KEYS]; int wpa_version; u8 command_buffer[COMMAND_BUFFER_SIZE]; @@ -1535,6 +1536,9 @@ static int remove_key(struct usbnet *usbdev, int index, const u8 *bssid) bool is_wpa; int ret; + if (index >= RNDIS_WLAN_NUM_KEYS) + return -ENOENT; + if (priv->encr_keys[index].len == 0) return 0; @@ -1972,11 +1976,12 @@ static int rndis_scan(struct wiphy *wiphy, struct net_device *dev, return ret; } -static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev, - struct ndis_80211_bssid_ex *bssid) +static bool rndis_bss_info_update(struct usbnet *usbdev, + struct ndis_80211_bssid_ex *bssid) { struct rndis_wlan_private *priv = get_rndis_wlan_priv(usbdev); struct ieee80211_channel *channel; + struct cfg80211_bss *bss; s32 signal; u64 timestamp; u16 capability; @@ -2015,9 +2020,12 @@ static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev, capability = le16_to_cpu(fixed->capabilities); beacon_interval = le16_to_cpu(fixed->beacon_interval); - return cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid->mac, + bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid->mac, timestamp, capability, beacon_interval, ie, ie_len, signal, GFP_KERNEL); + cfg80211_put_bss(bss); + + return (bss != NULL); } static struct ndis_80211_bssid_ex *next_bssid_list_item( @@ -2451,6 +2459,9 @@ static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, netdev_dbg(usbdev->net, "%s(%i)\n", __func__, key_index); + if (key_index >= RNDIS_WLAN_NUM_KEYS) + return -ENOENT; + priv->encr_tx_key_index = key_index; if (is_wpa_key(priv, key_index)) @@ -2641,6 +2652,7 @@ static void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid, struct ieee80211_channel *channel; struct ndis_80211_conf config; struct ndis_80211_ssid ssid; + struct cfg80211_bss *bss; s32 signal; u64 timestamp; u16 capability; @@ -2714,9 +2726,10 @@ static void rndis_wlan_craft_connected_bss(struct usbnet *usbdev, u8 *bssid, bssid, (u32)timestamp, capability, beacon_interval, ie_len, ssid.essid, signal); - cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid, + bss = cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid, timestamp, capability, beacon_interval, ie_buf, ie_len, signal, GFP_KERNEL); + cfg80211_put_bss(bss); } /* diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c index feed1ed8d9b6..b9a158e5eb0e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c @@ -274,6 +274,8 @@ static struct usb_device_id rtl8192c_usb_ids[] = { {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8191, rtl92cu_hal_cfg)}, /****** 8188CU ********/ + /* RTL8188CTV */ + {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x018a, rtl92cu_hal_cfg)}, /* 8188CE-VAU USB minCard */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8170, rtl92cu_hal_cfg)}, /* 8188cu 1*1 dongle */ @@ -290,14 +292,14 @@ static struct usb_device_id rtl8192c_usb_ids[] = { {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817e, rtl92cu_hal_cfg)}, /* 8188RU in Alfa AWUS036NHR */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817f, rtl92cu_hal_cfg)}, + /* RTL8188CUS-VL */ + {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x818a, rtl92cu_hal_cfg)}, /* 8188 Combo for BC4 */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8754, rtl92cu_hal_cfg)}, /****** 8192CU ********/ - /* 8191cu 1*2 */ - {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8177, rtl92cu_hal_cfg)}, /* 8192cu 2*2 */ - {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817b, rtl92cu_hal_cfg)}, + {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x8178, rtl92cu_hal_cfg)}, /* 8192CE-VAU USB minCard */ {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x817c, rtl92cu_hal_cfg)}, @@ -308,13 +310,17 @@ static struct usb_device_id rtl8192c_usb_ids[] = { {RTL_USB_DEVICE(0x07b8, 0x8188, rtl92cu_hal_cfg)}, /*Abocom - Abocom*/ {RTL_USB_DEVICE(0x07b8, 0x8189, rtl92cu_hal_cfg)}, /*Funai - Abocom*/ {RTL_USB_DEVICE(0x0846, 0x9041, rtl92cu_hal_cfg)}, /*NetGear WNA1000M*/ - {RTL_USB_DEVICE(0x0Df6, 0x0052, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ + {RTL_USB_DEVICE(0x0df6, 0x0052, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ + {RTL_USB_DEVICE(0x0df6, 0x005c, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ {RTL_USB_DEVICE(0x0eb0, 0x9071, rtl92cu_hal_cfg)}, /*NO Brand - Etop*/ /* HP - Lite-On ,8188CUS Slim Combo */ {RTL_USB_DEVICE(0x103c, 0x1629, rtl92cu_hal_cfg)}, {RTL_USB_DEVICE(0x13d3, 0x3357, rtl92cu_hal_cfg)}, /* AzureWave */ {RTL_USB_DEVICE(0x2001, 0x3308, rtl92cu_hal_cfg)}, /*D-Link - Alpha*/ + {RTL_USB_DEVICE(0x2019, 0x4902, rtl92cu_hal_cfg)}, /*Planex - Etop*/ {RTL_USB_DEVICE(0x2019, 0xab2a, rtl92cu_hal_cfg)}, /*Planex - Abocom*/ + /*SW-WF02-AD15 -Abocom*/ + {RTL_USB_DEVICE(0x2019, 0xab2e, rtl92cu_hal_cfg)}, {RTL_USB_DEVICE(0x2019, 0xed17, rtl92cu_hal_cfg)}, /*PCI - Edimax*/ {RTL_USB_DEVICE(0x20f4, 0x648b, rtl92cu_hal_cfg)}, /*TRENDnet - Cameo*/ {RTL_USB_DEVICE(0x7392, 0x7811, rtl92cu_hal_cfg)}, /*Edimax - Edimax*/ @@ -325,14 +331,36 @@ static struct usb_device_id rtl8192c_usb_ids[] = { {RTL_USB_DEVICE(0x4855, 0x0091, rtl92cu_hal_cfg)}, /* NetweeN-Feixun */ {RTL_USB_DEVICE(0x9846, 0x9041, rtl92cu_hal_cfg)}, /* Netgear Cameo */ + /****** 8188 RU ********/ + /* Netcore */ + {RTL_USB_DEVICE(USB_VENDER_ID_REALTEK, 0x317f, rtl92cu_hal_cfg)}, + + /****** 8188CUS Slim Solo********/ + {RTL_USB_DEVICE(0x04f2, 0xaff7, rtl92cu_hal_cfg)}, /*Xavi*/ + {RTL_USB_DEVICE(0x04f2, 0xaff9, rtl92cu_hal_cfg)}, /*Xavi*/ + {RTL_USB_DEVICE(0x04f2, 0xaffa, rtl92cu_hal_cfg)}, /*Xavi*/ + + /****** 8188CUS Slim Combo ********/ + {RTL_USB_DEVICE(0x04f2, 0xaff8, rtl92cu_hal_cfg)}, /*Xavi*/ + {RTL_USB_DEVICE(0x04f2, 0xaffb, rtl92cu_hal_cfg)}, /*Xavi*/ + {RTL_USB_DEVICE(0x04f2, 0xaffc, rtl92cu_hal_cfg)}, /*Xavi*/ + {RTL_USB_DEVICE(0x2019, 0x1201, rtl92cu_hal_cfg)}, /*Planex-Vencer*/ + /****** 8192CU ********/ + {RTL_USB_DEVICE(0x050d, 0x2102, rtl92cu_hal_cfg)}, /*Belcom-Sercomm*/ + {RTL_USB_DEVICE(0x050d, 0x2103, rtl92cu_hal_cfg)}, /*Belcom-Edimax*/ {RTL_USB_DEVICE(0x0586, 0x341f, rtl92cu_hal_cfg)}, /*Zyxel -Abocom*/ {RTL_USB_DEVICE(0x07aa, 0x0056, rtl92cu_hal_cfg)}, /*ATKK-Gemtek*/ {RTL_USB_DEVICE(0x07b8, 0x8178, rtl92cu_hal_cfg)}, /*Funai -Abocom*/ + {RTL_USB_DEVICE(0x0846, 0x9021, rtl92cu_hal_cfg)}, /*Netgear-Sercomm*/ + {RTL_USB_DEVICE(0x0b05, 0x17ab, rtl92cu_hal_cfg)}, /*ASUS-Edimax*/ + {RTL_USB_DEVICE(0x0df6, 0x0061, rtl92cu_hal_cfg)}, /*Sitecom-Edimax*/ + {RTL_USB_DEVICE(0x0e66, 0x0019, rtl92cu_hal_cfg)}, /*Hawking-Edimax*/ {RTL_USB_DEVICE(0x2001, 0x3307, rtl92cu_hal_cfg)}, /*D-Link-Cameo*/ {RTL_USB_DEVICE(0x2001, 0x3309, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/ {RTL_USB_DEVICE(0x2001, 0x330a, rtl92cu_hal_cfg)}, /*D-Link-Alpha*/ {RTL_USB_DEVICE(0x2019, 0xab2b, rtl92cu_hal_cfg)}, /*Planex -Abocom*/ + {RTL_USB_DEVICE(0x20f4, 0x624d, rtl92cu_hal_cfg)}, /*TRENDNet*/ {RTL_USB_DEVICE(0x7392, 0x7822, rtl92cu_hal_cfg)}, /*Edimax -Edimax*/ {} }; diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 128ccb79318c..fc29c671cf3b 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -559,7 +559,7 @@ wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl, break; } /* Fail if SSID isn't present in the filters */ - if (j == req->n_ssids) { + if (j == cmd->n_ssids) { ret = -EINVAL; goto out_free; } diff --git a/include/asm-generic/socket.h b/include/asm-generic/socket.h index 9a6115e7cf63..49c1704173e7 100644 --- a/include/asm-generic/socket.h +++ b/include/asm-generic/socket.h @@ -64,4 +64,7 @@ #define SO_DOMAIN 39 #define SO_RXQ_OVFL 40 + +#define SO_WIFI_STATUS 41 +#define SCM_WIFI_STATUS SO_WIFI_STATUS #endif /* __ASM_GENERIC_SOCKET_H */ diff --git a/include/linux/errqueue.h b/include/linux/errqueue.h index 034072cea853..c9f522bd17e4 100644 --- a/include/linux/errqueue.h +++ b/include/linux/errqueue.h @@ -17,7 +17,8 @@ struct sock_extended_err { #define SO_EE_ORIGIN_LOCAL 1 #define SO_EE_ORIGIN_ICMP 2 #define SO_EE_ORIGIN_ICMP6 3 -#define SO_EE_ORIGIN_TIMESTAMPING 4 +#define SO_EE_ORIGIN_TXSTATUS 4 +#define SO_EE_ORIGIN_TIMESTAMPING SO_EE_ORIGIN_TXSTATUS #define SO_EE_OFFENDER(ee) ((struct sockaddr*)((ee)+1)) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 48363c3c40f8..66cedf6eb5c2 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -128,6 +128,7 @@ #define IEEE80211_QOS_CTL_ACK_POLICY_NOACK 0x0020 #define IEEE80211_QOS_CTL_ACK_POLICY_NO_EXPL 0x0040 #define IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK 0x0060 +#define IEEE80211_QOS_CTL_ACK_POLICY_MASK 0x0060 /* A-MSDU 802.11n */ #define IEEE80211_QOS_CTL_A_MSDU_PRESENT 0x0080 /* Mesh Control 802.11s */ @@ -770,6 +771,9 @@ struct ieee80211_mgmt { } u; } __attribute__ ((packed)); +/* Supported Rates value encodings in 802.11n-2009 7.3.2.2 */ +#define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127 + /* mgmt header + 1 byte category code */ #define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u) @@ -1552,6 +1556,8 @@ enum ieee80211_sa_query_action { #define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 #define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 +#define WLAN_CIPHER_SUITE_SMS4 0x00147201 + /* AKM suite selectors */ #define WLAN_AKM_SUITE_8021X 0x000FAC01 #define WLAN_AKM_SUITE_PSK 0x000FAC02 diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 8049bf77d799..3152ddfb4294 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -509,6 +509,35 @@ * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. * + * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP + * (or GO) interface (i.e. hostapd) to ask for unexpected frames to + * implement sending deauth to stations that send unexpected class 3 + * frames. Also used as the event sent by the kernel when such a frame + * is received. + * For the event, the %NL80211_ATTR_MAC attribute carries the TA and + * other attributes like the interface index are present. + * If used as the command it must have an interface index and you can + * only unsubscribe from the event by closing the socket. Subscription + * is also for %NL80211_CMD_UNEXPECTED_4ADDR_FRAME events. + * + * @NL80211_CMD_UNEXPECTED_4ADDR_FRAME: Sent as an event indicating that the + * associated station identified by %NL80211_ATTR_MAC sent a 4addr frame + * and wasn't already in a 4-addr VLAN. The event will be sent similarly + * to the %NL80211_CMD_UNEXPECTED_FRAME event, to the same listener. + * + * @NL80211_CMD_PROBE_CLIENT: Probe an associated station on an AP interface + * by sending a null data frame to it and reporting when the frame is + * acknowleged. This is used to allow timing out inactive clients. Uses + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_MAC. The command returns a + * direct reply with an %NL80211_ATTR_COOKIE that is later used to match + * up the event with the request. The event includes the same data and + * has %NL80211_ATTR_ACK set if the frame was ACKed. + * + * @NL80211_CMD_REGISTER_BEACONS: Register this socket to receive beacons from + * other BSSes when any interfaces are in AP mode. This helps implement + * OLBC handling in hostapd. Beacons are reported in %NL80211_CMD_FRAME + * messages. Note that per PHY only one application may register. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -638,6 +667,14 @@ enum nl80211_commands { NL80211_CMD_TDLS_OPER, NL80211_CMD_TDLS_MGMT, + NL80211_CMD_UNEXPECTED_FRAME, + + NL80211_CMD_PROBE_CLIENT, + + NL80211_CMD_REGISTER_BEACONS, + + NL80211_CMD_UNEXPECTED_4ADDR_FRAME, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -658,6 +695,8 @@ enum nl80211_commands { #define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE #define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT +#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS + /* source-level API compatibility */ #define NL80211_CMD_GET_MESH_PARAMS NL80211_CMD_GET_MESH_CONFIG #define NL80211_CMD_SET_MESH_PARAMS NL80211_CMD_SET_MESH_CONFIG @@ -1109,6 +1148,19 @@ enum nl80211_commands { * %NL80211_CMD_TDLS_MGMT. Otherwise %NL80211_CMD_TDLS_OPER should be * used for asking the driver to perform a TDLS operation. * + * @NL80211_ATTR_DEVICE_AP_SME: This u32 attribute may be listed for devices + * that have AP support to indicate that they have the AP SME integrated + * with support for the features listed in this attribute, see + * &enum nl80211_ap_sme_features. + * + * @NL80211_ATTR_DONT_WAIT_FOR_ACK: Used with %NL80211_CMD_FRAME, this tells + * the driver to not wait for an acknowledgement. Note that due to this, + * it will also not give a status callback nor return a cookie. This is + * mostly useful for probe responses to save airtime. + * + * @NL80211_ATTR_FEATURE_FLAGS: This u32 attribute contains flags from + * &enum nl80211_feature_flags and is advertised in wiphy information. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1337,6 +1389,12 @@ enum nl80211_attrs { NL80211_ATTR_TDLS_SUPPORT, NL80211_ATTR_TDLS_EXTERNAL_SETUP, + NL80211_ATTR_DEVICE_AP_SME, + + NL80211_ATTR_DONT_WAIT_FOR_ACK, + + NL80211_ATTR_FEATURE_FLAGS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1371,6 +1429,7 @@ enum nl80211_attrs { #define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES #define NL80211_ATTR_KEY NL80211_ATTR_KEY #define NL80211_ATTR_KEYS NL80211_ATTR_KEYS +#define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_REG_RULES 32 @@ -2650,4 +2709,22 @@ enum nl80211_tdls_operation { NL80211_TDLS_DISABLE_LINK, }; +/* + * enum nl80211_ap_sme_features - device-integrated AP features + * Reserved for future use, no bits are defined in + * NL80211_ATTR_DEVICE_AP_SME yet. +enum nl80211_ap_sme_features { +}; + */ + +/** + * enum nl80211_feature_flags - device/driver features + * @NL80211_FEATURE_SK_TX_STATUS: This driver supports reflecting back + * TX status to the socket error queue when requested with the + * socket option. + */ +enum nl80211_feature_flags { + NL80211_FEATURE_SK_TX_STATUS = 1 << 0, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 6a6b352326d7..ff7e1306a2d2 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -218,6 +218,9 @@ enum { /* device driver supports TX zero-copy buffers */ SKBTX_DEV_ZEROCOPY = 1 << 4, + + /* generate wifi status information (where possible) */ + SKBTX_WIFI_STATUS = 1 << 5, }; /* @@ -352,6 +355,8 @@ typedef unsigned char *sk_buff_data_t; * @ooo_okay: allow the mapping of a socket to a queue to be changed * @l4_rxhash: indicate rxhash is a canonical 4-tuple hash over transport * ports. + * @wifi_acked_valid: wifi_acked was set + * @wifi_acked: whether frame was acked on wifi or not * @dma_cookie: a cookie to one of several possible DMA operations * done by skb DMA functions * @secmark: security marking @@ -445,10 +450,11 @@ struct sk_buff { #endif __u8 ooo_okay:1; __u8 l4_rxhash:1; + __u8 wifi_acked_valid:1; + __u8 wifi_acked:1; + /* 10/12 bit hole (depending on ndisc_nodetype presence) */ kmemcheck_bitfield_end(flags2); - /* 0/13 bit hole */ - #ifdef CONFIG_NET_DMA dma_cookie_t dma_cookie; #endif @@ -2263,6 +2269,15 @@ static inline void skb_tx_timestamp(struct sk_buff *skb) sw_tx_timestamp(skb); } +/** + * skb_complete_wifi_ack - deliver skb with wifi status + * + * @skb: the original outgoing packet + * @acked: ack status + * + */ +void skb_complete_wifi_ack(struct sk_buff *skb, bool acked); + extern __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len); extern __sum16 __skb_checksum_complete(struct sk_buff *skb); diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index e727555d4ee9..38cd3dab7f1d 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -77,6 +77,33 @@ struct bt_power { #define BT_POWER_FORCE_ACTIVE_OFF 0 #define BT_POWER_FORCE_ACTIVE_ON 1 +#define BT_CHANNEL_POLICY 10 + +/* BR/EDR only (default policy) + * AMP controllers cannot be used. + * Channel move requests from the remote device are denied. + * If the L2CAP channel is currently using AMP, move the channel to BR/EDR. + */ +#define BT_CHANNEL_POLICY_BREDR_ONLY 0 + +/* BR/EDR Preferred + * Allow use of AMP controllers. + * If the L2CAP channel is currently on AMP, move it to BR/EDR. + * Channel move requests from the remote device are allowed. + */ +#define BT_CHANNEL_POLICY_BREDR_PREFERRED 1 + +/* AMP Preferred + * Allow use of AMP controllers + * If the L2CAP channel is currently on BR/EDR and AMP controller + * resources are available, initiate a channel move to AMP. + * Channel move requests from the remote device are allowed. + * If the L2CAP socket has not been connected yet, try to create + * and configure the channel directly on an AMP controller rather + * than BR/EDR. + */ +#define BT_CHANNEL_POLICY_AMP_PREFERRED 2 + __attribute__((format (printf, 2, 3))) int bt_printk(const char *level, const char *fmt, ...); @@ -158,7 +185,7 @@ struct bt_skb_cb { __u8 pkt_type; __u8 incoming; __u16 expect; - __u8 tx_seq; + __u16 tx_seq; __u8 retries; __u8 sar; unsigned short channel; diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index aaf79af72432..67ad98430348 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -88,6 +88,14 @@ enum { HCI_RESET, }; +/* + * BR/EDR and/or LE controller flags: the flags defined here should represent + * states from the controller. + */ +enum { + HCI_LE_SCAN, +}; + /* HCI ioctl defines */ #define HCIDEVUP _IOW('H', 201, int) #define HCIDEVDOWN _IOW('H', 202, int) @@ -264,6 +272,13 @@ enum { #define HCI_LK_SMP_IRK 0x82 #define HCI_LK_SMP_CSRK 0x83 +/* ---- HCI Error Codes ---- */ +#define HCI_ERROR_AUTH_FAILURE 0x05 +#define HCI_ERROR_REJ_BAD_ADDR 0x0f +#define HCI_ERROR_REMOTE_USER_TERM 0x13 +#define HCI_ERROR_LOCAL_HOST_TERM 0x16 +#define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18 + /* ----- HCI Commands ---- */ #define HCI_OP_NOP 0x0000 @@ -446,6 +461,14 @@ struct hci_rp_user_confirm_reply { #define HCI_OP_USER_CONFIRM_NEG_REPLY 0x042d +#define HCI_OP_USER_PASSKEY_REPLY 0x042e +struct hci_cp_user_passkey_reply { + bdaddr_t bdaddr; + __le32 passkey; +} __packed; + +#define HCI_OP_USER_PASSKEY_NEG_REPLY 0x042f + #define HCI_OP_REMOTE_OOB_DATA_REPLY 0x0430 struct hci_cp_remote_oob_data_reply { bdaddr_t bdaddr; @@ -662,6 +685,12 @@ struct hci_rp_read_local_oob_data { #define HCI_OP_READ_INQ_RSP_TX_POWER 0x0c58 +#define HCI_OP_READ_FLOW_CONTROL_MODE 0x0c66 +struct hci_rp_read_flow_control_mode { + __u8 status; + __u8 mode; +} __packed; + #define HCI_OP_WRITE_LE_HOST_SUPPORTED 0x0c6d struct hci_cp_write_le_host_supported { __u8 le; @@ -726,6 +755,21 @@ struct hci_cp_write_page_scan_activity { #define PAGE_SCAN_TYPE_STANDARD 0x00 #define PAGE_SCAN_TYPE_INTERLACED 0x01 +#define HCI_OP_READ_LOCAL_AMP_INFO 0x1409 +struct hci_rp_read_local_amp_info { + __u8 status; + __u8 amp_status; + __le32 total_bw; + __le32 max_bw; + __le32 min_latency; + __le32 max_pdu; + __u8 amp_type; + __le16 pal_cap; + __le16 max_assoc_size; + __le32 max_flush_to; + __le32 be_flush_to; +} __packed; + #define HCI_OP_LE_SET_EVENT_MASK 0x2001 struct hci_cp_le_set_event_mask { __u8 mask[8]; @@ -738,6 +782,15 @@ struct hci_rp_le_read_buffer_size { __u8 le_max_pkt; } __packed; +#define HCI_OP_LE_SET_SCAN_PARAM 0x200b +struct hci_cp_le_set_scan_param { + __u8 type; + __le16 interval; + __le16 window; + __u8 own_address_type; + __u8 filter_policy; +} __packed; + #define HCI_OP_LE_SET_SCAN_ENABLE 0x200c struct hci_cp_le_set_scan_enable { __u8 enable; @@ -1054,6 +1107,11 @@ struct hci_ev_user_confirm_req { __le32 passkey; } __packed; +#define HCI_EV_USER_PASSKEY_REQUEST 0x34 +struct hci_ev_user_passkey_req { + bdaddr_t bdaddr; +} __packed; + #define HCI_EV_REMOTE_OOB_DATA_REQUEST 0x35 struct hci_ev_remote_oob_data_request { bdaddr_t bdaddr; @@ -1309,4 +1367,6 @@ struct hci_inquiry_req { }; #define IREQ_CACHE_FLUSH 0x0001 +extern int enable_hs; + #endif /* __HCI_H */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5b924423cf20..e34cd71a586e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -32,6 +32,9 @@ #define HCI_PROTO_L2CAP 0 #define HCI_PROTO_SCO 1 +/* HCI priority */ +#define HCI_PRIO_MAX 7 + /* HCI Core structures */ struct inquiry_data { bdaddr_t bdaddr; @@ -64,6 +67,12 @@ struct hci_conn_hash { unsigned int le_num; }; +struct hci_chan_hash { + struct list_head list; + spinlock_t lock; + unsigned int num; +}; + struct bdaddr_list { struct list_head list; bdaddr_t bdaddr; @@ -150,6 +159,19 @@ struct hci_dev { __u16 sniff_min_interval; __u16 sniff_max_interval; + __u8 amp_status; + __u32 amp_total_bw; + __u32 amp_max_bw; + __u32 amp_min_latency; + __u32 amp_max_pdu; + __u8 amp_type; + __u16 amp_pal_cap; + __u16 amp_assoc_size; + __u32 amp_max_flush_to; + __u32 amp_be_flush_to; + + __u8 flow_ctl_mode; + unsigned int auto_accept_delay; unsigned long quirks; @@ -173,8 +195,10 @@ struct hci_dev { struct workqueue_struct *workqueue; struct work_struct power_on; - struct work_struct power_off; - struct timer_list off_timer; + struct delayed_work power_off; + + __u16 discov_timeout; + struct delayed_work discov_off; struct timer_list cmd_timer; struct tasklet_struct cmd_task; @@ -195,6 +219,8 @@ struct hci_dev { __u16 init_last_cmd; + struct list_head mgmt_pending; + struct inquiry_cache inq_cache; struct hci_conn_hash conn_hash; struct list_head blacklist; @@ -226,6 +252,8 @@ struct hci_dev { struct module *owner; + unsigned long dev_flags; + int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); @@ -273,6 +301,7 @@ struct hci_conn { unsigned int sent; struct sk_buff_head data_q; + struct hci_chan_hash chan_hash; struct timer_list disc_timer; struct timer_list idle_timer; @@ -295,6 +324,14 @@ struct hci_conn { void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason); }; +struct hci_chan { + struct list_head list; + + struct hci_conn *conn; + struct sk_buff_head data_q; + unsigned int sent; +}; + extern struct hci_proto *hci_proto[]; extern struct list_head hci_dev_list; extern struct list_head hci_cb_list; @@ -455,6 +492,28 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev, return NULL; } +static inline void hci_chan_hash_init(struct hci_conn *c) +{ + struct hci_chan_hash *h = &c->chan_hash; + INIT_LIST_HEAD(&h->list); + spin_lock_init(&h->lock); + h->num = 0; +} + +static inline void hci_chan_hash_add(struct hci_conn *c, struct hci_chan *chan) +{ + struct hci_chan_hash *h = &c->chan_hash; + list_add(&chan->list, &h->list); + h->num++; +} + +static inline void hci_chan_hash_del(struct hci_conn *c, struct hci_chan *chan) +{ + struct hci_chan_hash *h = &c->chan_hash; + list_del(&chan->list); + h->num--; +} + void hci_acl_connect(struct hci_conn *conn); void hci_acl_disconn(struct hci_conn *conn, __u8 reason); void hci_add_sco(struct hci_conn *conn, __u16 handle); @@ -466,6 +525,10 @@ int hci_conn_del(struct hci_conn *conn); void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev); +struct hci_chan *hci_chan_create(struct hci_conn *conn); +int hci_chan_del(struct hci_chan *chan); +void hci_chan_hash_flush(struct hci_conn *conn); + struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type); int hci_conn_check_link_mode(struct hci_conn *conn); @@ -543,7 +606,7 @@ struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst); struct hci_dev *hci_alloc_dev(void); void hci_free_dev(struct hci_dev *hdev); int hci_register_dev(struct hci_dev *hdev); -int hci_unregister_dev(struct hci_dev *hdev); +void hci_unregister_dev(struct hci_dev *hdev); int hci_suspend_dev(struct hci_dev *hdev); int hci_resume_dev(struct hci_dev *hdev); int hci_dev_open(__u16 dev); @@ -597,8 +660,9 @@ int hci_recv_frame(struct sk_buff *skb); int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count); int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count); -int hci_register_sysfs(struct hci_dev *hdev); -void hci_unregister_sysfs(struct hci_dev *hdev); +void hci_init_sysfs(struct hci_dev *hdev); +int hci_add_sysfs(struct hci_dev *hdev); +void hci_del_sysfs(struct hci_dev *hdev); void hci_conn_init_sysfs(struct hci_conn *conn); void hci_conn_add_sysfs(struct hci_conn *conn); void hci_conn_del_sysfs(struct hci_conn *conn); @@ -674,7 +738,7 @@ static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status) static inline int hci_proto_disconn_ind(struct hci_conn *conn) { register struct hci_proto *hp; - int reason = 0x13; + int reason = HCI_ERROR_REMOTE_USER_TERM; hp = hci_proto[HCI_PROTO_L2CAP]; if (hp && hp->disconn_ind) @@ -834,7 +898,7 @@ int hci_register_notifier(struct notifier_block *nb); int hci_unregister_notifier(struct notifier_block *nb); int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param); -void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags); +void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags); void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb); void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); @@ -847,34 +911,49 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, /* Management interface */ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); -int mgmt_index_added(u16 index); -int mgmt_index_removed(u16 index); -int mgmt_powered(u16 index, u8 powered); -int mgmt_discoverable(u16 index, u8 discoverable); -int mgmt_connectable(u16 index, u8 connectable); -int mgmt_new_key(u16 index, struct link_key *key, u8 persistent); -int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type); -int mgmt_disconnected(u16 index, bdaddr_t *bdaddr); -int mgmt_disconnect_failed(u16 index); -int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status); -int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure); -int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); -int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); -int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value, - u8 confirm_hint); -int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); -int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr, +int mgmt_index_added(struct hci_dev *hdev); +int mgmt_index_removed(struct hci_dev *hdev); +int mgmt_powered(struct hci_dev *hdev, u8 powered); +int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); +int mgmt_connectable(struct hci_dev *hdev, u8 connectable); +int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); +int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, + u8 persistent); +int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type); +int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type); +int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); +int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 status); +int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure); +int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); -int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status); -int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status); -int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer, +int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); -int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi, - u8 *eir); -int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name); -int mgmt_discovering(u16 index, u8 discovering); -int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr); -int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr); +int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, + __le32 value, u8 confirm_hint); +int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status); +int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 status); +int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr); +int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status); +int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 status); +int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); +int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); +int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, + u8 *randomizer, u8 status); +int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 *dev_class, s8 rssi, u8 *eir); +int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name); +int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status); +int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status); +int mgmt_discovering(struct hci_dev *hdev, u8 discovering); +int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr); +int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) @@ -913,4 +992,7 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]); void hci_le_ltk_neg_reply(struct hci_conn *conn); +int hci_do_inquiry(struct hci_dev *hdev, u8 length); +int hci_cancel_inquiry(struct hci_dev *hdev); + #endif /* __HCI_CORE_H */ diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 6cc18f371675..30719eb2e77c 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -27,17 +27,23 @@ #ifndef __L2CAP_H #define __L2CAP_H +#include <asm/unaligned.h> + /* L2CAP defaults */ #define L2CAP_DEFAULT_MTU 672 #define L2CAP_DEFAULT_MIN_MTU 48 #define L2CAP_DEFAULT_FLUSH_TO 0xffff #define L2CAP_DEFAULT_TX_WINDOW 63 +#define L2CAP_DEFAULT_EXT_WINDOW 0x3FFF #define L2CAP_DEFAULT_MAX_TX 3 #define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */ #define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */ #define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */ #define L2CAP_DEFAULT_ACK_TO 200 #define L2CAP_LE_DEFAULT_MTU 23 +#define L2CAP_DEFAULT_MAX_SDU_SIZE 0xFFFF +#define L2CAP_DEFAULT_SDU_ITIME 0xFFFFFFFF +#define L2CAP_DEFAULT_ACC_LAT 0xFFFFFFFF #define L2CAP_DISC_TIMEOUT (100) #define L2CAP_DISC_REJ_TIMEOUT (5000) /* 5 seconds */ @@ -91,52 +97,82 @@ struct l2cap_conninfo { #define L2CAP_ECHO_RSP 0x09 #define L2CAP_INFO_REQ 0x0a #define L2CAP_INFO_RSP 0x0b +#define L2CAP_CREATE_CHAN_REQ 0x0c +#define L2CAP_CREATE_CHAN_RSP 0x0d +#define L2CAP_MOVE_CHAN_REQ 0x0e +#define L2CAP_MOVE_CHAN_RSP 0x0f +#define L2CAP_MOVE_CHAN_CFM 0x10 +#define L2CAP_MOVE_CHAN_CFM_RSP 0x11 #define L2CAP_CONN_PARAM_UPDATE_REQ 0x12 #define L2CAP_CONN_PARAM_UPDATE_RSP 0x13 -/* L2CAP feature mask */ +/* L2CAP extended feature mask */ #define L2CAP_FEAT_FLOWCTL 0x00000001 #define L2CAP_FEAT_RETRANS 0x00000002 +#define L2CAP_FEAT_BIDIR_QOS 0x00000004 #define L2CAP_FEAT_ERTM 0x00000008 #define L2CAP_FEAT_STREAMING 0x00000010 #define L2CAP_FEAT_FCS 0x00000020 +#define L2CAP_FEAT_EXT_FLOW 0x00000040 #define L2CAP_FEAT_FIXED_CHAN 0x00000080 +#define L2CAP_FEAT_EXT_WINDOW 0x00000100 +#define L2CAP_FEAT_UCD 0x00000200 /* L2CAP checksum option */ #define L2CAP_FCS_NONE 0x00 #define L2CAP_FCS_CRC16 0x01 +/* L2CAP fixed channels */ +#define L2CAP_FC_L2CAP 0x02 +#define L2CAP_FC_A2MP 0x08 + /* L2CAP Control Field bit masks */ -#define L2CAP_CTRL_SAR 0xC000 -#define L2CAP_CTRL_REQSEQ 0x3F00 -#define L2CAP_CTRL_TXSEQ 0x007E -#define L2CAP_CTRL_RETRANS 0x0080 -#define L2CAP_CTRL_FINAL 0x0080 -#define L2CAP_CTRL_POLL 0x0010 -#define L2CAP_CTRL_SUPERVISE 0x000C -#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */ - -#define L2CAP_CTRL_TXSEQ_SHIFT 1 -#define L2CAP_CTRL_REQSEQ_SHIFT 8 -#define L2CAP_CTRL_SAR_SHIFT 14 +#define L2CAP_CTRL_SAR 0xC000 +#define L2CAP_CTRL_REQSEQ 0x3F00 +#define L2CAP_CTRL_TXSEQ 0x007E +#define L2CAP_CTRL_SUPERVISE 0x000C + +#define L2CAP_CTRL_RETRANS 0x0080 +#define L2CAP_CTRL_FINAL 0x0080 +#define L2CAP_CTRL_POLL 0x0010 +#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */ + +#define L2CAP_CTRL_TXSEQ_SHIFT 1 +#define L2CAP_CTRL_SUPER_SHIFT 2 +#define L2CAP_CTRL_REQSEQ_SHIFT 8 +#define L2CAP_CTRL_SAR_SHIFT 14 + +/* L2CAP Extended Control Field bit mask */ +#define L2CAP_EXT_CTRL_TXSEQ 0xFFFC0000 +#define L2CAP_EXT_CTRL_SAR 0x00030000 +#define L2CAP_EXT_CTRL_SUPERVISE 0x00030000 +#define L2CAP_EXT_CTRL_REQSEQ 0x0000FFFC + +#define L2CAP_EXT_CTRL_POLL 0x00040000 +#define L2CAP_EXT_CTRL_FINAL 0x00000002 +#define L2CAP_EXT_CTRL_FRAME_TYPE 0x00000001 /* I- or S-Frame */ + +#define L2CAP_EXT_CTRL_REQSEQ_SHIFT 2 +#define L2CAP_EXT_CTRL_SAR_SHIFT 16 +#define L2CAP_EXT_CTRL_SUPER_SHIFT 16 +#define L2CAP_EXT_CTRL_TXSEQ_SHIFT 18 /* L2CAP Supervisory Function */ -#define L2CAP_SUPER_RCV_READY 0x0000 -#define L2CAP_SUPER_REJECT 0x0004 -#define L2CAP_SUPER_RCV_NOT_READY 0x0008 -#define L2CAP_SUPER_SELECT_REJECT 0x000C +#define L2CAP_SUPER_RR 0x00 +#define L2CAP_SUPER_REJ 0x01 +#define L2CAP_SUPER_RNR 0x02 +#define L2CAP_SUPER_SREJ 0x03 /* L2CAP Segmentation and Reassembly */ -#define L2CAP_SDU_UNSEGMENTED 0x0000 -#define L2CAP_SDU_START 0x4000 -#define L2CAP_SDU_END 0x8000 -#define L2CAP_SDU_CONTINUE 0xC000 +#define L2CAP_SAR_UNSEGMENTED 0x00 +#define L2CAP_SAR_START 0x01 +#define L2CAP_SAR_END 0x02 +#define L2CAP_SAR_CONTINUE 0x03 /* L2CAP Command rej. reasons */ -#define L2CAP_REJ_NOT_UNDERSTOOD 0x0000 -#define L2CAP_REJ_MTU_EXCEEDED 0x0001 -#define L2CAP_REJ_INVALID_CID 0x0002 - +#define L2CAP_REJ_NOT_UNDERSTOOD 0x0000 +#define L2CAP_REJ_MTU_EXCEEDED 0x0001 +#define L2CAP_REJ_INVALID_CID 0x0002 /* L2CAP structures */ struct l2cap_hdr { @@ -144,6 +180,12 @@ struct l2cap_hdr { __le16 cid; } __packed; #define L2CAP_HDR_SIZE 4 +#define L2CAP_ENH_HDR_SIZE 6 +#define L2CAP_EXT_HDR_SIZE 8 + +#define L2CAP_FCS_SIZE 2 +#define L2CAP_SDULEN_SIZE 2 +#define L2CAP_PSMLEN_SIZE 2 struct l2cap_cmd_hdr { __u8 code; @@ -188,14 +230,15 @@ struct l2cap_conn_rsp { #define L2CAP_CID_DYN_START 0x0040 #define L2CAP_CID_DYN_END 0xffff -/* connect result */ +/* connect/create channel results */ #define L2CAP_CR_SUCCESS 0x0000 #define L2CAP_CR_PEND 0x0001 #define L2CAP_CR_BAD_PSM 0x0002 #define L2CAP_CR_SEC_BLOCK 0x0003 #define L2CAP_CR_NO_MEM 0x0004 +#define L2CAP_CR_BAD_AMP 0x0005 -/* connect status */ +/* connect/create channel status */ #define L2CAP_CS_NO_INFO 0x0000 #define L2CAP_CS_AUTHEN_PEND 0x0001 #define L2CAP_CS_AUTHOR_PEND 0x0002 @@ -217,6 +260,8 @@ struct l2cap_conf_rsp { #define L2CAP_CONF_UNACCEPT 0x0001 #define L2CAP_CONF_REJECT 0x0002 #define L2CAP_CONF_UNKNOWN 0x0003 +#define L2CAP_CONF_PENDING 0x0004 +#define L2CAP_CONF_EFS_REJECT 0x0005 struct l2cap_conf_opt { __u8 type; @@ -233,6 +278,8 @@ struct l2cap_conf_opt { #define L2CAP_CONF_QOS 0x03 #define L2CAP_CONF_RFC 0x04 #define L2CAP_CONF_FCS 0x05 +#define L2CAP_CONF_EFS 0x06 +#define L2CAP_CONF_EWS 0x07 #define L2CAP_CONF_MAX_SIZE 22 @@ -251,6 +298,21 @@ struct l2cap_conf_rfc { #define L2CAP_MODE_ERTM 0x03 #define L2CAP_MODE_STREAMING 0x04 +struct l2cap_conf_efs { + __u8 id; + __u8 stype; + __le16 msdu; + __le32 sdu_itime; + __le32 acc_lat; + __le32 flush_to; +} __packed; + +#define L2CAP_SERV_NOTRAFIC 0x00 +#define L2CAP_SERV_BESTEFFORT 0x01 +#define L2CAP_SERV_GUARANTEED 0x02 + +#define L2CAP_BESTEFFORT_ID 0x01 + struct l2cap_disconn_req { __le16 dcid; __le16 scid; @@ -271,14 +333,57 @@ struct l2cap_info_rsp { __u8 data[0]; } __packed; +struct l2cap_create_chan_req { + __le16 psm; + __le16 scid; + __u8 amp_id; +} __packed; + +struct l2cap_create_chan_rsp { + __le16 dcid; + __le16 scid; + __le16 result; + __le16 status; +} __packed; + +struct l2cap_move_chan_req { + __le16 icid; + __u8 dest_amp_id; +} __packed; + +struct l2cap_move_chan_rsp { + __le16 icid; + __le16 result; +} __packed; + +#define L2CAP_MR_SUCCESS 0x0000 +#define L2CAP_MR_PEND 0x0001 +#define L2CAP_MR_BAD_ID 0x0002 +#define L2CAP_MR_SAME_ID 0x0003 +#define L2CAP_MR_NOT_SUPP 0x0004 +#define L2CAP_MR_COLLISION 0x0005 +#define L2CAP_MR_NOT_ALLOWED 0x0006 + +struct l2cap_move_chan_cfm { + __le16 icid; + __le16 result; +} __packed; + +#define L2CAP_MC_CONFIRMED 0x0000 +#define L2CAP_MC_UNCONFIRMED 0x0001 + +struct l2cap_move_chan_cfm_rsp { + __le16 icid; +} __packed; + /* info type */ -#define L2CAP_IT_CL_MTU 0x0001 -#define L2CAP_IT_FEAT_MASK 0x0002 -#define L2CAP_IT_FIXED_CHAN 0x0003 +#define L2CAP_IT_CL_MTU 0x0001 +#define L2CAP_IT_FEAT_MASK 0x0002 +#define L2CAP_IT_FIXED_CHAN 0x0003 /* info result */ -#define L2CAP_IR_SUCCESS 0x0000 -#define L2CAP_IR_NOTSUPP 0x0001 +#define L2CAP_IR_SUCCESS 0x0000 +#define L2CAP_IR_NOTSUPP 0x0001 struct l2cap_conn_param_update_req { __le16 min; @@ -297,7 +402,7 @@ struct l2cap_conn_param_update_rsp { /* ----- L2CAP channels and connections ----- */ struct srej_list { - __u8 tx_seq; + __u16 tx_seq; struct list_head list; }; @@ -319,14 +424,11 @@ struct l2cap_chan { __u16 flush_to; __u8 mode; __u8 chan_type; + __u8 chan_policy; __le16 sport; __u8 sec_level; - __u8 role_switch; - __u8 force_reliable; - __u8 flushable; - __u8 force_active; __u8 ident; @@ -337,7 +439,8 @@ struct l2cap_chan { __u8 fcs; - __u8 tx_win; + __u16 tx_win; + __u16 tx_win_max; __u8 max_tx; __u16 retrans_timeout; __u16 monitor_timeout; @@ -345,25 +448,40 @@ struct l2cap_chan { unsigned long conf_state; unsigned long conn_state; - - __u8 next_tx_seq; - __u8 expected_ack_seq; - __u8 expected_tx_seq; - __u8 buffer_seq; - __u8 buffer_seq_srej; - __u8 srej_save_reqseq; - __u8 frames_sent; - __u8 unacked_frames; + unsigned long flags; + + __u16 next_tx_seq; + __u16 expected_ack_seq; + __u16 expected_tx_seq; + __u16 buffer_seq; + __u16 buffer_seq_srej; + __u16 srej_save_reqseq; + __u16 frames_sent; + __u16 unacked_frames; __u8 retry_count; __u8 num_acked; __u16 sdu_len; struct sk_buff *sdu; struct sk_buff *sdu_last_frag; - __u8 remote_tx_win; + __u16 remote_tx_win; __u8 remote_max_tx; __u16 remote_mps; + __u8 local_id; + __u8 local_stype; + __u16 local_msdu; + __u32 local_sdu_itime; + __u32 local_acc_lat; + __u32 local_flush_to; + + __u8 remote_id; + __u8 remote_stype; + __u16 remote_msdu; + __u32 remote_sdu_itime; + __u32 remote_acc_lat; + __u32 remote_flush_to; + struct timer_list chan_timer; struct timer_list retrans_timer; struct timer_list monitor_timer; @@ -391,6 +509,7 @@ struct l2cap_ops { struct l2cap_conn { struct hci_conn *hcon; + struct hci_chan *hchan; bdaddr_t *dst; bdaddr_t *src; @@ -445,6 +564,9 @@ enum { CONF_CONNECT_PEND, CONF_NO_FCS_RECV, CONF_STATE2_DEVICE, + CONF_EWS_RECV, + CONF_LOC_CONF_PEND, + CONF_REM_CONF_PEND, }; #define L2CAP_CONF_MAX_CONF_REQ 2 @@ -462,6 +584,16 @@ enum { CONN_RNR_SENT, }; +/* Definitions for flags in l2cap_chan */ +enum { + FLAG_ROLE_SWITCH, + FLAG_FORCE_ACTIVE, + FLAG_FORCE_RELIABLE, + FLAG_FLUSHABLE, + FLAG_EXT_CTRL, + FLAG_EFS_ENABLE, +}; + #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) #define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer) #define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \ @@ -474,6 +606,22 @@ enum { L2CAP_DEFAULT_ACK_TO); #define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer) +static inline int __seq_offset(struct l2cap_chan *chan, __u16 seq1, __u16 seq2) +{ + int offset; + + offset = (seq1 - seq2) % (chan->tx_win_max + 1); + if (offset < 0) + offset += (chan->tx_win_max + 1); + + return offset; +} + +static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq) +{ + return (seq + 1) % (chan->tx_win_max + 1); +} + static inline int l2cap_tx_window_full(struct l2cap_chan *ch) { int sub; @@ -486,11 +634,162 @@ static inline int l2cap_tx_window_full(struct l2cap_chan *ch) return sub == ch->remote_tx_win; } -#define __get_txseq(ctrl) (((ctrl) & L2CAP_CTRL_TXSEQ) >> 1) -#define __get_reqseq(ctrl) (((ctrl) & L2CAP_CTRL_REQSEQ) >> 8) -#define __is_iframe(ctrl) (!((ctrl) & L2CAP_CTRL_FRAME_TYPE)) -#define __is_sframe(ctrl) ((ctrl) & L2CAP_CTRL_FRAME_TYPE) -#define __is_sar_start(ctrl) (((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START) +static inline __u16 __get_reqseq(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (ctrl & L2CAP_EXT_CTRL_REQSEQ) >> + L2CAP_EXT_CTRL_REQSEQ_SHIFT; + else + return (ctrl & L2CAP_CTRL_REQSEQ) >> L2CAP_CTRL_REQSEQ_SHIFT; +} + +static inline __u32 __set_reqseq(struct l2cap_chan *chan, __u32 reqseq) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT) & + L2CAP_EXT_CTRL_REQSEQ; + else + return (reqseq << L2CAP_CTRL_REQSEQ_SHIFT) & L2CAP_CTRL_REQSEQ; +} + +static inline __u16 __get_txseq(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (ctrl & L2CAP_EXT_CTRL_TXSEQ) >> + L2CAP_EXT_CTRL_TXSEQ_SHIFT; + else + return (ctrl & L2CAP_CTRL_TXSEQ) >> L2CAP_CTRL_TXSEQ_SHIFT; +} + +static inline __u32 __set_txseq(struct l2cap_chan *chan, __u32 txseq) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT) & + L2CAP_EXT_CTRL_TXSEQ; + else + return (txseq << L2CAP_CTRL_TXSEQ_SHIFT) & L2CAP_CTRL_TXSEQ; +} + +static inline bool __is_sframe(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return ctrl & L2CAP_EXT_CTRL_FRAME_TYPE; + else + return ctrl & L2CAP_CTRL_FRAME_TYPE; +} + +static inline __u32 __set_sframe(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_CTRL_FRAME_TYPE; + else + return L2CAP_CTRL_FRAME_TYPE; +} + +static inline __u8 __get_ctrl_sar(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (ctrl & L2CAP_EXT_CTRL_SAR) >> L2CAP_EXT_CTRL_SAR_SHIFT; + else + return (ctrl & L2CAP_CTRL_SAR) >> L2CAP_CTRL_SAR_SHIFT; +} + +static inline __u32 __set_ctrl_sar(struct l2cap_chan *chan, __u32 sar) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (sar << L2CAP_EXT_CTRL_SAR_SHIFT) & L2CAP_EXT_CTRL_SAR; + else + return (sar << L2CAP_CTRL_SAR_SHIFT) & L2CAP_CTRL_SAR; +} + +static inline bool __is_sar_start(struct l2cap_chan *chan, __u32 ctrl) +{ + return __get_ctrl_sar(chan, ctrl) == L2CAP_SAR_START; +} + +static inline __u32 __get_sar_mask(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_CTRL_SAR; + else + return L2CAP_CTRL_SAR; +} + +static inline __u8 __get_ctrl_super(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (ctrl & L2CAP_EXT_CTRL_SUPERVISE) >> + L2CAP_EXT_CTRL_SUPER_SHIFT; + else + return (ctrl & L2CAP_CTRL_SUPERVISE) >> L2CAP_CTRL_SUPER_SHIFT; +} + +static inline __u32 __set_ctrl_super(struct l2cap_chan *chan, __u32 super) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (super << L2CAP_EXT_CTRL_SUPER_SHIFT) & + L2CAP_EXT_CTRL_SUPERVISE; + else + return (super << L2CAP_CTRL_SUPER_SHIFT) & + L2CAP_CTRL_SUPERVISE; +} + +static inline __u32 __set_ctrl_final(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_CTRL_FINAL; + else + return L2CAP_CTRL_FINAL; +} + +static inline bool __is_ctrl_final(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return ctrl & L2CAP_EXT_CTRL_FINAL; + else + return ctrl & L2CAP_CTRL_FINAL; +} + +static inline __u32 __set_ctrl_poll(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_CTRL_POLL; + else + return L2CAP_CTRL_POLL; +} + +static inline bool __is_ctrl_poll(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return ctrl & L2CAP_EXT_CTRL_POLL; + else + return ctrl & L2CAP_CTRL_POLL; +} + +static inline __u32 __get_control(struct l2cap_chan *chan, void *p) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return get_unaligned_le32(p); + else + return get_unaligned_le16(p); +} + +static inline void __put_control(struct l2cap_chan *chan, __u32 control, + void *p) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return put_unaligned_le32(control, p); + else + return put_unaligned_le16(control, p); +} + +static inline __u8 __ctrl_size(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_HDR_SIZE - L2CAP_HDR_SIZE; + else + return L2CAP_ENH_HDR_SIZE - L2CAP_HDR_SIZE; +} extern int disable_ertm; @@ -507,7 +806,9 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk); void l2cap_chan_close(struct l2cap_chan *chan, int reason); void l2cap_chan_destroy(struct l2cap_chan *chan); int l2cap_chan_connect(struct l2cap_chan *chan); -int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len); +int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, + u32 priority); void l2cap_chan_busy(struct l2cap_chan *chan, int busy); +int l2cap_chan_check_security(struct l2cap_chan *chan); #endif /* __L2CAP_H */ diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index d66da0f94f95..3b6880690a78 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -23,6 +23,23 @@ #define MGMT_INDEX_NONE 0xFFFF +#define MGMT_STATUS_SUCCESS 0x00 +#define MGMT_STATUS_UNKNOWN_COMMAND 0x01 +#define MGMT_STATUS_NOT_CONNECTED 0x02 +#define MGMT_STATUS_FAILED 0x03 +#define MGMT_STATUS_CONNECT_FAILED 0x04 +#define MGMT_STATUS_AUTH_FAILED 0x05 +#define MGMT_STATUS_NOT_PAIRED 0x06 +#define MGMT_STATUS_NO_RESOURCES 0x07 +#define MGMT_STATUS_TIMEOUT 0x08 +#define MGMT_STATUS_ALREADY_CONNECTED 0x09 +#define MGMT_STATUS_BUSY 0x0a +#define MGMT_STATUS_REJECTED 0x0b +#define MGMT_STATUS_NOT_SUPPORTED 0x0c +#define MGMT_STATUS_INVALID_PARAMS 0x0d +#define MGMT_STATUS_DISCONNECTED 0x0e +#define MGMT_STATUS_NOT_POWERED 0x0f + struct mgmt_hdr { __le16 opcode; __le16 index; @@ -69,6 +86,10 @@ struct mgmt_mode { #define MGMT_OP_SET_POWERED 0x0005 #define MGMT_OP_SET_DISCOVERABLE 0x0006 +struct mgmt_cp_set_discoverable { + __u8 val; + __u16 timeout; +} __packed; #define MGMT_OP_SET_CONNECTABLE 0x0007 @@ -96,27 +117,29 @@ struct mgmt_cp_set_service_cache { __u8 enable; } __packed; -struct mgmt_key_info { +struct mgmt_link_key_info { bdaddr_t bdaddr; u8 type; u8 val[16]; u8 pin_len; - u8 dlen; - u8 data[0]; } __packed; -#define MGMT_OP_LOAD_KEYS 0x000D -struct mgmt_cp_load_keys { +#define MGMT_OP_LOAD_LINK_KEYS 0x000D +struct mgmt_cp_load_link_keys { __u8 debug_keys; __le16 key_count; - struct mgmt_key_info keys[0]; + struct mgmt_link_key_info keys[0]; } __packed; -#define MGMT_OP_REMOVE_KEY 0x000E -struct mgmt_cp_remove_key { +#define MGMT_OP_REMOVE_KEYS 0x000E +struct mgmt_cp_remove_keys { bdaddr_t bdaddr; __u8 disconnect; } __packed; +struct mgmt_rp_remove_keys { + bdaddr_t bdaddr; + __u8 status; +}; #define MGMT_OP_DISCONNECT 0x000F struct mgmt_cp_disconnect { @@ -124,12 +147,23 @@ struct mgmt_cp_disconnect { } __packed; struct mgmt_rp_disconnect { bdaddr_t bdaddr; + __u8 status; +} __packed; + +#define MGMT_ADDR_BREDR 0x00 +#define MGMT_ADDR_LE_PUBLIC 0x01 +#define MGMT_ADDR_LE_RANDOM 0x02 +#define MGMT_ADDR_INVALID 0xff + +struct mgmt_addr_info { + bdaddr_t bdaddr; + __u8 type; } __packed; #define MGMT_OP_GET_CONNECTIONS 0x0010 struct mgmt_rp_get_connections { __le16 conn_count; - bdaddr_t conn[0]; + struct mgmt_addr_info addr[0]; } __packed; #define MGMT_OP_PIN_CODE_REPLY 0x0011 @@ -155,11 +189,11 @@ struct mgmt_cp_set_io_capability { #define MGMT_OP_PAIR_DEVICE 0x0014 struct mgmt_cp_pair_device { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 io_cap; } __packed; struct mgmt_rp_pair_device { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 status; } __packed; @@ -198,6 +232,9 @@ struct mgmt_cp_remove_remote_oob_data { } __packed; #define MGMT_OP_START_DISCOVERY 0x001B +struct mgmt_cp_start_discovery { + __u8 type; +} __packed; #define MGMT_OP_STOP_DISCOVERY 0x001C @@ -216,6 +253,17 @@ struct mgmt_cp_set_fast_connectable { __u8 enable; } __packed; +#define MGMT_OP_USER_PASSKEY_REPLY 0x0020 +struct mgmt_cp_user_passkey_reply { + bdaddr_t bdaddr; + __le32 passkey; +} __packed; + +#define MGMT_OP_USER_PASSKEY_NEG_REPLY 0x0021 +struct mgmt_cp_user_passkey_neg_reply { + bdaddr_t bdaddr; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -245,26 +293,19 @@ struct mgmt_ev_controller_error { #define MGMT_EV_PAIRABLE 0x0009 -#define MGMT_EV_NEW_KEY 0x000A -struct mgmt_ev_new_key { +#define MGMT_EV_NEW_LINK_KEY 0x000A +struct mgmt_ev_new_link_key { __u8 store_hint; - struct mgmt_key_info key; + struct mgmt_link_key_info key; } __packed; #define MGMT_EV_CONNECTED 0x000B -struct mgmt_ev_connected { - bdaddr_t bdaddr; - __u8 link_type; -} __packed; #define MGMT_EV_DISCONNECTED 0x000C -struct mgmt_ev_disconnected { - bdaddr_t bdaddr; -} __packed; #define MGMT_EV_CONNECT_FAILED 0x000D struct mgmt_ev_connect_failed { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 status; } __packed; @@ -294,7 +335,7 @@ struct mgmt_ev_local_name_changed { #define MGMT_EV_DEVICE_FOUND 0x0012 struct mgmt_ev_device_found { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 dev_class[3]; __s8 rssi; __u8 eir[HCI_MAX_EIR_LENGTH]; @@ -317,3 +358,8 @@ struct mgmt_ev_device_blocked { struct mgmt_ev_device_unblocked { bdaddr_t bdaddr; } __packed; + +#define MGMT_EV_USER_PASSKEY_REQUEST 0x0017 +struct mgmt_ev_user_passkey_request { + bdaddr_t bdaddr; +} __packed; diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 92cf1c2c30c9..e1ee1416631d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -456,6 +456,9 @@ enum station_parameters_apply_mask { * as the AC bitmap in the QoS info field * @max_sp: max Service Period. same format as the MAX_SP in the * QoS info field (but already shifted down) + * @sta_modify_mask: bitmap indicating which parameters changed + * (for those that don't have a natural "no change" value), + * see &enum station_parameters_apply_mask */ struct station_parameters { u8 *supported_rates; @@ -615,6 +618,7 @@ struct sta_bss_parameters { * user space MLME/SME implementation. The information is provided for * the cfg80211_new_sta() calls to notify user space of the IEs. * @assoc_req_ies_len: Length of assoc_req_ies buffer in octets. + * @sta_flags: station flags mask & values */ struct station_info { u32 filled; @@ -1428,6 +1432,9 @@ struct cfg80211_gtk_rekey_data { * * @tdls_mgmt: Transmit a TDLS management frame. * @tdls_oper: Perform a high-level TDLS operation (e.g. TDLS link setup). + * + * @probe_client: probe an associated client, must return a cookie that it + * later passes to cfg80211_probe_status(). */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -1581,7 +1588,7 @@ struct cfg80211_ops { enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, - u64 *cookie); + bool dont_wait_for_ack, u64 *cookie); int (*mgmt_tx_cancel_wait)(struct wiphy *wiphy, struct net_device *dev, u64 cookie); @@ -1617,6 +1624,9 @@ struct cfg80211_ops { u16 status_code, const u8 *buf, size_t len); int (*tdls_oper)(struct wiphy *wiphy, struct net_device *dev, u8 *peer, enum nl80211_tdls_operation oper); + + int (*probe_client)(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u64 *cookie); }; /* @@ -1675,6 +1685,10 @@ struct cfg80211_ops { * teardown packets should be sent through the @NL80211_CMD_TDLS_MGMT * command. When this flag is not set, @NL80211_CMD_TDLS_OPER should be * used for asking the driver/firmware to perform a TDLS operation. + * @WIPHY_FLAG_HAVE_AP_SME: device integrates AP SME + * @WIPHY_FLAG_REPORTS_OBSS: the device will report beacons from other BSSes + * when there are virtual interfaces in AP mode by calling + * cfg80211_report_obss_beacon(). */ enum wiphy_flags { WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0), @@ -1693,6 +1707,8 @@ enum wiphy_flags { WIPHY_FLAG_AP_UAPSD = BIT(14), WIPHY_FLAG_SUPPORTS_TDLS = BIT(15), WIPHY_FLAG_TDLS_EXTERNAL_SETUP = BIT(16), + WIPHY_FLAG_HAVE_AP_SME = BIT(17), + WIPHY_FLAG_REPORTS_OBSS = BIT(18), }; /** @@ -1865,6 +1881,7 @@ struct wiphy_wowlan_support { * @software_iftypes: bitmask of software interface types, these are not * subject to any restrictions since they are purely managed in SW. * @flags: wiphy flags, see &enum wiphy_flags + * @features: features advertised to nl80211, see &enum nl80211_feature_flags. * @bss_priv_size: each BSS struct has private data allocated with it, * this variable determines its size * @max_scan_ssids: maximum number of SSIDs the device can scan for in @@ -1903,6 +1920,8 @@ struct wiphy_wowlan_support { * may request, if implemented. * * @wowlan: WoWLAN support information + * + * @ap_sme_capa: AP SME capabilities, flags from &enum nl80211_ap_sme_features. */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -1924,7 +1943,9 @@ struct wiphy { /* Supported interface modes, OR together BIT(NL80211_IFTYPE_...) */ u16 interface_modes; - u32 flags; + u32 flags, features; + + u32 ap_sme_capa; enum cfg80211_signal_type signal_type; @@ -2179,6 +2200,8 @@ struct wireless_dev { int beacon_interval; + u32 ap_unexpected_nlpid; + #ifdef CONFIG_CFG80211_WEXT /* wext data */ struct { @@ -2632,8 +2655,10 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy); * * This informs cfg80211 that BSS information was found and * the BSS should be updated/added. + * + * NOTE: Returns a referenced struct, must be released with cfg80211_put_bss()! */ -struct cfg80211_bss* +struct cfg80211_bss * __must_check cfg80211_inform_bss_frame(struct wiphy *wiphy, struct ieee80211_channel *channel, struct ieee80211_mgmt *mgmt, size_t len, @@ -2655,8 +2680,10 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, * * This informs cfg80211 that BSS information was found and * the BSS should be updated/added. + * + * NOTE: Returns a referenced struct, must be released with cfg80211_put_bss()! */ -struct cfg80211_bss* +struct cfg80211_bss * __must_check cfg80211_inform_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, const u8 *bssid, @@ -3185,6 +3212,64 @@ void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, const u8 *bssid, bool preauth, gfp_t gfp); +/** + * cfg80211_rx_spurious_frame - inform userspace about a spurious frame + * @dev: The device the frame matched to + * @addr: the transmitter address + * @gfp: context flags + * + * This function is used in AP mode (only!) to inform userspace that + * a spurious class 3 frame was received, to be able to deauth the + * sender. + * Returns %true if the frame was passed to userspace (or this failed + * for a reason other than not having a subscription.) + */ +bool cfg80211_rx_spurious_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp); + +/** + * cfg80211_rx_unexpected_4addr_frame - inform about unexpected WDS frame + * @dev: The device the frame matched to + * @addr: the transmitter address + * @gfp: context flags + * + * This function is used in AP mode (only!) to inform userspace that + * an associated station sent a 4addr frame but that wasn't expected. + * It is allowed and desirable to send this event only once for each + * station to avoid event flooding. + * Returns %true if the frame was passed to userspace (or this failed + * for a reason other than not having a subscription.) + */ +bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp); + +/** + * cfg80211_probe_status - notify userspace about probe status + * @dev: the device the probe was sent on + * @addr: the address of the peer + * @cookie: the cookie filled in @probe_client previously + * @acked: indicates whether probe was acked or not + * @gfp: allocation flags + */ +void cfg80211_probe_status(struct net_device *dev, const u8 *addr, + u64 cookie, bool acked, gfp_t gfp); + +/** + * cfg80211_report_obss_beacon - report beacon from other APs + * @wiphy: The wiphy that received the beacon + * @frame: the frame + * @len: length of the frame + * @freq: frequency the frame was received on + * @gfp: allocation flags + * + * Use this function to report to userspace when a beacon was + * received. It is not useful to call this when there is no + * netdev that is in AP/GO mode. + */ +void cfg80211_report_obss_beacon(struct wiphy *wiphy, + const u8 *frame, size_t len, + int freq, gfp_t gfp); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index dc1123aa8181..2714646b298f 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -518,7 +518,7 @@ struct ieee80211_tx_rate { * @flags: transmit info flags, defined above * @band: the band to transmit on (use for checking for races) * @antenna_sel_tx: antenna to use, 0 for automatic diversity - * @pad: padding, ignore + * @ack_frame_id: internal frame ID for TX status, used internally * @control: union for control data * @status: union for status data * @driver_data: array of driver_data pointers @@ -535,8 +535,7 @@ struct ieee80211_tx_info { u8 antenna_sel_tx; - /* 2 byte hole */ - u8 pad[2]; + u16 ack_frame_id; union { struct { @@ -901,6 +900,10 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) * @IEEE80211_KEY_FLAG_SW_MGMT: This flag should be set by the driver for a * CCMP key if it requires CCMP encryption of management frames (MFP) to * be done in software. + * @IEEE80211_KEY_FLAG_PUT_IV_SPACE: This flag should be set by the driver + * for a CCMP key if space should be prepared for the IV, but the IV + * itself should not be generated. Do not set together with + * @IEEE80211_KEY_FLAG_GENERATE_IV on the same key. */ enum ieee80211_key_flags { IEEE80211_KEY_FLAG_WMM_STA = 1<<0, @@ -908,6 +911,7 @@ enum ieee80211_key_flags { IEEE80211_KEY_FLAG_GENERATE_MMIC= 1<<2, IEEE80211_KEY_FLAG_PAIRWISE = 1<<3, IEEE80211_KEY_FLAG_SW_MGMT = 1<<4, + IEEE80211_KEY_FLAG_PUT_IV_SPACE = 1<<5, }; /** @@ -1304,6 +1308,16 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, } /** + * ieee80211_free_txskb - free TX skb + * @hw: the hardware + * @skb: the skb + * + * Free a transmit skb. Use this funtion when some failure + * to transmit happened and thus status cannot be reported. + */ +void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb); + +/** * DOC: Hardware crypto acceleration * * mac80211 is capable of taking advantage of many hardware @@ -3567,8 +3581,9 @@ rate_lowest_index(struct ieee80211_supported_band *sband, return i; /* warn when we cannot find a rate. */ - WARN_ON(1); + WARN_ON_ONCE(1); + /* and return 0 (the lowest index) */ return 0; } diff --git a/include/net/sock.h b/include/net/sock.h index 5ac682f73d63..fa6f5381c5d6 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -564,6 +564,7 @@ enum sock_flags { SOCK_FASYNC, /* fasync() active */ SOCK_RXQ_OVFL, SOCK_ZEROCOPY, /* buffers from userspace */ + SOCK_WIFI_STATUS, /* push wifi status to userspace */ }; static inline void sock_copy_flags(struct sock *nsk, struct sock *osk) @@ -1714,6 +1715,8 @@ static inline int sock_intr_errno(long timeo) extern void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb); +extern void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk, + struct sk_buff *skb); static __inline__ void sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) @@ -1741,6 +1744,9 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) __sock_recv_timestamp(msg, sk, skb); else sk->sk_stamp = kt; + + if (sock_flag(sk, SOCK_WIFI_STATUS) && skb->wifi_acked_valid) + __sock_recv_wifi_status(msg, sk, skb); } extern void __sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 1eea8208b2cc..42d53b85a808 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -65,15 +65,13 @@ static DECLARE_RWSEM(bnep_session_sem); static struct bnep_session *__bnep_get_session(u8 *dst) { struct bnep_session *s; - struct list_head *p; BT_DBG(""); - list_for_each(p, &bnep_session_list) { - s = list_entry(p, struct bnep_session, list); + list_for_each_entry(s, &bnep_session_list, list) if (!compare_ether_addr(dst, s->eh.h_source)) return s; - } + return NULL; } @@ -665,17 +663,14 @@ static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s) int bnep_get_connlist(struct bnep_connlist_req *req) { - struct list_head *p; + struct bnep_session *s; int err = 0, n = 0; down_read(&bnep_session_sem); - list_for_each(p, &bnep_session_list) { - struct bnep_session *s; + list_for_each_entry(s, &bnep_session_list, list) { struct bnep_conninfo ci; - s = list_entry(p, struct bnep_session, list); - __bnep_copy_ci(&ci, s); if (copy_to_user(req->ci, &ci, sizeof(ci))) { diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c index 5a6e634f7fca..6c9c1fd601ca 100644 --- a/net/bluetooth/cmtp/core.c +++ b/net/bluetooth/cmtp/core.c @@ -53,15 +53,13 @@ static LIST_HEAD(cmtp_session_list); static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr) { struct cmtp_session *session; - struct list_head *p; BT_DBG(""); - list_for_each(p, &cmtp_session_list) { - session = list_entry(p, struct cmtp_session, list); + list_for_each_entry(session, &cmtp_session_list, list) if (!bacmp(bdaddr, &session->bdaddr)) return session; - } + return NULL; } @@ -432,19 +430,16 @@ int cmtp_del_connection(struct cmtp_conndel_req *req) int cmtp_get_connlist(struct cmtp_connlist_req *req) { - struct list_head *p; + struct cmtp_session *session; int err = 0, n = 0; BT_DBG(""); down_read(&cmtp_session_sem); - list_for_each(p, &cmtp_session_list) { - struct cmtp_session *session; + list_for_each_entry(session, &cmtp_session_list, list) { struct cmtp_conninfo ci; - session = list_entry(p, struct cmtp_session, list); - __cmtp_copy_session(session, &ci); if (copy_to_user(req->ci, &ci, sizeof(ci))) { diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index e0af7237cd92..de0b93e45980 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -374,6 +374,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) skb_queue_head_init(&conn->data_q); + hci_chan_hash_init(conn); + setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn); setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn); setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept, @@ -432,6 +434,8 @@ int hci_conn_del(struct hci_conn *conn) tasklet_disable(&hdev->tx_task); + hci_chan_hash_flush(conn); + hci_conn_hash_del(hdev, conn); if (hdev->notify) hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); @@ -453,16 +457,13 @@ int hci_conn_del(struct hci_conn *conn) struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) { int use_src = bacmp(src, BDADDR_ANY); - struct hci_dev *hdev = NULL; - struct list_head *p; + struct hci_dev *hdev = NULL, *d; BT_DBG("%s -> %s", batostr(src), batostr(dst)); read_lock_bh(&hci_dev_list_lock); - list_for_each(p, &hci_dev_list) { - struct hci_dev *d = list_entry(p, struct hci_dev, list); - + list_for_each_entry(d, &hci_dev_list, list) { if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags)) continue; @@ -819,7 +820,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev) c->state = BT_CLOSED; - hci_proto_disconn_cfm(c, 0x16); + hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM); hci_conn_del(c); } } @@ -855,10 +856,10 @@ EXPORT_SYMBOL(hci_conn_put_device); int hci_get_conn_list(void __user *arg) { + register struct hci_conn *c; struct hci_conn_list_req req, *cl; struct hci_conn_info *ci; struct hci_dev *hdev; - struct list_head *p; int n = 0, size, err; if (copy_from_user(&req, arg, sizeof(req))) @@ -882,10 +883,7 @@ int hci_get_conn_list(void __user *arg) ci = cl->conn_info; hci_dev_lock_bh(hdev); - list_for_each(p, &hdev->conn_hash.list) { - register struct hci_conn *c; - c = list_entry(p, struct hci_conn, list); - + list_for_each_entry(c, &hdev->conn_hash.list, list) { bacpy(&(ci + n)->bdaddr, &c->dst); (ci + n)->handle = c->handle; (ci + n)->type = c->type; @@ -956,3 +954,52 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg) return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0; } + +struct hci_chan *hci_chan_create(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_chan *chan; + + BT_DBG("%s conn %p", hdev->name, conn); + + chan = kzalloc(sizeof(struct hci_chan), GFP_ATOMIC); + if (!chan) + return NULL; + + chan->conn = conn; + skb_queue_head_init(&chan->data_q); + + tasklet_disable(&hdev->tx_task); + hci_chan_hash_add(conn, chan); + tasklet_enable(&hdev->tx_task); + + return chan; +} + +int hci_chan_del(struct hci_chan *chan) +{ + struct hci_conn *conn = chan->conn; + struct hci_dev *hdev = conn->hdev; + + BT_DBG("%s conn %p chan %p", hdev->name, conn, chan); + + tasklet_disable(&hdev->tx_task); + hci_chan_hash_del(conn, chan); + tasklet_enable(&hdev->tx_task); + + skb_queue_purge(&chan->data_q); + kfree(chan); + + return 0; +} + +void hci_chan_hash_flush(struct hci_conn *conn) +{ + struct hci_chan_hash *h = &conn->chan_hash; + struct hci_chan *chan, *tmp; + + BT_DBG("conn %p", conn); + + list_for_each_entry_safe(chan, tmp, &h->list, list) + hci_chan_del(chan); +} diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index be84ae33ae36..ce3727ecc0c4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -54,6 +54,8 @@ #define AUTO_OFF_TIMEOUT 2000 +int enable_hs; + static void hci_cmd_task(unsigned long arg); static void hci_rx_task(unsigned long arg); static void hci_tx_task(unsigned long arg); @@ -228,18 +230,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) /* Read Buffer Size (ACL mtu, max pkt, etc.) */ hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); -#if 0 - /* Host buffer size */ - { - struct hci_cp_host_buffer_size cp; - cp.acl_mtu = cpu_to_le16(HCI_MAX_ACL_SIZE); - cp.sco_mtu = HCI_MAX_SCO_SIZE; - cp.acl_max_pkt = cpu_to_le16(0xffff); - cp.sco_max_pkt = cpu_to_le16(0xffff); - hci_send_cmd(hdev, HCI_OP_HOST_BUFFER_SIZE, sizeof(cp), &cp); - } -#endif - /* Read BD Address */ hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); @@ -319,8 +309,7 @@ static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt) * Device is held on return. */ struct hci_dev *hci_dev_get(int index) { - struct hci_dev *hdev = NULL; - struct list_head *p; + struct hci_dev *hdev = NULL, *d; BT_DBG("%d", index); @@ -328,8 +317,7 @@ struct hci_dev *hci_dev_get(int index) return NULL; read_lock(&hci_dev_list_lock); - list_for_each(p, &hci_dev_list) { - struct hci_dev *d = list_entry(p, struct hci_dev, list); + list_for_each_entry(d, &hci_dev_list, list) { if (d->id == index) { hdev = hci_dev_hold(d); break; @@ -523,8 +511,9 @@ int hci_dev_open(__u16 dev) if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) set_bit(HCI_RAW, &hdev->flags); - /* Treat all non BR/EDR controllers as raw devices for now */ - if (hdev->dev_type != HCI_BREDR) + /* Treat all non BR/EDR controllers as raw devices if + enable_hs is not set */ + if (hdev->dev_type != HCI_BREDR && !enable_hs) set_bit(HCI_RAW, &hdev->flags); if (hdev->open(hdev)) { @@ -551,8 +540,11 @@ int hci_dev_open(__u16 dev) hci_dev_hold(hdev); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); - if (!test_bit(HCI_SETUP, &hdev->flags)) - mgmt_powered(hdev->id, 1); + if (!test_bit(HCI_SETUP, &hdev->flags)) { + hci_dev_lock_bh(hdev); + mgmt_powered(hdev, 1); + hci_dev_unlock_bh(hdev); + } } else { /* Init failed, cleanup */ tasklet_kill(&hdev->rx_task); @@ -597,6 +589,14 @@ static int hci_dev_do_close(struct hci_dev *hdev) tasklet_kill(&hdev->rx_task); tasklet_kill(&hdev->tx_task); + if (hdev->discov_timeout > 0) { + cancel_delayed_work(&hdev->discov_off); + hdev->discov_timeout = 0; + } + + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) + cancel_delayed_work(&hdev->power_off); + hci_dev_lock_bh(hdev); inquiry_cache_flush(hdev); hci_conn_hash_flush(hdev); @@ -636,7 +636,9 @@ static int hci_dev_do_close(struct hci_dev *hdev) * and no tasks are scheduled. */ hdev->close(hdev); - mgmt_powered(hdev->id, 0); + hci_dev_lock_bh(hdev); + mgmt_powered(hdev, 0); + hci_dev_unlock_bh(hdev); /* Clear flags */ hdev->flags = 0; @@ -794,9 +796,9 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) int hci_get_dev_list(void __user *arg) { + struct hci_dev *hdev; struct hci_dev_list_req *dl; struct hci_dev_req *dr; - struct list_head *p; int n = 0, size, err; __u16 dev_num; @@ -815,12 +817,9 @@ int hci_get_dev_list(void __user *arg) dr = dl->dev_req; read_lock_bh(&hci_dev_list_lock); - list_for_each(p, &hci_dev_list) { - struct hci_dev *hdev; - - hdev = list_entry(p, struct hci_dev, list); - - hci_del_off_timer(hdev); + list_for_each_entry(hdev, &hci_dev_list, list) { + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) + cancel_delayed_work(&hdev->power_off); if (!test_bit(HCI_MGMT, &hdev->flags)) set_bit(HCI_PAIRABLE, &hdev->flags); @@ -855,7 +854,8 @@ int hci_get_dev_info(void __user *arg) if (!hdev) return -ENODEV; - hci_del_off_timer(hdev); + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) + cancel_delayed_work_sync(&hdev->power_off); if (!test_bit(HCI_MGMT, &hdev->flags)) set_bit(HCI_PAIRABLE, &hdev->flags); @@ -912,6 +912,7 @@ struct hci_dev *hci_alloc_dev(void) if (!hdev) return NULL; + hci_init_sysfs(hdev); skb_queue_head_init(&hdev->driver_init); return hdev; @@ -938,39 +939,41 @@ static void hci_power_on(struct work_struct *work) return; if (test_bit(HCI_AUTO_OFF, &hdev->flags)) - mod_timer(&hdev->off_timer, - jiffies + msecs_to_jiffies(AUTO_OFF_TIMEOUT)); + queue_delayed_work(hdev->workqueue, &hdev->power_off, + msecs_to_jiffies(AUTO_OFF_TIMEOUT)); if (test_and_clear_bit(HCI_SETUP, &hdev->flags)) - mgmt_index_added(hdev->id); + mgmt_index_added(hdev); } static void hci_power_off(struct work_struct *work) { - struct hci_dev *hdev = container_of(work, struct hci_dev, power_off); + struct hci_dev *hdev = container_of(work, struct hci_dev, + power_off.work); BT_DBG("%s", hdev->name); + clear_bit(HCI_AUTO_OFF, &hdev->flags); + hci_dev_close(hdev->id); } -static void hci_auto_off(unsigned long data) +static void hci_discov_off(struct work_struct *work) { - struct hci_dev *hdev = (struct hci_dev *) data; + struct hci_dev *hdev; + u8 scan = SCAN_PAGE; + + hdev = container_of(work, struct hci_dev, discov_off.work); BT_DBG("%s", hdev->name); - clear_bit(HCI_AUTO_OFF, &hdev->flags); + hci_dev_lock_bh(hdev); - queue_work(hdev->workqueue, &hdev->power_off); -} + hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan); -void hci_del_off_timer(struct hci_dev *hdev) -{ - BT_DBG("%s", hdev->name); + hdev->discov_timeout = 0; - clear_bit(HCI_AUTO_OFF, &hdev->flags); - del_timer(&hdev->off_timer); + hci_dev_unlock_bh(hdev); } int hci_uuids_clear(struct hci_dev *hdev) @@ -1007,16 +1010,11 @@ int hci_link_keys_clear(struct hci_dev *hdev) struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) { - struct list_head *p; - - list_for_each(p, &hdev->link_keys) { - struct link_key *k; - - k = list_entry(p, struct link_key, list); + struct link_key *k; + list_for_each_entry(k, &hdev->link_keys, list) if (bacmp(bdaddr, &k->bdaddr) == 0) return k; - } return NULL; } @@ -1138,7 +1136,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, persistent = hci_persistent_key(hdev, conn, type, old_key_type); - mgmt_new_key(hdev->id, key, persistent); + mgmt_new_link_key(hdev, key, persistent); if (!persistent) { list_del(&key->list); @@ -1181,7 +1179,7 @@ int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, memcpy(id->rand, rand, sizeof(id->rand)); if (new_key) - mgmt_new_key(hdev->id, key, old_key_type); + mgmt_new_link_key(hdev, key, old_key_type); return 0; } @@ -1279,16 +1277,11 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) { - struct list_head *p; - - list_for_each(p, &hdev->blacklist) { - struct bdaddr_list *b; - - b = list_entry(p, struct bdaddr_list, list); + struct bdaddr_list *b; + list_for_each_entry(b, &hdev->blacklist, list) if (bacmp(bdaddr, &b->bdaddr) == 0) return b; - } return NULL; } @@ -1327,26 +1320,24 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) list_add(&entry->list, &hdev->blacklist); - return mgmt_device_blocked(hdev->id, bdaddr); + return mgmt_device_blocked(hdev, bdaddr); } int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct bdaddr_list *entry; - if (bacmp(bdaddr, BDADDR_ANY) == 0) { + if (bacmp(bdaddr, BDADDR_ANY) == 0) return hci_blacklist_clear(hdev); - } entry = hci_blacklist_lookup(hdev, bdaddr); - if (!entry) { + if (!entry) return -ENOENT; - } list_del(&entry->list); kfree(entry); - return mgmt_device_unblocked(hdev->id, bdaddr); + return mgmt_device_unblocked(hdev, bdaddr); } static void hci_clear_adv_cache(unsigned long arg) @@ -1425,7 +1416,7 @@ int hci_add_adv_entry(struct hci_dev *hdev, int hci_register_dev(struct hci_dev *hdev) { struct list_head *head = &hci_dev_list, *p; - int i, id = 0; + int i, id, error; BT_DBG("%p name %s bus %d owner %p", hdev, hdev->name, hdev->bus, hdev->owner); @@ -1433,6 +1424,11 @@ int hci_register_dev(struct hci_dev *hdev) if (!hdev->open || !hdev->close || !hdev->destruct) return -EINVAL; + /* Do not allow HCI_AMP devices to register at index 0, + * so the index can be used as the AMP controller ID. + */ + id = (hdev->dev_type == HCI_BREDR) ? 0 : 1; + write_lock_bh(&hci_dev_list_lock); /* Find first available device id */ @@ -1444,12 +1440,13 @@ int hci_register_dev(struct hci_dev *hdev) sprintf(hdev->name, "hci%d", id); hdev->id = id; - list_add(&hdev->list, head); + list_add_tail(&hdev->list, head); atomic_set(&hdev->refcnt, 1); spin_lock_init(&hdev->lock); hdev->flags = 0; + hdev->dev_flags = 0; hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); hdev->esco_type = (ESCO_HV1); hdev->link_mode = (HCI_LM_ACCEPT); @@ -1479,6 +1476,8 @@ int hci_register_dev(struct hci_dev *hdev) hci_conn_hash_init(hdev); + INIT_LIST_HEAD(&hdev->mgmt_pending); + INIT_LIST_HEAD(&hdev->blacklist); INIT_LIST_HEAD(&hdev->uuids); @@ -1492,8 +1491,9 @@ int hci_register_dev(struct hci_dev *hdev) (unsigned long) hdev); INIT_WORK(&hdev->power_on, hci_power_on); - INIT_WORK(&hdev->power_off, hci_power_off); - setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev); + INIT_DELAYED_WORK(&hdev->power_off, hci_power_off); + + INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off); memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); @@ -1502,10 +1502,14 @@ int hci_register_dev(struct hci_dev *hdev) write_unlock_bh(&hci_dev_list_lock); hdev->workqueue = create_singlethread_workqueue(hdev->name); - if (!hdev->workqueue) - goto nomem; + if (!hdev->workqueue) { + error = -ENOMEM; + goto err; + } - hci_register_sysfs(hdev); + error = hci_add_sysfs(hdev); + if (error < 0) + goto err_wqueue; hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, hdev); @@ -1524,17 +1528,19 @@ int hci_register_dev(struct hci_dev *hdev) return id; -nomem: +err_wqueue: + destroy_workqueue(hdev->workqueue); +err: write_lock_bh(&hci_dev_list_lock); list_del(&hdev->list); write_unlock_bh(&hci_dev_list_lock); - return -ENOMEM; + return error; } EXPORT_SYMBOL(hci_register_dev); /* Unregister HCI device */ -int hci_unregister_dev(struct hci_dev *hdev) +void hci_unregister_dev(struct hci_dev *hdev) { int i; @@ -1550,8 +1556,15 @@ int hci_unregister_dev(struct hci_dev *hdev) kfree_skb(hdev->reassembly[i]); if (!test_bit(HCI_INIT, &hdev->flags) && - !test_bit(HCI_SETUP, &hdev->flags)) - mgmt_index_removed(hdev->id); + !test_bit(HCI_SETUP, &hdev->flags)) { + hci_dev_lock_bh(hdev); + mgmt_index_removed(hdev); + hci_dev_unlock_bh(hdev); + } + + /* mgmt_index_removed should take care of emptying the + * pending list */ + BUG_ON(!list_empty(&hdev->mgmt_pending)); hci_notify(hdev, HCI_DEV_UNREG); @@ -1560,9 +1573,8 @@ int hci_unregister_dev(struct hci_dev *hdev) rfkill_destroy(hdev->rfkill); } - hci_unregister_sysfs(hdev); + hci_del_sysfs(hdev); - hci_del_off_timer(hdev); del_timer(&hdev->adv_timer); destroy_workqueue(hdev->workqueue); @@ -1576,8 +1588,6 @@ int hci_unregister_dev(struct hci_dev *hdev) hci_dev_unlock_bh(hdev); __hci_dev_put(hdev); - - return 0; } EXPORT_SYMBOL(hci_unregister_dev); @@ -1948,23 +1958,18 @@ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags) hdr->dlen = cpu_to_le16(len); } -void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) +static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue, + struct sk_buff *skb, __u16 flags) { struct hci_dev *hdev = conn->hdev; struct sk_buff *list; - BT_DBG("%s conn %p flags 0x%x", hdev->name, conn, flags); - - skb->dev = (void *) hdev; - bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_add_acl_hdr(skb, conn->handle, flags); - list = skb_shinfo(skb)->frag_list; if (!list) { /* Non fragmented */ BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len); - skb_queue_tail(&conn->data_q, skb); + skb_queue_tail(queue, skb); } else { /* Fragmented */ BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); @@ -1972,9 +1977,9 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) skb_shinfo(skb)->frag_list = NULL; /* Queue all fragments atomically */ - spin_lock_bh(&conn->data_q.lock); + spin_lock_bh(&queue->lock); - __skb_queue_tail(&conn->data_q, skb); + __skb_queue_tail(queue, skb); flags &= ~ACL_START; flags |= ACL_CONT; @@ -1987,11 +1992,25 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); - __skb_queue_tail(&conn->data_q, skb); + __skb_queue_tail(queue, skb); } while (list); - spin_unlock_bh(&conn->data_q.lock); + spin_unlock_bh(&queue->lock); } +} + +void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags) +{ + struct hci_conn *conn = chan->conn; + struct hci_dev *hdev = conn->hdev; + + BT_DBG("%s chan %p flags 0x%x", hdev->name, chan, flags); + + skb->dev = (void *) hdev; + bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; + hci_add_acl_hdr(skb, conn->handle, flags); + + hci_queue_acl(conn, &chan->data_q, skb, flags); tasklet_schedule(&hdev->tx_task); } @@ -2026,16 +2045,12 @@ EXPORT_SYMBOL(hci_send_sco); static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote) { struct hci_conn_hash *h = &hdev->conn_hash; - struct hci_conn *conn = NULL; + struct hci_conn *conn = NULL, *c; int num = 0, min = ~0; - struct list_head *p; /* We don't have to lock device here. Connections are always * added and removed with TX task disabled. */ - list_for_each(p, &h->list) { - struct hci_conn *c; - c = list_entry(p, struct hci_conn, list); - + list_for_each_entry(c, &h->list, list) { if (c->type != type || skb_queue_empty(&c->data_q)) continue; @@ -2084,14 +2099,12 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type) { struct hci_conn_hash *h = &hdev->conn_hash; - struct list_head *p; - struct hci_conn *c; + struct hci_conn *c; BT_ERR("%s link tx timeout", hdev->name); /* Kill stalled connections */ - list_for_each(p, &h->list) { - c = list_entry(p, struct hci_conn, list); + list_for_each_entry(c, &h->list, list) { if (c->type == type && c->sent) { BT_ERR("%s killing stalled connection %s", hdev->name, batostr(&c->dst)); @@ -2100,11 +2113,137 @@ static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type) } } -static inline void hci_sched_acl(struct hci_dev *hdev) +static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, + int *quote) { + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_chan *chan = NULL; + int num = 0, min = ~0, cur_prio = 0; struct hci_conn *conn; + int cnt, q, conn_num = 0; + + BT_DBG("%s", hdev->name); + + list_for_each_entry(conn, &h->list, list) { + struct hci_chan_hash *ch; + struct hci_chan *tmp; + + if (conn->type != type) + continue; + + if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG) + continue; + + conn_num++; + + ch = &conn->chan_hash; + + list_for_each_entry(tmp, &ch->list, list) { + struct sk_buff *skb; + + if (skb_queue_empty(&tmp->data_q)) + continue; + + skb = skb_peek(&tmp->data_q); + if (skb->priority < cur_prio) + continue; + + if (skb->priority > cur_prio) { + num = 0; + min = ~0; + cur_prio = skb->priority; + } + + num++; + + if (conn->sent < min) { + min = conn->sent; + chan = tmp; + } + } + + if (hci_conn_num(hdev, type) == conn_num) + break; + } + + if (!chan) + return NULL; + + switch (chan->conn->type) { + case ACL_LINK: + cnt = hdev->acl_cnt; + break; + case SCO_LINK: + case ESCO_LINK: + cnt = hdev->sco_cnt; + break; + case LE_LINK: + cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt; + break; + default: + cnt = 0; + BT_ERR("Unknown link type"); + } + + q = cnt / num; + *quote = q ? q : 1; + BT_DBG("chan %p quote %d", chan, *quote); + return chan; +} + +static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_conn *conn; + int num = 0; + + BT_DBG("%s", hdev->name); + + list_for_each_entry(conn, &h->list, list) { + struct hci_chan_hash *ch; + struct hci_chan *chan; + + if (conn->type != type) + continue; + + if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG) + continue; + + num++; + + ch = &conn->chan_hash; + list_for_each_entry(chan, &ch->list, list) { + struct sk_buff *skb; + + if (chan->sent) { + chan->sent = 0; + continue; + } + + if (skb_queue_empty(&chan->data_q)) + continue; + + skb = skb_peek(&chan->data_q); + if (skb->priority >= HCI_PRIO_MAX - 1) + continue; + + skb->priority = HCI_PRIO_MAX - 1; + + BT_DBG("chan %p skb %p promoted to %d", chan, skb, + skb->priority); + } + + if (hci_conn_num(hdev, type) == num) + break; + } +} + +static inline void hci_sched_acl(struct hci_dev *hdev) +{ + struct hci_chan *chan; struct sk_buff *skb; int quote; + unsigned int cnt; BT_DBG("%s", hdev->name); @@ -2118,19 +2257,35 @@ static inline void hci_sched_acl(struct hci_dev *hdev) hci_link_tx_to(hdev, ACL_LINK); } - while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) { - while (quote-- && (skb = skb_dequeue(&conn->data_q))) { - BT_DBG("skb %p len %d", skb, skb->len); + cnt = hdev->acl_cnt; - hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active); + while (hdev->acl_cnt && + (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { + u32 priority = (skb_peek(&chan->data_q))->priority; + while (quote-- && (skb = skb_peek(&chan->data_q))) { + BT_DBG("chan %p skb %p len %d priority %u", chan, skb, + skb->len, skb->priority); + + /* Stop if priority has changed */ + if (skb->priority < priority) + break; + + skb = skb_dequeue(&chan->data_q); + + hci_conn_enter_active_mode(chan->conn, + bt_cb(skb)->force_active); hci_send_frame(skb); hdev->acl_last_tx = jiffies; hdev->acl_cnt--; - conn->sent++; + chan->sent++; + chan->conn->sent++; } } + + if (cnt != hdev->acl_cnt) + hci_prio_recalculate(hdev, ACL_LINK); } /* Schedule SCO */ @@ -2182,9 +2337,9 @@ static inline void hci_sched_esco(struct hci_dev *hdev) static inline void hci_sched_le(struct hci_dev *hdev) { - struct hci_conn *conn; + struct hci_chan *chan; struct sk_buff *skb; - int quote, cnt; + int quote, cnt, tmp; BT_DBG("%s", hdev->name); @@ -2200,21 +2355,35 @@ static inline void hci_sched_le(struct hci_dev *hdev) } cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt; - while (cnt && (conn = hci_low_sent(hdev, LE_LINK, "e))) { - while (quote-- && (skb = skb_dequeue(&conn->data_q))) { - BT_DBG("skb %p len %d", skb, skb->len); + tmp = cnt; + while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) { + u32 priority = (skb_peek(&chan->data_q))->priority; + while (quote-- && (skb = skb_peek(&chan->data_q))) { + BT_DBG("chan %p skb %p len %d priority %u", chan, skb, + skb->len, skb->priority); + + /* Stop if priority has changed */ + if (skb->priority < priority) + break; + + skb = skb_dequeue(&chan->data_q); hci_send_frame(skb); hdev->le_last_tx = jiffies; cnt--; - conn->sent++; + chan->sent++; + chan->conn->sent++; } } + if (hdev->le_pkts) hdev->le_cnt = cnt; else hdev->acl_cnt = cnt; + + if (cnt != tmp) + hci_prio_recalculate(hdev, LE_LINK); } static void hci_tx_task(unsigned long arg) @@ -2407,3 +2576,34 @@ static void hci_cmd_task(unsigned long arg) } } } + +int hci_do_inquiry(struct hci_dev *hdev, u8 length) +{ + /* General inquiry access code (GIAC) */ + u8 lap[3] = { 0x33, 0x8b, 0x9e }; + struct hci_cp_inquiry cp; + + BT_DBG("%s", hdev->name); + + if (test_bit(HCI_INQUIRY, &hdev->flags)) + return -EINPROGRESS; + + memset(&cp, 0, sizeof(cp)); + memcpy(&cp.lap, lap, sizeof(cp.lap)); + cp.length = length; + + return hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); +} + +int hci_cancel_inquiry(struct hci_dev *hdev) +{ + BT_DBG("%s", hdev->name); + + if (!test_bit(HCI_INQUIRY, &hdev->flags)) + return -EPERM; + + return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL); +} + +module_param(enable_hs, bool, 0644); +MODULE_PARM_DESC(enable_hs, "Enable High Speed"); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 643a41b76e2e..e3f7a8192446 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -55,12 +55,18 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, status); - if (status) + if (status) { + hci_dev_lock(hdev); + mgmt_stop_discovery_failed(hdev, status); + hci_dev_unlock(hdev); return; + } - if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) && - test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 0); + clear_bit(HCI_INQUIRY, &hdev->flags); + + hci_dev_lock(hdev); + mgmt_discovering(hdev, 0); + hci_dev_unlock(hdev); hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); @@ -76,10 +82,6 @@ static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb) if (status) return; - if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) && - test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 0); - hci_conn_check_pending(hdev); } @@ -192,6 +194,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_RESET, &hdev->flags); hci_req_complete(hdev, HCI_OP_RESET, status); + + hdev->dev_flags = 0; } static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) @@ -205,13 +209,15 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) if (!sent) return; + hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_set_local_name_complete(hdev->id, sent, status); + mgmt_set_local_name_complete(hdev, sent, status); - if (status) - return; + if (status == 0) + memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH); - memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH); + hci_dev_unlock(hdev); } static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb) @@ -274,7 +280,8 @@ static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) { - __u8 status = *((__u8 *) skb->data); + __u8 param, status = *((__u8 *) skb->data); + int old_pscan, old_iscan; void *sent; BT_DBG("%s status 0x%x", hdev->name, status); @@ -283,28 +290,40 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) if (!sent) return; - if (!status) { - __u8 param = *((__u8 *) sent); - int old_pscan, old_iscan; - - old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); - old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); + param = *((__u8 *) sent); - if (param & SCAN_INQUIRY) { - set_bit(HCI_ISCAN, &hdev->flags); - if (!old_iscan) - mgmt_discoverable(hdev->id, 1); - } else if (old_iscan) - mgmt_discoverable(hdev->id, 0); + hci_dev_lock(hdev); - if (param & SCAN_PAGE) { - set_bit(HCI_PSCAN, &hdev->flags); - if (!old_pscan) - mgmt_connectable(hdev->id, 1); - } else if (old_pscan) - mgmt_connectable(hdev->id, 0); + if (status != 0) { + mgmt_write_scan_failed(hdev, param, status); + hdev->discov_timeout = 0; + goto done; } + old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); + old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); + + if (param & SCAN_INQUIRY) { + set_bit(HCI_ISCAN, &hdev->flags); + if (!old_iscan) + mgmt_discoverable(hdev, 1); + if (hdev->discov_timeout > 0) { + int to = msecs_to_jiffies(hdev->discov_timeout * 1000); + queue_delayed_work(hdev->workqueue, &hdev->discov_off, + to); + } + } else if (old_iscan) + mgmt_discoverable(hdev, 0); + + if (param & SCAN_PAGE) { + set_bit(HCI_PSCAN, &hdev->flags); + if (!old_pscan) + mgmt_connectable(hdev, 1); + } else if (old_pscan) + mgmt_connectable(hdev, 0); + +done: + hci_dev_unlock(hdev); hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); } @@ -700,6 +719,21 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev, hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status); } +static void hci_cc_read_flow_control_mode(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_flow_control_mode *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + hdev->flow_ctl_mode = rp->mode; + + hci_req_complete(hdev, HCI_OP_READ_FLOW_CONTROL_MODE, rp->status); +} + static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_buffer_size *rp = (void *) skb->data; @@ -748,6 +782,30 @@ static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status); } +static void hci_cc_read_local_amp_info(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_local_amp_info *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + hdev->amp_status = rp->amp_status; + hdev->amp_total_bw = __le32_to_cpu(rp->total_bw); + hdev->amp_max_bw = __le32_to_cpu(rp->max_bw); + hdev->amp_min_latency = __le32_to_cpu(rp->min_latency); + hdev->amp_max_pdu = __le32_to_cpu(rp->max_pdu); + hdev->amp_type = rp->amp_type; + hdev->amp_pal_cap = __le16_to_cpu(rp->pal_cap); + hdev->amp_assoc_size = __le16_to_cpu(rp->max_assoc_size); + hdev->amp_be_flush_to = __le32_to_cpu(rp->be_flush_to); + hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to); + + hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status); +} + static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, struct sk_buff *skb) { @@ -804,19 +862,24 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, rp->status); + hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_pin_code_reply_complete(hdev->id, &rp->bdaddr, rp->status); + mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status); if (rp->status != 0) - return; + goto unlock; cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY); if (!cp) - return; + goto unlock; conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); if (conn) conn->pin_length = cp->pin_len; + +unlock: + hci_dev_unlock(hdev); } static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -825,10 +888,15 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, rp->status); + hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr, + mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr, rp->status); + + hci_dev_unlock(hdev); } + static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) { @@ -855,9 +923,13 @@ static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, rp->status); + hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_user_confirm_reply_complete(hdev->id, &rp->bdaddr, + mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, rp->status); + + hci_dev_unlock(hdev); } static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, @@ -867,9 +939,44 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, BT_DBG("%s status 0x%x", hdev->name, rp->status); + hci_dev_lock(hdev); + + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr, + rp->status); + + hci_dev_unlock(hdev); +} + +static void hci_cc_user_passkey_reply(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_user_confirm_reply *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + hci_dev_lock(hdev); + + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr, + rp->status); + + hci_dev_unlock(hdev); +} + +static void hci_cc_user_passkey_neg_reply(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_user_confirm_reply *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_user_confirm_neg_reply_complete(hdev->id, &rp->bdaddr, + mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr, rp->status); + + hci_dev_unlock(hdev); } static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, @@ -879,8 +986,17 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, BT_DBG("%s status 0x%x", hdev->name, rp->status); - mgmt_read_local_oob_data_reply_complete(hdev->id, rp->hash, + hci_dev_lock(hdev); + mgmt_read_local_oob_data_reply_complete(hdev, rp->hash, rp->randomizer, rp->status); + hci_dev_unlock(hdev); +} + +static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%x", hdev->name, status); } static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, @@ -899,12 +1015,16 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, return; if (cp->enable == 0x01) { + set_bit(HCI_LE_SCAN, &hdev->dev_flags); + del_timer(&hdev->adv_timer); hci_dev_lock(hdev); hci_adv_entries_clear(hdev); hci_dev_unlock(hdev); } else if (cp->enable == 0x00) { + clear_bit(HCI_LE_SCAN, &hdev->dev_flags); + mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT); } } @@ -955,12 +1075,18 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) if (status) { hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); + hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_start_discovery_failed(hdev, status); + hci_dev_unlock(hdev); return; } - if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags) && - test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 1); + set_bit(HCI_INQUIRY, &hdev->flags); + + hci_dev_lock(hdev); + mgmt_discovering(hdev, 1); + hci_dev_unlock(hdev); } static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) @@ -1339,13 +1465,16 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff BT_DBG("%s status %d", hdev->name, status); - if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) && - test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 0); - hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); + + if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags)) + return; + + hci_dev_lock(hdev); + mgmt_discovering(hdev, 0); + hci_dev_unlock(hdev); } static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) @@ -1361,12 +1490,6 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * hci_dev_lock(hdev); - if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) { - - if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 1); - } - for (; num_rsp; num_rsp--, info++) { bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; @@ -1377,8 +1500,8 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * data.rssi = 0x00; data.ssp_mode = 0x00; hci_inquiry_cache_update(hdev, &data); - mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class, 0, - NULL); + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, + info->dev_class, 0, NULL); } hci_dev_unlock(hdev); @@ -1412,7 +1535,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn->state = BT_CONFIG; hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; - mgmt_connected(hdev->id, &ev->bdaddr, conn->type); + mgmt_connected(hdev, &ev->bdaddr, conn->type, + conn->dst_type); } else conn->state = BT_CONNECTED; @@ -1444,7 +1568,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s } else { conn->state = BT_CLOSED; if (conn->type == ACL_LINK) - mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status); + mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, + conn->dst_type, ev->status); } if (conn->type == ACL_LINK) @@ -1531,7 +1656,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk struct hci_cp_reject_conn_req cp; bacpy(&cp.bdaddr, &ev->bdaddr); - cp.reason = 0x0f; + cp.reason = HCI_ERROR_REJ_BAD_ADDR; hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp); } } @@ -1543,24 +1668,27 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff BT_DBG("%s status %d", hdev->name, ev->status); - if (ev->status) { - mgmt_disconnect_failed(hdev->id); - return; - } - hci_dev_lock(hdev); conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (!conn) goto unlock; - conn->state = BT_CLOSED; + if (ev->status == 0) + conn->state = BT_CLOSED; - if (conn->type == ACL_LINK || conn->type == LE_LINK) - mgmt_disconnected(hdev->id, &conn->dst); + if (conn->type == ACL_LINK || conn->type == LE_LINK) { + if (ev->status != 0) + mgmt_disconnect_failed(hdev, &conn->dst, ev->status); + else + mgmt_disconnected(hdev, &conn->dst, conn->type, + conn->dst_type); + } - hci_proto_disconn_cfm(conn, ev->reason); - hci_conn_del(conn); + if (ev->status == 0) { + hci_proto_disconn_cfm(conn, ev->reason); + hci_conn_del(conn); + } unlock: hci_dev_unlock(hdev); @@ -1588,7 +1716,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn->sec_level = conn->pending_sec_level; } } else { - mgmt_auth_failed(hdev->id, &conn->dst, ev->status); + mgmt_auth_failed(hdev, &conn->dst, ev->status); } clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); @@ -1643,7 +1771,7 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb hci_dev_lock(hdev); if (ev->status == 0 && test_bit(HCI_MGMT, &hdev->flags)) - mgmt_remote_name(hdev->id, &ev->bdaddr, ev->name); + mgmt_remote_name(hdev, &ev->bdaddr, ev->name); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (!conn) @@ -1898,6 +2026,14 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_write_ca_timeout(hdev, skb); break; + case HCI_OP_READ_FLOW_CONTROL_MODE: + hci_cc_read_flow_control_mode(hdev, skb); + break; + + case HCI_OP_READ_LOCAL_AMP_INFO: + hci_cc_read_local_amp_info(hdev, skb); + break; + case HCI_OP_DELETE_STORED_LINK_KEY: hci_cc_delete_stored_link_key(hdev, skb); break; @@ -1942,6 +2078,17 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_user_confirm_neg_reply(hdev, skb); break; + case HCI_OP_USER_PASSKEY_REPLY: + hci_cc_user_passkey_reply(hdev, skb); + break; + + case HCI_OP_USER_PASSKEY_NEG_REPLY: + hci_cc_user_passkey_neg_reply(hdev, skb); + + case HCI_OP_LE_SET_SCAN_PARAM: + hci_cc_le_set_scan_param(hdev, skb); + break; + case HCI_OP_LE_SET_SCAN_ENABLE: hci_cc_le_set_scan_enable(hdev, skb); break; @@ -2029,7 +2176,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) case HCI_OP_DISCONNECT: if (ev->status != 0) - mgmt_disconnect_failed(hdev->id); + mgmt_disconnect_failed(hdev, NULL, ev->status); break; case HCI_OP_LE_CREATE_CONN: @@ -2194,7 +2341,7 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff else secure = 0; - mgmt_pin_code_request(hdev->id, &ev->bdaddr, secure); + mgmt_pin_code_request(hdev, &ev->bdaddr, secure); } unlock: @@ -2363,12 +2510,6 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct hci_dev_lock(hdev); - if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) { - - if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 1); - } - if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) { struct inquiry_info_with_rssi_and_pscan_mode *info; info = (void *) (skb->data + 1); @@ -2383,7 +2524,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.rssi = info->rssi; data.ssp_mode = 0x00; hci_inquiry_cache_update(hdev, &data); - mgmt_device_found(hdev->id, &info->bdaddr, + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, NULL); } @@ -2400,7 +2541,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.rssi = info->rssi; data.ssp_mode = 0x00; hci_inquiry_cache_update(hdev, &data); - mgmt_device_found(hdev->id, &info->bdaddr, + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, NULL); } @@ -2531,12 +2672,6 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct if (!num_rsp) return; - if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) { - - if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 1); - } - hci_dev_lock(hdev); for (; num_rsp; num_rsp--, info++) { @@ -2549,8 +2684,8 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct data.rssi = info->rssi; data.ssp_mode = 0x01; hci_inquiry_cache_update(hdev, &data); - mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class, - info->rssi, info->data); + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, + info->dev_class, info->rssi, info->data); } hci_dev_unlock(hdev); @@ -2614,7 +2749,7 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff struct hci_cp_io_capability_neg_reply cp; bacpy(&cp.bdaddr, &ev->bdaddr); - cp.reason = 0x18; /* Pairing not allowed */ + cp.reason = HCI_ERROR_PAIRING_NOT_ALLOWED; hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY, sizeof(cp), &cp); @@ -2706,13 +2841,28 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev, } confirm: - mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey, + mgmt_user_confirm_request(hdev, &ev->bdaddr, ev->passkey, confirm_hint); unlock: hci_dev_unlock(hdev); } +static inline void hci_user_passkey_request_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_user_passkey_req *ev = (void *) skb->data; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_user_passkey_request(hdev, &ev->bdaddr); + + hci_dev_unlock(hdev); +} + static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_simple_pair_complete *ev = (void *) skb->data; @@ -2732,7 +2882,7 @@ static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_ * event gets always produced as initiator and is also mapped to * the mgmt_auth_failed event */ if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) && ev->status != 0) - mgmt_auth_failed(hdev->id, &conn->dst, ev->status); + mgmt_auth_failed(hdev, &conn->dst, ev->status); hci_conn_put(conn); @@ -2813,14 +2963,15 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff } if (ev->status) { - mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status); + mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, + conn->dst_type, ev->status); hci_proto_connect_cfm(conn, ev->status); conn->state = BT_CLOSED; hci_conn_del(conn); goto unlock; } - mgmt_connected(hdev->id, &ev->bdaddr, conn->type); + mgmt_connected(hdev, &ev->bdaddr, conn->type, conn->dst_type); conn->sec_level = BT_SECURITY_LOW; conn->handle = __le16_to_cpu(ev->handle); @@ -3051,6 +3202,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_user_confirm_request_evt(hdev, skb); break; + case HCI_EV_USER_PASSKEY_REQUEST: + hci_user_passkey_request_evt(hdev, skb); + break; + case HCI_EV_SIMPLE_PAIR_COMPLETE: hci_simple_pair_complete_evt(hdev, skb); break; @@ -3104,5 +3259,5 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) kfree_skb(skb); } -module_param(enable_le, bool, 0444); +module_param(enable_le, bool, 0644); MODULE_PARM_DESC(enable_le, "Enable LE support"); diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 22f1a6c87035..f8e6aa386cef 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -435,17 +435,12 @@ static const struct file_operations inquiry_cache_fops = { static int blacklist_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; - struct list_head *l; + struct bdaddr_list *b; hci_dev_lock_bh(hdev); - list_for_each(l, &hdev->blacklist) { - struct bdaddr_list *b; - - b = list_entry(l, struct bdaddr_list, list); - + list_for_each_entry(b, &hdev->blacklist, list) seq_printf(f, "%s\n", batostr(&b->bdaddr)); - } hci_dev_unlock_bh(hdev); @@ -484,17 +479,12 @@ static void print_bt_uuid(struct seq_file *f, u8 *uuid) static int uuids_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; - struct list_head *l; + struct bt_uuid *uuid; hci_dev_lock_bh(hdev); - list_for_each(l, &hdev->uuids) { - struct bt_uuid *uuid; - - uuid = list_entry(l, struct bt_uuid, list); - + list_for_each_entry(uuid, &hdev->uuids, list) print_bt_uuid(f, uuid->uuid); - } hci_dev_unlock_bh(hdev); @@ -542,22 +532,28 @@ static int auto_accept_delay_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get, auto_accept_delay_set, "%llu\n"); -int hci_register_sysfs(struct hci_dev *hdev) +void hci_init_sysfs(struct hci_dev *hdev) +{ + struct device *dev = &hdev->dev; + + dev->type = &bt_host; + dev->class = bt_class; + + dev_set_drvdata(dev, hdev); + device_initialize(dev); +} + +int hci_add_sysfs(struct hci_dev *hdev) { struct device *dev = &hdev->dev; int err; BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - dev->type = &bt_host; - dev->class = bt_class; dev->parent = hdev->parent; - dev_set_name(dev, "%s", hdev->name); - dev_set_drvdata(dev, hdev); - - err = device_register(dev); + err = device_add(dev); if (err < 0) return err; @@ -581,7 +577,7 @@ int hci_register_sysfs(struct hci_dev *hdev) return 0; } -void hci_unregister_sysfs(struct hci_dev *hdev) +void hci_del_sysfs(struct hci_dev *hdev) { BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 075a3e920caf..3c2d888925d7 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -81,24 +81,20 @@ static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr) { struct hidp_session *session; - struct list_head *p; BT_DBG(""); - list_for_each(p, &hidp_session_list) { - session = list_entry(p, struct hidp_session, list); + list_for_each_entry(session, &hidp_session_list, list) { if (!bacmp(bdaddr, &session->bdaddr)) return session; } + return NULL; } static void __hidp_link_session(struct hidp_session *session) { - __module_get(THIS_MODULE); list_add(&session->list, &hidp_session_list); - - hci_conn_hold_device(session->conn); } static void __hidp_unlink_session(struct hidp_session *session) @@ -106,7 +102,6 @@ static void __hidp_unlink_session(struct hidp_session *session) hci_conn_put_device(session->conn); list_del(&session->list); - module_put(THIS_MODULE); } static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci) @@ -255,6 +250,9 @@ static int __hidp_send_ctrl_message(struct hidp_session *session, BT_DBG("session %p data %p size %d", session, data, size); + if (atomic_read(&session->terminate)) + return -EIO; + skb = alloc_skb(size + 1, GFP_ATOMIC); if (!skb) { BT_ERR("Can't allocate memory for new frame"); @@ -329,6 +327,7 @@ static int hidp_get_raw_report(struct hid_device *hid, struct sk_buff *skb; size_t len; int numbered_reports = hid->report_enum[report_type].numbered; + int ret; switch (report_type) { case HID_FEATURE_REPORT: @@ -352,8 +351,9 @@ static int hidp_get_raw_report(struct hid_device *hid, session->waiting_report_number = numbered_reports ? report_number : -1; set_bit(HIDP_WAITING_FOR_RETURN, &session->flags); data[0] = report_number; - if (hidp_send_ctrl_message(hid->driver_data, report_type, data, 1)) - goto err_eio; + ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, 1); + if (ret) + goto err; /* Wait for the return of the report. The returned report gets put in session->report_return. */ @@ -365,11 +365,13 @@ static int hidp_get_raw_report(struct hid_device *hid, 5*HZ); if (res == 0) { /* timeout */ - goto err_eio; + ret = -EIO; + goto err; } if (res < 0) { /* signal */ - goto err_restartsys; + ret = -ERESTARTSYS; + goto err; } } @@ -390,14 +392,10 @@ static int hidp_get_raw_report(struct hid_device *hid, return len; -err_restartsys: - clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); - mutex_unlock(&session->report_mutex); - return -ERESTARTSYS; -err_eio: +err: clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); mutex_unlock(&session->report_mutex); - return -EIO; + return ret; } static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, @@ -422,11 +420,10 @@ static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, s /* Set up our wait, and send the report request to the device. */ set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); - if (hidp_send_ctrl_message(hid->driver_data, report_type, - data, count)) { - ret = -ENOMEM; + ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, + count); + if (ret) goto err; - } /* Wait for the ACK from the device. */ while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { @@ -496,10 +493,9 @@ static void hidp_process_handshake(struct hidp_session *session, case HIDP_HSHK_ERR_INVALID_REPORT_ID: case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: case HIDP_HSHK_ERR_INVALID_PARAMETER: - if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) { - clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); + if (test_and_clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) wake_up_interruptible(&session->report_queue); - } + /* FIXME: Call into SET_ GET_ handlers here */ break; @@ -520,10 +516,8 @@ static void hidp_process_handshake(struct hidp_session *session, } /* Wake up the waiting thread. */ - if (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { - clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); + if (test_and_clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) wake_up_interruptible(&session->report_queue); - } } static void hidp_process_hid_control(struct hidp_session *session, @@ -663,25 +657,32 @@ static int hidp_send_frame(struct socket *sock, unsigned char *data, int len) return kernel_sendmsg(sock, &msg, &iv, 1, len); } -static void hidp_process_transmit(struct hidp_session *session) +static void hidp_process_intr_transmit(struct hidp_session *session) { struct sk_buff *skb; BT_DBG("session %p", session); - while ((skb = skb_dequeue(&session->ctrl_transmit))) { - if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) { - skb_queue_head(&session->ctrl_transmit, skb); + while ((skb = skb_dequeue(&session->intr_transmit))) { + if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) { + skb_queue_head(&session->intr_transmit, skb); break; } hidp_set_timer(session); kfree_skb(skb); } +} - while ((skb = skb_dequeue(&session->intr_transmit))) { - if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) { - skb_queue_head(&session->intr_transmit, skb); +static void hidp_process_ctrl_transmit(struct hidp_session *session) +{ + struct sk_buff *skb; + + BT_DBG("session %p", session); + + while ((skb = skb_dequeue(&session->ctrl_transmit))) { + if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) { + skb_queue_head(&session->ctrl_transmit, skb); break; } @@ -700,6 +701,7 @@ static int hidp_session(void *arg) BT_DBG("session %p", session); + __module_get(THIS_MODULE); set_user_nice(current, -15); init_waitqueue_entry(&ctrl_wait, current); @@ -714,23 +716,25 @@ static int hidp_session(void *arg) intr_sk->sk_state != BT_CONNECTED) break; - while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) { + while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) { skb_orphan(skb); if (!skb_linearize(skb)) - hidp_recv_ctrl_frame(session, skb); + hidp_recv_intr_frame(session, skb); else kfree_skb(skb); } - while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) { + hidp_process_intr_transmit(session); + + while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) { skb_orphan(skb); if (!skb_linearize(skb)) - hidp_recv_intr_frame(session, skb); + hidp_recv_ctrl_frame(session, skb); else kfree_skb(skb); } - hidp_process_transmit(session); + hidp_process_ctrl_transmit(session); schedule(); set_current_state(TASK_INTERRUPTIBLE); @@ -739,6 +743,10 @@ static int hidp_session(void *arg) remove_wait_queue(sk_sleep(intr_sk), &intr_wait); remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); + clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); + clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); + wake_up_interruptible(&session->report_queue); + down_write(&hidp_session_sem); hidp_del_timer(session); @@ -772,34 +780,37 @@ static int hidp_session(void *arg) kfree(session->rd_data); kfree(session); + module_put_and_exit(0); return 0; } -static struct device *hidp_get_device(struct hidp_session *session) +static struct hci_conn *hidp_get_connection(struct hidp_session *session) { bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src; bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst; - struct device *device = NULL; + struct hci_conn *conn; struct hci_dev *hdev; hdev = hci_get_route(dst, src); if (!hdev) return NULL; - session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); - if (session->conn) - device = &session->conn->dev; + hci_dev_lock_bh(hdev); + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); + if (conn) + hci_conn_hold_device(conn); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); - return device; + return conn; } static int hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req) { struct input_dev *input; - int err, i; + int i; input = input_allocate_device(); if (!input) @@ -842,17 +853,10 @@ static int hidp_setup_input(struct hidp_session *session, input->relbit[0] |= BIT_MASK(REL_WHEEL); } - input->dev.parent = hidp_get_device(session); + input->dev.parent = &session->conn->dev; input->event = hidp_input_event; - err = input_register_device(input); - if (err < 0) { - input_free_device(input); - session->input = NULL; - return err; - } - return 0; } @@ -949,7 +953,7 @@ static int hidp_setup_hid(struct hidp_session *session, strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64); strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64); - hid->dev.parent = hidp_get_device(session); + hid->dev.parent = &session->conn->dev; hid->ll_driver = &hidp_hid_driver; hid->hid_get_raw_report = hidp_get_raw_report; @@ -976,18 +980,20 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst)) return -ENOTUNIQ; - session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL); - if (!session) - return -ENOMEM; - BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); down_write(&hidp_session_sem); s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); if (s && s->state == BT_CONNECTED) { - err = -EEXIST; - goto failed; + up_write(&hidp_session_sem); + return -EEXIST; + } + + session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL); + if (!session) { + up_write(&hidp_session_sem); + return -ENOMEM; } bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst); @@ -1003,6 +1009,12 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, session->intr_sock = intr_sock; session->state = BT_CONNECTED; + session->conn = hidp_get_connection(session); + if (!session->conn) { + err = -ENOTCONN; + goto failed; + } + setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session); skb_queue_head_init(&session->ctrl_transmit); @@ -1015,9 +1027,11 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); session->idle_to = req->idle_to; + __hidp_link_session(session); + if (req->rd_size > 0) { err = hidp_setup_hid(session, req); - if (err && err != -ENODEV) + if (err) goto purge; } @@ -1027,8 +1041,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, goto purge; } - __hidp_link_session(session); - hidp_set_timer(session); if (session->hid) { @@ -1054,7 +1066,11 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, !session->waiting_for_startup); } - err = hid_add_device(session->hid); + if (session->hid) + err = hid_add_device(session->hid); + else + err = input_register_device(session->input); + if (err < 0) { atomic_inc(&session->terminate); wake_up_process(session->task); @@ -1077,8 +1093,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, unlink: hidp_del_timer(session); - __hidp_unlink_session(session); - if (session->input) { input_unregister_device(session->input); session->input = NULL; @@ -1093,6 +1107,8 @@ unlink: session->rd_data = NULL; purge: + __hidp_unlink_session(session); + skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->intr_transmit); @@ -1134,19 +1150,16 @@ int hidp_del_connection(struct hidp_conndel_req *req) int hidp_get_connlist(struct hidp_connlist_req *req) { - struct list_head *p; + struct hidp_session *session; int err = 0, n = 0; BT_DBG(""); down_read(&hidp_session_sem); - list_for_each(p, &hidp_session_list) { - struct hidp_session *session; + list_for_each_entry(session, &hidp_session_list, list) { struct hidp_conninfo ci; - session = list_entry(p, struct hidp_session, list); - __hidp_copy_session(session, &ci); if (copy_to_user(req->ci, &ci, sizeof(ci))) { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 5ea94a1eecf2..014fdec17113 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -59,7 +59,7 @@ int disable_ertm; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; -static u8 l2cap_fixed_chan[8] = { 0x02, }; +static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, }; static LIST_HEAD(chan_list); static DEFINE_RWLOCK(chan_list_lock); @@ -96,7 +96,6 @@ static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 return c; } return NULL; - } static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid) @@ -153,12 +152,9 @@ static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src) list_for_each_entry(c, &chan_list, global_l) { if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src)) - goto found; + return c; } - - c = NULL; -found: - return c; + return NULL; } int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm) @@ -219,7 +215,7 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, long timeout) { - BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout); + BT_DBG("chan %p state %d timeout %ld", chan, chan->state, timeout); if (!mod_timer(timer, jiffies + msecs_to_jiffies(timeout))) chan_hold(chan); @@ -233,8 +229,37 @@ static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer) chan_put(chan); } +static char *state_to_string(int state) +{ + switch(state) { + case BT_CONNECTED: + return "BT_CONNECTED"; + case BT_OPEN: + return "BT_OPEN"; + case BT_BOUND: + return "BT_BOUND"; + case BT_LISTEN: + return "BT_LISTEN"; + case BT_CONNECT: + return "BT_CONNECT"; + case BT_CONNECT2: + return "BT_CONNECT2"; + case BT_CONFIG: + return "BT_CONFIG"; + case BT_DISCONN: + return "BT_DISCONN"; + case BT_CLOSED: + return "BT_CLOSED"; + } + + return "invalid state"; +} + static void l2cap_state_change(struct l2cap_chan *chan, int state) { + BT_DBG("%p %s -> %s", chan, state_to_string(chan->state), + state_to_string(state)); + chan->state = state; chan->ops->state_change(chan->data, state); } @@ -293,6 +318,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk) atomic_set(&chan->refcnt, 1); + BT_DBG("sk %p chan %p", sk, chan); + return chan; } @@ -310,7 +337,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, chan->psm, chan->dcid); - conn->disc_reason = 0x13; + conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; chan->conn = conn; @@ -337,6 +364,13 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) chan->omtu = L2CAP_DEFAULT_MTU; } + chan->local_id = L2CAP_BESTEFFORT_ID; + chan->local_stype = L2CAP_SERV_BESTEFFORT; + chan->local_msdu = L2CAP_DEFAULT_MAX_SDU_SIZE; + chan->local_sdu_itime = L2CAP_DEFAULT_SDU_ITIME; + chan->local_acc_lat = L2CAP_DEFAULT_ACC_LAT; + chan->local_flush_to = L2CAP_DEFAULT_FLUSH_TO; + chan_hold(chan); list_add(&chan->list, &conn->chan_l); @@ -508,7 +542,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) } /* Service level security */ -static inline int l2cap_check_security(struct l2cap_chan *chan) +int l2cap_chan_check_security(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; __u8 auth_type; @@ -556,34 +590,58 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, flags = ACL_START; bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON; + skb->priority = HCI_PRIO_MAX; + + hci_send_acl(conn->hchan, skb, flags); +} + +static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) +{ + struct hci_conn *hcon = chan->conn->hcon; + u16 flags; + + BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len, + skb->priority); - hci_send_acl(conn->hcon, skb, flags); + if (!test_bit(FLAG_FLUSHABLE, &chan->flags) && + lmp_no_flush_capable(hcon->hdev)) + flags = ACL_START_NO_FLUSH; + else + flags = ACL_START; + + bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); + hci_send_acl(chan->conn->hchan, skb, flags); } -static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) +static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control) { struct sk_buff *skb; struct l2cap_hdr *lh; struct l2cap_conn *conn = chan->conn; - int count, hlen = L2CAP_HDR_SIZE + 2; - u8 flags; + int count, hlen; if (chan->state != BT_CONNECTED) return; + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + hlen = L2CAP_EXT_HDR_SIZE; + else + hlen = L2CAP_ENH_HDR_SIZE; + if (chan->fcs == L2CAP_FCS_CRC16) - hlen += 2; + hlen += L2CAP_FCS_SIZE; - BT_DBG("chan %p, control 0x%2.2x", chan, control); + BT_DBG("chan %p, control 0x%8.8x", chan, control); count = min_t(unsigned int, conn->mtu, hlen); - control |= L2CAP_CTRL_FRAME_TYPE; + + control |= __set_sframe(chan); if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) - control |= L2CAP_CTRL_FINAL; + control |= __set_ctrl_final(chan); if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state)) - control |= L2CAP_CTRL_POLL; + control |= __set_ctrl_poll(chan); skb = bt_skb_alloc(count, GFP_ATOMIC); if (!skb) @@ -592,32 +650,27 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); - put_unaligned_le16(control, skb_put(skb, 2)); + + __put_control(chan, control, skb_put(skb, __ctrl_size(chan))); if (chan->fcs == L2CAP_FCS_CRC16) { - u16 fcs = crc16(0, (u8 *)lh, count - 2); - put_unaligned_le16(fcs, skb_put(skb, 2)); + u16 fcs = crc16(0, (u8 *)lh, count - L2CAP_FCS_SIZE); + put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE)); } - if (lmp_no_flush_capable(conn->hcon->hdev)) - flags = ACL_START_NO_FLUSH; - else - flags = ACL_START; - - bt_cb(skb)->force_active = chan->force_active; - - hci_send_acl(chan->conn->hcon, skb, flags); + skb->priority = HCI_PRIO_MAX; + l2cap_do_send(chan, skb); } -static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control) +static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u32 control) { if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - control |= L2CAP_SUPER_RCV_NOT_READY; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); set_bit(CONN_RNR_SENT, &chan->conn_state); } else - control |= L2CAP_SUPER_RCV_READY; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); - control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= __set_reqseq(chan, chan->buffer_seq); l2cap_send_sframe(chan, control); } @@ -635,7 +688,7 @@ static void l2cap_do_start(struct l2cap_chan *chan) if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)) return; - if (l2cap_check_security(chan) && + if (l2cap_chan_check_security(chan) && __l2cap_no_conn_pending(chan)) { struct l2cap_conn_req req; req.scid = cpu_to_le16(chan->scid); @@ -725,7 +778,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) if (chan->state == BT_CONNECT) { struct l2cap_conn_req req; - if (!l2cap_check_security(chan) || + if (!l2cap_chan_check_security(chan) || !__l2cap_no_conn_pending(chan)) { bh_unlock_sock(sk); continue; @@ -758,7 +811,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn) rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); - if (l2cap_check_security(chan)) { + if (l2cap_chan_check_security(chan)) { if (bt_sk(sk)->defer_setup) { struct sock *parent = bt_sk(sk)->parent; rsp.result = cpu_to_le16(L2CAP_CR_PEND); @@ -947,7 +1000,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) list_for_each_entry(chan, &conn->chan_l, list) { struct sock *sk = chan->sk; - if (chan->force_reliable) + if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) sk->sk_err = err; } @@ -986,6 +1039,8 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) chan->ops->close(chan->data); } + hci_chan_del(conn->hchan); + if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) del_timer_sync(&conn->info_timer); @@ -1008,18 +1063,26 @@ static void security_timeout(unsigned long arg) static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) { struct l2cap_conn *conn = hcon->l2cap_data; + struct hci_chan *hchan; if (conn || status) return conn; + hchan = hci_chan_create(hcon); + if (!hchan) + return NULL; + conn = kzalloc(sizeof(struct l2cap_conn), GFP_ATOMIC); - if (!conn) + if (!conn) { + hci_chan_del(hchan); return NULL; + } hcon->l2cap_data = conn; conn->hcon = hcon; + conn->hchan = hchan; - BT_DBG("hcon %p conn %p", hcon, conn); + BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan); if (hcon->hdev->le_mtu && hcon->type == LE_LINK) conn->mtu = hcon->hdev->le_mtu; @@ -1043,7 +1106,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) setup_timer(&conn->info_timer, l2cap_info_timeout, (unsigned long) conn); - conn->disc_reason = 0x13; + conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; return conn; } @@ -1142,7 +1205,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan) if (hcon->state == BT_CONNECTED) { if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { __clear_chan_timer(chan); - if (l2cap_check_security(chan)) + if (l2cap_chan_check_security(chan)) l2cap_state_change(chan, BT_CONNECTED); } else l2cap_do_start(chan); @@ -1245,60 +1308,46 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan) __clear_retrans_timer(chan); } -static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) -{ - struct hci_conn *hcon = chan->conn->hcon; - u16 flags; - - BT_DBG("chan %p, skb %p len %d", chan, skb, skb->len); - - if (!chan->flushable && lmp_no_flush_capable(hcon->hdev)) - flags = ACL_START_NO_FLUSH; - else - flags = ACL_START; - - bt_cb(skb)->force_active = chan->force_active; - hci_send_acl(hcon, skb, flags); -} - static void l2cap_streaming_send(struct l2cap_chan *chan) { struct sk_buff *skb; - u16 control, fcs; + u32 control; + u16 fcs; while ((skb = skb_dequeue(&chan->tx_q))) { - control = get_unaligned_le16(skb->data + L2CAP_HDR_SIZE); - control |= chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; - put_unaligned_le16(control, skb->data + L2CAP_HDR_SIZE); + control = __get_control(chan, skb->data + L2CAP_HDR_SIZE); + control |= __set_txseq(chan, chan->next_tx_seq); + __put_control(chan, control, skb->data + L2CAP_HDR_SIZE); if (chan->fcs == L2CAP_FCS_CRC16) { - fcs = crc16(0, (u8 *)skb->data, skb->len - 2); - put_unaligned_le16(fcs, skb->data + skb->len - 2); + fcs = crc16(0, (u8 *)skb->data, + skb->len - L2CAP_FCS_SIZE); + put_unaligned_le16(fcs, + skb->data + skb->len - L2CAP_FCS_SIZE); } l2cap_do_send(chan, skb); - chan->next_tx_seq = (chan->next_tx_seq + 1) % 64; + chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq); } } -static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) +static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq) { struct sk_buff *skb, *tx_skb; - u16 control, fcs; + u16 fcs; + u32 control; skb = skb_peek(&chan->tx_q); if (!skb) return; - do { - if (bt_cb(skb)->tx_seq == tx_seq) - break; - + while (bt_cb(skb)->tx_seq != tx_seq) { if (skb_queue_is_last(&chan->tx_q, skb)) return; - } while ((skb = skb_queue_next(&chan->tx_q, skb))); + skb = skb_queue_next(&chan->tx_q, skb); + } if (chan->remote_max_tx && bt_cb(skb)->retries == chan->remote_max_tx) { @@ -1308,20 +1357,23 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) tx_skb = skb_clone(skb, GFP_ATOMIC); bt_cb(skb)->retries++; - control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); - control &= L2CAP_CTRL_SAR; + + control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE); + control &= __get_sar_mask(chan); if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) - control |= L2CAP_CTRL_FINAL; + control |= __set_ctrl_final(chan); - control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) - | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); + control |= __set_reqseq(chan, chan->buffer_seq); + control |= __set_txseq(chan, tx_seq); - put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE); if (chan->fcs == L2CAP_FCS_CRC16) { - fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2); - put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2); + fcs = crc16(0, (u8 *)tx_skb->data, + tx_skb->len - L2CAP_FCS_SIZE); + put_unaligned_le16(fcs, + tx_skb->data + tx_skb->len - L2CAP_FCS_SIZE); } l2cap_do_send(chan, tx_skb); @@ -1330,7 +1382,8 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) static int l2cap_ertm_send(struct l2cap_chan *chan) { struct sk_buff *skb, *tx_skb; - u16 control, fcs; + u16 fcs; + u32 control; int nsent = 0; if (chan->state != BT_CONNECTED) @@ -1348,20 +1401,22 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) bt_cb(skb)->retries++; - control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); - control &= L2CAP_CTRL_SAR; + control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE); + control &= __get_sar_mask(chan); if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) - control |= L2CAP_CTRL_FINAL; + control |= __set_ctrl_final(chan); - control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) - | (chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); - put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + control |= __set_reqseq(chan, chan->buffer_seq); + control |= __set_txseq(chan, chan->next_tx_seq); + __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE); if (chan->fcs == L2CAP_FCS_CRC16) { - fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2); - put_unaligned_le16(fcs, skb->data + tx_skb->len - 2); + fcs = crc16(0, (u8 *)skb->data, + tx_skb->len - L2CAP_FCS_SIZE); + put_unaligned_le16(fcs, skb->data + + tx_skb->len - L2CAP_FCS_SIZE); } l2cap_do_send(chan, tx_skb); @@ -1369,7 +1424,8 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) __set_retrans_timer(chan); bt_cb(skb)->tx_seq = chan->next_tx_seq; - chan->next_tx_seq = (chan->next_tx_seq + 1) % 64; + + chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq); if (bt_cb(skb)->retries == 1) chan->unacked_frames++; @@ -1401,12 +1457,12 @@ static int l2cap_retransmit_frames(struct l2cap_chan *chan) static void l2cap_send_ack(struct l2cap_chan *chan) { - u16 control = 0; + u32 control = 0; - control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= __set_reqseq(chan, chan->buffer_seq); if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - control |= L2CAP_SUPER_RCV_NOT_READY; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); set_bit(CONN_RNR_SENT, &chan->conn_state); l2cap_send_sframe(chan, control); return; @@ -1415,20 +1471,20 @@ static void l2cap_send_ack(struct l2cap_chan *chan) if (l2cap_ertm_send(chan) > 0) return; - control |= L2CAP_SUPER_RCV_READY; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); l2cap_send_sframe(chan, control); } static void l2cap_send_srejtail(struct l2cap_chan *chan) { struct srej_list *tail; - u16 control; + u32 control; - control = L2CAP_SUPER_SELECT_REJECT; - control |= L2CAP_CTRL_FINAL; + control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); + control |= __set_ctrl_final(chan); tail = list_entry((&chan->srej_l)->prev, struct srej_list, list); - control |= tail->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= __set_reqseq(chan, tail->tx_seq); l2cap_send_sframe(chan, control); } @@ -1456,6 +1512,8 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) return -EFAULT; + (*frag)->priority = skb->priority; + sent += count; len -= count; @@ -1465,15 +1523,17 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in return sent; } -static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, + struct msghdr *msg, size_t len, + u32 priority) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; - int err, count, hlen = L2CAP_HDR_SIZE + 2; + int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE; struct l2cap_hdr *lh; - BT_DBG("sk %p len %d", sk, (int)len); + BT_DBG("sk %p len %d priority %u", sk, (int)len, priority); count = min_t(unsigned int, (conn->mtu - hlen), len); skb = bt_skb_send_alloc(sk, count + hlen, @@ -1481,6 +1541,8 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct if (!skb) return ERR_PTR(err); + skb->priority = priority; + /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); @@ -1495,7 +1557,9 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct return skb; } -static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, + struct msghdr *msg, size_t len, + u32 priority) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; @@ -1511,6 +1575,8 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct ms if (!skb) return ERR_PTR(err); + skb->priority = priority; + /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); @@ -1526,12 +1592,12 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct ms static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, - u16 control, u16 sdulen) + u32 control, u16 sdulen) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; - int err, count, hlen = L2CAP_HDR_SIZE + 2; + int err, count, hlen; struct l2cap_hdr *lh; BT_DBG("sk %p len %d", sk, (int)len); @@ -1539,11 +1605,16 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, if (!conn) return ERR_PTR(-ENOTCONN); + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + hlen = L2CAP_EXT_HDR_SIZE; + else + hlen = L2CAP_ENH_HDR_SIZE; + if (sdulen) - hlen += 2; + hlen += L2CAP_SDULEN_SIZE; if (chan->fcs == L2CAP_FCS_CRC16) - hlen += 2; + hlen += L2CAP_FCS_SIZE; count = min_t(unsigned int, (conn->mtu - hlen), len); skb = bt_skb_send_alloc(sk, count + hlen, @@ -1555,9 +1626,11 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); - put_unaligned_le16(control, skb_put(skb, 2)); + + __put_control(chan, control, skb_put(skb, __ctrl_size(chan))); + if (sdulen) - put_unaligned_le16(sdulen, skb_put(skb, 2)); + put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE)); err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); if (unlikely(err < 0)) { @@ -1566,7 +1639,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, } if (chan->fcs == L2CAP_FCS_CRC16) - put_unaligned_le16(0, skb_put(skb, 2)); + put_unaligned_le16(0, skb_put(skb, L2CAP_FCS_SIZE)); bt_cb(skb)->retries = 0; return skb; @@ -1576,11 +1649,11 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si { struct sk_buff *skb; struct sk_buff_head sar_queue; - u16 control; + u32 control; size_t size = 0; skb_queue_head_init(&sar_queue); - control = L2CAP_SDU_START; + control = __set_ctrl_sar(chan, L2CAP_SAR_START); skb = l2cap_create_iframe_pdu(chan, msg, chan->remote_mps, control, len); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -1593,10 +1666,10 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si size_t buflen; if (len > chan->remote_mps) { - control = L2CAP_SDU_CONTINUE; + control = __set_ctrl_sar(chan, L2CAP_SAR_CONTINUE); buflen = chan->remote_mps; } else { - control = L2CAP_SDU_END; + control = __set_ctrl_sar(chan, L2CAP_SAR_END); buflen = len; } @@ -1617,15 +1690,16 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si return size; } -int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, + u32 priority) { struct sk_buff *skb; - u16 control; + u32 control; int err; /* Connectionless channel */ if (chan->chan_type == L2CAP_CHAN_CONN_LESS) { - skb = l2cap_create_connless_pdu(chan, msg, len); + skb = l2cap_create_connless_pdu(chan, msg, len, priority); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -1640,7 +1714,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) return -EMSGSIZE; /* Create a basic PDU */ - skb = l2cap_create_basic_pdu(chan, msg, len); + skb = l2cap_create_basic_pdu(chan, msg, len, priority); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -1652,7 +1726,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) case L2CAP_MODE_STREAMING: /* Entire SDU fits into one PDU */ if (len <= chan->remote_mps) { - control = L2CAP_SDU_UNSEGMENTED; + control = __set_ctrl_sar(chan, L2CAP_SAR_UNSEGMENTED); skb = l2cap_create_iframe_pdu(chan, msg, len, control, 0); if (IS_ERR(skb)) @@ -1850,6 +1924,37 @@ static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val) *ptr += L2CAP_CONF_OPT_SIZE + len; } +static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan) +{ + struct l2cap_conf_efs efs; + + switch (chan->mode) { + case L2CAP_MODE_ERTM: + efs.id = chan->local_id; + efs.stype = chan->local_stype; + efs.msdu = cpu_to_le16(chan->local_msdu); + efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime); + efs.acc_lat = cpu_to_le32(L2CAP_DEFAULT_ACC_LAT); + efs.flush_to = cpu_to_le32(L2CAP_DEFAULT_FLUSH_TO); + break; + + case L2CAP_MODE_STREAMING: + efs.id = 1; + efs.stype = L2CAP_SERV_BESTEFFORT; + efs.msdu = cpu_to_le16(chan->local_msdu); + efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime); + efs.acc_lat = 0; + efs.flush_to = 0; + break; + + default: + return; + } + + l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs), + (unsigned long) &efs); +} + static void l2cap_ack_timeout(unsigned long arg) { struct l2cap_chan *chan = (void *) arg; @@ -1896,11 +2001,36 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) } } +static inline bool __l2cap_ews_supported(struct l2cap_chan *chan) +{ + return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW; +} + +static inline bool __l2cap_efs_supported(struct l2cap_chan *chan) +{ + return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW; +} + +static inline void l2cap_txwin_setup(struct l2cap_chan *chan) +{ + if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && + __l2cap_ews_supported(chan)) { + /* use extended control field */ + set_bit(FLAG_EXT_CTRL, &chan->flags); + chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; + } else { + chan->tx_win = min_t(u16, chan->tx_win, + L2CAP_DEFAULT_TX_WINDOW); + chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW; + } +} + static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) { struct l2cap_conf_req *req = data; struct l2cap_conf_rfc rfc = { .mode = chan->mode }; void *ptr = req->data; + u16 size; BT_DBG("chan %p", chan); @@ -1913,6 +2043,9 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) break; + if (__l2cap_efs_supported(chan)) + set_bit(FLAG_EFS_ENABLE, &chan->flags); + /* fall through */ default: chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask); @@ -1942,17 +2075,27 @@ done: case L2CAP_MODE_ERTM: rfc.mode = L2CAP_MODE_ERTM; - rfc.txwin_size = chan->tx_win; rfc.max_transmit = chan->max_tx; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; - rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); - if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10) - rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); + + size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - + L2CAP_EXT_HDR_SIZE - + L2CAP_SDULEN_SIZE - + L2CAP_FCS_SIZE); + rfc.max_pdu_size = cpu_to_le16(size); + + l2cap_txwin_setup(chan); + + rfc.txwin_size = min_t(u16, chan->tx_win, + L2CAP_DEFAULT_TX_WINDOW); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); + if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) + l2cap_add_opt_efs(&ptr, chan); + if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) break; @@ -1961,6 +2104,10 @@ done: chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } + + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, + chan->tx_win); break; case L2CAP_MODE_STREAMING: @@ -1969,13 +2116,19 @@ done: rfc.max_transmit = 0; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; - rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); - if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10) - rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); + + size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - + L2CAP_EXT_HDR_SIZE - + L2CAP_SDULEN_SIZE - + L2CAP_FCS_SIZE); + rfc.max_pdu_size = cpu_to_le16(size); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); + if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) + l2cap_add_opt_efs(&ptr, chan); + if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) break; @@ -2002,8 +2155,11 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) int type, hint, olen; unsigned long val; struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; + struct l2cap_conf_efs efs; + u8 remote_efs = 0; u16 mtu = L2CAP_DEFAULT_MTU; u16 result = L2CAP_CONF_SUCCESS; + u16 size; BT_DBG("chan %p", chan); @@ -2033,7 +2189,22 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) case L2CAP_CONF_FCS: if (val == L2CAP_FCS_NONE) set_bit(CONF_NO_FCS_RECV, &chan->conf_state); + break; + case L2CAP_CONF_EFS: + remote_efs = 1; + if (olen == sizeof(efs)) + memcpy(&efs, (void *) val, olen); + break; + + case L2CAP_CONF_EWS: + if (!enable_hs) + return -ECONNREFUSED; + + set_bit(FLAG_EXT_CTRL, &chan->flags); + set_bit(CONF_EWS_RECV, &chan->conf_state); + chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; + chan->remote_tx_win = val; break; default: @@ -2058,6 +2229,13 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) break; } + if (remote_efs) { + if (__l2cap_efs_supported(chan)) + set_bit(FLAG_EFS_ENABLE, &chan->flags); + else + return -ECONNREFUSED; + } + if (chan->mode != rfc.mode) return -ECONNREFUSED; @@ -2076,7 +2254,6 @@ done: sizeof(rfc), (unsigned long) &rfc); } - if (result == L2CAP_CONF_SUCCESS) { /* Configure output options and let the other side know * which ones we don't like. */ @@ -2089,6 +2266,26 @@ done: } l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu); + if (remote_efs) { + if (chan->local_stype != L2CAP_SERV_NOTRAFIC && + efs.stype != L2CAP_SERV_NOTRAFIC && + efs.stype != chan->local_stype) { + + result = L2CAP_CONF_UNACCEPT; + + if (chan->num_conf_req >= 1) + return -ECONNREFUSED; + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, + sizeof(efs), + (unsigned long) &efs); + } else { + /* Send PENDING Conf Rsp */ + result = L2CAP_CONF_PENDING; + set_bit(CONF_LOC_CONF_PEND, &chan->conf_state); + } + } + switch (rfc.mode) { case L2CAP_MODE_BASIC: chan->fcs = L2CAP_FCS_NONE; @@ -2096,13 +2293,20 @@ done: break; case L2CAP_MODE_ERTM: - chan->remote_tx_win = rfc.txwin_size; - chan->remote_max_tx = rfc.max_transmit; + if (!test_bit(CONF_EWS_RECV, &chan->conf_state)) + chan->remote_tx_win = rfc.txwin_size; + else + rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW; - if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10) - rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); + chan->remote_max_tx = rfc.max_transmit; - chan->remote_mps = le16_to_cpu(rfc.max_pdu_size); + size = min_t(u16, le16_to_cpu(rfc.max_pdu_size), + chan->conn->mtu - + L2CAP_EXT_HDR_SIZE - + L2CAP_SDULEN_SIZE - + L2CAP_FCS_SIZE); + rfc.max_pdu_size = cpu_to_le16(size); + chan->remote_mps = size; rfc.retrans_timeout = le16_to_cpu(L2CAP_DEFAULT_RETRANS_TO); @@ -2114,13 +2318,29 @@ done: l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); + if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) { + chan->remote_id = efs.id; + chan->remote_stype = efs.stype; + chan->remote_msdu = le16_to_cpu(efs.msdu); + chan->remote_flush_to = + le32_to_cpu(efs.flush_to); + chan->remote_acc_lat = + le32_to_cpu(efs.acc_lat); + chan->remote_sdu_itime = + le32_to_cpu(efs.sdu_itime); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, + sizeof(efs), (unsigned long) &efs); + } break; case L2CAP_MODE_STREAMING: - if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10) - rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); - - chan->remote_mps = le16_to_cpu(rfc.max_pdu_size); + size = min_t(u16, le16_to_cpu(rfc.max_pdu_size), + chan->conn->mtu - + L2CAP_EXT_HDR_SIZE - + L2CAP_SDULEN_SIZE - + L2CAP_FCS_SIZE); + rfc.max_pdu_size = cpu_to_le16(size); + chan->remote_mps = size; set_bit(CONF_MODE_DONE, &chan->conf_state); @@ -2153,6 +2373,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi int type, olen; unsigned long val; struct l2cap_conf_rfc rfc; + struct l2cap_conf_efs efs; BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data); @@ -2188,6 +2409,26 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); break; + + case L2CAP_CONF_EWS: + chan->tx_win = min_t(u16, val, + L2CAP_DEFAULT_EXT_WINDOW); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, + chan->tx_win); + break; + + case L2CAP_CONF_EFS: + if (olen == sizeof(efs)) + memcpy(&efs, (void *)val, olen); + + if (chan->local_stype != L2CAP_SERV_NOTRAFIC && + efs.stype != L2CAP_SERV_NOTRAFIC && + efs.stype != chan->local_stype) + return -ECONNREFUSED; + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, + sizeof(efs), (unsigned long) &efs); + break; } } @@ -2196,13 +2437,23 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi chan->mode = rfc.mode; - if (*result == L2CAP_CONF_SUCCESS) { + if (*result == L2CAP_CONF_SUCCESS || *result == L2CAP_CONF_PENDING) { switch (rfc.mode) { case L2CAP_MODE_ERTM: chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); chan->mps = le16_to_cpu(rfc.max_pdu_size); + + if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) { + chan->local_msdu = le16_to_cpu(efs.msdu); + chan->local_sdu_itime = + le32_to_cpu(efs.sdu_itime); + chan->local_acc_lat = le32_to_cpu(efs.acc_lat); + chan->local_flush_to = + le32_to_cpu(efs.flush_to); + } break; + case L2CAP_MODE_STREAMING: chan->mps = le16_to_cpu(rfc.max_pdu_size); } @@ -2330,7 +2581,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd /* Check if the ACL is secure enough (if not SDP) */ if (psm != cpu_to_le16(0x0001) && !hci_conn_check_link_mode(conn->hcon)) { - conn->disc_reason = 0x05; + conn->disc_reason = HCI_ERROR_AUTH_FAILURE; result = L2CAP_CR_SEC_BLOCK; goto response; } @@ -2377,7 +2628,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd chan->ident = cmd->ident; if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) { - if (l2cap_check_security(chan)) { + if (l2cap_chan_check_security(chan)) { if (bt_sk(sk)->defer_setup) { l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; @@ -2602,6 +2853,21 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr chan->num_conf_req++; } + /* Got Conf Rsp PENDING from remote side and asume we sent + Conf Rsp PENDING in the code above */ + if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) && + test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { + + /* check compatibility */ + + clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); + set_bit(CONF_OUTPUT_DONE, &chan->conf_state); + + l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, + l2cap_build_conf_rsp(chan, rsp, + L2CAP_CONF_SUCCESS, 0x0000), rsp); + } + unlock: bh_unlock_sock(sk); return 0; @@ -2631,8 +2897,33 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr switch (result) { case L2CAP_CONF_SUCCESS: l2cap_conf_rfc_get(chan, rsp->data, len); + clear_bit(CONF_REM_CONF_PEND, &chan->conf_state); break; + case L2CAP_CONF_PENDING: + set_bit(CONF_REM_CONF_PEND, &chan->conf_state); + + if (test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { + char buf[64]; + + len = l2cap_parse_conf_rsp(chan, rsp->data, len, + buf, &result); + if (len < 0) { + l2cap_send_disconn_req(conn, chan, ECONNRESET); + goto done; + } + + /* check compatibility */ + + clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); + set_bit(CONF_OUTPUT_DONE, &chan->conf_state); + + l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, + l2cap_build_conf_rsp(chan, buf, + L2CAP_CONF_SUCCESS, 0x0000), buf); + } + goto done; + case L2CAP_CONF_UNACCEPT: if (chan->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) { char req[64]; @@ -2750,7 +3041,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd /* don't delete l2cap channel if sk is owned by user */ if (sock_owned_by_user(sk)) { - l2cap_state_change(chan,BT_DISCONN); + l2cap_state_change(chan, BT_DISCONN); __clear_chan_timer(chan); __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); bh_unlock_sock(sk); @@ -2782,15 +3073,25 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm if (!disable_ertm) feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING | L2CAP_FEAT_FCS; + if (enable_hs) + feat_mask |= L2CAP_FEAT_EXT_FLOW + | L2CAP_FEAT_EXT_WINDOW; + put_unaligned_le32(feat_mask, rsp->data); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); } else if (type == L2CAP_IT_FIXED_CHAN) { u8 buf[12]; struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; + + if (enable_hs) + l2cap_fixed_chan[0] |= L2CAP_FC_A2MP; + else + l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP; + rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); - memcpy(buf + 4, l2cap_fixed_chan, 8); + memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan)); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); } else { @@ -2857,6 +3158,165 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm return 0; } +static inline int l2cap_create_channel_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + void *data) +{ + struct l2cap_create_chan_req *req = data; + struct l2cap_create_chan_rsp rsp; + u16 psm, scid; + + if (cmd_len != sizeof(*req)) + return -EPROTO; + + if (!enable_hs) + return -EINVAL; + + psm = le16_to_cpu(req->psm); + scid = le16_to_cpu(req->scid); + + BT_DBG("psm %d, scid %d, amp_id %d", psm, scid, req->amp_id); + + /* Placeholder: Always reject */ + rsp.dcid = 0; + rsp.scid = cpu_to_le16(scid); + rsp.result = L2CAP_CR_NO_MEM; + rsp.status = L2CAP_CS_NO_INFO; + + l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP, + sizeof(rsp), &rsp); + + return 0; +} + +static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, void *data) +{ + BT_DBG("conn %p", conn); + + return l2cap_connect_rsp(conn, cmd, data); +} + +static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, + u16 icid, u16 result) +{ + struct l2cap_move_chan_rsp rsp; + + BT_DBG("icid %d, result %d", icid, result); + + rsp.icid = cpu_to_le16(icid); + rsp.result = cpu_to_le16(result); + + l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp); +} + +static void l2cap_send_move_chan_cfm(struct l2cap_conn *conn, + struct l2cap_chan *chan, u16 icid, u16 result) +{ + struct l2cap_move_chan_cfm cfm; + u8 ident; + + BT_DBG("icid %d, result %d", icid, result); + + ident = l2cap_get_ident(conn); + if (chan) + chan->ident = ident; + + cfm.icid = cpu_to_le16(icid); + cfm.result = cpu_to_le16(result); + + l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM, sizeof(cfm), &cfm); +} + +static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident, + u16 icid) +{ + struct l2cap_move_chan_cfm_rsp rsp; + + BT_DBG("icid %d", icid); + + rsp.icid = cpu_to_le16(icid); + l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp); +} + +static inline int l2cap_move_channel_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) +{ + struct l2cap_move_chan_req *req = data; + u16 icid = 0; + u16 result = L2CAP_MR_NOT_ALLOWED; + + if (cmd_len != sizeof(*req)) + return -EPROTO; + + icid = le16_to_cpu(req->icid); + + BT_DBG("icid %d, dest_amp_id %d", icid, req->dest_amp_id); + + if (!enable_hs) + return -EINVAL; + + /* Placeholder: Always refuse */ + l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result); + + return 0; +} + +static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) +{ + struct l2cap_move_chan_rsp *rsp = data; + u16 icid, result; + + if (cmd_len != sizeof(*rsp)) + return -EPROTO; + + icid = le16_to_cpu(rsp->icid); + result = le16_to_cpu(rsp->result); + + BT_DBG("icid %d, result %d", icid, result); + + /* Placeholder: Always unconfirmed */ + l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED); + + return 0; +} + +static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) +{ + struct l2cap_move_chan_cfm *cfm = data; + u16 icid, result; + + if (cmd_len != sizeof(*cfm)) + return -EPROTO; + + icid = le16_to_cpu(cfm->icid); + result = le16_to_cpu(cfm->result); + + BT_DBG("icid %d, result %d", icid, result); + + l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid); + + return 0; +} + +static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) +{ + struct l2cap_move_chan_cfm_rsp *rsp = data; + u16 icid; + + if (cmd_len != sizeof(*rsp)) + return -EPROTO; + + icid = le16_to_cpu(rsp->icid); + + BT_DBG("icid %d", icid); + + return 0; +} + static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency, u16 to_multiplier) { @@ -2969,6 +3429,30 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, err = l2cap_information_rsp(conn, cmd, data); break; + case L2CAP_CREATE_CHAN_REQ: + err = l2cap_create_channel_req(conn, cmd, cmd_len, data); + break; + + case L2CAP_CREATE_CHAN_RSP: + err = l2cap_create_channel_rsp(conn, cmd, data); + break; + + case L2CAP_MOVE_CHAN_REQ: + err = l2cap_move_channel_req(conn, cmd, cmd_len, data); + break; + + case L2CAP_MOVE_CHAN_RSP: + err = l2cap_move_channel_rsp(conn, cmd, cmd_len, data); + break; + + case L2CAP_MOVE_CHAN_CFM: + err = l2cap_move_channel_confirm(conn, cmd, cmd_len, data); + break; + + case L2CAP_MOVE_CHAN_CFM_RSP: + err = l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data); + break; + default: BT_ERR("Unknown BR/EDR signaling command 0x%2.2x", cmd->code); err = -EINVAL; @@ -3047,10 +3531,15 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb) { u16 our_fcs, rcv_fcs; - int hdr_size = L2CAP_HDR_SIZE + 2; + int hdr_size; + + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + hdr_size = L2CAP_EXT_HDR_SIZE; + else + hdr_size = L2CAP_ENH_HDR_SIZE; if (chan->fcs == L2CAP_FCS_CRC16) { - skb_trim(skb, skb->len - 2); + skb_trim(skb, skb->len - L2CAP_FCS_SIZE); rcv_fcs = get_unaligned_le16(skb->data + skb->len); our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size); @@ -3062,14 +3551,14 @@ static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb) static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) { - u16 control = 0; + u32 control = 0; chan->frames_sent = 0; - control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= __set_reqseq(chan, chan->buffer_seq); if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - control |= L2CAP_SUPER_RCV_NOT_READY; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); l2cap_send_sframe(chan, control); set_bit(CONN_RNR_SENT, &chan->conn_state); } @@ -3081,12 +3570,12 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) && chan->frames_sent == 0) { - control |= L2CAP_SUPER_RCV_READY; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); l2cap_send_sframe(chan, control); } } -static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u8 tx_seq, u8 sar) +static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u16 tx_seq, u8 sar) { struct sk_buff *next_skb; int tx_seq_offset, next_tx_seq_offset; @@ -3095,23 +3584,15 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, bt_cb(skb)->sar = sar; next_skb = skb_peek(&chan->srej_q); - if (!next_skb) { - __skb_queue_tail(&chan->srej_q, skb); - return 0; - } - tx_seq_offset = (tx_seq - chan->buffer_seq) % 64; - if (tx_seq_offset < 0) - tx_seq_offset += 64; + tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq); - do { + while (next_skb) { if (bt_cb(next_skb)->tx_seq == tx_seq) return -EINVAL; - next_tx_seq_offset = (bt_cb(next_skb)->tx_seq - - chan->buffer_seq) % 64; - if (next_tx_seq_offset < 0) - next_tx_seq_offset += 64; + next_tx_seq_offset = __seq_offset(chan, + bt_cb(next_skb)->tx_seq, chan->buffer_seq); if (next_tx_seq_offset > tx_seq_offset) { __skb_queue_before(&chan->srej_q, next_skb, skb); @@ -3119,9 +3600,10 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, } if (skb_queue_is_last(&chan->srej_q, next_skb)) - break; - - } while ((next_skb = skb_queue_next(&chan->srej_q, next_skb))); + next_skb = NULL; + else + next_skb = skb_queue_next(&chan->srej_q, next_skb); + } __skb_queue_tail(&chan->srej_q, skb); @@ -3147,24 +3629,24 @@ static void append_skb_frag(struct sk_buff *skb, skb->truesize += new_frag->truesize; } -static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) +static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u32 control) { int err = -EINVAL; - switch (control & L2CAP_CTRL_SAR) { - case L2CAP_SDU_UNSEGMENTED: + switch (__get_ctrl_sar(chan, control)) { + case L2CAP_SAR_UNSEGMENTED: if (chan->sdu) break; err = chan->ops->recv(chan->data, skb); break; - case L2CAP_SDU_START: + case L2CAP_SAR_START: if (chan->sdu) break; chan->sdu_len = get_unaligned_le16(skb->data); - skb_pull(skb, 2); + skb_pull(skb, L2CAP_SDULEN_SIZE); if (chan->sdu_len > chan->imtu) { err = -EMSGSIZE; @@ -3181,7 +3663,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1 err = 0; break; - case L2CAP_SDU_CONTINUE: + case L2CAP_SAR_CONTINUE: if (!chan->sdu) break; @@ -3195,7 +3677,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1 err = 0; break; - case L2CAP_SDU_END: + case L2CAP_SAR_END: if (!chan->sdu) break; @@ -3230,14 +3712,14 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1 static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) { - u16 control; + u32 control; BT_DBG("chan %p, Enter local busy", chan); set_bit(CONN_LOCAL_BUSY, &chan->conn_state); - control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - control |= L2CAP_SUPER_RCV_NOT_READY; + control = __set_reqseq(chan, chan->buffer_seq); + control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); l2cap_send_sframe(chan, control); set_bit(CONN_RNR_SENT, &chan->conn_state); @@ -3247,13 +3729,14 @@ static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan) { - u16 control; + u32 control; if (!test_bit(CONN_RNR_SENT, &chan->conn_state)) goto done; - control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - control |= L2CAP_SUPER_RCV_READY | L2CAP_CTRL_POLL; + control = __set_reqseq(chan, chan->buffer_seq); + control |= __set_ctrl_poll(chan); + control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); l2cap_send_sframe(chan, control); chan->retry_count = 1; @@ -3279,10 +3762,10 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy) } } -static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) +static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq) { struct sk_buff *skb; - u16 control; + u32 control; while ((skb = skb_peek(&chan->srej_q)) && !test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { @@ -3292,7 +3775,7 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) break; skb = skb_dequeue(&chan->srej_q); - control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; + control = __set_ctrl_sar(chan, bt_cb(skb)->sar); err = l2cap_reassemble_sdu(chan, skb, control); if (err < 0) { @@ -3300,16 +3783,15 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) break; } - chan->buffer_seq_srej = - (chan->buffer_seq_srej + 1) % 64; - tx_seq = (tx_seq + 1) % 64; + chan->buffer_seq_srej = __next_seq(chan, chan->buffer_seq_srej); + tx_seq = __next_seq(chan, tx_seq); } } -static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq) +static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq) { struct srej_list *l, *tmp; - u16 control; + u32 control; list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { if (l->tx_seq == tx_seq) { @@ -3317,45 +3799,53 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq) kfree(l); return; } - control = L2CAP_SUPER_SELECT_REJECT; - control |= l->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); + control |= __set_reqseq(chan, l->tx_seq); l2cap_send_sframe(chan, control); list_del(&l->list); list_add_tail(&l->list, &chan->srej_l); } } -static void l2cap_send_srejframe(struct l2cap_chan *chan, u8 tx_seq) +static int l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq) { struct srej_list *new; - u16 control; + u32 control; while (tx_seq != chan->expected_tx_seq) { - control = L2CAP_SUPER_SELECT_REJECT; - control |= chan->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); + control |= __set_reqseq(chan, chan->expected_tx_seq); l2cap_send_sframe(chan, control); new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); + if (!new) + return -ENOMEM; + new->tx_seq = chan->expected_tx_seq; - chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; + + chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); + list_add_tail(&new->list, &chan->srej_l); } - chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; + + chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); + + return 0; } -static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb) +static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb) { - u8 tx_seq = __get_txseq(rx_control); - u8 req_seq = __get_reqseq(rx_control); - u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT; + u16 tx_seq = __get_txseq(chan, rx_control); + u16 req_seq = __get_reqseq(chan, rx_control); + u8 sar = __get_ctrl_sar(chan, rx_control); int tx_seq_offset, expected_tx_seq_offset; int num_to_ack = (chan->tx_win/6) + 1; int err = 0; - BT_DBG("chan %p len %d tx_seq %d rx_control 0x%4.4x", chan, skb->len, + BT_DBG("chan %p len %d tx_seq %d rx_control 0x%8.8x", chan, skb->len, tx_seq, rx_control); - if (L2CAP_CTRL_FINAL & rx_control && + if (__is_ctrl_final(chan, rx_control) && test_bit(CONN_WAIT_F, &chan->conn_state)) { __clear_monitor_timer(chan); if (chan->unacked_frames > 0) @@ -3366,9 +3856,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont chan->expected_ack_seq = req_seq; l2cap_drop_acked_frames(chan); - tx_seq_offset = (tx_seq - chan->buffer_seq) % 64; - if (tx_seq_offset < 0) - tx_seq_offset += 64; + tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq); /* invalid tx_seq */ if (tx_seq_offset >= chan->tx_win) { @@ -3413,13 +3901,16 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont return 0; } } - l2cap_send_srejframe(chan, tx_seq); + + err = l2cap_send_srejframe(chan, tx_seq); + if (err < 0) { + l2cap_send_disconn_req(chan->conn, chan, -err); + return err; + } } } else { - expected_tx_seq_offset = - (chan->expected_tx_seq - chan->buffer_seq) % 64; - if (expected_tx_seq_offset < 0) - expected_tx_seq_offset += 64; + expected_tx_seq_offset = __seq_offset(chan, + chan->expected_tx_seq, chan->buffer_seq); /* duplicated tx_seq */ if (tx_seq_offset < expected_tx_seq_offset) @@ -3437,14 +3928,18 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont set_bit(CONN_SEND_PBIT, &chan->conn_state); - l2cap_send_srejframe(chan, tx_seq); + err = l2cap_send_srejframe(chan, tx_seq); + if (err < 0) { + l2cap_send_disconn_req(chan->conn, chan, -err); + return err; + } __clear_ack_timer(chan); } return 0; expected: - chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; + chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { bt_cb(skb)->tx_seq = tx_seq; @@ -3454,22 +3949,24 @@ expected: } err = l2cap_reassemble_sdu(chan, skb, rx_control); - chan->buffer_seq = (chan->buffer_seq + 1) % 64; + chan->buffer_seq = __next_seq(chan, chan->buffer_seq); + if (err < 0) { l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); return err; } - if (rx_control & L2CAP_CTRL_FINAL) { + if (__is_ctrl_final(chan, rx_control)) { if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) l2cap_retransmit_frames(chan); } - __set_ack_timer(chan); chan->num_acked = (chan->num_acked + 1) % num_to_ack; if (chan->num_acked == num_to_ack - 1) l2cap_send_ack(chan); + else + __set_ack_timer(chan); return 0; @@ -3478,15 +3975,15 @@ drop: return 0; } -static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_control) +static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u32 rx_control) { - BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, __get_reqseq(rx_control), - rx_control); + BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, + __get_reqseq(chan, rx_control), rx_control); - chan->expected_ack_seq = __get_reqseq(rx_control); + chan->expected_ack_seq = __get_reqseq(chan, rx_control); l2cap_drop_acked_frames(chan); - if (rx_control & L2CAP_CTRL_POLL) { + if (__is_ctrl_poll(chan, rx_control)) { set_bit(CONN_SEND_FBIT, &chan->conn_state); if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && @@ -3499,7 +3996,7 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co l2cap_send_i_or_rr_or_rnr(chan); } - } else if (rx_control & L2CAP_CTRL_FINAL) { + } else if (__is_ctrl_final(chan, rx_control)) { clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) @@ -3518,18 +4015,18 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co } } -static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_control) +static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u32 rx_control) { - u8 tx_seq = __get_reqseq(rx_control); + u16 tx_seq = __get_reqseq(chan, rx_control); - BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); + BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); chan->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(chan); - if (rx_control & L2CAP_CTRL_FINAL) { + if (__is_ctrl_final(chan, rx_control)) { if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) l2cap_retransmit_frames(chan); } else { @@ -3539,15 +4036,15 @@ static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_c set_bit(CONN_REJ_ACT, &chan->conn_state); } } -static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_control) +static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u32 rx_control) { - u8 tx_seq = __get_reqseq(rx_control); + u16 tx_seq = __get_reqseq(chan, rx_control); - BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); + BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - if (rx_control & L2CAP_CTRL_POLL) { + if (__is_ctrl_poll(chan, rx_control)) { chan->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(chan); @@ -3560,7 +4057,7 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_ chan->srej_save_reqseq = tx_seq; set_bit(CONN_SREJ_ACT, &chan->conn_state); } - } else if (rx_control & L2CAP_CTRL_FINAL) { + } else if (__is_ctrl_final(chan, rx_control)) { if (test_bit(CONN_SREJ_ACT, &chan->conn_state) && chan->srej_save_reqseq == tx_seq) clear_bit(CONN_SREJ_ACT, &chan->conn_state); @@ -3575,37 +4072,39 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_ } } -static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_control) +static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u32 rx_control) { - u8 tx_seq = __get_reqseq(rx_control); + u16 tx_seq = __get_reqseq(chan, rx_control); - BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); + BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); set_bit(CONN_REMOTE_BUSY, &chan->conn_state); chan->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(chan); - if (rx_control & L2CAP_CTRL_POLL) + if (__is_ctrl_poll(chan, rx_control)) set_bit(CONN_SEND_FBIT, &chan->conn_state); if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) { __clear_retrans_timer(chan); - if (rx_control & L2CAP_CTRL_POLL) + if (__is_ctrl_poll(chan, rx_control)) l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL); return; } - if (rx_control & L2CAP_CTRL_POLL) + if (__is_ctrl_poll(chan, rx_control)) { l2cap_send_srejtail(chan); - else - l2cap_send_sframe(chan, L2CAP_SUPER_RCV_READY); + } else { + rx_control = __set_ctrl_super(chan, L2CAP_SUPER_RR); + l2cap_send_sframe(chan, rx_control); + } } -static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb) +static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb) { - BT_DBG("chan %p rx_control 0x%4.4x len %d", chan, rx_control, skb->len); + BT_DBG("chan %p rx_control 0x%8.8x len %d", chan, rx_control, skb->len); - if (L2CAP_CTRL_FINAL & rx_control && + if (__is_ctrl_final(chan, rx_control) && test_bit(CONN_WAIT_F, &chan->conn_state)) { __clear_monitor_timer(chan); if (chan->unacked_frames > 0) @@ -3613,20 +4112,20 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont clear_bit(CONN_WAIT_F, &chan->conn_state); } - switch (rx_control & L2CAP_CTRL_SUPERVISE) { - case L2CAP_SUPER_RCV_READY: + switch (__get_ctrl_super(chan, rx_control)) { + case L2CAP_SUPER_RR: l2cap_data_channel_rrframe(chan, rx_control); break; - case L2CAP_SUPER_REJECT: + case L2CAP_SUPER_REJ: l2cap_data_channel_rejframe(chan, rx_control); break; - case L2CAP_SUPER_SELECT_REJECT: + case L2CAP_SUPER_SREJ: l2cap_data_channel_srejframe(chan, rx_control); break; - case L2CAP_SUPER_RCV_NOT_READY: + case L2CAP_SUPER_RNR: l2cap_data_channel_rnrframe(chan, rx_control); break; } @@ -3638,12 +4137,12 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) { struct l2cap_chan *chan = l2cap_pi(sk)->chan; - u16 control; - u8 req_seq; + u32 control; + u16 req_seq; int len, next_tx_seq_offset, req_seq_offset; - control = get_unaligned_le16(skb->data); - skb_pull(skb, 2); + control = __get_control(chan, skb->data); + skb_pull(skb, __ctrl_size(chan)); len = skb->len; /* @@ -3654,26 +4153,23 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) if (l2cap_check_fcs(chan, skb)) goto drop; - if (__is_sar_start(control) && __is_iframe(control)) - len -= 2; + if (__is_sar_start(chan, control) && !__is_sframe(chan, control)) + len -= L2CAP_SDULEN_SIZE; if (chan->fcs == L2CAP_FCS_CRC16) - len -= 2; + len -= L2CAP_FCS_SIZE; if (len > chan->mps) { l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto drop; } - req_seq = __get_reqseq(control); - req_seq_offset = (req_seq - chan->expected_ack_seq) % 64; - if (req_seq_offset < 0) - req_seq_offset += 64; + req_seq = __get_reqseq(chan, control); - next_tx_seq_offset = - (chan->next_tx_seq - chan->expected_ack_seq) % 64; - if (next_tx_seq_offset < 0) - next_tx_seq_offset += 64; + req_seq_offset = __seq_offset(chan, req_seq, chan->expected_ack_seq); + + next_tx_seq_offset = __seq_offset(chan, chan->next_tx_seq, + chan->expected_ack_seq); /* check for invalid req-seq */ if (req_seq_offset > next_tx_seq_offset) { @@ -3681,7 +4177,7 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) goto drop; } - if (__is_iframe(control)) { + if (!__is_sframe(chan, control)) { if (len < 0) { l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto drop; @@ -3709,8 +4205,8 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk { struct l2cap_chan *chan; struct sock *sk = NULL; - u16 control; - u8 tx_seq; + u32 control; + u16 tx_seq; int len; chan = l2cap_get_chan_by_scid(conn, cid); @@ -3751,23 +4247,23 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk goto done; case L2CAP_MODE_STREAMING: - control = get_unaligned_le16(skb->data); - skb_pull(skb, 2); + control = __get_control(chan, skb->data); + skb_pull(skb, __ctrl_size(chan)); len = skb->len; if (l2cap_check_fcs(chan, skb)) goto drop; - if (__is_sar_start(control)) - len -= 2; + if (__is_sar_start(chan, control)) + len -= L2CAP_SDULEN_SIZE; if (chan->fcs == L2CAP_FCS_CRC16) - len -= 2; + len -= L2CAP_FCS_SIZE; - if (len > chan->mps || len < 0 || __is_sframe(control)) + if (len > chan->mps || len < 0 || __is_sframe(chan, control)) goto drop; - tx_seq = __get_txseq(control); + tx_seq = __get_txseq(chan, control); if (chan->expected_tx_seq != tx_seq) { /* Frame(s) missing - must discard partial SDU */ @@ -3779,7 +4275,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk /* TODO: Notify userland of missing data */ } - chan->expected_tx_seq = (tx_seq + 1) % 64; + chan->expected_tx_seq = __next_seq(chan, tx_seq); if (l2cap_reassemble_sdu(chan, skb, control) == -EMSGSIZE) l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); @@ -3933,12 +4429,12 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { lm1 |= HCI_LM_ACCEPT; - if (c->role_switch) + if (test_bit(FLAG_ROLE_SWITCH, &c->flags)) lm1 |= HCI_LM_MASTER; exact++; } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { lm2 |= HCI_LM_ACCEPT; - if (c->role_switch) + if (test_bit(FLAG_ROLE_SWITCH, &c->flags)) lm2 |= HCI_LM_MASTER; } } @@ -3973,7 +4469,7 @@ static int l2cap_disconn_ind(struct hci_conn *hcon) BT_DBG("hcon %p", hcon); if ((hcon->type != ACL_LINK && hcon->type != LE_LINK) || !conn) - return 0x13; + return HCI_ERROR_REMOTE_USER_TERM; return conn->disc_reason; } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index e8292369cdcf..b85e3906b3a8 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -333,7 +333,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us opts.mode = chan->mode; opts.fcs = chan->fcs; opts.max_tx = chan->max_tx; - opts.txwin_size = (__u16)chan->tx_win; + opts.txwin_size = chan->tx_win; len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *) &opts, len)) @@ -358,10 +358,10 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us break; } - if (chan->role_switch) + if (test_bit(FLAG_ROLE_SWITCH, &chan->flags)) opt |= L2CAP_LM_MASTER; - if (chan->force_reliable) + if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) opt |= L2CAP_LM_RELIABLE; if (put_user(opt, (u32 __user *) optval)) @@ -448,7 +448,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch break; case BT_FLUSHABLE: - if (put_user(chan->flushable, (u32 __user *) optval)) + if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags), + (u32 __user *) optval)) err = -EFAULT; break; @@ -460,7 +461,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch break; } - pwr.force_active = chan->force_active; + pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); len = min_t(unsigned int, len, sizeof(pwr)); if (copy_to_user(optval, (char *) &pwr, len)) @@ -468,6 +469,16 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch break; + case BT_CHANNEL_POLICY: + if (!enable_hs) { + err = -ENOPROTOOPT; + break; + } + + if (put_user(chan->chan_policy, (u32 __user *) optval)) + err = -EFAULT; + break; + default: err = -ENOPROTOOPT; break; @@ -502,7 +513,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us opts.mode = chan->mode; opts.fcs = chan->fcs; opts.max_tx = chan->max_tx; - opts.txwin_size = (__u16)chan->tx_win; + opts.txwin_size = chan->tx_win; len = min_t(unsigned int, sizeof(opts), optlen); if (copy_from_user((char *) &opts, optval, len)) { @@ -510,7 +521,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us break; } - if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) { + if (opts.txwin_size > L2CAP_DEFAULT_EXT_WINDOW) { err = -EINVAL; break; } @@ -534,7 +545,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us chan->omtu = opts.omtu; chan->fcs = opts.fcs; chan->max_tx = opts.max_tx; - chan->tx_win = (__u8)opts.txwin_size; + chan->tx_win = opts.txwin_size; break; case L2CAP_LM: @@ -550,8 +561,15 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us if (opt & L2CAP_LM_SECURE) chan->sec_level = BT_SECURITY_HIGH; - chan->role_switch = (opt & L2CAP_LM_MASTER); - chan->force_reliable = (opt & L2CAP_LM_RELIABLE); + if (opt & L2CAP_LM_MASTER) + set_bit(FLAG_ROLE_SWITCH, &chan->flags); + else + clear_bit(FLAG_ROLE_SWITCH, &chan->flags); + + if (opt & L2CAP_LM_RELIABLE) + set_bit(FLAG_FORCE_RELIABLE, &chan->flags); + else + clear_bit(FLAG_FORCE_RELIABLE, &chan->flags); break; default: @@ -607,8 +625,13 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch chan->sec_level = sec.level; + if (!chan->conn) + break; + conn = chan->conn; - if (conn && chan->scid == L2CAP_CID_LE_DATA) { + + /*change security for LE channels */ + if (chan->scid == L2CAP_CID_LE_DATA) { if (!conn->hcon->out) { err = -EINVAL; break; @@ -616,9 +639,14 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch if (smp_conn_security(conn, sec.level)) break; - - err = 0; sk->sk_state = BT_CONFIG; + + /* or for ACL link, under defer_setup time */ + } else if (sk->sk_state == BT_CONNECT2 && + bt_sk(sk)->defer_setup) { + err = l2cap_chan_check_security(chan); + } else { + err = -EINVAL; } break; @@ -657,7 +685,10 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch } } - chan->flushable = opt; + if (opt) + set_bit(FLAG_FLUSHABLE, &chan->flags); + else + clear_bit(FLAG_FLUSHABLE, &chan->flags); break; case BT_POWER: @@ -674,7 +705,36 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch err = -EFAULT; break; } - chan->force_active = pwr.force_active; + + if (pwr.force_active) + set_bit(FLAG_FORCE_ACTIVE, &chan->flags); + else + clear_bit(FLAG_FORCE_ACTIVE, &chan->flags); + break; + + case BT_CHANNEL_POLICY: + if (!enable_hs) { + err = -ENOPROTOOPT; + break; + } + + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + if (opt > BT_CHANNEL_POLICY_AMP_PREFERRED) { + err = -EINVAL; + break; + } + + if (chan->mode != L2CAP_MODE_ERTM && + chan->mode != L2CAP_MODE_STREAMING) { + err = -EOPNOTSUPP; + break; + } + + chan->chan_policy = (u8) opt; break; default: @@ -708,7 +768,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms return -ENOTCONN; } - err = l2cap_chan_send(chan, msg, len); + err = l2cap_chan_send(chan, msg, len, sk->sk_priority); release_sock(sk); return err; @@ -930,11 +990,9 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->fcs = pchan->fcs; chan->max_tx = pchan->max_tx; chan->tx_win = pchan->tx_win; + chan->tx_win_max = pchan->tx_win_max; chan->sec_level = pchan->sec_level; - chan->role_switch = pchan->role_switch; - chan->force_reliable = pchan->force_reliable; - chan->flushable = pchan->flushable; - chan->force_active = pchan->force_active; + chan->flags = pchan->flags; security_sk_clone(parent, sk); } else { @@ -963,12 +1021,10 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->max_tx = L2CAP_DEFAULT_MAX_TX; chan->fcs = L2CAP_FCS_CRC16; chan->tx_win = L2CAP_DEFAULT_TX_WINDOW; + chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW; chan->sec_level = BT_SECURITY_LOW; - chan->role_switch = 0; - chan->force_reliable = 0; - chan->flushable = BT_FLUSHABLE_OFF; - chan->force_active = BT_POWER_FORCE_ACTIVE_ON; - + chan->flags = 0; + set_bit(FLAG_FORCE_ACTIVE, &chan->flags); } /* Default config options */ diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5caff4d47596..7a23f211d602 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -22,6 +22,7 @@ /* Bluetooth HCI Management interface */ +#include <linux/kernel.h> #include <linux/uaccess.h> #include <asm/unaligned.h> @@ -32,22 +33,96 @@ #define MGMT_VERSION 0 #define MGMT_REVISION 1 +#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */ + struct pending_cmd { struct list_head list; - __u16 opcode; + u16 opcode; int index; void *param; struct sock *sk; void *user_data; }; -static LIST_HEAD(cmd_list); +/* HCI to MGMT error code conversion table */ +static u8 mgmt_status_table[] = { + MGMT_STATUS_SUCCESS, + MGMT_STATUS_UNKNOWN_COMMAND, /* Unknown Command */ + MGMT_STATUS_NOT_CONNECTED, /* No Connection */ + 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_NO_RESOURCES, /* Memory Full */ + MGMT_STATUS_TIMEOUT, /* Connection Timeout */ + MGMT_STATUS_NO_RESOURCES, /* Max Number of Connections */ + MGMT_STATUS_NO_RESOURCES, /* Max Number of SCO Connections */ + MGMT_STATUS_ALREADY_CONNECTED, /* ACL Connection Exists */ + MGMT_STATUS_BUSY, /* Command Disallowed */ + MGMT_STATUS_NO_RESOURCES, /* Rejected Limited Resources */ + MGMT_STATUS_REJECTED, /* Rejected Security */ + MGMT_STATUS_REJECTED, /* Rejected Personal */ + MGMT_STATUS_TIMEOUT, /* Host Timeout */ + MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Feature */ + MGMT_STATUS_INVALID_PARAMS, /* Invalid Parameters */ + MGMT_STATUS_DISCONNECTED, /* OE User Ended Connection */ + MGMT_STATUS_NO_RESOURCES, /* OE Low Resources */ + MGMT_STATUS_DISCONNECTED, /* OE Power Off */ + MGMT_STATUS_DISCONNECTED, /* Connection Terminated */ + MGMT_STATUS_BUSY, /* Repeated Attempts */ + MGMT_STATUS_REJECTED, /* Pairing Not Allowed */ + MGMT_STATUS_FAILED, /* Unknown LMP PDU */ + MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Remote Feature */ + MGMT_STATUS_REJECTED, /* SCO Offset Rejected */ + MGMT_STATUS_REJECTED, /* SCO Interval Rejected */ + MGMT_STATUS_REJECTED, /* Air Mode Rejected */ + MGMT_STATUS_INVALID_PARAMS, /* Invalid LMP Parameters */ + MGMT_STATUS_FAILED, /* Unspecified Error */ + MGMT_STATUS_NOT_SUPPORTED, /* Unsupported LMP Parameter Value */ + MGMT_STATUS_FAILED, /* Role Change Not Allowed */ + MGMT_STATUS_TIMEOUT, /* LMP Response Timeout */ + MGMT_STATUS_FAILED, /* LMP Error Transaction Collision */ + MGMT_STATUS_FAILED, /* LMP PDU Not Allowed */ + MGMT_STATUS_REJECTED, /* Encryption Mode Not Accepted */ + MGMT_STATUS_FAILED, /* Unit Link Key Used */ + MGMT_STATUS_NOT_SUPPORTED, /* QoS Not Supported */ + MGMT_STATUS_TIMEOUT, /* Instant Passed */ + MGMT_STATUS_NOT_SUPPORTED, /* Pairing Not Supported */ + MGMT_STATUS_FAILED, /* Transaction Collision */ + MGMT_STATUS_INVALID_PARAMS, /* Unacceptable Parameter */ + MGMT_STATUS_REJECTED, /* QoS Rejected */ + MGMT_STATUS_NOT_SUPPORTED, /* Classification Not Supported */ + MGMT_STATUS_REJECTED, /* Insufficient Security */ + MGMT_STATUS_INVALID_PARAMS, /* Parameter Out Of Range */ + MGMT_STATUS_BUSY, /* Role Switch Pending */ + MGMT_STATUS_FAILED, /* Slot Violation */ + MGMT_STATUS_FAILED, /* Role Switch Failed */ + MGMT_STATUS_INVALID_PARAMS, /* EIR Too Large */ + MGMT_STATUS_NOT_SUPPORTED, /* Simple Pairing Not Supported */ + MGMT_STATUS_BUSY, /* Host Busy Pairing */ + MGMT_STATUS_REJECTED, /* Rejected, No Suitable Channel */ + MGMT_STATUS_BUSY, /* Controller Busy */ + MGMT_STATUS_INVALID_PARAMS, /* Unsuitable Connection Interval */ + MGMT_STATUS_TIMEOUT, /* Directed Advertising Timeout */ + MGMT_STATUS_AUTH_FAILED, /* Terminated Due to MIC Failure */ + MGMT_STATUS_CONNECT_FAILED, /* Connection Establishment Failed */ + MGMT_STATUS_CONNECT_FAILED, /* MAC Connection Failed */ +}; + +static u8 mgmt_status(u8 hci_status) +{ + if (hci_status < ARRAY_SIZE(mgmt_status_table)) + return mgmt_status_table[hci_status]; + + return MGMT_STATUS_FAILED; +} static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) { struct sk_buff *skb; struct mgmt_hdr *hdr; struct mgmt_ev_cmd_status *ev; + int err; BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status); @@ -65,10 +140,11 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) ev->status = status; put_unaligned_le16(cmd, &ev->opcode); - if (sock_queue_rcv_skb(sk, skb) < 0) + err = sock_queue_rcv_skb(sk, skb); + if (err < 0) kfree_skb(skb); - return 0; + return err; } static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp, @@ -77,6 +153,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp, struct sk_buff *skb; struct mgmt_hdr *hdr; struct mgmt_ev_cmd_complete *ev; + int err; BT_DBG("sock %p", sk); @@ -96,10 +173,11 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp, if (rp) memcpy(ev->data, rp, rp_len); - if (sock_queue_rcv_skb(sk, skb) < 0) + err = sock_queue_rcv_skb(sk, skb); + if (err < 0) kfree_skb(skb); - return 0; + return err;; } static int read_version(struct sock *sk) @@ -119,6 +197,7 @@ static int read_index_list(struct sock *sk) { struct mgmt_rp_read_index_list *rp; struct list_head *p; + struct hci_dev *d; size_t rp_len; u16 count; int i, err; @@ -142,10 +221,9 @@ static int read_index_list(struct sock *sk) put_unaligned_le16(count, &rp->num_controllers); i = 0; - list_for_each(p, &hci_dev_list) { - struct hci_dev *d = list_entry(p, struct hci_dev, list); - - hci_del_off_timer(d); + list_for_each_entry(d, &hci_dev_list, list) { + if (test_and_clear_bit(HCI_AUTO_OFF, &d->flags)) + cancel_delayed_work(&d->power_off); if (test_bit(HCI_SETUP, &d->flags)) continue; @@ -173,9 +251,11 @@ static int read_controller_info(struct sock *sk, u16 index) hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_READ_INFO, ENODEV); + return cmd_status(sk, index, MGMT_OP_READ_INFO, + MGMT_STATUS_INVALID_PARAMS); - hci_del_off_timer(hdev); + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) + cancel_delayed_work_sync(&hdev->power_off); hci_dev_lock_bh(hdev); @@ -220,7 +300,8 @@ static void mgmt_pending_free(struct pending_cmd *cmd) } static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, - u16 index, void *data, u16 len) + struct hci_dev *hdev, + void *data, u16 len) { struct pending_cmd *cmd; @@ -229,7 +310,7 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, return NULL; cmd->opcode = opcode; - cmd->index = index; + cmd->index = hdev->id; cmd->param = kmalloc(len, GFP_ATOMIC); if (!cmd->param) { @@ -243,48 +324,36 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, cmd->sk = sk; sock_hold(sk); - list_add(&cmd->list, &cmd_list); + list_add(&cmd->list, &hdev->mgmt_pending); return cmd; } -static void mgmt_pending_foreach(u16 opcode, int index, +static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, void (*cb)(struct pending_cmd *cmd, void *data), void *data) { struct list_head *p, *n; - list_for_each_safe(p, n, &cmd_list) { + list_for_each_safe(p, n, &hdev->mgmt_pending) { struct pending_cmd *cmd; cmd = list_entry(p, struct pending_cmd, list); - if (cmd->opcode != opcode) - continue; - - if (index >= 0 && cmd->index != index) + if (opcode > 0 && cmd->opcode != opcode) continue; cb(cmd, data); } } -static struct pending_cmd *mgmt_pending_find(u16 opcode, int index) +static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev) { - struct list_head *p; - - list_for_each(p, &cmd_list) { - struct pending_cmd *cmd; - - cmd = list_entry(p, struct pending_cmd, list); - - if (cmd->opcode != opcode) - continue; - - if (index >= 0 && cmd->index != index) - continue; + struct pending_cmd *cmd; - return cmd; + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { + if (cmd->opcode == opcode) + return cmd; } return NULL; @@ -296,6 +365,15 @@ static void mgmt_pending_remove(struct pending_cmd *cmd) mgmt_pending_free(cmd); } +static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val) +{ + struct mgmt_mode rp; + + rp.val = val; + + return cmd_complete(sk, index, opcode, &rp, sizeof(rp)); +} + static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct mgmt_mode *cp; @@ -308,26 +386,29 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) BT_DBG("request for hci%u", index); if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_SET_POWERED, EINVAL); + return cmd_status(sk, index, MGMT_OP_SET_POWERED, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_SET_POWERED, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_POWERED, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); up = test_bit(HCI_UP, &hdev->flags); if ((cp->val && up) || (!cp->val && !up)) { - err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EALREADY); + err = send_mode_rsp(sk, index, MGMT_OP_SET_POWERED, cp->val); goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_POWERED, index)) { - err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EBUSY); + if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) { + err = cmd_status(sk, index, MGMT_OP_SET_POWERED, + MGMT_STATUS_BUSY); goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -336,7 +417,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) if (cp->val) queue_work(hdev->workqueue, &hdev->power_on); else - queue_work(hdev->workqueue, &hdev->power_off); + queue_work(hdev->workqueue, &hdev->power_off.work); err = 0; @@ -349,7 +430,7 @@ failed: static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, u16 len) { - struct mgmt_mode *cp; + struct mgmt_cp_set_discoverable *cp; struct hci_dev *hdev; struct pending_cmd *cmd; u8 scan; @@ -360,32 +441,37 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, BT_DBG("request for hci%u", index); if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EINVAL); + return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENETDOWN); + err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, + MGMT_STATUS_NOT_POWERED); goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) { - err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EBUSY); + if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || + mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { + err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, + MGMT_STATUS_BUSY); goto failed; } if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) && test_bit(HCI_PSCAN, &hdev->flags)) { - err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EALREADY); + err = send_mode_rsp(sk, index, MGMT_OP_SET_DISCOVERABLE, + cp->val); goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -395,11 +481,16 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, if (cp->val) scan |= SCAN_INQUIRY; + else + cancel_delayed_work(&hdev->discov_off); err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); if (err < 0) mgmt_pending_remove(cmd); + if (cp->val) + hdev->discov_timeout = get_unaligned_le16(&cp->timeout); + failed: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); @@ -421,31 +512,36 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data, BT_DBG("request for hci%u", index); if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EINVAL); + return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENETDOWN); + err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, + MGMT_STATUS_NOT_POWERED); goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) { - err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EBUSY); + if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || + mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { + err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, + MGMT_STATUS_BUSY); goto failed; } if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { - err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EALREADY); + err = send_mode_rsp(sk, index, MGMT_OP_SET_CONNECTABLE, + cp->val); goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -467,8 +563,8 @@ failed: return err; } -static int mgmt_event(u16 event, u16 index, void *data, u16 data_len, - struct sock *skip_sk) +static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, + u16 data_len, struct sock *skip_sk) { struct sk_buff *skb; struct mgmt_hdr *hdr; @@ -481,7 +577,10 @@ static int mgmt_event(u16 event, u16 index, void *data, u16 data_len, hdr = (void *) skb_put(skb, sizeof(*hdr)); hdr->opcode = cpu_to_le16(event); - hdr->index = cpu_to_le16(index); + if (hdev) + hdr->index = cpu_to_le16(hdev->id); + else + hdr->index = cpu_to_le16(MGMT_INDEX_NONE); hdr->len = cpu_to_le16(data_len); if (data) @@ -493,15 +592,6 @@ static int mgmt_event(u16 event, u16 index, void *data, u16 data_len, return 0; } -static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val) -{ - struct mgmt_mode rp; - - rp.val = val; - - return cmd_complete(sk, index, opcode, &rp, sizeof(rp)); -} - static int set_pairable(struct sock *sk, u16 index, unsigned char *data, u16 len) { @@ -514,11 +604,13 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data, BT_DBG("request for hci%u", index); if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, EINVAL); + return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); @@ -533,7 +625,7 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data, ev.val = cp->val; - err = mgmt_event(MGMT_EV_PAIRABLE, index, &ev, sizeof(ev), sk); + err = mgmt_event(MGMT_EV_PAIRABLE, hdev, &ev, sizeof(ev), sk); failed: hci_dev_unlock_bh(hdev); @@ -586,7 +678,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data) u16 eir_len = 0; u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)]; int i, truncated = 0; - struct list_head *p; + struct bt_uuid *uuid; size_t name_len; name_len = strlen(hdev->dev_name); @@ -611,8 +703,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data) memset(uuid16_list, 0, sizeof(uuid16_list)); /* Group all UUID16 types */ - list_for_each(p, &hdev->uuids) { - struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list); + list_for_each_entry(uuid, &hdev->uuids, list) { u16 uuid16; uuid16 = get_uuid16(uuid->uuid); @@ -688,14 +779,11 @@ static int update_eir(struct hci_dev *hdev) static u8 get_service_classes(struct hci_dev *hdev) { - struct list_head *p; + struct bt_uuid *uuid; u8 val = 0; - list_for_each(p, &hdev->uuids) { - struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list); - + list_for_each_entry(uuid, &hdev->uuids, list) val |= uuid->svc_hint; - } return val; } @@ -731,11 +819,13 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) BT_DBG("request for hci%u", index); if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_ADD_UUID, EINVAL); + return cmd_status(sk, index, MGMT_OP_ADD_UUID, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_ADD_UUID, ENODEV); + return cmd_status(sk, index, MGMT_OP_ADD_UUID, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); @@ -780,11 +870,13 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) BT_DBG("request for hci%u", index); if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, EINVAL); + return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENODEV); + return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); @@ -806,7 +898,8 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) } if (found == 0) { - err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENOENT); + err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID, + MGMT_STATUS_INVALID_PARAMS); goto unlock; } @@ -839,11 +932,13 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data, BT_DBG("request for hci%u", index); if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, EINVAL); + return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); @@ -871,11 +966,13 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data, cp = (void *) data; if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, EINVAL); + return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); @@ -894,6 +991,9 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data, if (err == 0) err = cmd_complete(sk, index, MGMT_OP_SET_SERVICE_CACHE, NULL, 0); + else + cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, -err); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); @@ -901,30 +1001,35 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data, return err; } -static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) +static int load_link_keys(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct hci_dev *hdev; - struct mgmt_cp_load_keys *cp; + struct mgmt_cp_load_link_keys *cp; u16 key_count, expected_len; int i; cp = (void *) data; if (len < sizeof(*cp)) - return -EINVAL; + return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, + MGMT_STATUS_INVALID_PARAMS); key_count = get_unaligned_le16(&cp->key_count); - expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info); + expected_len = sizeof(*cp) + key_count * + sizeof(struct mgmt_link_key_info); if (expected_len != len) { - BT_ERR("load_keys: expected %u bytes, got %u bytes", + BT_ERR("load_link_keys: expected %u bytes, got %u bytes", len, expected_len); - return -EINVAL; + return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, + MGMT_STATUS_INVALID_PARAMS); } hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_LOAD_KEYS, ENODEV); + return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, + MGMT_STATUS_INVALID_PARAMS); BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys, key_count); @@ -941,57 +1046,83 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) clear_bit(HCI_DEBUG_KEYS, &hdev->flags); for (i = 0; i < key_count; i++) { - struct mgmt_key_info *key = &cp->keys[i]; + struct mgmt_link_key_info *key = &cp->keys[i]; hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type, key->pin_len); } + cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return 0; } -static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len) +static int remove_keys(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct hci_dev *hdev; - struct mgmt_cp_remove_key *cp; + struct mgmt_cp_remove_keys *cp; + struct mgmt_rp_remove_keys rp; + struct hci_cp_disconnect dc; + struct pending_cmd *cmd; struct hci_conn *conn; int err; cp = (void *) data; if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, EINVAL); + return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, ENODEV); + return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.bdaddr, &cp->bdaddr); + rp.status = MGMT_STATUS_FAILED; + err = hci_remove_link_key(hdev, &cp->bdaddr); if (err < 0) { - err = cmd_status(sk, index, MGMT_OP_REMOVE_KEY, -err); + rp.status = MGMT_STATUS_NOT_PAIRED; goto unlock; } - err = 0; - - if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) + if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) { + err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp, + sizeof(rp)); goto unlock; + } conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); - if (conn) { - struct hci_cp_disconnect dc; + if (!conn) { + err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp, + sizeof(rp)); + goto unlock; + } - put_unaligned_le16(conn->handle, &dc.handle); - dc.reason = 0x13; /* Remote User Terminated Connection */ - err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); + cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_KEYS, hdev, cp, sizeof(*cp)); + if (!cmd) { + err = -ENOMEM; + goto unlock; } + put_unaligned_le16(conn->handle, &dc.handle); + dc.reason = 0x13; /* Remote User Terminated Connection */ + err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); + if (err < 0) + mgmt_pending_remove(cmd); + unlock: + if (err < 0) + err = cmd_complete(sk, index, MGMT_OP_REMOVE_KEYS, &rp, + sizeof(rp)); hci_dev_unlock_bh(hdev); hci_dev_put(hdev); @@ -1012,21 +1143,25 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) cp = (void *) data; if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_DISCONNECT, EINVAL); + return cmd_status(sk, index, MGMT_OP_DISCONNECT, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_DISCONNECT, ENODEV); + return cmd_status(sk, index, MGMT_OP_DISCONNECT, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENETDOWN); + err = cmd_status(sk, index, MGMT_OP_DISCONNECT, + MGMT_STATUS_NOT_POWERED); goto failed; } - if (mgmt_pending_find(MGMT_OP_DISCONNECT, index)) { - err = cmd_status(sk, index, MGMT_OP_DISCONNECT, EBUSY); + if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) { + err = cmd_status(sk, index, MGMT_OP_DISCONNECT, + MGMT_STATUS_BUSY); goto failed; } @@ -1035,11 +1170,12 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr); if (!conn) { - err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENOTCONN); + err = cmd_status(sk, index, MGMT_OP_DISCONNECT, + MGMT_STATUS_NOT_CONNECTED); goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -1059,10 +1195,30 @@ failed: return err; } +static u8 link_to_mgmt(u8 link_type, u8 addr_type) +{ + switch (link_type) { + case LE_LINK: + switch (addr_type) { + case ADDR_LE_DEV_PUBLIC: + return MGMT_ADDR_LE_PUBLIC; + case ADDR_LE_DEV_RANDOM: + return MGMT_ADDR_LE_RANDOM; + default: + return MGMT_ADDR_INVALID; + } + case ACL_LINK: + return MGMT_ADDR_BREDR; + default: + return MGMT_ADDR_INVALID; + } +} + static int get_connections(struct sock *sk, u16 index) { struct mgmt_rp_get_connections *rp; struct hci_dev *hdev; + struct hci_conn *c; struct list_head *p; size_t rp_len; u16 count; @@ -1072,7 +1228,8 @@ static int get_connections(struct sock *sk, u16 index) hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS, ENODEV); + return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); @@ -1081,7 +1238,7 @@ static int get_connections(struct sock *sk, u16 index) count++; } - rp_len = sizeof(*rp) + (count * sizeof(bdaddr_t)); + rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info)); rp = kmalloc(rp_len, GFP_ATOMIC); if (!rp) { err = -ENOMEM; @@ -1091,12 +1248,17 @@ static int get_connections(struct sock *sk, u16 index) put_unaligned_le16(count, &rp->conn_count); i = 0; - list_for_each(p, &hdev->conn_hash.list) { - struct hci_conn *c = list_entry(p, struct hci_conn, list); - - bacpy(&rp->conn[i++], &c->dst); + list_for_each_entry(c, &hdev->conn_hash.list, list) { + bacpy(&rp->addr[i].bdaddr, &c->dst); + rp->addr[i].type = link_to_mgmt(c->type, c->dst_type); + if (rp->addr[i].type == MGMT_ADDR_INVALID) + continue; + i++; } + /* Recalculate length in case of filtered SCO connections, etc */ + rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info)); + err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len); unlock: @@ -1112,7 +1274,7 @@ static int send_pin_code_neg_reply(struct sock *sk, u16 index, struct pending_cmd *cmd; int err; - cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, index, cp, + cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp, sizeof(*cp)); if (!cmd) return -ENOMEM; @@ -1141,22 +1303,26 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, cp = (void *) data; if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, EINVAL); + return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENODEV); + return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENETDOWN); + err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, + MGMT_STATUS_NOT_POWERED); goto failed; } conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); if (!conn) { - err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENOTCONN); + err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, + MGMT_STATUS_NOT_CONNECTED); goto failed; } @@ -1168,12 +1334,12 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, err = send_pin_code_neg_reply(sk, index, hdev, &ncp); if (err >= 0) err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, - EINVAL); + MGMT_STATUS_INVALID_PARAMS); goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -1207,18 +1373,18 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data, if (len != sizeof(*cp)) return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, - EINVAL); + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, - ENODEV); + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, - ENETDOWN); + MGMT_STATUS_NOT_POWERED); goto failed; } @@ -1242,11 +1408,13 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data, cp = (void *) data; if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, EINVAL); + return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); @@ -1264,19 +1432,12 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data, static inline struct pending_cmd *find_pairing(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; - struct list_head *p; - - list_for_each(p, &cmd_list) { - struct pending_cmd *cmd; - - cmd = list_entry(p, struct pending_cmd, list); + struct pending_cmd *cmd; + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { if (cmd->opcode != MGMT_OP_PAIR_DEVICE) continue; - if (cmd->index != hdev->id) - continue; - if (cmd->user_data != conn) continue; @@ -1291,7 +1452,8 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status) struct mgmt_rp_pair_device rp; struct hci_conn *conn = cmd->user_data; - bacpy(&rp.bdaddr, &conn->dst); + bacpy(&rp.addr.bdaddr, &conn->dst); + rp.addr.type = link_to_mgmt(conn->type, conn->dst_type); rp.status = status; cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp)); @@ -1313,20 +1475,18 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status) BT_DBG("status %u", status); cmd = find_pairing(conn); - if (!cmd) { + if (!cmd) BT_DBG("Unable to find a pending command"); - return; - } - - pairing_complete(cmd, status); + else + pairing_complete(cmd, status); } static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; struct mgmt_cp_pair_device *cp; + struct mgmt_rp_pair_device rp; struct pending_cmd *cmd; - struct adv_entry *entry; u8 sec_level, auth_type; struct hci_conn *conn; int err; @@ -1336,11 +1496,13 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) cp = (void *) data; if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, EINVAL); + return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, ENODEV); + return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); @@ -1350,26 +1512,33 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) else auth_type = HCI_AT_DEDICATED_BONDING_MITM; - entry = hci_find_adv_entry(hdev, &cp->bdaddr); - if (entry) - conn = hci_connect(hdev, LE_LINK, &cp->bdaddr, sec_level, + if (cp->addr.type == MGMT_ADDR_BREDR) + conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level, auth_type); else - conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level, + conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level, auth_type); + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; + if (IS_ERR(conn)) { - err = PTR_ERR(conn); + rp.status = -PTR_ERR(conn); + err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE, + &rp, sizeof(rp)); goto unlock; } if (conn->connect_cfm_cb) { hci_conn_put(conn); - err = cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, EBUSY); + rp.status = EBUSY; + err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE, + &rp, sizeof(rp)); goto unlock; } - cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len); if (!cmd) { err = -ENOMEM; hci_conn_put(conn); @@ -1377,7 +1546,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) } /* For LE, just connecting isn't a proof that the pairing finished */ - if (!entry) + if (cp->addr.type == MGMT_ADDR_BREDR) conn->connect_cfm_cb = pairing_complete_cb; conn->security_cfm_cb = pairing_complete_cb; @@ -1398,56 +1567,138 @@ unlock: return err; } -static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data, - u16 len, int success) +static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr, + u16 mgmt_op, u16 hci_op, __le32 passkey) { - struct mgmt_cp_user_confirm_reply *cp = (void *) data; - u16 mgmt_op, hci_op; struct pending_cmd *cmd; struct hci_dev *hdev; + struct hci_conn *conn; int err; - BT_DBG(""); - - if (success) { - mgmt_op = MGMT_OP_USER_CONFIRM_REPLY; - hci_op = HCI_OP_USER_CONFIRM_REPLY; - } else { - mgmt_op = MGMT_OP_USER_CONFIRM_NEG_REPLY; - hci_op = HCI_OP_USER_CONFIRM_NEG_REPLY; - } - - if (len != sizeof(*cp)) - return cmd_status(sk, index, mgmt_op, EINVAL); - hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, mgmt_op, ENODEV); + return cmd_status(sk, index, mgmt_op, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, index, mgmt_op, ENETDOWN); - goto failed; + err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED); + goto done; + } + + /* + * Check for an existing ACL link, if present pair via + * HCI commands. + * + * If no ACL link is present, check for an LE link and if + * present, pair via the SMP engine. + * + * If neither ACL nor LE links are present, fail with error. + */ + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr); + if (!conn) { + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr); + if (!conn) { + err = cmd_status(sk, index, mgmt_op, + MGMT_STATUS_NOT_CONNECTED); + goto done; + } + + /* Continue with pairing via SMP */ + + err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_SUCCESS); + goto done; } - cmd = mgmt_pending_add(sk, mgmt_op, index, data, len); + cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr)); if (!cmd) { err = -ENOMEM; - goto failed; + goto done; } - err = hci_send_cmd(hdev, hci_op, sizeof(cp->bdaddr), &cp->bdaddr); + /* Continue with pairing via HCI */ + if (hci_op == HCI_OP_USER_PASSKEY_REPLY) { + struct hci_cp_user_passkey_reply cp; + + bacpy(&cp.bdaddr, bdaddr); + cp.passkey = passkey; + err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp); + } else + err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr); + if (err < 0) mgmt_pending_remove(cmd); -failed: +done: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; } +static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len) +{ + struct mgmt_cp_user_confirm_reply *cp = (void *) data; + + BT_DBG(""); + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_REPLY, + MGMT_STATUS_INVALID_PARAMS); + + return user_pairing_resp(sk, index, &cp->bdaddr, + MGMT_OP_USER_CONFIRM_REPLY, + HCI_OP_USER_CONFIRM_REPLY, 0); +} + +static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data, + u16 len) +{ + struct mgmt_cp_user_confirm_reply *cp = (void *) data; + + BT_DBG(""); + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_NEG_REPLY, + MGMT_STATUS_INVALID_PARAMS); + + return user_pairing_resp(sk, index, &cp->bdaddr, + MGMT_OP_USER_CONFIRM_NEG_REPLY, + HCI_OP_USER_CONFIRM_NEG_REPLY, 0); +} + +static int user_passkey_reply(struct sock *sk, u16 index, void *data, u16 len) +{ + struct mgmt_cp_user_passkey_reply *cp = (void *) data; + + BT_DBG(""); + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_REPLY, + EINVAL); + + return user_pairing_resp(sk, index, &cp->bdaddr, + MGMT_OP_USER_PASSKEY_REPLY, + HCI_OP_USER_PASSKEY_REPLY, cp->passkey); +} + +static int user_passkey_neg_reply(struct sock *sk, u16 index, void *data, + u16 len) +{ + struct mgmt_cp_user_passkey_neg_reply *cp = (void *) data; + + BT_DBG(""); + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_NEG_REPLY, + EINVAL); + + return user_pairing_resp(sk, index, &cp->bdaddr, + MGMT_OP_USER_PASSKEY_NEG_REPLY, + HCI_OP_USER_PASSKEY_NEG_REPLY, 0); +} + static int set_local_name(struct sock *sk, u16 index, unsigned char *data, u16 len) { @@ -1460,15 +1711,17 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data, BT_DBG(""); if (len != sizeof(*mgmt_cp)) - return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, EINVAL); + return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); - cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -1498,28 +1751,29 @@ static int read_local_oob_data(struct sock *sk, u16 index) hdev = hci_dev_get(index); if (!hdev) return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, - ENODEV); + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, - ENETDOWN); + MGMT_STATUS_NOT_POWERED); goto unlock; } if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) { err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, - EOPNOTSUPP); + MGMT_STATUS_NOT_SUPPORTED); goto unlock; } - if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index)) { - err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, EBUSY); + if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) { + err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, + MGMT_STATUS_BUSY); goto unlock; } - cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, index, NULL, 0); + cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0); if (!cmd) { err = -ENOMEM; goto unlock; @@ -1547,19 +1801,20 @@ static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data, if (len != sizeof(*cp)) return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, - EINVAL); + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, - ENODEV); + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash, cp->randomizer); if (err < 0) - err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, -err); + err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, + MGMT_STATUS_FAILED); else err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL, 0); @@ -1581,19 +1836,19 @@ static int remove_remote_oob_data(struct sock *sk, u16 index, if (len != sizeof(*cp)) return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, - EINVAL); + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, - ENODEV); + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); err = hci_remove_remote_oob_data(hdev, &cp->bdaddr); if (err < 0) err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, - -err); + MGMT_STATUS_INVALID_PARAMS); else err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, NULL, 0); @@ -1604,34 +1859,40 @@ static int remove_remote_oob_data(struct sock *sk, u16 index, return err; } -static int start_discovery(struct sock *sk, u16 index) +static int start_discovery(struct sock *sk, u16 index, + unsigned char *data, u16 len) { - u8 lap[3] = { 0x33, 0x8b, 0x9e }; - struct hci_cp_inquiry cp; + struct mgmt_cp_start_discovery *cp = (void *) data; struct pending_cmd *cmd; struct hci_dev *hdev; int err; BT_DBG("hci%u", index); + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENODEV); + return cmd_status(sk, index, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); - cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, index, NULL, 0); + if (!test_bit(HCI_UP, &hdev->flags)) { + err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY, + MGMT_STATUS_NOT_POWERED); + goto failed; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0); if (!cmd) { err = -ENOMEM; goto failed; } - memset(&cp, 0, sizeof(cp)); - memcpy(&cp.lap, lap, 3); - cp.length = 0x08; - cp.num_rsp = 0x00; - - err = hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); + err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); if (err < 0) mgmt_pending_remove(cmd); @@ -1652,17 +1913,18 @@ static int stop_discovery(struct sock *sk, u16 index) hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, ENODEV); + return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY, + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); - cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, NULL, 0); + cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0); if (!cmd) { err = -ENOMEM; goto failed; } - err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL); + err = hci_cancel_inquiry(hdev); if (err < 0) mgmt_pending_remove(cmd); @@ -1677,7 +1939,6 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; - struct pending_cmd *cmd; struct mgmt_cp_block_device *cp = (void *) data; int err; @@ -1685,32 +1946,23 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, if (len != sizeof(*cp)) return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, - EINVAL); + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, - ENODEV); + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); - cmd = mgmt_pending_add(sk, MGMT_OP_BLOCK_DEVICE, index, NULL, 0); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - err = hci_blacklist_add(hdev, &cp->bdaddr); - if (err < 0) - err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -err); + err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, + MGMT_STATUS_FAILED); else err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, NULL, 0); - mgmt_pending_remove(cmd); - -failed: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); @@ -1721,7 +1973,6 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; - struct pending_cmd *cmd; struct mgmt_cp_unblock_device *cp = (void *) data; int err; @@ -1729,32 +1980,24 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, if (len != sizeof(*cp)) return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, - EINVAL); + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, - ENODEV); + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock_bh(hdev); - cmd = mgmt_pending_add(sk, MGMT_OP_UNBLOCK_DEVICE, index, NULL, 0); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - err = hci_blacklist_del(hdev, &cp->bdaddr); if (err < 0) - err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, -err); + err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, + MGMT_STATUS_INVALID_PARAMS); else err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, NULL, 0); - mgmt_pending_remove(cmd); - -failed: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); @@ -1774,12 +2017,12 @@ static int set_fast_connectable(struct sock *sk, u16 index, if (len != sizeof(*cp)) return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, - EINVAL); + MGMT_STATUS_INVALID_PARAMS); hdev = hci_dev_get(index); if (!hdev) return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, - ENODEV); + MGMT_STATUS_INVALID_PARAMS); hci_dev_lock(hdev); @@ -1797,14 +2040,14 @@ static int set_fast_connectable(struct sock *sk, u16 index, sizeof(acp), &acp); if (err < 0) { err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, - -err); + MGMT_STATUS_FAILED); goto done; } err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); if (err < 0) { err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, - -err); + MGMT_STATUS_FAILED); goto done; } @@ -1882,11 +2125,11 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_SET_SERVICE_CACHE: err = set_service_cache(sk, index, buf + sizeof(*hdr), len); break; - case MGMT_OP_LOAD_KEYS: - err = load_keys(sk, index, buf + sizeof(*hdr), len); + case MGMT_OP_LOAD_LINK_KEYS: + err = load_link_keys(sk, index, buf + sizeof(*hdr), len); break; - case MGMT_OP_REMOVE_KEY: - err = remove_key(sk, index, buf + sizeof(*hdr), len); + case MGMT_OP_REMOVE_KEYS: + err = remove_keys(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_DISCONNECT: err = disconnect(sk, index, buf + sizeof(*hdr), len); @@ -1907,10 +2150,18 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) err = pair_device(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_USER_CONFIRM_REPLY: - err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 1); + err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_USER_CONFIRM_NEG_REPLY: - err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 0); + err = user_confirm_neg_reply(sk, index, buf + sizeof(*hdr), + len); + break; + case MGMT_OP_USER_PASSKEY_REPLY: + err = user_passkey_reply(sk, index, buf + sizeof(*hdr), len); + break; + case MGMT_OP_USER_PASSKEY_NEG_REPLY: + err = user_passkey_neg_reply(sk, index, buf + sizeof(*hdr), + len); break; case MGMT_OP_SET_LOCAL_NAME: err = set_local_name(sk, index, buf + sizeof(*hdr), len); @@ -1926,7 +2177,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) len); break; case MGMT_OP_START_DISCOVERY: - err = start_discovery(sk, index); + err = start_discovery(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_STOP_DISCOVERY: err = stop_discovery(sk, index); @@ -1943,7 +2194,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) break; default: BT_DBG("Unknown op %u", opcode); - err = cmd_status(sk, index, opcode, 0x01); + err = cmd_status(sk, index, opcode, + MGMT_STATUS_UNKNOWN_COMMAND); break; } @@ -1957,14 +2209,26 @@ done: return err; } -int mgmt_index_added(u16 index) +static void cmd_status_rsp(struct pending_cmd *cmd, void *data) +{ + u8 *status = data; + + cmd_status(cmd->sk, cmd->index, cmd->opcode, *status); + mgmt_pending_remove(cmd); +} + +int mgmt_index_added(struct hci_dev *hdev) { - return mgmt_event(MGMT_EV_INDEX_ADDED, index, NULL, 0, NULL); + return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); } -int mgmt_index_removed(u16 index) +int mgmt_index_removed(struct hci_dev *hdev) { - return mgmt_event(MGMT_EV_INDEX_REMOVED, index, NULL, 0, NULL); + u8 status = ENODEV; + + mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); + + return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); } struct cmd_lookup { @@ -1992,17 +2256,22 @@ static void mode_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_free(cmd); } -int mgmt_powered(u16 index, u8 powered) +int mgmt_powered(struct hci_dev *hdev, u8 powered) { struct mgmt_mode ev; struct cmd_lookup match = { powered, NULL }; int ret; - mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match); + mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, mode_rsp, &match); + + if (!powered) { + u8 status = ENETDOWN; + mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); + } ev.val = powered; - ret = mgmt_event(MGMT_EV_POWERED, index, &ev, sizeof(ev), match.sk); + ret = mgmt_event(MGMT_EV_POWERED, hdev, &ev, sizeof(ev), match.sk); if (match.sk) sock_put(match.sk); @@ -2010,17 +2279,17 @@ int mgmt_powered(u16 index, u8 powered) return ret; } -int mgmt_discoverable(u16 index, u8 discoverable) +int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) { struct mgmt_mode ev; struct cmd_lookup match = { discoverable, NULL }; int ret; - mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, mode_rsp, &match); + mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, mode_rsp, &match); ev.val = discoverable; - ret = mgmt_event(MGMT_EV_DISCOVERABLE, index, &ev, sizeof(ev), + ret = mgmt_event(MGMT_EV_DISCOVERABLE, hdev, &ev, sizeof(ev), match.sk); if (match.sk) @@ -2029,17 +2298,17 @@ int mgmt_discoverable(u16 index, u8 discoverable) return ret; } -int mgmt_connectable(u16 index, u8 connectable) +int mgmt_connectable(struct hci_dev *hdev, u8 connectable) { struct mgmt_mode ev; struct cmd_lookup match = { connectable, NULL }; int ret; - mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, mode_rsp, &match); + mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, mode_rsp, &match); ev.val = connectable; - ret = mgmt_event(MGMT_EV_CONNECTABLE, index, &ev, sizeof(ev), match.sk); + ret = mgmt_event(MGMT_EV_CONNECTABLE, hdev, &ev, sizeof(ev), match.sk); if (match.sk) sock_put(match.sk); @@ -2047,9 +2316,25 @@ int mgmt_connectable(u16 index, u8 connectable) return ret; } -int mgmt_new_key(u16 index, struct link_key *key, u8 persistent) +int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) { - struct mgmt_ev_new_key ev; + u8 mgmt_err = mgmt_status(status); + + if (scan & SCAN_PAGE) + mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, + cmd_status_rsp, &mgmt_err); + + if (scan & SCAN_INQUIRY) + mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, + cmd_status_rsp, &mgmt_err); + + return 0; +} + +int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, + u8 persistent) +{ + struct mgmt_ev_new_link_key ev; memset(&ev, 0, sizeof(ev)); @@ -2059,17 +2344,18 @@ int mgmt_new_key(u16 index, struct link_key *key, u8 persistent) memcpy(ev.key.val, key->val, 16); ev.key.pin_len = key->pin_len; - return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); } -int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type) +int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type) { - struct mgmt_ev_connected ev; + struct mgmt_addr_info ev; bacpy(&ev.bdaddr, bdaddr); - ev.link_type = link_type; + ev.type = link_to_mgmt(link_type, addr_type); - return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_CONNECTED, hdev, &ev, sizeof(ev), NULL); } static void disconnect_rsp(struct pending_cmd *cmd, void *data) @@ -2079,6 +2365,7 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data) struct mgmt_rp_disconnect rp; bacpy(&rp.bdaddr, &cp->bdaddr); + rp.status = 0; cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp)); @@ -2088,75 +2375,110 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_remove(cmd); } -int mgmt_disconnected(u16 index, bdaddr_t *bdaddr) +static void remove_keys_rsp(struct pending_cmd *cmd, void *data) { - struct mgmt_ev_disconnected ev; + u8 *status = data; + struct mgmt_cp_remove_keys *cp = cmd->param; + struct mgmt_rp_remove_keys rp; + + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.bdaddr, &cp->bdaddr); + if (status != NULL) + rp.status = *status; + + cmd_complete(cmd->sk, cmd->index, MGMT_OP_REMOVE_KEYS, &rp, + sizeof(rp)); + + mgmt_pending_remove(cmd); +} + +int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type) +{ + struct mgmt_addr_info ev; struct sock *sk = NULL; int err; - mgmt_pending_foreach(MGMT_OP_DISCONNECT, index, disconnect_rsp, &sk); + mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk); bacpy(&ev.bdaddr, bdaddr); + ev.type = link_to_mgmt(link_type, addr_type); - err = mgmt_event(MGMT_EV_DISCONNECTED, index, &ev, sizeof(ev), sk); + err = mgmt_event(MGMT_EV_DISCONNECTED, hdev, &ev, sizeof(ev), sk); if (sk) sock_put(sk); + mgmt_pending_foreach(MGMT_OP_REMOVE_KEYS, hdev, remove_keys_rsp, NULL); + return err; } -int mgmt_disconnect_failed(u16 index) +int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status) { struct pending_cmd *cmd; + u8 mgmt_err = mgmt_status(status); int err; - cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, index); + cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev); if (!cmd) return -ENOENT; - err = cmd_status(cmd->sk, index, MGMT_OP_DISCONNECT, EIO); + if (bdaddr) { + struct mgmt_rp_disconnect rp; + + bacpy(&rp.bdaddr, bdaddr); + rp.status = status; + + err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, + &rp, sizeof(rp)); + } else + err = cmd_status(cmd->sk, hdev->id, MGMT_OP_DISCONNECT, + mgmt_err); mgmt_pending_remove(cmd); return err; } -int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 status) { struct mgmt_ev_connect_failed ev; - bacpy(&ev.bdaddr, bdaddr); - ev.status = status; + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_mgmt(link_type, addr_type); + ev.status = mgmt_status(status); - return mgmt_event(MGMT_EV_CONNECT_FAILED, index, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL); } -int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure) +int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure) { struct mgmt_ev_pin_code_request ev; bacpy(&ev.bdaddr, bdaddr); ev.secure = secure; - return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, index, &ev, sizeof(ev), + return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), NULL); } -int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status) { struct pending_cmd *cmd; struct mgmt_rp_pin_code_reply rp; int err; - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, index); + cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev); if (!cmd) return -ENOENT; bacpy(&rp.bdaddr, bdaddr); - rp.status = status; + rp.status = mgmt_status(status); - err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_REPLY, &rp, + err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -2164,20 +2486,21 @@ int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) return err; } -int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status) { struct pending_cmd *cmd; struct mgmt_rp_pin_code_reply rp; int err; - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, index); + cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev); if (!cmd) return -ENOENT; bacpy(&rp.bdaddr, bdaddr); - rp.status = status; + rp.status = mgmt_status(status); - err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, &rp, + err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -2185,97 +2508,119 @@ int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) return err; } -int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value, - u8 confirm_hint) +int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, + __le32 value, u8 confirm_hint) { struct mgmt_ev_user_confirm_request ev; - BT_DBG("hci%u", index); + BT_DBG("%s", hdev->name); bacpy(&ev.bdaddr, bdaddr); ev.confirm_hint = confirm_hint; put_unaligned_le32(value, &ev.value); - return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, index, &ev, sizeof(ev), + return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev), NULL); } -static int confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status, - u8 opcode) +int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ + struct mgmt_ev_user_passkey_request ev; + + BT_DBG("%s", hdev->name); + + bacpy(&ev.bdaddr, bdaddr); + + return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev), + NULL); +} + +static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status, u8 opcode) { struct pending_cmd *cmd; struct mgmt_rp_user_confirm_reply rp; int err; - cmd = mgmt_pending_find(opcode, index); + cmd = mgmt_pending_find(opcode, hdev); if (!cmd) return -ENOENT; bacpy(&rp.bdaddr, bdaddr); - rp.status = status; - err = cmd_complete(cmd->sk, index, opcode, &rp, sizeof(rp)); + rp.status = mgmt_status(status); + err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp)); mgmt_pending_remove(cmd); return err; } -int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status) { - return confirm_reply_complete(index, bdaddr, status, + return user_pairing_resp_complete(hdev, bdaddr, status, MGMT_OP_USER_CONFIRM_REPLY); } -int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 status) { - return confirm_reply_complete(index, bdaddr, status, + return user_pairing_resp_complete(hdev, bdaddr, status, MGMT_OP_USER_CONFIRM_NEG_REPLY); } -int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status) +{ + return user_pairing_resp_complete(hdev, bdaddr, status, + MGMT_OP_USER_PASSKEY_REPLY); +} + +int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 status) +{ + return user_pairing_resp_complete(hdev, bdaddr, status, + MGMT_OP_USER_PASSKEY_NEG_REPLY); +} + +int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status) { struct mgmt_ev_auth_failed ev; bacpy(&ev.bdaddr, bdaddr); - ev.status = status; + ev.status = mgmt_status(status); - return mgmt_event(MGMT_EV_AUTH_FAILED, index, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL); } -int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status) +int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) { struct pending_cmd *cmd; - struct hci_dev *hdev; struct mgmt_cp_set_local_name ev; int err; memset(&ev, 0, sizeof(ev)); memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); - cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, index); + cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); if (!cmd) goto send_event; if (status) { - err = cmd_status(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, EIO); + err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, + mgmt_status(status)); goto failed; } - hdev = hci_dev_get(index); - if (hdev) { - hci_dev_lock_bh(hdev); - update_eir(hdev); - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - } + update_eir(hdev); - err = cmd_complete(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, &ev, + err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, &ev, sizeof(ev)); if (err < 0) goto failed; send_event: - err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, index, &ev, sizeof(ev), + err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), cmd ? cmd->sk : NULL); failed: @@ -2284,29 +2629,31 @@ failed: return err; } -int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer, - u8 status) +int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, + u8 *randomizer, u8 status) { struct pending_cmd *cmd; int err; - BT_DBG("hci%u status %u", index, status); + BT_DBG("%s status %u", hdev->name, status); - cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index); + cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev); if (!cmd) return -ENOENT; if (status) { - err = cmd_status(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, - EIO); + err = cmd_status(cmd->sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_DATA, + mgmt_status(status)); } else { struct mgmt_rp_read_local_oob_data rp; memcpy(rp.hash, hash, sizeof(rp.hash)); memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer)); - err = cmd_complete(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, - &rp, sizeof(rp)); + err = cmd_complete(cmd->sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_DATA, + &rp, sizeof(rp)); } mgmt_pending_remove(cmd); @@ -2314,14 +2661,15 @@ int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer, return err; } -int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi, - u8 *eir) +int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 *dev_class, s8 rssi, u8 *eir) { struct mgmt_ev_device_found ev; memset(&ev, 0, sizeof(ev)); - bacpy(&ev.bdaddr, bdaddr); + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_mgmt(link_type, addr_type); ev.rssi = rssi; if (eir) @@ -2330,10 +2678,10 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi, if (dev_class) memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class)); - return mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, &ev, sizeof(ev), NULL); } -int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name) +int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name) { struct mgmt_ev_remote_name ev; @@ -2342,37 +2690,79 @@ int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name) bacpy(&ev.bdaddr, bdaddr); memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); - return mgmt_event(MGMT_EV_REMOTE_NAME, index, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_REMOTE_NAME, hdev, &ev, sizeof(ev), NULL); +} + +int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status) +{ + struct pending_cmd *cmd; + int err; + + cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); + if (!cmd) + return -ENOENT; + + err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status)); + mgmt_pending_remove(cmd); + + return err; +} + +int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status) +{ + struct pending_cmd *cmd; + int err; + + cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); + if (!cmd) + return -ENOENT; + + err = cmd_status(cmd->sk, hdev->id, cmd->opcode, status); + mgmt_pending_remove(cmd); + + return err; } -int mgmt_discovering(u16 index, u8 discovering) +int mgmt_discovering(struct hci_dev *hdev, u8 discovering) { - return mgmt_event(MGMT_EV_DISCOVERING, index, &discovering, + struct pending_cmd *cmd; + + if (discovering) + cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); + else + cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); + + if (cmd != NULL) { + cmd_complete(cmd->sk, hdev->id, cmd->opcode, NULL, 0); + mgmt_pending_remove(cmd); + } + + return mgmt_event(MGMT_EV_DISCOVERING, hdev, &discovering, sizeof(discovering), NULL); } -int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr) +int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct pending_cmd *cmd; struct mgmt_ev_device_blocked ev; - cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, index); + cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev); bacpy(&ev.bdaddr, bdaddr); - return mgmt_event(MGMT_EV_DEVICE_BLOCKED, index, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); + return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); } -int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr) +int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct pending_cmd *cmd; struct mgmt_ev_device_unblocked ev; - cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, index); + cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev); bacpy(&ev.bdaddr, bdaddr); - return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, index, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); + return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); } diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 4e32e18211f9..8743f369ed3f 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -65,7 +65,8 @@ static DEFINE_MUTEX(rfcomm_mutex); static LIST_HEAD(session_list); -static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len); +static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len, + u32 priority); static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci); static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci); static int rfcomm_queue_disc(struct rfcomm_dlc *d); @@ -377,13 +378,11 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci) { struct rfcomm_dlc *d; - struct list_head *p; - list_for_each(p, &s->dlcs) { - d = list_entry(p, struct rfcomm_dlc, list); + list_for_each_entry(d, &s->dlcs, list) if (d->dlci == dlci) return d; - } + return NULL; } @@ -749,19 +748,34 @@ void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *d } /* ---- RFCOMM frame sending ---- */ -static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len) +static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len, + u32 priority) { struct socket *sock = s->sock; + struct sock *sk = sock->sk; struct kvec iv = { data, len }; struct msghdr msg; - BT_DBG("session %p len %d", s, len); + BT_DBG("session %p len %d priority %u", s, len, priority); + + if (sk->sk_priority != priority) { + lock_sock(sk); + sk->sk_priority = priority; + release_sock(sk); + } memset(&msg, 0, sizeof(msg)); return kernel_sendmsg(sock, &msg, &iv, 1, len); } +static int rfcomm_send_cmd(struct rfcomm_session *s, struct rfcomm_cmd *cmd) +{ + BT_DBG("%p cmd %u", s, cmd->ctrl); + + return rfcomm_send_frame(s, (void *) cmd, sizeof(*cmd), HCI_PRIO_MAX); +} + static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci) { struct rfcomm_cmd cmd; @@ -773,7 +787,7 @@ static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci) cmd.len = __len8(0); cmd.fcs = __fcs2((u8 *) &cmd); - return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); + return rfcomm_send_cmd(s, &cmd); } static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci) @@ -787,7 +801,7 @@ static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci) cmd.len = __len8(0); cmd.fcs = __fcs2((u8 *) &cmd); - return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); + return rfcomm_send_cmd(s, &cmd); } static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci) @@ -801,7 +815,7 @@ static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci) cmd.len = __len8(0); cmd.fcs = __fcs2((u8 *) &cmd); - return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); + return rfcomm_send_cmd(s, &cmd); } static int rfcomm_queue_disc(struct rfcomm_dlc *d) @@ -815,6 +829,8 @@ static int rfcomm_queue_disc(struct rfcomm_dlc *d) if (!skb) return -ENOMEM; + skb->priority = HCI_PRIO_MAX; + cmd = (void *) __skb_put(skb, sizeof(*cmd)); cmd->addr = d->addr; cmd->ctrl = __ctrl(RFCOMM_DISC, 1); @@ -837,7 +853,7 @@ static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci) cmd.len = __len8(0); cmd.fcs = __fcs2((u8 *) &cmd); - return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); + return rfcomm_send_cmd(s, &cmd); } static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type) @@ -862,7 +878,7 @@ static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type) *ptr = __fcs(buf); ptr++; - return rfcomm_send_frame(s, buf, ptr - buf); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d) @@ -904,7 +920,7 @@ static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d *ptr = __fcs(buf); ptr++; - return rfcomm_send_frame(s, buf, ptr - buf); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, @@ -942,7 +958,7 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, *ptr = __fcs(buf); ptr++; - return rfcomm_send_frame(s, buf, ptr - buf); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status) @@ -969,7 +985,7 @@ static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status) *ptr = __fcs(buf); ptr++; - return rfcomm_send_frame(s, buf, ptr - buf); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig) @@ -996,7 +1012,7 @@ static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig *ptr = __fcs(buf); ptr++; - return rfcomm_send_frame(s, buf, ptr - buf); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr) @@ -1018,7 +1034,7 @@ static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr) *ptr = __fcs(buf); ptr++; - return rfcomm_send_frame(s, buf, ptr - buf); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static int rfcomm_send_fcon(struct rfcomm_session *s, int cr) @@ -1040,7 +1056,7 @@ static int rfcomm_send_fcon(struct rfcomm_session *s, int cr) *ptr = __fcs(buf); ptr++; - return rfcomm_send_frame(s, buf, ptr - buf); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len) @@ -1091,7 +1107,7 @@ static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits) *ptr = __fcs(buf); ptr++; - return rfcomm_send_frame(s, buf, ptr - buf); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static void rfcomm_make_uih(struct sk_buff *skb, u8 addr) @@ -1769,7 +1785,8 @@ static inline int rfcomm_process_tx(struct rfcomm_dlc *d) return skb_queue_len(&d->tx_queue); while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) { - err = rfcomm_send_frame(d->session, skb->data, skb->len); + err = rfcomm_send_frame(d->session, skb->data, skb->len, + skb->priority); if (err < 0) { skb_queue_head(&d->tx_queue, skb); break; @@ -2120,15 +2137,13 @@ static struct hci_cb rfcomm_cb = { static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x) { struct rfcomm_session *s; - struct list_head *pp, *p; rfcomm_lock(); - list_for_each(p, &session_list) { - s = list_entry(p, struct rfcomm_session, list); - list_for_each(pp, &s->dlcs) { + list_for_each_entry(s, &session_list, list) { + struct rfcomm_dlc *d; + list_for_each_entry(d, &s->dlcs, list) { struct sock *sk = s->sock->sk; - struct rfcomm_dlc *d = list_entry(pp, struct rfcomm_dlc, list); seq_printf(f, "%s %s %ld %d %d %d %d\n", batostr(&bt_sk(sk)->src), diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 5417f6127323..aea2bdd1510f 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -600,6 +600,8 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, break; } + skb->priority = sk->sk_priority; + err = rfcomm_dlc_send(d, skb); if (err < 0) { kfree_skb(skb); diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index c258796313e0..fa8f4de53b99 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -34,6 +34,7 @@ #include <linux/capability.h> #include <linux/slab.h> #include <linux/skbuff.h> +#include <linux/workqueue.h> #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> @@ -65,7 +66,7 @@ struct rfcomm_dev { struct rfcomm_dlc *dlc; struct tty_struct *tty; wait_queue_head_t wait; - struct tasklet_struct wakeup_task; + struct work_struct wakeup_task; struct device *tty_dev; @@ -81,7 +82,7 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb); static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err); static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig); -static void rfcomm_tty_wakeup(unsigned long arg); +static void rfcomm_tty_wakeup(struct work_struct *work); /* ---- Device functions ---- */ static void rfcomm_dev_destruct(struct rfcomm_dev *dev) @@ -133,13 +134,10 @@ static inline void rfcomm_dev_put(struct rfcomm_dev *dev) static struct rfcomm_dev *__rfcomm_dev_get(int id) { struct rfcomm_dev *dev; - struct list_head *p; - list_for_each(p, &rfcomm_dev_list) { - dev = list_entry(p, struct rfcomm_dev, list); + list_for_each_entry(dev, &rfcomm_dev_list, list) if (dev->id == id) return dev; - } return NULL; } @@ -197,7 +195,7 @@ static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL); static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) { - struct rfcomm_dev *dev; + struct rfcomm_dev *dev, *entry; struct list_head *head = &rfcomm_dev_list, *p; int err = 0; @@ -212,8 +210,8 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) if (req->dev_id < 0) { dev->id = 0; - list_for_each(p, &rfcomm_dev_list) { - if (list_entry(p, struct rfcomm_dev, list)->id != dev->id) + list_for_each_entry(entry, &rfcomm_dev_list, list) { + if (entry->id != dev->id) break; dev->id++; @@ -222,9 +220,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) } else { dev->id = req->dev_id; - list_for_each(p, &rfcomm_dev_list) { - struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list); - + list_for_each_entry(entry, &rfcomm_dev_list, list) { if (entry->id == dev->id) { err = -EADDRINUSE; goto out; @@ -257,7 +253,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) atomic_set(&dev->opened, 0); init_waitqueue_head(&dev->wait); - tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev); + INIT_WORK(&dev->wakeup_task, rfcomm_tty_wakeup); skb_queue_head_init(&dev->pending); @@ -351,7 +347,7 @@ static void rfcomm_wfree(struct sk_buff *skb) struct rfcomm_dev *dev = (void *) skb->sk; atomic_sub(skb->truesize, &dev->wmem_alloc); if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags)) - tasklet_schedule(&dev->wakeup_task); + queue_work(system_nrt_wq, &dev->wakeup_task); rfcomm_dev_put(dev); } @@ -455,9 +451,9 @@ static int rfcomm_release_dev(void __user *arg) static int rfcomm_get_dev_list(void __user *arg) { + struct rfcomm_dev *dev; struct rfcomm_dev_list_req *dl; struct rfcomm_dev_info *di; - struct list_head *p; int n = 0, size, err; u16 dev_num; @@ -479,8 +475,7 @@ static int rfcomm_get_dev_list(void __user *arg) read_lock_bh(&rfcomm_dev_lock); - list_for_each(p, &rfcomm_dev_list) { - struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list); + list_for_each_entry(dev, &rfcomm_dev_list, list) { if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) continue; (di + n)->id = dev->id; @@ -635,9 +630,10 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig) } /* ---- TTY functions ---- */ -static void rfcomm_tty_wakeup(unsigned long arg) +static void rfcomm_tty_wakeup(struct work_struct *work) { - struct rfcomm_dev *dev = (void *) arg; + struct rfcomm_dev *dev = container_of(work, struct rfcomm_dev, + wakeup_task); struct tty_struct *tty = dev->tty; if (!tty) return; @@ -762,7 +758,7 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) rfcomm_dlc_close(dev->dlc, 0); clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); - tasklet_kill(&dev->wakeup_task); + cancel_work_sync(&dev->wakeup_task); rfcomm_dlc_lock(dev->dlc); tty->driver_data = NULL; @@ -1155,9 +1151,11 @@ static const struct tty_operations rfcomm_ops = { int __init rfcomm_init_ttys(void) { + int error; + rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS); if (!rfcomm_tty_driver) - return -1; + return -ENOMEM; rfcomm_tty_driver->owner = THIS_MODULE; rfcomm_tty_driver->driver_name = "rfcomm"; @@ -1172,10 +1170,11 @@ int __init rfcomm_init_ttys(void) rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON; tty_set_operations(rfcomm_tty_driver, &rfcomm_ops); - if (tty_register_driver(rfcomm_tty_driver)) { + error = tty_register_driver(rfcomm_tty_driver); + if (error) { BT_ERR("Can't register RFCOMM TTY driver"); put_tty_driver(rfcomm_tty_driver); - return -1; + return error; } BT_INFO("RFCOMM TTY layer initialized"); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 759b63572641..0b96737d0ad3 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -181,7 +181,8 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) if (!skb) return; - hci_send_acl(conn->hcon, skb, 0); + skb->priority = HCI_PRIO_MAX; + hci_send_acl(conn->hchan, skb, 0); mod_timer(&conn->security_timer, jiffies + msecs_to_jiffies(SMP_TIMEOUT)); @@ -231,6 +232,18 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) return 0; } +static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send) +{ + if (send) + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), + &reason); + + clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->pend); + mgmt_auth_failed(conn->hcon->hdev, conn->dst, reason); + del_timer(&conn->security_timer); + smp_chan_destroy(conn); +} + static void confirm_work(struct work_struct *work) { struct smp_chan *smp = container_of(work, struct smp_chan, confirm); @@ -269,8 +282,7 @@ static void confirm_work(struct work_struct *work) return; error: - smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason); - smp_chan_destroy(conn); + smp_failure(conn, reason, 1); } static void random_work(struct work_struct *work) @@ -353,8 +365,7 @@ static void random_work(struct work_struct *work) return; error: - smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason); - smp_chan_destroy(conn); + smp_failure(conn, reason, 1); } static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) @@ -378,7 +389,15 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) void smp_chan_destroy(struct l2cap_conn *conn) { - kfree(conn->smp_chan); + struct smp_chan *smp = conn->smp_chan; + + clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend); + + if (smp->tfm) + crypto_free_blkcipher(smp->tfm); + + kfree(smp); + conn->smp_chan = NULL; hci_conn_put(conn->hcon); } @@ -646,6 +665,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) break; case SMP_CMD_PAIRING_FAIL: + smp_failure(conn, skb->data[0], 0); reason = 0; err = -EPERM; break; @@ -691,8 +711,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) done: if (reason) - smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), - &reason); + smp_failure(conn, reason, 1); kfree_skb(skb); return err; diff --git a/net/core/skbuff.c b/net/core/skbuff.c index ca4db40e75b8..2f6babd5a570 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -3168,6 +3168,26 @@ void skb_tstamp_tx(struct sk_buff *orig_skb, } EXPORT_SYMBOL_GPL(skb_tstamp_tx); +void skb_complete_wifi_ack(struct sk_buff *skb, bool acked) +{ + struct sock *sk = skb->sk; + struct sock_exterr_skb *serr; + int err; + + skb->wifi_acked_valid = 1; + skb->wifi_acked = acked; + + serr = SKB_EXT_ERR(skb); + memset(serr, 0, sizeof(*serr)); + serr->ee.ee_errno = ENOMSG; + serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS; + + err = sock_queue_err_skb(sk, skb); + if (err) + kfree_skb(skb); +} +EXPORT_SYMBOL_GPL(skb_complete_wifi_ack); + /** * skb_partial_csum_set - set up and verify partial csum values for packet diff --git a/net/core/sock.c b/net/core/sock.c index 4ed7b1d12f5e..cbdf51c0d5ac 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -740,6 +740,11 @@ set_rcvbuf: case SO_RXQ_OVFL: sock_valbool_flag(sk, SOCK_RXQ_OVFL, valbool); break; + + case SO_WIFI_STATUS: + sock_valbool_flag(sk, SOCK_WIFI_STATUS, valbool); + break; + default: ret = -ENOPROTOOPT; break; @@ -961,6 +966,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.val = !!sock_flag(sk, SOCK_RXQ_OVFL); break; + case SO_WIFI_STATUS: + v.val = !!sock_flag(sk, SOCK_WIFI_STATUS); + break; + default: return -ENOPROTOOPT; } diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 97f33588b65f..f9ec0d97e349 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -176,7 +176,8 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); if (sdata->vif.type == NL80211_IFTYPE_AP || - sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + sdata->vif.type == NL80211_IFTYPE_MESH_POINT) memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_STATION) memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 2ac033989e01..fefc7e506754 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -77,7 +77,8 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); if (sdata->vif.type == NL80211_IFTYPE_AP || - sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + sdata->vif.type == NL80211_IFTYPE_MESH_POINT) memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_STATION) memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); @@ -371,13 +372,8 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, pubsta->addr, tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ - /* - * The aggregation code is not prepared to handle - * anything but STA/AP due to the BSSID handling. - * IBSS could work in the code but isn't supported - * by drivers or the standard. - */ if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT && sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_AP) return -EINVAL; diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d06c65fa5526..02a43234b91c 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -411,7 +411,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | BIT(NL80211_STA_FLAG_WME) | BIT(NL80211_STA_FLAG_MFP) | - BIT(NL80211_STA_FLAG_AUTHENTICATED); + BIT(NL80211_STA_FLAG_AUTHENTICATED) | + BIT(NL80211_STA_FLAG_TDLS_PEER); if (test_sta_flag(sta, WLAN_STA_AUTHORIZED)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHORIZED); if (test_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE)) @@ -422,6 +423,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_MFP); if (test_sta_flag(sta, WLAN_STA_AUTH)) sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) + sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); } @@ -594,6 +597,8 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, { struct ieee80211_sub_if_data *sdata; struct beacon_data *old; + struct ieee80211_sub_if_data *vlan; + int ret; sdata = IEEE80211_DEV_TO_SUB_IF(dev); @@ -601,7 +606,24 @@ static int ieee80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, if (old) return -EALREADY; - return ieee80211_config_beacon(sdata, params); + ret = ieee80211_config_beacon(sdata, params); + if (ret) + return ret; + + /* + * Apply control port protocol, this allows us to + * not encrypt dynamic WEP control frames. + */ + sdata->control_port_protocol = params->crypto.control_port_ethertype; + sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt; + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { + vlan->control_port_protocol = + params->crypto.control_port_ethertype; + vlan->control_port_no_encrypt = + params->crypto.control_port_no_encrypt; + } + + return 0; } static int ieee80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, @@ -1394,7 +1416,7 @@ static int ieee80211_set_channel(struct wiphy *wiphy, (old_oper_type != local->_oper_channel_type)) ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); - if ((sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR) && + if (sdata && sdata->vif.type != NL80211_IFTYPE_MONITOR && old_vif_oper_type != sdata->vif.bss_conf.channel_type) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_HT); @@ -1917,7 +1939,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, - u64 *cookie) + bool dont_wait_for_ack, u64 *cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -1925,10 +1947,15 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct sta_info *sta; struct ieee80211_work *wk; const struct ieee80211_mgmt *mgmt = (void *)buf; - u32 flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | - IEEE80211_TX_CTL_REQ_TX_STATUS; + u32 flags; bool is_offchan = false; + if (dont_wait_for_ack) + flags = IEEE80211_TX_CTL_NO_ACK; + else + flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX | + IEEE80211_TX_CTL_REQ_TX_STATUS; + /* Check that we are on the requested channel for transmission */ if (chan != local->tmp_channel && chan != local->oper_channel) @@ -2488,6 +2515,73 @@ static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, return 0; } +static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u64 *cookie) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + struct ieee80211_qos_hdr *nullfunc; + struct sk_buff *skb; + int size = sizeof(*nullfunc); + __le16 fc; + bool qos; + struct ieee80211_tx_info *info; + struct sta_info *sta; + + rcu_read_lock(); + sta = sta_info_get(sdata, peer); + if (sta) + qos = test_sta_flag(sta, WLAN_STA_WME); + rcu_read_unlock(); + + if (!sta) + return -ENOLINK; + + if (qos) { + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_NULLFUNC | + IEEE80211_FCTL_FROMDS); + } else { + size -= 2; + fc = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_FROMDS); + } + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + size); + if (!skb) + return -ENOMEM; + + skb->dev = dev; + + skb_reserve(skb, local->hw.extra_tx_headroom); + + nullfunc = (void *) skb_put(skb, size); + nullfunc->frame_control = fc; + nullfunc->duration_id = 0; + memcpy(nullfunc->addr1, sta->sta.addr, ETH_ALEN); + memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); + memcpy(nullfunc->addr3, sdata->vif.addr, ETH_ALEN); + nullfunc->seq_ctrl = 0; + + info = IEEE80211_SKB_CB(skb); + + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS | + IEEE80211_TX_INTFL_NL80211_FRAME_TX; + + skb_set_queue_mapping(skb, IEEE80211_AC_VO); + skb->priority = 7; + if (qos) + nullfunc->qos_ctrl = cpu_to_le16(7); + + local_bh_disable(); + ieee80211_xmit(sdata, skb); + local_bh_enable(); + + *cookie = (unsigned long) skb; + return 0; +} + struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -2553,4 +2647,5 @@ struct cfg80211_ops mac80211_config_ops = { .set_rekey_data = ieee80211_set_rekey_data, .tdls_oper = ieee80211_tdls_oper, .tdls_mgmt = ieee80211_tdls_mgmt, + .probe_client = ieee80211_probe_client, }; diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 883996b2f99f..00cefcb493eb 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -190,7 +190,7 @@ static ssize_t uapsd_max_sp_len_write(struct file *file, return -EFAULT; buf[len] = '\0'; - ret = strict_strtoul(buf, 0, &val); + ret = kstrtoul(buf, 0, &val); if (ret) return -EINVAL; diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 5f165d7eb2db..b12ed52732c8 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -5,6 +5,11 @@ #include "ieee80211_i.h" #include "driver-trace.h" +static inline void check_sdata_in_driver(struct ieee80211_sub_if_data *sdata) +{ + WARN_ON(!(sdata->flags & IEEE80211_SDATA_IN_DRIVER)); +} + static inline void drv_tx(struct ieee80211_local *local, struct sk_buff *skb) { local->ops->tx(&local->hw, skb); @@ -69,15 +74,23 @@ static inline int drv_resume(struct ieee80211_local *local) #endif static inline int drv_add_interface(struct ieee80211_local *local, - struct ieee80211_vif *vif) + struct ieee80211_sub_if_data *sdata) { int ret; might_sleep(); - trace_drv_add_interface(local, vif_to_sdata(vif)); - ret = local->ops->add_interface(&local->hw, vif); + if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + sdata->vif.type == NL80211_IFTYPE_MONITOR)) + return -EINVAL; + + trace_drv_add_interface(local, sdata); + ret = local->ops->add_interface(&local->hw, &sdata->vif); trace_drv_return_int(local, ret); + + if (ret == 0) + sdata->flags |= IEEE80211_SDATA_IN_DRIVER; + return ret; } @@ -89,6 +102,8 @@ static inline int drv_change_interface(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_change_interface(local, sdata, type, p2p); ret = local->ops->change_interface(&local->hw, &sdata->vif, type, p2p); trace_drv_return_int(local, ret); @@ -96,12 +111,15 @@ static inline int drv_change_interface(struct ieee80211_local *local, } static inline void drv_remove_interface(struct ieee80211_local *local, - struct ieee80211_vif *vif) + struct ieee80211_sub_if_data *sdata) { might_sleep(); - trace_drv_remove_interface(local, vif_to_sdata(vif)); - local->ops->remove_interface(&local->hw, vif); + check_sdata_in_driver(sdata); + + trace_drv_remove_interface(local, sdata); + local->ops->remove_interface(&local->hw, &sdata->vif); + sdata->flags &= ~IEEE80211_SDATA_IN_DRIVER; trace_drv_return_void(local); } @@ -124,6 +142,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local, { might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_bss_info_changed(local, sdata, info, changed); if (local->ops->bss_info_changed) local->ops->bss_info_changed(&local->hw, &sdata->vif, info, changed); @@ -139,6 +159,8 @@ static inline int drv_tx_sync(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_tx_sync(local, sdata, bssid, type); if (local->ops->tx_sync) ret = local->ops->tx_sync(&local->hw, &sdata->vif, @@ -154,6 +176,8 @@ static inline void drv_finish_tx_sync(struct ieee80211_local *local, { might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_finish_tx_sync(local, sdata, bssid, type); if (local->ops->finish_tx_sync) local->ops->finish_tx_sync(&local->hw, &sdata->vif, @@ -211,6 +235,8 @@ static inline int drv_set_key(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_set_key(local, cmd, sdata, sta, key); ret = local->ops->set_key(&local->hw, cmd, &sdata->vif, sta, key); trace_drv_return_int(local, ret); @@ -228,6 +254,8 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local, if (sta) ista = &sta->sta; + check_sdata_in_driver(sdata); + trace_drv_update_tkip_key(local, sdata, conf, ista, iv32); if (local->ops->update_tkip_key) local->ops->update_tkip_key(&local->hw, &sdata->vif, conf, @@ -243,6 +271,8 @@ static inline int drv_hw_scan(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_hw_scan(local, sdata); ret = local->ops->hw_scan(&local->hw, &sdata->vif, req); trace_drv_return_int(local, ret); @@ -254,6 +284,8 @@ static inline void drv_cancel_hw_scan(struct ieee80211_local *local, { might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_cancel_hw_scan(local, sdata); local->ops->cancel_hw_scan(&local->hw, &sdata->vif); trace_drv_return_void(local); @@ -269,6 +301,8 @@ drv_sched_scan_start(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_sched_scan_start(local, sdata); ret = local->ops->sched_scan_start(&local->hw, &sdata->vif, req, ies); @@ -281,6 +315,8 @@ static inline void drv_sched_scan_stop(struct ieee80211_local *local, { 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); @@ -377,6 +413,8 @@ static inline void drv_sta_notify(struct ieee80211_local *local, enum sta_notify_cmd cmd, struct ieee80211_sta *sta) { + check_sdata_in_driver(sdata); + trace_drv_sta_notify(local, sdata, cmd, sta); if (local->ops->sta_notify) local->ops->sta_notify(&local->hw, &sdata->vif, cmd, sta); @@ -391,6 +429,8 @@ static inline int drv_sta_add(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_sta_add(local, sdata, sta); if (local->ops->sta_add) ret = local->ops->sta_add(&local->hw, &sdata->vif, sta); @@ -406,6 +446,8 @@ static inline void drv_sta_remove(struct ieee80211_local *local, { might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_sta_remove(local, sdata, sta); if (local->ops->sta_remove) local->ops->sta_remove(&local->hw, &sdata->vif, sta); @@ -421,6 +463,8 @@ static inline int drv_conf_tx(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_conf_tx(local, sdata, queue, params); if (local->ops->conf_tx) ret = local->ops->conf_tx(&local->hw, &sdata->vif, @@ -436,6 +480,8 @@ static inline u64 drv_get_tsf(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_get_tsf(local, sdata); if (local->ops->get_tsf) ret = local->ops->get_tsf(&local->hw, &sdata->vif); @@ -449,6 +495,8 @@ static inline void drv_set_tsf(struct ieee80211_local *local, { might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_set_tsf(local, sdata, tsf); if (local->ops->set_tsf) local->ops->set_tsf(&local->hw, &sdata->vif, tsf); @@ -460,6 +508,8 @@ static inline void drv_reset_tsf(struct ieee80211_local *local, { might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_reset_tsf(local, sdata); if (local->ops->reset_tsf) local->ops->reset_tsf(&local->hw, &sdata->vif); @@ -489,6 +539,8 @@ static inline int drv_ampdu_action(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size); if (local->ops->ampdu_action) @@ -644,6 +696,8 @@ static inline int drv_set_bitrate_mask(struct ieee80211_local *local, might_sleep(); + check_sdata_in_driver(sdata); + trace_drv_set_bitrate_mask(local, sdata, mask); if (local->ops->set_bitrate_mask) ret = local->ops->set_bitrate_mask(&local->hw, @@ -657,6 +711,8 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct cfg80211_gtk_rekey_data *data) { + check_sdata_in_driver(sdata); + trace_drv_set_rekey_data(local, sdata, data); if (local->ops->set_rekey_data) local->ops->set_rekey_data(&local->hw, &sdata->vif, data); diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index f80a35c0d000..988c7ec0898c 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -195,7 +195,8 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, memcpy(mgmt->da, da, ETH_ALEN); memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); if (sdata->vif.type == NL80211_IFTYPE_AP || - sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + sdata->vif.type == NL80211_IFTYPE_MESH_POINT) memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); else if (sdata->vif.type == NL80211_IFTYPE_STATION) memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ea10a51babda..76e656bf78f9 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -24,6 +24,7 @@ #include <linux/spinlock.h> #include <linux/etherdevice.h> #include <linux/leds.h> +#include <linux/idr.h> #include <net/ieee80211_radiotap.h> #include <net/cfg80211.h> #include <net/mac80211.h> @@ -184,12 +185,15 @@ enum ieee80211_packet_rx_flags { * enum ieee80211_rx_flags - RX data flags * * @IEEE80211_RX_CMNTR: received on cooked monitor already + * @IEEE80211_RX_BEACON_REPORTED: This frame was already reported + * to cfg80211_report_obss_beacon(). * * These flags are used across handling multiple interfaces * for a single frame. */ enum ieee80211_rx_flags { IEEE80211_RX_CMNTR = BIT(0), + IEEE80211_RX_BEACON_REPORTED = BIT(1), }; struct ieee80211_rx_data { @@ -543,6 +547,7 @@ struct ieee80211_if_mesh { * associated stations and deliver multicast frames both * back to wireless media and to the local net stack. * @IEEE80211_SDATA_DISCONNECT_RESUME: Disconnect after resume. + * @IEEE80211_SDATA_IN_DRIVER: indicates interface was added to driver */ enum ieee80211_sub_if_data_flags { IEEE80211_SDATA_ALLMULTI = BIT(0), @@ -550,6 +555,7 @@ enum ieee80211_sub_if_data_flags { IEEE80211_SDATA_OPERATING_GMODE = BIT(2), IEEE80211_SDATA_DONT_BRIDGE_PACKETS = BIT(3), IEEE80211_SDATA_DISCONNECT_RESUME = BIT(4), + IEEE80211_SDATA_IN_DRIVER = BIT(5), }; /** @@ -1012,6 +1018,9 @@ struct ieee80211_local { u32 hw_roc_cookie; bool hw_roc_for_tx; + struct idr ack_status_frames; + spinlock_t ack_status_lock; + /* dummy netdev for use w/ NAPI */ struct net_device napi_dev; @@ -1334,6 +1343,12 @@ void ieee80211_recalc_smps(struct ieee80211_local *local); size_t ieee80211_ie_split(const u8 *ies, size_t ielen, const u8 *ids, int n_ids, size_t offset); size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); +u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_supported_band *sband, + u16 cap); +u8 *ieee80211_ie_build_ht_info(u8 *pos, + struct ieee80211_sta_ht_cap *ht_cap, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type); /* internal work items */ void ieee80211_work_init(struct ieee80211_local *local); @@ -1362,6 +1377,8 @@ ieee80211_get_channel_mode(struct ieee80211_local *local, bool ieee80211_set_channel_type(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum nl80211_channel_type chantype); +enum nl80211_channel_type +ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info); #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 30d73552e9ab..b7bc4b7b80af 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -188,11 +188,22 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) if (!is_valid_ether_addr(sdata->u.wds.remote_addr)) return -ENOLINK; break; - case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_AP_VLAN: { + struct ieee80211_sub_if_data *master; + if (!sdata->bss) return -ENOLINK; + list_add(&sdata->u.vlan.list, &sdata->bss->vlans); + + master = container_of(sdata->bss, + struct ieee80211_sub_if_data, u.ap); + sdata->control_port_protocol = + master->control_port_protocol; + sdata->control_port_no_encrypt = + master->control_port_no_encrypt; break; + } case NL80211_IFTYPE_AP: sdata->bss = &sdata->u.ap; break; @@ -265,7 +276,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) break; default: if (coming_up) { - res = drv_add_interface(local, &sdata->vif); + res = drv_add_interface(local, sdata); if (res) goto err_stop; } @@ -286,6 +297,13 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) netif_carrier_off(dev); else netif_carrier_on(dev); + + /* + * set default queue parameters so drivers don't + * need to initialise the hardware if the hardware + * doesn't start up with sane defaults + */ + ieee80211_set_wmm_default(sdata); } set_bit(SDATA_STATE_RUNNING, &sdata->state); @@ -329,15 +347,8 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) if (coming_up) local->open_count++; - if (hw_reconf_flags) { + if (hw_reconf_flags) ieee80211_hw_config(local, hw_reconf_flags); - /* - * set default queue parameters so drivers don't - * need to initialise the hardware if the hardware - * doesn't start up with sane defaults - */ - ieee80211_set_wmm_default(sdata); - } ieee80211_recalc_ps(local, -1); @@ -345,7 +356,7 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) return 0; err_del_interface: - drv_remove_interface(local, &sdata->vif); + drv_remove_interface(local, sdata); err_stop: if (!local->open_count) drv_stop(local); @@ -520,7 +531,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ieee80211_free_keys(sdata); if (going_down) - drv_remove_interface(local, &sdata->vif); + drv_remove_interface(local, sdata); } sdata->bss = NULL; diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 756b157c2edd..17a5220ed450 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -133,9 +133,13 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || - (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))) + (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || + (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) sdata->crypto_tx_tailroom_needed_cnt--; + WARN_ON((key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) && + (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)); + return 0; } @@ -178,7 +182,8 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) sdata = key->sdata; if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) || - (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))) + (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) || + (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))) increment_tailroom_need_count(sdata); if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) diff --git a/net/mac80211/main.c b/net/mac80211/main.c index d999bf3b84e1..e323d4e6647b 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -100,7 +100,7 @@ static void ieee80211_reconfig_filter(struct work_struct *work) */ bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local) { - struct ieee80211_channel *chan, *scan_chan; + struct ieee80211_channel *chan; enum nl80211_channel_type channel_type; /* This logic needs to match logic in ieee80211_hw_config */ @@ -114,7 +114,7 @@ bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local) else channel_type = NL80211_CHAN_NO_HT; } else if (local->tmp_channel) { - chan = scan_chan = local->tmp_channel; + chan = local->tmp_channel; channel_type = local->tmp_channel_type; } else { chan = local->oper_channel; @@ -126,8 +126,8 @@ bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local) return false; /* Check current hardware-config against oper_channel. */ - if ((local->oper_channel != local->hw.conf.channel) || - (local->_oper_channel_type != local->hw.conf.channel_type)) + if (local->oper_channel != local->hw.conf.channel || + local->_oper_channel_type != local->hw.conf.channel_type) return false; return true; @@ -135,7 +135,7 @@ bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local) int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) { - struct ieee80211_channel *chan, *scan_chan; + struct ieee80211_channel *chan; int ret = 0; int power; enum nl80211_channel_type channel_type; @@ -143,14 +143,12 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) might_sleep(); - scan_chan = local->scan_channel; - /* If this off-channel logic ever changes, ieee80211_on_oper_channel * may need to change as well. */ offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; - if (scan_chan) { - chan = scan_chan; + if (local->scan_channel) { + chan = local->scan_channel; /* If scanning on oper channel, use whatever channel-type * is currently in use. */ @@ -159,7 +157,7 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) else channel_type = NL80211_CHAN_NO_HT; } else if (local->tmp_channel) { - chan = scan_chan = local->tmp_channel; + chan = local->tmp_channel; channel_type = local->tmp_channel_type; } else { chan = local->oper_channel; @@ -595,7 +593,10 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, wiphy->flags |= WIPHY_FLAG_NETNS_OK | WIPHY_FLAG_4ADDR_AP | - WIPHY_FLAG_4ADDR_STATION; + WIPHY_FLAG_4ADDR_STATION | + WIPHY_FLAG_REPORTS_OBSS; + + wiphy->features = NL80211_FEATURE_SK_TX_STATUS; if (!ops->set_key) wiphy->flags |= WIPHY_FLAG_IBSS_RSN; @@ -670,6 +671,11 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, INIT_WORK(&local->sched_scan_stopped_work, ieee80211_sched_scan_stopped_work); + spin_lock_init(&local->ack_status_lock); + idr_init(&local->ack_status_frames); + /* preallocate at least one entry */ + idr_pre_get(&local->ack_status_frames, GFP_KERNEL); + sta_info_init(local); for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { @@ -1045,6 +1051,13 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) } EXPORT_SYMBOL(ieee80211_unregister_hw); +static int ieee80211_free_ack_frame(int id, void *p, void *data) +{ + WARN_ONCE(1, "Have pending ack frames!\n"); + kfree_skb(p); + return 0; +} + void ieee80211_free_hw(struct ieee80211_hw *hw) { struct ieee80211_local *local = hw_to_local(hw); @@ -1055,6 +1068,10 @@ void ieee80211_free_hw(struct ieee80211_hw *hw) if (local->wiphy_ciphers_allocated) kfree(local->hw.wiphy->cipher_suites); + idr_for_each(&local->ack_status_frames, + ieee80211_free_ack_frame, NULL); + idr_destroy(&local->ack_status_frames); + wiphy_free(local->hw.wiphy); } EXPORT_SYMBOL(ieee80211_free_hw); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index a7078fdba8ca..b3a125f60347 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -76,6 +76,7 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data) bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee80211_local *local = sdata->local; /* * As support for each feature is added, check for matching @@ -87,15 +88,23 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat * - MDA enabled * - Power management control on fc */ - if (ifmsh->mesh_id_len == ie->mesh_id_len && - memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 && - (ifmsh->mesh_pp_id == ie->mesh_config->meshconf_psel) && - (ifmsh->mesh_pm_id == ie->mesh_config->meshconf_pmetric) && - (ifmsh->mesh_cc_id == ie->mesh_config->meshconf_congest) && - (ifmsh->mesh_sp_id == ie->mesh_config->meshconf_synch) && - (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)) - return true; - + if (!(ifmsh->mesh_id_len == ie->mesh_id_len && + memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 && + (ifmsh->mesh_pp_id == ie->mesh_config->meshconf_psel) && + (ifmsh->mesh_pm_id == ie->mesh_config->meshconf_pmetric) && + (ifmsh->mesh_cc_id == ie->mesh_config->meshconf_congest) && + (ifmsh->mesh_sp_id == ie->mesh_config->meshconf_synch) && + (ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth))) + goto mismatch; + + /* disallow peering with mismatched channel types for now */ + if (ie->ht_info_elem && + (local->_oper_channel_type != + ieee80211_ht_info_to_channel_type(ie->ht_info_elem))) + goto mismatch; + + return true; +mismatch: return false; } @@ -341,6 +350,49 @@ int mesh_add_ds_params_ie(struct sk_buff *skb, return 0; } +int mesh_add_ht_cap_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + u8 *pos; + + sband = local->hw.wiphy->bands[local->oper_channel->band]; + if (!sband->ht_cap.ht_supported || + local->_oper_channel_type == NL80211_CHAN_NO_HT) + return 0; + + if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap)) + return -ENOMEM; + + pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_cap)); + ieee80211_ie_build_ht_cap(pos, sband, sband->ht_cap.cap); + + return 0; +} + +int mesh_add_ht_info_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_channel *channel = local->oper_channel; + enum nl80211_channel_type channel_type = local->_oper_channel_type; + struct ieee80211_supported_band *sband = + local->hw.wiphy->bands[channel->band]; + struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap; + u8 *pos; + + if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT) + return 0; + + if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_info)) + return -ENOMEM; + + pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_info)); + ieee80211_ie_build_ht_info(pos, ht_cap, channel, channel_type); + + return 0; +} static void ieee80211_mesh_path_timer(unsigned long data) { struct ieee80211_sub_if_data *sdata = diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 8c00e2d1d636..622cc96eb4de 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -31,6 +31,8 @@ * @MESH_PATH_FIXED: the mesh path has been manually set and should not be * modified * @MESH_PATH_RESOLVED: the mesh path can has been resolved + * @MESH_PATH_REQ_QUEUED: there is an unsent path request for this destination + * already queued up, waiting for the discovery process to start. * * MESH_PATH_RESOLVED is used by the mesh path timer to * decide when to stop or cancel the mesh path discovery. @@ -41,6 +43,7 @@ enum mesh_path_flags { MESH_PATH_SN_VALID = BIT(2), MESH_PATH_FIXED = BIT(3), MESH_PATH_RESOLVED = BIT(4), + MESH_PATH_REQ_QUEUED = BIT(5), }; /** @@ -212,6 +215,10 @@ int mesh_add_vendor_ies(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); int mesh_add_ds_params_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata); +int mesh_add_ht_cap_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata); +int mesh_add_ht_info_ie(struct sk_buff *skb, + struct ieee80211_sub_if_data *sdata); void mesh_rmc_free(struct ieee80211_sub_if_data *sdata); int mesh_rmc_init(struct ieee80211_sub_if_data *sdata); void ieee80211s_init(void); diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index 174040a42887..a7afb2d32def 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -113,20 +113,20 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags, struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; - struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); + struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos; - int ie_len; + u8 *pos, ie_len; + int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.mesh_action) + + sizeof(mgmt->u.action.u.mesh_action); + skb = dev_alloc_skb(local->hw.extra_tx_headroom + + hdr_len + + 2 + 37); /* max HWMP IE */ if (!skb) return -1; skb_reserve(skb, local->hw.extra_tx_headroom); - /* 25 is the size of the common mgmt part (24) plus the size of the - * common action part (1) - */ - mgmt = (struct ieee80211_mgmt *) - skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action)); - memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action)); + mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); + memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); @@ -240,20 +240,20 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn, struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; - struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); + struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos; - int ie_len; + u8 *pos, ie_len; + int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.mesh_action) + + sizeof(mgmt->u.action.u.mesh_action); + skb = dev_alloc_skb(local->hw.extra_tx_headroom + + hdr_len + + 2 + 15 /* PERR IE */); if (!skb) return -1; skb_reserve(skb, local->tx_headroom + local->hw.extra_tx_headroom); - /* 25 is the size of the common mgmt part (24) plus the size of the - * common action part (1) - */ - mgmt = (struct ieee80211_mgmt *) - skb_put(skb, 25 + sizeof(mgmt->u.action.u.mesh_action)); - memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.mesh_action)); + mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); + memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); @@ -867,9 +867,19 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags) return; } + spin_lock_bh(&mpath->state_lock); + if (mpath->flags & MESH_PATH_REQ_QUEUED) { + spin_unlock_bh(&mpath->state_lock); + spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); + return; + } + memcpy(preq_node->dst, mpath->dst, ETH_ALEN); preq_node->flags = flags; + mpath->flags |= MESH_PATH_REQ_QUEUED; + spin_unlock_bh(&mpath->state_lock); + list_add_tail(&preq_node->list, &ifmsh->preq_queue.list); ++ifmsh->preq_queue_len; spin_unlock_bh(&ifmsh->mesh_preq_queue_lock); @@ -921,6 +931,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata) goto enddiscovery; spin_lock_bh(&mpath->state_lock); + mpath->flags &= ~MESH_PATH_REQ_QUEUED; if (preq_node->flags & PREQ_Q_F_START) { if (mpath->flags & MESH_PATH_RESOLVING) { spin_unlock_bh(&mpath->state_lock); @@ -1028,11 +1039,11 @@ int mesh_nexthop_lookup(struct sk_buff *skb, mesh_queue_preq(mpath, PREQ_Q_F_START); } - if (skb_queue_len(&mpath->frame_queue) >= - MESH_FRAME_QUEUE_LEN) + if (skb_queue_len(&mpath->frame_queue) >= MESH_FRAME_QUEUE_LEN) skb_to_free = skb_dequeue(&mpath->frame_queue); info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; + ieee80211_set_qos_hdr(sdata, skb); skb_queue_tail(&mpath->frame_queue, skb); if (skb_to_free) mesh_path_discard_frame(skb_to_free, sdata); @@ -1061,6 +1072,7 @@ void mesh_path_timer(unsigned long data) } else if (mpath->discovery_retries < max_preq_retries(sdata)) { ++mpath->discovery_retries; mpath->discovery_timeout *= 2; + mpath->flags &= ~MESH_PATH_REQ_QUEUED; spin_unlock_bh(&mpath->state_lock); mesh_queue_preq(mpath, 0); } else { diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 7f54c5042235..4fc23d1b9c3a 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c @@ -213,7 +213,6 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) struct ieee80211_hdr *hdr; struct sk_buff_head tmpq; unsigned long flags; - struct ieee80211_sub_if_data *sdata = mpath->sdata; rcu_assign_pointer(mpath->next_hop, sta); @@ -224,8 +223,6 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) while ((skb = __skb_dequeue(&mpath->frame_queue)) != NULL) { hdr = (struct ieee80211_hdr *) skb->data; memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN); - skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb)); - ieee80211_set_qos_hdr(sdata, skb); __skb_queue_tail(&tmpq, skb); } diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 7e57f5d07f66..0140e88a8220 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -80,11 +80,15 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta) * on it in the lifecycle management section! */ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, - u8 *hw_addr, u32 rates) + u8 *hw_addr, u32 rates, + struct ieee802_11_elems *elems) { struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; struct sta_info *sta; + sband = local->hw.wiphy->bands[local->oper_channel->band]; + if (local->num_sta >= MESH_MAX_PLINKS) return NULL; @@ -96,6 +100,9 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, set_sta_flag(sta, WLAN_STA_AUTHORIZED); set_sta_flag(sta, WLAN_STA_WME); sta->sta.supp_rates[local->hw.conf.channel->band] = rates; + if (elems->ht_cap_elem) + ieee80211_ht_cap_ie_to_sta_ht_cap(sband, elems->ht_cap_elem, + &sta->sta.ht_cap); rate_control_rate_init(sta); return sta; @@ -153,23 +160,31 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, enum ieee80211_self_protected_actioncode action, u8 *da, __le16 llid, __le16 plid, __le16 reason) { struct ieee80211_local *local = sdata->local; - struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 + - sdata->u.mesh.ie_len); + struct sk_buff *skb; struct ieee80211_mgmt *mgmt; bool include_plid = false; - int ie_len = 4; u16 peering_proto = 0; - u8 *pos; - + u8 *pos, ie_len = 4; + int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.self_prot) + + sizeof(mgmt->u.action.u.self_prot); + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + + hdr_len + + 2 + /* capability info */ + 2 + /* AID */ + 2 + 8 + /* supported rates */ + 2 + (IEEE80211_MAX_SUPP_RATES - 8) + + 2 + sdata->u.mesh.mesh_id_len + + 2 + sizeof(struct ieee80211_meshconf_ie) + + 2 + sizeof(struct ieee80211_ht_cap) + + 2 + sizeof(struct ieee80211_ht_info) + + 2 + 8 + /* peering IE */ + sdata->u.mesh.ie_len); if (!skb) return -1; skb_reserve(skb, local->hw.extra_tx_headroom); - /* 25 is the size of the common mgmt part (24) plus the size of the - * common action part (1) - */ - mgmt = (struct ieee80211_mgmt *) - skb_put(skb, 25 + sizeof(mgmt->u.action.u.self_prot)); - memset(mgmt, 0, 25 + sizeof(mgmt->u.action.u.self_prot)); + mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); + memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); memcpy(mgmt->da, da, ETH_ALEN); @@ -235,6 +250,13 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, memcpy(pos, &reason, 2); pos += 2; } + + if (action != WLAN_SP_MESH_PEERING_CLOSE) { + if (mesh_add_ht_cap_ie(skb, sdata) || + mesh_add_ht_info_ie(skb, sdata)) + return -1; + } + if (mesh_add_vendor_ies(skb, sdata)) return -1; @@ -261,7 +283,7 @@ void mesh_neighbour_update(u8 *hw_addr, u32 rates, elems->ie_start, elems->total_len, GFP_KERNEL); else - sta = mesh_plink_alloc(sdata, hw_addr, rates); + sta = mesh_plink_alloc(sdata, hw_addr, rates, elems); if (!sta) return; if (sta_info_insert_rcu(sta)) { @@ -552,7 +574,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m } rates = ieee80211_sta_get_rates(local, &elems, rx_status->band); - sta = mesh_plink_alloc(sdata, mgmt->sa, rates); + sta = mesh_plink_alloc(sdata, mgmt->sa, rates, &elems); if (!sta) { mpl_dbg("Mesh plink error: plink table full\n"); return; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 17258feaab9b..b25567a32f92 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1466,6 +1466,47 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, return RX_MGMT_CFG80211_DISASSOC; } +static void ieee80211_get_rates(struct ieee80211_supported_band *sband, + u8 *supp_rates, unsigned int supp_rates_len, + u32 *rates, u32 *basic_rates, + bool *have_higher_than_11mbit, + int *min_rate, int *min_rate_index) +{ + int i, j; + + for (i = 0; i < supp_rates_len; i++) { + int rate = (supp_rates[i] & 0x7f) * 5; + bool is_basic = !!(supp_rates[i] & 0x80); + + if (rate > 110) + *have_higher_than_11mbit = true; + + /* + * BSS_MEMBERSHIP_SELECTOR_HT_PHY is defined in 802.11n-2009 + * 7.3.2.2 as a magic value instead of a rate. Hence, skip it. + * + * Note: Even through the membership selector and the basic + * rate flag share the same bit, they are not exactly + * the same. + */ + if (!!(supp_rates[i] & 0x80) && + (supp_rates[i] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY) + continue; + + for (j = 0; j < sband->n_bitrates; j++) { + if (sband->bitrates[j].bitrate == rate) { + *rates |= BIT(j); + if (is_basic) + *basic_rates |= BIT(j); + if (rate < *min_rate) { + *min_rate = rate; + *min_rate_index = j; + } + break; + } + } + } +} static bool ieee80211_assoc_success(struct ieee80211_work *wk, struct ieee80211_mgmt *mgmt, size_t len) @@ -1482,9 +1523,10 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, struct ieee802_11_elems elems; struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf; u32 changed = 0; - int i, j, err; + int err; bool have_higher_than_11mbit = false; u16 ap_ht_cap_flags; + int min_rate = INT_MAX, min_rate_index = -1; /* AssocResp and ReassocResp have identical structure */ @@ -1539,38 +1581,23 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, basic_rates = 0; sband = local->hw.wiphy->bands[wk->chan->band]; - for (i = 0; i < elems.supp_rates_len; i++) { - int rate = (elems.supp_rates[i] & 0x7f) * 5; - bool is_basic = !!(elems.supp_rates[i] & 0x80); - - if (rate > 110) - have_higher_than_11mbit = true; - - for (j = 0; j < sband->n_bitrates; j++) { - if (sband->bitrates[j].bitrate == rate) { - rates |= BIT(j); - if (is_basic) - basic_rates |= BIT(j); - break; - } - } - } + ieee80211_get_rates(sband, elems.supp_rates, elems.supp_rates_len, + &rates, &basic_rates, &have_higher_than_11mbit, + &min_rate, &min_rate_index); - for (i = 0; i < elems.ext_supp_rates_len; i++) { - int rate = (elems.ext_supp_rates[i] & 0x7f) * 5; - bool is_basic = !!(elems.ext_supp_rates[i] & 0x80); + ieee80211_get_rates(sband, elems.ext_supp_rates, + elems.ext_supp_rates_len, &rates, &basic_rates, + &have_higher_than_11mbit, + &min_rate, &min_rate_index); - if (rate > 110) - have_higher_than_11mbit = true; - - for (j = 0; j < sband->n_bitrates; j++) { - if (sband->bitrates[j].bitrate == rate) { - rates |= BIT(j); - if (is_basic) - basic_rates |= BIT(j); - break; - } - } + /* + * some buggy APs don't advertise basic_rates. use the lowest + * supported rate instead. + */ + if (unlikely(!basic_rates) && min_rate_index >= 0) { + printk(KERN_DEBUG "%s: No basic rates in AssocResp. " + "Using min supported rate instead.\n", sdata->name); + basic_rates = BIT(min_rate_index); } sta->sta.supp_rates[wk->chan->band] = rates; diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 9ee7164b207c..596efaf50e09 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -125,7 +125,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); - drv_remove_interface(local, &sdata->vif); + drv_remove_interface(local, sdata); } /* stop hardware - this must stop RX */ diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index b867bd55de7a..5f6751a07405 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -744,10 +744,11 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx) struct ieee80211_local *local = rx->local; struct ieee80211_hw *hw = &local->hw; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct sta_info *sta = rx->sta; struct tid_ampdu_rx *tid_agg_rx; u16 sc; - int tid; + u8 tid, ack_policy; if (!ieee80211_is_data_qos(hdr->frame_control)) goto dont_reorder; @@ -760,6 +761,8 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx) if (!sta) goto dont_reorder; + ack_policy = *ieee80211_get_qos_ctl(hdr) & + IEEE80211_QOS_CTL_ACK_POLICY_MASK; tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]); @@ -770,6 +773,15 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx) if (unlikely(hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_NULLFUNC))) goto dont_reorder; + /* not part of a BA session */ + if (ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_BLOCKACK && + ack_policy != IEEE80211_QOS_CTL_ACK_POLICY_NORMAL) + goto dont_reorder; + + /* not actually part of this BA session */ + if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) + goto dont_reorder; + /* new, potentially un-ordered, ampdu frame - process it */ /* reset session timer */ @@ -854,6 +866,13 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) rx->sdata->control_port_protocol) return RX_CONTINUE; } + + if (rx->sdata->vif.type == NL80211_IFTYPE_AP && + cfg80211_rx_spurious_frame(rx->sdata->dev, + hdr->addr2, + GFP_ATOMIC)) + return RX_DROP_UNUSABLE; + return RX_DROP_MONITOR; } @@ -1323,15 +1342,20 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) /* * If we receive a 4-addr nullfunc frame from a STA - * that was not moved to a 4-addr STA vlan yet, drop - * the frame to the monitor interface, to make sure - * that hostapd sees it + * that was not moved to a 4-addr STA vlan yet send + * the event to userspace and for older hostapd drop + * the frame to the monitor interface. */ if (ieee80211_has_a4(hdr->frame_control) && (rx->sdata->vif.type == NL80211_IFTYPE_AP || (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && - !rx->sdata->u.vlan.sta))) + !rx->sdata->u.vlan.sta))) { + if (!test_and_set_sta_flag(sta, WLAN_STA_4ADDR_EVENT)) + cfg80211_rx_unexpected_4addr_frame( + rx->sdata->dev, sta->sta.addr, + GFP_ATOMIC); return RX_DROP_MONITOR; + } /* * Update counter and free packet here to avoid * counting this as a dropped packed. @@ -1929,6 +1953,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) compare_ether_addr(sdata->vif.addr, hdr->addr3) == 0) return RX_CONTINUE; + skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb)); mesh_hdr->ttl--; if (status->rx_flags & IEEE80211_RX_RA_MATCH) { @@ -1953,12 +1978,10 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) memset(info, 0, sizeof(*info)); info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; info->control.vif = &rx->sdata->vif; + info->control.jiffies = jiffies; if (is_multicast_ether_addr(fwd_hdr->addr1)) { IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, fwded_mcast); - skb_set_queue_mapping(fwd_skb, - ieee80211_select_queue(sdata, fwd_skb)); - ieee80211_set_qos_hdr(sdata, fwd_skb); } else { int err; /* @@ -2010,12 +2033,17 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) return RX_DROP_MONITOR; /* - * Allow the cooked monitor interface of an AP to see 4-addr frames so - * that a 4-addr station can be detected and moved into a separate VLAN + * Send unexpected-4addr-frame event to hostapd. For older versions, + * also drop the frame to cooked monitor interfaces. */ if (ieee80211_has_a4(hdr->frame_control) && - sdata->vif.type == NL80211_IFTYPE_AP) + sdata->vif.type == NL80211_IFTYPE_AP) { + if (rx->sta && + !test_and_set_sta_flag(rx->sta, WLAN_STA_4ADDR_EVENT)) + cfg80211_rx_unexpected_4addr_frame( + rx->sdata->dev, rx->sta->sta.addr, GFP_ATOMIC); return RX_DROP_MONITOR; + } err = __ieee80211_data_to_8023(rx, &port_control); if (unlikely(err)) @@ -2170,6 +2198,18 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx) if (!ieee80211_is_mgmt(mgmt->frame_control)) return RX_DROP_MONITOR; + if (rx->sdata->vif.type == NL80211_IFTYPE_AP && + ieee80211_is_beacon(mgmt->frame_control) && + !(rx->flags & IEEE80211_RX_BEACON_REPORTED)) { + struct ieee80211_rx_status *status; + + status = IEEE80211_SKB_RXCB(rx->skb); + cfg80211_report_obss_beacon(rx->local->hw.wiphy, + rx->skb->data, rx->skb->len, + status->freq, GFP_ATOMIC); + rx->flags |= IEEE80211_RX_BEACON_REPORTED; + } + if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) return RX_DROP_MONITOR; @@ -2203,13 +2243,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) switch (mgmt->u.action.category) { case WLAN_CATEGORY_BACK: - /* - * The aggregation code is not prepared to handle - * anything but STA/AP due to the BSSID handling; - * IBSS could work in the code but isn't supported - * by drivers or the standard. - */ if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT && sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_AP) break; @@ -2489,6 +2524,10 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx, goto out_free_skb; rx->flags |= IEEE80211_RX_CMNTR; + /* If there are no cooked monitor interfaces, just free the SKB */ + if (!local->cooked_mntrs) + goto out_free_skb; + if (skb_headroom(skb) < sizeof(*rthdr) && pskb_expand_head(skb, sizeof(*rthdr), 0, GFP_ATOMIC)) goto out_free_skb; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index ce962d2c8782..8eaa746ec7a2 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1354,12 +1354,12 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, * Use MoreData flag to indicate whether there are * more buffered frames for this STA */ - if (!more_data) - hdr->frame_control &= - cpu_to_le16(~IEEE80211_FCTL_MOREDATA); - else + if (more_data || !skb_queue_empty(&frames)) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + else + hdr->frame_control &= + cpu_to_le16(~IEEE80211_FCTL_MOREDATA); if (ieee80211_is_data_qos(hdr->frame_control) || ieee80211_is_qos_nullfunc(hdr->frame_control)) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 8c8ce05ad26f..c5923ab8a070 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -52,6 +52,7 @@ * unblocks the station. * @WLAN_STA_SP: Station is in a service period, so don't try to * reply to other uAPSD trigger frames or PS-Poll. + * @WLAN_STA_4ADDR_EVENT: 4-addr event was already sent for this frame. */ enum ieee80211_sta_info_flags { WLAN_STA_AUTH, @@ -71,6 +72,7 @@ enum ieee80211_sta_info_flags { WLAN_STA_TDLS_PEER_AUTH, WLAN_STA_UAPSD, WLAN_STA_SP, + WLAN_STA_4ADDR_EVENT, }; #define STA_TID_NUM 16 @@ -390,6 +392,12 @@ static inline int test_and_clear_sta_flag(struct sta_info *sta, return test_and_clear_bit(flag, &sta->_flags); } +static inline int test_and_set_sta_flag(struct sta_info *sta, + enum ieee80211_sta_info_flags flag) +{ + return test_and_set_bit(flag, &sta->_flags); +} + void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, struct tid_ampdu_tx *tid_tx); diff --git a/net/mac80211/status.c b/net/mac80211/status.c index df643cedf9b9..83b800d17a9a 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -516,27 +516,54 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) } if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { - struct ieee80211_work *wk; u64 cookie = (unsigned long)skb; - rcu_read_lock(); - list_for_each_entry_rcu(wk, &local->work_list, list) { - if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX) - continue; - if (wk->offchan_tx.frame != skb) - continue; - wk->offchan_tx.status = true; - break; - } - rcu_read_unlock(); - if (local->hw_roc_skb_for_status == skb) { - cookie = local->hw_roc_cookie ^ 2; - local->hw_roc_skb_for_status = NULL; + if (ieee80211_is_nullfunc(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control)) { + bool acked = info->flags & IEEE80211_TX_STAT_ACK; + cfg80211_probe_status(skb->dev, hdr->addr1, + cookie, acked, GFP_ATOMIC); + } else { + struct ieee80211_work *wk; + + rcu_read_lock(); + list_for_each_entry_rcu(wk, &local->work_list, list) { + if (wk->type != IEEE80211_WORK_OFFCHANNEL_TX) + continue; + if (wk->offchan_tx.frame != skb) + continue; + wk->offchan_tx.status = true; + break; + } + rcu_read_unlock(); + if (local->hw_roc_skb_for_status == skb) { + cookie = local->hw_roc_cookie ^ 2; + local->hw_roc_skb_for_status = NULL; + } + + cfg80211_mgmt_tx_status( + skb->dev, cookie, skb->data, skb->len, + !!(info->flags & IEEE80211_TX_STAT_ACK), + GFP_ATOMIC); } + } - cfg80211_mgmt_tx_status( - skb->dev, cookie, skb->data, skb->len, - !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); + if (unlikely(info->ack_frame_id)) { + struct sk_buff *ack_skb; + unsigned long flags; + + spin_lock_irqsave(&local->ack_status_lock, flags); + ack_skb = idr_find(&local->ack_status_frames, + info->ack_frame_id); + if (ack_skb) + idr_remove(&local->ack_status_frames, + info->ack_frame_id); + spin_unlock_irqrestore(&local->ack_status_lock, flags); + + /* consumes ack_skb */ + if (ack_skb) + skb_complete_wifi_ack(ack_skb, + info->flags & IEEE80211_TX_STAT_ACK); } /* this was a transmitted frame, but now we want to reuse it */ @@ -609,3 +636,29 @@ void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets) num_packets, GFP_ATOMIC); } EXPORT_SYMBOL(ieee80211_report_low_ack); + +void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (unlikely(info->ack_frame_id)) { + struct sk_buff *ack_skb; + unsigned long flags; + + spin_lock_irqsave(&local->ack_status_lock, flags); + ack_skb = idr_find(&local->ack_status_frames, + info->ack_frame_id); + if (ack_skb) + idr_remove(&local->ack_status_frames, + info->ack_frame_id); + spin_unlock_irqrestore(&local->ack_status_lock, flags); + + /* consumes ack_skb */ + if (ack_skb) + dev_kfree_skb_any(ack_skb); + } + + dev_kfree_skb_any(skb); +} +EXPORT_SYMBOL(ieee80211_free_txskb); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 48bbb96d8edb..ab6cb56bc74d 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1684,8 +1684,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, int nh_pos, h_pos; struct sta_info *sta = NULL; bool wme_sta = false, authorized = false, tdls_auth = false; - struct sk_buff *tmp_skb; bool tdls_direct = false; + bool multicast; + u32 info_flags = 0; + u16 info_id = 0; if (unlikely(skb->len < ETH_HLEN)) { ret = NETDEV_TX_OK; @@ -1872,7 +1874,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, * if it is a multicast address (which can only happen * in AP mode) */ - if (!is_multicast_ether_addr(hdr.addr1)) { + multicast = is_multicast_ether_addr(hdr.addr1); + if (!multicast) { rcu_read_lock(); sta = sta_info_get(sdata, hdr.addr1); if (sta) { @@ -1913,11 +1916,54 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, goto fail; } + if (unlikely(!multicast && skb->sk && + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) { + struct sk_buff *orig_skb = skb; + + skb = skb_clone(skb, GFP_ATOMIC); + if (skb) { + unsigned long flags; + int id, r; + + spin_lock_irqsave(&local->ack_status_lock, flags); + r = idr_get_new_above(&local->ack_status_frames, + orig_skb, 1, &id); + if (r == -EAGAIN) { + idr_pre_get(&local->ack_status_frames, + GFP_ATOMIC); + r = idr_get_new_above(&local->ack_status_frames, + orig_skb, 1, &id); + } + if (WARN_ON(!id) || id > 0xffff) { + idr_remove(&local->ack_status_frames, id); + r = -ERANGE; + } + spin_unlock_irqrestore(&local->ack_status_lock, flags); + + if (!r) { + info_id = id; + info_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + } else if (skb_shared(skb)) { + kfree_skb(orig_skb); + } else { + kfree_skb(skb); + skb = orig_skb; + } + } else { + /* couldn't clone -- lose tx status ... */ + skb = orig_skb; + } + } + /* * If the skb is shared we need to obtain our own copy. */ if (skb_shared(skb)) { - tmp_skb = skb; + struct sk_buff *tmp_skb = skb; + + /* can't happen -- skb is a clone if info_id != 0 */ + WARN_ON(info_id); + skb = skb_clone(skb, GFP_ATOMIC); kfree_skb(tmp_skb); @@ -2018,6 +2064,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, memset(info, 0, sizeof(*info)); dev->trans_start = jiffies; + + info->flags = info_flags; + info->ack_frame_id = info_id; + ieee80211_xmit(sdata, skb); return NETDEV_TX_OK; @@ -2278,22 +2328,31 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, } else if (ieee80211_vif_is_mesh(&sdata->vif)) { struct ieee80211_mgmt *mgmt; u8 *pos; + int hdr_len = offsetof(struct ieee80211_mgmt, u.beacon) + + sizeof(mgmt->u.beacon); #ifdef CONFIG_MAC80211_MESH if (!sdata->u.mesh.mesh_id_len) goto out; #endif - /* headroom, head length, tail length and maximum TIM length */ - skb = dev_alloc_skb(local->tx_headroom + 400 + - sdata->u.mesh.ie_len); + skb = dev_alloc_skb(local->tx_headroom + + hdr_len + + 2 + /* NULL SSID */ + 2 + 8 + /* supported rates */ + 2 + 3 + /* DS params */ + 2 + (IEEE80211_MAX_SUPP_RATES - 8) + + 2 + sizeof(struct ieee80211_ht_cap) + + 2 + sizeof(struct ieee80211_ht_info) + + 2 + sdata->u.mesh.mesh_id_len + + 2 + sizeof(struct ieee80211_meshconf_ie) + + sdata->u.mesh.ie_len); if (!skb) goto out; skb_reserve(skb, local->hw.extra_tx_headroom); - mgmt = (struct ieee80211_mgmt *) - skb_put(skb, 24 + sizeof(mgmt->u.beacon)); - memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon)); + mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len); + memset(mgmt, 0, hdr_len); mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_BEACON); memset(mgmt->da, 0xff, ETH_ALEN); @@ -2312,6 +2371,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, mesh_add_ds_params_ie(skb, sdata) || ieee80211_add_ext_srates_ie(&sdata->vif, skb) || mesh_add_rsn_ie(skb, sdata) || + mesh_add_ht_cap_ie(skb, sdata) || + mesh_add_ht_info_ie(skb, sdata) || mesh_add_meshid_ie(skb, sdata) || mesh_add_meshconf_ie(skb, sdata) || mesh_add_vendor_ies(skb, sdata)) { diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7439d26bf5f9..98ca5479324b 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -811,23 +811,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, offset = noffset; } - if (sband->ht_cap.ht_supported) { - u16 cap = sband->ht_cap.cap; - __le16 tmp; - - *pos++ = WLAN_EID_HT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_ht_cap); - memset(pos, 0, sizeof(struct ieee80211_ht_cap)); - tmp = cpu_to_le16(cap); - memcpy(pos, &tmp, sizeof(u16)); - pos += sizeof(u16); - *pos++ = sband->ht_cap.ampdu_factor | - (sband->ht_cap.ampdu_density << - IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); - memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); - pos += sizeof(sband->ht_cap.mcs); - pos += 2 + 4 + 1; /* ext info, BF cap, antsel */ - } + if (sband->ht_cap.ht_supported) + pos = ieee80211_ie_build_ht_cap(pos, sband, sband->ht_cap.cap); /* * If adding more here, adjust code in main.c @@ -1021,7 +1006,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_MONITOR && ieee80211_sdata_running(sdata)) - res = drv_add_interface(local, &sdata->vif); + res = drv_add_interface(local, sdata); } /* add STAs back */ @@ -1072,7 +1057,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) BSS_CHANGED_BEACON_INT | BSS_CHANGED_BSSID | BSS_CHANGED_CQM | - BSS_CHANGED_QOS; + BSS_CHANGED_QOS | + BSS_CHANGED_IDLE; switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: @@ -1362,6 +1348,103 @@ void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif) } EXPORT_SYMBOL(ieee80211_disable_rssi_reports); +u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_supported_band *sband, + u16 cap) +{ + __le16 tmp; + + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + memset(pos, 0, sizeof(struct ieee80211_ht_cap)); + + /* capability flags */ + tmp = cpu_to_le16(cap); + memcpy(pos, &tmp, sizeof(u16)); + pos += sizeof(u16); + + /* AMPDU parameters */ + *pos++ = sband->ht_cap.ampdu_factor | + (sband->ht_cap.ampdu_density << + IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); + + /* MCS set */ + memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); + pos += sizeof(sband->ht_cap.mcs); + + /* extended capabilities */ + pos += sizeof(__le16); + + /* BF capabilities */ + pos += sizeof(__le32); + + /* antenna selection */ + pos += sizeof(u8); + + return pos; +} + +u8 *ieee80211_ie_build_ht_info(u8 *pos, + struct ieee80211_sta_ht_cap *ht_cap, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type) +{ + struct ieee80211_ht_info *ht_info; + /* Build HT Information */ + *pos++ = WLAN_EID_HT_INFORMATION; + *pos++ = sizeof(struct ieee80211_ht_info); + ht_info = (struct ieee80211_ht_info *)pos; + ht_info->control_chan = + ieee80211_frequency_to_channel(channel->center_freq); + switch (channel_type) { + case NL80211_CHAN_HT40MINUS: + ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + break; + case NL80211_CHAN_HT40PLUS: + ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + break; + case NL80211_CHAN_HT20: + default: + ht_info->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; + break; + } + if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) + ht_info->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; + ht_info->operation_mode = 0x0000; + ht_info->stbc_param = 0x0000; + + /* It seems that Basic MCS set and Supported MCS set + are identical for the first 10 bytes */ + memset(&ht_info->basic_set, 0, 16); + memcpy(&ht_info->basic_set, &ht_cap->mcs, 10); + + return pos + sizeof(struct ieee80211_ht_info); +} + +enum nl80211_channel_type +ieee80211_ht_info_to_channel_type(struct ieee80211_ht_info *ht_info) +{ + enum nl80211_channel_type channel_type; + + if (!ht_info) + return NL80211_CHAN_NO_HT; + + switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { + case IEEE80211_HT_PARAM_CHA_SEC_NONE: + channel_type = NL80211_CHAN_HT20; + break; + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + channel_type = NL80211_CHAN_HT40PLUS; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + channel_type = NL80211_CHAN_HT40MINUS; + break; + default: + channel_type = NL80211_CHAN_NO_HT; + } + + return channel_type; +} + int ieee80211_add_srates_ie(struct ieee80211_vif *vif, struct sk_buff *skb) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index fd52e695c071..43327115b490 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -83,7 +83,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, break; #ifdef CONFIG_MAC80211_MESH case NL80211_IFTYPE_MESH_POINT: - ra = skb->data; + qos = true; break; #endif case NL80211_IFTYPE_STATION: @@ -143,11 +143,15 @@ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, /* Fill in the QoS header if there is one. */ if (ieee80211_is_data_qos(hdr->frame_control)) { u8 *p = ieee80211_get_qos_ctl(hdr); - u8 ack_policy = 0, tid; + u8 ack_policy, tid; tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; - if (unlikely(sdata->local->wifi_wme_noack_test)) + /* preserve EOSP bit */ + ack_policy = *p & IEEE80211_QOS_CTL_EOSP; + + if (unlikely(sdata->local->wifi_wme_noack_test) || + is_multicast_ether_addr(hdr->addr1)) ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK; /* qos header is 2 bytes */ *p++ = ack_policy | tid; diff --git a/net/mac80211/work.c b/net/mac80211/work.c index 6c53b6d1002b..3dd5a89e99a7 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c @@ -103,7 +103,6 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie, u8 *pos; u32 flags = channel->flags; u16 cap = sband->ht_cap.cap; - __le16 tmp; if (!sband->ht_cap.ht_supported) return; @@ -154,34 +153,8 @@ static void ieee80211_add_ht_ie(struct sk_buff *skb, const u8 *ht_info_ie, } /* reserve and fill IE */ - pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); - *pos++ = WLAN_EID_HT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_ht_cap); - memset(pos, 0, sizeof(struct ieee80211_ht_cap)); - - /* capability flags */ - tmp = cpu_to_le16(cap); - memcpy(pos, &tmp, sizeof(u16)); - pos += sizeof(u16); - - /* AMPDU parameters */ - *pos++ = sband->ht_cap.ampdu_factor | - (sband->ht_cap.ampdu_density << - IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT); - - /* MCS set */ - memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); - pos += sizeof(sband->ht_cap.mcs); - - /* extended capabilities */ - pos += sizeof(__le16); - - /* BF capabilities */ - pos += sizeof(__le32); - - /* antenna selection */ - pos += sizeof(u8); + ieee80211_ie_build_ht_cap(pos, sband, cap); } static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, @@ -969,10 +942,9 @@ static void ieee80211_work_work(struct work_struct *work) } if (!started && !local->tmp_channel) { - bool on_oper_chan; - bool tmp_chan_changed = false; - bool on_oper_chan2; + bool on_oper_chan, on_oper_chan2; enum nl80211_channel_type wk_ct; + on_oper_chan = ieee80211_cfg_on_oper_channel(local); /* Work with existing channel type if possible. */ @@ -981,11 +953,6 @@ static void ieee80211_work_work(struct work_struct *work) wk_ct = ieee80211_calc_ct(wk->chan_type, local->hw.conf.channel_type); - if (local->tmp_channel) - if ((local->tmp_channel != wk->chan) || - (local->tmp_channel_type != wk_ct)) - tmp_chan_changed = true; - local->tmp_channel = wk->chan; local->tmp_channel_type = wk_ct; /* @@ -1008,12 +975,7 @@ static void ieee80211_work_work(struct work_struct *work) true, false); } - } else if (tmp_chan_changed) - /* Still off-channel, but on some other - * channel, so update hardware. - * PS should already be off-channel. - */ - ieee80211_hw_config(local, 0); + } started = true; wk->timeout = jiffies; diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index f614ce7bb6e3..13efab5bf12a 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -390,7 +390,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) u8 scratch[6 * AES_BLOCK_SIZE]; if (info->control.hw_key && - !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV)) { + !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && + !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { /* * hwaccel has no need for preallocated room for CCMP * header or MIC fields @@ -412,6 +413,11 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) pos = skb_push(skb, CCMP_HDR_LEN); memmove(pos, pos + CCMP_HDR_LEN, hdrlen); + + /* the HW only needs room for the IV, but not the actual IV */ + if (info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) + return 0; + hdr = (struct ieee80211_hdr *) pos; pos += hdrlen; diff --git a/net/rfkill/core.c b/net/rfkill/core.c index 5be19575c340..354760ebbbd2 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -644,7 +644,7 @@ static ssize_t rfkill_soft_store(struct device *dev, if (!capable(CAP_NET_ADMIN)) return -EPERM; - err = strict_strtoul(buf, 0, &state); + err = kstrtoul(buf, 0, &state); if (err) return err; @@ -688,7 +688,7 @@ static ssize_t rfkill_state_store(struct device *dev, if (!capable(CAP_NET_ADMIN)) return -EPERM; - err = strict_strtoul(buf, 0, &state); + err = kstrtoul(buf, 0, &state); if (err) return err; diff --git a/net/socket.c b/net/socket.c index 2877647f347b..425ef4270460 100644 --- a/net/socket.c +++ b/net/socket.c @@ -538,6 +538,8 @@ int sock_tx_timestamp(struct sock *sk, __u8 *tx_flags) *tx_flags |= SKBTX_HW_TSTAMP; if (sock_flag(sk, SOCK_TIMESTAMPING_TX_SOFTWARE)) *tx_flags |= SKBTX_SW_TSTAMP; + if (sock_flag(sk, SOCK_WIFI_STATUS)) + *tx_flags |= SKBTX_WIFI_STATUS; return 0; } EXPORT_SYMBOL(sock_tx_timestamp); @@ -674,6 +676,22 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, } EXPORT_SYMBOL_GPL(__sock_recv_timestamp); +void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk, + struct sk_buff *skb) +{ + int ack; + + if (!sock_flag(sk, SOCK_WIFI_STATUS)) + return; + if (!skb->wifi_acked_valid) + return; + + ack = skb->wifi_acked; + + put_cmsg(msg, SOL_SOCKET, SCM_WIFI_STATUS, sizeof(ack), &ack); +} +EXPORT_SYMBOL_GPL(__sock_recv_wifi_status); + static inline void sock_recv_drops(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) { diff --git a/net/wireless/core.c b/net/wireless/core.c index 220f3bd176f8..ccdfed897651 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -492,6 +492,10 @@ int wiphy_register(struct wiphy *wiphy) !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) return -EINVAL; + if (WARN_ON(wiphy->ap_sme_capa && + !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) + return -EINVAL; + if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) return -EINVAL; diff --git a/net/wireless/core.h b/net/wireless/core.h index b9ec3061ed72..1c7d4df5418c 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -54,6 +54,8 @@ struct cfg80211_registered_device { int opencount; /* also protected by devlist_mtx */ wait_queue_head_t dev_wait; + u32 ap_beacons_nlpid; + /* BSSes/scanning */ spinlock_t bss_lock; struct list_head bss_list; @@ -376,7 +378,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, - u64 *cookie); + bool dont_wait_for_ack, u64 *cookie); /* SME */ int __cfg80211_connect(struct cfg80211_registered_device *rdev, diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 21fc9702f81c..6c1bafd508c8 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -879,6 +879,9 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid) } spin_unlock_bh(&wdev->mgmt_registrations_lock); + + if (nlpid == wdev->ap_unexpected_nlpid) + wdev->ap_unexpected_nlpid = 0; } void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) @@ -901,7 +904,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, bool no_cck, - u64 *cookie) + bool dont_wait_for_ack, u64 *cookie) { struct wireless_dev *wdev = dev->ieee80211_ptr; const struct ieee80211_mgmt *mgmt; @@ -992,7 +995,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, /* Transmit the Action frame as requested by user space */ return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, offchan, channel_type, channel_type_valid, - wait, buf, len, no_cck, cookie); + wait, buf, len, no_cck, dont_wait_for_ack, + cookie); } bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, @@ -1107,3 +1111,30 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); } EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); + +bool cfg80211_rx_spurious_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO)) + return false; + + return nl80211_unexpected_frame(dev, addr, gfp); +} +EXPORT_SYMBOL(cfg80211_rx_spurious_frame); + +bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO && + wdev->iftype != NL80211_IFTYPE_AP_VLAN)) + return false; + + return nl80211_unexpected_4addr_frame(dev, addr, gfp); +} +EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 48260c2d092a..864fcb6f217e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -98,7 +98,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 }, [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 }, [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG }, - [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 }, + [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 }, [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 }, @@ -132,8 +132,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED }, [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG }, - [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, - .len = NL80211_HT_CAPABILITY_LEN }, + [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN }, [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, [NL80211_ATTR_IE] = { .type = NLA_BINARY, @@ -197,6 +196,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 }, [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG }, [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG }, + [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -204,7 +204,7 @@ static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, [NL80211_KEY_IDX] = { .type = NLA_U8 }, [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, - [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 }, + [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG }, [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, [NL80211_KEY_TYPE] = { .type = NLA_U32 }, @@ -891,6 +891,11 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, } if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) CMD(sched_scan_start, START_SCHED_SCAN); + CMD(probe_client, PROBE_CLIENT); + if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_REGISTER_BEACONS); + } #undef CMD @@ -1008,6 +1013,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (nl80211_put_iface_combinations(&dev->wiphy, msg)) goto nla_put_failure; + if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) + NLA_PUT_U32(msg, NL80211_ATTR_DEVICE_AP_SME, + dev->wiphy.ap_sme_capa); + + NLA_PUT_U32(msg, NL80211_ATTR_FEATURE_FLAGS, dev->wiphy.features); + return genlmsg_end(msg, hdr); nla_put_failure: @@ -1253,6 +1264,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) goto bad_res; } + if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { + result = -EINVAL; + goto bad_res; + } + nla_for_each_nested(nl_txq_params, info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], rem_txq_params) { @@ -5268,10 +5285,11 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) int err; void *hdr; u64 cookie; - struct sk_buff *msg; + struct sk_buff *msg = NULL; unsigned int wait = 0; - bool offchan; - bool no_cck; + bool offchan, no_cck, dont_wait_for_ack; + + dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK]; if (!info->attrs[NL80211_ATTR_FRAME] || !info->attrs[NL80211_ATTR_WIPHY_FREQ]) @@ -5315,29 +5333,36 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) if (chan == NULL) return -EINVAL; - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) - return -ENOMEM; + if (!dont_wait_for_ack) { + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; - hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_FRAME); + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_FRAME); - if (IS_ERR(hdr)) { - err = PTR_ERR(hdr); - goto free_msg; + if (IS_ERR(hdr)) { + err = PTR_ERR(hdr); + goto free_msg; + } } + err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type, channel_type_valid, wait, nla_data(info->attrs[NL80211_ATTR_FRAME]), nla_len(info->attrs[NL80211_ATTR_FRAME]), - no_cck, &cookie); + no_cck, dont_wait_for_ack, &cookie); if (err) goto free_msg; - NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); + if (msg) { + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); - genlmsg_end(msg, hdr); - return genlmsg_reply(msg, info); + genlmsg_end(msg, hdr); + return genlmsg_reply(msg, info); + } + + return 0; nla_put_failure: err = -ENOBUFS; @@ -5827,6 +5852,91 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) return err; } +static int nl80211_register_unexpected_frame(struct sk_buff *skb, + struct genl_info *info) +{ + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + + if (wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO) + return -EINVAL; + + if (wdev->ap_unexpected_nlpid) + return -EBUSY; + + wdev->ap_unexpected_nlpid = info->snd_pid; + return 0; +} + +static int nl80211_probe_client(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]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct sk_buff *msg; + void *hdr; + const u8 *addr; + u64 cookie; + int err; + + if (wdev->iftype != NL80211_IFTYPE_AP && + wdev->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + if (!info->attrs[NL80211_ATTR_MAC]) + return -EINVAL; + + if (!rdev->ops->probe_client) + return -EOPNOTSUPP; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_PROBE_CLIENT); + + if (IS_ERR(hdr)) { + err = PTR_ERR(hdr); + goto free_msg; + } + + addr = nla_data(info->attrs[NL80211_ATTR_MAC]); + + err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie); + if (err) + goto free_msg; + + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); + + genlmsg_end(msg, hdr); + + return genlmsg_reply(msg, info); + + nla_put_failure: + err = -ENOBUFS; + free_msg: + nlmsg_free(msg); + return err; +} + +static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + + if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS)) + return -EOPNOTSUPP; + + if (rdev->ap_beacons_nlpid) + return -EBUSY; + + rdev->ap_beacons_nlpid = info->snd_pid; + + return 0; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -6382,6 +6492,30 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_UNEXPECTED_FRAME, + .doit = nl80211_register_unexpected_frame, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_PROBE_CLIENT, + .doit = nl80211_probe_client, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_REGISTER_BEACONS, + .doit = nl80211_register_beacons, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -6634,10 +6768,7 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) if (wiphy_idx_valid(request->wiphy_idx)) NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); rcu_read_lock(); genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, @@ -6673,10 +6804,7 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); @@ -6757,10 +6885,7 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); @@ -6816,10 +6941,7 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, if (resp_ie) NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); @@ -6857,10 +6979,7 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, if (resp_ie) NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); @@ -6898,10 +7017,7 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, if (ie) NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL); @@ -6934,10 +7050,7 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); @@ -6972,10 +7085,7 @@ void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev, if (ie_len && ie) NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); @@ -7014,10 +7124,7 @@ void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, if (tsc) NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); @@ -7068,10 +7175,7 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy, goto nla_put_failure; nla_nest_end(msg, nl_freq); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); rcu_read_lock(); genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, @@ -7114,10 +7218,7 @@ static void nl80211_send_remain_on_chan_event( if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL) NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); @@ -7188,10 +7289,7 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); @@ -7202,13 +7300,68 @@ void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd, + const u8 *addr, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + int err; + u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid); + + if (!nlpid) + return false; + + msg = nlmsg_new(100, gfp); + if (!msg) + return true; + + hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); + if (!hdr) { + nlmsg_free(msg); + return true; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + err = genlmsg_end(msg, hdr); + if (err < 0) { + nlmsg_free(msg); + return true; + } + + genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); + return true; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); + return true; +} + +bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp) +{ + return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME, + addr, gfp); +} + +bool nl80211_unexpected_4addr_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp) +{ + return __nl80211_unexpected_frame(dev, + NL80211_CMD_UNEXPECTED_4ADDR_FRAME, + addr, gfp); +} + int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, struct net_device *netdev, u32 nlpid, int freq, const u8 *buf, size_t len, gfp_t gfp) { struct sk_buff *msg; void *hdr; - int err; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) @@ -7225,16 +7378,9 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf); - err = genlmsg_end(msg, hdr); - if (err < 0) { - nlmsg_free(msg); - return err; - } + genlmsg_end(msg, hdr); - err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); - if (err < 0) - return err; - return 0; + return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); nla_put_failure: genlmsg_cancel(msg, hdr); @@ -7267,10 +7413,7 @@ void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev, if (ack) NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); return; @@ -7312,10 +7455,7 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, nla_nest_end(msg, pinfoattr); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); @@ -7357,10 +7497,7 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, nla_nest_end(msg, rekey_attr); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); @@ -7403,10 +7540,7 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, nla_nest_end(msg, attr); - if (genlmsg_end(msg, hdr) < 0) { - nlmsg_free(msg); - return; - } + genlmsg_end(msg, hdr); genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, nl80211_mlme_mcgrp.id, gfp); @@ -7448,7 +7582,45 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, nla_nest_end(msg, pinfoattr); - if (genlmsg_end(msg, hdr) < 0) { + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + +void cfg80211_probe_status(struct net_device *dev, const u8 *addr, + u64 cookie, bool acked, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + int err; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); + if (acked) + NLA_PUT_FLAG(msg, NL80211_ATTR_ACK); + + err = genlmsg_end(msg, hdr); + if (err < 0) { nlmsg_free(msg); return; } @@ -7461,6 +7633,45 @@ nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, genlmsg_cancel(msg, hdr); nlmsg_free(msg); } +EXPORT_SYMBOL(cfg80211_probe_status); + +void cfg80211_report_obss_beacon(struct wiphy *wiphy, + const u8 *frame, size_t len, + int freq, gfp_t gfp) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + struct sk_buff *msg; + void *hdr; + u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid); + + if (!nlpid) + return; + + msg = nlmsg_new(len + 100, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + if (freq) + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq); + NLA_PUT(msg, NL80211_ATTR_FRAME, len, frame); + + genlmsg_end(msg, hdr); + + genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_report_obss_beacon); static int nl80211_netlink_notify(struct notifier_block * nb, unsigned long state, @@ -7475,9 +7686,12 @@ static int nl80211_netlink_notify(struct notifier_block * nb, rcu_read_lock(); - list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) + list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { list_for_each_entry_rcu(wdev, &rdev->netdev_list, list) cfg80211_mlme_unregister_socket(wdev, notify->pid); + if (rdev->ap_beacons_nlpid == notify->pid) + rdev->ap_beacons_nlpid = 0; + } rcu_read_unlock(); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index f24a1fbeaf19..12bf4d185abe 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -117,4 +117,9 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, int index, const u8 *bssid, bool preauth, gfp_t gfp); +bool nl80211_unexpected_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp); +bool nl80211_unexpected_4addr_frame(struct net_device *dev, + const u8 *addr, gfp_t gfp); + #endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 0fb142410404..dc23b31594e0 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -259,17 +259,20 @@ static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) { const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); const u8 *ie2 = cfg80211_find_ie(num, ies2, len2); - int r; + /* equal if both missing */ if (!ie1 && !ie2) return 0; - if (!ie1 || !ie2) + /* sort missing IE before (left of) present IE */ + if (!ie1) return -1; + if (!ie2) + return 1; - r = memcmp(ie1 + 2, ie2 + 2, min(ie1[1], ie2[1])); - if (r == 0 && ie1[1] != ie2[1]) + /* sort by length first, then by contents */ + if (ie1[1] != ie2[1]) return ie2[1] - ie1[1]; - return r; + return memcmp(ie1 + 2, ie2 + 2, ie1[1]); } static bool is_bss(struct cfg80211_bss *a, |