Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

357
drivers/bluetooth/Kconfig Normal file
View File

@@ -0,0 +1,357 @@
menu "Bluetooth device drivers"
depends on BT
config BT_INTEL
tristate
select REGMAP
config BT_BCM
tristate
select FW_LOADER
config BT_RTL
tristate
select FW_LOADER
config BT_QCA
tristate
select FW_LOADER
config BT_HCIBTUSB
tristate "HCI USB driver"
depends on USB
select BT_INTEL
help
Bluetooth HCI USB driver.
This driver is required if you want to use Bluetooth devices with
USB interface.
Say Y here to compile support for Bluetooth USB devices into the
kernel or say M to compile it as module (btusb).
config BT_HCIBTUSB_BCM
bool "Broadcom protocol support"
depends on BT_HCIBTUSB
select BT_BCM
default y
help
The Broadcom protocol support enables firmware and patchram
download support for Broadcom Bluetooth controllers.
Say Y here to compile support for Broadcom protocol.
config BT_HCIBTUSB_RTL
bool "Realtek protocol support"
depends on BT_HCIBTUSB
select BT_RTL
default y
help
The Realtek protocol support enables firmware and configuration
download support for Realtek Bluetooth controllers.
Say Y here to compile support for Realtek protocol.
config BT_HCIBTSDIO
tristate "HCI SDIO driver"
depends on MMC
help
Bluetooth HCI SDIO driver.
This driver is required if you want to use Bluetooth device with
SDIO interface.
Say Y here to compile support for Bluetooth SDIO devices into the
kernel or say M to compile it as module (btsdio).
config BT_HCIUART
tristate "HCI UART driver"
depends on TTY
help
Bluetooth HCI UART driver.
This driver is required if you want to use Bluetooth devices with
serial port interface. You will also need this driver if you have
UART based Bluetooth PCMCIA and CF devices like Xircom Credit Card
adapter and BrainBoxes Bluetooth PC Card.
Say Y here to compile support for Bluetooth UART devices into the
kernel or say M to compile it as module (hci_uart).
config BT_HCIUART_H4
bool "UART (H4) protocol support"
depends on BT_HCIUART
help
UART (H4) is serial protocol for communication between Bluetooth
device and host. This protocol is required for most Bluetooth devices
with UART interface, including PCMCIA and CF cards.
Say Y here to compile support for HCI UART (H4) protocol.
config BT_HCIUART_BCSP
bool "BCSP protocol support"
depends on BT_HCIUART
select BITREVERSE
help
BCSP (BlueCore Serial Protocol) is serial protocol for communication
between Bluetooth device and host. This protocol is required for non
USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and
CF cards.
Say Y here to compile support for HCI BCSP protocol.
config BT_HCIUART_ATH3K
bool "Atheros AR300x serial support"
depends on BT_HCIUART
select BT_HCIUART_H4
help
HCIATH3K (HCI Atheros AR300x) is a serial protocol for
communication between host and Atheros AR300x Bluetooth devices.
This protocol enables AR300x chips to be enabled with
power management support.
Enable this if you have Atheros AR300x serial Bluetooth device.
Say Y here to compile support for HCI UART ATH3K protocol.
config BT_HCIUART_LL
bool "HCILL protocol support"
depends on BT_HCIUART
help
HCILL (HCI Low Level) is a serial protocol for communication
between Bluetooth device and host. This protocol is required for
serial Bluetooth devices that are based on Texas Instruments'
BRF chips.
Say Y here to compile support for HCILL protocol.
config BT_HCIUART_3WIRE
bool "Three-wire UART (H5) protocol support"
depends on BT_HCIUART
help
The HCI Three-wire UART Transport Layer makes it possible to
user the Bluetooth HCI over a serial port interface. The HCI
Three-wire UART Transport Layer assumes that the UART
communication may have bit errors, overrun errors or burst
errors and thereby making CTS/RTS lines unnecessary.
Say Y here to compile support for Three-wire UART protocol.
config BT_HCIUART_INTEL
bool "Intel protocol support"
depends on BT_HCIUART
select BT_HCIUART_H4
select BT_INTEL
help
The Intel protocol support enables Bluetooth HCI over serial
port interface for Intel Bluetooth controllers.
Say Y here to compile support for Intel protocol.
config BT_HCIUART_BCM
bool "Broadcom protocol support"
depends on BT_HCIUART
select BT_HCIUART_H4
select BT_BCM
help
The Broadcom protocol support enables Bluetooth HCI over serial
port interface for Broadcom Bluetooth controllers.
Say Y here to compile support for Broadcom protocol.
config BT_HCIUART_QCA
bool "Qualcomm Atheros protocol support"
depends on BT_HCIUART
select BT_HCIUART_H4
select BT_QCA
help
The Qualcomm Atheros protocol supports HCI In-Band Sleep feature
over serial port interface(H4) between controller and host.
This protocol is required for UART clock control for QCA Bluetooth
devices.
Say Y here to compile support for QCA protocol.
config BT_HCIUART_AG6XX
bool "Intel AG6XX protocol support"
depends on BT_HCIUART
select BT_HCIUART_H4
select BT_INTEL
help
The Intel/AG6XX protocol support enables Bluetooth HCI over serial
port interface for Intel ibt 2.1 Bluetooth controllers.
Say Y here to compile support for Intel AG6XX protocol.
config BT_HCIUART_MRVL
bool "Marvell protocol support"
depends on BT_HCIUART
select BT_HCIUART_H4
help
Marvell is serial protocol for communication between Bluetooth
device and host. This protocol is required for most Marvell Bluetooth
devices with UART interface.
Say Y here to compile support for HCI MRVL protocol.
config BT_HCIBCM203X
tristate "HCI BCM203x USB driver"
depends on USB
select FW_LOADER
help
Bluetooth HCI BCM203x USB driver.
This driver provides the firmware loading mechanism for the Broadcom
Blutonium based devices.
Say Y here to compile support for HCI BCM203x devices into the
kernel or say M to compile it as module (bcm203x).
config BT_HCIBPA10X
tristate "HCI BPA10x USB driver"
depends on USB && BT_HCIUART
select BT_HCIUART_H4
help
Bluetooth HCI BPA10x USB driver.
This driver provides support for the Digianswer BPA 100/105 Bluetooth
sniffer devices.
Say Y here to compile support for HCI BPA10x devices into the
kernel or say M to compile it as module (bpa10x).
config BT_HCIBFUSB
tristate "HCI BlueFRITZ! USB driver"
depends on USB
select FW_LOADER
help
Bluetooth HCI BlueFRITZ! USB driver.
This driver provides support for Bluetooth USB devices with AVM
interface:
AVM BlueFRITZ! USB
Say Y here to compile support for HCI BFUSB devices into the
kernel or say M to compile it as module (bfusb).
config BT_HCIDTL1
tristate "HCI DTL1 (PC Card) driver"
depends on PCMCIA
help
Bluetooth HCI DTL1 (PC Card) driver.
This driver provides support for Bluetooth PCMCIA devices with
Nokia DTL1 interface:
Nokia Bluetooth Card
Socket Bluetooth CF Card
Say Y here to compile support for HCI DTL1 devices into the
kernel or say M to compile it as module (dtl1_cs).
config BT_HCIBT3C
tristate "HCI BT3C (PC Card) driver"
depends on PCMCIA
select FW_LOADER
help
Bluetooth HCI BT3C (PC Card) driver.
This driver provides support for Bluetooth PCMCIA devices with
3Com BT3C interface:
3Com Bluetooth Card (3CRWB6096)
HP Bluetooth Card
Say Y here to compile support for HCI BT3C devices into the
kernel or say M to compile it as module (bt3c_cs).
config BT_HCIBLUECARD
tristate "HCI BlueCard (PC Card) driver"
depends on PCMCIA
help
Bluetooth HCI BlueCard (PC Card) driver.
This driver provides support for Bluetooth PCMCIA devices with
Anycom BlueCard interface:
Anycom Bluetooth PC Card
Anycom Bluetooth CF Card
Say Y here to compile support for HCI BlueCard devices into the
kernel or say M to compile it as module (bluecard_cs).
config BT_HCIBTUART
tristate "HCI UART (PC Card) device driver"
depends on PCMCIA
help
Bluetooth HCI UART (PC Card) driver.
This driver provides support for Bluetooth PCMCIA devices with
an UART interface:
Xircom CreditCard Bluetooth Adapter
Xircom RealPort2 Bluetooth Adapter
Sphinx PICO Card
H-Soft blue+Card
Cyber-blue Compact Flash Card
Say Y here to compile support for HCI UART devices into the
kernel or say M to compile it as module (btuart_cs).
config BT_HCIVHCI
tristate "HCI VHCI (Virtual HCI device) driver"
help
Bluetooth Virtual HCI device driver.
This driver is required if you want to use HCI Emulation software.
Say Y here to compile support for virtual HCI devices into the
kernel or say M to compile it as module (hci_vhci).
config BT_MRVL
tristate "Marvell Bluetooth driver support"
help
The core driver to support Marvell Bluetooth devices.
This driver is required if you want to support
Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897/8997.
Say Y here to compile Marvell Bluetooth driver
into the kernel or say M to compile it as module.
config BT_MRVL_SDIO
tristate "Marvell BT-over-SDIO driver"
depends on BT_MRVL && MMC
select FW_LOADER
select WANT_DEV_COREDUMP
help
The driver for Marvell Bluetooth chipsets with SDIO interface.
This driver is required if you want to use Marvell Bluetooth
devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897/SD8997
chipsets are supported.
Say Y here to compile support for Marvell BT-over-SDIO driver
into the kernel or say M to compile it as module.
config BT_ATH3K
tristate "Atheros firmware download driver"
depends on BT_HCIBTUSB
select FW_LOADER
help
Bluetooth firmware download driver.
This driver loads the firmware into the Atheros Bluetooth
chipset.
Say Y here to compile support for "Atheros firmware download driver"
into the kernel or say M to compile it as module (ath3k).
config BT_WILINK
tristate "Texas Instruments WiLink7 driver"
depends on TI_ST
help
This enables the Bluetooth driver for Texas Instrument's BT/FM/GPS
combo devices. This makes use of shared transport line discipline
core driver to communicate with the BT core of the combo chip.
Say Y here to compile support for Texas Instrument's WiLink7 driver
into the kernel or say M to compile it as module (btwilink).
config BT_QCOMSMD
tristate "Qualcomm SMD based HCI support"
depends on QCOM_SMD && QCOM_WCNSS_CTRL
select BT_QCA
help
Qualcomm SMD based HCI driver.
This driver is used to bridge HCI data onto the shared memory
channels to the WCNSS core.
Say Y here to compile support for HCI over Qualcomm SMD into the
kernel or say M to compile as a module.
endmenu

View File

@@ -0,0 +1,44 @@
#
# Makefile for the Linux Bluetooth HCI device drivers.
#
obj-$(CONFIG_BT_HCIVHCI) += hci_vhci.o
obj-$(CONFIG_BT_HCIUART) += hci_uart.o
obj-$(CONFIG_BT_HCIBCM203X) += bcm203x.o
obj-$(CONFIG_BT_HCIBPA10X) += bpa10x.o
obj-$(CONFIG_BT_HCIBFUSB) += bfusb.o
obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o
obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o
obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o
obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o
obj-$(CONFIG_BT_HCIBTUSB) += btusb.o
obj-$(CONFIG_BT_HCIBTSDIO) += btsdio.o
obj-$(CONFIG_BT_INTEL) += btintel.o
obj-$(CONFIG_BT_ATH3K) += ath3k.o
obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
obj-$(CONFIG_BT_WILINK) += btwilink.o
obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
obj-$(CONFIG_BT_BCM) += btbcm.o
obj-$(CONFIG_BT_RTL) += btrtl.o
obj-$(CONFIG_BT_QCA) += btqca.o
btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
hci_uart-y := hci_ldisc.o
hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o
hci_uart-$(CONFIG_BT_HCIUART_BCSP) += hci_bcsp.o
hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o
hci_uart-$(CONFIG_BT_HCIUART_ATH3K) += hci_ath.o
hci_uart-$(CONFIG_BT_HCIUART_3WIRE) += hci_h5.o
hci_uart-$(CONFIG_BT_HCIUART_INTEL) += hci_intel.o
hci_uart-$(CONFIG_BT_HCIUART_BCM) += hci_bcm.o
hci_uart-$(CONFIG_BT_HCIUART_QCA) += hci_qca.o
hci_uart-$(CONFIG_BT_HCIUART_AG6XX) += hci_ag6xx.o
hci_uart-$(CONFIG_BT_HCIUART_MRVL) += hci_mrvl.o
hci_uart-objs := $(hci_uart-y)
ccflags-y += -D__CHECK_ENDIAN__

582
drivers/bluetooth/ath3k.c Normal file
View File

@@ -0,0 +1,582 @@
/*
* Copyright (c) 2008-2009 Atheros Communications Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/usb.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#define VERSION "1.0"
#define ATH3K_FIRMWARE "ath3k-1.fw"
#define ATH3K_DNLOAD 0x01
#define ATH3K_GETSTATE 0x05
#define ATH3K_SET_NORMAL_MODE 0x07
#define ATH3K_GETVERSION 0x09
#define USB_REG_SWITCH_VID_PID 0x0a
#define ATH3K_MODE_MASK 0x3F
#define ATH3K_NORMAL_MODE 0x0E
#define ATH3K_PATCH_UPDATE 0x80
#define ATH3K_SYSCFG_UPDATE 0x40
#define ATH3K_XTAL_FREQ_26M 0x00
#define ATH3K_XTAL_FREQ_40M 0x01
#define ATH3K_XTAL_FREQ_19P2 0x02
#define ATH3K_NAME_LEN 0xFF
struct ath3k_version {
__le32 rom_version;
__le32 build_version;
__le32 ram_version;
__u8 ref_clock;
__u8 reserved[7];
} __packed;
static const struct usb_device_id ath3k_table[] = {
/* Atheros AR3011 */
{ USB_DEVICE(0x0CF3, 0x3000) },
/* Atheros AR3011 with sflash firmware*/
{ USB_DEVICE(0x0489, 0xE027) },
{ USB_DEVICE(0x0489, 0xE03D) },
{ USB_DEVICE(0x04F2, 0xAFF1) },
{ USB_DEVICE(0x0930, 0x0215) },
{ USB_DEVICE(0x0CF3, 0x3002) },
{ USB_DEVICE(0x0CF3, 0xE019) },
{ USB_DEVICE(0x13d3, 0x3304) },
/* Atheros AR9285 Malbec with sflash firmware */
{ USB_DEVICE(0x03F0, 0x311D) },
/* Atheros AR3012 with sflash firmware*/
{ USB_DEVICE(0x0489, 0xe04d) },
{ USB_DEVICE(0x0489, 0xe04e) },
{ USB_DEVICE(0x0489, 0xe057) },
{ USB_DEVICE(0x0489, 0xe056) },
{ USB_DEVICE(0x0489, 0xe05f) },
{ USB_DEVICE(0x0489, 0xe076) },
{ USB_DEVICE(0x0489, 0xe078) },
{ USB_DEVICE(0x0489, 0xe095) },
{ USB_DEVICE(0x04c5, 0x1330) },
{ USB_DEVICE(0x04CA, 0x3004) },
{ USB_DEVICE(0x04CA, 0x3005) },
{ USB_DEVICE(0x04CA, 0x3006) },
{ USB_DEVICE(0x04CA, 0x3007) },
{ USB_DEVICE(0x04CA, 0x3008) },
{ USB_DEVICE(0x04CA, 0x300b) },
{ USB_DEVICE(0x04CA, 0x300d) },
{ USB_DEVICE(0x04CA, 0x300f) },
{ USB_DEVICE(0x04CA, 0x3010) },
{ USB_DEVICE(0x04CA, 0x3014) },
{ USB_DEVICE(0x0930, 0x0219) },
{ USB_DEVICE(0x0930, 0x021c) },
{ USB_DEVICE(0x0930, 0x0220) },
{ USB_DEVICE(0x0930, 0x0227) },
{ USB_DEVICE(0x0b05, 0x17d0) },
{ USB_DEVICE(0x0CF3, 0x0036) },
{ USB_DEVICE(0x0CF3, 0x3004) },
{ USB_DEVICE(0x0CF3, 0x3008) },
{ USB_DEVICE(0x0CF3, 0x311D) },
{ USB_DEVICE(0x0CF3, 0x311E) },
{ USB_DEVICE(0x0CF3, 0x311F) },
{ USB_DEVICE(0x0cf3, 0x3121) },
{ USB_DEVICE(0x0CF3, 0x817a) },
{ USB_DEVICE(0x0CF3, 0x817b) },
{ USB_DEVICE(0x0cf3, 0xe003) },
{ USB_DEVICE(0x0CF3, 0xE004) },
{ USB_DEVICE(0x0CF3, 0xE005) },
{ USB_DEVICE(0x0CF3, 0xE006) },
{ USB_DEVICE(0x13d3, 0x3362) },
{ USB_DEVICE(0x13d3, 0x3375) },
{ USB_DEVICE(0x13d3, 0x3393) },
{ USB_DEVICE(0x13d3, 0x3395) },
{ USB_DEVICE(0x13d3, 0x3402) },
{ USB_DEVICE(0x13d3, 0x3408) },
{ USB_DEVICE(0x13d3, 0x3423) },
{ USB_DEVICE(0x13d3, 0x3432) },
{ USB_DEVICE(0x13d3, 0x3472) },
{ USB_DEVICE(0x13d3, 0x3474) },
{ USB_DEVICE(0x13d3, 0x3487) },
{ USB_DEVICE(0x13d3, 0x3490) },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE02C) },
/* Atheros AR5BBU22 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE036) },
{ USB_DEVICE(0x0489, 0xE03C) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, ath3k_table);
#define BTUSB_ATH3012 0x80
/* This table is to load patch and sysconfig files
* for AR3012 */
static const struct usb_device_id ath3k_blist_tbl[] = {
/* Atheros AR3012 with sflash firmware*/
{ USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe04d), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe095), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3006), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0227), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0x311E), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0x311F), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0x3121), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0CF3, 0x817a), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0CF3, 0x817b), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe006), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe003), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3395), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3472), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3487), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3490), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU22 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
{ } /* Terminating entry */
};
#define USB_REQ_DFU_DNLOAD 1
#define BULK_SIZE 4096
#define FW_HDR_SIZE 20
#define TIMEGAP_USEC_MIN 50
#define TIMEGAP_USEC_MAX 100
static int ath3k_load_firmware(struct usb_device *udev,
const struct firmware *firmware)
{
u8 *send_buf;
int len = 0;
int err, pipe, size, sent = 0;
int count = firmware->size;
BT_DBG("udev %p", udev);
pipe = usb_sndctrlpipe(udev, 0);
send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
if (!send_buf) {
BT_ERR("Can't allocate memory chunk for firmware");
return -ENOMEM;
}
memcpy(send_buf, firmware->data, 20);
err = usb_control_msg(udev, pipe, USB_REQ_DFU_DNLOAD, USB_TYPE_VENDOR,
0, 0, send_buf, 20, USB_CTRL_SET_TIMEOUT);
if (err < 0) {
BT_ERR("Can't change to loading configuration err");
goto error;
}
sent += 20;
count -= 20;
pipe = usb_sndbulkpipe(udev, 0x02);
while (count) {
/* workaround the compatibility issue with xHCI controller*/
usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX);
size = min_t(uint, count, BULK_SIZE);
memcpy(send_buf, firmware->data + sent, size);
err = usb_bulk_msg(udev, pipe, send_buf, size,
&len, 3000);
if (err || (len != size)) {
BT_ERR("Error in firmware loading err = %d,"
"len = %d, size = %d", err, len, size);
goto error;
}
sent += size;
count -= size;
}
error:
kfree(send_buf);
return err;
}
static int ath3k_get_state(struct usb_device *udev, unsigned char *state)
{
int ret, pipe = 0;
char *buf;
buf = kmalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
return -ENOMEM;
pipe = usb_rcvctrlpipe(udev, 0);
ret = usb_control_msg(udev, pipe, ATH3K_GETSTATE,
USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
buf, sizeof(*buf), USB_CTRL_SET_TIMEOUT);
*state = *buf;
kfree(buf);
return ret;
}
static int ath3k_get_version(struct usb_device *udev,
struct ath3k_version *version)
{
int ret, pipe = 0;
struct ath3k_version *buf;
const int size = sizeof(*buf);
buf = kmalloc(size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
pipe = usb_rcvctrlpipe(udev, 0);
ret = usb_control_msg(udev, pipe, ATH3K_GETVERSION,
USB_TYPE_VENDOR | USB_DIR_IN, 0, 0,
buf, size, USB_CTRL_SET_TIMEOUT);
memcpy(version, buf, size);
kfree(buf);
return ret;
}
static int ath3k_load_fwfile(struct usb_device *udev,
const struct firmware *firmware)
{
u8 *send_buf;
int len = 0;
int err, pipe, size, count, sent = 0;
int ret;
count = firmware->size;
send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
if (!send_buf) {
BT_ERR("Can't allocate memory chunk for firmware");
return -ENOMEM;
}
size = min_t(uint, count, FW_HDR_SIZE);
memcpy(send_buf, firmware->data, size);
pipe = usb_sndctrlpipe(udev, 0);
ret = usb_control_msg(udev, pipe, ATH3K_DNLOAD,
USB_TYPE_VENDOR, 0, 0, send_buf,
size, USB_CTRL_SET_TIMEOUT);
if (ret < 0) {
BT_ERR("Can't change to loading configuration err");
kfree(send_buf);
return ret;
}
sent += size;
count -= size;
pipe = usb_sndbulkpipe(udev, 0x02);
while (count) {
/* workaround the compatibility issue with xHCI controller*/
usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX);
size = min_t(uint, count, BULK_SIZE);
memcpy(send_buf, firmware->data + sent, size);
err = usb_bulk_msg(udev, pipe, send_buf, size,
&len, 3000);
if (err || (len != size)) {
BT_ERR("Error in firmware loading err = %d,"
"len = %d, size = %d", err, len, size);
kfree(send_buf);
return err;
}
sent += size;
count -= size;
}
kfree(send_buf);
return 0;
}
static int ath3k_switch_pid(struct usb_device *udev)
{
int pipe = 0;
pipe = usb_sndctrlpipe(udev, 0);
return usb_control_msg(udev, pipe, USB_REG_SWITCH_VID_PID,
USB_TYPE_VENDOR, 0, 0,
NULL, 0, USB_CTRL_SET_TIMEOUT);
}
static int ath3k_set_normal_mode(struct usb_device *udev)
{
unsigned char fw_state;
int pipe = 0, ret;
ret = ath3k_get_state(udev, &fw_state);
if (ret < 0) {
BT_ERR("Can't get state to change to normal mode err");
return ret;
}
if ((fw_state & ATH3K_MODE_MASK) == ATH3K_NORMAL_MODE) {
BT_DBG("firmware was already in normal mode");
return 0;
}
pipe = usb_sndctrlpipe(udev, 0);
return usb_control_msg(udev, pipe, ATH3K_SET_NORMAL_MODE,
USB_TYPE_VENDOR, 0, 0,
NULL, 0, USB_CTRL_SET_TIMEOUT);
}
static int ath3k_load_patch(struct usb_device *udev)
{
unsigned char fw_state;
char filename[ATH3K_NAME_LEN] = {0};
const struct firmware *firmware;
struct ath3k_version fw_version;
__u32 pt_rom_version, pt_build_version;
int ret;
ret = ath3k_get_state(udev, &fw_state);
if (ret < 0) {
BT_ERR("Can't get state to change to load ram patch err");
return ret;
}
if (fw_state & ATH3K_PATCH_UPDATE) {
BT_DBG("Patch was already downloaded");
return 0;
}
ret = ath3k_get_version(udev, &fw_version);
if (ret < 0) {
BT_ERR("Can't get version to change to load ram patch err");
return ret;
}
snprintf(filename, ATH3K_NAME_LEN, "ar3k/AthrBT_0x%08x.dfu",
le32_to_cpu(fw_version.rom_version));
ret = request_firmware(&firmware, filename, &udev->dev);
if (ret < 0) {
BT_ERR("Patch file not found %s", filename);
return ret;
}
pt_rom_version = get_unaligned_le32(firmware->data +
firmware->size - 8);
pt_build_version = get_unaligned_le32(firmware->data +
firmware->size - 4);
if (pt_rom_version != le32_to_cpu(fw_version.rom_version) ||
pt_build_version <= le32_to_cpu(fw_version.build_version)) {
BT_ERR("Patch file version did not match with firmware");
release_firmware(firmware);
return -EINVAL;
}
ret = ath3k_load_fwfile(udev, firmware);
release_firmware(firmware);
return ret;
}
static int ath3k_load_syscfg(struct usb_device *udev)
{
unsigned char fw_state;
char filename[ATH3K_NAME_LEN] = {0};
const struct firmware *firmware;
struct ath3k_version fw_version;
int clk_value, ret;
ret = ath3k_get_state(udev, &fw_state);
if (ret < 0) {
BT_ERR("Can't get state to change to load configuration err");
return -EBUSY;
}
ret = ath3k_get_version(udev, &fw_version);
if (ret < 0) {
BT_ERR("Can't get version to change to load ram patch err");
return ret;
}
switch (fw_version.ref_clock) {
case ATH3K_XTAL_FREQ_26M:
clk_value = 26;
break;
case ATH3K_XTAL_FREQ_40M:
clk_value = 40;
break;
case ATH3K_XTAL_FREQ_19P2:
clk_value = 19;
break;
default:
clk_value = 0;
break;
}
snprintf(filename, ATH3K_NAME_LEN, "ar3k/ramps_0x%08x_%d%s",
le32_to_cpu(fw_version.rom_version), clk_value, ".dfu");
ret = request_firmware(&firmware, filename, &udev->dev);
if (ret < 0) {
BT_ERR("Configuration file not found %s", filename);
return ret;
}
ret = ath3k_load_fwfile(udev, firmware);
release_firmware(firmware);
return ret;
}
static int ath3k_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
const struct firmware *firmware;
struct usb_device *udev = interface_to_usbdev(intf);
int ret;
BT_DBG("intf %p id %p", intf, id);
if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
return -ENODEV;
/* match device ID in ath3k blacklist table */
if (!id->driver_info) {
const struct usb_device_id *match;
match = usb_match_id(intf, ath3k_blist_tbl);
if (match)
id = match;
}
/* load patch and sysconfig files for AR3012 */
if (id->driver_info & BTUSB_ATH3012) {
/* New firmware with patch and sysconfig files already loaded */
if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x0001)
return -ENODEV;
ret = ath3k_load_patch(udev);
if (ret < 0) {
BT_ERR("Loading patch file failed");
return ret;
}
ret = ath3k_load_syscfg(udev);
if (ret < 0) {
BT_ERR("Loading sysconfig file failed");
return ret;
}
ret = ath3k_set_normal_mode(udev);
if (ret < 0) {
BT_ERR("Set normal mode failed");
return ret;
}
ath3k_switch_pid(udev);
return 0;
}
ret = request_firmware(&firmware, ATH3K_FIRMWARE, &udev->dev);
if (ret < 0) {
if (ret == -ENOENT)
BT_ERR("Firmware file \"%s\" not found",
ATH3K_FIRMWARE);
else
BT_ERR("Firmware file \"%s\" request failed (err=%d)",
ATH3K_FIRMWARE, ret);
return ret;
}
ret = ath3k_load_firmware(udev, firmware);
release_firmware(firmware);
return ret;
}
static void ath3k_disconnect(struct usb_interface *intf)
{
BT_DBG("ath3k_disconnect intf %p", intf);
}
static struct usb_driver ath3k_driver = {
.name = "ath3k",
.probe = ath3k_probe,
.disconnect = ath3k_disconnect,
.id_table = ath3k_table,
.disable_hub_initiated_lpm = 1,
};
module_usb_driver(ath3k_driver);
MODULE_AUTHOR("Atheros Communications");
MODULE_DESCRIPTION("Atheros AR30xx firmware driver");
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(ATH3K_FIRMWARE);

282
drivers/bluetooth/bcm203x.c Normal file
View File

@@ -0,0 +1,282 @@
/*
*
* Broadcom Blutonium firmware driver
*
* Copyright (C) 2003 Maxim Krasnyansky <maxk@qualcomm.com>
* Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/usb.h>
#include <net/bluetooth/bluetooth.h>
#define VERSION "1.2"
static const struct usb_device_id bcm203x_table[] = {
/* Broadcom Blutonium (BCM2033) */
{ USB_DEVICE(0x0a5c, 0x2033) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, bcm203x_table);
#define BCM203X_ERROR 0
#define BCM203X_RESET 1
#define BCM203X_LOAD_MINIDRV 2
#define BCM203X_SELECT_MEMORY 3
#define BCM203X_CHECK_MEMORY 4
#define BCM203X_LOAD_FIRMWARE 5
#define BCM203X_CHECK_FIRMWARE 6
#define BCM203X_IN_EP 0x81
#define BCM203X_OUT_EP 0x02
struct bcm203x_data {
struct usb_device *udev;
unsigned long state;
struct work_struct work;
atomic_t shutdown;
struct urb *urb;
unsigned char *buffer;
unsigned char *fw_data;
unsigned int fw_size;
unsigned int fw_sent;
};
static void bcm203x_complete(struct urb *urb)
{
struct bcm203x_data *data = urb->context;
struct usb_device *udev = urb->dev;
int len;
BT_DBG("udev %p urb %p", udev, urb);
if (urb->status) {
BT_ERR("URB failed with status %d", urb->status);
data->state = BCM203X_ERROR;
return;
}
switch (data->state) {
case BCM203X_LOAD_MINIDRV:
memcpy(data->buffer, "#", 1);
usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
data->buffer, 1, bcm203x_complete, data);
data->state = BCM203X_SELECT_MEMORY;
/* use workqueue to have a small delay */
schedule_work(&data->work);
break;
case BCM203X_SELECT_MEMORY:
usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
data->buffer, 32, bcm203x_complete, data, 1);
data->state = BCM203X_CHECK_MEMORY;
if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
BT_ERR("Can't submit URB");
break;
case BCM203X_CHECK_MEMORY:
if (data->buffer[0] != '#') {
BT_ERR("Memory select failed");
data->state = BCM203X_ERROR;
break;
}
data->state = BCM203X_LOAD_FIRMWARE;
case BCM203X_LOAD_FIRMWARE:
if (data->fw_sent == data->fw_size) {
usb_fill_int_urb(urb, udev, usb_rcvintpipe(udev, BCM203X_IN_EP),
data->buffer, 32, bcm203x_complete, data, 1);
data->state = BCM203X_CHECK_FIRMWARE;
} else {
len = min_t(uint, data->fw_size - data->fw_sent, 4096);
usb_fill_bulk_urb(urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
data->fw_data + data->fw_sent, len, bcm203x_complete, data);
data->fw_sent += len;
}
if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0)
BT_ERR("Can't submit URB");
break;
case BCM203X_CHECK_FIRMWARE:
if (data->buffer[0] != '.') {
BT_ERR("Firmware loading failed");
data->state = BCM203X_ERROR;
break;
}
data->state = BCM203X_RESET;
break;
}
}
static void bcm203x_work(struct work_struct *work)
{
struct bcm203x_data *data =
container_of(work, struct bcm203x_data, work);
if (atomic_read(&data->shutdown))
return;
if (usb_submit_urb(data->urb, GFP_KERNEL) < 0)
BT_ERR("Can't submit URB");
}
static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
const struct firmware *firmware;
struct usb_device *udev = interface_to_usbdev(intf);
struct bcm203x_data *data;
int size;
BT_DBG("intf %p id %p", intf, id);
if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
return -ENODEV;
data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->udev = udev;
data->state = BCM203X_LOAD_MINIDRV;
data->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!data->urb)
return -ENOMEM;
if (request_firmware(&firmware, "BCM2033-MD.hex", &udev->dev) < 0) {
BT_ERR("Mini driver request failed");
usb_free_urb(data->urb);
return -EIO;
}
BT_DBG("minidrv data %p size %zu", firmware->data, firmware->size);
size = max_t(uint, firmware->size, 4096);
data->buffer = kmalloc(size, GFP_KERNEL);
if (!data->buffer) {
BT_ERR("Can't allocate memory for mini driver");
release_firmware(firmware);
usb_free_urb(data->urb);
return -ENOMEM;
}
memcpy(data->buffer, firmware->data, firmware->size);
usb_fill_bulk_urb(data->urb, udev, usb_sndbulkpipe(udev, BCM203X_OUT_EP),
data->buffer, firmware->size, bcm203x_complete, data);
release_firmware(firmware);
if (request_firmware(&firmware, "BCM2033-FW.bin", &udev->dev) < 0) {
BT_ERR("Firmware request failed");
usb_free_urb(data->urb);
kfree(data->buffer);
return -EIO;
}
BT_DBG("firmware data %p size %zu", firmware->data, firmware->size);
data->fw_data = kmemdup(firmware->data, firmware->size, GFP_KERNEL);
if (!data->fw_data) {
BT_ERR("Can't allocate memory for firmware image");
release_firmware(firmware);
usb_free_urb(data->urb);
kfree(data->buffer);
return -ENOMEM;
}
data->fw_size = firmware->size;
data->fw_sent = 0;
release_firmware(firmware);
INIT_WORK(&data->work, bcm203x_work);
usb_set_intfdata(intf, data);
/* use workqueue to have a small delay */
schedule_work(&data->work);
return 0;
}
static void bcm203x_disconnect(struct usb_interface *intf)
{
struct bcm203x_data *data = usb_get_intfdata(intf);
BT_DBG("intf %p", intf);
atomic_inc(&data->shutdown);
cancel_work_sync(&data->work);
usb_kill_urb(data->urb);
usb_set_intfdata(intf, NULL);
usb_free_urb(data->urb);
kfree(data->fw_data);
kfree(data->buffer);
}
static struct usb_driver bcm203x_driver = {
.name = "bcm203x",
.probe = bcm203x_probe,
.disconnect = bcm203x_disconnect,
.id_table = bcm203x_table,
.disable_hub_initiated_lpm = 1,
};
module_usb_driver(bcm203x_driver);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("BCM2033-MD.hex");
MODULE_FIRMWARE("BCM2033-FW.bin");

738
drivers/bluetooth/bfusb.c Normal file
View File

@@ -0,0 +1,738 @@
/*
*
* AVM BlueFRITZ! USB driver
*
* Copyright (C) 2003-2006 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/usb.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#define VERSION "1.2"
static struct usb_driver bfusb_driver;
static const struct usb_device_id bfusb_table[] = {
/* AVM BlueFRITZ! USB */
{ USB_DEVICE(0x057c, 0x2200) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, bfusb_table);
#define BFUSB_MAX_BLOCK_SIZE 256
#define BFUSB_BLOCK_TIMEOUT 3000
#define BFUSB_TX_PROCESS 1
#define BFUSB_TX_WAKEUP 2
#define BFUSB_MAX_BULK_TX 2
#define BFUSB_MAX_BULK_RX 2
struct bfusb_data {
struct hci_dev *hdev;
unsigned long state;
struct usb_device *udev;
unsigned int bulk_in_ep;
unsigned int bulk_out_ep;
unsigned int bulk_pkt_size;
rwlock_t lock;
struct sk_buff_head transmit_q;
struct sk_buff *reassembly;
atomic_t pending_tx;
struct sk_buff_head pending_q;
struct sk_buff_head completed_q;
};
struct bfusb_data_scb {
struct urb *urb;
};
static void bfusb_tx_complete(struct urb *urb);
static void bfusb_rx_complete(struct urb *urb);
static struct urb *bfusb_get_completed(struct bfusb_data *data)
{
struct sk_buff *skb;
struct urb *urb = NULL;
BT_DBG("bfusb %p", data);
skb = skb_dequeue(&data->completed_q);
if (skb) {
urb = ((struct bfusb_data_scb *) skb->cb)->urb;
kfree_skb(skb);
}
return urb;
}
static void bfusb_unlink_urbs(struct bfusb_data *data)
{
struct sk_buff *skb;
struct urb *urb;
BT_DBG("bfusb %p", data);
while ((skb = skb_dequeue(&data->pending_q))) {
urb = ((struct bfusb_data_scb *) skb->cb)->urb;
usb_kill_urb(urb);
skb_queue_tail(&data->completed_q, skb);
}
while ((urb = bfusb_get_completed(data)))
usb_free_urb(urb);
}
static int bfusb_send_bulk(struct bfusb_data *data, struct sk_buff *skb)
{
struct bfusb_data_scb *scb = (void *) skb->cb;
struct urb *urb = bfusb_get_completed(data);
int err, pipe;
BT_DBG("bfusb %p skb %p len %d", data, skb, skb->len);
if (!urb) {
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb)
return -ENOMEM;
}
pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len,
bfusb_tx_complete, skb);
scb->urb = urb;
skb_queue_tail(&data->pending_q, skb);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
BT_ERR("%s bulk tx submit failed urb %p err %d",
data->hdev->name, urb, err);
skb_unlink(skb, &data->pending_q);
usb_free_urb(urb);
} else
atomic_inc(&data->pending_tx);
return err;
}
static void bfusb_tx_wakeup(struct bfusb_data *data)
{
struct sk_buff *skb;
BT_DBG("bfusb %p", data);
if (test_and_set_bit(BFUSB_TX_PROCESS, &data->state)) {
set_bit(BFUSB_TX_WAKEUP, &data->state);
return;
}
do {
clear_bit(BFUSB_TX_WAKEUP, &data->state);
while ((atomic_read(&data->pending_tx) < BFUSB_MAX_BULK_TX) &&
(skb = skb_dequeue(&data->transmit_q))) {
if (bfusb_send_bulk(data, skb) < 0) {
skb_queue_head(&data->transmit_q, skb);
break;
}
}
} while (test_bit(BFUSB_TX_WAKEUP, &data->state));
clear_bit(BFUSB_TX_PROCESS, &data->state);
}
static void bfusb_tx_complete(struct urb *urb)
{
struct sk_buff *skb = (struct sk_buff *) urb->context;
struct bfusb_data *data = (struct bfusb_data *) skb->dev;
BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len);
atomic_dec(&data->pending_tx);
if (!test_bit(HCI_RUNNING, &data->hdev->flags))
return;
if (!urb->status)
data->hdev->stat.byte_tx += skb->len;
else
data->hdev->stat.err_tx++;
read_lock(&data->lock);
skb_unlink(skb, &data->pending_q);
skb_queue_tail(&data->completed_q, skb);
bfusb_tx_wakeup(data);
read_unlock(&data->lock);
}
static int bfusb_rx_submit(struct bfusb_data *data, struct urb *urb)
{
struct bfusb_data_scb *scb;
struct sk_buff *skb;
int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
BT_DBG("bfusb %p urb %p", data, urb);
if (!urb) {
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb)
return -ENOMEM;
}
skb = bt_skb_alloc(size, GFP_ATOMIC);
if (!skb) {
usb_free_urb(urb);
return -ENOMEM;
}
skb->dev = (void *) data;
scb = (struct bfusb_data_scb *) skb->cb;
scb->urb = urb;
pipe = usb_rcvbulkpipe(data->udev, data->bulk_in_ep);
usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, size,
bfusb_rx_complete, skb);
skb_queue_tail(&data->pending_q, skb);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
BT_ERR("%s bulk rx submit failed urb %p err %d",
data->hdev->name, urb, err);
skb_unlink(skb, &data->pending_q);
kfree_skb(skb);
usb_free_urb(urb);
}
return err;
}
static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned char *buf, int len)
{
BT_DBG("bfusb %p hdr 0x%02x data %p len %d", data, hdr, buf, len);
if (hdr & 0x10) {
BT_ERR("%s error in block", data->hdev->name);
kfree_skb(data->reassembly);
data->reassembly = NULL;
return -EIO;
}
if (hdr & 0x04) {
struct sk_buff *skb;
unsigned char pkt_type;
int pkt_len = 0;
if (data->reassembly) {
BT_ERR("%s unexpected start block", data->hdev->name);
kfree_skb(data->reassembly);
data->reassembly = NULL;
}
if (len < 1) {
BT_ERR("%s no packet type found", data->hdev->name);
return -EPROTO;
}
pkt_type = *buf++; len--;
switch (pkt_type) {
case HCI_EVENT_PKT:
if (len >= HCI_EVENT_HDR_SIZE) {
struct hci_event_hdr *hdr = (struct hci_event_hdr *) buf;
pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
} else {
BT_ERR("%s event block is too short", data->hdev->name);
return -EILSEQ;
}
break;
case HCI_ACLDATA_PKT:
if (len >= HCI_ACL_HDR_SIZE) {
struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) buf;
pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
} else {
BT_ERR("%s data block is too short", data->hdev->name);
return -EILSEQ;
}
break;
case HCI_SCODATA_PKT:
if (len >= HCI_SCO_HDR_SIZE) {
struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) buf;
pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
} else {
BT_ERR("%s audio block is too short", data->hdev->name);
return -EILSEQ;
}
break;
}
skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
if (!skb) {
BT_ERR("%s no memory for the packet", data->hdev->name);
return -ENOMEM;
}
hci_skb_pkt_type(skb) = pkt_type;
data->reassembly = skb;
} else {
if (!data->reassembly) {
BT_ERR("%s unexpected continuation block", data->hdev->name);
return -EIO;
}
}
if (len > 0)
memcpy(skb_put(data->reassembly, len), buf, len);
if (hdr & 0x08) {
hci_recv_frame(data->hdev, data->reassembly);
data->reassembly = NULL;
}
return 0;
}
static void bfusb_rx_complete(struct urb *urb)
{
struct sk_buff *skb = (struct sk_buff *) urb->context;
struct bfusb_data *data = (struct bfusb_data *) skb->dev;
unsigned char *buf = urb->transfer_buffer;
int count = urb->actual_length;
int err, hdr, len;
BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len);
read_lock(&data->lock);
if (!test_bit(HCI_RUNNING, &data->hdev->flags))
goto unlock;
if (urb->status || !count)
goto resubmit;
data->hdev->stat.byte_rx += count;
skb_put(skb, count);
while (count) {
hdr = buf[0] | (buf[1] << 8);
if (hdr & 0x4000) {
len = 0;
count -= 2;
buf += 2;
} else {
len = (buf[2] == 0) ? 256 : buf[2];
count -= 3;
buf += 3;
}
if (count < len) {
BT_ERR("%s block extends over URB buffer ranges",
data->hdev->name);
}
if ((hdr & 0xe1) == 0xc1)
bfusb_recv_block(data, hdr, buf, len);
count -= len;
buf += len;
}
skb_unlink(skb, &data->pending_q);
kfree_skb(skb);
bfusb_rx_submit(data, urb);
read_unlock(&data->lock);
return;
resubmit:
urb->dev = data->udev;
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
BT_ERR("%s bulk resubmit failed urb %p err %d",
data->hdev->name, urb, err);
}
unlock:
read_unlock(&data->lock);
}
static int bfusb_open(struct hci_dev *hdev)
{
struct bfusb_data *data = hci_get_drvdata(hdev);
unsigned long flags;
int i, err;
BT_DBG("hdev %p bfusb %p", hdev, data);
write_lock_irqsave(&data->lock, flags);
err = bfusb_rx_submit(data, NULL);
if (!err) {
for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
bfusb_rx_submit(data, NULL);
}
write_unlock_irqrestore(&data->lock, flags);
return err;
}
static int bfusb_flush(struct hci_dev *hdev)
{
struct bfusb_data *data = hci_get_drvdata(hdev);
BT_DBG("hdev %p bfusb %p", hdev, data);
skb_queue_purge(&data->transmit_q);
return 0;
}
static int bfusb_close(struct hci_dev *hdev)
{
struct bfusb_data *data = hci_get_drvdata(hdev);
unsigned long flags;
BT_DBG("hdev %p bfusb %p", hdev, data);
write_lock_irqsave(&data->lock, flags);
write_unlock_irqrestore(&data->lock, flags);
bfusb_unlink_urbs(data);
bfusb_flush(hdev);
return 0;
}
static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct bfusb_data *data = hci_get_drvdata(hdev);
struct sk_buff *nskb;
unsigned char buf[3];
int sent = 0, size, count;
BT_DBG("hdev %p skb %p type %d len %d", hdev, skb,
hci_skb_pkt_type(skb), skb->len);
switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
break;
}
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
count = skb->len;
/* Max HCI frame size seems to be 1511 + 1 */
nskb = bt_skb_alloc(count + 32, GFP_ATOMIC);
if (!nskb) {
BT_ERR("Can't allocate memory for new packet");
return -ENOMEM;
}
nskb->dev = (void *) data;
while (count) {
size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
buf[0] = 0xc1 | ((sent == 0) ? 0x04 : 0) | ((count == size) ? 0x08 : 0);
buf[1] = 0x00;
buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size;
memcpy(skb_put(nskb, 3), buf, 3);
skb_copy_from_linear_data_offset(skb, sent, skb_put(nskb, size), size);
sent += size;
count -= size;
}
/* Don't send frame with multiple size of bulk max packet */
if ((nskb->len % data->bulk_pkt_size) == 0) {
buf[0] = 0xdd;
buf[1] = 0x00;
memcpy(skb_put(nskb, 2), buf, 2);
}
read_lock(&data->lock);
skb_queue_tail(&data->transmit_q, nskb);
bfusb_tx_wakeup(data);
read_unlock(&data->lock);
kfree_skb(skb);
return 0;
}
static int bfusb_load_firmware(struct bfusb_data *data,
const unsigned char *firmware, int count)
{
unsigned char *buf;
int err, pipe, len, size, sent = 0;
BT_DBG("bfusb %p udev %p", data, data->udev);
BT_INFO("BlueFRITZ! USB loading firmware");
buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_KERNEL);
if (!buf) {
BT_ERR("Can't allocate memory chunk for firmware");
return -ENOMEM;
}
pipe = usb_sndctrlpipe(data->udev, 0);
if (usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
0, 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT) < 0) {
BT_ERR("Can't change to loading configuration");
kfree(buf);
return -EBUSY;
}
data->udev->toggle[0] = data->udev->toggle[1] = 0;
pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
while (count) {
size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
memcpy(buf, firmware + sent, size);
err = usb_bulk_msg(data->udev, pipe, buf, size,
&len, BFUSB_BLOCK_TIMEOUT);
if (err || (len != size)) {
BT_ERR("Error in firmware loading");
goto error;
}
sent += size;
count -= size;
}
err = usb_bulk_msg(data->udev, pipe, NULL, 0,
&len, BFUSB_BLOCK_TIMEOUT);
if (err < 0) {
BT_ERR("Error in null packet request");
goto error;
}
pipe = usb_sndctrlpipe(data->udev, 0);
err = usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
0, 2, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
if (err < 0) {
BT_ERR("Can't change to running configuration");
goto error;
}
data->udev->toggle[0] = data->udev->toggle[1] = 0;
BT_INFO("BlueFRITZ! USB device ready");
kfree(buf);
return 0;
error:
kfree(buf);
pipe = usb_sndctrlpipe(data->udev, 0);
usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
0, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
return err;
}
static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
const struct firmware *firmware;
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_host_endpoint *bulk_out_ep;
struct usb_host_endpoint *bulk_in_ep;
struct hci_dev *hdev;
struct bfusb_data *data;
BT_DBG("intf %p id %p", intf, id);
/* Check number of endpoints */
if (intf->cur_altsetting->desc.bNumEndpoints < 2)
return -EIO;
bulk_out_ep = &intf->cur_altsetting->endpoint[0];
bulk_in_ep = &intf->cur_altsetting->endpoint[1];
if (!bulk_out_ep || !bulk_in_ep) {
BT_ERR("Bulk endpoints not found");
goto done;
}
/* Initialize control structure and load firmware */
data = devm_kzalloc(&intf->dev, sizeof(struct bfusb_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->udev = udev;
data->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress;
data->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress;
data->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize);
rwlock_init(&data->lock);
data->reassembly = NULL;
skb_queue_head_init(&data->transmit_q);
skb_queue_head_init(&data->pending_q);
skb_queue_head_init(&data->completed_q);
if (request_firmware(&firmware, "bfubase.frm", &udev->dev) < 0) {
BT_ERR("Firmware request failed");
goto done;
}
BT_DBG("firmware data %p size %zu", firmware->data, firmware->size);
if (bfusb_load_firmware(data, firmware->data, firmware->size) < 0) {
BT_ERR("Firmware loading failed");
goto release;
}
release_firmware(firmware);
/* Initialize and register HCI device */
hdev = hci_alloc_dev();
if (!hdev) {
BT_ERR("Can't allocate HCI device");
goto done;
}
data->hdev = hdev;
hdev->bus = HCI_USB;
hci_set_drvdata(hdev, data);
SET_HCIDEV_DEV(hdev, &intf->dev);
hdev->open = bfusb_open;
hdev->close = bfusb_close;
hdev->flush = bfusb_flush;
hdev->send = bfusb_send_frame;
set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device");
hci_free_dev(hdev);
goto done;
}
usb_set_intfdata(intf, data);
return 0;
release:
release_firmware(firmware);
done:
return -EIO;
}
static void bfusb_disconnect(struct usb_interface *intf)
{
struct bfusb_data *data = usb_get_intfdata(intf);
struct hci_dev *hdev = data->hdev;
BT_DBG("intf %p", intf);
if (!hdev)
return;
usb_set_intfdata(intf, NULL);
bfusb_close(hdev);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
static struct usb_driver bfusb_driver = {
.name = "bfusb",
.probe = bfusb_probe,
.disconnect = bfusb_disconnect,
.id_table = bfusb_table,
.disable_hub_initiated_lpm = 1,
};
module_usb_driver(bfusb_driver);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("bfubase.frm");

View File

@@ -0,0 +1,920 @@
/*
*
* Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)
*
* Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/spinlock.h>
#include <linux/moduleparam.h>
#include <linux/wait.h>
#include <linux/skbuff.h>
#include <linux/io.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
#include <pcmcia/ds.h>
#include <pcmcia/cisreg.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
/* ======================== Module parameters ======================== */
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)");
MODULE_LICENSE("GPL");
/* ======================== Local structures ======================== */
struct bluecard_info {
struct pcmcia_device *p_dev;
struct hci_dev *hdev;
spinlock_t lock; /* For serializing operations */
struct timer_list timer; /* For LED control */
struct sk_buff_head txq;
unsigned long tx_state;
unsigned long rx_state;
unsigned long rx_count;
struct sk_buff *rx_skb;
unsigned char ctrl_reg;
unsigned long hw_state; /* Status of the hardware and LED control */
};
static int bluecard_config(struct pcmcia_device *link);
static void bluecard_release(struct pcmcia_device *link);
static void bluecard_detach(struct pcmcia_device *p_dev);
/* Default baud rate: 57600, 115200, 230400 or 460800 */
#define DEFAULT_BAUD_RATE 230400
/* Hardware states */
#define CARD_READY 1
#define CARD_HAS_PCCARD_ID 4
#define CARD_HAS_POWER_LED 5
#define CARD_HAS_ACTIVITY_LED 6
/* Transmit states */
#define XMIT_SENDING 1
#define XMIT_WAKEUP 2
#define XMIT_BUFFER_NUMBER 5 /* unset = buffer one, set = buffer two */
#define XMIT_BUF_ONE_READY 6
#define XMIT_BUF_TWO_READY 7
#define XMIT_SENDING_READY 8
/* Receiver states */
#define RECV_WAIT_PACKET_TYPE 0
#define RECV_WAIT_EVENT_HEADER 1
#define RECV_WAIT_ACL_HEADER 2
#define RECV_WAIT_SCO_HEADER 3
#define RECV_WAIT_DATA 4
/* Special packet types */
#define PKT_BAUD_RATE_57600 0x80
#define PKT_BAUD_RATE_115200 0x81
#define PKT_BAUD_RATE_230400 0x82
#define PKT_BAUD_RATE_460800 0x83
/* These are the register offsets */
#define REG_COMMAND 0x20
#define REG_INTERRUPT 0x21
#define REG_CONTROL 0x22
#define REG_RX_CONTROL 0x24
#define REG_CARD_RESET 0x30
#define REG_LED_CTRL 0x30
/* REG_COMMAND */
#define REG_COMMAND_TX_BUF_ONE 0x01
#define REG_COMMAND_TX_BUF_TWO 0x02
#define REG_COMMAND_RX_BUF_ONE 0x04
#define REG_COMMAND_RX_BUF_TWO 0x08
#define REG_COMMAND_RX_WIN_ONE 0x00
#define REG_COMMAND_RX_WIN_TWO 0x10
/* REG_CONTROL */
#define REG_CONTROL_BAUD_RATE_57600 0x00
#define REG_CONTROL_BAUD_RATE_115200 0x01
#define REG_CONTROL_BAUD_RATE_230400 0x02
#define REG_CONTROL_BAUD_RATE_460800 0x03
#define REG_CONTROL_RTS 0x04
#define REG_CONTROL_BT_ON 0x08
#define REG_CONTROL_BT_RESET 0x10
#define REG_CONTROL_BT_RES_PU 0x20
#define REG_CONTROL_INTERRUPT 0x40
#define REG_CONTROL_CARD_RESET 0x80
/* REG_RX_CONTROL */
#define RTS_LEVEL_SHIFT_BITS 0x02
/* ======================== LED handling routines ======================== */
static void bluecard_activity_led_timeout(u_long arg)
{
struct bluecard_info *info = (struct bluecard_info *)arg;
unsigned int iobase = info->p_dev->resource[0]->start;
if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
return;
if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
/* Disable activity LED */
outb(0x08 | 0x20, iobase + 0x30);
} else {
/* Disable power LED */
outb(0x00, iobase + 0x30);
}
}
static void bluecard_enable_activity_led(struct bluecard_info *info)
{
unsigned int iobase = info->p_dev->resource[0]->start;
if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
return;
if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
/* Enable activity LED */
outb(0x10 | 0x40, iobase + 0x30);
/* Stop the LED after HZ/4 */
mod_timer(&(info->timer), jiffies + HZ / 4);
} else {
/* Enable power LED */
outb(0x08 | 0x20, iobase + 0x30);
/* Stop the LED after HZ/2 */
mod_timer(&(info->timer), jiffies + HZ / 2);
}
}
/* ======================== Interrupt handling ======================== */
static int bluecard_write(unsigned int iobase, unsigned int offset, __u8 *buf, int len)
{
int i, actual;
actual = (len > 15) ? 15 : len;
outb_p(actual, iobase + offset);
for (i = 0; i < actual; i++)
outb_p(buf[i], iobase + offset + i + 1);
return actual;
}
static void bluecard_write_wakeup(struct bluecard_info *info)
{
if (!info) {
BT_ERR("Unknown device");
return;
}
if (!test_bit(XMIT_SENDING_READY, &(info->tx_state)))
return;
if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
set_bit(XMIT_WAKEUP, &(info->tx_state));
return;
}
do {
unsigned int iobase = info->p_dev->resource[0]->start;
unsigned int offset;
unsigned char command;
unsigned long ready_bit;
register struct sk_buff *skb;
int len;
clear_bit(XMIT_WAKEUP, &(info->tx_state));
if (!pcmcia_dev_present(info->p_dev))
return;
if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) {
if (!test_bit(XMIT_BUF_TWO_READY, &(info->tx_state)))
break;
offset = 0x10;
command = REG_COMMAND_TX_BUF_TWO;
ready_bit = XMIT_BUF_TWO_READY;
} else {
if (!test_bit(XMIT_BUF_ONE_READY, &(info->tx_state)))
break;
offset = 0x00;
command = REG_COMMAND_TX_BUF_ONE;
ready_bit = XMIT_BUF_ONE_READY;
}
skb = skb_dequeue(&(info->txq));
if (!skb)
break;
if (hci_skb_pkt_type(skb) & 0x80) {
/* Disable RTS */
info->ctrl_reg |= REG_CONTROL_RTS;
outb(info->ctrl_reg, iobase + REG_CONTROL);
}
/* Activate LED */
bluecard_enable_activity_led(info);
/* Send frame */
len = bluecard_write(iobase, offset, skb->data, skb->len);
/* Tell the FPGA to send the data */
outb_p(command, iobase + REG_COMMAND);
/* Mark the buffer as dirty */
clear_bit(ready_bit, &(info->tx_state));
if (hci_skb_pkt_type(skb) & 0x80) {
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
DEFINE_WAIT(wait);
unsigned char baud_reg;
switch (hci_skb_pkt_type(skb)) {
case PKT_BAUD_RATE_460800:
baud_reg = REG_CONTROL_BAUD_RATE_460800;
break;
case PKT_BAUD_RATE_230400:
baud_reg = REG_CONTROL_BAUD_RATE_230400;
break;
case PKT_BAUD_RATE_115200:
baud_reg = REG_CONTROL_BAUD_RATE_115200;
break;
case PKT_BAUD_RATE_57600:
/* Fall through... */
default:
baud_reg = REG_CONTROL_BAUD_RATE_57600;
break;
}
/* Wait until the command reaches the baseband */
prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);
schedule_timeout(HZ/10);
finish_wait(&wq, &wait);
/* Set baud on baseband */
info->ctrl_reg &= ~0x03;
info->ctrl_reg |= baud_reg;
outb(info->ctrl_reg, iobase + REG_CONTROL);
/* Enable RTS */
info->ctrl_reg &= ~REG_CONTROL_RTS;
outb(info->ctrl_reg, iobase + REG_CONTROL);
/* Wait before the next HCI packet can be send */
prepare_to_wait(&wq, &wait, TASK_INTERRUPTIBLE);
schedule_timeout(HZ);
finish_wait(&wq, &wait);
}
if (len == skb->len) {
kfree_skb(skb);
} else {
skb_pull(skb, len);
skb_queue_head(&(info->txq), skb);
}
info->hdev->stat.byte_tx += len;
/* Change buffer */
change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state));
} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
clear_bit(XMIT_SENDING, &(info->tx_state));
}
static int bluecard_read(unsigned int iobase, unsigned int offset, __u8 *buf, int size)
{
int i, n, len;
outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND);
len = inb(iobase + offset);
n = 0;
i = 1;
while (n < len) {
if (i == 16) {
outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND);
i = 0;
}
buf[n] = inb(iobase + offset + i);
n++;
i++;
}
return len;
}
static void bluecard_receive(struct bluecard_info *info,
unsigned int offset)
{
unsigned int iobase;
unsigned char buf[31];
int i, len;
if (!info) {
BT_ERR("Unknown device");
return;
}
iobase = info->p_dev->resource[0]->start;
if (test_bit(XMIT_SENDING_READY, &(info->tx_state)))
bluecard_enable_activity_led(info);
len = bluecard_read(iobase, offset, buf, sizeof(buf));
for (i = 0; i < len; i++) {
/* Allocate packet */
if (!info->rx_skb) {
info->rx_state = RECV_WAIT_PACKET_TYPE;
info->rx_count = 0;
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
if (!info->rx_skb) {
BT_ERR("Can't allocate mem for new packet");
return;
}
}
if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
hci_skb_pkt_type(info->rx_skb) = buf[i];
switch (hci_skb_pkt_type(info->rx_skb)) {
case 0x00:
/* init packet */
if (offset != 0x00) {
set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
set_bit(XMIT_SENDING_READY, &(info->tx_state));
bluecard_write_wakeup(info);
}
kfree_skb(info->rx_skb);
info->rx_skb = NULL;
break;
case HCI_EVENT_PKT:
info->rx_state = RECV_WAIT_EVENT_HEADER;
info->rx_count = HCI_EVENT_HDR_SIZE;
break;
case HCI_ACLDATA_PKT:
info->rx_state = RECV_WAIT_ACL_HEADER;
info->rx_count = HCI_ACL_HDR_SIZE;
break;
case HCI_SCODATA_PKT:
info->rx_state = RECV_WAIT_SCO_HEADER;
info->rx_count = HCI_SCO_HDR_SIZE;
break;
default:
/* unknown packet */
BT_ERR("Unknown HCI packet with type 0x%02x received",
hci_skb_pkt_type(info->rx_skb));
info->hdev->stat.err_rx++;
kfree_skb(info->rx_skb);
info->rx_skb = NULL;
break;
}
} else {
*skb_put(info->rx_skb, 1) = buf[i];
info->rx_count--;
if (info->rx_count == 0) {
int dlen;
struct hci_event_hdr *eh;
struct hci_acl_hdr *ah;
struct hci_sco_hdr *sh;
switch (info->rx_state) {
case RECV_WAIT_EVENT_HEADER:
eh = hci_event_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = eh->plen;
break;
case RECV_WAIT_ACL_HEADER:
ah = hci_acl_hdr(info->rx_skb);
dlen = __le16_to_cpu(ah->dlen);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = dlen;
break;
case RECV_WAIT_SCO_HEADER:
sh = hci_sco_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = sh->dlen;
break;
case RECV_WAIT_DATA:
hci_recv_frame(info->hdev, info->rx_skb);
info->rx_skb = NULL;
break;
}
}
}
}
info->hdev->stat.byte_rx += len;
}
static irqreturn_t bluecard_interrupt(int irq, void *dev_inst)
{
struct bluecard_info *info = dev_inst;
unsigned int iobase;
unsigned char reg;
if (!info || !info->hdev)
/* our irq handler is shared */
return IRQ_NONE;
if (!test_bit(CARD_READY, &(info->hw_state)))
return IRQ_HANDLED;
iobase = info->p_dev->resource[0]->start;
spin_lock(&(info->lock));
/* Disable interrupt */
info->ctrl_reg &= ~REG_CONTROL_INTERRUPT;
outb(info->ctrl_reg, iobase + REG_CONTROL);
reg = inb(iobase + REG_INTERRUPT);
if ((reg != 0x00) && (reg != 0xff)) {
if (reg & 0x04) {
bluecard_receive(info, 0x00);
outb(0x04, iobase + REG_INTERRUPT);
outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
}
if (reg & 0x08) {
bluecard_receive(info, 0x10);
outb(0x08, iobase + REG_INTERRUPT);
outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
}
if (reg & 0x01) {
set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
outb(0x01, iobase + REG_INTERRUPT);
bluecard_write_wakeup(info);
}
if (reg & 0x02) {
set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
outb(0x02, iobase + REG_INTERRUPT);
bluecard_write_wakeup(info);
}
}
/* Enable interrupt */
info->ctrl_reg |= REG_CONTROL_INTERRUPT;
outb(info->ctrl_reg, iobase + REG_CONTROL);
spin_unlock(&(info->lock));
return IRQ_HANDLED;
}
/* ======================== Device specific HCI commands ======================== */
static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
{
struct bluecard_info *info = hci_get_drvdata(hdev);
struct sk_buff *skb;
/* Ericsson baud rate command */
unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 };
skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
if (!skb) {
BT_ERR("Can't allocate mem for new packet");
return -1;
}
switch (baud) {
case 460800:
cmd[4] = 0x00;
hci_skb_pkt_type(skb) = PKT_BAUD_RATE_460800;
break;
case 230400:
cmd[4] = 0x01;
hci_skb_pkt_type(skb) = PKT_BAUD_RATE_230400;
break;
case 115200:
cmd[4] = 0x02;
hci_skb_pkt_type(skb) = PKT_BAUD_RATE_115200;
break;
case 57600:
/* Fall through... */
default:
cmd[4] = 0x03;
hci_skb_pkt_type(skb) = PKT_BAUD_RATE_57600;
break;
}
memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
skb_queue_tail(&(info->txq), skb);
bluecard_write_wakeup(info);
return 0;
}
/* ======================== HCI interface ======================== */
static int bluecard_hci_flush(struct hci_dev *hdev)
{
struct bluecard_info *info = hci_get_drvdata(hdev);
/* Drop TX queue */
skb_queue_purge(&(info->txq));
return 0;
}
static int bluecard_hci_open(struct hci_dev *hdev)
{
struct bluecard_info *info = hci_get_drvdata(hdev);
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
unsigned int iobase = info->p_dev->resource[0]->start;
/* Enable LED */
outb(0x08 | 0x20, iobase + 0x30);
}
return 0;
}
static int bluecard_hci_close(struct hci_dev *hdev)
{
struct bluecard_info *info = hci_get_drvdata(hdev);
bluecard_hci_flush(hdev);
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
unsigned int iobase = info->p_dev->resource[0]->start;
/* Disable LED */
outb(0x00, iobase + 0x30);
}
return 0;
}
static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct bluecard_info *info = hci_get_drvdata(hdev);
switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
break;
}
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&(info->txq), skb);
bluecard_write_wakeup(info);
return 0;
}
/* ======================== Card services HCI interaction ======================== */
static int bluecard_open(struct bluecard_info *info)
{
unsigned int iobase = info->p_dev->resource[0]->start;
struct hci_dev *hdev;
unsigned char id;
spin_lock_init(&(info->lock));
init_timer(&(info->timer));
info->timer.function = &bluecard_activity_led_timeout;
info->timer.data = (u_long)info;
skb_queue_head_init(&(info->txq));
info->rx_state = RECV_WAIT_PACKET_TYPE;
info->rx_count = 0;
info->rx_skb = NULL;
/* Initialize HCI device */
hdev = hci_alloc_dev();
if (!hdev) {
BT_ERR("Can't allocate HCI device");
return -ENOMEM;
}
info->hdev = hdev;
hdev->bus = HCI_PCCARD;
hci_set_drvdata(hdev, info);
SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
hdev->open = bluecard_hci_open;
hdev->close = bluecard_hci_close;
hdev->flush = bluecard_hci_flush;
hdev->send = bluecard_hci_send_frame;
id = inb(iobase + 0x30);
if ((id & 0x0f) == 0x02)
set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state));
if (id & 0x10)
set_bit(CARD_HAS_POWER_LED, &(info->hw_state));
if (id & 0x20)
set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state));
/* Reset card */
info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
outb(info->ctrl_reg, iobase + REG_CONTROL);
/* Turn FPGA off */
outb(0x80, iobase + 0x30);
/* Wait some time */
msleep(10);
/* Turn FPGA on */
outb(0x00, iobase + 0x30);
/* Activate card */
info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU;
outb(info->ctrl_reg, iobase + REG_CONTROL);
/* Enable interrupt */
outb(0xff, iobase + REG_INTERRUPT);
info->ctrl_reg |= REG_CONTROL_INTERRUPT;
outb(info->ctrl_reg, iobase + REG_CONTROL);
if ((id & 0x0f) == 0x03) {
/* Disable RTS */
info->ctrl_reg |= REG_CONTROL_RTS;
outb(info->ctrl_reg, iobase + REG_CONTROL);
/* Set baud rate */
info->ctrl_reg |= 0x03;
outb(info->ctrl_reg, iobase + REG_CONTROL);
/* Enable RTS */
info->ctrl_reg &= ~REG_CONTROL_RTS;
outb(info->ctrl_reg, iobase + REG_CONTROL);
set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
set_bit(XMIT_SENDING_READY, &(info->tx_state));
}
/* Start the RX buffers */
outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
/* Signal that the hardware is ready */
set_bit(CARD_READY, &(info->hw_state));
/* Drop TX queue */
skb_queue_purge(&(info->txq));
/* Control the point at which RTS is enabled */
outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL);
/* Timeout before it is safe to send the first HCI packet */
msleep(1250);
/* Register HCI device */
if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device");
info->hdev = NULL;
hci_free_dev(hdev);
return -ENODEV;
}
return 0;
}
static int bluecard_close(struct bluecard_info *info)
{
unsigned int iobase = info->p_dev->resource[0]->start;
struct hci_dev *hdev = info->hdev;
if (!hdev)
return -ENODEV;
bluecard_hci_close(hdev);
clear_bit(CARD_READY, &(info->hw_state));
/* Reset card */
info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
outb(info->ctrl_reg, iobase + REG_CONTROL);
/* Turn FPGA off */
outb(0x80, iobase + 0x30);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
return 0;
}
static int bluecard_probe(struct pcmcia_device *link)
{
struct bluecard_info *info;
/* Create new info device */
info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->p_dev = link;
link->priv = info;
link->config_flags |= CONF_ENABLE_IRQ;
return bluecard_config(link);
}
static void bluecard_detach(struct pcmcia_device *link)
{
bluecard_release(link);
}
static int bluecard_config(struct pcmcia_device *link)
{
struct bluecard_info *info = link->priv;
int i, n;
link->config_index = 0x20;
link->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
link->resource[0]->end = 64;
link->io_lines = 6;
for (n = 0; n < 0x400; n += 0x40) {
link->resource[0]->start = n ^ 0x300;
i = pcmcia_request_io(link);
if (i == 0)
break;
}
if (i != 0)
goto failed;
i = pcmcia_request_irq(link, bluecard_interrupt);
if (i != 0)
goto failed;
i = pcmcia_enable_device(link);
if (i != 0)
goto failed;
if (bluecard_open(info) != 0)
goto failed;
return 0;
failed:
bluecard_release(link);
return -ENODEV;
}
static void bluecard_release(struct pcmcia_device *link)
{
struct bluecard_info *info = link->priv;
bluecard_close(info);
del_timer_sync(&(info->timer));
pcmcia_disable_device(link);
}
static const struct pcmcia_device_id bluecard_ids[] = {
PCMCIA_DEVICE_PROD_ID12("BlueCard", "LSE041", 0xbaf16fbf, 0x657cc15e),
PCMCIA_DEVICE_PROD_ID12("BTCFCARD", "LSE139", 0xe3987764, 0x2524b59c),
PCMCIA_DEVICE_PROD_ID12("WSS", "LSE039", 0x0a0736ec, 0x24e6dfab),
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, bluecard_ids);
static struct pcmcia_driver bluecard_driver = {
.owner = THIS_MODULE,
.name = "bluecard_cs",
.probe = bluecard_probe,
.remove = bluecard_detach,
.id_table = bluecard_ids,
};
module_pcmcia_driver(bluecard_driver);

462
drivers/bluetooth/bpa10x.c Normal file
View File

@@ -0,0 +1,462 @@
/*
*
* Digianswer Bluetooth USB driver
*
* Copyright (C) 2004-2007 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/usb.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
#define VERSION "0.11"
static const struct usb_device_id bpa10x_table[] = {
/* Tektronix BPA 100/105 (Digianswer) */
{ USB_DEVICE(0x08fd, 0x0002) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, bpa10x_table);
struct bpa10x_data {
struct hci_dev *hdev;
struct usb_device *udev;
struct usb_anchor tx_anchor;
struct usb_anchor rx_anchor;
struct sk_buff *rx_skb[2];
};
static void bpa10x_tx_complete(struct urb *urb)
{
struct sk_buff *skb = urb->context;
struct hci_dev *hdev = (struct hci_dev *) skb->dev;
BT_DBG("%s urb %p status %d count %d", hdev->name,
urb, urb->status, urb->actual_length);
if (!test_bit(HCI_RUNNING, &hdev->flags))
goto done;
if (!urb->status)
hdev->stat.byte_tx += urb->transfer_buffer_length;
else
hdev->stat.err_tx++;
done:
kfree(urb->setup_packet);
kfree_skb(skb);
}
#define HCI_VENDOR_HDR_SIZE 5
#define HCI_RECV_VENDOR \
.type = HCI_VENDOR_PKT, \
.hlen = HCI_VENDOR_HDR_SIZE, \
.loff = 3, \
.lsize = 2, \
.maxlen = HCI_MAX_FRAME_SIZE
static const struct h4_recv_pkt bpa10x_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
{ HCI_RECV_VENDOR, .recv = hci_recv_diag },
};
static void bpa10x_rx_complete(struct urb *urb)
{
struct hci_dev *hdev = urb->context;
struct bpa10x_data *data = hci_get_drvdata(hdev);
int err;
BT_DBG("%s urb %p status %d count %d", hdev->name,
urb, urb->status, urb->actual_length);
if (!test_bit(HCI_RUNNING, &hdev->flags))
return;
if (urb->status == 0) {
bool idx = usb_pipebulk(urb->pipe);
data->rx_skb[idx] = h4_recv_buf(hdev, data->rx_skb[idx],
urb->transfer_buffer,
urb->actual_length,
bpa10x_recv_pkts,
ARRAY_SIZE(bpa10x_recv_pkts));
if (IS_ERR(data->rx_skb[idx])) {
BT_ERR("%s corrupted event packet", hdev->name);
hdev->stat.err_rx++;
data->rx_skb[idx] = NULL;
}
}
usb_anchor_urb(urb, &data->rx_anchor);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) {
BT_ERR("%s urb %p failed to resubmit (%d)",
hdev->name, urb, -err);
usb_unanchor_urb(urb);
}
}
static inline int bpa10x_submit_intr_urb(struct hci_dev *hdev)
{
struct bpa10x_data *data = hci_get_drvdata(hdev);
struct urb *urb;
unsigned char *buf;
unsigned int pipe;
int err, size = 16;
BT_DBG("%s", hdev->name);
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return -ENOMEM;
buf = kmalloc(size, GFP_KERNEL);
if (!buf) {
usb_free_urb(urb);
return -ENOMEM;
}
pipe = usb_rcvintpipe(data->udev, 0x81);
usb_fill_int_urb(urb, data->udev, pipe, buf, size,
bpa10x_rx_complete, hdev, 1);
urb->transfer_flags |= URB_FREE_BUFFER;
usb_anchor_urb(urb, &data->rx_anchor);
err = usb_submit_urb(urb, GFP_KERNEL);
if (err < 0) {
BT_ERR("%s urb %p submission failed (%d)",
hdev->name, urb, -err);
usb_unanchor_urb(urb);
}
usb_free_urb(urb);
return err;
}
static inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev)
{
struct bpa10x_data *data = hci_get_drvdata(hdev);
struct urb *urb;
unsigned char *buf;
unsigned int pipe;
int err, size = 64;
BT_DBG("%s", hdev->name);
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return -ENOMEM;
buf = kmalloc(size, GFP_KERNEL);
if (!buf) {
usb_free_urb(urb);
return -ENOMEM;
}
pipe = usb_rcvbulkpipe(data->udev, 0x82);
usb_fill_bulk_urb(urb, data->udev, pipe,
buf, size, bpa10x_rx_complete, hdev);
urb->transfer_flags |= URB_FREE_BUFFER;
usb_anchor_urb(urb, &data->rx_anchor);
err = usb_submit_urb(urb, GFP_KERNEL);
if (err < 0) {
BT_ERR("%s urb %p submission failed (%d)",
hdev->name, urb, -err);
usb_unanchor_urb(urb);
}
usb_free_urb(urb);
return err;
}
static int bpa10x_open(struct hci_dev *hdev)
{
struct bpa10x_data *data = hci_get_drvdata(hdev);
int err;
BT_DBG("%s", hdev->name);
err = bpa10x_submit_intr_urb(hdev);
if (err < 0)
goto error;
err = bpa10x_submit_bulk_urb(hdev);
if (err < 0)
goto error;
return 0;
error:
usb_kill_anchored_urbs(&data->rx_anchor);
return err;
}
static int bpa10x_close(struct hci_dev *hdev)
{
struct bpa10x_data *data = hci_get_drvdata(hdev);
BT_DBG("%s", hdev->name);
usb_kill_anchored_urbs(&data->rx_anchor);
return 0;
}
static int bpa10x_flush(struct hci_dev *hdev)
{
struct bpa10x_data *data = hci_get_drvdata(hdev);
BT_DBG("%s", hdev->name);
usb_kill_anchored_urbs(&data->tx_anchor);
return 0;
}
static int bpa10x_setup(struct hci_dev *hdev)
{
const u8 req[] = { 0x07 };
struct sk_buff *skb;
BT_DBG("%s", hdev->name);
/* Read revision string */
skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT);
if (IS_ERR(skb))
return PTR_ERR(skb);
BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
hci_set_fw_info(hdev, "%s", skb->data + 1);
kfree_skb(skb);
return 0;
}
static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct bpa10x_data *data = hci_get_drvdata(hdev);
struct usb_ctrlrequest *dr;
struct urb *urb;
unsigned int pipe;
int err;
BT_DBG("%s", hdev->name);
skb->dev = (void *) hdev;
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb)
return -ENOMEM;
/* Prepend skb with frame type */
*skb_push(skb, 1) = hci_skb_pkt_type(skb);
switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
dr = kmalloc(sizeof(*dr), GFP_ATOMIC);
if (!dr) {
usb_free_urb(urb);
return -ENOMEM;
}
dr->bRequestType = USB_TYPE_VENDOR;
dr->bRequest = 0;
dr->wIndex = 0;
dr->wValue = 0;
dr->wLength = __cpu_to_le16(skb->len);
pipe = usb_sndctrlpipe(data->udev, 0x00);
usb_fill_control_urb(urb, data->udev, pipe, (void *) dr,
skb->data, skb->len, bpa10x_tx_complete, skb);
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
pipe = usb_sndbulkpipe(data->udev, 0x02);
usb_fill_bulk_urb(urb, data->udev, pipe,
skb->data, skb->len, bpa10x_tx_complete, skb);
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
pipe = usb_sndbulkpipe(data->udev, 0x02);
usb_fill_bulk_urb(urb, data->udev, pipe,
skb->data, skb->len, bpa10x_tx_complete, skb);
hdev->stat.sco_tx++;
break;
default:
usb_free_urb(urb);
return -EILSEQ;
}
usb_anchor_urb(urb, &data->tx_anchor);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err < 0) {
BT_ERR("%s urb %p submission failed", hdev->name, urb);
kfree(urb->setup_packet);
usb_unanchor_urb(urb);
}
usb_free_urb(urb);
return 0;
}
static int bpa10x_set_diag(struct hci_dev *hdev, bool enable)
{
const u8 req[] = { 0x00, enable };
struct sk_buff *skb;
BT_DBG("%s", hdev->name);
if (!test_bit(HCI_RUNNING, &hdev->flags))
return -ENETDOWN;
/* Enable sniffer operation */
skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT);
if (IS_ERR(skb))
return PTR_ERR(skb);
kfree_skb(skb);
return 0;
}
static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct bpa10x_data *data;
struct hci_dev *hdev;
int err;
BT_DBG("intf %p id %p", intf, id);
if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
return -ENODEV;
data = devm_kzalloc(&intf->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->udev = interface_to_usbdev(intf);
init_usb_anchor(&data->tx_anchor);
init_usb_anchor(&data->rx_anchor);
hdev = hci_alloc_dev();
if (!hdev)
return -ENOMEM;
hdev->bus = HCI_USB;
hci_set_drvdata(hdev, data);
data->hdev = hdev;
SET_HCIDEV_DEV(hdev, &intf->dev);
hdev->open = bpa10x_open;
hdev->close = bpa10x_close;
hdev->flush = bpa10x_flush;
hdev->setup = bpa10x_setup;
hdev->send = bpa10x_send_frame;
hdev->set_diag = bpa10x_set_diag;
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
err = hci_register_dev(hdev);
if (err < 0) {
hci_free_dev(hdev);
return err;
}
usb_set_intfdata(intf, data);
return 0;
}
static void bpa10x_disconnect(struct usb_interface *intf)
{
struct bpa10x_data *data = usb_get_intfdata(intf);
BT_DBG("intf %p", intf);
if (!data)
return;
usb_set_intfdata(intf, NULL);
hci_unregister_dev(data->hdev);
hci_free_dev(data->hdev);
kfree_skb(data->rx_skb[0]);
kfree_skb(data->rx_skb[1]);
}
static struct usb_driver bpa10x_driver = {
.name = "bpa10x",
.probe = bpa10x_probe,
.disconnect = bpa10x_disconnect,
.id_table = bpa10x_table,
.disable_hub_initiated_lpm = 1,
};
module_usb_driver(bpa10x_driver);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Digianswer Bluetooth USB driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");

744
drivers/bluetooth/bt3c_cs.c Normal file
View File

@@ -0,0 +1,744 @@
/*
*
* Driver for the 3Com Bluetooth PCMCIA card
*
* Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
* Jose Orlando Pereira <jop@di.uminho.pt>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/spinlock.h>
#include <linux/moduleparam.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/bitops.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/firmware.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
#include <pcmcia/ds.h>
#include <pcmcia/cisreg.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
/* ======================== Module parameters ======================== */
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("BT3CPCC.bin");
/* ======================== Local structures ======================== */
struct bt3c_info {
struct pcmcia_device *p_dev;
struct hci_dev *hdev;
spinlock_t lock; /* For serializing operations */
struct sk_buff_head txq;
unsigned long tx_state;
unsigned long rx_state;
unsigned long rx_count;
struct sk_buff *rx_skb;
};
static int bt3c_config(struct pcmcia_device *link);
static void bt3c_release(struct pcmcia_device *link);
static void bt3c_detach(struct pcmcia_device *p_dev);
/* Transmit states */
#define XMIT_SENDING 1
#define XMIT_WAKEUP 2
#define XMIT_WAITING 8
/* Receiver states */
#define RECV_WAIT_PACKET_TYPE 0
#define RECV_WAIT_EVENT_HEADER 1
#define RECV_WAIT_ACL_HEADER 2
#define RECV_WAIT_SCO_HEADER 3
#define RECV_WAIT_DATA 4
/* ======================== Special I/O functions ======================== */
#define DATA_L 0
#define DATA_H 1
#define ADDR_L 2
#define ADDR_H 3
#define CONTROL 4
static inline void bt3c_address(unsigned int iobase, unsigned short addr)
{
outb(addr & 0xff, iobase + ADDR_L);
outb((addr >> 8) & 0xff, iobase + ADDR_H);
}
static inline void bt3c_put(unsigned int iobase, unsigned short value)
{
outb(value & 0xff, iobase + DATA_L);
outb((value >> 8) & 0xff, iobase + DATA_H);
}
static inline void bt3c_io_write(unsigned int iobase, unsigned short addr, unsigned short value)
{
bt3c_address(iobase, addr);
bt3c_put(iobase, value);
}
static inline unsigned short bt3c_get(unsigned int iobase)
{
unsigned short value = inb(iobase + DATA_L);
value |= inb(iobase + DATA_H) << 8;
return value;
}
static inline unsigned short bt3c_read(unsigned int iobase, unsigned short addr)
{
bt3c_address(iobase, addr);
return bt3c_get(iobase);
}
/* ======================== Interrupt handling ======================== */
static int bt3c_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
{
int actual = 0;
bt3c_address(iobase, 0x7080);
/* Fill FIFO with current frame */
while (actual < len) {
/* Transmit next byte */
bt3c_put(iobase, buf[actual]);
actual++;
}
bt3c_io_write(iobase, 0x7005, actual);
return actual;
}
static void bt3c_write_wakeup(struct bt3c_info *info)
{
if (!info) {
BT_ERR("Unknown device");
return;
}
if (test_and_set_bit(XMIT_SENDING, &(info->tx_state)))
return;
do {
unsigned int iobase = info->p_dev->resource[0]->start;
register struct sk_buff *skb;
int len;
if (!pcmcia_dev_present(info->p_dev))
break;
skb = skb_dequeue(&(info->txq));
if (!skb) {
clear_bit(XMIT_SENDING, &(info->tx_state));
break;
}
/* Send frame */
len = bt3c_write(iobase, 256, skb->data, skb->len);
if (len != skb->len)
BT_ERR("Very strange");
kfree_skb(skb);
info->hdev->stat.byte_tx += len;
} while (0);
}
static void bt3c_receive(struct bt3c_info *info)
{
unsigned int iobase;
int size = 0, avail;
if (!info) {
BT_ERR("Unknown device");
return;
}
iobase = info->p_dev->resource[0]->start;
avail = bt3c_read(iobase, 0x7006);
bt3c_address(iobase, 0x7480);
while (size < avail) {
size++;
info->hdev->stat.byte_rx++;
/* Allocate packet */
if (!info->rx_skb) {
info->rx_state = RECV_WAIT_PACKET_TYPE;
info->rx_count = 0;
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
if (!info->rx_skb) {
BT_ERR("Can't allocate mem for new packet");
return;
}
}
if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
hci_skb_pkt_type(info->rx_skb) = inb(iobase + DATA_L);
inb(iobase + DATA_H);
switch (hci_skb_pkt_type(info->rx_skb)) {
case HCI_EVENT_PKT:
info->rx_state = RECV_WAIT_EVENT_HEADER;
info->rx_count = HCI_EVENT_HDR_SIZE;
break;
case HCI_ACLDATA_PKT:
info->rx_state = RECV_WAIT_ACL_HEADER;
info->rx_count = HCI_ACL_HDR_SIZE;
break;
case HCI_SCODATA_PKT:
info->rx_state = RECV_WAIT_SCO_HEADER;
info->rx_count = HCI_SCO_HDR_SIZE;
break;
default:
/* Unknown packet */
BT_ERR("Unknown HCI packet with type 0x%02x received",
hci_skb_pkt_type(info->rx_skb));
info->hdev->stat.err_rx++;
kfree_skb(info->rx_skb);
info->rx_skb = NULL;
break;
}
} else {
__u8 x = inb(iobase + DATA_L);
*skb_put(info->rx_skb, 1) = x;
inb(iobase + DATA_H);
info->rx_count--;
if (info->rx_count == 0) {
int dlen;
struct hci_event_hdr *eh;
struct hci_acl_hdr *ah;
struct hci_sco_hdr *sh;
switch (info->rx_state) {
case RECV_WAIT_EVENT_HEADER:
eh = hci_event_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = eh->plen;
break;
case RECV_WAIT_ACL_HEADER:
ah = hci_acl_hdr(info->rx_skb);
dlen = __le16_to_cpu(ah->dlen);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = dlen;
break;
case RECV_WAIT_SCO_HEADER:
sh = hci_sco_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = sh->dlen;
break;
case RECV_WAIT_DATA:
hci_recv_frame(info->hdev, info->rx_skb);
info->rx_skb = NULL;
break;
}
}
}
}
bt3c_io_write(iobase, 0x7006, 0x0000);
}
static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)
{
struct bt3c_info *info = dev_inst;
unsigned int iobase;
int iir;
irqreturn_t r = IRQ_NONE;
if (!info || !info->hdev)
/* our irq handler is shared */
return IRQ_NONE;
iobase = info->p_dev->resource[0]->start;
spin_lock(&(info->lock));
iir = inb(iobase + CONTROL);
if (iir & 0x80) {
int stat = bt3c_read(iobase, 0x7001);
if ((stat & 0xff) == 0x7f) {
BT_ERR("Very strange (stat=0x%04x)", stat);
} else if ((stat & 0xff) != 0xff) {
if (stat & 0x0020) {
int status = bt3c_read(iobase, 0x7002) & 0x10;
BT_INFO("%s: Antenna %s", info->hdev->name,
status ? "out" : "in");
}
if (stat & 0x0001)
bt3c_receive(info);
if (stat & 0x0002) {
clear_bit(XMIT_SENDING, &(info->tx_state));
bt3c_write_wakeup(info);
}
bt3c_io_write(iobase, 0x7001, 0x0000);
outb(iir, iobase + CONTROL);
}
r = IRQ_HANDLED;
}
spin_unlock(&(info->lock));
return r;
}
/* ======================== HCI interface ======================== */
static int bt3c_hci_flush(struct hci_dev *hdev)
{
struct bt3c_info *info = hci_get_drvdata(hdev);
/* Drop TX queue */
skb_queue_purge(&(info->txq));
return 0;
}
static int bt3c_hci_open(struct hci_dev *hdev)
{
return 0;
}
static int bt3c_hci_close(struct hci_dev *hdev)
{
bt3c_hci_flush(hdev);
return 0;
}
static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct bt3c_info *info = hci_get_drvdata(hdev);
unsigned long flags;
switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
break;
}
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&(info->txq), skb);
spin_lock_irqsave(&(info->lock), flags);
bt3c_write_wakeup(info);
spin_unlock_irqrestore(&(info->lock), flags);
return 0;
}
/* ======================== Card services HCI interaction ======================== */
static int bt3c_load_firmware(struct bt3c_info *info,
const unsigned char *firmware,
int count)
{
char *ptr = (char *) firmware;
char b[9];
unsigned int iobase, tmp;
unsigned long size, addr, fcs;
int i, err = 0;
iobase = info->p_dev->resource[0]->start;
/* Reset */
bt3c_io_write(iobase, 0x8040, 0x0404);
bt3c_io_write(iobase, 0x8040, 0x0400);
udelay(1);
bt3c_io_write(iobase, 0x8040, 0x0404);
udelay(17);
/* Load */
while (count) {
if (ptr[0] != 'S') {
BT_ERR("Bad address in firmware");
err = -EFAULT;
goto error;
}
memset(b, 0, sizeof(b));
memcpy(b, ptr + 2, 2);
if (kstrtoul(b, 16, &size) < 0)
return -EINVAL;
memset(b, 0, sizeof(b));
memcpy(b, ptr + 4, 8);
if (kstrtoul(b, 16, &addr) < 0)
return -EINVAL;
memset(b, 0, sizeof(b));
memcpy(b, ptr + (size * 2) + 2, 2);
if (kstrtoul(b, 16, &fcs) < 0)
return -EINVAL;
memset(b, 0, sizeof(b));
for (tmp = 0, i = 0; i < size; i++) {
memcpy(b, ptr + (i * 2) + 2, 2);
tmp += simple_strtol(b, NULL, 16);
}
if (((tmp + fcs) & 0xff) != 0xff) {
BT_ERR("Checksum error in firmware");
err = -EILSEQ;
goto error;
}
if (ptr[1] == '3') {
bt3c_address(iobase, addr);
memset(b, 0, sizeof(b));
for (i = 0; i < (size - 4) / 2; i++) {
memcpy(b, ptr + (i * 4) + 12, 4);
tmp = simple_strtoul(b, NULL, 16);
bt3c_put(iobase, tmp);
}
}
ptr += (size * 2) + 6;
count -= (size * 2) + 6;
}
udelay(17);
/* Boot */
bt3c_address(iobase, 0x3000);
outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL);
error:
udelay(17);
/* Clear */
bt3c_io_write(iobase, 0x7006, 0x0000);
bt3c_io_write(iobase, 0x7005, 0x0000);
bt3c_io_write(iobase, 0x7001, 0x0000);
return err;
}
static int bt3c_open(struct bt3c_info *info)
{
const struct firmware *firmware;
struct hci_dev *hdev;
int err;
spin_lock_init(&(info->lock));
skb_queue_head_init(&(info->txq));
info->rx_state = RECV_WAIT_PACKET_TYPE;
info->rx_count = 0;
info->rx_skb = NULL;
/* Initialize HCI device */
hdev = hci_alloc_dev();
if (!hdev) {
BT_ERR("Can't allocate HCI device");
return -ENOMEM;
}
info->hdev = hdev;
hdev->bus = HCI_PCCARD;
hci_set_drvdata(hdev, info);
SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
hdev->open = bt3c_hci_open;
hdev->close = bt3c_hci_close;
hdev->flush = bt3c_hci_flush;
hdev->send = bt3c_hci_send_frame;
/* Load firmware */
err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev);
if (err < 0) {
BT_ERR("Firmware request failed");
goto error;
}
err = bt3c_load_firmware(info, firmware->data, firmware->size);
release_firmware(firmware);
if (err < 0) {
BT_ERR("Firmware loading failed");
goto error;
}
/* Timeout before it is safe to send the first HCI packet */
msleep(1000);
/* Register HCI device */
err = hci_register_dev(hdev);
if (err < 0) {
BT_ERR("Can't register HCI device");
goto error;
}
return 0;
error:
info->hdev = NULL;
hci_free_dev(hdev);
return err;
}
static int bt3c_close(struct bt3c_info *info)
{
struct hci_dev *hdev = info->hdev;
if (!hdev)
return -ENODEV;
bt3c_hci_close(hdev);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
return 0;
}
static int bt3c_probe(struct pcmcia_device *link)
{
struct bt3c_info *info;
/* Create new info device */
info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->p_dev = link;
link->priv = info;
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP |
CONF_AUTO_SET_IO;
return bt3c_config(link);
}
static void bt3c_detach(struct pcmcia_device *link)
{
bt3c_release(link);
}
static int bt3c_check_config(struct pcmcia_device *p_dev, void *priv_data)
{
int *try = priv_data;
if (!try)
p_dev->io_lines = 16;
if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0))
return -EINVAL;
p_dev->resource[0]->end = 8;
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
return pcmcia_request_io(p_dev);
}
static int bt3c_check_config_notpicky(struct pcmcia_device *p_dev,
void *priv_data)
{
static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
int j;
if (p_dev->io_lines > 3)
return -ENODEV;
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
p_dev->resource[0]->end = 8;
for (j = 0; j < 5; j++) {
p_dev->resource[0]->start = base[j];
p_dev->io_lines = base[j] ? 16 : 3;
if (!pcmcia_request_io(p_dev))
return 0;
}
return -ENODEV;
}
static int bt3c_config(struct pcmcia_device *link)
{
struct bt3c_info *info = link->priv;
int i;
unsigned long try;
/* First pass: look for a config entry that looks normal.
Two tries: without IO aliases, then with aliases */
for (try = 0; try < 2; try++)
if (!pcmcia_loop_config(link, bt3c_check_config, (void *) try))
goto found_port;
/* Second pass: try to find an entry that isn't picky about
its base address, then try to grab any standard serial port
address, and finally try to get any free port. */
if (!pcmcia_loop_config(link, bt3c_check_config_notpicky, NULL))
goto found_port;
BT_ERR("No usable port range found");
goto failed;
found_port:
i = pcmcia_request_irq(link, &bt3c_interrupt);
if (i != 0)
goto failed;
i = pcmcia_enable_device(link);
if (i != 0)
goto failed;
if (bt3c_open(info) != 0)
goto failed;
return 0;
failed:
bt3c_release(link);
return -ENODEV;
}
static void bt3c_release(struct pcmcia_device *link)
{
struct bt3c_info *info = link->priv;
bt3c_close(info);
pcmcia_disable_device(link);
}
static const struct pcmcia_device_id bt3c_ids[] = {
PCMCIA_DEVICE_PROD_ID13("3COM", "Bluetooth PC Card", 0xefce0a31, 0xd4ce9b02),
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, bt3c_ids);
static struct pcmcia_driver bt3c_driver = {
.owner = THIS_MODULE,
.name = "bt3c_cs",
.probe = bt3c_probe,
.remove = bt3c_detach,
.id_table = bt3c_ids,
};
module_pcmcia_driver(bt3c_driver);

556
drivers/bluetooth/btbcm.c Normal file
View File

@@ -0,0 +1,556 @@
/*
*
* Bluetooth support for Broadcom devices
*
* Copyright (C) 2015 Intel Corporation
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/firmware.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btbcm.h"
#define VERSION "0.1"
#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
#define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}})
#define BDADDR_BCM4330B1 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb1, 0x30, 0x43}})
int btbcm_check_bdaddr(struct hci_dev *hdev)
{
struct hci_rp_read_bd_addr *bda;
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
int err = PTR_ERR(skb);
BT_ERR("%s: BCM: Reading device address failed (%d)",
hdev->name, err);
return err;
}
if (skb->len != sizeof(*bda)) {
BT_ERR("%s: BCM: Device address length mismatch", hdev->name);
kfree_skb(skb);
return -EIO;
}
bda = (struct hci_rp_read_bd_addr *)skb->data;
/* Check if the address indicates a controller with either an
* invalid or default address. In both cases the device needs
* to be marked as not having a valid address.
*
* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
* with no configured address.
*
* The address 43:24:B3:00:00:00 indicates a BCM4324B3 controller
* with waiting for configuration state.
*
* The address 43:30:B1:00:00:00 indicates a BCM4330B1 controller
* with waiting for configuration state.
*/
if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0) ||
!bacmp(&bda->bdaddr, BDADDR_BCM4324B3) ||
!bacmp(&bda->bdaddr, BDADDR_BCM4330B1)) {
BT_INFO("%s: BCM: Using default device address (%pMR)",
hdev->name, &bda->bdaddr);
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
}
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(btbcm_check_bdaddr);
int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
struct sk_buff *skb;
int err;
skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
BT_ERR("%s: BCM: Change address command failed (%d)",
hdev->name, err);
return err;
}
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(btbcm_set_bdaddr);
int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw)
{
const struct hci_command_hdr *cmd;
const u8 *fw_ptr;
size_t fw_size;
struct sk_buff *skb;
u16 opcode;
int err = 0;
/* Start Download */
skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
BT_ERR("%s: BCM: Download Minidrv command failed (%d)",
hdev->name, err);
goto done;
}
kfree_skb(skb);
/* 50 msec delay after Download Minidrv completes */
msleep(50);
fw_ptr = fw->data;
fw_size = fw->size;
while (fw_size >= sizeof(*cmd)) {
const u8 *cmd_param;
cmd = (struct hci_command_hdr *)fw_ptr;
fw_ptr += sizeof(*cmd);
fw_size -= sizeof(*cmd);
if (fw_size < cmd->plen) {
BT_ERR("%s: BCM: Patch is corrupted", hdev->name);
err = -EINVAL;
goto done;
}
cmd_param = fw_ptr;
fw_ptr += cmd->plen;
fw_size -= cmd->plen;
opcode = le16_to_cpu(cmd->opcode);
skb = __hci_cmd_sync(hdev, opcode, cmd->plen, cmd_param,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
BT_ERR("%s: BCM: Patch command %04x failed (%d)",
hdev->name, opcode, err);
goto done;
}
kfree_skb(skb);
}
/* 250 msec delay after Launch Ram completes */
msleep(250);
done:
return err;
}
EXPORT_SYMBOL(btbcm_patchram);
static int btbcm_reset(struct hci_dev *hdev)
{
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
int err = PTR_ERR(skb);
BT_ERR("%s: BCM: Reset failed (%d)", hdev->name, err);
return err;
}
kfree_skb(skb);
return 0;
}
static struct sk_buff *btbcm_read_local_name(struct hci_dev *hdev)
{
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: BCM: Reading local name failed (%ld)",
hdev->name, PTR_ERR(skb));
return skb;
}
if (skb->len != sizeof(struct hci_rp_read_local_name)) {
BT_ERR("%s: BCM: Local name length mismatch", hdev->name);
kfree_skb(skb);
return ERR_PTR(-EIO);
}
return skb;
}
static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev)
{
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: BCM: Reading local version info failed (%ld)",
hdev->name, PTR_ERR(skb));
return skb;
}
if (skb->len != sizeof(struct hci_rp_read_local_version)) {
BT_ERR("%s: BCM: Local version length mismatch", hdev->name);
kfree_skb(skb);
return ERR_PTR(-EIO);
}
return skb;
}
static struct sk_buff *btbcm_read_verbose_config(struct hci_dev *hdev)
{
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: BCM: Read verbose config info failed (%ld)",
hdev->name, PTR_ERR(skb));
return skb;
}
if (skb->len != 7) {
BT_ERR("%s: BCM: Verbose config length mismatch", hdev->name);
kfree_skb(skb);
return ERR_PTR(-EIO);
}
return skb;
}
static struct sk_buff *btbcm_read_usb_product(struct hci_dev *hdev)
{
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, 0xfc5a, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: BCM: Read USB product info failed (%ld)",
hdev->name, PTR_ERR(skb));
return skb;
}
if (skb->len != 5) {
BT_ERR("%s: BCM: USB product length mismatch", hdev->name);
kfree_skb(skb);
return ERR_PTR(-EIO);
}
return skb;
}
static const struct {
u16 subver;
const char *name;
} bcm_uart_subver_table[] = {
{ 0x4103, "BCM4330B1" }, /* 002.001.003 */
{ 0x410e, "BCM43341B0" }, /* 002.001.014 */
{ 0x4406, "BCM4324B3" }, /* 002.004.006 */
{ 0x610c, "BCM4354" }, /* 003.001.012 */
{ }
};
int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len)
{
u16 subver, rev;
const char *hw_name = NULL;
struct sk_buff *skb;
struct hci_rp_read_local_version *ver;
int i, err;
/* Reset */
err = btbcm_reset(hdev);
if (err)
return err;
/* Read Local Version Info */
skb = btbcm_read_local_version(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
ver = (struct hci_rp_read_local_version *)skb->data;
rev = le16_to_cpu(ver->hci_rev);
subver = le16_to_cpu(ver->lmp_subver);
kfree_skb(skb);
/* Read Verbose Config Version Info */
skb = btbcm_read_verbose_config(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
kfree_skb(skb);
switch ((rev & 0xf000) >> 12) {
case 0:
case 1:
case 3:
for (i = 0; bcm_uart_subver_table[i].name; i++) {
if (subver == bcm_uart_subver_table[i].subver) {
hw_name = bcm_uart_subver_table[i].name;
break;
}
}
snprintf(fw_name, len, "brcm/%s.hcd", hw_name ? : "BCM");
break;
default:
return 0;
}
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
hw_name ? : "BCM", (subver & 0xe000) >> 13,
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
return 0;
}
EXPORT_SYMBOL_GPL(btbcm_initialize);
int btbcm_finalize(struct hci_dev *hdev)
{
struct sk_buff *skb;
struct hci_rp_read_local_version *ver;
u16 subver, rev;
int err;
/* Reset */
err = btbcm_reset(hdev);
if (err)
return err;
/* Read Local Version Info */
skb = btbcm_read_local_version(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
ver = (struct hci_rp_read_local_version *)skb->data;
rev = le16_to_cpu(ver->hci_rev);
subver = le16_to_cpu(ver->lmp_subver);
kfree_skb(skb);
BT_INFO("%s: BCM (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
(subver & 0xe000) >> 13, (subver & 0x1f00) >> 8,
(subver & 0x00ff), rev & 0x0fff);
btbcm_check_bdaddr(hdev);
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
return 0;
}
EXPORT_SYMBOL_GPL(btbcm_finalize);
static const struct {
u16 subver;
const char *name;
} bcm_usb_subver_table[] = {
{ 0x210b, "BCM43142A0" }, /* 001.001.011 */
{ 0x2112, "BCM4314A0" }, /* 001.001.018 */
{ 0x2118, "BCM20702A0" }, /* 001.001.024 */
{ 0x2126, "BCM4335A0" }, /* 001.001.038 */
{ 0x220e, "BCM20702A1" }, /* 001.002.014 */
{ 0x230f, "BCM4354A2" }, /* 001.003.015 */
{ 0x4106, "BCM4335B0" }, /* 002.001.006 */
{ 0x410e, "BCM20702B0" }, /* 002.001.014 */
{ 0x6109, "BCM4335C0" }, /* 003.001.009 */
{ 0x610c, "BCM4354" }, /* 003.001.012 */
{ }
};
int btbcm_setup_patchram(struct hci_dev *hdev)
{
char fw_name[64];
const struct firmware *fw;
u16 subver, rev, pid, vid;
const char *hw_name = NULL;
struct sk_buff *skb;
struct hci_rp_read_local_version *ver;
int i, err;
/* Reset */
err = btbcm_reset(hdev);
if (err)
return err;
/* Read Local Version Info */
skb = btbcm_read_local_version(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
ver = (struct hci_rp_read_local_version *)skb->data;
rev = le16_to_cpu(ver->hci_rev);
subver = le16_to_cpu(ver->lmp_subver);
kfree_skb(skb);
/* Read Verbose Config Version Info */
skb = btbcm_read_verbose_config(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
kfree_skb(skb);
/* Read Local Name */
skb = btbcm_read_local_name(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
kfree_skb(skb);
switch ((rev & 0xf000) >> 12) {
case 0:
case 3:
for (i = 0; bcm_uart_subver_table[i].name; i++) {
if (subver == bcm_uart_subver_table[i].subver) {
hw_name = bcm_uart_subver_table[i].name;
break;
}
}
snprintf(fw_name, sizeof(fw_name), "brcm/%s.hcd",
hw_name ? : "BCM");
break;
case 1:
case 2:
/* Read USB Product Info */
skb = btbcm_read_usb_product(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
vid = get_unaligned_le16(skb->data + 1);
pid = get_unaligned_le16(skb->data + 3);
kfree_skb(skb);
for (i = 0; bcm_usb_subver_table[i].name; i++) {
if (subver == bcm_usb_subver_table[i].subver) {
hw_name = bcm_usb_subver_table[i].name;
break;
}
}
snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd",
hw_name ? : "BCM", vid, pid);
break;
default:
return 0;
}
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
hw_name ? : "BCM", (subver & 0xe000) >> 13,
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
err = request_firmware(&fw, fw_name, &hdev->dev);
if (err < 0) {
BT_INFO("%s: BCM: Patch %s not found", hdev->name, fw_name);
goto done;
}
btbcm_patchram(hdev, fw);
release_firmware(fw);
/* Reset */
err = btbcm_reset(hdev);
if (err)
return err;
/* Read Local Version Info */
skb = btbcm_read_local_version(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
ver = (struct hci_rp_read_local_version *)skb->data;
rev = le16_to_cpu(ver->hci_rev);
subver = le16_to_cpu(ver->lmp_subver);
kfree_skb(skb);
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
hw_name ? : "BCM", (subver & 0xe000) >> 13,
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
/* Read Local Name */
skb = btbcm_read_local_name(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
kfree_skb(skb);
done:
btbcm_check_bdaddr(hdev);
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
return 0;
}
EXPORT_SYMBOL_GPL(btbcm_setup_patchram);
int btbcm_setup_apple(struct hci_dev *hdev)
{
struct sk_buff *skb;
int err;
/* Reset */
err = btbcm_reset(hdev);
if (err)
return err;
/* Read Verbose Config Version Info */
skb = btbcm_read_verbose_config(hdev);
if (!IS_ERR(skb)) {
BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name,
skb->data[1], get_unaligned_le16(skb->data + 5));
kfree_skb(skb);
}
/* Read USB Product Info */
skb = btbcm_read_usb_product(hdev);
if (!IS_ERR(skb)) {
BT_INFO("%s: BCM: product %4.4x:%4.4x", hdev->name,
get_unaligned_le16(skb->data + 1),
get_unaligned_le16(skb->data + 3));
kfree_skb(skb);
}
/* Read Local Name */
skb = btbcm_read_local_name(hdev);
if (!IS_ERR(skb)) {
BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
kfree_skb(skb);
}
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
return 0;
}
EXPORT_SYMBOL_GPL(btbcm_setup_apple);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth support for Broadcom devices ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");

117
drivers/bluetooth/btbcm.h Normal file
View File

@@ -0,0 +1,117 @@
/*
*
* Bluetooth support for Broadcom devices
*
* Copyright (C) 2015 Intel Corporation
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#define BCM_UART_CLOCK_48MHZ 0x01
#define BCM_UART_CLOCK_24MHZ 0x02
struct bcm_update_uart_baud_rate {
__le16 zero;
__le32 baud_rate;
} __packed;
struct bcm_write_uart_clock_setting {
__u8 type;
} __packed;
struct bcm_set_sleep_mode {
__u8 sleep_mode;
__u8 idle_host;
__u8 idle_dev;
__u8 bt_wake_active;
__u8 host_wake_active;
__u8 allow_host_sleep;
__u8 combine_modes;
__u8 tristate_control;
__u8 usb_auto_sleep;
__u8 usb_resume_timeout;
__u8 pulsed_host_wake;
__u8 break_to_host;
} __packed;
struct bcm_set_pcm_int_params {
__u8 routing;
__u8 rate;
__u8 frame_sync;
__u8 sync_mode;
__u8 clock_mode;
} __packed;
struct bcm_set_pcm_format_params {
__u8 lsb_first;
__u8 fill_value;
__u8 fill_method;
__u8 fill_num;
__u8 right_justify;
} __packed;
#if IS_ENABLED(CONFIG_BT_BCM)
int btbcm_check_bdaddr(struct hci_dev *hdev);
int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw);
int btbcm_setup_patchram(struct hci_dev *hdev);
int btbcm_setup_apple(struct hci_dev *hdev);
int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len);
int btbcm_finalize(struct hci_dev *hdev);
#else
static inline int btbcm_check_bdaddr(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
}
static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
return -EOPNOTSUPP;
}
static inline int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw)
{
return -EOPNOTSUPP;
}
static inline int btbcm_setup_patchram(struct hci_dev *hdev)
{
return 0;
}
static inline int btbcm_setup_apple(struct hci_dev *hdev)
{
return 0;
}
static inline int btbcm_initialize(struct hci_dev *hdev, char *fw_name,
size_t len)
{
return 0;
}
static inline int btbcm_finalize(struct hci_dev *hdev)
{
return 0;
}
#endif

577
drivers/bluetooth/btintel.c Normal file
View File

@@ -0,0 +1,577 @@
/*
*
* Bluetooth support for Intel devices
*
* Copyright (C) 2015 Intel Corporation
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/firmware.h>
#include <linux/regmap.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btintel.h"
#define VERSION "0.1"
#define BDADDR_INTEL (&(bdaddr_t) {{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}})
int btintel_check_bdaddr(struct hci_dev *hdev)
{
struct hci_rp_read_bd_addr *bda;
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
int err = PTR_ERR(skb);
BT_ERR("%s: Reading Intel device address failed (%d)",
hdev->name, err);
return err;
}
if (skb->len != sizeof(*bda)) {
BT_ERR("%s: Intel device address length mismatch", hdev->name);
kfree_skb(skb);
return -EIO;
}
bda = (struct hci_rp_read_bd_addr *)skb->data;
/* For some Intel based controllers, the default Bluetooth device
* address 00:03:19:9E:8B:00 can be found. These controllers are
* fully operational, but have the danger of duplicate addresses
* and that in turn can cause problems with Bluetooth operation.
*/
if (!bacmp(&bda->bdaddr, BDADDR_INTEL)) {
BT_ERR("%s: Found Intel default device address (%pMR)",
hdev->name, &bda->bdaddr);
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
}
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(btintel_check_bdaddr);
int btintel_enter_mfg(struct hci_dev *hdev)
{
const u8 param[] = { 0x01, 0x00 };
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Entering manufacturer mode failed (%ld)",
PTR_ERR(skb));
return PTR_ERR(skb);
}
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(btintel_enter_mfg);
int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched)
{
u8 param[] = { 0x00, 0x00 };
struct sk_buff *skb;
/* The 2nd command parameter specifies the manufacturing exit method:
* 0x00: Just disable the manufacturing mode (0x00).
* 0x01: Disable manufacturing mode and reset with patches deactivated.
* 0x02: Disable manufacturing mode and reset with patches activated.
*/
if (reset)
param[1] |= patched ? 0x02 : 0x01;
skb = __hci_cmd_sync(hdev, 0xfc11, 2, param, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Exiting manufacturer mode failed (%ld)",
PTR_ERR(skb));
return PTR_ERR(skb);
}
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(btintel_exit_mfg);
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
struct sk_buff *skb;
int err;
skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
BT_ERR("%s: Changing Intel device address failed (%d)",
hdev->name, err);
return err;
}
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(btintel_set_bdaddr);
int btintel_set_diag(struct hci_dev *hdev, bool enable)
{
struct sk_buff *skb;
u8 param[3];
int err;
if (enable) {
param[0] = 0x03;
param[1] = 0x03;
param[2] = 0x03;
} else {
param[0] = 0x00;
param[1] = 0x00;
param[2] = 0x00;
}
skb = __hci_cmd_sync(hdev, 0xfc43, 3, param, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
if (err == -ENODATA)
goto done;
BT_ERR("%s: Changing Intel diagnostic mode failed (%d)",
hdev->name, err);
return err;
}
kfree_skb(skb);
done:
btintel_set_event_mask(hdev, enable);
return 0;
}
EXPORT_SYMBOL_GPL(btintel_set_diag);
int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable)
{
int err, ret;
err = btintel_enter_mfg(hdev);
if (err)
return err;
ret = btintel_set_diag(hdev, enable);
err = btintel_exit_mfg(hdev, false, false);
if (err)
return err;
return ret;
}
EXPORT_SYMBOL_GPL(btintel_set_diag_mfg);
void btintel_hw_error(struct hci_dev *hdev, u8 code)
{
struct sk_buff *skb;
u8 type = 0x00;
BT_ERR("%s: Hardware error 0x%2.2x", hdev->name, code);
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: Reset after hardware error failed (%ld)",
hdev->name, PTR_ERR(skb));
return;
}
kfree_skb(skb);
skb = __hci_cmd_sync(hdev, 0xfc22, 1, &type, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: Retrieving Intel exception info failed (%ld)",
hdev->name, PTR_ERR(skb));
return;
}
if (skb->len != 13) {
BT_ERR("%s: Exception info size mismatch", hdev->name);
kfree_skb(skb);
return;
}
BT_ERR("%s: Exception info %s", hdev->name, (char *)(skb->data + 1));
kfree_skb(skb);
}
EXPORT_SYMBOL_GPL(btintel_hw_error);
void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
{
const char *variant;
switch (ver->fw_variant) {
case 0x06:
variant = "Bootloader";
break;
case 0x23:
variant = "Firmware";
break;
default:
return;
}
BT_INFO("%s: %s revision %u.%u build %u week %u %u", hdev->name,
variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f,
ver->fw_build_num, ver->fw_build_ww, 2000 + ver->fw_build_yy);
}
EXPORT_SYMBOL_GPL(btintel_version_info);
int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
const void *param)
{
while (plen > 0) {
struct sk_buff *skb;
u8 cmd_param[253], fragment_len = (plen > 252) ? 252 : plen;
cmd_param[0] = fragment_type;
memcpy(cmd_param + 1, param, fragment_len);
skb = __hci_cmd_sync(hdev, 0xfc09, fragment_len + 1,
cmd_param, HCI_INIT_TIMEOUT);
if (IS_ERR(skb))
return PTR_ERR(skb);
kfree_skb(skb);
plen -= fragment_len;
param += fragment_len;
}
return 0;
}
EXPORT_SYMBOL_GPL(btintel_secure_send);
int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name)
{
const struct firmware *fw;
struct sk_buff *skb;
const u8 *fw_ptr;
int err;
err = request_firmware(&fw, ddc_name, &hdev->dev);
if (err < 0) {
bt_dev_err(hdev, "Failed to load Intel DDC file %s (%d)",
ddc_name, err);
return err;
}
bt_dev_info(hdev, "Found Intel DDC parameters: %s", ddc_name);
fw_ptr = fw->data;
/* DDC file contains one or more DDC structure which has
* Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2).
*/
while (fw->size > fw_ptr - fw->data) {
u8 cmd_plen = fw_ptr[0] + sizeof(u8);
skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Failed to send Intel_Write_DDC (%ld)",
PTR_ERR(skb));
release_firmware(fw);
return PTR_ERR(skb);
}
fw_ptr += cmd_plen;
kfree_skb(skb);
}
release_firmware(fw);
bt_dev_info(hdev, "Applying Intel DDC parameters completed");
return 0;
}
EXPORT_SYMBOL_GPL(btintel_load_ddc_config);
int btintel_set_event_mask(struct hci_dev *hdev, bool debug)
{
u8 mask[8] = { 0x87, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
struct sk_buff *skb;
int err;
if (debug)
mask[1] |= 0x62;
skb = __hci_cmd_sync(hdev, 0xfc52, 8, mask, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
BT_ERR("%s: Setting Intel event mask failed (%d)",
hdev->name, err);
return err;
}
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(btintel_set_event_mask);
int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
{
int err, ret;
err = btintel_enter_mfg(hdev);
if (err)
return err;
ret = btintel_set_event_mask(hdev, debug);
err = btintel_exit_mfg(hdev, false, false);
if (err)
return err;
return ret;
}
EXPORT_SYMBOL_GPL(btintel_set_event_mask_mfg);
int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver)
{
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Reading Intel version information failed (%ld)",
PTR_ERR(skb));
return PTR_ERR(skb);
}
if (skb->len != sizeof(*ver)) {
bt_dev_err(hdev, "Intel version event size mismatch");
kfree_skb(skb);
return -EILSEQ;
}
memcpy(ver, skb->data, sizeof(*ver));
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(btintel_read_version);
/* ------- REGMAP IBT SUPPORT ------- */
#define IBT_REG_MODE_8BIT 0x00
#define IBT_REG_MODE_16BIT 0x01
#define IBT_REG_MODE_32BIT 0x02
struct regmap_ibt_context {
struct hci_dev *hdev;
__u16 op_write;
__u16 op_read;
};
struct ibt_cp_reg_access {
__le32 addr;
__u8 mode;
__u8 len;
__u8 data[0];
} __packed;
struct ibt_rp_reg_access {
__u8 status;
__le32 addr;
__u8 data[0];
} __packed;
static int regmap_ibt_read(void *context, const void *addr, size_t reg_size,
void *val, size_t val_size)
{
struct regmap_ibt_context *ctx = context;
struct ibt_cp_reg_access cp;
struct ibt_rp_reg_access *rp;
struct sk_buff *skb;
int err = 0;
if (reg_size != sizeof(__le32))
return -EINVAL;
switch (val_size) {
case 1:
cp.mode = IBT_REG_MODE_8BIT;
break;
case 2:
cp.mode = IBT_REG_MODE_16BIT;
break;
case 4:
cp.mode = IBT_REG_MODE_32BIT;
break;
default:
return -EINVAL;
}
/* regmap provides a little-endian formatted addr */
cp.addr = *(__le32 *)addr;
cp.len = val_size;
bt_dev_dbg(ctx->hdev, "Register (0x%x) read", le32_to_cpu(cp.addr));
skb = hci_cmd_sync(ctx->hdev, ctx->op_read, sizeof(cp), &cp,
HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error (%d)",
le32_to_cpu(cp.addr), err);
return err;
}
if (skb->len != sizeof(*rp) + val_size) {
bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error, bad len",
le32_to_cpu(cp.addr));
err = -EINVAL;
goto done;
}
rp = (struct ibt_rp_reg_access *)skb->data;
if (rp->addr != cp.addr) {
bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error, bad addr",
le32_to_cpu(rp->addr));
err = -EINVAL;
goto done;
}
memcpy(val, rp->data, val_size);
done:
kfree_skb(skb);
return err;
}
static int regmap_ibt_gather_write(void *context,
const void *addr, size_t reg_size,
const void *val, size_t val_size)
{
struct regmap_ibt_context *ctx = context;
struct ibt_cp_reg_access *cp;
struct sk_buff *skb;
int plen = sizeof(*cp) + val_size;
u8 mode;
int err = 0;
if (reg_size != sizeof(__le32))
return -EINVAL;
switch (val_size) {
case 1:
mode = IBT_REG_MODE_8BIT;
break;
case 2:
mode = IBT_REG_MODE_16BIT;
break;
case 4:
mode = IBT_REG_MODE_32BIT;
break;
default:
return -EINVAL;
}
cp = kmalloc(plen, GFP_KERNEL);
if (!cp)
return -ENOMEM;
/* regmap provides a little-endian formatted addr/value */
cp->addr = *(__le32 *)addr;
cp->mode = mode;
cp->len = val_size;
memcpy(&cp->data, val, val_size);
bt_dev_dbg(ctx->hdev, "Register (0x%x) write", le32_to_cpu(cp->addr));
skb = hci_cmd_sync(ctx->hdev, ctx->op_write, plen, cp, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(ctx->hdev, "regmap: Register (0x%x) write error (%d)",
le32_to_cpu(cp->addr), err);
goto done;
}
kfree_skb(skb);
done:
kfree(cp);
return err;
}
static int regmap_ibt_write(void *context, const void *data, size_t count)
{
/* data contains register+value, since we only support 32bit addr,
* minimum data size is 4 bytes.
*/
if (WARN_ONCE(count < 4, "Invalid register access"))
return -EINVAL;
return regmap_ibt_gather_write(context, data, 4, data + 4, count - 4);
}
static void regmap_ibt_free_context(void *context)
{
kfree(context);
}
static struct regmap_bus regmap_ibt = {
.read = regmap_ibt_read,
.write = regmap_ibt_write,
.gather_write = regmap_ibt_gather_write,
.free_context = regmap_ibt_free_context,
.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
};
/* Config is the same for all register regions */
static const struct regmap_config regmap_ibt_cfg = {
.name = "btintel_regmap",
.reg_bits = 32,
.val_bits = 32,
};
struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
u16 opcode_write)
{
struct regmap_ibt_context *ctx;
bt_dev_info(hdev, "regmap: Init R%x-W%x region", opcode_read,
opcode_write);
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return ERR_PTR(-ENOMEM);
ctx->op_read = opcode_read;
ctx->op_write = opcode_write;
ctx->hdev = hdev;
return regmap_init(&hdev->dev, &regmap_ibt, ctx, &regmap_ibt_cfg);
}
EXPORT_SYMBOL_GPL(btintel_regmap_init);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("intel/ibt-11-5.sfi");
MODULE_FIRMWARE("intel/ibt-11-5.ddc");

168
drivers/bluetooth/btintel.h Normal file
View File

@@ -0,0 +1,168 @@
/*
*
* Bluetooth support for Intel devices
*
* Copyright (C) 2015 Intel Corporation
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
struct intel_version {
u8 status;
u8 hw_platform;
u8 hw_variant;
u8 hw_revision;
u8 fw_variant;
u8 fw_revision;
u8 fw_build_num;
u8 fw_build_ww;
u8 fw_build_yy;
u8 fw_patch_num;
} __packed;
struct intel_boot_params {
__u8 status;
__u8 otp_format;
__u8 otp_content;
__u8 otp_patch;
__le16 dev_revid;
__u8 secure_boot;
__u8 key_from_hdr;
__u8 key_type;
__u8 otp_lock;
__u8 api_lock;
__u8 debug_lock;
bdaddr_t otp_bdaddr;
__u8 min_fw_build_nn;
__u8 min_fw_build_cw;
__u8 min_fw_build_yy;
__u8 limited_cce;
__u8 unlocked_state;
} __packed;
struct intel_bootup {
__u8 zero;
__u8 num_cmds;
__u8 source;
__u8 reset_type;
__u8 reset_reason;
__u8 ddc_status;
} __packed;
struct intel_secure_send_result {
__u8 result;
__le16 opcode;
__u8 status;
} __packed;
#if IS_ENABLED(CONFIG_BT_INTEL)
int btintel_check_bdaddr(struct hci_dev *hdev);
int btintel_enter_mfg(struct hci_dev *hdev);
int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched);
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int btintel_set_diag(struct hci_dev *hdev, bool enable);
int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable);
void btintel_hw_error(struct hci_dev *hdev, u8 code);
void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver);
int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
const void *param);
int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name);
int btintel_set_event_mask(struct hci_dev *hdev, bool debug);
int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug);
int btintel_read_version(struct hci_dev *hdev, struct intel_version *ver);
struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
u16 opcode_write);
#else
static inline int btintel_check_bdaddr(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
}
static inline int btintel_enter_mfg(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
}
static inline int btintel_exit_mfg(struct hci_dev *hdev, bool reset, bool patched)
{
return -EOPNOTSUPP;
}
static inline int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
return -EOPNOTSUPP;
}
static inline int btintel_set_diag(struct hci_dev *hdev, bool enable)
{
return -EOPNOTSUPP;
}
static inline int btintel_set_diag_mfg(struct hci_dev *hdev, bool enable)
{
return -EOPNOTSUPP;
}
static inline void btintel_hw_error(struct hci_dev *hdev, u8 code)
{
}
static inline void btintel_version_info(struct hci_dev *hdev,
struct intel_version *ver)
{
}
static inline int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type,
u32 plen, const void *param)
{
return -EOPNOTSUPP;
}
static inline int btintel_load_ddc_config(struct hci_dev *hdev,
const char *ddc_name)
{
return -EOPNOTSUPP;
}
static inline int btintel_set_event_mask(struct hci_dev *hdev, bool debug)
{
return -EOPNOTSUPP;
}
static inline int btintel_set_event_mask_mfg(struct hci_dev *hdev, bool debug)
{
return -EOPNOTSUPP;
}
static inline int btintel_read_version(struct hci_dev *hdev,
struct intel_version *ver)
{
return -EOPNOTSUPP;
}
static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev,
u16 opcode_read,
u16 opcode_write)
{
return ERR_PTR(-EINVAL);
}
#endif

View File

@@ -0,0 +1,255 @@
/**
* Marvell Bluetooth driver: debugfs related functions
*
* Copyright (C) 2009, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
**/
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btmrvl_drv.h"
struct btmrvl_debugfs_data {
struct dentry *config_dir;
struct dentry *status_dir;
};
static ssize_t btmrvl_hscfgcmd_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
long result, ret;
memset(buf, 0, sizeof(buf));
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
ret = kstrtol(buf, 10, &result);
if (ret)
return ret;
priv->btmrvl_dev.hscfgcmd = result;
if (priv->btmrvl_dev.hscfgcmd) {
btmrvl_prepare_command(priv);
wake_up_interruptible(&priv->main_thread.wait_q);
}
return count;
}
static ssize_t btmrvl_hscfgcmd_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
int ret;
ret = snprintf(buf, sizeof(buf) - 1, "%d\n",
priv->btmrvl_dev.hscfgcmd);
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
}
static const struct file_operations btmrvl_hscfgcmd_fops = {
.read = btmrvl_hscfgcmd_read,
.write = btmrvl_hscfgcmd_write,
.open = simple_open,
.llseek = default_llseek,
};
static ssize_t btmrvl_pscmd_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
long result, ret;
memset(buf, 0, sizeof(buf));
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
ret = kstrtol(buf, 10, &result);
if (ret)
return ret;
priv->btmrvl_dev.pscmd = result;
if (priv->btmrvl_dev.pscmd) {
btmrvl_prepare_command(priv);
wake_up_interruptible(&priv->main_thread.wait_q);
}
return count;
}
static ssize_t btmrvl_pscmd_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
int ret;
ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.pscmd);
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
}
static const struct file_operations btmrvl_pscmd_fops = {
.read = btmrvl_pscmd_read,
.write = btmrvl_pscmd_write,
.open = simple_open,
.llseek = default_llseek,
};
static ssize_t btmrvl_hscmd_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
long result, ret;
memset(buf, 0, sizeof(buf));
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
ret = kstrtol(buf, 10, &result);
if (ret)
return ret;
priv->btmrvl_dev.hscmd = result;
if (priv->btmrvl_dev.hscmd) {
btmrvl_prepare_command(priv);
wake_up_interruptible(&priv->main_thread.wait_q);
}
return count;
}
static ssize_t btmrvl_hscmd_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
int ret;
ret = snprintf(buf, sizeof(buf) - 1, "%d\n", priv->btmrvl_dev.hscmd);
return simple_read_from_buffer(userbuf, count, ppos, buf, ret);
}
static const struct file_operations btmrvl_hscmd_fops = {
.read = btmrvl_hscmd_read,
.write = btmrvl_hscmd_write,
.open = simple_open,
.llseek = default_llseek,
};
static ssize_t btmrvl_fwdump_write(struct file *file, const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct btmrvl_private *priv = file->private_data;
char buf[16];
bool result;
memset(buf, 0, sizeof(buf));
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (strtobool(buf, &result))
return -EINVAL;
if (!result)
return -EINVAL;
btmrvl_firmware_dump(priv);
return count;
}
static const struct file_operations btmrvl_fwdump_fops = {
.write = btmrvl_fwdump_write,
.open = simple_open,
.llseek = default_llseek,
};
void btmrvl_debugfs_init(struct hci_dev *hdev)
{
struct btmrvl_private *priv = hci_get_drvdata(hdev);
struct btmrvl_debugfs_data *dbg;
if (!hdev->debugfs)
return;
dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
priv->debugfs_data = dbg;
if (!dbg) {
BT_ERR("Can not allocate memory for btmrvl_debugfs_data.");
return;
}
dbg->config_dir = debugfs_create_dir("config", hdev->debugfs);
debugfs_create_u8("psmode", 0644, dbg->config_dir,
&priv->btmrvl_dev.psmode);
debugfs_create_file("pscmd", 0644, dbg->config_dir,
priv, &btmrvl_pscmd_fops);
debugfs_create_x16("gpiogap", 0644, dbg->config_dir,
&priv->btmrvl_dev.gpio_gap);
debugfs_create_u8("hsmode", 0644, dbg->config_dir,
&priv->btmrvl_dev.hsmode);
debugfs_create_file("hscmd", 0644, dbg->config_dir,
priv, &btmrvl_hscmd_fops);
debugfs_create_file("hscfgcmd", 0644, dbg->config_dir,
priv, &btmrvl_hscfgcmd_fops);
debugfs_create_file("fw_dump", 0200, dbg->config_dir,
priv, &btmrvl_fwdump_fops);
dbg->status_dir = debugfs_create_dir("status", hdev->debugfs);
debugfs_create_u8("curpsmode", 0444, dbg->status_dir,
&priv->adapter->psmode);
debugfs_create_u8("psstate", 0444, dbg->status_dir,
&priv->adapter->ps_state);
debugfs_create_u8("hsstate", 0444, dbg->status_dir,
&priv->adapter->hs_state);
debugfs_create_u8("txdnldready", 0444, dbg->status_dir,
&priv->btmrvl_dev.tx_dnld_rdy);
}
void btmrvl_debugfs_remove(struct hci_dev *hdev)
{
struct btmrvl_private *priv = hci_get_drvdata(hdev);
struct btmrvl_debugfs_data *dbg = priv->debugfs_data;
if (!dbg)
return;
debugfs_remove_recursive(dbg->config_dir);
debugfs_remove_recursive(dbg->status_dir);
kfree(dbg);
}

View File

@@ -0,0 +1,192 @@
/*
* Marvell Bluetooth driver: global definitions & declarations
*
* Copyright (C) 2009, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
#include <linux/kthread.h>
#include <linux/bitops.h>
#include <linux/slab.h>
#include <net/bluetooth/bluetooth.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/gfp.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/of_irq.h>
#define BTM_HEADER_LEN 4
#define BTM_UPLD_SIZE 2312
/* Time to wait until Host Sleep state change in millisecond */
#define WAIT_UNTIL_HS_STATE_CHANGED msecs_to_jiffies(5000)
/* Time to wait for command response in millisecond */
#define WAIT_UNTIL_CMD_RESP msecs_to_jiffies(5000)
enum rdwr_status {
RDWR_STATUS_SUCCESS = 0,
RDWR_STATUS_FAILURE = 1,
RDWR_STATUS_DONE = 2
};
#define FW_DUMP_MAX_NAME_LEN 8
#define FW_DUMP_HOST_READY 0xEE
#define FW_DUMP_DONE 0xFF
#define FW_DUMP_READ_DONE 0xFE
struct memory_type_mapping {
u8 mem_name[FW_DUMP_MAX_NAME_LEN];
u8 *mem_ptr;
u32 mem_size;
u8 done_flag;
};
struct btmrvl_thread {
struct task_struct *task;
wait_queue_head_t wait_q;
void *priv;
};
struct btmrvl_device {
void *card;
struct hci_dev *hcidev;
u8 dev_type;
u8 tx_dnld_rdy;
u8 psmode;
u8 pscmd;
u8 hsmode;
u8 hscmd;
/* Low byte is gap, high byte is GPIO */
u16 gpio_gap;
u8 hscfgcmd;
u8 sendcmdflag;
};
struct btmrvl_adapter {
void *hw_regs_buf;
u8 *hw_regs;
u32 int_count;
struct sk_buff_head tx_queue;
u8 psmode;
u8 ps_state;
u8 hs_state;
u8 wakeup_tries;
wait_queue_head_t cmd_wait_q;
wait_queue_head_t event_hs_wait_q;
u8 cmd_complete;
bool is_suspended;
bool is_suspending;
};
struct btmrvl_private {
struct btmrvl_device btmrvl_dev;
struct btmrvl_adapter *adapter;
struct btmrvl_thread main_thread;
int (*hw_host_to_card)(struct btmrvl_private *priv,
u8 *payload, u16 nb);
int (*hw_wakeup_firmware)(struct btmrvl_private *priv);
int (*hw_process_int_status)(struct btmrvl_private *priv);
void (*firmware_dump)(struct btmrvl_private *priv);
spinlock_t driver_lock; /* spinlock used by driver */
#ifdef CONFIG_DEBUG_FS
void *debugfs_data;
#endif
bool surprise_removed;
};
#define MRVL_VENDOR_PKT 0xFE
/* Vendor specific Bluetooth commands */
#define BT_CMD_PSCAN_WIN_REPORT_ENABLE 0xFC03
#define BT_CMD_ROUTE_SCO_TO_HOST 0xFC1D
#define BT_CMD_SET_BDADDR 0xFC22
#define BT_CMD_AUTO_SLEEP_MODE 0xFC23
#define BT_CMD_HOST_SLEEP_CONFIG 0xFC59
#define BT_CMD_HOST_SLEEP_ENABLE 0xFC5A
#define BT_CMD_MODULE_CFG_REQ 0xFC5B
#define BT_CMD_LOAD_CONFIG_DATA 0xFC61
/* Sub-commands: Module Bringup/Shutdown Request/Response */
#define MODULE_BRINGUP_REQ 0xF1
#define MODULE_BROUGHT_UP 0x00
#define MODULE_ALREADY_UP 0x0C
#define MODULE_SHUTDOWN_REQ 0xF2
/* Vendor specific Bluetooth events */
#define BT_EVENT_AUTO_SLEEP_MODE 0x23
#define BT_EVENT_HOST_SLEEP_CONFIG 0x59
#define BT_EVENT_HOST_SLEEP_ENABLE 0x5A
#define BT_EVENT_MODULE_CFG_REQ 0x5B
#define BT_EVENT_POWER_STATE 0x20
/* Bluetooth Power States */
#define BT_PS_ENABLE 0x02
#define BT_PS_DISABLE 0x03
#define BT_PS_SLEEP 0x01
/* Host Sleep states */
#define HS_ACTIVATED 0x01
#define HS_DEACTIVATED 0x00
/* Power Save modes */
#define PS_SLEEP 0x01
#define PS_AWAKE 0x00
#define BT_CAL_HDR_LEN 4
#define BT_CAL_DATA_SIZE 28
struct btmrvl_event {
u8 ec; /* event counter */
u8 length;
u8 data[4];
} __packed;
/* Prototype of global function */
int btmrvl_register_hdev(struct btmrvl_private *priv);
struct btmrvl_private *btmrvl_add_card(void *card);
int btmrvl_remove_card(struct btmrvl_private *priv);
void btmrvl_interrupt(struct btmrvl_private *priv);
bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb);
int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb);
int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd);
int btmrvl_pscan_window_reporting(struct btmrvl_private *priv, u8 subcmd);
int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv);
int btmrvl_enable_ps(struct btmrvl_private *priv);
int btmrvl_prepare_command(struct btmrvl_private *priv);
int btmrvl_enable_hs(struct btmrvl_private *priv);
void btmrvl_firmware_dump(struct btmrvl_private *priv);
#ifdef CONFIG_DEBUG_FS
void btmrvl_debugfs_init(struct hci_dev *hdev);
void btmrvl_debugfs_remove(struct hci_dev *hdev);
#endif

View File

@@ -0,0 +1,801 @@
/**
* Marvell Bluetooth driver
*
* Copyright (C) 2009, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
**/
#include <linux/module.h>
#include <linux/of.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <linux/mmc/sdio_func.h>
#include "btmrvl_drv.h"
#include "btmrvl_sdio.h"
#define VERSION "1.0"
/*
* This function is called by interface specific interrupt handler.
* It updates Power Save & Host Sleep states, and wakes up the main
* thread.
*/
void btmrvl_interrupt(struct btmrvl_private *priv)
{
priv->adapter->ps_state = PS_AWAKE;
priv->adapter->wakeup_tries = 0;
priv->adapter->int_count++;
if (priv->adapter->hs_state == HS_ACTIVATED) {
BT_DBG("BT: HS DEACTIVATED in ISR!");
priv->adapter->hs_state = HS_DEACTIVATED;
}
wake_up_interruptible(&priv->main_thread.wait_q);
}
EXPORT_SYMBOL_GPL(btmrvl_interrupt);
bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb)
{
struct hci_event_hdr *hdr = (void *) skb->data;
if (hdr->evt == HCI_EV_CMD_COMPLETE) {
struct hci_ev_cmd_complete *ec;
u16 opcode;
ec = (void *) (skb->data + HCI_EVENT_HDR_SIZE);
opcode = __le16_to_cpu(ec->opcode);
if (priv->btmrvl_dev.sendcmdflag) {
priv->btmrvl_dev.sendcmdflag = false;
priv->adapter->cmd_complete = true;
wake_up_interruptible(&priv->adapter->cmd_wait_q);
if (hci_opcode_ogf(opcode) == 0x3F) {
BT_DBG("vendor event skipped: opcode=%#4.4x",
opcode);
kfree_skb(skb);
return false;
}
}
}
return true;
}
EXPORT_SYMBOL_GPL(btmrvl_check_evtpkt);
int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb)
{
struct btmrvl_adapter *adapter = priv->adapter;
struct btmrvl_event *event;
int ret = 0;
event = (struct btmrvl_event *) skb->data;
if (event->ec != 0xff) {
BT_DBG("Not Marvell Event=%x", event->ec);
ret = -EINVAL;
goto exit;
}
switch (event->data[0]) {
case BT_EVENT_AUTO_SLEEP_MODE:
if (!event->data[2]) {
if (event->data[1] == BT_PS_ENABLE)
adapter->psmode = 1;
else
adapter->psmode = 0;
BT_DBG("PS Mode:%s",
(adapter->psmode) ? "Enable" : "Disable");
} else {
BT_DBG("PS Mode command failed");
}
break;
case BT_EVENT_HOST_SLEEP_CONFIG:
if (!event->data[3])
BT_DBG("gpio=%x, gap=%x", event->data[1],
event->data[2]);
else
BT_DBG("HSCFG command failed");
break;
case BT_EVENT_HOST_SLEEP_ENABLE:
if (!event->data[1]) {
adapter->hs_state = HS_ACTIVATED;
if (adapter->psmode)
adapter->ps_state = PS_SLEEP;
wake_up_interruptible(&adapter->event_hs_wait_q);
BT_DBG("HS ACTIVATED!");
} else {
BT_DBG("HS Enable failed");
}
break;
case BT_EVENT_MODULE_CFG_REQ:
if (priv->btmrvl_dev.sendcmdflag &&
event->data[1] == MODULE_BRINGUP_REQ) {
BT_DBG("EVENT:%s",
((event->data[2] == MODULE_BROUGHT_UP) ||
(event->data[2] == MODULE_ALREADY_UP)) ?
"Bring-up succeed" : "Bring-up failed");
if (event->length > 3 && event->data[3])
priv->btmrvl_dev.dev_type = HCI_AMP;
else
priv->btmrvl_dev.dev_type = HCI_PRIMARY;
BT_DBG("dev_type: %d", priv->btmrvl_dev.dev_type);
} else if (priv->btmrvl_dev.sendcmdflag &&
event->data[1] == MODULE_SHUTDOWN_REQ) {
BT_DBG("EVENT:%s", (event->data[2]) ?
"Shutdown failed" : "Shutdown succeed");
} else {
BT_DBG("BT_CMD_MODULE_CFG_REQ resp for APP");
ret = -EINVAL;
}
break;
case BT_EVENT_POWER_STATE:
if (event->data[1] == BT_PS_SLEEP)
adapter->ps_state = PS_SLEEP;
BT_DBG("EVENT:%s",
(adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE");
break;
default:
BT_DBG("Unknown Event=%d", event->data[0]);
ret = -EINVAL;
break;
}
exit:
if (!ret)
kfree_skb(skb);
return ret;
}
EXPORT_SYMBOL_GPL(btmrvl_process_event);
static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode,
const void *param, u8 len)
{
struct sk_buff *skb;
struct hci_command_hdr *hdr;
if (priv->surprise_removed) {
BT_ERR("Card is removed");
return -EFAULT;
}
skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC);
if (!skb) {
BT_ERR("No free skb");
return -ENOMEM;
}
hdr = (struct hci_command_hdr *)skb_put(skb, HCI_COMMAND_HDR_SIZE);
hdr->opcode = cpu_to_le16(opcode);
hdr->plen = len;
if (len)
memcpy(skb_put(skb, len), param, len);
hci_skb_pkt_type(skb) = MRVL_VENDOR_PKT;
skb_queue_head(&priv->adapter->tx_queue, skb);
priv->btmrvl_dev.sendcmdflag = true;
priv->adapter->cmd_complete = false;
wake_up_interruptible(&priv->main_thread.wait_q);
if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q,
priv->adapter->cmd_complete ||
priv->surprise_removed,
WAIT_UNTIL_CMD_RESP))
return -ETIMEDOUT;
if (priv->surprise_removed)
return -EFAULT;
return 0;
}
int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd)
{
int ret;
ret = btmrvl_send_sync_cmd(priv, BT_CMD_MODULE_CFG_REQ, &subcmd, 1);
if (ret)
BT_ERR("module_cfg_cmd(%x) failed", subcmd);
return ret;
}
EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);
static int btmrvl_enable_sco_routing_to_host(struct btmrvl_private *priv)
{
int ret;
u8 subcmd = 0;
ret = btmrvl_send_sync_cmd(priv, BT_CMD_ROUTE_SCO_TO_HOST, &subcmd, 1);
if (ret)
BT_ERR("BT_CMD_ROUTE_SCO_TO_HOST command failed: %#x", ret);
return ret;
}
int btmrvl_pscan_window_reporting(struct btmrvl_private *priv, u8 subcmd)
{
struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
int ret;
if (!card->support_pscan_win_report)
return 0;
ret = btmrvl_send_sync_cmd(priv, BT_CMD_PSCAN_WIN_REPORT_ENABLE,
&subcmd, 1);
if (ret)
BT_ERR("PSCAN_WIN_REPORT_ENABLE command failed: %#x", ret);
return ret;
}
EXPORT_SYMBOL_GPL(btmrvl_pscan_window_reporting);
int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv)
{
int ret;
u8 param[2];
param[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8;
param[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff);
BT_DBG("Sending HSCFG Command, gpio=0x%x, gap=0x%x",
param[0], param[1]);
ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_CONFIG, param, 2);
if (ret)
BT_ERR("HSCFG command failed");
return ret;
}
EXPORT_SYMBOL_GPL(btmrvl_send_hscfg_cmd);
int btmrvl_enable_ps(struct btmrvl_private *priv)
{
int ret;
u8 param;
if (priv->btmrvl_dev.psmode)
param = BT_PS_ENABLE;
else
param = BT_PS_DISABLE;
ret = btmrvl_send_sync_cmd(priv, BT_CMD_AUTO_SLEEP_MODE, &param, 1);
if (ret)
BT_ERR("PSMODE command failed");
return 0;
}
EXPORT_SYMBOL_GPL(btmrvl_enable_ps);
int btmrvl_enable_hs(struct btmrvl_private *priv)
{
struct btmrvl_adapter *adapter = priv->adapter;
int ret;
ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0);
if (ret) {
BT_ERR("Host sleep enable command failed");
return ret;
}
ret = wait_event_interruptible_timeout(adapter->event_hs_wait_q,
adapter->hs_state ||
priv->surprise_removed,
WAIT_UNTIL_HS_STATE_CHANGED);
if (ret < 0 || priv->surprise_removed) {
BT_ERR("event_hs_wait_q terminated (%d): %d,%d,%d",
ret, adapter->hs_state, adapter->ps_state,
adapter->wakeup_tries);
} else if (!ret) {
BT_ERR("hs_enable timeout: %d,%d,%d", adapter->hs_state,
adapter->ps_state, adapter->wakeup_tries);
ret = -ETIMEDOUT;
} else {
BT_DBG("host sleep enabled: %d,%d,%d", adapter->hs_state,
adapter->ps_state, adapter->wakeup_tries);
ret = 0;
}
return ret;
}
EXPORT_SYMBOL_GPL(btmrvl_enable_hs);
int btmrvl_prepare_command(struct btmrvl_private *priv)
{
int ret = 0;
if (priv->btmrvl_dev.hscfgcmd) {
priv->btmrvl_dev.hscfgcmd = 0;
btmrvl_send_hscfg_cmd(priv);
}
if (priv->btmrvl_dev.pscmd) {
priv->btmrvl_dev.pscmd = 0;
btmrvl_enable_ps(priv);
}
if (priv->btmrvl_dev.hscmd) {
priv->btmrvl_dev.hscmd = 0;
if (priv->btmrvl_dev.hsmode) {
ret = btmrvl_enable_hs(priv);
} else {
ret = priv->hw_wakeup_firmware(priv);
priv->adapter->hs_state = HS_DEACTIVATED;
BT_DBG("BT: HS DEACTIVATED due to host activity!");
}
}
return ret;
}
void btmrvl_firmware_dump(struct btmrvl_private *priv)
{
if (priv->firmware_dump)
priv->firmware_dump(priv);
}
static int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb)
{
int ret = 0;
if (!skb || !skb->data)
return -EINVAL;
if (!skb->len || ((skb->len + BTM_HEADER_LEN) > BTM_UPLD_SIZE)) {
BT_ERR("Tx Error: Bad skb length %d : %d",
skb->len, BTM_UPLD_SIZE);
return -EINVAL;
}
skb_push(skb, BTM_HEADER_LEN);
/* header type: byte[3]
* HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor
* header length: byte[2][1][0]
*/
skb->data[0] = (skb->len & 0x0000ff);
skb->data[1] = (skb->len & 0x00ff00) >> 8;
skb->data[2] = (skb->len & 0xff0000) >> 16;
skb->data[3] = hci_skb_pkt_type(skb);
if (priv->hw_host_to_card)
ret = priv->hw_host_to_card(priv, skb->data, skb->len);
return ret;
}
static void btmrvl_init_adapter(struct btmrvl_private *priv)
{
int buf_size;
skb_queue_head_init(&priv->adapter->tx_queue);
priv->adapter->ps_state = PS_AWAKE;
buf_size = ALIGN_SZ(SDIO_BLOCK_SIZE, BTSDIO_DMA_ALIGN);
priv->adapter->hw_regs_buf = kzalloc(buf_size, GFP_KERNEL);
if (!priv->adapter->hw_regs_buf) {
priv->adapter->hw_regs = NULL;
BT_ERR("Unable to allocate buffer for hw_regs.");
} else {
priv->adapter->hw_regs =
(u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf,
BTSDIO_DMA_ALIGN);
BT_DBG("hw_regs_buf=%p hw_regs=%p",
priv->adapter->hw_regs_buf, priv->adapter->hw_regs);
}
init_waitqueue_head(&priv->adapter->cmd_wait_q);
init_waitqueue_head(&priv->adapter->event_hs_wait_q);
}
static void btmrvl_free_adapter(struct btmrvl_private *priv)
{
skb_queue_purge(&priv->adapter->tx_queue);
kfree(priv->adapter->hw_regs_buf);
kfree(priv->adapter);
priv->adapter = NULL;
}
static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btmrvl_private *priv = hci_get_drvdata(hdev);
if (priv->adapter->is_suspending || priv->adapter->is_suspended) {
BT_ERR("%s: Device is suspending or suspended", __func__);
return -EBUSY;
}
BT_DBG("type=%d, len=%d", hci_skb_pkt_type(skb), skb->len);
if (priv->adapter->is_suspending || priv->adapter->is_suspended) {
BT_ERR("%s: Device is suspending or suspended", __func__);
return -EBUSY;
}
switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
break;
}
skb_queue_tail(&priv->adapter->tx_queue, skb);
if (!priv->adapter->is_suspended)
wake_up_interruptible(&priv->main_thread.wait_q);
return 0;
}
static int btmrvl_flush(struct hci_dev *hdev)
{
struct btmrvl_private *priv = hci_get_drvdata(hdev);
skb_queue_purge(&priv->adapter->tx_queue);
return 0;
}
static int btmrvl_close(struct hci_dev *hdev)
{
struct btmrvl_private *priv = hci_get_drvdata(hdev);
skb_queue_purge(&priv->adapter->tx_queue);
return 0;
}
static int btmrvl_open(struct hci_dev *hdev)
{
return 0;
}
static int btmrvl_download_cal_data(struct btmrvl_private *priv,
u8 *data, int len)
{
int ret;
data[0] = 0x00;
data[1] = 0x00;
data[2] = 0x00;
data[3] = len;
print_hex_dump_bytes("Calibration data: ",
DUMP_PREFIX_OFFSET, data, BT_CAL_HDR_LEN + len);
ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data,
BT_CAL_HDR_LEN + len);
if (ret)
BT_ERR("Failed to download caibration data");
return 0;
}
static int btmrvl_check_device_tree(struct btmrvl_private *priv)
{
struct device_node *dt_node;
struct btmrvl_sdio_card *card = priv->btmrvl_dev.card;
u8 cal_data[BT_CAL_HDR_LEN + BT_CAL_DATA_SIZE];
int ret = 0;
u16 gpio, gap;
if (card->plt_of_node) {
dt_node = card->plt_of_node;
ret = of_property_read_u16(dt_node, "marvell,wakeup-pin",
&gpio);
if (ret)
gpio = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8;
ret = of_property_read_u16(dt_node, "marvell,wakeup-gap-ms",
&gap);
if (ret)
gap = (u8)(priv->btmrvl_dev.gpio_gap & 0x00ff);
priv->btmrvl_dev.gpio_gap = (gpio << 8) + gap;
ret = of_property_read_u8_array(dt_node, "marvell,cal-data",
cal_data + BT_CAL_HDR_LEN,
BT_CAL_DATA_SIZE);
if (ret)
return ret;
BT_DBG("Use cal data from device tree");
ret = btmrvl_download_cal_data(priv, cal_data,
BT_CAL_DATA_SIZE);
if (ret)
BT_ERR("Fail to download calibrate data");
}
return ret;
}
static int btmrvl_setup(struct hci_dev *hdev)
{
struct btmrvl_private *priv = hci_get_drvdata(hdev);
int ret;
ret = btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
if (ret)
return ret;
priv->btmrvl_dev.gpio_gap = 0xfffe;
btmrvl_check_device_tree(priv);
btmrvl_enable_sco_routing_to_host(priv);
btmrvl_pscan_window_reporting(priv, 0x01);
priv->btmrvl_dev.psmode = 1;
btmrvl_enable_ps(priv);
btmrvl_send_hscfg_cmd(priv);
return 0;
}
static int btmrvl_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
struct sk_buff *skb;
long ret;
u8 buf[8];
buf[0] = MRVL_VENDOR_PKT;
buf[1] = sizeof(bdaddr_t);
memcpy(buf + 2, bdaddr, sizeof(bdaddr_t));
skb = __hci_cmd_sync(hdev, BT_CMD_SET_BDADDR, sizeof(buf), buf,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
BT_ERR("%s: changing btmrvl device address failed (%ld)",
hdev->name, ret);
return ret;
}
kfree_skb(skb);
return 0;
}
/*
* This function handles the event generated by firmware, rx data
* received from firmware, and tx data sent from kernel.
*/
static int btmrvl_service_main_thread(void *data)
{
struct btmrvl_thread *thread = data;
struct btmrvl_private *priv = thread->priv;
struct btmrvl_adapter *adapter = priv->adapter;
wait_queue_t wait;
struct sk_buff *skb;
ulong flags;
init_waitqueue_entry(&wait, current);
for (;;) {
add_wait_queue(&thread->wait_q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop() || priv->surprise_removed) {
BT_DBG("main_thread: break from main thread");
break;
}
if (adapter->wakeup_tries ||
((!adapter->int_count) &&
(!priv->btmrvl_dev.tx_dnld_rdy ||
skb_queue_empty(&adapter->tx_queue)))) {
BT_DBG("main_thread is sleeping...");
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&thread->wait_q, &wait);
BT_DBG("main_thread woke up");
if (kthread_should_stop() || priv->surprise_removed) {
BT_DBG("main_thread: break from main thread");
break;
}
spin_lock_irqsave(&priv->driver_lock, flags);
if (adapter->int_count) {
adapter->int_count = 0;
spin_unlock_irqrestore(&priv->driver_lock, flags);
priv->hw_process_int_status(priv);
} else if (adapter->ps_state == PS_SLEEP &&
!skb_queue_empty(&adapter->tx_queue)) {
spin_unlock_irqrestore(&priv->driver_lock, flags);
adapter->wakeup_tries++;
priv->hw_wakeup_firmware(priv);
continue;
} else {
spin_unlock_irqrestore(&priv->driver_lock, flags);
}
if (adapter->ps_state == PS_SLEEP)
continue;
if (!priv->btmrvl_dev.tx_dnld_rdy ||
priv->adapter->is_suspended)
continue;
skb = skb_dequeue(&adapter->tx_queue);
if (skb) {
if (btmrvl_tx_pkt(priv, skb))
priv->btmrvl_dev.hcidev->stat.err_tx++;
else
priv->btmrvl_dev.hcidev->stat.byte_tx += skb->len;
kfree_skb(skb);
}
}
return 0;
}
int btmrvl_register_hdev(struct btmrvl_private *priv)
{
struct hci_dev *hdev = NULL;
int ret;
hdev = hci_alloc_dev();
if (!hdev) {
BT_ERR("Can not allocate HCI device");
goto err_hdev;
}
priv->btmrvl_dev.hcidev = hdev;
hci_set_drvdata(hdev, priv);
hdev->bus = HCI_SDIO;
hdev->open = btmrvl_open;
hdev->close = btmrvl_close;
hdev->flush = btmrvl_flush;
hdev->send = btmrvl_send_frame;
hdev->setup = btmrvl_setup;
hdev->set_bdaddr = btmrvl_set_bdaddr;
hdev->dev_type = priv->btmrvl_dev.dev_type;
ret = hci_register_dev(hdev);
if (ret < 0) {
BT_ERR("Can not register HCI device");
goto err_hci_register_dev;
}
#ifdef CONFIG_DEBUG_FS
btmrvl_debugfs_init(hdev);
#endif
return 0;
err_hci_register_dev:
hci_free_dev(hdev);
err_hdev:
/* Stop the thread servicing the interrupts */
kthread_stop(priv->main_thread.task);
btmrvl_free_adapter(priv);
kfree(priv);
return -ENOMEM;
}
EXPORT_SYMBOL_GPL(btmrvl_register_hdev);
struct btmrvl_private *btmrvl_add_card(void *card)
{
struct btmrvl_private *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
BT_ERR("Can not allocate priv");
goto err_priv;
}
priv->adapter = kzalloc(sizeof(*priv->adapter), GFP_KERNEL);
if (!priv->adapter) {
BT_ERR("Allocate buffer for btmrvl_adapter failed!");
goto err_adapter;
}
btmrvl_init_adapter(priv);
BT_DBG("Starting kthread...");
priv->main_thread.priv = priv;
spin_lock_init(&priv->driver_lock);
init_waitqueue_head(&priv->main_thread.wait_q);
priv->main_thread.task = kthread_run(btmrvl_service_main_thread,
&priv->main_thread, "btmrvl_main_service");
priv->btmrvl_dev.card = card;
priv->btmrvl_dev.tx_dnld_rdy = true;
return priv;
err_adapter:
kfree(priv);
err_priv:
return NULL;
}
EXPORT_SYMBOL_GPL(btmrvl_add_card);
int btmrvl_remove_card(struct btmrvl_private *priv)
{
struct hci_dev *hdev;
hdev = priv->btmrvl_dev.hcidev;
wake_up_interruptible(&priv->adapter->cmd_wait_q);
wake_up_interruptible(&priv->adapter->event_hs_wait_q);
kthread_stop(priv->main_thread.task);
#ifdef CONFIG_DEBUG_FS
btmrvl_debugfs_remove(hdev);
#endif
hci_unregister_dev(hdev);
hci_free_dev(hdev);
priv->btmrvl_dev.hcidev = NULL;
btmrvl_free_adapter(priv);
kfree(priv);
return 0;
}
EXPORT_SYMBOL_GPL(btmrvl_remove_card);
MODULE_AUTHOR("Marvell International Ltd.");
MODULE_DESCRIPTION("Marvell Bluetooth driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,128 @@
/**
* Marvell BT-over-SDIO driver: SDIO interface related definitions
*
* Copyright (C) 2009, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
**/
#define SDIO_HEADER_LEN 4
/* SD block size can not bigger than 64 due to buf size limit in firmware */
/* define SD block size for data Tx/Rx */
#define SDIO_BLOCK_SIZE 64
/* Number of blocks for firmware transfer */
#define FIRMWARE_TRANSFER_NBLOCK 2
/* This is for firmware specific length */
#define FW_EXTRA_LEN 36
#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024)
#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \
(HCI_MAX_FRAME_SIZE + FW_EXTRA_LEN)
#define ALLOC_BUF_SIZE (((max_t (int, MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \
MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \
+ SDIO_BLOCK_SIZE - 1) / SDIO_BLOCK_SIZE) \
* SDIO_BLOCK_SIZE)
/* The number of times to try when polling for status */
#define MAX_POLL_TRIES 100
/* Max retry number of CMD53 write */
#define MAX_WRITE_IOMEM_RETRY 2
/* register bitmasks */
#define HOST_POWER_UP BIT(1)
#define HOST_CMD53_FIN BIT(2)
#define HIM_DISABLE 0xff
#define HIM_ENABLE (BIT(0) | BIT(1))
#define UP_LD_HOST_INT_STATUS BIT(0)
#define DN_LD_HOST_INT_STATUS BIT(1)
#define DN_LD_CARD_RDY BIT(0)
#define CARD_IO_READY BIT(3)
#define FIRMWARE_READY 0xfedc
struct btmrvl_plt_wake_cfg {
int irq_bt;
bool wake_by_bt;
};
struct btmrvl_sdio_card_reg {
u8 cfg;
u8 host_int_mask;
u8 host_intstatus;
u8 card_status;
u8 sq_read_base_addr_a0;
u8 sq_read_base_addr_a1;
u8 card_revision;
u8 card_fw_status0;
u8 card_fw_status1;
u8 card_rx_len;
u8 card_rx_unit;
u8 io_port_0;
u8 io_port_1;
u8 io_port_2;
bool int_read_to_clear;
u8 host_int_rsr;
u8 card_misc_cfg;
u8 fw_dump_ctrl;
u8 fw_dump_start;
u8 fw_dump_end;
};
struct btmrvl_sdio_card {
struct sdio_func *func;
u32 ioport;
const char *helper;
const char *firmware;
const struct btmrvl_sdio_card_reg *reg;
bool support_pscan_win_report;
bool supports_fw_dump;
u16 sd_blksz_fw_dl;
u8 rx_unit;
struct btmrvl_private *priv;
struct device_node *plt_of_node;
struct btmrvl_plt_wake_cfg *plt_wake_cfg;
};
struct btmrvl_sdio_device {
const char *helper;
const char *firmware;
const struct btmrvl_sdio_card_reg *reg;
const bool support_pscan_win_report;
u16 sd_blksz_fw_dl;
bool supports_fw_dump;
};
/* Platform specific DMA alignment */
#define BTSDIO_DMA_ALIGN 8
/* Macros for Data Alignment : size */
#define ALIGN_SZ(p, a) \
(((p) + ((a) - 1)) & ~((a) - 1))
/* Macros for Data Alignment : address */
#define ALIGN_ADDR(p, a) \
((((unsigned long)(p)) + (((unsigned long)(a)) - 1)) & \
~(((unsigned long)(a)) - 1))

392
drivers/bluetooth/btqca.c Normal file
View File

@@ -0,0 +1,392 @@
/*
* Bluetooth supports for Qualcomm Atheros chips
*
* Copyright (c) 2015 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/firmware.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btqca.h"
#define VERSION "0.1"
static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version)
{
struct sk_buff *skb;
struct edl_event_hdr *edl;
struct rome_version *ver;
char cmd;
int err = 0;
BT_DBG("%s: ROME Patch Version Request", hdev->name);
cmd = EDL_PATCH_VER_REQ_CMD;
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
&cmd, HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
BT_ERR("%s: Failed to read version of ROME (%d)", hdev->name,
err);
return err;
}
if (skb->len != sizeof(*edl) + sizeof(*ver)) {
BT_ERR("%s: Version size mismatch len %d", hdev->name,
skb->len);
err = -EILSEQ;
goto out;
}
edl = (struct edl_event_hdr *)(skb->data);
if (!edl) {
BT_ERR("%s: TLV with no header", hdev->name);
err = -EILSEQ;
goto out;
}
if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
edl->rtype != EDL_APP_VER_RES_EVT) {
BT_ERR("%s: Wrong packet received %d %d", hdev->name,
edl->cresp, edl->rtype);
err = -EIO;
goto out;
}
ver = (struct rome_version *)(edl->data);
BT_DBG("%s: Product:0x%08x", hdev->name, le32_to_cpu(ver->product_id));
BT_DBG("%s: Patch :0x%08x", hdev->name, le16_to_cpu(ver->patch_ver));
BT_DBG("%s: ROM :0x%08x", hdev->name, le16_to_cpu(ver->rome_ver));
BT_DBG("%s: SOC :0x%08x", hdev->name, le32_to_cpu(ver->soc_id));
/* ROME chipset version can be decided by patch and SoC
* version, combination with upper 2 bytes from SoC
* and lower 2 bytes from patch will be used.
*/
*rome_version = (le32_to_cpu(ver->soc_id) << 16) |
(le16_to_cpu(ver->rome_ver) & 0x0000ffff);
out:
kfree_skb(skb);
return err;
}
static int rome_reset(struct hci_dev *hdev)
{
struct sk_buff *skb;
int err;
BT_DBG("%s: ROME HCI_RESET", hdev->name);
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
BT_ERR("%s: Reset failed (%d)", hdev->name, err);
return err;
}
kfree_skb(skb);
return 0;
}
static void rome_tlv_check_data(struct rome_config *config,
const struct firmware *fw)
{
const u8 *data;
u32 type_len;
u16 tag_id, tag_len;
int idx, length;
struct tlv_type_hdr *tlv;
struct tlv_type_patch *tlv_patch;
struct tlv_type_nvm *tlv_nvm;
tlv = (struct tlv_type_hdr *)fw->data;
type_len = le32_to_cpu(tlv->type_len);
length = (type_len >> 8) & 0x00ffffff;
BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
BT_DBG("Length\t\t : %d bytes", length);
switch (config->type) {
case TLV_TYPE_PATCH:
tlv_patch = (struct tlv_type_patch *)tlv->data;
BT_DBG("Total Length\t\t : %d bytes",
le32_to_cpu(tlv_patch->total_size));
BT_DBG("Patch Data Length\t : %d bytes",
le32_to_cpu(tlv_patch->data_length));
BT_DBG("Signing Format Version : 0x%x",
tlv_patch->format_version);
BT_DBG("Signature Algorithm\t : 0x%x",
tlv_patch->signature);
BT_DBG("Reserved\t\t : 0x%x",
le16_to_cpu(tlv_patch->reserved1));
BT_DBG("Product ID\t\t : 0x%04x",
le16_to_cpu(tlv_patch->product_id));
BT_DBG("Rom Build Version\t : 0x%04x",
le16_to_cpu(tlv_patch->rom_build));
BT_DBG("Patch Version\t\t : 0x%04x",
le16_to_cpu(tlv_patch->patch_version));
BT_DBG("Reserved\t\t : 0x%x",
le16_to_cpu(tlv_patch->reserved2));
BT_DBG("Patch Entry Address\t : 0x%x",
le32_to_cpu(tlv_patch->entry));
break;
case TLV_TYPE_NVM:
idx = 0;
data = tlv->data;
while (idx < length) {
tlv_nvm = (struct tlv_type_nvm *)(data + idx);
tag_id = le16_to_cpu(tlv_nvm->tag_id);
tag_len = le16_to_cpu(tlv_nvm->tag_len);
/* Update NVM tags as needed */
switch (tag_id) {
case EDL_TAG_ID_HCI:
/* HCI transport layer parameters
* enabling software inband sleep
* onto controller side.
*/
tlv_nvm->data[0] |= 0x80;
/* UART Baud Rate */
tlv_nvm->data[2] = config->user_baud_rate;
break;
case EDL_TAG_ID_DEEP_SLEEP:
/* Sleep enable mask
* enabling deep sleep feature on controller.
*/
tlv_nvm->data[0] |= 0x01;
break;
}
idx += (sizeof(u16) + sizeof(u16) + 8 + tag_len);
}
break;
default:
BT_ERR("Unknown TLV type %d", config->type);
break;
}
}
static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int seg_size,
const u8 *data)
{
struct sk_buff *skb;
struct edl_event_hdr *edl;
struct tlv_seg_resp *tlv_resp;
u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2];
int err = 0;
BT_DBG("%s: Download segment #%d size %d", hdev->name, idx, seg_size);
cmd[0] = EDL_PATCH_TLV_REQ_CMD;
cmd[1] = seg_size;
memcpy(cmd + 2, data, seg_size);
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, cmd,
HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
BT_ERR("%s: Failed to send TLV segment (%d)", hdev->name, err);
return err;
}
if (skb->len != sizeof(*edl) + sizeof(*tlv_resp)) {
BT_ERR("%s: TLV response size mismatch", hdev->name);
err = -EILSEQ;
goto out;
}
edl = (struct edl_event_hdr *)(skb->data);
if (!edl) {
BT_ERR("%s: TLV with no header", hdev->name);
err = -EILSEQ;
goto out;
}
tlv_resp = (struct tlv_seg_resp *)(edl->data);
if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
edl->rtype != EDL_TVL_DNLD_RES_EVT || tlv_resp->result != 0x00) {
BT_ERR("%s: TLV with error stat 0x%x rtype 0x%x (0x%x)",
hdev->name, edl->cresp, edl->rtype, tlv_resp->result);
err = -EIO;
}
out:
kfree_skb(skb);
return err;
}
static int rome_tlv_download_request(struct hci_dev *hdev,
const struct firmware *fw)
{
const u8 *buffer, *data;
int total_segment, remain_size;
int ret, i;
if (!fw || !fw->data)
return -EINVAL;
total_segment = fw->size / MAX_SIZE_PER_TLV_SEGMENT;
remain_size = fw->size % MAX_SIZE_PER_TLV_SEGMENT;
BT_DBG("%s: Total segment num %d remain size %d total size %zu",
hdev->name, total_segment, remain_size, fw->size);
data = fw->data;
for (i = 0; i < total_segment; i++) {
buffer = data + i * MAX_SIZE_PER_TLV_SEGMENT;
ret = rome_tlv_send_segment(hdev, i, MAX_SIZE_PER_TLV_SEGMENT,
buffer);
if (ret < 0)
return -EIO;
}
if (remain_size) {
buffer = data + total_segment * MAX_SIZE_PER_TLV_SEGMENT;
ret = rome_tlv_send_segment(hdev, total_segment, remain_size,
buffer);
if (ret < 0)
return -EIO;
}
return 0;
}
static int rome_download_firmware(struct hci_dev *hdev,
struct rome_config *config)
{
const struct firmware *fw;
int ret;
BT_INFO("%s: ROME Downloading %s", hdev->name, config->fwname);
ret = request_firmware(&fw, config->fwname, &hdev->dev);
if (ret) {
BT_ERR("%s: Failed to request file: %s (%d)", hdev->name,
config->fwname, ret);
return ret;
}
rome_tlv_check_data(config, fw);
ret = rome_tlv_download_request(hdev, fw);
if (ret) {
BT_ERR("%s: Failed to download file: %s (%d)", hdev->name,
config->fwname, ret);
}
release_firmware(fw);
return ret;
}
int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
struct sk_buff *skb;
u8 cmd[9];
int err;
cmd[0] = EDL_NVM_ACCESS_SET_REQ_CMD;
cmd[1] = 0x02; /* TAG ID */
cmd[2] = sizeof(bdaddr_t); /* size */
memcpy(cmd + 3, bdaddr, sizeof(bdaddr_t));
skb = __hci_cmd_sync_ev(hdev, EDL_NVM_ACCESS_OPCODE, sizeof(cmd), cmd,
HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
BT_ERR("%s: Change address command failed (%d)",
hdev->name, err);
return err;
}
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);
int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate)
{
u32 rome_ver = 0;
struct rome_config config;
int err;
BT_DBG("%s: ROME setup on UART", hdev->name);
config.user_baud_rate = baudrate;
/* Get ROME version information */
err = rome_patch_ver_req(hdev, &rome_ver);
if (err < 0 || rome_ver == 0) {
BT_ERR("%s: Failed to get version 0x%x", hdev->name, err);
return err;
}
BT_INFO("%s: ROME controller version 0x%08x", hdev->name, rome_ver);
/* Download rampatch file */
config.type = TLV_TYPE_PATCH;
snprintf(config.fwname, sizeof(config.fwname), "qca/rampatch_%08x.bin",
rome_ver);
err = rome_download_firmware(hdev, &config);
if (err < 0) {
BT_ERR("%s: Failed to download patch (%d)", hdev->name, err);
return err;
}
/* Download NVM configuration */
config.type = TLV_TYPE_NVM;
snprintf(config.fwname, sizeof(config.fwname), "qca/nvm_%08x.bin",
rome_ver);
err = rome_download_firmware(hdev, &config);
if (err < 0) {
BT_ERR("%s: Failed to download NVM (%d)", hdev->name, err);
return err;
}
/* Perform HCI reset */
err = rome_reset(hdev);
if (err < 0) {
BT_ERR("%s: Failed to run HCI_RESET (%d)", hdev->name, err);
return err;
}
BT_INFO("%s: ROME setup on UART is completed", hdev->name);
return 0;
}
EXPORT_SYMBOL_GPL(qca_uart_setup_rome);
MODULE_AUTHOR("Ben Young Tae Kim <ytkim@qca.qualcomm.com>");
MODULE_DESCRIPTION("Bluetooth support for Qualcomm Atheros family ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");

135
drivers/bluetooth/btqca.h Normal file
View File

@@ -0,0 +1,135 @@
/*
* Bluetooth supports for Qualcomm Atheros ROME chips
*
* Copyright (c) 2015 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#define EDL_PATCH_CMD_OPCODE (0xFC00)
#define EDL_NVM_ACCESS_OPCODE (0xFC0B)
#define EDL_PATCH_CMD_LEN (1)
#define EDL_PATCH_VER_REQ_CMD (0x19)
#define EDL_PATCH_TLV_REQ_CMD (0x1E)
#define EDL_NVM_ACCESS_SET_REQ_CMD (0x01)
#define MAX_SIZE_PER_TLV_SEGMENT (243)
#define EDL_CMD_REQ_RES_EVT (0x00)
#define EDL_PATCH_VER_RES_EVT (0x19)
#define EDL_APP_VER_RES_EVT (0x02)
#define EDL_TVL_DNLD_RES_EVT (0x04)
#define EDL_CMD_EXE_STATUS_EVT (0x00)
#define EDL_SET_BAUDRATE_RSP_EVT (0x92)
#define EDL_NVM_ACCESS_CODE_EVT (0x0B)
#define EDL_TAG_ID_HCI (17)
#define EDL_TAG_ID_DEEP_SLEEP (27)
enum qca_bardrate {
QCA_BAUDRATE_115200 = 0,
QCA_BAUDRATE_57600,
QCA_BAUDRATE_38400,
QCA_BAUDRATE_19200,
QCA_BAUDRATE_9600,
QCA_BAUDRATE_230400,
QCA_BAUDRATE_250000,
QCA_BAUDRATE_460800,
QCA_BAUDRATE_500000,
QCA_BAUDRATE_720000,
QCA_BAUDRATE_921600,
QCA_BAUDRATE_1000000,
QCA_BAUDRATE_1250000,
QCA_BAUDRATE_2000000,
QCA_BAUDRATE_3000000,
QCA_BAUDRATE_4000000,
QCA_BAUDRATE_1600000,
QCA_BAUDRATE_3200000,
QCA_BAUDRATE_3500000,
QCA_BAUDRATE_AUTO = 0xFE,
QCA_BAUDRATE_RESERVED
};
enum rome_tlv_type {
TLV_TYPE_PATCH = 1,
TLV_TYPE_NVM
};
struct rome_config {
u8 type;
char fwname[64];
uint8_t user_baud_rate;
};
struct edl_event_hdr {
__u8 cresp;
__u8 rtype;
__u8 data[0];
} __packed;
struct rome_version {
__le32 product_id;
__le16 patch_ver;
__le16 rome_ver;
__le32 soc_id;
} __packed;
struct tlv_seg_resp {
__u8 result;
} __packed;
struct tlv_type_patch {
__le32 total_size;
__le32 data_length;
__u8 format_version;
__u8 signature;
__le16 reserved1;
__le16 product_id;
__le16 rom_build;
__le16 patch_version;
__le16 reserved2;
__le32 entry;
} __packed;
struct tlv_type_nvm {
__le16 tag_id;
__le16 tag_len;
__le32 reserve1;
__le32 reserve2;
__u8 data[0];
} __packed;
struct tlv_type_hdr {
__le32 type_len;
__u8 data[0];
} __packed;
#if IS_ENABLED(CONFIG_BT_QCA)
int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate);
#else
static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
return -EOPNOTSUPP;
}
static inline int qca_uart_setup_rome(struct hci_dev *hdev, int speed)
{
return -EOPNOTSUPP;
}
#endif

View File

@@ -0,0 +1,182 @@
/*
* Copyright (c) 2016, Linaro Ltd.
* Copyright (c) 2015, Sony Mobile Communications Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/soc/qcom/smd.h>
#include <linux/soc/qcom/wcnss_ctrl.h>
#include <linux/platform_device.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btqca.h"
struct btqcomsmd {
struct hci_dev *hdev;
struct qcom_smd_channel *acl_channel;
struct qcom_smd_channel *cmd_channel;
};
static int btqcomsmd_recv(struct hci_dev *hdev, unsigned int type,
const void *data, size_t count)
{
struct sk_buff *skb;
/* Use GFP_ATOMIC as we're in IRQ context */
skb = bt_skb_alloc(count, GFP_ATOMIC);
if (!skb) {
hdev->stat.err_rx++;
return -ENOMEM;
}
hci_skb_pkt_type(skb) = type;
memcpy(skb_put(skb, count), data, count);
return hci_recv_frame(hdev, skb);
}
static int btqcomsmd_acl_callback(struct qcom_smd_channel *channel,
const void *data, size_t count)
{
struct btqcomsmd *btq = qcom_smd_get_drvdata(channel);
btq->hdev->stat.byte_rx += count;
return btqcomsmd_recv(btq->hdev, HCI_ACLDATA_PKT, data, count);
}
static int btqcomsmd_cmd_callback(struct qcom_smd_channel *channel,
const void *data, size_t count)
{
struct btqcomsmd *btq = qcom_smd_get_drvdata(channel);
return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count);
}
static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btqcomsmd *btq = hci_get_drvdata(hdev);
int ret;
switch (hci_skb_pkt_type(skb)) {
case HCI_ACLDATA_PKT:
ret = qcom_smd_send(btq->acl_channel, skb->data, skb->len);
hdev->stat.acl_tx++;
hdev->stat.byte_tx += skb->len;
break;
case HCI_COMMAND_PKT:
ret = qcom_smd_send(btq->cmd_channel, skb->data, skb->len);
hdev->stat.cmd_tx++;
break;
default:
ret = -EILSEQ;
break;
}
kfree_skb(skb);
return ret;
}
static int btqcomsmd_open(struct hci_dev *hdev)
{
return 0;
}
static int btqcomsmd_close(struct hci_dev *hdev)
{
return 0;
}
static int btqcomsmd_probe(struct platform_device *pdev)
{
struct btqcomsmd *btq;
struct hci_dev *hdev;
void *wcnss;
int ret;
btq = devm_kzalloc(&pdev->dev, sizeof(*btq), GFP_KERNEL);
if (!btq)
return -ENOMEM;
wcnss = dev_get_drvdata(pdev->dev.parent);
btq->acl_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_ACL",
btqcomsmd_acl_callback);
if (IS_ERR(btq->acl_channel))
return PTR_ERR(btq->acl_channel);
btq->cmd_channel = qcom_wcnss_open_channel(wcnss, "APPS_RIVA_BT_CMD",
btqcomsmd_cmd_callback);
if (IS_ERR(btq->cmd_channel))
return PTR_ERR(btq->cmd_channel);
qcom_smd_set_drvdata(btq->acl_channel, btq);
qcom_smd_set_drvdata(btq->cmd_channel, btq);
hdev = hci_alloc_dev();
if (!hdev)
return -ENOMEM;
hci_set_drvdata(hdev, btq);
btq->hdev = hdev;
SET_HCIDEV_DEV(hdev, &pdev->dev);
hdev->bus = HCI_SMD;
hdev->open = btqcomsmd_open;
hdev->close = btqcomsmd_close;
hdev->send = btqcomsmd_send;
hdev->set_bdaddr = qca_set_bdaddr_rome;
ret = hci_register_dev(hdev);
if (ret < 0) {
hci_free_dev(hdev);
return ret;
}
platform_set_drvdata(pdev, btq);
return 0;
}
static int btqcomsmd_remove(struct platform_device *pdev)
{
struct btqcomsmd *btq = platform_get_drvdata(pdev);
hci_unregister_dev(btq->hdev);
hci_free_dev(btq->hdev);
return 0;
}
static const struct of_device_id btqcomsmd_of_match[] = {
{ .compatible = "qcom,wcnss-bt", },
{ },
};
static struct platform_driver btqcomsmd_driver = {
.probe = btqcomsmd_probe,
.remove = btqcomsmd_remove,
.driver = {
.name = "btqcomsmd",
.of_match_table = btqcomsmd_of_match,
},
};
module_platform_driver(btqcomsmd_driver);
MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@sonymobile.com>");
MODULE_DESCRIPTION("Qualcomm SMD HCI driver");
MODULE_LICENSE("GPL v2");

473
drivers/bluetooth/btrtl.c Normal file
View File

@@ -0,0 +1,473 @@
/*
* Bluetooth support for Realtek devices
*
* Copyright (C) 2015 Endless Mobile, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/firmware.h>
#include <asm/unaligned.h>
#include <linux/usb.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btrtl.h"
#define VERSION "0.1"
#define RTL_EPATCH_SIGNATURE "Realtech"
#define RTL_ROM_LMP_3499 0x3499
#define RTL_ROM_LMP_8723A 0x1200
#define RTL_ROM_LMP_8723B 0x8723
#define RTL_ROM_LMP_8821A 0x8821
#define RTL_ROM_LMP_8761A 0x8761
#define RTL_ROM_LMP_8822B 0x8822
static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version)
{
struct rtl_rom_version_evt *rom_version;
struct sk_buff *skb;
/* Read RTL ROM version command */
skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: Read ROM version failed (%ld)",
hdev->name, PTR_ERR(skb));
return PTR_ERR(skb);
}
if (skb->len != sizeof(*rom_version)) {
BT_ERR("%s: RTL version event length mismatch", hdev->name);
kfree_skb(skb);
return -EIO;
}
rom_version = (struct rtl_rom_version_evt *)skb->data;
BT_INFO("%s: rom_version status=%x version=%x",
hdev->name, rom_version->status, rom_version->version);
*version = rom_version->version;
kfree_skb(skb);
return 0;
}
static int rtl8723b_parse_firmware(struct hci_dev *hdev, u16 lmp_subver,
const struct firmware *fw,
unsigned char **_buf)
{
const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
struct rtl_epatch_header *epatch_info;
unsigned char *buf;
int i, ret, len;
size_t min_size;
u8 opcode, length, data, rom_version = 0;
int project_id = -1;
const unsigned char *fwptr, *chip_id_base;
const unsigned char *patch_length_base, *patch_offset_base;
u32 patch_offset = 0;
u16 patch_length, num_patches;
static const struct {
__u16 lmp_subver;
__u8 id;
} project_id_to_lmp_subver[] = {
{ RTL_ROM_LMP_8723A, 0 },
{ RTL_ROM_LMP_8723B, 1 },
{ RTL_ROM_LMP_8821A, 2 },
{ RTL_ROM_LMP_8761A, 3 },
{ RTL_ROM_LMP_8822B, 8 },
};
ret = rtl_read_rom_version(hdev, &rom_version);
if (ret)
return ret;
min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
if (fw->size < min_size)
return -EINVAL;
fwptr = fw->data + fw->size - sizeof(extension_sig);
if (memcmp(fwptr, extension_sig, sizeof(extension_sig)) != 0) {
BT_ERR("%s: extension section signature mismatch", hdev->name);
return -EINVAL;
}
/* Loop from the end of the firmware parsing instructions, until
* we find an instruction that identifies the "project ID" for the
* hardware supported by this firwmare file.
* Once we have that, we double-check that that project_id is suitable
* for the hardware we are working with.
*/
while (fwptr >= fw->data + (sizeof(struct rtl_epatch_header) + 3)) {
opcode = *--fwptr;
length = *--fwptr;
data = *--fwptr;
BT_DBG("check op=%x len=%x data=%x", opcode, length, data);
if (opcode == 0xff) /* EOF */
break;
if (length == 0) {
BT_ERR("%s: found instruction with length 0",
hdev->name);
return -EINVAL;
}
if (opcode == 0 && length == 1) {
project_id = data;
break;
}
fwptr -= length;
}
if (project_id < 0) {
BT_ERR("%s: failed to find version instruction", hdev->name);
return -EINVAL;
}
/* Find project_id in table */
for (i = 0; i < ARRAY_SIZE(project_id_to_lmp_subver); i++) {
if (project_id == project_id_to_lmp_subver[i].id)
break;
}
if (i >= ARRAY_SIZE(project_id_to_lmp_subver)) {
BT_ERR("%s: unknown project id %d", hdev->name, project_id);
return -EINVAL;
}
if (lmp_subver != project_id_to_lmp_subver[i].lmp_subver) {
BT_ERR("%s: firmware is for %x but this is a %x", hdev->name,
project_id_to_lmp_subver[i].lmp_subver, lmp_subver);
return -EINVAL;
}
epatch_info = (struct rtl_epatch_header *)fw->data;
if (memcmp(epatch_info->signature, RTL_EPATCH_SIGNATURE, 8) != 0) {
BT_ERR("%s: bad EPATCH signature", hdev->name);
return -EINVAL;
}
num_patches = le16_to_cpu(epatch_info->num_patches);
BT_DBG("fw_version=%x, num_patches=%d",
le32_to_cpu(epatch_info->fw_version), num_patches);
/* After the rtl_epatch_header there is a funky patch metadata section.
* Assuming 2 patches, the layout is:
* ChipID1 ChipID2 PatchLength1 PatchLength2 PatchOffset1 PatchOffset2
*
* Find the right patch for this chip.
*/
min_size += 8 * num_patches;
if (fw->size < min_size)
return -EINVAL;
chip_id_base = fw->data + sizeof(struct rtl_epatch_header);
patch_length_base = chip_id_base + (sizeof(u16) * num_patches);
patch_offset_base = patch_length_base + (sizeof(u16) * num_patches);
for (i = 0; i < num_patches; i++) {
u16 chip_id = get_unaligned_le16(chip_id_base +
(i * sizeof(u16)));
if (chip_id == rom_version + 1) {
patch_length = get_unaligned_le16(patch_length_base +
(i * sizeof(u16)));
patch_offset = get_unaligned_le32(patch_offset_base +
(i * sizeof(u32)));
break;
}
}
if (!patch_offset) {
BT_ERR("%s: didn't find patch for chip id %d",
hdev->name, rom_version);
return -EINVAL;
}
BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i);
min_size = patch_offset + patch_length;
if (fw->size < min_size)
return -EINVAL;
/* Copy the firmware into a new buffer and write the version at
* the end.
*/
len = patch_length;
buf = kmemdup(fw->data + patch_offset, patch_length, GFP_KERNEL);
if (!buf)
return -ENOMEM;
memcpy(buf + patch_length - 4, &epatch_info->fw_version, 4);
*_buf = buf;
return len;
}
static int rtl_download_firmware(struct hci_dev *hdev,
const unsigned char *data, int fw_len)
{
struct rtl_download_cmd *dl_cmd;
int frag_num = fw_len / RTL_FRAG_LEN + 1;
int frag_len = RTL_FRAG_LEN;
int ret = 0;
int i;
dl_cmd = kmalloc(sizeof(struct rtl_download_cmd), GFP_KERNEL);
if (!dl_cmd)
return -ENOMEM;
for (i = 0; i < frag_num; i++) {
struct sk_buff *skb;
BT_DBG("download fw (%d/%d)", i, frag_num);
dl_cmd->index = i;
if (i == (frag_num - 1)) {
dl_cmd->index |= 0x80; /* data end */
frag_len = fw_len % RTL_FRAG_LEN;
}
memcpy(dl_cmd->data, data, frag_len);
/* Send download command */
skb = __hci_cmd_sync(hdev, 0xfc20, frag_len + 1, dl_cmd,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: download fw command failed (%ld)",
hdev->name, PTR_ERR(skb));
ret = -PTR_ERR(skb);
goto out;
}
if (skb->len != sizeof(struct rtl_download_response)) {
BT_ERR("%s: download fw event length mismatch",
hdev->name);
kfree_skb(skb);
ret = -EIO;
goto out;
}
kfree_skb(skb);
data += RTL_FRAG_LEN;
}
out:
kfree(dl_cmd);
return ret;
}
static int rtl_load_config(struct hci_dev *hdev, const char *name, u8 **buff)
{
const struct firmware *fw;
int ret;
BT_INFO("%s: rtl: loading %s", hdev->name, name);
ret = request_firmware(&fw, name, &hdev->dev);
if (ret < 0) {
BT_ERR("%s: Failed to load %s", hdev->name, name);
return ret;
}
ret = fw->size;
*buff = kmemdup(fw->data, ret, GFP_KERNEL);
release_firmware(fw);
return ret;
}
static int btrtl_setup_rtl8723a(struct hci_dev *hdev)
{
const struct firmware *fw;
int ret;
BT_INFO("%s: rtl: loading rtl_bt/rtl8723a_fw.bin", hdev->name);
ret = request_firmware(&fw, "rtl_bt/rtl8723a_fw.bin", &hdev->dev);
if (ret < 0) {
BT_ERR("%s: Failed to load rtl_bt/rtl8723a_fw.bin", hdev->name);
return ret;
}
if (fw->size < 8) {
ret = -EINVAL;
goto out;
}
/* Check that the firmware doesn't have the epatch signature
* (which is only for RTL8723B and newer).
*/
if (!memcmp(fw->data, RTL_EPATCH_SIGNATURE, 8)) {
BT_ERR("%s: unexpected EPATCH signature!", hdev->name);
ret = -EINVAL;
goto out;
}
ret = rtl_download_firmware(hdev, fw->data, fw->size);
out:
release_firmware(fw);
return ret;
}
static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver,
const char *fw_name)
{
unsigned char *fw_data = NULL;
const struct firmware *fw;
int ret;
int cfg_sz;
u8 *cfg_buff = NULL;
u8 *tbuff;
char *cfg_name = NULL;
switch (lmp_subver) {
case RTL_ROM_LMP_8723B:
cfg_name = "rtl_bt/rtl8723b_config.bin";
break;
case RTL_ROM_LMP_8821A:
cfg_name = "rtl_bt/rtl8821a_config.bin";
break;
case RTL_ROM_LMP_8761A:
cfg_name = "rtl_bt/rtl8761a_config.bin";
break;
case RTL_ROM_LMP_8822B:
cfg_name = "rtl_bt/rtl8822b_config.bin";
break;
default:
BT_ERR("%s: rtl: no config according to lmp_subver %04x",
hdev->name, lmp_subver);
break;
}
if (cfg_name) {
cfg_sz = rtl_load_config(hdev, cfg_name, &cfg_buff);
if (cfg_sz < 0)
cfg_sz = 0;
} else
cfg_sz = 0;
BT_INFO("%s: rtl: loading %s", hdev->name, fw_name);
ret = request_firmware(&fw, fw_name, &hdev->dev);
if (ret < 0) {
BT_ERR("%s: Failed to load %s", hdev->name, fw_name);
goto err_req_fw;
}
ret = rtl8723b_parse_firmware(hdev, lmp_subver, fw, &fw_data);
if (ret < 0)
goto out;
if (cfg_sz) {
tbuff = kzalloc(ret + cfg_sz, GFP_KERNEL);
if (!tbuff) {
ret = -ENOMEM;
goto out;
}
memcpy(tbuff, fw_data, ret);
kfree(fw_data);
memcpy(tbuff + ret, cfg_buff, cfg_sz);
ret += cfg_sz;
fw_data = tbuff;
}
BT_INFO("cfg_sz %d, total size %d", cfg_sz, ret);
ret = rtl_download_firmware(hdev, fw_data, ret);
out:
release_firmware(fw);
kfree(fw_data);
err_req_fw:
if (cfg_sz)
kfree(cfg_buff);
return ret;
}
static struct sk_buff *btrtl_read_local_version(struct hci_dev *hdev)
{
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
hdev->name, PTR_ERR(skb));
return skb;
}
if (skb->len != sizeof(struct hci_rp_read_local_version)) {
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
hdev->name);
kfree_skb(skb);
return ERR_PTR(-EIO);
}
return skb;
}
int btrtl_setup_realtek(struct hci_dev *hdev)
{
struct sk_buff *skb;
struct hci_rp_read_local_version *resp;
u16 lmp_subver;
skb = btrtl_read_local_version(hdev);
if (IS_ERR(skb))
return -PTR_ERR(skb);
resp = (struct hci_rp_read_local_version *)skb->data;
BT_INFO("%s: rtl: examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x "
"lmp_subver=%04x", hdev->name, resp->hci_ver, resp->hci_rev,
resp->lmp_ver, resp->lmp_subver);
lmp_subver = le16_to_cpu(resp->lmp_subver);
kfree_skb(skb);
/* Match a set of subver values that correspond to stock firmware,
* which is not compatible with standard btusb.
* If matched, upload an alternative firmware that does conform to
* standard btusb. Once that firmware is uploaded, the subver changes
* to a different value.
*/
switch (lmp_subver) {
case RTL_ROM_LMP_8723A:
case RTL_ROM_LMP_3499:
return btrtl_setup_rtl8723a(hdev);
case RTL_ROM_LMP_8723B:
return btrtl_setup_rtl8723b(hdev, lmp_subver,
"rtl_bt/rtl8723b_fw.bin");
case RTL_ROM_LMP_8821A:
return btrtl_setup_rtl8723b(hdev, lmp_subver,
"rtl_bt/rtl8821a_fw.bin");
case RTL_ROM_LMP_8761A:
return btrtl_setup_rtl8723b(hdev, lmp_subver,
"rtl_bt/rtl8761a_fw.bin");
case RTL_ROM_LMP_8822B:
return btrtl_setup_rtl8723b(hdev, lmp_subver,
"rtl_bt/rtl8822b_fw.bin");
default:
BT_INFO("rtl: assuming no firmware upload needed.");
return 0;
}
}
EXPORT_SYMBOL_GPL(btrtl_setup_realtek);
MODULE_AUTHOR("Daniel Drake <drake@endlessm.com>");
MODULE_DESCRIPTION("Bluetooth support for Realtek devices ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");

52
drivers/bluetooth/btrtl.h Normal file
View File

@@ -0,0 +1,52 @@
/*
* Bluetooth support for Realtek devices
*
* Copyright (C) 2015 Endless Mobile, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
*/
#define RTL_FRAG_LEN 252
struct rtl_download_cmd {
__u8 index;
__u8 data[RTL_FRAG_LEN];
} __packed;
struct rtl_download_response {
__u8 status;
__u8 index;
} __packed;
struct rtl_rom_version_evt {
__u8 status;
__u8 version;
} __packed;
struct rtl_epatch_header {
__u8 signature[8];
__le32 fw_version;
__le16 num_patches;
} __packed;
#if IS_ENABLED(CONFIG_BT_RTL)
int btrtl_setup_realtek(struct hci_dev *hdev);
#else
static inline int btrtl_setup_realtek(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
}
#endif

383
drivers/bluetooth/btsdio.c Normal file
View File

@@ -0,0 +1,383 @@
/*
*
* Generic Bluetooth SDIO driver
*
* Copyright (C) 2007 Cambridge Silicon Radio Ltd.
* Copyright (C) 2007 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio_func.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#define VERSION "0.1"
static const struct sdio_device_id btsdio_table[] = {
/* Generic Bluetooth Type-A SDIO device */
{ SDIO_DEVICE_CLASS(SDIO_CLASS_BT_A) },
/* Generic Bluetooth Type-B SDIO device */
{ SDIO_DEVICE_CLASS(SDIO_CLASS_BT_B) },
/* Generic Bluetooth AMP controller */
{ SDIO_DEVICE_CLASS(SDIO_CLASS_BT_AMP) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(sdio, btsdio_table);
struct btsdio_data {
struct hci_dev *hdev;
struct sdio_func *func;
struct work_struct work;
struct sk_buff_head txq;
};
#define REG_RDAT 0x00 /* Receiver Data */
#define REG_TDAT 0x00 /* Transmitter Data */
#define REG_PC_RRT 0x10 /* Read Packet Control */
#define REG_PC_WRT 0x11 /* Write Packet Control */
#define REG_RTC_STAT 0x12 /* Retry Control Status */
#define REG_RTC_SET 0x12 /* Retry Control Set */
#define REG_INTRD 0x13 /* Interrupt Indication */
#define REG_CL_INTRD 0x13 /* Interrupt Clear */
#define REG_EN_INTRD 0x14 /* Interrupt Enable */
#define REG_MD_STAT 0x20 /* Bluetooth Mode Status */
#define REG_MD_SET 0x20 /* Bluetooth Mode Set */
static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb)
{
int err;
BT_DBG("%s", data->hdev->name);
/* Prepend Type-A header */
skb_push(skb, 4);
skb->data[0] = (skb->len & 0x0000ff);
skb->data[1] = (skb->len & 0x00ff00) >> 8;
skb->data[2] = (skb->len & 0xff0000) >> 16;
skb->data[3] = hci_skb_pkt_type(skb);
err = sdio_writesb(data->func, REG_TDAT, skb->data, skb->len);
if (err < 0) {
skb_pull(skb, 4);
sdio_writeb(data->func, 0x01, REG_PC_WRT, NULL);
return err;
}
data->hdev->stat.byte_tx += skb->len;
kfree_skb(skb);
return 0;
}
static void btsdio_work(struct work_struct *work)
{
struct btsdio_data *data = container_of(work, struct btsdio_data, work);
struct sk_buff *skb;
int err;
BT_DBG("%s", data->hdev->name);
sdio_claim_host(data->func);
while ((skb = skb_dequeue(&data->txq))) {
err = btsdio_tx_packet(data, skb);
if (err < 0) {
data->hdev->stat.err_tx++;
skb_queue_head(&data->txq, skb);
break;
}
}
sdio_release_host(data->func);
}
static int btsdio_rx_packet(struct btsdio_data *data)
{
u8 hdr[4] __attribute__ ((aligned(4)));
struct sk_buff *skb;
int err, len;
BT_DBG("%s", data->hdev->name);
err = sdio_readsb(data->func, hdr, REG_RDAT, 4);
if (err < 0)
return err;
len = hdr[0] | (hdr[1] << 8) | (hdr[2] << 16);
if (len < 4 || len > 65543)
return -EILSEQ;
skb = bt_skb_alloc(len - 4, GFP_KERNEL);
if (!skb) {
/* Out of memory. Prepare a read retry and just
* return with the expectation that the next time
* we're called we'll have more memory. */
return -ENOMEM;
}
skb_put(skb, len - 4);
err = sdio_readsb(data->func, skb->data, REG_RDAT, len - 4);
if (err < 0) {
kfree_skb(skb);
return err;
}
data->hdev->stat.byte_rx += len;
hci_skb_pkt_type(skb) = hdr[3];
err = hci_recv_frame(data->hdev, skb);
if (err < 0)
return err;
sdio_writeb(data->func, 0x00, REG_PC_RRT, NULL);
return 0;
}
static void btsdio_interrupt(struct sdio_func *func)
{
struct btsdio_data *data = sdio_get_drvdata(func);
int intrd;
BT_DBG("%s", data->hdev->name);
intrd = sdio_readb(func, REG_INTRD, NULL);
if (intrd & 0x01) {
sdio_writeb(func, 0x01, REG_CL_INTRD, NULL);
if (btsdio_rx_packet(data) < 0) {
data->hdev->stat.err_rx++;
sdio_writeb(data->func, 0x01, REG_PC_RRT, NULL);
}
}
}
static int btsdio_open(struct hci_dev *hdev)
{
struct btsdio_data *data = hci_get_drvdata(hdev);
int err;
BT_DBG("%s", hdev->name);
sdio_claim_host(data->func);
err = sdio_enable_func(data->func);
if (err < 0)
goto release;
err = sdio_claim_irq(data->func, btsdio_interrupt);
if (err < 0) {
sdio_disable_func(data->func);
goto release;
}
if (data->func->class == SDIO_CLASS_BT_B)
sdio_writeb(data->func, 0x00, REG_MD_SET, NULL);
sdio_writeb(data->func, 0x01, REG_EN_INTRD, NULL);
release:
sdio_release_host(data->func);
return err;
}
static int btsdio_close(struct hci_dev *hdev)
{
struct btsdio_data *data = hci_get_drvdata(hdev);
BT_DBG("%s", hdev->name);
sdio_claim_host(data->func);
sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL);
sdio_release_irq(data->func);
sdio_disable_func(data->func);
sdio_release_host(data->func);
return 0;
}
static int btsdio_flush(struct hci_dev *hdev)
{
struct btsdio_data *data = hci_get_drvdata(hdev);
BT_DBG("%s", hdev->name);
skb_queue_purge(&data->txq);
return 0;
}
static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btsdio_data *data = hci_get_drvdata(hdev);
BT_DBG("%s", hdev->name);
switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
break;
default:
return -EILSEQ;
}
skb_queue_tail(&data->txq, skb);
schedule_work(&data->work);
return 0;
}
static int btsdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
struct btsdio_data *data;
struct hci_dev *hdev;
struct sdio_func_tuple *tuple = func->tuples;
int err;
BT_DBG("func %p id %p class 0x%04x", func, id, func->class);
while (tuple) {
BT_DBG("code 0x%x size %d", tuple->code, tuple->size);
tuple = tuple->next;
}
data = devm_kzalloc(&func->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->func = func;
INIT_WORK(&data->work, btsdio_work);
skb_queue_head_init(&data->txq);
hdev = hci_alloc_dev();
if (!hdev)
return -ENOMEM;
hdev->bus = HCI_SDIO;
hci_set_drvdata(hdev, data);
if (id->class == SDIO_CLASS_BT_AMP)
hdev->dev_type = HCI_AMP;
else
hdev->dev_type = HCI_PRIMARY;
data->hdev = hdev;
SET_HCIDEV_DEV(hdev, &func->dev);
hdev->open = btsdio_open;
hdev->close = btsdio_close;
hdev->flush = btsdio_flush;
hdev->send = btsdio_send_frame;
if (func->vendor == 0x0104 && func->device == 0x00c5)
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
err = hci_register_dev(hdev);
if (err < 0) {
hci_free_dev(hdev);
return err;
}
sdio_set_drvdata(func, data);
return 0;
}
static void btsdio_remove(struct sdio_func *func)
{
struct btsdio_data *data = sdio_get_drvdata(func);
struct hci_dev *hdev;
BT_DBG("func %p", func);
if (!data)
return;
hdev = data->hdev;
sdio_set_drvdata(func, NULL);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
static struct sdio_driver btsdio_driver = {
.name = "btsdio",
.probe = btsdio_probe,
.remove = btsdio_remove,
.id_table = btsdio_table,
};
static int __init btsdio_init(void)
{
BT_INFO("Generic Bluetooth SDIO driver ver %s", VERSION);
return sdio_register_driver(&btsdio_driver);
}
static void __exit btsdio_exit(void)
{
sdio_unregister_driver(&btsdio_driver);
}
module_init(btsdio_init);
module_exit(btsdio_exit);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Generic Bluetooth SDIO driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,673 @@
/*
*
* Driver for Bluetooth PCMCIA cards with HCI UART interface
*
* Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/spinlock.h>
#include <linux/moduleparam.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
#include <pcmcia/ds.h>
#include <pcmcia/cisreg.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
/* ======================== Module parameters ======================== */
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth driver for Bluetooth PCMCIA cards with HCI UART interface");
MODULE_LICENSE("GPL");
/* ======================== Local structures ======================== */
struct btuart_info {
struct pcmcia_device *p_dev;
struct hci_dev *hdev;
spinlock_t lock; /* For serializing operations */
struct sk_buff_head txq;
unsigned long tx_state;
unsigned long rx_state;
unsigned long rx_count;
struct sk_buff *rx_skb;
};
static int btuart_config(struct pcmcia_device *link);
static void btuart_release(struct pcmcia_device *link);
static void btuart_detach(struct pcmcia_device *p_dev);
/* Maximum baud rate */
#define SPEED_MAX 115200
/* Default baud rate: 57600, 115200, 230400 or 460800 */
#define DEFAULT_BAUD_RATE 115200
/* Transmit states */
#define XMIT_SENDING 1
#define XMIT_WAKEUP 2
#define XMIT_WAITING 8
/* Receiver states */
#define RECV_WAIT_PACKET_TYPE 0
#define RECV_WAIT_EVENT_HEADER 1
#define RECV_WAIT_ACL_HEADER 2
#define RECV_WAIT_SCO_HEADER 3
#define RECV_WAIT_DATA 4
/* ======================== Interrupt handling ======================== */
static int btuart_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
{
int actual = 0;
/* Tx FIFO should be empty */
if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
return 0;
/* Fill FIFO with current frame */
while ((fifo_size-- > 0) && (actual < len)) {
/* Transmit next byte */
outb(buf[actual], iobase + UART_TX);
actual++;
}
return actual;
}
static void btuart_write_wakeup(struct btuart_info *info)
{
if (!info) {
BT_ERR("Unknown device");
return;
}
if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
set_bit(XMIT_WAKEUP, &(info->tx_state));
return;
}
do {
unsigned int iobase = info->p_dev->resource[0]->start;
register struct sk_buff *skb;
int len;
clear_bit(XMIT_WAKEUP, &(info->tx_state));
if (!pcmcia_dev_present(info->p_dev))
return;
skb = skb_dequeue(&(info->txq));
if (!skb)
break;
/* Send frame */
len = btuart_write(iobase, 16, skb->data, skb->len);
set_bit(XMIT_WAKEUP, &(info->tx_state));
if (len == skb->len) {
kfree_skb(skb);
} else {
skb_pull(skb, len);
skb_queue_head(&(info->txq), skb);
}
info->hdev->stat.byte_tx += len;
} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
clear_bit(XMIT_SENDING, &(info->tx_state));
}
static void btuart_receive(struct btuart_info *info)
{
unsigned int iobase;
int boguscount = 0;
if (!info) {
BT_ERR("Unknown device");
return;
}
iobase = info->p_dev->resource[0]->start;
do {
info->hdev->stat.byte_rx++;
/* Allocate packet */
if (!info->rx_skb) {
info->rx_state = RECV_WAIT_PACKET_TYPE;
info->rx_count = 0;
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
if (!info->rx_skb) {
BT_ERR("Can't allocate mem for new packet");
return;
}
}
if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
hci_skb_pkt_type(info->rx_skb) = inb(iobase + UART_RX);
switch (hci_skb_pkt_type(info->rx_skb)) {
case HCI_EVENT_PKT:
info->rx_state = RECV_WAIT_EVENT_HEADER;
info->rx_count = HCI_EVENT_HDR_SIZE;
break;
case HCI_ACLDATA_PKT:
info->rx_state = RECV_WAIT_ACL_HEADER;
info->rx_count = HCI_ACL_HDR_SIZE;
break;
case HCI_SCODATA_PKT:
info->rx_state = RECV_WAIT_SCO_HEADER;
info->rx_count = HCI_SCO_HDR_SIZE;
break;
default:
/* Unknown packet */
BT_ERR("Unknown HCI packet with type 0x%02x received",
hci_skb_pkt_type(info->rx_skb));
info->hdev->stat.err_rx++;
kfree_skb(info->rx_skb);
info->rx_skb = NULL;
break;
}
} else {
*skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
info->rx_count--;
if (info->rx_count == 0) {
int dlen;
struct hci_event_hdr *eh;
struct hci_acl_hdr *ah;
struct hci_sco_hdr *sh;
switch (info->rx_state) {
case RECV_WAIT_EVENT_HEADER:
eh = hci_event_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = eh->plen;
break;
case RECV_WAIT_ACL_HEADER:
ah = hci_acl_hdr(info->rx_skb);
dlen = __le16_to_cpu(ah->dlen);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = dlen;
break;
case RECV_WAIT_SCO_HEADER:
sh = hci_sco_hdr(info->rx_skb);
info->rx_state = RECV_WAIT_DATA;
info->rx_count = sh->dlen;
break;
case RECV_WAIT_DATA:
hci_recv_frame(info->hdev, info->rx_skb);
info->rx_skb = NULL;
break;
}
}
}
/* Make sure we don't stay here too long */
if (boguscount++ > 16)
break;
} while (inb(iobase + UART_LSR) & UART_LSR_DR);
}
static irqreturn_t btuart_interrupt(int irq, void *dev_inst)
{
struct btuart_info *info = dev_inst;
unsigned int iobase;
int boguscount = 0;
int iir, lsr;
irqreturn_t r = IRQ_NONE;
if (!info || !info->hdev)
/* our irq handler is shared */
return IRQ_NONE;
iobase = info->p_dev->resource[0]->start;
spin_lock(&(info->lock));
iir = inb(iobase + UART_IIR) & UART_IIR_ID;
while (iir) {
r = IRQ_HANDLED;
/* Clear interrupt */
lsr = inb(iobase + UART_LSR);
switch (iir) {
case UART_IIR_RLSI:
BT_ERR("RLSI");
break;
case UART_IIR_RDI:
/* Receive interrupt */
btuart_receive(info);
break;
case UART_IIR_THRI:
if (lsr & UART_LSR_THRE) {
/* Transmitter ready for data */
btuart_write_wakeup(info);
}
break;
default:
BT_ERR("Unhandled IIR=%#x", iir);
break;
}
/* Make sure we don't stay here too long */
if (boguscount++ > 100)
break;
iir = inb(iobase + UART_IIR) & UART_IIR_ID;
}
spin_unlock(&(info->lock));
return r;
}
static void btuart_change_speed(struct btuart_info *info,
unsigned int speed)
{
unsigned long flags;
unsigned int iobase;
int fcr; /* FIFO control reg */
int lcr; /* Line control reg */
int divisor;
if (!info) {
BT_ERR("Unknown device");
return;
}
iobase = info->p_dev->resource[0]->start;
spin_lock_irqsave(&(info->lock), flags);
/* Turn off interrupts */
outb(0, iobase + UART_IER);
divisor = SPEED_MAX / speed;
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT;
/*
* Use trigger level 1 to avoid 3 ms. timeout delay at 9600 bps, and
* almost 1,7 ms at 19200 bps. At speeds above that we can just forget
* about this timeout since it will always be fast enough.
*/
if (speed < 38400)
fcr |= UART_FCR_TRIGGER_1;
else
fcr |= UART_FCR_TRIGGER_14;
/* Bluetooth cards use 8N1 */
lcr = UART_LCR_WLEN8;
outb(UART_LCR_DLAB | lcr, iobase + UART_LCR); /* Set DLAB */
outb(divisor & 0xff, iobase + UART_DLL); /* Set speed */
outb(divisor >> 8, iobase + UART_DLM);
outb(lcr, iobase + UART_LCR); /* Set 8N1 */
outb(fcr, iobase + UART_FCR); /* Enable FIFO's */
/* Turn on interrupts */
outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
spin_unlock_irqrestore(&(info->lock), flags);
}
/* ======================== HCI interface ======================== */
static int btuart_hci_flush(struct hci_dev *hdev)
{
struct btuart_info *info = hci_get_drvdata(hdev);
/* Drop TX queue */
skb_queue_purge(&(info->txq));
return 0;
}
static int btuart_hci_open(struct hci_dev *hdev)
{
return 0;
}
static int btuart_hci_close(struct hci_dev *hdev)
{
btuart_hci_flush(hdev);
return 0;
}
static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btuart_info *info = hci_get_drvdata(hdev);
switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
break;
}
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&(info->txq), skb);
btuart_write_wakeup(info);
return 0;
}
/* ======================== Card services HCI interaction ======================== */
static int btuart_open(struct btuart_info *info)
{
unsigned long flags;
unsigned int iobase = info->p_dev->resource[0]->start;
struct hci_dev *hdev;
spin_lock_init(&(info->lock));
skb_queue_head_init(&(info->txq));
info->rx_state = RECV_WAIT_PACKET_TYPE;
info->rx_count = 0;
info->rx_skb = NULL;
/* Initialize HCI device */
hdev = hci_alloc_dev();
if (!hdev) {
BT_ERR("Can't allocate HCI device");
return -ENOMEM;
}
info->hdev = hdev;
hdev->bus = HCI_PCCARD;
hci_set_drvdata(hdev, info);
SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
hdev->open = btuart_hci_open;
hdev->close = btuart_hci_close;
hdev->flush = btuart_hci_flush;
hdev->send = btuart_hci_send_frame;
spin_lock_irqsave(&(info->lock), flags);
/* Reset UART */
outb(0, iobase + UART_MCR);
/* Turn off interrupts */
outb(0, iobase + UART_IER);
/* Initialize UART */
outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
/* Turn on interrupts */
// outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
spin_unlock_irqrestore(&(info->lock), flags);
btuart_change_speed(info, DEFAULT_BAUD_RATE);
/* Timeout before it is safe to send the first HCI packet */
msleep(1000);
/* Register HCI device */
if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device");
info->hdev = NULL;
hci_free_dev(hdev);
return -ENODEV;
}
return 0;
}
static int btuart_close(struct btuart_info *info)
{
unsigned long flags;
unsigned int iobase = info->p_dev->resource[0]->start;
struct hci_dev *hdev = info->hdev;
if (!hdev)
return -ENODEV;
btuart_hci_close(hdev);
spin_lock_irqsave(&(info->lock), flags);
/* Reset UART */
outb(0, iobase + UART_MCR);
/* Turn off interrupts */
outb(0, iobase + UART_IER);
spin_unlock_irqrestore(&(info->lock), flags);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
return 0;
}
static int btuart_probe(struct pcmcia_device *link)
{
struct btuart_info *info;
/* Create new info device */
info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->p_dev = link;
link->priv = info;
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_VPP |
CONF_AUTO_SET_IO;
return btuart_config(link);
}
static void btuart_detach(struct pcmcia_device *link)
{
btuart_release(link);
}
static int btuart_check_config(struct pcmcia_device *p_dev, void *priv_data)
{
int *try = priv_data;
if (!try)
p_dev->io_lines = 16;
if ((p_dev->resource[0]->end != 8) || (p_dev->resource[0]->start == 0))
return -EINVAL;
p_dev->resource[0]->end = 8;
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
return pcmcia_request_io(p_dev);
}
static int btuart_check_config_notpicky(struct pcmcia_device *p_dev,
void *priv_data)
{
static unsigned int base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
int j;
if (p_dev->io_lines > 3)
return -ENODEV;
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
p_dev->resource[0]->end = 8;
for (j = 0; j < 5; j++) {
p_dev->resource[0]->start = base[j];
p_dev->io_lines = base[j] ? 16 : 3;
if (!pcmcia_request_io(p_dev))
return 0;
}
return -ENODEV;
}
static int btuart_config(struct pcmcia_device *link)
{
struct btuart_info *info = link->priv;
int i;
int try;
/* First pass: look for a config entry that looks normal.
Two tries: without IO aliases, then with aliases */
for (try = 0; try < 2; try++)
if (!pcmcia_loop_config(link, btuart_check_config, &try))
goto found_port;
/* Second pass: try to find an entry that isn't picky about
its base address, then try to grab any standard serial port
address, and finally try to get any free port. */
if (!pcmcia_loop_config(link, btuart_check_config_notpicky, NULL))
goto found_port;
BT_ERR("No usable port range found");
goto failed;
found_port:
i = pcmcia_request_irq(link, btuart_interrupt);
if (i != 0)
goto failed;
i = pcmcia_enable_device(link);
if (i != 0)
goto failed;
if (btuart_open(info) != 0)
goto failed;
return 0;
failed:
btuart_release(link);
return -ENODEV;
}
static void btuart_release(struct pcmcia_device *link)
{
struct btuart_info *info = link->priv;
btuart_close(info);
pcmcia_disable_device(link);
}
static const struct pcmcia_device_id btuart_ids[] = {
/* don't use this driver. Use serial_cs + hci_uart instead */
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, btuart_ids);
static struct pcmcia_driver btuart_driver = {
.owner = THIS_MODULE,
.name = "btuart_cs",
.probe = btuart_probe,
.remove = btuart_detach,
.id_table = btuart_ids,
};
module_pcmcia_driver(btuart_driver);

3368
drivers/bluetooth/btusb.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,351 @@
/*
* Texas Instrument's Bluetooth Driver For Shared Transport.
*
* Bluetooth Driver acts as interface between HCI core and
* TI Shared Transport Layer.
*
* Copyright (C) 2009-2010 Texas Instruments
* Author: Raja Mani <raja_mani@ti.com>
* Pavan Savoy <pavan_savoy@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/platform_device.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/hci.h>
#include <linux/ti_wilink_st.h>
#include <linux/module.h>
/* Bluetooth Driver Version */
#define VERSION "1.0"
#define MAX_BT_CHNL_IDS 3
/* Number of seconds to wait for registration completion
* when ST returns PENDING status.
*/
#define BT_REGISTER_TIMEOUT 6000 /* 6 sec */
/**
* struct ti_st - driver operation structure
* @hdev: hci device pointer which binds to bt driver
* @reg_status: ST registration callback status
* @st_write: write function provided by the ST driver
* to be used by the driver during send_frame.
* @wait_reg_completion - completion sync between ti_st_open
* and st_reg_completion_cb.
*/
struct ti_st {
struct hci_dev *hdev;
int reg_status;
long (*st_write) (struct sk_buff *);
struct completion wait_reg_completion;
};
/* Increments HCI counters based on pocket ID (cmd,acl,sco) */
static inline void ti_st_tx_complete(struct ti_st *hst, int pkt_type)
{
struct hci_dev *hdev = hst->hdev;
/* Update HCI stat counters */
switch (pkt_type) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
break;
}
}
/* ------- Interfaces to Shared Transport ------ */
/* Called by ST layer to indicate protocol registration completion
* status.ti_st_open() function will wait for signal from this
* API when st_register() function returns ST_PENDING.
*/
static void st_reg_completion_cb(void *priv_data, int data)
{
struct ti_st *lhst = priv_data;
/* Save registration status for use in ti_st_open() */
lhst->reg_status = data;
/* complete the wait in ti_st_open() */
complete(&lhst->wait_reg_completion);
}
/* Called by Shared Transport layer when receive data is
* available */
static long st_receive(void *priv_data, struct sk_buff *skb)
{
struct ti_st *lhst = priv_data;
int err;
if (!skb)
return -EFAULT;
if (!lhst) {
kfree_skb(skb);
return -EFAULT;
}
/* Forward skb to HCI core layer */
err = hci_recv_frame(lhst->hdev, skb);
if (err < 0) {
BT_ERR("Unable to push skb to HCI core(%d)", err);
return err;
}
lhst->hdev->stat.byte_rx += skb->len;
return 0;
}
/* ------- Interfaces to HCI layer ------ */
/* protocol structure registered with shared transport */
static struct st_proto_s ti_st_proto[MAX_BT_CHNL_IDS] = {
{
.chnl_id = HCI_EVENT_PKT, /* HCI Events */
.hdr_len = sizeof(struct hci_event_hdr),
.offset_len_in_hdr = offsetof(struct hci_event_hdr, plen),
.len_size = 1, /* sizeof(plen) in struct hci_event_hdr */
.reserve = 8,
},
{
.chnl_id = HCI_ACLDATA_PKT, /* ACL */
.hdr_len = sizeof(struct hci_acl_hdr),
.offset_len_in_hdr = offsetof(struct hci_acl_hdr, dlen),
.len_size = 2, /* sizeof(dlen) in struct hci_acl_hdr */
.reserve = 8,
},
{
.chnl_id = HCI_SCODATA_PKT, /* SCO */
.hdr_len = sizeof(struct hci_sco_hdr),
.offset_len_in_hdr = offsetof(struct hci_sco_hdr, dlen),
.len_size = 1, /* sizeof(dlen) in struct hci_sco_hdr */
.reserve = 8,
},
};
/* Called from HCI core to initialize the device */
static int ti_st_open(struct hci_dev *hdev)
{
unsigned long timeleft;
struct ti_st *hst;
int err, i;
BT_DBG("%s %p", hdev->name, hdev);
/* provide contexts for callbacks from ST */
hst = hci_get_drvdata(hdev);
for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
ti_st_proto[i].priv_data = hst;
ti_st_proto[i].max_frame_size = HCI_MAX_FRAME_SIZE;
ti_st_proto[i].recv = st_receive;
ti_st_proto[i].reg_complete_cb = st_reg_completion_cb;
/* Prepare wait-for-completion handler */
init_completion(&hst->wait_reg_completion);
/* Reset ST registration callback status flag,
* this value will be updated in
* st_reg_completion_cb()
* function whenever it called from ST driver.
*/
hst->reg_status = -EINPROGRESS;
err = st_register(&ti_st_proto[i]);
if (!err)
goto done;
if (err != -EINPROGRESS) {
BT_ERR("st_register failed %d", err);
return err;
}
/* ST is busy with either protocol
* registration or firmware download.
*/
BT_DBG("waiting for registration "
"completion signal from ST");
timeleft = wait_for_completion_timeout
(&hst->wait_reg_completion,
msecs_to_jiffies(BT_REGISTER_TIMEOUT));
if (!timeleft) {
BT_ERR("Timeout(%d sec),didn't get reg "
"completion signal from ST",
BT_REGISTER_TIMEOUT / 1000);
return -ETIMEDOUT;
}
/* Is ST registration callback
* called with ERROR status? */
if (hst->reg_status != 0) {
BT_ERR("ST registration completed with invalid "
"status %d", hst->reg_status);
return -EAGAIN;
}
done:
hst->st_write = ti_st_proto[i].write;
if (!hst->st_write) {
BT_ERR("undefined ST write function");
for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
/* Undo registration with ST */
err = st_unregister(&ti_st_proto[i]);
if (err)
BT_ERR("st_unregister() failed with "
"error %d", err);
hst->st_write = NULL;
}
return -EIO;
}
}
return 0;
}
/* Close device */
static int ti_st_close(struct hci_dev *hdev)
{
int err, i;
struct ti_st *hst = hci_get_drvdata(hdev);
for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) {
err = st_unregister(&ti_st_proto[i]);
if (err)
BT_ERR("st_unregister(%d) failed with error %d",
ti_st_proto[i].chnl_id, err);
}
hst->st_write = NULL;
return err;
}
static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct ti_st *hst;
long len;
int pkt_type;
hst = hci_get_drvdata(hdev);
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb),
skb->len);
/* Insert skb to shared transport layer's transmit queue.
* Freeing skb memory is taken care in shared transport layer,
* so don't free skb memory here.
*/
pkt_type = hci_skb_pkt_type(skb);
len = hst->st_write(skb);
if (len < 0) {
kfree_skb(skb);
BT_ERR("ST write failed (%ld)", len);
/* Try Again, would only fail if UART has gone bad */
return -EAGAIN;
}
/* ST accepted our skb. So, Go ahead and do rest */
hdev->stat.byte_tx += len;
ti_st_tx_complete(hst, pkt_type);
return 0;
}
static int bt_ti_probe(struct platform_device *pdev)
{
static struct ti_st *hst;
struct hci_dev *hdev;
int err;
hst = devm_kzalloc(&pdev->dev, sizeof(struct ti_st), GFP_KERNEL);
if (!hst)
return -ENOMEM;
/* Expose "hciX" device to user space */
hdev = hci_alloc_dev();
if (!hdev)
return -ENOMEM;
BT_DBG("hdev %p", hdev);
hst->hdev = hdev;
hdev->bus = HCI_UART;
hci_set_drvdata(hdev, hst);
hdev->open = ti_st_open;
hdev->close = ti_st_close;
hdev->flush = NULL;
hdev->send = ti_st_send_frame;
err = hci_register_dev(hdev);
if (err < 0) {
BT_ERR("Can't register HCI device error %d", err);
hci_free_dev(hdev);
return err;
}
BT_DBG("HCI device registered (hdev %p)", hdev);
dev_set_drvdata(&pdev->dev, hst);
return 0;
}
static int bt_ti_remove(struct platform_device *pdev)
{
struct hci_dev *hdev;
struct ti_st *hst = dev_get_drvdata(&pdev->dev);
if (!hst)
return -EFAULT;
BT_DBG("%s", hst->hdev->name);
hdev = hst->hdev;
ti_st_close(hdev);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
static struct platform_driver btwilink_driver = {
.probe = bt_ti_probe,
.remove = bt_ti_remove,
.driver = {
.name = "btwilink",
},
};
module_platform_driver(btwilink_driver);
/* ------ Module Info ------ */
MODULE_AUTHOR("Raja Mani <raja_mani@ti.com>");
MODULE_DESCRIPTION("Bluetooth Driver for TI Shared Transport" VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");

614
drivers/bluetooth/dtl1_cs.c Normal file
View File

@@ -0,0 +1,614 @@
/*
*
* A driver for Nokia Connectivity Card DTL-1 devices
*
* Copyright (C) 2001-2002 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The initial developer of the original code is David A. Hinds
* <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
* are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/spinlock.h>
#include <linux/moduleparam.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/bitops.h>
#include <asm/io.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
#include <pcmcia/ds.h>
#include <pcmcia/cisreg.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
/* ======================== Module parameters ======================== */
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth driver for Nokia Connectivity Card DTL-1");
MODULE_LICENSE("GPL");
/* ======================== Local structures ======================== */
struct dtl1_info {
struct pcmcia_device *p_dev;
struct hci_dev *hdev;
spinlock_t lock; /* For serializing operations */
unsigned long flowmask; /* HCI flow mask */
int ri_latch;
struct sk_buff_head txq;
unsigned long tx_state;
unsigned long rx_state;
unsigned long rx_count;
struct sk_buff *rx_skb;
};
static int dtl1_config(struct pcmcia_device *link);
/* Transmit states */
#define XMIT_SENDING 1
#define XMIT_WAKEUP 2
#define XMIT_WAITING 8
/* Receiver States */
#define RECV_WAIT_NSH 0
#define RECV_WAIT_DATA 1
struct nsh {
u8 type;
u8 zero;
u16 len;
} __packed; /* Nokia Specific Header */
#define NSHL 4 /* Nokia Specific Header Length */
/* ======================== Interrupt handling ======================== */
static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len)
{
int actual = 0;
/* Tx FIFO should be empty */
if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
return 0;
/* Fill FIFO with current frame */
while ((fifo_size-- > 0) && (actual < len)) {
/* Transmit next byte */
outb(buf[actual], iobase + UART_TX);
actual++;
}
return actual;
}
static void dtl1_write_wakeup(struct dtl1_info *info)
{
if (!info) {
BT_ERR("Unknown device");
return;
}
if (test_bit(XMIT_WAITING, &(info->tx_state))) {
set_bit(XMIT_WAKEUP, &(info->tx_state));
return;
}
if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
set_bit(XMIT_WAKEUP, &(info->tx_state));
return;
}
do {
unsigned int iobase = info->p_dev->resource[0]->start;
register struct sk_buff *skb;
int len;
clear_bit(XMIT_WAKEUP, &(info->tx_state));
if (!pcmcia_dev_present(info->p_dev))
return;
skb = skb_dequeue(&(info->txq));
if (!skb)
break;
/* Send frame */
len = dtl1_write(iobase, 32, skb->data, skb->len);
if (len == skb->len) {
set_bit(XMIT_WAITING, &(info->tx_state));
kfree_skb(skb);
} else {
skb_pull(skb, len);
skb_queue_head(&(info->txq), skb);
}
info->hdev->stat.byte_tx += len;
} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
clear_bit(XMIT_SENDING, &(info->tx_state));
}
static void dtl1_control(struct dtl1_info *info, struct sk_buff *skb)
{
u8 flowmask = *(u8 *)skb->data;
int i;
printk(KERN_INFO "Bluetooth: Nokia control data =");
for (i = 0; i < skb->len; i++)
printk(" %02x", skb->data[i]);
printk("\n");
/* transition to active state */
if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) {
clear_bit(XMIT_WAITING, &(info->tx_state));
dtl1_write_wakeup(info);
}
info->flowmask = flowmask;
kfree_skb(skb);
}
static void dtl1_receive(struct dtl1_info *info)
{
unsigned int iobase;
struct nsh *nsh;
int boguscount = 0;
if (!info) {
BT_ERR("Unknown device");
return;
}
iobase = info->p_dev->resource[0]->start;
do {
info->hdev->stat.byte_rx++;
/* Allocate packet */
if (info->rx_skb == NULL) {
info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
if (!info->rx_skb) {
BT_ERR("Can't allocate mem for new packet");
info->rx_state = RECV_WAIT_NSH;
info->rx_count = NSHL;
return;
}
}
*skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
nsh = (struct nsh *)info->rx_skb->data;
info->rx_count--;
if (info->rx_count == 0) {
switch (info->rx_state) {
case RECV_WAIT_NSH:
info->rx_state = RECV_WAIT_DATA;
info->rx_count = nsh->len + (nsh->len & 0x0001);
break;
case RECV_WAIT_DATA:
hci_skb_pkt_type(info->rx_skb) = nsh->type;
/* remove PAD byte if it exists */
if (nsh->len & 0x0001) {
info->rx_skb->tail--;
info->rx_skb->len--;
}
/* remove NSH */
skb_pull(info->rx_skb, NSHL);
switch (hci_skb_pkt_type(info->rx_skb)) {
case 0x80:
/* control data for the Nokia Card */
dtl1_control(info, info->rx_skb);
break;
case 0x82:
case 0x83:
case 0x84:
/* send frame to the HCI layer */
hci_skb_pkt_type(info->rx_skb) &= 0x0f;
hci_recv_frame(info->hdev, info->rx_skb);
break;
default:
/* unknown packet */
BT_ERR("Unknown HCI packet with type 0x%02x received",
hci_skb_pkt_type(info->rx_skb));
kfree_skb(info->rx_skb);
break;
}
info->rx_state = RECV_WAIT_NSH;
info->rx_count = NSHL;
info->rx_skb = NULL;
break;
}
}
/* Make sure we don't stay here too long */
if (boguscount++ > 32)
break;
} while (inb(iobase + UART_LSR) & UART_LSR_DR);
}
static irqreturn_t dtl1_interrupt(int irq, void *dev_inst)
{
struct dtl1_info *info = dev_inst;
unsigned int iobase;
unsigned char msr;
int boguscount = 0;
int iir, lsr;
irqreturn_t r = IRQ_NONE;
if (!info || !info->hdev)
/* our irq handler is shared */
return IRQ_NONE;
iobase = info->p_dev->resource[0]->start;
spin_lock(&(info->lock));
iir = inb(iobase + UART_IIR) & UART_IIR_ID;
while (iir) {
r = IRQ_HANDLED;
/* Clear interrupt */
lsr = inb(iobase + UART_LSR);
switch (iir) {
case UART_IIR_RLSI:
BT_ERR("RLSI");
break;
case UART_IIR_RDI:
/* Receive interrupt */
dtl1_receive(info);
break;
case UART_IIR_THRI:
if (lsr & UART_LSR_THRE) {
/* Transmitter ready for data */
dtl1_write_wakeup(info);
}
break;
default:
BT_ERR("Unhandled IIR=%#x", iir);
break;
}
/* Make sure we don't stay here too long */
if (boguscount++ > 100)
break;
iir = inb(iobase + UART_IIR) & UART_IIR_ID;
}
msr = inb(iobase + UART_MSR);
if (info->ri_latch ^ (msr & UART_MSR_RI)) {
info->ri_latch = msr & UART_MSR_RI;
clear_bit(XMIT_WAITING, &(info->tx_state));
dtl1_write_wakeup(info);
r = IRQ_HANDLED;
}
spin_unlock(&(info->lock));
return r;
}
/* ======================== HCI interface ======================== */
static int dtl1_hci_open(struct hci_dev *hdev)
{
return 0;
}
static int dtl1_hci_flush(struct hci_dev *hdev)
{
struct dtl1_info *info = hci_get_drvdata(hdev);
/* Drop TX queue */
skb_queue_purge(&(info->txq));
return 0;
}
static int dtl1_hci_close(struct hci_dev *hdev)
{
dtl1_hci_flush(hdev);
return 0;
}
static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct dtl1_info *info = hci_get_drvdata(hdev);
struct sk_buff *s;
struct nsh nsh;
switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
nsh.type = 0x81;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
nsh.type = 0x82;
break;
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
nsh.type = 0x83;
break;
default:
return -EILSEQ;
}
nsh.zero = 0;
nsh.len = skb->len;
s = bt_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC);
if (!s)
return -ENOMEM;
skb_reserve(s, NSHL);
skb_copy_from_linear_data(skb, skb_put(s, skb->len), skb->len);
if (skb->len & 0x0001)
*skb_put(s, 1) = 0; /* PAD */
/* Prepend skb with Nokia frame header and queue */
memcpy(skb_push(s, NSHL), &nsh, NSHL);
skb_queue_tail(&(info->txq), s);
dtl1_write_wakeup(info);
kfree_skb(skb);
return 0;
}
/* ======================== Card services HCI interaction ======================== */
static int dtl1_open(struct dtl1_info *info)
{
unsigned long flags;
unsigned int iobase = info->p_dev->resource[0]->start;
struct hci_dev *hdev;
spin_lock_init(&(info->lock));
skb_queue_head_init(&(info->txq));
info->rx_state = RECV_WAIT_NSH;
info->rx_count = NSHL;
info->rx_skb = NULL;
set_bit(XMIT_WAITING, &(info->tx_state));
/* Initialize HCI device */
hdev = hci_alloc_dev();
if (!hdev) {
BT_ERR("Can't allocate HCI device");
return -ENOMEM;
}
info->hdev = hdev;
hdev->bus = HCI_PCCARD;
hci_set_drvdata(hdev, info);
SET_HCIDEV_DEV(hdev, &info->p_dev->dev);
hdev->open = dtl1_hci_open;
hdev->close = dtl1_hci_close;
hdev->flush = dtl1_hci_flush;
hdev->send = dtl1_hci_send_frame;
spin_lock_irqsave(&(info->lock), flags);
/* Reset UART */
outb(0, iobase + UART_MCR);
/* Turn off interrupts */
outb(0, iobase + UART_IER);
/* Initialize UART */
outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */
outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
info->ri_latch = inb(info->p_dev->resource[0]->start + UART_MSR)
& UART_MSR_RI;
/* Turn on interrupts */
outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
spin_unlock_irqrestore(&(info->lock), flags);
/* Timeout before it is safe to send the first HCI packet */
msleep(2000);
/* Register HCI device */
if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device");
info->hdev = NULL;
hci_free_dev(hdev);
return -ENODEV;
}
return 0;
}
static int dtl1_close(struct dtl1_info *info)
{
unsigned long flags;
unsigned int iobase = info->p_dev->resource[0]->start;
struct hci_dev *hdev = info->hdev;
if (!hdev)
return -ENODEV;
dtl1_hci_close(hdev);
spin_lock_irqsave(&(info->lock), flags);
/* Reset UART */
outb(0, iobase + UART_MCR);
/* Turn off interrupts */
outb(0, iobase + UART_IER);
spin_unlock_irqrestore(&(info->lock), flags);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
return 0;
}
static int dtl1_probe(struct pcmcia_device *link)
{
struct dtl1_info *info;
/* Create new info device */
info = devm_kzalloc(&link->dev, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->p_dev = link;
link->priv = info;
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
return dtl1_config(link);
}
static void dtl1_detach(struct pcmcia_device *link)
{
struct dtl1_info *info = link->priv;
dtl1_close(info);
pcmcia_disable_device(link);
}
static int dtl1_confcheck(struct pcmcia_device *p_dev, void *priv_data)
{
if ((p_dev->resource[1]->end) || (p_dev->resource[1]->end < 8))
return -ENODEV;
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8;
return pcmcia_request_io(p_dev);
}
static int dtl1_config(struct pcmcia_device *link)
{
struct dtl1_info *info = link->priv;
int ret;
/* Look for a generic full-sized window */
link->resource[0]->end = 8;
ret = pcmcia_loop_config(link, dtl1_confcheck, NULL);
if (ret)
goto failed;
ret = pcmcia_request_irq(link, dtl1_interrupt);
if (ret)
goto failed;
ret = pcmcia_enable_device(link);
if (ret)
goto failed;
ret = dtl1_open(info);
if (ret)
goto failed;
return 0;
failed:
dtl1_detach(link);
return ret;
}
static const struct pcmcia_device_id dtl1_ids[] = {
PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-1", 0xe1bfdd64, 0xe168480d),
PCMCIA_DEVICE_PROD_ID12("Nokia Mobile Phones", "DTL-4", 0xe1bfdd64, 0x9102bc82),
PCMCIA_DEVICE_PROD_ID12("Socket", "CF", 0xb38bcc2e, 0x44ebf863),
PCMCIA_DEVICE_PROD_ID12("Socket", "CF+ Personal Network Card", 0xb38bcc2e, 0xe732bae3),
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, dtl1_ids);
static struct pcmcia_driver dtl1_driver = {
.owner = THIS_MODULE,
.name = "dtl1_cs",
.probe = dtl1_probe,
.remove = dtl1_detach,
.id_table = dtl1_ids,
};
module_pcmcia_driver(dtl1_driver);

View File

@@ -0,0 +1,337 @@
/*
*
* Bluetooth HCI UART driver for Intel/AG6xx devices
*
* Copyright (C) 2016 Intel Corporation
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
#include "btintel.h"
struct ag6xx_data {
struct sk_buff *rx_skb;
struct sk_buff_head txq;
};
struct pbn_entry {
__le32 addr;
__le32 plen;
__u8 data[0];
} __packed;
static int ag6xx_open(struct hci_uart *hu)
{
struct ag6xx_data *ag6xx;
BT_DBG("hu %p", hu);
ag6xx = kzalloc(sizeof(*ag6xx), GFP_KERNEL);
if (!ag6xx)
return -ENOMEM;
skb_queue_head_init(&ag6xx->txq);
hu->priv = ag6xx;
return 0;
}
static int ag6xx_close(struct hci_uart *hu)
{
struct ag6xx_data *ag6xx = hu->priv;
BT_DBG("hu %p", hu);
skb_queue_purge(&ag6xx->txq);
kfree_skb(ag6xx->rx_skb);
kfree(ag6xx);
hu->priv = NULL;
return 0;
}
static int ag6xx_flush(struct hci_uart *hu)
{
struct ag6xx_data *ag6xx = hu->priv;
BT_DBG("hu %p", hu);
skb_queue_purge(&ag6xx->txq);
return 0;
}
static struct sk_buff *ag6xx_dequeue(struct hci_uart *hu)
{
struct ag6xx_data *ag6xx = hu->priv;
struct sk_buff *skb;
skb = skb_dequeue(&ag6xx->txq);
if (!skb)
return skb;
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
return skb;
}
static int ag6xx_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct ag6xx_data *ag6xx = hu->priv;
skb_queue_tail(&ag6xx->txq, skb);
return 0;
}
static const struct h4_recv_pkt ag6xx_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
};
static int ag6xx_recv(struct hci_uart *hu, const void *data, int count)
{
struct ag6xx_data *ag6xx = hu->priv;
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
ag6xx->rx_skb = h4_recv_buf(hu->hdev, ag6xx->rx_skb, data, count,
ag6xx_recv_pkts,
ARRAY_SIZE(ag6xx_recv_pkts));
if (IS_ERR(ag6xx->rx_skb)) {
int err = PTR_ERR(ag6xx->rx_skb);
bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
ag6xx->rx_skb = NULL;
return err;
}
return count;
}
static int intel_mem_write(struct hci_dev *hdev, u32 addr, u32 plen,
const void *data)
{
/* Can write a maximum of 247 bytes per HCI command.
* HCI cmd Header (3), Intel mem write header (6), data (247).
*/
while (plen > 0) {
struct sk_buff *skb;
u8 cmd_param[253], fragment_len = (plen > 247) ? 247 : plen;
__le32 leaddr = cpu_to_le32(addr);
memcpy(cmd_param, &leaddr, 4);
cmd_param[4] = 0;
cmd_param[5] = fragment_len;
memcpy(cmd_param + 6, data, fragment_len);
skb = __hci_cmd_sync(hdev, 0xfc8e, fragment_len + 6, cmd_param,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb))
return PTR_ERR(skb);
kfree_skb(skb);
plen -= fragment_len;
data += fragment_len;
addr += fragment_len;
}
return 0;
}
static int ag6xx_setup(struct hci_uart *hu)
{
struct hci_dev *hdev = hu->hdev;
struct sk_buff *skb;
struct intel_version ver;
const struct firmware *fw;
const u8 *fw_ptr;
char fwname[64];
bool patched = false;
int err;
hu->hdev->set_diag = btintel_set_diag;
hu->hdev->set_bdaddr = btintel_set_bdaddr;
err = btintel_enter_mfg(hdev);
if (err)
return err;
err = btintel_read_version(hdev, &ver);
if (err)
return err;
btintel_version_info(hdev, &ver);
/* The hardware platform number has a fixed value of 0x37 and
* for now only accept this single value.
*/
if (ver.hw_platform != 0x37) {
bt_dev_err(hdev, "Unsupported Intel hardware platform: 0x%X",
ver.hw_platform);
return -EINVAL;
}
/* Only the hardware variant iBT 2.1 (AG6XX) is supported by this
* firmware setup method.
*/
if (ver.hw_variant != 0x0a) {
bt_dev_err(hdev, "Unsupported Intel hardware variant: 0x%x",
ver.hw_variant);
return -EINVAL;
}
snprintf(fwname, sizeof(fwname), "intel/ibt-hw-%x.%x.bddata",
ver.hw_platform, ver.hw_variant);
err = request_firmware(&fw, fwname, &hdev->dev);
if (err < 0) {
bt_dev_err(hdev, "Failed to open Intel bddata file: %s (%d)",
fwname, err);
goto patch;
}
fw_ptr = fw->data;
bt_dev_info(hdev, "Applying bddata (%s)", fwname);
skb = __hci_cmd_sync_ev(hdev, 0xfc2f, fw->size, fw->data,
HCI_EV_CMD_STATUS, HCI_CMD_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Applying bddata failed (%ld)", PTR_ERR(skb));
release_firmware(fw);
return PTR_ERR(skb);
}
kfree_skb(skb);
release_firmware(fw);
patch:
/* If there is no applied patch, fw_patch_num is always 0x00. In other
* cases, current firmware is already patched. No need to patch it.
*/
if (ver.fw_patch_num) {
bt_dev_info(hdev, "Device is already patched. patch num: %02x",
ver.fw_patch_num);
patched = true;
goto complete;
}
snprintf(fwname, sizeof(fwname),
"intel/ibt-hw-%x.%x.%x-fw-%x.%x.%x.%x.%x.pbn",
ver.hw_platform, ver.hw_variant, ver.hw_revision,
ver.fw_variant, ver.fw_revision, ver.fw_build_num,
ver.fw_build_ww, ver.fw_build_yy);
err = request_firmware(&fw, fwname, &hdev->dev);
if (err < 0) {
bt_dev_err(hdev, "Failed to open Intel patch file: %s(%d)",
fwname, err);
goto complete;
}
fw_ptr = fw->data;
bt_dev_info(hdev, "Patching firmware file (%s)", fwname);
/* PBN patch file contains a list of binary patches to be applied on top
* of the embedded firmware. Each patch entry header contains the target
* address and patch size.
*
* Patch entry:
* | addr(le) | patch_len(le) | patch_data |
* | 4 Bytes | 4 Bytes | n Bytes |
*
* PBN file is terminated by a patch entry whose address is 0xffffffff.
*/
while (fw->size > fw_ptr - fw->data) {
struct pbn_entry *pbn = (void *)fw_ptr;
u32 addr, plen;
if (pbn->addr == 0xffffffff) {
bt_dev_info(hdev, "Patching complete");
patched = true;
break;
}
addr = le32_to_cpu(pbn->addr);
plen = le32_to_cpu(pbn->plen);
if (fw->data + fw->size <= pbn->data + plen) {
bt_dev_info(hdev, "Invalid patch len (%d)", plen);
break;
}
bt_dev_info(hdev, "Patching %td/%zu", (fw_ptr - fw->data),
fw->size);
err = intel_mem_write(hdev, addr, plen, pbn->data);
if (err) {
bt_dev_err(hdev, "Patching failed");
break;
}
fw_ptr = pbn->data + plen;
}
release_firmware(fw);
complete:
/* Exit manufacturing mode and reset */
err = btintel_exit_mfg(hdev, true, patched);
if (err)
return err;
/* Set the event mask for Intel specific vendor events. This enables
* a few extra events that are useful during general operation.
*/
btintel_set_event_mask_mfg(hdev, false);
btintel_check_bdaddr(hdev);
return 0;
}
static const struct hci_uart_proto ag6xx_proto = {
.id = HCI_UART_AG6XX,
.name = "AG6XX",
.manufacturer = 2,
.open = ag6xx_open,
.close = ag6xx_close,
.flush = ag6xx_flush,
.setup = ag6xx_setup,
.recv = ag6xx_recv,
.enqueue = ag6xx_enqueue,
.dequeue = ag6xx_dequeue,
};
int __init ag6xx_init(void)
{
return hci_uart_register_proto(&ag6xx_proto);
}
int __exit ag6xx_deinit(void)
{
return hci_uart_unregister_proto(&ag6xx_proto);
}

264
drivers/bluetooth/hci_ath.c Normal file
View File

@@ -0,0 +1,264 @@
/*
* Atheros Communication Bluetooth HCIATH3K UART protocol
*
* HCIATH3K (HCI Atheros AR300x Protocol) is a Atheros Communication's
* power management protocol extension to H4 to support AR300x Bluetooth Chip.
*
* Copyright (c) 2009-2010 Atheros Communications Inc.
*
* Acknowledgements:
* This file is based on hci_h4.c, which was written
* by Maxim Krasnyansky and Marcel Holtmann.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/skbuff.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
struct ath_struct {
struct hci_uart *hu;
unsigned int cur_sleep;
struct sk_buff *rx_skb;
struct sk_buff_head txq;
struct work_struct ctxtsw;
};
static int ath_wakeup_ar3k(struct tty_struct *tty)
{
int status = tty->driver->ops->tiocmget(tty);
if (status & TIOCM_CTS)
return status;
/* Clear RTS first */
tty->driver->ops->tiocmget(tty);
tty->driver->ops->tiocmset(tty, 0x00, TIOCM_RTS);
mdelay(20);
/* Set RTS, wake up board */
tty->driver->ops->tiocmget(tty);
tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
mdelay(20);
status = tty->driver->ops->tiocmget(tty);
return status;
}
static void ath_hci_uart_work(struct work_struct *work)
{
int status;
struct ath_struct *ath;
struct hci_uart *hu;
struct tty_struct *tty;
ath = container_of(work, struct ath_struct, ctxtsw);
hu = ath->hu;
tty = hu->tty;
/* verify and wake up controller */
if (ath->cur_sleep) {
status = ath_wakeup_ar3k(tty);
if (!(status & TIOCM_CTS))
return;
}
/* Ready to send Data */
clear_bit(HCI_UART_SENDING, &hu->tx_state);
hci_uart_tx_wakeup(hu);
}
static int ath_open(struct hci_uart *hu)
{
struct ath_struct *ath;
BT_DBG("hu %p", hu);
ath = kzalloc(sizeof(*ath), GFP_KERNEL);
if (!ath)
return -ENOMEM;
skb_queue_head_init(&ath->txq);
hu->priv = ath;
ath->hu = hu;
INIT_WORK(&ath->ctxtsw, ath_hci_uart_work);
return 0;
}
static int ath_close(struct hci_uart *hu)
{
struct ath_struct *ath = hu->priv;
BT_DBG("hu %p", hu);
skb_queue_purge(&ath->txq);
kfree_skb(ath->rx_skb);
cancel_work_sync(&ath->ctxtsw);
hu->priv = NULL;
kfree(ath);
return 0;
}
static int ath_flush(struct hci_uart *hu)
{
struct ath_struct *ath = hu->priv;
BT_DBG("hu %p", hu);
skb_queue_purge(&ath->txq);
return 0;
}
static int ath_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
struct sk_buff *skb;
u8 buf[10];
int err;
buf[0] = 0x01;
buf[1] = 0x01;
buf[2] = 0x00;
buf[3] = sizeof(bdaddr_t);
memcpy(buf + 4, bdaddr, sizeof(bdaddr_t));
skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
BT_ERR("%s: Change address command failed (%d)",
hdev->name, err);
return err;
}
kfree_skb(skb);
return 0;
}
static int ath_setup(struct hci_uart *hu)
{
BT_DBG("hu %p", hu);
hu->hdev->set_bdaddr = ath_set_bdaddr;
return 0;
}
static const struct h4_recv_pkt ath_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
};
static int ath_recv(struct hci_uart *hu, const void *data, int count)
{
struct ath_struct *ath = hu->priv;
ath->rx_skb = h4_recv_buf(hu->hdev, ath->rx_skb, data, count,
ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts));
if (IS_ERR(ath->rx_skb)) {
int err = PTR_ERR(ath->rx_skb);
BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
ath->rx_skb = NULL;
return err;
}
return count;
}
#define HCI_OP_ATH_SLEEP 0xFC04
static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct ath_struct *ath = hu->priv;
if (hci_skb_pkt_type(skb) == HCI_SCODATA_PKT) {
kfree_skb(skb);
return 0;
}
/* Update power management enable flag with parameters of
* HCI sleep enable vendor specific HCI command.
*/
if (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT) {
struct hci_command_hdr *hdr = (void *)skb->data;
if (__le16_to_cpu(hdr->opcode) == HCI_OP_ATH_SLEEP)
ath->cur_sleep = skb->data[HCI_COMMAND_HDR_SIZE];
}
BT_DBG("hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&ath->txq, skb);
set_bit(HCI_UART_SENDING, &hu->tx_state);
schedule_work(&ath->ctxtsw);
return 0;
}
static struct sk_buff *ath_dequeue(struct hci_uart *hu)
{
struct ath_struct *ath = hu->priv;
return skb_dequeue(&ath->txq);
}
static const struct hci_uart_proto athp = {
.id = HCI_UART_ATH3K,
.name = "ATH3K",
.manufacturer = 69,
.open = ath_open,
.close = ath_close,
.flush = ath_flush,
.setup = ath_setup,
.recv = ath_recv,
.enqueue = ath_enqueue,
.dequeue = ath_dequeue,
};
int __init ath_init(void)
{
return hci_uart_register_proto(&athp);
}
int __exit ath_deinit(void)
{
return hci_uart_unregister_proto(&athp);
}

872
drivers/bluetooth/hci_bcm.c Normal file
View File

@@ -0,0 +1,872 @@
/*
*
* Bluetooth HCI UART driver for Broadcom devices
*
* Copyright (C) 2015 Intel Corporation
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/tty.h>
#include <linux/interrupt.h>
#include <linux/dmi.h>
#include <linux/pm_runtime.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btbcm.h"
#include "hci_uart.h"
#define BCM_LM_DIAG_PKT 0x07
#define BCM_LM_DIAG_SIZE 63
#define BCM_AUTOSUSPEND_DELAY 5000 /* default autosleep delay */
struct bcm_device {
struct list_head list;
struct platform_device *pdev;
const char *name;
struct gpio_desc *device_wakeup;
struct gpio_desc *shutdown;
struct clk *clk;
bool clk_enabled;
u32 init_speed;
int irq;
u8 irq_polarity;
#ifdef CONFIG_PM
struct hci_uart *hu;
bool is_suspended; /* suspend/resume flag */
#endif
};
struct bcm_data {
struct sk_buff *rx_skb;
struct sk_buff_head txq;
struct bcm_device *dev;
};
/* List of BCM BT UART devices */
static DEFINE_MUTEX(bcm_device_lock);
static LIST_HEAD(bcm_device_list);
static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
{
struct hci_dev *hdev = hu->hdev;
struct sk_buff *skb;
struct bcm_update_uart_baud_rate param;
if (speed > 3000000) {
struct bcm_write_uart_clock_setting clock;
clock.type = BCM_UART_CLOCK_48MHZ;
bt_dev_dbg(hdev, "Set Controller clock (%d)", clock.type);
/* This Broadcom specific command changes the UART's controller
* clock for baud rate > 3000000.
*/
skb = __hci_cmd_sync(hdev, 0xfc45, 1, &clock, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
int err = PTR_ERR(skb);
bt_dev_err(hdev, "BCM: failed to write clock (%d)",
err);
return err;
}
kfree_skb(skb);
}
bt_dev_dbg(hdev, "Set Controller UART speed to %d bit/s", speed);
param.zero = cpu_to_le16(0);
param.baud_rate = cpu_to_le32(speed);
/* This Broadcom specific command changes the UART's controller baud
* rate.
*/
skb = __hci_cmd_sync(hdev, 0xfc18, sizeof(param), &param,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
int err = PTR_ERR(skb);
bt_dev_err(hdev, "BCM: failed to write update baudrate (%d)",
err);
return err;
}
kfree_skb(skb);
return 0;
}
/* bcm_device_exists should be protected by bcm_device_lock */
static bool bcm_device_exists(struct bcm_device *device)
{
struct list_head *p;
list_for_each(p, &bcm_device_list) {
struct bcm_device *dev = list_entry(p, struct bcm_device, list);
if (device == dev)
return true;
}
return false;
}
static int bcm_gpio_set_power(struct bcm_device *dev, bool powered)
{
if (powered && !IS_ERR(dev->clk) && !dev->clk_enabled)
clk_enable(dev->clk);
gpiod_set_value(dev->shutdown, powered);
gpiod_set_value(dev->device_wakeup, powered);
if (!powered && !IS_ERR(dev->clk) && dev->clk_enabled)
clk_disable(dev->clk);
dev->clk_enabled = powered;
return 0;
}
#ifdef CONFIG_PM
static irqreturn_t bcm_host_wake(int irq, void *data)
{
struct bcm_device *bdev = data;
bt_dev_dbg(bdev, "Host wake IRQ");
pm_runtime_get(&bdev->pdev->dev);
pm_runtime_mark_last_busy(&bdev->pdev->dev);
pm_runtime_put_autosuspend(&bdev->pdev->dev);
return IRQ_HANDLED;
}
static int bcm_request_irq(struct bcm_data *bcm)
{
struct bcm_device *bdev = bcm->dev;
int err = 0;
/* If this is not a platform device, do not enable PM functionalities */
mutex_lock(&bcm_device_lock);
if (!bcm_device_exists(bdev)) {
err = -ENODEV;
goto unlock;
}
if (bdev->irq > 0) {
err = devm_request_irq(&bdev->pdev->dev, bdev->irq,
bcm_host_wake, IRQF_TRIGGER_RISING,
"host_wake", bdev);
if (err)
goto unlock;
device_init_wakeup(&bdev->pdev->dev, true);
pm_runtime_set_autosuspend_delay(&bdev->pdev->dev,
BCM_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(&bdev->pdev->dev);
pm_runtime_set_active(&bdev->pdev->dev);
pm_runtime_enable(&bdev->pdev->dev);
}
unlock:
mutex_unlock(&bcm_device_lock);
return err;
}
static const struct bcm_set_sleep_mode default_sleep_params = {
.sleep_mode = 1, /* 0=Disabled, 1=UART, 2=Reserved, 3=USB */
.idle_host = 2, /* idle threshold HOST, in 300ms */
.idle_dev = 2, /* idle threshold device, in 300ms */
.bt_wake_active = 1, /* BT_WAKE active mode: 1 = high, 0 = low */
.host_wake_active = 0, /* HOST_WAKE active mode: 1 = high, 0 = low */
.allow_host_sleep = 1, /* Allow host sleep in SCO flag */
.combine_modes = 1, /* Combine sleep and LPM flag */
.tristate_control = 0, /* Allow tri-state control of UART tx flag */
/* Irrelevant USB flags */
.usb_auto_sleep = 0,
.usb_resume_timeout = 0,
.pulsed_host_wake = 0,
.break_to_host = 0
};
static int bcm_setup_sleep(struct hci_uart *hu)
{
struct bcm_data *bcm = hu->priv;
struct sk_buff *skb;
struct bcm_set_sleep_mode sleep_params = default_sleep_params;
sleep_params.host_wake_active = !bcm->dev->irq_polarity;
skb = __hci_cmd_sync(hu->hdev, 0xfc27, sizeof(sleep_params),
&sleep_params, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
int err = PTR_ERR(skb);
bt_dev_err(hu->hdev, "Sleep VSC failed (%d)", err);
return err;
}
kfree_skb(skb);
bt_dev_dbg(hu->hdev, "Set Sleep Parameters VSC succeeded");
return 0;
}
#else
static inline int bcm_request_irq(struct bcm_data *bcm) { return 0; }
static inline int bcm_setup_sleep(struct hci_uart *hu) { return 0; }
#endif
static int bcm_set_diag(struct hci_dev *hdev, bool enable)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct bcm_data *bcm = hu->priv;
struct sk_buff *skb;
if (!test_bit(HCI_RUNNING, &hdev->flags))
return -ENETDOWN;
skb = bt_skb_alloc(3, GFP_KERNEL);
if (!skb)
return -ENOMEM;
*skb_put(skb, 1) = BCM_LM_DIAG_PKT;
*skb_put(skb, 1) = 0xf0;
*skb_put(skb, 1) = enable;
skb_queue_tail(&bcm->txq, skb);
hci_uart_tx_wakeup(hu);
return 0;
}
static int bcm_open(struct hci_uart *hu)
{
struct bcm_data *bcm;
struct list_head *p;
bt_dev_dbg(hu->hdev, "hu %p", hu);
bcm = kzalloc(sizeof(*bcm), GFP_KERNEL);
if (!bcm)
return -ENOMEM;
skb_queue_head_init(&bcm->txq);
hu->priv = bcm;
mutex_lock(&bcm_device_lock);
list_for_each(p, &bcm_device_list) {
struct bcm_device *dev = list_entry(p, struct bcm_device, list);
/* Retrieve saved bcm_device based on parent of the
* platform device (saved during device probe) and
* parent of tty device used by hci_uart
*/
if (hu->tty->dev->parent == dev->pdev->dev.parent) {
bcm->dev = dev;
hu->init_speed = dev->init_speed;
#ifdef CONFIG_PM
dev->hu = hu;
#endif
bcm_gpio_set_power(bcm->dev, true);
break;
}
}
mutex_unlock(&bcm_device_lock);
return 0;
}
static int bcm_close(struct hci_uart *hu)
{
struct bcm_data *bcm = hu->priv;
struct bcm_device *bdev = bcm->dev;
bt_dev_dbg(hu->hdev, "hu %p", hu);
/* Protect bcm->dev against removal of the device or driver */
mutex_lock(&bcm_device_lock);
if (bcm_device_exists(bdev)) {
bcm_gpio_set_power(bdev, false);
#ifdef CONFIG_PM
pm_runtime_disable(&bdev->pdev->dev);
pm_runtime_set_suspended(&bdev->pdev->dev);
if (device_can_wakeup(&bdev->pdev->dev)) {
devm_free_irq(&bdev->pdev->dev, bdev->irq, bdev);
device_init_wakeup(&bdev->pdev->dev, false);
}
bdev->hu = NULL;
#endif
}
mutex_unlock(&bcm_device_lock);
skb_queue_purge(&bcm->txq);
kfree_skb(bcm->rx_skb);
kfree(bcm);
hu->priv = NULL;
return 0;
}
static int bcm_flush(struct hci_uart *hu)
{
struct bcm_data *bcm = hu->priv;
bt_dev_dbg(hu->hdev, "hu %p", hu);
skb_queue_purge(&bcm->txq);
return 0;
}
static int bcm_setup(struct hci_uart *hu)
{
struct bcm_data *bcm = hu->priv;
char fw_name[64];
const struct firmware *fw;
unsigned int speed;
int err;
bt_dev_dbg(hu->hdev, "hu %p", hu);
hu->hdev->set_diag = bcm_set_diag;
hu->hdev->set_bdaddr = btbcm_set_bdaddr;
err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name));
if (err)
return err;
err = request_firmware(&fw, fw_name, &hu->hdev->dev);
if (err < 0) {
bt_dev_info(hu->hdev, "BCM: Patch %s not found", fw_name);
return 0;
}
err = btbcm_patchram(hu->hdev, fw);
if (err) {
bt_dev_info(hu->hdev, "BCM: Patch failed (%d)", err);
goto finalize;
}
/* Init speed if any */
if (hu->init_speed)
speed = hu->init_speed;
else if (hu->proto->init_speed)
speed = hu->proto->init_speed;
else
speed = 0;
if (speed)
hci_uart_set_baudrate(hu, speed);
/* Operational speed if any */
if (hu->oper_speed)
speed = hu->oper_speed;
else if (hu->proto->oper_speed)
speed = hu->proto->oper_speed;
else
speed = 0;
if (speed) {
err = bcm_set_baudrate(hu, speed);
if (!err)
hci_uart_set_baudrate(hu, speed);
}
finalize:
release_firmware(fw);
err = btbcm_finalize(hu->hdev);
if (err)
return err;
err = bcm_request_irq(bcm);
if (!err)
err = bcm_setup_sleep(hu);
return err;
}
#define BCM_RECV_LM_DIAG \
.type = BCM_LM_DIAG_PKT, \
.hlen = BCM_LM_DIAG_SIZE, \
.loff = 0, \
.lsize = 0, \
.maxlen = BCM_LM_DIAG_SIZE
static const struct h4_recv_pkt bcm_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
{ BCM_RECV_LM_DIAG, .recv = hci_recv_diag },
};
static int bcm_recv(struct hci_uart *hu, const void *data, int count)
{
struct bcm_data *bcm = hu->priv;
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
bcm->rx_skb = h4_recv_buf(hu->hdev, bcm->rx_skb, data, count,
bcm_recv_pkts, ARRAY_SIZE(bcm_recv_pkts));
if (IS_ERR(bcm->rx_skb)) {
int err = PTR_ERR(bcm->rx_skb);
bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
bcm->rx_skb = NULL;
return err;
} else if (!bcm->rx_skb) {
/* Delay auto-suspend when receiving completed packet */
mutex_lock(&bcm_device_lock);
if (bcm->dev && bcm_device_exists(bcm->dev)) {
pm_runtime_get(&bcm->dev->pdev->dev);
pm_runtime_mark_last_busy(&bcm->dev->pdev->dev);
pm_runtime_put_autosuspend(&bcm->dev->pdev->dev);
}
mutex_unlock(&bcm_device_lock);
}
return count;
}
static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct bcm_data *bcm = hu->priv;
bt_dev_dbg(hu->hdev, "hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&bcm->txq, skb);
return 0;
}
static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
{
struct bcm_data *bcm = hu->priv;
struct sk_buff *skb = NULL;
struct bcm_device *bdev = NULL;
mutex_lock(&bcm_device_lock);
if (bcm_device_exists(bcm->dev)) {
bdev = bcm->dev;
pm_runtime_get_sync(&bdev->pdev->dev);
/* Shall be resumed here */
}
skb = skb_dequeue(&bcm->txq);
if (bdev) {
pm_runtime_mark_last_busy(&bdev->pdev->dev);
pm_runtime_put_autosuspend(&bdev->pdev->dev);
}
mutex_unlock(&bcm_device_lock);
return skb;
}
#ifdef CONFIG_PM
static int bcm_suspend_device(struct device *dev)
{
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
bt_dev_dbg(bdev, "");
if (!bdev->is_suspended && bdev->hu) {
hci_uart_set_flow_control(bdev->hu, true);
/* Once this returns, driver suspends BT via GPIO */
bdev->is_suspended = true;
}
/* Suspend the device */
if (bdev->device_wakeup) {
gpiod_set_value(bdev->device_wakeup, false);
bt_dev_dbg(bdev, "suspend, delaying 15 ms");
mdelay(15);
}
return 0;
}
static int bcm_resume_device(struct device *dev)
{
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
bt_dev_dbg(bdev, "");
if (bdev->device_wakeup) {
gpiod_set_value(bdev->device_wakeup, true);
bt_dev_dbg(bdev, "resume, delaying 15 ms");
mdelay(15);
}
/* When this executes, the device has woken up already */
if (bdev->is_suspended && bdev->hu) {
bdev->is_suspended = false;
hci_uart_set_flow_control(bdev->hu, false);
}
return 0;
}
#endif
#ifdef CONFIG_PM_SLEEP
/* Platform suspend callback */
static int bcm_suspend(struct device *dev)
{
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
int error;
bt_dev_dbg(bdev, "suspend: is_suspended %d", bdev->is_suspended);
/* bcm_suspend can be called at any time as long as platform device is
* bound, so it should use bcm_device_lock to protect access to hci_uart
* and device_wake-up GPIO.
*/
mutex_lock(&bcm_device_lock);
if (!bdev->hu)
goto unlock;
if (pm_runtime_active(dev))
bcm_suspend_device(dev);
if (device_may_wakeup(&bdev->pdev->dev)) {
error = enable_irq_wake(bdev->irq);
if (!error)
bt_dev_dbg(bdev, "BCM irq: enabled");
}
unlock:
mutex_unlock(&bcm_device_lock);
return 0;
}
/* Platform resume callback */
static int bcm_resume(struct device *dev)
{
struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
bt_dev_dbg(bdev, "resume: is_suspended %d", bdev->is_suspended);
/* bcm_resume can be called at any time as long as platform device is
* bound, so it should use bcm_device_lock to protect access to hci_uart
* and device_wake-up GPIO.
*/
mutex_lock(&bcm_device_lock);
if (!bdev->hu)
goto unlock;
if (device_may_wakeup(&bdev->pdev->dev)) {
disable_irq_wake(bdev->irq);
bt_dev_dbg(bdev, "BCM irq: disabled");
}
bcm_resume_device(dev);
unlock:
mutex_unlock(&bcm_device_lock);
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return 0;
}
#endif
static const struct acpi_gpio_params device_wakeup_gpios = { 0, 0, false };
static const struct acpi_gpio_params shutdown_gpios = { 1, 0, false };
static const struct acpi_gpio_params host_wakeup_gpios = { 2, 0, false };
static const struct acpi_gpio_mapping acpi_bcm_default_gpios[] = {
{ "device-wakeup-gpios", &device_wakeup_gpios, 1 },
{ "shutdown-gpios", &shutdown_gpios, 1 },
{ "host-wakeup-gpios", &host_wakeup_gpios, 1 },
{ },
};
#ifdef CONFIG_ACPI
static u8 acpi_active_low = ACPI_ACTIVE_LOW;
/* IRQ polarity of some chipsets are not defined correctly in ACPI table. */
static const struct dmi_system_id bcm_wrong_irq_dmi_table[] = {
{
.ident = "Asus T100TA",
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR,
"ASUSTeK COMPUTER INC."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"),
},
.driver_data = &acpi_active_low,
},
{ /* Handle ThinkPad 8 tablets with BCM2E55 chipset ACPI ID */
.ident = "Lenovo ThinkPad 8",
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"),
},
.driver_data = &acpi_active_low,
},
{ }
};
static int bcm_resource(struct acpi_resource *ares, void *data)
{
struct bcm_device *dev = data;
struct acpi_resource_extended_irq *irq;
struct acpi_resource_gpio *gpio;
struct acpi_resource_uart_serialbus *sb;
switch (ares->type) {
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
irq = &ares->data.extended_irq;
dev->irq_polarity = irq->polarity;
break;
case ACPI_RESOURCE_TYPE_GPIO:
gpio = &ares->data.gpio;
if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT)
dev->irq_polarity = gpio->polarity;
break;
case ACPI_RESOURCE_TYPE_SERIAL_BUS:
sb = &ares->data.uart_serial_bus;
if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_UART)
dev->init_speed = sb->default_baud_rate;
break;
default:
break;
}
/* Always tell the ACPI core to skip this resource */
return 1;
}
static int bcm_acpi_probe(struct bcm_device *dev)
{
struct platform_device *pdev = dev->pdev;
LIST_HEAD(resources);
const struct dmi_system_id *dmi_id;
int ret;
/* Retrieve GPIO data */
dev->name = dev_name(&pdev->dev);
ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
acpi_bcm_default_gpios);
if (ret)
return ret;
dev->clk = devm_clk_get(&pdev->dev, NULL);
dev->device_wakeup = devm_gpiod_get_optional(&pdev->dev,
"device-wakeup",
GPIOD_OUT_LOW);
if (IS_ERR(dev->device_wakeup))
return PTR_ERR(dev->device_wakeup);
dev->shutdown = devm_gpiod_get_optional(&pdev->dev, "shutdown",
GPIOD_OUT_LOW);
if (IS_ERR(dev->shutdown))
return PTR_ERR(dev->shutdown);
/* IRQ can be declared in ACPI table as Interrupt or GpioInt */
dev->irq = platform_get_irq(pdev, 0);
if (dev->irq <= 0) {
struct gpio_desc *gpio;
gpio = devm_gpiod_get_optional(&pdev->dev, "host-wakeup",
GPIOD_IN);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
dev->irq = gpiod_to_irq(gpio);
}
dev_info(&pdev->dev, "BCM irq: %d\n", dev->irq);
/* Make sure at-least one of the GPIO is defined and that
* a name is specified for this instance
*/
if ((!dev->device_wakeup && !dev->shutdown) || !dev->name) {
dev_err(&pdev->dev, "invalid platform data\n");
return -EINVAL;
}
/* Retrieve UART ACPI info */
ret = acpi_dev_get_resources(ACPI_COMPANION(&dev->pdev->dev),
&resources, bcm_resource, dev);
if (ret < 0)
return ret;
acpi_dev_free_resource_list(&resources);
dmi_id = dmi_first_match(bcm_wrong_irq_dmi_table);
if (dmi_id) {
bt_dev_warn(dev, "%s: Overwriting IRQ polarity to active low",
dmi_id->ident);
dev->irq_polarity = *(u8 *)dmi_id->driver_data;
}
return 0;
}
#else
static int bcm_acpi_probe(struct bcm_device *dev)
{
return -EINVAL;
}
#endif /* CONFIG_ACPI */
static int bcm_probe(struct platform_device *pdev)
{
struct bcm_device *dev;
int ret;
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->pdev = pdev;
ret = bcm_acpi_probe(dev);
if (ret)
return ret;
platform_set_drvdata(pdev, dev);
dev_info(&pdev->dev, "%s device registered.\n", dev->name);
/* Place this instance on the device list */
mutex_lock(&bcm_device_lock);
list_add_tail(&dev->list, &bcm_device_list);
mutex_unlock(&bcm_device_lock);
bcm_gpio_set_power(dev, false);
return 0;
}
static int bcm_remove(struct platform_device *pdev)
{
struct bcm_device *dev = platform_get_drvdata(pdev);
mutex_lock(&bcm_device_lock);
list_del(&dev->list);
mutex_unlock(&bcm_device_lock);
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pdev->dev));
dev_info(&pdev->dev, "%s device unregistered.\n", dev->name);
return 0;
}
static const struct hci_uart_proto bcm_proto = {
.id = HCI_UART_BCM,
.name = "Broadcom",
.manufacturer = 15,
.init_speed = 115200,
.oper_speed = 4000000,
.open = bcm_open,
.close = bcm_close,
.flush = bcm_flush,
.setup = bcm_setup,
.set_baudrate = bcm_set_baudrate,
.recv = bcm_recv,
.enqueue = bcm_enqueue,
.dequeue = bcm_dequeue,
};
#ifdef CONFIG_ACPI
static const struct acpi_device_id bcm_acpi_match[] = {
{ "BCM2E1A", 0 },
{ "BCM2E39", 0 },
{ "BCM2E3A", 0 },
{ "BCM2E3D", 0 },
{ "BCM2E3F", 0 },
{ "BCM2E40", 0 },
{ "BCM2E54", 0 },
{ "BCM2E55", 0 },
{ "BCM2E64", 0 },
{ "BCM2E65", 0 },
{ "BCM2E67", 0 },
{ "BCM2E71", 0 },
{ "BCM2E7B", 0 },
{ "BCM2E7C", 0 },
{ },
};
MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
#endif
/* Platform suspend and resume callbacks */
static const struct dev_pm_ops bcm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(bcm_suspend, bcm_resume)
SET_RUNTIME_PM_OPS(bcm_suspend_device, bcm_resume_device, NULL)
};
static struct platform_driver bcm_driver = {
.probe = bcm_probe,
.remove = bcm_remove,
.driver = {
.name = "hci_bcm",
.acpi_match_table = ACPI_PTR(bcm_acpi_match),
.pm = &bcm_pm_ops,
},
};
int __init bcm_init(void)
{
platform_driver_register(&bcm_driver);
return hci_uart_register_proto(&bcm_proto);
}
int __exit bcm_deinit(void)
{
platform_driver_unregister(&bcm_driver);
return hci_uart_unregister_proto(&bcm_proto);
}

View File

@@ -0,0 +1,791 @@
/*
*
* Bluetooth HCI UART driver
*
* Copyright (C) 2002-2003 Fabrizio Gennari <fabrizio.gennari@philips.com>
* Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/signal.h>
#include <linux/ioctl.h>
#include <linux/skbuff.h>
#include <linux/bitrev.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
static bool txcrc = 1;
static bool hciextn = 1;
#define BCSP_TXWINSIZE 4
#define BCSP_ACK_PKT 0x05
#define BCSP_LE_PKT 0x06
struct bcsp_struct {
struct sk_buff_head unack; /* Unack'ed packets queue */
struct sk_buff_head rel; /* Reliable packets queue */
struct sk_buff_head unrel; /* Unreliable packets queue */
unsigned long rx_count;
struct sk_buff *rx_skb;
u8 rxseq_txack; /* rxseq == txack. */
u8 rxack; /* Last packet sent by us that the peer ack'ed */
struct timer_list tbcsp;
enum {
BCSP_W4_PKT_DELIMITER,
BCSP_W4_PKT_START,
BCSP_W4_BCSP_HDR,
BCSP_W4_DATA,
BCSP_W4_CRC
} rx_state;
enum {
BCSP_ESCSTATE_NOESC,
BCSP_ESCSTATE_ESC
} rx_esc_state;
u8 use_crc;
u16 message_crc;
u8 txack_req; /* Do we need to send ack's to the peer? */
/* Reliable packet sequence number - used to assign seq to each rel pkt. */
u8 msgq_txseq;
};
/* ---- BCSP CRC calculation ---- */
/* Table for calculating CRC for polynomial 0x1021, LSB processed first,
* initial value 0xffff, bits shifted in reverse order.
*/
static const u16 crc_table[] = {
0x0000, 0x1081, 0x2102, 0x3183,
0x4204, 0x5285, 0x6306, 0x7387,
0x8408, 0x9489, 0xa50a, 0xb58b,
0xc60c, 0xd68d, 0xe70e, 0xf78f
};
/* Initialise the crc calculator */
#define BCSP_CRC_INIT(x) x = 0xffff
/* Update crc with next data byte
*
* Implementation note
* The data byte is treated as two nibbles. The crc is generated
* in reverse, i.e., bits are fed into the register from the top.
*/
static void bcsp_crc_update(u16 *crc, u8 d)
{
u16 reg = *crc;
reg = (reg >> 4) ^ crc_table[(reg ^ d) & 0x000f];
reg = (reg >> 4) ^ crc_table[(reg ^ (d >> 4)) & 0x000f];
*crc = reg;
}
/* ---- BCSP core ---- */
static void bcsp_slip_msgdelim(struct sk_buff *skb)
{
const char pkt_delim = 0xc0;
memcpy(skb_put(skb, 1), &pkt_delim, 1);
}
static void bcsp_slip_one_byte(struct sk_buff *skb, u8 c)
{
const char esc_c0[2] = { 0xdb, 0xdc };
const char esc_db[2] = { 0xdb, 0xdd };
switch (c) {
case 0xc0:
memcpy(skb_put(skb, 2), &esc_c0, 2);
break;
case 0xdb:
memcpy(skb_put(skb, 2), &esc_db, 2);
break;
default:
memcpy(skb_put(skb, 1), &c, 1);
}
}
static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct bcsp_struct *bcsp = hu->priv;
if (skb->len > 0xFFF) {
BT_ERR("Packet too long");
kfree_skb(skb);
return 0;
}
switch (hci_skb_pkt_type(skb)) {
case HCI_ACLDATA_PKT:
case HCI_COMMAND_PKT:
skb_queue_tail(&bcsp->rel, skb);
break;
case HCI_SCODATA_PKT:
skb_queue_tail(&bcsp->unrel, skb);
break;
default:
BT_ERR("Unknown packet type");
kfree_skb(skb);
break;
}
return 0;
}
static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
int len, int pkt_type)
{
struct sk_buff *nskb;
u8 hdr[4], chan;
u16 BCSP_CRC_INIT(bcsp_txmsg_crc);
int rel, i;
switch (pkt_type) {
case HCI_ACLDATA_PKT:
chan = 6; /* BCSP ACL channel */
rel = 1; /* reliable channel */
break;
case HCI_COMMAND_PKT:
chan = 5; /* BCSP cmd/evt channel */
rel = 1; /* reliable channel */
break;
case HCI_SCODATA_PKT:
chan = 7; /* BCSP SCO channel */
rel = 0; /* unreliable channel */
break;
case BCSP_LE_PKT:
chan = 1; /* BCSP LE channel */
rel = 0; /* unreliable channel */
break;
case BCSP_ACK_PKT:
chan = 0; /* BCSP internal channel */
rel = 0; /* unreliable channel */
break;
default:
BT_ERR("Unknown packet type");
return NULL;
}
if (hciextn && chan == 5) {
__le16 opcode = ((struct hci_command_hdr *)data)->opcode;
/* Vendor specific commands */
if (hci_opcode_ogf(__le16_to_cpu(opcode)) == 0x3f) {
u8 desc = *(data + HCI_COMMAND_HDR_SIZE);
if ((desc & 0xf0) == 0xc0) {
data += HCI_COMMAND_HDR_SIZE + 1;
len -= HCI_COMMAND_HDR_SIZE + 1;
chan = desc & 0x0f;
}
}
}
/* Max len of packet: (original len +4(bcsp hdr) +2(crc))*2
* (because bytes 0xc0 and 0xdb are escaped, worst case is
* when the packet is all made of 0xc0 and 0xdb :) )
* + 2 (0xc0 delimiters at start and end).
*/
nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC);
if (!nskb)
return NULL;
hci_skb_pkt_type(nskb) = pkt_type;
bcsp_slip_msgdelim(nskb);
hdr[0] = bcsp->rxseq_txack << 3;
bcsp->txack_req = 0;
BT_DBG("We request packet no %u to card", bcsp->rxseq_txack);
if (rel) {
hdr[0] |= 0x80 + bcsp->msgq_txseq;
BT_DBG("Sending packet with seqno %u", bcsp->msgq_txseq);
bcsp->msgq_txseq = (bcsp->msgq_txseq + 1) & 0x07;
}
if (bcsp->use_crc)
hdr[0] |= 0x40;
hdr[1] = ((len << 4) & 0xff) | chan;
hdr[2] = len >> 4;
hdr[3] = ~(hdr[0] + hdr[1] + hdr[2]);
/* Put BCSP header */
for (i = 0; i < 4; i++) {
bcsp_slip_one_byte(nskb, hdr[i]);
if (bcsp->use_crc)
bcsp_crc_update(&bcsp_txmsg_crc, hdr[i]);
}
/* Put payload */
for (i = 0; i < len; i++) {
bcsp_slip_one_byte(nskb, data[i]);
if (bcsp->use_crc)
bcsp_crc_update(&bcsp_txmsg_crc, data[i]);
}
/* Put CRC */
if (bcsp->use_crc) {
bcsp_txmsg_crc = bitrev16(bcsp_txmsg_crc);
bcsp_slip_one_byte(nskb, (u8)((bcsp_txmsg_crc >> 8) & 0x00ff));
bcsp_slip_one_byte(nskb, (u8)(bcsp_txmsg_crc & 0x00ff));
}
bcsp_slip_msgdelim(nskb);
return nskb;
}
/* This is a rewrite of pkt_avail in ABCSP */
static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
{
struct bcsp_struct *bcsp = hu->priv;
unsigned long flags;
struct sk_buff *skb;
/* First of all, check for unreliable messages in the queue,
* since they have priority
*/
skb = skb_dequeue(&bcsp->unrel);
if (skb != NULL) {
struct sk_buff *nskb;
nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len,
hci_skb_pkt_type(skb));
if (nskb) {
kfree_skb(skb);
return nskb;
} else {
skb_queue_head(&bcsp->unrel, skb);
BT_ERR("Could not dequeue pkt because alloc_skb failed");
}
}
/* Now, try to send a reliable pkt. We can only send a
* reliable packet if the number of packets sent but not yet ack'ed
* is < than the winsize
*/
spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING);
if (bcsp->unack.qlen < BCSP_TXWINSIZE) {
skb = skb_dequeue(&bcsp->rel);
if (skb != NULL) {
struct sk_buff *nskb;
nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len,
hci_skb_pkt_type(skb));
if (nskb) {
__skb_queue_tail(&bcsp->unack, skb);
mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
spin_unlock_irqrestore(&bcsp->unack.lock, flags);
return nskb;
} else {
skb_queue_head(&bcsp->rel, skb);
BT_ERR("Could not dequeue pkt because alloc_skb failed");
}
}
}
spin_unlock_irqrestore(&bcsp->unack.lock, flags);
/* We could not send a reliable packet, either because there are
* none or because there are too many unack'ed pkts. Did we receive
* any packets we have not acknowledged yet ?
*/
if (bcsp->txack_req) {
/* if so, craft an empty ACK pkt and send it on BCSP unreliable
* channel 0
*/
struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, NULL, 0, BCSP_ACK_PKT);
return nskb;
}
/* We have nothing to send */
return NULL;
}
static int bcsp_flush(struct hci_uart *hu)
{
BT_DBG("hu %p", hu);
return 0;
}
/* Remove ack'ed packets */
static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
{
struct sk_buff *skb, *tmp;
unsigned long flags;
int i, pkts_to_be_removed;
u8 seqno;
spin_lock_irqsave(&bcsp->unack.lock, flags);
pkts_to_be_removed = skb_queue_len(&bcsp->unack);
seqno = bcsp->msgq_txseq;
while (pkts_to_be_removed) {
if (bcsp->rxack == seqno)
break;
pkts_to_be_removed--;
seqno = (seqno - 1) & 0x07;
}
if (bcsp->rxack != seqno)
BT_ERR("Peer acked invalid packet");
BT_DBG("Removing %u pkts out of %u, up to seqno %u",
pkts_to_be_removed, skb_queue_len(&bcsp->unack),
(seqno - 1) & 0x07);
i = 0;
skb_queue_walk_safe(&bcsp->unack, skb, tmp) {
if (i >= pkts_to_be_removed)
break;
i++;
__skb_unlink(skb, &bcsp->unack);
kfree_skb(skb);
}
if (skb_queue_empty(&bcsp->unack))
del_timer(&bcsp->tbcsp);
spin_unlock_irqrestore(&bcsp->unack.lock, flags);
if (i != pkts_to_be_removed)
BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed);
}
/* Handle BCSP link-establishment packets. When we
* detect a "sync" packet, symptom that the BT module has reset,
* we do nothing :) (yet)
*/
static void bcsp_handle_le_pkt(struct hci_uart *hu)
{
struct bcsp_struct *bcsp = hu->priv;
u8 conf_pkt[4] = { 0xad, 0xef, 0xac, 0xed };
u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 };
u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed };
/* spot "conf" pkts and reply with a "conf rsp" pkt */
if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
!memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
BT_DBG("Found a LE conf pkt");
if (!nskb)
return;
memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
hci_skb_pkt_type(nskb) = BCSP_LE_PKT;
skb_queue_head(&bcsp->unrel, nskb);
hci_uart_tx_wakeup(hu);
}
/* Spot "sync" pkts. If we find one...disaster! */
else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
!memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) {
BT_ERR("Found a LE sync pkt, card has reset");
}
}
static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char byte)
{
const u8 c0 = 0xc0, db = 0xdb;
switch (bcsp->rx_esc_state) {
case BCSP_ESCSTATE_NOESC:
switch (byte) {
case 0xdb:
bcsp->rx_esc_state = BCSP_ESCSTATE_ESC;
break;
default:
memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1);
if ((bcsp->rx_skb->data[0] & 0x40) != 0 &&
bcsp->rx_state != BCSP_W4_CRC)
bcsp_crc_update(&bcsp->message_crc, byte);
bcsp->rx_count--;
}
break;
case BCSP_ESCSTATE_ESC:
switch (byte) {
case 0xdc:
memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1);
if ((bcsp->rx_skb->data[0] & 0x40) != 0 &&
bcsp->rx_state != BCSP_W4_CRC)
bcsp_crc_update(&bcsp->message_crc, 0xc0);
bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
bcsp->rx_count--;
break;
case 0xdd:
memcpy(skb_put(bcsp->rx_skb, 1), &db, 1);
if ((bcsp->rx_skb->data[0] & 0x40) != 0 &&
bcsp->rx_state != BCSP_W4_CRC)
bcsp_crc_update(&bcsp->message_crc, 0xdb);
bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
bcsp->rx_count--;
break;
default:
BT_ERR("Invalid byte %02x after esc byte", byte);
kfree_skb(bcsp->rx_skb);
bcsp->rx_skb = NULL;
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_count = 0;
}
}
}
static void bcsp_complete_rx_pkt(struct hci_uart *hu)
{
struct bcsp_struct *bcsp = hu->priv;
int pass_up = 0;
if (bcsp->rx_skb->data[0] & 0x80) { /* reliable pkt */
BT_DBG("Received seqno %u from card", bcsp->rxseq_txack);
/* check the rx sequence number is as expected */
if ((bcsp->rx_skb->data[0] & 0x07) == bcsp->rxseq_txack) {
bcsp->rxseq_txack++;
bcsp->rxseq_txack %= 0x8;
} else {
/* handle re-transmitted packet or
* when packet was missed
*/
BT_ERR("Out-of-order packet arrived, got %u expected %u",
bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
/* do not process out-of-order packet payload */
pass_up = 2;
}
/* send current txack value to all received reliable packets */
bcsp->txack_req = 1;
/* If needed, transmit an ack pkt */
hci_uart_tx_wakeup(hu);
}
bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07;
BT_DBG("Request for pkt %u from card", bcsp->rxack);
/* handle received ACK indications,
* including those from out-of-order packets
*/
bcsp_pkt_cull(bcsp);
if (pass_up != 2) {
if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
(bcsp->rx_skb->data[0] & 0x80)) {
hci_skb_pkt_type(bcsp->rx_skb) = HCI_ACLDATA_PKT;
pass_up = 1;
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
(bcsp->rx_skb->data[0] & 0x80)) {
hci_skb_pkt_type(bcsp->rx_skb) = HCI_EVENT_PKT;
pass_up = 1;
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
hci_skb_pkt_type(bcsp->rx_skb) = HCI_SCODATA_PKT;
pass_up = 1;
} else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
!(bcsp->rx_skb->data[0] & 0x80)) {
bcsp_handle_le_pkt(hu);
pass_up = 0;
} else {
pass_up = 0;
}
}
if (pass_up == 0) {
struct hci_event_hdr hdr;
u8 desc = (bcsp->rx_skb->data[1] & 0x0f);
if (desc != 0 && desc != 1) {
if (hciextn) {
desc |= 0xc0;
skb_pull(bcsp->rx_skb, 4);
memcpy(skb_push(bcsp->rx_skb, 1), &desc, 1);
hdr.evt = 0xff;
hdr.plen = bcsp->rx_skb->len;
memcpy(skb_push(bcsp->rx_skb, HCI_EVENT_HDR_SIZE), &hdr, HCI_EVENT_HDR_SIZE);
hci_skb_pkt_type(bcsp->rx_skb) = HCI_EVENT_PKT;
hci_recv_frame(hu->hdev, bcsp->rx_skb);
} else {
BT_ERR("Packet for unknown channel (%u %s)",
bcsp->rx_skb->data[1] & 0x0f,
bcsp->rx_skb->data[0] & 0x80 ?
"reliable" : "unreliable");
kfree_skb(bcsp->rx_skb);
}
} else
kfree_skb(bcsp->rx_skb);
} else if (pass_up == 1) {
/* Pull out BCSP hdr */
skb_pull(bcsp->rx_skb, 4);
hci_recv_frame(hu->hdev, bcsp->rx_skb);
} else {
/* ignore packet payload of already ACKed re-transmitted
* packets or when a packet was missed in the BCSP window
*/
kfree_skb(bcsp->rx_skb);
}
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_skb = NULL;
}
static u16 bscp_get_crc(struct bcsp_struct *bcsp)
{
return get_unaligned_be16(&bcsp->rx_skb->data[bcsp->rx_skb->len - 2]);
}
/* Recv data */
static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
{
struct bcsp_struct *bcsp = hu->priv;
const unsigned char *ptr;
BT_DBG("hu %p count %d rx_state %d rx_count %ld",
hu, count, bcsp->rx_state, bcsp->rx_count);
ptr = data;
while (count) {
if (bcsp->rx_count) {
if (*ptr == 0xc0) {
BT_ERR("Short BCSP packet");
kfree_skb(bcsp->rx_skb);
bcsp->rx_state = BCSP_W4_PKT_START;
bcsp->rx_count = 0;
} else
bcsp_unslip_one_byte(bcsp, *ptr);
ptr++; count--;
continue;
}
switch (bcsp->rx_state) {
case BCSP_W4_BCSP_HDR:
if ((0xff & (u8)~(bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] +
bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
BT_ERR("Error in BCSP hdr checksum");
kfree_skb(bcsp->rx_skb);
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_count = 0;
continue;
}
bcsp->rx_state = BCSP_W4_DATA;
bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) +
(bcsp->rx_skb->data[2] << 4); /* May be 0 */
continue;
case BCSP_W4_DATA:
if (bcsp->rx_skb->data[0] & 0x40) { /* pkt with crc */
bcsp->rx_state = BCSP_W4_CRC;
bcsp->rx_count = 2;
} else
bcsp_complete_rx_pkt(hu);
continue;
case BCSP_W4_CRC:
if (bitrev16(bcsp->message_crc) != bscp_get_crc(bcsp)) {
BT_ERR("Checksum failed: computed %04x received %04x",
bitrev16(bcsp->message_crc),
bscp_get_crc(bcsp));
kfree_skb(bcsp->rx_skb);
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_count = 0;
continue;
}
skb_trim(bcsp->rx_skb, bcsp->rx_skb->len - 2);
bcsp_complete_rx_pkt(hu);
continue;
case BCSP_W4_PKT_DELIMITER:
switch (*ptr) {
case 0xc0:
bcsp->rx_state = BCSP_W4_PKT_START;
break;
default:
/*BT_ERR("Ignoring byte %02x", *ptr);*/
break;
}
ptr++; count--;
break;
case BCSP_W4_PKT_START:
switch (*ptr) {
case 0xc0:
ptr++; count--;
break;
default:
bcsp->rx_state = BCSP_W4_BCSP_HDR;
bcsp->rx_count = 4;
bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
BCSP_CRC_INIT(bcsp->message_crc);
/* Do not increment ptr or decrement count
* Allocate packet. Max len of a BCSP pkt=
* 0xFFF (payload) +4 (header) +2 (crc)
*/
bcsp->rx_skb = bt_skb_alloc(0x1005, GFP_ATOMIC);
if (!bcsp->rx_skb) {
BT_ERR("Can't allocate mem for new packet");
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
bcsp->rx_count = 0;
return 0;
}
break;
}
break;
}
}
return count;
}
/* Arrange to retransmit all messages in the relq. */
static void bcsp_timed_event(unsigned long arg)
{
struct hci_uart *hu = (struct hci_uart *)arg;
struct bcsp_struct *bcsp = hu->priv;
struct sk_buff *skb;
unsigned long flags;
BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen);
spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING);
while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07;
skb_queue_head(&bcsp->rel, skb);
}
spin_unlock_irqrestore(&bcsp->unack.lock, flags);
hci_uart_tx_wakeup(hu);
}
static int bcsp_open(struct hci_uart *hu)
{
struct bcsp_struct *bcsp;
BT_DBG("hu %p", hu);
bcsp = kzalloc(sizeof(*bcsp), GFP_KERNEL);
if (!bcsp)
return -ENOMEM;
hu->priv = bcsp;
skb_queue_head_init(&bcsp->unack);
skb_queue_head_init(&bcsp->rel);
skb_queue_head_init(&bcsp->unrel);
init_timer(&bcsp->tbcsp);
bcsp->tbcsp.function = bcsp_timed_event;
bcsp->tbcsp.data = (u_long)hu;
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
if (txcrc)
bcsp->use_crc = 1;
return 0;
}
static int bcsp_close(struct hci_uart *hu)
{
struct bcsp_struct *bcsp = hu->priv;
del_timer_sync(&bcsp->tbcsp);
hu->priv = NULL;
BT_DBG("hu %p", hu);
skb_queue_purge(&bcsp->unack);
skb_queue_purge(&bcsp->rel);
skb_queue_purge(&bcsp->unrel);
kfree(bcsp);
return 0;
}
static const struct hci_uart_proto bcsp = {
.id = HCI_UART_BCSP,
.name = "BCSP",
.open = bcsp_open,
.close = bcsp_close,
.enqueue = bcsp_enqueue,
.dequeue = bcsp_dequeue,
.recv = bcsp_recv,
.flush = bcsp_flush
};
int __init bcsp_init(void)
{
return hci_uart_register_proto(&bcsp);
}
int __exit bcsp_deinit(void)
{
return hci_uart_unregister_proto(&bcsp);
}
module_param(txcrc, bool, 0644);
MODULE_PARM_DESC(txcrc, "Transmit CRC with every BCSP packet");
module_param(hciextn, bool, 0644);
MODULE_PARM_DESC(hciextn, "Convert HCI Extensions into BCSP packets");

269
drivers/bluetooth/hci_h4.c Normal file
View File

@@ -0,0 +1,269 @@
/*
*
* Bluetooth HCI UART driver
*
* Copyright (C) 2000-2001 Qualcomm Incorporated
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
* Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/signal.h>
#include <linux/ioctl.h>
#include <linux/skbuff.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
struct h4_struct {
struct sk_buff *rx_skb;
struct sk_buff_head txq;
};
/* Initialize protocol */
static int h4_open(struct hci_uart *hu)
{
struct h4_struct *h4;
BT_DBG("hu %p", hu);
h4 = kzalloc(sizeof(*h4), GFP_KERNEL);
if (!h4)
return -ENOMEM;
skb_queue_head_init(&h4->txq);
hu->priv = h4;
return 0;
}
/* Flush protocol data */
static int h4_flush(struct hci_uart *hu)
{
struct h4_struct *h4 = hu->priv;
BT_DBG("hu %p", hu);
skb_queue_purge(&h4->txq);
return 0;
}
/* Close protocol */
static int h4_close(struct hci_uart *hu)
{
struct h4_struct *h4 = hu->priv;
hu->priv = NULL;
BT_DBG("hu %p", hu);
skb_queue_purge(&h4->txq);
kfree_skb(h4->rx_skb);
hu->priv = NULL;
kfree(h4);
return 0;
}
/* Enqueue frame for transmittion (padding, crc, etc) */
static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct h4_struct *h4 = hu->priv;
BT_DBG("hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&h4->txq, skb);
return 0;
}
static const struct h4_recv_pkt h4_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
};
/* Recv data */
static int h4_recv(struct hci_uart *hu, const void *data, int count)
{
struct h4_struct *h4 = hu->priv;
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
h4->rx_skb = h4_recv_buf(hu->hdev, h4->rx_skb, data, count,
h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts));
if (IS_ERR(h4->rx_skb)) {
int err = PTR_ERR(h4->rx_skb);
BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
h4->rx_skb = NULL;
return err;
}
return count;
}
static struct sk_buff *h4_dequeue(struct hci_uart *hu)
{
struct h4_struct *h4 = hu->priv;
return skb_dequeue(&h4->txq);
}
static const struct hci_uart_proto h4p = {
.id = HCI_UART_H4,
.name = "H4",
.open = h4_open,
.close = h4_close,
.recv = h4_recv,
.enqueue = h4_enqueue,
.dequeue = h4_dequeue,
.flush = h4_flush,
};
int __init h4_init(void)
{
return hci_uart_register_proto(&h4p);
}
int __exit h4_deinit(void)
{
return hci_uart_unregister_proto(&h4p);
}
struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
const unsigned char *buffer, int count,
const struct h4_recv_pkt *pkts, int pkts_count)
{
while (count) {
int i, len;
if (!skb) {
for (i = 0; i < pkts_count; i++) {
if (buffer[0] != (&pkts[i])->type)
continue;
skb = bt_skb_alloc((&pkts[i])->maxlen,
GFP_ATOMIC);
if (!skb)
return ERR_PTR(-ENOMEM);
hci_skb_pkt_type(skb) = (&pkts[i])->type;
hci_skb_expect(skb) = (&pkts[i])->hlen;
break;
}
/* Check for invalid packet type */
if (!skb)
return ERR_PTR(-EILSEQ);
count -= 1;
buffer += 1;
}
len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
memcpy(skb_put(skb, len), buffer, len);
count -= len;
buffer += len;
/* Check for partial packet */
if (skb->len < hci_skb_expect(skb))
continue;
for (i = 0; i < pkts_count; i++) {
if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
break;
}
if (i >= pkts_count) {
kfree_skb(skb);
return ERR_PTR(-EILSEQ);
}
if (skb->len == (&pkts[i])->hlen) {
u16 dlen;
switch ((&pkts[i])->lsize) {
case 0:
/* No variable data length */
dlen = 0;
break;
case 1:
/* Single octet variable length */
dlen = skb->data[(&pkts[i])->loff];
hci_skb_expect(skb) += dlen;
if (skb_tailroom(skb) < dlen) {
kfree_skb(skb);
return ERR_PTR(-EMSGSIZE);
}
break;
case 2:
/* Double octet variable length */
dlen = get_unaligned_le16(skb->data +
(&pkts[i])->loff);
hci_skb_expect(skb) += dlen;
if (skb_tailroom(skb) < dlen) {
kfree_skb(skb);
return ERR_PTR(-EMSGSIZE);
}
break;
default:
/* Unsupported variable length */
kfree_skb(skb);
return ERR_PTR(-EILSEQ);
}
if (!dlen) {
/* No more data, complete frame */
(&pkts[i])->recv(hdev, skb);
skb = NULL;
}
} else {
/* Complete frame */
(&pkts[i])->recv(hdev, skb);
skb = NULL;
}
}
return skb;
}
EXPORT_SYMBOL_GPL(h4_recv_buf);

761
drivers/bluetooth/hci_h5.c Normal file
View File

@@ -0,0 +1,761 @@
/*
*
* Bluetooth HCI Three-wire UART driver
*
* Copyright (C) 2012 Intel Corporation
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
#define HCI_3WIRE_ACK_PKT 0
#define HCI_3WIRE_LINK_PKT 15
/* Sliding window size */
#define H5_TX_WIN_MAX 4
#define H5_ACK_TIMEOUT msecs_to_jiffies(250)
#define H5_SYNC_TIMEOUT msecs_to_jiffies(100)
/*
* Maximum Three-wire packet:
* 4 byte header + max value for 12-bit length + 2 bytes for CRC
*/
#define H5_MAX_LEN (4 + 0xfff + 2)
/* Convenience macros for reading Three-wire header values */
#define H5_HDR_SEQ(hdr) ((hdr)[0] & 0x07)
#define H5_HDR_ACK(hdr) (((hdr)[0] >> 3) & 0x07)
#define H5_HDR_CRC(hdr) (((hdr)[0] >> 6) & 0x01)
#define H5_HDR_RELIABLE(hdr) (((hdr)[0] >> 7) & 0x01)
#define H5_HDR_PKT_TYPE(hdr) ((hdr)[1] & 0x0f)
#define H5_HDR_LEN(hdr) ((((hdr)[1] >> 4) & 0x0f) + ((hdr)[2] << 4))
#define SLIP_DELIMITER 0xc0
#define SLIP_ESC 0xdb
#define SLIP_ESC_DELIM 0xdc
#define SLIP_ESC_ESC 0xdd
/* H5 state flags */
enum {
H5_RX_ESC, /* SLIP escape mode */
H5_TX_ACK_REQ, /* Pending ack to send */
};
struct h5 {
struct sk_buff_head unack; /* Unack'ed packets queue */
struct sk_buff_head rel; /* Reliable packets queue */
struct sk_buff_head unrel; /* Unreliable packets queue */
unsigned long flags;
struct sk_buff *rx_skb; /* Receive buffer */
size_t rx_pending; /* Expecting more bytes */
u8 rx_ack; /* Last ack number received */
int (*rx_func)(struct hci_uart *hu, u8 c);
struct timer_list timer; /* Retransmission timer */
u8 tx_seq; /* Next seq number to send */
u8 tx_ack; /* Next ack number to send */
u8 tx_win; /* Sliding window size */
enum {
H5_UNINITIALIZED,
H5_INITIALIZED,
H5_ACTIVE,
} state;
enum {
H5_AWAKE,
H5_SLEEPING,
H5_WAKING_UP,
} sleep;
};
static void h5_reset_rx(struct h5 *h5);
static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
{
struct h5 *h5 = hu->priv;
struct sk_buff *nskb;
nskb = alloc_skb(3, GFP_ATOMIC);
if (!nskb)
return;
hci_skb_pkt_type(nskb) = HCI_3WIRE_LINK_PKT;
memcpy(skb_put(nskb, len), data, len);
skb_queue_tail(&h5->unrel, nskb);
}
static u8 h5_cfg_field(struct h5 *h5)
{
/* Sliding window size (first 3 bits) */
return h5->tx_win & 0x07;
}
static void h5_timed_event(unsigned long arg)
{
const unsigned char sync_req[] = { 0x01, 0x7e };
unsigned char conf_req[3] = { 0x03, 0xfc };
struct hci_uart *hu = (struct hci_uart *)arg;
struct h5 *h5 = hu->priv;
struct sk_buff *skb;
unsigned long flags;
BT_DBG("%s", hu->hdev->name);
if (h5->state == H5_UNINITIALIZED)
h5_link_control(hu, sync_req, sizeof(sync_req));
if (h5->state == H5_INITIALIZED) {
conf_req[2] = h5_cfg_field(h5);
h5_link_control(hu, conf_req, sizeof(conf_req));
}
if (h5->state != H5_ACTIVE) {
mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT);
goto wakeup;
}
if (h5->sleep != H5_AWAKE) {
h5->sleep = H5_SLEEPING;
goto wakeup;
}
BT_DBG("hu %p retransmitting %u pkts", hu, h5->unack.qlen);
spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING);
while ((skb = __skb_dequeue_tail(&h5->unack)) != NULL) {
h5->tx_seq = (h5->tx_seq - 1) & 0x07;
skb_queue_head(&h5->rel, skb);
}
spin_unlock_irqrestore(&h5->unack.lock, flags);
wakeup:
hci_uart_tx_wakeup(hu);
}
static void h5_peer_reset(struct hci_uart *hu)
{
struct h5 *h5 = hu->priv;
BT_ERR("Peer device has reset");
h5->state = H5_UNINITIALIZED;
del_timer(&h5->timer);
skb_queue_purge(&h5->rel);
skb_queue_purge(&h5->unrel);
skb_queue_purge(&h5->unack);
h5->tx_seq = 0;
h5->tx_ack = 0;
/* Send reset request to upper stack */
hci_reset_dev(hu->hdev);
}
static int h5_open(struct hci_uart *hu)
{
struct h5 *h5;
const unsigned char sync[] = { 0x01, 0x7e };
BT_DBG("hu %p", hu);
h5 = kzalloc(sizeof(*h5), GFP_KERNEL);
if (!h5)
return -ENOMEM;
hu->priv = h5;
skb_queue_head_init(&h5->unack);
skb_queue_head_init(&h5->rel);
skb_queue_head_init(&h5->unrel);
h5_reset_rx(h5);
init_timer(&h5->timer);
h5->timer.function = h5_timed_event;
h5->timer.data = (unsigned long)hu;
h5->tx_win = H5_TX_WIN_MAX;
set_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags);
/* Send initial sync request */
h5_link_control(hu, sync, sizeof(sync));
mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT);
return 0;
}
static int h5_close(struct hci_uart *hu)
{
struct h5 *h5 = hu->priv;
del_timer_sync(&h5->timer);
skb_queue_purge(&h5->unack);
skb_queue_purge(&h5->rel);
skb_queue_purge(&h5->unrel);
kfree(h5);
return 0;
}
static void h5_pkt_cull(struct h5 *h5)
{
struct sk_buff *skb, *tmp;
unsigned long flags;
int i, to_remove;
u8 seq;
spin_lock_irqsave(&h5->unack.lock, flags);
to_remove = skb_queue_len(&h5->unack);
if (to_remove == 0)
goto unlock;
seq = h5->tx_seq;
while (to_remove > 0) {
if (h5->rx_ack == seq)
break;
to_remove--;
seq = (seq - 1) & 0x07;
}
if (seq != h5->rx_ack)
BT_ERR("Controller acked invalid packet");
i = 0;
skb_queue_walk_safe(&h5->unack, skb, tmp) {
if (i++ >= to_remove)
break;
__skb_unlink(skb, &h5->unack);
kfree_skb(skb);
}
if (skb_queue_empty(&h5->unack))
del_timer(&h5->timer);
unlock:
spin_unlock_irqrestore(&h5->unack.lock, flags);
}
static void h5_handle_internal_rx(struct hci_uart *hu)
{
struct h5 *h5 = hu->priv;
const unsigned char sync_req[] = { 0x01, 0x7e };
const unsigned char sync_rsp[] = { 0x02, 0x7d };
unsigned char conf_req[3] = { 0x03, 0xfc };
const unsigned char conf_rsp[] = { 0x04, 0x7b };
const unsigned char wakeup_req[] = { 0x05, 0xfa };
const unsigned char woken_req[] = { 0x06, 0xf9 };
const unsigned char sleep_req[] = { 0x07, 0x78 };
const unsigned char *hdr = h5->rx_skb->data;
const unsigned char *data = &h5->rx_skb->data[4];
BT_DBG("%s", hu->hdev->name);
if (H5_HDR_PKT_TYPE(hdr) != HCI_3WIRE_LINK_PKT)
return;
if (H5_HDR_LEN(hdr) < 2)
return;
conf_req[2] = h5_cfg_field(h5);
if (memcmp(data, sync_req, 2) == 0) {
if (h5->state == H5_ACTIVE)
h5_peer_reset(hu);
h5_link_control(hu, sync_rsp, 2);
} else if (memcmp(data, sync_rsp, 2) == 0) {
if (h5->state == H5_ACTIVE)
h5_peer_reset(hu);
h5->state = H5_INITIALIZED;
h5_link_control(hu, conf_req, 3);
} else if (memcmp(data, conf_req, 2) == 0) {
h5_link_control(hu, conf_rsp, 2);
h5_link_control(hu, conf_req, 3);
} else if (memcmp(data, conf_rsp, 2) == 0) {
if (H5_HDR_LEN(hdr) > 2)
h5->tx_win = (data[2] & 0x07);
BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win);
h5->state = H5_ACTIVE;
hci_uart_init_ready(hu);
return;
} else if (memcmp(data, sleep_req, 2) == 0) {
BT_DBG("Peer went to sleep");
h5->sleep = H5_SLEEPING;
return;
} else if (memcmp(data, woken_req, 2) == 0) {
BT_DBG("Peer woke up");
h5->sleep = H5_AWAKE;
} else if (memcmp(data, wakeup_req, 2) == 0) {
BT_DBG("Peer requested wakeup");
h5_link_control(hu, woken_req, 2);
h5->sleep = H5_AWAKE;
} else {
BT_DBG("Link Control: 0x%02hhx 0x%02hhx", data[0], data[1]);
return;
}
hci_uart_tx_wakeup(hu);
}
static void h5_complete_rx_pkt(struct hci_uart *hu)
{
struct h5 *h5 = hu->priv;
const unsigned char *hdr = h5->rx_skb->data;
if (H5_HDR_RELIABLE(hdr)) {
h5->tx_ack = (h5->tx_ack + 1) % 8;
set_bit(H5_TX_ACK_REQ, &h5->flags);
hci_uart_tx_wakeup(hu);
}
h5->rx_ack = H5_HDR_ACK(hdr);
h5_pkt_cull(h5);
switch (H5_HDR_PKT_TYPE(hdr)) {
case HCI_EVENT_PKT:
case HCI_ACLDATA_PKT:
case HCI_SCODATA_PKT:
hci_skb_pkt_type(h5->rx_skb) = H5_HDR_PKT_TYPE(hdr);
/* Remove Three-wire header */
skb_pull(h5->rx_skb, 4);
hci_recv_frame(hu->hdev, h5->rx_skb);
h5->rx_skb = NULL;
break;
default:
h5_handle_internal_rx(hu);
break;
}
h5_reset_rx(h5);
}
static int h5_rx_crc(struct hci_uart *hu, unsigned char c)
{
h5_complete_rx_pkt(hu);
return 0;
}
static int h5_rx_payload(struct hci_uart *hu, unsigned char c)
{
struct h5 *h5 = hu->priv;
const unsigned char *hdr = h5->rx_skb->data;
if (H5_HDR_CRC(hdr)) {
h5->rx_func = h5_rx_crc;
h5->rx_pending = 2;
} else {
h5_complete_rx_pkt(hu);
}
return 0;
}
static int h5_rx_3wire_hdr(struct hci_uart *hu, unsigned char c)
{
struct h5 *h5 = hu->priv;
const unsigned char *hdr = h5->rx_skb->data;
BT_DBG("%s rx: seq %u ack %u crc %u rel %u type %u len %u",
hu->hdev->name, H5_HDR_SEQ(hdr), H5_HDR_ACK(hdr),
H5_HDR_CRC(hdr), H5_HDR_RELIABLE(hdr), H5_HDR_PKT_TYPE(hdr),
H5_HDR_LEN(hdr));
if (((hdr[0] + hdr[1] + hdr[2] + hdr[3]) & 0xff) != 0xff) {
BT_ERR("Invalid header checksum");
h5_reset_rx(h5);
return 0;
}
if (H5_HDR_RELIABLE(hdr) && H5_HDR_SEQ(hdr) != h5->tx_ack) {
BT_ERR("Out-of-order packet arrived (%u != %u)",
H5_HDR_SEQ(hdr), h5->tx_ack);
h5_reset_rx(h5);
return 0;
}
if (h5->state != H5_ACTIVE &&
H5_HDR_PKT_TYPE(hdr) != HCI_3WIRE_LINK_PKT) {
BT_ERR("Non-link packet received in non-active state");
h5_reset_rx(h5);
return 0;
}
h5->rx_func = h5_rx_payload;
h5->rx_pending = H5_HDR_LEN(hdr);
return 0;
}
static int h5_rx_pkt_start(struct hci_uart *hu, unsigned char c)
{
struct h5 *h5 = hu->priv;
if (c == SLIP_DELIMITER)
return 1;
h5->rx_func = h5_rx_3wire_hdr;
h5->rx_pending = 4;
h5->rx_skb = bt_skb_alloc(H5_MAX_LEN, GFP_ATOMIC);
if (!h5->rx_skb) {
BT_ERR("Can't allocate mem for new packet");
h5_reset_rx(h5);
return -ENOMEM;
}
h5->rx_skb->dev = (void *)hu->hdev;
return 0;
}
static int h5_rx_delimiter(struct hci_uart *hu, unsigned char c)
{
struct h5 *h5 = hu->priv;
if (c == SLIP_DELIMITER)
h5->rx_func = h5_rx_pkt_start;
return 1;
}
static void h5_unslip_one_byte(struct h5 *h5, unsigned char c)
{
const u8 delim = SLIP_DELIMITER, esc = SLIP_ESC;
const u8 *byte = &c;
if (!test_bit(H5_RX_ESC, &h5->flags) && c == SLIP_ESC) {
set_bit(H5_RX_ESC, &h5->flags);
return;
}
if (test_and_clear_bit(H5_RX_ESC, &h5->flags)) {
switch (c) {
case SLIP_ESC_DELIM:
byte = &delim;
break;
case SLIP_ESC_ESC:
byte = &esc;
break;
default:
BT_ERR("Invalid esc byte 0x%02hhx", c);
h5_reset_rx(h5);
return;
}
}
memcpy(skb_put(h5->rx_skb, 1), byte, 1);
h5->rx_pending--;
BT_DBG("unsliped 0x%02hhx, rx_pending %zu", *byte, h5->rx_pending);
}
static void h5_reset_rx(struct h5 *h5)
{
if (h5->rx_skb) {
kfree_skb(h5->rx_skb);
h5->rx_skb = NULL;
}
h5->rx_func = h5_rx_delimiter;
h5->rx_pending = 0;
clear_bit(H5_RX_ESC, &h5->flags);
}
static int h5_recv(struct hci_uart *hu, const void *data, int count)
{
struct h5 *h5 = hu->priv;
const unsigned char *ptr = data;
BT_DBG("%s pending %zu count %d", hu->hdev->name, h5->rx_pending,
count);
while (count > 0) {
int processed;
if (h5->rx_pending > 0) {
if (*ptr == SLIP_DELIMITER) {
BT_ERR("Too short H5 packet");
h5_reset_rx(h5);
continue;
}
h5_unslip_one_byte(h5, *ptr);
ptr++; count--;
continue;
}
processed = h5->rx_func(hu, *ptr);
if (processed < 0)
return processed;
ptr += processed;
count -= processed;
}
return 0;
}
static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct h5 *h5 = hu->priv;
if (skb->len > 0xfff) {
BT_ERR("Packet too long (%u bytes)", skb->len);
kfree_skb(skb);
return 0;
}
if (h5->state != H5_ACTIVE) {
BT_ERR("Ignoring HCI data in non-active state");
kfree_skb(skb);
return 0;
}
switch (hci_skb_pkt_type(skb)) {
case HCI_ACLDATA_PKT:
case HCI_COMMAND_PKT:
skb_queue_tail(&h5->rel, skb);
break;
case HCI_SCODATA_PKT:
skb_queue_tail(&h5->unrel, skb);
break;
default:
BT_ERR("Unknown packet type %u", hci_skb_pkt_type(skb));
kfree_skb(skb);
break;
}
return 0;
}
static void h5_slip_delim(struct sk_buff *skb)
{
const char delim = SLIP_DELIMITER;
memcpy(skb_put(skb, 1), &delim, 1);
}
static void h5_slip_one_byte(struct sk_buff *skb, u8 c)
{
const char esc_delim[2] = { SLIP_ESC, SLIP_ESC_DELIM };
const char esc_esc[2] = { SLIP_ESC, SLIP_ESC_ESC };
switch (c) {
case SLIP_DELIMITER:
memcpy(skb_put(skb, 2), &esc_delim, 2);
break;
case SLIP_ESC:
memcpy(skb_put(skb, 2), &esc_esc, 2);
break;
default:
memcpy(skb_put(skb, 1), &c, 1);
}
}
static bool valid_packet_type(u8 type)
{
switch (type) {
case HCI_ACLDATA_PKT:
case HCI_COMMAND_PKT:
case HCI_SCODATA_PKT:
case HCI_3WIRE_LINK_PKT:
case HCI_3WIRE_ACK_PKT:
return true;
default:
return false;
}
}
static struct sk_buff *h5_prepare_pkt(struct hci_uart *hu, u8 pkt_type,
const u8 *data, size_t len)
{
struct h5 *h5 = hu->priv;
struct sk_buff *nskb;
u8 hdr[4];
int i;
if (!valid_packet_type(pkt_type)) {
BT_ERR("Unknown packet type %u", pkt_type);
return NULL;
}
/*
* Max len of packet: (original len + 4 (H5 hdr) + 2 (crc)) * 2
* (because bytes 0xc0 and 0xdb are escaped, worst case is when
* the packet is all made of 0xc0 and 0xdb) + 2 (0xc0
* delimiters at start and end).
*/
nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC);
if (!nskb)
return NULL;
hci_skb_pkt_type(nskb) = pkt_type;
h5_slip_delim(nskb);
hdr[0] = h5->tx_ack << 3;
clear_bit(H5_TX_ACK_REQ, &h5->flags);
/* Reliable packet? */
if (pkt_type == HCI_ACLDATA_PKT || pkt_type == HCI_COMMAND_PKT) {
hdr[0] |= 1 << 7;
hdr[0] |= h5->tx_seq;
h5->tx_seq = (h5->tx_seq + 1) % 8;
}
hdr[1] = pkt_type | ((len & 0x0f) << 4);
hdr[2] = len >> 4;
hdr[3] = ~((hdr[0] + hdr[1] + hdr[2]) & 0xff);
BT_DBG("%s tx: seq %u ack %u crc %u rel %u type %u len %u",
hu->hdev->name, H5_HDR_SEQ(hdr), H5_HDR_ACK(hdr),
H5_HDR_CRC(hdr), H5_HDR_RELIABLE(hdr), H5_HDR_PKT_TYPE(hdr),
H5_HDR_LEN(hdr));
for (i = 0; i < 4; i++)
h5_slip_one_byte(nskb, hdr[i]);
for (i = 0; i < len; i++)
h5_slip_one_byte(nskb, data[i]);
h5_slip_delim(nskb);
return nskb;
}
static struct sk_buff *h5_dequeue(struct hci_uart *hu)
{
struct h5 *h5 = hu->priv;
unsigned long flags;
struct sk_buff *skb, *nskb;
if (h5->sleep != H5_AWAKE) {
const unsigned char wakeup_req[] = { 0x05, 0xfa };
if (h5->sleep == H5_WAKING_UP)
return NULL;
h5->sleep = H5_WAKING_UP;
BT_DBG("Sending wakeup request");
mod_timer(&h5->timer, jiffies + HZ / 100);
return h5_prepare_pkt(hu, HCI_3WIRE_LINK_PKT, wakeup_req, 2);
}
skb = skb_dequeue(&h5->unrel);
if (skb) {
nskb = h5_prepare_pkt(hu, hci_skb_pkt_type(skb),
skb->data, skb->len);
if (nskb) {
kfree_skb(skb);
return nskb;
}
skb_queue_head(&h5->unrel, skb);
BT_ERR("Could not dequeue pkt because alloc_skb failed");
}
spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING);
if (h5->unack.qlen >= h5->tx_win)
goto unlock;
skb = skb_dequeue(&h5->rel);
if (skb) {
nskb = h5_prepare_pkt(hu, hci_skb_pkt_type(skb),
skb->data, skb->len);
if (nskb) {
__skb_queue_tail(&h5->unack, skb);
mod_timer(&h5->timer, jiffies + H5_ACK_TIMEOUT);
spin_unlock_irqrestore(&h5->unack.lock, flags);
return nskb;
}
skb_queue_head(&h5->rel, skb);
BT_ERR("Could not dequeue pkt because alloc_skb failed");
}
unlock:
spin_unlock_irqrestore(&h5->unack.lock, flags);
if (test_bit(H5_TX_ACK_REQ, &h5->flags))
return h5_prepare_pkt(hu, HCI_3WIRE_ACK_PKT, NULL, 0);
return NULL;
}
static int h5_flush(struct hci_uart *hu)
{
BT_DBG("hu %p", hu);
return 0;
}
static const struct hci_uart_proto h5p = {
.id = HCI_UART_3WIRE,
.name = "Three-wire (H5)",
.open = h5_open,
.close = h5_close,
.recv = h5_recv,
.enqueue = h5_enqueue,
.dequeue = h5_dequeue,
.flush = h5_flush,
};
int __init h5_init(void)
{
return hci_uart_register_proto(&h5p);
}
int __exit h5_deinit(void)
{
return hci_uart_unregister_proto(&h5p);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,870 @@
/*
*
* Bluetooth HCI UART driver
*
* Copyright (C) 2000-2001 Qualcomm Incorporated
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
* Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/signal.h>
#include <linux/ioctl.h>
#include <linux/skbuff.h>
#include <linux/firmware.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btintel.h"
#include "btbcm.h"
#include "hci_uart.h"
#define VERSION "2.3"
static const struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
int hci_uart_register_proto(const struct hci_uart_proto *p)
{
if (p->id >= HCI_UART_MAX_PROTO)
return -EINVAL;
if (hup[p->id])
return -EEXIST;
hup[p->id] = p;
BT_INFO("HCI UART protocol %s registered", p->name);
return 0;
}
int hci_uart_unregister_proto(const struct hci_uart_proto *p)
{
if (p->id >= HCI_UART_MAX_PROTO)
return -EINVAL;
if (!hup[p->id])
return -EINVAL;
hup[p->id] = NULL;
return 0;
}
static const struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
{
if (id >= HCI_UART_MAX_PROTO)
return NULL;
return hup[id];
}
static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type)
{
struct hci_dev *hdev = hu->hdev;
/* Update HCI stat counters */
switch (pkt_type) {
case HCI_COMMAND_PKT:
hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
hdev->stat.sco_tx++;
break;
}
}
static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
{
struct sk_buff *skb = hu->tx_skb;
if (!skb)
skb = hu->proto->dequeue(hu);
else
hu->tx_skb = NULL;
return skb;
}
int hci_uart_tx_wakeup(struct hci_uart *hu)
{
if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) {
set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
return 0;
}
BT_DBG("");
schedule_work(&hu->write_work);
return 0;
}
static void hci_uart_write_work(struct work_struct *work)
{
struct hci_uart *hu = container_of(work, struct hci_uart, write_work);
struct tty_struct *tty = hu->tty;
struct hci_dev *hdev = hu->hdev;
struct sk_buff *skb;
/* REVISIT: should we cope with bad skbs or ->write() returning
* and error value ?
*/
restart:
clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
while ((skb = hci_uart_dequeue(hu))) {
int len;
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
len = tty->ops->write(tty, skb->data, skb->len);
hdev->stat.byte_tx += len;
skb_pull(skb, len);
if (skb->len) {
hu->tx_skb = skb;
break;
}
hci_uart_tx_complete(hu, hci_skb_pkt_type(skb));
kfree_skb(skb);
}
if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state))
goto restart;
clear_bit(HCI_UART_SENDING, &hu->tx_state);
}
static void hci_uart_init_work(struct work_struct *work)
{
struct hci_uart *hu = container_of(work, struct hci_uart, init_ready);
int err;
if (!test_and_clear_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
return;
err = hci_register_dev(hu->hdev);
if (err < 0) {
BT_ERR("Can't register HCI device");
hci_free_dev(hu->hdev);
hu->hdev = NULL;
hu->proto->close(hu);
}
set_bit(HCI_UART_REGISTERED, &hu->flags);
}
int hci_uart_init_ready(struct hci_uart *hu)
{
if (!test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
return -EALREADY;
schedule_work(&hu->init_ready);
return 0;
}
/* ------- Interface to HCI layer ------ */
/* Initialize device */
static int hci_uart_open(struct hci_dev *hdev)
{
BT_DBG("%s %p", hdev->name, hdev);
/* Nothing to do for UART driver */
return 0;
}
/* Reset device */
static int hci_uart_flush(struct hci_dev *hdev)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct tty_struct *tty = hu->tty;
BT_DBG("hdev %p tty %p", hdev, tty);
if (hu->tx_skb) {
kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
}
/* Flush any pending characters in the driver and discipline. */
tty_ldisc_flush(tty);
tty_driver_flush_buffer(tty);
if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
hu->proto->flush(hu);
return 0;
}
/* Close device */
static int hci_uart_close(struct hci_dev *hdev)
{
BT_DBG("hdev %p", hdev);
hci_uart_flush(hdev);
hdev->flush = NULL;
return 0;
}
/* Send frames from HCI layer */
static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
BT_DBG("%s: type %d len %d", hdev->name, hci_skb_pkt_type(skb),
skb->len);
hu->proto->enqueue(hu, skb);
hci_uart_tx_wakeup(hu);
return 0;
}
/* Flow control or un-flow control the device */
void hci_uart_set_flow_control(struct hci_uart *hu, bool enable)
{
struct tty_struct *tty = hu->tty;
struct ktermios ktermios;
int status;
unsigned int set = 0;
unsigned int clear = 0;
if (enable) {
/* Disable hardware flow control */
ktermios = tty->termios;
ktermios.c_cflag &= ~CRTSCTS;
status = tty_set_termios(tty, &ktermios);
BT_DBG("Disabling hardware flow control: %s",
status ? "failed" : "success");
/* Clear RTS to prevent the device from sending */
/* Most UARTs need OUT2 to enable interrupts */
status = tty->driver->ops->tiocmget(tty);
BT_DBG("Current tiocm 0x%x", status);
set &= ~(TIOCM_OUT2 | TIOCM_RTS);
clear = ~set;
set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
TIOCM_OUT2 | TIOCM_LOOP;
clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
TIOCM_OUT2 | TIOCM_LOOP;
status = tty->driver->ops->tiocmset(tty, set, clear);
BT_DBG("Clearing RTS: %s", status ? "failed" : "success");
} else {
/* Set RTS to allow the device to send again */
status = tty->driver->ops->tiocmget(tty);
BT_DBG("Current tiocm 0x%x", status);
set |= (TIOCM_OUT2 | TIOCM_RTS);
clear = ~set;
set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
TIOCM_OUT2 | TIOCM_LOOP;
clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
TIOCM_OUT2 | TIOCM_LOOP;
status = tty->driver->ops->tiocmset(tty, set, clear);
BT_DBG("Setting RTS: %s", status ? "failed" : "success");
/* Re-enable hardware flow control */
ktermios = tty->termios;
ktermios.c_cflag |= CRTSCTS;
status = tty_set_termios(tty, &ktermios);
BT_DBG("Enabling hardware flow control: %s",
status ? "failed" : "success");
}
}
void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
unsigned int oper_speed)
{
hu->init_speed = init_speed;
hu->oper_speed = oper_speed;
}
void hci_uart_init_tty(struct hci_uart *hu)
{
struct tty_struct *tty = hu->tty;
struct ktermios ktermios;
/* Bring the UART into a known 8 bits no parity hw fc state */
ktermios = tty->termios;
ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
INLCR | IGNCR | ICRNL | IXON);
ktermios.c_oflag &= ~OPOST;
ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
ktermios.c_cflag &= ~(CSIZE | PARENB);
ktermios.c_cflag |= CS8;
ktermios.c_cflag |= CRTSCTS;
/* tty_set_termios() return not checked as it is always 0 */
tty_set_termios(tty, &ktermios);
}
void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed)
{
struct tty_struct *tty = hu->tty;
struct ktermios ktermios;
ktermios = tty->termios;
ktermios.c_cflag &= ~CBAUD;
tty_termios_encode_baud_rate(&ktermios, speed, speed);
/* tty_set_termios() return not checked as it is always 0 */
tty_set_termios(tty, &ktermios);
BT_DBG("%s: New tty speeds: %d/%d", hu->hdev->name,
tty->termios.c_ispeed, tty->termios.c_ospeed);
}
static int hci_uart_setup(struct hci_dev *hdev)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct hci_rp_read_local_version *ver;
struct sk_buff *skb;
unsigned int speed;
int err;
/* Init speed if any */
if (hu->init_speed)
speed = hu->init_speed;
else if (hu->proto->init_speed)
speed = hu->proto->init_speed;
else
speed = 0;
if (speed)
hci_uart_set_baudrate(hu, speed);
/* Operational speed if any */
if (hu->oper_speed)
speed = hu->oper_speed;
else if (hu->proto->oper_speed)
speed = hu->proto->oper_speed;
else
speed = 0;
if (hu->proto->set_baudrate && speed) {
err = hu->proto->set_baudrate(hu, speed);
if (!err)
hci_uart_set_baudrate(hu, speed);
}
if (hu->proto->setup)
return hu->proto->setup(hu);
if (!test_bit(HCI_UART_VND_DETECT, &hu->hdev_flags))
return 0;
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
BT_ERR("%s: Reading local version information failed (%ld)",
hdev->name, PTR_ERR(skb));
return 0;
}
if (skb->len != sizeof(*ver)) {
BT_ERR("%s: Event length mismatch for version information",
hdev->name);
goto done;
}
ver = (struct hci_rp_read_local_version *)skb->data;
switch (le16_to_cpu(ver->manufacturer)) {
#ifdef CONFIG_BT_HCIUART_INTEL
case 2:
hdev->set_bdaddr = btintel_set_bdaddr;
btintel_check_bdaddr(hdev);
break;
#endif
#ifdef CONFIG_BT_HCIUART_BCM
case 15:
hdev->set_bdaddr = btbcm_set_bdaddr;
btbcm_check_bdaddr(hdev);
break;
#endif
}
done:
kfree_skb(skb);
return 0;
}
/* ------ LDISC part ------ */
/* hci_uart_tty_open
*
* Called when line discipline changed to HCI_UART.
*
* Arguments:
* tty pointer to tty info structure
* Return Value:
* 0 if success, otherwise error code
*/
static int hci_uart_tty_open(struct tty_struct *tty)
{
struct hci_uart *hu;
BT_DBG("tty %p", tty);
/* Error if the tty has no write op instead of leaving an exploitable
hole */
if (tty->ops->write == NULL)
return -EOPNOTSUPP;
hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL);
if (!hu) {
BT_ERR("Can't allocate control structure");
return -ENFILE;
}
tty->disc_data = hu;
hu->tty = tty;
tty->receive_room = 65536;
INIT_WORK(&hu->init_ready, hci_uart_init_work);
INIT_WORK(&hu->write_work, hci_uart_write_work);
/* Flush any pending characters in the driver */
tty_driver_flush_buffer(tty);
return 0;
}
/* hci_uart_tty_close()
*
* Called when the line discipline is changed to something
* else, the tty is closed, or the tty detects a hangup.
*/
static void hci_uart_tty_close(struct tty_struct *tty)
{
struct hci_uart *hu = tty->disc_data;
struct hci_dev *hdev;
BT_DBG("tty %p", tty);
/* Detach from the tty */
tty->disc_data = NULL;
if (!hu)
return;
hdev = hu->hdev;
if (hdev)
hci_uart_close(hdev);
cancel_work_sync(&hu->write_work);
if (test_and_clear_bit(HCI_UART_PROTO_READY, &hu->flags)) {
if (hdev) {
if (test_bit(HCI_UART_REGISTERED, &hu->flags))
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
hu->proto->close(hu);
}
clear_bit(HCI_UART_PROTO_SET, &hu->flags);
kfree(hu);
}
/* hci_uart_tty_wakeup()
*
* Callback for transmit wakeup. Called when low level
* device driver can accept more send data.
*
* Arguments: tty pointer to associated tty instance data
* Return Value: None
*/
static void hci_uart_tty_wakeup(struct tty_struct *tty)
{
struct hci_uart *hu = tty->disc_data;
BT_DBG("");
if (!hu)
return;
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
if (tty != hu->tty)
return;
if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
hci_uart_tx_wakeup(hu);
}
/* hci_uart_tty_receive()
*
* Called by tty low level driver when receive data is
* available.
*
* Arguments: tty pointer to tty isntance data
* data pointer to received data
* flags pointer to flags for data
* count count of received data in bytes
*
* Return Value: None
*/
static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
char *flags, int count)
{
struct hci_uart *hu = tty->disc_data;
if (!hu || tty != hu->tty)
return;
if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
return;
/* It does not need a lock here as it is already protected by a mutex in
* tty caller
*/
hu->proto->recv(hu, data, count);
if (hu->hdev)
hu->hdev->stat.byte_rx += count;
tty_unthrottle(tty);
}
static int hci_uart_register_dev(struct hci_uart *hu)
{
struct hci_dev *hdev;
BT_DBG("");
/* Initialize and register HCI device */
hdev = hci_alloc_dev();
if (!hdev) {
BT_ERR("Can't allocate HCI device");
return -ENOMEM;
}
hu->hdev = hdev;
hdev->bus = HCI_UART;
hci_set_drvdata(hdev, hu);
/* Only when vendor specific setup callback is provided, consider
* the manufacturer information valid. This avoids filling in the
* value for Ericsson when nothing is specified.
*/
if (hu->proto->setup)
hdev->manufacturer = hu->proto->manufacturer;
hdev->open = hci_uart_open;
hdev->close = hci_uart_close;
hdev->flush = hci_uart_flush;
hdev->send = hci_uart_send_frame;
hdev->setup = hci_uart_setup;
SET_HCIDEV_DEV(hdev, hu->tty->dev);
if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags))
set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks);
if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags))
set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
if (test_bit(HCI_UART_CREATE_AMP, &hu->hdev_flags))
hdev->dev_type = HCI_AMP;
else
hdev->dev_type = HCI_PRIMARY;
if (test_bit(HCI_UART_INIT_PENDING, &hu->hdev_flags))
return 0;
if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device");
hci_free_dev(hdev);
return -ENODEV;
}
set_bit(HCI_UART_REGISTERED, &hu->flags);
return 0;
}
static int hci_uart_set_proto(struct hci_uart *hu, int id)
{
const struct hci_uart_proto *p;
int err;
p = hci_uart_get_proto(id);
if (!p)
return -EPROTONOSUPPORT;
err = p->open(hu);
if (err)
return err;
hu->proto = p;
set_bit(HCI_UART_PROTO_READY, &hu->flags);
err = hci_uart_register_dev(hu);
if (err) {
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
p->close(hu);
return err;
}
return 0;
}
static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags)
{
unsigned long valid_flags = BIT(HCI_UART_RAW_DEVICE) |
BIT(HCI_UART_RESET_ON_INIT) |
BIT(HCI_UART_CREATE_AMP) |
BIT(HCI_UART_INIT_PENDING) |
BIT(HCI_UART_EXT_CONFIG) |
BIT(HCI_UART_VND_DETECT);
if (flags & ~valid_flags)
return -EINVAL;
hu->hdev_flags = flags;
return 0;
}
/* hci_uart_tty_ioctl()
*
* Process IOCTL system call for the tty device.
*
* Arguments:
*
* tty pointer to tty instance data
* file pointer to open file object for device
* cmd IOCTL command code
* arg argument for IOCTL call (cmd dependent)
*
* Return Value: Command dependent
*/
static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct hci_uart *hu = tty->disc_data;
int err = 0;
BT_DBG("");
/* Verify the status of the device */
if (!hu)
return -EBADF;
switch (cmd) {
case HCIUARTSETPROTO:
if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
err = hci_uart_set_proto(hu, arg);
if (err)
clear_bit(HCI_UART_PROTO_SET, &hu->flags);
} else
err = -EBUSY;
break;
case HCIUARTGETPROTO:
if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
err = hu->proto->id;
else
err = -EUNATCH;
break;
case HCIUARTGETDEVICE:
if (test_bit(HCI_UART_REGISTERED, &hu->flags))
err = hu->hdev->id;
else
err = -EUNATCH;
break;
case HCIUARTSETFLAGS:
if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
err = -EBUSY;
else
err = hci_uart_set_flags(hu, arg);
break;
case HCIUARTGETFLAGS:
err = hu->hdev_flags;
break;
default:
err = n_tty_ioctl_helper(tty, file, cmd, arg);
break;
}
return err;
}
/*
* We don't provide read/write/poll interface for user space.
*/
static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
return 0;
}
static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file,
const unsigned char *data, size_t count)
{
return 0;
}
static unsigned int hci_uart_tty_poll(struct tty_struct *tty,
struct file *filp, poll_table *wait)
{
return 0;
}
static int __init hci_uart_init(void)
{
static struct tty_ldisc_ops hci_uart_ldisc;
int err;
BT_INFO("HCI UART driver ver %s", VERSION);
/* Register the tty discipline */
memset(&hci_uart_ldisc, 0, sizeof(hci_uart_ldisc));
hci_uart_ldisc.magic = TTY_LDISC_MAGIC;
hci_uart_ldisc.name = "n_hci";
hci_uart_ldisc.open = hci_uart_tty_open;
hci_uart_ldisc.close = hci_uart_tty_close;
hci_uart_ldisc.read = hci_uart_tty_read;
hci_uart_ldisc.write = hci_uart_tty_write;
hci_uart_ldisc.ioctl = hci_uart_tty_ioctl;
hci_uart_ldisc.poll = hci_uart_tty_poll;
hci_uart_ldisc.receive_buf = hci_uart_tty_receive;
hci_uart_ldisc.write_wakeup = hci_uart_tty_wakeup;
hci_uart_ldisc.owner = THIS_MODULE;
err = tty_register_ldisc(N_HCI, &hci_uart_ldisc);
if (err) {
BT_ERR("HCI line discipline registration failed. (%d)", err);
return err;
}
#ifdef CONFIG_BT_HCIUART_H4
h4_init();
#endif
#ifdef CONFIG_BT_HCIUART_BCSP
bcsp_init();
#endif
#ifdef CONFIG_BT_HCIUART_LL
ll_init();
#endif
#ifdef CONFIG_BT_HCIUART_ATH3K
ath_init();
#endif
#ifdef CONFIG_BT_HCIUART_3WIRE
h5_init();
#endif
#ifdef CONFIG_BT_HCIUART_INTEL
intel_init();
#endif
#ifdef CONFIG_BT_HCIUART_BCM
bcm_init();
#endif
#ifdef CONFIG_BT_HCIUART_QCA
qca_init();
#endif
#ifdef CONFIG_BT_HCIUART_AG6XX
ag6xx_init();
#endif
#ifdef CONFIG_BT_HCIUART_MRVL
mrvl_init();
#endif
return 0;
}
static void __exit hci_uart_exit(void)
{
int err;
#ifdef CONFIG_BT_HCIUART_H4
h4_deinit();
#endif
#ifdef CONFIG_BT_HCIUART_BCSP
bcsp_deinit();
#endif
#ifdef CONFIG_BT_HCIUART_LL
ll_deinit();
#endif
#ifdef CONFIG_BT_HCIUART_ATH3K
ath_deinit();
#endif
#ifdef CONFIG_BT_HCIUART_3WIRE
h5_deinit();
#endif
#ifdef CONFIG_BT_HCIUART_INTEL
intel_deinit();
#endif
#ifdef CONFIG_BT_HCIUART_BCM
bcm_deinit();
#endif
#ifdef CONFIG_BT_HCIUART_QCA
qca_deinit();
#endif
#ifdef CONFIG_BT_HCIUART_AG6XX
ag6xx_deinit();
#endif
#ifdef CONFIG_BT_HCIUART_MRVL
mrvl_deinit();
#endif
/* Release tty registration of line discipline */
err = tty_unregister_ldisc(N_HCI);
if (err)
BT_ERR("Can't unregister HCI line discipline (%d)", err);
}
module_init(hci_uart_init);
module_exit(hci_uart_exit);
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth HCI UART driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS_LDISC(N_HCI);

527
drivers/bluetooth/hci_ll.c Normal file
View File

@@ -0,0 +1,527 @@
/*
* Texas Instruments' Bluetooth HCILL UART protocol
*
* HCILL (HCI Low Level) is a Texas Instruments' power management
* protocol extension to H4.
*
* Copyright (C) 2007 Texas Instruments, Inc.
*
* Written by Ohad Ben-Cohen <ohad@bencohen.org>
*
* Acknowledgements:
* This file is based on hci_h4.c, which was written
* by Maxim Krasnyansky and Marcel Holtmann.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/signal.h>
#include <linux/ioctl.h>
#include <linux/skbuff.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
/* HCILL commands */
#define HCILL_GO_TO_SLEEP_IND 0x30
#define HCILL_GO_TO_SLEEP_ACK 0x31
#define HCILL_WAKE_UP_IND 0x32
#define HCILL_WAKE_UP_ACK 0x33
/* HCILL receiver States */
#define HCILL_W4_PACKET_TYPE 0
#define HCILL_W4_EVENT_HDR 1
#define HCILL_W4_ACL_HDR 2
#define HCILL_W4_SCO_HDR 3
#define HCILL_W4_DATA 4
/* HCILL states */
enum hcill_states_e {
HCILL_ASLEEP,
HCILL_ASLEEP_TO_AWAKE,
HCILL_AWAKE,
HCILL_AWAKE_TO_ASLEEP
};
struct hcill_cmd {
u8 cmd;
} __packed;
struct ll_struct {
unsigned long rx_state;
unsigned long rx_count;
struct sk_buff *rx_skb;
struct sk_buff_head txq;
spinlock_t hcill_lock; /* HCILL state lock */
unsigned long hcill_state; /* HCILL power state */
struct sk_buff_head tx_wait_q; /* HCILL wait queue */
};
/*
* Builds and sends an HCILL command packet.
* These are very simple packets with only 1 cmd byte
*/
static int send_hcill_cmd(u8 cmd, struct hci_uart *hu)
{
int err = 0;
struct sk_buff *skb = NULL;
struct ll_struct *ll = hu->priv;
struct hcill_cmd *hcill_packet;
BT_DBG("hu %p cmd 0x%x", hu, cmd);
/* allocate packet */
skb = bt_skb_alloc(1, GFP_ATOMIC);
if (!skb) {
BT_ERR("cannot allocate memory for HCILL packet");
err = -ENOMEM;
goto out;
}
/* prepare packet */
hcill_packet = (struct hcill_cmd *) skb_put(skb, 1);
hcill_packet->cmd = cmd;
/* send packet */
skb_queue_tail(&ll->txq, skb);
out:
return err;
}
/* Initialize protocol */
static int ll_open(struct hci_uart *hu)
{
struct ll_struct *ll;
BT_DBG("hu %p", hu);
ll = kzalloc(sizeof(*ll), GFP_KERNEL);
if (!ll)
return -ENOMEM;
skb_queue_head_init(&ll->txq);
skb_queue_head_init(&ll->tx_wait_q);
spin_lock_init(&ll->hcill_lock);
ll->hcill_state = HCILL_AWAKE;
hu->priv = ll;
return 0;
}
/* Flush protocol data */
static int ll_flush(struct hci_uart *hu)
{
struct ll_struct *ll = hu->priv;
BT_DBG("hu %p", hu);
skb_queue_purge(&ll->tx_wait_q);
skb_queue_purge(&ll->txq);
return 0;
}
/* Close protocol */
static int ll_close(struct hci_uart *hu)
{
struct ll_struct *ll = hu->priv;
BT_DBG("hu %p", hu);
skb_queue_purge(&ll->tx_wait_q);
skb_queue_purge(&ll->txq);
kfree_skb(ll->rx_skb);
hu->priv = NULL;
kfree(ll);
return 0;
}
/*
* internal function, which does common work of the device wake up process:
* 1. places all pending packets (waiting in tx_wait_q list) in txq list.
* 2. changes internal state to HCILL_AWAKE.
* Note: assumes that hcill_lock spinlock is taken,
* shouldn't be called otherwise!
*/
static void __ll_do_awake(struct ll_struct *ll)
{
struct sk_buff *skb = NULL;
while ((skb = skb_dequeue(&ll->tx_wait_q)))
skb_queue_tail(&ll->txq, skb);
ll->hcill_state = HCILL_AWAKE;
}
/*
* Called upon a wake-up-indication from the device
*/
static void ll_device_want_to_wakeup(struct hci_uart *hu)
{
unsigned long flags;
struct ll_struct *ll = hu->priv;
BT_DBG("hu %p", hu);
/* lock hcill state */
spin_lock_irqsave(&ll->hcill_lock, flags);
switch (ll->hcill_state) {
case HCILL_ASLEEP_TO_AWAKE:
/*
* This state means that both the host and the BRF chip
* have simultaneously sent a wake-up-indication packet.
* Traditionally, in this case, receiving a wake-up-indication
* was enough and an additional wake-up-ack wasn't needed.
* This has changed with the BRF6350, which does require an
* explicit wake-up-ack. Other BRF versions, which do not
* require an explicit ack here, do accept it, thus it is
* perfectly safe to always send one.
*/
BT_DBG("dual wake-up-indication");
/* deliberate fall-through - do not add break */
case HCILL_ASLEEP:
/* acknowledge device wake up */
if (send_hcill_cmd(HCILL_WAKE_UP_ACK, hu) < 0) {
BT_ERR("cannot acknowledge device wake up");
goto out;
}
break;
default:
/* any other state is illegal */
BT_ERR("received HCILL_WAKE_UP_IND in state %ld", ll->hcill_state);
break;
}
/* send pending packets and change state to HCILL_AWAKE */
__ll_do_awake(ll);
out:
spin_unlock_irqrestore(&ll->hcill_lock, flags);
/* actually send the packets */
hci_uart_tx_wakeup(hu);
}
/*
* Called upon a sleep-indication from the device
*/
static void ll_device_want_to_sleep(struct hci_uart *hu)
{
unsigned long flags;
struct ll_struct *ll = hu->priv;
BT_DBG("hu %p", hu);
/* lock hcill state */
spin_lock_irqsave(&ll->hcill_lock, flags);
/* sanity check */
if (ll->hcill_state != HCILL_AWAKE)
BT_ERR("ERR: HCILL_GO_TO_SLEEP_IND in state %ld", ll->hcill_state);
/* acknowledge device sleep */
if (send_hcill_cmd(HCILL_GO_TO_SLEEP_ACK, hu) < 0) {
BT_ERR("cannot acknowledge device sleep");
goto out;
}
/* update state */
ll->hcill_state = HCILL_ASLEEP;
out:
spin_unlock_irqrestore(&ll->hcill_lock, flags);
/* actually send the sleep ack packet */
hci_uart_tx_wakeup(hu);
}
/*
* Called upon wake-up-acknowledgement from the device
*/
static void ll_device_woke_up(struct hci_uart *hu)
{
unsigned long flags;
struct ll_struct *ll = hu->priv;
BT_DBG("hu %p", hu);
/* lock hcill state */
spin_lock_irqsave(&ll->hcill_lock, flags);
/* sanity check */
if (ll->hcill_state != HCILL_ASLEEP_TO_AWAKE)
BT_ERR("received HCILL_WAKE_UP_ACK in state %ld", ll->hcill_state);
/* send pending packets and change state to HCILL_AWAKE */
__ll_do_awake(ll);
spin_unlock_irqrestore(&ll->hcill_lock, flags);
/* actually send the packets */
hci_uart_tx_wakeup(hu);
}
/* Enqueue frame for transmittion (padding, crc, etc) */
/* may be called from two simultaneous tasklets */
static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
unsigned long flags = 0;
struct ll_struct *ll = hu->priv;
BT_DBG("hu %p skb %p", hu, skb);
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
/* lock hcill state */
spin_lock_irqsave(&ll->hcill_lock, flags);
/* act according to current state */
switch (ll->hcill_state) {
case HCILL_AWAKE:
BT_DBG("device awake, sending normally");
skb_queue_tail(&ll->txq, skb);
break;
case HCILL_ASLEEP:
BT_DBG("device asleep, waking up and queueing packet");
/* save packet for later */
skb_queue_tail(&ll->tx_wait_q, skb);
/* awake device */
if (send_hcill_cmd(HCILL_WAKE_UP_IND, hu) < 0) {
BT_ERR("cannot wake up device");
break;
}
ll->hcill_state = HCILL_ASLEEP_TO_AWAKE;
break;
case HCILL_ASLEEP_TO_AWAKE:
BT_DBG("device waking up, queueing packet");
/* transient state; just keep packet for later */
skb_queue_tail(&ll->tx_wait_q, skb);
break;
default:
BT_ERR("illegal hcill state: %ld (losing packet)", ll->hcill_state);
kfree_skb(skb);
break;
}
spin_unlock_irqrestore(&ll->hcill_lock, flags);
return 0;
}
static inline int ll_check_data_len(struct hci_dev *hdev, struct ll_struct *ll, int len)
{
int room = skb_tailroom(ll->rx_skb);
BT_DBG("len %d room %d", len, room);
if (!len) {
hci_recv_frame(hdev, ll->rx_skb);
} else if (len > room) {
BT_ERR("Data length is too large");
kfree_skb(ll->rx_skb);
} else {
ll->rx_state = HCILL_W4_DATA;
ll->rx_count = len;
return len;
}
ll->rx_state = HCILL_W4_PACKET_TYPE;
ll->rx_skb = NULL;
ll->rx_count = 0;
return 0;
}
/* Recv data */
static int ll_recv(struct hci_uart *hu, const void *data, int count)
{
struct ll_struct *ll = hu->priv;
const char *ptr;
struct hci_event_hdr *eh;
struct hci_acl_hdr *ah;
struct hci_sco_hdr *sh;
int len, type, dlen;
BT_DBG("hu %p count %d rx_state %ld rx_count %ld", hu, count, ll->rx_state, ll->rx_count);
ptr = data;
while (count) {
if (ll->rx_count) {
len = min_t(unsigned int, ll->rx_count, count);
memcpy(skb_put(ll->rx_skb, len), ptr, len);
ll->rx_count -= len; count -= len; ptr += len;
if (ll->rx_count)
continue;
switch (ll->rx_state) {
case HCILL_W4_DATA:
BT_DBG("Complete data");
hci_recv_frame(hu->hdev, ll->rx_skb);
ll->rx_state = HCILL_W4_PACKET_TYPE;
ll->rx_skb = NULL;
continue;
case HCILL_W4_EVENT_HDR:
eh = hci_event_hdr(ll->rx_skb);
BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
ll_check_data_len(hu->hdev, ll, eh->plen);
continue;
case HCILL_W4_ACL_HDR:
ah = hci_acl_hdr(ll->rx_skb);
dlen = __le16_to_cpu(ah->dlen);
BT_DBG("ACL header: dlen %d", dlen);
ll_check_data_len(hu->hdev, ll, dlen);
continue;
case HCILL_W4_SCO_HDR:
sh = hci_sco_hdr(ll->rx_skb);
BT_DBG("SCO header: dlen %d", sh->dlen);
ll_check_data_len(hu->hdev, ll, sh->dlen);
continue;
}
}
/* HCILL_W4_PACKET_TYPE */
switch (*ptr) {
case HCI_EVENT_PKT:
BT_DBG("Event packet");
ll->rx_state = HCILL_W4_EVENT_HDR;
ll->rx_count = HCI_EVENT_HDR_SIZE;
type = HCI_EVENT_PKT;
break;
case HCI_ACLDATA_PKT:
BT_DBG("ACL packet");
ll->rx_state = HCILL_W4_ACL_HDR;
ll->rx_count = HCI_ACL_HDR_SIZE;
type = HCI_ACLDATA_PKT;
break;
case HCI_SCODATA_PKT:
BT_DBG("SCO packet");
ll->rx_state = HCILL_W4_SCO_HDR;
ll->rx_count = HCI_SCO_HDR_SIZE;
type = HCI_SCODATA_PKT;
break;
/* HCILL signals */
case HCILL_GO_TO_SLEEP_IND:
BT_DBG("HCILL_GO_TO_SLEEP_IND packet");
ll_device_want_to_sleep(hu);
ptr++; count--;
continue;
case HCILL_GO_TO_SLEEP_ACK:
/* shouldn't happen */
BT_ERR("received HCILL_GO_TO_SLEEP_ACK (in state %ld)", ll->hcill_state);
ptr++; count--;
continue;
case HCILL_WAKE_UP_IND:
BT_DBG("HCILL_WAKE_UP_IND packet");
ll_device_want_to_wakeup(hu);
ptr++; count--;
continue;
case HCILL_WAKE_UP_ACK:
BT_DBG("HCILL_WAKE_UP_ACK packet");
ll_device_woke_up(hu);
ptr++; count--;
continue;
default:
BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
hu->hdev->stat.err_rx++;
ptr++; count--;
continue;
}
ptr++; count--;
/* Allocate packet */
ll->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
if (!ll->rx_skb) {
BT_ERR("Can't allocate mem for new packet");
ll->rx_state = HCILL_W4_PACKET_TYPE;
ll->rx_count = 0;
return -ENOMEM;
}
hci_skb_pkt_type(ll->rx_skb) = type;
}
return count;
}
static struct sk_buff *ll_dequeue(struct hci_uart *hu)
{
struct ll_struct *ll = hu->priv;
return skb_dequeue(&ll->txq);
}
static const struct hci_uart_proto llp = {
.id = HCI_UART_LL,
.name = "LL",
.open = ll_open,
.close = ll_close,
.recv = ll_recv,
.enqueue = ll_enqueue,
.dequeue = ll_dequeue,
.flush = ll_flush,
};
int __init ll_init(void)
{
return hci_uart_register_proto(&llp);
}
int __exit ll_deinit(void)
{
return hci_uart_unregister_proto(&llp);
}

View File

@@ -0,0 +1,387 @@
/*
*
* Bluetooth HCI UART driver for marvell devices
*
* Copyright (C) 2016 Marvell International Ltd.
* Copyright (C) 2016 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
#define HCI_FW_REQ_PKT 0xA5
#define HCI_CHIP_VER_PKT 0xAA
#define MRVL_ACK 0x5A
#define MRVL_NAK 0xBF
#define MRVL_RAW_DATA 0x1F
enum {
STATE_CHIP_VER_PENDING,
STATE_FW_REQ_PENDING,
};
struct mrvl_data {
struct sk_buff *rx_skb;
struct sk_buff_head txq;
struct sk_buff_head rawq;
unsigned long flags;
unsigned int tx_len;
u8 id, rev;
};
struct hci_mrvl_pkt {
__le16 lhs;
__le16 rhs;
} __packed;
#define HCI_MRVL_PKT_SIZE 4
static int mrvl_open(struct hci_uart *hu)
{
struct mrvl_data *mrvl;
BT_DBG("hu %p", hu);
mrvl = kzalloc(sizeof(*mrvl), GFP_KERNEL);
if (!mrvl)
return -ENOMEM;
skb_queue_head_init(&mrvl->txq);
skb_queue_head_init(&mrvl->rawq);
set_bit(STATE_CHIP_VER_PENDING, &mrvl->flags);
hu->priv = mrvl;
return 0;
}
static int mrvl_close(struct hci_uart *hu)
{
struct mrvl_data *mrvl = hu->priv;
BT_DBG("hu %p", hu);
skb_queue_purge(&mrvl->txq);
skb_queue_purge(&mrvl->rawq);
kfree_skb(mrvl->rx_skb);
kfree(mrvl);
hu->priv = NULL;
return 0;
}
static int mrvl_flush(struct hci_uart *hu)
{
struct mrvl_data *mrvl = hu->priv;
BT_DBG("hu %p", hu);
skb_queue_purge(&mrvl->txq);
skb_queue_purge(&mrvl->rawq);
return 0;
}
static struct sk_buff *mrvl_dequeue(struct hci_uart *hu)
{
struct mrvl_data *mrvl = hu->priv;
struct sk_buff *skb;
skb = skb_dequeue(&mrvl->txq);
if (!skb) {
/* Any raw data ? */
skb = skb_dequeue(&mrvl->rawq);
} else {
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
}
return skb;
}
static int mrvl_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct mrvl_data *mrvl = hu->priv;
skb_queue_tail(&mrvl->txq, skb);
return 0;
}
static void mrvl_send_ack(struct hci_uart *hu, unsigned char type)
{
struct mrvl_data *mrvl = hu->priv;
struct sk_buff *skb;
/* No H4 payload, only 1 byte header */
skb = bt_skb_alloc(0, GFP_ATOMIC);
if (!skb) {
bt_dev_err(hu->hdev, "Unable to alloc ack/nak packet");
return;
}
hci_skb_pkt_type(skb) = type;
skb_queue_tail(&mrvl->txq, skb);
hci_uart_tx_wakeup(hu);
}
static int mrvl_recv_fw_req(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_mrvl_pkt *pkt = (void *)skb->data;
struct hci_uart *hu = hci_get_drvdata(hdev);
struct mrvl_data *mrvl = hu->priv;
int ret = 0;
if ((pkt->lhs ^ pkt->rhs) != 0xffff) {
bt_dev_err(hdev, "Corrupted mrvl header");
mrvl_send_ack(hu, MRVL_NAK);
ret = -EINVAL;
goto done;
}
mrvl_send_ack(hu, MRVL_ACK);
if (!test_bit(STATE_FW_REQ_PENDING, &mrvl->flags)) {
bt_dev_err(hdev, "Received unexpected firmware request");
ret = -EINVAL;
goto done;
}
mrvl->tx_len = le16_to_cpu(pkt->lhs);
clear_bit(STATE_FW_REQ_PENDING, &mrvl->flags);
smp_mb__after_atomic();
wake_up_bit(&mrvl->flags, STATE_FW_REQ_PENDING);
done:
kfree_skb(skb);
return ret;
}
static int mrvl_recv_chip_ver(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_mrvl_pkt *pkt = (void *)skb->data;
struct hci_uart *hu = hci_get_drvdata(hdev);
struct mrvl_data *mrvl = hu->priv;
u16 version = le16_to_cpu(pkt->lhs);
int ret = 0;
if ((pkt->lhs ^ pkt->rhs) != 0xffff) {
bt_dev_err(hdev, "Corrupted mrvl header");
mrvl_send_ack(hu, MRVL_NAK);
ret = -EINVAL;
goto done;
}
mrvl_send_ack(hu, MRVL_ACK);
if (!test_bit(STATE_CHIP_VER_PENDING, &mrvl->flags)) {
bt_dev_err(hdev, "Received unexpected chip version");
goto done;
}
mrvl->id = version;
mrvl->rev = version >> 8;
bt_dev_info(hdev, "Controller id = %x, rev = %x", mrvl->id, mrvl->rev);
clear_bit(STATE_CHIP_VER_PENDING, &mrvl->flags);
smp_mb__after_atomic();
wake_up_bit(&mrvl->flags, STATE_CHIP_VER_PENDING);
done:
kfree_skb(skb);
return ret;
}
#define HCI_RECV_CHIP_VER \
.type = HCI_CHIP_VER_PKT, \
.hlen = HCI_MRVL_PKT_SIZE, \
.loff = 0, \
.lsize = 0, \
.maxlen = HCI_MRVL_PKT_SIZE
#define HCI_RECV_FW_REQ \
.type = HCI_FW_REQ_PKT, \
.hlen = HCI_MRVL_PKT_SIZE, \
.loff = 0, \
.lsize = 0, \
.maxlen = HCI_MRVL_PKT_SIZE
static const struct h4_recv_pkt mrvl_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
{ HCI_RECV_FW_REQ, .recv = mrvl_recv_fw_req },
{ HCI_RECV_CHIP_VER, .recv = mrvl_recv_chip_ver },
};
static int mrvl_recv(struct hci_uart *hu, const void *data, int count)
{
struct mrvl_data *mrvl = hu->priv;
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
mrvl->rx_skb = h4_recv_buf(hu->hdev, mrvl->rx_skb, data, count,
mrvl_recv_pkts,
ARRAY_SIZE(mrvl_recv_pkts));
if (IS_ERR(mrvl->rx_skb)) {
int err = PTR_ERR(mrvl->rx_skb);
bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
mrvl->rx_skb = NULL;
return err;
}
return count;
}
static int mrvl_load_firmware(struct hci_dev *hdev, const char *name)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct mrvl_data *mrvl = hu->priv;
const struct firmware *fw = NULL;
const u8 *fw_ptr, *fw_max;
int err;
err = request_firmware(&fw, name, &hdev->dev);
if (err < 0) {
bt_dev_err(hdev, "Failed to load firmware file %s", name);
return err;
}
fw_ptr = fw->data;
fw_max = fw->data + fw->size;
bt_dev_info(hdev, "Loading %s", name);
set_bit(STATE_FW_REQ_PENDING, &mrvl->flags);
while (fw_ptr <= fw_max) {
struct sk_buff *skb;
/* Controller drives the firmware load by sending firmware
* request packets containing the expected fragment size.
*/
err = wait_on_bit_timeout(&mrvl->flags, STATE_FW_REQ_PENDING,
TASK_INTERRUPTIBLE,
msecs_to_jiffies(2000));
if (err == 1) {
bt_dev_err(hdev, "Firmware load interrupted");
err = -EINTR;
break;
} else if (err) {
bt_dev_err(hdev, "Firmware request timeout");
err = -ETIMEDOUT;
break;
}
bt_dev_dbg(hdev, "Firmware request, expecting %d bytes",
mrvl->tx_len);
if (fw_ptr == fw_max) {
/* Controller requests a null size once firmware is
* fully loaded. If controller expects more data, there
* is an issue.
*/
if (!mrvl->tx_len) {
bt_dev_info(hdev, "Firmware loading complete");
} else {
bt_dev_err(hdev, "Firmware loading failure");
err = -EINVAL;
}
break;
}
if (fw_ptr + mrvl->tx_len > fw_max) {
mrvl->tx_len = fw_max - fw_ptr;
bt_dev_dbg(hdev, "Adjusting tx_len to %d",
mrvl->tx_len);
}
skb = bt_skb_alloc(mrvl->tx_len, GFP_KERNEL);
if (!skb) {
bt_dev_err(hdev, "Failed to alloc mem for FW packet");
err = -ENOMEM;
break;
}
bt_cb(skb)->pkt_type = MRVL_RAW_DATA;
memcpy(skb_put(skb, mrvl->tx_len), fw_ptr, mrvl->tx_len);
fw_ptr += mrvl->tx_len;
set_bit(STATE_FW_REQ_PENDING, &mrvl->flags);
skb_queue_tail(&mrvl->rawq, skb);
hci_uart_tx_wakeup(hu);
}
release_firmware(fw);
return err;
}
static int mrvl_setup(struct hci_uart *hu)
{
int err;
hci_uart_set_flow_control(hu, true);
err = mrvl_load_firmware(hu->hdev, "mrvl/helper_uart_3000000.bin");
if (err) {
bt_dev_err(hu->hdev, "Unable to download firmware helper");
return -EINVAL;
}
hci_uart_set_baudrate(hu, 3000000);
hci_uart_set_flow_control(hu, false);
err = mrvl_load_firmware(hu->hdev, "mrvl/uart8897_bt.bin");
if (err)
return err;
return 0;
}
static const struct hci_uart_proto mrvl_proto = {
.id = HCI_UART_MRVL,
.name = "Marvell",
.init_speed = 115200,
.open = mrvl_open,
.close = mrvl_close,
.flush = mrvl_flush,
.setup = mrvl_setup,
.recv = mrvl_recv,
.enqueue = mrvl_enqueue,
.dequeue = mrvl_dequeue,
};
int __init mrvl_init(void)
{
return hci_uart_register_proto(&mrvl_proto);
}
int __exit mrvl_deinit(void)
{
return hci_uart_unregister_proto(&mrvl_proto);
}

970
drivers/bluetooth/hci_qca.c Normal file
View File

@@ -0,0 +1,970 @@
/*
* Bluetooth Software UART Qualcomm protocol
*
* HCI_IBS (HCI In-Band Sleep) is Qualcomm's power management
* protocol extension to H4.
*
* Copyright (C) 2007 Texas Instruments, Inc.
* Copyright (c) 2010, 2012 The Linux Foundation. All rights reserved.
*
* Acknowledgements:
* This file is based on hci_ll.c, which was...
* Written by Ohad Ben-Cohen <ohad@bencohen.org>
* which was in turn based on hci_h4.c, which was written
* by Maxim Krasnyansky and Marcel Holtmann.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
#include "btqca.h"
/* HCI_IBS protocol messages */
#define HCI_IBS_SLEEP_IND 0xFE
#define HCI_IBS_WAKE_IND 0xFD
#define HCI_IBS_WAKE_ACK 0xFC
#define HCI_MAX_IBS_SIZE 10
/* Controller states */
#define STATE_IN_BAND_SLEEP_ENABLED 1
#define IBS_WAKE_RETRANS_TIMEOUT_MS 100
#define IBS_TX_IDLE_TIMEOUT_MS 2000
#define BAUDRATE_SETTLE_TIMEOUT_MS 300
/* HCI_IBS transmit side sleep protocol states */
enum tx_ibs_states {
HCI_IBS_TX_ASLEEP,
HCI_IBS_TX_WAKING,
HCI_IBS_TX_AWAKE,
};
/* HCI_IBS receive side sleep protocol states */
enum rx_states {
HCI_IBS_RX_ASLEEP,
HCI_IBS_RX_AWAKE,
};
/* HCI_IBS transmit and receive side clock state vote */
enum hci_ibs_clock_state_vote {
HCI_IBS_VOTE_STATS_UPDATE,
HCI_IBS_TX_VOTE_CLOCK_ON,
HCI_IBS_TX_VOTE_CLOCK_OFF,
HCI_IBS_RX_VOTE_CLOCK_ON,
HCI_IBS_RX_VOTE_CLOCK_OFF,
};
struct qca_data {
struct hci_uart *hu;
struct sk_buff *rx_skb;
struct sk_buff_head txq;
struct sk_buff_head tx_wait_q; /* HCI_IBS wait queue */
spinlock_t hci_ibs_lock; /* HCI_IBS state lock */
u8 tx_ibs_state; /* HCI_IBS transmit side power state*/
u8 rx_ibs_state; /* HCI_IBS receive side power state */
u32 tx_vote; /* Clock must be on for TX */
u32 rx_vote; /* Clock must be on for RX */
struct timer_list tx_idle_timer;
u32 tx_idle_delay;
struct timer_list wake_retrans_timer;
u32 wake_retrans;
struct workqueue_struct *workqueue;
struct work_struct ws_awake_rx;
struct work_struct ws_awake_device;
struct work_struct ws_rx_vote_off;
struct work_struct ws_tx_vote_off;
unsigned long flags;
/* For debugging purpose */
u64 ibs_sent_wacks;
u64 ibs_sent_slps;
u64 ibs_sent_wakes;
u64 ibs_recv_wacks;
u64 ibs_recv_slps;
u64 ibs_recv_wakes;
u64 vote_last_jif;
u32 vote_on_ms;
u32 vote_off_ms;
u64 tx_votes_on;
u64 rx_votes_on;
u64 tx_votes_off;
u64 rx_votes_off;
u64 votes_on;
u64 votes_off;
};
static void __serial_clock_on(struct tty_struct *tty)
{
/* TODO: Some chipset requires to enable UART clock on client
* side to save power consumption or manual work is required.
* Please put your code to control UART clock here if needed
*/
}
static void __serial_clock_off(struct tty_struct *tty)
{
/* TODO: Some chipset requires to disable UART clock on client
* side to save power consumption or manual work is required.
* Please put your code to control UART clock off here if needed
*/
}
/* serial_clock_vote needs to be called with the ibs lock held */
static void serial_clock_vote(unsigned long vote, struct hci_uart *hu)
{
struct qca_data *qca = hu->priv;
unsigned int diff;
bool old_vote = (qca->tx_vote | qca->rx_vote);
bool new_vote;
switch (vote) {
case HCI_IBS_VOTE_STATS_UPDATE:
diff = jiffies_to_msecs(jiffies - qca->vote_last_jif);
if (old_vote)
qca->vote_off_ms += diff;
else
qca->vote_on_ms += diff;
return;
case HCI_IBS_TX_VOTE_CLOCK_ON:
qca->tx_vote = true;
qca->tx_votes_on++;
new_vote = true;
break;
case HCI_IBS_RX_VOTE_CLOCK_ON:
qca->rx_vote = true;
qca->rx_votes_on++;
new_vote = true;
break;
case HCI_IBS_TX_VOTE_CLOCK_OFF:
qca->tx_vote = false;
qca->tx_votes_off++;
new_vote = qca->rx_vote | qca->tx_vote;
break;
case HCI_IBS_RX_VOTE_CLOCK_OFF:
qca->rx_vote = false;
qca->rx_votes_off++;
new_vote = qca->rx_vote | qca->tx_vote;
break;
default:
BT_ERR("Voting irregularity");
return;
}
if (new_vote != old_vote) {
if (new_vote)
__serial_clock_on(hu->tty);
else
__serial_clock_off(hu->tty);
BT_DBG("Vote serial clock %s(%s)", new_vote ? "true" : "false",
vote ? "true" : "false");
diff = jiffies_to_msecs(jiffies - qca->vote_last_jif);
if (new_vote) {
qca->votes_on++;
qca->vote_off_ms += diff;
} else {
qca->votes_off++;
qca->vote_on_ms += diff;
}
qca->vote_last_jif = jiffies;
}
}
/* Builds and sends an HCI_IBS command packet.
* These are very simple packets with only 1 cmd byte.
*/
static int send_hci_ibs_cmd(u8 cmd, struct hci_uart *hu)
{
int err = 0;
struct sk_buff *skb = NULL;
struct qca_data *qca = hu->priv;
BT_DBG("hu %p send hci ibs cmd 0x%x", hu, cmd);
skb = bt_skb_alloc(1, GFP_ATOMIC);
if (!skb) {
BT_ERR("Failed to allocate memory for HCI_IBS packet");
return -ENOMEM;
}
/* Assign HCI_IBS type */
*skb_put(skb, 1) = cmd;
skb_queue_tail(&qca->txq, skb);
return err;
}
static void qca_wq_awake_device(struct work_struct *work)
{
struct qca_data *qca = container_of(work, struct qca_data,
ws_awake_device);
struct hci_uart *hu = qca->hu;
unsigned long retrans_delay;
BT_DBG("hu %p wq awake device", hu);
/* Vote for serial clock */
serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_ON, hu);
spin_lock(&qca->hci_ibs_lock);
/* Send wake indication to device */
if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0)
BT_ERR("Failed to send WAKE to device");
qca->ibs_sent_wakes++;
/* Start retransmit timer */
retrans_delay = msecs_to_jiffies(qca->wake_retrans);
mod_timer(&qca->wake_retrans_timer, jiffies + retrans_delay);
spin_unlock(&qca->hci_ibs_lock);
/* Actually send the packets */
hci_uart_tx_wakeup(hu);
}
static void qca_wq_awake_rx(struct work_struct *work)
{
struct qca_data *qca = container_of(work, struct qca_data,
ws_awake_rx);
struct hci_uart *hu = qca->hu;
BT_DBG("hu %p wq awake rx", hu);
serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_ON, hu);
spin_lock(&qca->hci_ibs_lock);
qca->rx_ibs_state = HCI_IBS_RX_AWAKE;
/* Always acknowledge device wake up,
* sending IBS message doesn't count as TX ON.
*/
if (send_hci_ibs_cmd(HCI_IBS_WAKE_ACK, hu) < 0)
BT_ERR("Failed to acknowledge device wake up");
qca->ibs_sent_wacks++;
spin_unlock(&qca->hci_ibs_lock);
/* Actually send the packets */
hci_uart_tx_wakeup(hu);
}
static void qca_wq_serial_rx_clock_vote_off(struct work_struct *work)
{
struct qca_data *qca = container_of(work, struct qca_data,
ws_rx_vote_off);
struct hci_uart *hu = qca->hu;
BT_DBG("hu %p rx clock vote off", hu);
serial_clock_vote(HCI_IBS_RX_VOTE_CLOCK_OFF, hu);
}
static void qca_wq_serial_tx_clock_vote_off(struct work_struct *work)
{
struct qca_data *qca = container_of(work, struct qca_data,
ws_tx_vote_off);
struct hci_uart *hu = qca->hu;
BT_DBG("hu %p tx clock vote off", hu);
/* Run HCI tx handling unlocked */
hci_uart_tx_wakeup(hu);
/* Now that message queued to tty driver, vote for tty clocks off.
* It is up to the tty driver to pend the clocks off until tx done.
*/
serial_clock_vote(HCI_IBS_TX_VOTE_CLOCK_OFF, hu);
}
static void hci_ibs_tx_idle_timeout(unsigned long arg)
{
struct hci_uart *hu = (struct hci_uart *)arg;
struct qca_data *qca = hu->priv;
unsigned long flags;
BT_DBG("hu %p idle timeout in %d state", hu, qca->tx_ibs_state);
spin_lock_irqsave_nested(&qca->hci_ibs_lock,
flags, SINGLE_DEPTH_NESTING);
switch (qca->tx_ibs_state) {
case HCI_IBS_TX_AWAKE:
/* TX_IDLE, go to SLEEP */
if (send_hci_ibs_cmd(HCI_IBS_SLEEP_IND, hu) < 0) {
BT_ERR("Failed to send SLEEP to device");
break;
}
qca->tx_ibs_state = HCI_IBS_TX_ASLEEP;
qca->ibs_sent_slps++;
queue_work(qca->workqueue, &qca->ws_tx_vote_off);
break;
case HCI_IBS_TX_ASLEEP:
case HCI_IBS_TX_WAKING:
/* Fall through */
default:
BT_ERR("Spurrious timeout tx state %d", qca->tx_ibs_state);
break;
}
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
}
static void hci_ibs_wake_retrans_timeout(unsigned long arg)
{
struct hci_uart *hu = (struct hci_uart *)arg;
struct qca_data *qca = hu->priv;
unsigned long flags, retrans_delay;
bool retransmit = false;
BT_DBG("hu %p wake retransmit timeout in %d state",
hu, qca->tx_ibs_state);
spin_lock_irqsave_nested(&qca->hci_ibs_lock,
flags, SINGLE_DEPTH_NESTING);
switch (qca->tx_ibs_state) {
case HCI_IBS_TX_WAKING:
/* No WAKE_ACK, retransmit WAKE */
retransmit = true;
if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
BT_ERR("Failed to acknowledge device wake up");
break;
}
qca->ibs_sent_wakes++;
retrans_delay = msecs_to_jiffies(qca->wake_retrans);
mod_timer(&qca->wake_retrans_timer, jiffies + retrans_delay);
break;
case HCI_IBS_TX_ASLEEP:
case HCI_IBS_TX_AWAKE:
/* Fall through */
default:
BT_ERR("Spurrious timeout tx state %d", qca->tx_ibs_state);
break;
}
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
if (retransmit)
hci_uart_tx_wakeup(hu);
}
/* Initialize protocol */
static int qca_open(struct hci_uart *hu)
{
struct qca_data *qca;
BT_DBG("hu %p qca_open", hu);
qca = kzalloc(sizeof(struct qca_data), GFP_ATOMIC);
if (!qca)
return -ENOMEM;
skb_queue_head_init(&qca->txq);
skb_queue_head_init(&qca->tx_wait_q);
spin_lock_init(&qca->hci_ibs_lock);
qca->workqueue = alloc_ordered_workqueue("qca_wq", 0);
if (!qca->workqueue) {
BT_ERR("QCA Workqueue not initialized properly");
kfree(qca);
return -ENOMEM;
}
INIT_WORK(&qca->ws_awake_rx, qca_wq_awake_rx);
INIT_WORK(&qca->ws_awake_device, qca_wq_awake_device);
INIT_WORK(&qca->ws_rx_vote_off, qca_wq_serial_rx_clock_vote_off);
INIT_WORK(&qca->ws_tx_vote_off, qca_wq_serial_tx_clock_vote_off);
qca->hu = hu;
/* Assume we start with both sides asleep -- extra wakes OK */
qca->tx_ibs_state = HCI_IBS_TX_ASLEEP;
qca->rx_ibs_state = HCI_IBS_RX_ASLEEP;
/* clocks actually on, but we start votes off */
qca->tx_vote = false;
qca->rx_vote = false;
qca->flags = 0;
qca->ibs_sent_wacks = 0;
qca->ibs_sent_slps = 0;
qca->ibs_sent_wakes = 0;
qca->ibs_recv_wacks = 0;
qca->ibs_recv_slps = 0;
qca->ibs_recv_wakes = 0;
qca->vote_last_jif = jiffies;
qca->vote_on_ms = 0;
qca->vote_off_ms = 0;
qca->votes_on = 0;
qca->votes_off = 0;
qca->tx_votes_on = 0;
qca->tx_votes_off = 0;
qca->rx_votes_on = 0;
qca->rx_votes_off = 0;
hu->priv = qca;
init_timer(&qca->wake_retrans_timer);
qca->wake_retrans_timer.function = hci_ibs_wake_retrans_timeout;
qca->wake_retrans_timer.data = (u_long)hu;
qca->wake_retrans = IBS_WAKE_RETRANS_TIMEOUT_MS;
init_timer(&qca->tx_idle_timer);
qca->tx_idle_timer.function = hci_ibs_tx_idle_timeout;
qca->tx_idle_timer.data = (u_long)hu;
qca->tx_idle_delay = IBS_TX_IDLE_TIMEOUT_MS;
BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u",
qca->tx_idle_delay, qca->wake_retrans);
return 0;
}
static void qca_debugfs_init(struct hci_dev *hdev)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct qca_data *qca = hu->priv;
struct dentry *ibs_dir;
umode_t mode;
if (!hdev->debugfs)
return;
ibs_dir = debugfs_create_dir("ibs", hdev->debugfs);
/* read only */
mode = S_IRUGO;
debugfs_create_u8("tx_ibs_state", mode, ibs_dir, &qca->tx_ibs_state);
debugfs_create_u8("rx_ibs_state", mode, ibs_dir, &qca->rx_ibs_state);
debugfs_create_u64("ibs_sent_sleeps", mode, ibs_dir,
&qca->ibs_sent_slps);
debugfs_create_u64("ibs_sent_wakes", mode, ibs_dir,
&qca->ibs_sent_wakes);
debugfs_create_u64("ibs_sent_wake_acks", mode, ibs_dir,
&qca->ibs_sent_wacks);
debugfs_create_u64("ibs_recv_sleeps", mode, ibs_dir,
&qca->ibs_recv_slps);
debugfs_create_u64("ibs_recv_wakes", mode, ibs_dir,
&qca->ibs_recv_wakes);
debugfs_create_u64("ibs_recv_wake_acks", mode, ibs_dir,
&qca->ibs_recv_wacks);
debugfs_create_bool("tx_vote", mode, ibs_dir, &qca->tx_vote);
debugfs_create_u64("tx_votes_on", mode, ibs_dir, &qca->tx_votes_on);
debugfs_create_u64("tx_votes_off", mode, ibs_dir, &qca->tx_votes_off);
debugfs_create_bool("rx_vote", mode, ibs_dir, &qca->rx_vote);
debugfs_create_u64("rx_votes_on", mode, ibs_dir, &qca->rx_votes_on);
debugfs_create_u64("rx_votes_off", mode, ibs_dir, &qca->rx_votes_off);
debugfs_create_u64("votes_on", mode, ibs_dir, &qca->votes_on);
debugfs_create_u64("votes_off", mode, ibs_dir, &qca->votes_off);
debugfs_create_u32("vote_on_ms", mode, ibs_dir, &qca->vote_on_ms);
debugfs_create_u32("vote_off_ms", mode, ibs_dir, &qca->vote_off_ms);
/* read/write */
mode = S_IRUGO | S_IWUSR;
debugfs_create_u32("wake_retrans", mode, ibs_dir, &qca->wake_retrans);
debugfs_create_u32("tx_idle_delay", mode, ibs_dir,
&qca->tx_idle_delay);
}
/* Flush protocol data */
static int qca_flush(struct hci_uart *hu)
{
struct qca_data *qca = hu->priv;
BT_DBG("hu %p qca flush", hu);
skb_queue_purge(&qca->tx_wait_q);
skb_queue_purge(&qca->txq);
return 0;
}
/* Close protocol */
static int qca_close(struct hci_uart *hu)
{
struct qca_data *qca = hu->priv;
BT_DBG("hu %p qca close", hu);
serial_clock_vote(HCI_IBS_VOTE_STATS_UPDATE, hu);
skb_queue_purge(&qca->tx_wait_q);
skb_queue_purge(&qca->txq);
del_timer(&qca->tx_idle_timer);
del_timer(&qca->wake_retrans_timer);
destroy_workqueue(qca->workqueue);
qca->hu = NULL;
kfree_skb(qca->rx_skb);
hu->priv = NULL;
kfree(qca);
return 0;
}
/* Called upon a wake-up-indication from the device.
*/
static void device_want_to_wakeup(struct hci_uart *hu)
{
unsigned long flags;
struct qca_data *qca = hu->priv;
BT_DBG("hu %p want to wake up", hu);
spin_lock_irqsave(&qca->hci_ibs_lock, flags);
qca->ibs_recv_wakes++;
switch (qca->rx_ibs_state) {
case HCI_IBS_RX_ASLEEP:
/* Make sure clock is on - we may have turned clock off since
* receiving the wake up indicator awake rx clock.
*/
queue_work(qca->workqueue, &qca->ws_awake_rx);
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
return;
case HCI_IBS_RX_AWAKE:
/* Always acknowledge device wake up,
* sending IBS message doesn't count as TX ON.
*/
if (send_hci_ibs_cmd(HCI_IBS_WAKE_ACK, hu) < 0) {
BT_ERR("Failed to acknowledge device wake up");
break;
}
qca->ibs_sent_wacks++;
break;
default:
/* Any other state is illegal */
BT_ERR("Received HCI_IBS_WAKE_IND in rx state %d",
qca->rx_ibs_state);
break;
}
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
/* Actually send the packets */
hci_uart_tx_wakeup(hu);
}
/* Called upon a sleep-indication from the device.
*/
static void device_want_to_sleep(struct hci_uart *hu)
{
unsigned long flags;
struct qca_data *qca = hu->priv;
BT_DBG("hu %p want to sleep", hu);
spin_lock_irqsave(&qca->hci_ibs_lock, flags);
qca->ibs_recv_slps++;
switch (qca->rx_ibs_state) {
case HCI_IBS_RX_AWAKE:
/* Update state */
qca->rx_ibs_state = HCI_IBS_RX_ASLEEP;
/* Vote off rx clock under workqueue */
queue_work(qca->workqueue, &qca->ws_rx_vote_off);
break;
case HCI_IBS_RX_ASLEEP:
/* Fall through */
default:
/* Any other state is illegal */
BT_ERR("Received HCI_IBS_SLEEP_IND in rx state %d",
qca->rx_ibs_state);
break;
}
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
}
/* Called upon wake-up-acknowledgement from the device
*/
static void device_woke_up(struct hci_uart *hu)
{
unsigned long flags, idle_delay;
struct qca_data *qca = hu->priv;
struct sk_buff *skb = NULL;
BT_DBG("hu %p woke up", hu);
spin_lock_irqsave(&qca->hci_ibs_lock, flags);
qca->ibs_recv_wacks++;
switch (qca->tx_ibs_state) {
case HCI_IBS_TX_AWAKE:
/* Expect one if we send 2 WAKEs */
BT_DBG("Received HCI_IBS_WAKE_ACK in tx state %d",
qca->tx_ibs_state);
break;
case HCI_IBS_TX_WAKING:
/* Send pending packets */
while ((skb = skb_dequeue(&qca->tx_wait_q)))
skb_queue_tail(&qca->txq, skb);
/* Switch timers and change state to HCI_IBS_TX_AWAKE */
del_timer(&qca->wake_retrans_timer);
idle_delay = msecs_to_jiffies(qca->tx_idle_delay);
mod_timer(&qca->tx_idle_timer, jiffies + idle_delay);
qca->tx_ibs_state = HCI_IBS_TX_AWAKE;
break;
case HCI_IBS_TX_ASLEEP:
/* Fall through */
default:
BT_ERR("Received HCI_IBS_WAKE_ACK in tx state %d",
qca->tx_ibs_state);
break;
}
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
/* Actually send the packets */
hci_uart_tx_wakeup(hu);
}
/* Enqueue frame for transmittion (padding, crc, etc) may be called from
* two simultaneous tasklets.
*/
static int qca_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
unsigned long flags = 0, idle_delay;
struct qca_data *qca = hu->priv;
BT_DBG("hu %p qca enq skb %p tx_ibs_state %d", hu, skb,
qca->tx_ibs_state);
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
/* Don't go to sleep in middle of patch download or
* Out-Of-Band(GPIOs control) sleep is selected.
*/
if (!test_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags)) {
skb_queue_tail(&qca->txq, skb);
return 0;
}
spin_lock_irqsave(&qca->hci_ibs_lock, flags);
/* Act according to current state */
switch (qca->tx_ibs_state) {
case HCI_IBS_TX_AWAKE:
BT_DBG("Device awake, sending normally");
skb_queue_tail(&qca->txq, skb);
idle_delay = msecs_to_jiffies(qca->tx_idle_delay);
mod_timer(&qca->tx_idle_timer, jiffies + idle_delay);
break;
case HCI_IBS_TX_ASLEEP:
BT_DBG("Device asleep, waking up and queueing packet");
/* Save packet for later */
skb_queue_tail(&qca->tx_wait_q, skb);
qca->tx_ibs_state = HCI_IBS_TX_WAKING;
/* Schedule a work queue to wake up device */
queue_work(qca->workqueue, &qca->ws_awake_device);
break;
case HCI_IBS_TX_WAKING:
BT_DBG("Device waking up, queueing packet");
/* Transient state; just keep packet for later */
skb_queue_tail(&qca->tx_wait_q, skb);
break;
default:
BT_ERR("Illegal tx state: %d (losing packet)",
qca->tx_ibs_state);
kfree_skb(skb);
break;
}
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
return 0;
}
static int qca_ibs_sleep_ind(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
BT_DBG("hu %p recv hci ibs cmd 0x%x", hu, HCI_IBS_SLEEP_IND);
device_want_to_sleep(hu);
kfree_skb(skb);
return 0;
}
static int qca_ibs_wake_ind(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
BT_DBG("hu %p recv hci ibs cmd 0x%x", hu, HCI_IBS_WAKE_IND);
device_want_to_wakeup(hu);
kfree_skb(skb);
return 0;
}
static int qca_ibs_wake_ack(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
BT_DBG("hu %p recv hci ibs cmd 0x%x", hu, HCI_IBS_WAKE_ACK);
device_woke_up(hu);
kfree_skb(skb);
return 0;
}
#define QCA_IBS_SLEEP_IND_EVENT \
.type = HCI_IBS_SLEEP_IND, \
.hlen = 0, \
.loff = 0, \
.lsize = 0, \
.maxlen = HCI_MAX_IBS_SIZE
#define QCA_IBS_WAKE_IND_EVENT \
.type = HCI_IBS_WAKE_IND, \
.hlen = 0, \
.loff = 0, \
.lsize = 0, \
.maxlen = HCI_MAX_IBS_SIZE
#define QCA_IBS_WAKE_ACK_EVENT \
.type = HCI_IBS_WAKE_ACK, \
.hlen = 0, \
.loff = 0, \
.lsize = 0, \
.maxlen = HCI_MAX_IBS_SIZE
static const struct h4_recv_pkt qca_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
{ QCA_IBS_WAKE_IND_EVENT, .recv = qca_ibs_wake_ind },
{ QCA_IBS_WAKE_ACK_EVENT, .recv = qca_ibs_wake_ack },
{ QCA_IBS_SLEEP_IND_EVENT, .recv = qca_ibs_sleep_ind },
};
static int qca_recv(struct hci_uart *hu, const void *data, int count)
{
struct qca_data *qca = hu->priv;
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
qca->rx_skb = h4_recv_buf(hu->hdev, qca->rx_skb, data, count,
qca_recv_pkts, ARRAY_SIZE(qca_recv_pkts));
if (IS_ERR(qca->rx_skb)) {
int err = PTR_ERR(qca->rx_skb);
BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
qca->rx_skb = NULL;
return err;
}
return count;
}
static struct sk_buff *qca_dequeue(struct hci_uart *hu)
{
struct qca_data *qca = hu->priv;
return skb_dequeue(&qca->txq);
}
static uint8_t qca_get_baudrate_value(int speed)
{
switch (speed) {
case 9600:
return QCA_BAUDRATE_9600;
case 19200:
return QCA_BAUDRATE_19200;
case 38400:
return QCA_BAUDRATE_38400;
case 57600:
return QCA_BAUDRATE_57600;
case 115200:
return QCA_BAUDRATE_115200;
case 230400:
return QCA_BAUDRATE_230400;
case 460800:
return QCA_BAUDRATE_460800;
case 500000:
return QCA_BAUDRATE_500000;
case 921600:
return QCA_BAUDRATE_921600;
case 1000000:
return QCA_BAUDRATE_1000000;
case 2000000:
return QCA_BAUDRATE_2000000;
case 3000000:
return QCA_BAUDRATE_3000000;
case 3500000:
return QCA_BAUDRATE_3500000;
default:
return QCA_BAUDRATE_115200;
}
}
static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct qca_data *qca = hu->priv;
struct sk_buff *skb;
u8 cmd[] = { 0x01, 0x48, 0xFC, 0x01, 0x00 };
if (baudrate > QCA_BAUDRATE_3000000)
return -EINVAL;
cmd[4] = baudrate;
skb = bt_skb_alloc(sizeof(cmd), GFP_ATOMIC);
if (!skb) {
BT_ERR("Failed to allocate memory for baudrate packet");
return -ENOMEM;
}
/* Assign commands to change baudrate and packet type. */
memcpy(skb_put(skb, sizeof(cmd)), cmd, sizeof(cmd));
hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
skb_queue_tail(&qca->txq, skb);
hci_uart_tx_wakeup(hu);
/* wait 300ms to change new baudrate on controller side
* controller will come back after they receive this HCI command
* then host can communicate with new baudrate to controller
*/
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(msecs_to_jiffies(BAUDRATE_SETTLE_TIMEOUT_MS));
set_current_state(TASK_INTERRUPTIBLE);
return 0;
}
static int qca_setup(struct hci_uart *hu)
{
struct hci_dev *hdev = hu->hdev;
struct qca_data *qca = hu->priv;
unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200;
int ret;
BT_INFO("%s: ROME setup", hdev->name);
/* Patch downloading has to be done without IBS mode */
clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
/* Setup initial baudrate */
speed = 0;
if (hu->init_speed)
speed = hu->init_speed;
else if (hu->proto->init_speed)
speed = hu->proto->init_speed;
if (speed)
hci_uart_set_baudrate(hu, speed);
/* Setup user speed if needed */
speed = 0;
if (hu->oper_speed)
speed = hu->oper_speed;
else if (hu->proto->oper_speed)
speed = hu->proto->oper_speed;
if (speed) {
qca_baudrate = qca_get_baudrate_value(speed);
BT_INFO("%s: Set UART speed to %d", hdev->name, speed);
ret = qca_set_baudrate(hdev, qca_baudrate);
if (ret) {
BT_ERR("%s: Failed to change the baud rate (%d)",
hdev->name, ret);
return ret;
}
hci_uart_set_baudrate(hu, speed);
}
/* Setup patch / NVM configurations */
ret = qca_uart_setup_rome(hdev, qca_baudrate);
if (!ret) {
set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
qca_debugfs_init(hdev);
}
/* Setup bdaddr */
hu->hdev->set_bdaddr = qca_set_bdaddr_rome;
return ret;
}
static struct hci_uart_proto qca_proto = {
.id = HCI_UART_QCA,
.name = "QCA",
.manufacturer = 29,
.init_speed = 115200,
.oper_speed = 3000000,
.open = qca_open,
.close = qca_close,
.flush = qca_flush,
.setup = qca_setup,
.recv = qca_recv,
.enqueue = qca_enqueue,
.dequeue = qca_dequeue,
};
int __init qca_init(void)
{
return hci_uart_register_proto(&qca_proto);
}
int __exit qca_deinit(void)
{
return hci_uart_unregister_proto(&qca_proto);
}

View File

@@ -0,0 +1,198 @@
/*
*
* Bluetooth HCI UART driver
*
* Copyright (C) 2000-2001 Qualcomm Incorporated
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
* Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef N_HCI
#define N_HCI 15
#endif
/* Ioctls */
#define HCIUARTSETPROTO _IOW('U', 200, int)
#define HCIUARTGETPROTO _IOR('U', 201, int)
#define HCIUARTGETDEVICE _IOR('U', 202, int)
#define HCIUARTSETFLAGS _IOW('U', 203, int)
#define HCIUARTGETFLAGS _IOR('U', 204, int)
/* UART protocols */
#define HCI_UART_MAX_PROTO 12
#define HCI_UART_H4 0
#define HCI_UART_BCSP 1
#define HCI_UART_3WIRE 2
#define HCI_UART_H4DS 3
#define HCI_UART_LL 4
#define HCI_UART_ATH3K 5
#define HCI_UART_INTEL 6
#define HCI_UART_BCM 7
#define HCI_UART_QCA 8
#define HCI_UART_AG6XX 9
#define HCI_UART_NOKIA 10
#define HCI_UART_MRVL 11
#define HCI_UART_RAW_DEVICE 0
#define HCI_UART_RESET_ON_INIT 1
#define HCI_UART_CREATE_AMP 2
#define HCI_UART_INIT_PENDING 3
#define HCI_UART_EXT_CONFIG 4
#define HCI_UART_VND_DETECT 5
struct hci_uart;
struct hci_uart_proto {
unsigned int id;
const char *name;
unsigned int manufacturer;
unsigned int init_speed;
unsigned int oper_speed;
int (*open)(struct hci_uart *hu);
int (*close)(struct hci_uart *hu);
int (*flush)(struct hci_uart *hu);
int (*setup)(struct hci_uart *hu);
int (*set_baudrate)(struct hci_uart *hu, unsigned int speed);
int (*recv)(struct hci_uart *hu, const void *data, int len);
int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
struct sk_buff *(*dequeue)(struct hci_uart *hu);
};
struct hci_uart {
struct tty_struct *tty;
struct hci_dev *hdev;
unsigned long flags;
unsigned long hdev_flags;
struct work_struct init_ready;
struct work_struct write_work;
const struct hci_uart_proto *proto;
void *priv;
struct sk_buff *tx_skb;
unsigned long tx_state;
unsigned int init_speed;
unsigned int oper_speed;
};
/* HCI_UART proto flag bits */
#define HCI_UART_PROTO_SET 0
#define HCI_UART_REGISTERED 1
#define HCI_UART_PROTO_READY 2
/* TX states */
#define HCI_UART_SENDING 1
#define HCI_UART_TX_WAKEUP 2
int hci_uart_register_proto(const struct hci_uart_proto *p);
int hci_uart_unregister_proto(const struct hci_uart_proto *p);
int hci_uart_tx_wakeup(struct hci_uart *hu);
int hci_uart_init_ready(struct hci_uart *hu);
void hci_uart_init_tty(struct hci_uart *hu);
void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed);
void hci_uart_set_flow_control(struct hci_uart *hu, bool enable);
void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
unsigned int oper_speed);
#ifdef CONFIG_BT_HCIUART_H4
int h4_init(void);
int h4_deinit(void);
struct h4_recv_pkt {
u8 type; /* Packet type */
u8 hlen; /* Header length */
u8 loff; /* Data length offset in header */
u8 lsize; /* Data length field size */
u16 maxlen; /* Max overall packet length */
int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
};
#define H4_RECV_ACL \
.type = HCI_ACLDATA_PKT, \
.hlen = HCI_ACL_HDR_SIZE, \
.loff = 2, \
.lsize = 2, \
.maxlen = HCI_MAX_FRAME_SIZE \
#define H4_RECV_SCO \
.type = HCI_SCODATA_PKT, \
.hlen = HCI_SCO_HDR_SIZE, \
.loff = 2, \
.lsize = 1, \
.maxlen = HCI_MAX_SCO_SIZE
#define H4_RECV_EVENT \
.type = HCI_EVENT_PKT, \
.hlen = HCI_EVENT_HDR_SIZE, \
.loff = 1, \
.lsize = 1, \
.maxlen = HCI_MAX_EVENT_SIZE
struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
const unsigned char *buffer, int count,
const struct h4_recv_pkt *pkts, int pkts_count);
#endif
#ifdef CONFIG_BT_HCIUART_BCSP
int bcsp_init(void);
int bcsp_deinit(void);
#endif
#ifdef CONFIG_BT_HCIUART_LL
int ll_init(void);
int ll_deinit(void);
#endif
#ifdef CONFIG_BT_HCIUART_ATH3K
int ath_init(void);
int ath_deinit(void);
#endif
#ifdef CONFIG_BT_HCIUART_3WIRE
int h5_init(void);
int h5_deinit(void);
#endif
#ifdef CONFIG_BT_HCIUART_INTEL
int intel_init(void);
int intel_deinit(void);
#endif
#ifdef CONFIG_BT_HCIUART_BCM
int bcm_init(void);
int bcm_deinit(void);
#endif
#ifdef CONFIG_BT_HCIUART_QCA
int qca_init(void);
int qca_deinit(void);
#endif
#ifdef CONFIG_BT_HCIUART_AG6XX
int ag6xx_init(void);
int ag6xx_deinit(void);
#endif
#ifdef CONFIG_BT_HCIUART_MRVL
int mrvl_init(void);
int mrvl_deinit(void);
#endif

View File

@@ -0,0 +1,410 @@
/*
*
* Bluetooth virtual HCI driver
*
* Copyright (C) 2000-2001 Qualcomm Incorporated
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
* Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <asm/unaligned.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/skbuff.h>
#include <linux/miscdevice.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#define VERSION "1.5"
static bool amp;
struct vhci_data {
struct hci_dev *hdev;
wait_queue_head_t read_wait;
struct sk_buff_head readq;
struct mutex open_mutex;
struct delayed_work open_timeout;
};
static int vhci_open_dev(struct hci_dev *hdev)
{
return 0;
}
static int vhci_close_dev(struct hci_dev *hdev)
{
struct vhci_data *data = hci_get_drvdata(hdev);
skb_queue_purge(&data->readq);
return 0;
}
static int vhci_flush(struct hci_dev *hdev)
{
struct vhci_data *data = hci_get_drvdata(hdev);
skb_queue_purge(&data->readq);
return 0;
}
static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct vhci_data *data = hci_get_drvdata(hdev);
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
skb_queue_tail(&data->readq, skb);
wake_up_interruptible(&data->read_wait);
return 0;
}
static int __vhci_create_device(struct vhci_data *data, __u8 opcode)
{
struct hci_dev *hdev;
struct sk_buff *skb;
__u8 dev_type;
if (data->hdev)
return -EBADFD;
/* bits 0-1 are dev_type (Primary or AMP) */
dev_type = opcode & 0x03;
if (dev_type != HCI_PRIMARY && dev_type != HCI_AMP)
return -EINVAL;
/* bits 2-5 are reserved (must be zero) */
if (opcode & 0x3c)
return -EINVAL;
skb = bt_skb_alloc(4, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdev = hci_alloc_dev();
if (!hdev) {
kfree_skb(skb);
return -ENOMEM;
}
data->hdev = hdev;
hdev->bus = HCI_VIRTUAL;
hdev->dev_type = dev_type;
hci_set_drvdata(hdev, data);
hdev->open = vhci_open_dev;
hdev->close = vhci_close_dev;
hdev->flush = vhci_flush;
hdev->send = vhci_send_frame;
/* bit 6 is for external configuration */
if (opcode & 0x40)
set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks);
/* bit 7 is for raw device */
if (opcode & 0x80)
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device");
hci_free_dev(hdev);
data->hdev = NULL;
kfree_skb(skb);
return -EBUSY;
}
hci_skb_pkt_type(skb) = HCI_VENDOR_PKT;
*skb_put(skb, 1) = 0xff;
*skb_put(skb, 1) = opcode;
put_unaligned_le16(hdev->id, skb_put(skb, 2));
skb_queue_tail(&data->readq, skb);
wake_up_interruptible(&data->read_wait);
return 0;
}
static int vhci_create_device(struct vhci_data *data, __u8 opcode)
{
int err;
mutex_lock(&data->open_mutex);
err = __vhci_create_device(data, opcode);
mutex_unlock(&data->open_mutex);
return err;
}
static inline ssize_t vhci_get_user(struct vhci_data *data,
const struct iovec *iov,
unsigned long count)
{
size_t len = iov_length(iov, count);
struct sk_buff *skb;
__u8 pkt_type, opcode;
unsigned long i;
int ret;
if (len < 2 || len > HCI_MAX_FRAME_SIZE)
return -EINVAL;
skb = bt_skb_alloc(len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
for (i = 0; i < count; i++) {
if (copy_from_user(skb_put(skb, iov[i].iov_len),
iov[i].iov_base, iov[i].iov_len)) {
kfree_skb(skb);
return -EFAULT;
}
}
pkt_type = *((__u8 *) skb->data);
skb_pull(skb, 1);
switch (pkt_type) {
case HCI_EVENT_PKT:
case HCI_ACLDATA_PKT:
case HCI_SCODATA_PKT:
if (!data->hdev) {
kfree_skb(skb);
return -ENODEV;
}
hci_skb_pkt_type(skb) = pkt_type;
ret = hci_recv_frame(data->hdev, skb);
break;
case HCI_VENDOR_PKT:
cancel_delayed_work_sync(&data->open_timeout);
opcode = *((__u8 *) skb->data);
skb_pull(skb, 1);
if (skb->len > 0) {
kfree_skb(skb);
return -EINVAL;
}
kfree_skb(skb);
ret = vhci_create_device(data, opcode);
break;
default:
kfree_skb(skb);
return -EINVAL;
}
return (ret < 0) ? ret : len;
}
static inline ssize_t vhci_put_user(struct vhci_data *data,
struct sk_buff *skb,
char __user *buf, int count)
{
char __user *ptr = buf;
int len;
len = min_t(unsigned int, skb->len, count);
if (copy_to_user(ptr, skb->data, len))
return -EFAULT;
if (!data->hdev)
return len;
data->hdev->stat.byte_tx += len;
switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
data->hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
data->hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
data->hdev->stat.sco_tx++;
break;
}
return len;
}
static ssize_t vhci_read(struct file *file,
char __user *buf, size_t count, loff_t *pos)
{
struct vhci_data *data = file->private_data;
struct sk_buff *skb;
ssize_t ret = 0;
while (count) {
skb = skb_dequeue(&data->readq);
if (skb) {
ret = vhci_put_user(data, skb, buf, count);
if (ret < 0)
skb_queue_head(&data->readq, skb);
else
kfree_skb(skb);
break;
}
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
ret = wait_event_interruptible(data->read_wait,
!skb_queue_empty(&data->readq));
if (ret < 0)
break;
}
return ret;
}
static ssize_t vhci_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long count, loff_t pos)
{
struct file *file = iocb->ki_filp;
struct vhci_data *data = file->private_data;
return vhci_get_user(data, iov, count);
}
static unsigned int vhci_poll(struct file *file, poll_table *wait)
{
struct vhci_data *data = file->private_data;
poll_wait(file, &data->read_wait, wait);
if (!skb_queue_empty(&data->readq))
return POLLIN | POLLRDNORM;
return POLLOUT | POLLWRNORM;
}
static void vhci_open_timeout(struct work_struct *work)
{
struct vhci_data *data = container_of(work, struct vhci_data,
open_timeout.work);
vhci_create_device(data, amp ? HCI_AMP : HCI_PRIMARY);
}
static int vhci_open(struct inode *inode, struct file *file)
{
struct vhci_data *data;
data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
skb_queue_head_init(&data->readq);
init_waitqueue_head(&data->read_wait);
mutex_init(&data->open_mutex);
INIT_DELAYED_WORK(&data->open_timeout, vhci_open_timeout);
file->private_data = data;
nonseekable_open(inode, file);
schedule_delayed_work(&data->open_timeout, msecs_to_jiffies(1000));
return 0;
}
static int vhci_release(struct inode *inode, struct file *file)
{
struct vhci_data *data = file->private_data;
struct hci_dev *hdev;
cancel_delayed_work_sync(&data->open_timeout);
hdev = data->hdev;
if (hdev) {
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
skb_queue_purge(&data->readq);
file->private_data = NULL;
kfree(data);
return 0;
}
static const struct file_operations vhci_fops = {
.owner = THIS_MODULE,
.read = vhci_read,
.aio_write = vhci_write,
.poll = vhci_poll,
.open = vhci_open,
.release = vhci_release,
.llseek = no_llseek,
};
static struct miscdevice vhci_miscdev = {
.name = "vhci",
.fops = &vhci_fops,
.minor = VHCI_MINOR,
};
static int __init vhci_init(void)
{
BT_INFO("Virtual HCI driver ver %s", VERSION);
return misc_register(&vhci_miscdev);
}
static void __exit vhci_exit(void)
{
misc_deregister(&vhci_miscdev);
}
module_init(vhci_init);
module_exit(vhci_exit);
module_param(amp, bool, 0644);
MODULE_PARM_DESC(amp, "Create AMP controller device");
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS("devname:vhci");
MODULE_ALIAS_MISCDEV(VHCI_MINOR);