Files
kernel-tenderloin-3.0/net/bluetooth/hci_event.c
Rohit Vaswani d4e3d52392 Merge branch 'google-android' into msm-3.0
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.

commit 1f8c5cecfe
Author: 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(-)

commit 4dc43d7079
Author: 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(-)

commit 7cc846069a
Author: 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(-)

commit 0e80804a2e
Author: 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(-)

commit dfc896e1c8
Author: 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(-)

commit 256a6b23be
Author: 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(-)

commit ff93146589
Author: 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(-)

commit 96034c2006
Author: 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(-)

commit 494661a1ac
Author: 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(-)

commit 1dddb0cc0d
Author: 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(-)

commit 52bdb6f543
Author: 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(-)

commit b1a94205e9
Author: 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(-)

commit 09701e3edf
Author: 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(-)

commit a6ccb73389
Author: 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(-)

commit 87159de9c3
Author: 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(-)

commit d692df224b
Author: 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(-)

commit d1e94136fc
Author: 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(-)

commit dac306d896
Author: Dima Zavin <dima@android.com>
Date:   Thu Jan 19 09:51:07 2012 -0800

    Revert "proc: enable writing to /proc/pid/mem"

    This reverts commit 198214a7ee.

 fs/proc/base.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

commit a65e28a014
Author: 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(-)

commit f16e6fb3e3
Author: 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 commit ffdab0c0c4.

    Not needed anymore in 2.6.39 and 3.0, dev_get_stats has been fixed
    and may be called from atomic context. See:
    1ac9ad1 net: remove dev_txq_stats_fold()

 drivers/usb/gadget/rndis.c |   23 ++---------------------
 1 files changed, 2 insertions(+), 21 deletions(-)

commit e1493f1544
Author: 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(-)

commit bbd08c6e95
Author: 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(-)

commit 37ff4411a5
Author: 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(-)

commit 599c8566fa
Author: 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(-)

commit f227b88c89
Author: 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(-)

commit ed3f356087
Author: 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(-)

commit c561cedf2b
Author: 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(-)

commit 4f36cb88d6
Author: 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(-)

commit af16732d4c
Author: 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(-)

commit 7caeacd6ed
Author: 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(-)

commit 8d71d882e7
Author: 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(-)

commit 35047200c4
Author: 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(-)

commit ea693bf7f8
Author: 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(-)

commit dc1b634039
Author: 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(-)

commit e0de0a507d
Author: 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: 5513065 5440193

    Change-Id: I063c22bf5d104a3d2df71cf622409459fac5f27a
    Signed-off-by: Benoit Goby <benoit@android.com>

 drivers/usb/gadget/android.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

commit b86fd0b622
Author: 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(-)

commit 6a4a38525d
Author: 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(-)

commit dbb18fb2c1
Author: 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(-)

commit 9d187300df
Author: 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(-)

commit 452d440ab2
Author: 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(-)

commit cf70c6a400
Author: 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(-)

commit 340ede3671
Author: 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>
2012-03-28 16:03:47 -07:00

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);
}