Merge Google's Android tree into msm-3.0 This consists of the following conflicts and 42 commits: Conflicts: drivers/mmc/core/bus.c drivers/rtc/class.c drivers/usb/gadget/android.c Most of the conflicts stem from the changes made to the local msm-3.0 branch. commit1f8c5cecfeAuthor: Heiko Stuebner <heiko@sntech.de> Date: Wed Feb 1 10:33:01 2012 -0800 Input: evdev - fix variable initialisation Commit 509f87c5f564 (evdev - do not block waiting for an event if fd is nonblock) created a code path were it was possible to use retval uninitialized. This could lead to the xorg evdev input driver getting corrupt data and refusing to work with log messages like AUO-Pixcir touchscreen: Read error: Success sg060_keys: Read error: Success AUO-Pixcir touchscreen: Read error: Success sg060_keys: Read error: Success (for drivers auo-pixcir-ts and gpio-keys). Signed-off-by: Heiko Stuebner <heiko@sntech.de> drivers/input/evdev.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commit4dc43d7079Author: Arve Hjønnevåg <arve@android.com> Date: Fri Oct 17 15:20:55 2008 -0700 Input: evdev - Add ioctl to block suspend while event queue is not empty. Add an ioctl, EVIOCSSUSPENDBLOCK, to enable a wakelock that will block suspend while the event queue is not empty. This allows userspace code to process input events while the device appears to be asleep. The current code holds the wakelock for up 5 seconds for every input device and client. This can prevent suspend if sensor with a high data rate is active, even when that sensor is not capable of waking the device once it is suspended. Change-Id: I624d66ef30a0b3abb543685c343382b8419b42b9 Signed-off-by: Arve Hjønnevåg <arve@android.com> drivers/input/evdev.c | 53 +++++++++++++++++++++++++++++++++++++++++++----- include/linux/input.h | 3 ++ 2 files changed, 50 insertions(+), 6 deletions(-) commit7cc846069aAuthor: Arve Hjønnevåg <arve@android.com> Date: Mon Jan 23 17:15:45 2012 -0800 Input: evdev - Don't hold wakelock when no data is available to user-space If there is no SYN_REPORT event in the buffer the buffer data is invisible to user-space. The wakelock should not be held in this case. Change-Id: Idae890ff0da8eb46a2cfce61a95b3a97252551ad Signed-off-by: Arve Hjønnevåg <arve@android.com> drivers/input/evdev.c | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) commit0e80804a2eAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Jan 31 11:06:23 2012 -0800 net: wireless: bcmdhd: Increase pm_notify callback priority Make pm_notify callback to be called the first on suspend/resume path to ensure it will always be called. Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_linux.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commitdfc896e1c8Author: Dmitry Shmidt <dimitrysh@google.com> Date: Mon Jan 30 15:43:31 2012 -0800 net: wireless: bcmdhd: Fix crash on dhdsdio_probe_attach() failure Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_linux.c | 17 ++++++++++++++--- drivers/net/wireless/bcmdhd/dhd_sdio.c | 8 +++++++- 2 files changed, 21 insertions(+), 4 deletions(-) commit256a6b23beAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Mon Jan 30 13:03:19 2012 -0800 net: wireless: bcmdhd: Daemonize wl_event_handler Daemonizing makes thread (besides other things) NON-FREEZABLE, and it will not get fake signal on suspend to quicl down_interruptible() Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_linux.c | 13 ------------- drivers/net/wireless/bcmdhd/include/linuxver.h | 12 ++++++++++++ drivers/net/wireless/bcmdhd/wl_cfg80211.c | 3 +++ 3 files changed, 15 insertions(+), 13 deletions(-) commitff93146589Author: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Jan 24 13:59:40 2012 -0800 net: wireless: bcmdhd: Update to Version 5.90.195.23 - WFD fixes Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_cfg80211.c | 269 ------------------------- drivers/net/wireless/bcmdhd/dhd_linux.c | 2 +- drivers/net/wireless/bcmdhd/dhd_sdio.c | 8 +- drivers/net/wireless/bcmdhd/include/epivers.h | 8 +- drivers/net/wireless/bcmdhd/siutils.c | 5 + drivers/net/wireless/bcmdhd/wl_cfg80211.c | 223 +++++++++++++++------ drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 175 ++++++++++++++++- drivers/net/wireless/bcmdhd/wl_cfgp2p.h | 37 ++-- 8 files changed, 368 insertions(+), 359 deletions(-) commit96034c2006Author: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Jan 24 13:55:00 2012 -0800 net: wireless: bcmdhd: Update to Version 5.90.195.22 - Disable Ad-hoc support for cfg80211 - dhd_linux.c: Fix incorrect pid check - Merge Android changes from Android tree Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/bcmsdh_linux.c | 24 ++------------------ drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c | 25 +++++++++++++++------ drivers/net/wireless/bcmdhd/dhd_linux.c | 10 ++++---- drivers/net/wireless/bcmdhd/dhd_sdio.c | 11 +++++++-- drivers/net/wireless/bcmdhd/hndpmu.c | 16 ++++++++++++++ drivers/net/wireless/bcmdhd/include/bcmdevs.h | 2 + drivers/net/wireless/bcmdhd/include/epivers.h | 8 +++--- drivers/net/wireless/bcmdhd/siutils.c | 3 ++ drivers/net/wireless/bcmdhd/wl_cfg80211.c | 11 ++++++++- 9 files changed, 68 insertions(+), 42 deletions(-) commit494661a1acAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Jan 24 13:47:47 2012 -0800 net: wireless: bcmdhd: Update to Version 5.90.195.19 - Add WFD changes - Add extra locking for internal ioctl operations Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/Makefile | 4 +- drivers/net/wireless/bcmdhd/bcmsdh_linux.c | 25 +- drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c | 4 +- drivers/net/wireless/bcmdhd/dhd.h | 10 +- drivers/net/wireless/bcmdhd/dhd_cdc.c | 7 +- drivers/net/wireless/bcmdhd/dhd_cfg80211.c | 862 ++++++++ drivers/net/wireless/bcmdhd/dhd_cfg80211.h | 42 + drivers/net/wireless/bcmdhd/dhd_common.c | 7 +- drivers/net/wireless/bcmdhd/dhd_linux.c | 103 +- drivers/net/wireless/bcmdhd/dhd_linux_mon.c | 409 ---- drivers/net/wireless/bcmdhd/dhd_sdio.c | 4 +- drivers/net/wireless/bcmdhd/include/dhdioctl.h | 3 +- drivers/net/wireless/bcmdhd/include/epivers.h | 8 +- drivers/net/wireless/bcmdhd/include/proto/802.11.h | 12 +- drivers/net/wireless/bcmdhd/include/wlioctl.h | 19 +- drivers/net/wireless/bcmdhd/wl_android.c | 22 +- drivers/net/wireless/bcmdhd/wl_android.h | 2 +- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 2142 ++++++-------------- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 125 +- drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 245 ++- drivers/net/wireless/bcmdhd/wl_cfgp2p.h | 27 +- drivers/net/wireless/bcmdhd/wl_linux_mon.c | 409 ++++ drivers/net/wireless/bcmdhd/wldev_common.c | 69 +- drivers/net/wireless/bcmdhd/wldev_common.h | 20 +- 24 files changed, 2485 insertions(+), 2095 deletions(-) commit1dddb0cc0dAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Jan 24 13:37:34 2012 -0800 net: wireless: bcmdhd: Update to Version 5.90.195.15 - Add WFD concurrent mode support Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/bcmevent.c | 8 +- drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c | 12 +- drivers/net/wireless/bcmdhd/bcmutils.c | 4 +- drivers/net/wireless/bcmdhd/dhd.h | 25 +- drivers/net/wireless/bcmdhd/dhd_cdc.c | 9 +- drivers/net/wireless/bcmdhd/dhd_common.c | 4 +- drivers/net/wireless/bcmdhd/dhd_linux.c | 148 +++-- drivers/net/wireless/bcmdhd/dhd_linux_mon.c | 28 +- drivers/net/wireless/bcmdhd/dhd_sdio.c | 27 +- drivers/net/wireless/bcmdhd/dhd_wlfc.h | 12 +- drivers/net/wireless/bcmdhd/hndpmu.c | 44 +- drivers/net/wireless/bcmdhd/include/Makefile | 2 +- drivers/net/wireless/bcmdhd/include/aidmp.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmcdc.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmdefs.h | 37 +- drivers/net/wireless/bcmdhd/include/bcmdevs.h | 573 +++++++++++++- drivers/net/wireless/bcmdhd/include/bcmendian.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmpcispi.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmperf.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmsdbus.h | 10 +- drivers/net/wireless/bcmdhd/include/bcmsdh.h | 10 +- drivers/net/wireless/bcmdhd/include/bcmsdh_sdmmc.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmsdpcm.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmsdspi.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmsdstd.h | 34 +- drivers/net/wireless/bcmdhd/include/bcmspi.h | 2 +- drivers/net/wireless/bcmdhd/include/bcmutils.h | 14 +- drivers/net/wireless/bcmdhd/include/bcmwifi.h | 2 +- drivers/net/wireless/bcmdhd/include/dhdioctl.h | 2 +- drivers/net/wireless/bcmdhd/include/epivers.h | 14 +- drivers/net/wireless/bcmdhd/include/hndpmu.h | 2 +- .../net/wireless/bcmdhd/include/hndrte_armtrap.h | 2 +- drivers/net/wireless/bcmdhd/include/hndrte_cons.h | 2 +- drivers/net/wireless/bcmdhd/include/hndsoc.h | 2 +- drivers/net/wireless/bcmdhd/include/htsf.h | 2 +- drivers/net/wireless/bcmdhd/include/linux_osl.h | 2 +- drivers/net/wireless/bcmdhd/include/linuxver.h | 6 +- drivers/net/wireless/bcmdhd/include/miniopt.h | 2 +- drivers/net/wireless/bcmdhd/include/msgtrace.h | 2 +- drivers/net/wireless/bcmdhd/include/osl.h | 2 +- .../wireless/bcmdhd/include/packed_section_end.h | 2 +- .../wireless/bcmdhd/include/packed_section_start.h | 2 +- drivers/net/wireless/bcmdhd/include/pcicfg.h | 28 +- drivers/net/wireless/bcmdhd/include/proto/802.11.h | 317 +++++++- .../net/wireless/bcmdhd/include/proto/802.11_bta.h | 2 +- .../net/wireless/bcmdhd/include/proto/802.11e.h | 2 +- drivers/net/wireless/bcmdhd/include/proto/802.1d.h | 2 +- drivers/net/wireless/bcmdhd/include/proto/bcmeth.h | 2 +- .../net/wireless/bcmdhd/include/proto/bcmevent.h | 9 +- drivers/net/wireless/bcmdhd/include/proto/bcmip.h | 2 +- .../net/wireless/bcmdhd/include/proto/bt_amp_hci.h | 2 +- drivers/net/wireless/bcmdhd/include/proto/eapol.h | 2 +- .../net/wireless/bcmdhd/include/proto/ethernet.h | 3 +- drivers/net/wireless/bcmdhd/include/proto/p2p.h | 2 +- drivers/net/wireless/bcmdhd/include/proto/sdspi.h | 2 +- drivers/net/wireless/bcmdhd/include/proto/vlan.h | 2 +- drivers/net/wireless/bcmdhd/include/proto/wpa.h | 12 +- drivers/net/wireless/bcmdhd/include/sbchipc.h | 168 ++++- drivers/net/wireless/bcmdhd/include/sbconfig.h | 2 +- drivers/net/wireless/bcmdhd/include/sbhnddma.h | 6 +- drivers/net/wireless/bcmdhd/include/sbpcmcia.h | 2 +- drivers/net/wireless/bcmdhd/include/sbsdio.h | 2 +- drivers/net/wireless/bcmdhd/include/sbsdpcmdev.h | 2 +- drivers/net/wireless/bcmdhd/include/sbsocram.h | 2 +- drivers/net/wireless/bcmdhd/include/sdio.h | 5 +- drivers/net/wireless/bcmdhd/include/sdioh.h | 32 +- drivers/net/wireless/bcmdhd/include/sdiovar.h | 2 +- drivers/net/wireless/bcmdhd/include/siutils.h | 30 +- drivers/net/wireless/bcmdhd/include/trxhdr.h | 3 +- drivers/net/wireless/bcmdhd/include/typedefs.h | 5 +- drivers/net/wireless/bcmdhd/include/wlfc_proto.h | 2 +- drivers/net/wireless/bcmdhd/include/wlioctl.h | 87 ++- drivers/net/wireless/bcmdhd/linux_osl.c | 23 +- drivers/net/wireless/bcmdhd/siutils.c | 195 +++++- drivers/net/wireless/bcmdhd/wl_android.c | 6 +- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 875 ++++++++++++-------- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 282 +++++--- drivers/net/wireless/bcmdhd/wl_cfgp2p.c | 165 ++++- drivers/net/wireless/bcmdhd/wl_cfgp2p.h | 12 +- drivers/net/wireless/bcmdhd/wl_iw.c | 110 +++- drivers/net/wireless/bcmdhd/wl_iw.h | 17 +- 81 files changed, 2737 insertions(+), 752 deletions(-) commit52bdb6f543Author: Dmitry Shmidt <dimitrysh@google.com> Date: Mon Jan 23 12:47:21 2012 -0800 net: wireless: bcmdhd: Add WIPHY_FLAG_SUPPORTS_FW_ROAM flag Adding this flag will allow NL80211_ATTR_ROAM_SUPPORT, and will set WPA_DRIVER_FLAGS_BSS_SELECTION flag in wpa_supplicant Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/wl_cfg80211.c | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) commitb1a94205e9Author: Dmitry Shmidt <dimitrysh@google.com> Date: Fri Jan 20 14:15:05 2012 -0800 net: wireless: bcmdhd: Fake PNO event to wake up the wpa_supplicant Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_linux.c | 4 +++- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) commit09701e3edfAuthor: Scott Anderson <saa@google.com> Date: Wed Jan 18 15:56:51 2012 -0800 usb: gadget: android: Honor CONFIG_USB_GADGET_VBUS_DRAW The maximum current draw was hard coded to 500 mA. composite.c has code that uses CONFIG_USB_GADGET_VBUS_DRAW to set the bMaxPower and to set whether or not the device is self-powered if they haven't been set. This change removes the code in android.c to allow composite.c to set them. Change-Id: I9db37922e91ee86e9e5c0e14519e119e5c41ca48 Signed-off-by: Scott Anderson <saa@google.com> drivers/usb/gadget/android.c | 3 --- 1 files changed, 0 insertions(+), 3 deletions(-) commita6ccb73389Author: Benoit Goby <benoit@android.com> Date: Fri Jan 20 14:42:41 2012 -0800 usb: gadget: Fix usb string id allocation Don't reset next_string_id every time the gadget is enabled, this makes the next strings allocated overwrite strings allocated at probe time. Instead, fix rndis not to allocate new string ids on every config bind. Change-Id: Ied28ee416bb6f00c434c34176fe5b7f0dcb2b2d4 Signed-off-by: Benoit Goby <benoit@android.com> drivers/usb/gadget/android.c | 1 - drivers/usb/gadget/f_rndis.c | 12 +++++------- drivers/usb/gadget/rndis.c | 11 +++++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) commit87159de9c3Author: Jouni Malinen <jouni@qca.qualcomm.com> Date: Thu Aug 11 11:46:22 2011 +0300 nl80211/cfg80211: Make addition of new sinfo fields safer Add a comment pointing out the use of enum station_info_flags for all new struct station_info fields. In addition, memset the sinfo buffer to zero before use on all paths in the current tree to avoid leaving uninitialized pointers in the data. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> include/net/cfg80211.h | 5 +++++ net/mac80211/sta_info.c | 1 + net/wireless/nl80211.c | 1 + 3 files changed, 7 insertions(+), 0 deletions(-) commitd692df224bAuthor: Jouni Malinen <jouni@qca.qualcomm.com> Date: Mon Aug 8 12:11:52 2011 +0300 cfg80211/nl80211: Send AssocReq IEs to user space in AP mode When user space SME/MLME (e.g., hostapd) is not used in AP mode, the IEs from the (Re)Association Request frame that was processed in firmware need to be made available for user space (e.g., RSN IE for hostapd). Allow this to be done with cfg80211_new_sta(). Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com> Acked-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> include/net/cfg80211.h | 8 ++++++++ net/wireless/nl80211.c | 4 ++++ 2 files changed, 12 insertions(+), 0 deletions(-) commitd1e94136fcAuthor: Dima Zavin <dima@android.com> Date: Mon Jan 23 10:39:02 2012 -0800 misc: remove android pmem driver, it's obsolete. Change-Id: I48d9778007e1e9eed2bb34e33ceee818c23afaa5 Signed-off-by: Dima Zavin <dima@android.com> drivers/misc/Kconfig | 4 - drivers/misc/Makefile | 1 - drivers/misc/pmem.c | 1345 ------------------------------------------ include/linux/android_pmem.h | 93 --- 4 files changed, 0 insertions(+), 1443 deletions(-) commitdac306d896Author: Dima Zavin <dima@android.com> Date: Thu Jan 19 09:51:07 2012 -0800 Revert "proc: enable writing to /proc/pid/mem" This reverts commit198214a7ee. fs/proc/base.c | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) commita65e28a014Author: Dima Zavin <dima@android.com> Date: Thu Jan 12 15:55:25 2012 -0800 ram_console: set CON_ANYTIME console flag We want to ensure that we get all the console messages, even ones that occur while the printing CPU is not yet online. Change-Id: I1d2694d05ac9415669a92f38efdd8e71c927705b Signed-off-by: Dima Zavin <dima@android.com> drivers/staging/android/ram_console.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commitf16e6fb3e3Author: Benoit Goby <benoit@android.com> Date: Thu Dec 15 18:40:37 2011 -0800 Revert "usb: gadget: rndis: don't use dev_get_stats" This reverts commitffdab0c0c4. Not needed anymore in 2.6.39 and 3.0, dev_get_stats has been fixed and may be called from atomic context. See:1ac9ad1net: remove dev_txq_stats_fold() drivers/usb/gadget/rndis.c | 23 ++--------------------- 1 files changed, 2 insertions(+), 21 deletions(-) commite1493f1544Author: Dmitry Shmidt <dimitrysh@google.com> Date: Mon Dec 19 10:24:09 2011 -0800 net: wireless: bcmdhd: Enable wlan access on resume for all sdio functions Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/bcmsdh_sdmmc_linux.c | 4 +--- 1 files changed, 1 insertions(+), 3 deletions(-) commitbbd08c6e95Author: Dmitry Shmidt <dimitrysh@google.com> Date: Fri Dec 16 12:54:51 2011 -0800 net: wireless: bcmdhd: Fix P2P interface removal Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_linux.c | 3 ++ drivers/net/wireless/bcmdhd/wl_cfg80211.c | 43 ++++++++++++++++++++++------- drivers/net/wireless/bcmdhd/wl_cfg80211.h | 9 ++++-- 3 files changed, 42 insertions(+), 13 deletions(-) commit37ff4411a5Author: Dmitry Shmidt <dimitrysh@google.com> Date: Thu Dec 15 12:12:20 2011 -0800 net: wireless: bcm4329: Fix pno_enable if disassociated Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcm4329/dhd_common.c | 43 ++++++++++++++++++++++++----- 1 files changed, 36 insertions(+), 7 deletions(-) commit599c8566faAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Dec 13 17:39:48 2011 -0800 net: wireless: bcmdhd: Fix proper scan command even if request is NULL Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/wl_cfg80211.c | 16 ++++++++++------ 1 files changed, 10 insertions(+), 6 deletions(-) commitf227b88c89Author: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Dec 13 12:27:49 2011 -0800 net: wireless: bcmdhd: Decrease event wake_lock timeout to 1500 ms Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd.h | 4 ++-- drivers/net/wireless/bcmdhd/dhd_linux.c | 8 ++++---- drivers/net/wireless/bcmdhd/wl_iw.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) commited3f356087Author: Dmitry Shmidt <dimitrysh@google.com> Date: Mon Dec 12 15:40:33 2011 -0800 net: wireless: bcmdhd: Fix getting arp_hostip table Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_common.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commitc561cedf2bAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Tue Dec 6 16:27:37 2011 -0800 net: wireless: bcmdhd: Allow to push more packets to FW for Tx Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_sdio.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commit4f36cb88d6Author: Dmitry Shmidt <dimitrysh@google.com> Date: Fri Dec 2 13:24:01 2011 -0800 net: wireless: bcmdhd: Fix scan crash in ibss mode Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/wl_cfg80211.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commitaf16732d4cAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Fri Dec 2 13:10:47 2011 -0800 net: wireless: bcmdhd: Add FW reloading in case of FW hang Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_linux.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) commit7caeacd6edAuthor: Dmitry Shmidt <dimitrysh@google.com> Date: Wed Nov 30 12:49:02 2011 -0800 net: wireless: bcmdhd: Update to Version 5.90.125.94.1 - Return zeroed private command buffer - Fix memory leak in wl_inform_single_bss() Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/dhd_common.c | 5 +++-- drivers/net/wireless/bcmdhd/dhd_linux_mon.c | 10 ++++++---- drivers/net/wireless/bcmdhd/include/epivers.h | 2 +- drivers/net/wireless/bcmdhd/wl_android.c | 9 ++++++--- drivers/net/wireless/bcmdhd/wl_cfg80211.c | 9 ++++++--- 5 files changed, 22 insertions(+), 13 deletions(-) commit8d71d882e7Author: Dmitry Shmidt <dimitrysh@google.com> Date: Fri Nov 11 16:04:12 2011 -0800 net: wireless: bcmdhd: Use CONFIG_DHD_USE_STATIC_BUF for preallocated memory Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/net/wireless/bcmdhd/Kconfig | 7 +++ drivers/net/wireless/bcmdhd/bcmsdh_sdmmc.c | 16 ++++---- drivers/net/wireless/bcmdhd/dhd.h | 4 +- drivers/net/wireless/bcmdhd/dhd_cdc.c | 4 +- drivers/net/wireless/bcmdhd/dhd_linux.c | 4 +- drivers/net/wireless/bcmdhd/dhd_sdio.c | 4 +- drivers/net/wireless/bcmdhd/include/linux_osl.h | 2 +- drivers/net/wireless/bcmdhd/linux_osl.c | 52 +++++++++++++---------- drivers/net/wireless/bcmdhd/wl_android.c | 7 ++- 9 files changed, 57 insertions(+), 43 deletions(-) commit35047200c4Author: Dmitry Shmidt <dimitrysh@google.com> Date: Mon Dec 19 12:32:21 2011 -0800 wireless: Protect regdomain change by mutex Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> net/wireless/reg.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) commitea693bf7f8Author: Dmitry Shmidt <dimitrysh@google.com> Date: Fri Dec 16 17:52:18 2011 -0800 mmc: Set suspend/resume bus operations if CONFIG_PM_RUNTIME is used Signed-off-by: Dmitry Shmidt <dimitrysh@google.com> drivers/mmc/core/bus.c | 24 +++++++----------------- 1 files changed, 7 insertions(+), 17 deletions(-) commitdc1b634039Author: Benoit Goby <benoit@android.com> Date: Fri Dec 9 18:05:00 2011 -0800 usb: gadget: android: Don't allow changing the functions list if enabled Change-Id: I3ad39b420ce79a8602a7eca1daac1f56b30bad5c Signed-off-by: Benoit Goby <benoit@android.com> drivers/usb/gadget/android.c | 28 ++++++++++++++++++++++++---- 1 files changed, 24 insertions(+), 4 deletions(-) commite0de0a507dAuthor: Benoit Goby <benoit@android.com> Date: Tue Nov 29 13:49:27 2011 -0800 usb: gadget: android: Cancel pending ctrlrequest before disabling Make sure there is no pending ctrlrequest before removing the config. Otherwise the ctrlrequest complete callback could access structures after they have been freed. Unbind cancels pending transfers but not ep0 requests. Bug:55130655440193 Change-Id: I063c22bf5d104a3d2df71cf622409459fac5f27a Signed-off-by: Benoit Goby <benoit@android.com> drivers/usb/gadget/android.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) commitb86fd0b622Author: Colin Cross <ccross@android.com> Date: Tue Nov 29 16:37:07 2011 -0800 ARM: idle: call idle notifiers before stopping nohz tick If an idle notifier modifies a timer, calling the notifier after the sched tick has been stopped may leave the sched tick set too early. Move teh idle notifier call before the call to tick_nohz_stop_sched_tick. Change-Id: I0db3284bec6d0193bc5e2a57650ab06bd8342319 Signed-off-by: Colin Cross <ccross@android.com> arch/arm/kernel/process.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commit6a4a38525dAuthor: Benoit Goby <benoit@android.com> Date: Mon Nov 28 18:01:03 2011 -0800 usb: gadget: android: Reset next_string_id before enable Reset next_string_id to 0 before enabling the gadget driver. Otherwise, after a large number of enable/disable cycles, bind will fail because we cannot allocate new string ids. String ids cannot be larger than 254 per USB spec. Change-Id: I44f5fece45008b7a0a18c025d4eb5ce842585c28 Signed-off-by: Benoit Goby <benoit@android.com> drivers/usb/gadget/android.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) commitdbb18fb2c1Author: hyungseoung.yoo <hyungseoung.yoo@samsung.com> Date: Fri Nov 18 13:57:01 2011 +0900 Bluetooth: Keep master role when SCO or eSCO is active This improves compatbility with a lot of headset / chipset combinations. Ideally this should not be needed. Change-Id: I8b676701e12e416aa7d60801b9d353b15d102709 Signed-off-by: hyungseoung.yoo <hyungseoung.yoo@samsung.com> Signed-off-by: Jaikumar Ganesh <jaikumarg@android.com> net/bluetooth/hci_event.c | 12 +++++++++++- 1 files changed, 11 insertions(+), 1 deletions(-) commit9d187300dfAuthor: Arve Hjønnevåg <arve@android.com> Date: Tue Nov 22 14:56:50 2011 -0800 rtc: Fix some bugs that allowed accumulating time drift in suspend/resume The current code checks if abs(delta_delta.tv_sec) is greater or equal to two before it discards the old delta value, but this can trigger at close to -1 seconds since -1.000000001 seconds is stored as tv_sec -2 and tv_nsec 999999999 in a normalized timespec. rtc_resume had an early return check if the rtc value had not changed since rtc_suspend. This effectivly stops time for the duration of the short sleep. Check if sleep_time is positive after all the adjustments have been applied instead since this allows the old_system adjustment in rtc_suspend to have an effect even for short sleep cycles. Change-Id: I00b45c0349ec91a4bab9b41a126b377515427898 Signed-off-by: Arve Hjønnevåg <arve@android.com> drivers/rtc/class.c | 10 +++++----- 1 files changed, 5 insertions(+), 5 deletions(-) commit452d440ab2Author: Arve Hjønnevåg <arve@android.com> Date: Tue Nov 22 15:28:27 2011 -0800 Fix "time: Catch invalid timespec sleep values in __timekeeping_inject_sleeptime" to compile on 3.0 Change-Id: I1225f279cda04dedbfb7f853f6b58f1032bd6d2b kernel/time/timekeeping.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) commitcf70c6a400Author: John Stultz <john.stultz@linaro.org> Date: Wed Jun 1 18:18:09 2011 -0700 time: Catch invalid timespec sleep values in __timekeeping_inject_sleeptime Arve suggested making sure we catch possible negative sleep time intervals that could be passed into timekeeping_inject_sleeptime. CC: Arve Hjønnevåg <arve@android.com> CC: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: John Stultz <john.stultz@linaro.org> kernel/time/timekeeping.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) commit340ede3671Author: John Stultz <john.stultz@linaro.org> Date: Fri May 27 11:33:18 2011 -0700 rtc: Avoid accumulating time drift in suspend/resume Because the RTC interface is only a second granular interface, each time we read from the RTC for suspend/resume, we introduce a half second (on average) of error. In order to avoid this error accumulating as the system is suspended over and over, this patch measures the time delta between the RTC and the system CLOCK_REALTIME. If the delta is less then 2 seconds from the last suspend, we compensate by using the previous time delta (keeping it close). If it is larger then 2 seconds, we assume the clock was set or has been changed, so we do no correction and update the delta. Note: If NTP is running, ths could seem to "fight" with the NTP corrected time, where as if the system time was off by 1 second, and NTP slewed the value in, a suspend/resume cycle could undo this correction, by trying to restore the previous offset from the RTC. However, without this patch, since each read could cause almost a full second worth of error, its possible to get almost 2 seconds of error just from the suspend/resume cycle alone, so this about equal to any offset added by the compensation. Further on systems that suspend/resume frequently, this should keep time closer then NTP could compensate for if the errors were allowed to accumulate. Credits to Arve Hjønnevåg for suggesting this solution. This patch also improves some of the variable names and adds more clear comments. CC: Arve Hjønnevåg <arve@android.com> CC: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: John Stultz <john.stultz@linaro.org> drivers/rtc/class.c | 65 +++++++++++++++++++++++++++++++++++++------------- 1 files changed, 48 insertions(+), 17 deletions(-) Change-Id: I16f522e7ee2b301cbdaea62d52d50d7249f565c2 Signed-off-by: Rohit Vaswani <rvaswani@codeaurora.org>
3575 lines
82 KiB
C
Executable File
3575 lines
82 KiB
C
Executable File
/*
|
|
BlueZ - Bluetooth protocol stack for Linux
|
|
Copyright (c) 2000-2001, 2010-2012, Code Aurora Forum. All rights reserved.
|
|
|
|
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License version 2 as
|
|
published by the Free Software Foundation;
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
|
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
|
|
CLAIM, OR ANY SPECIAL 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.
|
|
|
|
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
|
|
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
|
SOFTWARE IS DISCLAIMED.
|
|
*/
|
|
|
|
/* Bluetooth HCI event handling. */
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/fcntl.h>
|
|
#include <linux/init.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/notifier.h>
|
|
#include <net/sock.h>
|
|
|
|
#include <asm/system.h>
|
|
#include <linux/uaccess.h>
|
|
#include <asm/unaligned.h>
|
|
|
|
#include <net/bluetooth/bluetooth.h>
|
|
#include <net/bluetooth/hci_core.h>
|
|
|
|
/* Handle HCI Event packets */
|
|
|
|
static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
if (status)
|
|
return;
|
|
|
|
clear_bit(HCI_INQUIRY, &hdev->flags);
|
|
|
|
hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status);
|
|
|
|
hci_conn_check_pending(hdev);
|
|
}
|
|
|
|
static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
if (status)
|
|
return;
|
|
|
|
clear_bit(HCI_INQUIRY, &hdev->flags);
|
|
|
|
hci_conn_check_pending(hdev);
|
|
}
|
|
|
|
static void hci_cc_link_key_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_link_key_reply *rp = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
struct hci_cp_link_key_reply *cp;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
if (rp->status)
|
|
return;
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_LINK_KEY_REPLY);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
|
|
if (conn) {
|
|
hci_conn_hold(conn);
|
|
memcpy(conn->link_key, cp->link_key, sizeof(conn->link_key));
|
|
conn->key_type = 5;
|
|
hci_conn_put(conn);
|
|
}
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cc_remote_name_req_cancel(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
BT_DBG("%s", hdev->name);
|
|
}
|
|
|
|
static void hci_cc_role_discovery(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_role_discovery *rp = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
|
|
if (conn) {
|
|
if (rp->role)
|
|
conn->link_mode &= ~HCI_LM_MASTER;
|
|
else
|
|
conn->link_mode |= HCI_LM_MASTER;
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cc_read_link_policy(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_read_link_policy *rp = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
|
|
if (conn)
|
|
conn->link_policy = __le16_to_cpu(rp->policy);
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cc_write_link_policy(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_write_link_policy *rp = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
void *sent;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LINK_POLICY);
|
|
if (!sent)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
|
|
if (conn)
|
|
conn->link_policy = get_unaligned_le16(sent + 2);
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cc_read_def_link_policy(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_read_def_link_policy *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
hdev->link_policy = __le16_to_cpu(rp->policy);
|
|
}
|
|
|
|
static void hci_cc_write_def_link_policy(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
void *sent;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_DEF_LINK_POLICY);
|
|
if (!sent)
|
|
return;
|
|
|
|
if (!status)
|
|
hdev->link_policy = get_unaligned_le16(sent);
|
|
|
|
hci_req_complete(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, status);
|
|
}
|
|
|
|
static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
clear_bit(HCI_RESET, &hdev->flags);
|
|
|
|
hci_req_complete(hdev, HCI_OP_RESET, status);
|
|
}
|
|
|
|
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
void *sent;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LOCAL_NAME);
|
|
if (!sent)
|
|
return;
|
|
hci_dev_lock(hdev);
|
|
if (!status)
|
|
memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
|
|
|
|
if (test_bit(HCI_MGMT, &hdev->flags))
|
|
mgmt_set_local_name_complete(hdev->id, sent, status);
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_read_local_name *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
memcpy(hdev->dev_name, rp->name, HCI_MAX_NAME_LENGTH);
|
|
}
|
|
|
|
static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
void *sent;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_AUTH_ENABLE);
|
|
if (!sent)
|
|
return;
|
|
|
|
if (!status) {
|
|
__u8 param = *((__u8 *) sent);
|
|
|
|
if (param == AUTH_ENABLED)
|
|
set_bit(HCI_AUTH, &hdev->flags);
|
|
else
|
|
clear_bit(HCI_AUTH, &hdev->flags);
|
|
}
|
|
|
|
hci_req_complete(hdev, HCI_OP_WRITE_AUTH_ENABLE, status);
|
|
}
|
|
|
|
static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
void *sent;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_ENCRYPT_MODE);
|
|
if (!sent)
|
|
return;
|
|
|
|
if (!status) {
|
|
__u8 param = *((__u8 *) sent);
|
|
|
|
if (param)
|
|
set_bit(HCI_ENCRYPT, &hdev->flags);
|
|
else
|
|
clear_bit(HCI_ENCRYPT, &hdev->flags);
|
|
}
|
|
|
|
hci_req_complete(hdev, HCI_OP_WRITE_ENCRYPT_MODE, status);
|
|
}
|
|
|
|
static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
void *sent;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SCAN_ENABLE);
|
|
if (!sent)
|
|
return;
|
|
|
|
if (!status) {
|
|
__u8 param = *((__u8 *) sent);
|
|
int old_pscan, old_iscan;
|
|
hci_dev_lock(hdev);
|
|
|
|
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->id, 1);
|
|
} else if (old_iscan)
|
|
mgmt_discoverable(hdev->id, 0);
|
|
|
|
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);
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status);
|
|
}
|
|
|
|
static void hci_cc_read_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_read_class_of_dev *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
memcpy(hdev->dev_class, rp->dev_class, 3);
|
|
|
|
BT_DBG("%s class 0x%.2x%.2x%.2x", hdev->name,
|
|
hdev->dev_class[2], hdev->dev_class[1], hdev->dev_class[0]);
|
|
}
|
|
|
|
static void hci_cc_write_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
void *sent;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
if (status)
|
|
return;
|
|
|
|
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_CLASS_OF_DEV);
|
|
if (!sent)
|
|
return;
|
|
|
|
memcpy(hdev->dev_class, sent, 3);
|
|
}
|
|
|
|
static void hci_cc_read_voice_setting(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_read_voice_setting *rp = (void *) skb->data;
|
|
__u16 setting;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
setting = __le16_to_cpu(rp->voice_setting);
|
|
|
|
if (hdev->voice_setting == setting)
|
|
return;
|
|
|
|
hdev->voice_setting = setting;
|
|
|
|
BT_DBG("%s voice setting 0x%04x", hdev->name, setting);
|
|
|
|
if (hdev->notify) {
|
|
tasklet_disable(&hdev->tx_task);
|
|
hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING);
|
|
tasklet_enable(&hdev->tx_task);
|
|
}
|
|
}
|
|
|
|
static void hci_cc_write_voice_setting(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
__u16 setting;
|
|
void *sent;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
if (status)
|
|
return;
|
|
|
|
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_VOICE_SETTING);
|
|
if (!sent)
|
|
return;
|
|
|
|
setting = get_unaligned_le16(sent);
|
|
|
|
if (hdev->voice_setting == setting)
|
|
return;
|
|
|
|
hdev->voice_setting = setting;
|
|
|
|
BT_DBG("%s voice setting 0x%04x", hdev->name, setting);
|
|
|
|
if (hdev->notify) {
|
|
tasklet_disable(&hdev->tx_task);
|
|
hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING);
|
|
tasklet_enable(&hdev->tx_task);
|
|
}
|
|
}
|
|
|
|
static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
hci_req_complete(hdev, HCI_OP_HOST_BUFFER_SIZE, status);
|
|
}
|
|
|
|
static void hci_cc_read_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_read_ssp_mode *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
hdev->ssp_mode = rp->mode;
|
|
}
|
|
|
|
static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
void *sent;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
if (status)
|
|
return;
|
|
|
|
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_MODE);
|
|
if (!sent)
|
|
return;
|
|
|
|
hdev->ssp_mode = *((__u8 *) sent);
|
|
}
|
|
|
|
static u8 hci_get_inquiry_mode(struct hci_dev *hdev)
|
|
{
|
|
if (hdev->features[6] & LMP_EXT_INQ)
|
|
return 2;
|
|
|
|
if (hdev->features[3] & LMP_RSSI_INQ)
|
|
return 1;
|
|
|
|
if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 &&
|
|
hdev->lmp_subver == 0x0757)
|
|
return 1;
|
|
|
|
if (hdev->manufacturer == 15) {
|
|
if (hdev->hci_rev == 0x03 && hdev->lmp_subver == 0x6963)
|
|
return 1;
|
|
if (hdev->hci_rev == 0x09 && hdev->lmp_subver == 0x6963)
|
|
return 1;
|
|
if (hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x6965)
|
|
return 1;
|
|
}
|
|
|
|
if (hdev->manufacturer == 31 && hdev->hci_rev == 0x2005 &&
|
|
hdev->lmp_subver == 0x1805)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void hci_setup_inquiry_mode(struct hci_dev *hdev)
|
|
{
|
|
u8 mode;
|
|
|
|
mode = hci_get_inquiry_mode(hdev);
|
|
|
|
hci_send_cmd(hdev, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode);
|
|
}
|
|
|
|
static void hci_setup_event_mask(struct hci_dev *hdev)
|
|
{
|
|
/* The second byte is 0xff instead of 0x9f (two reserved bits
|
|
* disabled) since a Broadcom 1.2 dongle doesn't respond to the
|
|
* command otherwise */
|
|
u8 events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
BT_DBG("");
|
|
|
|
/* Events for 1.2 and newer controllers */
|
|
if (hdev->lmp_ver > 1) {
|
|
events[4] |= 0x01; /* Flow Specification Complete */
|
|
events[4] |= 0x02; /* Inquiry Result with RSSI */
|
|
events[4] |= 0x04; /* Read Remote Extended Features Complete */
|
|
events[5] |= 0x08; /* Synchronous Connection Complete */
|
|
events[5] |= 0x10; /* Synchronous Connection Changed */
|
|
}
|
|
|
|
if (hdev->features[3] & LMP_RSSI_INQ)
|
|
events[4] |= 0x04; /* Inquiry Result with RSSI */
|
|
|
|
if (hdev->features[5] & LMP_SNIFF_SUBR)
|
|
events[5] |= 0x20; /* Sniff Subrating */
|
|
|
|
if (hdev->features[5] & LMP_PAUSE_ENC)
|
|
events[5] |= 0x80; /* Encryption Key Refresh Complete */
|
|
|
|
if (hdev->features[6] & LMP_EXT_INQ)
|
|
events[5] |= 0x40; /* Extended Inquiry Result */
|
|
|
|
if (hdev->features[6] & LMP_NO_FLUSH)
|
|
events[7] |= 0x01; /* Enhanced Flush Complete */
|
|
|
|
if (hdev->features[7] & LMP_LSTO)
|
|
events[6] |= 0x80; /* Link Supervision Timeout Changed */
|
|
|
|
if (hdev->features[6] & LMP_SIMPLE_PAIR) {
|
|
events[6] |= 0x01; /* IO Capability Request */
|
|
events[6] |= 0x02; /* IO Capability Response */
|
|
events[6] |= 0x04; /* User Confirmation Request */
|
|
events[6] |= 0x08; /* User Passkey Request */
|
|
events[6] |= 0x10; /* Remote OOB Data Request */
|
|
events[6] |= 0x20; /* Simple Pairing Complete */
|
|
events[7] |= 0x04; /* User Passkey Notification */
|
|
events[7] |= 0x08; /* Keypress Notification */
|
|
events[7] |= 0x10; /* Remote Host Supported
|
|
* Features Notification */
|
|
}
|
|
|
|
if (hdev->features[4] & LMP_LE)
|
|
events[7] |= 0x20; /* LE Meta-Event */
|
|
|
|
hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
|
|
}
|
|
|
|
static void hci_setup(struct hci_dev *hdev)
|
|
{
|
|
if (hdev->lmp_ver > 1)
|
|
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
|
|
|
|
if (hdev->features[6] & LMP_SIMPLE_PAIR) {
|
|
u8 mode = 0x01;
|
|
hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
|
|
}
|
|
|
|
if (hdev->features[3] & LMP_RSSI_INQ)
|
|
hci_setup_inquiry_mode(hdev);
|
|
|
|
if (hdev->features[7] & LMP_INQ_TX_PWR)
|
|
hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL);
|
|
}
|
|
|
|
static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_read_local_version *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
hdev->hci_ver = rp->hci_ver;
|
|
hdev->hci_rev = __le16_to_cpu(rp->hci_rev);
|
|
hdev->lmp_ver = rp->lmp_ver;
|
|
hdev->manufacturer = __le16_to_cpu(rp->manufacturer);
|
|
hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver);
|
|
|
|
BT_DBG("%s manufacturer %d hci ver %d:%d", hdev->name,
|
|
hdev->manufacturer,
|
|
hdev->hci_ver, hdev->hci_rev);
|
|
|
|
if (hdev->dev_type == HCI_BREDR && test_bit(HCI_INIT, &hdev->flags))
|
|
hci_setup(hdev);
|
|
}
|
|
|
|
static void hci_setup_link_policy(struct hci_dev *hdev)
|
|
{
|
|
u16 link_policy = 0;
|
|
|
|
if (hdev->features[0] & LMP_RSWITCH)
|
|
link_policy |= HCI_LP_RSWITCH;
|
|
if (hdev->features[0] & LMP_HOLD)
|
|
link_policy |= HCI_LP_HOLD;
|
|
if (hdev->features[0] & LMP_SNIFF)
|
|
link_policy |= HCI_LP_SNIFF;
|
|
if (hdev->features[1] & LMP_PARK)
|
|
link_policy |= HCI_LP_PARK;
|
|
|
|
link_policy = cpu_to_le16(link_policy);
|
|
hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY,
|
|
sizeof(link_policy), &link_policy);
|
|
}
|
|
|
|
static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_read_local_commands *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
goto done;
|
|
|
|
memcpy(hdev->commands, rp->commands, sizeof(hdev->commands));
|
|
|
|
if (test_bit(HCI_INIT, &hdev->flags) && (hdev->commands[5] & 0x10))
|
|
hci_setup_link_policy(hdev);
|
|
|
|
done:
|
|
hci_req_complete(hdev, HCI_OP_READ_LOCAL_COMMANDS, rp->status);
|
|
}
|
|
|
|
static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_read_local_features *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
memcpy(hdev->features, rp->features, 8);
|
|
|
|
if (hdev->dev_type == HCI_BREDR && test_bit(HCI_INIT, &hdev->flags)) {
|
|
if (hdev->features[6] & LMP_SIMPLE_PAIR) {
|
|
u8 mode = 0x01;
|
|
hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE,
|
|
sizeof(mode), &mode);
|
|
}
|
|
|
|
if (hdev->features[3] & LMP_RSSI_INQ)
|
|
hci_setup_inquiry_mode(hdev);
|
|
|
|
if (hdev->features[7] & LMP_INQ_TX_PWR)
|
|
hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER,
|
|
0, NULL);
|
|
|
|
hci_setup_event_mask(hdev);
|
|
}
|
|
|
|
/* Adjust default settings according to features
|
|
* supported by device. */
|
|
|
|
if (hdev->features[0] & LMP_3SLOT)
|
|
hdev->pkt_type |= (HCI_DM3 | HCI_DH3);
|
|
|
|
if (hdev->features[0] & LMP_5SLOT)
|
|
hdev->pkt_type |= (HCI_DM5 | HCI_DH5);
|
|
|
|
if (hdev->features[1] & LMP_HV2) {
|
|
hdev->pkt_type |= (HCI_HV2);
|
|
hdev->esco_type |= (ESCO_HV2);
|
|
}
|
|
|
|
if (hdev->features[1] & LMP_HV3) {
|
|
hdev->pkt_type |= (HCI_HV3);
|
|
hdev->esco_type |= (ESCO_HV3);
|
|
}
|
|
|
|
if (hdev->features[3] & LMP_ESCO)
|
|
hdev->esco_type |= (ESCO_EV3);
|
|
|
|
if (hdev->features[4] & LMP_EV4)
|
|
hdev->esco_type |= (ESCO_EV4);
|
|
|
|
if (hdev->features[4] & LMP_EV5)
|
|
hdev->esco_type |= (ESCO_EV5);
|
|
|
|
if (hdev->features[5] & LMP_EDR_ESCO_2M)
|
|
hdev->esco_type |= (ESCO_2EV3);
|
|
|
|
if (hdev->features[5] & LMP_EDR_ESCO_3M)
|
|
hdev->esco_type |= (ESCO_3EV3);
|
|
|
|
if (hdev->features[5] & LMP_EDR_3S_ESCO)
|
|
hdev->esco_type |= (ESCO_2EV5 | ESCO_3EV5);
|
|
|
|
BT_DBG("%s features 0x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", hdev->name,
|
|
hdev->features[0], hdev->features[1],
|
|
hdev->features[2], hdev->features[3],
|
|
hdev->features[4], hdev->features[5],
|
|
hdev->features[6], hdev->features[7]);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
if (hdev->flow_ctl_mode == HCI_PACKET_BASED_FLOW_CTL_MODE) {
|
|
hdev->acl_mtu = __le16_to_cpu(rp->acl_mtu);
|
|
hdev->sco_mtu = rp->sco_mtu;
|
|
hdev->acl_pkts = __le16_to_cpu(rp->acl_max_pkt);
|
|
hdev->sco_pkts = __le16_to_cpu(rp->sco_max_pkt);
|
|
hdev->acl_cnt = hdev->acl_pkts;
|
|
hdev->sco_cnt = hdev->sco_pkts;
|
|
}
|
|
|
|
if (test_bit(HCI_QUIRK_FIXUP_BUFFER_SIZE, &hdev->quirks)) {
|
|
hdev->sco_mtu = 64;
|
|
hdev->sco_pkts = 8;
|
|
}
|
|
|
|
|
|
BT_DBG("%s acl mtu %d:%d sco mtu %d:%d", hdev->name,
|
|
hdev->acl_mtu, hdev->acl_pkts,
|
|
hdev->sco_mtu, hdev->sco_pkts);
|
|
}
|
|
|
|
static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_read_bd_addr *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (!rp->status)
|
|
bacpy(&hdev->bdaddr, &rp->bdaddr);
|
|
|
|
hci_req_complete(hdev, HCI_OP_READ_BD_ADDR, rp->status);
|
|
}
|
|
|
|
static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status);
|
|
}
|
|
|
|
static void hci_cc_read_data_block_size(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_read_data_block_size *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
if (hdev->flow_ctl_mode == HCI_BLOCK_BASED_FLOW_CTL_MODE) {
|
|
hdev->acl_mtu = __le16_to_cpu(rp->max_acl_len);
|
|
hdev->sco_mtu = 0;
|
|
hdev->data_block_len = __le16_to_cpu(rp->data_block_len);
|
|
/* acl_pkts indicates the number of blocks */
|
|
hdev->acl_pkts = __le16_to_cpu(rp->num_blocks);
|
|
hdev->sco_pkts = 0;
|
|
hdev->acl_cnt = hdev->acl_pkts;
|
|
hdev->sco_cnt = 0;
|
|
}
|
|
|
|
BT_DBG("%s acl mtu %d:%d, data block len %d", hdev->name,
|
|
hdev->acl_mtu, hdev->acl_cnt, hdev->data_block_len);
|
|
}
|
|
|
|
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)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
hci_req_complete(hdev, HCI_OP_DELETE_STORED_LINK_KEY, status);
|
|
}
|
|
|
|
static void hci_cc_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
hci_req_complete(hdev, HCI_OP_SET_EVENT_MASK, status);
|
|
}
|
|
|
|
static void hci_cc_write_inquiry_mode(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
hci_req_complete(hdev, HCI_OP_WRITE_INQUIRY_MODE, status);
|
|
}
|
|
|
|
static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
hci_req_complete(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, status);
|
|
}
|
|
|
|
static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
hci_req_complete(hdev, HCI_OP_SET_EVENT_FLT, status);
|
|
}
|
|
|
|
static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_pin_code_reply *rp = (void *) skb->data;
|
|
struct hci_cp_pin_code_reply *cp;
|
|
struct hci_conn *conn;
|
|
|
|
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);
|
|
|
|
if (rp->status != 0)
|
|
goto unlock;
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY);
|
|
if (!cp)
|
|
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)
|
|
{
|
|
struct hci_rp_pin_code_neg_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_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr,
|
|
rp->status);
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_le_read_buffer_size *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
hdev->le_mtu = __le16_to_cpu(rp->le_mtu);
|
|
hdev->le_pkts = rp->le_max_pkt;
|
|
|
|
hdev->le_cnt = hdev->le_pkts;
|
|
|
|
BT_DBG("%s le mtu %d:%d", hdev->name, hdev->le_mtu, hdev->le_pkts);
|
|
|
|
hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status);
|
|
}
|
|
|
|
static void hci_cc_user_confirm_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_reply_complete(hdev->id, &rp->bdaddr,
|
|
rp->status);
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cc_user_confirm_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,
|
|
rp->status);
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cc_read_rssi(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_conn *conn;
|
|
struct hci_rp_read_rssi *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
BT_DBG("%s rssi : %d handle : %d", hdev->name, rp->rssi, rp->handle);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
|
|
if (conn)
|
|
mgmt_read_rssi_complete(hdev->id, rp->rssi, &conn->dst,
|
|
__le16_to_cpu(rp->handle), rp->status);
|
|
}
|
|
|
|
static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
hci_dev_lock(hdev);
|
|
|
|
mgmt_read_local_oob_data_reply_complete(hdev->id, rp->hash,
|
|
rp->randomizer, rp->status);
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_le_ltk_reply *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status);
|
|
}
|
|
|
|
static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_rp_le_ltk_neg_reply *rp = (void *) skb->data;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
|
|
|
if (rp->status)
|
|
return;
|
|
|
|
hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status);
|
|
}
|
|
|
|
static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
void *sent;
|
|
__u8 param_scan_enable;
|
|
__u8 status = *((__u8 *) skb->data);
|
|
|
|
if (status)
|
|
return;
|
|
|
|
sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE);
|
|
if (!sent)
|
|
return;
|
|
|
|
param_scan_enable = *((__u8 *) sent);
|
|
if (param_scan_enable == 0x01) {
|
|
del_timer(&hdev->adv_timer);
|
|
} else if (param_scan_enable == 0x00) {
|
|
mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
if (status) {
|
|
hci_req_complete(hdev, HCI_OP_INQUIRY, status);
|
|
|
|
hci_conn_check_pending(hdev);
|
|
} else {
|
|
set_bit(HCI_INQUIRY, &hdev->flags);
|
|
hci_dev_lock(hdev);
|
|
if (test_bit(HCI_MGMT, &hdev->flags))
|
|
mgmt_inquiry_started(hdev->id);
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
}
|
|
|
|
static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_create_conn *cp;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_CONN);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
|
|
|
|
BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->bdaddr), conn);
|
|
|
|
if (status) {
|
|
if (conn && conn->state == BT_CONNECT) {
|
|
if (status != 0x0c || conn->attempt > 2) {
|
|
conn->state = BT_CLOSED;
|
|
hci_proto_connect_cfm(conn, status);
|
|
hci_conn_del(conn);
|
|
} else
|
|
conn->state = BT_CONNECT2;
|
|
}
|
|
} else {
|
|
if (!conn) {
|
|
conn = hci_conn_add(hdev, ACL_LINK, 0, &cp->bdaddr);
|
|
if (conn) {
|
|
conn->out = 1;
|
|
conn->link_mode |= HCI_LM_MASTER;
|
|
} else
|
|
BT_ERR("No memory for new connection");
|
|
}
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_add_sco(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_add_sco *cp;
|
|
struct hci_conn *acl, *sco;
|
|
__u16 handle;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
if (!status)
|
|
return;
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_ADD_SCO);
|
|
if (!cp)
|
|
return;
|
|
|
|
handle = __le16_to_cpu(cp->handle);
|
|
|
|
BT_DBG("%s handle %d", hdev->name, handle);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
acl = hci_conn_hash_lookup_handle(hdev, handle);
|
|
if (acl) {
|
|
sco = acl->link;
|
|
if (sco) {
|
|
sco->state = BT_CLOSED;
|
|
|
|
hci_proto_connect_cfm(sco, status);
|
|
hci_conn_del(sco);
|
|
}
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_auth_requested(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_auth_requested *cp;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_AUTH_REQUESTED);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
|
if (conn) {
|
|
if (status) {
|
|
mgmt_auth_failed(hdev->id, &conn->dst, status);
|
|
clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
|
|
|
|
if (conn->state == BT_CONFIG) {
|
|
conn->state = BT_CONNECTED;
|
|
hci_proto_connect_cfm(conn, status);
|
|
hci_conn_put(conn);
|
|
} else {
|
|
hci_auth_cfm(conn, status);
|
|
hci_conn_hold(conn);
|
|
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
|
hci_conn_put(conn);
|
|
}
|
|
|
|
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
|
|
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
|
|
hci_encrypt_cfm(conn, status, 0x00);
|
|
}
|
|
}
|
|
conn->auth_initiator = 1;
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_set_conn_encrypt(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_set_conn_encrypt *cp;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
if (!status)
|
|
return;
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_SET_CONN_ENCRYPT);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
|
if (conn) {
|
|
if (conn->state == BT_CONFIG) {
|
|
hci_proto_connect_cfm(conn, status);
|
|
hci_conn_put(conn);
|
|
}
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static int hci_outgoing_auth_needed(struct hci_dev *hdev,
|
|
struct hci_conn *conn)
|
|
{
|
|
if (conn->state != BT_CONFIG || !conn->out)
|
|
return 0;
|
|
|
|
if (conn->pending_sec_level == BT_SECURITY_SDP)
|
|
return 0;
|
|
|
|
/* Only request authentication for SSP connections or non-SSP
|
|
* devices with sec_level >= BT_SECURITY_MEDIUM*/
|
|
BT_DBG("Pending sec level is %d", conn->pending_sec_level);
|
|
if (!(hdev->ssp_mode > 0 && conn->ssp_mode > 0) &&
|
|
conn->pending_sec_level < BT_SECURITY_MEDIUM)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_remote_name_req *cp;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
/* If successful wait for the name req complete event before
|
|
* checking for the need to do authentication */
|
|
if (!status)
|
|
return;
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_REMOTE_NAME_REQ);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
|
|
if (conn && hci_outgoing_auth_needed(hdev, conn)) {
|
|
struct hci_cp_auth_requested cp;
|
|
cp.handle = __cpu_to_le16(conn->handle);
|
|
hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_read_remote_features(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_read_remote_features *cp;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
if (!status)
|
|
return;
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_FEATURES);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
|
if (conn) {
|
|
if (conn->state == BT_CONFIG) {
|
|
hci_proto_connect_cfm(conn, status);
|
|
hci_conn_put(conn);
|
|
}
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_read_remote_ext_features(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_read_remote_ext_features *cp;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
if (!status)
|
|
return;
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_READ_REMOTE_EXT_FEATURES);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
|
if (conn) {
|
|
if (conn->state == BT_CONFIG) {
|
|
hci_proto_connect_cfm(conn, status);
|
|
hci_conn_put(conn);
|
|
}
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_setup_sync_conn(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_setup_sync_conn *cp;
|
|
struct hci_conn *acl, *sco;
|
|
__u16 handle;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
if (!status)
|
|
return;
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_SETUP_SYNC_CONN);
|
|
if (!cp)
|
|
return;
|
|
|
|
handle = __le16_to_cpu(cp->handle);
|
|
|
|
BT_DBG("%s handle %d", hdev->name, handle);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
acl = hci_conn_hash_lookup_handle(hdev, handle);
|
|
if (acl) {
|
|
sco = acl->link;
|
|
if (sco) {
|
|
sco->state = BT_CLOSED;
|
|
|
|
hci_proto_connect_cfm(sco, status);
|
|
hci_conn_del(sco);
|
|
}
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_sniff_mode *cp;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
if (!status)
|
|
return;
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_SNIFF_MODE);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
|
if (conn) {
|
|
clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend);
|
|
|
|
if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend))
|
|
hci_sco_setup(conn, status);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_exit_sniff_mode *cp;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
if (!status)
|
|
return;
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_EXIT_SNIFF_MODE);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
|
if (conn) {
|
|
clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend);
|
|
|
|
if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend))
|
|
hci_sco_setup(conn, status);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_le_create_conn *cp;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CONN);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr);
|
|
|
|
BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->peer_addr),
|
|
conn);
|
|
|
|
if (status) {
|
|
if (conn && conn->state == BT_CONNECT) {
|
|
conn->state = BT_CLOSED;
|
|
hci_proto_connect_cfm(conn, status);
|
|
hci_conn_del(conn);
|
|
}
|
|
} else {
|
|
if (!conn) {
|
|
conn = hci_le_conn_add(hdev, &cp->peer_addr,
|
|
cp->peer_addr_type);
|
|
if (conn)
|
|
conn->out = 1;
|
|
else
|
|
BT_ERR("No memory for new connection");
|
|
}
|
|
|
|
if (conn)
|
|
mod_timer(&conn->disc_timer,
|
|
jiffies + msecs_to_jiffies(5000));
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_accept_logical_link(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_create_logical_link *ap;
|
|
struct hci_chan *chan;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
ap = hci_sent_cmd_data(hdev, HCI_OP_ACCEPT_LOGICAL_LINK);
|
|
if (!ap)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
chan = hci_chan_list_lookup_id(hdev, ap->phy_handle);
|
|
|
|
BT_DBG("%s chan %p", hdev->name, chan);
|
|
|
|
if (status) {
|
|
if (chan && chan->state == BT_CONNECT) {
|
|
chan->state = BT_CLOSED;
|
|
hci_proto_create_cfm(chan, status);
|
|
}
|
|
} else if (chan) {
|
|
chan->state = BT_CONNECT2;
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_create_logical_link(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_create_logical_link *cp;
|
|
struct hci_chan *chan;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_LOGICAL_LINK);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
chan = hci_chan_list_lookup_id(hdev, cp->phy_handle);
|
|
|
|
BT_DBG("%s chan %p", hdev->name, chan);
|
|
|
|
if (status) {
|
|
if (chan && chan->state == BT_CONNECT) {
|
|
chan->state = BT_CLOSED;
|
|
hci_proto_create_cfm(chan, status);
|
|
}
|
|
} else if (chan)
|
|
chan->state = BT_CONNECT2;
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_flow_spec_modify(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_flow_spec_modify *cp;
|
|
struct hci_chan *chan;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_FLOW_SPEC_MODIFY);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
chan = hci_chan_list_lookup_handle(hdev, cp->log_handle);
|
|
if (chan) {
|
|
if (status)
|
|
hci_proto_modify_cfm(chan, status);
|
|
else {
|
|
chan->tx_fs = cp->tx_fs;
|
|
chan->rx_fs = cp->rx_fs;
|
|
}
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_disconn_logical_link(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_disconn_logical_link *cp;
|
|
struct hci_chan *chan;
|
|
|
|
if (!status)
|
|
return;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_DISCONN_LOGICAL_LINK);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
chan = hci_chan_list_lookup_handle(hdev, cp->log_handle);
|
|
if (chan)
|
|
hci_chan_del(chan);
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_disconn_physical_link(struct hci_dev *hdev, __u8 status)
|
|
{
|
|
struct hci_cp_disconn_phys_link *cp;
|
|
struct hci_conn *conn;
|
|
|
|
if (!status)
|
|
return;
|
|
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
|
cp = hci_sent_cmd_data(hdev, HCI_OP_DISCONN_PHYS_LINK);
|
|
if (!cp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, cp->phy_handle);
|
|
if (conn) {
|
|
conn->state = BT_CLOSED;
|
|
hci_conn_del(conn);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
|
|
{
|
|
BT_DBG("%s status 0x%x", hdev->name, status);
|
|
}
|
|
|
|
static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
__u8 status = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s status %d", hdev->name, status);
|
|
|
|
if (!hdev->disco_state)
|
|
clear_bit(HCI_INQUIRY, &hdev->flags);
|
|
|
|
hci_req_complete(hdev, HCI_OP_INQUIRY, status);
|
|
hci_dev_lock(hdev);
|
|
|
|
if (test_bit(HCI_MGMT, &hdev->flags))
|
|
mgmt_inquiry_complete_evt(hdev->id, status);
|
|
hci_dev_unlock(hdev);
|
|
|
|
if (!lmp_le_capable(hdev))
|
|
hci_conn_check_pending(hdev);
|
|
}
|
|
|
|
static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct inquiry_data data;
|
|
struct inquiry_info *info = (void *) (skb->data + 1);
|
|
int num_rsp = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
|
|
|
|
if (!num_rsp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
for (; num_rsp; num_rsp--, info++) {
|
|
bacpy(&data.bdaddr, &info->bdaddr);
|
|
data.pscan_rep_mode = info->pscan_rep_mode;
|
|
data.pscan_period_mode = info->pscan_period_mode;
|
|
data.pscan_mode = info->pscan_mode;
|
|
memcpy(data.dev_class, info->dev_class, 3);
|
|
data.clock_offset = info->clock_offset;
|
|
data.rssi = 0x00;
|
|
data.ssp_mode = 0x00;
|
|
hci_inquiry_cache_update(hdev, &data);
|
|
mgmt_device_found(hdev->id, &info->bdaddr, 0, 0,
|
|
info->dev_class, 0, 0, NULL);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_conn_complete *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr);
|
|
if (!conn) {
|
|
if (ev->link_type != SCO_LINK)
|
|
goto unlock;
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK, &ev->bdaddr);
|
|
if (!conn)
|
|
goto unlock;
|
|
|
|
conn->type = SCO_LINK;
|
|
}
|
|
|
|
if (!ev->status) {
|
|
conn->handle = __le16_to_cpu(ev->handle);
|
|
|
|
if (conn->type == ACL_LINK) {
|
|
conn->state = BT_CONFIG;
|
|
hci_conn_hold(conn);
|
|
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
|
mgmt_connected(hdev->id, &ev->bdaddr, 0);
|
|
} else if (conn->type == LE_LINK) {
|
|
conn->state = BT_CONNECTED;
|
|
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
|
mgmt_connected(hdev->id, &ev->bdaddr, 1);
|
|
} else
|
|
conn->state = BT_CONNECTED;
|
|
|
|
hci_conn_hold_device(conn);
|
|
hci_conn_add_sysfs(conn);
|
|
|
|
if (test_bit(HCI_AUTH, &hdev->flags))
|
|
conn->link_mode |= HCI_LM_AUTH;
|
|
|
|
if (test_bit(HCI_ENCRYPT, &hdev->flags))
|
|
conn->link_mode |= HCI_LM_ENCRYPT;
|
|
|
|
/* Get remote version */
|
|
if (conn->type == ACL_LINK) {
|
|
struct hci_cp_read_remote_version cp;
|
|
cp.handle = ev->handle;
|
|
hci_send_cmd(hdev, HCI_OP_READ_REMOTE_VERSION,
|
|
sizeof(cp), &cp);
|
|
}
|
|
|
|
/* Set packet type for incoming connection */
|
|
if (!conn->out && hdev->hci_ver < 3) {
|
|
struct hci_cp_change_conn_ptype cp;
|
|
cp.handle = ev->handle;
|
|
cp.pkt_type = cpu_to_le16(conn->pkt_type);
|
|
hci_send_cmd(hdev, HCI_OP_CHANGE_CONN_PTYPE,
|
|
sizeof(cp), &cp);
|
|
}
|
|
} else {
|
|
conn->state = BT_CLOSED;
|
|
if (conn->type == ACL_LINK || conn->type == LE_LINK)
|
|
mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status);
|
|
}
|
|
|
|
if (conn->type == ACL_LINK)
|
|
hci_sco_setup(conn, ev->status);
|
|
|
|
if (ev->status) {
|
|
hci_proto_connect_cfm(conn, ev->status);
|
|
hci_conn_del(conn);
|
|
} else if (ev->link_type != ACL_LINK)
|
|
hci_proto_connect_cfm(conn, ev->status);
|
|
|
|
unlock:
|
|
hci_dev_unlock(hdev);
|
|
|
|
hci_conn_check_pending(hdev);
|
|
}
|
|
|
|
static inline bool is_sco_active(struct hci_dev *hdev)
|
|
{
|
|
if (hci_conn_hash_lookup_state(hdev, SCO_LINK, BT_CONNECTED) ||
|
|
(hci_conn_hash_lookup_state(hdev, ESCO_LINK,
|
|
BT_CONNECTED)))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_conn_request *ev = (void *) skb->data;
|
|
int mask = hdev->link_mode;
|
|
|
|
BT_DBG("%s bdaddr %s type 0x%x", hdev->name,
|
|
batostr(&ev->bdaddr), ev->link_type);
|
|
|
|
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
|
|
|
|
if ((mask & HCI_LM_ACCEPT) &&
|
|
!hci_blacklist_lookup(hdev, &ev->bdaddr)) {
|
|
/* Connection accepted */
|
|
struct inquiry_entry *ie;
|
|
struct hci_conn *conn;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr);
|
|
if (ie)
|
|
memcpy(ie->data.dev_class, ev->dev_class, 3);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr);
|
|
if (!conn) {
|
|
/* pkt_type not yet used for incoming connections */
|
|
conn = hci_conn_add(hdev, ev->link_type, 0, &ev->bdaddr);
|
|
if (!conn) {
|
|
BT_ERR("No memory for new connection");
|
|
hci_dev_unlock(hdev);
|
|
return;
|
|
}
|
|
}
|
|
|
|
memcpy(conn->dev_class, ev->dev_class, 3);
|
|
/* For incoming connection update remote class to userspace */
|
|
mgmt_remote_class(hdev->id, &ev->bdaddr, ev->dev_class);
|
|
conn->state = BT_CONNECT;
|
|
|
|
hci_dev_unlock(hdev);
|
|
|
|
if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) {
|
|
struct hci_cp_accept_conn_req cp;
|
|
|
|
bacpy(&cp.bdaddr, &ev->bdaddr);
|
|
|
|
if (lmp_rswitch_capable(hdev) && ((mask & HCI_LM_MASTER)
|
|
|| is_sco_active(hdev)))
|
|
cp.role = 0x00; /* Become master */
|
|
else
|
|
cp.role = 0x01; /* Remain slave */
|
|
|
|
hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ,
|
|
sizeof(cp), &cp);
|
|
} else {
|
|
struct hci_cp_accept_sync_conn_req cp;
|
|
|
|
bacpy(&cp.bdaddr, &ev->bdaddr);
|
|
cp.pkt_type = cpu_to_le16(conn->pkt_type);
|
|
|
|
cp.tx_bandwidth = cpu_to_le32(0x00001f40);
|
|
cp.rx_bandwidth = cpu_to_le32(0x00001f40);
|
|
cp.max_latency = cpu_to_le16(0x000A);
|
|
cp.content_format = cpu_to_le16(hdev->voice_setting);
|
|
cp.retrans_effort = 0x01;
|
|
|
|
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
|
|
sizeof(cp), &cp);
|
|
}
|
|
} else {
|
|
/* Connection rejected */
|
|
struct hci_cp_reject_conn_req cp;
|
|
|
|
bacpy(&cp.bdaddr, &ev->bdaddr);
|
|
cp.reason = 0x0f;
|
|
hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp);
|
|
}
|
|
}
|
|
|
|
static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_disconn_complete *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
|
if (ev->status) {
|
|
hci_dev_lock(hdev);
|
|
mgmt_disconnect_failed(hdev->id);
|
|
hci_dev_unlock(hdev);
|
|
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 (conn->type == ACL_LINK || conn->type == LE_LINK)
|
|
mgmt_disconnected(hdev->id, &conn->dst);
|
|
|
|
if (conn->type == LE_LINK)
|
|
del_timer(&conn->smp_timer);
|
|
|
|
hci_proto_disconn_cfm(conn, ev->reason, 0);
|
|
hci_conn_del(conn);
|
|
|
|
unlock:
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_auth_complete *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
|
if (conn) {
|
|
if (ev->status == 0x06 && hdev->ssp_mode > 0 &&
|
|
conn->ssp_mode > 0) {
|
|
struct hci_cp_auth_requested cp;
|
|
hci_remove_link_key(hdev, &conn->dst);
|
|
cp.handle = cpu_to_le16(conn->handle);
|
|
hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
|
|
sizeof(cp), &cp);
|
|
hci_dev_unlock(hdev);
|
|
BT_INFO("Pin or key missing");
|
|
return;
|
|
}
|
|
|
|
if (!ev->status) {
|
|
conn->link_mode |= HCI_LM_AUTH;
|
|
conn->sec_level = conn->pending_sec_level;
|
|
} else {
|
|
mgmt_auth_failed(hdev->id, &conn->dst, ev->status);
|
|
conn->sec_level = BT_SECURITY_LOW;
|
|
}
|
|
|
|
clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
|
|
|
|
if (conn->state == BT_CONFIG) {
|
|
if (!ev->status && hdev->ssp_mode > 0 &&
|
|
conn->ssp_mode > 0) {
|
|
struct hci_cp_set_conn_encrypt cp;
|
|
cp.handle = ev->handle;
|
|
cp.encrypt = 0x01;
|
|
hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT,
|
|
sizeof(cp), &cp);
|
|
} else {
|
|
conn->state = BT_CONNECTED;
|
|
hci_proto_connect_cfm(conn, ev->status);
|
|
hci_conn_put(conn);
|
|
}
|
|
} else {
|
|
hci_auth_cfm(conn, ev->status);
|
|
|
|
hci_conn_hold(conn);
|
|
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
|
hci_conn_put(conn);
|
|
}
|
|
|
|
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
|
|
if (!ev->status) {
|
|
struct hci_cp_set_conn_encrypt cp;
|
|
cp.handle = ev->handle;
|
|
cp.encrypt = 0x01;
|
|
hci_send_cmd(hdev, HCI_OP_SET_CONN_ENCRYPT,
|
|
sizeof(cp), &cp);
|
|
} else {
|
|
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
|
|
hci_encrypt_cfm(conn, ev->status, 0x00);
|
|
}
|
|
}
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_remote_name *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
hci_conn_check_pending(hdev);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
if (test_bit(HCI_MGMT, &hdev->flags))
|
|
mgmt_remote_name(hdev->id, &ev->bdaddr, ev->status, ev->name);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
|
if (conn && hci_outgoing_auth_needed(hdev, conn)) {
|
|
struct hci_cp_auth_requested cp;
|
|
cp.handle = __cpu_to_le16(conn->handle);
|
|
hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_encrypt_change *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
|
if (conn) {
|
|
if (!ev->status) {
|
|
if (ev->encrypt) {
|
|
/* Encryption implies authentication */
|
|
conn->link_mode |= HCI_LM_AUTH;
|
|
conn->link_mode |= HCI_LM_ENCRYPT;
|
|
conn->sec_level = conn->pending_sec_level;
|
|
} else
|
|
conn->link_mode &= ~HCI_LM_ENCRYPT;
|
|
}
|
|
|
|
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
|
|
|
|
if (conn->state == BT_CONFIG) {
|
|
if (!ev->status)
|
|
conn->state = BT_CONNECTED;
|
|
|
|
hci_proto_connect_cfm(conn, ev->status);
|
|
hci_conn_put(conn);
|
|
} else
|
|
hci_encrypt_cfm(conn, ev->status, ev->encrypt);
|
|
|
|
if (test_bit(HCI_MGMT, &hdev->flags))
|
|
mgmt_encrypt_change(hdev->id, &conn->dst, ev->status);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_change_link_key_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_change_link_key_complete *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
|
if (conn) {
|
|
if (!ev->status)
|
|
conn->link_mode |= HCI_LM_SECURE;
|
|
|
|
clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
|
|
|
|
hci_key_change_cfm(conn, ev->status);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_remote_features_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_remote_features *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
|
if (!conn)
|
|
goto unlock;
|
|
|
|
if (!ev->status) {
|
|
memcpy(conn->features, ev->features, 8);
|
|
mgmt_remote_features(hdev->id, &conn->dst, ev->features);
|
|
}
|
|
|
|
if (conn->state != BT_CONFIG)
|
|
goto unlock;
|
|
|
|
if (!ev->status && lmp_ssp_capable(hdev) && lmp_ssp_capable(conn)) {
|
|
struct hci_cp_read_remote_ext_features cp;
|
|
cp.handle = ev->handle;
|
|
cp.page = 0x01;
|
|
hci_send_cmd(hdev, HCI_OP_READ_REMOTE_EXT_FEATURES,
|
|
sizeof(cp), &cp);
|
|
goto unlock;
|
|
} else if (!(lmp_ssp_capable(conn)) && conn->auth_initiator &&
|
|
(conn->pending_sec_level == BT_SECURITY_HIGH)) {
|
|
conn->pending_sec_level = BT_SECURITY_MEDIUM;
|
|
}
|
|
|
|
if (!ev->status) {
|
|
struct hci_cp_remote_name_req cp;
|
|
memset(&cp, 0, sizeof(cp));
|
|
bacpy(&cp.bdaddr, &conn->dst);
|
|
cp.pscan_rep_mode = 0x02;
|
|
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
|
|
}
|
|
|
|
if (!hci_outgoing_auth_needed(hdev, conn)) {
|
|
conn->state = BT_CONNECTED;
|
|
hci_proto_connect_cfm(conn, ev->status);
|
|
hci_conn_put(conn);
|
|
}
|
|
|
|
unlock:
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_remote_version_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_remote_version *ev = (void *) skb->data;
|
|
struct hci_cp_read_remote_features cp;
|
|
struct hci_conn *conn;
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
cp.handle = ev->handle;
|
|
hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
|
|
sizeof(cp), &cp);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
|
if (!conn)
|
|
goto unlock;
|
|
if (!ev->status)
|
|
mgmt_remote_version(hdev->id, &conn->dst, ev->lmp_ver,
|
|
ev->manufacturer, ev->lmp_subver);
|
|
unlock:
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_qos_setup_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
BT_DBG("%s", hdev->name);
|
|
}
|
|
|
|
static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_cmd_complete *ev = (void *) skb->data;
|
|
__u16 opcode;
|
|
|
|
skb_pull(skb, sizeof(*ev));
|
|
|
|
opcode = __le16_to_cpu(ev->opcode);
|
|
|
|
switch (opcode) {
|
|
case HCI_OP_INQUIRY_CANCEL:
|
|
hci_cc_inquiry_cancel(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_EXIT_PERIODIC_INQ:
|
|
hci_cc_exit_periodic_inq(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_LINK_KEY_REPLY:
|
|
hci_cc_link_key_reply(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_REMOTE_NAME_REQ_CANCEL:
|
|
hci_cc_remote_name_req_cancel(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_ROLE_DISCOVERY:
|
|
hci_cc_role_discovery(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_LINK_POLICY:
|
|
hci_cc_read_link_policy(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_WRITE_LINK_POLICY:
|
|
hci_cc_write_link_policy(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_DEF_LINK_POLICY:
|
|
hci_cc_read_def_link_policy(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_WRITE_DEF_LINK_POLICY:
|
|
hci_cc_write_def_link_policy(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_RESET:
|
|
hci_cc_reset(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_WRITE_LOCAL_NAME:
|
|
hci_cc_write_local_name(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_LOCAL_NAME:
|
|
hci_cc_read_local_name(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_WRITE_AUTH_ENABLE:
|
|
hci_cc_write_auth_enable(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_WRITE_ENCRYPT_MODE:
|
|
hci_cc_write_encrypt_mode(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_WRITE_SCAN_ENABLE:
|
|
hci_cc_write_scan_enable(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_CLASS_OF_DEV:
|
|
hci_cc_read_class_of_dev(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_WRITE_CLASS_OF_DEV:
|
|
hci_cc_write_class_of_dev(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_VOICE_SETTING:
|
|
hci_cc_read_voice_setting(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_WRITE_VOICE_SETTING:
|
|
hci_cc_write_voice_setting(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_HOST_BUFFER_SIZE:
|
|
hci_cc_host_buffer_size(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_SSP_MODE:
|
|
hci_cc_read_ssp_mode(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_WRITE_SSP_MODE:
|
|
hci_cc_write_ssp_mode(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_LOCAL_VERSION:
|
|
hci_cc_read_local_version(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_LOCAL_COMMANDS:
|
|
hci_cc_read_local_commands(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_LOCAL_FEATURES:
|
|
hci_cc_read_local_features(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_BUFFER_SIZE:
|
|
hci_cc_read_buffer_size(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_BD_ADDR:
|
|
hci_cc_read_bd_addr(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_WRITE_CA_TIMEOUT:
|
|
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_DATA_BLOCK_SIZE:
|
|
hci_cc_read_data_block_size(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_LOCAL_AMP_INFO:
|
|
hci_cc_read_local_amp_info(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_LOCAL_AMP_ASSOC:
|
|
case HCI_OP_WRITE_REMOTE_AMP_ASSOC:
|
|
hci_amp_cmd_complete(hdev, opcode, skb);
|
|
break;
|
|
|
|
case HCI_OP_DELETE_STORED_LINK_KEY:
|
|
hci_cc_delete_stored_link_key(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_SET_EVENT_MASK:
|
|
hci_cc_set_event_mask(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_WRITE_INQUIRY_MODE:
|
|
hci_cc_write_inquiry_mode(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_INQ_RSP_TX_POWER:
|
|
hci_cc_read_inq_rsp_tx_power(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_SET_EVENT_FLT:
|
|
hci_cc_set_event_flt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_PIN_CODE_REPLY:
|
|
hci_cc_pin_code_reply(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_PIN_CODE_NEG_REPLY:
|
|
hci_cc_pin_code_neg_reply(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_LOCAL_OOB_DATA:
|
|
hci_cc_read_local_oob_data_reply(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_LE_READ_BUFFER_SIZE:
|
|
hci_cc_le_read_buffer_size(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_READ_RSSI:
|
|
hci_cc_read_rssi(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_USER_CONFIRM_REPLY:
|
|
hci_cc_user_confirm_reply(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_USER_CONFIRM_NEG_REPLY:
|
|
hci_cc_user_confirm_neg_reply(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_LE_LTK_REPLY:
|
|
hci_cc_le_ltk_reply(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_LE_LTK_NEG_REPLY:
|
|
hci_cc_le_ltk_neg_reply(hdev, skb);
|
|
break;
|
|
|
|
case HCI_OP_LE_SET_SCAN_ENABLE:
|
|
hci_cc_le_set_scan_enable(hdev, skb);
|
|
break;
|
|
|
|
default:
|
|
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
|
break;
|
|
}
|
|
|
|
if (ev->opcode != HCI_OP_NOP)
|
|
del_timer(&hdev->cmd_timer);
|
|
|
|
if (ev->ncmd) {
|
|
atomic_set(&hdev->cmd_cnt, 1);
|
|
if (!skb_queue_empty(&hdev->cmd_q))
|
|
tasklet_schedule(&hdev->cmd_task);
|
|
}
|
|
}
|
|
|
|
static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_cmd_status *ev = (void *) skb->data;
|
|
__u16 opcode;
|
|
|
|
skb_pull(skb, sizeof(*ev));
|
|
|
|
opcode = __le16_to_cpu(ev->opcode);
|
|
|
|
switch (opcode) {
|
|
case HCI_OP_INQUIRY:
|
|
hci_cs_inquiry(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_CREATE_CONN:
|
|
hci_cs_create_conn(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_ADD_SCO:
|
|
hci_cs_add_sco(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_AUTH_REQUESTED:
|
|
hci_cs_auth_requested(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_SET_CONN_ENCRYPT:
|
|
hci_cs_set_conn_encrypt(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_REMOTE_NAME_REQ:
|
|
hci_cs_remote_name_req(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_READ_REMOTE_FEATURES:
|
|
hci_cs_read_remote_features(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_READ_REMOTE_EXT_FEATURES:
|
|
hci_cs_read_remote_ext_features(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_SETUP_SYNC_CONN:
|
|
hci_cs_setup_sync_conn(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_SNIFF_MODE:
|
|
hci_cs_sniff_mode(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_EXIT_SNIFF_MODE:
|
|
hci_cs_exit_sniff_mode(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_CREATE_LOGICAL_LINK:
|
|
hci_cs_create_logical_link(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_ACCEPT_LOGICAL_LINK:
|
|
hci_cs_accept_logical_link(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_DISCONN_LOGICAL_LINK:
|
|
hci_cs_disconn_logical_link(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_FLOW_SPEC_MODIFY:
|
|
hci_cs_flow_spec_modify(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_CREATE_PHYS_LINK:
|
|
case HCI_OP_ACCEPT_PHYS_LINK:
|
|
hci_amp_cmd_status(hdev, opcode, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_DISCONN_PHYS_LINK:
|
|
hci_cs_disconn_physical_link(hdev, ev->status);
|
|
|
|
case HCI_OP_DISCONNECT:
|
|
if (ev->status != 0)
|
|
mgmt_disconnect_failed(hdev->id);
|
|
break;
|
|
|
|
case HCI_OP_LE_CREATE_CONN:
|
|
hci_cs_le_create_conn(hdev, ev->status);
|
|
break;
|
|
|
|
case HCI_OP_LE_START_ENC:
|
|
hci_cs_le_start_enc(hdev, ev->status);
|
|
break;
|
|
|
|
default:
|
|
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
|
break;
|
|
}
|
|
|
|
if (ev->opcode != HCI_OP_NOP)
|
|
del_timer(&hdev->cmd_timer);
|
|
|
|
if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
|
|
atomic_set(&hdev->cmd_cnt, 1);
|
|
if (!skb_queue_empty(&hdev->cmd_q))
|
|
tasklet_schedule(&hdev->cmd_task);
|
|
}
|
|
}
|
|
|
|
static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_role_change *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
|
if (conn) {
|
|
if (!ev->status) {
|
|
if (ev->role)
|
|
conn->link_mode &= ~HCI_LM_MASTER;
|
|
else
|
|
conn->link_mode |= HCI_LM_MASTER;
|
|
}
|
|
|
|
clear_bit(HCI_CONN_RSWITCH_PEND, &conn->pend);
|
|
|
|
hci_role_switch_cfm(conn, ev->status, ev->role);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_num_comp_pkts *ev = (void *) skb->data;
|
|
__le16 *ptr;
|
|
int i;
|
|
|
|
skb_pull(skb, sizeof(*ev));
|
|
|
|
BT_DBG("%s num_hndl %d", hdev->name, ev->num_hndl);
|
|
|
|
if (skb->len < ev->num_hndl * 4) {
|
|
BT_DBG("%s bad parameters", hdev->name);
|
|
return;
|
|
}
|
|
|
|
tasklet_disable(&hdev->tx_task);
|
|
|
|
for (i = 0, ptr = (__le16 *) skb->data; i < ev->num_hndl; i++) {
|
|
struct hci_conn *conn = NULL;
|
|
struct hci_chan *chan;
|
|
__u16 handle, count;
|
|
|
|
handle = get_unaligned_le16(ptr++);
|
|
count = get_unaligned_le16(ptr++);
|
|
|
|
if (hdev->dev_type == HCI_BREDR)
|
|
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
|
else {
|
|
chan = hci_chan_list_lookup_handle(hdev, handle);
|
|
if (chan)
|
|
conn = chan->conn;
|
|
}
|
|
if (conn) {
|
|
conn->sent -= count;
|
|
|
|
if (conn->type == ACL_LINK) {
|
|
hdev->acl_cnt += count;
|
|
if (hdev->acl_cnt > hdev->acl_pkts)
|
|
hdev->acl_cnt = hdev->acl_pkts;
|
|
} else if (conn->type == LE_LINK) {
|
|
if (hdev->le_pkts) {
|
|
hdev->le_cnt += count;
|
|
if (hdev->le_cnt > hdev->le_pkts)
|
|
hdev->le_cnt = hdev->le_pkts;
|
|
} else {
|
|
hdev->acl_cnt += count;
|
|
if (hdev->acl_cnt > hdev->acl_pkts)
|
|
hdev->acl_cnt = hdev->acl_pkts;
|
|
}
|
|
} else {
|
|
hdev->sco_cnt += count;
|
|
if (hdev->sco_cnt > hdev->sco_pkts)
|
|
hdev->sco_cnt = hdev->sco_pkts;
|
|
}
|
|
}
|
|
}
|
|
|
|
tasklet_schedule(&hdev->tx_task);
|
|
|
|
tasklet_enable(&hdev->tx_task);
|
|
}
|
|
|
|
static inline void hci_num_comp_blocks_evt(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_num_comp_blocks *ev = (void *) skb->data;
|
|
__le16 *ptr;
|
|
int i;
|
|
|
|
skb_pull(skb, sizeof(*ev));
|
|
|
|
BT_DBG("%s total_num_blocks %d num_hndl %d",
|
|
hdev->name, ev->total_num_blocks, ev->num_hndl);
|
|
|
|
if (skb->len < ev->num_hndl * 6) {
|
|
BT_DBG("%s bad parameters", hdev->name);
|
|
return;
|
|
}
|
|
|
|
tasklet_disable(&hdev->tx_task);
|
|
|
|
for (i = 0, ptr = (__le16 *) skb->data; i < ev->num_hndl; i++) {
|
|
struct hci_conn *conn = NULL;
|
|
struct hci_chan *chan;
|
|
__u16 handle, block_count;
|
|
|
|
handle = get_unaligned_le16(ptr++);
|
|
|
|
/* Skip packet count */
|
|
ptr++;
|
|
block_count = get_unaligned_le16(ptr++);
|
|
|
|
BT_DBG("%s handle %d count %d", hdev->name, handle,
|
|
block_count);
|
|
|
|
if (hdev->dev_type == HCI_BREDR)
|
|
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
|
else {
|
|
chan = hci_chan_list_lookup_handle(hdev, handle);
|
|
if (chan)
|
|
conn = chan->conn;
|
|
}
|
|
if (conn) {
|
|
BT_DBG("%s conn %p sent %d", hdev->name,
|
|
conn, conn->sent);
|
|
|
|
conn->sent -= block_count;
|
|
|
|
if (conn->type == ACL_LINK) {
|
|
hdev->acl_cnt += block_count;
|
|
if (hdev->acl_cnt > hdev->acl_pkts)
|
|
hdev->acl_cnt = hdev->acl_pkts;
|
|
} else {
|
|
/* We should not find ourselves here */
|
|
BT_DBG("Unexpected event for SCO connection");
|
|
}
|
|
}
|
|
}
|
|
|
|
tasklet_schedule(&hdev->tx_task);
|
|
|
|
tasklet_enable(&hdev->tx_task);
|
|
}
|
|
|
|
static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_mode_change *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
|
if (conn) {
|
|
conn->mode = ev->mode;
|
|
conn->interval = __le16_to_cpu(ev->interval);
|
|
|
|
if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend)) {
|
|
if (conn->mode == HCI_CM_ACTIVE)
|
|
conn->power_save = 1;
|
|
else
|
|
conn->power_save = 0;
|
|
}
|
|
|
|
if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend))
|
|
hci_sco_setup(conn, ev->status);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_pin_code_req *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
|
if (conn && conn->state == BT_CONNECTED) {
|
|
hci_conn_hold(conn);
|
|
conn->disc_timeout = HCI_PAIRING_TIMEOUT;
|
|
hci_conn_put(conn);
|
|
hci_conn_enter_active_mode(conn, 0);
|
|
}
|
|
|
|
if (!test_bit(HCI_PAIRABLE, &hdev->flags))
|
|
hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
|
|
sizeof(ev->bdaddr), &ev->bdaddr);
|
|
|
|
if (test_bit(HCI_MGMT, &hdev->flags))
|
|
mgmt_pin_code_request(hdev->id, &ev->bdaddr);
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_link_key_req *ev = (void *) skb->data;
|
|
struct hci_cp_link_key_reply cp;
|
|
struct hci_conn *conn;
|
|
struct link_key *key;
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
if (!test_bit(HCI_LINK_KEYS, &hdev->flags))
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
key = hci_find_link_key(hdev, &ev->bdaddr);
|
|
if (!key) {
|
|
BT_DBG("%s link key not found for %s", hdev->name,
|
|
batostr(&ev->bdaddr));
|
|
goto not_found;
|
|
}
|
|
|
|
BT_DBG("%s found key type %u for %s", hdev->name, key->key_type,
|
|
batostr(&ev->bdaddr));
|
|
|
|
if (!test_bit(HCI_DEBUG_KEYS, &hdev->flags) && key->key_type == 0x03) {
|
|
BT_DBG("%s ignoring debug key", hdev->name);
|
|
goto not_found;
|
|
}
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
|
|
|
if (conn) {
|
|
BT_DBG("Conn pending sec level is %d, ssp is %d, key len is %d",
|
|
conn->pending_sec_level, conn->ssp_mode, key->pin_len);
|
|
}
|
|
if (conn && (conn->ssp_mode == 0) &&
|
|
(conn->pending_sec_level == BT_SECURITY_HIGH) &&
|
|
(key->pin_len != 16)) {
|
|
BT_DBG("Security is high ignoring this key");
|
|
goto not_found;
|
|
}
|
|
|
|
if (key->key_type == 0x04 && conn && conn->auth_type != 0xff &&
|
|
(conn->auth_type & 0x01)) {
|
|
BT_DBG("%s ignoring unauthenticated key", hdev->name);
|
|
goto not_found;
|
|
}
|
|
|
|
bacpy(&cp.bdaddr, &ev->bdaddr);
|
|
memcpy(cp.link_key, key->val, 16);
|
|
|
|
hci_send_cmd(hdev, HCI_OP_LINK_KEY_REPLY, sizeof(cp), &cp);
|
|
|
|
hci_dev_unlock(hdev);
|
|
|
|
return;
|
|
|
|
not_found:
|
|
hci_send_cmd(hdev, HCI_OP_LINK_KEY_NEG_REPLY, 6, &ev->bdaddr);
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_link_key_notify *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
u8 pin_len = 0;
|
|
|
|
BT_DBG("%s type %d", hdev->name, ev->key_type);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
|
if (conn) {
|
|
hci_conn_hold(conn);
|
|
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
|
|
|
memcpy(conn->link_key, ev->link_key, 16);
|
|
conn->key_type = ev->key_type;
|
|
hci_disconnect_amp(conn, 0x06);
|
|
|
|
pin_len = conn->pin_length;
|
|
hci_conn_put(conn);
|
|
hci_conn_enter_active_mode(conn, 0);
|
|
}
|
|
|
|
if (test_bit(HCI_LINK_KEYS, &hdev->flags))
|
|
hci_add_link_key(hdev, 1, &ev->bdaddr, ev->link_key,
|
|
ev->key_type, pin_len);
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_clock_offset_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_clock_offset *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
|
if (conn && !ev->status) {
|
|
struct inquiry_entry *ie;
|
|
|
|
ie = hci_inquiry_cache_lookup(hdev, &conn->dst);
|
|
if (ie) {
|
|
ie->data.clock_offset = ev->clock_offset;
|
|
ie->timestamp = jiffies;
|
|
}
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_pkt_type_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_pkt_type_change *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
|
if (conn && !ev->status)
|
|
conn->pkt_type = __le16_to_cpu(ev->pkt_type);
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_pscan_rep_mode_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_pscan_rep_mode *ev = (void *) skb->data;
|
|
struct inquiry_entry *ie;
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr);
|
|
if (ie) {
|
|
ie->data.pscan_rep_mode = ev->pscan_rep_mode;
|
|
ie->timestamp = jiffies;
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct inquiry_data data;
|
|
int num_rsp = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
|
|
|
|
if (!num_rsp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
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);
|
|
|
|
for (; num_rsp; num_rsp--, info++) {
|
|
bacpy(&data.bdaddr, &info->bdaddr);
|
|
data.pscan_rep_mode = info->pscan_rep_mode;
|
|
data.pscan_period_mode = info->pscan_period_mode;
|
|
data.pscan_mode = info->pscan_mode;
|
|
memcpy(data.dev_class, info->dev_class, 3);
|
|
data.clock_offset = info->clock_offset;
|
|
data.rssi = info->rssi;
|
|
data.ssp_mode = 0x00;
|
|
hci_inquiry_cache_update(hdev, &data);
|
|
mgmt_device_found(hdev->id, &info->bdaddr, 0, 0,
|
|
info->dev_class, info->rssi,
|
|
0, NULL);
|
|
}
|
|
} else {
|
|
struct inquiry_info_with_rssi *info = (void *) (skb->data + 1);
|
|
|
|
for (; num_rsp; num_rsp--, info++) {
|
|
bacpy(&data.bdaddr, &info->bdaddr);
|
|
data.pscan_rep_mode = info->pscan_rep_mode;
|
|
data.pscan_period_mode = info->pscan_period_mode;
|
|
data.pscan_mode = 0x00;
|
|
memcpy(data.dev_class, info->dev_class, 3);
|
|
data.clock_offset = info->clock_offset;
|
|
data.rssi = info->rssi;
|
|
data.ssp_mode = 0x00;
|
|
hci_inquiry_cache_update(hdev, &data);
|
|
mgmt_device_found(hdev->id, &info->bdaddr, 0, 0,
|
|
info->dev_class, info->rssi,
|
|
0, NULL);
|
|
}
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_remote_ext_features *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
|
if (!conn)
|
|
goto unlock;
|
|
|
|
if (!ev->status && ev->page == 0x01) {
|
|
struct inquiry_entry *ie;
|
|
|
|
ie = hci_inquiry_cache_lookup(hdev, &conn->dst);
|
|
if (ie)
|
|
ie->data.ssp_mode = (ev->features[0] & 0x01);
|
|
|
|
conn->ssp_mode = (ev->features[0] & 0x01);
|
|
/*In case if remote device ssp supported/2.0 device
|
|
reduce the security level to MEDIUM if it is HIGH*/
|
|
if (!conn->ssp_mode && conn->auth_initiator &&
|
|
(conn->pending_sec_level == BT_SECURITY_HIGH))
|
|
conn->pending_sec_level = BT_SECURITY_MEDIUM;
|
|
}
|
|
|
|
if (conn->state != BT_CONFIG)
|
|
goto unlock;
|
|
|
|
if (!ev->status) {
|
|
struct hci_cp_remote_name_req cp;
|
|
memset(&cp, 0, sizeof(cp));
|
|
bacpy(&cp.bdaddr, &conn->dst);
|
|
cp.pscan_rep_mode = 0x02;
|
|
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
|
|
}
|
|
|
|
if (!hci_outgoing_auth_needed(hdev, conn)) {
|
|
conn->state = BT_CONNECTED;
|
|
hci_proto_connect_cfm(conn, ev->status);
|
|
hci_conn_put(conn);
|
|
}
|
|
|
|
unlock:
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_sync_conn_complete *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr);
|
|
if (!conn) {
|
|
if (ev->link_type == ESCO_LINK)
|
|
goto unlock;
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ESCO_LINK, &ev->bdaddr);
|
|
if (!conn)
|
|
goto unlock;
|
|
|
|
conn->type = SCO_LINK;
|
|
}
|
|
|
|
switch (ev->status) {
|
|
case 0x00:
|
|
conn->handle = __le16_to_cpu(ev->handle);
|
|
conn->state = BT_CONNECTED;
|
|
|
|
hci_conn_hold_device(conn);
|
|
hci_conn_add_sysfs(conn);
|
|
break;
|
|
|
|
case 0x11: /* Unsupported Feature or Parameter Value */
|
|
case 0x1c: /* SCO interval rejected */
|
|
case 0x1a: /* Unsupported Remote Feature */
|
|
case 0x1f: /* Unspecified error */
|
|
if (conn->out && conn->attempt < 2) {
|
|
if (!conn->hdev->is_wbs)
|
|
conn->pkt_type =
|
|
(hdev->esco_type & SCO_ESCO_MASK) |
|
|
(hdev->esco_type & EDR_ESCO_MASK);
|
|
hci_setup_sync(conn, conn->link->handle);
|
|
goto unlock;
|
|
}
|
|
/* fall through */
|
|
|
|
default:
|
|
conn->state = BT_CLOSED;
|
|
break;
|
|
}
|
|
|
|
hci_proto_connect_cfm(conn, ev->status);
|
|
if (ev->status)
|
|
hci_conn_del(conn);
|
|
|
|
unlock:
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_sync_conn_changed_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
BT_DBG("%s", hdev->name);
|
|
}
|
|
|
|
static inline void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_sniff_subrate *ev = (void *) skb->data;
|
|
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
}
|
|
|
|
static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct inquiry_data data;
|
|
struct extended_inquiry_info *info = (void *) (skb->data + 1);
|
|
int num_rsp = *((__u8 *) skb->data);
|
|
|
|
BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
|
|
|
|
if (!num_rsp)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
for (; num_rsp; num_rsp--, info++) {
|
|
bacpy(&data.bdaddr, &info->bdaddr);
|
|
data.pscan_rep_mode = info->pscan_rep_mode;
|
|
data.pscan_period_mode = info->pscan_period_mode;
|
|
data.pscan_mode = 0x00;
|
|
memcpy(data.dev_class, info->dev_class, 3);
|
|
data.clock_offset = info->clock_offset;
|
|
data.rssi = info->rssi;
|
|
data.ssp_mode = 0x01;
|
|
hci_inquiry_cache_update(hdev, &data);
|
|
mgmt_device_found(hdev->id, &info->bdaddr, 0, 0,
|
|
info->dev_class, info->rssi,
|
|
HCI_MAX_EIR_LENGTH, info->data);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline u8 hci_get_auth_req(struct hci_conn *conn)
|
|
{
|
|
BT_DBG("%p", conn);
|
|
|
|
/* If remote requests dedicated bonding follow that lead */
|
|
if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) {
|
|
/* If both remote and local IO capabilities allow MITM
|
|
* protection then require it, otherwise don't */
|
|
if (conn->remote_cap == 0x03 || conn->io_capability == 0x03) {
|
|
return 0x02;
|
|
} else {
|
|
conn->auth_type |= 0x01;
|
|
return 0x03;
|
|
}
|
|
}
|
|
|
|
/* If remote requests no-bonding follow that lead */
|
|
if (conn->remote_auth <= 0x01)
|
|
return 0x00;
|
|
|
|
return conn->auth_type;
|
|
}
|
|
|
|
static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_io_capa_request *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
|
if (!conn)
|
|
goto unlock;
|
|
|
|
hci_conn_hold(conn);
|
|
|
|
if (!test_bit(HCI_MGMT, &hdev->flags))
|
|
goto unlock;
|
|
|
|
if (test_bit(HCI_PAIRABLE, &hdev->flags) ||
|
|
(conn->remote_auth & ~0x01) == HCI_AT_NO_BONDING) {
|
|
struct hci_cp_io_capability_reply cp;
|
|
u8 io_cap = conn->io_capability;
|
|
|
|
/* ACL-SSP does not support IO CAP 0x04 */
|
|
cp.capability = (io_cap == 0x04) ? 0x01 : io_cap;
|
|
bacpy(&cp.bdaddr, &ev->bdaddr);
|
|
if (conn->auth_initiator)
|
|
cp.authentication = conn->auth_type;
|
|
else
|
|
cp.authentication = hci_get_auth_req(conn);
|
|
|
|
if ((conn->out == 0x01 || conn->remote_oob == 0x01) &&
|
|
hci_find_remote_oob_data(hdev, &conn->dst))
|
|
cp.oob_data = 0x01;
|
|
else
|
|
cp.oob_data = 0x00;
|
|
|
|
hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_REPLY,
|
|
sizeof(cp), &cp);
|
|
} else {
|
|
struct hci_cp_io_capability_neg_reply cp;
|
|
|
|
bacpy(&cp.bdaddr, &ev->bdaddr);
|
|
cp.reason = 0x16; /* Pairing not allowed */
|
|
|
|
hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY,
|
|
sizeof(cp), &cp);
|
|
}
|
|
|
|
unlock:
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_io_capa_reply_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_io_capa_reply *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
|
if (!conn)
|
|
goto unlock;
|
|
|
|
conn->remote_cap = ev->capability;
|
|
conn->remote_oob = ev->oob_data;
|
|
conn->remote_auth = ev->authentication;
|
|
|
|
unlock:
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_user_ssp_confirmation_evt(struct hci_dev *hdev,
|
|
u8 event, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_user_confirm_req *ev = (void *) skb->data;
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
if (test_bit(HCI_MGMT, &hdev->flags)) {
|
|
if (event == HCI_EV_USER_PASSKEY_REQUEST)
|
|
mgmt_user_confirm_request(hdev->id, event,
|
|
&ev->bdaddr, 0);
|
|
else
|
|
mgmt_user_confirm_request(hdev->id, event,
|
|
&ev->bdaddr, ev->passkey);
|
|
}
|
|
|
|
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;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
|
if (!conn)
|
|
goto unlock;
|
|
|
|
/* To avoid duplicate auth_failed events to user space we check
|
|
* the HCI_CONN_AUTH_PEND flag which will be set if we
|
|
* initiated the authentication. A traditional auth_complete
|
|
* event gets always produced as initiator and is also mapped to
|
|
* the mgmt_auth_failed event */
|
|
if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) && ev->status != 0)
|
|
mgmt_auth_failed(hdev->id, &conn->dst, ev->status);
|
|
|
|
hci_conn_put(conn);
|
|
|
|
unlock:
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_remote_host_features *ev = (void *) skb->data;
|
|
struct inquiry_entry *ie;
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr);
|
|
if (ie)
|
|
ie->data.ssp_mode = (ev->features[0] & 0x01);
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_remote_oob_data_request *ev = (void *) skb->data;
|
|
struct oob_data *data;
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
if (!test_bit(HCI_MGMT, &hdev->flags))
|
|
goto unlock;
|
|
|
|
data = hci_find_remote_oob_data(hdev, &ev->bdaddr);
|
|
if (data) {
|
|
struct hci_cp_remote_oob_data_reply cp;
|
|
|
|
bacpy(&cp.bdaddr, &ev->bdaddr);
|
|
memcpy(cp.hash, data->hash, sizeof(cp.hash));
|
|
memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer));
|
|
|
|
hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY, sizeof(cp),
|
|
&cp);
|
|
} else {
|
|
struct hci_cp_remote_oob_data_neg_reply cp;
|
|
|
|
bacpy(&cp.bdaddr, &ev->bdaddr);
|
|
hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY, sizeof(cp),
|
|
&cp);
|
|
}
|
|
|
|
unlock:
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_le_conn_complete *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr);
|
|
if (!conn) {
|
|
conn = hci_le_conn_add(hdev, &ev->bdaddr, ev->bdaddr_type);
|
|
if (!conn) {
|
|
BT_ERR("No memory for new connection");
|
|
hci_dev_unlock(hdev);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ev->status) {
|
|
hci_proto_connect_cfm(conn, ev->status);
|
|
conn->state = BT_CLOSED;
|
|
hci_conn_del(conn);
|
|
goto unlock;
|
|
}
|
|
|
|
conn->sec_level = BT_SECURITY_LOW;
|
|
conn->handle = __le16_to_cpu(ev->handle);
|
|
conn->state = BT_CONNECTED;
|
|
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
|
mgmt_connected(hdev->id, &ev->bdaddr, 1);
|
|
|
|
hci_conn_hold(conn);
|
|
hci_conn_hold_device(conn);
|
|
hci_conn_add_sysfs(conn);
|
|
|
|
hci_proto_connect_cfm(conn, ev->status);
|
|
|
|
unlock:
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_le_ltk_req *ev = (void *) skb->data;
|
|
struct hci_cp_le_ltk_reply cp;
|
|
struct hci_cp_le_ltk_neg_reply neg;
|
|
struct hci_conn *conn;
|
|
struct link_key *ltk;
|
|
|
|
BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle));
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
|
if (conn == NULL)
|
|
goto not_found;
|
|
|
|
ltk = hci_find_ltk(hdev, ev->ediv, ev->random);
|
|
if (ltk == NULL)
|
|
goto not_found;
|
|
|
|
memcpy(cp.ltk, ltk->val, sizeof(ltk->val));
|
|
cp.handle = cpu_to_le16(conn->handle);
|
|
conn->pin_length = ltk->pin_len;
|
|
|
|
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
|
|
|
|
hci_dev_unlock(hdev);
|
|
|
|
return;
|
|
|
|
not_found:
|
|
neg.handle = ev->handle;
|
|
hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(neg), &neg);
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_le_advertising_info *ev;
|
|
u8 num_reports;
|
|
|
|
num_reports = skb->data[0];
|
|
ev = (void *) &skb->data[1];
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
while (num_reports--) {
|
|
mgmt_device_found(hdev->id, &ev->bdaddr, ev->bdaddr_type,
|
|
1, NULL, 0, ev->length, ev->data);
|
|
hci_add_adv_entry(hdev, ev);
|
|
ev = (void *) (ev->data + ev->length + 1);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_le_meta *le_ev = (void *) skb->data;
|
|
|
|
skb_pull(skb, sizeof(*le_ev));
|
|
|
|
switch (le_ev->subevent) {
|
|
case HCI_EV_LE_CONN_COMPLETE:
|
|
hci_le_conn_complete_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_LE_LTK_REQ:
|
|
hci_le_ltk_request_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_LE_ADVERTISING_REPORT:
|
|
hci_le_adv_report_evt(hdev, skb);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline void hci_phy_link_complete(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_phys_link_complete *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s handle %d status %d", hdev->name, ev->phy_handle,
|
|
ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
if (ev->status == 0) {
|
|
conn = hci_conn_add(hdev, ACL_LINK, 0, BDADDR_ANY);
|
|
if (conn) {
|
|
conn->handle = ev->phy_handle;
|
|
conn->state = BT_CONNECTED;
|
|
|
|
hci_conn_hold(conn);
|
|
conn->disc_timeout = HCI_DISCONN_TIMEOUT/2;
|
|
hci_conn_put(conn);
|
|
|
|
hci_conn_hold_device(conn);
|
|
hci_conn_add_sysfs(conn);
|
|
} else
|
|
BT_ERR("No memory for new connection");
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_log_link_complete(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_log_link_complete *ev = (void *) skb->data;
|
|
struct hci_chan *chan;
|
|
|
|
BT_DBG("%s handle %d status %d", hdev->name,
|
|
__le16_to_cpu(ev->log_handle), ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
chan = hci_chan_list_lookup_id(hdev, ev->phy_handle);
|
|
|
|
if (chan) {
|
|
if (ev->status == 0) {
|
|
chan->ll_handle = __le16_to_cpu(ev->log_handle);
|
|
chan->state = BT_CONNECTED;
|
|
} else {
|
|
chan->state = BT_CLOSED;
|
|
}
|
|
|
|
hci_proto_create_cfm(chan, ev->status);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_flow_spec_modify_complete(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_flow_spec_modify_complete *ev = (void *) skb->data;
|
|
struct hci_chan *chan;
|
|
|
|
BT_DBG("%s handle %d status %d", hdev->name,
|
|
__le16_to_cpu(ev->log_handle), ev->status);
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
chan = hci_chan_list_lookup_handle(hdev, ev->log_handle);
|
|
if (chan)
|
|
hci_proto_modify_cfm(chan, ev->status);
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_disconn_log_link_complete_evt(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_disconn_log_link_complete *ev = (void *) skb->data;
|
|
struct hci_chan *chan;
|
|
|
|
BT_DBG("%s handle %d status %d", hdev->name,
|
|
__le16_to_cpu(ev->log_handle), ev->status);
|
|
|
|
if (ev->status)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
chan = hci_chan_list_lookup_handle(hdev, __le16_to_cpu(ev->log_handle));
|
|
if (chan)
|
|
hci_proto_destroy_cfm(chan, ev->reason);
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
static inline void hci_disconn_phy_link_complete_evt(struct hci_dev *hdev,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct hci_ev_disconn_phys_link_complete *ev = (void *) skb->data;
|
|
struct hci_conn *conn;
|
|
|
|
BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
|
if (ev->status)
|
|
return;
|
|
|
|
hci_dev_lock(hdev);
|
|
|
|
conn = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
|
|
if (conn) {
|
|
conn->state = BT_CLOSED;
|
|
|
|
hci_proto_disconn_cfm(conn, ev->reason, 0);
|
|
hci_conn_del(conn);
|
|
}
|
|
|
|
hci_dev_unlock(hdev);
|
|
}
|
|
|
|
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct hci_event_hdr *hdr = (void *) skb->data;
|
|
__u8 event = hdr->evt;
|
|
|
|
BT_DBG("");
|
|
|
|
skb_pull(skb, HCI_EVENT_HDR_SIZE);
|
|
|
|
switch (event) {
|
|
case HCI_EV_INQUIRY_COMPLETE:
|
|
hci_inquiry_complete_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_INQUIRY_RESULT:
|
|
hci_inquiry_result_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_CONN_COMPLETE:
|
|
hci_conn_complete_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_CONN_REQUEST:
|
|
hci_conn_request_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_DISCONN_COMPLETE:
|
|
hci_disconn_complete_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_AUTH_COMPLETE:
|
|
hci_auth_complete_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_REMOTE_NAME:
|
|
hci_remote_name_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_ENCRYPT_CHANGE:
|
|
hci_encrypt_change_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_CHANGE_LINK_KEY_COMPLETE:
|
|
hci_change_link_key_complete_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_REMOTE_FEATURES:
|
|
hci_remote_features_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_REMOTE_VERSION:
|
|
hci_remote_version_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_QOS_SETUP_COMPLETE:
|
|
hci_qos_setup_complete_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_CMD_COMPLETE:
|
|
hci_cmd_complete_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_CMD_STATUS:
|
|
hci_cmd_status_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_ROLE_CHANGE:
|
|
hci_role_change_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_NUM_COMP_PKTS:
|
|
hci_num_comp_pkts_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_MODE_CHANGE:
|
|
hci_mode_change_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_PIN_CODE_REQ:
|
|
hci_pin_code_request_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_LINK_KEY_REQ:
|
|
hci_link_key_request_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_LINK_KEY_NOTIFY:
|
|
hci_link_key_notify_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_CLOCK_OFFSET:
|
|
hci_clock_offset_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_PKT_TYPE_CHANGE:
|
|
hci_pkt_type_change_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_PSCAN_REP_MODE:
|
|
hci_pscan_rep_mode_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_INQUIRY_RESULT_WITH_RSSI:
|
|
hci_inquiry_result_with_rssi_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_REMOTE_EXT_FEATURES:
|
|
hci_remote_ext_features_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_SYNC_CONN_COMPLETE:
|
|
hci_sync_conn_complete_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_SYNC_CONN_CHANGED:
|
|
hci_sync_conn_changed_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_SNIFF_SUBRATE:
|
|
hci_sniff_subrate_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_EXTENDED_INQUIRY_RESULT:
|
|
hci_extended_inquiry_result_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_IO_CAPA_REQUEST:
|
|
hci_io_capa_request_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_IO_CAPA_REPLY:
|
|
hci_io_capa_reply_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_USER_PASSKEY_REQUEST:
|
|
case HCI_EV_USER_PASSKEY_NOTIFICATION:
|
|
case HCI_EV_USER_CONFIRM_REQUEST:
|
|
hci_user_ssp_confirmation_evt(hdev, event, skb);
|
|
break;
|
|
|
|
case HCI_EV_SIMPLE_PAIR_COMPLETE:
|
|
hci_simple_pair_complete_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_REMOTE_HOST_FEATURES:
|
|
hci_remote_host_features_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_LE_META:
|
|
hci_le_meta_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_REMOTE_OOB_DATA_REQUEST:
|
|
hci_remote_oob_data_request_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_PHYS_LINK_COMPLETE:
|
|
hci_phy_link_complete(hdev, skb);
|
|
hci_amp_event_packet(hdev, event, skb);
|
|
break;
|
|
|
|
case HCI_EV_LOG_LINK_COMPLETE:
|
|
hci_log_link_complete(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_FLOW_SPEC_MODIFY_COMPLETE:
|
|
hci_flow_spec_modify_complete(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_DISCONN_LOG_LINK_COMPLETE:
|
|
hci_disconn_log_link_complete_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_DISCONN_PHYS_LINK_COMPLETE:
|
|
hci_disconn_phy_link_complete_evt(hdev, skb);
|
|
hci_amp_event_packet(hdev, event, skb);
|
|
break;
|
|
|
|
case HCI_EV_NUM_COMP_BLOCKS:
|
|
hci_num_comp_blocks_evt(hdev, skb);
|
|
break;
|
|
|
|
case HCI_EV_CHANNEL_SELECTED:
|
|
hci_amp_event_packet(hdev, event, skb);
|
|
break;
|
|
|
|
case HCI_EV_AMP_STATUS_CHANGE:
|
|
hci_amp_event_packet(hdev, event, skb);
|
|
break;
|
|
|
|
default:
|
|
BT_DBG("%s event 0x%x", hdev->name, event);
|
|
break;
|
|
}
|
|
|
|
kfree_skb(skb);
|
|
hdev->stat.evt_rx++;
|
|
}
|
|
|
|
/* Generate internal stack event */
|
|
void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
|
|
{
|
|
struct hci_event_hdr *hdr;
|
|
struct hci_ev_stack_internal *ev;
|
|
struct sk_buff *skb;
|
|
|
|
skb = bt_skb_alloc(HCI_EVENT_HDR_SIZE + sizeof(*ev) + dlen, GFP_ATOMIC);
|
|
if (!skb)
|
|
return;
|
|
|
|
hdr = (void *) skb_put(skb, HCI_EVENT_HDR_SIZE);
|
|
hdr->evt = HCI_EV_STACK_INTERNAL;
|
|
hdr->plen = sizeof(*ev) + dlen;
|
|
|
|
ev = (void *) skb_put(skb, sizeof(*ev) + dlen);
|
|
ev->type = type;
|
|
memcpy(ev->data, data, dlen);
|
|
|
|
bt_cb(skb)->incoming = 1;
|
|
__net_timestamp(skb);
|
|
|
|
bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
|
|
skb->dev = (void *) hdev;
|
|
hci_send_to_sock(hdev, skb, NULL);
|
|
kfree_skb(skb);
|
|
}
|