author | Jiamin Miao <jiamin.miao@amlogic.com> | 2015-04-08 02:08:04 (GMT) |
---|---|---|
committer | tao.dong <tao.dong@amlogic.com> | 2015-11-11 04:51:43 (GMT) |
commit | 10840d5dbd967c086fa1bfbc82fa4cb0840d4893 (patch) | |
tree | 9be77945963dd1c999e0a69f9ab7e2b037211c84 | |
parent | 352745e75d3ab4887ee0e3fa2ddb2f5312b7ca20 (diff) | |
download | btusb-10840d5dbd967c086fa1bfbc82fa4cb0840d4893.zip btusb-10840d5dbd967c086fa1bfbc82fa4cb0840d4893.tar.gz btusb-10840d5dbd967c086fa1bfbc82fa4cb0840d4893.tar.bz2 |
PD#103871:bcm20705 usb bt driver
-rwxr-xr-x | Makefile | 128 | ||||
-rwxr-xr-x | Release_btusb.txt | 26 | ||||
-rwxr-xr-x | inc/bt_target.h | 0 | ||||
-rwxr-xr-x | inc/bt_types.h | 78 | ||||
-rwxr-xr-x | inc/btusb.h | 367 | ||||
-rwxr-xr-x | inc/btusb_cq.h | 118 | ||||
-rwxr-xr-x | inc/btusb_lite.h | 203 | ||||
-rwxr-xr-x | inc/btusb_lite_av.h | 135 | ||||
-rwxr-xr-x | inc/btusb_lite_avdt.h | 148 | ||||
-rwxr-xr-x | inc/btusb_lite_hci.h | 71 | ||||
-rwxr-xr-x | inc/btusb_lite_l2c.h | 105 | ||||
-rwxr-xr-x | inc/btusb_proc.h | 49 | ||||
-rwxr-xr-x | inc/btusbext.h | 130 | ||||
-rwxr-xr-x | inc/data_types.h | 79 | ||||
-rwxr-xr-x | inc/gki.h | 190 | ||||
-rwxr-xr-x | inc/gki_int.h | 197 | ||||
-rwxr-xr-x | inc/hcidefs.h | 1819 | ||||
-rwxr-xr-x | inc/target.h | 121 | ||||
-rwxr-xr-x | src/btusb.c | 1596 | ||||
-rwxr-xr-x | src/btusb_dev.c | 2001 | ||||
-rwxr-xr-x | src/btusb_isoc.c | 292 | ||||
-rwxr-xr-x | src/btusb_lite.c | 1785 | ||||
-rwxr-xr-x | src/btusb_lite_av.c | 1365 | ||||
-rwxr-xr-x | src/btusb_lite_avdt.c | 426 | ||||
-rwxr-xr-x | src/btusb_lite_hci.c | 440 | ||||
-rwxr-xr-x | src/btusb_lite_l2c.c | 278 | ||||
-rwxr-xr-x | src/btusb_proc.c | 763 | ||||
-rwxr-xr-x | src/btusb_version.c | 30 | ||||
-rwxr-xr-x | src/gki/gki_buffer.c | 1291 | ||||
-rwxr-xr-x | src/gki/gki_klinux.c | 271 |
30 files changed, 14502 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..dd522eb --- a/dev/null +++ b/Makefile @@ -0,0 +1,128 @@ + +# Specify Include folders +EXTRA_CFLAGS += -I$(SUBDIRS) +EXTRA_CFLAGS += -I$(SUBDIRS)/inc +EXTRA_CFLAGS += -I$(SUBDIRS)/src +EXTRA_CFLAGS += -I$(SUBDIRS)/src/gki +EXTRA_CFLAGS += -DEXPORT_SYMTAB + +LITE ?= FALSE +ifeq ($(strip $(LITE)),TRUE) +EXTRA_CFLAGS += -DBUILDCFG +EXTRA_CFLAGS += -DBTUSB_LITE +EXTRA_CFLAGS += -I$(SUBDIRS)/../btpcm +EXTRA_CFLAGS += -I$(SUBDIRS)/../btsbc +COMPONENTS_PATH := $(SUBDIRS)/../../../../../Components +EXTRA_CFLAGS += -I$(COMPONENTS_PATH)/bta/include +EXTRA_CFLAGS += -I$(COMPONENTS_PATH)/stack/include +EXTRA_CFLAGS += -I$(COMPONENTS_PATH)/hcis +endif + +SEC ?= FALSE +ifeq ($(strip $(SEC)),TRUE) +EXTRA_CFLAGS += -DBTUSB_LITE_SEC +EXTRA_CFLAGS += -I$(SUBDIRS)/../btsec +endif + +EXTRA_CFLAGS += -DEXPORT_SYMTAB + +# BTUSB_VID and BTUSB_PID can be defined to support a single BT controller type +# The following definitions are examples for 20702A1 chip +#EXTRA_CFLAGS += -DBTUSB_VID=0x0A5C +#EXTRA_CFLAGS += -DBTUSB_PID=0x22BE + +usbobjs := \ + src/btusb.o \ + src/btusb_dev.o \ + src/btusb_isoc.o \ + src/btusb_version.o + +ifeq ($(CONFIG_PROC_FS),y) +usbobjs += \ + src/btusb_proc.o +endif + +ifeq ($(strip $(LITE)),TRUE) +liteobjs := src/btusb_lite.o \ + src/btusb_lite_av.o \ + src/btusb_lite_avdt.o \ + src/btusb_lite_l2c.o \ + src/btusb_lite_hci.o +endif + +gkiobjs := \ + src/gki/gki_buffer.o \ + src/gki/gki_klinux.o + +obj-m += btusb.o +btusb-objs := $(usbobjs) $(gkiobjs) $(liteobjs) + +# specify Kernel build location (can be overriden at command line) +#KDIR ?= /lib/modules/$(shell uname -r)/build +KDIR ?= /host/android/tiny4412/tiny_4412/linux-3.5 +PWD := $(shell pwd) + +default: +ifeq ($(strip $(LITE)),TRUE) + cat ../btpcm/Module.symvers > ./Module.symvers + cat ../btsbc/Module.symvers >> ./Module.symvers +ifeq ($(strip $(SEC)),TRUE) + cat ../btsec/Module.symvers >> ./Module.symvers +endif +endif + $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules + +install: + cp -v btusb.ko /lib/modules/$(uname -r)/kernel/drivers/brcm/btusb.ko + +clean: + rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions + rm -f Module.markers + rm -f Module.symvers + rm -f modules.order + rm -f ./src/*.o + rm -f ./src/.*.o.cmd + rm -f ./src/gki/*.o + rm -f ./src/gki/.*.o.cmd + +# define the location of the Linux stable repository +PROJECTS_DIR ?= ~/projects +LINUX_STABLE = $(PROJECTS_DIR)/linux-stable + +# rule to run a regression on a specific file +v2.% v3.%: + @echo Running regression on: $@ + # clean the previous version before switching version + cd $(LINUX_STABLE); make mrproper >/dev/null + # switch version + cd $(LINUX_STABLE); git checkout $@ >/dev/null + # prepare to build modules + cd $(LINUX_STABLE); make defconfig >/dev/null + cd $(LINUX_STABLE); make modules_prepare >/dev/null + make KDIR=$(LINUX_STABLE) + +regression-install: + cd $(PROJECTS_DIR) + git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git + +regression-update: + cd $(LINUX_STABLE) + git pull + +regression: v2.6.28 v2.6.35 v2.6.36 v3.2.1 v3.12.1 + @echo Regression completed successfully! + +.PHONY: help regression regression-update regression-install clean install + +help: + @echo "BTUSB module configurations and targets:" + @echo " LITE=FALSE : No StackLite (Default)" + @echo " LITE=TRUE : StackLite compiled" + @echo " SEC=FALSE : No SEC Encoder Support(default)" + @echo " SEC=TRUE : SEC Encoder Support. Requires StackLite" + @echo " clean : remove the build output files" + @echo " install : copy the driver in the current platform modules list" + @echo " regression : run a regression on several Linux versions" + + + diff --git a/Release_btusb.txt b/Release_btusb.txt new file mode 100755 index 0000000..f6228fe --- a/dev/null +++ b/Release_btusb.txt @@ -0,0 +1,26 @@ +To compile the Bluetooth USB Driver (BTUSB) you have to: +> cd /3rdparty/embedded/brcm/linux/btusb +> make + +The Makefile may need to be adapted to the target: +KDIR := /lib/modules/$(shell uname -r)/build => For local kernel (currently running) +KDIR := /opt/brcm/linux => For different kernel, it should point to the appropriate kernel version + +By default btusb supports the most Broadcom's BT controller types. +To restrict the support to a single VID/PID (USB identifier of the BT controller type), the BTUSB_VID and BTUSB_PID can be defined. +For 20702 the values to use are: +EXTRA_CFLAGS += -DBTUSB_VID=0x0A5C +EXTRA_CFLAGS += -DBTUSB_PID=0x22BE + +To install the btusb.ko module, you may have to create a node in /dev: +mknod btusb0 c 180 194 + +Furthermore, BlueZ must be uninstalled to avoid driver conflicts. + +See BSA Software user guide for further information. + +BTUSB Compile Options for Lite Stack Support: +> make LITE=FALSE => No StackLite (Default)" +> make LITE=TRUE => StackLite compiled" +> make SEC=FALSE => No SEC Encoder Support(default)" +> make SEC=TRUE => SEC Encoder Support (Requires StackLite) diff --git a/inc/bt_target.h b/inc/bt_target.h new file mode 100755 index 0000000..e69de29 --- a/dev/null +++ b/inc/bt_target.h diff --git a/inc/bt_types.h b/inc/bt_types.h new file mode 100755 index 0000000..f214d54 --- a/dev/null +++ b/inc/bt_types.h @@ -0,0 +1,78 @@ +/* +* +* bt_types.h +* +* +* +* Copyright (C) 2011-2013 Broadcom Corporation. +* +* +* +* This software is licensed under the terms of the GNU General Public License, +* version 2, as published by the Free Software Foundation (the "GPL"), and may +* be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. +* +* +* A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php +* or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA +* +* +*/ + +#ifndef BT_TYPES_H +#define BT_TYPES_H + +#include "data_types.h" + +/* READ WELL !! +** +** This section defines global events. These are events that cross layers. +** Any event that passes between layers MUST be one of these events. Tasks +** can use their own events internally, but a FUNDAMENTAL design issue is +** that global events MUST be one of these events defined below. +** +** The convention used is the the event name contains the layer that the +** event is going to. +*/ +#define BT_EVT_TO_BTU_HCI_EVT 0x1000 /* HCI Event */ + +#define BT_EVT_TO_BTU_HCI_SCO 0x1200 /* SCO Data from HCI */ + +#define BT_EVT_TO_LM_HCI_CMD 0x2000 /* HCI Command */ +#define BT_EVT_TO_LM_HCI_ACL 0x2100 /* HCI ACL Data */ +#define BT_EVT_TO_LM_DIAG 0x2c00 /* LM Diagnostics commands */ + + +#define BT_EVT_BTU_IPC_EVT 0x9000 +#define BT_EVT_BTU_IPC_LOGMSG_EVT (0x0000 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_ACL_EVT (0x0001 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_BTU_EVT (0x0002 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_L2C_EVT (0x0003 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_L2C_MSG_EVT (0x0004 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_BTM_EVT (0x0005 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_AVDT_EVT (0x0006 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_SLIP_EVT (0x0007 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_MGMT_EVT (0x0008 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_BTTRC_EVT (0x0009 | BT_EVT_BTU_IPC_EVT) +#define BT_EVT_BTU_IPC_BURST_EVT (0x000A | BT_EVT_BTU_IPC_EVT) + + +/* Define the header of each buffer used in the Bluetooth stack. +*/ +typedef struct +{ + UINT16 event; + UINT16 len; + UINT16 offset; + UINT16 layer_specific; +} BT_HDR; + +#define BT_HDR_SIZE (sizeof (BT_HDR)) + + +#endif diff --git a/inc/btusb.h b/inc/btusb.h new file mode 100755 index 0000000..b9995db --- a/dev/null +++ b/inc/btusb.h @@ -0,0 +1,367 @@ +/* + * + * btusb.h + * + * + * + * Copyright (C) 2011-2014 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ + +#ifndef BTUSB_H +#define BTUSB_H + +#include <linux/version.h> +#include <linux/usb.h> +#include <linux/tty.h> +#include <linux/time.h> + +#include "bt_types.h" +#include "btusb_cq.h" +#include "gki_int.h" +#include "btusbext.h" + +#ifdef BTUSB_LITE +#include "btusb_lite.h" +#endif + +extern const char bsa_version_string[]; + +/* Linux kernel compatibility abstraction */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 34) +#define BTUSB_USHRT_MAX USHORT_MAX +#else +#define BTUSB_USHRT_MAX USHRT_MAX +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) +#define BTUSB_PDE_DATA(inode) PDE(inode)->data +#define BTUSB_PDE_PARENT_DATA(inode) PDE(inode)->parent->data +#else +#define BTUSB_PDE_DATA(inode) PDE_DATA(inode) +#define BTUSB_PDE_PARENT_DATA(inode) proc_get_parent_data(inode) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) +#define BTUSB_BUFFER_ALLOC usb_buffer_alloc +#define BTUSB_BUFFER_FREE usb_buffer_free +#else +#define BTUSB_BUFFER_ALLOC usb_alloc_coherent +#define BTUSB_BUFFER_FREE usb_free_coherent +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) +#define BTUSB_EP_TYPE(ep) (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) +#define BTUSB_EP_DIR_IN(ep) ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) +#define BTUSB_EP_DIR_OUT(ep) ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) +#else +#define BTUSB_EP_TYPE(ep) usb_endpoint_type(ep) +#define BTUSB_EP_DIR_IN(ep) usb_endpoint_dir_in(ep) +#define BTUSB_EP_DIR_OUT(ep) usb_endpoint_dir_out(ep) +#endif + +/* debug information flags. one-hot encoded: + bit0 : enable printk(KERN_DEBUG) + bit1 : enable printk(KERN_INFO) + bit16 : enable all message dumps in printk + bit17 : enable timestamp calculations on voice RX packets + bit18 : enable timestamp calculations on voice TX packets + bit19 : enable GKI buffer check */ +#define BTUSB_DBG_MSG 0x0001 +#define BTUSB_INFO_MSG 0x0002 +#define BTUSB_DUMP_MSG 0x0100 +#define BTUSB_VOICERX_TIME 0x0200 +#define BTUSB_VOICETX_TIME 0x0400 +#define BTUSB_GKI_CHK_MSG 0x0800 +/* #define BTUSB_DBGFLAGS (BTUSB_DBG_MSG | BTUSB_INFO_MSG | BTUSB_DUMP_MSG | BTUSB_GKI_CHK_MSG) */ +#define BTUSB_DBGFLAGS 0 + +extern int dbgflags; + +#define BTUSB_DBG(fmt, ...) if (unlikely(dbgflags & BTUSB_DBG_MSG)) \ + printk(KERN_INFO "BTUSB %s: " fmt, __FUNCTION__, ##__VA_ARGS__) + +#define BTUSB_INFO(fmt, ...) if (unlikely(dbgflags & BTUSB_INFO_MSG))\ + printk(KERN_INFO "BTUSB %s: " fmt, __FUNCTION__, ##__VA_ARGS__) + +#define BTUSB_ERR(fmt, ...) \ + printk(KERN_ERR "BTUSB %s: " fmt, __FUNCTION__, ##__VA_ARGS__) + +/* Get a minor range for your devices from the usb maintainer */ +#define BTUSB_MINOR_BASE 194 + +/* 1025 = size(con_hdl) + size(acl_len) + size(3-dh5) = 2 + 2 + 1021 */ +#define BTUSB_HCI_MAX_ACL_SIZE 1025 +/* Maximum size of command and events packets (events = 255 + 2, commands = 255 + 3) */ +#define BTUSB_HCI_MAX_CMD_SIZE 258 +#define BTUSB_HCI_MAX_EVT_SIZE 257 + +/* Maximum HCI H4 size = HCI type + ACL packet */ +#define BTUSB_H4_MAX_SIZE (1 + BTUSB_HCI_MAX_ACL_SIZE) + +#define BTUSB_NUM_OF_ACL_RX_BUFFERS 12 +#define BTUSB_NUM_OF_ACL_TX_BUFFERS 12 +#define BTUSB_NUM_OF_DIAG_RX_BUFFERS 2 /* must not be less than 2 */ +#define BTUSB_NUM_OF_DIAG_TX_BUFFERS 2 +#define BTUSB_NUM_OF_EVENT_BUFFERS 8 /* must not be less than 2 */ +#define BTUSB_NUM_OF_CMD_BUFFERS 8 +#define BTUSB_MAXIMUM_TX_VOICE_SIZE 192 +#define BTUSB_NUM_OF_VOICE_RX_BUFFERS 2 /* must not be less than 2 */ +#define BTUSB_NUM_OF_VOICE_RX_PACKETS 8 /* maximum voice packets pending for user */ +#define BTUSB_NUM_OF_VOICE_TX_BUFFERS 16 + +#define BTUSB_VOICE_BURST_SIZE 48 +#define BTUSB_VOICE_HEADER_SIZE 3 +#define BTUSB_VOICE_FRAMES_PER_URB 9 +#define BTUSB_VOICE_BUFFER_MAXSIZE (BTUSB_VOICE_FRAMES_PER_URB * \ + ALIGN(BTUSB_VOICE_BURST_SIZE + BTUSB_VOICE_HEADER_SIZE, 4)) + +/* size of the SCO packets sent to user application (5 * 48 is the max below 256) */ +#define BTUSB_SCO_RX_LEN (BTUSB_VOICE_BURST_SIZE * 5) +#if BTUSB_SCO_RX_LEN > 256 +#error SCO RX packet length is larger than the format supports +#endif + +#ifndef UINT8_TO_STREAM +#define UINT8_TO_STREAM(p, u8) {*(p)++ = (UINT8)(u8);} +#define UINT16_TO_STREAM(p, u16) {*(p)++ = (UINT8)(u16); *(p)++ = (UINT8)((u16) >> 8);} +#define BDADDR_TO_STREAM(p, a) {register int ijk; for (ijk = 0; ijk < BD_ADDR_LEN; ijk++) *(p)++ = (UINT8) a[BD_ADDR_LEN - 1 - ijk];} +#define ARRAY_TO_STREAM(p, a, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) *(p)++ = (UINT8) a[ijk];} + +#define STREAM_TO_UINT8(u8, p) {u8 = (UINT8)(*(p)); (p) += 1;} +#define STREAM_TO_UINT16(u16, p) {u16 = (UINT16)((*(p)) + ((*((p) + 1)) << 8)); (p) += 2;} +#define STREAM_TO_UINT32(u32, p) {u32 = (((UINT32)(*(p))) + ((((UINT32)(*((p) + 1)))) << 8) + ((((UINT32)(*((p) + 2)))) << 16) + ((((UINT32)(*((p) + 3)))) << 24)); (p) += 4;} +#define STREAM_TO_BDADDR(a, p) {register int ijk; register UINT8 *pbda = (UINT8 *)a + BD_ADDR_LEN - 1; for (ijk = 0; ijk < BD_ADDR_LEN; ijk++) *pbda-- = *p++;} +#define STREAM_TO_ARRAY(a, p, len) {register int ijk; for (ijk = 0; ijk < len; ijk++) ((UINT8 *) a)[ijk] = *p++;} +#endif + +#define UINT32_TO_BE_STREAM(p, u32) {*(p)++ = (UINT8)((u32) >> 24); *(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)(u32); } +#define UINT24_TO_BE_STREAM(p, u24) {*(p)++ = (UINT8)((u24) >> 16); *(p)++ = (UINT8)((u24) >> 8); *(p)++ = (UINT8)(u24);} +#define UINT16_TO_BE_STREAM(p, u16) {*(p)++ = (UINT8)((u16) >> 8); *(p)++ = (UINT8)(u16);} +#define UINT8_TO_BE_STREAM(p, u8) {*(p)++ = (UINT8)(u8);} + +/* macro that helps parsing arrays */ +#define BTUSB_ARRAY_FOR_EACH_TRANS(__a) \ + for (idx = 0, p_trans = &__a[0]; idx < ARRAY_SIZE(__a); idx++, p_trans = &__a[idx]) + +/* Layer Specific field: Used to send packet to User Space */ +#define BTUSB_LS_H4_TYPE_SENT (1<<0) /* H4 HCI Type already sent */ +#define BTUSB_LS_GKI_BUFFER (1<<1) /* Locally allocated buffer-not to resubmit */ + +struct btusb; + +/* Container used to copy the URB to sniff the SCO */ +struct btusb_scosniff +{ + struct list_head lh; /* to add element to a list */ + int s; /* start frame */ + unsigned int n; /* number of descriptors */ + unsigned int l; /* buffer length */ + struct usb_iso_packet_descriptor d[0]; /* descriptors */ +}; + +/* USB transaction */ +struct btusb_trans +{ + /* This is mapped to a GKI buffer to allow queuing */ + BUFFER_HDR_T gki_hdr; + /* Sharing queue with other packets -> needs BT header to multiplex */ + BT_HDR bt_hdr; + /* Pointer to the location where the data is stored */ + UINT8 *dma_buffer; + /* DMA structure */ + dma_addr_t dma; + /* URB for this transaction */ + struct urb *p_urb; + /* pointer to the device information */ + struct btusb *p_dev; + void (*complete)(struct btusb *p_dev, struct btusb_trans *p_trans, struct urb *p_urb); +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + /* Magic number */ + UINT32 magic; +#endif +}; + +/* Voice packet */ +struct btusb_voice_pkt +{ + /* This is mapped to a GKI buffer to allow queuing */ + BUFFER_HDR_T gki_hdr; + /* Sharing queue with other packets -> needs BT header to multiplex */ + BT_HDR bt_hdr; + UINT8 data[BTUSB_VOICE_HEADER_SIZE + BTUSB_SCO_RX_LEN]; +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + /* Magic number */ + UINT32 magic; +#endif +}; + +/* Voice channel descriptor */ +struct btusb_voice_channel +{ + bool used; + unsigned short handle; + unsigned char burst; + /* pointer to HCI packet being reconstructed */ + struct btusb_voice_pkt *p_pkt; +}; + +/* hardware quirks required for some BT controllers */ +#define BTUSB_QUIRK_ZLP_TX_REQ (1 << 0) +#define BTUSB_QUIRK_EXAMPLE (1 << 1) + +/* Define the main structure */ +struct btusb +{ + struct usb_device *p_udev; /* usb device for this device */ + const struct usb_device_id *p_id; /* device id from probe */ + struct usb_interface *p_main_intf; /* main interface reference */ + struct usb_interface *p_voice_intf; /* voice interface reference */ + struct usb_interface *p_dfu_intf; /* DFU interface reference */ + struct usb_interface *p_diag_intf; /* diag interface reference */ + + struct usb_host_endpoint *p_acl_in; /* acl bulk in endpoint */ + struct usb_host_endpoint *p_acl_out; /* acl bulk out endpoint */ + struct usb_host_endpoint *p_diag_in; /* diag bulk in endpoint */ + struct usb_host_endpoint *p_diag_out; /* diag bulk out endpoint */ + struct usb_host_endpoint *p_event_in; /* event interrupt in endpoint */ + struct usb_host_endpoint *p_voice_out; /* isoc voice out endpoint */ + struct usb_host_endpoint *p_voice_in; /* isoc voice in endpoint */ + struct ktermios kterm; /* TTY emulation */ + struct kref kref; + tBTUSB_STATS stats; + bool issharedusb; + unsigned int quirks; + + /* reception queue */ + wait_queue_head_t rx_wait_q; + + /* tx tasklet */ + struct tasklet_struct tx_task; + + /* proc filesystem entry to retrieve info from driver environment */ + struct proc_dir_entry *p_pde; + bool scosniff_active; + struct list_head scosniff_list; + struct completion scosniff_completion; + + /* Command transmit path */ + struct btusb_trans cmd_array[BTUSB_NUM_OF_CMD_BUFFERS]; + struct usb_ctrlrequest cmd_req_array[BTUSB_NUM_OF_CMD_BUFFERS]; + struct usb_anchor cmd_submitted; + + /* Event receive path */ + struct btusb_trans event_array[BTUSB_NUM_OF_EVENT_BUFFERS]; + struct usb_anchor event_submitted; + + /* ACL receive path */ + struct btusb_trans acl_rx_array[BTUSB_NUM_OF_ACL_RX_BUFFERS]; + struct usb_anchor acl_rx_submitted; + + /* ACL transmit path */ + struct btusb_trans acl_tx_array[BTUSB_NUM_OF_ACL_TX_BUFFERS]; + struct usb_anchor acl_tx_submitted; + + /* Diagnostics receive path */ + struct btusb_trans diag_rx_array[BTUSB_NUM_OF_DIAG_RX_BUFFERS]; + struct usb_anchor diag_rx_submitted; + + /* Diagnostics transmit path */ + struct btusb_trans diag_tx_array[BTUSB_NUM_OF_DIAG_TX_BUFFERS]; + struct usb_anchor diag_tx_submitted; + + /* Voice RX */ + struct btusb_trans voice_rx_array[BTUSB_NUM_OF_VOICE_RX_BUFFERS]; + struct usb_anchor voice_rx_submitted; + struct btusb_voice_pkt voice_rx_pkts[BTUSB_NUM_OF_VOICE_RX_PACKETS]; + DECLARE_BTUSB_CQ(voice_rx_list, struct btusb_voice_pkt *, BTUSB_NUM_OF_VOICE_RX_PACKETS); + + struct + { + struct btusb_voice_channel channels[3]; + unsigned int remaining; + struct btusb_voice_pkt **pp_pkt; + unsigned char hdr[BTUSB_VOICE_HEADER_SIZE]; + unsigned int hdr_size; + } voice_rx; + + /* Voice TX */ + struct btusb_trans voice_tx_array[BTUSB_NUM_OF_VOICE_TX_BUFFERS]; + struct usb_anchor voice_tx_submitted; + atomic_t voice_tx_active; + + BT_HDR *p_write_msg; + + BUFFER_Q rx_queue; + BT_HDR *p_rx_msg; + BUFFER_Q tx_queue; + +#ifdef BTUSB_LITE + struct btusb_lite_cb lite_cb; +#endif +}; + +/* Function prototypes */ +void btusb_delete(struct kref *kref); + +void btusb_tx_task(unsigned long arg); + +void btusb_cancel_voice(struct btusb *p_dev); +void btusb_cancel_urbs(struct btusb *p_dev); + +void btusb_voice_stats(unsigned long *p_max, unsigned long *p_min, + struct timeval *p_result, struct timeval *p_last_time); + +/* URB submit */ +int btusb_submit(struct btusb *p_dev, struct usb_anchor *p_anchor, struct btusb_trans *p_trans, int mem_flags); +void btusb_submit_voice_rx(struct btusb *p_dev, struct btusb_trans *p_trans, int mem_flags); + +/* BT controller to host routines */ +void btusb_rx_enqueue(struct btusb *p_dev, struct btusb_trans *p_trans, UINT8 hcitype); +void btusb_rx_enqueue_voice(struct btusb *p_dev, struct btusb_voice_pkt *p_pkt); +void btusb_rx_dequeued(struct btusb *p_dev, BT_HDR *p_msg); + +void btusb_dump_data(const UINT8 *p, int len, const char *p_title); + +void btusb_cmd_complete(struct btusb *p_dev, struct btusb_trans *p_trans, struct urb *p_urb); +void btusb_voicetx_complete(struct btusb *p_dev, struct btusb_trans *p_trans, struct urb *p_urb); +void btusb_urb_out_complete(struct urb *p_urb); +void btusb_voicerx_complete(struct urb *p_urb); + +/* Voice commands */ +int btusb_add_voice_channel(struct btusb *p_dev, unsigned short sco_handle, unsigned char burst); +int btusb_remove_voice_channel(struct btusb *p_dev, unsigned short sco_handle); + +/* USB device interface */ +int btusb_open(struct inode *inode, struct file *file); +int btusb_release(struct inode *inode, struct file *file); +ssize_t btusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos); +ssize_t btusb_write(struct file *file, const char __user *user_buffer, size_t count, loff_t *ppos); +unsigned int btusb_poll(struct file *file, struct poll_table_struct *p_pt); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) +long btusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +#else +int btusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +#endif + +/* Globals */ +extern struct usb_driver btusb_driver; +extern bool autopm; + +#endif /* BTUSB_H */ diff --git a/inc/btusb_cq.h b/inc/btusb_cq.h new file mode 100755 index 0000000..252b236 --- a/dev/null +++ b/inc/btusb_cq.h @@ -0,0 +1,118 @@ +/* + * + * btusb_cq.h + * + * + * + * Copyright (C) 2011-2014 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * Circular queue implementation, inspired from the Linux kernel cq as of 3.13... + * The reason why Linux version is not used is that it changed too much across + * kernel versions. + * + */ + +#ifndef BTUSB_CQ_H +#define BTUSB_CQ_H + +/* for smp_* */ +#include <linux/spinlock.h> + +struct __btusb_cq { + unsigned int in; + unsigned int out; + unsigned int mask; +}; + +#define __STRUCT_BTUSB_CQ_COMMON(datatype) \ + union { \ + struct __btusb_cq cq; \ + datatype *type; \ + const datatype *const_type; \ + } + +/* contains the power of 2 size check when declaring the buf array */ +#define STRUCT_BTUSB_CQ(type, size) \ +struct { \ + __STRUCT_BTUSB_CQ_COMMON(type); \ + type buf[((size < 2) || (size & (size - 1))) ? -1 : size]; \ +} + +#define DECLARE_BTUSB_CQ(queue, type, size) STRUCT_BTUSB_CQ(type, size) queue + +#define INIT_BTUSB_CQ(queue) \ +(void)({ \ + typeof(&(queue)) __tmp = &(queue); \ + struct __btusb_cq *__cq = &__tmp->cq; \ + __cq->in = 0; \ + __cq->out = 0; \ + __cq->mask = ARRAY_SIZE(__tmp->buf) - 1;\ +}) + +#define btusb_cq_len(queue) \ +({ \ + typeof((queue) + 1) __tmpl = (queue); \ + __tmpl->cq.in - __tmpl->cq.out; \ +}) + +#define btusb_cq_is_empty(queue) \ +({ \ + typeof((queue) + 1) __tmpq = (queue); \ + __tmpq->cq.in == __tmpq->cq.out; \ +}) + +#define btusb_cq_is_full(queue) \ +({ \ + typeof((queue) + 1) __tmpq = (queue); \ + btusb_cq_len(__tmpq) > __tmpq->cq.mask; \ +}) + + +#define btusb_cq_put(queue, val) \ +({ \ + typeof((queue) + 1) __tmp = (queue); \ + typeof(*__tmp->const_type) __val = (val); \ + unsigned int __ret; \ + struct __btusb_cq *__cq = &__tmp->cq; \ + __ret = !btusb_cq_is_full(__tmp); \ + if (__ret) { \ + __tmp->buf[__cq->in & __cq->mask] = \ + (typeof(*__tmp->type))__val; \ + smp_wmb(); \ + __cq->in++; \ + } \ + __ret; \ +}) + +#define btusb_cq_get(queue, val) \ +({ \ + typeof((queue) + 1) __tmp = (queue); \ + typeof(__tmp->type) __val = (val); \ + unsigned int __ret; \ + struct __btusb_cq *__cq = &__tmp->cq; \ + __ret = !btusb_cq_is_empty(__tmp); \ + if (__ret) { \ + *(typeof(__tmp->type))__val = \ + __tmp->buf[__cq->out & __cq->mask]; \ + smp_wmb(); \ + __cq->out++; \ + } \ + __ret; \ +}) + +#endif diff --git a/inc/btusb_lite.h b/inc/btusb_lite.h new file mode 100755 index 0000000..0f6fe06 --- a/dev/null +++ b/inc/btusb_lite.h @@ -0,0 +1,203 @@ +/* + * + * btusb_lite.h + * + * + * + * Copyright (C) 2011-2013 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ +#ifndef BTUSB_LITE_H +#define BTUSB_LITE_H + +struct btusb; +struct btpcm; + +#include "bt_target.h" +#include "hcidefs.h" +#include "bd.h" +#include "uipc_msg.h" +#include "btusb_lite_av.h" +#include "btusb_lite_avdt.h" +#include "btusb_lite_l2c.h" +#include "btusb_lite_hci.h" + +/* + * Definitions + */ +/* IPC Length Header Size (16 bits) */ +#define BTUSB_LITE_IPC_HDR_LEN_SIZE (sizeof(UINT16)) + +/* IPC Event Header Size (16 bits) */ +#define BTUSB_LITE_IPC_HDR_EVT_SIZE (sizeof(UINT16)) + +/* IPC Header contains a Length and an Event code */ +#define BTUSB_LITE_IPC_HDR_SIZE (BTUSB_LITE_IPC_HDR_LEN_SIZE + BTUSB_LITE_IPC_HDR_EVT_SIZE) + +struct btusb_lite_mgt_cb +{ + bool opened; +}; + +struct btusb_lite_stat +{ + unsigned long event_bytes; + unsigned long event_completed; +}; + +struct btusb_lite_from_app +{ + BT_HDR *p_rx_msg; + UINT8 rx_header[BTUSB_LITE_IPC_HDR_SIZE]; + UINT8 rx_header_len; + UINT16 rx_len; /* Decoded Rx Payload length */ +}; + +struct btusb_lite_to_app +{ + BUFFER_Q ipc_queue; /* IPC message queue */ + BT_HDR *p_ipc_msg; + BT_HDR *p_hdr_msg; + BT_HDR *p_hci_msg; +}; + +/* Lite Stack mode */ +#define BTU_FULL_STACK_ACTIVE 0 +#define BTU_LITE_STACK_ACTIVE 1 +#define BTU_TRANSPORT_HOLD 2 +#define BTU_FULL_TRANSPORT_ACTIVE 3 +#define BTU_LITE_TRANSPORT_ACTIVE 4 + +#define BTU_IPC_CMD_SET_TRANSPORT_STATE 0 /* Set transport state (param=transprt state) */ +#define BTU_IPC_CMD_DISABLE_TRANSPORT 1 /* Set transport hardware (param=1 to disable) */ + +struct btusb_lite_btu_cb +{ + UINT8 transport_state; + UINT8 transport_disabled; +}; + +enum btusb_lite_pcm_state +{ + PCM_CLOSED = 0, + PCM_OPENED, + PCM_CONFIGURED, + PCM_STARTED +}; + +struct btusb_lite_pcm_ccb +{ + enum btusb_lite_pcm_state state; + int frequency; +}; + +struct btusb_lite_encoder_ccb +{ + int channel; + int opened; + int encoded_frame_size; + int pcm_frame_size; + int header_size; + int type; /* SBC, SEC, etc. */ + tBTA_AV_AUDIO_CODEC_INFO encoder; +}; + +struct btusb_lite_av_cb +{ + UINT8 audio_open_cnt; + UINT16 stack_mtu; /* received value from bt stack */ + UINT16 curr_mtu; /* modified value in btusb to meet PCM buffer size */ + UINT8 multi_av; + struct btusb_lite_pcm_ccb pcm; + struct btusb_lite_encoder_ccb encoder; + struct btusb_lite_av_scb scb[BTA_AV_NUM_STRS]; + BT_HDR *p_buf_working; + UINT8 option; + UINT8 m_pt; + int header_len; + int payload_len; + UINT32 timestamp; +}; + +/* Main Lite Control Block */ +struct btusb_lite_cb +{ + /* static entries */ + struct proc_dir_entry *p_lite_pde; + struct btpcm *p_btpcm; + /* dynamic entries */ + struct { + bool opened; + struct btusb_lite_stat stat; + struct btusb_lite_from_app from_app; + struct btusb_lite_to_app to_app; + struct btusb_lite_mgt_cb mgt; /* Management */ + struct btusb_lite_btu_cb btu; /* BTU */ + struct btusb_lite_l2c_cb l2c; /* L2C */ + struct btusb_lite_av_cb av; /* AV */ + struct btusb_lite_avdt_cb avdt; + } s; +}; + +/******************************************************************************* + ** + ** Function btusb_lite_create + ** + ** Description Create BTUSB Lite interface + ** + ** Returns status (< 0 if error) + ** + *******************************************************************************/ +int btusb_lite_create(struct btusb *p_dev, struct usb_interface *p_interface); + +/******************************************************************************* + ** + ** Function btusb_lite_delete + ** + ** Description Delete BTUSB Lite interface + ** + ** Returns none + ** + *******************************************************************************/ +void btusb_lite_delete(struct btusb *p_dev, struct usb_interface *p_interface); + +/******************************************************************************* + ** + ** Function btusb_lite_stop_all + ** + ** Description Stop all sound streams + ** + ** Returns void + ** + *******************************************************************************/ +void btusb_lite_stop_all(struct btusb *p_dev); + +/******************************************************************************* + ** + ** Function btusb_lite_is_hci_over_ipc + ** + ** Description Check if HCI is over IPC (Lite Interface). + ** + ** Returns int (1 if HCI is over IPC otherwise 0) + ** + *******************************************************************************/ +int btusb_lite_is_hci_over_ipc(struct btusb *p_dev); + +#endif /* BTUSB_LITE_H*/ + diff --git a/inc/btusb_lite_av.h b/inc/btusb_lite_av.h new file mode 100755 index 0000000..5b2f05d --- a/dev/null +++ b/inc/btusb_lite_av.h @@ -0,0 +1,135 @@ +/* + * + * btusb_lite_av.h + * + * + * + * Copyright (C) 2011-2013 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ +#ifndef BTUSB_LITE_AV_H +#define BTUSB_LITE_AV_H + +typedef UINT8 tBTA_AV_CODEC; /* codec type */ +typedef UINT8 tBTA_AV_CHNL; /* the channel: audio/video */ +typedef UINT8 tBTA_AV_HNDL; /* the handle: ((hdi + 1)|chnl) */ + +/* data type for the Audio Codec Information*/ +typedef struct +{ + UINT16 bit_rate; /* SBC encoder bit rate in kbps */ + UINT16 bit_rate_busy; /* SBC encoder bit rate in kbps */ + UINT16 bit_rate_swampd;/* SBC encoder bit rate in kbps */ + UINT8 busy_level; /* Busy level indicating the bit-rate to be used */ + UINT8 codec_info[AVDT_CODEC_SIZE]; + UINT8 codec_type; /* Codec type */ +} tBTA_AV_AUDIO_CODEC_INFO; + +/* type for AV stream control block on Lite stack*/ +struct btusb_lite_av_scb +{ + BUFFER_Q out_q; /* used for audio channels only */ + BD_ADDR peer_addr; /* peer BD address */ + UINT16 l2c_cid; /* L2CAP channel ID */ + tBTA_AV_CODEC codec_type; /* codec type */ + BOOLEAN cong; /* TRUE if AVDTP congested */ + tBTA_AV_CHNL chnl; /* the channel: audio/video */ + tBTA_AV_HNDL hndl; /* the handle: ((hdi + 1)|chnl) */ + UINT8 avdt_handle; /* AVDTP handle */ + UINT8 hdi; /* the index to SCB[] */ + UINT8 l2c_bufs; /* the number of buffers queued to L2CAP */ + BOOLEAN started; /* TRUE if stream started from call-out perspective */ + BOOLEAN start_stop_flag;/* TRUE when snk is INT and bta_av_start_ok() decides that */ +}; + +/* these bits are defined for btusb_lite_av_cb.multi_av */ +#define BTA_AV_MULTI_AV_SUPPORTED 0x01 +#define BTA_AV_MULTI_AV_IN_USE 0x02 + +/* + * Globals + */ +extern int pcm0_mute; + +/******************************************************************************* +** +** Function btusb_lite_av_add +** +** Description Add (Sync) an AV channel. +** +** +** Returns None. +** +*******************************************************************************/ +void btusb_lite_av_add(struct btusb *p_dev, tBTA_AV_SYNC_INFO *p_sync_info, + UINT8 multi_av_supported, UINT16 curr_mtu); + +/******************************************************************************* +** +** Function btusb_lite_av_remove +** +** Description Remove (Cleanup) an AV channel. +** +** +** Returns None. +** +*******************************************************************************/ +void btusb_lite_av_remove(struct btusb *p_dev, UINT8 scb_idx, + UINT8 audio_open_cnt, UINT16 curr_mtu); + +/******************************************************************************* +** +** Function btusb_lite_av_start +** +** Description Start AV +** +** +** Returns Pointer to scb or NULL if index is out of range or scb +** is not allocated. +** +*******************************************************************************/ +void btusb_lite_av_start(struct btusb *p_dev, UINT8 scb_idx, UINT8 start_stop_flag, + UINT8 audio_open_cnt, tBTA_AV_AUDIO_CODEC_INFO *p_codec_cfg); + +/******************************************************************************* +** +** Function btusb_lite_av_stop +** +** Description Start AV +** +** +** Returns Pointer to scb or NULL if index is out of range or scb +** is not allocated. +** +*******************************************************************************/ +void btusb_lite_av_stop(struct btusb *p_dev, UINT8 scb_idx, UINT8 audio_open_cnt); + +/******************************************************************************* +** +** Function btusb_lite_av_suspend +** +** Description Suspend AV +** +** Returns none. +** +*******************************************************************************/ +void btusb_lite_av_suspend(struct btusb *p_dev, UINT8 scb_idx, UINT8 audio_open_cnt); + +#endif /* BTUSB_LITE_AV_H*/ + diff --git a/inc/btusb_lite_avdt.h b/inc/btusb_lite_avdt.h new file mode 100755 index 0000000..2be89a1 --- a/dev/null +++ b/inc/btusb_lite_avdt.h @@ -0,0 +1,148 @@ +/* + * + * btusb_lite_avdt.h + * + * + * + * Copyright (C) 2011-2013 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ +#ifndef BTUSB_LITE_AVDT_H +#define BTUSB_LITE_AVDT_H + +/* AVDT Option */ +#define BTUSB_LITE_AVDT_OPT_NO_RTP 0x01 /* No RTP Header */ +#define BTUSB_LITE_AVDT_OPT_NO_MPH 0x02 /* No Media Payload Header */ + +/* Definitions for A2DP packets */ +#define BTUSB_LITE_RTP_SIZE 12 /* RTP Header size */ +#define BTUSB_LITE_MEDIA_SIZE 1 /* Media Header size */ +#define BTUSB_LITE_SCMS_SIZE 1 /* SCMS Header size */ + +/* Some A2DP Definitions */ +#define AVDT_MEDIA_OCTET1 0x80 /* First byte of media packet header */ + +#define AVDT_MARKER_SET 0x80 +#define AVDT_RTP_PAYLOAD_TYPE 0x60 /* First Dynamic Payload type */ + +/* AVDT Header size */ +#define BTUSB_LITE_AVDT_HDR_SIZE (BTUSB_LITE_RTP_SIZE + BTUSB_LITE_MEDIA_SIZE) + +/* A2DP Definitions */ +#define A2D_SBC_HDR_NUM_MSK 0x0F /* A2DP Media Header Frame Mask */ + + +/* channel control block type */ +struct btusb_lite_avdt_ccb +{ + BD_ADDR peer_addr; /* BD address of peer */ + BOOLEAN allocated; /* Whether ccb is allocated */ + UINT16 lcid; /* local L2CAP channel ID */ + UINT16 peer_mtu; /* L2CAP mtu of the peer device */ +}; + +/* SCMS information */ +struct btusb_lite_avdt_scms +{ + BOOLEAN enable; /* Indicates if SCMS is enabled */ + UINT8 header; /* SCMS Header */ +}; + + +/* stream control block type */ +struct btusb_lite_avdt_scb +{ + BT_HDR *p_pkt; /* packet waiting to be sent */ + struct btusb_lite_avdt_ccb *p_ccb; /* ccb associated with this scb */ + UINT16 media_seq; /* media packet sequence number */ + BOOLEAN allocated; /* whether scb is allocated or unused */ + BOOLEAN in_use; /* whether stream being used by peer */ + BOOLEAN cong; /* Whether media transport channel is congested */ + UINT8 handle; + UINT8 mux_tsid_media; /* TSID for media transport session */ + struct btusb_lite_avdt_scms scms; +}; + +struct btusb_lite_avdt_cb +{ + struct btusb_lite_avdt_ccb ccb[AVDT_NUM_LINKS]; /* channel control blocks */ + struct btusb_lite_avdt_scb scb[AVDT_NUM_SEPS]; /* stream control blocks */ + +}; +/******************************************************************************* +** +** Function btusb_lite_avdt_scb_by_hdl +** +** Description Given an scb handle (or seid), return a pointer to the scb. +** +** +** Returns Pointer to scb or NULL if index is out of range or scb +** is not allocated. +** +*******************************************************************************/ +struct btusb_lite_avdt_scb *btusb_lite_avdt_scb_by_hdl(struct btusb *p_dev, UINT8 hdl); + +/******************************************************************************* +** +** Function btusb_lite_avdt_init_scb +** +** Description allocate and initialize SCB/CCB with received sync info +** +** Returns AVDT_SYNC_SUCCESS/AVDT_SYNC_FAILURE +** +*******************************************************************************/ +UINT8 btusb_lite_avdt_init_scb(struct btusb *p_dev, tAVDT_SCB_SYNC_INFO *p_scb_info); + +/******************************************************************************* +** +** Function btusb_lite_avdt_remove_scb +** +** Description deallocate SCB and CCB +** +** Returns AVDT_SYNC_SUCCESS/AVDT_SYNC_FAILURE +** +*******************************************************************************/ +UINT8 btusb_lite_avdt_remove_scb(struct btusb *p_dev, UINT8 handle, tAVDT_SCB_SYNC_INFO *p_scb_info); + +/******************************************************************************* +** +** Function btusb_lite_avdt_send +** +** Description AVDT packet send +** +** Returns None +** +*******************************************************************************/ +void btusb_lite_avdt_send(struct btusb *p_dev, BT_HDR *p_msg, UINT8 avdt_handle, + UINT8 m_pt, UINT8 option, UINT32 timestamp); + +/******************************************************************************* +** +** Function btusb_lite_avdt_cp_set_scms +** +** Description Set SCMS Content Protection for a channel +** +** Returns AVDT_SYNC_SUCCESS/AVDT_SYNC_FAILURE +** +*******************************************************************************/ +UINT8 btusb_lite_avdt_cp_set_scms(struct btusb *p_dev, UINT8 avdt_handle, + BOOLEAN enable, UINT8 scms_hdr); + +#endif /* BTUSB_LITE_AVDT_H*/ + diff --git a/inc/btusb_lite_hci.h b/inc/btusb_lite_hci.h new file mode 100755 index 0000000..ca634a1 --- a/dev/null +++ b/inc/btusb_lite_hci.h @@ -0,0 +1,71 @@ +/* + * + * btusb_lite_hci.h + * + * + * + * Copyright (C) 2011-2013 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ + +#ifndef BTUSB_LITE_HCI_H +#define BTUSB_LITE_HCI_H + +/* Definitions for HCI ACL packets */ + +/* HCI ACL Packet: HCI_Type, ConHdl, Length */ +#define BTUSB_LITE_HCI_ACL_HDR_SIZE (sizeof(UINT8) + sizeof(UINT16) + sizeof(UINT16)) + +/******************************************************************************* + ** + ** Function btusb_lite_hci_acl_send + ** + ** Description Send an ACL packet to HCI + ** + ** Returns Status + ** + *******************************************************************************/ +int btusb_lite_hci_acl_send(struct btusb *p_dev, BT_HDR *p_msg, UINT16 con_hdl); + +/******************************************************************************* + ** + ** Function btusb_lite_hci_cmd_filter + ** + ** Description Check if the Sent HCI Command need to be handled/caught (not + ** sent to BT controller). + ** + ** Returns status: <> 0 if the command must be send to BT controller + ** 0 if the command is handled + ** + *******************************************************************************/ +int btusb_lite_hci_cmd_filter(struct btusb *p_dev, BT_HDR *p_msg); + +/******************************************************************************* + ** + ** Function btusb_lite_hci_event_filter + ** + ** Description Handle/Filter HCI Events received from BT controller + ** + ** Returns int (1 if HCI is over IPC otherwise 0) + ** + *******************************************************************************/ +int btusb_lite_hci_event_filter(struct btusb *p_dev, UINT8 *p_data, int length); + +#endif /* BTUSB_LITE_HCI_H */ + diff --git a/inc/btusb_lite_l2c.h b/inc/btusb_lite_l2c.h new file mode 100755 index 0000000..c740a29 --- a/dev/null +++ b/inc/btusb_lite_l2c.h @@ -0,0 +1,105 @@ +/* + * + * btusb_lite_l2c.h + * + * + * + * Copyright (C) 2011-2013 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ +#ifndef BTUSB_LITE_L2C_H +#define BTUSB_LITE_L2C_H + +/* L2CAP Header Definition (Length, CID) */ +#define BTUSB_LITE_L2CAP_HDR_SIZE (sizeof(UINT16) + sizeof(UINT16)) + +struct btusb_lite_l2c_ccb +{ + BOOLEAN in_use; + UINT16 local_cid; + UINT16 remote_cid; + UINT16 out_mtu; + UINT16 handle; + UINT16 link_xmit_quota; + UINT8 is_flushable; + + UINT16 tx_pending; +}; + +struct btusb_lite_l2c_cb +{ + /* Req */ + UINT16 light_xmit_quota; + UINT16 acl_data_size; + UINT16 non_flushable_pbf; + UINT8 multi_av_data_cong_start; + UINT8 multi_av_data_cong_end; + UINT8 multi_av_data_cong_discard; + struct btusb_lite_l2c_ccb ccb[BTM_SYNC_INFO_NUM_STR]; + + /* Status */ + UINT16 light_xmit_unacked; +}; + +/******************************************************************************* +** +** Function btusb_lite_l2c_add +** +** Description Synchronize (Add) L2CAP Stream +** +** Returns Status. +** +*******************************************************************************/ +int btusb_lite_l2c_add(struct btusb *p_dev, tL2C_STREAM_INFO *p_l2c_stream); + +/******************************************************************************* +** +** Function btusb_lite_l2c_remove +** +** Description Synchronize (Remove) L2CAP Stream +** +** Returns Status. +** +*******************************************************************************/ +int btusb_lite_l2c_remove(struct btusb *p_dev, UINT16 local_cid); + +/******************************************************************************* +** +** Function btusb_lite_l2c_send +** +** Description Send L2CAP packet +** +** Returns Status +** +*******************************************************************************/ +int btusb_lite_l2c_send(struct btusb *p_dev, BT_HDR *p_msg, UINT16 local_cid); + +/******************************************************************************* + ** + ** Function btusb_lite_l2c_nocp_hdlr + ** + ** Description L2CAP NumberOfcompletePacket Handler function + ** + ** Returns Number Of Complete Packet caught + ** + *******************************************************************************/ +UINT16 btusb_lite_l2c_nocp_hdlr(struct btusb *p_dev, UINT16 con_hdl, UINT16 num_cplt_pck); + +#endif /* BTUSB_LITE_L2C_H*/ + diff --git a/inc/btusb_proc.h b/inc/btusb_proc.h new file mode 100755 index 0000000..4a96270 --- a/dev/null +++ b/inc/btusb_proc.h @@ -0,0 +1,49 @@ +/* + * + * btusb_proc.h + * + * + * + * Copyright (C) 2011-2014 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * Module that defines the proc file system routines. + * + */ + +#ifndef BTUSB_PROC_H +#define BTUSB_PROC_H + +struct btusb; + +#ifdef CONFIG_PROC_FS + +extern void btusb_proc_init(void); +extern void btusb_proc_exit(void); +extern void btusb_proc_add(struct btusb *p_dev, const char *name); +extern void btusb_proc_remove(struct btusb *p_dev, const char *name); + +#else + +static inline void btusb_proc_init(void) {} +static inline void btusb_proc_exit(void) {} +static inline void btusb_proc_add(struct btusb *p_dev, const char *name) {} +static inline void btusb_proc_remove(struct btusb *p_dev, const char *name) {} + +#endif + +#endif diff --git a/inc/btusbext.h b/inc/btusbext.h new file mode 100755 index 0000000..4654f25 --- a/dev/null +++ b/inc/btusbext.h @@ -0,0 +1,130 @@ +/* + * + * btusbext.h + * + * + * + * Copyright (C) 2011-2013 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ + +#ifndef BTUSBEXT_H +#define BTUSBEXT_H + +#include <linux/time.h> + +/* BTUSB Statistics structure */ +typedef struct +{ + /* All URB submit counter */ + unsigned long urb_submit_ok; + unsigned long urb_submit_err; + + /* All URB out complete */ + unsigned long urb_out_complete; + unsigned long urb_out_complete_err; + + /* CMD counters */ + unsigned long cmd_submit_ok; + unsigned long cmd_submit_err; + unsigned long cmd_complete; + unsigned long cmd_complete_err; + + /* ACL RX counters */ + unsigned long acl_rx_submit_ok; + unsigned long acl_rx_submit_err; + unsigned long acl_rx_complete; + unsigned long acl_rx_complete_err; + unsigned long acl_rx_resubmit; + unsigned long acl_rx_bytes; + + /* DIAG RX counters */ + unsigned long diag_rx_submit_ok; + unsigned long diag_rx_submit_err; + unsigned long diag_rx_complete; + unsigned long diag_rx_complete_err; + unsigned long diag_rx_resubmit; + unsigned long diag_rx_bytes; + + /* EVENT counters */ + unsigned long event_submit_ok; + unsigned long event_submit_err; + unsigned long event_complete; + unsigned long event_complete_err; + unsigned long event_resubmit; + unsigned long event_bytes; + + /* VOICE RX counters */ + unsigned long voicerx_submit_ok; + unsigned long voicerx_submit_err; + unsigned long voicerx_complete; + unsigned long voicerx_complete_err; + unsigned long voicerx_bad_frames; + unsigned long voicerx_raw_bytes; + unsigned long voicerx_skipped_bytes; + unsigned long voicerx_split_hdr; + unsigned long voicerx_bad_hdr; + unsigned long voicerx_bad_size; + + /* VOICE TX counters */ + unsigned long voicetx_submit_ok; + unsigned long voicetx_submit_err; + unsigned long voicetx_nobuf; + unsigned long voicetx_complete; + unsigned long voicetx_complete_err; + + /* voice timings */ + unsigned long voice_max_tx_done_delta_time; + unsigned long voice_min_tx_done_delta_time; + struct timeval voice_tx_done_delta_time; + struct timeval voice_last_tx_done_ts; + + /* max delta time between 2 consecutive tx done routine in us */ + unsigned long voice_max_rx_rdy_delta_time; + unsigned long voice_min_rx_rdy_delta_time; + struct timeval voice_rx_rdy_delta_time; + struct timeval voice_last_rx_rdy_ts; + + /* max delta time between 2 consecutive tx done routine in us */ + unsigned long voice_max_rx_feeding_interval; + unsigned long voice_min_rx_feeding_interval; + struct timeval voice_rx_feeding_interval; + struct timeval voice_last_rx_feeding_ts; +} tBTUSB_STATS; + +/* IOCTL definitions (shared among all user mode applications, do not modify) */ +#define IOCTL_BTWUSB_GET_STATS 0x1001 +#define IOCTL_BTWUSB_CLEAR_STATS 0x1002 +#define IOCTL_BTWUSB_PUT 0x1003 +#define IOCTL_BTWUSB_PUT_CMD 0x1004 +#define IOCTL_BTWUSB_GET 0x1005 +#define IOCTL_BTWUSB_GET_EVENT 0x1006 +#define IOCTL_BTWUSB_PUT_VOICE 0x1007 +#define IOCTL_BTWUSB_GET_VOICE 0x1008 +#define IOCTL_BTWUSB_ADD_VOICE_CHANNEL 0x1009 +#define IOCTL_BTWUSB_REMOVE_VOICE_CHANNEL 0x100a +#define IOCTL_BTWUSB_DEV_RESET 0x100b + +struct btusb_ioctl_sco_info +{ + unsigned short handle; + unsigned char burst; +}; + +#endif diff --git a/inc/data_types.h b/inc/data_types.h new file mode 100755 index 0000000..e8ccf4b --- a/dev/null +++ b/inc/data_types.h @@ -0,0 +1,79 @@ +/* +* +* data_types.h +* +* +* +* Copyright (C) 2011 Broadcom Corporation. +* +* +* +* This software is licensed under the terms of the GNU General Public License, +* version 2, as published by the Free Software Foundation (the "GPL"), and may +* be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. +* +* +* A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php +* or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA +* +* +*/ + +#ifndef DATA_TYPES_H +#define DATA_TYPES_H + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned int UINT32, *PUINT32; +typedef signed int INT32, *PINT32; +typedef signed char INT8; +typedef signed short INT16; + + +#if defined(_WIDE_CHAR) || defined(_UNICODE) +typedef unsigned short WIDECHAR; /* for _BT_WIN32 possibly use wchar_t */ +#define WIDE_NULL_CHAR ((WIDECHAR)0) +#else +typedef unsigned char WIDECHAR; +#define WIDE_NULL_CHAR '\0' +#endif + +typedef UINT32 TIME_STAMP; + + +#ifndef _WINDOWS_VXD +typedef unsigned char BOOLEAN; + +#ifndef TRUE +#define TRUE (!FALSE) +#endif +#endif + +typedef unsigned char UBYTE; + +#define PACKED +#define INLINE + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN FALSE +#endif + +#define UINT16_LOW_BYTE(x) ((x) & 0xff) +#define UINT16_HI_BYTE(x) ((x) >> 8) + +#define ARRAY_SIZEOF(x) (sizeof(x)/sizeof(x[0])) + +#endif diff --git a/inc/gki.h b/inc/gki.h new file mode 100755 index 0000000..8b04756 --- a/dev/null +++ b/inc/gki.h @@ -0,0 +1,190 @@ +/* + * + * gki.h + * + * + * + * Copyright (C) 2011-2012 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ + +#ifndef GKI_H +#define GKI_H + +#include "target.h" +#include "data_types.h" + +//#define TRACE_GKI_BUFFERS + +/* Error codes */ +#define GKI_SUCCESS 0x00 +#define GKI_FAILURE 0x01 +#define GKI_INVALID_TASK 0xF0 +#define GKI_INVALID_POOL 0xFF + + + +/************************************************************************ +** Mailbox definitions. Each task has 4 mailboxes that are used to +** send buffers to the task. +*/ +#define TASK_MBOX_0 0 +#define TASK_MBOX_1 1 +#define TASK_MBOX_2 2 +#define TASK_MBOX_3 3 + +#define NUM_TASK_MBOX 4 + +/************************************************************************ +** Event definitions. +** +** There are 4 reserved events used to signal messages rcvd in task mailboxes. +** There are 4 reserved events used to signal timeout events. +** There are 8 general purpose events available for applications. +*/ +#define MAX_EVENTS 16 + +#define TASK_MBOX_0_EVT_MASK 0x0001 +#define TASK_MBOX_1_EVT_MASK 0x0002 +#define TASK_MBOX_2_EVT_MASK 0x0004 +#define TASK_MBOX_3_EVT_MASK 0x0008 + + +#define TIMER_0 0 +#define TIMER_1 1 +#define TIMER_2 2 +#define TIMER_3 3 + +#define TIMER_0_EVT_MASK 0x0010 +#define TIMER_1_EVT_MASK 0x0020 +#define TIMER_2_EVT_MASK 0x0040 +#define TIMER_3_EVT_MASK 0x0080 + +#define APPL_EVT_0 8 +#define APPL_EVT_1 9 +#define APPL_EVT_2 10 +#define APPL_EVT_3 11 +#define APPL_EVT_4 12 +#define APPL_EVT_5 13 +#define APPL_EVT_6 14 +#define APPL_EVT_7 15 + +#define EVENT_MASK(evt) ((UINT16)(0x0001 << (evt))) + +/*********************************************************************** +** This queue is a general purpose buffer queue, for application use. +*/ +typedef struct +{ + void *p_first; + void *p_last; + UINT16 count; +} BUFFER_Q; + +#define GKI_IS_QUEUE_EMPTY(p_q) ((p_q)->count == 0) + +/* Task constants +*/ +#ifndef TASKPTR_DEF +#define TASKPTR_DEF +#if defined (LINUX_KERNEL) +typedef int (*TASKPTR)(UINT32); +#else +typedef void (*TASKPTR)(UINT32); +#endif +#endif + +#define GKI_PUBLIC_POOL 0 /* General pool accessible to GKI_getbuf() */ +#define GKI_RESTRICTED_POOL 1 /* Inaccessible pool to GKI_getbuf() */ + +/*********************************************************************** +** Function prototypes +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Task management */ +BT_API extern void GKI_init(void); +BT_API extern void GKI_shutdown(void); + +/* To get and release buffers, change owner and get size +*/ +BT_API extern void GKI_change_buf_owner (void *, UINT8); +BT_API extern UINT8 GKI_create_pool (UINT16, UINT16, UINT8, void *); +BT_API extern void GKI_delete_pool (UINT8); +BT_API extern void *GKI_find_buf_start (void *); +BT_API extern void GKI_freebuf (void *); +#ifdef TRACE_GKI_BUFFERS +BT_API extern void *GKI_getbuf_trace (UINT16, char *, int); +#define GKI_getbuf(_size) GKI_getbuf_trace(_size,__FILE__,__LINE__) +#else +BT_API extern void *GKI_getbuf (UINT16); +#endif + +BT_API extern UINT16 GKI_get_buf_size (void *); +BT_API extern UINT16 GKI_get_pool_bufsize (UINT8 pool_id); + +// For compatibility with BTE +#define GKI_POOL_SIZE(x) GKI_get_pool_bufsize(x) + + +#ifdef TRACE_GKI_BUFFERS +BT_API extern void *GKI_getpoolbuf_trace (UINT8, char *, int); +#define GKI_getpoolbuf(_pool) GKI_getpoolbuf_trace(_pool,__FILE__,__LINE__) +#else +BT_API extern void *GKI_getpoolbuf (UINT8); +#endif + +BT_API extern UINT16 GKI_poolfreecount (UINT8); +BT_API extern UINT16 GKI_poolutilization (UINT8); +BT_API extern UINT8 GKI_set_pool_permission(UINT8, UINT8); + + +/* User buffer queue management +*/ +BT_API extern void *GKI_dequeue (BUFFER_Q *); +BT_API extern UINT8 GKI_buffer_status(void *p_buf); +BT_API extern void GKI_enqueue (BUFFER_Q *, void *); +BT_API extern void GKI_enqueue_head (BUFFER_Q *, void *); +BT_API extern void *GKI_getfirst (BUFFER_Q *); +BT_API extern void *GKI_getnext (void *); +BT_API extern void GKI_init_q (BUFFER_Q *); +BT_API extern BOOLEAN GKI_queue_is_empty(BUFFER_Q *); +BT_API extern void *GKI_remove_from_queue (BUFFER_Q *, void *); + +BT_API extern BOOLEAN GKI_chk_buf_pool_damage(UINT8 pool_id); + +/* Disable Interrupts, Enable Interrupts +*/ +BT_API extern void GKI_enable(void); +BT_API extern void GKI_disable(void); + +/* Exception handling +*/ +BT_API extern void GKI_exception (UINT16, const char *msg, ...); + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/inc/gki_int.h b/inc/gki_int.h new file mode 100755 index 0000000..f210e58 --- a/dev/null +++ b/inc/gki_int.h @@ -0,0 +1,197 @@ +/* + * + * gki_int.h + * + * + * + * Copyright (C) 2011-2012 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ + +#ifndef GKI_INT_H +#define GKI_INT_H + +#include "gki.h" + +/* Task States: (For OSRdyTbl) */ +// TASK_DEAD is already defined by Linux => undefine it +#ifdef TASK_DEAD +#undef TASK_DEAD +#endif + +#define TASK_DEAD 0 /* b0000 */ +#define TASK_READY 1 /* b0001 */ +#define TASK_WAIT 2 /* b0010 */ +#define TASK_DELAY 4 /* b0100 */ +#define TASK_SUSPEND 8 /* b1000 */ + + +/******************************************************************** +** Internal Error codes +*********************************************************************/ +#define GKI_ERROR_BUF_CORRUPTED 0xFFFF +#define GKI_ERROR_NOT_BUF_OWNER 0xFFFE +#define GKI_ERROR_FREEBUF_BAD_QID 0xFFFD +#define GKI_ERROR_FREEBUF_BUF_LINKED 0xFFFC +#define GKI_ERROR_SEND_MSG_BAD_DEST 0xFFFB +#define GKI_ERROR_SEND_MSG_BUF_LINKED 0xFFFA +#define GKI_ERROR_ENQUEUE_BUF_LINKED 0xFFF9 +#define GKI_ERROR_DELETE_POOL_BAD_QID 0xFFF8 +#define GKI_ERROR_BUF_SIZE_TOOBIG 0xFFF7 +#define GKI_ERROR_BUF_SIZE_ZERO 0xFFF6 +#define GKI_ERROR_ADDR_NOT_IN_BUF 0xFFF5 +#define GKI_ERROR_BUF_POOL_CORRUPT 0xFFF4 +#define GKI_ERROR_BUFFER_TRACE_ON 0xFFF3 +#define GKI_ERROR_SEGS_EXCEEDED 0xFFF2 +#define GKI_ERROR_NO_MEMORY_FOR_SEG 0xFFF1 + + +/******************************************************************** +** Buffer Management Data Structures +*********************************************************************/ + +typedef struct _buffer_hdr +{ + struct _buffer_hdr *p_next; + UINT8 q_id; + UINT8 task_id; + UINT8 status; + UINT8 Type; +#ifdef TRACE_GKI_BUFFERS + struct _buffer_hdr *p_next_all; + char *pFile; + int linenum; + UINT32 times_alloc; +#endif +} BUFFER_HDR_T; + +/* Allocate each buffer pool in up to 4 memory segments, for efficiency +*/ +#define MAX_BUFFPOOL_SEGS 4 + +typedef struct _free_queue +{ + UINT8 *seg_start[MAX_BUFFPOOL_SEGS]; + UINT8 *seg_end[MAX_BUFFPOOL_SEGS]; + + BUFFER_HDR_T *p_first; + BUFFER_HDR_T *p_last; + UINT16 total; + UINT16 cur_cnt; + UINT16 max_cnt; +#ifdef TRACE_GKI_BUFFERS + BUFFER_HDR_T *p_first_all; +#endif +} FREE_QUEUE_T; + + +/* Buffer related defines +*/ +#define BUFFER_HDR_SIZE (sizeof(BUFFER_HDR_T)) /* Offset past header */ +#define BUFFER_PADDING_SIZE (sizeof(BUFFER_HDR_T) + sizeof(UINT32)) /* Header + Magic Number */ +#define MAX_USER_BUF_SIZE ((UINT16)0xffff - BUFFER_PADDING_SIZE) /* pool size must allow for header */ +#define MAGIC_NO 0xDDBADDBA + +#define BUF_STATUS_FREE 0 +#define BUF_STATUS_UNLINKED 1 +#define BUF_STATUS_QUEUED 2 + +/* Exception related structures +*/ +#define MAX_EXCEPTION 8 +#define MAX_EXCEPTION_MSGLEN 64 + +typedef struct +{ + UINT16 type; + UINT8 taskid; + UINT8 msg[MAX_EXCEPTION_MSGLEN]; +} EXCEPTION_T; + + +/* Put all GKI variables into one control block +*/ +typedef struct +{ + /* Task management variables + */ + /* The stack and stack size are not used on Windows + */ +#if (GKI_USE_DYNAMIC_BUFFERS == FALSE) + +#if (GKI_NUM_FIXED_BUF_POOLS > 0) + UINT8 bufpool0[(GKI_BUF0_SIZE + BUFFER_PADDING_SIZE) * GKI_BUF0_MAX]; +#endif + +#if (GKI_NUM_FIXED_BUF_POOLS > 1) + UINT8 bufpool1[(GKI_BUF1_SIZE + BUFFER_PADDING_SIZE) * GKI_BUF1_MAX]; +#endif + +#if (GKI_NUM_FIXED_BUF_POOLS > 2) + UINT8 bufpool2[(GKI_BUF2_SIZE + BUFFER_PADDING_SIZE) * GKI_BUF2_MAX]; +#endif + +#if (GKI_NUM_FIXED_BUF_POOLS > 3) + UINT8 bufpool3[(GKI_BUF3_SIZE + BUFFER_PADDING_SIZE) * GKI_BUF3_MAX]; +#endif + +#endif + + INT IsRunning; + + /* Define the buffer pool management variables + */ + FREE_QUEUE_T freeq[GKI_NUM_TOTAL_BUF_POOLS]; + + UINT16 pool_buf_size[GKI_NUM_TOTAL_BUF_POOLS]; + UINT16 pool_max_count[GKI_NUM_TOTAL_BUF_POOLS]; + UINT16 pool_additions[GKI_NUM_TOTAL_BUF_POOLS]; + + UINT16 ExceptionCnt; + EXCEPTION_T Exception[MAX_EXCEPTION]; + + /* Define the buffer pool access control variables */ + UINT16 pool_access_mask; + UINT8 pool_list[GKI_NUM_TOTAL_BUF_POOLS]; /* buffer pools arranged in the order of size */ + int curr_total_no_of_pools; +} tGKI_CB; + + +void gki_buffer_init(void); +BOOLEAN gki_chk_buf_damage(void *p_buf); +void *gki_reserve_os_memory (UINT32 size); +void gki_release_os_memory (void *p_mem); + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _BT_DYNAMIC_MEMORY +extern tGKI_CB gki_cb; +#else +extern tGKI_CB *gp_gki_cb; +#define gki_cb (*gp_gki_cb) +#endif + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/inc/hcidefs.h b/inc/hcidefs.h new file mode 100755 index 0000000..57cd195 --- a/dev/null +++ b/inc/hcidefs.h @@ -0,0 +1,1819 @@ +/* +* +* hcidefs.h +* +* +* +* Copyright (C) 2011 Broadcom Corporation. +* +* +* +* This software is licensed under the terms of the GNU General Public License, +* version 2, as published by the Free Software Foundation (the "GPL"), and may +* be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +* or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. +* +* +* A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php +* or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA +* +* +*/ + +#ifndef HCIDEFS_H +#define HCIDEFS_H + +#define HCI_PROTO_VERSION 0x01 /* Version for BT spec 1.1 */ +#define HCI_PROTO_VERSION_1_2 0x02 /* Version for BT spec 1.2 */ +#define HCI_PROTO_VERSION_2_0 0x03 /* Version for BT spec 2.0 */ +#define HCI_PROTO_VERSION_2_1 0x04 /* Version for BT spec 2.1 [Lisbon] */ +#define HCI_PROTO_REVISION 0x000C /* Current implementation version */ +/* +** Definitions for HCI groups +*/ +#define HCI_GRP_LINK_CONTROL_CMDS (0x01 << 10) +#define HCI_GRP_LINK_POLICY_CMDS (0x02 << 10) +#define HCI_GRP_HOST_CONT_BASEBAND_CMDS (0x03 << 10) +#define HCI_GRP_INFORMATIONAL_PARAMS (0x04 << 10) +#define HCI_GRP_STATUS_PARAMS (0x05 << 10) +#define HCI_GRP_TESTING_CMDS (0x06 << 10) +#define HCI_GRP_L2CAP_CMDS (0x07 << 10) +#define HCI_GRP_L2CAP_HCI_EVTS (0x08 << 10) + +#define HCI_GRP_VENDOR_SPECIFIC (0x3F << 10) + +/* Group occupies high 6 bits of the HCI command rest is opcode itself */ +#define HCI_OGF(p) (UINT8)((0xFC00 & (p)) >> 10) +#define HCI_OCF(p) ( 0x3FF & (p)) + +/* +** Definitions for Link Control Commands +*/ +/* Following opcode is used only in command complete event for flow control */ +#define HCI_COMMAND_NONE 0x0000 + +/* Commands of HCI_GRP_LINK_CONTROL_CMDS group */ +#define HCI_INQUIRY (0x0001 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_INQUIRY_CANCEL (0x0002 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_PERIODIC_INQUIRY_MODE (0x0003 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_EXIT_PERIODIC_INQUIRY_MODE (0x0004 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CREATE_CONNECTION (0x0005 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_DISCONNECT (0x0006 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ADD_SCO_CONNECTION (0x0007 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CREATE_CONNECTION_CANCEL (0x0008 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ACCEPT_CONNECTION_REQUEST (0x0009 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_REJECT_CONNECTION_REQUEST (0x000A | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_LINK_KEY_REQUEST_REPLY (0x000B | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_LINK_KEY_REQUEST_NEG_REPLY (0x000C | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_PIN_CODE_REQUEST_REPLY (0x000D | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_PIN_CODE_REQUEST_NEG_REPLY (0x000E | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CHANGE_CONN_PACKET_TYPE (0x000F | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_AUTHENTICATION_REQUESTED (0x0011 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_SET_CONN_ENCRYPTION (0x0013 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_CHANGE_CONN_LINK_KEY (0x0015 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_MASTER_LINK_KEY (0x0017 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_RMT_NAME_REQUEST (0x0019 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_RMT_NAME_REQUEST_CANCEL (0x001A | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_RMT_FEATURES (0x001B | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_RMT_EXT_FEATURES (0x001C | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_RMT_VERSION_INFO (0x001D | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_RMT_CLOCK_OFFSET (0x001F | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_READ_LMP_HANDLE (0x0020 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_SETUP_ESCO_CONNECTION (0x0028 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_ACCEPT_ESCO_CONNECTION (0x0029 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_REJECT_ESCO_CONNECTION (0x002A | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_IO_CAPABILITY_RESPONSE (0x002B | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_USER_CONF_REQUEST_REPLY (0x002C | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_USER_CONF_VALUE_NEG_REPLY (0x002D | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_USER_PASSKEY_REQ_REPLY (0x002E | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_USER_PASSKEY_REQ_NEG_REPLY (0x002F | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_REM_OOB_DATA_REQ_REPLY (0x0030 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_REM_OOB_DATA_REQ_NEG_REPLY (0x0033 | HCI_GRP_LINK_CONTROL_CMDS) +#define HCI_IO_CAP_REQ_NEG_REPLY (0x0034 | HCI_GRP_LINK_CONTROL_CMDS) + +#define HCI_LINK_CTRL_CMDS_FIRST HCI_INQUIRY +#define HCI_LINK_CTRL_CMDS_LAST HCI_IO_CAP_REQ_NEG_REPLY + +/* Commands of HCI_GRP_LINK_POLICY_CMDS */ +#define HCI_HOLD_MODE (0x0001 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_SNIFF_MODE (0x0003 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_EXIT_SNIFF_MODE (0x0004 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_PARK_MODE (0x0005 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_EXIT_PARK_MODE (0x0006 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_QOS_SETUP (0x0007 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_ROLE_DISCOVERY (0x0009 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_SWITCH_ROLE (0x000B | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_READ_POLICY_SETTINGS (0x000C | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_WRITE_POLICY_SETTINGS (0x000D | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_READ_DEF_POLICY_SETTINGS (0x000E | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_WRITE_DEF_POLICY_SETTINGS (0x000F | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_FLOW_SPECIFICATION (0x0010 | HCI_GRP_LINK_POLICY_CMDS) +#define HCI_SNIFF_SUB_RATE (0x0011 | HCI_GRP_LINK_POLICY_CMDS) + +#define HCI_LINK_POLICY_CMDS_FIRST HCI_HOLD_MODE +#define HCI_LINK_POLICY_CMDS_LAST HCI_SNIFF_SUB_RATE + + +/* Commands of HCI_GRP_HOST_CONT_BASEBAND_CMDS */ +#define HCI_SET_EVENT_MASK (0x0001 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_RESET (0x0003 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_EVENT_FILTER (0x0005 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_FLUSH (0x0008 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PIN_TYPE (0x0009 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PIN_TYPE (0x000A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_CREATE_NEW_UNIT_KEY (0x000B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_STORED_LINK_KEY (0x000D | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_STORED_LINK_KEY (0x0011 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_DELETE_STORED_LINK_KEY (0x0012 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_CHANGE_LOCAL_NAME (0x0013 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_LOCAL_NAME (0x0014 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_CONN_ACCEPT_TOUT (0x0015 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_CONN_ACCEPT_TOUT (0x0016 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGE_TOUT (0x0017 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGE_TOUT (0x0018 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_SCAN_ENABLE (0x0019 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_SCAN_ENABLE (0x001A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGESCAN_CFG (0x001B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGESCAN_CFG (0x001C | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_INQUIRYSCAN_CFG (0x001D | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_INQUIRYSCAN_CFG (0x001E | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_AUTHENTICATION_ENABLE (0x001F | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_AUTHENTICATION_ENABLE (0x0020 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_ENCRYPTION_MODE (0x0021 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_ENCRYPTION_MODE (0x0022 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_CLASS_OF_DEVICE (0x0023 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_CLASS_OF_DEVICE (0x0024 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_VOICE_SETTINGS (0x0025 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_VOICE_SETTINGS (0x0026 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_AUTO_FLUSH_TOUT (0x0027 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_AUTO_FLUSH_TOUT (0x0028 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_NUM_BCAST_REXMITS (0x0029 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_NUM_BCAST_REXMITS (0x002A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_HOLD_MODE_ACTIVITY (0x002B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_HOLD_MODE_ACTIVITY (0x002C | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_TRANSMIT_POWER_LEVEL (0x002D | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_SCO_FLOW_CTRL_ENABLE (0x002E | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_SCO_FLOW_CTRL_ENABLE (0x002F | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_HC_TO_HOST_FLOW_CTRL (0x0031 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_HOST_BUFFER_SIZE (0x0033 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_HOST_NUM_PACKETS_DONE (0x0035 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_LINK_SUPER_TOUT (0x0036 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_LINK_SUPER_TOUT (0x0037 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_NUM_SUPPORTED_IAC (0x0038 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_CURRENT_IAC_LAP (0x0039 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_CURRENT_IAC_LAP (0x003A | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGESCAN_PERIOD_MODE (0x003B | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGESCAN_PERIOD_MODE (0x003C | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGESCAN_MODE (0x003D | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGESCAN_MODE (0x003E | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SET_AFH_CHANNELS (0x003F | HCI_GRP_HOST_CONT_BASEBAND_CMDS) + +#define HCI_READ_INQSCAN_TYPE (0x0042 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_INQSCAN_TYPE (0x0043 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_INQUIRY_MODE (0x0044 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_INQUIRY_MODE (0x0045 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_PAGESCAN_TYPE (0x0046 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_PAGESCAN_TYPE (0x0047 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_AFH_ASSESSMENT_MODE (0x0048 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_AFH_ASSESSMENT_MODE (0x0049 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_EXT_INQ_RESPONSE (0x0051 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_EXT_INQ_RESPONSE (0x0052 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_REFRESH_ENCRYPTION_KEY (0x0053 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_SIMPLE_PAIRING_MODE (0x0055 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_SIMPLE_PAIRING_MODE (0x0056 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_LOCAL_OOB_DATA (0x0057 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_READ_INQ_TX_POWER_LEVEL (0x0058 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_WRITE_INQ_TX_POWER_LEVEL (0x0059 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) +#define HCI_SEND_KEYPRESS_NOTIF (0x0060 | HCI_GRP_HOST_CONT_BASEBAND_CMDS) + +#define HCI_ENHANCED_FLUSH (0x005F | HCI_GRP_HOST_CONT_BASEBAND_CMDS) + +#define HCI_CONT_BASEBAND_CMDS_FIRST HCI_SET_EVENT_MASK +#define HCI_CONT_BASEBAND_CMDS_LAST HCI_SEND_KEYPRESS_NOTIF + + +/* Commands of HCI_GRP_INFORMATIONAL_PARAMS group */ +#define HCI_READ_LOCAL_VERSION_INFO (0x0001 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_LOCAL_SUPPORTED_CMDS (0x0002 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_LOCAL_FEATURES (0x0003 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_LOCAL_EXT_FEATURES (0x0004 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_BUFFER_SIZE (0x0005 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_COUNTRY_CODE (0x0007 | HCI_GRP_INFORMATIONAL_PARAMS) +#define HCI_READ_BD_ADDR (0x0009 | HCI_GRP_INFORMATIONAL_PARAMS) + +#define HCI_INFORMATIONAL_CMDS_FIRST HCI_READ_LOCAL_VERSION_INFO +#define HCI_INFORMATIONAL_CMDS_LAST HCI_READ_BD_ADDR + + +/* Commands of HCI_GRP_STATUS_PARAMS group */ +#define HCI_READ_FAILED_CONTACT_COUNT (0x0001 | HCI_GRP_STATUS_PARAMS) +#define HCI_RESET_FAILED_CONTACT_COUNT (0x0002 | HCI_GRP_STATUS_PARAMS) +#define HCI_GET_LINK_QUALITY (0x0003 | HCI_GRP_STATUS_PARAMS) +#define HCI_READ_RSSI (0x0005 | HCI_GRP_STATUS_PARAMS) +#define HCI_READ_AFH_CH_MAP (0x0006 | HCI_GRP_STATUS_PARAMS) +#define HCI_READ_CLOCK (0x0007 | HCI_GRP_STATUS_PARAMS) + +#define HCI_STATUS_PARAMS_CMDS_FIRST HCI_READ_FAILED_CONTACT_COUNT +#define HCI_STATUS_PARAMS_CMDS_LAST HCI_READ_CLOCK + +/* Commands of HCI_GRP_TESTING_CMDS group */ +#define HCI_READ_LOOPBACK_MODE (0x0001 | HCI_GRP_TESTING_CMDS) +#define HCI_WRITE_LOOPBACK_MODE (0x0002 | HCI_GRP_TESTING_CMDS) +#define HCI_ENABLE_DEV_UNDER_TEST_MODE (0x0003 | HCI_GRP_TESTING_CMDS) +#define HCI_WRITE_SIMP_PAIR_DEBUG_MODE (0x0004 | HCI_GRP_TESTING_CMDS) + +#define HCI_TESTING_CMDS_FIRST HCI_READ_LOOPBACK_MODE +#define HCI_TESTING_CMDS_LAST HCI_WRITE_SIMP_PAIR_DEBUG_MODE + +#define HCI_VENDOR_CMDS_FIRST 0x0001 +#define HCI_VENDOR_CMDS_LAST 0xFFFF + + +/* Vendor specific commands for BRCM chipset */ +#define HCI_BRCM_UPDATE_BAUD_RATE_ENCODED_LENGTH 0x02 +#define HCI_BRCM_UPDATE_BAUD_RATE_UNENCODED_LENGTH 0x06 +#define HCI_BRCM_WRITE_SLEEP_MODE_LENGTH 10 +#define HCI_BRCM_FM_OPCODE (0x0015 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_UPDATE_BAUDRATE_CMD (0x0018 | HCI_GRP_VENDOR_SPECIFIC) /* set baudrate of BCM2035 */ +#define HCI_BRCM_WRITE_SLEEP_MODE (0x0027 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_READ_SLEEP_MODE (0x0028 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_H4IBSS_CMD (0x0029 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_DOWNLOAD_MINI_DRV (0x002E | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_READ_USER_DEFINED_NVRAM (0x0033 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_ENABLE_RADIO (0x0034 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_READ_DIAGNOSTIC_VALUE (0x0035 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_GET_HID_DEVICE_LIST (0x0036 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_ADD_HID_DEVICE (0x0037 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_WRITE_HID_DEVICE_NVRAM (0x0038 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_DELETE_HID_DEVICE (0x0039 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_ENABLE_USB_HID_EMULATION (0x003B | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_BTW_STARTUP (0x0053 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_SET_ACL_PRIORITY (0x0057 | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_WRITE_RAM (0x004C | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_LAUNCH_RAM (0x004E | HCI_GRP_VENDOR_SPECIFIC) + +/* Dual Stack */ +#define HCI_BRCM_PAUSE_TRANSPORT (0x007A | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_TRANSPORT_RESUME (0x007B | HCI_GRP_VENDOR_SPECIFIC) +#define HCI_BRCM_TRANSPORT_ERROR_EVT 0x0C + +/* Parameter information for HCI_BRCM_SET_ACL_PRIORITY */ +#define HCI_BRCM_ACL_PRIORITY_PARAM_SIZE 3 +#define HCI_BRCM_ACL_PRIORITY_LOW 0x00 +#define HCI_BRCM_ACL_PRIORITY_HIGH 0xFF + +#define HCI_BRCM_UPDATE_BAUD_RATE_ENCODED_LENGTH 0x02 +#define HCI_BRCM_UPDATE_BAUD_RATE_UNENCODED_LENGTH 0x06 +#define HCI_BRCM_WRITE_SLEEP_MODE_LENGTH 10 +#define HCI_BRCM_PAUSE_TRANSPORT_LENGTH 6 + +/* Broadcom-specific SCO routing */ +#define HCI_BRCM_Write_SCO_PCM_Int_Params (0x001c | HCI_GRP_VENDOR_SPECIFIC) +#define BRCM_SCO_PCM_Int_Params_ROUTING_PCM 0 +#define BRCM_SCO_PCM_Int_Params_ROUTING_TRANSPORT 1 + +#define HCI_BRCM_SYNC_PACKET_TYPE (0x006B | HCI_GRP_VENDOR_SPECIFIC) + +#define HCI_BRCM_UIPC_OVER_HCI (0x008B | HCI_GRP_VENDOR_SPECIFIC) + +/* +** Definitions for HCI Events +*/ +#define HCI_INQUIRY_COMP_EVT 0x01 +#define HCI_INQUIRY_RESULT_EVT 0x02 +#define HCI_CONNECTION_COMP_EVT 0x03 +#define HCI_CONNECTION_REQUEST_EVT 0x04 +#define HCI_DISCONNECTION_COMP_EVT 0x05 +#define HCI_AUTHENTICATION_COMP_EVT 0x06 +#define HCI_RMT_NAME_REQUEST_COMP_EVT 0x07 +#define HCI_ENCRYPTION_CHANGE_EVT 0x08 +#define HCI_CHANGE_CONN_LINK_KEY_EVT 0x09 +#define HCI_MASTER_LINK_KEY_COMP_EVT 0x0A +#define HCI_READ_RMT_FEATURES_COMP_EVT 0x0B +#define HCI_READ_RMT_VERSION_COMP_EVT 0x0C +#define HCI_QOS_SETUP_COMP_EVT 0x0D +#define HCI_COMMAND_COMPLETE_EVT 0x0E +#define HCI_COMMAND_STATUS_EVT 0x0F +#define HCI_HARDWARE_ERROR_EVT 0x10 +#define HCI_FLUSH_OCCURED_EVT 0x11 +#define HCI_ROLE_CHANGE_EVT 0x12 +#define HCI_NUM_COMPL_DATA_PKTS_EVT 0x13 +#define HCI_MODE_CHANGE_EVT 0x14 +#define HCI_RETURN_LINK_KEYS_EVT 0x15 +#define HCI_PIN_CODE_REQUEST_EVT 0x16 +#define HCI_LINK_KEY_REQUEST_EVT 0x17 +#define HCI_LINK_KEY_NOTIFICATION_EVT 0x18 +#define HCI_LOOPBACK_COMMAND_EVT 0x19 +#define HCI_DATA_BUF_OVERFLOW_EVT 0x1A +#define HCI_MAX_SLOTS_CHANGED_EVT 0x1B +#define HCI_READ_CLOCK_OFF_COMP_EVT 0x1C +#define HCI_CONN_PKT_TYPE_CHANGE_EVT 0x1D +#define HCI_QOS_VIOLATION_EVT 0x1E +#define HCI_PAGE_SCAN_MODE_CHANGE_EVT 0x1F +#define HCI_PAGE_SCAN_REP_MODE_CHNG_EVT 0x20 +#define HCI_FLOW_SPECIFICATION_COMP_EVT 0x21 +#define HCI_INQUIRY_RSSI_RESULT_EVT 0x22 +#define HCI_READ_RMT_EXT_FEATURES_COMP_EVT 0x23 +#define HCI_ESCO_CONNECTION_COMP_EVT 0x2C +#define HCI_ESCO_CONNECTION_CHANGED_EVT 0x2D +#define HCI_SNIFF_SUB_RATE_EVT 0x2E +#define HCI_EXTENDED_INQUIRY_RESULT_EVT 0x2F +#define HCI_ENCRYPTION_KEY_REFRESH_COMP_EVT 0x30 +#define HCI_IO_CAPABILITY_REQUEST_EVT 0x31 +#define HCI_IO_CAPABILITY_RESPONSE_EVT 0x32 +#define HCI_USER_CONFIRMATION_REQUEST_EVT 0x33 +#define HCI_USER_PASSKEY_REQUEST_EVT 0x34 +#define HCI_REMOTE_OOB_DATA_REQUEST_EVT 0x35 +#define HCI_SIMPLE_PAIRING_COMPLETE_EVT 0x36 +#define HCI_LINK_SUPER_TOUT_CHANGED_EVT 0x38 +#define HCI_ENHANCED_FLUSH_COMPLETE_EVT 0x39 +#define HCI_USER_PASSKEY_NOTIFY_EVT 0x3B +#define HCI_KEYPRESS_NOTIFY_EVT 0x3C +#define HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT 0x3D + +#define HCI_EVENT_RSP_FIRST HCI_INQUIRY_COMP_EVT +#define HCI_EVENT_RSP_LAST HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT + +#define HCI_BRCM_H4IBSS_EVT 0xEF /* Vendor specific events for H4IBSS */ +#define HCI_VENDOR_SPECIFIC_EVT 0xFF /* Vendor specific events */ +#define HCI_NAP_TRACE_EVT 0xFF /* was define 0xFE, 0xFD, change to 0xFF + because conflict w/ TCI_EVT and per + specification compliant */ + +/* +** HCI Error Codes definition +*/ +#define HCI_SUCCESS 0x00 +#define HCI_PENDING 0x00 +#define HCI_ERR_ILLEGAL_COMMAND 0x01 +#define HCI_ERR_NO_CONNECTION 0x02 +#define HCI_ERR_HW_FAILURE 0x03 +#define HCI_ERR_PAGE_TIMEOUT 0x04 +#define HCI_ERR_AUTH_FAILURE 0x05 +#define HCI_ERR_KEY_MISSING 0x06 +#define HCI_ERR_MEMORY_FULL 0x07 +#define HCI_ERR_CONNECTION_TOUT 0x08 +#define HCI_ERR_MAX_NUM_OF_CONNECTIONS 0x09 +#define HCI_ERR_MAX_NUM_OF_SCOS 0x0A +#define HCI_ERR_CONNECTION_EXISTS 0x0B +#define HCI_ERR_COMMAND_DISALLOWED 0x0C +#define HCI_ERR_HOST_REJECT_RESOURCES 0x0D +#define HCI_ERR_HOST_REJECT_SECURITY 0x0E +#define HCI_ERR_HOST_REJECT_DEVICE 0x0F +#define HCI_ERR_HOST_TIMEOUT 0x10 +#define HCI_ERR_UNSUPPORTED_VALUE 0x11 +#define HCI_ERR_ILLEGAL_PARAMETER_FMT 0x12 +#define HCI_ERR_PEER_USER 0x13 +#define HCI_ERR_PEER_LOW_RESOURCES 0x14 +#define HCI_ERR_PEER_POWER_OFF 0x15 +#define HCI_ERR_CONN_CAUSE_LOCAL_HOST 0x16 +#define HCI_ERR_REPEATED_ATTEMPTS 0x17 +#define HCI_ERR_PAIRING_NOT_ALLOWED 0x18 +#define HCI_ERR_UNKNOWN_LMP_PDU 0x19 +#define HCI_ERR_UNSUPPORTED_REM_FEATURE 0x1A +#define HCI_ERR_SCO_OFFSET_REJECTED 0x1B +#define HCI_ERR_SCO_INTERVAL_REJECTED 0x1C +#define HCI_ERR_SCO_AIR_MODE 0x1D +#define HCI_ERR_INVALID_LMP_PARAM 0x1E +#define HCI_ERR_UNSPECIFIED 0x1F +#define HCI_ERR_UNSUPPORTED_LMP_FEATURE 0x20 +#define HCI_ERR_ROLE_CHANGE_NOT_ALLOWED 0x21 +#define HCI_ERR_LMP_RESPONSE_TIMEOUT 0x22 +#define HCI_ERR_LMP_ERR_TRANS_COLLISION 0x23 +#define HCI_ERR_LMP_PDU_NOT_ALLOWED 0x24 +#define HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE 0x25 +#define HCI_ERR_UNIT_KEY_USED 0x26 +#define HCI_ERR_QOS_NOT_SUPPORTED 0x27 +#define HCI_ERR_INSTANT_PASSED 0x28 +#define HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED 0x29 +#define HCI_ERR_DIFF_TRANSACTION_COLLISION 0x2A +#define HCI_ERR_UNDEFINED_0x2B 0x2B +#define HCI_ERR_QOS_UNACCEPTABLE_PARAM 0x2C +#define HCI_ERR_QOS_REJECTED 0x2D +#define HCI_ERR_CHAN_CLASSIF_NOT_SUPPORTED 0x2E +#define HCI_ERR_INSUFFCIENT_SECURITY 0x2F +#define HCI_ERR_PARAM_OUT_OF_RANGE 0x30 +#define HCI_ERR_UNDEFINED_0x31 0x31 +#define HCI_ERR_ROLE_SWITCH_PENDING 0x32 +#define HCI_ERR_UNDEFINED_0x33 0x33 +#define HCI_ERR_RESERVED_SLOT_VIOLATION 0x34 +#define HCI_ERR_ROLE_SWITCH_FAILED 0x35 +#define HCI_ERR_INQ_RSP_DATA_TOO_LARGE 0x36 +#define HCI_ERR_SIMPLE_PAIRING_NOT_SUPPORTED 0x37 +#define HCI_ERR_HOST_BUSY_PAIRING 0x38 + +#define HCI_ERR_MAX_ERR 0x38 + +/* +** Definitions for HCI enable event +*/ +#define HCI_INQUIRY_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000001) +#define HCI_INQUIRY_RESULT_EV(p) (*((UINT32 *)(p)) & 0x00000002) +#define HCI_CONNECTION_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000004) +#define HCI_CONNECTION_REQUEST_EV(p) (*((UINT32 *)(p)) & 0x00000008) +#define HCI_DISCONNECTION_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000010) +#define HCI_AUTHENTICATION_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000020) +#define HCI_RMT_NAME_REQUEST_COMPL_EV(p) (*((UINT32 *)(p)) & 0x00000040) +#define HCI_CHANGE_CONN_ENCRPT_ENABLE_EV(p) (*((UINT32 *)(p)) & 0x00000080) +#define HCI_CHANGE_CONN_LINK_KEY_EV(p) (*((UINT32 *)(p)) & 0x00000100) +#define HCI_MASTER_LINK_KEY_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00000200) +#define HCI_READ_RMT_FEATURES_COMPL_EV(p) (*((UINT32 *)(p)) & 0x00000400) +#define HCI_READ_RMT_VERSION_COMPL_EV(p) (*((UINT32 *)(p)) & 0x00000800) +#define HCI_QOS_SETUP_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00001000) +#define HCI_COMMAND_COMPLETE_EV(p) (*((UINT32 *)(p)) & 0x00002000) +#define HCI_COMMAND_STATUS_EV(p) (*((UINT32 *)(p)) & 0x00004000) +#define HCI_HARDWARE_ERROR_EV(p) (*((UINT32 *)(p)) & 0x00008000) +#define HCI_FLASH_OCCURED_EV(p) (*((UINT32 *)(p)) & 0x00010000) +#define HCI_ROLE_CHANGE_EV(p) (*((UINT32 *)(p)) & 0x00020000) +#define HCI_NUM_COMPLETED_PKTS_EV(p) (*((UINT32 *)(p)) & 0x00040000) +#define HCI_MODE_CHANGE_EV(p) (*((UINT32 *)(p)) & 0x00080000) +#define HCI_RETURN_LINK_KEYS_EV(p) (*((UINT32 *)(p)) & 0x00100000) +#define HCI_PIN_CODE_REQUEST_EV(p) (*((UINT32 *)(p)) & 0x00200000) +#define HCI_LINK_KEY_REQUEST_EV(p) (*((UINT32 *)(p)) & 0x00400000) +#define HCI_LINK_KEY_NOTIFICATION_EV(p) (*((UINT32 *)(p)) & 0x00800000) +#define HCI_LOOPBACK_COMMAND_EV(p) (*((UINT32 *)(p)) & 0x01000000) +#define HCI_DATA_BUF_OVERFLOW_EV(p) (*((UINT32 *)(p)) & 0x02000000) +#define HCI_MAX_SLOTS_CHANGE_EV(p) (*((UINT32 *)(p)) & 0x04000000) +#define HCI_READ_CLOCK_OFFSET_COMP_EV(p) (*((UINT32 *)(p)) & 0x08000000) +#define HCI_CONN_PKT_TYPE_CHANGED_EV(p) (*((UINT32 *)(p)) & 0x10000000) +#define HCI_QOS_VIOLATION_EV(p) (*((UINT32 *)(p)) & 0x20000000) +#define HCI_PAGE_SCAN_MODE_CHANGED_EV(p) (*((UINT32 *)(p)) & 0x40000000) +#define HCI_PAGE_SCAN_REP_MODE_CHNG_EV(p) (*((UINT32 *)(p)) & 0x80000000) + +/* the default event mask for 2.1+EDR (Lisbon) does not include Lisbon events */ +#define HCI_DEFAULT_EVENT_MASK_0 0xFFFFFFFF +#define HCI_DEFAULT_EVENT_MASK_1 0x00001FFF + +/* the event mask for 2.1+EDR include Lisbon events */ +#define HCI_LISBON_EVENT_MASK_0 0xFFFFFFFF +#define HCI_LISBON_EVENT_MASK_1 0x1DBFFFFF +#define HCI_LISBON_EVENT_MASK "\x0D\xBF\xFF\xFF\xFF\xFF\xFF\xFF" +#define HCI_LISBON_EVENT_MASK_EXT "\x1D\xBF\xFF\xFF\xFF\xFF\xFF\xFF" +/* 0x00001FFF FFFFFFFF Default - no Lisbon events + 0x00002000 00000000 Sniff Subrate Event + 0x00004000 00000000 Extended Inquiry Result Event + 0x00008000 00000000 Encryption Key Refresh Complete Event + 0x00010000 00000000 IO Capability Request Event + 0x00020000 00000000 IO Capability Response Event + 0x00040000 00000000 User Confirmation Request Event + 0x00080000 00000000 User Passkey Request Event + 0x00100000 00000000 Remote OOB Data Request Event + 0x00200000 00000000 Simple Pairing Complete Event + 0x00800000 00000000 Link Supervision Timeout Changed Event + 0x01000000 00000000 Enhanced Flush Complete Event + 0x04000000 00000000 User Passkey Notification Event + 0x08000000 00000000 Keypress Notification Event + 0x10000000 00000000 Remote Host Supported Features Notification Event + */ + +/* +** Definitions for packet type masks (BT1.2 and BT2.0 definitions) +*/ +#define HCI_PKT_TYPES_MASK_NO_2_DH1 0x0002 +#define HCI_PKT_TYPES_MASK_NO_3_DH1 0x0004 +#define HCI_PKT_TYPES_MASK_DM1 0x0008 +#define HCI_PKT_TYPES_MASK_DH1 0x0010 +#define HCI_PKT_TYPES_MASK_HV1 0x0020 +#define HCI_PKT_TYPES_MASK_HV2 0x0040 +#define HCI_PKT_TYPES_MASK_HV3 0x0080 +#define HCI_PKT_TYPES_MASK_NO_2_DH3 0x0100 +#define HCI_PKT_TYPES_MASK_NO_3_DH3 0x0200 +#define HCI_PKT_TYPES_MASK_DM3 0x0400 +#define HCI_PKT_TYPES_MASK_DH3 0x0800 +#define HCI_PKT_TYPES_MASK_NO_2_DH5 0x1000 +#define HCI_PKT_TYPES_MASK_NO_3_DH5 0x2000 +#define HCI_PKT_TYPES_MASK_DM5 0x4000 +#define HCI_PKT_TYPES_MASK_DH5 0x8000 + +/* Packet type should be one of valid but at least one should be specified */ +#define HCI_VALID_SCO_PKT_TYPE(t) (((((t) & ~(HCI_PKT_TYPES_MASK_HV1 \ + | HCI_PKT_TYPES_MASK_HV2 \ + | HCI_PKT_TYPES_MASK_HV3)) == 0)) \ + && ((t) != 0)) + + + + + +/* Packet type should not be invalid and at least one should be specified */ +#define HCI_VALID_ACL_PKT_TYPE(t) (((((t) & ~(HCI_PKT_TYPES_MASK_DM1 \ + | HCI_PKT_TYPES_MASK_DH1 \ + | HCI_PKT_TYPES_MASK_DM3 \ + | HCI_PKT_TYPES_MASK_DH3 \ + | HCI_PKT_TYPES_MASK_DM5 \ + | HCI_PKT_TYPES_MASK_DH5 \ + | HCI_PKT_TYPES_MASK_NO_2_DH1 \ + | HCI_PKT_TYPES_MASK_NO_3_DH1 \ + | HCI_PKT_TYPES_MASK_NO_2_DH3 \ + | HCI_PKT_TYPES_MASK_NO_3_DH3 \ + | HCI_PKT_TYPES_MASK_NO_2_DH5 \ + | HCI_PKT_TYPES_MASK_NO_3_DH5 )) == 0)) \ + && (((t) & (HCI_PKT_TYPES_MASK_DM1 \ + | HCI_PKT_TYPES_MASK_DH1 \ + | HCI_PKT_TYPES_MASK_DM3 \ + | HCI_PKT_TYPES_MASK_DH3 \ + | HCI_PKT_TYPES_MASK_DM5 \ + | HCI_PKT_TYPES_MASK_DH5)) != 0)) + + + +/* +** Definitions for eSCO packet type masks (BT1.2 and BT2.0 definitions) +*/ +#define HCI_ESCO_PKT_TYPES_MASK_HV1 0x0001 +#define HCI_ESCO_PKT_TYPES_MASK_HV2 0x0002 +#define HCI_ESCO_PKT_TYPES_MASK_HV3 0x0004 +#define HCI_ESCO_PKT_TYPES_MASK_EV3 0x0008 +#define HCI_ESCO_PKT_TYPES_MASK_EV4 0x0010 +#define HCI_ESCO_PKT_TYPES_MASK_EV5 0x0020 +#define HCI_ESCO_PKT_TYPES_MASK_NO_2_EV3 0x0040 +#define HCI_ESCO_PKT_TYPES_MASK_NO_3_EV3 0x0080 +#define HCI_ESCO_PKT_TYPES_MASK_NO_2_EV5 0x0100 +#define HCI_ESCO_PKT_TYPES_MASK_NO_3_EV5 0x0200 + +/* Packet type should be one of valid but at least one should be specified for 1.2 */ +#define HCI_VALID_ESCO_PKT_TYPE(t) (((((t) & ~(HCI_ESCO_PKT_TYPES_MASK_EV3 \ + | HCI_ESCO_PKT_TYPES_MASK_EV4 \ + | HCI_ESCO_PKT_TYPES_MASK_EV5)) == 0)) \ + && ((t) != 0))/* Packet type should be one of valid but at least one should be specified */ + +#define HCI_VALID_ESCO_SCOPKT_TYPE(t) (((((t) & ~(HCI_ESCO_PKT_TYPES_MASK_HV1 \ + | HCI_ESCO_PKT_TYPES_MASK_HV2 \ + | HCI_ESCO_PKT_TYPES_MASK_HV3)) == 0)) \ + && ((t) != 0)) + +#define HCI_VALID_SCO_ALL_PKT_TYPE(t) (((((t) & ~(HCI_ESCO_PKT_TYPES_MASK_HV1 \ + | HCI_ESCO_PKT_TYPES_MASK_HV2 \ + | HCI_ESCO_PKT_TYPES_MASK_HV3 \ + | HCI_ESCO_PKT_TYPES_MASK_EV3 \ + | HCI_ESCO_PKT_TYPES_MASK_EV4 \ + | HCI_ESCO_PKT_TYPES_MASK_EV5)) == 0)) \ + && ((t) != 0)) + +/* +** Define parameters to allow role switch during create connection +*/ +#define HCI_CR_CONN_NOT_ALLOW_SWITCH 0x00 +#define HCI_CR_CONN_ALLOW_SWITCH 0x01 + +/* +** Hold Mode command destination +*/ +#define HOLD_MODE_DEST_LOCAL_DEVICE 0x00 +#define HOLD_MODE_DEST_RMT_DEVICE 0x01 + +/* +** Definitions of different HCI parameters +*/ +#define HCI_PER_INQ_MIN_MAX_PERIOD 0x0003 +#define HCI_PER_INQ_MAX_MAX_PERIOD 0xFFFF +#define HCI_PER_INQ_MIN_MIN_PERIOD 0x0002 +#define HCI_PER_INQ_MAX_MIN_PERIOD 0xFFFE + +#define HCI_MAX_INQUIRY_LENGTH 0x30 + +#define HCI_MIN_INQ_LAP 0x9E8B00 +#define HCI_MAX_INQ_LAP 0x9E8B3F + +/* HCI role definition */ +#define HCI_ROLE_MASTER 0x00 +#define HCI_ROLE_SLAVE 0x01 +#define HCI_ROLE_UNKNOWN 0xff + +/* HCI mode definition */ +#define HCI_MODE_ACTIVE 0x00 +#define HCI_MODE_HOLD 0x01 +#define HCI_MODE_SNIFF 0x02 +#define HCI_MODE_PARK 0x03 + +/* Define Packet types as requested by the Host */ +#define HCI_ACL_PKT_TYPE_NONE 0x0000 +#define HCI_ACL_PKT_TYPE_DM1 0x0008 +#define HCI_ACL_PKT_TYPE_DH1 0x0010 +#define HCI_ACL_PKT_TYPE_AUX1 0x0200 +#define HCI_ACL_PKT_TYPE_DM3 0x0400 +#define HCI_ACL_PKT_TYPE_DH3 0x0800 +#define HCI_ACL_PKT_TYPE_DM5 0x4000 +#define HCI_ACL_PKT_TYPE_DH5 0x8000 + +/* Define key type in the Master Link Key command */ +#define HCI_USE_SEMI_PERMANENT_KEY 0x00 +#define HCI_USE_TEMPORARY_KEY 0x01 + +/* Page scan period modes */ +#define HCI_PAGE_SCAN_REP_MODE_R0 0x00 +#define HCI_PAGE_SCAN_REP_MODE_R1 0x01 +#define HCI_PAGE_SCAN_REP_MODE_R2 0x02 + +/* Define limits for page scan repetition modes */ +#define HCI_PAGE_SCAN_R1_LIMIT 0x0800 +#define HCI_PAGE_SCAN_R2_LIMIT 0x1000 + +/* Page scan period modes */ +#define HCI_PAGE_SCAN_PER_MODE_P0 0x00 +#define HCI_PAGE_SCAN_PER_MODE_P1 0x01 +#define HCI_PAGE_SCAN_PER_MODE_P2 0x02 + +/* Page scan modes */ +#define HCI_MANDATARY_PAGE_SCAN_MODE 0x00 +#define HCI_OPTIONAL_PAGE_SCAN_MODE1 0x01 +#define HCI_OPTIONAL_PAGE_SCAN_MODE2 0x02 +#define HCI_OPTIONAL_PAGE_SCAN_MODE3 0x03 + +/* Page and inquiry scan types */ +#define HCI_SCAN_TYPE_STANDARD 0x00 +#define HCI_SCAN_TYPE_INTERLACED 0x01 /* 1.2 devices or later */ +#define HCI_DEF_SCAN_TYPE HCI_SCAN_TYPE_STANDARD + +/* Definitions for quality of service service types */ +#define HCI_SERVICE_NO_TRAFFIC 0x00 +#define HCI_SERVICE_BEST_EFFORT 0x01 +#define HCI_SERVICE_GUARANTEED 0x02 + +#define HCI_QOS_LATENCY_DO_NOT_CARE 0xFFFFFFFF +#define HCI_QOS_DELAY_DO_NOT_CARE 0xFFFFFFFF + +/* Definitions for Flow Specification */ +#define HCI_FLOW_SPEC_LATENCY_DO_NOT_CARE 0xFFFFFFFF + +/* Definitions for AFH Channel Map */ +#define HCI_AFH_CHANNEL_MAP_LEN 10 + +/* Definitions for Extended Inquiry Response */ +#define HCI_EXT_INQ_RESPONSE_LEN 240 +#define HCI_EIR_FLAGS_TYPE 0x01 +#define HCI_EIR_MORE_16BITS_UUID_TYPE 0x02 +#define HCI_EIR_COMPLETE_16BITS_UUID_TYPE 0x03 +#define HCI_EIR_MORE_32BITS_UUID_TYPE 0x04 +#define HCI_EIR_COMPLETE_32BITS_UUID_TYPE 0x05 +#define HCI_EIR_MORE_128BITS_UUID_TYPE 0x06 +#define HCI_EIR_COMPLETE_128BITS_UUID_TYPE 0x07 +#define HCI_EIR_SHORTENED_LOCAL_NAME_TYPE 0x08 +#define HCI_EIR_COMPLETE_LOCAL_NAME_TYPE 0x09 +#define HCI_EIR_TX_POWER_LEVEL_TYPE 0x0A +#define HCI_EIR_MANUFACTURER_SPECIFIC_TYPE 0xFF + +/* Definitions for Write Simple Pairing Mode */ +#define HCI_SP_MODE_UNDEFINED 0x00 +#define HCI_SP_MODE_ENABLED 0x01 + +/* Definitions for Write Simple Pairing Debug Mode */ +#define HCI_SPD_MODE_DISABLED 0x00 +#define HCI_SPD_MODE_ENABLED 0x01 + +/* Definitions for IO Capability Response/Command */ +#define HCI_IO_CAP_DISPLAY_ONLY 0x00 +#define HCI_IO_CAP_DISPLAY_YESNO 0x01 +#define HCI_IO_CAP_KEYBOARD_ONLY 0x02 +#define HCI_IO_CAP_NO_IO 0x03 + +#define HCI_OOB_AUTH_DATA_NOT_PRESENT 0x00 +#define HCI_OOB_REM_AUTH_DATA_PRESENT 0x01 + +#define HCI_MITM_PROTECT_NOT_REQUIRED 0x00 +#define HCI_MITM_PROTECT_REQUIRED 0x01 + + +/* Policy settings status */ +#define HCI_DISABLE_ALL_LM_MODES 0x0000 +#define HCI_ENABLE_MASTER_SLAVE_SWITCH 0x0001 +#define HCI_ENABLE_HOLD_MODE 0x0002 +#define HCI_ENABLE_SNIFF_MODE 0x0004 +#define HCI_ENABLE_PARK_MODE 0x0008 + +/* By default allow switch, because host can not allow that */ +/* that until he created the connection */ +#define HCI_DEFAULT_POLICY_SETTINGS HCI_DISABLE_ALL_LM_MODES + +/* Filters that are sent in set filter command */ +#define HCI_FILTER_TYPE_CLEAR_ALL 0x00 +#define HCI_FILTER_INQUIRY_RESULT 0x01 +#define HCI_FILTER_CONNECTION_SETUP 0x02 + +#define HCI_FILTER_COND_NEW_DEVICE 0x00 +#define HCI_FILTER_COND_DEVICE_CLASS 0x01 +#define HCI_FILTER_COND_BD_ADDR 0x02 + +#define HCI_DO_NOT_AUTO_ACCEPT_CONNECT 1 +#define HCI_DO_AUTO_ACCEPT_CONNECT 2 /* role switch disabled */ +#define HCI_DO_AUTO_ACCEPT_CONNECT_RS 3 /* role switch enabled (1.1 errata 1115) */ + +/* Auto accept flags */ +#define HCI_AUTO_ACCEPT_OFF 0x00 +#define HCI_AUTO_ACCEPT_ACL_CONNECTIONS 0x01 +#define HCI_AUTO_ACCEPT_SCO_CONNECTIONS 0x02 + +/* PIN type */ +#define HCI_PIN_TYPE_VARIABLE 0 +#define HCI_PIN_TYPE_FIXED 1 + +/* Loopback Modes */ +#define HCI_LOOPBACK_MODE_DISABLED 0 +#define HCI_LOOPBACK_MODE_LOCAL 1 +#define HCI_LOOPBACK_MODE_REMOTE 2 + +#define SLOTS_PER_10MS 16 /* 0.625 ms slots in a 10 ms tick */ + +/* Maximum connection accept timeout in 0.625msec */ +#define HCI_MAX_CONN_ACCEPT_TOUT 0xB540 /* 29 sec */ +#define HCI_DEF_CONN_ACCEPT_TOUT 0x1FA0 /* 5 sec */ + +/* Page timeout is used in LC only and LC is counting down slots not using OS */ +#define HCI_DEFAULT_PAGE_TOUT 0x2000 /* 5.12 sec (in slots) */ + +/* Scan enable flags */ +#define HCI_NO_SCAN_ENABLED 0x00 +#define HCI_INQUIRY_SCAN_ENABLED 0x01 +#define HCI_PAGE_SCAN_ENABLED 0x02 + +/* Pagescan timer definitions in 0.625 ms */ +#define HCI_MIN_PAGESCAN_INTERVAL 0x12 /* 11.25 ms */ +#define HCI_MAX_PAGESCAN_INTERVAL 0x1000 /* 2.56 sec */ +#define HCI_DEF_PAGESCAN_INTERVAL 0x0800 /* 1.28 sec */ + +/* Parameter for pagescan window is passed to LC and is kept in slots */ +#define HCI_MIN_PAGESCAN_WINDOW 0x12 /* 11.25 ms */ +#define HCI_MAX_PAGESCAN_WINDOW 0x1000 /* 2.56 sec */ +#define HCI_DEF_PAGESCAN_WINDOW 0x12 /* 11.25 ms */ + +/* Inquiryscan timer definitions in 0.625 ms */ +#define HCI_MIN_INQUIRYSCAN_INTERVAL 0x12 /* 11.25 ms */ +#define HCI_MAX_INQUIRYSCAN_INTERVAL 0x1000 /* 2.56 sec */ +#define HCI_DEF_INQUIRYSCAN_INTERVAL 0x0800 /* 1.28 sec */ + +/* Parameter for inquiryscan window is passed to LC and is kept in slots */ +#define HCI_MIN_INQUIRYSCAN_WINDOW 0x12 /* 11.25 ms */ +#define HCI_MAX_INQUIRYSCAN_WINDOW 0x1000 /* 2.56 sec */ +#define HCI_DEF_INQUIRYSCAN_WINDOW 0x12 /* 11.25 ms */ + +/* Encryption modes */ +#define HCI_ENCRYPT_MODE_DISABLED 0x00 +#define HCI_ENCRYPT_MODE_POINT_TO_POINT 0x01 +#define HCI_ENCRYPT_MODE_ALL 0x02 + +/* Voice settings */ +#define HCI_INP_CODING_LINEAR 0x0000 /* 0000000000 */ +#define HCI_INP_CODING_U_LAW 0x0100 /* 0100000000 */ +#define HCI_INP_CODING_A_LAW 0x0200 /* 1000000000 */ +#define HCI_INP_CODING_MASK 0x0300 /* 1100000000 */ + +#define HCI_INP_DATA_FMT_1S_COMPLEMENT 0x0000 /* 0000000000 */ +#define HCI_INP_DATA_FMT_2S_COMPLEMENT 0x0040 /* 0001000000 */ +#define HCI_INP_DATA_FMT_SIGN_MAGNITUDE 0x0080 /* 0010000000 */ +#define HCI_INP_DATA_FMT_UNSIGNED 0x00c0 /* 0011000000 */ +#define HCI_INP_DATA_FMT_MASK 0x00c0 /* 0011000000 */ + +#define HCI_INP_SAMPLE_SIZE_8BIT 0x0000 /* 0000000000 */ +#define HCI_INP_SAMPLE_SIZE_16BIT 0x0020 /* 0000100000 */ +#define HCI_INP_SAMPLE_SIZE_MASK 0x0020 /* 0000100000 */ + +#define HCI_INP_LINEAR_PCM_BIT_POS_MASK 0x001c /* 0000011100 */ +#define HCI_INP_LINEAR_PCM_BIT_POS_OFFS 2 + +#define HCI_AIR_CODING_FORMAT_CVSD 0x0000 /* 0000000000 */ +#define HCI_AIR_CODING_FORMAT_U_LAW 0x0001 /* 0000000001 */ +#define HCI_AIR_CODING_FORMAT_A_LAW 0x0002 /* 0000000010 */ +#define HCI_AIR_CODING_FORMAT_TRANSPNT 0x0003 /* 0000000011 */ +#define HCI_AIR_CODING_FORMAT_MASK 0x0003 /* 0000000011 */ + +/* default 0001100000 */ +#define HCI_DEFAULT_VOICE_SETTINGS (HCI_INP_CODING_LINEAR \ + | HCI_INP_DATA_FMT_2S_COMPLEMENT \ + | HCI_INP_SAMPLE_SIZE_16BIT \ + | HCI_AIR_CODING_FORMAT_CVSD) + +#define HCI_CVSD_SUPPORTED(x) (((x) & HCI_AIR_CODING_FORMAT_MASK) == HCI_AIR_CODING_FORMAT_CVSD) +#define HCI_U_LAW_SUPPORTED(x) (((x) & HCI_AIR_CODING_FORMAT_MASK) == HCI_AIR_CODING_FORMAT_U_LAW) +#define HCI_A_LAW_SUPPORTED(x) (((x) & HCI_AIR_CODING_FORMAT_MASK) == HCI_AIR_CODING_FORMAT_A_LAW) +#define HCI_TRANSPNT_SUPPORTED(x) (((x) & HCI_AIR_CODING_FORMAT_MASK) == HCI_AIR_CODING_FORMAT_TRANSPNT) + +/* Retransmit timer definitions in 0.625 */ +#define HCI_MAX_AUTO_FLUSH_TOUT 0x07FF +#define HCI_DEFAULT_AUTO_FLUSH_TOUT 0 /* No auto flush */ + +/* Broadcast retransmitions */ +#define HCI_DEFAULT_NUM_BCAST_RETRAN 1 + +/* Define broadcast data types as passed in the hci data packet */ +#define HCI_DATA_POINT_TO_POINT 0x00 +#define HCI_DATA_ACTIVE_BCAST 0x01 +#define HCI_DATA_PICONET_BCAST 0x02 + +/* Hold mode activity */ +#define HCI_MAINTAIN_CUR_POWER_STATE 0x00 +#define HCI_SUSPEND_PAGE_SCAN 0x01 +#define HCI_SUSPEND_INQUIRY_SCAN 0x02 +#define HCI_SUSPEND_PERIODIC_INQUIRIES 0x04 + +/* Default Link Supervision timeoout */ +#define HCI_DEFAULT_INACT_TOUT 0x7D00 /* 20 seconds */ + +/* Read transmit power level parameter */ +#define HCI_READ_CURRENT 0x00 +#define HCI_READ_MAXIMUM 0x01 + +/* Link types for connection complete event */ +#define HCI_LINK_TYPE_SCO 0x00 +#define HCI_LINK_TYPE_ACL 0x01 +#define HCI_LINK_TYPE_ESCO 0x02 + +/* Link Key Notification Event (Key Type) definitions */ +#define HCI_LKEY_TYPE_COMBINATION 0x00 +#define HCI_LKEY_TYPE_LOCAL_UNIT 0x01 +#define HCI_LKEY_TYPE_REMOTE_UNIT 0x02 +#define HCI_LKEY_TYPE_DEBUG_COMB 0x03 +#define HCI_LKEY_TYPE_UNAUTH_COMB 0x04 +#define HCI_LKEY_TYPE_AUTH_COMB 0x05 +#define HCI_LKEY_TYPE_CHANGED_COMB 0x06 + +/* Read Local Version HCI Version return values (Command Complete Event) */ +#define HCI_VERSION_1_0B 0x00 +#define HCI_VERSION_1_1 0x01 + +/* Define an invalid value for a handle */ +#define HCI_INVALID_HANDLE 0xFFFF + +/* Define max amount of data in the HCI command */ +#define HCI_COMMAND_SIZE 255 + +/* Define the preamble length for all HCI Commands. +** This is 2-bytes for opcode and 1 byte for length +*/ +#define HCIC_PREAMBLE_SIZE 3 + +/* Define the preamble length for all HCI Events +** This is 1-byte for opcode and 1 byte for length +*/ +#define HCIE_PREAMBLE_SIZE 2 +#define HCI_SCO_PREAMBLE_SIZE 3 +#define HCI_DATA_PREAMBLE_SIZE 4 + + +/* HCI message type definitions (for H4 messages) */ +#define HCIT_TYPE_COMMAND 1 +#define HCIT_TYPE_ACL_DATA 2 +#define HCIT_TYPE_SCO_DATA 3 +#define HCIT_TYPE_EVENT 4 +#define HCIT_TYPE_LM_DIAG 7 + +#define HCIT_LM_DIAG_LENGTH 63 + +/* Define values for LMP Test Control parameters +** Test Scenario, Hopping Mode, Power Control Mode +*/ +#define LMP_TESTCTL_TESTSC_PAUSE 0 +#define LMP_TESTCTL_TESTSC_TXTEST_0 1 +#define LMP_TESTCTL_TESTSC_TXTEST_1 2 +#define LMP_TESTCTL_TESTSC_TXTEST_1010 3 +#define LMP_TESTCTL_TESTSC_PSRND_BITSEQ 4 +#define LMP_TESTCTL_TESTSC_CLOSEDLB_ACL 5 +#define LMP_TESTCTL_TESTSC_CLOSEDLB_SCO 6 +#define LMP_TESTCTL_TESTSC_ACL_NOWHIT 7 +#define LMP_TESTCTL_TESTSC_SCO_NOWHIT 8 +#define LMP_TESTCTL_TESTSC_TXTEST_11110000 9 +#define LMP_TESTCTL_TESTSC_EXITTESTMODE 255 + +#define LMP_TESTCTL_HOPMOD_RXTX1FREQ 0 +#define LMP_TESTCTL_HOPMOD_HOP_EURUSA 1 +#define LMP_TESTCTL_HOPMOD_HOP_JAPAN 2 +#define LMP_TESTCTL_HOPMOD_HOP_FRANCE 3 +#define LMP_TESTCTL_HOPMOD_HOP_SPAIN 4 +#define LMP_TESTCTL_HOPMOD_REDUCED_HOP 5 + +#define LMP_TESTCTL_POWCTL_FIXEDTX_OP 0 +#define LMP_TESTCTL_POWCTL_ADAPTIVE 1 + + +/* +** Define company IDs (from Bluetooth Assigned Numbers v1.1, section 2.2) +*/ +#define LMP_COMPID_ERICSSON 0 +#define LMP_COMPID_NOKIA 1 +#define LMP_COMPID_INTEL 2 +#define LMP_COMPID_IBM 3 +#define LMP_COMPID_TOSHIBA 4 +#define LMP_COMPID_3COM 5 +#define LMP_COMPID_MICROSOFT 6 +#define LMP_COMPID_LUCENT 7 +#define LMP_COMPID_MOTOROLA 8 +#define LMP_COMPID_INFINEON 9 +#define LMP_COMPID_CSR 10 +#define LMP_COMPID_SILICON_WAVE 11 +#define LMP_COMPID_DIGIANSWER 12 +#define LMP_COMPID_TEXAS_INSTRUMENTS 13 +#define LMP_COMPID_PARTHUS 14 +#define LMP_COMPID_BROADCOM 15 +#define LMP_COMPID_MITEL_SEMI 16 +#define LMP_COMPID_WIDCOMM 17 +#define LMP_COMPID_ZEEVO 18 +#define LMP_COMPID_ATMEL 19 +#define LMP_COMPID_MITSUBISHI 20 +#define LMP_COMPID_RTX_TELECOM 21 +#define LMP_COMPID_KC_TECH 22 +#define LMP_COMPID_NEWLOGIC 23 +#define LMP_COMPID_TRANSILICA 24 +#define LMP_COMPID_ROHDE_SCHWARZ 25 +#define LMP_COMPID_TTPCOM 26 +#define LMP_COMPID_SIGNIA 27 +#define LMP_COMPID_CONEXANT 28 +#define LMP_COMPID_QUALCOMM 29 +#define LMP_COMPID_INVENTEL 30 +#define LMP_COMPID_AVM 31 +#define LMP_COMPID_BANDSPEED 32 +#define LMP_COMPID_MANSELLA 33 +#define LMP_COMPID_NEC_CORP 34 +#define LMP_COMPID_WAVEPLUS 35 +#define LMP_COMPID_ALCATEL 36 +#define LMP_COMPID_PHILIPS 37 +#define LMP_COMPID_C_TECHNOLOGIES 38 +#define LMP_COMPID_OPEN_INTERFACE 39 +#define LMP_COMPID_RF_MICRO 40 +#define LMP_COMPID_HITACHI 41 +#define LMP_COMPID_SYMBOL_TECH 42 +#define LMP_COMPID_TENOVIS 43 +#define LMP_COMPID_MACRONIX 44 +#define LMP_COMPID_GCT_SEMI 45 +#define LMP_COMPID_NORWOOD_SYSTEMS 46 +#define LMP_COMPID_MEWTEL_TECH 47 +#define LMP_COMPID_STM 48 +#define LMP_COMPID_SYNOPSYS 49 +#define LMP_COMPID_RED_M_LTD 50 +#define LMP_COMPID_COMMIL_LTD 51 +#define LMP_COMPID_CATC 52 +#define LMP_COMPID_ECLIPSE 53 +#define LMP_COMPID_RENESAS_TECH 54 +#define LMP_COMPID_MOBILIAN_CORP 55 +#define LMP_COMPID_TERAX 56 +#define LMP_COMPID_ISSC 57 +#define LMP_COMPID_MATSUSHITA 58 +#define LMP_COMPID_GENNUM_CORP 59 +#define LMP_COMPID_RESEARCH_IN_MOTION 60 +#define LMP_COMPID_IPEXTREME 61 +#define LMP_COMPID_SYSTEMS_AND_CHIPS 62 +#define LMP_COMPID_BLUETOOTH_SIG 63 +#define LMP_COMPID_SEIKO_EPSON_CORP 64 +#define LMP_COMPID_ISS_TAIWAN 65 +#define LMP_COMPID_CONWISE_TECHNOLOGIES 66 +#define LMP_COMPID_PARROT_SA 67 +#define LMP_COMPID_SOCKET_COMM 68 +#define LMP_COMPID_MAX_ID 69 /* this is a place holder */ +#define LMP_COMPID_INTERNAL 65535 + +#define MAX_LMP_COMPID (LMP_COMPID_MAX_ID) +/* +** Define the packet types in the packet header, and a couple extra +*/ +#define PKT_TYPE_NULL 0x00 +#define PKT_TYPE_POLL 0x01 +#define PKT_TYPE_FHS 0x02 +#define PKT_TYPE_DM1 0x03 + +#define PKT_TYPE_DH1 0x04 +#define PKT_TYPE_HV1 0x05 +#define PKT_TYPE_HV2 0x06 +#define PKT_TYPE_HV3 0x07 +#define PKT_TYPE_DV 0x08 +#define PKT_TYPE_AUX1 0x09 + +#define PKT_TYPE_DM3 0x0a +#define PKT_TYPE_DH3 0x0b + +#define PKT_TYPE_DM5 0x0e +#define PKT_TYPE_DH5 0x0f + + +#define PKT_TYPE_ID 0x10 /* Internally used packet types */ +#define PKT_TYPE_BAD 0x11 +#define PKT_TYPE_NONE 0x12 + +/* +** Define packet size +*/ +#define HCI_DM1_PACKET_SIZE 17 +#define HCI_DH1_PACKET_SIZE 27 +#define HCI_DM3_PACKET_SIZE 121 +#define HCI_DH3_PACKET_SIZE 183 +#define HCI_DM5_PACKET_SIZE 224 +#define HCI_DH5_PACKET_SIZE 339 +#define HCI_AUX1_PACKET_SIZE 29 +#define HCI_HV1_PACKET_SIZE 10 +#define HCI_HV2_PACKET_SIZE 20 +#define HCI_HV3_PACKET_SIZE 30 +#define HCI_DV_PACKET_SIZE 9 +#define HCI_EDR2_DH1_PACKET_SIZE 54 +#define HCI_EDR2_DH3_PACKET_SIZE 367 +#define HCI_EDR2_DH5_PACKET_SIZE 679 +#define HCI_EDR3_DH1_PACKET_SIZE 83 +#define HCI_EDR3_DH3_PACKET_SIZE 552 +#define HCI_EDR3_DH5_PACKET_SIZE 1021 + +/* +** Features encoding - page 0 +*/ +#define HCI_NUM_FEATURE_BYTES 8 +#define HCI_FEATURES_KNOWN(x) ((x[0] | x[1] | x[2] | x[3] | x[4] | x[5] | x[6] | x[7]) != 0) + +#define HCI_FEATURE_3_SLOT_PACKETS_MASK 0x01 +#define HCI_FEATURE_3_SLOT_PACKETS_OFF 0 +#define HCI_3_SLOT_PACKETS_SUPPORTED(x) ((x)[HCI_FEATURE_3_SLOT_PACKETS_OFF] & HCI_FEATURE_3_SLOT_PACKETS_MASK) + +#define HCI_FEATURE_5_SLOT_PACKETS_MASK 0x02 +#define HCI_FEATURE_5_SLOT_PACKETS_OFF 0 +#define HCI_5_SLOT_PACKETS_SUPPORTED(x) ((x)[HCI_FEATURE_5_SLOT_PACKETS_OFF] & HCI_FEATURE_5_SLOT_PACKETS_MASK) + +#define HCI_FEATURE_ENCRYPTION_MASK 0x04 +#define HCI_FEATURE_ENCRYPTION_OFF 0 +#define HCI_ENCRYPTION_SUPPORTED(x) ((x)[HCI_FEATURE_ENCRYPTION_OFF] & HCI_FEATURE_ENCRYPTION_MASK) + +#define HCI_FEATURE_SLOT_OFFSET_MASK 0x08 +#define HCI_FEATURE_SLOT_OFFSET_OFF 0 +#define HCI_SLOT_OFFSET_SUPPORTED(x) ((x)[HCI_FEATURE_SLOT_OFFSET_OFF] & HCI_FEATURE_SLOT_OFFSET_MASK) + +#define HCI_FEATURE_TIMING_ACC_MASK 0x10 +#define HCI_FEATURE_TIMING_ACC_OFF 0 +#define HCI_TIMING_ACC_SUPPORTED(x) ((x)[HCI_FEATURE_TIMING_ACC_OFF] & HCI_FEATURE_TIMING_ACC_MASK) + +#define HCI_FEATURE_SWITCH_MASK 0x20 +#define HCI_FEATURE_SWITCH_OFF 0 +#define HCI_SWITCH_SUPPORTED(x) ((x)[HCI_FEATURE_SWITCH_OFF] & HCI_FEATURE_SWITCH_MASK) + +#define HCI_FEATURE_HOLD_MODE_MASK 0x40 +#define HCI_FEATURE_HOLD_MODE_OFF 0 +#define HCI_HOLD_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_HOLD_MODE_OFF] & HCI_FEATURE_HOLD_MODE_MASK) + +#define HCI_FEATURE_SNIFF_MODE_MASK 0x80 +#define HCI_FEATURE_SNIFF_MODE_OFF 0 +#define HCI_SNIFF_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_SNIFF_MODE_OFF] & HCI_FEATURE_SNIFF_MODE_MASK) + +#define HCI_FEATURE_PARK_MODE_MASK 0x01 +#define HCI_FEATURE_PARK_MODE_OFF 1 +#define HCI_PARK_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_PARK_MODE_OFF] & HCI_FEATURE_PARK_MODE_MASK) + +#define HCI_FEATURE_RSSI_MASK 0x02 +#define HCI_FEATURE_RSSI_OFF 1 +#define HCI_RSSI_SUPPORTED(x) ((x)[HCI_FEATURE_RSSI_OFF] & HCI_FEATURE_RSSI_MASK) + +#define HCI_FEATURE_CQM_DATA_RATE_MASK 0x04 +#define HCI_FEATURE_CQM_DATA_RATE_OFF 1 +#define HCI_CQM_DATA_RATE_SUPPORTED(x) ((x)[HCI_FEATURE_CQM_DATA_RATE_OFF] & HCI_FEATURE_CQM_DATA_RATE_MASK) + +#define HCI_FEATURE_SCO_LINK_MASK 0x08 +#define HCI_FEATURE_SCO_LINK_OFF 1 +#define HCI_SCO_LINK_SUPPORTED(x) ((x)[HCI_FEATURE_SCO_LINK_OFF] & HCI_FEATURE_SCO_LINK_MASK) + +#define HCI_FEATURE_HV2_PACKETS_MASK 0x10 +#define HCI_FEATURE_HV2_PACKETS_OFF 1 +#define HCI_HV2_PACKETS_SUPPORTED(x) ((x)[HCI_FEATURE_HV2_PACKETS_OFF] & HCI_FEATURE_HV2_PACKETS_MASK) + +#define HCI_FEATURE_HV3_PACKETS_MASK 0x20 +#define HCI_FEATURE_HV3_PACKETS_OFF 1 +#define HCI_HV3_PACKETS_SUPPORTED(x) ((x)[HCI_FEATURE_HV3_PACKETS_OFF] & HCI_FEATURE_HV3_PACKETS_MASK) + +#define HCI_FEATURE_U_LAW_MASK 0x40 +#define HCI_FEATURE_U_LAW_OFF 1 +#define HCI_LMP_U_LAW_SUPPORTED(x) ((x)[HCI_FEATURE_U_LAW_OFF] & HCI_FEATURE_U_LAW_MASK) + +#define HCI_FEATURE_A_LAW_MASK 0x80 +#define HCI_FEATURE_A_LAW_OFF 1 +#define HCI_LMP_A_LAW_SUPPORTED(x) ((x)[HCI_FEATURE_A_LAW_OFF] & HCI_FEATURE_A_LAW_MASK) + +#define HCI_FEATURE_CVSD_MASK 0x01 +#define HCI_FEATURE_CVSD_OFF 2 +#define HCI_LMP_CVSD_SUPPORTED(x) ((x)[HCI_FEATURE_CVSD_OFF] & HCI_FEATURE_CVSD_MASK) + +#define HCI_FEATURE_PAGING_SCHEME_MASK 0x02 +#define HCI_FEATURE_PAGING_SCHEME_OFF 2 +#define HCI_PAGING_SCHEME_SUPPORTED(x) ((x)[HCI_FEATURE_PAGING_SCHEME_OFF] & HCI_FEATURE_PAGING_SCHEME_MASK) + +#define HCI_FEATURE_POWER_CTRL_MASK 0x04 +#define HCI_FEATURE_POWER_CTRL_OFF 2 +#define HCI_POWER_CTRL_SUPPORTED(x) ((x)[HCI_FEATURE_POWER_CTRL_OFF] & HCI_FEATURE_POWER_CTRL_MASK) + +#define HCI_FEATURE_TRANSPNT_MASK 0x08 +#define HCI_FEATURE_TRANSPNT_OFF 2 +#define HCI_LMP_TRANSPNT_SUPPORTED(x) ((x)[HCI_FEATURE_TRANSPNT_OFF] & HCI_FEATURE_TRANSPNT_MASK) + +#define HCI_FEATURE_FLOW_CTRL_LAG_MASK 0x70 +#define HCI_FEATURE_FLOW_CTRL_LAG_OFF 2 +#define HCI_FLOW_CTRL_LAG_VALUE(x) (((x)[HCI_FEATURE_FLOW_CTRL_LAG_OFF] & HCI_FEATURE_FLOW_CTRL_LAG_MASK) >> 4) + +#define HCI_FEATURE_BROADCAST_ENC_MASK 0x80 +#define HCI_FEATURE_BROADCAST_ENC_OFF 2 +#define HCI_LMP_BCAST_ENC_SUPPORTED(x) ((x)[HCI_FEATURE_BROADCAST_ENC_OFF] & HCI_FEATURE_BROADCAST_ENC_MASK) + +#define HCI_FEATURE_SCATTER_MODE_MASK 0x01 +#define HCI_FEATURE_SCATTER_MODE_OFF 3 +#define HCI_LMP_SCATTER_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_SCATTER_MODE_OFF] & HCI_FEATURE_SCATTER_MODE_MASK) + +#define HCI_FEATURE_EDR_ACL_2MPS_MASK 0x02 +#define HCI_FEATURE_EDR_ACL_2MPS_OFF 3 +#define HCI_EDR_ACL_2MPS_SUPPORTED(x) ((x)[HCI_FEATURE_EDR_ACL_2MPS_OFF] & HCI_FEATURE_EDR_ACL_2MPS_MASK) + +#define HCI_FEATURE_EDR_ACL_3MPS_MASK 0x04 +#define HCI_FEATURE_EDR_ACL_3MPS_OFF 3 +#define HCI_EDR_ACL_3MPS_SUPPORTED(x) ((x)[HCI_FEATURE_EDR_ACL_3MPS_OFF] & HCI_FEATURE_EDR_ACL_3MPS_MASK) + +#define HCI_FEATURE_ENHANCED_INQ_MASK 0x08 +#define HCI_FEATURE_ENHANCED_INQ_OFF 3 +#define HCI_ENHANCED_INQ_SUPPORTED(x) ((x)[HCI_FEATURE_ENHANCED_INQ_OFF] & HCI_FEATURE_ENHANCED_INQ_MASK) + +#define HCI_FEATURE_INTERLACED_INQ_SCAN_MASK 0x10 +#define HCI_FEATURE_INTERLACED_INQ_SCAN_OFF 3 +#define HCI_LMP_INTERLACED_INQ_SCAN_SUPPORTED(x) ((x)[HCI_FEATURE_INTERLACED_INQ_SCAN_OFF] & HCI_FEATURE_INTERLACED_INQ_SCAN_MASK) + +#define HCI_FEATURE_INTERLACED_PAGE_SCAN_MASK 0x20 +#define HCI_FEATURE_INTERLACED_PAGE_SCAN_OFF 3 +#define HCI_LMP_INTERLACED_PAGE_SCAN_SUPPORTED(x) ((x)[HCI_FEATURE_INTERLACED_PAGE_SCAN_OFF] & HCI_FEATURE_INTERLACED_PAGE_SCAN_MASK) + +#define HCI_FEATURE_INQ_RSSI_MASK 0x40 +#define HCI_FEATURE_INQ_RSSI_OFF 3 +#define HCI_LMP_INQ_RSSI_SUPPORTED(x) ((x)[HCI_FEATURE_INQ_RSSI_OFF] & HCI_FEATURE_INQ_RSSI_MASK) + +#define HCI_FEATURE_ESCO_EV3_MASK 0x80 +#define HCI_FEATURE_ESCO_EV3_OFF 3 +#define HCI_ESCO_EV3_SUPPORTED(x) ((x)[HCI_FEATURE_ESCO_EV3_OFF] & HCI_FEATURE_ESCO_EV3_MASK) + +#define HCI_FEATURE_ESCO_EV4_MASK 0x01 +#define HCI_FEATURE_ESCO_EV4_OFF 4 +#define HCI_ESCO_EV4_SUPPORTED(x) ((x)[HCI_FEATURE_ESCO_EV4_OFF] & HCI_FEATURE_ESCO_EV4_MASK) + +#define HCI_FEATURE_ESCO_EV5_MASK 0x02 +#define HCI_FEATURE_ESCO_EV5_OFF 4 +#define HCI_ESCO_EV5_SUPPORTED(x) ((x)[HCI_FEATURE_ESCO_EV5_OFF] & HCI_FEATURE_ESCO_EV5_MASK) + +#define HCI_FEATURE_ABSENCE_MASKS_MASK 0x04 +#define HCI_FEATURE_ABSENCE_MASKS_OFF 4 +#define HCI_LMP_ABSENCE_MASKS_SUPPORTED(x) ((x)[HCI_FEATURE_ABSENCE_MASKS_OFF] & HCI_FEATURE_ABSENCE_MASKS_MASK) + +#define HCI_FEATURE_AFH_CAP_SLAVE_MASK 0x08 +#define HCI_FEATURE_AFH_CAP_SLAVE_OFF 4 +#define HCI_LMP_AFH_CAP_SLAVE_SUPPORTED(x) ((x)[HCI_FEATURE_AFH_CAP_SLAVE_OFF] & HCI_FEATURE_AFH_CAP_SLAVE_MASK) + +#define HCI_FEATURE_AFH_CLASS_SLAVE_MASK 0x10 +#define HCI_FEATURE_AFH_CLASS_SLAVE_OFF 4 +#define HCI_LMP_AFH_CLASS_SLAVE_SUPPORTED(x) ((x)[HCI_FEATURE_AFH_CLASS_SLAVE_OFF] & HCI_FEATURE_AFH_CLASS_SLAVE_MASK) + +#define HCI_FEATURE_ALIAS_AUTH_MASK 0x20 +#define HCI_FEATURE_ALIAS_AUTH_OFF 4 +#define HCI_LMP_ALIAS_AUTH_SUPPORTED(x) ((x)[HCI_FEATURE_ALIAS_AUTH_OFF] & HCI_FEATURE_ALIAS_AUTH_MASK) + +#define HCI_FEATURE_ANON_MODE_MASK 0x40 +#define HCI_FEATURE_ANON_MODE_OFF 4 +#define HCI_LMP_ANON_MODE_SUPPORTED(x) ((x)[HCI_FEATURE_ANON_MODE_OFF] & HCI_FEATURE_ANON_MODE_MASK) + +#define HCI_FEATURE_3_SLOT_EDR_ACL_MASK 0x80 +#define HCI_FEATURE_3_SLOT_EDR_ACL_OFF 4 +#define HCI_3_SLOT_EDR_ACL_SUPPORTED(x) ((x)[HCI_FEATURE_3_SLOT_EDR_ACL_OFF] & HCI_FEATURE_3_SLOT_EDR_ACL_MASK) + +#define HCI_FEATURE_5_SLOT_EDR_ACL_MASK 0x01 +#define HCI_FEATURE_5_SLOT_EDR_ACL_OFF 5 +#define HCI_5_SLOT_EDR_ACL_SUPPORTED(x) ((x)[HCI_FEATURE_5_SLOT_EDR_ACL_OFF] & HCI_FEATURE_5_SLOT_EDR_ACL_MASK) + +#define HCI_FEATURE_SNIFF_SUB_RATE_MASK 0x02 +#define HCI_FEATURE_SNIFF_SUB_RATE_OFF 5 +#define HCI_SNIFF_SUB_RATE_SUPPORTED(x) ((x)[HCI_FEATURE_SNIFF_SUB_RATE_OFF] & HCI_FEATURE_SNIFF_SUB_RATE_MASK) + +#define HCI_FEATURE_ATOMIC_ENCRYPT_MASK 0x04 +#define HCI_FEATURE_ATOMIC_ENCRYPT_OFF 5 +#define HCI_ATOMIC_ENCRYPT_SUPPORTED(x) ((x)[HCI_FEATURE_ATOMIC_ENCRYPT_OFF] & HCI_FEATURE_ATOMIC_ENCRYPT_MASK) + +#define HCI_FEATURE_AFH_CAP_MASTR_MASK 0x08 +#define HCI_FEATURE_AFH_CAP_MASTR_OFF 5 +#define HCI_LMP_AFH_CAP_MASTR_SUPPORTED(x) ((x)[HCI_FEATURE_AFH_CAP_MASTR_OFF] & HCI_FEATURE_AFH_CAP_MASTR_MASK) + +#define HCI_FEATURE_AFH_CLASS_MASTR_MASK 0x10 +#define HCI_FEATURE_AFH_CLASS_MASTR_OFF 5 +#define HCI_LMP_AFH_CLASS_MASTR_SUPPORTED(x) ((x)[HCI_FEATURE_AFH_CLASS_MASTR_OFF] & HCI_FEATURE_AFH_CLASS_MASTR_MASK) + +#define HCI_FEATURE_EDR_ESCO_2MPS_MASK 0x20 +#define HCI_FEATURE_EDR_ESCO_2MPS_OFF 5 +#define HCI_EDR_ESCO_2MPS_SUPPORTED(x) ((x)[HCI_FEATURE_EDR_ESCO_2MPS_OFF] & HCI_FEATURE_EDR_ESCO_2MPS_MASK) + +#define HCI_FEATURE_EDR_ESCO_3MPS_MASK 0x40 +#define HCI_FEATURE_EDR_ESCO_3MPS_OFF 5 +#define HCI_EDR_ESCO_3MPS_SUPPORTED(x) ((x)[HCI_FEATURE_EDR_ESCO_3MPS_OFF] & HCI_FEATURE_EDR_ESCO_3MPS_MASK) + +#define HCI_FEATURE_3_SLOT_EDR_ESCO_MASK 0x80 +#define HCI_FEATURE_3_SLOT_EDR_ESCO_OFF 5 +#define HCI_3_SLOT_EDR_ESCO_SUPPORTED(x) ((x)[HCI_FEATURE_3_SLOT_EDR_ESCO_OFF] & HCI_FEATURE_3_SLOT_EDR_ESCO_MASK) + +#define HCI_FEATURE_EXT_INQ_RSP_MASK 0x01 +#define HCI_FEATURE_EXT_INQ_RSP_OFF 6 +#define HCI_EXT_INQ_RSP_SUPPORTED(x) ((x)[HCI_FEATURE_EXT_INQ_RSP_OFF] & HCI_FEATURE_EXT_INQ_RSP_MASK) + +#define HCI_FEATURE_ANUM_PIN_AWARE_MASK 0x02 +#define HCI_FEATURE_ANUM_PIN_AWARE_OFF 6 +#define HCI_ANUM_PIN_AWARE_SUPPORTED(x) ((x)[HCI_FEATURE_ANUM_PIN_AWARE_OFF] & HCI_FEATURE_ANUM_PIN_AWARE_MASK) + +#define HCI_FEATURE_ANUM_PIN_CAP_MASK 0x04 +#define HCI_FEATURE_ANUM_PIN_CAP_OFF 6 +#define HCI_ANUM_PIN_CAP_SUPPORTED(x) ((x)[HCI_FEATURE_ANUM_PIN_CAP_OFF] & HCI_FEATURE_ANUM_PIN_CAP_MASK) + +#define HCI_FEATURE_SIMPLE_PAIRING_MASK 0x08 +#define HCI_FEATURE_SIMPLE_PAIRING_OFF 6 +#define HCI_SIMPLE_PAIRING_SUPPORTED(x) ((x)[HCI_FEATURE_SIMPLE_PAIRING_OFF] & HCI_FEATURE_SIMPLE_PAIRING_MASK) + +#define HCI_FEATURE_ENCAP_PDU_MASK 0x10 +#define HCI_FEATURE_ENCAP_PDU_OFF 6 +#define HCI_ENCAP_PDU_SUPPORTED(x) ((x)[HCI_FEATURE_ENCAP_PDU_OFF] & HCI_FEATURE_ENCAP_PDU_MASK) + +#define HCI_FEATURE_ERROR_DATA_MASK 0x20 +#define HCI_FEATURE_ERROR_DATA_OFF 6 +#define HCI_ERROR_DATA_SUPPORTED(x) ((x)[HCI_FEATURE_ERROR_DATA_OFF] & HCI_FEATURE_ERROR_DATA_MASK) + +#define HCI_FEATURE_NON_FLUSHABLE_PB_MASK 0x40 +#define HCI_FEATURE_NON_FLUSHABLE_PB_OFF 6 +#define HCI_NON_FLUSHABLE_PB_SUPPORTED(x) ((x)[HCI_FEATURE_NON_FLUSHABLE_PB_OFF] & HCI_FEATURE_NON_FLUSHABLE_PB_MASK) + +#define HCI_FEATURE_LINK_SUP_TO_EVT_MASK 0x01 +#define HCI_FEATURE_LINK_SUP_TO_EVT_OFF 7 +#define HCI_LINK_SUP_TO_EVT_SUPPORTED(x) ((x)[HCI_FEATURE_LINK_SUP_TO_EVT_OFF] & HCI_FEATURE_LINK_SUP_TO_EVT_MASK) + +#define HCI_FEATURE_INQ_RESP_TX_MASK 0x02 +#define HCI_FEATURE_INQ_RESP_TX_OFF 7 +#define HCI_INQ_RESP_TX_SUPPORTED(x) ((x)[HCI_FEATURE_INQ_RESP_TX_OFF] & HCI_FEATURE_INQ_RESP_TX_MASK) + +#define HCI_FEATURE_EXTENDED_MASK 0x80 +#define HCI_FEATURE_EXTENDED_OFF 7 +#define HCI_LMP_EXTENDED_SUPPORTED(x) ((x)[HCI_FEATURE_EXTENDED_OFF] & HCI_FEATURE_EXTENDED_MASK) + +/* +** Features encoding - page 1 +*/ +#define HCI_EXT_FEATURE_SSP_HOST_MASK 0x01 +#define HCI_EXT_FEATURE_SSP_HOST_OFF 0 +#define HCI_SSP_HOST_SUPPORTED(x) ((x)[HCI_EXT_FEATURE_SSP_HOST_OFF] & HCI_EXT_FEATURE_SSP_HOST_MASK) + +/* +** Local Supported Commands encoding +*/ +#define HCI_NUM_SUPP_COMMANDS_BYTES 64 + +#define HCI_SUPP_COMMANDS_INQUIRY_MASK 0x01 +#define HCI_SUPP_COMMANDS_INQUIRY_OFF 0 +#define HCI_INQUIRY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_INQUIRY_OFF] & HCI_SUPP_COMMANDS_INQUIRY_MASK) + +#define HCI_SUPP_COMMANDS_INQUIRY_CANCEL_MASK 0x02 +#define HCI_SUPP_COMMANDS_INQUIRY_CANCEL_OFF 0 +#define HCI_INQUIRY_CANCEL_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_INQUIRY_CANCEL_OFF] & HCI_SUPP_COMMANDS_INQUIRY_CANCEL_MASK) + +#define HCI_SUPP_COMMANDS_PERIODIC_INQUIRY_MASK 0x04 +#define HCI_SUPP_COMMANDS_PERIODIC_INQUIRY_OFF 0 +#define HCI_PERIODIC_INQUIRY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_PERIODIC_INQUIRY_OFF] & HCI_SUPP_COMMANDS_PERIODIC_INQUIRY_MASK) + +#define HCI_SUPP_COMMANDS_EXIT_PERIODIC_INQUIRY_MASK 0x08 +#define HCI_SUPP_COMMANDS_EXIT_PERIODIC_INQUIRY_OFF 0 +#define HCI_EXIT_PERIODIC_INQUIRY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_EXIT_PERIODIC_INQUIRY_OFF] & HCI_SUPP_COMMANDS_EXIT_PERIODIC_INQUIRY_MASK) + +#define HCI_SUPP_COMMANDS_CREATE_CONN_MASK 0x10 +#define HCI_SUPP_COMMANDS_CREATE_CONN_OFF 0 +#define HCI_CREATE_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CREATE_CONN_OFF] & HCI_SUPP_COMMANDS_CREATE_CONN_MASK) + +#define HCI_SUPP_COMMANDS_DISCONNECT_MASK 0x20 +#define HCI_SUPP_COMMANDS_DISCONNECT_OFF 0 +#define HCI_DISCONNECT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_DISCONNECT_OFF] & HCI_SUPP_COMMANDS_DISCONNECT_MASK) + +#define HCI_SUPP_COMMANDS_ADD_SCO_CONN_MASK 0x40 +#define HCI_SUPP_COMMANDS_ADD_SCO_CONN_OFF 0 +#define HCI_ADD_SCO_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ADD_SCO_CONN_OFF] & HCI_SUPP_COMMANDS_ADD_SCO_CONN_MASK) + +#define HCI_SUPP_COMMANDS_CANCEL_CREATE_CONN_MASK 0x80 +#define HCI_SUPP_COMMANDS_CANCEL_CREATE_CONN_OFF 0 +#define HCI_CANCEL_CREATE_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CANCEL_CREATE_CONN_OFF] & HCI_SUPP_COMMANDS_CANCEL_CREATE_CONN_MASK) + +#define HCI_SUPP_COMMANDS_ACCEPT_CONN_REQUEST_MASK 0x01 +#define HCI_SUPP_COMMANDS_ACCEPT_CONN_REQUEST_OFF 1 +#define HCI_ACCEPT_CONN_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ACCEPT_CONN_REQUEST_OFF] & HCI_SUPP_COMMANDS_ACCEPT_CONN_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_REJECT_CONN_REQUEST_MASK 0x02 +#define HCI_SUPP_COMMANDS_REJECT_CONN_REQUEST_OFF 1 +#define HCI_REJECT_CONN_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REJECT_CONN_REQUEST_OFF] & HCI_SUPP_COMMANDS_REJECT_CONN_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_REPLY_MASK 0x04 +#define HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_REPLY_OFF 1 +#define HCI_LINK_KEY_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_NEG_REPLY_MASK 0x08 +#define HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_NEG_REPLY_OFF 1 +#define HCI_LINK_KEY_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_LINK_KEY_REQUEST_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_REPLY_MASK 0x10 +#define HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_REPLY_OFF 1 +#define HCI_PIN_CODE_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_NEG_REPLY_MASK 0x20 +#define HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_NEG_REPLY_OFF 1 +#define HCI_PIN_CODE_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_PIN_CODE_REQUEST_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_CHANGE_CONN_PKT_TYPE_MASK 0x40 +#define HCI_SUPP_COMMANDS_CHANGE_CONN_PKT_TYPE_OFF 1 +#define HCI_CHANGE_CONN_PKT_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CHANGE_CONN_PKT_TYPE_OFF] & HCI_SUPP_COMMANDS_CHANGE_CONN_PKT_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_AUTH_REQUEST_MASK 0x80 +#define HCI_SUPP_COMMANDS_AUTH_REQUEST_OFF 1 +#define HCI_AUTH_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_AUTH_REQUEST_OFF] & HCI_SUPP_COMMANDS_AUTH_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_SET_CONN_ENCRYPTION_MASK 0x01 +#define HCI_SUPP_COMMANDS_SET_CONN_ENCRYPTION_OFF 2 +#define HCI_SET_CONN_ENCRYPTION_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_CONN_ENCRYPTION_OFF] & HCI_SUPP_COMMANDS_SET_CONN_ENCRYPTION_MASK) + +#define HCI_SUPP_COMMANDS_CHANGE_CONN_LINK_KEY_MASK 0x02 +#define HCI_SUPP_COMMANDS_CHANGE_CONN_LINK_KEY_OFF 2 +#define HCI_CHANGE_CONN_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CHANGE_CONN_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_CHANGE_CONN_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_MASTER_LINK_KEY_MASK 0x04 +#define HCI_SUPP_COMMANDS_MASTER_LINK_KEY_OFF 2 +#define HCI_MASTER_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_MASTER_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_MASTER_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_REMOTE_NAME_REQUEST_MASK 0x08 +#define HCI_SUPP_COMMANDS_REMOTE_NAME_REQUEST_OFF 2 +#define HCI_REMOTE_NAME_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REMOTE_NAME_REQUEST_OFF] & HCI_SUPP_COMMANDS_REMOTE_NAME_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_CANCEL_REMOTE_NAME_REQUEST_MASK 0x10 +#define HCI_SUPP_COMMANDS_CANCEL_REMOTE_NAME_REQUEST_OFF 2 +#define HCI_CANCEL_REMOTE_NAME_REQUEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CANCEL_REMOTE_NAME_REQUEST_OFF] & HCI_SUPP_COMMANDS_CANCEL_REMOTE_NAME_REQUEST_MASK) + +#define HCI_SUPP_COMMANDS_READ_REMOTE_SUPP_FEATURES_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_REMOTE_SUPP_FEATURES_OFF 2 +#define HCI_READ_REMOTE_SUPP_FEATURES_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_REMOTE_SUPP_FEATURES_OFF] & HCI_SUPP_COMMANDS_READ_REMOTE_SUPP_FEATURES_MASK) + +#define HCI_SUPP_COMMANDS_READ_REMOTE_EXT_FEATURES_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_REMOTE_EXT_FEATURES_OFF 2 +#define HCI_READ_REMOTE_EXT_FEATURES_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_REMOTE_EXT_FEATURES_OFF] & HCI_SUPP_COMMANDS_READ_REMOTE_EXT_FEATURES_MASK) + +#define HCI_SUPP_COMMANDS_READ_REMOTE_VER_INFO_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_REMOTE_VER_INFO_OFF 2 +#define HCI_READ_REMOTE_VER_INFO_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_REMOTE_VER_INFO_OFF] & HCI_SUPP_COMMANDS_READ_REMOTE_VER_INFO_MASK) + +#define HCI_SUPP_COMMANDS_READ_CLOCK_OFFSET_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_CLOCK_OFFSET_OFF 3 +#define HCI_READ_CLOCK_OFFSET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_CLOCK_OFFSET_OFF] & HCI_SUPP_COMMANDS_READ_CLOCK_OFFSET_MASK) + +#define HCI_SUPP_COMMANDS_READ_LMP_HANDLE_MASK 0x02 +#define HCI_SUPP_COMMANDS_READ_LMP_HANDLE_OFF 3 +#define HCI_READ_LMP_HANDLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LMP_HANDLE_OFF] & HCI_SUPP_COMMANDS_READ_LMP_HANDLE_MASK) + +#define HCI_SUPP_COMMANDS_HOLD_MODE_CMD_MASK 0x02 +#define HCI_SUPP_COMMANDS_HOLD_MODE_CMD_OFF 4 +#define HCI_HOLD_MODE_CMD_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_HOLD_MODE_CMD_OFF] & HCI_SUPP_COMMANDS_HOLD_MODE_CMD_MASK) + +#define HCI_SUPP_COMMANDS_SNIFF_MODE_CMD_MASK 0x04 +#define HCI_SUPP_COMMANDS_SNIFF_MODE_CMD_OFF 4 +#define HCI_SNIFF_MODE_CMD_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SNIFF_MODE_CMD_OFF] & HCI_SUPP_COMMANDS_SNIFF_MODE_CMD_MASK) + +#define HCI_SUPP_COMMANDS_EXIT_SNIFF_MODE_MASK 0x08 +#define HCI_SUPP_COMMANDS_EXIT_SNIFF_MODE_OFF 4 +#define HCI_EXIT_SNIFF_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_EXIT_SNIFF_MODE_OFF] & HCI_SUPP_COMMANDS_EXIT_SNIFF_MODE_MASK) + +#define HCI_SUPP_COMMANDS_PARK_STATE_MASK 0x10 +#define HCI_SUPP_COMMANDS_PARK_STATE_OFF 4 +#define HCI_PARK_STATE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_PARK_STATE_OFF] & HCI_SUPP_COMMANDS_PARK_STATE_MASK) + +#define HCI_SUPP_COMMANDS_EXIT_PARK_STATE_MASK 0x20 +#define HCI_SUPP_COMMANDS_EXIT_PARK_STATE_OFF 4 +#define HCI_EXIT_PARK_STATE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_EXIT_PARK_STATE_OFF] & HCI_SUPP_COMMANDS_EXIT_PARK_STATE_MASK) + +#define HCI_SUPP_COMMANDS_QOS_SETUP_MASK 0x40 +#define HCI_SUPP_COMMANDS_QOS_SETUP_OFF 4 +#define HCI_QOS_SETUP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_QOS_SETUP_OFF] & HCI_SUPP_COMMANDS_QOS_SETUP_MASK) + +#define HCI_SUPP_COMMANDS_ROLE_DISCOVERY_MASK 0x80 +#define HCI_SUPP_COMMANDS_ROLE_DISCOVERY_OFF 4 +#define HCI_ROLE_DISCOVERY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ROLE_DISCOVERY_OFF] & HCI_SUPP_COMMANDS_ROLE_DISCOVERY_MASK) + +#define HCI_SUPP_COMMANDS_SWITCH_ROLE_MASK 0x01 +#define HCI_SUPP_COMMANDS_SWITCH_ROLE_OFF 5 +#define HCI_SWITCH_ROLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SWITCH_ROLE_OFF] & HCI_SUPP_COMMANDS_SWITCH_ROLE_MASK) + +#define HCI_SUPP_COMMANDS_READ_LINK_POLICY_SET_MASK 0x02 +#define HCI_SUPP_COMMANDS_READ_LINK_POLICY_SET_OFF 5 +#define HCI_READ_LINK_POLICY_SET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LINK_POLICY_SET_OFF] & HCI_SUPP_COMMANDS_READ_LINK_POLICY_SET_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LINK_POLICY_SET_MASK 0x04 +#define HCI_SUPP_COMMANDS_WRITE_LINK_POLICY_SET_OFF 5 +#define HCI_WRITE_LINK_POLICY_SET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LINK_POLICY_SET_OFF] & HCI_SUPP_COMMANDS_WRITE_LINK_POLICY_SET_MASK) + +#define HCI_SUPP_COMMANDS_READ_DEF_LINK_POLICY_SET_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_DEF_LINK_POLICY_SET_OFF 5 +#define HCI_READ_DEF_LINK_POLICY_SET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_DEF_LINK_POLICY_SET_OFF] & HCI_SUPP_COMMANDS_READ_DEF_LINK_POLICY_SET_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_DEF_LINK_POLICY_SET_MASK 0x10 +#define HCI_SUPP_COMMANDS_WRITE_DEF_LINK_POLICY_SET_OFF 5 +#define HCI_WRITE_DEF_LINK_POLICY_SET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_DEF_LINK_POLICY_SET_OFF] & HCI_SUPP_COMMANDS_WRITE_DEF_LINK_POLICY_SET_MASK) + +#define HCI_SUPP_COMMANDS_FLOW_SPECIFICATION_MASK 0x20 +#define HCI_SUPP_COMMANDS_FLOW_SPECIFICATION_OFF 5 +#define HCI_FLOW_SPECIFICATION_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_FLOW_SPECIFICATION_OFF] & HCI_SUPP_COMMANDS_FLOW_SPECIFICATION_MASK) + +#define HCI_SUPP_COMMANDS_SET_EVENT_MASK_MASK 0x40 +#define HCI_SUPP_COMMANDS_SET_EVENT_MASK_OFF 5 +#define HCI_SET_EVENT_MASK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_EVENT_MASK_OFF] & HCI_SUPP_COMMANDS_SET_EVENT_MASK_MASK) + +#define HCI_SUPP_COMMANDS_RESET_MASK 0x80 +#define HCI_SUPP_COMMANDS_RESET_OFF 5 +#define HCI_RESET_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_RESET_OFF] & HCI_SUPP_COMMANDS_RESET_MASK) + +#define HCI_SUPP_COMMANDS_SET_EVENT_FILTER_MASK 0x01 +#define HCI_SUPP_COMMANDS_SET_EVENT_FILTER_OFF 6 +#define HCI_SET_EVENT_FILTER_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_EVENT_FILTER_OFF] & HCI_SUPP_COMMANDS_SET_EVENT_FILTER_MASK) + +#define HCI_SUPP_COMMANDS_FLUSH_MASK 0x02 +#define HCI_SUPP_COMMANDS_FLUSH_OFF 6 +#define HCI_FLUSH_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_FLUSH_OFF] & HCI_SUPP_COMMANDS_FLUSH_MASK) + +#define HCI_SUPP_COMMANDS_READ_PIN_TYPE_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_PIN_TYPE_OFF 6 +#define HCI_READ_PIN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PIN_TYPE_OFF] & HCI_SUPP_COMMANDS_READ_PIN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PIN_TYPE_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_PIN_TYPE_OFF 6 +#define HCI_WRITE_PIN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PIN_TYPE_OFF] & HCI_SUPP_COMMANDS_WRITE_PIN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_CREATE_NEW_UNIT_KEY_MASK 0x10 +#define HCI_SUPP_COMMANDS_CREATE_NEW_UNIT_KEY_OFF 6 +#define HCI_CREATE_NEW_UNIT_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_CREATE_NEW_UNIT_KEY_OFF] & HCI_SUPP_COMMANDS_CREATE_NEW_UNIT_KEY_MASK) + +#define HCI_SUPP_COMMANDS_READ_STORED_LINK_KEY_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_STORED_LINK_KEY_OFF 6 +#define HCI_READ_STORED_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_STORED_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_READ_STORED_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_STORED_LINK_KEY_MASK 0x40 +#define HCI_SUPP_COMMANDS_WRITE_STORED_LINK_KEY_OFF 6 +#define HCI_WRITE_STORED_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_STORED_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_WRITE_STORED_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_DELETE_STORED_LINK_KEY_MASK 0x80 +#define HCI_SUPP_COMMANDS_DELETE_STORED_LINK_KEY_OFF 6 +#define HCI_DELETE_STORED_LINK_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_DELETE_STORED_LINK_KEY_OFF] & HCI_SUPP_COMMANDS_DELETE_STORED_LINK_KEY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LOCAL_NAME_MASK 0x01 +#define HCI_SUPP_COMMANDS_WRITE_LOCAL_NAME_OFF 7 +#define HCI_WRITE_LOCAL_NAME_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LOCAL_NAME_OFF] & HCI_SUPP_COMMANDS_WRITE_LOCAL_NAME_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_NAME_MASK 0x02 +#define HCI_SUPP_COMMANDS_READ_LOCAL_NAME_OFF 7 +#define HCI_READ_LOCAL_NAME_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_NAME_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_NAME_MASK) + +#define HCI_SUPP_COMMANDS_READ_CONN_ACCEPT_TOUT_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_CONN_ACCEPT_TOUT_OFF 7 +#define HCI_READ_CONN_ACCEPT_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_CONN_ACCEPT_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_CONN_ACCEPT_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_CONN_ACCEPT_TOUT_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_CONN_ACCEPT_TOUT_OFF 7 +#define HCI_WRITE_CONN_ACCEPT_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_CONN_ACCEPT_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_CONN_ACCEPT_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_TOUT_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_PAGE_TOUT_OFF 7 +#define HCI_READ_PAGE_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_TOUT_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_TOUT_OFF 7 +#define HCI_WRITE_PAGE_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_READ_SCAN_ENABLE_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_SCAN_ENABLE_OFF 7 +#define HCI_READ_SCAN_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_SCAN_ENABLE_OFF] & HCI_SUPP_COMMANDS_READ_SCAN_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SCAN_ENABLE_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_SCAN_ENABLE_OFF 7 +#define HCI_WRITE_SCAN_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SCAN_ENABLE_OFF] & HCI_SUPP_COMMANDS_WRITE_SCAN_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_ACTIVITY_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_ACTIVITY_OFF 8 +#define HCI_READ_PAGE_SCAN_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_SCAN_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_SCAN_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_ACTIVITY_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_ACTIVITY_OFF 8 +#define HCI_WRITE_PAGE_SCAN_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_READ_INQURIY_SCAN_ACTIVITY_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_INQURIY_SCAN_ACTIVITY_OFF 8 +#define HCI_READ_INQURIY_SCAN_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_INQURIY_SCAN_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_READ_INQURIY_SCAN_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_INQURIY_SCAN_ACTIVITY_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_INQURIY_SCAN_ACTIVITY_OFF 8 +#define HCI_WRITE_INQURIY_SCAN_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_INQURIY_SCAN_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_WRITE_INQURIY_SCAN_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_READ_AUTH_ENABLE_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_AUTH_ENABLE_OFF 8 +#define HCI_READ_AUTH_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_AUTH_ENABLE_OFF] & HCI_SUPP_COMMANDS_READ_AUTH_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_AUTH_ENABLE_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_AUTH_ENABLE_OFF 8 +#define HCI_WRITE_AUTH_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_AUTH_ENABLE_OFF] & HCI_SUPP_COMMANDS_WRITE_AUTH_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_READ_ENCRYPT_ENABLE_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_ENCRYPT_ENABLE_OFF 8 +#define HCI_READ_ENCRYPT_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_ENCRYPT_ENABLE_OFF] & HCI_SUPP_COMMANDS_READ_ENCRYPT_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_ENCRYPT_ENABLE_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_ENCRYPT_ENABLE_OFF 8 +#define HCI_WRITE_ENCRYPT_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_ENCRYPT_ENABLE_OFF] & HCI_SUPP_COMMANDS_WRITE_ENCRYPT_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_READ_CLASS_DEVICE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_CLASS_DEVICE_OFF 9 +#define HCI_READ_CLASS_DEVICE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_CLASS_DEVICE_OFF] & HCI_SUPP_COMMANDS_READ_CLASS_DEVICE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_CLASS_DEVICE_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_CLASS_DEVICE_OFF 9 +#define HCI_WRITE_CLASS_DEVICE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_CLASS_DEVICE_OFF] & HCI_SUPP_COMMANDS_WRITE_CLASS_DEVICE_MASK) + +#define HCI_SUPP_COMMANDS_READ_VOICE_SETTING_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_VOICE_SETTING_OFF 9 +#define HCI_READ_VOICE_SETTING_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_VOICE_SETTING_OFF] & HCI_SUPP_COMMANDS_READ_VOICE_SETTING_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_VOICE_SETTING_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_VOICE_SETTING_OFF 9 +#define HCI_WRITE_VOICE_SETTING_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_VOICE_SETTING_OFF] & HCI_SUPP_COMMANDS_WRITE_VOICE_SETTING_MASK) + +#define HCI_SUPP_COMMANDS_READ_AUTO_FLUSH_TOUT_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_AUTO_FLUSH_TOUT_OFF 9 +#define HCI_READ_AUTO_FLUSH_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_AUTO_FLUSH_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_AUTO_FLUSH_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_AUTO_FLUSH_TOUT_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_AUTO_FLUSH_TOUT_OFF 9 +#define HCI_WRITE_AUTO_FLUSH_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_AUTO_FLUSH_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_AUTO_FLUSH_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_READ_NUM_BROAD_RETRANS_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_NUM_BROAD_RETRANS_OFF 9 +#define HCI_READ_NUM_BROAD_RETRANS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_NUM_BROAD_RETRANS_OFF] & HCI_SUPP_COMMANDS_READ_NUM_BROAD_RETRANS_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_NUM_BROAD_RETRANS_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_NUM_BROAD_RETRANS_OFF 9 +#define HCI_WRITE_NUM_BROAD_RETRANS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_NUM_BROAD_RETRANS_OFF] & HCI_SUPP_COMMANDS_WRITE_NUM_BROAD_RETRANS_MASK) + +#define HCI_SUPP_COMMANDS_READ_HOLD_MODE_ACTIVITY_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_HOLD_MODE_ACTIVITY_OFF 10 +#define HCI_READ_HOLD_MODE_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_HOLD_MODE_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_READ_HOLD_MODE_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_HOLD_MODE_ACTIVITY_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_HOLD_MODE_ACTIVITY_OFF 10 +#define HCI_WRITE_HOLD_MODE_ACTIVITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_HOLD_MODE_ACTIVITY_OFF] & HCI_SUPP_COMMANDS_WRITE_HOLD_MODE_ACTIVITY_MASK) + +#define HCI_SUPP_COMMANDS_READ_TRANS_PWR_LEVEL_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_TRANS_PWR_LEVEL_OFF 10 +#define HCI_READ_TRANS_PWR_LEVEL_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_TRANS_PWR_LEVEL_OFF] & HCI_SUPP_COMMANDS_READ_TRANS_PWR_LEVEL_MASK) + +#define HCI_SUPP_COMMANDS_READ_SYNCH_FLOW_CTRL_ENABLE_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_SYNCH_FLOW_CTRL_ENABLE_OFF 10 +#define HCI_READ_SYNCH_FLOW_CTRL_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_SYNCH_FLOW_CTRL_ENABLE_OFF] & HCI_SUPP_COMMANDS_READ_SYNCH_FLOW_CTRL_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SYNCH_FLOW_CTRL_ENABLE_MASK 0x10 +#define HCI_SUPP_COMMANDS_WRITE_SYNCH_FLOW_CTRL_ENABLE_OFF 10 +#define HCI_WRITE_SYNCH_FLOW_CTRL_ENABLE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SYNCH_FLOW_CTRL_ENABLE_OFF] & HCI_SUPP_COMMANDS_WRITE_SYNCH_FLOW_CTRL_ENABLE_MASK) + +#define HCI_SUPP_COMMANDS_SET_HOST_CTRLR_TO_HOST_FC_MASK 0x20 +#define HCI_SUPP_COMMANDS_SET_HOST_CTRLR_TO_HOST_FC_OFF 10 +#define HCI_SET_HOST_CTRLR_TO_HOST_FC_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_HOST_CTRLR_TO_HOST_FC_OFF] & HCI_SUPP_COMMANDS_SET_HOST_CTRLR_TO_HOST_FC_MASK) + +#define HCI_SUPP_COMMANDS_HOST_BUFFER_SIZE_MASK 0x40 +#define HCI_SUPP_COMMANDS_HOST_BUFFER_SIZE_OFF 10 +#define HCI_HOST_BUFFER_SIZE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_HOST_BUFFER_SIZE_OFF] & HCI_SUPP_COMMANDS_HOST_BUFFER_SIZE_MASK) + +#define HCI_SUPP_COMMANDS_HOST_NUM_COMPLETED_PKTS_MASK 0x80 +#define HCI_SUPP_COMMANDS_HOST_NUM_COMPLETED_PKTS_OFF 10 +#define HCI_HOST_NUM_COMPLETED_PKTS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_HOST_NUM_COMPLETED_PKTS_OFF] & HCI_SUPP_COMMANDS_HOST_NUM_COMPLETED_PKTS_MASK) + +#define HCI_SUPP_COMMANDS_READ_LINK_SUP_TOUT_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_LINK_SUP_TOUT_OFF 11 +#define HCI_READ_LINK_SUP_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LINK_SUP_TOUT_OFF] & HCI_SUPP_COMMANDS_READ_LINK_SUP_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LINK_SUP_TOUT_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_LINK_SUP_TOUT_OFF 11 +#define HCI_WRITE_LINK_SUP_TOUT_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LINK_SUP_TOUT_OFF] & HCI_SUPP_COMMANDS_WRITE_LINK_SUP_TOUT_MASK) + +#define HCI_SUPP_COMMANDS_READ_NUM_SUPP_IAC_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_NUM_SUPP_IAC_OFF 11 +#define HCI_READ_NUM_SUPP_IAC_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_NUM_SUPP_IAC_OFF] & HCI_SUPP_COMMANDS_READ_NUM_SUPP_IAC_MASK) + +#define HCI_SUPP_COMMANDS_READ_CURRENT_IAC_LAP_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_CURRENT_IAC_LAP_OFF 11 +#define HCI_READ_CURRENT_IAC_LAP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_CURRENT_IAC_LAP_OFF] & HCI_SUPP_COMMANDS_READ_CURRENT_IAC_LAP_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_CURRENT_IAC_LAP_MASK 0x10 +#define HCI_SUPP_COMMANDS_WRITE_CURRENT_IAC_LAP_OFF 11 +#define HCI_WRITE_CURRENT_IAC_LAP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_CURRENT_IAC_LAP_OFF] & HCI_SUPP_COMMANDS_WRITE_CURRENT_IAC_LAP_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_PER_MODE_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_PER_MODE_OFF 11 +#define HCI_READ_PAGE_SCAN_PER_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_SCAN_PER_MODE_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_SCAN_PER_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_PER_MODE_MASK 0x40 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_PER_MODE_OFF 11 +#define HCI_WRITE_PAGE_SCAN_PER_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_PER_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_PER_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_MODE_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_MODE_OFF 11 +#define HCI_READ_PAGE_SCAN_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_SCAN_MODE_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_SCAN_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_MODE_MASK 0x01 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_MODE_OFF 12 +#define HCI_WRITE_PAGE_SCAN_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_MODE_MASK) + +#define HCI_SUPP_COMMANDS_SET_AFH_CHNL_CLASS_MASK 0x02 +#define HCI_SUPP_COMMANDS_SET_AFH_CHNL_CLASS_OFF 12 +#define HCI_SET_AFH_CHNL_CLASS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SET_AFH_CHNL_CLASS_OFF] & HCI_SUPP_COMMANDS_SET_AFH_CHNL_CLASS_MASK) + +#define HCI_SUPP_COMMANDS_READ_INQUIRY_SCAN_TYPE_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_INQUIRY_SCAN_TYPE_OFF 12 +#define HCI_READ_INQUIRY_SCAN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_INQUIRY_SCAN_TYPE_OFF] & HCI_SUPP_COMMANDS_READ_INQUIRY_SCAN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_SCAN_TYPE_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_SCAN_TYPE_OFF 12 +#define HCI_WRITE_INQUIRY_SCAN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_INQUIRY_SCAN_TYPE_OFF] & HCI_SUPP_COMMANDS_WRITE_INQUIRY_SCAN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_READ_INQUIRY_MODE_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_INQUIRY_MODE_OFF 12 +#define HCI_READ_INQUIRY_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_INQUIRY_MODE_OFF] & HCI_SUPP_COMMANDS_READ_INQUIRY_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_MODE_MASK 0x80 +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_MODE_OFF 12 +#define HCI_WRITE_INQUIRY_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_INQUIRY_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_INQUIRY_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_TYPE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_PAGE_SCAN_TYPE_OFF 13 +#define HCI_READ_PAGE_SCAN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_PAGE_SCAN_TYPE_OFF] & HCI_SUPP_COMMANDS_READ_PAGE_SCAN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_TYPE_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_TYPE_OFF 13 +#define HCI_WRITE_PAGE_SCAN_TYPE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_TYPE_OFF] & HCI_SUPP_COMMANDS_WRITE_PAGE_SCAN_TYPE_MASK) + +#define HCI_SUPP_COMMANDS_READ_AFH_CHNL_ASSESS_MODE_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_AFH_CHNL_ASSESS_MODE_OFF 13 +#define HCI_READ_AFH_CHNL_ASSESS_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_AFH_CHNL_ASSESS_MODE_OFF] & HCI_SUPP_COMMANDS_READ_AFH_CHNL_ASSESS_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_AFH_CHNL_ASSESS_MODE_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_AFH_CHNL_ASSESS_MODE_OFF 13 +#define HCI_WRITE_AFH_CHNL_ASSESS_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_AFH_CHNL_ASSESS_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_AFH_CHNL_ASSESS_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_VER_INFO_MASK 0x08 +#define HCI_SUPP_COMMANDS_READ_LOCAL_VER_INFO_OFF 14 +#define HCI_READ_LOCAL_VER_INFO_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_VER_INFO_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_VER_INFO_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_SUP_CMDS_MASK 0x10 +#define HCI_SUPP_COMMANDS_READ_LOCAL_SUP_CMDS_OFF 14 +#define HCI_READ_LOCAL_SUP_CMDS_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_SUP_CMDS_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_SUP_CMDS_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_SUPP_FEATURES_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_LOCAL_SUPP_FEATURES_OFF 14 +#define HCI_READ_LOCAL_SUPP_FEATURES_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_SUPP_FEATURES_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_SUPP_FEATURES_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_EXT_FEATURES_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_LOCAL_EXT_FEATURES_OFF 14 +#define HCI_READ_LOCAL_EXT_FEATURES_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_EXT_FEATURES_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_EXT_FEATURES_MASK) + +#define HCI_SUPP_COMMANDS_READ_BUFFER_SIZE_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_BUFFER_SIZE_OFF 14 +#define HCI_READ_BUFFER_SIZE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_BUFFER_SIZE_OFF] & HCI_SUPP_COMMANDS_READ_BUFFER_SIZE_MASK) + +#define HCI_SUPP_COMMANDS_READ_COUNTRY_CODE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_COUNTRY_CODE_OFF 15 +#define HCI_READ_COUNTRY_CODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_COUNTRY_CODE_OFF] & HCI_SUPP_COMMANDS_READ_COUNTRY_CODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_BD_ADDR_MASK 0x02 +#define HCI_SUPP_COMMANDS_READ_BD_ADDR_OFF 15 +#define HCI_READ_BD_ADDR_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_BD_ADDR_OFF] & HCI_SUPP_COMMANDS_READ_BD_ADDR_MASK) + +#define HCI_SUPP_COMMANDS_READ_FAIL_CONTACT_CNTR_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_FAIL_CONTACT_CNTR_OFF 15 +#define HCI_READ_FAIL_CONTACT_CNTR_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_FAIL_CONTACT_CNTR_OFF] & HCI_SUPP_COMMANDS_READ_FAIL_CONTACT_CNTR_MASK) + +#define HCI_SUPP_COMMANDS_RESET_FAIL_CONTACT_CNTR_MASK 0x08 +#define HCI_SUPP_COMMANDS_RESET_FAIL_CONTACT_CNTR_OFF 15 +#define HCI_RESET_FAIL_CONTACT_CNTR_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_RESET_FAIL_CONTACT_CNTR_OFF] & HCI_SUPP_COMMANDS_RESET_FAIL_CONTACT_CNTR_MASK) + +#define HCI_SUPP_COMMANDS_GET_LINK_QUALITY_MASK 0x10 +#define HCI_SUPP_COMMANDS_GET_LINK_QUALITY_OFF 15 +#define HCI_GET_LINK_QUALITY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_GET_LINK_QUALITY_OFF] & HCI_SUPP_COMMANDS_GET_LINK_QUALITY_MASK) + +#define HCI_SUPP_COMMANDS_READ_RSSI_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_RSSI_OFF 15 +#define HCI_READ_RSSI_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_RSSI_OFF] & HCI_SUPP_COMMANDS_READ_RSSI_MASK) + +#define HCI_SUPP_COMMANDS_READ_AFH_CH_MAP_MASK 0x40 +#define HCI_SUPP_COMMANDS_READ_AFH_CH_MAP_OFF 15 +#define HCI_READ_AFH_CH_MAP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_AFH_CH_MAP_OFF] & HCI_SUPP_COMMANDS_READ_AFH_CH_MAP_MASK) + +#define HCI_SUPP_COMMANDS_READ_BD_CLOCK_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_BD_CLOCK_OFF 15 +#define HCI_READ_BD_CLOCK_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_BD_CLOCK_OFF] & HCI_SUPP_COMMANDS_READ_BD_CLOCK_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOOPBACK_MODE_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_LOOPBACK_MODE_OFF 16 +#define HCI_READ_LOOPBACK_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOOPBACK_MODE_OFF] & HCI_SUPP_COMMANDS_READ_LOOPBACK_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_LOOPBACK_MODE_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_LOOPBACK_MODE_OFF 16 +#define HCI_WRITE_LOOPBACK_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_LOOPBACK_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_LOOPBACK_MODE_MASK) + +#define HCI_SUPP_COMMANDS_ENABLE_DEV_UNDER_TEST_MASK 0x04 +#define HCI_SUPP_COMMANDS_ENABLE_DEV_UNDER_TEST_OFF 16 +#define HCI_ENABLE_DEV_UNDER_TEST_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ENABLE_DEV_UNDER_TEST_OFF] & HCI_SUPP_COMMANDS_ENABLE_DEV_UNDER_TEST_MASK) + +#define HCI_SUPP_COMMANDS_SETUP_SYNCH_CONN_MASK 0x08 +#define HCI_SUPP_COMMANDS_SETUP_SYNCH_CONN_OFF 16 +#define HCI_SETUP_SYNCH_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SETUP_SYNCH_CONN_OFF] & HCI_SUPP_COMMANDS_SETUP_SYNCH_CONN_MASK) + +#define HCI_SUPP_COMMANDS_ACCEPT_SYNCH_CONN_MASK 0x10 +#define HCI_SUPP_COMMANDS_ACCEPT_SYNCH_CONN_OFF 16 +#define HCI_ACCEPT_SYNCH_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ACCEPT_SYNCH_CONN_OFF] & HCI_SUPP_COMMANDS_ACCEPT_SYNCH_CONN_MASK) + +#define HCI_SUPP_COMMANDS_REJECT_SYNCH_CONN_MASK 0x20 +#define HCI_SUPP_COMMANDS_REJECT_SYNCH_CONN_OFF 16 +#define HCI_REJECT_SYNCH_CONN_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REJECT_SYNCH_CONN_OFF] & HCI_SUPP_COMMANDS_REJECT_SYNCH_CONN_MASK) + +#define HCI_SUPP_COMMANDS_READ_EXT_INQUIRY_RESP_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_EXT_INQUIRY_RESP_OFF 17 +#define HCI_READ_EXT_INQUIRY_RESP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_EXT_INQUIRY_RESP_OFF] & HCI_SUPP_COMMANDS_READ_EXT_INQUIRY_RESP_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_EXT_INQUIRY_RESP_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_EXT_INQUIRY_RESP_OFF 17 +#define HCI_WRITE_EXT_INQUIRY_RESP_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_EXT_INQUIRY_RESP_OFF] & HCI_SUPP_COMMANDS_WRITE_EXT_INQUIRY_RESP_MASK) + +#define HCI_SUPP_COMMANDS_REFRESH_ENCRYPTION_KEY_MASK 0x04 +#define HCI_SUPP_COMMANDS_REFRESH_ENCRYPTION_KEY_OFF 17 +#define HCI_REFRESH_ENCRYPTION_KEY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REFRESH_ENCRYPTION_KEY_OFF] & HCI_SUPP_COMMANDS_REFRESH_ENCRYPTION_KEY_MASK) + +/* Octet 17, bit 3 is reserved */ + +#define HCI_SUPP_COMMANDS_SNIFF_SUB_RATE_MASK 0x10 +#define HCI_SUPP_COMMANDS_SNIFF_SUB_RATE_OFF 17 +#define HCI_SNIFF_SUB_RATE_CMD_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SNIFF_SUB_RATE_OFF] & HCI_SUPP_COMMANDS_SNIFF_SUB_RATE_MASK) + +#define HCI_SUPP_COMMANDS_READ_SIMPLE_PAIRING_MODE_MASK 0x20 +#define HCI_SUPP_COMMANDS_READ_SIMPLE_PAIRING_MODE_OFF 17 +#define HCI_READ_SIMPLE_PAIRING_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_SIMPLE_PAIRING_MODE_OFF] & HCI_SUPP_COMMANDS_READ_SIMPLE_PAIRING_MODE_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_MODE_MASK 0x40 +#define HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_MODE_OFF 17 +#define HCI_WRITE_SIMPLE_PAIRING_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_MODE_MASK) + +#define HCI_SUPP_COMMANDS_READ_LOCAL_OOB_DATA_MASK 0x80 +#define HCI_SUPP_COMMANDS_READ_LOCAL_OOB_DATA_OFF 17 +#define HCI_READ_LOCAL_OOB_DATA_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_LOCAL_OOB_DATA_OFF] & HCI_SUPP_COMMANDS_READ_LOCAL_OOB_DATA_MASK) + +#define HCI_SUPP_COMMANDS_READ_INQUIRY_RESPONSE_TX_POWER_MASK 0x01 +#define HCI_SUPP_COMMANDS_READ_INQUIRY_RESPONSE_TX_POWER_OFF 18 +#define HCI_READ_INQUIRY_RESPONSE_TX_POWER_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_INQUIRY_RESPONSE_TX_POWER_OFF] & HCI_SUPP_COMMANDS_READ_INQUIRY_RESPONSE_TX_POWER_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_RESPONSE_TX_POWER_MASK 0x02 +#define HCI_SUPP_COMMANDS_WRITE_INQUIRY_RESPONSE_TX_POWER_OFF 18 +#define HCI_WRITE_INQUIRY_RESPONSE_TX_POWER_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_INQUIRY_RESPONSE_TX_POWER_OFF] & HCI_SUPP_COMMANDS_WRITE_INQUIRY_RESPONSE_TX_POWER_MASK) + +#define HCI_SUPP_COMMANDS_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_MASK 0x04 +#define HCI_SUPP_COMMANDS_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_OFF 18 +#define HCI_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_OFF] & HCI_SUPP_COMMANDS_READ_DEFAULT_ERRONEOUS_DATA_REPORTING_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_MASK 0x08 +#define HCI_SUPP_COMMANDS_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_OFF 18 +#define HCI_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_OFF] & HCI_SUPP_COMMANDS_WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING_MASK) + +#define HCI_SUPP_COMMANDS_IO_CAPABILITY_RESPONSE_MASK 0x80 +#define HCI_SUPP_COMMANDS_IO_CAPABILITY_RESPONSE_OFF 18 +#define HCI_IO_CAPABILITY_RESPONSE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_IO_CAPABILITY_RESPONSE_OFF] & HCI_SUPP_COMMANDS_IO_CAPABILITY_RESPONSE_MASK) + +#define HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_REPLY_MASK 0x01 +#define HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_REPLY_OFF 19 +#define HCI_USER_CONFIRMATION_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_NEG_REPLY_MASK 0x02 +#define HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_NEG_REPLY_OFF 19 +#define HCI_USER_CONFIRMATION_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_USER_CONFIRMATION_REQUEST_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_REPLY_MASK 0x04 +#define HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_REPLY_OFF 19 +#define HCI_USER_PASSKEY_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_NEG_REPLY_MASK 0x08 +#define HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_NEG_REPLY_OFF 19 +#define HCI_USER_PASSKEY_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_USER_PASSKEY_REQUEST_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_REPLY_MASK 0x10 +#define HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_REPLY_OFF 19 +#define HCI_REMOTE_OOB_DATA_REQUEST_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_REPLY_OFF] & HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_DBG_MODE_MASK 0x20 +#define HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_DBG_MODE_OFF 19 +#define HCI_WRITE_SIMPLE_PAIRING_DBG_MODE_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_DBG_MODE_OFF] & HCI_SUPP_COMMANDS_WRITE_SIMPLE_PAIRING_DBG_MODE_MASK) + +#define HCI_SUPP_COMMANDS_ENHANCED_FLUSH_MASK 0x40 +#define HCI_SUPP_COMMANDS_ENHANCED_FLUSH_OFF 19 +#define HCI_ENHANCED_FLUSH_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_ENHANCED_FLUSH_OFF] & HCI_SUPP_COMMANDS_ENHANCED_FLUSH_MASK) + +#define HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_MASK 0x80 +#define HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_OFF 19 +#define HCI_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_REMOTE_OOB_DATA_REQUEST_NEG_REPLY_MASK) + +#define HCI_SUPP_COMMANDS_SEND_KEYPRESS_NOTIF_MASK 0x04 +#define HCI_SUPP_COMMANDS_SEND_KEYPRESS_NOTIF_OFF 20 +#define HCI_SEND_NOTIF_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_SEND_KEYPRESS_NOTIF_OFF] & HCI_SUPP_COMMANDS_SEND_KEYPRESS_NOTIF_MASK) + +#define HCI_SUPP_COMMANDS_IO_CAP_REQ_NEG_REPLY_MASK 0x08 +#define HCI_SUPP_COMMANDS_IO_CAP_REQ_NEG_REPLY_OFF 20 +#define HCI_IO_CAP_REQ_NEG_REPLY_SUPPORTED(x) ((x)[HCI_SUPP_COMMANDS_IO_CAP_REQ_NEG_REPLY_OFF] & HCI_SUPP_COMMANDS_IO_CAP_REQ_NEG_REPLY_MASK) + +#endif + diff --git a/inc/target.h b/inc/target.h new file mode 100755 index 0000000..32b5bf9 --- a/dev/null +++ b/inc/target.h @@ -0,0 +1,121 @@ +/* + * + * target.h + * + * + * + * Copyright (C) 2011-2012 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ + +#ifndef _TARGET_H +#define _TARGET_H + +#include <linux/usb.h> + +#define BT_API +#define RFC_API +#define L2C_API + +#define INT int +#define UINT unsigned int +#define DWORD unsigned long + +#ifndef BOOL +#define BOOL int +#endif + +#ifndef USHORT +#define USHORT unsigned short +#endif + +#ifndef ULONG +#define ULONG unsigned long +#endif + +#ifndef VOID +#define VOID void +#endif + +#define _vsnprintf vsnprintf + +#define __cdecl + +#define GKI_USE_DYNAMIC_BUFFERS TRUE /* TRUE if using dynamic buffers */ + +#define GKI_NUM_FIXED_BUF_POOLS 1 +#define GKI_DEF_BUFPOOL_PERM_MASK 0xfffc /* Pool ID 0 is public */ + +/* Define the total number of buffer pools used, fixed and dynamic (Maximum is 16) */ +#define GKI_NUM_TOTAL_BUF_POOLS 1 + +#define GKI_BUF0_SIZE 1740 +#ifdef BTUSB_LITE +#define GKI_BUF0_MAX 10 +#else +#define GKI_BUF0_MAX 5 +#endif + +#define GKI_POOL_ID_0 0 + +#define HCI_SCO_POOL_ID GKI_POOL_ID_0 // all SCO data to/from the device + +/* Set this flag to non zero if you want to do buffer corruption checks. +** If set, GKI will check buffer tail corruption every time it processes +** a buffer. This is very useful for debug, and is minimal overhead in +** a running system. +*/ +#define GKI_ENABLE_BUF_CORRUPTION_CHECK 1 + +#define L2CAP_MTU_SIZE 1691 +//#define L2CAP_MTU_SIZE 200 + +/* Maximum size in bytes of the codec capabilities information element. */ +#ifndef AVDT_CODEC_SIZE +#define AVDT_CODEC_SIZE 10 +#endif + +/* Number of streams for dual stack */ +#ifndef BTM_SYNC_INFO_NUM_STR +#define BTM_SYNC_INFO_NUM_STR 2 +#endif + +/* Number of streams for dual stack in BT Controller (simulation) */ +#ifndef BTM_SYNC_INFO_NUM_STR_BTC +#define BTM_SYNC_INFO_NUM_STR_BTC 2 +#endif + +#ifndef BTA_AV_NUM_STRS +#define BTA_AV_NUM_STRS 2 +#endif + +#define BTU_STACK_LITE_ENABLED TRUE +#define BTU_MULTI_AV_INCLUDED TRUE + +/* Number of simultaneous links to different peer devices. */ +#ifndef AVDT_NUM_LINKS +#define AVDT_NUM_LINKS 2 +#endif + +/* Number of simultaneous stream endpoints. */ +#ifndef AVDT_NUM_SEPS +#define AVDT_NUM_SEPS 2 +#endif + +#endif diff --git a/src/btusb.c b/src/btusb.c new file mode 100755 index 0000000..5253dcb --- a/dev/null +++ b/src/btusb.c @@ -0,0 +1,1596 @@ +/* + * + * btusb.c + * + * + * + * Copyright (C) 2011-2014 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ +#include "btusb.h" +#include <linux/module.h> +#include <linux/slab.h> + +#include "hcidefs.h" +#include "btusb_proc.h" + +#define BTUSB_VID_BRCM 0x0A5C + +/* forward reference */ +struct usb_driver btusb_driver; + +/* table of devices that work with this driver */ +static struct usb_device_id btusb_table [] = +{ +#if defined(BTUSB_VID) && defined(BTUSB_PID) + /* If a specific Vid/Pid is specified at compilation time */ + { USB_DEVICE(BTUSB_VID, BTUSB_PID) }, +#else + /* all BT devices */ + { .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS | USB_DEVICE_ID_MATCH_DEV_SUBCLASS | + USB_DEVICE_ID_MATCH_DEV_PROTOCOL, + .bDeviceClass = USB_CLASS_WIRELESS_CONTROLLER, + /* 0x01 / 0x01 = Bluetooth programming interface */ + .bDeviceSubClass = 0x01, + .bDeviceProtocol = 0x01 + }, + /* all BRCM BT interfaces */ + { .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS | + USB_DEVICE_ID_MATCH_INT_PROTOCOL, + .idVendor = BTUSB_VID_BRCM, + .bInterfaceClass = USB_CLASS_WIRELESS_CONTROLLER, + /* 0x01 / 0x01 = Bluetooth programming interface */ + .bInterfaceSubClass = 0x01, + .bInterfaceProtocol = 0x01 + }, + /* all BRCM vendor specific with Bluetooth programming interface */ + { .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_DEV_CLASS | + USB_DEVICE_ID_MATCH_DEV_SUBCLASS | USB_DEVICE_ID_MATCH_DEV_PROTOCOL, + .idVendor = BTUSB_VID_BRCM, + .bDeviceClass = USB_CLASS_VENDOR_SPEC, + /* 0x01 / 0x01 = Bluetooth programming interface */ + .bDeviceSubClass = 0x01, + .bDeviceProtocol = 0x01 + }, +#endif + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, btusb_table); + +static struct file_operations btusb_fops = +{ + .owner = THIS_MODULE, + .read = btusb_read, + .write = btusb_write, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) + .unlocked_ioctl = btusb_ioctl, +#else + .ioctl = btusb_ioctl, +#endif + .poll = btusb_poll, + .open = btusb_open, + .release = btusb_release, +}; + +/* + * usb class driver info in order to get a minor number from the usb core, + * and to have the device registered with devfs and the driver core + */ +static struct usb_class_driver btusb_class = +{ + .name = "usb/btusb%d", + .fops = &btusb_fops, + .minor_base = BTUSB_MINOR_BASE, +}; + + +/* static functions */ +static int btusb_create(struct btusb *p_dev, struct usb_interface *p_interface, const struct usb_device_id *p_id); + +/* module parameter to enable suspend/resume with remote wakeup */ +bool autopm = 0; + +/* module parameter to enable debug flags */ +int dbgflags = BTUSB_DBGFLAGS; + +/******************************************************************************* + ** + ** Function btusb_submit + ** + ** Description Submit a BTUSB transaction + ** + ** Parameters p_dev: driver control block + ** p_anchor: anchor to hook URB to when submitting + ** p_trans: transaction to submit + ** mem_flags: memory flags to submit the URB with + ** + ** Returns 0 upon success, error core else. + ** + *******************************************************************************/ +int btusb_submit(struct btusb *p_dev, struct usb_anchor *p_anchor, struct btusb_trans *p_trans, int mem_flags) +{ + int status; + struct urb *p_urb = p_trans->p_urb; + + BTUSB_DBG("urb %p b=%p\n", p_urb, p_urb->transfer_buffer); + + if (unlikely(!p_dev->p_main_intf)) + { + return -ENODEV; + } + + usb_anchor_urb(p_urb, p_anchor); + status = usb_submit_urb(p_urb, mem_flags); + if (unlikely(status)) + { + /* this can happen when device is disconnecting */ + BTUSB_DBG("usb_submit_urb failed(%d)\n", status); + usb_unanchor_urb(p_urb); + p_dev->stats.urb_submit_err++; + } + else + { + p_dev->stats.urb_submit_ok++; + } + + return status; +} + + +/******************************************************************************* + ** + ** Function btusb_acl_read_complete + ** + ** Description ACL Bulk pipe completion routine + ** + ** Parameters p_urb: URB that completed + ** + ** Returns void + ** + *******************************************************************************/ +static void btusb_acl_read_complete(struct urb *p_urb) +{ + struct btusb_trans *p_trans = p_urb->context; + struct btusb *p_dev = p_trans->p_dev; + int count = p_urb->actual_length; + + BTUSB_DBG("urb %p status %d count %d flags x%x\n", p_urb, + p_urb->status, count, p_urb->transfer_flags); + + /* number of ACL URB completed */ + p_dev->stats.acl_rx_complete++; + + /* check if device is disconnecting, has been unplugged or is closing */ + if (unlikely(p_urb->status)) + { + BTUSB_DBG("not queuing URB\n"); + p_dev->stats.acl_rx_complete_err++; + goto exit; + } + + /* if there was no data, do not forward to upper layers */ + if (unlikely(!count)) + { + p_dev->stats.acl_rx_resubmit++; + goto resubmit; + } + + /* increment the number of bytes transferred */ + p_dev->stats.acl_rx_bytes += count; + + /* forward up the data */ + btusb_dump_data(p_urb->transfer_buffer, count, "btusb_acl_read_complete"); + btusb_rx_enqueue(p_dev, p_trans, HCIT_TYPE_ACL_DATA); + + /* Do not resubmit since it was forwarded to the user */ + goto exit; + +resubmit: + if (unlikely(btusb_submit(p_dev, &p_dev->acl_rx_submitted, p_trans, GFP_ATOMIC))) + p_dev->stats.acl_rx_submit_err++; + else + p_dev->stats.acl_rx_submit_ok++; + +exit: + return; +} + +/******************************************************************************* + ** + ** Function btusb_diag_read_complete + ** + ** Description Diag Bulk pipe completion routine + ** + ** Parameters p_urb: URB that completed + ** + ** Returns void + ** + *******************************************************************************/ +static void btusb_diag_read_complete(struct urb *p_urb) +{ + struct btusb_trans *p_trans = p_urb->context; + struct btusb *p_dev = p_trans->p_dev; + int count = p_urb->actual_length; + + BTUSB_DBG("urb %p status %d count %d flags %x\n", p_urb, + p_urb->status, count, p_urb->transfer_flags); + + p_dev->stats.diag_rx_complete++; + + /* check if device is disconnecting, has been unplugged or is closing */ + if (unlikely(p_urb->status)) + { + p_dev->stats.diag_rx_complete_err++; + BTUSB_DBG("not queuing URB\n"); + goto exit; + } + + /* if there was no data or no more submitted diags, do not forward to upper layers */ + if (unlikely(!count || usb_anchor_empty(&p_dev->diag_rx_submitted))) + { + p_dev->stats.diag_rx_resubmit++; + goto resubmit; + } + + /* increment the number of bytes transferred */ + p_dev->stats.diag_rx_bytes += count; + + /* forward up the data */ + btusb_dump_data(p_urb->transfer_buffer, count, "btusb_diag_read_complete"); + btusb_rx_enqueue(p_dev, p_trans, HCIT_TYPE_LM_DIAG); + + /* Do not resubmit since it was forwarded to the user */ + goto exit; + +resubmit: + if (unlikely(btusb_submit(p_dev, &p_dev->diag_rx_submitted, p_trans, GFP_ATOMIC))) + p_dev->stats.diag_rx_submit_err++; + else + p_dev->stats.diag_rx_submit_ok++; + +exit: + return; +} + + +/******************************************************************************* + ** + ** Function btusb_event_complete + ** + ** Description Interrupt pipe completion routine + ** + ** Parameters p_urb: URB that completed + ** + ** Returns void + ** + *******************************************************************************/ +static void btusb_event_complete(struct urb *p_urb) +{ + struct btusb_trans *p_trans = p_urb->context; + struct btusb *p_dev = p_trans->p_dev; + int count = p_urb->actual_length; +#if defined (BTUSB_LITE) + BT_HDR *p_buf; +#endif + + BTUSB_DBG("urb %p status %d count %d flags %x\n", p_urb, + p_urb->status, count, p_urb->transfer_flags); + + p_dev->stats.event_complete++; + + /* check if device is disconnecting, has been unplugged or is closing */ + if (unlikely(p_urb->status)) + { + BTUSB_DBG("not queuing URB\n"); + p_dev->stats.event_complete_err++; + goto exit; + } + + /* if there was no data, do not forward to upper layers */ + if (unlikely(!count)) + { + p_dev->stats.event_resubmit++; + goto resubmit; + } + +#ifdef BTUSB_LITE + /* Filter Events received from BT Controller*/ + if (btusb_lite_hci_event_filter(p_dev, p_trans->dma_buffer, count) == 0) + { + p_buf = &p_trans->bt_hdr; + p_buf->offset = 0; + p_buf->event = HCIT_TYPE_EVENT; + p_buf->len = p_trans->p_urb->actual_length; + btusb_rx_dequeued(p_dev, p_buf); + } + else +#endif + { + /* increment the number of bytes transferred */ + p_dev->stats.event_bytes += count; + + /* forward up the event */ + btusb_dump_data(p_urb->transfer_buffer, count, "btusb_event_complete"); + btusb_rx_enqueue(p_dev, p_trans, HCIT_TYPE_EVENT); + } + + /* Do not resubmit since it was forwarded to the user */ + goto exit; + +resubmit: + if (unlikely(btusb_submit(p_dev, &p_dev->event_submitted, p_trans, GFP_ATOMIC))) + p_dev->stats.event_submit_err++; + else + p_dev->stats.event_submit_ok++; + +exit: + return; +} + +/******************************************************************************* + ** + ** Function btusb_probe + ** + ** Description Device probe callback + ** assuming the following USB device configuration : + ** + ** NumInterfaces = 4 + ** Interface 0 Alt settings 0 + ** 3 end point excluding end point 0 + ** 1 interrupt end point -> event + ** 2 bulk for acl in and out + ** Interface 1 Alt settings 0 + ** 2 Iso end point excluding end point 0 + ** Interface 1 Alt settings 1 + ** 2 Iso end point excluding end point 0 + ** Interface 1 Alt settings 2 + ** 2 Iso end point excluding end point 0 + ** Interface 1 Alt settings 3 + ** 2 Iso end point excluding end point 0 + ** Interface 1 Alt settings 4 + ** 2 Iso end point excluding end point 0 + ** Interface 1 Alt settings 5 + ** 2 Iso end point excluding end point 0 + ** Interface 2 Alt settings 0 + ** 2 bulk end point for diags + ** Interface 3 Alt settings 0 + ** 0 end point point i.e. only the control endpoint is used + ** Parameters + ** + ** Returns 0 upon success, error core else. + ** + *******************************************************************************/ +static int btusb_probe(struct usb_interface *p_interface, const struct usb_device_id *p_id) +{ + struct btusb *p_dev; + struct usb_ctrlrequest *p_dr; + struct usb_host_interface *p_host_intf; + struct usb_endpoint_descriptor *p_ep_desc; + struct urb *p_urb; + int idx, jdx; + struct btusb_trans *p_trans; + int retval = -ENOMEM; + + GKI_init(); + + BTUSB_INFO("p_interface=%p, p_id=%p\n", p_interface, p_id); + BTUSB_INFO("p_interface->cur_altsetting->desc.bInterfaceNumber=%d\n", p_interface->cur_altsetting->desc.bInterfaceNumber); + BTUSB_DBG("match=0x%x VID=0x%x PID=0x%x class=0x%x subclass=0x%x protocol=0x%x\n", + p_id->match_flags, p_id->idVendor, p_id->idProduct, p_id->bDeviceClass, p_id->bDeviceSubClass, + p_id->bDeviceProtocol); + + /* Bluetooth core Interface number must be 0 (hardcoded in the spec) */ + if (p_interface->cur_altsetting->desc.bInterfaceNumber != 0) + { + BTUSB_INFO("InterfaceNumber is not 0. This is not a Bluetooth Interface\n"); + return -ENODEV; + } + + /* allocate memory for our device */ + p_dev = kzalloc(sizeof(struct btusb), GFP_KERNEL); + if (p_dev == NULL) + { + dev_err(&p_interface->dev, "Out of memory\n"); + goto error; + } + BTUSB_DBG("allocated p_dev=%p\n", p_dev); + + if (btusb_create(p_dev, p_interface, p_id)) + { + goto error; + } + + /* set up the endpoint information + use only the first bulk-in and bulk-out endpoints */ + p_host_intf = p_interface->cur_altsetting; + BTUSB_DBG("%u endpoints\n", p_host_intf->desc.bNumEndpoints); + + for (idx = 0; idx < p_host_intf->desc.bNumEndpoints; ++idx) + { + p_ep_desc = &p_host_intf->endpoint[idx].desc; + BTUSB_DBG("endpoint addr 0x%x attr 0x%x\n", + p_ep_desc->bEndpointAddress, p_ep_desc->bmAttributes); + if ((!p_dev->p_acl_in) && + BTUSB_EP_DIR_IN(p_ep_desc) && + (BTUSB_EP_TYPE(p_ep_desc) == USB_ENDPOINT_XFER_BULK)) + { + /* we found a bulk in endpoint */ + p_dev->p_acl_in = &p_host_intf->endpoint[idx]; + } + + if ((!p_dev->p_event_in) && + BTUSB_EP_DIR_IN(p_ep_desc) && + (BTUSB_EP_TYPE(p_ep_desc) == USB_ENDPOINT_XFER_INT)) + { + /* we found an interrupt in end point */ + p_dev->p_event_in = &p_host_intf->endpoint[idx]; + } + + if ((!p_dev->p_acl_out) && + BTUSB_EP_DIR_OUT(p_ep_desc) && + (BTUSB_EP_TYPE(p_ep_desc) == USB_ENDPOINT_XFER_BULK)) + { + /* we found a bulk out end point */ + p_dev->p_acl_out = &p_host_intf->endpoint[idx]; + } + } + + /* now start looking at the voice interface (check if it a BT interface) */ + p_dev->p_voice_intf = usb_ifnum_to_if(p_dev->p_udev, 1); + if (p_dev->p_voice_intf) + { + p_host_intf = &p_dev->p_voice_intf->altsetting[0]; + if (((p_host_intf->desc.bInterfaceClass == USB_CLASS_WIRELESS_CONTROLLER) || + (p_host_intf->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC)) && + (p_host_intf->desc.bInterfaceSubClass == 1) && + (p_host_intf->desc.bInterfaceProtocol == 1)) + { + /* can claim here, but prefer to check there are ISO endpoints */ + } + else + { + BTUSB_INFO("Interface 1 is not VOICE\n"); + goto no_voice; + } + for (idx = 0; idx < p_dev->p_voice_intf->num_altsetting; idx++) + { + p_host_intf = &p_dev->p_voice_intf->altsetting[idx]; + for (jdx = 0; jdx < p_host_intf->desc.bNumEndpoints; jdx++) + { + p_ep_desc = &p_host_intf->endpoint[jdx].desc; + if (BTUSB_EP_TYPE(p_ep_desc) == USB_ENDPOINT_XFER_ISOC) + { + /* found an ISO endpoint, claim interface and stop here */ + if (usb_driver_claim_interface(&btusb_driver, p_dev->p_voice_intf, p_dev) != 0) + { + BTUSB_ERR("failed claiming iso interface\n"); + /* reset it to prevent releasing it */ + p_dev->p_voice_intf = NULL; + goto error_claim; + } + BTUSB_DBG("claimed iso interface\n"); + + /* set it into a disabled state */ + if (usb_set_interface(p_dev->p_udev, 1, 0)) + { + BTUSB_ERR("failed to set iso intf to 0\n"); + } + goto found_voice; + } + } + } + } +no_voice: + p_dev->p_voice_intf = NULL; +found_voice: + + /* grab other interfaces if present */ + p_dev->p_diag_intf = usb_ifnum_to_if(p_dev->p_udev, 2); + if (p_dev->p_diag_intf) + { + p_host_intf = &p_dev->p_diag_intf->altsetting[0]; + if ((p_host_intf->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) && + (p_host_intf->desc.bInterfaceSubClass == 255) && + (p_host_intf->desc.bInterfaceProtocol == 255)) + { + if (usb_driver_claim_interface(&btusb_driver, p_dev->p_diag_intf, p_dev) != 0) + { + BTUSB_ERR("failed claiming diag interface\n"); + p_dev->p_diag_intf = NULL; + goto no_diag; + } + } + else + { + BTUSB_INFO("Interface 2 is not DIAG\n"); + p_dev->p_diag_intf = NULL; + goto no_diag; + } + + BTUSB_DBG("claimed diag interface bNumEndpoints %d\n", p_host_intf->desc.bNumEndpoints); + for (idx = 0; idx < p_host_intf->desc.bNumEndpoints; ++idx) + { + p_ep_desc = &p_host_intf->endpoint[idx].desc; + BTUSB_DBG("diag endpoint addr 0x%x attr 0x%x\n", + p_ep_desc->bEndpointAddress, p_ep_desc->bmAttributes); + if ((!p_dev->p_diag_in) && + BTUSB_EP_DIR_IN(p_ep_desc) && + (BTUSB_EP_TYPE(p_ep_desc) == USB_ENDPOINT_XFER_BULK)) + { + /* we found a bulk in end point */ + p_dev->p_diag_in = &p_host_intf->endpoint[idx]; + } + if ((!p_dev->p_diag_out) && + BTUSB_EP_DIR_OUT(p_ep_desc) && + (BTUSB_EP_TYPE(p_ep_desc) == USB_ENDPOINT_XFER_BULK)) + { + /* we found a bulk out end point */ + p_dev->p_diag_out = &p_host_intf->endpoint[idx]; + } + } + } +no_diag: + + /* try to get the DFU interface */ + p_dev->p_dfu_intf = usb_ifnum_to_if(p_dev->p_udev, 3); + if (p_dev->p_dfu_intf) + { + p_host_intf = &p_dev->p_dfu_intf->altsetting[0]; + if ((p_host_intf->desc.bInterfaceClass == USB_CLASS_APP_SPEC) && + (p_host_intf->desc.bInterfaceSubClass == 1)) + { + if (usb_driver_claim_interface(&btusb_driver, p_dev->p_dfu_intf, p_dev) != 0) + { + BTUSB_ERR("failed claiming dfu interface\n"); + p_dev->p_dfu_intf = NULL; + goto no_dfu; + } + BTUSB_DBG("claimed DFU interface bNumEndpoints %d\n", p_dev->p_dfu_intf->cur_altsetting->desc.bNumEndpoints); + } + else + { + BTUSB_INFO("Interface 3 is not DFU\n"); + p_dev->p_dfu_intf = NULL; + goto no_dfu; + } + } +no_dfu: + + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->cmd_array) + { + p_trans->gki_hdr.status = BUF_STATUS_FREE; +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + /* write the magic number to allow checking corruption */ + p_trans->gki_hdr.q_id = GKI_NUM_TOTAL_BUF_POOLS; + p_trans->magic = MAGIC_NO; +#endif + p_trans->p_dev = p_dev; + p_trans->complete = btusb_cmd_complete; + p_trans->dma_buffer = BTUSB_BUFFER_ALLOC(p_dev->p_udev, + BTUSB_HCI_MAX_CMD_SIZE, GFP_KERNEL, &p_trans->dma); + p_urb = p_trans->p_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!p_trans->dma_buffer || !p_urb) + { + BTUSB_ERR("transaction allocation failed\n"); + goto error_claim; + } + BTUSB_DBG("cmd_array[%d]: b=%p\n", idx, p_trans->dma_buffer); + p_dr = &p_dev->cmd_req_array[idx]; + p_dr->bRequestType = USB_TYPE_CLASS; + p_dr->bRequest = 0; + p_dr->wIndex = 0; + p_dr->wValue = 0; + p_dr->wLength = 0; + + usb_fill_control_urb(p_urb, + p_dev->p_udev, + usb_sndctrlpipe(p_dev->p_udev, 0), + (void *)p_dr, + p_trans->dma_buffer, + BTUSB_HCI_MAX_CMD_SIZE, + btusb_urb_out_complete, + p_trans); + p_urb->transfer_dma = p_trans->dma; + p_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } + + /* initialize the USB data paths */ + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->event_array) + { + p_trans->gki_hdr.status = BUF_STATUS_UNLINKED; +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + /* write the magic number to allow checking corruption */ + p_trans->gki_hdr.q_id = GKI_NUM_TOTAL_BUF_POOLS; + p_trans->magic = MAGIC_NO; +#endif + p_trans->p_dev = p_dev; + p_trans->complete = NULL; + p_trans->dma_buffer = BTUSB_BUFFER_ALLOC(p_dev->p_udev, + BTUSB_HCI_MAX_EVT_SIZE, GFP_KERNEL, &p_trans->dma); + p_urb = p_trans->p_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!p_trans->dma_buffer || !p_urb) + { + BTUSB_ERR("transaction allocation failed\n"); + goto error_claim; + } + BTUSB_DBG("event_array[%d]: b=%p\n", idx, p_trans->dma_buffer); + usb_fill_int_urb(p_urb, + p_dev->p_udev, + usb_rcvintpipe(p_dev->p_udev, p_dev->p_event_in->desc.bEndpointAddress), + p_trans->dma_buffer, + BTUSB_HCI_MAX_EVT_SIZE, + btusb_event_complete, + p_trans, + p_dev->p_event_in->desc.bInterval); + p_urb->transfer_dma = p_trans->dma; + p_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } + + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->acl_rx_array) + { + p_trans->gki_hdr.status = BUF_STATUS_UNLINKED; +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + /* write the magic number to allow checking corruption */ + p_trans->gki_hdr.q_id = GKI_NUM_TOTAL_BUF_POOLS; + p_trans->magic = MAGIC_NO; +#endif + p_trans->p_dev = p_dev; + p_trans->complete = NULL; + p_trans->dma_buffer = BTUSB_BUFFER_ALLOC(p_dev->p_udev, + BTUSB_HCI_MAX_ACL_SIZE, GFP_KERNEL, &p_trans->dma); + p_urb = p_trans->p_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!p_trans->dma_buffer || !p_urb) + { + BTUSB_ERR("transaction allocation failed\n"); + goto error_claim; + } + BTUSB_DBG("acl_rx_array[%d]: b=%p\n", idx, p_trans->dma_buffer); + usb_fill_bulk_urb(p_urb, + p_dev->p_udev, + usb_rcvbulkpipe(p_dev->p_udev, p_dev->p_acl_in->desc.bEndpointAddress), + p_trans->dma_buffer, + BTUSB_HCI_MAX_ACL_SIZE, + btusb_acl_read_complete, + p_trans); + p_urb->transfer_dma = p_trans->dma; + p_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } + + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->acl_tx_array) + { + p_trans->gki_hdr.status = BUF_STATUS_FREE; +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + /* write the magic number to allow checking corruption */ + p_trans->gki_hdr.q_id = GKI_NUM_TOTAL_BUF_POOLS; + p_trans->magic = MAGIC_NO; +#endif + p_trans->p_dev = p_dev; + p_trans->complete = NULL; + p_trans->dma_buffer = BTUSB_BUFFER_ALLOC(p_dev->p_udev, + BTUSB_HCI_MAX_ACL_SIZE, GFP_KERNEL, &p_trans->dma); + p_urb = p_trans->p_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!p_trans->dma_buffer || !p_urb) + { + BTUSB_ERR("transaction allocation failed\n"); + goto error_claim; + } + BTUSB_DBG("acl_tx_array[%d]: b=%p\n", idx, p_trans->dma_buffer); + usb_fill_bulk_urb(p_urb, + p_dev->p_udev, + usb_sndbulkpipe(p_dev->p_udev, p_dev->p_acl_out->desc.bEndpointAddress), + p_trans->dma_buffer, + BTUSB_HCI_MAX_ACL_SIZE, + btusb_urb_out_complete, + p_trans); + p_urb->transfer_dma = p_trans->dma; + p_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + /* if it is a composite device, BULK out transfers must be ZERO packet terminated */ + if (p_dev->quirks & BTUSB_QUIRK_ZLP_TX_REQ) + { + BTUSB_DBG("acl_tx_array[%d]: add ZERO_PACKET\n", idx); + p_urb->transfer_flags |= URB_ZERO_PACKET; + } + } + + if (p_dev->p_diag_in) + { + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->diag_rx_array) + { + p_trans->gki_hdr.status = BUF_STATUS_UNLINKED; +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + /* write the magic number to allow checking corruption */ + p_trans->gki_hdr.q_id = GKI_NUM_TOTAL_BUF_POOLS; + p_trans->magic = MAGIC_NO; +#endif + p_trans->p_dev = p_dev; + p_trans->complete = NULL; + p_trans->dma_buffer = BTUSB_BUFFER_ALLOC(p_dev->p_udev, + BTUSB_HCI_MAX_ACL_SIZE, GFP_KERNEL, &p_trans->dma); + p_urb = p_trans->p_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!p_trans->dma_buffer || !p_urb) + { + BTUSB_ERR("transaction allocation failed\n"); + goto error_claim; + } + BTUSB_DBG("diag_rx_array[%d]: b=%p\n", idx, p_trans->dma_buffer); + usb_fill_bulk_urb(p_urb, + p_dev->p_udev, + usb_rcvbulkpipe(p_dev->p_udev, p_dev->p_diag_in->desc.bEndpointAddress), + p_trans->dma_buffer, + BTUSB_HCI_MAX_ACL_SIZE, + btusb_diag_read_complete, + p_trans); + p_urb->transfer_dma = p_trans->dma; + p_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } + } + + if (p_dev->p_diag_out) + { + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->diag_tx_array) + { + p_trans->gki_hdr.status = BUF_STATUS_FREE; +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + /* write the magic number to allow checking corruption */ + p_trans->gki_hdr.q_id = GKI_NUM_TOTAL_BUF_POOLS; + p_trans->magic = MAGIC_NO; +#endif + p_trans->p_dev = p_dev; + p_trans->complete = NULL; + p_trans->dma_buffer = BTUSB_BUFFER_ALLOC(p_dev->p_udev, + BTUSB_HCI_MAX_ACL_SIZE, GFP_KERNEL, &p_trans->dma); + p_urb = p_trans->p_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!p_trans->dma_buffer || !p_urb) + { + BTUSB_ERR("transaction allocation failed\n"); + goto error_claim; + } + BTUSB_DBG("diag_tx_array[%d]: b=%p\n", idx, p_trans->dma_buffer); + usb_fill_bulk_urb(p_urb, + p_dev->p_udev, + usb_sndbulkpipe(p_dev->p_udev, p_dev->p_diag_out->desc.bEndpointAddress), + p_trans->dma_buffer, + BTUSB_HCI_MAX_ACL_SIZE, + btusb_urb_out_complete, + p_trans); + p_urb->transfer_dma = p_trans->dma; + p_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + } + } + + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->voice_rx_array) + { + p_trans->gki_hdr.status = BUF_STATUS_FREE; +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + /* write the magic number to allow checking corruption */ + p_trans->gki_hdr.q_id = GKI_NUM_TOTAL_BUF_POOLS; + p_trans->magic = MAGIC_NO; +#endif + p_trans->p_dev = p_dev; + p_trans->complete = NULL; + p_trans->dma_buffer = BTUSB_BUFFER_ALLOC(p_dev->p_udev, + BTUSB_VOICE_BUFFER_MAXSIZE, GFP_KERNEL, &p_trans->dma); + p_urb = p_trans->p_urb = usb_alloc_urb(BTUSB_VOICE_FRAMES_PER_URB, GFP_KERNEL); + if (!p_trans->dma_buffer || !p_urb) + { + BTUSB_ERR("transaction allocation failed\n"); + goto error_claim; + } + BTUSB_DBG("voice_rx_array[%d]: b=%p\n", idx, p_trans->dma_buffer); + + p_urb->dev = p_dev->p_udev; + p_urb->transfer_buffer = p_trans->dma_buffer; + p_urb->transfer_buffer_length = BTUSB_VOICE_BUFFER_MAXSIZE; + p_urb->complete = btusb_voicerx_complete; + p_urb->context = p_trans; + p_urb->transfer_dma = p_trans->dma; + p_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP | URB_ISO_ASAP; + } + + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->voice_tx_array) + { + p_trans->gki_hdr.status = BUF_STATUS_FREE; +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + /* write the magic number to allow checking corruption */ + p_trans->gki_hdr.q_id = GKI_NUM_TOTAL_BUF_POOLS; + p_trans->magic = MAGIC_NO; +#endif + p_trans->p_dev = p_dev; + p_trans->complete = btusb_voicetx_complete; + p_trans->dma_buffer = BTUSB_BUFFER_ALLOC(p_dev->p_udev, + BTUSB_VOICE_BUFFER_MAXSIZE, GFP_KERNEL, &p_trans->dma); + p_urb = p_trans->p_urb = usb_alloc_urb(BTUSB_VOICE_FRAMES_PER_URB, GFP_KERNEL); + if (!p_trans->dma_buffer || !p_urb) + { + BTUSB_ERR("transaction allocation failed\n"); + goto error_claim; + } + BTUSB_DBG("voice_tx_array[%d]: b=%p\n", idx, p_trans->dma_buffer); + + p_urb->dev = p_dev->p_udev; + p_urb->transfer_buffer = p_trans->dma_buffer; + p_urb->transfer_buffer_length = BTUSB_VOICE_BUFFER_MAXSIZE; + p_urb->complete = btusb_urb_out_complete; + p_urb->context = p_trans; + p_urb->transfer_dma = p_trans->dma; + p_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP | URB_ISO_ASAP; + } + + /* we can register the device now, as it is ready */ + usb_set_intfdata(p_interface, p_dev); + retval = usb_register_dev(p_interface, &btusb_class); + if (retval) + { + /* something prevented us from registering this driver */ + BTUSB_ERR("usb_register_dev failed: %d\n", retval); + goto error_claim; + } + + /* start sending IN tokens */ + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->event_array) + { + if (btusb_submit(p_dev, &p_dev->event_submitted, p_trans, GFP_KERNEL)) + { + p_dev->stats.event_submit_err++; + BTUSB_ERR("btusb_submit(event) failed\n"); + goto error_bt_submit; + } + p_dev->stats.event_submit_ok++; + } + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->acl_rx_array) + { + if (btusb_submit(p_dev, &p_dev->acl_rx_submitted, p_trans, GFP_KERNEL)) + { + p_dev->stats.acl_rx_submit_err++; + BTUSB_ERR("btusb_submit(acl_rx) failed\n"); + goto error_bt_submit; + } + p_dev->stats.acl_rx_submit_ok++; + } + if (p_dev->p_diag_in) + { + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->diag_rx_array) + { + if (btusb_submit(p_dev, &p_dev->diag_rx_submitted, p_trans, GFP_KERNEL)) + { + p_dev->stats.diag_rx_submit_err++; + BTUSB_ERR("btusb_submit(diag) failed\n"); + goto error_bt_submit; + } + p_dev->stats.diag_rx_submit_ok++; + } + } + + /* Create the proc filesystem entry. To make it easy to retrieve, the + * entry created is called /proc/driver/btusb/btusbN where btusbN matches the + * file entry in /dev. + * The name of the device in /dev comes from the device created when + * usb_register_dev was called (in the current fn). The name was built + * from the btusb_class structure. usb_register_dev calls device_create + * which creates the VFS entry. This can be found in online linux code. + */ + btusb_proc_add(p_dev, kobject_name(&p_interface->usb_dev->kobj)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + if (autopm) + { + BTUSB_INFO("Enabling for remote wakeup\n"); + p_interface->needs_remote_wakeup = 1; + + BTUSB_INFO("Setting power/wakeup to enabled\n"); + device_set_wakeup_enable(&p_dev->p_udev->dev, 1); + + BTUSB_INFO("Enabling for autosuspend\n"); + usb_enable_autosuspend(p_dev->p_udev); + } +#else + autopm = 0; + BTUSB_INFO("USB suspend/resume not supported for this kernel version\n"); +#endif + +#ifdef BTUSB_LITE + /* must be called last because it uses the p_dev entries */ + btusb_lite_create(p_dev, p_interface); +#endif + + /* let the user know what node this device is now attached to */ + BTUSB_DBG("device now attached to minor %d\n", p_interface->minor); + + return 0; + +error_bt_submit: + usb_kill_anchored_urbs(&p_dev->acl_rx_submitted); + usb_kill_anchored_urbs(&p_dev->acl_tx_submitted); + usb_kill_anchored_urbs(&p_dev->diag_rx_submitted); + usb_kill_anchored_urbs(&p_dev->diag_tx_submitted); + usb_kill_anchored_urbs(&p_dev->event_submitted); + usb_kill_anchored_urbs(&p_dev->cmd_submitted); + usb_kill_anchored_urbs(&p_dev->voice_rx_submitted); + usb_kill_anchored_urbs(&p_dev->voice_tx_submitted); + + usb_deregister_dev(p_interface, &btusb_class); + +error_claim: + usb_set_intfdata(p_interface, NULL); + if (p_dev->p_diag_intf) + { + usb_driver_release_interface(&btusb_driver, p_dev->p_diag_intf); + p_dev->p_diag_intf = NULL; + BTUSB_DBG("released diag interface\n"); + } + if (p_dev->p_dfu_intf) + { + usb_driver_release_interface(&btusb_driver, p_dev->p_dfu_intf); + p_dev->p_dfu_intf = NULL; + BTUSB_DBG("released dfu interface\n"); + } + if (p_dev->p_voice_intf) + { + usb_driver_release_interface(&btusb_driver, p_dev->p_voice_intf); + p_dev->p_voice_intf = NULL; + BTUSB_DBG("released iso interface\n"); + } + +error: + if (p_dev) + { + kref_put(&p_dev->kref, btusb_delete); + } + return retval; +} + + +/******************************************************************************* + ** + ** Function btusb_cancel_voice + ** + ** Description Cancel all pending voice requests and wait for their cancellations + ** + ** Parameters p_dev: device instance control block + ** + ** Returns void. + ** + *******************************************************************************/ +void btusb_cancel_voice(struct btusb *p_dev) +{ + int idx; + struct btusb_voice_pkt *p_pkt; + + BTUSB_DBG("enter\n"); + + /* cancel rx */ + BTUSB_DBG("Removing SCO RX anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->voice_rx_submitted); + + /* since all URB have been killed, safely reset the state variables */ + p_dev->voice_rx.remaining = 0; + p_dev->voice_rx.hdr_size = 0; + p_dev->voice_rx.pp_pkt = NULL; + + /* free any SCO HCI message being consolidated on any channel */ + for (idx = 0; idx < ARRAY_SIZE(p_dev->voice_rx.channels); idx++) + { + p_pkt = p_dev->voice_rx.channels[idx].p_pkt; + /* mark the packet bad (length 0) and put it in the reception queue + because putting it back in the free list would require protecting + the access to the write counter between read and ioctl: + btusb_cq_put(&p_dev->voice_rx_list, *p_dev->voice_channels[idx].p_pkt) */ + if (p_pkt) + { + p_pkt->bt_hdr.len = 0; + btusb_rx_enqueue_voice(p_dev, p_pkt); + } + p_dev->voice_rx.channels[idx].p_pkt = NULL; + } + + /* cancel tx */ + BTUSB_DBG("Removing SCO RX anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->voice_tx_submitted); + atomic_set(&p_dev->voice_tx_active, 0); +} + +/******************************************************************************* + ** + ** Function btusb_cancel_urbs + ** + ** Description Cancel all pending non-voice requests and wait for their cancellations + ** + ** Parameters pointer on usb_bt struck that contains all the btusb info + ** + ** Returns void. + ** + *******************************************************************************/ +void btusb_cancel_urbs(struct btusb *p_dev) +{ + BTUSB_DBG("enter\n"); + + /* stop reading data */ + BTUSB_DBG("Removing ACL RX anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->acl_rx_submitted); + + /* stop reading events */ + BTUSB_DBG("Removing EVENT anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->event_submitted); + + /* stop reading diags */ + BTUSB_DBG("Removing DIAG anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->diag_rx_submitted); + + /* cancel ACL writes */ + BTUSB_DBG("Removing ACL TX anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->acl_tx_submitted); + + /* cancel commands */ + BTUSB_DBG("Removing CMD anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->cmd_submitted); + + /* cancel diags */ + BTUSB_DBG("Removing DIAG TX anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->diag_tx_submitted); + +} + +/******************************************************************************* + ** + ** Function btusb_disconnect + ** + ** Description + ** + ** Parameters p_interface: first interface being disconnected + ** + ** Returns void. + ** + *******************************************************************************/ +static void btusb_disconnect(struct usb_interface *p_interface) +{ + struct btusb *p_dev = usb_get_intfdata(p_interface); + + BTUSB_INFO("p_dev=%p\n", p_dev); + if (!p_dev) + { + BTUSB_ERR("p_dev == NULL\n"); + return; + } + + if (p_interface != p_dev->p_main_intf) + { + BTUSB_DBG("not the main interface\n"); + return; + } + + /* clear the interface data information */ + usb_set_intfdata(p_interface, NULL); + +#ifdef BTUSB_LITE + btusb_lite_stop_all(p_dev); + btusb_lite_delete(p_dev, p_interface); +#endif + + btusb_proc_remove(p_dev, p_interface->usb_dev->kobj.name); + + BTUSB_DBG("shutting down HCI intf\n"); + + /* prevent more I/O from starting */ + p_dev->p_main_intf = NULL; + + /* cancel pending requests */ + btusb_cancel_voice(p_dev); + btusb_cancel_urbs(p_dev); + + /* give back our minor */ + BTUSB_DBG("unregistering #%d\n", p_interface->minor); + usb_deregister_dev(p_interface, &btusb_class); + + /* release interfaces */ + if (p_dev->p_diag_intf) + { + usb_driver_release_interface(&btusb_driver, p_dev->p_diag_intf); + p_dev->p_diag_intf = NULL; + BTUSB_DBG("released diag interface\n"); + } + if (p_dev->p_dfu_intf) + { + usb_driver_release_interface(&btusb_driver, p_dev->p_dfu_intf); + p_dev->p_dfu_intf = NULL; + BTUSB_DBG("released dfu interface\n"); + } + if (p_dev->p_voice_intf) + { + usb_driver_release_interface(&btusb_driver, p_dev->p_voice_intf); + p_dev->p_voice_intf = NULL; + BTUSB_DBG("released iso interface\n"); + } + + if (autopm) + { + BTUSB_INFO("Disabling for remote wakeup\n"); + p_dev->p_main_intf->needs_remote_wakeup = 0; + + BTUSB_INFO("Setting power/wakeup to disabled\n"); + device_set_wakeup_enable(&p_dev->p_udev->dev, 0); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + BTUSB_INFO("Disabling autosuspend\n"); + usb_disable_autosuspend(p_dev->p_udev); +#endif + } + + /* decrement the reference counter */ + BTUSB_DBG("kref_put -> &p_dev->kref 0x%p \n", &p_dev->kref); + kref_put(&p_dev->kref, btusb_delete); +} + +/******************************************************************************* + ** + ** Function btusb_suspend + ** + ** Description + ** + ** Parameters p_interface: first interface being suspended + ** + ** Returns void. + ** + *******************************************************************************/ +static int btusb_suspend(struct usb_interface *p_interface, pm_message_t message) +{ + struct btusb *p_dev = usb_get_intfdata(p_interface); + + if (unlikely(!p_dev)) + return 0; + + if (p_dev->p_main_intf == p_interface) + { + BTUSB_INFO("Suspending USB\n"); + BTUSB_DBG("main interface (%p)\n",p_dev->p_main_intf); + + /* stop reading data */ + BTUSB_DBG("Removing ACL RX anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->acl_rx_submitted); + + /* stop reading events */ + BTUSB_DBG("Removing EVENT anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->event_submitted); + + /* cancel ACL writes */ + BTUSB_DBG("Removing ACL TX anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->acl_tx_submitted); + + /* cancel commands */ + BTUSB_DBG("Removing CMD anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->cmd_submitted); + + } + if (p_dev->p_voice_intf == p_interface) + { + BTUSB_DBG("voice interface (%p)\n",p_dev->p_voice_intf); + } + if (p_dev->p_dfu_intf == p_interface) + { + BTUSB_DBG("dfu interface (%p)\n",p_dev->p_dfu_intf); + } + if (p_dev->p_diag_intf == p_interface) + { + BTUSB_DBG("diag interface (%p)\n",p_dev->p_diag_intf); + + /* stop reading diags */ + BTUSB_DBG("Removing DIAG RX anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->diag_rx_submitted); + + /* cancel diags */ + BTUSB_DBG("Removing DIAG TX anchored URBs\n"); + usb_kill_anchored_urbs(&p_dev->diag_tx_submitted); + } + + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_resume + ** + ** Description + ** + ** Parameters p_interface: first interface being resumed + ** + ** Returns void. + ** + *******************************************************************************/ +static int btusb_resume(struct usb_interface *p_interface) +{ + struct btusb *p_dev = usb_get_intfdata(p_interface); + int idx; + int retval = -ENOMEM; + struct btusb_trans *p_trans; + + BTUSB_INFO("p_dev=%p\n", p_dev); + if (unlikely(p_dev == NULL)) + { + BTUSB_ERR("p_dev == NULL\n"); + return retval; + } + + if (p_dev->p_main_intf == p_interface) + { + BTUSB_DBG("main interface (%p)\n",p_dev->p_main_intf); + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->event_array) + { + if (likely(p_trans->dma_buffer != NULL)) + { + if (unlikely(btusb_submit(p_dev, &p_dev->event_submitted, p_trans, GFP_KERNEL))) + { + p_dev->stats.event_submit_err++; + BTUSB_ERR("btusb_submit(event) failed\n"); + goto error_bt_submit; + } + p_dev->stats.event_submit_ok++; + } + } + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->acl_rx_array) + { + if (likely(p_trans->dma_buffer != NULL)) + { + if (unlikely(btusb_submit(p_dev, &p_dev->acl_rx_submitted, p_trans, GFP_KERNEL))) + { + p_dev->stats.acl_rx_submit_err++; + BTUSB_ERR("btusb_submit(acl_rx) failed\n"); + goto error_bt_submit; + } + p_dev->stats.acl_rx_submit_ok++; + } + } + } + if (p_dev->p_voice_intf == p_interface) + { + BTUSB_DBG("voice interface (%p)\n",p_dev->p_voice_intf); + } + if (p_dev->p_dfu_intf == p_interface) + { + BTUSB_DBG("dfu interface (%p)\n",p_dev->p_dfu_intf); + } + if (p_dev->p_diag_intf == p_interface) + { + BTUSB_DBG("diag interface (%p)\n",p_dev->p_diag_intf); + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->diag_rx_array) + { + if (likely(p_trans->dma_buffer != NULL)) + { + if (unlikely(btusb_submit(p_dev, &p_dev->diag_rx_submitted, p_trans, GFP_KERNEL))) + { + p_dev->stats.diag_rx_submit_err++; + BTUSB_ERR("btusb_submit(diag) failed\n"); + goto error_bt_submit; + } + p_dev->stats.diag_rx_submit_ok++; + } + } + } + + return 0; + +error_bt_submit: + usb_kill_anchored_urbs(&p_dev->acl_rx_submitted); + usb_kill_anchored_urbs(&p_dev->diag_rx_submitted); + usb_kill_anchored_urbs(&p_dev->event_submitted); + + usb_deregister_dev(p_interface, &btusb_class); + return retval; +} + + +/******************************************************************************* + ** + ** Function btusb_reset_resume + ** + ** Description + ** + ** Parameters p_interface: first interface being resumed + ** + ** Returns void. + ** + *******************************************************************************/ +static int btusb_reset_resume(struct usb_interface *p_interface) +{ + BTUSB_DBG("enter\n"); + + return btusb_resume(p_interface); +} + + +struct usb_driver btusb_driver = +{ + .name = "btusb", + .id_table = btusb_table, + .probe = btusb_probe, + .disconnect = btusb_disconnect, + .suspend = btusb_suspend, + .resume = btusb_resume, + .reset_resume = btusb_reset_resume, + .supports_autosuspend = 1 +}; + +/******************************************************************************* + ** + ** Function btusb_create + ** + ** Description Initialize the control block + ** + ** Returns 0 upon success, error core else. + ** + *******************************************************************************/ +static int btusb_create(struct btusb *p_dev, struct usb_interface *p_interface, const struct usb_device_id *p_id) +{ + unsigned idx; + struct usb_host_config *p_config; + struct usb_host_interface *p_host_intf; + struct btusb_voice_pkt *p_pkt; + + /* initialize the elements of the device structure */ + kref_init(&p_dev->kref); + GKI_init_q(&p_dev->tx_queue); + GKI_init_q(&p_dev->rx_queue); + init_waitqueue_head(&p_dev->rx_wait_q); + init_usb_anchor(&p_dev->acl_rx_submitted); + init_usb_anchor(&p_dev->event_submitted); + init_usb_anchor(&p_dev->diag_rx_submitted); + init_usb_anchor(&p_dev->acl_tx_submitted); + init_usb_anchor(&p_dev->cmd_submitted); + init_usb_anchor(&p_dev->diag_tx_submitted); + init_usb_anchor(&p_dev->voice_rx_submitted); + init_usb_anchor(&p_dev->voice_tx_submitted); + atomic_set(&p_dev->voice_tx_active, 0); + INIT_LIST_HEAD(&p_dev->scosniff_list); + init_completion(&p_dev->scosniff_completion); + + + /* save the USB information */ + p_dev->p_udev = usb_get_dev(interface_to_usbdev(p_interface)); + p_dev->p_id = p_id; + p_dev->p_main_intf = p_interface; + + /* register tx task so we can start sending data to transport */ + tasklet_init(&p_dev->tx_task, btusb_tx_task, (unsigned long)p_dev); + BTUSB_DBG("tasklet_init complete\n"); + + INIT_BTUSB_CQ(p_dev->voice_rx_list); + for (idx = 0; idx < ARRAY_SIZE(p_dev->voice_rx_pkts); idx++) + { + p_pkt = &p_dev->voice_rx_pkts[idx]; +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + /* write the magic number to allow checking corruption */ + p_pkt->gki_hdr.q_id = GKI_NUM_TOTAL_BUF_POOLS + 1; + p_pkt->magic = MAGIC_NO; +#endif + p_pkt->gki_hdr.status = BUF_STATUS_UNLINKED; + p_pkt->bt_hdr.event = HCIT_TYPE_SCO_DATA; + + if (!btusb_cq_put(&p_dev->voice_rx_list, p_pkt)) + { + BTUSB_ERR("btusb_cq_put failed\n"); + } + } + + p_dev->kterm.c_line = N_HCI; + p_dev->kterm.c_iflag = IGNPAR | ICRNL; + p_dev->kterm.c_oflag = 0; + p_dev->kterm.c_cflag = B115200 | CRTSCTS | CS8 | CLOCAL | CREAD; + p_dev->kterm.c_lflag = ICANON; + p_dev->kterm.c_ispeed = p_dev->kterm.c_ospeed = 115200; + p_dev->kterm.c_cc[VEOF] = 4; + p_dev->kterm.c_cc[VMIN] = 1; + + /* check if there is a WiFi interface in the same device */ + p_config = p_dev->p_udev->actconfig; + for (idx = 1; idx < p_config->desc.bNumInterfaces; idx++) + { + p_host_intf = &p_config->interface[idx]->altsetting[0]; + if ((p_host_intf->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) && + (p_host_intf->desc.bInterfaceSubClass == 2) && + (p_host_intf->desc.bInterfaceProtocol == 255)) + { + p_dev->issharedusb = true; + } + } + + /* set quirks */ + if (p_dev->issharedusb) + { + /* all shared USB devices need a ZLP to be sent in TX */ + p_dev->quirks |= BTUSB_QUIRK_ZLP_TX_REQ; + } + + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_delete + ** + ** Description Fully remove the elements for a device + ** + ** Parameters kref: reference counter + ** + ** Returns None + ** + *******************************************************************************/ +void btusb_delete(struct kref *kref) +{ + struct btusb *p_dev = container_of(kref, struct btusb, kref); + struct btusb_trans *p_trans; + BT_HDR *p_buf = NULL; + int idx; + + BTUSB_DBG("enter\n"); + +#ifdef BTUSB_LITE + btusb_lite_stop_all(p_dev); +#endif + + /* stop tx_task and then remove any data packets from tx_q */ + tasklet_kill(&p_dev->tx_task); + + BTUSB_DBG("tasklet_kill complete\n"); + while ((p_buf = (BT_HDR *) GKI_dequeue(&p_dev->tx_queue)) != NULL) + { + GKI_freebuf(p_buf); + } + + BTUSB_DBG("dequeued all remaining buffers from TX Q\n"); + + while ((p_buf = (BT_HDR *) GKI_dequeue(&p_dev->rx_queue)) != NULL) + { + btusb_rx_dequeued(p_dev, p_buf); + } + + if (p_dev->p_write_msg) + { + GKI_freebuf(p_dev->p_write_msg); + } + + BTUSB_DBG("dequeued all remaining buffers from RX Q\n"); + + /* ensure that the process is not hanging on select()/poll() */ + wake_up_interruptible(&p_dev->rx_wait_q); + + /* free allocated USB DMA space */ + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->cmd_array) + { + BTUSB_BUFFER_FREE(p_dev->p_udev, BTUSB_HCI_MAX_CMD_SIZE, + p_trans->dma_buffer, + p_trans->dma); + usb_free_urb(p_trans->p_urb); + } + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->event_array) + { + BTUSB_BUFFER_FREE(p_dev->p_udev, BTUSB_HCI_MAX_EVT_SIZE, + p_trans->dma_buffer, + p_trans->dma); + usb_free_urb(p_trans->p_urb); + } + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->acl_rx_array) + { + BTUSB_BUFFER_FREE(p_dev->p_udev, BTUSB_HCI_MAX_ACL_SIZE, + p_trans->dma_buffer, + p_trans->dma); + usb_free_urb(p_trans->p_urb); + } + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->acl_tx_array) + { + BTUSB_BUFFER_FREE(p_dev->p_udev, BTUSB_HCI_MAX_ACL_SIZE, + p_trans->dma_buffer, + p_trans->dma); + usb_free_urb(p_trans->p_urb); + } + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->diag_rx_array) + { + BTUSB_BUFFER_FREE(p_dev->p_udev, BTUSB_HCI_MAX_ACL_SIZE, + p_trans->dma_buffer, + p_trans->dma); + usb_free_urb(p_trans->p_urb); + } + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->diag_tx_array) + { + BTUSB_BUFFER_FREE(p_dev->p_udev, BTUSB_HCI_MAX_ACL_SIZE, + p_trans->dma_buffer, + p_trans->dma); + usb_free_urb(p_trans->p_urb); + } + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->voice_rx_array) + { + BTUSB_BUFFER_FREE(p_dev->p_udev, BTUSB_VOICE_BUFFER_MAXSIZE, + p_trans->dma_buffer, + p_trans->dma); + usb_free_urb(p_trans->p_urb); + } + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->voice_tx_array) + { + BTUSB_BUFFER_FREE(p_dev->p_udev, BTUSB_VOICE_BUFFER_MAXSIZE, + p_trans->dma_buffer, + p_trans->dma); + usb_free_urb(p_trans->p_urb); + } + usb_put_dev(p_dev->p_udev); + + kfree(p_dev); + + GKI_shutdown(); +} + + +/******************************************************************************* + ** + ** Function btusb_init + ** + ** Description Module init routine + ** + ** Parameters None + ** + ** Returns None + ** + *******************************************************************************/ +static int __init btusb_init(void) +{ + int result; + + BTUSB_DBG("built %s,%s\n", __DATE__, __TIME__); + + btusb_proc_init(); + + /* register this driver with the USB subsystem */ + result = usb_register(&btusb_driver); + if (result) + { + BTUSB_ERR("usb_register failed (%d)\n", result); + } + + return result; +} + +/******************************************************************************* + ** + ** Function btusb_exit + ** + ** Description Module exit routine + ** + ** Parameters None + ** + ** Returns None + ** + *******************************************************************************/ +static void __exit btusb_exit(void) +{ + BTUSB_DBG("enter\n"); + + btusb_proc_exit(); + + /* deregister this driver from the USB subsystem */ + usb_deregister(&btusb_driver); +} + +module_init(btusb_init); +module_exit(btusb_exit); + +module_param(autopm, bool, 0644); +MODULE_PARM_DESC(autopm, "Enable suspend/resume with remote wakeup"); +module_param(dbgflags, int, 0644); +MODULE_PARM_DESC(dbgflags, "Debug flags"); + +MODULE_DESCRIPTION("Broadcom Bluetooth USB driver"); +MODULE_LICENSE("GPL"); + diff --git a/src/btusb_dev.c b/src/btusb_dev.c new file mode 100755 index 0000000..9e24adc --- a/dev/null +++ b/src/btusb_dev.c @@ -0,0 +1,2001 @@ +/* + * + * btusb_dev.c + * + * + * + * Copyright (C) 2011-2014 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * Module that implements the device routines. + * + */ + +#include "btusb.h" +#include <linux/module.h> +#include <linux/poll.h> +#include <linux/slab.h> +#include <linux/serial.h> + +#include <linux/proc_fs.h> +#include <linux/seq_file.h> + +#include "hcidefs.h" + +#include "gki_int.h" + +/* local functions declaration */ +static int btusb_update_voice_channels(struct btusb *p_dev); +static int btusb_update_voice_trans(struct btusb *p_dev); +static void btusb_submit_voice_tx(struct btusb *p_dev, char *p_data, unsigned long lenth); +static int btusb_submit_cmd(struct btusb *p_dev, char *packet, unsigned long length); +static int btusb_submit_acl(struct btusb *p_dev, char *packet, unsigned long length); +static int btusb_submit_diag(struct btusb *p_dev, char *packet, unsigned long length); + +/******************************************************************************* + ** + ** Function btusb_alloc_trans + ** + ** Description Allocate a transaction in the array of transactions. + ** + ** Parameters p_array: array of USB transactions + ** size: size of the array of transactions + ** + ** Returns Pointer to the next free transaction, NULL if not found + ** + *******************************************************************************/ +static struct btusb_trans *btusb_alloc_trans(struct btusb_trans *p_array, size_t size) +{ + int idx; + struct btusb_trans *p_trans; + + for (idx = 0; idx < size; idx++) + { + p_trans = &p_array[idx]; + if (p_trans->gki_hdr.status == BUF_STATUS_FREE) + { + p_trans->gki_hdr.status = BUF_STATUS_UNLINKED; + return p_trans; + } + } + return NULL; +} + +/******************************************************************************* + ** + ** Function btusb_dump_data + ** + ** Description Print the data into the kernel messages + ** + ** Parameters p: pointer to the data to print + ** len: length of the data to print + ** p_title: title to print before the data + ** + ** Returns void + ** + *******************************************************************************/ +void btusb_dump_data(const UINT8 *p, int len, const char *p_title) +{ + int idx; + + if (likely((dbgflags & BTUSB_DBG_MSG) == 0)) + { + return; + } + + if (p_title) + { + printk("---------------------- %s ----------------------\n", p_title); + } + + printk("%p: ", p); + + for(idx = 0; idx < len; idx++) + { + printk("%02x ", p[idx]); + } + printk("\n--------------------------------------------------------------------\n"); +} + +/******************************************************************************* + ** + ** Function btusb_voice_stats + ** + ** Description + ** + ** Parameters + ** + ** Returns void + ** + *******************************************************************************/ +void btusb_voice_stats(unsigned long *p_max, unsigned long *p_min, + struct timeval *p_result, struct timeval *p_last_time) +{ + struct timeval current_time; + + do_gettimeofday(¤t_time); + /* perform the carry for the later subtraction by updating y */ + if (current_time.tv_usec < p_last_time->tv_usec) + { + int nsec = (p_last_time->tv_usec - current_time.tv_usec) / 1000000 + 1; + p_last_time->tv_usec -= 1000000 * nsec; + p_last_time->tv_sec += nsec; + } + if (current_time.tv_usec - p_last_time->tv_usec > 1000000) + { + int nsec = (current_time.tv_usec - p_last_time->tv_usec) / 1000000; + p_last_time->tv_usec += 1000000 * nsec; + p_last_time->tv_sec -= nsec; + } + /* compute the time remaining to wait */ + p_result->tv_sec = current_time.tv_sec - p_last_time->tv_sec; + p_result->tv_usec = current_time.tv_usec - p_last_time->tv_usec; + + /* update the max except the first time where the calculation is wrong + because of the initial value assuming p_last is zero initialized */ + if (p_max != NULL) + { + if (p_result->tv_usec > *p_max && (p_last_time->tv_sec) && (p_last_time->tv_usec)) + { + *p_max = p_result->tv_usec; + } + } + + /* update the min except the first time where the calculation is wrong + because of the initial value assuming *p_last and p_min are zero initialized */ + if (p_min != NULL) + { + if ((p_result->tv_usec < *p_min || (*p_min == 0)) && + (p_last_time->tv_sec) && (p_last_time->tv_usec)) + { + *p_min = p_result->tv_usec; + } + } + + *p_last_time = current_time; + BTUSB_DBG("btusb_voice_stats len: %lu\n", p_result->tv_usec); +} + +/******************************************************************************* + ** + ** Function btusb_tx_task + ** + ** Description host to controller tasklet + ** + ** Parameters arg: + ** + ** Returns void + ** + *******************************************************************************/ +void btusb_tx_task(unsigned long arg) +{ + struct btusb *p_dev = (struct btusb *) arg; + BT_HDR *p_buf; + UINT8 *p_data; + UINT8 msg_type; + + /* dequeue data packet and submit to transport */ + if (unlikely((p_buf = (BT_HDR *) GKI_dequeue(&p_dev->tx_queue)) == NULL)) + { + BTUSB_DBG("dequeued data = NULL - just return\n"); + goto reschedule_task; + } + + /* bypass the header and retrieve the type */ + p_data = (UINT8 *) (p_buf + 1) + p_buf->offset; + + /* extract message type */ + STREAM_TO_UINT8(msg_type, p_data); + switch (msg_type) + { + case HCIT_TYPE_SCO_DATA: + btusb_submit_voice_tx(p_dev, p_data, p_buf->len - 1); + break; + case HCIT_TYPE_COMMAND: +#ifdef BTUSB_LITE + /* Filter User's Write operation to catch some commands(VSCs) */ + if (btusb_lite_hci_cmd_filter(p_dev, p_buf)) +#endif + { + btusb_submit_cmd(p_dev, p_data, p_buf->len - 1); + } + break; + case HCIT_TYPE_ACL_DATA: + btusb_submit_acl(p_dev, p_data, p_buf->len - 1); + break; + case HCIT_TYPE_LM_DIAG: + btusb_submit_diag(p_dev, p_data, p_buf->len - 1); + break; + } + GKI_freebuf(p_buf); + +reschedule_task: + if (p_dev->tx_queue.count) + tasklet_schedule(&p_dev->tx_task); + return; +} + +/******************************************************************************* + ** + ** Function btusb_rx_enqueue + ** + ** Description Add a received USB message to the RX queue + ** + ** Parameters p_dev: device instance control block + ** p_trans: transaction to forward + ** hcitype: type of HCI data in the USB transaction + ** + ** Returns void + ** + *******************************************************************************/ +void btusb_rx_enqueue(struct btusb *p_dev, struct btusb_trans *p_trans, UINT8 hcitype) +{ + BT_HDR *p_buf = &p_trans->bt_hdr; + struct urb *p_urb = p_trans->p_urb; + UINT8 *p = (UINT8 *)p_urb->transfer_buffer; + UINT16 len; + + BTUSB_DBG("len = %d\n", p_urb->actual_length); + + /* calculate the HCI packet length, depending on the type */ + switch (hcitype) + { + case HCIT_TYPE_ACL_DATA: + p += 2; + STREAM_TO_UINT16(len, p); + len += 4; + break; + + case HCIT_TYPE_EVENT: + p += 1; + STREAM_TO_UINT8(len, p); + len += 2; + break; + + case HCIT_TYPE_LM_DIAG: + len = HCIT_LM_DIAG_LENGTH; + break; + + default: + BTUSB_ERR("Unsupported HCI type: %d\n", hcitype); + len = p_urb->actual_length; + break; + } + + /* check HCI packet length matches that of the USB transaction */ + if (unlikely(len != p_urb->actual_length)) + { + /* firmware fix for specific HW that do not correctly support ZLP: + * - 43242 and 43569 shared USB + * - some USB hubs not supporting ZLP packets and requiring a workaround + */ + if (unlikely(((len % 16) == 0) && (p_urb->actual_length == (len + 1)))) + { + BTUSB_DBG("missing ZLP workaround: %d != %d\n", p_urb->actual_length, len); + p_urb->actual_length = len; + } + else + { + BTUSB_ERR("URB data length does not match packet %d != %d\n", p_urb->actual_length, len); + } + } + + /* fill up the header of the message */ + p_buf->event = hcitype; + p_buf->len = p_urb->actual_length; + p_buf->offset = 0; + p_buf->layer_specific &= ~BTUSB_LS_H4_TYPE_SENT; + + /* InQ for user-space to read */ + GKI_enqueue(&p_dev->rx_queue, p_buf); + + /* if the process is polling, indicate RX event */ + wake_up_interruptible(&p_dev->rx_wait_q); +} + +/******************************************************************************* + ** + ** Function btusb_rx_enqueue_voice + ** + ** Description Add a full HCI voice packet to the RX queue + ** + ** Parameters p_dev: device instance control block + ** p_pkt: transaction to forward + ** + ** Returns void + ** + *******************************************************************************/ +void btusb_rx_enqueue_voice(struct btusb *p_dev, struct btusb_voice_pkt *p_pkt) +{ + /* InQ for user-space to read */ + GKI_enqueue(&p_dev->rx_queue, &p_pkt->bt_hdr); + + /* if the process is polling, indicate RX event */ + wake_up_interruptible(&p_dev->rx_wait_q); +} + +/******************************************************************************* + ** + ** Function btusb_rx_dequeued + ** + ** Description Figure out what to do with a dequeued message + ** + ** Parameters p_dev: driver control block + ** p_msg: dequeued message + ** + ** Returns void + ** + *******************************************************************************/ +void btusb_rx_dequeued(struct btusb *p_dev, BT_HDR *p_msg) +{ + struct btusb_trans *p_trans = container_of(p_msg, struct btusb_trans, bt_hdr); + struct btusb_voice_pkt *p_pkt = container_of(p_msg, struct btusb_voice_pkt, bt_hdr); + +#if defined(BTUSB_LITE) + if (unlikely(p_msg->layer_specific & BTUSB_LS_GKI_BUFFER)) + { + GKI_freebuf(p_msg); + return; + } +#endif + + switch (p_msg->event) + { + case HCIT_TYPE_ACL_DATA: + if (unlikely(btusb_submit(p_dev, &p_dev->acl_rx_submitted, p_trans, GFP_KERNEL))) + p_dev->stats.acl_rx_submit_err++; + else + p_dev->stats.acl_rx_submit_ok++; + break; + + case HCIT_TYPE_EVENT: +#if defined(BTUSB_LITE) + /* when BTUSB_LITE is defined, EVT may be resubmitted from IRQ context */ + if (unlikely(btusb_submit(p_dev, &p_dev->event_submitted, p_trans, GFP_ATOMIC))) +#else + if (unlikely(btusb_submit(p_dev, &p_dev->event_submitted, p_trans, GFP_KERNEL))) +#endif + p_dev->stats.event_submit_err++; + else + p_dev->stats.event_submit_ok++; + break; + + case HCIT_TYPE_LM_DIAG: + if (unlikely(btusb_submit(p_dev, &p_dev->diag_rx_submitted, p_trans, GFP_KERNEL))) + p_dev->stats.diag_rx_submit_err++; + else + p_dev->stats.diag_rx_submit_ok++; + break; + + case HCIT_TYPE_SCO_DATA: + if (!btusb_cq_put(&p_dev->voice_rx_list, p_pkt)) + { + BTUSB_ERR("btusb_cq_put failed\n"); + } + break; + default: + BTUSB_ERR("Unexpected buffer type\n"); + break; + } +} + +/******************************************************************************* + ** + ** Function btusb_open + ** + ** Description User mode open callback + ** + *******************************************************************************/ +int btusb_open(struct inode *inode, struct file *file) +{ + struct btusb *p_dev; + struct usb_interface *interface; + int subminor; + int retval = 0; + + BTUSB_INFO("enter\n"); + + /* retrieve the minor for this inode */ + subminor = iminor(inode); + + /* retrieve the USB interface attached to this minor */ + interface = usb_find_interface(&btusb_driver, subminor); + if (!interface) + { + BTUSB_ERR("can't find interface for minor %d\n", subminor); + retval = -ENODEV; + goto exit; + } + BTUSB_INFO("minor %u\n", subminor); + + /* retrieve the device driver structure pointer */ + p_dev = usb_get_intfdata(interface); + BTUSB_INFO("p_dev=%p\n", p_dev); + if (!p_dev) + { + BTUSB_ERR("can't find device\n"); + retval = -ENODEV; + goto exit; + } + + /* save our object in the file's private structure */ + file->private_data = p_dev; + + /* increment our usage count for the device */ + BTUSB_INFO("kref_get -> &p_dev->kref 0x%p\n", &p_dev->kref); + kref_get(&p_dev->kref); + +exit: + return retval; +} + +/******************************************************************************* + ** + ** Function btusb_release + ** + ** Description User mode close callback + ** + *******************************************************************************/ +int btusb_release(struct inode *inode, struct file *file) +{ + struct btusb *p_dev; + int idx; + + BTUSB_INFO("enter\n"); + + p_dev = (struct btusb *) file->private_data; + BTUSB_INFO("p_dev=%p\n", p_dev); + if (p_dev == NULL) + { + BTUSB_ERR("can't find device\n"); + return -ENODEV; + } + +#ifdef BTUSB_LITE + btusb_lite_stop_all(p_dev); +#endif + + /* ensure there is no process hanging on poll / select */ + wake_up_interruptible(&p_dev->rx_wait_q); + + for (idx = 0; idx < ARRAY_SIZE(p_dev->voice_rx.channels); idx++) + { + if (p_dev->voice_rx.channels[idx].used) + { + if (btusb_remove_voice_channel(p_dev, p_dev->voice_rx.channels[idx].handle)) + { + BTUSB_ERR("btusb_remove_voice_channel failed\n"); + } + } + } + + /* decrement the usage count on our device */ + BTUSB_INFO("kref_put -> &p_dev->kref 0x%p\n", &p_dev->kref); + kref_put(&p_dev->kref, btusb_delete); + + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_get_rx_packet + ** + ** Description Get (extract) next buffer to sent to User Space + ** + *******************************************************************************/ +static BT_HDR *btusb_get_rx_packet(struct btusb *p_dev) +{ + /* if there is a pending Rx buffer */ + if (p_dev->p_rx_msg) + { + return p_dev->p_rx_msg; + } +#ifdef BTUSB_LITE + /* If HCI is over IPC */ + if (btusb_lite_is_hci_over_ipc(p_dev)) + { + return NULL; + } +#endif + + /* dequeue the next buffer and store its reference */ + p_dev->p_rx_msg = GKI_dequeue(&p_dev->rx_queue); + + return p_dev->p_rx_msg; +} + +/******************************************************************************* + ** + ** Function btusb_get_rx_packet_buffer + ** + ** Description Get pointer address of data to send to User Space + ** + *******************************************************************************/ +static UINT8 *btusb_get_rx_packet_buffer(struct btusb *p_dev, BT_HDR *p_msg) +{ + struct btusb_trans *p_trans; + + switch(p_msg->event) + { + case HCIT_TYPE_ACL_DATA: + case HCIT_TYPE_EVENT: + case HCIT_TYPE_LM_DIAG: +#ifdef BTUSB_LITE + if (unlikely(p_msg->layer_specific & BTUSB_LS_GKI_BUFFER)) + { + return ((char *)(p_msg + 1) + p_msg->offset); + } +#endif + p_trans = container_of(p_msg, struct btusb_trans, bt_hdr); + return (p_trans->dma_buffer + p_msg->offset); + default: /* SCO etc... */ + return ((char *)(p_msg + 1) + p_msg->offset); + } +} + +/******************************************************************************* + ** + ** Function btusb_read + ** + ** Description User mode read + ** + ** Returns Number of bytes read, error number if negative + ** + *******************************************************************************/ +ssize_t btusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct btusb *p_dev = (struct btusb *)file->private_data; + UINT16 len; + size_t remainder = count; + BT_HDR *p_buf; + char *p_data; + char type; + int retval; + + BTUSB_DBG("p_dev=%p count=%zu\n", p_dev, count); + if (unlikely(p_dev == NULL)) + { + BTUSB_ERR("can't find device\n"); + return -ENODEV; + } + + if (unlikely(!p_dev->p_main_intf)) + { + BTUSB_ERR("device unplugged\n"); + return -ENODEV; + } + + /* if read is non blocking and there is no data */ + if (unlikely((file->f_flags & O_NONBLOCK) && (!btusb_get_rx_packet(p_dev)))) + { + BTUSB_INFO("Non blocking read without any data\n"); + return -EAGAIN; + } + + /* wait for an event */ + retval = wait_event_interruptible(p_dev->rx_wait_q, btusb_get_rx_packet(p_dev)); + if (unlikely(retval)) + { + BTUSB_DBG("read wait interrupted\n"); + return retval; + } + + p_buf = p_dev->p_rx_msg; + BTUSB_DBG("buffer=%p len=%u ls=%u\n", p_buf, p_buf->len, p_buf->layer_specific); + /* if the buffer is not empty and this is the first time buffer is picked, + * add HCI header in user space */ + if (likely(p_buf->len && !(p_buf->layer_specific & BTUSB_LS_H4_TYPE_SENT))) + { + if (likely(remainder >= 1)) + { + type = p_buf->event; + /* add the H4 packet header */ + if (unlikely(copy_to_user(buffer, &type, 1))) + { + BTUSB_ERR("copy to user error\n"); + return -EINVAL; + } + p_buf->layer_specific |= BTUSB_LS_H4_TYPE_SENT; + buffer += 1; + remainder -= 1; + } + else + { + BTUSB_ERR("Not enough space to copy H4 ACL header\n"); + goto read_end; + } + } + + /* retrieve address of the next data to send (depends on event) */ + p_data = btusb_get_rx_packet_buffer(p_dev, p_buf); + + /* take the min of remainder and p_buf length */ + if (likely(p_buf->len < remainder)) + { + len = p_buf->len; + } + else + { + len = remainder; + } + + /* copy the message data to the available user space */ + if (unlikely(copy_to_user(buffer, p_data, len))) + { + BTUSB_ERR("copy to user error\n"); + return -EINVAL; + } + remainder -= len; + p_buf->len -= len; + p_buf->offset += len; + + /* free the first buffer if it is empty */ + if (likely(p_buf->len == 0)) + { + /* free (fake event) or resubmit (real USB buffer) the buffer sent to app */ + btusb_rx_dequeued(p_dev, p_buf); + p_dev->p_rx_msg = NULL; + } + +read_end: + return (count - remainder); +} + +/******************************************************************************* + ** + ** Function btusb_write + ** + ** Description User mode write + ** + ** Returns Number of bytes written, error number if negative + ** + *******************************************************************************/ +ssize_t btusb_write(struct file *file, const char __user *user_buffer, size_t count, loff_t *ppos) +{ + struct btusb *p_dev; + BT_HDR *p_msg; + UINT8 *p_data; + size_t remainder = count; + int err; + UINT16 appended, len; + + p_dev = (struct btusb *)file->private_data; + + BTUSB_DBG("p_dev=%p count=%zu\n", p_dev, count); + + if (unlikely(p_dev == NULL)) + { + BTUSB_ERR("can't find device\n"); + return -ENODEV; + } + + if (unlikely(!p_dev->p_main_intf)) + { + BTUSB_ERR("device unplugged\n"); + return -ENODEV; + } + + if (unlikely(count == 0)) + { + return 0; + } + + /* check that the incoming data is good */ + if (unlikely(!access_ok(VERIFY_READ, (void *)user_buffer, count))) + { + BTUSB_ERR("buffer access verification failed\n"); + return -EFAULT; + } + + while (remainder) + { + BTUSB_DBG("remain=%zu p_write_msg=%p\n", remainder, p_dev->p_write_msg); + /* check if there is already an active transmission buffer */ + p_msg = p_dev->p_write_msg; + if (likely(!p_msg)) + { + /* get a buffer from the pool */ + p_msg = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + BTUSB_H4_MAX_SIZE); + if (unlikely(!p_msg)) + { + BTUSB_ERR("unable to get GKI buffer - write failed\n"); + return -EINVAL; + } + if (unlikely(dbgflags & BTUSB_GKI_CHK_MSG) && + unlikely(GKI_buffer_status(p_msg) != BUF_STATUS_UNLINKED)) + { + BTUSB_ERR("buffer != BUF_STATUS_UNLINKED 0x%p\n", p_msg); + return -EINVAL; + } + p_dev->p_write_msg = p_msg; + + p_msg->event = 0; + p_msg->len = 0; + p_msg->offset = 0; + p_msg->layer_specific = 0; + } + p_data = (UINT8 *)(p_msg + 1) + p_msg->offset; + + /* append the entire data to the buffer (not exceeding buffer length) */ + if (likely(remainder < (BTUSB_H4_MAX_SIZE - p_msg->len))) + appended = remainder; + else + appended = BTUSB_H4_MAX_SIZE - p_msg->len; + if (unlikely(copy_from_user(p_data + p_msg->len, (void *)user_buffer, appended))) + { + BTUSB_ERR("copy from user error\n"); + return -EINVAL; + } + BTUSB_DBG("msg_len=%u appended=%u\n", p_msg->len, appended); + + /* update the size of the buffer */ + p_msg->len += appended; + + /* compute the real HCI packet length (by default 0xFFFF to mark incomplete) */ + len = 0xFFFF; + switch(p_data[0]) + { + case HCIT_TYPE_SCO_DATA: + case HCIT_TYPE_COMMAND: + if (likely(p_msg->len >= 4)) + { + /* bypass HCI type + opcode/connection handle */ + p_data += 3; + STREAM_TO_UINT8(len, p_data); + len += 4; + BTUSB_DBG("SCO/CMD len=%u cur=%u\n", len, p_msg->len); + } + break; + case HCIT_TYPE_ACL_DATA: + if (likely(p_msg->len >= 5)) + { + /* bypass HCI type + connection handle */ + p_data += 3; + STREAM_TO_UINT16(len, p_data); + len += 5; + /* sanity check : ACL buffer should not be larger than supported */ + if (unlikely(len > BTUSB_H4_MAX_SIZE)) + { + BTUSB_ERR("ACL packet too long (%u)\n", len); + GKI_freebuf(p_dev->p_write_msg); + p_dev->p_write_msg = NULL; + return -EINVAL; + } + BTUSB_DBG("ACL len=%u cur=%u\n", len, p_msg->len); + } + break; + case HCIT_TYPE_LM_DIAG: + /* this packet does not have a length, so just send everything */ + len = p_msg->len; + BTUSB_DBG("DIAG len=%u cur=%u\n", len, p_msg->len); + break; + default: + BTUSB_ERR("unsupported packet type\n"); + GKI_freebuf(p_dev->p_write_msg); + p_dev->p_write_msg = NULL; + return count; + break; + } + /* check if the buffer length exceeds the packet length */ + if (likely(p_msg->len >= len)) + { + /* remove the extra data (belonging to next HCI packet) */ + if (unlikely(p_msg->len > len)) + { + /* remove exceeding data */ + appended -= p_msg->len - len; + /* set len to computed HCI packet length */ + p_msg->len = len; + } + if (autopm) + { + err = usb_autopm_get_interface(p_dev->p_main_intf); + if (unlikely(err < 0)) + { + BTUSB_ERR("autopm failed\n"); + autopm = 0; + } + } + + /* add the incoming data and notify the btusb_tx_task to process */ + GKI_enqueue(&p_dev->tx_queue, p_msg); + + /* wake up tasklet */ + tasklet_schedule(&p_dev->tx_task); + + /* new write message */ + p_dev->p_write_msg = NULL; + } + remainder -= appended; + user_buffer += appended; + } + + return count; +} + +/******************************************************************************* + ** + ** Function btusb_poll + ** + ** Description Poll callback (to implement select) + ** + ** Parameters file : file structure of the opened instance + ** p_pt : poll table to which the local queue must be added + ** + ** Returns Mask of the type of polling supported, error number if negative + ** + *******************************************************************************/ +unsigned int btusb_poll(struct file *file, struct poll_table_struct *p_pt) +{ + struct btusb *p_dev; + unsigned int mask; + + BTUSB_DBG("enter\n"); + + /* retrieve the device from the file pointer */ + p_dev = (struct btusb *) file->private_data; + BTUSB_DBG("p_dev=%p\n", p_dev); + if (unlikely(p_dev == NULL)) + { + BTUSB_ERR("can't find device\n"); + return -ENODEV; + } + + /* check if the device was disconnected */ + if (unlikely(!p_dev->p_main_intf)) + { + BTUSB_ERR("device unplugged\n"); + return -ENODEV; + } + + /* indicate to the system on which queue it should poll (non blocking call) */ + poll_wait(file, &p_dev->rx_wait_q, p_pt); + + /* enable WRITE (select/poll is not write blocking) */ + mask = POLLOUT | POLLWRNORM; + + /* enable READ only if data is queued from HCI */ + if (btusb_get_rx_packet(p_dev)) + { + mask |= POLLIN | POLLRDNORM; + } + + return mask; +} + +#define BTUSB_RETURN_STR(__c) case __c: return #__c +static const char *btusb_ioctl_string(unsigned int cmd) +{ + switch(cmd) + { + BTUSB_RETURN_STR(IOCTL_BTWUSB_GET_STATS); + BTUSB_RETURN_STR(IOCTL_BTWUSB_CLEAR_STATS); + BTUSB_RETURN_STR(IOCTL_BTWUSB_ADD_VOICE_CHANNEL); + BTUSB_RETURN_STR(IOCTL_BTWUSB_REMOVE_VOICE_CHANNEL); + BTUSB_RETURN_STR(TCGETS); + BTUSB_RETURN_STR(TCSETS); + BTUSB_RETURN_STR(TCSETSW); + BTUSB_RETURN_STR(TCSETSF); + BTUSB_RETURN_STR(TCGETA); + BTUSB_RETURN_STR(TCSETA); + BTUSB_RETURN_STR(TCSETAW); + BTUSB_RETURN_STR(TCSETAF); + BTUSB_RETURN_STR(TCSBRK); + BTUSB_RETURN_STR(TCXONC); + BTUSB_RETURN_STR(TCFLSH); + BTUSB_RETURN_STR(TIOCGSOFTCAR); + BTUSB_RETURN_STR(TIOCSSOFTCAR); + BTUSB_RETURN_STR(TIOCGLCKTRMIOS); + BTUSB_RETURN_STR(TIOCSLCKTRMIOS); +#ifdef TIOCGETP + BTUSB_RETURN_STR(TIOCGETP); + BTUSB_RETURN_STR(TIOCSETP); + BTUSB_RETURN_STR(TIOCSETN); +#endif +#ifdef TIOCGETC + BTUSB_RETURN_STR(TIOCGETC); + BTUSB_RETURN_STR(TIOCSETC); +#endif +#ifdef TIOCGLTC + BTUSB_RETURN_STR(TIOCGLTC); + BTUSB_RETURN_STR(TIOCSLTC); +#endif +#ifdef TCGETX + BTUSB_RETURN_STR(TCGETX); + BTUSB_RETURN_STR(TCSETX); + BTUSB_RETURN_STR(TCSETXW); + BTUSB_RETURN_STR(TCSETXF); +#endif + BTUSB_RETURN_STR(TIOCMGET); + BTUSB_RETURN_STR(TIOCMSET); + BTUSB_RETURN_STR(TIOCGSERIAL); + BTUSB_RETURN_STR(TIOCMIWAIT); + BTUSB_RETURN_STR(TIOCMBIC); + BTUSB_RETURN_STR(TIOCMBIS); + default: + return "unknwown"; + } +} + +/******************************************************************************* + ** + ** Function btusb_ioctl + ** + ** Description User mode ioctl + ** + ** Parameters + ** + ** Returns 0 upon success or an error code. + ** + *******************************************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) +long btusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +#else +int btusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +#endif +{ + const char *s_cmd; + struct btusb *p_dev; + struct btusb_ioctl_sco_info sco_info; + int retval = 0; + + s_cmd = btusb_ioctl_string(cmd); + + BTUSB_INFO("cmd=%s\n", s_cmd); + + p_dev = (struct btusb *) file->private_data; + BTUSB_INFO("p_dev=%p\n", p_dev); + if (p_dev == NULL) + { + BTUSB_ERR("can't find device\n"); + return -ENODEV; + } + + /* check if the device was disconnected */ + if (unlikely(!p_dev->p_main_intf)) + { + BTUSB_ERR("device unplugged\n"); + return -ENODEV; + } + + switch (cmd) + { + case IOCTL_BTWUSB_GET_STATS: + if (copy_to_user((void *) arg, &p_dev->stats, sizeof(tBTUSB_STATS))) + { + retval = -EFAULT; + goto err_out; + } + break; + + case IOCTL_BTWUSB_CLEAR_STATS: + memset(&p_dev->stats, 0, sizeof(tBTUSB_STATS)); + break; + + case IOCTL_BTWUSB_ADD_VOICE_CHANNEL: + if (copy_from_user(&sco_info, (void *) arg, sizeof(sco_info))) + { + BTUSB_ERR("BTUSB_IOCTL_ADD_VOICE_CHANNEL failed getting 1st par\n"); + retval = -EFAULT; + goto err_out; + } + return btusb_add_voice_channel(p_dev, sco_info.handle, sco_info.burst); + + case IOCTL_BTWUSB_REMOVE_VOICE_CHANNEL: + if (copy_from_user(&sco_info, (void *) arg, sizeof(sco_info))) + { + BTUSB_ERR("BTUSB_IOCTL_ADD_VOICE_CHANNEL failed getting 1st par\n"); + retval = -EFAULT; + goto err_out; + } + return btusb_remove_voice_channel(p_dev, sco_info.handle); + + case TCGETS: + /* dummy support of TTY IOCTLs */ + if (!arg) + { + retval = -EFAULT; + goto err_out; + } +#ifndef TCGETS2 + if (kernel_termios_to_user_termios((struct termios __user *)arg, &p_dev->kterm)) +#else + if (kernel_termios_to_user_termios_1((struct termios __user *)arg, &p_dev->kterm)) +#endif + { + BTUSB_ERR("failure copying termios\n"); + retval = -EFAULT; + goto err_out; + } + break; + + case TCSETSW: + case TCSETS: + if (!arg) + { + retval = -EFAULT; + goto err_out; + } +#ifndef TCGETS2 + if (user_termios_to_kernel_termios(&p_dev->kterm, (struct termios __user *)arg)) +#else + if (user_termios_to_kernel_termios_1(&p_dev->kterm, (struct termios __user *)arg)) +#endif + { + retval = -EFAULT; + goto err_out; + } + break; + + case TCSETSF: + case TCGETA: + case TCSETA: + case TCSETAW: + case TCSETAF: + case TCSBRK: + case TCXONC: + case TCFLSH: + case TIOCGSOFTCAR: + case TIOCSSOFTCAR: + case TIOCGLCKTRMIOS: + case TIOCSLCKTRMIOS: +#ifdef TIOCGETP + case TIOCGETP: + case TIOCSETP: + case TIOCSETN: +#endif +#ifdef TIOCGETC + case TIOCGETC: + case TIOCSETC: +#endif +#ifdef TIOCGLTC + case TIOCGLTC: + case TIOCSLTC: +#endif +#ifdef TCGETX + case TCGETX: + case TCSETX: + case TCSETXW: + case TCSETXF: +#endif + case TIOCMSET: + case TIOCMBIC: + case TIOCMBIS: + /* dummy support of TTY IOCTLs */ + BTUSB_DBG("TTY ioctl not implemented\n"); + break; + + case TIOCGSERIAL: + { + struct serial_struct tmp; + if (!arg) + { + retval = -EFAULT; + goto err_out; + } + memset(&tmp, 0, sizeof(tmp)); + tmp.type = 0; + tmp.line = 0; + tmp.port = 0; + tmp.irq = 0; + tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ; + tmp.xmit_fifo_size = 4096; + tmp.baud_base = 115200; + tmp.close_delay = 5*HZ; + tmp.closing_wait = 30*HZ; + tmp.custom_divisor = 0; + tmp.hub6 = 0; + tmp.io_type = 0; + if (copy_to_user((void __user *)arg, &tmp, sizeof(tmp))) + { + retval = -EFAULT; + goto err_out; + } + retval = 0; + break; + } + + case TIOCMGET: + { + int tiocm = TIOCM_RTS | TIOCM_CTS; + if (!arg) + { + retval = -EFAULT; + goto err_out; + } + + if (copy_to_user((void __user *)arg, &tiocm, sizeof(tiocm))) + { + retval = -EFAULT; + goto err_out; + } + retval = 0; + break; + } + + case TIOCMIWAIT: + { + DECLARE_WAITQUEUE(wait, current); + + BTUSB_DBG("arg = %lu\n", arg); + while (1) + { + BTUSB_DBG("adding task to wait list\n"); + add_wait_queue(&p_dev->rx_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + BTUSB_DBG("removing task from wait list\n"); + remove_wait_queue(&p_dev->rx_wait_q, &wait); + /* see if a signal woke us up */ + if (signal_pending(current)) + { + BTUSB_ERR("signal was pending\n"); + retval = -ERESTARTSYS; + goto err_out; + } + /* do not check the expected signals */ + retval = 0; + break; + } + break; + } + default: + BTUSB_ERR("unknown cmd %u\n", cmd); + retval = -ENOIOCTLCMD; + break; + } + +err_out: + BTUSB_DBG("returning %d\n", retval); + return retval; +} + +/******************************************************************************* + ** + ** Function btusb_urb_out_complete + ** + ** Description Data write (bulk pipe) completion routine + ** + ** Parameters urb pointer + ** + ** Returns void + ** + *******************************************************************************/ +void btusb_urb_out_complete(struct urb *p_urb) +{ + struct btusb_trans *p_trans = p_urb->context; + struct btusb *p_dev = p_trans->p_dev; + + BTUSB_DBG("status %d length %u flags %x\n", p_urb->status, + p_urb->transfer_buffer_length, p_urb->transfer_flags); + + p_trans->gki_hdr.status = BUF_STATUS_FREE; + + p_dev->stats.urb_out_complete++; + if (unlikely(p_urb->status)) + { + /* this error can happen when unplugging */ + p_dev->stats.urb_out_complete_err++; + } + + if (p_trans->complete) + { + p_trans->complete(p_dev, p_trans, p_urb); + } + + if (autopm) + { + usb_autopm_put_interface(p_dev->p_main_intf); + } + + return; +} + +/******************************************************************************* + ** + ** Function btusb_submit_acl + ** + ** Parameters p_dev: device control block reference + ** packet: HCI ACL packet to transmit + ** length: length of the HCI packet + ** + ** Parameters + ** + ** Returns 0 upon success, negative value if error + ** + *******************************************************************************/ +int btusb_submit_acl(struct btusb *p_dev, char *packet, unsigned long length) +{ + int retval; + struct btusb_trans *p_trans; + + BTUSB_DBG("%p[%lu]\n", packet, length); + + btusb_dump_data(packet, length, "OUTGOING DATA"); + + p_trans = btusb_alloc_trans(p_dev->acl_tx_array, ARRAY_SIZE(p_dev->acl_tx_array)); + if (unlikely(p_trans == NULL)) + { + return -ENOMEM; + } + + if (unlikely(length > BTUSB_HCI_MAX_ACL_SIZE)) + { + retval = -E2BIG; + goto error; + } + +#if 0 + /* if this is called directly from write call */ + if (copy_from_user(p_trans->dma_buffer, user_buffer, length)) + { + retval = -EFAULT; + goto error; + } +#else + memcpy(p_trans->dma_buffer, packet, length); +#endif + p_trans->p_urb->transfer_buffer_length = length; + + retval = btusb_submit(p_dev, &p_dev->acl_tx_submitted, p_trans, GFP_ATOMIC); + if (unlikely(retval)) + { + goto error; + } + return retval; + +error: + BTUSB_ERR("failed : %d\n", retval); + p_trans->gki_hdr.status = BUF_STATUS_FREE; + return retval; +} + +/******************************************************************************* + ** + ** Function btusb_submit_diag + ** + ** Parameters p_dev: device control block reference + ** packet: HCI DIAG packet to transmit + ** length: length of the packet + ** + ** Parameters + ** + ** Returns 0 upon success, negative value if error + ** + *******************************************************************************/ +int btusb_submit_diag(struct btusb *p_dev, char *packet, unsigned long length) +{ + int retval; + struct btusb_trans *p_trans; + + BTUSB_DBG("%p[%lu]\n", packet, length); + + btusb_dump_data(packet, length, "OUTGOING DIAG"); + if (unlikely(!p_dev->p_diag_out)) + { + return -ENODEV; + } + + p_trans = btusb_alloc_trans(p_dev->diag_tx_array, ARRAY_SIZE(p_dev->diag_tx_array)); + if (unlikely(p_trans == NULL)) + { + return -ENOMEM; + } + + if (unlikely(length > BTUSB_HCI_MAX_ACL_SIZE)) + { + retval = -E2BIG; + goto error; + } + +#if 0 + /* if this is called directly from write call */ + if (copy_from_user(p_trans->dma_buffer, user_buffer, length)) + { + retval = -EFAULT; + goto error; + } +#else + memcpy(p_trans->dma_buffer, packet, length); +#endif + p_trans->p_urb->transfer_buffer_length = length; + + retval = btusb_submit(p_dev, &p_dev->diag_tx_submitted, p_trans, GFP_ATOMIC); + if (unlikely(retval)) + { + goto error; + } + return retval; + +error: + BTUSB_ERR("failed : %d\n", retval); + p_trans->gki_hdr.status = BUF_STATUS_FREE; + return retval; +} + + +/******************************************************************************* + ** + ** Function btusb_cmd_complete + ** + ** Description Command (control pipe) completion routine + ** + ** Parameters + ** + ** Returns void. + ** + *******************************************************************************/ +void btusb_cmd_complete(struct btusb *p_dev, struct btusb_trans *p_trans, struct urb *p_urb) +{ + p_dev->stats.cmd_complete++; + if (unlikely(p_urb->status)) + { + p_dev->stats.cmd_complete_err++; + } +} + +/******************************************************************************* + ** + ** Function btusb_submit_cmd + ** + ** Parameters p_dev: device control block reference + ** packet: HCI CMD packet to transmit + ** length: length of the HCI packet + ** + ** Parameters + ** + ** Returns 0 upon success, negative value if error + ** + *******************************************************************************/ +int btusb_submit_cmd(struct btusb *p_dev, char *packet, unsigned long length) +{ + int retval; + struct btusb_trans *p_trans; + struct usb_ctrlrequest *p_dr = NULL; + + BTUSB_DBG("%p[%lu]\n", packet, length); + + btusb_dump_data(packet, length, "OUTGOING CMD"); + + p_trans = btusb_alloc_trans(p_dev->cmd_array, ARRAY_SIZE(p_dev->cmd_array)); + if (unlikely(!p_trans)) + { + return -ENOMEM; + } + + if (unlikely(length > BTUSB_HCI_MAX_CMD_SIZE)) + { + retval = -E2BIG; + goto error; + } + +#if 0 + /* if this is called directly from write call */ + if (copy_from_user(p_trans->dma_buffer, packet, length)) + { + retval = -EFAULT; + goto error; + } +#else + memcpy(p_trans->dma_buffer, packet, length); +#endif + p_dr = (void *)p_trans->p_urb->setup_packet; + p_dr->wLength = __cpu_to_le16(length); + p_trans->p_urb->transfer_buffer_length = length; + + retval = btusb_submit(p_dev, &p_dev->cmd_submitted, p_trans, GFP_ATOMIC); + if (unlikely(retval)) + { + p_dev->stats.cmd_submit_err++; + goto error; + } + else + { + p_dev->stats.cmd_submit_ok++; + } + return retval; + +error: + p_trans->gki_hdr.status = BUF_STATUS_FREE; + return retval; +} + +/******************************************************************************* + ** + ** Function btusb_voicetx_complete + ** + ** Description Voice write (iso pipe) completion routine. + ** + ** Parameters + ** + ** Returns void + ** + *******************************************************************************/ +void btusb_voicetx_complete(struct btusb *p_dev, struct btusb_trans *p_trans, struct urb *p_urb) +{ + p_dev->stats.voicetx_complete++; + if (unlikely(p_urb->status)) + { + p_dev->stats.voicetx_complete_err++; + } + + if (unlikely(dbgflags & BTUSB_VOICETX_TIME)) + { + btusb_voice_stats(&(p_dev->stats.voice_max_tx_done_delta_time), &(p_dev->stats.voice_min_tx_done_delta_time), + &(p_dev->stats.voice_tx_done_delta_time), &(p_dev->stats.voice_last_tx_done_ts)); + } + + atomic_sub(p_urb->number_of_packets, &p_dev->voice_tx_active); +} + +/******************************************************************************* + ** + ** Function btusb_submit_voice_tx + ** + ** Description Voice write submission + ** + ** Parameters p_dev: device control block reference + ** packet: HCI SCO packet to transmit + ** length: length of the HCI packet + ** + ** Returns void + ** + *******************************************************************************/ +static void btusb_submit_voice_tx(struct btusb *p_dev, char *packet, unsigned long length) +{ + int i, retval; + unsigned int packet_size, pkt_used, total_size; + unsigned long remain; + unsigned char to_send; + struct btusb_trans *p_trans; + struct usb_iso_packet_descriptor *p_ifd; + char *p_cur; + + if (!p_dev->p_main_intf || !p_dev->p_voice_intf || !p_dev->p_voice_out) + { + BTUSB_DBG(" failing (p_dev removed or no voice intf)\n"); + return; + } + + if (unlikely(dbgflags & BTUSB_VOICETX_TIME)) + { + btusb_voice_stats(&(p_dev->stats.voice_max_rx_feeding_interval), &(p_dev->stats.voice_min_rx_feeding_interval), + &(p_dev->stats.voice_rx_feeding_interval), &(p_dev->stats.voice_last_rx_feeding_ts)); + } + + packet_size = le16_to_cpu(p_dev->p_voice_out->desc.wMaxPacketSize); + if (unlikely(!packet_size)) + { + BTUSB_ERR("Max Packet Size = 0\n"); + return; + } + + if (unlikely(length < BTUSB_VOICE_HEADER_SIZE)) + { + BTUSB_ERR("SCO packet length is too short\n"); + return; + } + + resend: + to_send = 0; + p_cur = packet + BTUSB_VOICE_HEADER_SIZE; + remain = length - BTUSB_VOICE_HEADER_SIZE; + while (remain) + { + /* find a free transaction */ + p_trans = btusb_alloc_trans(p_dev->voice_tx_array, ARRAY_SIZE(p_dev->voice_tx_array)); + if (unlikely(!p_trans)) + { + p_dev->stats.voicetx_nobuf++; + BTUSB_ERR("No transaction available\n"); + return; + } + + total_size = 0; + for (i = 0, p_ifd = &p_trans->p_urb->iso_frame_desc[0]; + i < BTUSB_VOICE_FRAMES_PER_URB; i++, p_ifd++) + { + if (unlikely(!to_send)) + { + if (!remain) + { + break; + } + else + { + if (remain >= BTUSB_VOICE_BURST_SIZE) + { + to_send = BTUSB_VOICE_BURST_SIZE; + } + else + { + BTUSB_DBG("Sending partial sco data len: %lu\n", remain); + to_send = remain; + } + /* remaining data computed before adding header*/ + remain -= to_send; + + /* rebuild a SCO header, copying the full packet header */ + p_cur -= BTUSB_VOICE_HEADER_SIZE; + p_cur[0] = packet[0]; + p_cur[1] = packet[1]; + p_cur[2] = to_send; + to_send += BTUSB_VOICE_HEADER_SIZE; + } + } + if (likely(to_send > packet_size)) + { + pkt_used = packet_size; + } + else + { + pkt_used = to_send; + } + memcpy(&p_trans->dma_buffer[p_ifd->offset], p_cur, pkt_used); + p_cur += pkt_used; + to_send -= pkt_used; + p_ifd->length = pkt_used; + total_size += pkt_used; + } + if (likely(i)) + { + p_trans->p_urb->number_of_packets = i; + + retval = btusb_submit(p_dev, &p_dev->voice_tx_submitted, p_trans, GFP_ATOMIC); + if (unlikely(retval)) + { + p_trans->gki_hdr.status = BUF_STATUS_FREE; + p_dev->stats.voicetx_submit_err++; + } + else + { + atomic_add(i, &p_dev->voice_tx_active); + p_dev->stats.voicetx_submit_ok++; + } + } + else + { + p_trans->gki_hdr.status = BUF_STATUS_FREE; + } + } + + /* if there are less than 30 ms of data, send the same data again */ + if (atomic_read(&p_dev->voice_tx_active) < 30) + { + BTUSB_DBG("less than 30 ms of data available, resending packet"); + goto resend; + } +} + +/******************************************************************************* + ** + ** Function btusb_fill_isoc_pkts + ** + ** Description Prepare the ISOC packet descriptors + ** + ** Parameters + ** + ** Returns void + ** + *******************************************************************************/ +static void btusb_fill_isoc_pkts(struct urb *p_urb, int len, int pkt_size) +{ + int off = 0, i; + + for (i = 0; len >= pkt_size; i++, off += ALIGN(pkt_size, 4), len -= ALIGN(pkt_size, 4)) + { + p_urb->iso_frame_desc[i].offset = off; + p_urb->iso_frame_desc[i].length = pkt_size; + BTUSB_DBG("off=%d pkt_size=%d\n", off, pkt_size); + } + p_urb->number_of_packets = i; +} + +/******************************************************************************* + ** + ** Function btusb_submit_voice_rx + ** + ** Description Submit a voice RX URB + ** + ** Parameters p_dev: device control block reference + ** p_trans: transaction reference + ** mem_flags: memory protection flags + ** Returns + ** + *******************************************************************************/ +void btusb_submit_voice_rx(struct btusb *p_dev, struct btusb_trans *p_trans, int mem_flags) +{ + if (unlikely(btusb_submit(p_dev, &p_dev->voice_rx_submitted, p_trans, mem_flags))) + { + p_dev->stats.voicerx_submit_err++; + } + else + { + p_dev->stats.voicerx_submit_ok++; + } +} + +/******************************************************************************* + ** + ** Function btusb_update_voice_trans + ** + ** Description Finish filling the voice URB and submit them. At this point + ** the URBs should NOT be submitted. + ** + ** Parameters p_dev: device control block reference + ** + ** Returns 0 upon success, error code otherwise + ** + *******************************************************************************/ +static int btusb_update_voice_trans(struct btusb *p_dev) +{ + unsigned int idx; + struct btusb_trans *p_trans; + struct urb *p_urb; + int packet_size; + + BTUSB_DBG("enter\n"); + + if (!p_dev->p_main_intf || !p_dev->p_voice_intf || !p_dev->p_voice_in) + { + BTUSB_ERR("failing (p_dev removed or no voice intf)\n"); + return -EFAULT; + } + + packet_size = le16_to_cpu(p_dev->p_voice_in->desc.wMaxPacketSize); + if (!packet_size) + { + BTUSB_ERR("Max Packet Size = 0\n"); + return -EINVAL; + } + BTUSB_DBG("packet_size=%d\n", packet_size); + + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->voice_rx_array) + { + p_urb = p_trans->p_urb; + p_urb->pipe = usb_rcvisocpipe(p_dev->p_udev, p_dev->p_voice_in->desc.bEndpointAddress); + BTUSB_DBG("ep=%d\n", p_dev->p_voice_in->desc.bEndpointAddress); + p_urb->interval = p_dev->p_voice_in->desc.bInterval; + BTUSB_DBG("interval=%d\n", p_urb->interval); + p_urb->transfer_buffer_length = ALIGN(packet_size, 4) * BTUSB_VOICE_FRAMES_PER_URB; + BTUSB_DBG("transfer_buffer_length=%d\n", p_urb->transfer_buffer_length); + if (p_urb->transfer_buffer_length > BTUSB_VOICE_BUFFER_MAXSIZE) + { + BTUSB_ERR("rx voice allocated insufficient for MaxPacketSize\n"); + p_urb->transfer_buffer_length = BTUSB_VOICE_BUFFER_MAXSIZE; + return -ENOMEM; + } + btusb_fill_isoc_pkts(p_urb, p_urb->transfer_buffer_length, packet_size); + + btusb_submit_voice_rx(p_dev, p_trans, GFP_KERNEL); + } + + packet_size = le16_to_cpu(p_dev->p_voice_out->desc.wMaxPacketSize); + if (!packet_size) + { + BTUSB_ERR("Max Packet Size = 0\n"); + return -EINVAL; + } + BTUSB_ARRAY_FOR_EACH_TRANS(p_dev->voice_tx_array) + { + p_urb = p_trans->p_urb; + p_urb->pipe = usb_sndisocpipe(p_dev->p_udev, p_dev->p_voice_out->desc.bEndpointAddress); + BTUSB_DBG("ep=%d\n", p_dev->p_voice_out->desc.bEndpointAddress); + p_urb->interval = p_dev->p_voice_out->desc.bInterval; + BTUSB_DBG("interval=%d\n", p_urb->interval); + p_urb->transfer_buffer_length = ALIGN(packet_size, 4) * BTUSB_VOICE_FRAMES_PER_URB; + if (p_urb->transfer_buffer_length > BTUSB_VOICE_BUFFER_MAXSIZE) + { + BTUSB_ERR("tx voice allocated insufficient for MaxPacketSize\n"); + p_urb->transfer_buffer_length = BTUSB_VOICE_BUFFER_MAXSIZE; + return -ENOMEM; + } + btusb_fill_isoc_pkts(p_urb, p_urb->transfer_buffer_length, packet_size); + } + + /* at this point, initiate voice TX */ + for (idx = 0; idx < ARRAY_SIZE(p_dev->voice_rx.channels); idx++) + { + struct btusb_voice_channel *p_chan = &p_dev->voice_rx.channels[idx]; + if (p_chan->used) + { + UINT8 data[BTUSB_VOICE_HEADER_SIZE + BTUSB_VOICE_BURST_SIZE]; + UINT8 *p_data = data; + memset(data, 0, sizeof(data)); + UINT16_TO_STREAM(p_data, p_chan->handle); + UINT8_TO_STREAM(p_data, BTUSB_VOICE_BURST_SIZE); + btusb_submit_voice_tx(p_dev, data, sizeof(data)); + } + } + return 0; +} + + +/******************************************************************************* + ** + ** Function btusb_set_voice + ** + ** Description Change voice interface setting processor + ** NOTE: Must be called at low execution level + ** + ** Parameters p_dev: pointer to the device control block + ** setting_number: new voice interface setting number + ** submit_urb: true if the URBs must be submitted + ** + ** Returns 0 upon success, error code otherwise + ** + *******************************************************************************/ +static int btusb_set_voice(struct btusb *p_dev, unsigned char setting_number, + bool submit_urb) +{ + int idx; + struct usb_host_interface *p_host_intf; + struct usb_endpoint_descriptor *p_ep_desc; + + BTUSB_DBG("setting_number=%d submit_urb=%u\n", setting_number, submit_urb); + + /* cancel all voice requests before switching buffers */ + p_dev->p_voice_in = NULL; + p_dev->p_voice_out = NULL; + btusb_cancel_voice(p_dev); + + /* configure the voice interface to the proper setting */ + if (usb_set_interface(p_dev->p_udev, 1, setting_number)) + { + BTUSB_ERR("failed to set iso intf to %u\n", setting_number); + return -EFAULT; + } + + /* find the endpoints */ + p_host_intf = p_dev->p_voice_intf->cur_altsetting; + + for (idx = 0; idx < p_host_intf->desc.bNumEndpoints; idx++) + { + p_ep_desc = &p_host_intf->endpoint[idx].desc; + if (usb_endpoint_type(p_ep_desc) == USB_ENDPOINT_XFER_ISOC) + { + if (usb_endpoint_dir_in(p_ep_desc)) + { + p_dev->p_voice_in = &p_host_intf->endpoint[idx]; + } + else + { + p_dev->p_voice_out = &p_host_intf->endpoint[idx]; + } + } + } + + if (!p_dev->p_voice_in || !p_dev->p_voice_out) + { + BTUSB_ERR("no iso pipes found!\n"); + return -EFAULT; + } + + if (submit_urb) + { + return btusb_update_voice_trans(p_dev); + } + + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_add_voice_channel + ** + ** Description Add a voice channel in the list of current channels + ** + ** Parameters p_dev: pointer to the device control structure + ** sco_handle: handle of the synchronous connection carrying voice + ** burst: size of the voice bursts + ** + ** Returns Return 0 upon success, error code otherwise + ** + *******************************************************************************/ +int btusb_add_voice_channel(struct btusb *p_dev, unsigned short sco_handle, unsigned char burst) +{ + int idx; + struct btusb_voice_channel *p_chan; + + if (!p_dev->p_voice_intf) + { + BTUSB_ERR("No voice interface detected\n"); + return -EOPNOTSUPP; + } + + /* kludge to support the backward compatibility with older BTKRNLs + not supplying the packet size... */ + if (burst == 0) + { + BTUSB_INFO("fixing legacy req to 48\n"); + burst = BTUSB_VOICE_BURST_SIZE; + } + + /* look for an available voice channel entry */ + for (idx = 0; idx < ARRAY_SIZE(p_dev->voice_rx.channels); idx++) + { + p_chan = &p_dev->voice_rx.channels[idx]; + if (!p_chan->used) + { + p_chan->handle = sco_handle; + p_chan->burst = burst; + p_chan->used = true; + goto found; + } + } + BTUSB_ERR("Could not find empty slot in internal tables\n"); + return -EMLINK; + +found: + if (btusb_update_voice_channels(p_dev)) + { + BTUSB_ERR("Failed adding voice channel\n"); + /* failure, remove the channel just added */ + btusb_remove_voice_channel(p_dev, sco_handle); + return -ENOMEM; + } + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_remove_voice_channel + ** + ** Description Remove a voice channel from the list of current channels + ** + ** Parameters p_dev: pointer to the device control structure + ** sco_handle: handle of the synchronous connection carrying voice + ** + ** Returns Return 0 upon success, error code otherwise + ** + *******************************************************************************/ +int btusb_remove_voice_channel(struct btusb *p_dev, unsigned short sco_handle) +{ + int idx; + struct btusb_voice_channel *p_chan; + + if (!p_dev->p_voice_intf) + { + BTUSB_ERR("No voice interface detected\n"); + return -EOPNOTSUPP; + } + + /* find the channel to be removed */ + for (idx = 0; idx < ARRAY_SIZE(p_dev->voice_rx.channels); idx++) + { + p_chan = &p_dev->voice_rx.channels[idx]; + if (p_chan->used && (p_chan->handle == sco_handle)) + { + p_chan->used = false; + goto found; + } + } + BTUSB_ERR("Could not find SCO handle in internal tables\n"); + return -ENOENT; + +found: + return btusb_update_voice_channels(p_dev); +} + +/******************************************************************************* + ** + ** Function btusb_update_voice_channels + ** + ** Description Voice channels just updated, reconfigure + ** + ** Parameters p_dev: pointer to the device control structure + ** + ** Returns Return 0 upon success, error code otherwise + ** + *******************************************************************************/ +static int btusb_update_voice_channels(struct btusb *p_dev) +{ + int idx, jdx; + unsigned char min_burst, max_burst, num_voice_chan, voice_setting; + unsigned short desired_packet_size, packet_size; + struct btusb_voice_channel *p_chan; + struct usb_host_interface *p_host_intf; + struct usb_endpoint_descriptor *p_ep_desc; + + BTUSB_DBG("\n"); + + /* get the number of voice channels and the size information */ + num_voice_chan = 0; + min_burst = 0xFF; + max_burst = 0; + for (idx = 0; idx < ARRAY_SIZE(p_dev->voice_rx.channels); idx++) + { + p_chan = &p_dev->voice_rx.channels[idx]; + if (p_chan->used) + { + num_voice_chan++; + min_burst = min(min_burst, p_chan->burst); + max_burst = max(max_burst, p_chan->burst); + } + } + + BTUSB_DBG("num_voice_chan=%d\n", num_voice_chan); + /* now calculate a desired_packet_size */ + switch (num_voice_chan) + { + case 0: + desired_packet_size = 0; + break; + + case 1: + /* single channel: we just need a third of the length + (rounded up so we add 2 before dividing) */ + desired_packet_size = ((max_burst + BTUSB_VOICE_HEADER_SIZE) + 2) / 3; + break; + + case 2: + /* two channels: we need the smaller one to fit in completely + and the larger one to fit in into two... */ + packet_size = (max_burst + BTUSB_VOICE_HEADER_SIZE + 1) / 2; + + desired_packet_size = min_burst + BTUSB_VOICE_HEADER_SIZE; + if (packet_size > desired_packet_size) + desired_packet_size = packet_size; + break; + + case 3: + /* three channels - we need all of them to fit into a single packet */ + desired_packet_size = max_burst + BTUSB_VOICE_HEADER_SIZE; + break; + + default: + /* this can not happen */ + BTUSB_ERR("invalid # (%d) of channels, failing...\n", num_voice_chan); + return 0; + } + + BTUSB_DBG("desired packet size is %u\n", desired_packet_size); + + /* now convert the desired_packet_size into the interface setting number */ + packet_size = BTUSB_USHRT_MAX; + voice_setting = 0; + for (idx = 0; idx < p_dev->p_voice_intf->num_altsetting; idx++) + { + p_host_intf = &p_dev->p_voice_intf->altsetting[idx]; + for (jdx = 0; jdx < p_host_intf->desc.bNumEndpoints; jdx++) + { + p_ep_desc = &p_host_intf->endpoint[jdx].desc; + if ((usb_endpoint_type(p_ep_desc) == USB_ENDPOINT_XFER_ISOC) && + usb_endpoint_dir_in(p_ep_desc)) + { + /* if the MaxPacketSize is large enough and if it is smaller + than the current setting */ + if ((desired_packet_size <= le16_to_cpu(p_ep_desc->wMaxPacketSize)) && + (le16_to_cpu(p_ep_desc->wMaxPacketSize) < packet_size)) + { + packet_size = le16_to_cpu(p_ep_desc->wMaxPacketSize); + voice_setting = p_host_intf->desc.bAlternateSetting; + } + } + } + } + if (packet_size == BTUSB_USHRT_MAX) + { + BTUSB_ERR("no appropriate ISO interface setting found, failing...\n"); + return -ERANGE; + } + + BTUSB_DBG("desired_packet_size=%d voice_setting=%d\n", desired_packet_size, voice_setting); + + /* set the voice setting and only submit the URBs if there is a channel */ + return btusb_set_voice(p_dev, voice_setting, num_voice_chan != 0); +} diff --git a/src/btusb_isoc.c b/src/btusb_isoc.c new file mode 100755 index 0000000..5987c68 --- a/dev/null +++ b/src/btusb_isoc.c @@ -0,0 +1,292 @@ +/* + * + * btusb_isoc.c + * + * + * + * Copyright (C) 2013-2014 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * Module that handles the reception of the voice HCI packets on the isochronous + * interface. + * + */ + +/* for kmalloc */ +#include <linux/slab.h> +#include "btusb.h" +#include "hcidefs.h" + +/******************************************************************************* + ** + ** Function btusb_isoc_check_hdr + ** + ** Description Check the packet header + ** + ** Parameters p_dev: device instance control block + ** + ** Returns void + ** + *******************************************************************************/ +static bool btusb_isoc_check_hdr(struct btusb *p_dev) +{ + unsigned char *p_data = p_dev->voice_rx.hdr; + int idx; + unsigned short sco_handle; + unsigned char size; + struct btusb_voice_pkt *p_pkt; + BT_HDR *p_hdr; + struct btusb_voice_channel *p_chan; + + STREAM_TO_UINT16(sco_handle, p_data); + sco_handle &= 0x0fff; + STREAM_TO_UINT8(size, p_data); + + for (idx = 0; idx < ARRAY_SIZE(p_dev->voice_rx.channels); idx++) + { + p_chan = &p_dev->voice_rx.channels[idx]; + if ((p_chan->used) && + (sco_handle == p_chan->handle) && + (size <= (2 * p_chan->burst))) + { + /* check if there is already a message being consolidated */ + if (unlikely(p_chan->p_pkt == NULL)) + { + if (!btusb_cq_get(&p_dev->voice_rx_list, &p_pkt)) + { + BTUSB_ERR("No buffer available for SCO defragmentation\n"); + return false; + } + p_hdr = &p_pkt->bt_hdr; + p_hdr->len = BTUSB_VOICE_HEADER_SIZE; + p_hdr->offset = 0; + p_hdr->layer_specific = 0; + + p_data = (unsigned char *) (p_hdr + 1); + + /* add sco handle and buffer size */ + UINT16_TO_STREAM(p_data, sco_handle); + UINT8_TO_STREAM(p_data, BTUSB_SCO_RX_LEN); + + p_chan->p_pkt = p_pkt; + } + p_dev->voice_rx.remaining = size; + p_dev->voice_rx.pp_pkt = &p_chan->p_pkt; + + return true; + } + } + return false; +} + +/******************************************************************************* + ** + ** Function btusb_isoc_check_pkt + ** + ** Description Check if the reconstructed HCI voice packet is large enough + ** + ** Parameters p_dev: device instance control block + ** + ** Returns void + ** + *******************************************************************************/ +static void btusb_isoc_check_pkt(struct btusb *p_dev) +{ + struct btusb_voice_pkt *p_pkt = *p_dev->voice_rx.pp_pkt; + BT_HDR *p_hdr = &p_pkt->bt_hdr; + + /* if enough data was received */ + if (unlikely(p_hdr->len == sizeof(((struct btusb_voice_pkt *)0)->data))) + { + btusb_rx_enqueue_voice(p_dev, p_pkt); + /* clear both references to the HCI packet */ + *p_dev->voice_rx.pp_pkt = NULL; + p_dev->voice_rx.pp_pkt = NULL; + } +} + +/******************************************************************************* + ** + ** Function btusb_voicerx_complete + ** + ** Description Voice read (iso pipe) completion routine. + ** + ** Parameters + ** + ** Returns void + ** + *******************************************************************************/ +void btusb_voicerx_complete(struct urb *p_urb) +{ + struct btusb_trans *p_trans = p_urb->context; + struct btusb *p_dev = p_trans->p_dev; + BT_HDR *p_hdr; + unsigned int length, packet_length; + unsigned char *p_packet, *p_frame, *p_data; + struct usb_iso_packet_descriptor *p_uipd, *p_end; + + if (unlikely(dbgflags & BTUSB_VOICERX_TIME)) + { + btusb_voice_stats(&(p_dev->stats.voice_max_rx_rdy_delta_time), &(p_dev->stats.voice_min_rx_rdy_delta_time), + &(p_dev->stats.voice_rx_rdy_delta_time), &(p_dev->stats.voice_last_rx_rdy_ts)); + } + + BTUSB_INFO("enter"); + + p_dev->stats.voicerx_complete++; + + if (unlikely(!p_dev->p_main_intf || !p_dev->p_voice_in)) + { + BTUSB_DBG("intf is down\n"); + return; + } + + /* entire URB error? */ + if (unlikely(p_urb->status)) + { + /* this error can happen when unplugging or updating channels */ + p_dev->stats.voicerx_complete_err++; + return; + } + + if (unlikely(p_dev->scosniff_active)) + { + struct btusb_scosniff *bs; + + bs = kmalloc(sizeof(struct btusb_scosniff) + + (p_urb->number_of_packets * sizeof(p_urb->iso_frame_desc[0])) + + p_urb->transfer_buffer_length, GFP_ATOMIC); + if (bs) + { + bs->s = p_urb->start_frame; + bs->n = p_urb->number_of_packets; + bs->l = p_urb->transfer_buffer_length; + /* copy the descriptors */ + memcpy(bs->d, p_urb->iso_frame_desc, bs->n * sizeof(p_urb->iso_frame_desc[0])); + /* then copy the content of the buffer */ + memcpy(&bs->d[bs->n], p_urb->transfer_buffer, bs->l); + /* protection not required because callback invoked with IRQ disabled */ + list_add_tail(&bs->lh, &p_dev->scosniff_list); + complete(&p_dev->scosniff_completion); + } + else + { + BTUSB_ERR("Failed allocating scosniff buffer"); + } + } + + p_frame = p_urb->transfer_buffer; + p_end = &p_urb->iso_frame_desc[p_urb->number_of_packets]; + for (p_uipd = p_urb->iso_frame_desc; p_uipd < p_end; p_uipd++) + { + if (unlikely(p_uipd->status)) + { + p_dev->stats.voicerx_bad_frames++; + /* should we do something if there is expected data? */ + continue; + } + + p_packet = p_frame + p_uipd->offset; + packet_length = p_uipd->actual_length; + p_dev->stats.voicerx_raw_bytes += packet_length; + + /* waiting for data? */ + if (likely(p_dev->voice_rx.remaining)) + { + fill_data: + if (likely(p_dev->voice_rx.remaining >= packet_length)) + { + length = packet_length; + } + else + { + length = p_dev->voice_rx.remaining; + } + p_hdr = &(*p_dev->voice_rx.pp_pkt)->bt_hdr; + p_data = (void *)(p_hdr + 1) + p_hdr->len; + + if (unlikely((p_hdr->len + length) > sizeof(((struct btusb_voice_pkt *)0)->data))) + { + BTUSB_ERR("SCO message too large for buffer\n"); + p_dev->stats.voicerx_bad_size++; + /* reset the length and pending bytes to end the current packet */ + p_dev->voice_rx.remaining = length = + sizeof(((struct btusb_voice_pkt *)0)->data) - p_hdr->len; + } + /* append data to the current message */ + memcpy(p_data, p_packet, length); + p_hdr->len += length; + + /* decrement the number of bytes remaining */ + p_dev->voice_rx.remaining -= length; + if (likely(p_dev->voice_rx.remaining)) + { + /* data still needed -> next descriptor */ + continue; + } + /* no more pending bytes, check if packet is full */ + btusb_isoc_check_pkt(p_dev); + packet_length -= length; + /* speedup peep-hole */ + if (likely(!packet_length)) + continue; + /* more bytes -> increment pointer */ + p_packet += length; + } + + /* if there is still data in the packet */ + if (likely(packet_length)) + { + /* at this point, there is NO SCO packet pending */ + if (likely(packet_length >= (BTUSB_VOICE_HEADER_SIZE - p_dev->voice_rx.hdr_size))) + { + length = BTUSB_VOICE_HEADER_SIZE - p_dev->voice_rx.hdr_size; + } + else + { + length = packet_length; + } + + /* fill the hdr (in case header is split across descriptors) */ + memcpy(&p_dev->voice_rx.hdr[p_dev->voice_rx.hdr_size], p_packet, length); + p_dev->voice_rx.hdr_size += length; + + if (likely(p_dev->voice_rx.hdr_size == BTUSB_VOICE_HEADER_SIZE)) + { + /* reset the pending size */ + p_dev->voice_rx.hdr_size = 0; + + if (likely(btusb_isoc_check_hdr(p_dev))) + { + p_packet += length; + packet_length -= length; + /* a correct header was found, get the data */ + goto fill_data; + } + p_dev->stats.voicerx_bad_hdr++; + p_dev->stats.voicerx_skipped_bytes += packet_length; + } + else + { + p_dev->stats.voicerx_split_hdr++; + } + } + } + + btusb_submit_voice_rx(p_dev, p_trans, GFP_ATOMIC); +} + diff --git a/src/btusb_lite.c b/src/btusb_lite.c new file mode 100755 index 0000000..7a7fc15 --- a/dev/null +++ b/src/btusb_lite.c @@ -0,0 +1,1785 @@ +/* + * + * btusb_lite.c + * + * + * + * Copyright (C) 2013 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ +#include <linux/proc_fs.h> +#include <linux/poll.h> + +#include "btusb.h" +#include "btpcm_api.h" +#include "bd.h" + + +/* + * Defines + */ + +#define BTUSB_LITE_IPC_AVDT_SYNC_INFO_RSP_LEN (1 + BTM_SYNC_INFO_NUM_STR * (1+6+2+2+1+2)) + +/* + * Local functions + */ +static void btusb_lite_reinit(struct btusb *p_dev); +static int btusb_lite_open(struct inode *inode, struct file *file); +static int btusb_lite_close(struct inode *inode, struct file *file); +static ssize_t btusb_lite_write(struct file *file, const char *buf, size_t count, + loff_t *p_off); +static ssize_t btusb_lite_read(struct file *file, char __user *buffer, size_t count, + loff_t *p_off); +static unsigned int btusb_lite_poll(struct file *file, struct poll_table_struct *p_pt); + +static BT_HDR *btusb_lite_msg_to_app_get(struct btusb *p_dev); +static void btusb_lite_msg_to_app_free(struct btusb *p_dev, BT_HDR *p_msg); +static UINT8 *btusb_lite_msg_to_app_get_data_addr(struct btusb *p_dev, BT_HDR *p_msg); + +static char *btusb_lite_ipc_event_desc(UINT16 event); + +static void btusb_lite_ipc_hndl(struct btusb *p_dev, BT_HDR *p_msg); +static void btusb_lite_ipc_hci_cmd_hndl(struct btusb *p_dev, BT_HDR *p_msg); +static void btusb_lite_ipc_hci_acl_hndl(struct btusb *p_dev, BT_HDR *p_msg); +static void btusb_lite_ipc_mgt_hndl(struct btusb *p_dev, BT_HDR *p_msg); +static void btusb_lite_ipc_btu_hndl(struct btusb *p_dev, BT_HDR *p_msg); +static void btusb_lite_ipc_btm_hndl(struct btusb *p_dev, BT_HDR *p_msg); +static void btusb_lite_ipc_l2c_hndl(struct btusb *p_dev, BT_HDR *p_msg); +static void btusb_lite_ipc_avdt_hndl(struct btusb *p_dev, BT_HDR *p_msg); + +static void btusb_lite_ipc_rsp_send(struct btusb *p_dev, UINT16 event, UINT8 op_code, + UINT8 *p_param, UINT8 param_len); +static void btusb_lite_ipc_cmd_cplt_evt_send(struct btusb *p_dev, + UINT16 opcode, UINT8 *p_param, UINT8 param_len); +static void btusb_lite_ipc_avdt_sync_info_send(struct btusb *p_dev, + tAVDT_SYNC_INFO *p_sync_rsp); + +static void btusb_lite_ipc_sent_to_user(struct btusb *p_dev, BT_HDR *p_msg); + +/* + * Globals + */ +static const struct file_operations btusb_lite_fops = +{ + .open = btusb_lite_open, + .read = btusb_lite_read, + .poll = btusb_lite_poll, + .write = btusb_lite_write, + .release = btusb_lite_close, +}; + +/******************************************************************************* + ** + ** Function btusb_lite_reinit + ** + ** Description Init BTUSB Lite interface + ** + ** Returns None + ** + *******************************************************************************/ +static void btusb_lite_reinit(struct btusb *p_dev) +{ + memset(&p_dev->lite_cb.s, 0, sizeof(p_dev->lite_cb.s)); +} + +/******************************************************************************* + ** + ** Function btusb_lite_open + ** + ** Description Open BTUSB Lite interface + ** + ** Returns status (< 0 if error) + ** + *******************************************************************************/ +static int btusb_lite_open(struct inode *inode, struct file *file) +{ + int rv; + struct btusb *p_dev = BTUSB_PDE_DATA(inode); + + BTUSB_INFO("btusb_lite_open\n"); + + if (!p_dev) + { + BTUSB_ERR("Unable to find p_dev reference\n"); + rv = -ENODEV; + goto out; + } + + if (p_dev->lite_cb.s.opened) + { + BTUSB_ERR("Lite interface already opened\n"); + rv = -EBUSY;; + goto out; + } + + file->private_data = p_dev; /* Save our private p_dev */ + + btusb_lite_reinit(p_dev); + p_dev->lite_cb.s.opened = true; + + rv = 0; + +out: + return rv; +} + +/******************************************************************************* + ** + ** Function btusb_lite_close + ** + ** Description Close BTUSB Lite interface + ** + ** Returns status (< 0 if error) + ** + *******************************************************************************/ +static int btusb_lite_close(struct inode *inode, struct file *file) +{ + int rv; + struct btusb *p_dev = BTUSB_PDE_DATA(inode); + + BTUSB_INFO("btusb_lite_close\n"); + + if (!p_dev) + { + BTUSB_ERR("Unable to find p_dev reference\n"); + rv = -ENODEV; + goto out; + } + + if (!p_dev->lite_cb.s.opened) + { + BTUSB_ERR("Lite interface was not opened\n"); + rv = -EBUSY;; + goto out; + } + + btusb_lite_reinit(p_dev); + + rv = 0; + +out: + return rv; +} + +/******************************************************************************* + ** + ** Function btusb_lite_write + ** + ** Description Write handler of the BTUSB Lite interface + ** + ** Returns Nb bytes written + ** + *******************************************************************************/ +static ssize_t btusb_lite_write(struct file *file, const char *p_user_buffer, + size_t count, loff_t *p_off) +{ + struct btusb *p_dev = file->private_data; + size_t remainder; + struct btusb_lite_cb *p_lite_cb; + unsigned long copy_len; + ssize_t copied_len; + UINT8 *p; + UINT16 rx_event; + BT_HDR *p_msg; + + BTUSB_DBG("btusb_lite_write count=%zu\n", count); + + if (!p_dev) + { + BTUSB_ERR("Unable to find p_dev reference\n"); + return -ENODEV; + } + + if (!p_dev->lite_cb.s.opened) + { + BTUSB_ERR("Lite interface was not opened\n"); + return -EBUSY; + } + + if (unlikely(count == 0)) + { + return 0; + } + + /* check that the incoming data is good */ + if (unlikely(!access_ok(VERIFY_READ, (void *)p_user_buffer, count))) + { + BTUSB_ERR("buffer access verification failed\n"); + return -EFAULT; + } + + p_lite_cb = &p_dev->lite_cb; + + copied_len = 0; + remainder = count; + + while(remainder) + { + /* If a full Header has not yet been received */ + if (p_lite_cb->s.from_app.rx_header_len < BTUSB_LITE_IPC_HDR_SIZE) + { + /* How many bytes are needed (1 to 4) */ + copy_len = BTUSB_LITE_IPC_HDR_SIZE - p_lite_cb->s.from_app.rx_header_len; + + /* If less bytes are available */ + if (remainder < copy_len) + { + copy_len = remainder; + } + + /* Copy the header (or a part of it) */ + if (copy_from_user(&p_lite_cb->s.from_app.rx_header[p_lite_cb->s.from_app.rx_header_len], + (void *)p_user_buffer, copy_len)) + { + BTUSB_ERR("Copy header from user error\n"); + return -EINVAL; + } + remainder -= copy_len; + p_lite_cb->s.from_app.rx_header_len += copy_len; + p_user_buffer += copy_len; + copied_len += copy_len; + + /* If the buffer has been read */ + if (p_lite_cb->s.from_app.rx_header_len == BTUSB_LITE_IPC_HDR_SIZE) + { + p = p_lite_cb->s.from_app.rx_header; + STREAM_TO_UINT16(p_lite_cb->s.from_app.rx_len, p); + STREAM_TO_UINT16(rx_event, p); + + BTUSB_DBG("Rx Len=%d RxEvent=0x%X\n", p_lite_cb->s.from_app.rx_len, rx_event); + + p_lite_cb->s.from_app.rx_len -= sizeof(UINT16); /* Do not count Event Field */ + + /* get a buffer from the pool (add one byte for HCI_Type) */ + if (unlikely((p_msg = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + p_lite_cb->s.from_app.rx_len + 1)) == NULL)) + { + BTUSB_ERR("unable to get GKI buffer - write failed\n"); + return -EINVAL; + } + p_msg->event = rx_event; + p_msg->layer_specific = 0; + p_msg->offset = 1; + p_msg->len = 0; + + p_lite_cb->s.from_app.p_rx_msg = p_msg; + } + } + /* If Header already received */ + else + { + p_msg = p_lite_cb->s.from_app.p_rx_msg; + + if (!p_msg) + { + BTUSB_ERR("no Rx buffer\n"); + return EINVAL; + } + p = (UINT8 *)(p_msg + 1) + p_msg->len + p_msg->offset; + + /* How many payload bytes are expected */ + copy_len = p_lite_cb->s.from_app.rx_len; + + /* If less bytes are available */ + if (remainder < copy_len) + { + copy_len = remainder; + } + + /* Copy the Payload (or a part of it) */ + if (copy_from_user(p, (void *)p_user_buffer, copy_len)) + { + BTUSB_ERR("Copy payload from user error\n"); + return -EINVAL; + } + remainder -= copy_len; + p_user_buffer += copy_len; + copied_len += copy_len; + p_lite_cb->s.from_app.rx_len -= copy_len; + p_msg->len += copy_len; + + if (p_lite_cb->s.from_app.rx_len == 0) + { + /* Handle the received message */ + btusb_lite_ipc_hndl(p_dev, p_msg); + + p_lite_cb->s.from_app.p_rx_msg = NULL; + p_lite_cb->s.from_app.rx_header_len = 0; /* Ready to receive a new header */ + } + } + } + + return copied_len; +} + +/******************************************************************************* + ** + ** Function btusb_lite_msg_to_app_get + ** + ** Description Get next message to send to IPC + ** + ** Returns GKI buffer to send (or Null) + ** + *******************************************************************************/ +static BT_HDR *btusb_lite_msg_to_app_get(struct btusb *p_dev) +{ + BT_HDR *p_msg; + BT_HDR *p_hdr_msg; + UINT8 *p_data; + + /* First, check if a locally generated Event (IPC) message is pending */ + p_msg = p_dev->lite_cb.s.to_app.p_ipc_msg; + if (p_msg) + { + return p_msg; + } + + /* Secondly, check if a locally generated IPC Header message is pending */ + p_msg = p_dev->lite_cb.s.to_app.p_hdr_msg; + if (p_msg) + { + return p_msg; + } + + /* Thirdly, check if an HCI message is pending */ + p_msg = p_dev->lite_cb.s.to_app.p_hci_msg; + if (p_msg) + { + return p_msg; + } + + /* + * No pending message. Check queues now + */ + + /* First, check if a locally generated Event (UIPC) message is enqueued */ + p_msg = GKI_dequeue(&p_dev->lite_cb.s.to_app.ipc_queue); + if (p_msg) + { + p_dev->lite_cb.s.to_app.p_ipc_msg = p_msg; + return p_msg; + } + + /* If HCI is not over IPC */ + if (!btusb_lite_is_hci_over_ipc(p_dev)) + { + return NULL; /* Nothing more to send on IPC */ + } + + /* Check if an HCI message (from BT controller) is ready */ + p_msg = GKI_getfirst(&p_dev->rx_queue); + if (p_msg) + { + /* We need to Build an IPC Header to send the HCI packet */ + p_hdr_msg = GKI_getbuf(sizeof(BT_HDR) + BTUSB_LITE_IPC_HDR_SIZE); + if (p_hdr_msg == NULL) + { + /* Leave the Received HCI packet in the queue (for next time) */ + BTUSB_ERR("No more buffer\n"); + p_dev->lite_cb.s.to_app.p_hci_msg = NULL; + return NULL; + } + p_hdr_msg->len = BTUSB_LITE_IPC_HDR_SIZE; + p_hdr_msg->offset = 0; + p_hdr_msg->layer_specific = 0; + p_hdr_msg->event = 0; + p_data = (UINT8 *)(p_hdr_msg + 1) + p_hdr_msg->offset; + + /* Write Length */ + UINT16_TO_STREAM(p_data, p_msg->len + BTUSB_LITE_IPC_HDR_EVT_SIZE); + + /* Write Event code */ + switch(p_msg->event) + { + case HCIT_TYPE_EVENT: + { + struct btusb_trans *p_trans; + p_trans = container_of(p_msg, struct btusb_trans, bt_hdr); + BTUSB_INFO("Event=0x%02X Received from BT Controller\n", + *(p_trans->dma_buffer + p_msg->offset)); + BTUSB_INFO("IPC Buffer TxEvent=BT_EVT_TO_BTU_HCI_EVT(%X) Length=%d\n", + BT_EVT_TO_BTU_HCI_EVT, p_msg->len); + } + UINT16_TO_STREAM(p_data, BT_EVT_TO_BTU_HCI_EVT); + break; + + case HCIT_TYPE_ACL_DATA: + BTUSB_INFO("IPC Buffer TxEvent=BT_EVT_BTU_IPC_ACL_EVT(%X) Length=%d\n", BT_EVT_BTU_IPC_ACL_EVT, p_msg->len); + UINT16_TO_STREAM(p_data, BT_EVT_BTU_IPC_ACL_EVT); + break; + + case HCIT_TYPE_LM_DIAG: + BTUSB_INFO("IPC Buffer TxEvent=BT_EVT_TO_LM_DIAG(%X) Length=%d\n", BT_EVT_TO_LM_DIAG, p_msg->len); + UINT16_TO_STREAM(p_data, BT_EVT_TO_LM_DIAG); + break; + + default: + /* Should not append. Set Event to 0xFFFF for debug */ + BTUSB_ERR("Unknown event=0x%x\n", p_msg->event); + UINT16_TO_STREAM(p_data, 0xFFFF); + break; + } + + /* Store Header */ + p_dev->lite_cb.s.to_app.p_hdr_msg = p_hdr_msg; + + /* Dequeue HCI message */ + p_msg = GKI_dequeue(&p_dev->rx_queue); + p_dev->lite_cb.s.to_app.p_hci_msg = p_msg; + + return p_hdr_msg; /* Return pointer on Header */ + } + + return p_msg; +} + +/******************************************************************************* + ** + ** Function btusb_lite_msg_to_app_get_data_addr + ** + ** Description Retrieve the data from a GKI Buffer pointer + ** + ** Returns None + ** + *******************************************************************************/ +static UINT8 *btusb_lite_msg_to_app_get_data_addr(struct btusb *p_dev, BT_HDR *p_msg) +{ + struct btusb_trans *p_trans; + + /* If the message is a "real" GKI buffer */ + if ((p_dev->lite_cb.s.to_app.p_ipc_msg == p_msg) || + (p_dev->lite_cb.s.to_app.p_hdr_msg == p_msg)) + { + return (UINT8 *)(p_msg + 1) + p_msg->offset; + } + + /* If the message is an HCI message, the data is located in the USB transaction */ + + /* Check if the message is an HCI message */ + if (p_dev->lite_cb.s.to_app.p_hci_msg == p_msg) + { + p_trans = container_of(p_msg, struct btusb_trans, bt_hdr); + return (p_trans->dma_buffer + p_msg->offset); + } + + BTUSB_ERR("Unknown buffer=%p\n", p_msg); + + return NULL; +} + +/******************************************************************************* + ** + ** Function btusb_lite_msg_to_app_free + ** + ** Description Free a message which has been sent to IPC + ** + ** Returns None + ** + *******************************************************************************/ +static void btusb_lite_msg_to_app_free(struct btusb *p_dev, BT_HDR *p_msg) +{ + /* Check if the message is a locally generated Event (IPC) message */ + if (p_dev->lite_cb.s.to_app.p_ipc_msg == p_msg) + { + GKI_freebuf(p_msg); + p_dev->lite_cb.s.to_app.p_ipc_msg = NULL; + return; + } + + /* Check if the message is a locally generated Header (IPC) message */ + if (p_dev->lite_cb.s.to_app.p_hdr_msg == p_msg) + { + GKI_freebuf(p_msg); + p_dev->lite_cb.s.to_app.p_hdr_msg = NULL; + return; + } + + /* Check if the message is an HCI message */ + if (p_dev->lite_cb.s.to_app.p_hci_msg == p_msg) + { + p_dev->lite_cb.s.to_app.p_hci_msg = NULL; + /* The buffer must not be freed. It has to be re-enqueue in USB core */ + btusb_rx_dequeued(p_dev, p_msg); + return; + } + + BTUSB_ERR("Unknown buffer=%p\n", p_msg); +} + +/******************************************************************************* + ** + ** Function btusb_lite_read + ** + ** Description Read handler of the BTUSB Lite interface + ** + ** Returns Nb bytes written + ** + *******************************************************************************/ +static ssize_t btusb_lite_read(struct file *file, char __user *p_user_buffer, size_t count, loff_t *p_off) +{ + struct btusb *p_dev = file->private_data; + ssize_t size; + UINT8 *p_data; + unsigned long copy_len; + BT_HDR *p_msg; + int rv; + + BTUSB_DBG("btusb_lite_read\n"); + + if (!p_dev) + { + BTUSB_ERR("Unable to find p_dev reference\n"); + size = -ENODEV; + goto out; + } + + if (!p_dev->lite_cb.s.opened) + { + BTUSB_ERR("Lite interface was not opened\n"); + size = -EBUSY;; + goto out; + } + + if (count == 0) + { + size = 0; + goto out; + } + + /* Check that the user buffer is good */ + if (unlikely(!access_ok(VERIFY_WRITE, (void *)p_user_buffer, count))) + { + BTUSB_ERR("buffer access verification failed\n"); + size = -EFAULT; + goto out; + } + + /* Sleep while nothing for the application */ + rv = wait_event_interruptible(p_dev->rx_wait_q, + ((p_msg = btusb_lite_msg_to_app_get(p_dev)) != NULL)); + + if (unlikely(rv)) + { + BTUSB_DBG("read wait interrupted"); + return rv; + } + + if (p_msg) + { + if (p_msg->len < count) + { + copy_len = p_msg->len; + } + else + { + copy_len = count; /* User asks for count bytes */ + } + + BTUSB_DBG("p_msg=%p msg->len=%d count=%zu copy_len=%lu\n", p_msg, p_msg->len, count, copy_len); + + p_data = btusb_lite_msg_to_app_get_data_addr(p_dev, p_msg); + if (p_data == NULL) + { + size = -EFAULT; + goto out; + } + + /* copy the message data to the available user space */ + if (unlikely(copy_to_user(p_user_buffer, p_data, copy_len))) + { + BTUSB_ERR("copy to user error\n"); + size = -EINVAL; + goto out; + } + + BTUSB_DBG("copied %ld bytes\n", copy_len); + + p_msg->offset += copy_len; + p_msg->len -= copy_len; + size = copy_len; + + if (p_msg->len == 0) + { + BTUSB_DBG("free gki buffer\n"); + btusb_lite_msg_to_app_free(p_dev, p_msg); + } + goto out; + } + else + { + BTUSB_ERR("No Buffer\n"); + } + + size = 0; /* should not happen */ + +out: + return size; +} + +/******************************************************************************* + ** + ** Function btusb_lite_poll + ** + ** Description Poll callback (to implement select) + ** + ** Parameters file : file structure of the opened instance + ** p_pt : poll table to which the local queue must be added + ** + ** Returns Mask of the type of polling supported, error number if negative + ** + *******************************************************************************/ +static unsigned int btusb_lite_poll(struct file *file, struct poll_table_struct *p_pt) +{ + struct btusb *p_dev = file->private_data; + unsigned int mask; + + BTUSB_DBG("enter\n"); + + /* retrieve the device from the file pointer*/ + BTUSB_DBG("p_dev=%p\n", p_dev); + if (unlikely(p_dev == NULL)) + { + BTUSB_ERR("can't find device\n"); + return -ENODEV; + } + + // check if the device was disconnected + if (unlikely(!p_dev->p_main_intf)) + { + BTUSB_ERR("device unplugged\n"); + return -ENODEV; + } + + if (unlikely(!p_dev->lite_cb.s.opened)) + { + BTUSB_ERR("Lite interface was not opened\n"); + return -EINVAL; + } + + /* indicate to the system on which queue it should poll (non blocking call) */ + poll_wait(file, &p_dev->rx_wait_q, p_pt); + + /* enable WRITE (select/poll is not write blocking) */ + mask = POLLOUT | POLLWRNORM; + + /* enable READ only if data is queued from HCI */ + if (btusb_lite_msg_to_app_get(p_dev)) + { + mask |= POLLIN | POLLRDNORM; + } + + return mask; +} + +/******************************************************************************* + ** + ** Function btusb_lite_create + ** + ** Description Create BTUSB Lite interface + ** + ** Returns status (< 0 if error) + ** + *******************************************************************************/ +int btusb_lite_create(struct btusb *p_dev, struct usb_interface *p_interface) +{ + int rv = -1; + + p_dev->lite_cb.p_btpcm = btpcm_init(kobject_name(&p_dev->p_main_intf->usb_dev->kobj)); + if (!p_dev->lite_cb.p_btpcm) + { + BTUSB_ERR("btpcm_init failed\n"); + } + + if (p_dev->p_pde) + { + p_dev->lite_cb.p_lite_pde = proc_create_data("lite", S_IRUGO | S_IWUGO, p_dev->p_pde, &btusb_lite_fops, p_dev); + if (!p_dev->lite_cb.p_lite_pde) + { + BTUSB_ERR("Couldn't create proc lite entry\n"); + } + else + { + BTUSB_INFO("Created proc lite entry\n"); + rv = 0; + } + } + + return rv; +} + +/******************************************************************************* + ** + ** Function btusb_lite_delete + ** + ** Description Delete BTUSB Lite interface + ** + ** Returns none + ** + *******************************************************************************/ +void btusb_lite_delete(struct btusb *p_dev, struct usb_interface *p_interface) +{ + if (p_dev->p_pde) + { + remove_proc_entry("lite", p_dev->p_pde); + BTUSB_INFO("proc lite removed\n"); + } + + if (btpcm_exit(p_dev->lite_cb.p_btpcm)) + { + BTUSB_ERR("btpcm_exit failed\n"); + } +} + +/******************************************************************************* + ** + ** Function btusb_lite_is_hci_over_ipc + ** + ** Description Check if HCI is over IPC (Lite Interface). + ** + ** Returns int (1 if HCI is over IPC otherwise 0) + ** + *******************************************************************************/ +int btusb_lite_is_hci_over_ipc(struct btusb *p_dev) +{ + if ((p_dev->lite_cb.s.opened) && /* User Space opened the Lite interface */ + (p_dev->lite_cb.s.mgt.opened) && /* IPC MGT Opened */ + /* Lite Transport is opened */ + ((p_dev->lite_cb.s.btu.transport_state == BTU_LITE_STACK_ACTIVE) || + (p_dev->lite_cb.s.btu.transport_state == BTU_LITE_TRANSPORT_ACTIVE))) + { + return 1; + } + return 0; +} + + +/******************************************************************************* + ** + ** Function btusb_lite_stop_all + ** + ** Description Stop all sound streams + ** + ** Returns void + ** + *******************************************************************************/ +void btusb_lite_stop_all(struct btusb *p_dev) +{ + int i; + + BTUSB_INFO(""); + + for (i = 0 ; i < BTA_AV_NUM_STRS ; i++) + { + btusb_lite_av_stop(p_dev, i, 0); + btusb_lite_av_remove(p_dev, i, 0, 0); + } +} + +/******************************************************************************* + ** + ** Function btusb_lite_ipc_hndl + ** + ** Description Handle received message from BTUSB Lite interface + ** + ** Returns none + ** + *******************************************************************************/ +static void btusb_lite_ipc_hndl(struct btusb *p_dev, BT_HDR *p_msg) +{ + BTUSB_INFO("IPC Buffer RxEvent=%s(0x%X) Length=%d\n", + btusb_lite_ipc_event_desc(p_msg->event), p_msg->event, p_msg->len); + + if (unlikely(dbgflags & BTUSB_DBG_MSG) && p_msg->len) + { + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + int len = p_msg->len>20?20:p_msg->len; + int i; + + for (i = 0 ; i < len ; i++, p++) + { + printk("%02X ", *p); + } + if (p_msg->len>20) + printk("...\n"); + else + printk("\n"); + } + + switch(p_msg->event) + { + case BT_EVT_TO_LM_HCI_CMD: + btusb_lite_ipc_hci_cmd_hndl(p_dev, p_msg); + /* NO GKI_freebuf here */ + break; + + case BT_EVT_TO_LM_HCI_ACL: + btusb_lite_ipc_hci_acl_hndl(p_dev, p_msg); + /* NO GKI_freebuf here */ + break; + + case BT_EVT_BTU_IPC_MGMT_EVT: + btusb_lite_ipc_mgt_hndl(p_dev, p_msg); + GKI_freebuf(p_msg); + break; + + case BT_EVT_BTU_IPC_BTU_EVT: + btusb_lite_ipc_btu_hndl(p_dev, p_msg); + GKI_freebuf(p_msg); + break; + + case BT_EVT_BTU_IPC_L2C_EVT: + btusb_lite_ipc_l2c_hndl(p_dev, p_msg); + GKI_freebuf(p_msg); + break; + + case BT_EVT_BTU_IPC_BTM_EVT: + btusb_lite_ipc_btm_hndl(p_dev, p_msg); + GKI_freebuf(p_msg); + break; + + case BT_EVT_BTU_IPC_AVDT_EVT: + btusb_lite_ipc_avdt_hndl(p_dev, p_msg); + GKI_freebuf(p_msg); + break; + + case BT_EVT_BTU_IPC_SLIP_EVT: + case BT_EVT_BTU_IPC_BTTRC_EVT: + case BT_EVT_BTU_IPC_BURST_EVT: + case BT_EVT_BTU_IPC_ACL_EVT: + case BT_EVT_BTU_IPC_L2C_MSG_EVT: + BTUSB_INFO("Event=0x%X Not expected\n", p_msg->event); + GKI_freebuf(p_msg); + break; + + default: + BTUSB_INFO("Event=0x%X Unknown\n", p_msg->event); + GKI_freebuf(p_msg); + break; + } +} + +/******************************************************************************* + ** + ** Function btusb_lite_ipc_hci_cmd_hndl + ** + ** Description Handle HCI CMD packet received from Lite interface + ** + ** Returns none + ** + *******************************************************************************/ +static void btusb_lite_ipc_hci_cmd_hndl(struct btusb *p_dev, BT_HDR *p_msg) +{ + UINT8 *p; + UINT16 hci_opcode; + UINT8 hci_status; + + /* First check if this HCI command must be caught by BTUSB */ + p = (UINT8 *)(p_msg + 1) + p_msg->offset; + + STREAM_TO_UINT16(hci_opcode, p); /* Extract HCI Opcode */ + + /* If the HCI Opcode must be caught */ + switch(hci_opcode) + { + case HCI_BRCM_PAUSE_TRANSPORT: + BTUSB_INFO("HCI TransportPause VSC (%X) caught\n", hci_opcode); + hci_status = HCI_SUCCESS; + btusb_lite_ipc_cmd_cplt_evt_send(p_dev, hci_opcode, + &hci_status, sizeof(hci_status)); + GKI_freebuf(p_msg); + return; + + case HCI_BRCM_TRANSPORT_RESUME: + BTUSB_INFO("HCI TransportResume VSC (%X) caught\n", hci_opcode); + + hci_status = HCI_SUCCESS; + btusb_lite_ipc_cmd_cplt_evt_send(p_dev, hci_opcode, + &hci_status, sizeof(hci_status)); + GKI_freebuf(p_msg); + return; + + default: + break; + } + + BTUSB_INFO("HCI OpCode=0x%04X Sent to BT Controller\n", hci_opcode); + + if (p_msg->offset < 1) + { + BTUSB_ERR("Offset=%d too small\n", p_msg->offset); + GKI_freebuf(p_msg); + return; + } + + p_msg->offset--; /* Reduce Offset to add H4 HCI Type (Command) */ + p_msg->len++; + p = (UINT8 *)(p_msg + 1) + p_msg->offset; + + UINT8_TO_STREAM(p, HCIT_TYPE_COMMAND); + + /* add the incoming data and notify the btusb_tx_task to process */ + GKI_enqueue(&p_dev->tx_queue, p_msg); + + // wake up tasklet + tasklet_schedule(&p_dev->tx_task); +} + +/******************************************************************************* + ** + ** Function btusb_lite_ipc_hci_acl_hndl + ** + ** Description Handle HCI ACL packet received from Lite interface + ** + ** Returns none + ** + *******************************************************************************/ +static void btusb_lite_ipc_hci_acl_hndl(struct btusb *p_dev, BT_HDR *p_msg) +{ + UINT8 *p; + + if (p_msg->offset < 1) + { + BTUSB_ERR("Offset=%d too small\n", p_msg->offset); + GKI_freebuf(p_msg); + return; + } + + p_msg->offset--; /* Reduce Offset to add H4 HCI Type (Command) */ + p_msg->len++; + p = (UINT8 *)(p_msg + 1) + p_msg->offset; + + UINT8_TO_STREAM(p, HCIT_TYPE_ACL_DATA); + + /* add the incoming data and notify the btusb_tx_task to process */ + GKI_enqueue(&p_dev->tx_queue, p_msg); + + // wake up tasklet + tasklet_schedule(&p_dev->tx_task); +} + +/******************************************************************************* + ** + ** Function btusb_lite_ipc_mgt_hndl + ** + ** Description Handle Mgt messages received from Lite interface + ** + ** Returns none + ** + *******************************************************************************/ +static void btusb_lite_ipc_mgt_hndl(struct btusb *p_dev, BT_HDR *p_msg) +{ + UINT8 cmd; + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT8 response[6]; + UINT8 *p_response = response; + + STREAM_TO_UINT8(cmd, p); /* Extract UIPC_MGMT Request */ + + switch(cmd) + { + case UIPC_OPEN_REQ: + BTUSB_INFO("IPC_MGT:OpenReq received\n"); + /* If Mgt already opened */ + if (p_dev->lite_cb.s.mgt.opened) + { + BTUSB_ERR("IPC_MGT Was already opened\n"); + UINT8_TO_STREAM(p_response, UIPC_STATUS_FAIL); /* Status */ + } + else + { + p_dev->lite_cb.s.mgt.opened = true; + UINT8_TO_STREAM(p_response, UIPC_STATUS_SUCCESS); /* Status */ + } + UINT16_TO_STREAM(p_response, UIPC_VERSION_MAJOR); /* version_major */ + UINT16_TO_STREAM(p_response, UIPC_VERSION_MINOR); /* version_minor */ + UINT8_TO_STREAM(p_response, BTM_SYNC_INFO_NUM_STR); /* Number of simultaneous streams supported by the light stack */ + btusb_lite_ipc_rsp_send(p_dev, BT_EVT_BTU_IPC_MGMT_EVT, UIPC_OPEN_RSP, + response, p_response - response); + break; + + case UIPC_CLOSE_REQ: + BTUSB_INFO("IPC_MGT:CloseReq received\n"); + p_dev->lite_cb.s.mgt.opened = false; + btusb_lite_ipc_rsp_send(p_dev, BT_EVT_BTU_IPC_MGMT_EVT, UIPC_CLOSE_RSP, NULL, 0); + break; + + default: + BTUSB_INFO("Unknown IPC_MGT command=%d\n", cmd); + break; + } +} + +/******************************************************************************* + ** + ** Function btusb_lite_ipc_btu_hndl + ** + ** Description Handle BTU messages received from Lite interface + ** + ** Returns none + ** + *******************************************************************************/ +static void btusb_lite_ipc_btu_hndl(struct btusb *p_dev, BT_HDR *p_msg) +{ + UINT8 cmd; + UINT8 byte; + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + + STREAM_TO_UINT8(cmd, p); /* Extract UIPC_BTU Request */ + + switch(cmd) + { + case BTU_IPC_CMD_SET_TRANSPORT_STATE: + STREAM_TO_UINT8(byte, p); /* Extract Param */ + BTUSB_INFO("IPC_BTU:SetTransportState (%d) received\n", byte); + p_dev->lite_cb.s.btu.transport_state = byte; + break; + + case BTU_IPC_CMD_DISABLE_TRANSPORT: + STREAM_TO_UINT8(byte, p); /* Extract Param */ + BTUSB_INFO("IPC_BTU:DisableTransport (%d) received\n", byte); + p_dev->lite_cb.s.btu.transport_disabled = byte; + break; + + default: + BTUSB_INFO("Unknown IPC_BTU command=%d\n", cmd); + break; + } +} + +/******************************************************************************* + ** + ** Function btusb_lite_ipc_btm_hndl + ** + ** Description Handle BTM messages received from Lite interface + ** + ** Returns none + ** + *******************************************************************************/ +static void btusb_lite_ipc_btm_hndl(struct btusb *p_dev, BT_HDR *p_msg) +{ + UINT8 cmd; + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + tBTA_AV_SYNC_INFO sync_info; + UINT8 scb_idx; + tBTA_AV_AUDIO_CODEC_INFO codec_cfg; + UINT8 start_stop_flag; + UINT8 multi_av_supported; + UINT16 curr_mtu; + UINT8 audio_open_cnt; + UINT8 response[1]; + UINT8 *p_response = response; + + STREAM_TO_UINT8(cmd, p); /* Extract UIPC_BTU Request */ + + switch(cmd) + { + case BTA_AV_SYNC_TO_LITE_REQ: + BTUSB_INFO("IPC_BTM:BtaAvSyncToLiteReq (%d)\n", cmd); + STREAM_TO_UINT8(sync_info.avdt_handle, p); + STREAM_TO_UINT8(sync_info.chnl, p); + STREAM_TO_UINT8(sync_info.codec_type, p); + STREAM_TO_UINT8(sync_info.cong, p); + STREAM_TO_UINT8(sync_info.hdi, p); + STREAM_TO_UINT8(sync_info.hndl, p); + STREAM_TO_UINT8(sync_info.l2c_bufs, p); + STREAM_TO_UINT16(sync_info.l2c_cid, p); + STREAM_TO_ARRAY (sync_info.peer_addr, p, BD_ADDR_LEN); + STREAM_TO_UINT8(multi_av_supported, p); + STREAM_TO_UINT16(curr_mtu, p); + BTUSB_INFO("avdt_hdl=0x%x chnl=0x%x codec_type=0x%x cong=%d hdi=%d hndl=0x%x\n", + sync_info.avdt_handle, sync_info.chnl, sync_info.codec_type, + sync_info.cong, sync_info.hdi, sync_info.hndl); + BTUSB_INFO("l2c_bufs=%d l2c_cid=0x%x multi_av_sup=%d curr_mtu=%d\n", + sync_info.l2c_bufs, sync_info.l2c_cid, multi_av_supported, curr_mtu); + BTUSB_INFO("BdAddr=%02X-%02X-%02X-%02X-%02X-%02X\n", + sync_info.peer_addr[0], sync_info.peer_addr[1],sync_info.peer_addr[2], + sync_info.peer_addr[3], sync_info.peer_addr[4],sync_info.peer_addr[5]); + + /* Add AV channel */ + btusb_lite_av_add(p_dev, &sync_info, multi_av_supported, curr_mtu); + + UINT8_TO_STREAM(p_response, sync_info.hdi); + btusb_lite_ipc_rsp_send(p_dev, BT_EVT_BTU_IPC_BTM_EVT, BTA_AV_SYNC_TO_LITE_RESP, + response, p_response - response); + break; + + case BTA_AV_STR_START_TO_LITE_REQ: + BTUSB_INFO("IPC_BTM:BtaAvStrStartToLiteReq (%d)\n", cmd); + STREAM_TO_UINT8(scb_idx, p); + STREAM_TO_UINT8(audio_open_cnt, p); + STREAM_TO_UINT16 (codec_cfg.bit_rate, p); + STREAM_TO_UINT16 (codec_cfg.bit_rate_busy, p); + STREAM_TO_UINT16 (codec_cfg.bit_rate_swampd, p); + STREAM_TO_UINT8 (codec_cfg.busy_level, p); + STREAM_TO_ARRAY (codec_cfg.codec_info, p, AVDT_CODEC_SIZE); + STREAM_TO_UINT8 (codec_cfg.codec_type, p); + STREAM_TO_UINT8 (start_stop_flag, p); + BTUSB_INFO(" scb_idx=%d audio_open_cnt=%d busy_level=%d start_stop_flag=%d\n", + scb_idx, audio_open_cnt, + codec_cfg.busy_level, start_stop_flag); + BTUSB_INFO(" codec_type=0x%x bit_rate=%d busy_level=%d bit_rate_busy=%d bit_rate_swampd=%d\n", + codec_cfg.codec_type, codec_cfg.bit_rate, codec_cfg.busy_level, + codec_cfg.bit_rate_busy, codec_cfg.bit_rate_swampd); + BTUSB_INFO(" CodecInfo:%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X", + codec_cfg.codec_info[0], codec_cfg.codec_info[1], codec_cfg.codec_info[2], + codec_cfg.codec_info[3], codec_cfg.codec_info[4], codec_cfg.codec_info[5], + codec_cfg.codec_info[6], codec_cfg.codec_info[7], codec_cfg.codec_info[8], + codec_cfg.codec_info[9]); + + /* Start AV */ + btusb_lite_av_start(p_dev, scb_idx, start_stop_flag, audio_open_cnt, &codec_cfg); + + /* Send the response */ + UINT8_TO_STREAM(p_response, scb_idx); + btusb_lite_ipc_rsp_send(p_dev, BT_EVT_BTU_IPC_BTM_EVT, BTA_AV_STR_START_TO_LITE_RESP, + response, p_response - response); + break; + + case BTA_AV_STR_STOP_TO_LITE_REQ: + BTUSB_INFO("IPC_BTM:BtaAvStrStopToLiteReq (%d)\n", cmd); + STREAM_TO_UINT8(scb_idx, p); + STREAM_TO_UINT8(audio_open_cnt, p); + BTUSB_INFO(" scb_idx=%d audio_open_cnt=%d\n", scb_idx, audio_open_cnt); + + btusb_lite_av_stop(p_dev, scb_idx, audio_open_cnt); + + UINT8_TO_STREAM(p_response, scb_idx); + btusb_lite_ipc_rsp_send(p_dev, BT_EVT_BTU_IPC_BTM_EVT, BTA_AV_STR_STOP_TO_LITE_RESP, + response, p_response - response); + break; + + case BTA_AV_STR_SUSPEND_TO_LITE_REQ: + BTUSB_INFO("IPC_BTM:BtaAvStrSuspendToLiteReq (%d)\n", cmd); + STREAM_TO_UINT8(scb_idx, p); + STREAM_TO_UINT8(audio_open_cnt, p); + BTUSB_INFO(" scb_idx=%d audio_open_cnt=%d\n", scb_idx, audio_open_cnt); + btusb_lite_av_suspend(p_dev, scb_idx, audio_open_cnt); + + UINT8_TO_STREAM(p_response, scb_idx); + btusb_lite_ipc_rsp_send(p_dev, BT_EVT_BTU_IPC_BTM_EVT, BTA_AV_STR_SUSPEND_TO_LITE_RESP, + response, p_response - response); + break; + + case BTA_AV_STR_CLEANUP_TO_LITE_REQ: + BTUSB_INFO("IPC_BTM:BtaAvStrCleanupToLiteReq (%d)\n", cmd); + STREAM_TO_UINT8(scb_idx, p); + STREAM_TO_UINT8(audio_open_cnt, p); + STREAM_TO_UINT16(curr_mtu, p); + BTUSB_INFO(" scb_idx=%d audio_open_cnt=%d curr_mtu=%d\n", scb_idx, audio_open_cnt, + curr_mtu); + + btusb_lite_av_remove(p_dev, scb_idx, audio_open_cnt, curr_mtu); + + UINT8_TO_STREAM(p_response, scb_idx); + btusb_lite_ipc_rsp_send(p_dev, BT_EVT_BTU_IPC_BTM_EVT, BTA_AV_STR_CLEANUP_TO_LITE_RESP, + response, p_response - response); + break; + + /* BTC commands (unexpected) */ + case A2DP_START_REQ: + case A2DP_START_RESP: + case A2DP_STOP_REQ: + case A2DP_STOP_RESP: + case A2DP_CLEANUP_REQ: + case A2DP_CLEANUP_RESP: + case A2DP_SUSPEND_REQ: + case A2DP_SUSPEND_RESP: + case A2DP_JITTER_DONE_IND: + + /* BTA AV Response commands (unexpected) */ + case BTA_AV_SYNC_TO_LITE_RESP: + case BTA_AV_STR_START_TO_LITE_RESP: + case BTA_AV_STR_STOP_TO_LITE_RESP: + case BTA_AV_STR_CLEANUP_TO_LITE_RESP: + case BTA_AV_STR_SUSPEND_TO_LITE_RESP: + case BTA_AV_SYNC_ERROR_RESP: + BTUSB_INFO("IPC_BTM: Unexpected Cmd=%d received\n", cmd); + break; + + default: + BTUSB_INFO("IPC_BTM: Unknown Cmd=%d received\n", cmd); + break; + } +} + +/******************************************************************************* + ** + ** Function btusb_lite_ipc_avdt_hndl + ** + ** Description Handle AVDT messages received from Lite interface + ** + ** Returns none + ** + *******************************************************************************/ +static void btusb_lite_ipc_avdt_hndl(struct btusb *p_dev, BT_HDR *p_msg) +{ + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + tAVDT_SYNC_INFO sync_req; + tAVDT_SYNC_INFO sync_rsp; + int stream; + struct btusb_lite_avdt_scb *p_scb; + UINT8 avdt_status; + + STREAM_TO_UINT8(sync_req.op_code, p); /* Extract UIPC_AVDT Request */ + + switch(sync_req.op_code) + { + case AVDT_SYNC_TO_LITE_REQ: + BTUSB_INFO("IPC_AVDT:AvdtSyncToLiteReq (%d) received\n", sync_req.op_code); + break; + + case AVDT_RESYNC_TO_LITE_REQ: + BTUSB_INFO("IPC_AVDT:AvdtReSyncToLiteReq (%d) received\n", sync_req.op_code); + break; + + case AVDT_SYNC_TO_FULL_REQ: + BTUSB_INFO("IPC_AVDT:AvdtSyncToFullReq (%d) received\n", sync_req.op_code); + break; + + case AVDT_REMOVE_TO_LITE_REQ: + BTUSB_INFO("IPC_AVDT:AvdtRemoveToLiteReq (%d)\n", sync_req.op_code); + break; + + case AVDT_SYNC_CP_TO_LITE_REQ: + BTUSB_INFO("IPC_AVDT:AvdtSyncCpToLiteReq (%d)\n", sync_req.op_code); + break; + + case AVDT_SYNC_TO_LITE_RESP: + case AVDT_RESYNC_TO_LITE_RESP: + case AVDT_SYNC_TO_FULL_RESP: + case AVDT_REMOVE_TO_LITE_RESP: + case AVDT_SYNC_CP_TO_LITE_RESP: + BTUSB_INFO("IPC_AVDT: Unexpected Cmd=%d received\n", sync_req.op_code); + sync_rsp.op_code = 0xFF; + sync_rsp.status = AVDT_SYNC_FAILURE; + btusb_lite_ipc_avdt_sync_info_send(p_dev, &sync_rsp); + return; + + default: + BTUSB_INFO("IPC_AVDT: Unknown Cmd=%d received\n", sync_req.op_code); + sync_rsp.op_code = 0xFE; + sync_rsp.status = AVDT_SYNC_FAILURE; + btusb_lite_ipc_avdt_sync_info_send(p_dev, &sync_rsp); + return; + } + + /* Extract parameters */ + STREAM_TO_UINT8(sync_req.status, p); + + /* Decode Sync CP parameters */ + if (sync_req.op_code == AVDT_SYNC_CP_TO_LITE_REQ) + { + for(stream = 0; stream < BTM_SYNC_INFO_NUM_STR; stream++ ) + { + STREAM_TO_UINT8(sync_req.scb_info[stream].handle, p); + STREAM_TO_UINT16(sync_req.scb_info[stream].cp.id, p); + STREAM_TO_UINT8(sync_req.scb_info[stream].cp.scms_hdr, p); + if (sync_req.scb_info[stream].handle) + { + BTUSB_INFO(" stream[%d]: handle=0x%x cp_id=0x%04X scms_hdr=0x%x\n", + stream, sync_req.scb_info[stream].handle, + sync_req.scb_info[stream].cp.id, + sync_req.scb_info[stream].cp.scms_hdr); + } + else + { + BTUSB_INFO(" stream[%d]: No Data\n", stream); + } + } + } + /* Decode other Sync message parameters */ + else + { + for(stream = 0; stream < BTM_SYNC_INFO_NUM_STR; stream++ ) + { + STREAM_TO_UINT8(sync_req.scb_info[stream].handle, p); + STREAM_TO_BDADDR(sync_req.scb_info[stream].peer_addr, p) + STREAM_TO_UINT16(sync_req.scb_info[stream].local_cid, p); + STREAM_TO_UINT16(sync_req.scb_info[stream].peer_mtu, p); + STREAM_TO_UINT8(sync_req.scb_info[stream].mux_tsid_media, p); + STREAM_TO_UINT16(sync_req.scb_info[stream].media_seq, p); + if (sync_req.scb_info[stream].handle) + { + BTUSB_INFO(" stream[%d]:\n", stream); + BTUSB_INFO(" BdAddr=%02X-%02X-%02X-%02X-%02X-%02X\n", + sync_req.scb_info[stream].peer_addr[0], + sync_req.scb_info[stream].peer_addr[1], + sync_req.scb_info[stream].peer_addr[2], + sync_req.scb_info[stream].peer_addr[3], + sync_req.scb_info[stream].peer_addr[4], + sync_req.scb_info[stream].peer_addr[5]); + BTUSB_INFO(" handle=0x%x local_cid=0x%x peer_mtu=%d mux_tsid_media=%d media_seq=%d\n", + sync_req.scb_info[stream].handle, + sync_req.scb_info[stream].local_cid, + sync_req.scb_info[stream].peer_mtu, + sync_req.scb_info[stream].mux_tsid_media, + sync_req.scb_info[stream].media_seq); + } + else + { + BTUSB_INFO(" stream[%d]: No data\n", stream); + } + } + } + memset(&sync_rsp, 0, sizeof(sync_rsp)); + + switch(sync_req.op_code) + { + case AVDT_SYNC_TO_LITE_REQ: + sync_rsp.op_code = AVDT_SYNC_TO_LITE_RESP; + + for(stream = 0; stream < BTM_SYNC_INFO_NUM_STR; stream++) + { + if(sync_req.scb_info[stream].handle == 0) + continue; + + if(btusb_lite_avdt_init_scb(p_dev, &(sync_req.scb_info[stream])) != AVDT_SYNC_SUCCESS) + { + sync_rsp.status = AVDT_SYNC_FAILURE; + btusb_lite_ipc_avdt_sync_info_send(p_dev, &sync_rsp); + return; + } + } + sync_rsp.status = AVDT_SYNC_SUCCESS; + btusb_lite_ipc_avdt_sync_info_send(p_dev, &sync_rsp); + break; + + case AVDT_RESYNC_TO_LITE_REQ: + sync_rsp.op_code = AVDT_RESYNC_TO_LITE_RESP; + + for(stream = 0; stream < BTM_SYNC_INFO_NUM_STR; stream++) + { + if(sync_req.scb_info[stream].handle == 0) + continue; + + if((p_scb = btusb_lite_avdt_scb_by_hdl(p_dev, sync_req.scb_info[stream].handle)) != NULL) + { + memcpy(p_scb->p_ccb->peer_addr, sync_req.scb_info[stream].peer_addr, BD_ADDR_LEN); + p_scb->p_ccb->lcid = sync_req.scb_info[stream].local_cid; + p_scb->p_ccb->peer_mtu = sync_req.scb_info[stream].peer_mtu; + p_scb->mux_tsid_media = sync_req.scb_info[stream].mux_tsid_media; + p_scb->media_seq = sync_req.scb_info[stream].media_seq; + } + else if(btusb_lite_avdt_init_scb(p_dev, &(sync_req.scb_info[stream])) != AVDT_SYNC_SUCCESS) + { + sync_rsp.status = AVDT_SYNC_FAILURE; + btusb_lite_ipc_avdt_sync_info_send(p_dev, &sync_rsp); + return; + } + } + sync_rsp.status = AVDT_SYNC_SUCCESS; + btusb_lite_ipc_avdt_sync_info_send(p_dev, &sync_rsp); + break; + + case AVDT_SYNC_TO_FULL_REQ: + sync_rsp.op_code = AVDT_SYNC_TO_FULL_RESP; + + for(stream = 0; stream < BTM_SYNC_INFO_NUM_STR; stream++) + { + if(sync_req.scb_info[stream].handle == 0) + { + sync_rsp.scb_info[stream].handle = 0; + continue; + } + + if(btusb_lite_avdt_remove_scb(p_dev, sync_req.scb_info[stream].handle, + &sync_rsp.scb_info[stream]) != AVDT_SYNC_SUCCESS) + { + sync_rsp.status = AVDT_SYNC_FAILURE; + btusb_lite_ipc_avdt_sync_info_send(p_dev, &sync_rsp); + return; + } + } + sync_rsp.status = AVDT_SYNC_SUCCESS; + btusb_lite_ipc_avdt_sync_info_send(p_dev, &sync_rsp); + break; + + case AVDT_REMOVE_TO_LITE_REQ: + sync_rsp.op_code = AVDT_REMOVE_TO_LITE_RESP; + for(stream = 0; stream < BTM_SYNC_INFO_NUM_STR; stream++) + { + if(sync_req.scb_info[stream].handle == 0) + continue; + + if(btusb_lite_avdt_remove_scb(p_dev, sync_req.scb_info[stream].handle, + &sync_rsp.scb_info[stream]) != AVDT_SYNC_SUCCESS) + { + sync_rsp.status = AVDT_SYNC_FAILURE; + btusb_lite_ipc_avdt_sync_info_send(p_dev, &sync_rsp); + return; + } + } + sync_rsp.status = AVDT_SYNC_SUCCESS; + btusb_lite_ipc_avdt_sync_info_send(p_dev, &sync_rsp); + break; + + case AVDT_SYNC_CP_TO_LITE_REQ: + sync_rsp.op_code = AVDT_SYNC_CP_TO_LITE_RESP; + for(stream = 0; stream < BTM_SYNC_INFO_NUM_STR; stream++) + { + if(sync_req.scb_info[stream].handle == 0) + continue; + + switch(sync_req.scb_info[stream].cp.id) + { + case AVDT_SYNC_CP_ID_NONE: + avdt_status = btusb_lite_avdt_cp_set_scms(p_dev, + sync_req.scb_info[stream].handle, FALSE, 0x00); + break; + + case AVDT_SYNC_CP_ID_SCMS: + avdt_status = btusb_lite_avdt_cp_set_scms(p_dev, + sync_req.scb_info[stream].handle, TRUE, + sync_req.scb_info[stream].cp.scms_hdr); + break; + + default: + avdt_status = AVDT_SYNC_FAILURE; + break; + } + if (avdt_status != AVDT_SYNC_SUCCESS) + { + sync_rsp.status = AVDT_SYNC_FAILURE; + btusb_lite_ipc_avdt_sync_info_send(p_dev, &sync_rsp); + return; + } + } + sync_rsp.status = AVDT_SYNC_SUCCESS; + btusb_lite_ipc_avdt_sync_info_send(p_dev, &sync_rsp); + break; + } +} + +/******************************************************************************* + ** + ** Function btusb_lite_ipc_avdt_sync_info_send + ** + ** Description Build and send an IPC AVDT Sync Info Response + ** + ** Returns none + ** + *******************************************************************************/ +static void btusb_lite_ipc_avdt_sync_info_send(struct btusb *p_dev, tAVDT_SYNC_INFO *p_sync_rsp) +{ + int stream; + UINT8 response[BTUSB_LITE_IPC_AVDT_SYNC_INFO_RSP_LEN]; + UINT8 *p_response = response; + + UINT8_TO_STREAM(p_response, p_sync_rsp->status); + + if (p_sync_rsp->op_code != AVDT_SYNC_CP_TO_LITE_RESP) + { + for(stream = 0; stream < BTM_SYNC_INFO_NUM_STR; stream++) + { + UINT8_TO_STREAM(p_response, p_sync_rsp->scb_info[stream].handle); + BDADDR_TO_STREAM(p_response, p_sync_rsp->scb_info[stream].peer_addr) + UINT16_TO_STREAM(p_response, p_sync_rsp->scb_info[stream].local_cid); + UINT16_TO_STREAM(p_response, p_sync_rsp->scb_info[stream].peer_mtu); + UINT8_TO_STREAM(p_response, p_sync_rsp->scb_info[stream].mux_tsid_media); + UINT16_TO_STREAM(p_response, p_sync_rsp->scb_info[stream].media_seq); + } + } + btusb_lite_ipc_rsp_send(p_dev, BT_EVT_BTU_IPC_AVDT_EVT, p_sync_rsp->op_code, + response, p_response - response); +} + +/******************************************************************************* + ** + ** Function btusb_lite_ipc_l2c_hndl + ** + ** Description Handle L2C messages received from Lite interface + ** + ** Returns none + ** + *******************************************************************************/ +static void btusb_lite_ipc_l2c_hndl(struct btusb *p_dev, BT_HDR *p_msg) +{ + UINT8 cmd; + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT8 response[3]; + UINT8 *p_response = response; + struct btusb_lite_l2c_cb *p_l2c = &p_dev->lite_cb.s.l2c; + int stream; + tL2C_STREAM_INFO l2c_stream; + UINT16 local_cid; + UINT8 num_stream; + + STREAM_TO_UINT8(cmd, p); /* Extract UIPC_MGMT Request */ + + switch(cmd) + { + case L2C_SYNC_TO_LITE_REQ: + BTUSB_INFO("IPC_L2C:L2cSyncToLiteReq (%d) received\n", L2C_SYNC_TO_LITE_REQ); + STREAM_TO_UINT16(p_l2c->light_xmit_quota, p); + STREAM_TO_UINT16(p_l2c->acl_data_size, p); + STREAM_TO_UINT16(p_l2c->non_flushable_pbf, p); + STREAM_TO_UINT8(p_l2c->multi_av_data_cong_start, p); + STREAM_TO_UINT8(p_l2c->multi_av_data_cong_end, p); + STREAM_TO_UINT8(p_l2c->multi_av_data_cong_discard, p); + STREAM_TO_UINT8(num_stream, p); + BTUSB_INFO("Xquota=%d AclSize=%d NFpbf=%d congStart=%d congEnd=%d congDisc=%d NbStr=%d\n", + p_l2c->light_xmit_quota, p_l2c->acl_data_size, p_l2c->non_flushable_pbf, + p_l2c->multi_av_data_cong_start, p_l2c->multi_av_data_cong_end, + p_l2c->multi_av_data_cong_discard, num_stream); + + /* Start building the response */ + UINT16_TO_STREAM(p_response, p_l2c->light_xmit_unacked); + UINT8_TO_STREAM(p_response, num_stream); + + for(stream = 0; stream < num_stream; stream++) + { + STREAM_TO_UINT16(l2c_stream.local_cid, p); + STREAM_TO_UINT16(l2c_stream.remote_cid, p); + STREAM_TO_UINT16(l2c_stream.out_mtu, p); + STREAM_TO_UINT16(l2c_stream.handle, p); + STREAM_TO_UINT16(l2c_stream.link_xmit_quota, p); + STREAM_TO_UINT8(l2c_stream.is_flushable, p); + BTUSB_INFO(" Stream[%d]:lcid=0x%X rcid=0x%X mtu=%d handle=0x%X xmit_quota=%d flushable=%d\n", + stream, l2c_stream.local_cid, l2c_stream.remote_cid, + l2c_stream.out_mtu, l2c_stream.handle, + l2c_stream.link_xmit_quota, l2c_stream.is_flushable); + + /* Resume building the response */ + UINT16_TO_STREAM(p_response, l2c_stream.local_cid); + + /* Synchronize (add) this L2CAP Stream */ + if (btusb_lite_l2c_add(p_dev, &l2c_stream) < 0) + { + UINT8_TO_STREAM(p_response, L2C_SYNC_FAILURE); + } + else + { + UINT8_TO_STREAM(p_response, L2C_SYNC_SUCCESS); + } + } + btusb_lite_ipc_rsp_send(p_dev, BT_EVT_BTU_IPC_L2C_EVT, L2C_SYNC_TO_LITE_RESP, + response, p_response - response); + break; + + case L2C_REMOVE_TO_LITE_REQ: + BTUSB_INFO("IPC_L2C:L2cRemoveToLiteReq (%d) received\n", L2C_REMOVE_TO_LITE_REQ); + STREAM_TO_UINT16(p_l2c->light_xmit_quota, p); + STREAM_TO_UINT8(num_stream, p); + BTUSB_INFO("Xquota=%d NbStr=%d\n", p_l2c->light_xmit_quota, num_stream); + + /* Start building the response */ + UINT16_TO_STREAM(p_response, p_l2c->light_xmit_unacked); + UINT8_TO_STREAM(p_response, num_stream); + + for(stream = 0; stream < num_stream; stream++) + { + STREAM_TO_UINT16(local_cid, p); + BTUSB_INFO(" Stream[%d]:lcid=0x%X\n", stream, p_l2c->ccb[stream].local_cid); + + /* Resume building the response */ + UINT16_TO_STREAM(p_response, local_cid); + + /* Synchronize (remove) this L2CAP Stream */ + if (btusb_lite_l2c_remove(p_dev, local_cid) < 0) + { + UINT8_TO_STREAM(p_response, L2C_SYNC_FAILURE); + } + else + { + UINT8_TO_STREAM(p_response, L2C_SYNC_SUCCESS); + } + } + + /* Send the response to the full stack */ + btusb_lite_ipc_rsp_send(p_dev, BT_EVT_BTU_IPC_L2C_EVT, L2C_REMOVE_TO_LITE_RESP, + response, p_response - response); + break; + + default: + BTUSB_INFO("Unknown IPC_MGT command=%d\n", cmd); + break; + } +} + +/******************************************************************************* + ** + ** Function btusb_lite_ipc_rsp_send + ** + ** Description Send an Response over Lite interface. + ** + ** Returns Void + ** + *******************************************************************************/ +static void btusb_lite_ipc_rsp_send(struct btusb *p_dev, + UINT16 event, UINT8 op_code, UINT8 *p_param, UINT8 param_len) +{ + BT_HDR *p_msg; + UINT16 size = param_len + BTUSB_LITE_IPC_HDR_SIZE + sizeof(UINT8); + UINT8 *p; + + BTUSB_INFO("Event=%s(0x%X), opcode=%d len=%d\n", btusb_lite_ipc_event_desc(event), + event, op_code, param_len); + + /* Get a buffer from the pool */ + p_msg = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + size); + if(unlikely(p_msg == NULL)) + { + BTUSB_ERR("Unable to get GKI buffer\n"); + return; + } + + if (unlikely(dbgflags & BTUSB_GKI_CHK_MSG) && + unlikely(GKI_buffer_status(p_msg) != BUF_STATUS_UNLINKED)) + { + BTUSB_ERR("buffer != BUF_STATUS_UNLINKED 0x%p\n", p_msg); + return; + } + + p_msg->offset = 0; + p_msg->event = 0; + p_msg->len = size; + + p = (UINT8 *)(p_msg + 1); + + UINT16_TO_STREAM(p, param_len + BTUSB_LITE_IPC_HDR_EVT_SIZE + sizeof(UINT8)); /* Length */ + UINT16_TO_STREAM(p, event); /* Event */ + UINT8_TO_STREAM(p, op_code); /* Opcode */ + if (p_param) + { + ARRAY_TO_STREAM(p, p_param, param_len) + } + + /* Send message to User Space */ + btusb_lite_ipc_sent_to_user(p_dev, p_msg); +} + +/******************************************************************************* + ** + ** Function btusb_lite_ipc_cmd_cplt_evt_send + ** + ** Description Send an UIPC_Over_HCI VSC Cmd Complete. + ** + ** Returns Void + ** + *******************************************************************************/ +static void btusb_lite_ipc_cmd_cplt_evt_send(struct btusb *p_dev, + UINT16 opcode, UINT8 *p_param, UINT8 param_len) +{ + BT_HDR *p_msg; + UINT16 size = param_len + BTUSB_LITE_IPC_HDR_SIZE + 5; + UINT8 *p; + + /* Get a buffer from the pool */ + p_msg = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + size); + if(unlikely(p_msg == NULL)) + { + BTUSB_ERR("Unable to get GKI buffer\n"); + return; + } + + if (unlikely(dbgflags & BTUSB_GKI_CHK_MSG) && + unlikely(GKI_buffer_status(p_msg) != BUF_STATUS_UNLINKED)) + { + BTUSB_ERR("buffer != BUF_STATUS_UNLINKED 0x%p\n", p_msg); + return; + } + + p_msg->offset = 0; + p_msg->event = 0; + p_msg->len = size; + + p = (UINT8 *)(p_msg + 1); + + UINT16_TO_STREAM(p, param_len + BTUSB_LITE_IPC_HDR_EVT_SIZE + 5); /* length */ + UINT16_TO_STREAM(p, BT_EVT_TO_BTU_HCI_EVT); /* IPC OpCode */ + UINT8_TO_STREAM(p, HCI_COMMAND_COMPLETE_EVT); /* Command Complete Evt */ + UINT8_TO_STREAM(p, param_len + 3); /* Param Length (param + NumCmd + OpCode) */ + UINT8_TO_STREAM(p, 0x01); /* HCI Num Command */ + UINT16_TO_STREAM(p, opcode); /* HCI OpCode */ + + if (p_param) + { + ARRAY_TO_STREAM(p, p_param, param_len) + } + + /* Send message to User Space */ + btusb_lite_ipc_sent_to_user(p_dev, p_msg); +} + +/******************************************************************************* + ** + ** Function btusb_lite_ipc_sent_to_user + ** + ** Description Send message to User Space (via IPC Interface). + ** + ** Returns status: <> 0 if the event must be send to user space (BSA) + ** 0 if the event is handled + ** + *******************************************************************************/ +static void btusb_lite_ipc_sent_to_user(struct btusb *p_dev, BT_HDR *p_msg) +{ + /* Update Lite Statistics */ + p_dev->lite_cb.s.stat.event_bytes += p_msg->len; + p_dev->lite_cb.s.stat.event_completed++; + + /* Enqueue message in IPC queue */ + GKI_enqueue(&p_dev->lite_cb.s.to_app.ipc_queue, p_msg); + + /* WakeUp IPC read */ + wake_up_interruptible(&p_dev->rx_wait_q); +} + +/******************************************************************************* + ** + ** Function btusb_lite_ipc_event_desc + ** + ** Description Get IPC Event description + ** + ** Returns status: <> 0 if the event must be send to user space (BSA) + ** 0 if the event is handled + ** + *******************************************************************************/ +static char *btusb_lite_ipc_event_desc(UINT16 event) +{ + switch(event) + { + case BT_EVT_TO_LM_HCI_CMD: + return "BT_EVT_TO_LM_HCI_CMD"; + case BT_EVT_TO_LM_HCI_ACL: + return "BT_EVT_TO_LM_HCI_ACL"; + case BT_EVT_BTU_IPC_MGMT_EVT: + return "BT_EVT_BTU_IPC_MGMT_EVT"; + case BT_EVT_BTU_IPC_BTU_EVT: + return "BT_EVT_BTU_IPC_BTU_EVT"; + case BT_EVT_BTU_IPC_L2C_EVT: + return "BT_EVT_BTU_IPC_L2C_EVT"; + case BT_EVT_BTU_IPC_ACL_EVT: + return "BT_EVT_BTU_IPC_ACL_EVT"; + case BT_EVT_BTU_IPC_BTM_EVT: + return "BT_EVT_BTU_IPC_BTM_EVT"; + case BT_EVT_BTU_IPC_L2C_MSG_EVT: + return "BT_EVT_BTU_IPC_L2C_MSG_EVT"; + case BT_EVT_BTU_IPC_AVDT_EVT: + return "BT_EVT_BTU_IPC_AVDT_EVT"; + case BT_EVT_BTU_IPC_SLIP_EVT: + return "BT_EVT_BTU_IPC_SLIP_EVT"; + case BT_EVT_BTU_IPC_BTTRC_EVT: + return "BT_EVT_BTU_IPC_BTTRC_EVT"; + case BT_EVT_BTU_IPC_BURST_EVT: + return "BT_EVT_BTU_IPC_BURST_EVT"; + default: + return "Unknown Event"; + } +} + diff --git a/src/btusb_lite_av.c b/src/btusb_lite_av.c new file mode 100755 index 0000000..dff6cc8 --- a/dev/null +++ b/src/btusb_lite_av.c @@ -0,0 +1,1365 @@ +/* + * + * btusb_lite_av.c + * + * + * + * Copyright (C) 2011-2014 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ + +#include <linux/moduleparam.h> +#include "btusb.h" +#include "bd.h" +#include "btpcm_api.h" +#include "btsbc_api.h" +#ifdef BTUSB_LITE_SEC +#include "btsec_api.h" +#endif + +struct btusb_lite_av_sbc_param +{ + int frequency; + unsigned char nb_blocks; + unsigned char nb_subbands; + unsigned char mode; + unsigned char allocation; + unsigned char bitpool_min; + unsigned char bitpool_max; +}; + + +/* Codec (From BT Spec) */ +#define A2D_MEDIA_TYPE_AUDIO 0x00 + +#define A2D_MEDIA_CT_SBC 0x00 /* SBC Codec */ +#define A2D_MEDIA_CT_VEND 0xFF /* Vendor specific */ + +/* SBC Codec (From BT Spec) */ +#define CODEC_SBC_LOSC 6 + +#define CODEC_SBC_FREQ_MASK 0xF0 +#define CODEC_SBC_FREQ_48 0x10 +#define CODEC_SBC_FREQ_44 0x20 +#define CODEC_SBC_FREQ_32 0x40 +#define CODEC_SBC_FREQ_16 0x80 + +#define CODEC_MODE_MASK 0x0F +#define CODEC_MODE_JOIN_STEREO 0x01 +#define CODEC_MODE_STEREO 0x02 +#define CODEC_MODE_DUAL 0x04 +#define CODEC_MODE_MONO 0x08 + +#define CODEC_SBC_BLOCK_MASK 0xF0 +#define CODEC_SBC_BLOCK_16 0x10 +#define CODEC_SBC_BLOCK_12 0x20 +#define CODEC_SBC_BLOCK_8 0x40 +#define CODEC_SBC_BLOCK_4 0x80 + +#define CODEC_SBC_NBBAND_MASK 0x0C +#define CODEC_SBC_NBBAND_8 0x04 +#define CODEC_SBC_NBBAND_4 0x08 + +#define CODEC_SBC_ALLOC_MASK 0x03 +#define CODEC_SBC_ALLOC_LOUDNESS 0x01 +#define CODEC_SBC_ALLOC_SNR 0x02 + +/* SEC codec */ +#ifdef BTUSB_LITE_SEC +#define A2D_MEDIA_CT_SEC 0x07 /* Internal SEC Codec type */ + +struct btusb_lite_av_sec_param +{ + int frequency; + unsigned char mode; +}; +#endif + + +/* + * Globals + */ +int pcm0_mute = 0; +module_param(pcm0_mute, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); +MODULE_PARM_DESC(pcm0_mute, "Mute PCM channel 0"); + +#define SILENSE_PCM_BUF_SIZE (2 * 128) /* 128 samples, Stereo */ +static const unsigned short btusb_lite_silence_pcm[SILENSE_PCM_BUF_SIZE] = {0}; + +/* + * Local functions + */ +static int btusb_lite_av_parse_sbc_codec(struct btusb_lite_av_sbc_param *p_sbc, UINT8 *p_codec); +static int btusb_lite_sbc_get_bitpool(struct btusb_lite_av_sbc_param *p_sbc_param, int target_bitrate); +static void btusb_lite_av_pcm_cback(void *p_opaque, void *p_data, int nb_pcm_frames); +static void btusb_lite_av_send_packet(struct btusb *p_dev, BT_HDR *p_msg); + +static int btusb_lite_av_sbc_start(struct btusb *p_dev, UINT8 scb_idx, + tBTA_AV_AUDIO_CODEC_INFO*p_codec_cfg); + +static int btusb_lite_av_parse_vendor_codec(UINT8 *p_codec_info, UINT32 *p_vendor_id, UINT16 *p_vendor_codec_id); + +#ifdef BTUSB_LITE_SEC +static int btusb_lite_av_parse_sec_codec(struct btusb_lite_av_sec_param *p_sec_param, + UINT8 *p_codec_info); +static int btusb_lite_av_sec_start(struct btusb *p_dev, UINT8 scb_idx, + tBTA_AV_AUDIO_CODEC_INFO*p_codec_cfg); +#endif + +/******************************************************************************* +** +** Function btusb_lite_av_add +** +** Description Add (Sync) an AV channel. +** +** Returns None. +** +*******************************************************************************/ +void btusb_lite_av_add(struct btusb *p_dev, tBTA_AV_SYNC_INFO *p_sync_info, + UINT8 multi_av_supported, UINT16 curr_mtu) +{ + struct btusb_lite_av_cb *p_av_cb = &p_dev->lite_cb.s.av; + struct btusb_lite_av_scb *p_av_scb; + int rv; + + p_av_cb->stack_mtu = curr_mtu; /* Update MTU */ + p_av_cb->curr_mtu = curr_mtu; /* Update MTU */ + + if (p_sync_info->hdi >= BTA_AV_NUM_STRS) + { + BTUSB_ERR("Bad AV Index=%d\n", p_sync_info->hdi); + return; + } + +#if (BTU_MULTI_AV_INCLUDED == TRUE) + p_av_cb->multi_av &= ~(BTA_AV_MULTI_AV_SUPPORTED); + p_av_cb->multi_av |= multi_av_supported; +#endif + + p_av_scb = &p_av_cb->scb[p_sync_info->hdi]; + + p_av_scb->avdt_handle = p_sync_info->avdt_handle; + p_av_scb->chnl = p_sync_info->chnl; + p_av_scb->codec_type = p_sync_info->codec_type; + p_av_scb->cong = p_sync_info->cong; + p_av_scb->hdi = p_sync_info->hdi; + p_av_scb->hndl = p_sync_info->hndl; + p_av_scb->l2c_bufs = p_sync_info->l2c_bufs; + p_av_scb->l2c_cid = p_sync_info->l2c_cid; + memcpy(p_av_scb->peer_addr, p_sync_info->peer_addr, BD_ADDR_LEN); + + if (p_av_cb->pcm.state == PCM_CLOSED) + { + /* Open the PCM Channel */ + rv = btpcm_open(p_dev->lite_cb.p_btpcm); + if (rv < 0) + { + BTUSB_ERR("btpcm_open failed\n"); + return; + } + p_av_cb->pcm.state = PCM_OPENED; + p_av_cb->pcm.frequency = -1; + } +} + +/******************************************************************************* +** +** Function btusb_lite_av_remove +** +** Description Remove (Cleanup) an AV channel. +** +** Returns None. +** +*******************************************************************************/ +void btusb_lite_av_remove(struct btusb *p_dev, UINT8 scb_idx, + UINT8 audio_open_cnt, UINT16 curr_mtu) +{ + struct btusb_lite_av_cb *p_av_cb = &p_dev->lite_cb.s.av; + struct btusb_lite_av_scb *p_av_scb; + int av_scb; + int cleanup_needed = 1; + int rv; + + p_av_cb->curr_mtu = curr_mtu; /* Update MTU */ + p_av_cb->audio_open_cnt = audio_open_cnt; /* Update audio_open_cnt */ + + if (scb_idx >= BTA_AV_NUM_STRS) + { + BTUSB_ERR("Bad Index=%d\n", scb_idx); + return; + } + + p_av_scb = &p_av_cb->scb[scb_idx]; + + /* Remove AVDT CCB and SCB */ + btusb_lite_avdt_remove_scb(p_dev, p_av_scb->avdt_handle, NULL); + + /* Clear the AV Stream Control Clock */ + memset(p_av_scb, 0, sizeof(*p_av_scb)); + + /* Check this is the last AV channel removed */ + p_av_scb = &p_av_cb->scb[0]; + for (av_scb = 0 ; av_scb < BTA_AV_NUM_STRS ; av_scb++, p_av_scb++) + { + if (p_av_scb->hndl) + { + cleanup_needed = 0; + break; + } + } + + if (cleanup_needed) + { + if (p_av_cb->pcm.state == PCM_STARTED) + { + /* Stop the PCM Channel */ + rv = btpcm_stop(p_dev->lite_cb.p_btpcm); + if (rv < 0) + { + BTUSB_ERR("btpcm_close failed\n"); + } + p_av_cb->pcm.state = PCM_CONFIGURED; + } + + if (p_av_cb->pcm.state != PCM_CLOSED) + { + /* Close the PCM Channel */ + rv = btpcm_close(p_dev->lite_cb.p_btpcm); + if (rv < 0) + { + BTUSB_ERR("btpcm_close failed\n"); + } + p_av_cb->pcm.state = PCM_CLOSED; + } + + if (p_av_cb->encoder.opened) + { + switch(p_av_cb->encoder.type) + { + case A2D_MEDIA_CT_SBC: + btsbc_free(p_av_cb->encoder.channel); + break; +#ifdef BTUSB_LITE_SEC + case A2D_MEDIA_CT_SEC: + btsec_free(p_av_cb->encoder.channel); + break; +#endif + default: + BTUSB_ERR("Unknown Encoder type=%d\n", p_av_cb->encoder.encoder.codec_type); + break; + } + p_av_cb->encoder.opened = 0; + } + } +} + +/******************************************************************************* +** +** Function btusb_lite_av_start +** +** Description Start AV +** +** Returns None. +** +*******************************************************************************/ +void btusb_lite_av_start(struct btusb *p_dev, UINT8 scb_idx, UINT8 start_stop_flag, + UINT8 audio_open_cnt, tBTA_AV_AUDIO_CODEC_INFO *p_codec_cfg) +{ + struct btusb_lite_av_cb *p_av_cb = &p_dev->lite_cb.s.av; + UINT32 vendor_id; + UINT16 vendor_codec_id; + struct btusb_lite_av_scb *p_av_scb; + + + if (scb_idx >= BTA_AV_NUM_STRS) + { + BTUSB_ERR("Bad scb_idx=%d", scb_idx); + return; + } + + p_av_scb = &p_av_cb->scb[scb_idx]; + + if (start_stop_flag) + { + p_av_cb->scb[scb_idx].started = FALSE; + BTUSB_ERR("start_stop_flag TODO!!!"); + } + else + { + /* If the Codec Type is SBC */ + if (p_codec_cfg->codec_type == A2D_MEDIA_CT_SBC) + { + if (btusb_lite_av_sbc_start(p_dev, scb_idx, p_codec_cfg) < 0) + { + BTUSB_ERR("SBC Stream not started\n"); + return; + } + } + /* Else if the Codec Type is a Vendor Specific Codec */ + else if (p_codec_cfg->codec_type == A2D_MEDIA_CT_VEND) + { + if (btusb_lite_av_parse_vendor_codec(p_codec_cfg->codec_info, &vendor_id, &vendor_codec_id) < 0) + { + BTUSB_ERR("Unable to parse Vendor Codec\n"); + return; + } +#ifdef BTUSB_LITE_SEC + /* If This is the SEC Encoder */ + if ((vendor_id == BTSEC_VENDOR_ID) && + (vendor_codec_id == BTSEC_VENDOR_CODEC_ID)) + { + btusb_lite_av_sec_start(p_dev, scb_idx, p_codec_cfg); + } + else +#endif + /* Add other Vendor Specific Vendor coder Here... */ + { + BTUSB_ERR("Unsupported Codec VendorId=0x%08x VendorCodecId=0x%04x\n", vendor_id, vendor_codec_id); + } + } + else + { + BTUSB_ERR("Unsupported Encoder type=%d\n", p_codec_cfg->codec_type); + } + } +} + +/******************************************************************************* +** +** Function btusb_lite_av_parse_vendor_codec +** +** Description Parse Vendor Id and Vendor Codec Id from Codec information +** +** Returns Status, VendorId and VendorCodecId +** +*******************************************************************************/ +static int btusb_lite_av_parse_vendor_codec(UINT8 *p_codec_info, UINT32 *p_vendor_id, UINT16 *p_vendor_codec_id) +{ + UINT8 byte; + UINT32 vendor_id; + UINT16 vendor_codec_id; + + if (!p_codec_info || !p_vendor_id || !p_vendor_codec_id) + { + BTUSB_ERR("Bad parameter\n"); + return -1; + } + + STREAM_TO_UINT8(byte, p_codec_info); /* Extract LOSC */ + if (byte < (1 + 1 + 4 + 2)) /* Media Type, Media Codec Type, Vid, VCId */ + { + BTUSB_ERR("Codec LOSC=%d too small\n", byte); + return -1; + } + + STREAM_TO_UINT8(byte, p_codec_info); /* Extract Media Type */ + if (byte != A2D_MEDIA_TYPE_AUDIO) + { + BTUSB_ERR("Unsupported Media Type=0x%x\n", byte); + return -1; + } + + STREAM_TO_UINT8(byte, p_codec_info); /* Extract Media Codec Type */ + if (byte != A2D_MEDIA_CT_VEND) + { + BTUSB_ERR("Media codec Type=0x%x is not Vendor(0xFF)\n", byte); + return -1; + } + + STREAM_TO_UINT32(vendor_id, p_codec_info); /* Extract Vendor Id */ + STREAM_TO_UINT16(vendor_codec_id, p_codec_info); /* Extract Vendor Codec Id */ + + *p_vendor_id = vendor_id; + *p_vendor_codec_id = vendor_codec_id; + + BTUSB_INFO("Extracted Codec VendorId=0x%08x VendorCodecId=0x%04x\n", vendor_id, vendor_codec_id); + + return 0; +} + +/******************************************************************************* +** +** Function btusb_lite_av_sbc_start +** +** Description Start AV SBC Stream +** +** Returns Status +** +*******************************************************************************/ +static int btusb_lite_av_sbc_start(struct btusb *p_dev, UINT8 scb_idx, + tBTA_AV_AUDIO_CODEC_INFO*p_codec_cfg) +{ + struct btusb_lite_av_cb *p_av_cb = &p_dev->lite_cb.s.av; + struct btusb_lite_encoder_ccb *p_encoder; + struct btusb_lite_av_scb *p_av_scb; + int nb_sbc_frames; + int rv; + int bitpool; + struct btusb_lite_av_sbc_param sbc_param; + int av_header_len; + + /* Parse SBC Codec Info */ + if (btusb_lite_av_parse_sbc_codec(&sbc_param, p_codec_cfg->codec_info) < 0) + { + BTUSB_ERR("Bad SBC Codec. Stream not started\n"); + return -1; + } + + if (scb_idx >= BTA_AV_NUM_STRS) + { + BTUSB_ERR("Bad scb_idx=%d", scb_idx); + return -1; + } + p_av_scb = &p_av_cb->scb[scb_idx]; + + /* Calculate the BitPool for this BitRate */ + bitpool = btusb_lite_sbc_get_bitpool(&sbc_param, p_codec_cfg->bit_rate); + if (bitpool <= 0) + { + BTUSB_ERR("btusb_lite_sbc_get_bitpool return wrong bitpool=%d\n", bitpool); + return -1; + } + BTUSB_INFO("SBC BitPool=%d\n", bitpool); + + p_av_cb->timestamp = 0; /* Reset TimeStamp */ + p_av_cb->option = 0; /* No specific Option (RTP and Media Payload Header presents) */ + p_av_cb->m_pt = AVDT_RTP_PAYLOAD_TYPE | A2D_MEDIA_CT_SBC; + p_av_cb->m_pt &= ~AVDT_MARKER_SET; + + /* Calculate Packet Header Size (HCI, L2CAP, RTP and MediaPayloadHeader) */ + p_av_cb->header_len = BTUSB_LITE_HCI_ACL_HDR_SIZE + + BTUSB_LITE_L2CAP_HDR_SIZE + + BTUSB_LITE_RTP_SIZE + + BTUSB_LITE_SCMS_SIZE + + BTUSB_LITE_MEDIA_SIZE; + /* Calculate AV Header Size */ + av_header_len = BTUSB_LITE_L2CAP_HDR_SIZE + + BTUSB_LITE_RTP_SIZE + + BTUSB_LITE_SCMS_SIZE + + BTUSB_LITE_MEDIA_SIZE; + + /* clear the congestion flag: full stack made it congested when opening */ + p_av_scb->cong = FALSE; + p_av_scb->started = TRUE; + + /* Get reference to AV's encoder */ + p_encoder = &p_av_cb->encoder; + + if (p_encoder->opened == 0) + { + /* Allocate an SBC Channel */ + rv = btsbc_alloc(); + if (rv < 0) + { + BTUSB_ERR("btsbc_alloc failed\n"); + return -1; + } + p_encoder->opened = 1; + p_encoder->channel = rv; + p_encoder->type = A2D_MEDIA_CT_SBC; + } + + /* Configure the SBC Channel */ + rv = btsbc_config(p_encoder->channel, + sbc_param.frequency, + sbc_param.nb_blocks, + sbc_param.nb_subbands, + sbc_param.mode, + sbc_param.allocation, + (unsigned char)bitpool); + if (rv <= 0) + { + BTUSB_ERR("btsbc_config failed\n"); + btsbc_free(p_encoder->channel); + p_encoder->opened = 0; + return -1; + } + + /* Save the calculated SBC Frame size */ + p_encoder->encoded_frame_size = rv; + BTUSB_INFO("encoded_frame_size=%d\n", rv); + + /* Configure the PCM Channel */ + rv = btpcm_config(p_dev->lite_cb.p_btpcm, + p_dev, + sbc_param.frequency, + sbc_param.mode==CODEC_MODE_MONO?1:2, + 16, /* SBC Encoder requires 16 bits per sample */ + btusb_lite_av_pcm_cback); + if (rv < 0) + { + BTUSB_ERR("btpcm_config failed\n"); + return -1; + } + + + /* Calculate and save the PCM frame size */ + p_encoder->pcm_frame_size = sbc_param.nb_blocks * sbc_param.nb_subbands; + BTUSB_INFO("pcm_frame_size=%d\n", p_encoder->pcm_frame_size); + +#if 0 + /* Calculate nb_sbc_frames depending on MTU */ + nb_sbc_frames = (p_av_cb->curr_mtu - av_header_len) / p_encoder->encoded_frame_size; +#else + nb_sbc_frames = 10; + if(p_av_cb->stack_mtu < (p_encoder->encoded_frame_size * nb_sbc_frames + av_header_len)) + { /* if 10 SBC frame is bigger than mtu size, should change nb_sbc_frames value to fit in mtu */ + nb_sbc_frames = (p_av_cb->stack_mtu - av_header_len) / p_encoder->encoded_frame_size; + p_av_cb->curr_mtu = p_av_cb->stack_mtu; + } + else + { + p_av_cb->curr_mtu = p_encoder->encoded_frame_size * nb_sbc_frames + av_header_len; + } +#endif + BTUSB_INFO("mtu:%d, nb_sbc_frames:%d, encoded_frame_size%d\n", + p_av_cb->curr_mtu, nb_sbc_frames, p_encoder->pcm_frame_size); + + /* Calculate the size of the Payload */ + p_av_cb->payload_len = nb_sbc_frames * p_encoder->encoded_frame_size; + + BTUSB_INFO("nb_sbc_frames=%d payload_len=%d\n", nb_sbc_frames, p_av_cb->payload_len); + + /* Start the PCM stream */ + rv = btpcm_start(p_dev->lite_cb.p_btpcm, p_encoder->pcm_frame_size, nb_sbc_frames, 0); + if (rv < 0) + { + BTUSB_ERR("btpcm_start failed\n"); + return -1; + } + p_av_cb->pcm.state = PCM_STARTED; + return 0; +} + + +#ifdef BTUSB_LITE_SEC +/******************************************************************************* +** +** Function btusb_lite_av_parse_vendor_codec +** +** Description Parse Vendor Id and Vendor Codec Id from Codec information +** +** Returns Status, VendorId and VendorCodecId +** +*******************************************************************************/ +static int btusb_lite_av_parse_sec_codec(struct btusb_lite_av_sec_param *p_sec_param, + UINT8 *p_codec_info) +{ + UINT8 byte; + UINT32 vendor_id; + UINT16 vendor_codec_id; + + if (!p_codec_info || !p_sec_param) + { + BTUSB_ERR("Bad parameter\n"); + return -1; + } + + /* Extract/Ignore parameters already checked */ + STREAM_TO_UINT8(byte, p_codec_info); /* Extract LOSC */ + STREAM_TO_UINT8(byte, p_codec_info); /* Extract Media Type */ + STREAM_TO_UINT8(byte, p_codec_info); /* Extract Media Codec Type */ + STREAM_TO_UINT32(vendor_id, p_codec_info); /* Extract Vendor Id */ + STREAM_TO_UINT16(vendor_codec_id, p_codec_info); /* Extract Vendor Codec Id */ + + STREAM_TO_UINT8(byte, p_codec_info); /* SEC codec configuration */ + + /* Check Frequency */ + switch(byte & BTSEC_FREQ_MASK) + { + case BTSEC_FREQ_48K: + p_sec_param->frequency = 48000; + break; + case BTSEC_FREQ_44K: + p_sec_param->frequency = 44100; + break; + case BTSEC_FREQ_32K: + p_sec_param->frequency = 32000; + break; + default: + BTUSB_ERR("SEC Frequency=0x%x unsupported\n", byte & BTSEC_FREQ_MASK); + return -1; + } + + /* Check Mode */ + switch(byte & BTSEC_MODE_MASK) + { + case BTSEC_MODE_MONO: + p_sec_param->mode = BTSEC_MODE_MONO; + break; + case BTSEC_MODE_STEREO: + p_sec_param->mode = BTSEC_MODE_STEREO; + break; + default: + BTUSB_ERR("SEC Mode=0x%x unsupported\n", byte & BTSEC_FREQ_MASK); + return -1; + } + + BTUSB_INFO("SEC Frequency=%d Mode=%s\n", p_sec_param->frequency, + p_sec_param->mode == BTSEC_MODE_MONO?"Mono":"Stereo"); + + return 0; +} + +/******************************************************************************* +** +** Function btusb_lite_av_sec_start +** +** Description Start AV SBC Stream +** +** Returns Status +** +*******************************************************************************/ +static int btusb_lite_av_sec_start(struct btusb *p_dev, UINT8 scb_idx, + tBTA_AV_AUDIO_CODEC_INFO*p_codec_cfg) +{ + struct btusb_lite_av_cb *p_av_cb = &p_dev->lite_cb.s.av; + struct btusb_lite_encoder_ccb *p_encoder; + struct btusb_lite_av_scb *p_av_scb; + int nb_sec_frames; + int rv; + struct btusb_lite_av_sec_param sec_param; + int av_header_len; + + /* Parse SBC Codec Info */ + if (btusb_lite_av_parse_sec_codec(&sec_param, p_codec_cfg->codec_info) < 0) + { + BTUSB_ERR("Bad SEC Codec parameters. Stream not started\n"); + return -1; + } + + if (scb_idx >= BTA_AV_NUM_STRS) + { + BTUSB_ERR("Bad scb_idx=%d", scb_idx); + return -1; + } + p_av_scb = &p_av_cb->scb[scb_idx]; + + BTUSB_INFO("SEC Bitrate =%d\n", p_codec_cfg->bit_rate); + + p_av_cb->timestamp = 0; /* Reset TimeStamp */ + p_av_cb->option = BTUSB_LITE_AVDT_OPT_NO_MPH; /* No Media Payload Header */ + p_av_cb->m_pt = AVDT_RTP_PAYLOAD_TYPE | A2D_MEDIA_CT_VEND; + p_av_cb->m_pt &= ~AVDT_MARKER_SET; + + /* Calculate Packet Header Size (HCI, L2CAP, RTP) */ + p_av_cb->header_len = BTUSB_LITE_HCI_ACL_HDR_SIZE + + BTUSB_LITE_L2CAP_HDR_SIZE + + BTUSB_LITE_RTP_SIZE; + /* Calculate AV Header Size */ + av_header_len = BTUSB_LITE_L2CAP_HDR_SIZE + + BTUSB_LITE_RTP_SIZE; + + /* Clear the congestion flag: full stack made it congested when opening */ + p_av_scb->cong = FALSE; + p_av_scb->started = TRUE; + + /* Get reference to AV's encoder */ + p_encoder = &p_av_cb->encoder; + + if (p_encoder->opened == 0) + { + /* Allocate a SEC Channel */ + rv = btsec_alloc(); + if (rv < 0) + { + BTUSB_ERR("btsec_alloc failed\n"); + return -1; + } + p_encoder->opened = 1; + p_encoder->channel = rv; + p_encoder->type = A2D_MEDIA_CT_SEC; + + } + + /* Configure the SEC Channel */ + rv = btsec_config(p_encoder->channel, + sec_param.frequency, + sec_param.mode, + p_codec_cfg->bit_rate * 1000); + if (rv <= 0) + { + BTUSB_ERR("btsec_config failed\n"); + btsec_free(p_encoder->channel); + p_encoder->opened = 0; + return -1; + } + + /* Save the calculated SEC Frame size */ + p_encoder->encoded_frame_size = rv; + BTUSB_INFO("encoded_frame_size=%d\n", rv); + + /* Configure the PCM Channel */ + rv = btpcm_config(p_dev->lite_cb.p_btpcm, + p_dev, + sec_param.frequency, + sec_param.mode==BTSEC_MODE_MONO?1:2, + 16, /* SBC Encoder requires 16 bits per sample */ + btusb_lite_av_pcm_cback); + if (rv < 0) + { + BTUSB_ERR("btpcm_config failed\n"); + btsec_free(p_encoder->channel); + p_encoder->opened = 0; + return -1; + } + + /* SEC requires a fixed number of PCM Samples */ + p_encoder->pcm_frame_size = BTSEC_FRAME_SIZE; + BTUSB_INFO("pcm_frame_size=%d\n", p_encoder->pcm_frame_size); + + + +#if 0 + /* Calculate nb_sec_frames depending on MTU */ + nb_sec_frames = (p_av_cb->curr_mtu - av_header_len) / p_encoder->encoded_frame_size; +#else + nb_sec_frames = 10; + if(p_av_cb->stack_mtu < (p_encoder->encoded_frame_size * nb_sec_frames + av_header_len)) + { /* if 10 SEC frames is bigger than mtu value, should use mtu */ + nb_sec_frames = (p_av_cb->stack_mtu - av_header_len) / p_encoder->encoded_frame_size; + p_av_cb->curr_mtu = p_av_cb->stack_mtu; + } + else + { + p_av_cb->curr_mtu = p_encoder->encoded_frame_size * nb_sec_frames + av_header_len; + } +#endif + + /* Calculate the size of the Payload */ + p_av_cb->payload_len = nb_sec_frames * p_encoder->encoded_frame_size; + + BTUSB_INFO("nb_sec_frames=%d payload_len=%d\n", nb_sec_frames, p_av_cb->payload_len); + + /* Start the PCM stream */ + rv = btpcm_start(p_dev->lite_cb.p_btpcm, + p_encoder->pcm_frame_size, nb_sec_frames, 0); + if (rv < 0) + { + BTUSB_ERR("btpcm_start failed\n"); + btsec_free(p_encoder->channel); + p_encoder->opened = 0; + return -1; + } + p_av_cb->pcm.state = BTPCM_LITE_PCM_STARTED; + return 0; +} +#endif + + +/******************************************************************************* +** +** Function btusb_lite_av_stop +** +** Description Start AV +** +** Returns None. +** +*******************************************************************************/ +void btusb_lite_av_stop(struct btusb *p_dev, UINT8 scb_idx, UINT8 audio_open_cnt) +{ + struct btusb_lite_av_cb *p_av_cb = &p_dev->lite_cb.s.av; + struct btusb_lite_av_scb *p_av_scb; + int rv; + + BTUSB_INFO("scb_idx=%d audio_open_cnt=%d\n", scb_idx, audio_open_cnt); + + if (scb_idx >= BTA_AV_NUM_STRS) + { + BTUSB_ERR("Bad scb_idx=%d", scb_idx); + return; + } + + p_av_cb->audio_open_cnt = audio_open_cnt; + + p_av_scb = &p_av_cb->scb[scb_idx]; + + p_av_cb->scb[scb_idx].started = FALSE; + + if (p_av_cb->pcm.state != PCM_STARTED) + { + BTUSB_ERR("BTPCM was not started\n"); + return; + } + + /* Stop the PCM stream */ + rv = btpcm_stop(p_dev->lite_cb.p_btpcm); + if (rv < 0) + { + BTUSB_ERR("btpcm_stop failed\n"); + return; + } + p_av_cb->pcm.state = PCM_CONFIGURED; +} + +/******************************************************************************* +** +** Function btusb_lite_av_suspend +** +** Description Suspend AV +** +** Returns none. +** +*******************************************************************************/ +void btusb_lite_av_suspend(struct btusb *p_dev, UINT8 scb_idx, UINT8 audio_open_cnt) +{ + struct btusb_lite_av_cb *p_av_cb = &p_dev->lite_cb.s.av; + struct btusb_lite_av_scb *p_av_scb; + int rv; + + BTUSB_INFO("scb_idx=%d audio_open_cnt=%d\n", scb_idx, audio_open_cnt); + + if (scb_idx >= BTA_AV_NUM_STRS) + { + BTUSB_ERR("Bad scb_idx=%d", scb_idx); + return; + } + + p_av_cb->audio_open_cnt = audio_open_cnt; + + p_av_scb = &p_av_cb->scb[scb_idx]; + + p_av_cb->scb[scb_idx].started = FALSE; + + if (p_av_cb->pcm.state != PCM_STARTED) + { + BTUSB_ERR("BTPCM was not started\n"); + return; + } + + /* Stop the PCM stream */ + rv = btpcm_stop(p_dev->lite_cb.p_btpcm); + if (rv < 0) + { + BTUSB_ERR("btpcm_stop failed\n"); + return; + } + p_av_cb->pcm.state = PCM_CONFIGURED; +} + +/******************************************************************************* +** +** Function btusb_lite_av_parse_sbc_codec +** +** Description Parse an SBC A2DP Codec +** +** Returns Status +** +*******************************************************************************/ +static int btusb_lite_av_parse_sbc_codec(struct btusb_lite_av_sbc_param *p_sbc, UINT8 *p_codec) +{ + UINT8 byte; + unsigned char codec_freq; + unsigned char codec_blocks; + unsigned char codec_subbands; + unsigned char codec_mode; + unsigned char codec_alloc; + unsigned char bitpool_min; + unsigned char bitpool_max; + + if (p_sbc == NULL) + { + BTUSB_ERR("p_sbc is NULL\n"); + return -1; + } + + /* Extract LOSC */ + byte = *p_codec++; + if (byte != CODEC_SBC_LOSC) + { + BTUSB_ERR("Bad SBC LOSC=%d", byte); + return -1; + } + + p_codec++; /* Ignore MT */ + + /* Extract Codec Type */ + byte = *p_codec++; + if (byte != A2D_MEDIA_CT_SBC) + { + BTUSB_ERR("Bad SBC codec type=%d", byte); + return -1; + } + + /* Extract Freq & Mode */ + byte = *p_codec++; + codec_freq = byte & CODEC_SBC_FREQ_MASK; + codec_mode = byte & CODEC_MODE_MASK; + + /* Extract NbBlock NbSubBand and Alloc Method */ + byte = *p_codec++; + codec_blocks = byte & CODEC_SBC_BLOCK_MASK; + codec_subbands = byte & CODEC_SBC_NBBAND_MASK; + codec_alloc = byte & CODEC_SBC_ALLOC_MASK; + + bitpool_min = *p_codec++; + bitpool_max = *p_codec++; + + switch(codec_freq) + { + case CODEC_SBC_FREQ_48: + BTUSB_INFO("SBC Freq=48K\n"); + p_sbc->frequency = 48000; + break; + case CODEC_SBC_FREQ_44: + BTUSB_INFO("SBC Freq=44.1K\n"); + p_sbc->frequency = 44100; + break; + case CODEC_SBC_FREQ_32: + BTUSB_INFO("SBC Freq=32K\n"); + p_sbc->frequency = 32000; + break; + case CODEC_SBC_FREQ_16: + BTUSB_INFO("SBC Freq=16K\n"); + p_sbc->frequency = 16000; + break; + default: + BTUSB_INFO("Bad SBC Freq=%d\n", codec_freq); + return -1; + } + + switch(codec_mode) + { + case CODEC_MODE_JOIN_STEREO: + BTUSB_INFO("SBC Join Stereo\n"); + p_sbc->mode = CODEC_MODE_JOIN_STEREO; + break; + case CODEC_MODE_STEREO: + BTUSB_INFO("SBC Stereo\n"); + p_sbc->mode = CODEC_MODE_STEREO; + break; + case CODEC_MODE_DUAL: + BTUSB_INFO("SBC Dual\n"); + p_sbc->mode = CODEC_MODE_DUAL; + break; + case CODEC_MODE_MONO: + BTUSB_INFO("SBC Mono\n"); + p_sbc->mode = CODEC_MODE_MONO; + break; + default: + BTUSB_INFO("Bad SBC mode=%d\n", codec_mode); + return -1; + } + + switch(codec_blocks) + { + case CODEC_SBC_BLOCK_16: + BTUSB_INFO("SBC Block=16\n"); + p_sbc->nb_blocks = 16; + break; + case CODEC_SBC_BLOCK_12: + BTUSB_INFO("SBC Block=12\n"); + p_sbc->nb_blocks = 12; + break; + case CODEC_SBC_BLOCK_8: + BTUSB_INFO("SBC Block=8\n"); + p_sbc->nb_blocks = 8; + break; + case CODEC_SBC_BLOCK_4: + BTUSB_INFO("SBC Block=4\n"); + p_sbc->nb_blocks = 4; + break; + default: + BTUSB_INFO("Bad SBC Block=%d\n", codec_blocks); + return -1; + } + + switch(codec_subbands) + { + case CODEC_SBC_NBBAND_8: + BTUSB_INFO("SBC NbSubBand=8\n"); + p_sbc->nb_subbands = 8; + break; + case CODEC_SBC_NBBAND_4: + BTUSB_INFO("SBC NbSubBand=4\n"); + p_sbc->nb_subbands = 4; + break; + default: + BTUSB_INFO("Bad SBC NbSubBand=%d\n", codec_blocks); + return -1; + } + + switch(codec_alloc) + { + case CODEC_SBC_ALLOC_LOUDNESS: + BTUSB_INFO("SBC Loudness\n"); + p_sbc->allocation = CODEC_SBC_ALLOC_LOUDNESS; + break; + case CODEC_SBC_ALLOC_SNR: + BTUSB_INFO("SBC SNR\n"); + p_sbc->allocation = CODEC_SBC_ALLOC_SNR; + break; + default: + BTUSB_INFO("Bad SBC AllocMethod=%d\n", codec_blocks); + return -1; + } + + BTUSB_INFO("BitpoolMin=%d BitpoolMax=%d\n", bitpool_min, bitpool_max); + + p_sbc->bitpool_min = bitpool_min; + p_sbc->bitpool_max = bitpool_max; + + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_lite_sbc_get_bitpool + ** + ** Description Calculate the BitPool for a specified BitRate (and other parameters) + ** + ** Returns Void + ** + *******************************************************************************/ +static int btusb_lite_sbc_get_bitpool(struct btusb_lite_av_sbc_param *p_sbc_param, int target_bitrate) +{ + int nb_channels; + int frame_length; + int bitpool = p_sbc_param->bitpool_max + 1; + int bitrate; + + /* Required number of channels */ + if (p_sbc_param->mode == CODEC_MODE_MONO) + nb_channels = 1; + else + nb_channels = 2; + + target_bitrate *= 1000; /* Bitrate from app is in Kbps */ + + do + { + bitpool--; /* Reduce Bit Pool by one */ + + /* Calculate common SBC Frame length */ + frame_length = 4 + (4 * p_sbc_param->nb_subbands * nb_channels) / 8; + + /* Add specific SBC Frame length (depending on mode) */ + switch(p_sbc_param->mode) + { + case CODEC_MODE_MONO: + case CODEC_MODE_DUAL: + frame_length += (p_sbc_param->nb_blocks * nb_channels * bitpool) / 8; + break; + case CODEC_MODE_JOIN_STEREO: + frame_length += (p_sbc_param->nb_subbands + p_sbc_param->nb_blocks * bitpool) / 8; + break; + case CODEC_MODE_STEREO: + frame_length += (p_sbc_param->nb_blocks * bitpool) / 8; + break; + } + + /* Calculate bit rate */ + bitrate = 8 * frame_length * p_sbc_param->frequency / p_sbc_param->nb_subbands / p_sbc_param->nb_blocks; + + } while (bitrate > target_bitrate); /* While bitrate is too big */ + + BTUSB_INFO("final bitpool=%d frame_length=%d bitrate=%d\n", bitpool, frame_length, bitrate); + + return (int)bitpool; +} + +/******************************************************************************* + ** + ** Function btusb_lite_av_pcm_cback + ** + ** Description BTUSB Lte AV PCM Callback function. + ** + ** Returns Void + ** + *******************************************************************************/ +static void btusb_lite_av_pcm_cback(void *p_opaque, void *p_data, int nb_pcm_frames) +{ + struct btusb *p_dev = p_opaque; + struct btusb_lite_av_cb *p_av_cb; + int pcm_frame_size_byte; + void *p_dest; + int written_enc_size; + struct btusb_lite_encoder_ccb *p_encoder; + + if (!p_dev) + { + BTUSB_ERR("Null p_dev\n"); + return; + } + + if (!p_data) + { + BTUSB_ERR("Null p_data\n"); + return; + } + + /* Get Reference on the SBC Stream (which is the same than the Encoder channel) */ + p_av_cb = &p_dev->lite_cb.s.av; + + if (p_av_cb->pcm.state != PCM_STARTED) + { + BTUSB_ERR("BTPCM is not started\n"); + btpcm_stop(p_dev->lite_cb.p_btpcm); + return; + } + + /* Get reference to AV's encoder */ + p_encoder = &p_av_cb->encoder; + + /* Calculate the size (in byte) of an Input PCM buffer (holding one encoded frame) */ + pcm_frame_size_byte = p_av_cb->encoder.pcm_frame_size; + pcm_frame_size_byte *= 2; /* Stereo */ + pcm_frame_size_byte *= 2; /* 16 bits per sample */ + + /* Sanity Check */ + if (pcm_frame_size_byte == 0) + { + BTUSB_ERR("Bad PCM Frame size=%d\n", pcm_frame_size_byte); + return; + } + + /* + * No PCM data in a timer period. + * Need to send AV data in a working buffer if it exists + */ + if (nb_pcm_frames == 0) + { + if (p_av_cb->p_buf_working) + { + if (p_av_cb->p_buf_working->len) + { + /* For AV channel, send the packet */ + btusb_lite_av_send_packet((struct btusb *)p_dev, p_av_cb->p_buf_working); + + /* A new working buffer must be allocated */ + p_av_cb->p_buf_working = NULL; + } + } + return; + } + + /* While received buffer is not empty */ + while (nb_pcm_frames) + { + /* Check if there are enough remaining frames in the buffer */ + if ((nb_pcm_frames * 2 * 2) < pcm_frame_size_byte) + { + BTUSB_ERR("Bad nb_pcm_frames=%d\n", nb_pcm_frames); + return; + } + + /* If no working buffer allocated */ + if (!p_av_cb->p_buf_working) + { + /* Get a buffer from the pool */ + p_av_cb->p_buf_working = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + p_av_cb->header_len + p_av_cb->payload_len); + if(unlikely(p_av_cb->p_buf_working == NULL)) + { + BTUSB_ERR("Unable to get GKI buffer - sent fail\n"); + return; + } + + if (unlikely(dbgflags & BTUSB_GKI_CHK_MSG) && + unlikely(GKI_buffer_status(p_av_cb->p_buf_working) != BUF_STATUS_UNLINKED)) + { + BTUSB_ERR("buffer != BUF_STATUS_UNLINKED 0x%p\n", p_av_cb->p_buf_working); + return; + } + + /* Skip headers */ + p_av_cb->p_buf_working->offset = p_av_cb->header_len; + p_av_cb->p_buf_working->len = 0; + p_av_cb->p_buf_working->layer_specific = 0; /* Used to store the number of Encoded Frames */ + } + + /* Fill the ACL Packet with SBC Frames */ + do + { + /* Get Write address */ + p_dest = (UINT8 *)(p_av_cb->p_buf_working + 1) + p_av_cb->p_buf_working->offset + + p_av_cb->p_buf_working->len; + + if (p_encoder->type == A2D_MEDIA_CT_SBC) + { + /* Encode one PCM frame with SBC Encoder*/ + btsbc_encode(p_encoder->channel, + /* If Mute => Zero filled PCM sample*/ + /* Otherwise => regular PCM data */ + pcm0_mute?btusb_lite_silence_pcm:p_data, + pcm_frame_size_byte, + p_dest, /* SBC Output buffer */ + p_av_cb->encoder.encoded_frame_size, /* Expected Output SBC frame size */ + &written_enc_size); + } +#ifdef BTUSB_LITE_SEC + else if (p_encoder->type == A2D_MEDIA_CT_SEC) + { + /* Encode one PCM frame with SEC Encoder*/ + written_enc_size = btsec_encode(p_av_cb->encoder.channel, + /* If Mute => Zero filled PCM sample*/ + /* Otherwise => regular PCM data */ + pcm0_mute?btusb_lite_silence_pcm:p_data, + pcm_frame_size_byte, + p_dest, /* SEC Output buffer */ + p_encoder->encoded_frame_size);/* Expected Output SEC frame size */ + } +#endif + else + { + BTUSB_ERR("Bad Encoding TYPE:%d)\n", p_encoder->type); + } + + if (written_enc_size != p_av_cb->encoder.encoded_frame_size) + { + BTUSB_ERR("Bad Encoded Frame length=%d (expected=%d)\n", + written_enc_size, p_av_cb->encoder.encoded_frame_size); + } + + /* Update Encoded packet length */ + p_av_cb->p_buf_working->len += (UINT16)p_av_cb->encoder.encoded_frame_size; + + /* One more Encoded Frame */ + p_av_cb->p_buf_working->layer_specific++; + + p_data += pcm_frame_size_byte; /* Jump to the next PCM sample */ + nb_pcm_frames -= pcm_frame_size_byte / 4; /* Update number of remaining samples */ + + } while (nb_pcm_frames && + (p_av_cb->p_buf_working->layer_specific < A2D_SBC_HDR_NUM_MSK) && + ((p_av_cb->p_buf_working->len + p_av_cb->encoder.encoded_frame_size) < p_av_cb->curr_mtu)); + + /* If no more room to store an encoded frame */ + if (p_av_cb->encoder.encoded_frame_size > (p_av_cb->curr_mtu - p_av_cb->p_buf_working->len)) + { + /* For AV channel, send the packet */ + btusb_lite_av_send_packet((struct btusb *)p_dev, p_av_cb->p_buf_working); + + /* A new working buffer must be allocated */ + p_av_cb->p_buf_working = NULL; + } + } +} + +/******************************************************************************* + ** + ** Function btusb_lite_av_send_packet + ** + ** Description Send a new BAV packet to the controller + ** + ** Returns Void + ** + *******************************************************************************/ +static void btusb_lite_av_send_packet(struct btusb *p_dev, BT_HDR *p_msg) +{ + struct btusb_lite_av_cb *p_av_cb; + struct btusb_lite_av_scb *p_av_scb; + int stream; + int nb_started_streams; + BT_HDR *p_msg_dup; + + if (!p_dev || !p_msg) + { + BTUSB_ERR("Bad reference p_dev=%p p_msg=%p\n", p_dev, p_msg); + return; + } + + /* Sanity */ + if (p_msg->len == 0) + { + BTUSB_ERR("Length is 0=%d\n", p_msg->len); + GKI_freebuf(p_msg); /* Free this ACL buffer */ + return; + } + + /* Get Reference on the AV Streams */ + p_av_cb = &p_dev->lite_cb.s.av; + + /* Update TimeStamp */ + p_av_cb->timestamp += p_msg->layer_specific * p_av_cb->encoder.pcm_frame_size; + + nb_started_streams = 0; + /* Count how many AV stream are started */ + for (stream = 0, p_av_scb = p_av_cb->scb ; stream < BTA_AV_NUM_STRS ; stream++, p_av_scb++) + { + if (p_av_scb->started) + { + nb_started_streams++; /* One more started stream */ + } + } + + if (nb_started_streams == 0) + { + BTUSB_ERR("No Started AV stream found\n"); + GKI_freebuf(p_msg); /* Free this ACL buffer */ + return; + } + else if (nb_started_streams == 1) + { + p_msg_dup = NULL; + } + else + { + /* + * Duplicate the AV packet + */ + /* Get a buffer from the pool */ + p_msg_dup = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + p_msg->offset + p_msg->offset); + if(p_msg_dup) + { + if (unlikely(dbgflags & BTUSB_GKI_CHK_MSG) && + unlikely(GKI_buffer_status(p_msg_dup) != BUF_STATUS_UNLINKED)) + { + BTUSB_ERR("buffer != BUF_STATUS_UNLINKED 0x%p\n", p_msg_dup); + p_msg_dup = NULL; /* Do not use this buffer */ + } + if(p_msg_dup) + { + /* Duplicate all the data (Header, and payload */ + memcpy(p_msg_dup, p_msg, sizeof(BT_HDR) + p_msg->offset + p_msg->offset); + } + } + if (nb_started_streams > 2) + { + BTUSB_ERR("nb_started_streams=%d force it to 2\n", nb_started_streams); + nb_started_streams = 2; + } + } + + /* For every AV stream Started */ + for (stream = 0, p_av_scb = p_av_cb->scb ; stream < BTA_AV_NUM_STRS ; stream++, p_av_scb++) + { + if (p_av_scb->started) + { + if (p_msg) + { + /* Send the original packet to AVDT */ + btusb_lite_avdt_send(p_dev, p_msg, p_av_scb->avdt_handle, + p_av_cb->m_pt, p_av_cb->option, p_av_cb->timestamp); + p_msg = NULL; + } + else if (p_msg_dup) + { + /* Send the duplicated packet to AVDT */ + btusb_lite_avdt_send(p_dev, p_msg_dup, p_av_scb->avdt_handle, + p_av_cb->m_pt, p_av_cb->option, p_av_cb->timestamp); + p_msg_dup = NULL; + } + else + { + BTUSB_ERR("No AV data to send for AV stream=%d \n", stream); + } + } + } +} + diff --git a/src/btusb_lite_avdt.c b/src/btusb_lite_avdt.c new file mode 100755 index 0000000..d1db2b7 --- a/dev/null +++ b/src/btusb_lite_avdt.c @@ -0,0 +1,426 @@ +/* + * + * btusb_lite_avdt.c + * + * + * + * Copyright (C) 2011-2013 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ + +#include "btusb.h" +#include "bd.h" + +/* + * Definitions + */ +#define AVDT_MULTIPLEXING FALSE + + +/* + * Local functions + */ +static struct btusb_lite_avdt_scb *btusb_lite_avdt_allocate_scb(struct btusb *p_dev); +static void btusb_lite_avdt_free_scb(struct btusb *p_dev, struct btusb_lite_avdt_scb *p_scb_free); +static struct btusb_lite_avdt_ccb *btusb_lite_avdt_allocate_ccb(struct btusb *p_dev); +static void btusb_lite_avdt_free_ccb(struct btusb *p_dev, struct btusb_lite_avdt_ccb *p_ccb_free); +static UINT8 *btusb_lite_avdt_write_rtp_header(UINT8 *p_data, UINT8 m_pt, UINT16 seq_number, UINT32 timestamp, UINT32 ssrc); + + + +/******************************************************************************* +** +** Function btusb_lite_avdt_scb_by_hdl +** +** Description Given an scb handle (or seid), return a pointer to the scb. +** +** +** Returns Pointer to scb or NULL if index is out of range or scb +** is not allocated. +** +*******************************************************************************/ +struct btusb_lite_avdt_scb *btusb_lite_avdt_scb_by_hdl(struct btusb *p_dev, UINT8 handle) +{ + struct btusb_lite_avdt_scb *p_scb; + UINT8 scb; + + p_scb = &p_dev->lite_cb.s.avdt.scb[0]; + for(scb = 0; scb < AVDT_NUM_SEPS; scb++, p_scb++ ) + { + if((p_scb->allocated) && + (p_scb->handle == handle)) + return(p_scb); + } + return(NULL); +} + +/******************************************************************************* +** +** Function btusb_lite_avdt_init_scb +** +** Description allocate and initialize SCB/CCB with received sync info +** +** Returns AVDT_SYNC_SUCCESS/AVDT_SYNC_FAILURE +** +*******************************************************************************/ +UINT8 btusb_lite_avdt_init_scb(struct btusb *p_dev, tAVDT_SCB_SYNC_INFO *p_scb_info) +{ + struct btusb_lite_avdt_scb *p_scb; + struct btusb_lite_avdt_ccb *p_ccb; + + if((p_scb = btusb_lite_avdt_allocate_scb(p_dev)) == NULL) + { + BTUSB_ERR("No SCB for handle %d\n", p_scb_info->handle); + return AVDT_SYNC_FAILURE; + } + else + { + if((p_ccb = btusb_lite_avdt_allocate_ccb(p_dev)) == NULL) + { + BTUSB_ERR("No CCB for handle %d\n", p_scb_info->handle); + p_scb->allocated = FALSE; + return AVDT_SYNC_FAILURE; + } + else + { + memcpy(p_ccb->peer_addr, p_scb_info->peer_addr, BD_ADDR_LEN); + p_ccb->lcid = p_scb_info->local_cid; + p_ccb->peer_mtu = p_scb_info->peer_mtu; +#if AVDT_MULTIPLEXING == TRUE + GKI_init_q(&p_scb->frag_q); +#endif + p_scb->handle = p_scb_info->handle; + p_scb->mux_tsid_media = p_scb_info->mux_tsid_media; + p_scb->media_seq = p_scb_info->media_seq; + p_scb->p_ccb = p_ccb; + BTUSB_INFO("Allocated SCB/CCB for handle %d\n", p_scb_info->handle); + } + } + return AVDT_SYNC_SUCCESS; +} + +/******************************************************************************* +** +** Function btusb_lite_avdt_remove_scb +** +** Description deallocate SCB and CCB +** +** Returns AVDT_SYNC_SUCCESS/AVDT_SYNC_FAILURE +** +*******************************************************************************/ +UINT8 btusb_lite_avdt_remove_scb(struct btusb *p_dev, UINT8 handle, tAVDT_SCB_SYNC_INFO *p_scb_info) +{ + struct btusb_lite_avdt_scb *p_scb; + + if((p_scb = btusb_lite_avdt_scb_by_hdl(p_dev, handle)) == NULL) + { + BTUSB_ERR("No SCB for handle %d\n", handle); + return AVDT_SYNC_FAILURE; + } + else + { + if (p_scb_info) + { + p_scb_info->handle = p_scb->handle; + p_scb_info->media_seq = p_scb->media_seq; + } + /* Free CCB first */ + btusb_lite_avdt_free_ccb(p_dev, p_scb->p_ccb); + /* Free SCB */ + btusb_lite_avdt_free_scb(p_dev, p_scb); + + return AVDT_SYNC_SUCCESS; + } +} + +/******************************************************************************* +** +** Function btusb_lite_avdt_allocate_ccb +** +** Description allocate CCB in lite stack +** +** Returns pointer of CCB +** +*******************************************************************************/ +static struct btusb_lite_avdt_ccb *btusb_lite_avdt_allocate_ccb(struct btusb *p_dev) +{ + struct btusb_lite_avdt_ccb *p_ccb; + UINT8 ccb; + + p_ccb = &p_dev->lite_cb.s.avdt.ccb[0]; + for(ccb = 0; ccb < AVDT_NUM_LINKS; ccb++, p_ccb++) + { + if (!p_ccb->allocated) + { + BTUSB_INFO("CCB=%d allocated\n", ccb); + memset(p_ccb, 0, sizeof(struct btusb_lite_avdt_ccb)); + p_ccb->allocated = TRUE; + return(p_ccb); + } + } + return(NULL); +} + +/******************************************************************************* +** +** Function btusb_lite_avdt_free_ccb +** +** Description Free CCB in lite stack +** +** Returns None +** +*******************************************************************************/ +static void btusb_lite_avdt_free_ccb(struct btusb *p_dev, struct btusb_lite_avdt_ccb *p_ccb_free) +{ + struct btusb_lite_avdt_ccb *p_ccb; + UINT8 ccb; + + p_ccb = &p_dev->lite_cb.s.avdt.ccb[0]; + for(ccb = 0; ccb < AVDT_NUM_LINKS; ccb++, p_ccb++) + { + if (p_ccb == p_ccb_free) + { + /* Sanity */ + if (!p_ccb_free->allocated) + { + BTUSB_ERR("CCB=%d was not allocated\n", ccb); + } + BTUSB_INFO("CCB=%d freed\n", ccb); + p_ccb_free->allocated = FALSE; + return; + } + } +} + +/******************************************************************************* +** +** Function btusb_lite_avdt_allocate_scb +** +** Description allocate SCB in lite stack +** +** Returns pointer of SCB +** +*******************************************************************************/ +static struct btusb_lite_avdt_scb *btusb_lite_avdt_allocate_scb(struct btusb *p_dev) +{ + struct btusb_lite_avdt_scb *p_scb; + UINT8 scb; + + p_scb = &p_dev->lite_cb.s.avdt.scb[0]; + for(scb = 0; scb < AVDT_NUM_SEPS; scb++, p_scb++) + { + if(!p_scb->allocated) + { + BTUSB_INFO("SCB=%d allocated\n", scb); + memset(p_scb, 0, sizeof(struct btusb_lite_avdt_scb)); + p_scb->allocated = TRUE; + return(p_scb); + } + } + return(NULL); +} + +/******************************************************************************* +** +** Function btusb_lite_avdt_free_scb +** +** Description Free SCB in lite stack +** +** Returns None +** +*******************************************************************************/ +static void btusb_lite_avdt_free_scb(struct btusb *p_dev, struct btusb_lite_avdt_scb *p_scb_free) +{ + struct btusb_lite_avdt_scb *p_scb; + UINT8 scb; + + p_scb = &p_dev->lite_cb.s.avdt.scb[0]; + for(scb = 0; scb < AVDT_NUM_SEPS; scb++, p_scb++) + { + if (p_scb == p_scb_free) + { + /* Sanity */ + if (!p_scb_free->allocated) + { + BTUSB_ERR("SCB=%d was not allocated\n", scb); + } + BTUSB_INFO("SCB=%d freed\n", scb); + p_scb_free->allocated = FALSE; + return; + } + } +} + +/******************************************************************************* +** +** Function btusb_lite_avdt_send +** +** Description AVDT packet send +** +** Returns None +** +*******************************************************************************/ +void btusb_lite_avdt_send(struct btusb *p_dev, BT_HDR *p_msg, UINT8 avdt_handle, + UINT8 m_pt, UINT8 option, UINT32 timestamp) +{ + UINT8 *p_data; + struct btusb_lite_avdt_scb *p_avdt_scb; + struct btusb_lite_avdt_ccb *p_avdt_ccb; + + if (p_dev == NULL) + { + BTUSB_ERR("p_dev is NULL\n"); + if (p_msg) + GKI_freebuf(p_msg); + return; + } + + if (p_msg == NULL) + { + BTUSB_ERR("p_msg is NULL\n"); + return; + } + + /* Find the AVDT SCB with this handle */ + p_avdt_scb = btusb_lite_avdt_scb_by_hdl(p_dev, avdt_handle); + if (p_avdt_scb == NULL) + { + BTUSB_ERR("No AVDT SCB stream found\n"); + GKI_freebuf(p_msg); /* Free this ACL buffer */ + return; + } + + /* Get the associated AVDT CCB */ + p_avdt_ccb = p_avdt_scb->p_ccb; + if (p_avdt_ccb == NULL) + { + BTUSB_ERR("No AVDT CCB stream found\n"); + GKI_freebuf(p_msg); /* Free this ACL buffer */ + return; + } + + /* Write the Media Payload Header if needed */ + if ((option & BTUSB_LITE_AVDT_OPT_NO_MPH) == 0) + { + if (p_msg->offset < BTUSB_LITE_MEDIA_SIZE) + { + BTUSB_ERR("Offset too small=%d for MediaPayloadHeader\n", p_msg->offset); + GKI_freebuf(p_msg); /* Free this ACL buffer */ + return; + } + p_msg->offset -= BTUSB_LITE_MEDIA_SIZE; + p_msg->len += BTUSB_LITE_MEDIA_SIZE; + /* Get write address */ + p_data = (UINT8 *)(p_msg + 1) + p_msg->offset; + /* Write Media Payload Header (Number of SBC Frames) */ + UINT8_TO_BE_STREAM(p_data, p_msg->layer_specific & A2D_SBC_HDR_NUM_MSK); + } + + /* Write the SCMS content Protection Header if needed */ + if (p_avdt_scb->scms.enable) + { + if (p_msg->offset < BTUSB_LITE_SCMS_SIZE) + { + BTUSB_ERR("Offset too small=%d for CP Header\n", p_msg->offset); + GKI_freebuf(p_msg); /* Free this ACL buffer */ + return; + } + p_msg->offset -= BTUSB_LITE_SCMS_SIZE; + p_msg->len += BTUSB_LITE_SCMS_SIZE; + /* Get write address */ + p_data = (UINT8 *)(p_msg + 1) + p_msg->offset; + /* Write Media Payload Header (Number of SBC Frames) */ + UINT8_TO_BE_STREAM(p_data, p_avdt_scb->scms.header); + } + + /* Write the RTP Header if needed */ + if ((option & BTUSB_LITE_AVDT_OPT_NO_RTP) == 0) + { + if (p_msg->offset < BTUSB_LITE_RTP_SIZE) + { + BTUSB_ERR("Offset too small=%d for RTP Header\n", p_msg->offset); + GKI_freebuf(p_msg); /* Free this ACL buffer */ + return; + } + p_msg->offset -= BTUSB_LITE_RTP_SIZE; + p_msg->len += BTUSB_LITE_RTP_SIZE; + /* Get write address */ + p_data = (UINT8 *)(p_msg + 1) + p_msg->offset; + /* Write RTP Header */ + p_data = btusb_lite_avdt_write_rtp_header(p_data, m_pt, p_avdt_scb->media_seq, timestamp, 0); + } + + p_avdt_scb->media_seq++; /* Increment Sequence number */ + + /* Request L2CAP to send this packet */ + btusb_lite_l2c_send(p_dev, p_msg, p_avdt_ccb->lcid); +} + +/******************************************************************************* + ** + ** Function btusb_lite_avdt_write_rtp_header + ** + ** Description Write A2DP RTP Header + ** + ** Returns New buffer location + ** + *******************************************************************************/ +static UINT8 *btusb_lite_avdt_write_rtp_header(UINT8 *p_data, UINT8 m_pt, UINT16 seq_number, + UINT32 timestamp, UINT32 ssrc) +{ + /* Write RTP Header */ + UINT8_TO_BE_STREAM(p_data, AVDT_MEDIA_OCTET1); /* Version, Padding, Ext, CSRC */ + UINT8_TO_BE_STREAM(p_data, m_pt); /* Marker & Packet Type */ + UINT16_TO_BE_STREAM(p_data, seq_number); /* Sequence number */ + UINT32_TO_BE_STREAM(p_data, timestamp); /* TimeStamp */ + UINT32_TO_BE_STREAM(p_data, ssrc); /* SSRC */ + return p_data; +} + +/******************************************************************************* +** +** Function btusb_lite_avdt_cp_set_scms +** +** Description Set SCMS Content Protection for a channel +** +** Returns AVDT_SYNC_SUCCESS/AVDT_SYNC_FAILURE +** +*******************************************************************************/ +UINT8 btusb_lite_avdt_cp_set_scms(struct btusb *p_dev, UINT8 avdt_handle, + BOOLEAN enable, UINT8 scms_hdr) +{ + struct btusb_lite_avdt_scb *p_avdt_scb; + + /* Find the AVDT SCB with this handle */ + p_avdt_scb = btusb_lite_avdt_scb_by_hdl(p_dev, avdt_handle); + if (p_avdt_scb == NULL) + { + BTUSB_ERR("No AVDT SCB stream found\n"); + return AVDT_SYNC_FAILURE; + } + + BTUSB_INFO("btusb_lite_avdt_cp_set_scms handle=0x%x enable=%d header=0x%x\n", + avdt_handle, enable, scms_hdr); + + p_avdt_scb->scms.enable = enable; + p_avdt_scb->scms.header = scms_hdr; + + return AVDT_SYNC_SUCCESS; + +} diff --git a/src/btusb_lite_hci.c b/src/btusb_lite_hci.c new file mode 100755 index 0000000..321174a --- a/dev/null +++ b/src/btusb_lite_hci.c @@ -0,0 +1,440 @@ +/* + * + * btusb_lite_hci.c + * + * + * + * Copyright (C) 2011-2013 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ + +#include "btusb.h" + +/* + * Definitions + */ +#define BTUSB_LITE_HCI_NUM_CMD 0x01 /* HCI Num Command */ + +/* HCI Definitions for the NumberOfCompletePacket Event */ +#define BTUSB_LITE_HCI_NOCP_HCI_LEN 7 /* HCI Length of the NumOfCpltPacket Event */ +#define BTUSB_A2DP_NOCP_LEN 5 /* Length of the NumOfCpltPacket Param */ + +/* + * Local functions + */ +static UINT8 *btusb_lite_hci_write_acl_header(UINT8 *p_data, UINT16 con_hdl, UINT16 length); +static UINT8 *btusb_lite_hci_write_evt_header(UINT8 *p_data, UINT8 event, UINT8 length); + +static int btusb_lite_hci_transport_pause_hndl(struct btusb *p_dev, BT_HDR *p_msg); +static int btusb_lite_hci_transport_resume_hndl(struct btusb *p_dev, BT_HDR *p_msg); +static void btusb_lite_hci_cmd_cplt_evt_send(struct btusb *p_dev, + UINT16 opcode, UINT8 *p_param, UINT8 param_len); +static int btusb_lite_hci_nocp_event_hdlr(struct btusb *p_dev, UINT8 *p_data, int length); + + +/******************************************************************************* + ** + ** Function btusb_lite_hci_acl_send + ** + ** Description Send an ACL packet to HCI + ** + ** Returns Void + ** + *******************************************************************************/ +int btusb_lite_hci_acl_send(struct btusb *p_dev, BT_HDR *p_msg, UINT16 con_hdl) +{ + UINT8 *p_data; + + /* Sanity */ + if (p_msg->offset < BTUSB_LITE_HCI_ACL_HDR_SIZE) + { + BTUSB_ERR("offset too small=%d\n", p_msg->offset); + GKI_freebuf(p_msg); /* Free this ACL buffer */ + return -1; + } + + /* Decrement offset to add headers */ + p_msg->offset -= BTUSB_LITE_HCI_ACL_HDR_SIZE; + + /* Get address of the HCI Header */ + p_data = (UINT8 *)(p_msg + 1) + p_msg->offset; + + /* Write L2CAP Header (length field is SBC Frames + RTP/A2DP/Media Header) */ + p_data = btusb_lite_hci_write_acl_header(p_data, con_hdl, p_msg->len); + + /* Increment length */ + p_msg->len += BTUSB_LITE_HCI_ACL_HDR_SIZE; + + /* Add this ACL data in the USB Tx queue and notify the btusb_tx_task to process */ + GKI_enqueue(&p_dev->tx_queue, p_msg); + + /* Wake up tasklet (with High priority) */ + tasklet_schedule(&p_dev->tx_task); + + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_lite_hci_cmd_filter + ** + ** Description Check if the Sent HCI Command need to be handled/caught (not + ** sent to BT controller). + ** + ** Returns status: <> 0 if the command must be send to BT controller + ** 0 if the command is handled + ** + *******************************************************************************/ +int btusb_lite_hci_cmd_filter(struct btusb *p_dev, BT_HDR *p_msg) +{ + UINT8 *p; + UINT8 hci_type; + UINT16 opcode; + int rv = 1; /* HCI command not handled by default */ + + p = (UINT8 *)(p_msg + 1); + + STREAM_TO_UINT8(hci_type, p); /* Extract HCI Type */ + + if (hci_type != HCIT_TYPE_COMMAND) + { + /* This is not an HCI Command */ + return rv; /* Send it to BT Controller */ + } + + STREAM_TO_UINT16(opcode, p); /* Extract HCI Command OpCode */ + + switch(opcode) + { + case HCI_BRCM_PAUSE_TRANSPORT: + rv = btusb_lite_hci_transport_pause_hndl(p_dev, p_msg); + break; + + case HCI_BRCM_TRANSPORT_RESUME: + /* Call the function in charge of filtering UIPC Over HCI VSC */ + rv = btusb_lite_hci_transport_resume_hndl(p_dev, p_msg); + break; + + /* Add here other HCI Command OpCodes to filter */ + default: + break; + /* Do not filter other HCI Command OpCodes */ + } + return rv; +} + +/******************************************************************************* + ** + ** Function btusb_lite_hci_transport_pause_hndl + ** + ** Description Handles the HCI Transport Pause VSC. + ** + ** Returns status: <> 0 if the command must be send to BT controller + ** 0 if the command is handled + ** + *******************************************************************************/ +static int btusb_lite_hci_transport_pause_hndl(struct btusb *p_dev, BT_HDR *p_msg) +{ + UINT8 param[sizeof(UINT8)]; + UINT8 *p_param = param; + + BTUSB_INFO("HCI_TransportPause VSC caught\n"); + + UINT8_TO_STREAM(p_param, HCI_SUCCESS); + + btusb_lite_hci_cmd_cplt_evt_send(p_dev, HCI_BRCM_PAUSE_TRANSPORT, param, + p_param - param); + + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_lite_hci_transport_resume_hndl + ** + ** Description Handles the HCI Transport Pause VSC. + ** + ** Returns status: <> 0 if the command must be send to BT controller + ** 0 if the command is handled + ** + *******************************************************************************/ +static int btusb_lite_hci_transport_resume_hndl(struct btusb *p_dev, BT_HDR *p_msg) +{ + UINT8 param[sizeof(UINT8)]; + UINT8 *p_param = param; + + BTUSB_INFO("HCI_TransportResume VSC caught\n"); + + UINT8_TO_STREAM(p_param, HCI_SUCCESS); + + btusb_lite_hci_cmd_cplt_evt_send(p_dev, HCI_BRCM_TRANSPORT_RESUME, param, + p_param - param); + + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_lite_hci_event_filter + ** + ** Description Filter HCI Events received from BT Controller. + ** + ** Returns status: <> 0 if the event must be send to user space (BSA) + ** 0 if the event is handled + ** + *******************************************************************************/ +int btusb_lite_hci_event_filter(struct btusb *p_dev, UINT8 *p_data, int length) +{ +#if 0 + BT_HDR *p_msg; + UINT8 *p; + UINT16 size; +#endif + + /* Check if HCI is over IPC */ + if (btusb_lite_is_hci_over_ipc(p_dev) == 0) + { + /* If it is not, the event have to be sent through regular HCI */ + return 1; + } + + /* Check if the Event is a NumberOfCompletePacket Event */ + if (btusb_lite_hci_nocp_event_hdlr(p_dev, p_data, length) == 0) + { + return 0; /* Do not Send this event to user space (we handled it) */ + } + + /* TODO: check if CSB VSE */ + + return 1; + +#if 0 + + /* Add size of both UIPC Length and Event header */ + size = length + sizeof(UINT16) + sizeof(UINT16); + + /* Get a buffer from the pool */ + p_msg = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + size); + if(unlikely(p_msg == NULL)) + { + BTUSB_ERR("Unable to get GKI buffer\n"); + return 0; + } + + if (unlikely(dbgflags & BTUSB_GKI_CHK_MSG) && + unlikely(GKI_buffer_status(p_msg) != BUF_STATUS_UNLINKED)) + { + BTUSB_ERR("buffer != BUF_STATUS_UNLINKED 0x%p\n", p_msg); + return; + } + + p_msg->offset = 0; + p_msg->event = 0; + p_msg->len = size; + + p = (UINT8 *)(p_msg + 1) + p_msg->offset; + + UINT16_TO_STREAM(p, length + sizeof(UINT16)); /* UIPC Length */ + UINT16_TO_STREAM(p, BT_EVT_TO_BTU_HCI_EVT); /* UIPC Event */ + UINT8_TO_STREAM(p, hci_event); /* Write back the HCI Event (we already read it) */ + ARRAY_TO_STREAM(p, p_data, length - 1); /* Copy Event data */ + + /* Send message to User Space */ + btusb_lite_ipc_sent_to_user(p_dev, p_msg); + + return 0; /* Event handled by the Stack Lite. No need to send it to HCI */ +#endif +} + +/******************************************************************************* + ** + ** Function btusb_lite_hci_nocp_event_hdlr + ** + ** Description Check if the received HCI Event is A NumberOfdComplete Evt + ** sub opcode for a Started BAV stream. + ** + ** Returns status: <> 0 if the event must be send to user space (BSA) + ** 0 if the event is handled + ** + *******************************************************************************/ +static int btusb_lite_hci_nocp_event_hdlr(struct btusb *p_dev, UINT8 *p_data, int length) +{ + UINT8 nb_handle; + UINT16 con_hdl; + UINT16 num_cplt_pck; + UINT8 byte; + UINT8 *p_save; + int send_to_user; + UINT16 num_cplt_pck_caugth; + + /* We are waiting for an Event of, at least, 7 bytes */ + if (length < BTUSB_LITE_HCI_NOCP_HCI_LEN) + { + return 1; /* This is not a NOCP. Send this event to user space */ + } + + /* Extract Event */ + STREAM_TO_UINT8(byte, p_data); + + /* Check if it's a NumberOfCompletePacket Event */ + if (byte != HCI_NUM_COMPL_DATA_PKTS_EVT) + { + return 1; /* This is not a NOCP. Send this event to user space */ + } + + /* Extract Parameter Length */ + STREAM_TO_UINT8(byte, p_data); + + /* Extract Number Of Handle */ + STREAM_TO_UINT8(nb_handle, p_data); + + /* Sanity */ + if (byte != (1 + (2 + 2) * nb_handle)) + { + BTUSB_ERR("Unexpected Evt Size=%d vs.NumberOfHandle=%d\n", byte, nb_handle); + return 1; /* This is not a NOCP. Send this event to user space */ + } + + send_to_user = 0; /* For the moment, no Complete Packet sent to user */ + + /* For every Handle */ + while(nb_handle--) + { + /* Extract the Connection Handle */ + STREAM_TO_UINT16(con_hdl, p_data); + + /* Save the current pointer position (to overwrite number of packet) */ + p_save = p_data; + + /* Extract the Number Of Complete Packet */ + STREAM_TO_UINT16(num_cplt_pck, p_data); + + /* Call the L2CAP NumberOfcompletePacket Handler */ + num_cplt_pck_caugth = btusb_lite_l2c_nocp_hdlr(p_dev, con_hdl, num_cplt_pck); + + /* If L2CAP "caught"at least one nocp packet */ + if (num_cplt_pck_caugth) + { + /* Overwrite the Number Of Complete Packet */ + UINT16_TO_STREAM(p_save, num_cplt_pck - num_cplt_pck_caugth); + + /* If at least one Number Of Complete Packet remains */ + if (num_cplt_pck - num_cplt_pck_caugth) + { + /* Send the event to user space */ + send_to_user = 1; + } + } + else + { + /* Don't update the number but send the event to user space */ + send_to_user = 1; + } + } + + return send_to_user; +} +/******************************************************************************* + ** + ** Function btusb_lite_hci_cmd_cplt_evt_send + ** + ** Description Send an HCI VSC Cmd Complete. + ** + ** Returns Void + ** + *******************************************************************************/ +static void btusb_lite_hci_cmd_cplt_evt_send(struct btusb *p_dev, + UINT16 opcode, UINT8 *p_param, UINT8 param_len) +{ + BT_HDR *p_msg; + UINT16 size = param_len + 5; + UINT8 *p; + + /* Get a buffer from the pool */ + p_msg = (BT_HDR *)GKI_getbuf(sizeof(BT_HDR) + size); + if(unlikely(p_msg == NULL)) + { + BTUSB_ERR("Unable to get GKI buffer\n"); + return; + } + + if (unlikely(dbgflags & BTUSB_GKI_CHK_MSG) && + unlikely(GKI_buffer_status(p_msg) != BUF_STATUS_UNLINKED)) + { + BTUSB_ERR("buffer != BUF_STATUS_UNLINKED 0x%p\n", p_msg); + return; + } + + p_msg->offset = 0; + p_msg->event = HCIT_TYPE_EVENT; + p_msg->len = size; + p_msg->layer_specific = BTUSB_LS_GKI_BUFFER; + + p = (UINT8 *)(p_msg + 1) + p_msg->offset; + + p = btusb_lite_hci_write_evt_header(p, HCI_COMMAND_COMPLETE_EVT, size - 2); + + UINT8_TO_STREAM(p, BTUSB_LITE_HCI_NUM_CMD); /* HCI Num Command */ + UINT16_TO_STREAM(p, opcode); /* HCI OpCode */ + + if (p_param) + { + ARRAY_TO_STREAM(p, p_param, param_len) + } + + /* InQ for user-space to read */ + GKI_enqueue(&p_dev->rx_queue, p_msg); + + /* if the process is polling, indicate RX event */ + wake_up_interruptible(&p_dev->rx_wait_q); +} + +/******************************************************************************* + ** + ** Function btusb_lite_hci_write_acl_header + ** + ** Description Write HCI ACL Header (HCI_Type, Connection Handle, Length) + ** + ** Returns New buffer location + ** + *******************************************************************************/ +static UINT8 *btusb_lite_hci_write_acl_header(UINT8 *p_data, UINT16 con_hdl, UINT16 length) +{ + UINT8_TO_STREAM(p_data, HCIT_TYPE_ACL_DATA); /* HCI_Type */ + UINT16_TO_STREAM(p_data, con_hdl); /* Connection Handle */ + UINT16_TO_STREAM(p_data, length); /* Length */ + return p_data; +} + +/******************************************************************************* + ** + ** Function btusb_lite_hci_write_evt_header + ** + ** Description Write HCI Event Header (HCI_Type, Connection Handle, Length) + ** + ** Returns New buffer location + ** + *******************************************************************************/ +static UINT8 *btusb_lite_hci_write_evt_header(UINT8 *p_data, UINT8 event, UINT8 length) +{ + UINT8_TO_STREAM(p_data, event); + UINT8_TO_STREAM(p_data, length); /* Param Length */ + return p_data; +} + diff --git a/src/btusb_lite_l2c.c b/src/btusb_lite_l2c.c new file mode 100755 index 0000000..9d227d2 --- a/dev/null +++ b/src/btusb_lite_l2c.c @@ -0,0 +1,278 @@ +/* + * + * btusb_lite_l2c.c + * + * + * + * Copyright (C) 2011-2013 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ + +#include "btusb.h" + +/* + * Definitions + */ + +/* + * Local functions + */ +static UINT8 *btusb_lite_l2c_write_header(UINT8 *p_data, UINT16 length, UINT16 cid); + +/******************************************************************************* +** +** Function btusb_lite_l2c_add +** +** Description Synchronize (Add) L2CAP Stream +** +** Returns Status. +** +*******************************************************************************/ +int btusb_lite_l2c_add(struct btusb *p_dev, tL2C_STREAM_INFO *p_l2c_stream) +{ + int idx; + struct btusb_lite_l2c_cb *p_l2c = &p_dev->lite_cb.s.l2c; + struct btusb_lite_l2c_ccb *p_l2c_ccb; + + /* Check if this L2CAP Stream exists */ + for (idx = 0, p_l2c_ccb = p_l2c->ccb ; idx < ARRAY_SIZE(p_l2c->ccb) ; idx++, p_l2c_ccb++) + { + if ((p_l2c_ccb->in_use) && + (p_l2c_ccb->handle == p_l2c_stream->handle)) + { + BTUSB_INFO("CCB=%d was already allocated. Update it.\n", idx); + break; + } + } + /* If Not found */ + if (idx == BTM_SYNC_INFO_NUM_STR) + { + /* Look for a free CCB */ + for (idx = 0, p_l2c_ccb = p_l2c->ccb ; idx < ARRAY_SIZE(p_l2c->ccb) ; idx++, p_l2c_ccb++) + { + if (p_l2c_ccb->in_use == FALSE) + { + BTUSB_INFO("CCB=%d allocated\n", idx); + memset(p_l2c_ccb, 0, sizeof(*p_l2c_ccb)); + p_l2c_ccb->in_use = TRUE; + p_l2c_ccb->local_cid = p_l2c_stream->local_cid; + p_l2c_ccb->remote_cid = p_l2c_stream->remote_cid; + p_l2c_ccb->out_mtu = p_l2c_stream->out_mtu; + p_l2c_ccb->handle = p_l2c_stream->handle; + p_l2c_ccb->is_flushable = p_l2c_stream->is_flushable; + break; + } + } + } + + /* If Not found ot not allocated */ + if (idx == BTM_SYNC_INFO_NUM_STR) + { + BTUSB_ERR("No Free L2C CCB found (handle=0x%x)\n", p_l2c_stream->handle); + return -1; + } + + /* Update Transmit Quota */ + p_l2c_ccb->link_xmit_quota = p_l2c_stream->link_xmit_quota; + + return 0; +} + +/******************************************************************************* +** +** Function btusb_lite_l2c_remove +** +** Description Synchronize (Remove) L2CAP Stream +** +** Returns Status. +** +*******************************************************************************/ +int btusb_lite_l2c_remove(struct btusb *p_dev, UINT16 local_cid) +{ + int idx; + struct btusb_lite_l2c_cb *p_l2c = &p_dev->lite_cb.s.l2c; + struct btusb_lite_l2c_ccb *p_l2c_ccb; + + /* Check if this L2CAP Stream exists */ + for (idx = 0, p_l2c_ccb = p_l2c->ccb ; idx < ARRAY_SIZE(p_l2c->ccb) ; idx++, p_l2c_ccb++) + { + if ((p_l2c_ccb->in_use) && + (p_l2c_ccb->local_cid == local_cid)) + { + break; + } + } + /* If Not found */ + if (idx == BTM_SYNC_INFO_NUM_STR) + { + BTUSB_ERR("L2C CCB found (lcid=0x%x)\n",local_cid); + return -1; + } + + BTUSB_INFO("CCB=%d freed\n", idx); + + /* Reset (Free) the L2CAP Stream */ + memset(p_l2c_ccb, 0, sizeof(*p_l2c_ccb)); + + return 0; +} + +/******************************************************************************* +** +** Function btusb_lite_l2c_send +** +** Description Send L2CAP packet +** +** Returns Status. +** +*******************************************************************************/ +int btusb_lite_l2c_send(struct btusb *p_dev, BT_HDR *p_msg, UINT16 local_cid) +{ + int idx; + struct btusb_lite_l2c_cb *p_l2c; + struct btusb_lite_l2c_ccb *p_l2c_ccb; + UINT8 *p_data; + + /* Look for the first AV stream Started */ + p_l2c = &p_dev->lite_cb.s.l2c; + for (idx = 0, p_l2c_ccb = p_l2c->ccb ; idx < BTM_SYNC_INFO_NUM_STR ; idx++, p_l2c_ccb++) + { + if (p_l2c_ccb->local_cid == local_cid) + { + break; + } + } + if (idx == BTM_SYNC_INFO_NUM_STR) + { + BTUSB_ERR("No L2C CCB found (lcid=0x%x)\n", local_cid); + GKI_freebuf(p_msg); /* Free this ACL buffer */ + return -1; + } + + /* Check if the Tx Quota has been reached for this channel */ + if (p_l2c_ccb->tx_pending >= p_l2c_ccb->link_xmit_quota) + { + BTUSB_ERR("Tx Quota reached for L2CAP channel (lcid=0x%x). Drop buffer\n", local_cid); + GKI_freebuf(p_msg); /* Free this ACL buffer */ + return -1; + } + + /* Sanity */ + if (p_msg->offset < BTUSB_LITE_L2CAP_HDR_SIZE) + { + BTUSB_ERR("offset too small=%d\n", p_msg->offset); + GKI_freebuf(p_msg); /* Free this ACL buffer */ + return-1; + } + + /* Decrement offset to add headers */ + p_msg->offset -= BTUSB_LITE_L2CAP_HDR_SIZE; + + /* Get address of the HCI Header */ + p_data = (UINT8 *)(p_msg + 1) + p_msg->offset; + + /* Write L2CAP Header (length field is SBC Frames + RTP/A2DP/Media Header) */ + p_data = btusb_lite_l2c_write_header(p_data, p_msg->len, p_l2c_ccb->remote_cid); + + /* Increment length */ + p_msg->len += BTUSB_LITE_L2CAP_HDR_SIZE; + + + GKI_disable(); /* tx_pending field can be updated by another context */ + p_l2c_ccb->tx_pending++; /* One more A2DP L2CAP packet pending */ + BTUSB_DBG("L2C Tx Pending=%d\n", p_l2c_ccb->tx_pending); + GKI_enable(); + + if (btusb_lite_hci_acl_send(p_dev, p_msg, p_l2c_ccb->handle) < 0) + { + GKI_disable(); /* tx_pending field can be updated by another context */ + p_l2c_ccb->tx_pending--; /* Remove A2DP L2CAP packet pending */ + GKI_enable(); + return -1; + } + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_lite_l2c_write_header + ** + ** Description Write L2CAP ACL Header (Length, Channel Id) + ** + ** Returns New buffer location + ** + *******************************************************************************/ +static UINT8 *btusb_lite_l2c_write_header(UINT8 *p_data, UINT16 length, UINT16 cid) +{ + UINT16_TO_STREAM(p_data, length); /* Length */ + UINT16_TO_STREAM(p_data, cid); /* Channel Id */ + return p_data; +} + +/******************************************************************************* + ** + ** Function btusb_lite_l2c_nocp_hdlr + ** + ** Description L2CAP NumberOfcompletePacket Handler function + ** + ** Returns Number Of Complete Packet caught + ** + *******************************************************************************/ +UINT16 btusb_lite_l2c_nocp_hdlr(struct btusb *p_dev, UINT16 con_hdl, UINT16 num_cplt_pck) +{ + struct btusb_lite_l2c_cb *p_l2c; + struct btusb_lite_l2c_ccb *p_l2c_ccb; + int i; + UINT16 num_cplt_pck_caugth; + + /* Look for the L2CAP channel matching the Connection Handle */ + p_l2c = &p_dev->lite_cb.s.l2c; + for (i = 0, p_l2c_ccb = p_l2c->ccb ; i < BTM_SYNC_INFO_NUM_STR ; i++, p_l2c_ccb++) + { + if (p_l2c_ccb->handle == con_hdl) + { + break; + } + } + /* If L2CAP channel not found/known */ + if (i == BTM_SYNC_INFO_NUM_STR) + { + return 0; + } + + /* If no Tx Pending */ + if (p_l2c_ccb->tx_pending == 0) + { + return 0; + } + + GKI_disable(); /* tx_pending field can be updated by another context */ + + /* Take the min between the number of pending packet and the number of acked packet */ + num_cplt_pck_caugth = min(p_l2c_ccb->tx_pending, num_cplt_pck); + + /* Update the number of pending packet */ + p_l2c_ccb->tx_pending-= num_cplt_pck_caugth; + + BTUSB_DBG("L2C NOCP Tx Pending=%d\n", p_l2c_ccb->tx_pending); + + GKI_enable(); + + return num_cplt_pck_caugth; +} diff --git a/src/btusb_proc.c b/src/btusb_proc.c new file mode 100755 index 0000000..809b25c --- a/dev/null +++ b/src/btusb_proc.c @@ -0,0 +1,763 @@ +/* + * + * btusb_proc.c + * + * + * + * Copyright (C) 2011-2014 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * Module that implements the proc file system routines. + * + */ + +#include "btusb_proc.h" + +#include <linux/slab.h> /* for kmalloc/kfree */ +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <asm/uaccess.h> +#include "btusb.h" + +static struct proc_dir_entry *btusb_proc_dir = NULL; + +/* Definition of the proc interface */ +struct btusb_proc_file +{ + const char *name; + umode_t mode; + const struct seq_operations seq_ops; +}; + + +/******************************************************************************* + ** + ** Function btusb_version_show + ** + ** Description Version display function + ** + *******************************************************************************/ +int btusb_version_show(struct seq_file *s, void *unused) +{ + /* s->private points to unknown data */ + seq_printf(s, "version : %s\n", bsa_version_string); + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_info_show + ** + ** Description Information display function + ** + *******************************************************************************/ +int btusb_info_show(struct seq_file *s, void *unused) +{ + struct btusb *p_dev = s->private; + const struct usb_device_id *p_id = p_dev->p_id; + struct usb_device *udev = p_dev->p_udev; + struct usb_host_interface *p_host_intf; + struct usb_endpoint_descriptor *p_ep_desc; + int idx, jdx; + + BTUSB_DBG("p_dev=%p\n", p_dev); + + seq_printf(s, "USB device :\n"); + seq_printf(s, " - Match info:\n"); + if (p_id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) + seq_printf(s, " * USB_DEVICE_ID_MATCH_VENDOR (0x%02X)\n", p_id->idVendor); + if (p_id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) + seq_printf(s, " * USB_DEVICE_ID_MATCH_PRODUCT 0x%02X)\n", p_id->idProduct); + if (p_id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) + seq_printf(s, " * USB_DEVICE_ID_MATCH_DEV_LO (%u)\n", p_id->bcdDevice_lo); + if (p_id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) + seq_printf(s, " * USB_DEVICE_ID_MATCH_DEV_HI (%u)\n", p_id->bcdDevice_hi); + if (p_id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) + seq_printf(s, " * USB_DEVICE_ID_MATCH_DEV_CLASS (0x%02X)\n", p_id->bDeviceClass); + if (p_id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) + seq_printf(s, " * USB_DEVICE_ID_MATCH_DEV_SUBCLASS (0x%02X)\n", p_id->bDeviceSubClass); + if (p_id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) + seq_printf(s, " * USB_DEVICE_ID_MATCH_DEV_PROTOCOL (0x%02X)\n", p_id->bDeviceProtocol); + if (p_id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) + seq_printf(s, " * USB_DEVICE_ID_MATCH_INT_CLASS (0x%02X)\n", p_id->bInterfaceClass); + if (p_id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) + seq_printf(s, " * USB_DEVICE_ID_MATCH_INT_SUBCLASS (0x%02X)\n", p_id->bInterfaceSubClass); + if (p_id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) + seq_printf(s, " * USB_DEVICE_ID_MATCH_INT_PROTOCOL (0x%02X)\n", p_id->bInterfaceProtocol); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + if (p_id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) + seq_printf(s, " * USB_DEVICE_ID_MATCH_INT_NUMBER (%u)\n", p_id->bInterfaceNumber); +#endif + + seq_printf(s, " - Address = %d\n", udev->devnum); + seq_printf(s, " - VendorId = %04x\n", le16_to_cpu(udev->descriptor.idVendor)); + seq_printf(s, " - ProductId = %04x\n", le16_to_cpu(udev->descriptor.idProduct)); + seq_printf(s, " - Manufacturer String = %s\n", udev->manufacturer); + seq_printf(s, " - Product String = %s\n", udev->product); + seq_printf(s, " - USB bus number = %d\n", udev->bus->busnum); + seq_printf(s, " - USB devpath = %s\n", udev->devpath); + seq_printf(s, " - USB devnum = %d\n", udev->devnum); + seq_printf(s, " - USB ttport = %d\n", udev->ttport); + seq_printf(s, " - Interfaces :\n"); + seq_printf(s, " * MAIN : "); + if (p_dev->p_main_intf) + { + seq_printf(s, "intf = %d (nb alt settings = %d, ", + p_dev->p_main_intf->cur_altsetting->desc.bInterfaceNumber, + p_dev->p_main_intf->num_altsetting); + seq_printf(s, "cur alt setting = %d)\n", p_dev->p_main_intf->cur_altsetting->desc.bAlternateSetting); + seq_printf(s, " * HCI EVENT : "); + if (p_dev->p_event_in) + { + seq_printf(s, "ep = 0x%02x\n", p_dev->p_event_in->desc.bEndpointAddress); + } + else + { + seq_printf(s, "ERROR (endpoint not found)\n"); + } + seq_printf(s, " * ACL RX : "); + if (p_dev->p_acl_in) + { + seq_printf(s, "ep = 0x%02x\n", p_dev->p_acl_in->desc.bEndpointAddress); + } + else + { + seq_printf(s, "ERROR (endpoint not found)\n"); + } + seq_printf(s, " * ACL TX : "); + if (p_dev->p_acl_out) + { + seq_printf(s, "ep = 0x%02x\n", p_dev->p_acl_out->desc.bEndpointAddress); + } + else + { + seq_printf(s, "ERROR (endpoint not found)\n"); + } + } + else + { + seq_printf(s, "Not present\n"); + } + seq_printf(s, " * VOICE :"); + if (p_dev->p_voice_intf) + { + seq_printf(s, " intf = %d (nb alt setting = %d, ", p_dev->p_voice_intf->cur_altsetting->desc.bInterfaceNumber, p_dev->p_voice_intf->num_altsetting); + seq_printf(s, "cur alt setting = %d)\n", p_dev->p_voice_intf->cur_altsetting->desc.bAlternateSetting); + for (idx = 0; idx < p_dev->p_voice_intf->num_altsetting; idx++) + { + p_host_intf = &p_dev->p_voice_intf->altsetting[idx]; + seq_printf(s, " * alt setting %d (idx %d) : %d enpoints\n", + p_host_intf->desc.bAlternateSetting, idx, p_host_intf->desc.bNumEndpoints); + for (jdx = 0; jdx < p_host_intf->desc.bNumEndpoints; jdx++) + { + p_ep_desc = &p_host_intf->endpoint[jdx].desc; + seq_printf(s, " * ep = 0x%02x : ", p_ep_desc->bEndpointAddress); + if (usb_endpoint_type(p_ep_desc) == USB_ENDPOINT_XFER_ISOC) + { + seq_printf(s, "Isoch "); + if (usb_endpoint_dir_out(p_ep_desc)) + { + seq_printf(s, "(OUT) "); + } + else + { + seq_printf(s, "(IN) "); + } + seq_printf(s, "wMaxPacketSize = %d\n", le16_to_cpu(p_ep_desc->wMaxPacketSize)); + } + else + { + seq_printf(s, "not isochronous endpoint\n"); + } + } + } + } + else + { + seq_printf(s, "Not present\n"); + } + seq_printf(s, " * DIAG RX : "); + if (p_dev->p_diag_in) + { + seq_printf(s, "intf = %d ep = 0x%02x\n", p_dev->p_diag_intf->cur_altsetting->desc.bInterfaceNumber, p_dev->p_diag_in->desc.bEndpointAddress); + } + else + { + seq_printf(s, "Not present\n"); + } + seq_printf(s, " * DIAG TX : "); + if (p_dev->p_diag_out) + { + seq_printf(s, "intf = %d ep = 0x%02x\n", p_dev->p_diag_intf->cur_altsetting->desc.bInterfaceNumber, p_dev->p_diag_out->desc.bEndpointAddress); + } + else + { + seq_printf(s, "Not present\n"); + } + seq_printf(s, " * DFU : "); + if (p_dev->p_dfu_intf) + { + seq_printf(s, "intf = %d\n", p_dev->p_dfu_intf->cur_altsetting->desc.bInterfaceNumber); + } + else + { + seq_printf(s, "Not present\n"); + } + + seq_printf(s, "Memory usage :\n"); + seq_printf(s, " - p_dev = %p\n", p_dev); + seq_printf(s, " - size = %zd\n", sizeof(*p_dev)); + seq_printf(s, " * CMD = off:%zd/size=%zd\n", offsetof(struct btusb, cmd_array), sizeof(p_dev->cmd_array)); + seq_printf(s, " * EVENT = off:%zd/size=%zd\n", offsetof(struct btusb, event_array), sizeof(p_dev->event_array)); + seq_printf(s, " * ACL RX = off:%zd/size=%zd\n", offsetof(struct btusb, acl_rx_array), sizeof(p_dev->acl_rx_array)); + seq_printf(s, " * ACL TX = off:%zd/size=%zd\n", offsetof(struct btusb, acl_tx_array), sizeof(p_dev->acl_tx_array)); + seq_printf(s, " * DIAG RX = off:%zd/size=%zd\n", offsetof(struct btusb, diag_rx_array), sizeof(p_dev->diag_rx_array)); + seq_printf(s, " * DIAG TX = off:%zd/size=%zd\n", offsetof(struct btusb, diag_tx_array), sizeof(p_dev->diag_tx_array)); + seq_printf(s, " * VOICE = off:%zd/size=%zd\n", offsetof(struct btusb, voice_rx.channels), offsetof(struct btusb, rx_queue)-offsetof(struct btusb, voice_rx.channels)); + + seq_printf(s, "Config :\n"); + seq_printf(s, " - issharedusb = %u\n", p_dev->issharedusb); + seq_printf(s, " - quirks = %u\n", p_dev->quirks); + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_status_show + ** + ** Description Status display function + ** + *******************************************************************************/ +int btusb_status_show(struct seq_file *s, void *unused) +{ + struct btusb *p_dev = s->private; + struct btusb_voice_channel *p_chan; + struct btusb_scosniff *p, *n; + int idx, jdx; + + BTUSB_DBG("p_dev=%p\n", p_dev); + + seq_printf(s, "Voice :\n"); + jdx = 0; + for (idx = 0; idx < ARRAY_SIZE(p_dev->voice_rx.channels); idx++) + { + p_chan = &p_dev->voice_rx.channels[idx]; + if (p_chan->used) + { + seq_printf(s, " - channel %d : SCO handle = %d(0x%02x) burst = %d\n", idx, + p_chan->handle, p_chan->handle, p_chan->burst); + jdx = 1; + } + } + if (!jdx) + { + seq_printf(s, " - No active channels\n"); + } + seq_printf(s, "SCO sniffing :\n"); + seq_printf(s, " - list: %p\n", &p_dev->scosniff_list); + seq_printf(s, " - list.next: %p\n", p_dev->scosniff_list.next); + seq_printf(s, " - list.prev: %p\n", p_dev->scosniff_list.prev); + seq_printf(s, " - whole list:\n"); + list_for_each_entry_safe(p, n, &p_dev->scosniff_list, lh) + { + seq_printf(s, " > %p\n", p); + } + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_stats_show + ** + ** Description Statistics display function + ** + *******************************************************************************/ +#define BTUSB_STATS(__n) \ + seq_printf(s, #__n " = %lu\n", p_dev->stats.__n) +int btusb_stats_show(struct seq_file *s, void *unused) +{ + struct btusb *p_dev = s->private; + + BTUSB_STATS(urb_submit_ok); + BTUSB_STATS(urb_submit_err); + BTUSB_STATS(acl_rx_submit_ok); + BTUSB_STATS(acl_rx_submit_err); + BTUSB_STATS(acl_rx_complete); + BTUSB_STATS(acl_rx_complete_err); + BTUSB_STATS(acl_rx_resubmit); + BTUSB_STATS(acl_rx_bytes); + BTUSB_STATS(event_submit_ok); + BTUSB_STATS(event_submit_err); + BTUSB_STATS(event_complete); + BTUSB_STATS(event_complete_err); + BTUSB_STATS(event_resubmit); + BTUSB_STATS(event_bytes); + BTUSB_STATS(diag_rx_submit_ok); + BTUSB_STATS(diag_rx_submit_err); + BTUSB_STATS(diag_rx_complete); + BTUSB_STATS(diag_rx_complete_err); + BTUSB_STATS(diag_rx_resubmit); + BTUSB_STATS(diag_rx_bytes); + BTUSB_STATS(diag_rx_bytes); + BTUSB_STATS(urb_out_complete); + BTUSB_STATS(urb_out_complete_err); + BTUSB_STATS(cmd_submit_ok); + BTUSB_STATS(cmd_submit_err); + BTUSB_STATS(cmd_complete); + BTUSB_STATS(cmd_complete_err); + BTUSB_STATS(voicerx_submit_ok); + BTUSB_STATS(voicerx_submit_err); + BTUSB_STATS(voicerx_complete); + BTUSB_STATS(voicerx_complete_err); + BTUSB_STATS(voicerx_bad_frames); + BTUSB_STATS(voicerx_bad_hdr); + BTUSB_STATS(voicerx_split_hdr); + BTUSB_STATS(voicerx_raw_bytes); + BTUSB_STATS(voicerx_skipped_bytes); + BTUSB_STATS(voicerx_bad_size); + BTUSB_STATS(voicetx_submit_ok); + BTUSB_STATS(voicetx_submit_err); + BTUSB_STATS(voicetx_nobuf); + BTUSB_STATS(voicetx_complete); + BTUSB_STATS(voicetx_complete_err); + return 0; +} + +/******************************************************************************* + ** + ** Function btusb_scosniff_start + ** + ** Description SCO sniffing sequence start function + ** + *******************************************************************************/ +void *btusb_scosniff_start(struct seq_file *s, loff_t *pos) +{ + struct btusb *p_dev = s->private; + struct btusb_scosniff *bs; + int rv; + + BTUSB_INFO("waiting %p\n", p_dev); + rv = wait_for_completion_interruptible(&p_dev->scosniff_completion); + if (rv < 0) + return NULL; + + BTUSB_INFO("triggered\n"); + + if (!list_empty(&p_dev->scosniff_list)) + { + bs = list_first_entry(&p_dev->scosniff_list, struct btusb_scosniff, lh); + + /* remove the element from the list */ + list_del(&bs->lh); + BTUSB_INFO("receiving %p\n", bs); + + return bs; + } + return NULL; +} + +/******************************************************************************* + ** + ** Function btusb_scosniff_next + ** + ** Description SCO sniffing sequence next function + ** + *******************************************************************************/ +void *btusb_scosniff_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct btusb_scosniff *bs = v; + BTUSB_INFO("next\n"); + + kfree(bs); + + (*pos)++; + + /* if you do not want to buffer the data, just return NULL, otherwise, call start + * again in order to use as much of the allocated PAGE in seq_read + * + * optional: + * return btusb_scosniff_start(s, pos); + */ + return NULL; +} + +/******************************************************************************* + ** + ** Function btusb_scosniff_stop + ** + ** Description SCO sniffing sequence stop function + ** + *******************************************************************************/ +void btusb_scosniff_stop(struct seq_file *s, void *v) +{ + BTUSB_INFO("stop\n"); +} + +/******************************************************************************* + ** + ** Function btusb_scosniff_show + ** + ** Description SCO sniffing display function + ** + *******************************************************************************/ +int btusb_scosniff_show(struct seq_file *s, void *v) +{ + struct btusb_scosniff *bs = v; + unsigned int i, j; + unsigned char *p_buf, *p_c; + unsigned char c; + struct usb_iso_packet_descriptor *p_uipd; + const char hexdigit[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + + seq_printf(s, "%u %u %d \n", bs->n, bs->l, bs->s); + p_buf = (unsigned char *)&bs->d[bs->n]; + for (i = 0; i < bs->n; i++) + { + p_uipd = &bs->d[i]; + seq_printf(s, " %d %u %u %u\n", p_uipd->status, p_uipd->actual_length, p_uipd->length, p_uipd->offset); + for (j = 0, p_c = &p_buf[p_uipd->offset]; j < p_uipd->actual_length; j++, p_c++) + { + c = *p_c; + seq_putc(s, hexdigit[c >> 4]); + seq_putc(s, hexdigit[c & 0xF]); + seq_putc(s, ' '); + } + seq_putc(s, '\n'); + } + + return 0; +} + +const struct seq_operations btusb_scosniff_seq_ops = +{ + .start = btusb_scosniff_start, + .next = btusb_scosniff_next, + .stop = btusb_scosniff_stop, + .show = btusb_scosniff_show, +}; + +/******************************************************************************* + ** + ** Function btusb_proc_open + ** + ** Description Open handler of the proc interface + ** + *******************************************************************************/ +int btusb_proc_open(struct inode *inode, struct file *file) +{ + int rv; + struct btusb_proc_file *p_file = BTUSB_PDE_DATA(inode); + /* in case of proc_files, there is no p_dev */ + struct btusb *p_dev = BTUSB_PDE_PARENT_DATA(inode); + + BTUSB_DBG("p_dev=%p\n", p_dev); + /* check if it is a single sequence */ + if (!p_file->seq_ops.start) + { + return single_open(file, p_file->seq_ops.show, p_dev); + } + else + { + rv = seq_open(file, &p_file->seq_ops); + if (!rv) + { + p_dev->scosniff_active = true; + ((struct seq_file *)file->private_data)->private = p_dev; + } + return rv; + } + return -1; +} + +/******************************************************************************* + ** + ** Function btusb_proc_release + ** + ** Description Close handler of the proc interface + ** + *******************************************************************************/ +int btusb_proc_release(struct inode *inode, struct file *file) +{ + struct btusb_proc_file *p_file = BTUSB_PDE_DATA(inode); + struct btusb *p_dev = BTUSB_PDE_PARENT_DATA(inode); + struct btusb_scosniff *p, *n; + + BTUSB_DBG("p_dev=%p\n", p_dev); + if (!p_file->seq_ops.start) + { + return single_release(inode, file); + } + else + { + p_dev->scosniff_active = false; + list_for_each_entry_safe(p, n, &p_dev->scosniff_list, lh) + { + list_del(&p->lh); + kfree(p); + } + return seq_release(inode, file); + } + return -1; +} + +/******************************************************************************* + ** + ** Function btusb_proc_write + ** + ** Description Write handler of the proc interface + ** + *******************************************************************************/ +ssize_t btusb_proc_write(struct file *file, const char *buf, + size_t count, loff_t *pos) +{ + struct seq_file *s = file->private_data; + struct btusb *p_dev = s->private; + unsigned char cmd; + + /* copy the first byte from the data written */ + if (copy_from_user(&cmd, buf, 1)) + { + return -EFAULT; + } + + /* unconditional print on purpose */ + BTUSB_INFO("'%c'\n", cmd); + + switch (cmd) + { + case '0': + /* reset the stats */ + memset(&p_dev->stats, 0, sizeof(p_dev->stats)); + break; + case '1': + btusb_add_voice_channel(p_dev, 6, BTUSB_VOICE_BURST_SIZE); + break; + + case '2': + btusb_remove_voice_channel(p_dev, 6); + break; + + case '3': + if (p_dev->scosniff_active) + { + struct btusb_scosniff *bs; + bs = kmalloc(sizeof(*bs), GFP_ATOMIC); + if (bs) + { + BTUSB_INFO("SCOSNIFF: adding %p\n", bs); + bs->n = 0; + list_add_tail(&bs->lh, &p_dev->scosniff_list); + complete(&p_dev->scosniff_completion); + } + } + break; + +#ifdef BTUSB_LITE + case '4': + BTUSB_INFO("Mute PCM0\n"); + pcm0_mute = 1; + break; + + case '5': + BTUSB_INFO("Unmute PCM0\n"); + pcm0_mute = 0; + break; +#endif + + default: + break; + } + + return count; +} + +static struct btusb_proc_file btusb_proc_files[] = +{ + { + "version", S_IRUGO, + { + .show=btusb_version_show, + .start=NULL + } + }, + {NULL, 0, {.start=NULL}} +}; + +static struct btusb_proc_file btusb_proc_dev_files[] = +{ + { + "info", S_IRUGO, + { + .show=btusb_info_show, + .start=NULL + } + }, + { + "stats", S_IRUGO | S_IWUGO, + { + .show=btusb_stats_show, + .start=NULL + } + }, + { + "status", S_IRUGO, + { + .show=btusb_status_show, + .start=NULL + } + }, + { + "scosniff", S_IRUGO, + { + .show=btusb_scosniff_show, + .start=btusb_scosniff_start, + .stop=btusb_scosniff_stop, + .next=btusb_scosniff_next + } + }, + {NULL, 0, {.start=NULL}} +}; + +static const struct file_operations btusb_proc_fops = +{ + .open = btusb_proc_open, + .read = seq_read, + .write = btusb_proc_write, + .llseek = seq_lseek, + .release = btusb_proc_release, +}; + + +/******************************************************************************* + ** + ** Function btusb_proc_create_files + ** + ** Description Proc interface file creation + ** + *******************************************************************************/ +static void btusb_proc_create_files(struct btusb_proc_file proc_files[], + struct proc_dir_entry *p_parent) +{ + struct proc_dir_entry *p_pde; + struct btusb_proc_file *p_file; + + if (p_parent) + { + for (p_file = proc_files; p_file->name; p_file++) + { + p_pde = proc_create_data(p_file->name, p_file->mode, + p_parent, &btusb_proc_fops, p_file); + if (!p_pde) + { + BTUSB_ERR("failed creating %s\n", p_file->name); + } + } + } +} + +/******************************************************************************* + ** + ** Function btusb_proc_remove_files + ** + ** Description Proc interface file removal + ** + *******************************************************************************/ +static void btusb_proc_remove_files(struct btusb_proc_file proc_files[], + struct proc_dir_entry *p_parent) +{ + struct btusb_proc_file *p_file; + if (p_parent) + { + for (p_file = proc_files; p_file->name; p_file++) + { + remove_proc_entry(p_file->name, p_parent); + } + } +} + +/******************************************************************************* + ** + ** Function btusb_proc_init + ** + ** Description Proc interface init + ** + *******************************************************************************/ +void btusb_proc_init(void) +{ + btusb_proc_dir = proc_mkdir("driver/btusb", NULL); + btusb_proc_create_files(btusb_proc_files, btusb_proc_dir); +} + +/******************************************************************************* + ** + ** Function btusb_proc_exit + ** + ** Description Proc interface exit + ** + *******************************************************************************/ +void btusb_proc_exit(void) +{ + btusb_proc_remove_files(btusb_proc_files, btusb_proc_dir); + remove_proc_entry("driver/btusb", NULL); + btusb_proc_dir = NULL; +} + +/******************************************************************************* + ** + ** Function btusb_proc_add + ** + ** Description Add a device to the proc interface + ** + *******************************************************************************/ +void btusb_proc_add(struct btusb *p_dev, const char *name) +{ + if (btusb_proc_dir) + { +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,0,0) + p_dev->p_pde = create_proc_entry(name, S_IFDIR | S_IRUGO | S_IWUGO | S_IXUGO, btusb_proc_dir); + if (p_dev->p_pde) + p_dev->p_pde->data = p_dev; +#elif LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0) + p_dev->p_pde = proc_mkdir_mode(name, S_IRUGO | S_IWUGO | S_IXUGO, btusb_proc_dir); + if (p_dev->p_pde) + p_dev->p_pde->data = p_dev; +#else + p_dev->p_pde = proc_mkdir_data(name, S_IRUGO | S_IWUGO | S_IXUGO, btusb_proc_dir, p_dev); +#endif + if (!p_dev->p_pde) + { + BTUSB_ERR("Couldn't create proc entry %s\n", name); + return; + } + + btusb_proc_create_files(btusb_proc_dev_files, p_dev->p_pde); + } +} + +/******************************************************************************* + ** + ** Function btusb_proc_remove + ** + ** Description Remove a device from the proc interface + ** + *******************************************************************************/ +void btusb_proc_remove(struct btusb *p_dev, const char *name) +{ + btusb_proc_remove_files(btusb_proc_dev_files, p_dev->p_pde); + remove_proc_entry(name, btusb_proc_dir); +} + diff --git a/src/btusb_version.c b/src/btusb_version.c new file mode 100755 index 0000000..01f5c8f --- a/dev/null +++ b/src/btusb_version.c @@ -0,0 +1,30 @@ +/* + * + * btusb_version.c + * + * + * + * Copyright (C) 2014 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * Module that implements the device routines. + * + */ + +#include "btusb.h" + +const char bsa_version_string[] = "BSA0106_00.40.00"; diff --git a/src/gki/gki_buffer.c b/src/gki/gki_buffer.c new file mode 100755 index 0000000..aa09483 --- a/dev/null +++ b/src/gki/gki_buffer.c @@ -0,0 +1,1291 @@ +/* + * + * gki_buffer.c + * + * + * + * Copyright (C) 2011-2012 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ +#include "gki_int.h" +#include "btusb.h" + +#if (GKI_NUM_TOTAL_BUF_POOLS > 16) +#error Number of pools out of range (16 Max)! +#endif + +static void gki_add_to_pool_list(UINT8 pool_id); +static void gki_remove_from_pool_list(UINT8 pool_id); + +static BOOL _b_in_gki_getbuf = FALSE; + +/******************************************************************************* +** +** Function gki_init_free_queue +** +** Description Internal function called at startup to initialize a free +** queue. It is called once for each free queue. +** +** Returns void +** +*******************************************************************************/ +static BOOL gki_init_free_queue(UINT8 id, UINT16 size, UINT16 total, void *p_mem) +{ + UINT16 i; + UINT16 act_size; + BUFFER_HDR_T *hdr; + BUFFER_HDR_T *hdr1 = NULL; + UINT32 *magic; + int seg_inx = gki_cb.pool_additions[id]; + FREE_QUEUE_T *p_freeq = &gki_cb.freeq[id]; + + if (seg_inx >= MAX_BUFFPOOL_SEGS) + { + GKI_exception(GKI_ERROR_SEGS_EXCEEDED, "Max segs exceeded"); + return(FALSE); + } + + act_size = (UINT16)(size + BUFFER_PADDING_SIZE); + + if (!p_mem) + p_mem = gki_reserve_os_memory(act_size * total); + + if (!p_mem) + { + GKI_exception(GKI_ERROR_NO_MEMORY_FOR_SEG, "No memory for segment"); + return(FALSE); + } + + /* Remember pool start and end addresses */ + p_freeq->seg_start[seg_inx] = (UINT8 *)p_mem; + p_freeq->seg_end[seg_inx] = (UINT8 *)p_mem + (act_size * total); + + if (seg_inx == 0) + { + gki_cb.freeq[id].total = total; + gki_cb.freeq[id].cur_cnt = 0; + gki_cb.freeq[id].max_cnt = 0; + } + else + gki_cb.freeq[id].total += total; + + /* Initialize index table */ + hdr = (BUFFER_HDR_T *)p_mem; + p_freeq->p_first = hdr; + +#ifdef TRACE_GKI_BUFFERS + gki_cb.freeq[id].p_first_all = hdr; +#endif + + for (i = 0; i < total; i++) + { + hdr->task_id = GKI_INVALID_TASK; + hdr->q_id = id; + hdr->status = BUF_STATUS_FREE; + magic = (UINT32 *)((UINT8 *)hdr + BUFFER_HDR_SIZE + size); + *magic = MAGIC_NO; + hdr1 = hdr; + hdr = (BUFFER_HDR_T *)((UINT8 *)hdr + act_size); + hdr1->p_next = hdr; +#ifdef TRACE_GKI_BUFFERS + hdr1->p_next_all = hdr; + hdr1->pFile = NULL; + hdr1->linenum = 0; + hdr1->times_alloc = 0; +#endif + } + + hdr1->p_next = NULL; + p_freeq->p_last = hdr1; + + gki_cb.pool_additions[id]++; + + return(TRUE); +} + + +/******************************************************************************* +** +** Function gki_buffer_init +** +** Description Called once internally by GKI at startup to initialize all +** buffers and free buffer pools. +** +** Returns void +** +*******************************************************************************/ +void gki_buffer_init(void) +{ + UINT8 tt; + UINT16 access_mask = GKI_DEF_BUFPOOL_PERM_MASK; + +#ifdef TRACE_GKI_BUFFERS + char aString[200]; + sprintf(aString,"%s: %d: WARNING! Running with GKI buffer tracing enabled!!",__FILE__,__LINE__); + GKI_exception(GKI_ERROR_BUFFER_TRACE_ON,aString); +#endif + + for (tt = 0; tt < GKI_NUM_TOTAL_BUF_POOLS; tt++) + { + memset(&gki_cb.freeq[tt], 0, sizeof(FREE_QUEUE_T)); + + gki_cb.pool_buf_size[tt] = 0; + gki_cb.pool_max_count[tt] = 0; + gki_cb.pool_additions[tt] = 0; + } + + /* Use default from target.h */ + gki_cb.pool_access_mask = GKI_DEF_BUFPOOL_PERM_MASK; + +#if (GKI_USE_DYNAMIC_BUFFERS == TRUE) + +#if (GKI_NUM_FIXED_BUF_POOLS > 0) + GKI_create_pool(GKI_BUF0_SIZE, GKI_BUF0_MAX, (UINT8) (access_mask & 1), NULL); + access_mask = access_mask >> 1; +#endif + +#if (GKI_NUM_FIXED_BUF_POOLS > 1) + GKI_create_pool(GKI_BUF1_SIZE, GKI_BUF1_MAX, (UINT8) (access_mask & 1), NULL); + access_mask = access_mask >> 1; +#endif + +#if (GKI_NUM_FIXED_BUF_POOLS > 2) + GKI_create_pool(GKI_BUF2_SIZE, GKI_BUF2_MAX, (UINT8) (access_mask & 1), NULL); + access_mask = access_mask >> 1; +#endif + +#if (GKI_NUM_FIXED_BUF_POOLS > 3) + GKI_create_pool(GKI_BUF3_SIZE, GKI_BUF3_MAX, (UINT8) (access_mask & 1), NULL); +#endif + +#else + +/* Static buffers */ + +#if (GKI_NUM_FIXED_BUF_POOLS > 0) + GKI_create_pool(GKI_BUF0_SIZE, GKI_BUF0_MAX, (UINT8) (access_mask & 1), gki_cb.bufpool0); + access_mask = access_mask >> 1; +#endif + +#if (GKI_NUM_FIXED_BUF_POOLS > 1) + GKI_create_pool(GKI_BUF1_SIZE, GKI_BUF1_MAX, (UINT8) (access_mask & 1), gki_cb.bufpool1); + access_mask = access_mask >> 1; +#endif + +#if (GKI_NUM_FIXED_BUF_POOLS > 2) + GKI_create_pool(GKI_BUF2_SIZE, GKI_BUF2_MAX, (UINT8) (access_mask & 1), gki_cb.bufpool2); + access_mask = access_mask >> 1; +#endif + +#if (GKI_NUM_FIXED_BUF_POOLS > 3) + GKI_create_pool(GKI_BUF3_SIZE, GKI_BUF3_MAX, (UINT8) (access_mask & 1), gki_cb.bufpool3); +#endif + +#endif +} + + +/******************************************************************************* +** +** Function GKI_set_pool_permission +** +** Description This function is called to set or change the permissions for +** the specified pool ID. +** +** Parameters pool_id - (input) pool ID to be set or changed +** permission - (input) GKI_PUBLIC_POOL or GKI_RESTRICTED_POOL +** +** Returns GKI_SUCCESS if successful +** GKI_INVALID_POOL if unsuccessful +** +*******************************************************************************/ +UINT8 GKI_set_pool_permission(UINT8 pool_id, UINT8 permission) +{ + if (pool_id < GKI_NUM_TOTAL_BUF_POOLS) + { + if (permission == GKI_RESTRICTED_POOL) + gki_cb.pool_access_mask |= (1 << pool_id); + + else /* mark the pool as public */ + gki_cb.pool_access_mask &= ~(1 << pool_id); + + return(GKI_SUCCESS); + } + else + return(GKI_INVALID_POOL); +} + + +/******************************************************************************* +** +** Function gki_add_to_pool_list +** +** Description Adds pool to the pool list which is arranged in the +** order of size +** +** Returns void +** +*******************************************************************************/ +static void gki_add_to_pool_list(UINT8 pool_id) +{ + INT32 i, j; + + /* Find the position where the specified pool should be inserted into the list */ + for (i = 0; i < gki_cb.curr_total_no_of_pools; i++) + { + if (gki_cb.pool_buf_size[pool_id] <= gki_cb.pool_buf_size[gki_cb.pool_list[i]]) + break; + } + + /* Insert the new buffer pool ID into the list of pools */ + for (j = gki_cb.curr_total_no_of_pools; j > i; j--) + { + gki_cb.pool_list[j] = gki_cb.pool_list[j-1]; + } + + /* Prevent warning */ + if ( i < GKI_NUM_TOTAL_BUF_POOLS) + { + gki_cb.pool_list[i] = pool_id; + } +} + + +/******************************************************************************* +** +** Function gki_remove_from_pool_list +** +** Description Removes pool from the pool list. Called when a pool is deleted +** +** Returns void +** +*******************************************************************************/ +static void gki_remove_from_pool_list(UINT8 pool_id) +{ + INT8 i; + + for (i = 0; (i < gki_cb.curr_total_no_of_pools) && (i < GKI_NUM_TOTAL_BUF_POOLS); i++) + { + if (pool_id == gki_cb.pool_list[i]) + break; + } + +/* Prevent warning. + * Since GKI_NUM_TOTAL_BUF_POOLS value is 1, this code was not needed */ +/* + while (i < (GKI_NUM_TOTAL_BUF_POOLS - 1)) + { + gki_cb.pool_list[i] = gki_cb.pool_list[i+1]; + i++; + } +*/ + return; +} + + +/******************************************************************************* +** +** Function GKI_init_q +** +** Description Called by an application to initialize a buffer queue. +** +** Returns void +** +*******************************************************************************/ +void GKI_init_q(BUFFER_Q *p_q) +{ + p_q->p_first = p_q->p_last = NULL; + p_q->count = 0; + + return; +} + + +/******************************************************************************* +** +** Function GKI_getbuf +** +** Description Called by an application to get a free buffer which +** is of size greater or equal to the requested size. +** +** Note: This routine only takes buffers from public pools. +** It will not use any buffers from pools +** marked GKI_RESTRICTED_POOL. +** +** Parameters size - (input) number of bytes needed. +** +** Returns A pointer to the buffer, or NULL if none available +** +*******************************************************************************/ +#ifdef TRACE_GKI_BUFFERS +void *GKI_getbuf_trace(UINT16 size, char *pFile, int linenum) +#else +void *GKI_getbuf(UINT16 size) +#endif +{ + UINT8 i, pool_id; + void *pp; + + if (unlikely(size == 0)) + { + GKI_exception(GKI_ERROR_BUF_SIZE_ZERO, "getbuf: Size is zero"); + return(NULL); + } + + /* Find the first buffer pool that is public that can hold the desired size */ + for (i = 0; i < gki_cb.curr_total_no_of_pools; i++) + { + if (gki_cb.pool_buf_size[gki_cb.pool_list[i]] >= size) + break; + } + + if (i == gki_cb.curr_total_no_of_pools) + { + char buff[40]; + snprintf(buff, sizeof(buff), "getbuf: Size: %u is too big", size); + GKI_exception(GKI_ERROR_BUF_SIZE_TOOBIG, buff); + return(NULL); + } + + _b_in_gki_getbuf = TRUE; + + /* search the public buffer pools that are big enough to hold the size + * until a free buffer is found */ + for ( ; i < gki_cb.curr_total_no_of_pools; i++) + { + pool_id = gki_cb.pool_list[i]; + + /* Only look at PUBLIC buffer pools (bypass RESTRICTED pools) */ + if (((UINT16)1 << pool_id) & gki_cb.pool_access_mask) + continue; + + if ((pp = GKI_getpoolbuf(pool_id)) != NULL) + { + _b_in_gki_getbuf = FALSE; + return(pp); + } + } + + _b_in_gki_getbuf = FALSE; + + return(NULL); +} + + +/******************************************************************************* +** +** Function GKI_getpoolbuf +** +** Description Called by an application to get a free buffer from +** a specific buffer pool. +** +** Note: If there are no more buffers available from the pool, +** the public buffers are searched for an available buffer. +** +** Parameters pool_id - (input) pool ID to get a buffer out of. +** +** Returns A pointer to the buffer, or NULL if none available +** +*******************************************************************************/ +#ifdef TRACE_GKI_BUFFERS +void *GKI_getpoolbuf_trace(UINT8 pool_id, char *pFile, int linenum) +#else +void *GKI_getpoolbuf(UINT8 pool_id) +#endif +{ + FREE_QUEUE_T *Q; + BUFFER_HDR_T *p_hdr; + + if (pool_id >= GKI_NUM_TOTAL_BUF_POOLS) + return(NULL); + + /* Make sure the buffers aren't disturbed til finished with allocation */ + GKI_disable(); + + Q = &gki_cb.freeq[pool_id]; + + /* If we have no buffers left, see if we can allocate another segment */ + if ((Q->cur_cnt >= Q->total) && (gki_cb.pool_additions[pool_id] < MAX_BUFFPOOL_SEGS)) + { + UINT16 count = (gki_cb.pool_max_count[pool_id] + MAX_BUFFPOOL_SEGS - 1) / MAX_BUFFPOOL_SEGS; + + gki_init_free_queue(pool_id, gki_cb.pool_buf_size[pool_id], count, NULL); + } + + if (Q->cur_cnt < Q->total) + { + p_hdr = Q->p_first; + Q->p_first = p_hdr->p_next; + + if (!Q->p_first) + Q->p_last = NULL; + + if (++Q->cur_cnt > Q->max_cnt) + Q->max_cnt = Q->cur_cnt; + + GKI_enable(); + + p_hdr->status = BUF_STATUS_UNLINKED; + p_hdr->p_next = NULL; + p_hdr->Type = 0; + +#ifdef TRACE_GKI_BUFFERS + p_hdr->pFile = pFile; + p_hdr->linenum = linenum; + p_hdr->times_alloc++; + /* The following is here to allow the test engineer to recognize and */ + /* set a breakpoint when a particular buffer is allocated for the nth time. */ + /* Simply change the address and allocation number in the following 'if' */ + /* statement to reflect the buffer and occurance desired. */ + if ((p_hdr == (BUFFER_HDR_T *)0x12345678) && (p_hdr->times_alloc == 123)) + p_hdr->times_alloc = 123; +#endif + + return((void *) ((UINT8 *)p_hdr + BUFFER_HDR_SIZE)); + } + + /* If here, no buffers in the specified pool */ + GKI_enable(); + + /* Try for free buffers in public pools. NOTE - no recursion allowed */ + if (!_b_in_gki_getbuf) + return(GKI_getbuf(gki_cb.pool_buf_size[pool_id])); + else + return(NULL); +} + + +/******************************************************************************* +** +** Function GKI_freebuf +** +** Description Called by an application to return a buffer to the free pool. +** +** Parameters p_buf - (input) address of the beginning of a buffer. +** +** Returns void +** +*******************************************************************************/ +void GKI_freebuf(void *p_buf) +{ + FREE_QUEUE_T *Q; + BUFFER_HDR_T *p_hdr; + void *pNextBuf; + +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + if (!p_buf || gki_chk_buf_damage(p_buf)) + { + GKI_exception(GKI_ERROR_BUF_CORRUPTED, "Free - Buf Corrupted"); + return; + } +#endif + + p_hdr = (BUFFER_HDR_T *) ((UINT8 *)p_buf - BUFFER_HDR_SIZE); + + if (p_hdr->status != BUF_STATUS_UNLINKED) + { + GKI_exception(GKI_ERROR_FREEBUF_BUF_LINKED, "Freeing Linked Buf"); + return; + } + + if (p_hdr->q_id >= GKI_NUM_TOTAL_BUF_POOLS) + { + GKI_exception(GKI_ERROR_FREEBUF_BAD_QID, "Bad Buf QId"); + return; + } + +#ifdef TRACE_GKI_BUFFERS + if ((p_hdr->pFile == NULL) || (p_hdr->linenum == 0)) + { + GKI_exception(GKI_ERROR_BUF_CORRUPTED, "Free - no file or line number"); + } +#endif + + GKI_disable(); + + /* + ** Releasing all buffers in the linked list + */ + while (p_buf) + { + p_hdr = (BUFFER_HDR_T *) ((UINT8 *)p_buf - BUFFER_HDR_SIZE); + + pNextBuf = NULL; + Q = &gki_cb.freeq[p_hdr->q_id]; + if (Q->p_last) + Q->p_last->p_next = p_hdr; + else + Q->p_first = p_hdr; + + Q->p_last = p_hdr; + p_hdr->p_next = NULL; + p_hdr->status = BUF_STATUS_FREE; + p_hdr->task_id = GKI_INVALID_TASK; +#ifdef TRACE_GKI_BUFFERS + p_hdr->pFile = NULL; + p_hdr->linenum = 0; +#endif + if (Q->cur_cnt > 0) + Q->cur_cnt--; + + p_buf = pNextBuf; + } + + GKI_enable(); + + return; +} + +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) +/******************************************************************************* +** +** Function GKI_get_buf_size +** +** Description Called by an application to get the size of a buffer. +** +** Parameters p_buf - (input) address of the beginning of a buffer. +** +** Returns the size of the buffer +** +*******************************************************************************/ +UINT16 GKI_get_buf_size(void *p_buf) +{ + BUFFER_HDR_T *p_hdr; + + p_hdr = (BUFFER_HDR_T *)((UINT8 *) p_buf - BUFFER_HDR_SIZE); + + if ((uintptr_t)p_hdr & 1) + { + return(0); + } + + if (p_hdr->q_id < GKI_NUM_TOTAL_BUF_POOLS) + { + return(gki_cb.pool_buf_size[p_hdr->q_id]); + } + else if (p_hdr->q_id == GKI_NUM_TOTAL_BUF_POOLS) + { + return offsetof(struct btusb_trans, magic) - offsetof(struct btusb_trans, bt_hdr); + } + else if (p_hdr->q_id == (GKI_NUM_TOTAL_BUF_POOLS + 1)) + { + return offsetof(struct btusb_voice_pkt, magic) - offsetof(struct btusb_voice_pkt, bt_hdr); + } + + + return(0); +} +#endif + +/******************************************************************************* +** +** Function GKI_get_pool_bufsize +** +** Description Called by an application to get the size of buffers in a pool +** +** Parameters Pool ID. +** +** Returns the size of buffers in the pool +** +*******************************************************************************/ +UINT16 GKI_get_pool_bufsize(UINT8 pool_id) +{ + if (pool_id < GKI_NUM_TOTAL_BUF_POOLS) + return(gki_cb.pool_buf_size[pool_id]); + + return(0); +} + +/******************************************************************************* +** +** Function GKI_poolfreecount +** +** Description Called by an application to get the number of free buffers +** in the specified buffer pool. +** +** Parameters pool_id - (input) pool ID to get the free count of. +** +** Returns the number of free buffers in the pool +** +*******************************************************************************/ +UINT16 GKI_poolfreecount(UINT8 pool_id) +{ + FREE_QUEUE_T *Q; + + if (pool_id >= GKI_NUM_TOTAL_BUF_POOLS) + return(0); + + Q = &gki_cb.freeq[pool_id]; + + return((UINT16)(Q->total - Q->cur_cnt)); +} + + +/******************************************************************************* +** +** Function GKI_poolutilization +** +** Description Called by an application to get the buffer utilization +** in the specified buffer pool. +** +** Parameters pool_id - (input) pool ID to get the free count of. +** +** Returns % of buffers used from 0 to 100 +** +*******************************************************************************/ +UINT16 GKI_poolutilization(UINT8 pool_id) +{ + FREE_QUEUE_T *Q; + + if (pool_id >= GKI_NUM_TOTAL_BUF_POOLS) + return(100); + + Q = &gki_cb.freeq[pool_id]; + + if (Q->total == 0) + return(100); + + return((Q->cur_cnt * 100) / Q->total); +} + + + +/******************************************************************************* +** +** Function gki_chk_buf_damage +** +** Description Called internally by OSS to check for buffer corruption. +** +** Returns TRUE if there is a problem, else FALSE +** +*******************************************************************************/ +BOOLEAN gki_chk_buf_damage(void *p_buf) +{ +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + + UINT32 *magic; + magic = (UINT32 *)((UINT8 *) p_buf + GKI_get_buf_size(p_buf)); + + if ((uintptr_t)magic & 1) + return(TRUE); + + if (*magic == MAGIC_NO) + return(FALSE); + + return(TRUE); + +#else + + return(FALSE); + +#endif +} + +/******************************************************************************* +** +** Function gki_chk_buf_owner +** +** Description Called internally by OSS to check if the current task +** is the owner of the buffer. +** +** Returns TRUE if not owner, else FALSE +** +*******************************************************************************/ +BOOLEAN gki_chk_buf_owner(void *p_buf) +{ + return(FALSE); +} + + +/******************************************************************************* +** +** Function GKI_change_buf_owner +** +** Description Called to change the task ownership of a buffer. +** +** Parameters: p_buf - (input) pointer to the buffer +** task_id - (input) task id to change ownership to +** +** Returns void +** +*******************************************************************************/ +void GKI_change_buf_owner(void *p_buf, UINT8 task_id) +{ + BUFFER_HDR_T *p_hdr = (BUFFER_HDR_T *) ((UINT8 *) p_buf - BUFFER_HDR_SIZE); + + p_hdr->task_id = task_id; + + return; +} + + + +/******************************************************************************* +** +** Function GKI_buffer_status +** +** Description check status of the buffer to see if it is linked +** +** Parameters: p_buf - (input) address of the buffer to enqueue +** +** Returns state +* BUF_STATUS_FREE 0 +* BUF_STATUS_UNLINKED 1 +* BUF_STATUS_QUEUED 2 +** +*******************************************************************************/ +UINT8 GKI_buffer_status(void *p_buf) +{ + BUFFER_HDR_T *p_hdr; + + p_hdr = (BUFFER_HDR_T *) ((UINT8 *) p_buf - BUFFER_HDR_SIZE); + + return(p_hdr->status); +} + +/******************************************************************************* +** +** Function GKI_enqueue +** +** Description Enqueue a buffer at the tail of the queue +** +** Parameters: p_q - (input) pointer to a queue. +** p_buf - (input) address of the buffer to enqueue +** +** Returns void +** +*******************************************************************************/ +void GKI_enqueue(BUFFER_Q *p_q, void *p_buf) +{ + BUFFER_HDR_T *p_hdr; + +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + if (gki_chk_buf_damage(p_buf)) + { + //printk("Enqueue - Buffer corrupted\n"); + GKI_exception(GKI_ERROR_BUF_CORRUPTED, "Enqueue - Buffer corrupted"); + return; + } +#endif + + p_hdr = (BUFFER_HDR_T *) ((UINT8 *) p_buf - BUFFER_HDR_SIZE); + + if (p_hdr->status != BUF_STATUS_UNLINKED) + { + printk("Enqueue - buf already linked\n"); + GKI_exception(GKI_ERROR_ENQUEUE_BUF_LINKED, "Enqueue - buf already linked : p_hdr->status: %d, buffer: 0x%p",p_hdr->status, p_buf); + return; + } + + GKI_disable(); + + /* Since the queue is exposed (C vs C++), keep the pointers in exposed format */ + /* check p_last != NULL before dereferencing it and if p_last == NULL then p_first == NULL */ + if (p_q->p_last) + { + BUFFER_HDR_T *p_last_hdr = (BUFFER_HDR_T *)((UINT8 *)p_q->p_last - BUFFER_HDR_SIZE); + p_last_hdr->p_next = p_hdr; + + /* sanity check, this should not happen */ + if (p_q->p_first == NULL) + { + printk("ERROR: Enqueue - first == NULL , last != NULL (0x%p)\n", p_q->p_last); + } + } + else + { + /* sanity check, this should not happen */ + if (p_q->p_first != NULL) + { + printk("ERROR: Enqueue - first != NULL (0x%p), last == NULL\n", p_q->p_first); + } + + p_q->p_first = p_buf; + } + + p_q->p_last = p_buf; + p_q->count++; + + p_hdr->p_next = NULL; + p_hdr->status = BUF_STATUS_QUEUED; + + GKI_enable(); + // printk("Enqueue: out from GKI_enqueue\n"); + + return; +} + + +/******************************************************************************* +** +** Function GKI_enqueue_head +** +** Description Enqueue a buffer at the head of the queue +** +** Parameters: p_q - (input) pointer to a queue. +** p_buf - (input) address of the buffer to enqueue +** +** Returns void +** +*******************************************************************************/ +void GKI_enqueue_head(BUFFER_Q *p_q, void *p_buf) +{ + BUFFER_HDR_T *p_hdr; + +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + if (gki_chk_buf_damage(p_buf)) + { + GKI_exception(GKI_ERROR_BUF_CORRUPTED, "Enqueue - Buffer corrupted"); + return; + } +#endif + + p_hdr = (BUFFER_HDR_T *) ((UINT8 *) p_buf - BUFFER_HDR_SIZE); + + if (p_hdr->status != BUF_STATUS_UNLINKED) + { + GKI_exception(GKI_ERROR_ENQUEUE_BUF_LINKED, "Enqeueue head - buf already linked"); + return; + } + + GKI_disable(); + + if (p_q->p_first) + { + p_hdr->p_next = (BUFFER_HDR_T *)((UINT8 *)p_q->p_first - BUFFER_HDR_SIZE); + p_q->p_first = p_buf; + } + else + { + p_q->p_first = p_buf; + p_q->p_last = p_buf; + p_hdr->p_next = NULL; + } + p_q->count++; + + p_hdr->status = BUF_STATUS_QUEUED; + + GKI_enable(); + + return; +} + + +/******************************************************************************* +** +** Function GKI_dequeue +** +** Description Dequeues a buffer from the head of a queue +** +** Parameters: p_q - (input) pointer to a queue. +** +** Returns NULL if queue is empty, else buffer +** +*******************************************************************************/ +void *GKI_dequeue(BUFFER_Q *p_q) +{ + BUFFER_HDR_T *p_hdr; + + GKI_disable(); + + if (!p_q || !p_q->count) + { + GKI_enable(); + return(NULL); + } + + p_hdr = (BUFFER_HDR_T *)((UINT8 *)p_q->p_first - BUFFER_HDR_SIZE); + + /* Keep buffers such that GKI header is invisible */ + if (p_hdr->p_next) + p_q->p_first = ((UINT8 *)p_hdr->p_next + BUFFER_HDR_SIZE); + else + { + p_q->p_first = NULL; + p_q->p_last = NULL; + } + + p_q->count--; + + p_hdr->p_next = NULL; + p_hdr->status = BUF_STATUS_UNLINKED; + + GKI_enable(); + + return((UINT8 *)p_hdr + BUFFER_HDR_SIZE); +} + + +/******************************************************************************* +** +** Function GKI_remove_from_queue +** +** Description Dequeue a buffer from the middle of the queue +** +** Parameters: p_q - (input) pointer to a queue. +** p_buf - (input) address of the buffer to enqueue +** +** Returns NULL if queue is empty, else buffer +** +*******************************************************************************/ +void *GKI_remove_from_queue(BUFFER_Q *p_q, void *p_buf) +{ + BUFFER_HDR_T *p_prev; + BUFFER_HDR_T *p_buf_hdr; + + GKI_disable(); + + if (p_buf == p_q->p_first) + { + GKI_enable(); + return(GKI_dequeue(p_q)); + } + + p_buf_hdr = (BUFFER_HDR_T *)((UINT8 *)p_buf - BUFFER_HDR_SIZE); + p_prev = (BUFFER_HDR_T *)((UINT8 *)p_q->p_first - BUFFER_HDR_SIZE); + + for ( ; p_prev; p_prev = p_prev->p_next) + { + /* If the previous points to this one, move the pointers around */ + if (p_prev->p_next == p_buf_hdr) + { + p_prev->p_next = p_buf_hdr->p_next; + + /* If we are removing the last guy in the queue, update p_last */ + if (p_buf == p_q->p_last) + p_q->p_last = p_prev + 1; + + /* One less in the queue */ + p_q->count--; + + /* The buffer is now unlinked */ + p_buf_hdr->p_next = NULL; + p_buf_hdr->status = BUF_STATUS_UNLINKED; + + GKI_enable(); + return(p_buf); + } + } + + GKI_enable(); + return(NULL); +} + + +/******************************************************************************* +** +** Function GKI_getfirst +** +** Description Return a pointer to the first buffer in a queue +** +** Parameters: p_q - (input) pointer to a queue. +** +** Returns NULL if queue is empty, else buffer address +** +*******************************************************************************/ +void *GKI_getfirst(BUFFER_Q *p_q) +{ + return(p_q->p_first); +} + + +/******************************************************************************* +** +** Function GKI_getnext +** +** Description Return a pointer to the next buffer in a queue +** +** Parameters: p_buf - (input) pointer to the buffer to find the next one from. +** +** Returns NULL if no more buffers in the queue, else next buffer address +** +*******************************************************************************/ +void *GKI_getnext(void *p_buf) +{ + BUFFER_HDR_T *p_hdr; + + p_hdr = (BUFFER_HDR_T *) ((UINT8 *) p_buf - BUFFER_HDR_SIZE); + + if (p_hdr->p_next) + return((UINT8 *)p_hdr->p_next + BUFFER_HDR_SIZE); + else + return(NULL); +} + + + +/******************************************************************************* +** +** Function GKI_queue_is_empty +** +** Description Check the status of a queue. +** +** Parameters: p_q - (input) pointer to a queue. +** +** Returns TRUE if queue is empty, else FALSE +** +*******************************************************************************/ +BOOLEAN GKI_queue_is_empty(BUFFER_Q *p_q) +{ + return((BOOLEAN)(p_q->count == 0)); +} + +/******************************************************************************* +** +** Function GKI_find_buf_start +** +** Description This function is called with an address inside a buffer, +** and returns the start address of the buffer. +** +** The buffer should be one allocated from one of GKI's pools. +** +** Parameters: p_user_area - (input) address of anywhere in a GKI buffer. +** +** Returns void * - Address of the beginning of the specified buffer if successful, +** otherwise NULL if unsuccessful +** +*******************************************************************************/ +void *GKI_find_buf_start(void *p_user_area) +{ + int pool_id, seg_inx; + UINT16 size; + UINT32 yy; + UINT8 *p_ua = (UINT8 *)p_user_area; + FREE_QUEUE_T *p_fq = gki_cb.freeq; + + for (pool_id = 0; pool_id < gki_cb.curr_total_no_of_pools; pool_id++, p_fq++) + { + for (seg_inx = 0; (seg_inx < MAX_BUFFPOOL_SEGS) && (p_fq->seg_start[seg_inx] != NULL); seg_inx++) + { + if ((p_ua > p_fq->seg_start[seg_inx]) && (p_ua < p_fq->seg_end[seg_inx])) + { + yy = (UINT32)(p_ua - p_fq->seg_start[seg_inx]); + + size = gki_cb.pool_buf_size[pool_id] + BUFFER_PADDING_SIZE; + + yy = (yy / size) * size; + + return((void *) (p_fq->seg_start[seg_inx] + yy + sizeof(BUFFER_HDR_T)) ); + } + } + } + + /* If here, invalid address - not in one of our buffers */ + GKI_exception(GKI_ERROR_BUF_SIZE_ZERO, "GKI_get_buf_start:: bad addr"); + + return(NULL); +} + + +/******************************************************************************* +** +** Function GKI_create_pool +** +** Description Called by applications to create a buffer pool. +** +** Parameters: size - (input) length (in bytes) of each buffer in the pool +** count - (input) number of buffers to allocate for the pool +** permission - (input) restricted or public access? +** (GKI_PUBLIC_POOL or GKI_RESTRICTED_POOL) +** p_mem_pool - (input) pointer to an OS memory pool, NULL if not provided +** +** Returns the buffer pool ID, which should be used in calls to +** GKI_getpoolbuf(). If a pool could not be created, this +** function returns 0xff. +** +*******************************************************************************/ +UINT8 GKI_create_pool(UINT16 size, UINT16 count, UINT8 permission, void *p_mem_pool) +{ + int xx; + + /* First make sure the size of each pool has a valid size with room for the header info */ + if (size > MAX_USER_BUF_SIZE) + return(GKI_INVALID_POOL); + + /* First, look for an unused pool */ + for (xx = 0; xx < GKI_NUM_TOTAL_BUF_POOLS; xx++) + { + if (!gki_cb.pool_buf_size[xx]) + break; + } + + if (xx == GKI_NUM_TOTAL_BUF_POOLS) + return(GKI_INVALID_POOL); + + GKI_disable(); + + /* Ensure an even number of longwords */ + size = ((size + 3) / 4) * 4; + + gki_cb.pool_buf_size[xx] = size; + gki_cb.pool_max_count[xx] = count; + gki_cb.pool_additions[xx] = 0; + + /* If memory was not passed in, create the pool in segments */ + if (!p_mem_pool) + count = (count + MAX_BUFFPOOL_SEGS - 1) / MAX_BUFFPOOL_SEGS; + + /* Initialize the new pool */ + if (gki_init_free_queue(xx, size, count, p_mem_pool)) + { + gki_add_to_pool_list(xx); + + (void) GKI_set_pool_permission(xx, permission); + gki_cb.curr_total_no_of_pools++; + } + else + { + /* Failed to create the pool ? */ + gki_cb.pool_buf_size[xx] = 0; + GKI_enable(); + return(GKI_INVALID_POOL); + } + + /* If memory was passed in, no pool additions allowed */ + if (p_mem_pool) + gki_cb.pool_additions[xx] = MAX_BUFFPOOL_SEGS; + + GKI_enable(); + return(xx); +} + + +/******************************************************************************* +** +** Function GKI_delete_pool +** +** Description Called by applications to delete a buffer pool. The function +** calls the operating specific function to free the actual memory. +** An exception is generated if an error is detected. +** +** Parameters: pool_id - (input) Id of the poll being deleted. +** +** Returns void +** +*******************************************************************************/ +void GKI_delete_pool(UINT8 pool_id) +{ + FREE_QUEUE_T *Q; + int xx; + + if ( (pool_id >= GKI_NUM_TOTAL_BUF_POOLS) || (!gki_cb.pool_buf_size[pool_id]) ) + return; + + GKI_disable(); + + Q = &gki_cb.freeq[pool_id]; + + Q->total = 0; + Q->cur_cnt = 0; + Q->max_cnt = 0; + Q->p_first = NULL; + Q->p_last = NULL; + + for (xx = 0; xx < MAX_BUFFPOOL_SEGS; xx++) + { + if (Q->seg_start[xx]) + gki_release_os_memory(Q->seg_start[xx]); + + Q->seg_start[xx] = NULL; + Q->seg_end[xx] = NULL; + } + + gki_cb.pool_buf_size[pool_id] = 0; + + gki_remove_from_pool_list(pool_id); + gki_cb.curr_total_no_of_pools--; + + GKI_enable(); + return; +} + +/******************************************************************************* +** +** Function GKI_chk_buf_pool_damage +** +** Description Called internally by OSS to check for buffer queue corruption. +** +** Returns TRUE if there is a problem, else FALSE +** +*******************************************************************************/ +BOOLEAN GKI_chk_buf_pool_damage(UINT8 pool_id) +{ +#if (GKI_ENABLE_BUF_CORRUPTION_CHECK == TRUE) + int i; + FREE_QUEUE_T *Q; + BUFFER_HDR_T *p_hdr; + UINT8 *p_buf; + + if (pool_id >= GKI_NUM_TOTAL_BUF_POOLS) + { + GKI_exception(GKI_ERROR_BUF_POOL_CORRUPT, "pool_id out of range"); + return(TRUE); + } + + /* Make sure the buffers aren't disturbed til finished with checking */ + GKI_disable(); + + Q = &gki_cb.freeq[pool_id]; + if (!Q->p_first) + { + if (Q->cur_cnt != Q->total) + { + GKI_enable(); + GKI_exception(GKI_ERROR_BUF_POOL_CORRUPT, "p_first is NULL in non-empty pool"); + return(TRUE); + } + return(FALSE); + } + p_hdr = Q->p_first; + i = 1; + while (p_hdr->p_next) + { + p_hdr = p_hdr->p_next; + p_buf = ((UINT8 *)p_hdr + BUFFER_HDR_SIZE); + if (gki_chk_buf_damage(p_buf)) + { + GKI_enable(); + GKI_exception(GKI_ERROR_BUF_CORRUPTED, "CHk Pool - Buf Corrupted"); + return(TRUE); + } + i++; + } + if (p_hdr != Q->p_last) + { + GKI_enable(); + GKI_exception(GKI_ERROR_BUF_POOL_CORRUPT, "last buffer in chain != p_last"); + return(TRUE); + } + if (i != (Q->total - Q->cur_cnt)) + { + GKI_enable(); + GKI_exception(GKI_ERROR_BUF_POOL_CORRUPT, "cur_cnt != number of buffers in pool"); + return(TRUE); + } + + GKI_enable(); + return(FALSE); + +#else + + return(FALSE); + +#endif +} + diff --git a/src/gki/gki_klinux.c b/src/gki/gki_klinux.c new file mode 100755 index 0000000..fb424ee --- a/dev/null +++ b/src/gki/gki_klinux.c @@ -0,0 +1,271 @@ +/* + * + * gki_klinux.c + * + * + * + * Copyright (C) 2011-2012 Broadcom Corporation. + * + * + * + * This software is licensed under the terms of the GNU General Public License, + * version 2, as published by the Free Software Foundation (the "GPL"), and may + * be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GPL for more details. + * + * + * A copy of the GPL is available at http://www.broadcom.com/licenses/GPLv2.php + * or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA + * + * + */ + +#include <linux/version.h> +#include <linux/slab.h> + +/* The location folder of the semaphore.h file changed at Kernel version 2.6.26 */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include <linux/semaphore.h> +#else +#include <asm/semaphore.h> +#endif + +#include "target.h" +#include "gki_int.h" +#include "bt_types.h" + + +volatile unsigned int _gki_lock_nesting = 0; +volatile pid_t LockThread = 0xffff; // thread currently holding the spinlock +DEFINE_SPINLOCK(Lock); +volatile unsigned long Kirql; // saved execution priority + +/* Define the structure that holds the GKI variables +*/ +#ifndef _BT_DYNAMIC_MEMORY +tGKI_CB gki_cb = {0}; +#else +tGKI_CB *gp_gki_cb = NULL; +#endif + +void LogMsg(int TraceMask, const char *format, ...) +{ + char temp1[400]; + char *temp = temp1; + va_list Next; + int len; + + // Go ahead and trace... + va_start(Next, format); + len = _vsnprintf(temp, 380, format, Next); + va_end(Next); + + + if ((len < 0) || (len > 380)) + { + len = strlen(temp); + } + + if (len > 380) + { + temp[380] = 0; + len = 380; + + } + + if (temp[len - 1] >= ' ') + { + temp[len] = '\n'; + temp[len + 1] = '\0'; + } + + + printk("%s", temp); + return; +} + +/******************************************************************************* +** +** Function GKI_init +** +** Description This function is called once at startup to initialize +** all the timer structures. +** +** Returns void +** +*******************************************************************************/ +void GKI_init(void) +{ + memset(&gki_cb, 0, sizeof(gki_cb)); + + gki_buffer_init(); + + spin_lock_init(&Lock); + gki_cb.IsRunning = TRUE; +} + + +/******************************************************************************* +** +** Function GKI_shutdown +** +** Description This function is called to shut down GKI +** +** Returns void +** +*******************************************************************************/ +void GKI_shutdown(void) +{ + int i; + + if (!gki_cb.IsRunning) + return; + + gki_cb.IsRunning = FALSE; + +#if (GKI_USE_DYNAMIC_BUFFERS == TRUE) + + for (i = 0; i < GKI_NUM_TOTAL_BUF_POOLS; i++) + { + if (gki_cb.pool_buf_size[i]) + GKI_delete_pool((UINT8)i); + } + +#endif + +} + +/******************************************************************************* +** +** Function GKI_enable +** +** Description This function enables interrupts. +** +** Returns void +** +*******************************************************************************/ +void GKI_enable(void) +{ + spin_unlock_irqrestore(&Lock, Kirql); +} + +/******************************************************************************* +** +** Function GKI_disable +** +** Description This function disables interrupts. +** +** Returns void +** +*******************************************************************************/ +void GKI_disable(void) +{ + volatile unsigned long tmp_irql; + + spin_lock_irqsave(&Lock, tmp_irql); + + // OK, we've acquired the spinlock. It means that + // the other guy has released it already and we can + // safely overwrite his saved irql... + Kirql = tmp_irql; +} + + +/******************************************************************************* +** +** Function GKI_exception +** +** Description This function throws an exception. +** This is normally only called for a non recoverable error. +** +** Parameters: code - (input) The code for the error +** msg - (input) The message that has to be logged +** +** Returns void +** +*******************************************************************************/ +void GKI_exception(UINT16 code, const char *msg, ...) +{ + char buff[MAX_EXCEPTION_MSGLEN]; + va_list Next; + int len; + + va_start(Next, msg); + len = vsnprintf(buff, MAX_EXCEPTION_MSGLEN - 1, msg, Next); + va_end(Next); + + LogMsg(-1, "GKI_exception: Entry Code %d Message %s\n", code, buff); + GKI_disable(); + + if (gki_cb.ExceptionCnt < MAX_EXCEPTION) + { + EXCEPTION_T *pExp; + + pExp = &gki_cb.Exception[gki_cb.ExceptionCnt++]; + pExp->type = code; + pExp->taskid = 0; + strncpy((INT8 *)pExp->msg, msg, MAX_EXCEPTION_MSGLEN - 1); + } + + GKI_enable(); + + LogMsg(-1, "GKI_Exception called with code: %d", code); + + return; +} + + +/******************************************************************************* +** +** Function gki_reserve_os_memory +** +** Description This function allocates memory +** +** Parameters: size - (input) The size of the memory that has to be +** allocated +** +** Returns the address of the memory allocated, or NULL if failed +** +** NOTE This function is NOT called by the Widcomm stack and +** profiles. It is only called from within GKI if dynamic +** buffer pools are used. +** +*******************************************************************************/ +void *gki_reserve_os_memory(UINT32 size) +{ + void *p; + p = kmalloc(size, GFP_ATOMIC); + if (p == NULL) + return NULL; + memset(p, 0x00, sizeof(*p)); + return p; +} + +/******************************************************************************* +** +** Function gki_release_os_memory +** +** Description This function frees memory +** +** Parameters: size - (input) The address of the memory that has to be +** freed +** +** Returns void +** +** NOTE This function is NOT called by the Widcomm stack and +** profiles. It is only called from within GKI if dynamic +** buffer pools are used. +** +*******************************************************************************/ +void gki_release_os_memory(void *p_mem) +{ + kfree(p_mem); + + return; +} + + |