Initial commit: Linux kernel 4.19 for Unplugged OS

This commit is contained in:
ziv committed 2025-10-06 13:52:42 +00:00
commit d30235b04c
80519 files changed
+35800681

No files matched your search

+416
View File
@@ -0,0 +1,416 @@
# SPDX-License-Identifier: GPL-2.0
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_AUTOSUSPEND
bool "Enable USB autosuspend for Bluetooth USB devices by default"
depends on BT_HCIBTUSB
help
Say Y here to enable USB autosuspend for Bluetooth USB devices by
default.
This can be overridden by passing btusb.enable_autosuspend=[y|n]
on the kernel commandline.
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 SERIAL_DEV_BUS || !SERIAL_DEV_BUS
depends on NVMEM || !NVMEM
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_SERDEV
bool
depends on SERIAL_DEV_BUS && BT_HCIUART
default y
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_NOKIA
tristate "UART Nokia H4+ protocol support"
depends on BT_HCIUART
depends on BT_HCIUART_SERDEV
depends on GPIOLIB
depends on PM
select BT_HCIUART_H4
select BT_BCM
help
Nokia H4+ is serial protocol for communication between Bluetooth
device and host. This protocol is required for Bluetooth devices
with UART interface in Nokia devices.
Say Y here to compile support for Nokia's 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_SERDEV
select BT_HCIUART_H4
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
depends on BT_HCIUART_SERDEV
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
depends on GPIOLIB
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
depends on BT_HCIUART_SERDEV
depends on (!ACPI || SERIAL_DEV_CTRL_TTYPORT)
depends on GPIOLIB
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_RTL
bool "Realtek protocol support"
depends on BT_HCIUART
depends on BT_HCIUART_SERDEV
depends on GPIOLIB
depends on ACPI
select BT_HCIUART_3WIRE
select BT_RTL
help
The Realtek protocol support enables Bluetooth HCI over 3-Wire
serial port internface for Realtek Bluetooth controllers.
Say Y here to compile support for Realtek protocol.
config BT_HCIUART_QCA
bool "Qualcomm Atheros protocol support"
depends on BT_HCIUART
depends on BT_HCIUART_SERDEV
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
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_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_MTKUART
tristate "MediaTek HCI UART driver"
depends on SERIAL_DEV_BUS
help
MediaTek Bluetooth HCI UART driver.
This driver is required if you want to use MediaTek Bluetooth
with serial interface.
Say Y here to compile support for MediaTek Bluetooth UART devices
into the kernel or say M to compile it as module (btmtkuart).
config BT_QCOMSMD
tristate "Qualcomm SMD based HCI support"
depends on RPMSG || (COMPILE_TEST && RPMSG=n)
depends on QCOM_WCNSS_CTRL || (COMPILE_TEST && QCOM_WCNSS_CTRL=n)
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.
config BT_HCIRSI
tristate
help
Redpine BT driver.
This driver handles BT traffic from upper layers and pass
to the RSI_91x coex module for further scheduling to device
Say Y here to compile support for HCI over Redpine into the
kernel or say M to compile as a module.
endmenu
+48
View File
@@ -0,0 +1,48 @@
# SPDX-License-Identifier: GPL-2.0
#
# 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_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_MTKUART) += btmtkuart.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
obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o
obj-$(CONFIG_BT_HCIRSI) += btrsi.o
btmrvl-y := btmrvl_main.o
btmrvl-$(CONFIG_DEBUG_FS) += btmrvl_debugfs.o
hci_uart-y := hci_ldisc.o
hci_uart-$(CONFIG_BT_HCIUART_SERDEV) += hci_serdev.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)
+589
View File
@@ -0,0 +1,589 @@
/*
* 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(0x04CA, 0x3018) },
{ 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(0x04ca, 0x3018), .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 */
};
static inline void ath3k_log_failed_loading(int err, int len, int size)
{
BT_ERR("Error in firmware loading err = %d, len = %d, size = %d",
err, len, size);
}
#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, FW_HDR_SIZE);
err = usb_control_msg(udev, pipe, USB_REQ_DFU_DNLOAD, USB_TYPE_VENDOR,
0, 0, send_buf, FW_HDR_SIZE,
USB_CTRL_SET_TIMEOUT);
if (err < 0) {
BT_ERR("Can't change to loading configuration err");
goto error;
}
sent += FW_HDR_SIZE;
count -= FW_HDR_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)) {
ath3k_log_failed_loading(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)) {
ath3k_log_failed_loading(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];
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];
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("%s intf %p", __func__, 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
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;
/* fall through */
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
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)
skb_put_data(data->reassembly, 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_KERNEL);
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;
skb_put_data(nskb, 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;
skb_put_data(nskb, 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");
+910
View File
@@ -0,0 +1,910 @@
/*
*
* 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_ACTIVITY 2
#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(struct timer_list *t)
{
struct bluecard_info *info = from_timer(info, t, timer);
unsigned int iobase = info->p_dev->resource[0]->start;
if (test_bit(CARD_ACTIVITY, &(info->hw_state))) {
/* leave LED in inactive state for HZ/10 for blink effect */
clear_bit(CARD_ACTIVITY, &(info->hw_state));
mod_timer(&(info->timer), jiffies + HZ / 10);
}
/* Disable activity LED, enable power LED */
outb(0x08 | 0x20, iobase + 0x30);
}
static void bluecard_enable_activity_led(struct bluecard_info *info)
{
unsigned int iobase = info->p_dev->resource[0]->start;
/* don't disturb running blink timer */
if (timer_pending(&(info->timer)))
return;
set_bit(CARD_ACTIVITY, &(info->hw_state));
if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
/* Enable activity LED, keep power LED enabled */
outb(0x18 | 0x60, iobase + 0x30);
} else {
/* Disable power LED */
outb(0x00, iobase + 0x30);
}
/* Stop the LED after HZ/10 */
mod_timer(&(info->timer), jiffies + HZ / 10);
}
/* ======================== 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 */
mdelay(100);
/* 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 */
mdelay(1000);
}
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_u8(info->rx_skb, 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_KERNEL);
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;
}
skb_put_data(skb, 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);
unsigned int iobase = info->p_dev->resource[0]->start;
if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
/* Enable power 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);
unsigned int iobase = info->p_dev->resource[0]->start;
bluecard_hci_flush(hdev);
/* Stop LED timer */
del_timer_sync(&(info->timer));
/* Disable power 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));
timer_setup(&info->timer, bluecard_activity_led_timeout, 0);
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);
+459
View File
@@ -0,0 +1,459 @@
/*
*
* 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 "h4_recv.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_dev_err(hdev, "corrupted event packet");
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_dev_err(hdev, "urb %p failed to resubmit (%d)", 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_dev_err(hdev, "urb %p submission failed (%d)", 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_dev_err(hdev, "urb %p submission failed (%d)", 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)
{
static 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_dev_info(hdev, "%s", (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_KERNEL);
if (!urb)
return -ENOMEM;
/* Prepend skb with frame type */
*(u8 *)skb_push(skb, 1) = hci_skb_pkt_type(skb);
switch (hci_skb_pkt_type(skb)) {
case HCI_COMMAND_PKT:
dr = kmalloc(sizeof(*dr), GFP_KERNEL);
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_KERNEL);
if (err < 0) {
bt_dev_err(hdev, "urb %p submission failed", 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");
+746
View File
@@ -0,0 +1,746 @@
/*
*
* 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_u8(info->rx_skb, 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_dev_info(info->hdev, "Antenna %s",
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);
+534
View File
@@ -0,0 +1,534 @@
/*
*
* 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_dev_err(hdev, "BCM: Reading device address failed (%d)", err);
return err;
}
if (skb->len != sizeof(*bda)) {
bt_dev_err(hdev, "BCM: Device address length mismatch");
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_dev_info(hdev, "BCM: Using default device address (%pMR)",
&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_dev_err(hdev, "BCM: Change address command failed (%d)", 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_dev_err(hdev, "BCM: Download Minidrv command failed (%d)",
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_dev_err(hdev, "BCM: Patch is corrupted");
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_dev_err(hdev, "BCM: Patch command %04x failed (%d)",
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_dev_err(hdev, "BCM: Reset failed (%d)", err);
return err;
}
kfree_skb(skb);
/* 100 msec delay for module to complete reset process */
msleep(100);
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_dev_err(hdev, "BCM: Reading local name failed (%ld)",
PTR_ERR(skb));
return skb;
}
if (skb->len != sizeof(struct hci_rp_read_local_name)) {
bt_dev_err(hdev, "BCM: Local name length mismatch");
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_dev_err(hdev, "BCM: Reading local version info failed (%ld)",
PTR_ERR(skb));
return skb;
}
if (skb->len != sizeof(struct hci_rp_read_local_version)) {
bt_dev_err(hdev, "BCM: Local version length mismatch");
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_dev_err(hdev, "BCM: Read verbose config info failed (%ld)",
PTR_ERR(skb));
return skb;
}
if (skb->len != 7) {
bt_dev_err(hdev, "BCM: Verbose config length mismatch");
kfree_skb(skb);
return ERR_PTR(-EIO);
}
return skb;
}
static struct sk_buff *btbcm_read_controller_features(struct hci_dev *hdev)
{
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, 0xfc6e, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "BCM: Read controller features failed (%ld)",
PTR_ERR(skb));
return skb;
}
if (skb->len != 9) {
bt_dev_err(hdev, "BCM: Controller features length mismatch");
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_dev_err(hdev, "BCM: Read USB product info failed (%ld)",
PTR_ERR(skb));
return skb;
}
if (skb->len != 5) {
bt_dev_err(hdev, "BCM: USB product length mismatch");
kfree_skb(skb);
return ERR_PTR(-EIO);
}
return skb;
}
static int btbcm_read_info(struct hci_dev *hdev)
{
struct sk_buff *skb;
/* Read Verbose Config Version Info */
skb = btbcm_read_verbose_config(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
bt_dev_info(hdev, "BCM: chip id %u", skb->data[1]);
kfree_skb(skb);
/* Read Controller Features */
skb = btbcm_read_controller_features(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
bt_dev_info(hdev, "BCM: features 0x%2.2x", skb->data[1]);
kfree_skb(skb);
/* Read Local Name */
skb = btbcm_read_local_name(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
bt_dev_info(hdev, "%s", (char *)(skb->data + 1));
kfree_skb(skb);
return 0;
}
struct bcm_subver_table {
u16 subver;
const char *name;
};
static const struct bcm_subver_table bcm_uart_subver_table[] = {
{ 0x4103, "BCM4330B1" }, /* 002.001.003 */
{ 0x410e, "BCM43341B0" }, /* 002.001.014 */
{ 0x4406, "BCM4324B3" }, /* 002.004.006 */
{ 0x4606, "BCM4324B5" }, /* 002.006.006 */
{ 0x6109, "BCM4335C0" }, /* 003.001.009 */
{ 0x610c, "BCM4354" }, /* 003.001.012 */
{ 0x2122, "BCM4343A0" }, /* 001.001.034 */
{ 0x2209, "BCM43430A1" }, /* 001.002.009 */
{ 0x6119, "BCM4345C0" }, /* 003.001.025 */
{ 0x230f, "BCM4356A2" }, /* 001.003.015 */
{ }
};
static const struct bcm_subver_table bcm_usb_subver_table[] = {
{ 0x2105, "BCM20703A1" }, /* 001.001.005 */
{ 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_initialize(struct hci_dev *hdev, char *fw_name, size_t len,
bool reinit)
{
u16 subver, rev, pid, vid;
const char *hw_name = "BCM";
struct sk_buff *skb;
struct hci_rp_read_local_version *ver;
const struct bcm_subver_table *bcm_subver_table;
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 controller information */
if (!reinit) {
err = btbcm_read_info(hdev);
if (err)
return err;
}
/* Upper nibble of rev should be between 0 and 3? */
if (((rev & 0xf000) >> 12) > 3)
return 0;
bcm_subver_table = (hdev->bus == HCI_USB) ? bcm_usb_subver_table :
bcm_uart_subver_table;
for (i = 0; bcm_subver_table[i].name; i++) {
if (subver == bcm_subver_table[i].subver) {
hw_name = bcm_subver_table[i].name;
break;
}
}
if (hdev->bus == HCI_USB) {
/* 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);
snprintf(fw_name, len, "brcm/%s-%4.4x-%4.4x.hcd",
hw_name, vid, pid);
} else {
snprintf(fw_name, len, "brcm/%s.hcd", hw_name);
}
bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u",
hw_name, (subver & 0xe000) >> 13,
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
return 0;
}
EXPORT_SYMBOL_GPL(btbcm_initialize);
int btbcm_finalize(struct hci_dev *hdev)
{
char fw_name[64];
int err;
/* Re-initialize */
err = btbcm_initialize(hdev, fw_name, sizeof(fw_name), true);
if (err)
return err;
btbcm_check_bdaddr(hdev);
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
return 0;
}
EXPORT_SYMBOL_GPL(btbcm_finalize);
int btbcm_setup_patchram(struct hci_dev *hdev)
{
char fw_name[64];
const struct firmware *fw;
struct sk_buff *skb;
int err;
/* Initialize */
err = btbcm_initialize(hdev, fw_name, sizeof(fw_name), false);
if (err)
return err;
err = request_firmware(&fw, fw_name, &hdev->dev);
if (err < 0) {
bt_dev_info(hdev, "BCM: Patch %s not found", fw_name);
goto done;
}
btbcm_patchram(hdev, fw);
release_firmware(fw);
/* Re-initialize */
err = btbcm_initialize(hdev, fw_name, sizeof(fw_name), true);
if (err)
return err;
/* Read Local Name */
skb = btbcm_read_local_name(hdev);
if (IS_ERR(skb))
return PTR_ERR(skb);
bt_dev_info(hdev, "%s", (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_dev_info(hdev, "BCM: chip id %u build %4.4u",
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_dev_info(hdev, "BCM: product %4.4x:%4.4x",
get_unaligned_le16(skb->data + 1),
get_unaligned_le16(skb->data + 3));
kfree_skb(skb);
}
/* Read Controller Features */
skb = btbcm_read_controller_features(hdev);
if (!IS_ERR(skb)) {
bt_dev_info(hdev, "BCM: features 0x%2.2x", skb->data[1]);
kfree_skb(skb);
}
/* Read Local Name */
skb = btbcm_read_local_name(hdev);
if (!IS_ERR(skb)) {
bt_dev_info(hdev, "%s", (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");
+118
View File
@@ -0,0 +1,118 @@
/*
*
* 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 break_to_host;
__u8 pulsed_host_wake;
} __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,
bool reinit);
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, bool reinit)
{
return 0;
}
static inline int btbcm_finalize(struct hci_dev *hdev)
{
return 0;
}
#endif
+734
View File
@@ -0,0 +1,734 @@
/*
*
* 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 <asm/unaligned.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_dev_err(hdev, "Reading Intel device address failed (%d)",
err);
return err;
}
if (skb->len != sizeof(*bda)) {
bt_dev_err(hdev, "Intel device address length mismatch");
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_dev_err(hdev, "Found Intel default device address (%pMR)",
&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)
{
static 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_dev_err(hdev, "Changing Intel device address failed (%d)",
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_dev_err(hdev, "Changing Intel diagnostic mode failed (%d)",
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_dev_err(hdev, "Hardware error 0x%2.2x", code);
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Reset after hardware error failed (%ld)",
PTR_ERR(skb));
return;
}
kfree_skb(skb);
skb = __hci_cmd_sync(hdev, 0xfc22, 1, &type, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Retrieving Intel exception info failed (%ld)",
PTR_ERR(skb));
return;
}
if (skb->len != 13) {
bt_dev_err(hdev, "Exception info size mismatch");
kfree_skb(skb);
return;
}
bt_dev_err(hdev, "Exception info %s", (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_dev_info(hdev, "%s revision %u.%u build %u week %u %u",
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_direct(&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_dev_err(hdev, "Setting Intel event mask failed (%d)", 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);
int btintel_send_intel_reset(struct hci_dev *hdev, u32 boot_param)
{
struct intel_reset params = { 0x00, 0x01, 0x00, 0x01, 0x00000000 };
struct sk_buff *skb;
params.boot_param = cpu_to_le32(boot_param);
skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(params), &params,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Failed to send Intel Reset command");
return PTR_ERR(skb);
}
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(btintel_send_intel_reset);
int btintel_read_boot_params(struct hci_dev *hdev,
struct intel_boot_params *params)
{
struct sk_buff *skb;
skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Reading Intel boot parameters failed (%ld)",
PTR_ERR(skb));
return PTR_ERR(skb);
}
if (skb->len != sizeof(*params)) {
bt_dev_err(hdev, "Intel boot parameters size mismatch");
kfree_skb(skb);
return -EILSEQ;
}
memcpy(params, skb->data, sizeof(*params));
kfree_skb(skb);
if (params->status) {
bt_dev_err(hdev, "Intel boot parameters command failed (%02x)",
params->status);
return -bt_to_errno(params->status);
}
bt_dev_info(hdev, "Device revision is %u",
le16_to_cpu(params->dev_revid));
bt_dev_info(hdev, "Secure boot is %s",
params->secure_boot ? "enabled" : "disabled");
bt_dev_info(hdev, "OTP lock is %s",
params->otp_lock ? "enabled" : "disabled");
bt_dev_info(hdev, "API lock is %s",
params->api_lock ? "enabled" : "disabled");
bt_dev_info(hdev, "Debug lock is %s",
params->debug_lock ? "enabled" : "disabled");
bt_dev_info(hdev, "Minimum firmware build %u week %u %u",
params->min_fw_build_nn, params->min_fw_build_cw,
2000 + params->min_fw_build_yy);
return 0;
}
EXPORT_SYMBOL_GPL(btintel_read_boot_params);
int btintel_download_firmware(struct hci_dev *hdev, const struct firmware *fw,
u32 *boot_param)
{
int err;
const u8 *fw_ptr;
u32 frag_len;
/* Start the firmware download transaction with the Init fragment
* represented by the 128 bytes of CSS header.
*/
err = btintel_secure_send(hdev, 0x00, 128, fw->data);
if (err < 0) {
bt_dev_err(hdev, "Failed to send firmware header (%d)", err);
goto done;
}
/* Send the 256 bytes of public key information from the firmware
* as the PKey fragment.
*/
err = btintel_secure_send(hdev, 0x03, 256, fw->data + 128);
if (err < 0) {
bt_dev_err(hdev, "Failed to send firmware pkey (%d)", err);
goto done;
}
/* Send the 256 bytes of signature information from the firmware
* as the Sign fragment.
*/
err = btintel_secure_send(hdev, 0x02, 256, fw->data + 388);
if (err < 0) {
bt_dev_err(hdev, "Failed to send firmware signature (%d)", err);
goto done;
}
fw_ptr = fw->data + 644;
frag_len = 0;
while (fw_ptr - fw->data < fw->size) {
struct hci_command_hdr *cmd = (void *)(fw_ptr + frag_len);
/* Each SKU has a different reset parameter to use in the
* HCI_Intel_Reset command and it is embedded in the firmware
* data. So, instead of using static value per SKU, check
* the firmware data and save it for later use.
*/
if (le16_to_cpu(cmd->opcode) == 0xfc0e) {
/* The boot parameter is the first 32-bit value
* and rest of 3 octets are reserved.
*/
*boot_param = get_unaligned_le32(fw_ptr + sizeof(*cmd));
bt_dev_dbg(hdev, "boot_param=0x%x", *boot_param);
}
frag_len += sizeof(*cmd) + cmd->plen;
/* The parameter length of the secure send command requires
* a 4 byte alignment. It happens so that the firmware file
* contains proper Intel_NOP commands to align the fragments
* as needed.
*
* Send set of commands with 4 byte alignment from the
* firmware data buffer as a single Data fragement.
*/
if (!(frag_len % 4)) {
err = btintel_secure_send(hdev, 0x01, frag_len, fw_ptr);
if (err < 0) {
bt_dev_err(hdev,
"Failed to send firmware data (%d)",
err);
goto done;
}
fw_ptr += frag_len;
frag_len = 0;
}
}
done:
return err;
}
EXPORT_SYMBOL_GPL(btintel_download_firmware);
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");
MODULE_FIRMWARE("intel/ibt-12-16.sfi");
MODULE_FIRMWARE("intel/ibt-12-16.ddc");
+199
View File
@@ -0,0 +1,199 @@
/*
*
* 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;
struct intel_reset {
__u8 reset_type;
__u8 patch_enable;
__u8 ddc_reload;
__u8 boot_option;
__le32 boot_param;
} __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);
int btintel_send_intel_reset(struct hci_dev *hdev, u32 boot_param);
int btintel_read_boot_params(struct hci_dev *hdev,
struct intel_boot_params *params);
int btintel_download_firmware(struct hci_dev *dev, const struct firmware *fw,
u32 *boot_param);
#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);
}
static inline int btintel_send_intel_reset(struct hci_dev *hdev,
u32 reset_param)
{
return -EOPNOTSUPP;
}
static inline int btintel_read_boot_params(struct hci_dev *hdev,
struct intel_boot_params *params)
{
return -EOPNOTSUPP;
}
static inline int btintel_download_firmware(struct hci_dev *dev,
const struct firmware *fw,
u32 *boot_param)
{
return -EOPNOTSUPP;
}
#endif
+206
View File
@@ -0,0 +1,206 @@
/**
* 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;
long result, ret;
ret = kstrtol_from_user(ubuf, count, 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;
long result, ret;
ret = kstrtol_from_user(ubuf, count, 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;
long result, ret;
ret = kstrtol_from_user(ubuf, count, 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,
};
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);
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);
}
+189
View File
@@ -0,0 +1,189 @@
/*
* 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/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);
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);
#ifdef CONFIG_DEBUG_FS
void btmrvl_debugfs_init(struct hci_dev *hdev);
void btmrvl_debugfs_remove(struct hci_dev *hdev);
#endif
+795
View File
@@ -0,0 +1,795 @@
/**
* 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_KERNEL);
if (!skb) {
BT_ERR("No free skb");
return -ENOMEM;
}
hdr = skb_put(skb, HCI_COMMAND_HDR_SIZE);
hdr->opcode = cpu_to_le16(opcode);
hdr->plen = len;
if (len)
skb_put_data(skb, 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;
}
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);
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 calibration 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_entry_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");
if (IS_ERR(priv->main_thread.task))
goto err_thread;
priv->btmrvl_dev.card = card;
priv->btmrvl_dev.tx_dnld_rdy = true;
return priv;
err_thread:
btmrvl_free_adapter(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
+128
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))
+633
View File
@@ -0,0 +1,633 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018 MediaTek Inc.
/*
* Bluetooth support for MediaTek serial devices
*
* Author: Sean Wang <sean.wang@mediatek.com>
*
*/
#include <asm/unaligned.h>
#include <linux/atomic.h>
#include <linux/clk.h>
#include <linux/firmware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/serdev.h>
#include <linux/skbuff.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "h4_recv.h"
#define VERSION "0.1"
#define FIRMWARE_MT7622 "mediatek/mt7622pr2h.bin"
#define MTK_STP_TLR_SIZE 2
#define BTMTKUART_TX_STATE_ACTIVE 1
#define BTMTKUART_TX_STATE_WAKEUP 2
#define BTMTKUART_TX_WAIT_VND_EVT 3
enum {
MTK_WMT_PATCH_DWNLD = 0x1,
MTK_WMT_FUNC_CTRL = 0x6,
MTK_WMT_RST = 0x7
};
struct mtk_stp_hdr {
u8 prefix;
__be16 dlen;
u8 cs;
} __packed;
struct mtk_wmt_hdr {
u8 dir;
u8 op;
__le16 dlen;
u8 flag;
} __packed;
struct mtk_hci_wmt_cmd {
struct mtk_wmt_hdr hdr;
u8 data[256];
} __packed;
struct btmtkuart_dev {
struct hci_dev *hdev;
struct serdev_device *serdev;
struct clk *clk;
struct work_struct tx_work;
unsigned long tx_state;
struct sk_buff_head txq;
struct sk_buff *rx_skb;
u8 stp_pad[6];
u8 stp_cursor;
u16 stp_dlen;
};
static int mtk_hci_wmt_sync(struct hci_dev *hdev, u8 op, u8 flag, u16 plen,
const void *param)
{
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
struct mtk_hci_wmt_cmd wc;
struct mtk_wmt_hdr *hdr;
u32 hlen;
int err;
hlen = sizeof(*hdr) + plen;
if (hlen > 255)
return -EINVAL;
hdr = (struct mtk_wmt_hdr *)&wc;
hdr->dir = 1;
hdr->op = op;
hdr->dlen = cpu_to_le16(plen + 1);
hdr->flag = flag;
memcpy(wc.data, param, plen);
set_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state);
err = __hci_cmd_send(hdev, 0xfc6f, hlen, &wc);
if (err < 0) {
clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state);
return err;
}
/* The vendor specific WMT commands are all answered by a vendor
* specific event and will not have the Command Status or Command
* Complete as with usual HCI command flow control.
*
* After sending the command, wait for BTMTKUART_TX_WAIT_VND_EVT
* state to be cleared. The driver speicfic event receive routine
* will clear that state and with that indicate completion of the
* WMT command.
*/
err = wait_on_bit_timeout(&bdev->tx_state, BTMTKUART_TX_WAIT_VND_EVT,
TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT);
if (err == -EINTR) {
bt_dev_err(hdev, "Execution of wmt command interrupted");
clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state);
return err;
}
if (err) {
bt_dev_err(hdev, "Execution of wmt command timed out");
clear_bit(BTMTKUART_TX_WAIT_VND_EVT, &bdev->tx_state);
return -ETIMEDOUT;
}
return 0;
}
static int mtk_setup_fw(struct hci_dev *hdev)
{
const struct firmware *fw;
const u8 *fw_ptr;
size_t fw_size;
int err, dlen;
u8 flag;
err = request_firmware(&fw, FIRMWARE_MT7622, &hdev->dev);
if (err < 0) {
bt_dev_err(hdev, "Failed to load firmware file (%d)", err);
return err;
}
fw_ptr = fw->data;
fw_size = fw->size;
/* The size of patch header is 30 bytes, should be skip */
if (fw_size < 30) {
err = -EINVAL;
goto free_fw;
}
fw_size -= 30;
fw_ptr += 30;
flag = 1;
while (fw_size > 0) {
dlen = min_t(int, 250, fw_size);
/* Tell device the position in sequence */
if (fw_size - dlen <= 0)
flag = 3;
else if (fw_size < fw->size - 30)
flag = 2;
err = mtk_hci_wmt_sync(hdev, MTK_WMT_PATCH_DWNLD, flag, dlen,
fw_ptr);
if (err < 0) {
bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
err);
break;
}
fw_size -= dlen;
fw_ptr += dlen;
}
free_fw:
release_firmware(fw);
return err;
}
static int btmtkuart_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
struct hci_event_hdr *hdr = (void *)skb->data;
int err;
/* Fix up the vendor event id with 0xff for vendor specific instead
* of 0xe4 so that event send via monitoring socket can be parsed
* properly.
*/
if (hdr->evt == 0xe4)
hdr->evt = HCI_EV_VENDOR;
err = hci_recv_frame(hdev, skb);
if (hdr->evt == HCI_EV_VENDOR) {
if (test_and_clear_bit(BTMTKUART_TX_WAIT_VND_EVT,
&bdev->tx_state)) {
/* Barrier to sync with other CPUs */
smp_mb__after_atomic();
wake_up_bit(&bdev->tx_state, BTMTKUART_TX_WAIT_VND_EVT);
}
}
return err;
}
static const struct h4_recv_pkt mtk_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = btmtkuart_recv_event },
};
static void btmtkuart_tx_work(struct work_struct *work)
{
struct btmtkuart_dev *bdev = container_of(work, struct btmtkuart_dev,
tx_work);
struct serdev_device *serdev = bdev->serdev;
struct hci_dev *hdev = bdev->hdev;
while (1) {
clear_bit(BTMTKUART_TX_STATE_WAKEUP, &bdev->tx_state);
while (1) {
struct sk_buff *skb = skb_dequeue(&bdev->txq);
int len;
if (!skb)
break;
len = serdev_device_write_buf(serdev, skb->data,
skb->len);
hdev->stat.byte_tx += len;
skb_pull(skb, len);
if (skb->len > 0) {
skb_queue_head(&bdev->txq, skb);
break;
}
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;
}
kfree_skb(skb);
}
if (!test_bit(BTMTKUART_TX_STATE_WAKEUP, &bdev->tx_state))
break;
}
clear_bit(BTMTKUART_TX_STATE_ACTIVE, &bdev->tx_state);
}
static void btmtkuart_tx_wakeup(struct btmtkuart_dev *bdev)
{
if (test_and_set_bit(BTMTKUART_TX_STATE_ACTIVE, &bdev->tx_state))
set_bit(BTMTKUART_TX_STATE_WAKEUP, &bdev->tx_state);
schedule_work(&bdev->tx_work);
}
static const unsigned char *
mtk_stp_split(struct btmtkuart_dev *bdev, const unsigned char *data, int count,
int *sz_h4)
{
struct mtk_stp_hdr *shdr;
/* The cursor is reset when all the data of STP is consumed out */
if (!bdev->stp_dlen && bdev->stp_cursor >= 6)
bdev->stp_cursor = 0;
/* Filling pad until all STP info is obtained */
while (bdev->stp_cursor < 6 && count > 0) {
bdev->stp_pad[bdev->stp_cursor] = *data;
bdev->stp_cursor++;
data++;
count--;
}
/* Retrieve STP info and have a sanity check */
if (!bdev->stp_dlen && bdev->stp_cursor >= 6) {
shdr = (struct mtk_stp_hdr *)&bdev->stp_pad[2];
bdev->stp_dlen = be16_to_cpu(shdr->dlen) & 0x0fff;
/* Resync STP when unexpected data is being read */
if (shdr->prefix != 0x80 || bdev->stp_dlen > 2048) {
bt_dev_err(bdev->hdev, "stp format unexpect (%d, %d)",
shdr->prefix, bdev->stp_dlen);
bdev->stp_cursor = 2;
bdev->stp_dlen = 0;
}
}
/* Directly quit when there's no data found for H4 can process */
if (count <= 0)
return NULL;
/* Tranlate to how much the size of data H4 can handle so far */
*sz_h4 = min_t(int, count, bdev->stp_dlen);
/* Update the remaining size of STP packet */
bdev->stp_dlen -= *sz_h4;
/* Data points to STP payload which can be handled by H4 */
return data;
}
static int btmtkuart_recv(struct hci_dev *hdev, const u8 *data, size_t count)
{
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
const unsigned char *p_left = data, *p_h4;
int sz_left = count, sz_h4, adv;
int err;
while (sz_left > 0) {
/* The serial data received from MT7622 BT controller is
* at all time padded around with the STP header and tailer.
*
* A full STP packet is looking like
* -----------------------------------
* | STP header | H:4 | STP tailer |
* -----------------------------------
* but it doesn't guarantee to contain a full H:4 packet which
* means that it's possible for multiple STP packets forms a
* full H:4 packet that means extra STP header + length doesn't
* indicate a full H:4 frame, things can fragment. Whose length
* recorded in STP header just shows up the most length the
* H:4 engine can handle currently.
*/
p_h4 = mtk_stp_split(bdev, p_left, sz_left, &sz_h4);
if (!p_h4)
break;
adv = p_h4 - p_left;
sz_left -= adv;
p_left += adv;
bdev->rx_skb = h4_recv_buf(bdev->hdev, bdev->rx_skb, p_h4,
sz_h4, mtk_recv_pkts,
ARRAY_SIZE(mtk_recv_pkts));
if (IS_ERR(bdev->rx_skb)) {
err = PTR_ERR(bdev->rx_skb);
bt_dev_err(bdev->hdev,
"Frame reassembly failed (%d)", err);
bdev->rx_skb = NULL;
return err;
}
sz_left -= sz_h4;
p_left += sz_h4;
}
return 0;
}
static int btmtkuart_receive_buf(struct serdev_device *serdev, const u8 *data,
size_t count)
{
struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev);
int err;
err = btmtkuart_recv(bdev->hdev, data, count);
if (err < 0)
return err;
bdev->hdev->stat.byte_rx += count;
return count;
}
static void btmtkuart_write_wakeup(struct serdev_device *serdev)
{
struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev);
btmtkuart_tx_wakeup(bdev);
}
static const struct serdev_device_ops btmtkuart_client_ops = {
.receive_buf = btmtkuart_receive_buf,
.write_wakeup = btmtkuart_write_wakeup,
};
static int btmtkuart_open(struct hci_dev *hdev)
{
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
struct device *dev;
int err;
err = serdev_device_open(bdev->serdev);
if (err) {
bt_dev_err(hdev, "Unable to open UART device %s",
dev_name(&bdev->serdev->dev));
goto err_open;
}
bdev->stp_cursor = 2;
bdev->stp_dlen = 0;
dev = &bdev->serdev->dev;
/* Enable the power domain and clock the device requires */
pm_runtime_enable(dev);
err = pm_runtime_get_sync(dev);
if (err < 0) {
pm_runtime_put_noidle(dev);
goto err_disable_rpm;
}
err = clk_prepare_enable(bdev->clk);
if (err < 0)
goto err_put_rpm;
return 0;
err_put_rpm:
pm_runtime_put_sync(dev);
err_disable_rpm:
pm_runtime_disable(dev);
err_open:
return err;
}
static int btmtkuart_close(struct hci_dev *hdev)
{
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
struct device *dev = &bdev->serdev->dev;
/* Shutdown the clock and power domain the device requires */
clk_disable_unprepare(bdev->clk);
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
serdev_device_close(bdev->serdev);
return 0;
}
static int btmtkuart_flush(struct hci_dev *hdev)
{
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
/* Flush any pending characters */
serdev_device_write_flush(bdev->serdev);
skb_queue_purge(&bdev->txq);
cancel_work_sync(&bdev->tx_work);
kfree_skb(bdev->rx_skb);
bdev->rx_skb = NULL;
bdev->stp_cursor = 2;
bdev->stp_dlen = 0;
return 0;
}
static int btmtkuart_setup(struct hci_dev *hdev)
{
u8 param = 0x1;
int err = 0;
/* Setup a firmware which the device definitely requires */
err = mtk_setup_fw(hdev);
if (err < 0)
return err;
/* Activate function the firmware providing to */
err = mtk_hci_wmt_sync(hdev, MTK_WMT_RST, 0x4, 0, 0);
if (err < 0) {
bt_dev_err(hdev, "Failed to send wmt rst (%d)", err);
return err;
}
/* Enable Bluetooth protocol */
err = mtk_hci_wmt_sync(hdev, MTK_WMT_FUNC_CTRL, 0x0, sizeof(param),
&param);
if (err < 0) {
bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
return err;
}
return 0;
}
static int btmtkuart_shutdown(struct hci_dev *hdev)
{
u8 param = 0x0;
int err;
/* Disable the device */
err = mtk_hci_wmt_sync(hdev, MTK_WMT_FUNC_CTRL, 0x0, sizeof(param),
&param);
if (err < 0) {
bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
return err;
}
return 0;
}
static int btmtkuart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btmtkuart_dev *bdev = hci_get_drvdata(hdev);
struct mtk_stp_hdr *shdr;
int err, dlen, type = 0;
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
/* Make sure that there is enough rooms for STP header and trailer */
if (unlikely(skb_headroom(skb) < sizeof(*shdr)) ||
(skb_tailroom(skb) < MTK_STP_TLR_SIZE)) {
err = pskb_expand_head(skb, sizeof(*shdr), MTK_STP_TLR_SIZE,
GFP_ATOMIC);
if (err < 0)
return err;
}
/* Add the STP header */
dlen = skb->len;
shdr = skb_push(skb, sizeof(*shdr));
shdr->prefix = 0x80;
shdr->dlen = cpu_to_be16((dlen & 0x0fff) | (type << 12));
shdr->cs = 0; /* MT7622 doesn't care about checksum value */
/* Add the STP trailer */
skb_put_zero(skb, MTK_STP_TLR_SIZE);
skb_queue_tail(&bdev->txq, skb);
btmtkuart_tx_wakeup(bdev);
return 0;
}
static int btmtkuart_probe(struct serdev_device *serdev)
{
struct btmtkuart_dev *bdev;
struct hci_dev *hdev;
bdev = devm_kzalloc(&serdev->dev, sizeof(*bdev), GFP_KERNEL);
if (!bdev)
return -ENOMEM;
bdev->clk = devm_clk_get(&serdev->dev, "ref");
if (IS_ERR(bdev->clk))
return PTR_ERR(bdev->clk);
bdev->serdev = serdev;
serdev_device_set_drvdata(serdev, bdev);
serdev_device_set_client_ops(serdev, &btmtkuart_client_ops);
INIT_WORK(&bdev->tx_work, btmtkuart_tx_work);
skb_queue_head_init(&bdev->txq);
/* Initialize and register HCI device */
hdev = hci_alloc_dev();
if (!hdev) {
dev_err(&serdev->dev, "Can't allocate HCI device\n");
return -ENOMEM;
}
bdev->hdev = hdev;
hdev->bus = HCI_UART;
hci_set_drvdata(hdev, bdev);
hdev->open = btmtkuart_open;
hdev->close = btmtkuart_close;
hdev->flush = btmtkuart_flush;
hdev->setup = btmtkuart_setup;
hdev->shutdown = btmtkuart_shutdown;
hdev->send = btmtkuart_send_frame;
SET_HCIDEV_DEV(hdev, &serdev->dev);
hdev->manufacturer = 70;
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
if (hci_register_dev(hdev) < 0) {
dev_err(&serdev->dev, "Can't register HCI device\n");
hci_free_dev(hdev);
return -ENODEV;
}
return 0;
}
static void btmtkuart_remove(struct serdev_device *serdev)
{
struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev);
struct hci_dev *hdev = bdev->hdev;
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
#ifdef CONFIG_OF
static const struct of_device_id mtk_of_match_table[] = {
{ .compatible = "mediatek,mt7622-bluetooth"},
{ }
};
MODULE_DEVICE_TABLE(of, mtk_of_match_table);
#endif
static struct serdev_device_driver btmtkuart_driver = {
.probe = btmtkuart_probe,
.remove = btmtkuart_remove,
.driver = {
.name = "btmtkuart",
.of_match_table = of_match_ptr(mtk_of_match_table),
},
};
module_serdev_device_driver(btmtkuart_driver);
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
MODULE_DESCRIPTION("MediaTek Bluetooth Serial driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(FIRMWARE_MT7622);
+400
View File
@@ -0,0 +1,400 @@
/*
* 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"
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
{
struct sk_buff *skb;
struct edl_event_hdr *edl;
struct rome_version *ver;
char cmd;
int err = 0;
bt_dev_dbg(hdev, "QCA Version Request");
cmd = EDL_PATCH_VER_REQ_CMD;
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
&cmd, HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "Reading QCA version information failed (%d)",
err);
return err;
}
if (skb->len != sizeof(*edl) + sizeof(*ver)) {
bt_dev_err(hdev, "QCA Version size mismatch len %d", skb->len);
err = -EILSEQ;
goto out;
}
edl = (struct edl_event_hdr *)(skb->data);
if (!edl) {
bt_dev_err(hdev, "QCA TLV with no header");
err = -EILSEQ;
goto out;
}
if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
edl->rtype != EDL_APP_VER_RES_EVT) {
bt_dev_err(hdev, "QCA Wrong packet received %d %d", 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));
/* QCA 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.
*/
*soc_version = (le32_to_cpu(ver->soc_id) << 16) |
(le16_to_cpu(ver->rome_ver) & 0x0000ffff);
if (*soc_version == 0)
err = -EILSEQ;
out:
kfree_skb(skb);
if (err)
bt_dev_err(hdev, "QCA Failed to get version (%d)", err);
return err;
}
EXPORT_SYMBOL_GPL(qca_read_soc_version);
static int qca_send_reset(struct hci_dev *hdev)
{
struct sk_buff *skb;
int err;
bt_dev_dbg(hdev, "QCA HCI_RESET");
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "QCA Reset failed (%d)", err);
return err;
}
kfree_skb(skb);
return 0;
}
static void qca_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);
config->dnld_mode = ROME_SKIP_EVT_NONE;
switch (config->type) {
case TLV_TYPE_PATCH:
tlv_patch = (struct tlv_type_patch *)tlv->data;
/* For Rome version 1.1 to 3.1, all segment commands
* are acked by a vendor specific event (VSE).
* For Rome >= 3.2, the download mode field indicates
* if VSE is skipped by the controller.
* In case VSE is skipped, only the last segment is acked.
*/
config->dnld_mode = tlv_patch->download_mode;
BT_DBG("Total Length : %d bytes",
le32_to_cpu(tlv_patch->total_size));
BT_DBG("Patch Data Length : %d bytes",
le32_to_cpu(tlv_patch->data_length));
BT_DBG("Signing Format Version : 0x%x",
tlv_patch->format_version);
BT_DBG("Signature Algorithm : 0x%x",
tlv_patch->signature);
BT_DBG("Download mode : 0x%x",
tlv_patch->download_mode);
BT_DBG("Reserved : 0x%x",
tlv_patch->reserved1);
BT_DBG("Product ID : 0x%04x",
le16_to_cpu(tlv_patch->product_id));
BT_DBG("Rom Build Version : 0x%04x",
le16_to_cpu(tlv_patch->rom_build));
BT_DBG("Patch Version : 0x%04x",
le16_to_cpu(tlv_patch->patch_version));
BT_DBG("Reserved : 0x%x",
le16_to_cpu(tlv_patch->reserved2));
BT_DBG("Patch Entry Address : 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 qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
const u8 *data, enum rome_tlv_dnld_mode mode)
{
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;
cmd[0] = EDL_PATCH_TLV_REQ_CMD;
cmd[1] = seg_size;
memcpy(cmd + 2, data, seg_size);
if (mode == ROME_SKIP_EVT_VSE_CC || mode == ROME_SKIP_EVT_VSE)
return __hci_cmd_send(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2,
cmd);
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, cmd,
HCI_EV_VENDOR, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "QCA Failed to send TLV segment (%d)", err);
return err;
}
if (skb->len != sizeof(*edl) + sizeof(*tlv_resp)) {
bt_dev_err(hdev, "QCA TLV response size mismatch");
err = -EILSEQ;
goto out;
}
edl = (struct edl_event_hdr *)(skb->data);
if (!edl) {
bt_dev_err(hdev, "TLV with no header");
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_dev_err(hdev, "QCA TLV with error stat 0x%x rtype 0x%x (0x%x)",
edl->cresp, edl->rtype, tlv_resp->result);
err = -EIO;
}
out:
kfree_skb(skb);
return err;
}
static int qca_download_firmware(struct hci_dev *hdev,
struct rome_config *config)
{
const struct firmware *fw;
const u8 *segment;
int ret, remain, i = 0;
bt_dev_info(hdev, "QCA Downloading %s", config->fwname);
ret = request_firmware(&fw, config->fwname, &hdev->dev);
if (ret) {
bt_dev_err(hdev, "QCA Failed to request file: %s (%d)",
config->fwname, ret);
return ret;
}
qca_tlv_check_data(config, fw);
segment = fw->data;
remain = fw->size;
while (remain > 0) {
int segsize = min(MAX_SIZE_PER_TLV_SEGMENT, remain);
bt_dev_dbg(hdev, "Send segment %d, size %d", i++, segsize);
remain -= segsize;
/* The last segment is always acked regardless download mode */
if (!remain || segsize < MAX_SIZE_PER_TLV_SEGMENT)
config->dnld_mode = ROME_SKIP_EVT_NONE;
ret = qca_tlv_send_segment(hdev, segsize, segment,
config->dnld_mode);
if (ret)
break;
segment += segsize;
}
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_EV_VENDOR, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
bt_dev_err(hdev, "QCA Change address command failed (%d)", err);
return err;
}
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type, u32 soc_ver)
{
struct rome_config config;
int err;
u8 rom_ver;
bt_dev_dbg(hdev, "QCA setup on UART");
config.user_baud_rate = baudrate;
/* Download rampatch file */
config.type = TLV_TYPE_PATCH;
if (soc_type == QCA_WCN3990) {
/* Firmware files to download are based on ROM version.
* ROM version is derived from last two bytes of soc_ver.
*/
rom_ver = ((soc_ver & 0x00000f00) >> 0x04) |
(soc_ver & 0x0000000f);
snprintf(config.fwname, sizeof(config.fwname),
"qca/crbtfw%02x.tlv", rom_ver);
} else {
snprintf(config.fwname, sizeof(config.fwname),
"qca/rampatch_%08x.bin", soc_ver);
}
err = qca_download_firmware(hdev, &config);
if (err < 0) {
bt_dev_err(hdev, "QCA Failed to download patch (%d)", err);
return err;
}
/* Give the controller some time to get ready to receive the NVM */
msleep(10);
/* Download NVM configuration */
config.type = TLV_TYPE_NVM;
if (soc_type == QCA_WCN3990)
snprintf(config.fwname, sizeof(config.fwname),
"qca/crnv%02x.bin", rom_ver);
else
snprintf(config.fwname, sizeof(config.fwname),
"qca/nvm_%08x.bin", soc_ver);
err = qca_download_firmware(hdev, &config);
if (err < 0) {
bt_dev_err(hdev, "QCA Failed to download NVM (%d)", err);
return err;
}
/* Perform HCI reset */
err = qca_send_reset(hdev);
if (err < 0) {
bt_dev_err(hdev, "QCA Failed to run HCI_RESET (%d)", err);
return err;
}
bt_dev_info(hdev, "QCA setup on UART is completed");
return 0;
}
EXPORT_SYMBOL_GPL(qca_uart_setup);
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");
+162
View File
@@ -0,0 +1,162 @@
/*
* 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)
#define QCA_WCN3990_POWERON_PULSE 0xFC
#define QCA_WCN3990_POWEROFF_PULSE 0xC0
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_dnld_mode {
ROME_SKIP_EVT_NONE,
ROME_SKIP_EVT_VSE,
ROME_SKIP_EVT_CC,
ROME_SKIP_EVT_VSE_CC
};
enum rome_tlv_type {
TLV_TYPE_PATCH = 1,
TLV_TYPE_NVM
};
struct rome_config {
u8 type;
char fwname[64];
uint8_t user_baud_rate;
enum rome_tlv_dnld_mode dnld_mode;
};
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;
__u8 download_mode;
__u8 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;
enum qca_btsoc_type {
QCA_INVALID = -1,
QCA_AR3002,
QCA_ROME,
QCA_WCN3990
};
#if IS_ENABLED(CONFIG_BT_QCA)
int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type, u32 soc_ver);
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version);
#else
static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
return -EOPNOTSUPP;
}
static inline int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
enum qca_btsoc_type soc_type, u32 soc_ver)
{
return -EOPNOTSUPP;
}
static inline int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version)
{
return -EOPNOTSUPP;
}
#endif
+250
View File
@@ -0,0 +1,250 @@
/*
* 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/rpmsg.h>
#include <linux/of.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;
bdaddr_t bdaddr;
struct rpmsg_endpoint *acl_channel;
struct rpmsg_endpoint *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;
skb_put_data(skb, data, count);
return hci_recv_frame(hdev, skb);
}
static int btqcomsmd_acl_callback(struct rpmsg_device *rpdev, void *data,
int count, void *priv, u32 addr)
{
struct btqcomsmd *btq = priv;
btq->hdev->stat.byte_rx += count;
return btqcomsmd_recv(btq->hdev, HCI_ACLDATA_PKT, data, count);
}
static int btqcomsmd_cmd_callback(struct rpmsg_device *rpdev, void *data,
int count, void *priv, u32 addr)
{
struct btqcomsmd *btq = priv;
btq->hdev->stat.byte_rx += count;
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 = rpmsg_send(btq->acl_channel, skb->data, skb->len);
if (ret) {
hdev->stat.err_tx++;
break;
}
hdev->stat.acl_tx++;
hdev->stat.byte_tx += skb->len;
break;
case HCI_COMMAND_PKT:
ret = rpmsg_send(btq->cmd_channel, skb->data, skb->len);
if (ret) {
hdev->stat.err_tx++;
break;
}
hdev->stat.cmd_tx++;
hdev->stat.byte_tx += skb->len;
break;
default:
ret = -EILSEQ;
break;
}
if (!ret)
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_setup(struct hci_dev *hdev)
{
struct btqcomsmd *btq = hci_get_drvdata(hdev);
struct sk_buff *skb;
int err;
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb))
return PTR_ERR(skb);
kfree_skb(skb);
/* Devices do not have persistent storage for BD address. If no
* BD address has been retrieved during probe, mark the device
* as having an invalid BD address.
*/
if (!bacmp(&btq->bdaddr, BDADDR_ANY)) {
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
return 0;
}
/* When setting a configured BD address fails, mark the device
* as having an invalid BD address.
*/
err = qca_set_bdaddr_rome(hdev, &btq->bdaddr);
if (err) {
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
return 0;
}
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, btq);
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, btq);
if (IS_ERR(btq->cmd_channel)) {
ret = PTR_ERR(btq->cmd_channel);
goto destroy_acl_channel;
}
/* The local-bd-address property is usually injected by the
* bootloader which has access to the allocated BD address.
*/
if (!of_property_read_u8_array(pdev->dev.of_node, "local-bd-address",
(u8 *)&btq->bdaddr, sizeof(bdaddr_t))) {
dev_info(&pdev->dev, "BD address %pMR retrieved from device-tree",
&btq->bdaddr);
}
hdev = hci_alloc_dev();
if (!hdev) {
ret = -ENOMEM;
goto destroy_cmd_channel;
}
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->setup = btqcomsmd_setup;
hdev->set_bdaddr = qca_set_bdaddr_rome;
ret = hci_register_dev(hdev);
if (ret < 0)
goto hci_free_dev;
platform_set_drvdata(pdev, btq);
return 0;
hci_free_dev:
hci_free_dev(hdev);
destroy_cmd_channel:
rpmsg_destroy_ept(btq->cmd_channel);
destroy_acl_channel:
rpmsg_destroy_ept(btq->acl_channel);
return ret;
}
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);
rpmsg_destroy_ept(btq->cmd_channel);
rpmsg_destroy_ept(btq->acl_channel);
return 0;
}
static const struct of_device_id btqcomsmd_of_match[] = {
{ .compatible = "qcom,wcnss-bt", },
{ },
};
MODULE_DEVICE_TABLE(of, btqcomsmd_of_match);
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");
+198
View File
@@ -0,0 +1,198 @@
/**
* Copyright (c) 2017 Redpine Signals Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <asm/unaligned.h>
#include <net/rsi_91x.h>
#include <net/genetlink.h>
#define RSI_DMA_ALIGN 8
#define RSI_FRAME_DESC_SIZE 16
#define RSI_HEADROOM_FOR_BT_HAL (RSI_FRAME_DESC_SIZE + RSI_DMA_ALIGN)
struct rsi_hci_adapter {
void *priv;
struct rsi_proto_ops *proto_ops;
struct hci_dev *hdev;
};
static int rsi_hci_open(struct hci_dev *hdev)
{
return 0;
}
static int rsi_hci_close(struct hci_dev *hdev)
{
return 0;
}
static int rsi_hci_flush(struct hci_dev *hdev)
{
return 0;
}
static int rsi_hci_send_pkt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct rsi_hci_adapter *h_adapter = hci_get_drvdata(hdev);
struct sk_buff *new_skb = NULL;
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;
}
if (skb_headroom(skb) < RSI_HEADROOM_FOR_BT_HAL) {
/* Insufficient skb headroom - allocate a new skb */
new_skb = skb_realloc_headroom(skb, RSI_HEADROOM_FOR_BT_HAL);
if (unlikely(!new_skb))
return -ENOMEM;
bt_cb(new_skb)->pkt_type = hci_skb_pkt_type(skb);
kfree_skb(skb);
skb = new_skb;
if (!IS_ALIGNED((unsigned long)skb->data, RSI_DMA_ALIGN)) {
u8 *skb_data = skb->data;
int skb_len = skb->len;
skb_push(skb, RSI_DMA_ALIGN);
skb_pull(skb, PTR_ALIGN(skb->data,
RSI_DMA_ALIGN) - skb->data);
memmove(skb->data, skb_data, skb_len);
skb_trim(skb, skb_len);
}
}
return h_adapter->proto_ops->coex_send_pkt(h_adapter->priv, skb,
RSI_BT_Q);
}
static int rsi_hci_recv_pkt(void *priv, const u8 *pkt)
{
struct rsi_hci_adapter *h_adapter = priv;
struct hci_dev *hdev = h_adapter->hdev;
struct sk_buff *skb;
int pkt_len = get_unaligned_le16(pkt) & 0x0fff;
skb = dev_alloc_skb(pkt_len);
if (!skb)
return -ENOMEM;
memcpy(skb->data, pkt + RSI_FRAME_DESC_SIZE, pkt_len);
skb_put(skb, pkt_len);
h_adapter->hdev->stat.byte_rx += skb->len;
hci_skb_pkt_type(skb) = pkt[14];
return hci_recv_frame(hdev, skb);
}
static int rsi_hci_attach(void *priv, struct rsi_proto_ops *ops)
{
struct rsi_hci_adapter *h_adapter = NULL;
struct hci_dev *hdev;
int err = 0;
h_adapter = kzalloc(sizeof(*h_adapter), GFP_KERNEL);
if (!h_adapter)
return -ENOMEM;
h_adapter->priv = priv;
ops->set_bt_context(priv, h_adapter);
h_adapter->proto_ops = ops;
hdev = hci_alloc_dev();
if (!hdev) {
BT_ERR("Failed to alloc HCI device");
goto err;
}
h_adapter->hdev = hdev;
if (ops->get_host_intf(priv) == RSI_HOST_INTF_SDIO)
hdev->bus = HCI_SDIO;
else
hdev->bus = HCI_USB;
hci_set_drvdata(hdev, h_adapter);
hdev->dev_type = HCI_PRIMARY;
hdev->open = rsi_hci_open;
hdev->close = rsi_hci_close;
hdev->flush = rsi_hci_flush;
hdev->send = rsi_hci_send_pkt;
err = hci_register_dev(hdev);
if (err < 0) {
BT_ERR("HCI registration failed with errcode %d", err);
hci_free_dev(hdev);
goto err;
}
return 0;
err:
h_adapter->hdev = NULL;
kfree(h_adapter);
return -EINVAL;
}
static void rsi_hci_detach(void *priv)
{
struct rsi_hci_adapter *h_adapter = priv;
struct hci_dev *hdev;
if (!h_adapter)
return;
hdev = h_adapter->hdev;
if (hdev) {
hci_unregister_dev(hdev);
hci_free_dev(hdev);
h_adapter->hdev = NULL;
}
kfree(h_adapter);
}
const struct rsi_mod_ops rsi_bt_ops = {
.attach = rsi_hci_attach,
.detach = rsi_hci_detach,
.recv_pkt = rsi_hci_recv_pkt,
};
EXPORT_SYMBOL(rsi_bt_ops);
static int rsi_91x_bt_module_init(void)
{
return 0;
}
static void rsi_91x_bt_module_exit(void)
{
return;
}
module_init(rsi_91x_bt_module_init);
module_exit(rsi_91x_bt_module_exit);
MODULE_AUTHOR("Redpine Signals Inc");
MODULE_DESCRIPTION("RSI BT driver");
MODULE_SUPPORTED_DEVICE("RSI-BT");
MODULE_LICENSE("Dual BSD/GPL");
+788
View File
@@ -0,0 +1,788 @@
/*
* 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
#define RTL_CONFIG_MAGIC 0x8723ab55
#define IC_MATCH_FL_LMPSUBV (1 << 0)
#define IC_MATCH_FL_HCIREV (1 << 1)
#define IC_MATCH_FL_HCIVER (1 << 2)
#define IC_MATCH_FL_HCIBUS (1 << 3)
#define IC_INFO(lmps, hcir) \
.match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV, \
.lmp_subver = (lmps), \
.hci_rev = (hcir)
struct id_table {
__u16 match_flags;
__u16 lmp_subver;
__u16 hci_rev;
__u8 hci_ver;
__u8 hci_bus;
bool config_needed;
bool has_rom_version;
char *fw_name;
char *cfg_name;
};
struct btrtl_device_info {
const struct id_table *ic_info;
u8 rom_version;
u8 *fw_data;
int fw_len;
u8 *cfg_data;
int cfg_len;
};
static const struct id_table ic_id_table[] = {
{ IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_8723A, 0x0,
.config_needed = false,
.has_rom_version = false,
.fw_name = "rtl_bt/rtl8723a_fw.bin",
.cfg_name = NULL },
{ IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_3499, 0x0,
.config_needed = false,
.has_rom_version = false,
.fw_name = "rtl_bt/rtl8723a_fw.bin",
.cfg_name = NULL },
/* 8723BS */
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV |
IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS,
.lmp_subver = RTL_ROM_LMP_8723B,
.hci_rev = 0xb,
.hci_ver = 6,
.hci_bus = HCI_UART,
.config_needed = true,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8723bs_fw.bin",
.cfg_name = "rtl_bt/rtl8723bs_config" },
/* 8723B */
{ IC_INFO(RTL_ROM_LMP_8723B, 0xb),
.config_needed = false,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8723b_fw.bin",
.cfg_name = "rtl_bt/rtl8723b_config" },
/* 8723D */
{ IC_INFO(RTL_ROM_LMP_8723B, 0xd),
.config_needed = true,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8723d_fw.bin",
.cfg_name = "rtl_bt/rtl8723d_config" },
/* 8723DS */
{ .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV |
IC_MATCH_FL_HCIVER | IC_MATCH_FL_HCIBUS,
.lmp_subver = RTL_ROM_LMP_8723B,
.hci_rev = 0xd,
.hci_ver = 8,
.hci_bus = HCI_UART,
.config_needed = true,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8723ds_fw.bin",
.cfg_name = "rtl_bt/rtl8723ds_config" },
/* 8821A */
{ IC_INFO(RTL_ROM_LMP_8821A, 0xa),
.config_needed = false,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8821a_fw.bin",
.cfg_name = "rtl_bt/rtl8821a_config" },
/* 8821C */
{ IC_INFO(RTL_ROM_LMP_8821A, 0xc),
.config_needed = false,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8821c_fw.bin",
.cfg_name = "rtl_bt/rtl8821c_config" },
/* 8761A */
{ IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_8761A, 0x0,
.config_needed = false,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8761a_fw.bin",
.cfg_name = "rtl_bt/rtl8761a_config" },
/* 8822B */
{ IC_INFO(RTL_ROM_LMP_8822B, 0xb),
.config_needed = true,
.has_rom_version = true,
.fw_name = "rtl_bt/rtl8822b_fw.bin",
.cfg_name = "rtl_bt/rtl8822b_config" },
};
static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev,
u8 hci_ver, u8 hci_bus)
{
int i;
for (i = 0; i < ARRAY_SIZE(ic_id_table); i++) {
if ((ic_id_table[i].match_flags & IC_MATCH_FL_LMPSUBV) &&
(ic_id_table[i].lmp_subver != lmp_subver))
continue;
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIREV) &&
(ic_id_table[i].hci_rev != hci_rev))
continue;
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIVER) &&
(ic_id_table[i].hci_ver != hci_ver))
continue;
if ((ic_id_table[i].match_flags & IC_MATCH_FL_HCIBUS) &&
(ic_id_table[i].hci_bus != hci_bus))
continue;
break;
}
if (i >= ARRAY_SIZE(ic_id_table))
return NULL;
return &ic_id_table[i];
}
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)) {
rtl_dev_err(hdev, "Read ROM version failed (%ld)\n",
PTR_ERR(skb));
return PTR_ERR(skb);
}
if (skb->len != sizeof(*rom_version)) {
rtl_dev_err(hdev, "RTL version event length mismatch\n");
kfree_skb(skb);
return -EIO;
}
rom_version = (struct rtl_rom_version_evt *)skb->data;
rtl_dev_info(hdev, "rom_version status=%x version=%x\n",
rom_version->status, rom_version->version);
*version = rom_version->version;
kfree_skb(skb);
return 0;
}
static int rtlbt_parse_firmware(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev,
unsigned char **_buf)
{
const u8 extension_sig[] = { 0x51, 0x04, 0xfd, 0x77 };
struct rtl_epatch_header *epatch_info;
unsigned char *buf;
int i, len;
size_t min_size;
u8 opcode, length, data;
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 },
{ RTL_ROM_LMP_8723B, 9 }, /* 8723D */
{ RTL_ROM_LMP_8821A, 10 }, /* 8821C */
};
min_size = sizeof(struct rtl_epatch_header) + sizeof(extension_sig) + 3;
if (btrtl_dev->fw_len < min_size)
return -EINVAL;
fwptr = btrtl_dev->fw_data + btrtl_dev->fw_len - sizeof(extension_sig);
if (memcmp(fwptr, extension_sig, sizeof(extension_sig)) != 0) {
rtl_dev_err(hdev, "extension section signature mismatch\n");
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 >= btrtl_dev->fw_data + (sizeof(*epatch_info) + 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) {
rtl_dev_err(hdev, "found instruction with length 0\n");
return -EINVAL;
}
if (opcode == 0 && length == 1) {
project_id = data;
break;
}
fwptr -= length;
}
if (project_id < 0) {
rtl_dev_err(hdev, "failed to find version instruction\n");
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)) {
rtl_dev_err(hdev, "unknown project id %d\n", project_id);
return -EINVAL;
}
if (btrtl_dev->ic_info->lmp_subver !=
project_id_to_lmp_subver[i].lmp_subver) {
rtl_dev_err(hdev, "firmware is for %x but this is a %x\n",
project_id_to_lmp_subver[i].lmp_subver,
btrtl_dev->ic_info->lmp_subver);
return -EINVAL;
}
epatch_info = (struct rtl_epatch_header *)btrtl_dev->fw_data;
if (memcmp(epatch_info->signature, RTL_EPATCH_SIGNATURE, 8) != 0) {
rtl_dev_err(hdev, "bad EPATCH signature\n");
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 (btrtl_dev->fw_len < min_size)
return -EINVAL;
chip_id_base = btrtl_dev->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 == btrtl_dev->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) {
rtl_dev_err(hdev, "didn't find patch for chip id %d",
btrtl_dev->rom_version);
return -EINVAL;
}
BT_DBG("length=%x offset=%x index %d", patch_length, patch_offset, i);
min_size = patch_offset + patch_length;
if (btrtl_dev->fw_len < min_size)
return -EINVAL;
/* Copy the firmware into a new buffer and write the version at
* the end.
*/
len = patch_length;
buf = kvmalloc(patch_length, GFP_KERNEL);
if (!buf)
return -ENOMEM;
memcpy(buf, btrtl_dev->fw_data + patch_offset, patch_length - 4);
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)) {
rtl_dev_err(hdev, "download fw command failed (%ld)\n",
PTR_ERR(skb));
ret = -PTR_ERR(skb);
goto out;
}
if (skb->len != sizeof(struct rtl_download_response)) {
rtl_dev_err(hdev, "download fw event length mismatch\n");
kfree_skb(skb);
ret = -EIO;
goto out;
}
kfree_skb(skb);
data += RTL_FRAG_LEN;
}
out:
kfree(dl_cmd);
return ret;
}
static int rtl_load_file(struct hci_dev *hdev, const char *name, u8 **buff)
{
const struct firmware *fw;
int ret;
rtl_dev_info(hdev, "rtl: loading %s\n", name);
ret = request_firmware(&fw, name, &hdev->dev);
if (ret < 0)
return ret;
ret = fw->size;
*buff = kvmalloc(fw->size, GFP_KERNEL);
if (*buff)
memcpy(*buff, fw->data, ret);
else
ret = -ENOMEM;
release_firmware(fw);
return ret;
}
static int btrtl_setup_rtl8723a(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev)
{
if (btrtl_dev->fw_len < 8)
return -EINVAL;
/* Check that the firmware doesn't have the epatch signature
* (which is only for RTL8723B and newer).
*/
if (!memcmp(btrtl_dev->fw_data, RTL_EPATCH_SIGNATURE, 8)) {
rtl_dev_err(hdev, "unexpected EPATCH signature!\n");
return -EINVAL;
}
return rtl_download_firmware(hdev, btrtl_dev->fw_data,
btrtl_dev->fw_len);
}
static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev)
{
unsigned char *fw_data = NULL;
int ret;
u8 *tbuff;
ret = rtlbt_parse_firmware(hdev, btrtl_dev, &fw_data);
if (ret < 0)
goto out;
if (btrtl_dev->cfg_len > 0) {
tbuff = kvzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL);
if (!tbuff) {
ret = -ENOMEM;
goto out;
}
memcpy(tbuff, fw_data, ret);
kvfree(fw_data);
memcpy(tbuff + ret, btrtl_dev->cfg_data, btrtl_dev->cfg_len);
ret += btrtl_dev->cfg_len;
fw_data = tbuff;
}
rtl_dev_info(hdev, "cfg_sz %d, total sz %d\n", btrtl_dev->cfg_len, ret);
ret = rtl_download_firmware(hdev, fw_data, ret);
out:
kvfree(fw_data);
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)) {
rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION failed (%ld)\n",
PTR_ERR(skb));
return skb;
}
if (skb->len != sizeof(struct hci_rp_read_local_version)) {
rtl_dev_err(hdev, "HCI_OP_READ_LOCAL_VERSION event length mismatch\n");
kfree_skb(skb);
return ERR_PTR(-EIO);
}
return skb;
}
void btrtl_free(struct btrtl_device_info *btrtl_dev)
{
kvfree(btrtl_dev->fw_data);
kvfree(btrtl_dev->cfg_data);
kfree(btrtl_dev);
}
EXPORT_SYMBOL_GPL(btrtl_free);
struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
const char *postfix)
{
struct btrtl_device_info *btrtl_dev;
struct sk_buff *skb;
struct hci_rp_read_local_version *resp;
char cfg_name[40];
u16 hci_rev, lmp_subver;
u8 hci_ver;
int ret;
btrtl_dev = kzalloc(sizeof(*btrtl_dev), GFP_KERNEL);
if (!btrtl_dev) {
ret = -ENOMEM;
goto err_alloc;
}
skb = btrtl_read_local_version(hdev);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
goto err_free;
}
resp = (struct hci_rp_read_local_version *)skb->data;
rtl_dev_info(hdev, "rtl: examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x\n",
resp->hci_ver, resp->hci_rev,
resp->lmp_ver, resp->lmp_subver);
hci_ver = resp->hci_ver;
hci_rev = le16_to_cpu(resp->hci_rev);
lmp_subver = le16_to_cpu(resp->lmp_subver);
kfree_skb(skb);
btrtl_dev->ic_info = btrtl_match_ic(lmp_subver, hci_rev, hci_ver,
hdev->bus);
if (!btrtl_dev->ic_info) {
rtl_dev_info(hdev, "rtl: unknown IC info, lmp subver %04x, hci rev %04x, hci ver %04x",
lmp_subver, hci_rev, hci_ver);
return btrtl_dev;
}
if (btrtl_dev->ic_info->has_rom_version) {
ret = rtl_read_rom_version(hdev, &btrtl_dev->rom_version);
if (ret)
goto err_free;
}
btrtl_dev->fw_len = rtl_load_file(hdev, btrtl_dev->ic_info->fw_name,
&btrtl_dev->fw_data);
if (btrtl_dev->fw_len < 0) {
rtl_dev_err(hdev, "firmware file %s not found\n",
btrtl_dev->ic_info->fw_name);
ret = btrtl_dev->fw_len;
goto err_free;
}
if (btrtl_dev->ic_info->cfg_name) {
if (postfix) {
snprintf(cfg_name, sizeof(cfg_name), "%s-%s.bin",
btrtl_dev->ic_info->cfg_name, postfix);
} else {
snprintf(cfg_name, sizeof(cfg_name), "%s.bin",
btrtl_dev->ic_info->cfg_name);
}
btrtl_dev->cfg_len = rtl_load_file(hdev, cfg_name,
&btrtl_dev->cfg_data);
if (btrtl_dev->ic_info->config_needed &&
btrtl_dev->cfg_len <= 0) {
rtl_dev_err(hdev, "mandatory config file %s not found\n",
btrtl_dev->ic_info->cfg_name);
ret = btrtl_dev->cfg_len;
goto err_free;
}
}
return btrtl_dev;
err_free:
btrtl_free(btrtl_dev);
err_alloc:
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(btrtl_initialize);
int btrtl_download_firmware(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev)
{
/* 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.
*/
if (!btrtl_dev->ic_info) {
rtl_dev_info(hdev, "rtl: assuming no firmware upload needed\n");
return 0;
}
switch (btrtl_dev->ic_info->lmp_subver) {
case RTL_ROM_LMP_8723A:
case RTL_ROM_LMP_3499:
return btrtl_setup_rtl8723a(hdev, btrtl_dev);
case RTL_ROM_LMP_8723B:
case RTL_ROM_LMP_8821A:
case RTL_ROM_LMP_8761A:
case RTL_ROM_LMP_8822B:
return btrtl_setup_rtl8723b(hdev, btrtl_dev);
default:
rtl_dev_info(hdev, "rtl: assuming no firmware upload needed\n");
return 0;
}
}
EXPORT_SYMBOL_GPL(btrtl_download_firmware);
int btrtl_setup_realtek(struct hci_dev *hdev)
{
struct btrtl_device_info *btrtl_dev;
int ret;
btrtl_dev = btrtl_initialize(hdev, NULL);
if (IS_ERR(btrtl_dev))
return PTR_ERR(btrtl_dev);
ret = btrtl_download_firmware(hdev, btrtl_dev);
btrtl_free(btrtl_dev);
return ret;
}
EXPORT_SYMBOL_GPL(btrtl_setup_realtek);
int btrtl_shutdown_realtek(struct hci_dev *hdev)
{
struct sk_buff *skb;
int ret;
/* According to the vendor driver, BT must be reset on close to avoid
* firmware crash.
*/
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
ret = PTR_ERR(skb);
bt_dev_err(hdev, "HCI reset during shutdown failed");
return ret;
}
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(btrtl_shutdown_realtek);
static unsigned int btrtl_convert_baudrate(u32 device_baudrate)
{
switch (device_baudrate) {
case 0x0252a00a:
return 230400;
case 0x05f75004:
return 921600;
case 0x00005004:
return 1000000;
case 0x04928002:
case 0x01128002:
return 1500000;
case 0x00005002:
return 2000000;
case 0x0000b001:
return 2500000;
case 0x04928001:
return 3000000;
case 0x052a6001:
return 3500000;
case 0x00005001:
return 4000000;
case 0x0252c014:
default:
return 115200;
}
}
int btrtl_get_uart_settings(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev,
unsigned int *controller_baudrate,
u32 *device_baudrate, bool *flow_control)
{
struct rtl_vendor_config *config;
struct rtl_vendor_config_entry *entry;
int i, total_data_len;
bool found = false;
total_data_len = btrtl_dev->cfg_len - sizeof(*config);
if (total_data_len <= 0) {
rtl_dev_warn(hdev, "no config loaded\n");
return -EINVAL;
}
config = (struct rtl_vendor_config *)btrtl_dev->cfg_data;
if (le32_to_cpu(config->signature) != RTL_CONFIG_MAGIC) {
rtl_dev_err(hdev, "invalid config magic\n");
return -EINVAL;
}
if (total_data_len < le16_to_cpu(config->total_len)) {
rtl_dev_err(hdev, "config is too short\n");
return -EINVAL;
}
for (i = 0; i < total_data_len; ) {
entry = ((void *)config->entry) + i;
switch (le16_to_cpu(entry->offset)) {
case 0xc:
if (entry->len < sizeof(*device_baudrate)) {
rtl_dev_err(hdev, "invalid UART config entry\n");
return -EINVAL;
}
*device_baudrate = get_unaligned_le32(entry->data);
*controller_baudrate = btrtl_convert_baudrate(
*device_baudrate);
if (entry->len >= 13)
*flow_control = !!(entry->data[12] & BIT(2));
else
*flow_control = false;
found = true;
break;
default:
rtl_dev_dbg(hdev, "skipping config entry 0x%x (len %u)\n",
le16_to_cpu(entry->offset), entry->len);
break;
};
i += sizeof(*entry) + entry->len;
}
if (!found) {
rtl_dev_err(hdev, "no UART config entry found\n");
return -ENOENT;
}
rtl_dev_dbg(hdev, "device baudrate = 0x%08x\n", *device_baudrate);
rtl_dev_dbg(hdev, "controller baudrate = %u\n", *controller_baudrate);
rtl_dev_dbg(hdev, "flow control %d\n", *flow_control);
return 0;
}
EXPORT_SYMBOL_GPL(btrtl_get_uart_settings);
MODULE_AUTHOR("Daniel Drake <drake@endlessm.com>");
MODULE_DESCRIPTION("Bluetooth support for Realtek devices ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("rtl_bt/rtl8723a_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8723b_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8723b_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8723bs_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8723bs_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8723ds_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8723ds_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8761a_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8761a_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8821a_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8821a_config.bin");
MODULE_FIRMWARE("rtl_bt/rtl8822b_fw.bin");
MODULE_FIRMWARE("rtl_bt/rtl8822b_config.bin");
+111
View File
@@ -0,0 +1,111 @@
/*
* 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
#define rtl_dev_err(dev, fmt, ...) bt_dev_err(dev, "RTL: " fmt, ##__VA_ARGS__)
#define rtl_dev_warn(dev, fmt, ...) bt_dev_warn(dev, "RTL: " fmt, ##__VA_ARGS__)
#define rtl_dev_info(dev, fmt, ...) bt_dev_info(dev, "RTL: " fmt, ##__VA_ARGS__)
#define rtl_dev_dbg(dev, fmt, ...) bt_dev_dbg(dev, "RTL: " fmt, ##__VA_ARGS__)
struct btrtl_device_info;
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;
struct rtl_vendor_config_entry {
__le16 offset;
__u8 len;
__u8 data[0];
} __packed;
struct rtl_vendor_config {
__le32 signature;
__le16 total_len;
struct rtl_vendor_config_entry entry[0];
} __packed;
#if IS_ENABLED(CONFIG_BT_RTL)
struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
const char *postfix);
void btrtl_free(struct btrtl_device_info *btrtl_dev);
int btrtl_download_firmware(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev);
int btrtl_setup_realtek(struct hci_dev *hdev);
int btrtl_shutdown_realtek(struct hci_dev *hdev);
int btrtl_get_uart_settings(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev,
unsigned int *controller_baudrate,
u32 *device_baudrate, bool *flow_control);
#else
static inline struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev,
const char *postfix)
{
return ERR_PTR(-EOPNOTSUPP);
}
static inline void btrtl_free(struct btrtl_device_info *btrtl_dev)
{
}
static inline int btrtl_download_firmware(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev)
{
return -EOPNOTSUPP;
}
static inline int btrtl_setup_realtek(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
}
static inline int btrtl_shutdown_realtek(struct hci_dev *hdev)
{
return -EOPNOTSUPP;
}
static inline int btrtl_get_uart_settings(struct hci_dev *hdev,
struct btrtl_device_info *btrtl_dev,
unsigned int *controller_baudrate,
u32 *device_baudrate,
bool *flow_control)
{
return -ENOENT;
}
#endif
+393
View File
@@ -0,0 +1,393 @@
/*
*
* 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/host.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;
}
/* BCM43341 devices soldered onto the PCB (non-removable) use an
* uart connection for bluetooth, ignore the BT SDIO interface.
*/
if (func->vendor == SDIO_VENDOR_ID_BROADCOM &&
func->device == SDIO_DEVICE_ID_BROADCOM_43341 &&
!mmc_card_is_removable(func->card->host))
return -ENODEV;
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");
File diff suppressed because it is too large. Load diff
+350
View File
@@ -0,0 +1,350 @@
/*
* 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) {
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)
{
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
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_u8(info->rx_skb, 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_u8(s, 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);
+164
View File
@@ -0,0 +1,164 @@
/*
*
* Generic Bluetooth HCI UART driver
*
* Copyright (C) 2015-2018 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 <asm/unaligned.h>
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
static inline 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)
{
/* Check for error from previous call */
if (IS_ERR(skb))
skb = NULL;
while (count) {
int i, len;
if (!count)
break;
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);
skb_put_data(skb, 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;
}
+337
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);
}
+282
View File
@@ -0,0 +1,282 @@
/*
* 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;
};
#define OP_WRITE_TAG 0x01
#define INDEX_BDADDR 0x01
struct ath_vendor_cmd {
__u8 opcode;
__le16 index;
__u8 len;
__u8 data[251];
} __packed;
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);
msleep(20);
/* Set RTS, wake up board */
tty->driver->ops->tiocmget(tty);
tty->driver->ops->tiocmset(tty, TIOCM_RTS, 0x00);
msleep(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);
if (!hci_uart_has_flow_control(hu))
return -EOPNOTSUPP;
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_vendor_cmd(struct hci_dev *hdev, uint8_t opcode, uint16_t index,
const void *data, size_t dlen)
{
struct sk_buff *skb;
struct ath_vendor_cmd cmd;
if (dlen > sizeof(cmd.data))
return -EINVAL;
cmd.opcode = opcode;
cmd.index = cpu_to_le16(index);
cmd.len = dlen;
memcpy(cmd.data, data, dlen);
skb = __hci_cmd_sync(hdev, 0xfc0b, dlen + 4, &cmd, HCI_INIT_TIMEOUT);
if (IS_ERR(skb))
return PTR_ERR(skb);
kfree_skb(skb);
return 0;
}
static int ath_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
return ath_vendor_cmd(hdev, OP_WRITE_TAG, INDEX_BDADDR, bdaddr,
sizeof(*bdaddr));
}
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_dev_err(hu->hdev, "Frame reassembly failed (%d)", 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);
}
File diff suppressed because it is too large. Load diff
+799
View File
@@ -0,0 +1,799 @@
/*
*
* 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 = true;
static bool hciextn = true;
#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;
struct hci_uart *hu;
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;
skb_put_data(skb, &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:
skb_put_data(skb, &esc_c0, 2);
break;
case 0xdb:
skb_put_data(skb, &esc_db, 2);
break;
default:
skb_put_data(skb, &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;
skb_put_data(nskb, 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:
skb_put_data(bcsp->rx_skb, &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:
skb_put_data(bcsp->rx_skb, &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:
skb_put_data(bcsp->rx_skb, &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_skb = NULL;
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_skb = NULL;
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_skb = NULL;
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(struct timer_list *t)
{
struct bcsp_struct *bcsp = from_timer(bcsp, t, tbcsp);
struct hci_uart *hu = bcsp->hu;
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;
bcsp->hu = hu;
skb_queue_head_init(&bcsp->unack);
skb_queue_head_init(&bcsp->rel);
skb_queue_head_init(&bcsp->unrel);
timer_setup(&bcsp->tbcsp, bcsp_timed_event, 0);
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);
if (bcsp->rx_skb) {
kfree_skb(bcsp->rx_skb);
bcsp->rx_skb = NULL;
}
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");
+290
View File
@@ -0,0 +1,290 @@
/*
*
* 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_dev_err(hu->hdev, "Frame reassembly failed (%d)", 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)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
u8 alignment = hu->alignment ? hu->alignment : 1;
/* Check for error from previous call */
if (IS_ERR(skb))
skb = NULL;
while (count) {
int i, len;
/* remove padding bytes from buffer */
for (; hu->padding && count > 0; hu->padding--) {
count--;
buffer++;
}
if (!count)
break;
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);
skb_put_data(skb, 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) {
hu->padding = (skb->len - 1) % alignment;
hu->padding = (alignment - hu->padding) % alignment;
/* No more data, complete frame */
(&pkts[i])->recv(hdev, skb);
skb = NULL;
}
} else {
hu->padding = (skb->len - 1) % alignment;
hu->padding = (alignment - hu->padding) % alignment;
/* Complete frame */
(&pkts[i])->recv(hdev, skb);
skb = NULL;
}
}
return skb;
}
EXPORT_SYMBOL_GPL(h4_recv_buf);
+965
View File
@@ -0,0 +1,965 @@
/*
*
* 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/acpi.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/serdev.h>
#include <linux/skbuff.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "btrtl.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 {
/* Must be the first member, hci_serdev.c expects this. */
struct hci_uart serdev_hu;
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 */
struct hci_uart *hu; /* Parent HCI UART */
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;
const struct h5_vnd *vnd;
const char *id;
struct gpio_desc *enable_gpio;
struct gpio_desc *device_wake_gpio;
};
struct h5_vnd {
int (*setup)(struct h5 *h5);
void (*open)(struct h5 *h5);
void (*close)(struct h5 *h5);
const struct acpi_gpio_mapping *acpi_gpio_map;
};
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;
skb_put_data(nskb, 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(struct timer_list *t)
{
const unsigned char sync_req[] = { 0x01, 0x7e };
unsigned char conf_req[3] = { 0x03, 0xfc };
struct h5 *h5 = from_timer(h5, t, timer);
struct hci_uart *hu = h5->hu;
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);
if (hu->serdev) {
h5 = serdev_device_get_drvdata(hu->serdev);
} else {
h5 = kzalloc(sizeof(*h5), GFP_KERNEL);
if (!h5)
return -ENOMEM;
}
hu->priv = h5;
h5->hu = hu;
skb_queue_head_init(&h5->unack);
skb_queue_head_init(&h5->rel);
skb_queue_head_init(&h5->unrel);
h5_reset_rx(h5);
timer_setup(&h5->timer, h5_timed_event, 0);
h5->tx_win = H5_TX_WIN_MAX;
if (h5->vnd && h5->vnd->open)
h5->vnd->open(h5);
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_skb(h5->rx_skb);
h5->rx_skb = NULL;
if (h5->vnd && h5->vnd->close)
h5->vnd->close(h5);
if (!hu->serdev)
kfree(h5);
return 0;
}
static int h5_setup(struct hci_uart *hu)
{
struct h5 *h5 = hu->priv;
if (h5->vnd && h5->vnd->setup)
return h5->vnd->setup(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;
}
}
skb_put_data(h5->rx_skb, 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;
skb_put_data(skb, &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:
skb_put_data(skb, &esc_delim, 2);
break;
case SLIP_ESC:
skb_put_data(skb, &esc_esc, 2);
break;
default:
skb_put_data(skb, &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,
.setup = h5_setup,
.recv = h5_recv,
.enqueue = h5_enqueue,
.dequeue = h5_dequeue,
.flush = h5_flush,
};
static int h5_serdev_probe(struct serdev_device *serdev)
{
const struct acpi_device_id *match;
struct device *dev = &serdev->dev;
struct h5 *h5;
h5 = devm_kzalloc(dev, sizeof(*h5), GFP_KERNEL);
if (!h5)
return -ENOMEM;
set_bit(HCI_UART_RESET_ON_INIT, &h5->serdev_hu.hdev_flags);
h5->hu = &h5->serdev_hu;
h5->serdev_hu.serdev = serdev;
serdev_device_set_drvdata(serdev, h5);
if (has_acpi_companion(dev)) {
match = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!match)
return -ENODEV;
h5->vnd = (const struct h5_vnd *)match->driver_data;
h5->id = (char *)match->id;
if (h5->vnd->acpi_gpio_map)
devm_acpi_dev_add_driver_gpios(dev,
h5->vnd->acpi_gpio_map);
}
h5->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(h5->enable_gpio))
return PTR_ERR(h5->enable_gpio);
h5->device_wake_gpio = devm_gpiod_get_optional(dev, "device-wake",
GPIOD_OUT_LOW);
if (IS_ERR(h5->device_wake_gpio))
return PTR_ERR(h5->device_wake_gpio);
return hci_uart_register_device(&h5->serdev_hu, &h5p);
}
static void h5_serdev_remove(struct serdev_device *serdev)
{
struct h5 *h5 = serdev_device_get_drvdata(serdev);
hci_uart_unregister_device(&h5->serdev_hu);
}
#ifdef CONFIG_BT_HCIUART_RTL
static int h5_btrtl_setup(struct h5 *h5)
{
struct btrtl_device_info *btrtl_dev;
struct sk_buff *skb;
__le32 baudrate_data;
u32 device_baudrate;
unsigned int controller_baudrate;
bool flow_control;
int err;
btrtl_dev = btrtl_initialize(h5->hu->hdev, h5->id);
if (IS_ERR(btrtl_dev))
return PTR_ERR(btrtl_dev);
err = btrtl_get_uart_settings(h5->hu->hdev, btrtl_dev,
&controller_baudrate, &device_baudrate,
&flow_control);
if (err)
goto out_free;
baudrate_data = cpu_to_le32(device_baudrate);
skb = __hci_cmd_sync(h5->hu->hdev, 0xfc17, sizeof(baudrate_data),
&baudrate_data, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
rtl_dev_err(h5->hu->hdev, "set baud rate command failed\n");
err = PTR_ERR(skb);
goto out_free;
} else {
kfree_skb(skb);
}
/* Give the device some time to set up the new baudrate. */
usleep_range(10000, 20000);
serdev_device_set_baudrate(h5->hu->serdev, controller_baudrate);
serdev_device_set_flow_control(h5->hu->serdev, flow_control);
err = btrtl_download_firmware(h5->hu->hdev, btrtl_dev);
/* Give the device some time before the hci-core sends it a reset */
usleep_range(10000, 20000);
/* Enable controller to do both LE scan and BR/EDR inquiry
* simultaneously.
*/
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &h5->hu->hdev->quirks);
out_free:
btrtl_free(btrtl_dev);
return err;
}
static void h5_btrtl_open(struct h5 *h5)
{
/* Devices always start with these fixed parameters */
serdev_device_set_flow_control(h5->hu->serdev, false);
serdev_device_set_parity(h5->hu->serdev, SERDEV_PARITY_EVEN);
serdev_device_set_baudrate(h5->hu->serdev, 115200);
/* The controller needs up to 500ms to wakeup */
gpiod_set_value_cansleep(h5->enable_gpio, 1);
gpiod_set_value_cansleep(h5->device_wake_gpio, 1);
msleep(500);
}
static void h5_btrtl_close(struct h5 *h5)
{
gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
gpiod_set_value_cansleep(h5->enable_gpio, 0);
}
static const struct acpi_gpio_params btrtl_device_wake_gpios = { 0, 0, false };
static const struct acpi_gpio_params btrtl_enable_gpios = { 1, 0, false };
static const struct acpi_gpio_params btrtl_host_wake_gpios = { 2, 0, false };
static const struct acpi_gpio_mapping acpi_btrtl_gpios[] = {
{ "device-wake-gpios", &btrtl_device_wake_gpios, 1 },
{ "enable-gpios", &btrtl_enable_gpios, 1 },
{ "host-wake-gpios", &btrtl_host_wake_gpios, 1 },
{},
};
static struct h5_vnd rtl_vnd = {
.setup = h5_btrtl_setup,
.open = h5_btrtl_open,
.close = h5_btrtl_close,
.acpi_gpio_map = acpi_btrtl_gpios,
};
#endif
#ifdef CONFIG_ACPI
static const struct acpi_device_id h5_acpi_match[] = {
#ifdef CONFIG_BT_HCIUART_RTL
{ "OBDA8723", (kernel_ulong_t)&rtl_vnd },
#endif
{ },
};
MODULE_DEVICE_TABLE(acpi, h5_acpi_match);
#endif
static struct serdev_device_driver h5_serdev_driver = {
.probe = h5_serdev_probe,
.remove = h5_serdev_remove,
.driver = {
.name = "hci_uart_h5",
.acpi_match_table = ACPI_PTR(h5_acpi_match),
},
};
int __init h5_init(void)
{
serdev_device_driver_register(&h5_serdev_driver);
return hci_uart_register_proto(&h5p);
}
int __exit h5_deinit(void)
{
serdev_device_driver_unregister(&h5_serdev_driver);
return hci_uart_unregister_proto(&h5p);
}
File diff suppressed because it is too large. Load diff
+939
View File
@@ -0,0 +1,939 @@
/*
*
* 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 <linux/serdev.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) {
percpu_down_read(&hu->proto_lock);
if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
skb = hu->proto->dequeue(hu);
percpu_up_read(&hu->proto_lock);
} else {
hu->tx_skb = NULL;
}
return skb;
}
int hci_uart_tx_wakeup(struct hci_uart *hu)
{
/* This may be called in an IRQ context, so we can't sleep. Therefore
* we try to acquire the lock only, and if that fails we assume the
* tty is being closed because that is the only time the write lock is
* acquired. If, however, at some point in the future the write lock
* is also acquired in other situations, then this must be revisited.
*/
if (!percpu_down_read_trylock(&hu->proto_lock))
return 0;
if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
goto no_schedule;
if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) {
set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
goto no_schedule;
}
BT_DBG("");
schedule_work(&hu->write_work);
no_schedule:
percpu_up_read(&hu->proto_lock);
return 0;
}
EXPORT_SYMBOL_GPL(hci_uart_tx_wakeup);
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);
}
void hci_uart_init_work(struct work_struct *work)
{
struct hci_uart *hu = container_of(work, struct hci_uart, init_ready);
int err;
struct hci_dev *hdev;
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");
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
hu->proto->close(hu);
hdev = hu->hdev;
hu->hdev = NULL;
hci_free_dev(hdev);
return;
}
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 ------ */
/* 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);
percpu_down_read(&hu->proto_lock);
if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
hu->proto->flush(hu);
percpu_up_read(&hu->proto_lock);
return 0;
}
/* Initialize device */
static int hci_uart_open(struct hci_dev *hdev)
{
BT_DBG("%s %p", hdev->name, hdev);
/* Undo clearing this from hci_uart_close() */
hdev->flush = hci_uart_flush;
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);
percpu_down_read(&hu->proto_lock);
if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
percpu_up_read(&hu->proto_lock);
return -EUNATCH;
}
hu->proto->enqueue(hu, skb);
percpu_up_read(&hu->proto_lock);
hci_uart_tx_wakeup(hu);
return 0;
}
/* Check the underlying device or tty has flow control support */
bool hci_uart_has_flow_control(struct hci_uart *hu)
{
/* serdev nodes check if the needed operations are present */
if (hu->serdev)
return true;
if (hu->tty->driver->ops->tiocmget && hu->tty->driver->ops->tiocmset)
return true;
return false;
}
/* 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 (hu->serdev) {
serdev_device_set_flow_control(hu->serdev, !enable);
serdev_device_set_rts(hu->serdev, !enable);
return;
}
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_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
default:
break;
}
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;
/* disable alignment support by default */
hu->alignment = 1;
hu->padding = 0;
INIT_WORK(&hu->init_ready, hci_uart_init_work);
INIT_WORK(&hu->write_work, hci_uart_write_work);
percpu_init_rwsem(&hu->proto_lock);
/* 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);
if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
percpu_down_write(&hu->proto_lock);
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
percpu_up_write(&hu->proto_lock);
cancel_work_sync(&hu->init_ready);
cancel_work_sync(&hu->write_work);
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);
percpu_free_rwsem(&hu->proto_lock);
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;
percpu_down_read(&hu->proto_lock);
if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
percpu_up_read(&hu->proto_lock);
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);
percpu_up_read(&hu->proto_lock);
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;
int err;
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;
/* Only call open() for the protocol after hdev is fully initialized as
* open() (or a timer/workqueue it starts) may attempt to reference it.
*/
err = hu->proto->open(hu);
if (err) {
hu->hdev = NULL;
hci_free_dev(hdev);
return err;
}
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");
hu->proto->close(hu);
hu->hdev = NULL;
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;
hu->proto = p;
err = hci_uart_register_dev(hu);
if (err) {
return err;
}
set_bit(HCI_UART_PROTO_READY, &hu->flags);
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 __poll_t 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);
+812
View File
@@ -0,0 +1,812 @@
/*
* 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/firmware.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/signal.h>
#include <linux/ioctl.h>
#include <linux/of.h>
#include <linux/serdev.h>
#include <linux/skbuff.h>
#include <linux/ti_wilink_st.h>
#include <linux/clk.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <linux/gpio/consumer.h>
#include <linux/nvmem-consumer.h>
#include "hci_uart.h"
/* Vendor-specific HCI commands */
#define HCI_VS_WRITE_BD_ADDR 0xfc06
#define HCI_VS_UPDATE_UART_HCI_BAUDRATE 0xff36
/* 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 states */
enum hcill_states_e {
HCILL_ASLEEP,
HCILL_ASLEEP_TO_AWAKE,
HCILL_AWAKE,
HCILL_AWAKE_TO_ASLEEP
};
struct ll_device {
struct hci_uart hu;
struct serdev_device *serdev;
struct gpio_desc *enable_gpio;
struct clk *ext_clk;
bdaddr_t bdaddr;
};
struct ll_struct {
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;
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 */
skb_put_u8(skb, 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;
if (hu->serdev) {
struct ll_device *lldev = serdev_device_get_drvdata(hu->serdev);
if (!IS_ERR(lldev->ext_clk))
clk_prepare_enable(lldev->ext_clk);
}
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);
if (hu->serdev) {
struct ll_device *lldev = serdev_device_get_drvdata(hu->serdev);
gpiod_set_value_cansleep(lldev->enable_gpio, 0);
clk_disable_unprepare(lldev->ext_clk);
}
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");
/* fall through */
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 int ll_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct ll_struct *ll = hu->priv;
switch (hci_skb_pkt_type(skb)) {
case HCILL_GO_TO_SLEEP_IND:
BT_DBG("HCILL_GO_TO_SLEEP_IND packet");
ll_device_want_to_sleep(hu);
break;
case HCILL_GO_TO_SLEEP_ACK:
/* shouldn't happen */
bt_dev_err(hdev, "received HCILL_GO_TO_SLEEP_ACK in state %ld",
ll->hcill_state);
break;
case HCILL_WAKE_UP_IND:
BT_DBG("HCILL_WAKE_UP_IND packet");
ll_device_want_to_wakeup(hu);
break;
case HCILL_WAKE_UP_ACK:
BT_DBG("HCILL_WAKE_UP_ACK packet");
ll_device_woke_up(hu);
break;
}
kfree_skb(skb);
return 0;
}
#define LL_RECV_SLEEP_IND \
.type = HCILL_GO_TO_SLEEP_IND, \
.hlen = 0, \
.loff = 0, \
.lsize = 0, \
.maxlen = 0
#define LL_RECV_SLEEP_ACK \
.type = HCILL_GO_TO_SLEEP_ACK, \
.hlen = 0, \
.loff = 0, \
.lsize = 0, \
.maxlen = 0
#define LL_RECV_WAKE_IND \
.type = HCILL_WAKE_UP_IND, \
.hlen = 0, \
.loff = 0, \
.lsize = 0, \
.maxlen = 0
#define LL_RECV_WAKE_ACK \
.type = HCILL_WAKE_UP_ACK, \
.hlen = 0, \
.loff = 0, \
.lsize = 0, \
.maxlen = 0
static const struct h4_recv_pkt ll_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
{ LL_RECV_SLEEP_IND, .recv = ll_recv_frame },
{ LL_RECV_SLEEP_ACK, .recv = ll_recv_frame },
{ LL_RECV_WAKE_IND, .recv = ll_recv_frame },
{ LL_RECV_WAKE_ACK, .recv = ll_recv_frame },
};
/* Recv data */
static int ll_recv(struct hci_uart *hu, const void *data, int count)
{
struct ll_struct *ll = hu->priv;
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
ll->rx_skb = h4_recv_buf(hu->hdev, ll->rx_skb, data, count,
ll_recv_pkts, ARRAY_SIZE(ll_recv_pkts));
if (IS_ERR(ll->rx_skb)) {
int err = PTR_ERR(ll->rx_skb);
bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
ll->rx_skb = NULL;
return err;
}
return count;
}
static struct sk_buff *ll_dequeue(struct hci_uart *hu)
{
struct ll_struct *ll = hu->priv;
return skb_dequeue(&ll->txq);
}
#if IS_ENABLED(CONFIG_SERIAL_DEV_BUS)
static int read_local_version(struct hci_dev *hdev)
{
int err = 0;
unsigned short version = 0;
struct sk_buff *skb;
struct hci_rp_read_local_version *ver;
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(hdev, "Reading TI version information failed (%ld)",
PTR_ERR(skb));
return PTR_ERR(skb);
}
if (skb->len != sizeof(*ver)) {
err = -EILSEQ;
goto out;
}
ver = (struct hci_rp_read_local_version *)skb->data;
if (le16_to_cpu(ver->manufacturer) != 13) {
err = -ENODEV;
goto out;
}
version = le16_to_cpu(ver->lmp_subver);
out:
if (err) bt_dev_err(hdev, "Failed to read TI version info: %d", err);
kfree_skb(skb);
return err ? err : version;
}
/**
* download_firmware -
* internal function which parses through the .bts firmware
* script file intreprets SEND, DELAY actions only as of now
*/
static int download_firmware(struct ll_device *lldev)
{
unsigned short chip, min_ver, maj_ver;
int version, err, len;
unsigned char *ptr, *action_ptr;
unsigned char bts_scr_name[40]; /* 40 char long bts scr name? */
const struct firmware *fw;
struct sk_buff *skb;
struct hci_command *cmd;
version = read_local_version(lldev->hu.hdev);
if (version < 0)
return version;
chip = (version & 0x7C00) >> 10;
min_ver = (version & 0x007F);
maj_ver = (version & 0x0380) >> 7;
if (version & 0x8000)
maj_ver |= 0x0008;
snprintf(bts_scr_name, sizeof(bts_scr_name),
"ti-connectivity/TIInit_%d.%d.%d.bts",
chip, maj_ver, min_ver);
err = request_firmware(&fw, bts_scr_name, &lldev->serdev->dev);
if (err || !fw->data || !fw->size) {
bt_dev_err(lldev->hu.hdev, "request_firmware failed(errno %d) for %s",
err, bts_scr_name);
return -EINVAL;
}
ptr = (void *)fw->data;
len = fw->size;
/* bts_header to remove out magic number and
* version
*/
ptr += sizeof(struct bts_header);
len -= sizeof(struct bts_header);
while (len > 0 && ptr) {
bt_dev_dbg(lldev->hu.hdev, " action size %d, type %d ",
((struct bts_action *)ptr)->size,
((struct bts_action *)ptr)->type);
action_ptr = &(((struct bts_action *)ptr)->data[0]);
switch (((struct bts_action *)ptr)->type) {
case ACTION_SEND_COMMAND: /* action send */
bt_dev_dbg(lldev->hu.hdev, "S");
cmd = (struct hci_command *)action_ptr;
if (cmd->opcode == HCI_VS_UPDATE_UART_HCI_BAUDRATE) {
/* ignore remote change
* baud rate HCI VS command
*/
bt_dev_warn(lldev->hu.hdev, "change remote baud rate command in firmware");
break;
}
if (cmd->prefix != 1)
bt_dev_dbg(lldev->hu.hdev, "command type %d", cmd->prefix);
skb = __hci_cmd_sync(lldev->hu.hdev, cmd->opcode, cmd->plen, &cmd->speed, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
bt_dev_err(lldev->hu.hdev, "send command failed");
err = PTR_ERR(skb);
goto out_rel_fw;
}
kfree_skb(skb);
break;
case ACTION_WAIT_EVENT: /* wait */
/* no need to wait as command was synchronous */
bt_dev_dbg(lldev->hu.hdev, "W");
break;
case ACTION_DELAY: /* sleep */
bt_dev_info(lldev->hu.hdev, "sleep command in scr");
msleep(((struct bts_action_delay *)action_ptr)->msec);
break;
}
len -= (sizeof(struct bts_action) +
((struct bts_action *)ptr)->size);
ptr += sizeof(struct bts_action) +
((struct bts_action *)ptr)->size;
}
out_rel_fw:
/* fw download complete */
release_firmware(fw);
return err;
}
static int ll_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
bdaddr_t bdaddr_swapped;
struct sk_buff *skb;
/* HCI_VS_WRITE_BD_ADDR (at least on a CC2560A chip) expects the BD
* address to be MSB first, but bdaddr_t has the convention of being
* LSB first.
*/
baswap(&bdaddr_swapped, bdaddr);
skb = __hci_cmd_sync(hdev, HCI_VS_WRITE_BD_ADDR, sizeof(bdaddr_t),
&bdaddr_swapped, HCI_INIT_TIMEOUT);
if (!IS_ERR(skb))
kfree_skb(skb);
return PTR_ERR_OR_ZERO(skb);
}
static int ll_setup(struct hci_uart *hu)
{
int err, retry = 3;
struct ll_device *lldev;
struct serdev_device *serdev = hu->serdev;
u32 speed;
if (!serdev)
return 0;
lldev = serdev_device_get_drvdata(serdev);
hu->hdev->set_bdaddr = ll_set_bdaddr;
serdev_device_set_flow_control(serdev, true);
do {
/* Reset the Bluetooth device */
gpiod_set_value_cansleep(lldev->enable_gpio, 0);
msleep(5);
gpiod_set_value_cansleep(lldev->enable_gpio, 1);
err = serdev_device_wait_for_cts(serdev, true, 200);
if (err) {
bt_dev_err(hu->hdev, "Failed to get CTS");
return err;
}
err = download_firmware(lldev);
if (!err)
break;
/* Toggle BT_EN and retry */
bt_dev_err(hu->hdev, "download firmware failed, retrying...");
} while (retry--);
if (err)
return err;
/* Set BD address if one was specified at probe */
if (!bacmp(&lldev->bdaddr, BDADDR_NONE)) {
/* This means that there was an error getting the BD address
* during probe, so mark the device as having a bad address.
*/
set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks);
} else if (bacmp(&lldev->bdaddr, BDADDR_ANY)) {
err = ll_set_bdaddr(hu->hdev, &lldev->bdaddr);
if (err)
set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks);
}
/* 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) {
__le32 speed_le = cpu_to_le32(speed);
struct sk_buff *skb;
skb = __hci_cmd_sync(hu->hdev, HCI_VS_UPDATE_UART_HCI_BAUDRATE,
sizeof(speed_le), &speed_le,
HCI_INIT_TIMEOUT);
if (!IS_ERR(skb)) {
kfree_skb(skb);
serdev_device_set_baudrate(serdev, speed);
}
}
return 0;
}
static const struct hci_uart_proto llp;
static int hci_ti_probe(struct serdev_device *serdev)
{
struct hci_uart *hu;
struct ll_device *lldev;
struct nvmem_cell *bdaddr_cell;
u32 max_speed = 3000000;
lldev = devm_kzalloc(&serdev->dev, sizeof(struct ll_device), GFP_KERNEL);
if (!lldev)
return -ENOMEM;
hu = &lldev->hu;
serdev_device_set_drvdata(serdev, lldev);
lldev->serdev = hu->serdev = serdev;
lldev->enable_gpio = devm_gpiod_get_optional(&serdev->dev, "enable", GPIOD_OUT_LOW);
if (IS_ERR(lldev->enable_gpio))
return PTR_ERR(lldev->enable_gpio);
lldev->ext_clk = devm_clk_get(&serdev->dev, "ext_clock");
if (IS_ERR(lldev->ext_clk) && PTR_ERR(lldev->ext_clk) != -ENOENT)
return PTR_ERR(lldev->ext_clk);
of_property_read_u32(serdev->dev.of_node, "max-speed", &max_speed);
hci_uart_set_speeds(hu, 115200, max_speed);
/* optional BD address from nvram */
bdaddr_cell = nvmem_cell_get(&serdev->dev, "bd-address");
if (IS_ERR(bdaddr_cell)) {
int err = PTR_ERR(bdaddr_cell);
if (err == -EPROBE_DEFER)
return err;
/* ENOENT means there is no matching nvmem cell and ENOSYS
* means that nvmem is not enabled in the kernel configuration.
*/
if (err != -ENOENT && err != -ENOSYS) {
/* If there was some other error, give userspace a
* chance to fix the problem instead of failing to load
* the driver. Using BDADDR_NONE as a flag that is
* tested later in the setup function.
*/
dev_warn(&serdev->dev,
"Failed to get \"bd-address\" nvmem cell (%d)\n",
err);
bacpy(&lldev->bdaddr, BDADDR_NONE);
}
} else {
bdaddr_t *bdaddr;
size_t len;
bdaddr = nvmem_cell_read(bdaddr_cell, &len);
nvmem_cell_put(bdaddr_cell);
if (IS_ERR(bdaddr)) {
dev_err(&serdev->dev, "Failed to read nvmem bd-address\n");
return PTR_ERR(bdaddr);
}
if (len != sizeof(bdaddr_t)) {
dev_err(&serdev->dev, "Invalid nvmem bd-address length\n");
kfree(bdaddr);
return -EINVAL;
}
/* As per the device tree bindings, the value from nvmem is
* expected to be MSB first, but in the kernel it is expected
* that bdaddr_t is LSB first.
*/
baswap(&lldev->bdaddr, bdaddr);
kfree(bdaddr);
}
return hci_uart_register_device(hu, &llp);
}
static void hci_ti_remove(struct serdev_device *serdev)
{
struct ll_device *lldev = serdev_device_get_drvdata(serdev);
hci_uart_unregister_device(&lldev->hu);
}
static const struct of_device_id hci_ti_of_match[] = {
{ .compatible = "ti,cc2560" },
{ .compatible = "ti,wl1271-st" },
{ .compatible = "ti,wl1273-st" },
{ .compatible = "ti,wl1281-st" },
{ .compatible = "ti,wl1283-st" },
{ .compatible = "ti,wl1285-st" },
{ .compatible = "ti,wl1801-st" },
{ .compatible = "ti,wl1805-st" },
{ .compatible = "ti,wl1807-st" },
{ .compatible = "ti,wl1831-st" },
{ .compatible = "ti,wl1835-st" },
{ .compatible = "ti,wl1837-st" },
{},
};
MODULE_DEVICE_TABLE(of, hci_ti_of_match);
static struct serdev_device_driver hci_ti_drv = {
.driver = {
.name = "hci-ti",
.of_match_table = of_match_ptr(hci_ti_of_match),
},
.probe = hci_ti_probe,
.remove = hci_ti_remove,
};
#else
#define ll_setup NULL
#endif
static const struct hci_uart_proto llp = {
.id = HCI_UART_LL,
.name = "LL",
.setup = ll_setup,
.open = ll_open,
.close = ll_close,
.recv = ll_recv,
.enqueue = ll_enqueue,
.dequeue = ll_dequeue,
.flush = ll_flush,
};
int __init ll_init(void)
{
serdev_device_driver_register(&hci_ti_drv);
return hci_uart_register_proto(&llp);
}
int __exit ll_deinit(void)
{
serdev_device_driver_unregister(&hci_ti_drv);
return hci_uart_unregister_proto(&llp);
}
+390
View File
@@ -0,0 +1,390 @@
/*
*
* 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);
if (!hci_uart_has_flow_control(hu))
return -EOPNOTSUPP;
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;
skb_put_data(skb, 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);
}
+816
View File
@@ -0,0 +1,816 @@
/*
* Bluetooth HCI UART H4 driver with Nokia Extensions AKA Nokia H4+
*
* Copyright (C) 2015 Marcel Holtmann <marcel@holtmann.org>
* Copyright (C) 2015-2017 Sebastian Reichel <sre@kernel.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.
*/
#include <linux/clk.h>
#include <linux/errno.h>
#include <linux/firmware.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/serdev.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/types.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
#include "btbcm.h"
#define VERSION "0.1"
#define NOKIA_ID_BCM2048 0x04
#define NOKIA_ID_TI1271 0x31
#define FIRMWARE_BCM2048 "nokia/bcmfw.bin"
#define FIRMWARE_TI1271 "nokia/ti1273.bin"
#define HCI_NOKIA_NEG_PKT 0x06
#define HCI_NOKIA_ALIVE_PKT 0x07
#define HCI_NOKIA_RADIO_PKT 0x08
#define HCI_NOKIA_NEG_HDR_SIZE 1
#define HCI_NOKIA_MAX_NEG_SIZE 255
#define HCI_NOKIA_ALIVE_HDR_SIZE 1
#define HCI_NOKIA_MAX_ALIVE_SIZE 255
#define HCI_NOKIA_RADIO_HDR_SIZE 2
#define HCI_NOKIA_MAX_RADIO_SIZE 255
#define NOKIA_PROTO_PKT 0x44
#define NOKIA_PROTO_BYTE 0x4c
#define NOKIA_NEG_REQ 0x00
#define NOKIA_NEG_ACK 0x20
#define NOKIA_NEG_NAK 0x40
#define H4_TYPE_SIZE 1
#define NOKIA_RECV_ALIVE \
.type = HCI_NOKIA_ALIVE_PKT, \
.hlen = HCI_NOKIA_ALIVE_HDR_SIZE, \
.loff = 0, \
.lsize = 1, \
.maxlen = HCI_NOKIA_MAX_ALIVE_SIZE \
#define NOKIA_RECV_NEG \
.type = HCI_NOKIA_NEG_PKT, \
.hlen = HCI_NOKIA_NEG_HDR_SIZE, \
.loff = 0, \
.lsize = 1, \
.maxlen = HCI_NOKIA_MAX_NEG_SIZE \
#define NOKIA_RECV_RADIO \
.type = HCI_NOKIA_RADIO_PKT, \
.hlen = HCI_NOKIA_RADIO_HDR_SIZE, \
.loff = 1, \
.lsize = 1, \
.maxlen = HCI_NOKIA_MAX_RADIO_SIZE \
struct hci_nokia_neg_hdr {
u8 dlen;
} __packed;
struct hci_nokia_neg_cmd {
u8 ack;
u16 baud;
u16 unused1;
u8 proto;
u16 sys_clk;
u16 unused2;
} __packed;
#define NOKIA_ALIVE_REQ 0x55
#define NOKIA_ALIVE_RESP 0xcc
struct hci_nokia_alive_hdr {
u8 dlen;
} __packed;
struct hci_nokia_alive_pkt {
u8 mid;
u8 unused;
} __packed;
struct hci_nokia_neg_evt {
u8 ack;
u16 baud;
u16 unused1;
u8 proto;
u16 sys_clk;
u16 unused2;
u8 man_id;
u8 ver_id;
} __packed;
#define MAX_BAUD_RATE 3692300
#define SETUP_BAUD_RATE 921600
#define INIT_BAUD_RATE 120000
struct hci_nokia_radio_hdr {
u8 evt;
u8 dlen;
} __packed;
struct nokia_bt_dev {
struct hci_uart hu;
struct serdev_device *serdev;
struct gpio_desc *reset;
struct gpio_desc *wakeup_host;
struct gpio_desc *wakeup_bt;
unsigned long sysclk_speed;
int wake_irq;
struct sk_buff *rx_skb;
struct sk_buff_head txq;
bdaddr_t bdaddr;
int init_error;
struct completion init_completion;
u8 man_id;
u8 ver_id;
bool initialized;
bool tx_enabled;
bool rx_enabled;
};
static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb);
static void nokia_flow_control(struct serdev_device *serdev, bool enable)
{
if (enable) {
serdev_device_set_rts(serdev, true);
serdev_device_set_flow_control(serdev, true);
} else {
serdev_device_set_flow_control(serdev, false);
serdev_device_set_rts(serdev, false);
}
}
static irqreturn_t wakeup_handler(int irq, void *data)
{
struct nokia_bt_dev *btdev = data;
struct device *dev = &btdev->serdev->dev;
int wake_state = gpiod_get_value(btdev->wakeup_host);
if (btdev->rx_enabled == wake_state)
return IRQ_HANDLED;
if (wake_state)
pm_runtime_get(dev);
else
pm_runtime_put(dev);
btdev->rx_enabled = wake_state;
return IRQ_HANDLED;
}
static int nokia_reset(struct hci_uart *hu)
{
struct nokia_bt_dev *btdev = hu->priv;
struct device *dev = &btdev->serdev->dev;
int err;
/* reset routine */
gpiod_set_value_cansleep(btdev->reset, 1);
gpiod_set_value_cansleep(btdev->wakeup_bt, 1);
msleep(100);
/* safety check */
err = gpiod_get_value_cansleep(btdev->wakeup_host);
if (err == 1) {
dev_err(dev, "reset: host wakeup not low!");
return -EPROTO;
}
/* flush queue */
serdev_device_write_flush(btdev->serdev);
/* init uart */
nokia_flow_control(btdev->serdev, false);
serdev_device_set_baudrate(btdev->serdev, INIT_BAUD_RATE);
gpiod_set_value_cansleep(btdev->reset, 0);
/* wait for cts */
err = serdev_device_wait_for_cts(btdev->serdev, true, 200);
if (err < 0) {
dev_err(dev, "CTS not received: %d", err);
return err;
}
nokia_flow_control(btdev->serdev, true);
return 0;
}
static int nokia_send_alive_packet(struct hci_uart *hu)
{
struct nokia_bt_dev *btdev = hu->priv;
struct device *dev = &btdev->serdev->dev;
struct hci_nokia_alive_hdr *hdr;
struct hci_nokia_alive_pkt *pkt;
struct sk_buff *skb;
int len;
init_completion(&btdev->init_completion);
len = H4_TYPE_SIZE + sizeof(*hdr) + sizeof(*pkt);
skb = bt_skb_alloc(len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hci_skb_pkt_type(skb) = HCI_NOKIA_ALIVE_PKT;
memset(skb->data, 0x00, len);
hdr = skb_put(skb, sizeof(*hdr));
hdr->dlen = sizeof(*pkt);
pkt = skb_put(skb, sizeof(*pkt));
pkt->mid = NOKIA_ALIVE_REQ;
nokia_enqueue(hu, skb);
hci_uart_tx_wakeup(hu);
dev_dbg(dev, "Alive sent");
if (!wait_for_completion_interruptible_timeout(&btdev->init_completion,
msecs_to_jiffies(1000))) {
return -ETIMEDOUT;
}
if (btdev->init_error < 0)
return btdev->init_error;
return 0;
}
static int nokia_send_negotiation(struct hci_uart *hu)
{
struct nokia_bt_dev *btdev = hu->priv;
struct device *dev = &btdev->serdev->dev;
struct hci_nokia_neg_cmd *neg_cmd;
struct hci_nokia_neg_hdr *neg_hdr;
struct sk_buff *skb;
int len, err;
u16 baud = DIV_ROUND_CLOSEST(btdev->sysclk_speed * 10, SETUP_BAUD_RATE);
int sysclk = btdev->sysclk_speed / 1000;
len = H4_TYPE_SIZE + sizeof(*neg_hdr) + sizeof(*neg_cmd);
skb = bt_skb_alloc(len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hci_skb_pkt_type(skb) = HCI_NOKIA_NEG_PKT;
neg_hdr = skb_put(skb, sizeof(*neg_hdr));
neg_hdr->dlen = sizeof(*neg_cmd);
neg_cmd = skb_put(skb, sizeof(*neg_cmd));
neg_cmd->ack = NOKIA_NEG_REQ;
neg_cmd->baud = cpu_to_le16(baud);
neg_cmd->unused1 = 0x0000;
neg_cmd->proto = NOKIA_PROTO_BYTE;
neg_cmd->sys_clk = cpu_to_le16(sysclk);
neg_cmd->unused2 = 0x0000;
btdev->init_error = 0;
init_completion(&btdev->init_completion);
nokia_enqueue(hu, skb);
hci_uart_tx_wakeup(hu);
dev_dbg(dev, "Negotiation sent");
if (!wait_for_completion_interruptible_timeout(&btdev->init_completion,
msecs_to_jiffies(10000))) {
return -ETIMEDOUT;
}
if (btdev->init_error < 0)
return btdev->init_error;
/* Change to previously negotiated speed. Flow Control
* is disabled until bluetooth adapter is ready to avoid
* broken bytes being received.
*/
nokia_flow_control(btdev->serdev, false);
serdev_device_set_baudrate(btdev->serdev, SETUP_BAUD_RATE);
err = serdev_device_wait_for_cts(btdev->serdev, true, 200);
if (err < 0) {
dev_err(dev, "CTS not received: %d", err);
return err;
}
nokia_flow_control(btdev->serdev, true);
dev_dbg(dev, "Negotiation successful");
return 0;
}
static int nokia_setup_fw(struct hci_uart *hu)
{
struct nokia_bt_dev *btdev = hu->priv;
struct device *dev = &btdev->serdev->dev;
const char *fwname;
const struct firmware *fw;
const u8 *fw_ptr;
size_t fw_size;
int err;
dev_dbg(dev, "setup firmware");
if (btdev->man_id == NOKIA_ID_BCM2048) {
fwname = FIRMWARE_BCM2048;
} else if (btdev->man_id == NOKIA_ID_TI1271) {
fwname = FIRMWARE_TI1271;
} else {
dev_err(dev, "Unsupported bluetooth device!");
return -ENODEV;
}
err = request_firmware(&fw, fwname, dev);
if (err < 0) {
dev_err(dev, "%s: Failed to load Nokia firmware file (%d)",
hu->hdev->name, err);
return err;
}
fw_ptr = fw->data;
fw_size = fw->size;
while (fw_size >= 4) {
u16 pkt_size = get_unaligned_le16(fw_ptr);
u8 pkt_type = fw_ptr[2];
const struct hci_command_hdr *cmd;
u16 opcode;
struct sk_buff *skb;
switch (pkt_type) {
case HCI_COMMAND_PKT:
cmd = (struct hci_command_hdr *)(fw_ptr + 3);
opcode = le16_to_cpu(cmd->opcode);
skb = __hci_cmd_sync(hu->hdev, opcode, cmd->plen,
fw_ptr + 3 + HCI_COMMAND_HDR_SIZE,
HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
err = PTR_ERR(skb);
dev_err(dev, "%s: FW command %04x failed (%d)",
hu->hdev->name, opcode, err);
goto done;
}
kfree_skb(skb);
break;
case HCI_NOKIA_RADIO_PKT:
case HCI_NOKIA_NEG_PKT:
case HCI_NOKIA_ALIVE_PKT:
break;
}
fw_ptr += pkt_size + 2;
fw_size -= pkt_size + 2;
}
done:
release_firmware(fw);
return err;
}
static int nokia_setup(struct hci_uart *hu)
{
struct nokia_bt_dev *btdev = hu->priv;
struct device *dev = &btdev->serdev->dev;
int err;
btdev->initialized = false;
nokia_flow_control(btdev->serdev, false);
pm_runtime_get_sync(dev);
if (btdev->tx_enabled) {
gpiod_set_value_cansleep(btdev->wakeup_bt, 0);
pm_runtime_put(&btdev->serdev->dev);
btdev->tx_enabled = false;
}
dev_dbg(dev, "protocol setup");
/* 0. reset connection */
err = nokia_reset(hu);
if (err < 0) {
dev_err(dev, "Reset failed: %d", err);
goto out;
}
/* 1. negotiate speed etc */
err = nokia_send_negotiation(hu);
if (err < 0) {
dev_err(dev, "Negotiation failed: %d", err);
goto out;
}
/* 2. verify correct setup using alive packet */
err = nokia_send_alive_packet(hu);
if (err < 0) {
dev_err(dev, "Alive check failed: %d", err);
goto out;
}
/* 3. send firmware */
err = nokia_setup_fw(hu);
if (err < 0) {
dev_err(dev, "Could not setup FW: %d", err);
goto out;
}
nokia_flow_control(btdev->serdev, false);
serdev_device_set_baudrate(btdev->serdev, MAX_BAUD_RATE);
nokia_flow_control(btdev->serdev, true);
if (btdev->man_id == NOKIA_ID_BCM2048) {
hu->hdev->set_bdaddr = btbcm_set_bdaddr;
set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks);
dev_dbg(dev, "bcm2048 has invalid bluetooth address!");
}
dev_dbg(dev, "protocol setup done!");
gpiod_set_value_cansleep(btdev->wakeup_bt, 0);
pm_runtime_put(dev);
btdev->tx_enabled = false;
btdev->initialized = true;
return 0;
out:
pm_runtime_put(dev);
return err;
}
static int nokia_open(struct hci_uart *hu)
{
struct device *dev = &hu->serdev->dev;
dev_dbg(dev, "protocol open");
pm_runtime_enable(dev);
return 0;
}
static int nokia_flush(struct hci_uart *hu)
{
struct nokia_bt_dev *btdev = hu->priv;
dev_dbg(&btdev->serdev->dev, "flush device");
skb_queue_purge(&btdev->txq);
return 0;
}
static int nokia_close(struct hci_uart *hu)
{
struct nokia_bt_dev *btdev = hu->priv;
struct device *dev = &btdev->serdev->dev;
dev_dbg(dev, "close device");
btdev->initialized = false;
skb_queue_purge(&btdev->txq);
kfree_skb(btdev->rx_skb);
/* disable module */
gpiod_set_value(btdev->reset, 1);
gpiod_set_value(btdev->wakeup_bt, 0);
pm_runtime_disable(&btdev->serdev->dev);
return 0;
}
/* Enqueue frame for transmittion (padding, crc, etc) */
static int nokia_enqueue(struct hci_uart *hu, struct sk_buff *skb)
{
struct nokia_bt_dev *btdev = hu->priv;
int err;
/* Prepend skb with frame type */
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
/* Packets must be word aligned */
if (skb->len % 2) {
err = skb_pad(skb, 1);
if (err)
return err;
skb_put_u8(skb, 0x00);
}
skb_queue_tail(&btdev->txq, skb);
return 0;
}
static int nokia_recv_negotiation_packet(struct hci_dev *hdev,
struct sk_buff *skb)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct nokia_bt_dev *btdev = hu->priv;
struct device *dev = &btdev->serdev->dev;
struct hci_nokia_neg_hdr *hdr;
struct hci_nokia_neg_evt *evt;
int ret = 0;
hdr = (struct hci_nokia_neg_hdr *)skb->data;
if (hdr->dlen != sizeof(*evt)) {
btdev->init_error = -EIO;
ret = -EIO;
goto finish_neg;
}
evt = skb_pull(skb, sizeof(*hdr));
if (evt->ack != NOKIA_NEG_ACK) {
dev_err(dev, "Negotiation received: wrong reply");
btdev->init_error = -EINVAL;
ret = -EINVAL;
goto finish_neg;
}
btdev->man_id = evt->man_id;
btdev->ver_id = evt->ver_id;
dev_dbg(dev, "Negotiation received: baud=%u:clk=%u:manu=%u:vers=%u",
evt->baud, evt->sys_clk, evt->man_id, evt->ver_id);
finish_neg:
complete(&btdev->init_completion);
kfree_skb(skb);
return ret;
}
static int nokia_recv_alive_packet(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
struct nokia_bt_dev *btdev = hu->priv;
struct device *dev = &btdev->serdev->dev;
struct hci_nokia_alive_hdr *hdr;
struct hci_nokia_alive_pkt *pkt;
int ret = 0;
hdr = (struct hci_nokia_alive_hdr *)skb->data;
if (hdr->dlen != sizeof(*pkt)) {
dev_err(dev, "Corrupted alive message");
btdev->init_error = -EIO;
ret = -EIO;
goto finish_alive;
}
pkt = skb_pull(skb, sizeof(*hdr));
if (pkt->mid != NOKIA_ALIVE_RESP) {
dev_err(dev, "Alive received: invalid response: 0x%02x!",
pkt->mid);
btdev->init_error = -EINVAL;
ret = -EINVAL;
goto finish_alive;
}
dev_dbg(dev, "Alive received");
finish_alive:
complete(&btdev->init_completion);
kfree_skb(skb);
return ret;
}
static int nokia_recv_radio(struct hci_dev *hdev, struct sk_buff *skb)
{
/* Packets received on the dedicated radio channel are
* HCI events and so feed them back into the core.
*/
hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
return hci_recv_frame(hdev, skb);
}
/* Recv data */
static const struct h4_recv_pkt nokia_recv_pkts[] = {
{ H4_RECV_ACL, .recv = hci_recv_frame },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = hci_recv_frame },
{ NOKIA_RECV_ALIVE, .recv = nokia_recv_alive_packet },
{ NOKIA_RECV_NEG, .recv = nokia_recv_negotiation_packet },
{ NOKIA_RECV_RADIO, .recv = nokia_recv_radio },
};
static int nokia_recv(struct hci_uart *hu, const void *data, int count)
{
struct nokia_bt_dev *btdev = hu->priv;
struct device *dev = &btdev->serdev->dev;
int err;
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
return -EUNATCH;
btdev->rx_skb = h4_recv_buf(hu->hdev, btdev->rx_skb, data, count,
nokia_recv_pkts, ARRAY_SIZE(nokia_recv_pkts));
if (IS_ERR(btdev->rx_skb)) {
err = PTR_ERR(btdev->rx_skb);
dev_err(dev, "Frame reassembly failed (%d)", err);
btdev->rx_skb = NULL;
return err;
}
return count;
}
static struct sk_buff *nokia_dequeue(struct hci_uart *hu)
{
struct nokia_bt_dev *btdev = hu->priv;
struct device *dev = &btdev->serdev->dev;
struct sk_buff *result = skb_dequeue(&btdev->txq);
if (!btdev->initialized)
return result;
if (btdev->tx_enabled == !!result)
return result;
if (result) {
pm_runtime_get_sync(dev);
gpiod_set_value_cansleep(btdev->wakeup_bt, 1);
} else {
serdev_device_wait_until_sent(btdev->serdev, 0);
gpiod_set_value_cansleep(btdev->wakeup_bt, 0);
pm_runtime_put(dev);
}
btdev->tx_enabled = !!result;
return result;
}
static const struct hci_uart_proto nokia_proto = {
.id = HCI_UART_NOKIA,
.name = "Nokia",
.open = nokia_open,
.close = nokia_close,
.recv = nokia_recv,
.enqueue = nokia_enqueue,
.dequeue = nokia_dequeue,
.flush = nokia_flush,
.setup = nokia_setup,
.manufacturer = 1,
};
static int nokia_bluetooth_serdev_probe(struct serdev_device *serdev)
{
struct device *dev = &serdev->dev;
struct nokia_bt_dev *btdev;
struct clk *sysclk;
int err = 0;
btdev = devm_kzalloc(dev, sizeof(*btdev), GFP_KERNEL);
if (!btdev)
return -ENOMEM;
btdev->hu.serdev = btdev->serdev = serdev;
serdev_device_set_drvdata(serdev, btdev);
btdev->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(btdev->reset)) {
err = PTR_ERR(btdev->reset);
dev_err(dev, "could not get reset gpio: %d", err);
return err;
}
btdev->wakeup_host = devm_gpiod_get(dev, "host-wakeup", GPIOD_IN);
if (IS_ERR(btdev->wakeup_host)) {
err = PTR_ERR(btdev->wakeup_host);
dev_err(dev, "could not get host wakeup gpio: %d", err);
return err;
}
btdev->wake_irq = gpiod_to_irq(btdev->wakeup_host);
err = devm_request_threaded_irq(dev, btdev->wake_irq, NULL,
wakeup_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"wakeup", btdev);
if (err) {
dev_err(dev, "could request wakeup irq: %d", err);
return err;
}
btdev->wakeup_bt = devm_gpiod_get(dev, "bluetooth-wakeup",
GPIOD_OUT_LOW);
if (IS_ERR(btdev->wakeup_bt)) {
err = PTR_ERR(btdev->wakeup_bt);
dev_err(dev, "could not get BT wakeup gpio: %d", err);
return err;
}
sysclk = devm_clk_get(dev, "sysclk");
if (IS_ERR(sysclk)) {
err = PTR_ERR(sysclk);
dev_err(dev, "could not get sysclk: %d", err);
return err;
}
clk_prepare_enable(sysclk);
btdev->sysclk_speed = clk_get_rate(sysclk);
clk_disable_unprepare(sysclk);
skb_queue_head_init(&btdev->txq);
btdev->hu.priv = btdev;
btdev->hu.alignment = 2; /* Nokia H4+ is word aligned */
err = hci_uart_register_device(&btdev->hu, &nokia_proto);
if (err) {
dev_err(dev, "could not register bluetooth uart: %d", err);
return err;
}
return 0;
}
static void nokia_bluetooth_serdev_remove(struct serdev_device *serdev)
{
struct nokia_bt_dev *btdev = serdev_device_get_drvdata(serdev);
hci_uart_unregister_device(&btdev->hu);
}
static int nokia_bluetooth_runtime_suspend(struct device *dev)
{
struct serdev_device *serdev = to_serdev_device(dev);
nokia_flow_control(serdev, false);
return 0;
}
static int nokia_bluetooth_runtime_resume(struct device *dev)
{
struct serdev_device *serdev = to_serdev_device(dev);
nokia_flow_control(serdev, true);
return 0;
}
static const struct dev_pm_ops nokia_bluetooth_pm_ops = {
SET_RUNTIME_PM_OPS(nokia_bluetooth_runtime_suspend,
nokia_bluetooth_runtime_resume,
NULL)
};
#ifdef CONFIG_OF
static const struct of_device_id nokia_bluetooth_of_match[] = {
{ .compatible = "nokia,h4p-bluetooth", },
{},
};
MODULE_DEVICE_TABLE(of, nokia_bluetooth_of_match);
#endif
static struct serdev_device_driver nokia_bluetooth_serdev_driver = {
.probe = nokia_bluetooth_serdev_probe,
.remove = nokia_bluetooth_serdev_remove,
.driver = {
.name = "nokia-bluetooth",
.pm = &nokia_bluetooth_pm_ops,
.of_match_table = of_match_ptr(nokia_bluetooth_of_match),
},
};
module_serdev_device_driver(nokia_bluetooth_serdev_driver);
MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
MODULE_DESCRIPTION("Bluetooth HCI UART Nokia H4+ driver ver " VERSION);
MODULE_VERSION(VERSION);
MODULE_LICENSE("GPL");
File diff suppressed because it is too large. Load diff
+383
View File
@@ -0,0 +1,383 @@
/*
* Bluetooth HCI serdev driver lib
*
* Copyright (C) 2017 Linaro, Ltd., Rob Herring <robh@kernel.org>
*
* Based on hci_ldisc.c:
*
* 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.
*
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/serdev.h>
#include <linux/skbuff.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include "hci_uart.h"
static struct serdev_device_ops hci_serdev_client_ops;
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;
}
static void hci_uart_write_work(struct work_struct *work)
{
struct hci_uart *hu = container_of(work, struct hci_uart, write_work);
struct serdev_device *serdev = hu->serdev;
struct hci_dev *hdev = hu->hdev;
struct sk_buff *skb;
/* REVISIT:
* should we cope with bad skbs or ->write() returning an error value?
*/
do {
clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
while ((skb = hci_uart_dequeue(hu))) {
int len;
len = serdev_device_write_buf(serdev,
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);
}
} while(test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state));
clear_bit(HCI_UART_SENDING, &hu->tx_state);
}
/* ------- Interface to HCI layer ------ */
/* Reset device */
static int hci_uart_flush(struct hci_dev *hdev)
{
struct hci_uart *hu = hci_get_drvdata(hdev);
BT_DBG("hdev %p serdev %p", hdev, hu->serdev);
if (hu->tx_skb) {
kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
}
/* Flush any pending characters in the driver and discipline. */
serdev_device_write_flush(hu->serdev);
if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
hu->proto->flush(hu);
return 0;
}
/* Initialize device */
static int hci_uart_open(struct hci_dev *hdev)
{
BT_DBG("%s %p", hdev->name, hdev);
/* Undo clearing this from hci_uart_close() */
hdev->flush = hci_uart_flush;
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;
}
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)
serdev_device_set_baudrate(hu->serdev, 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)
bt_dev_err(hdev, "Failed to set baudrate");
else
serdev_device_set_baudrate(hu->serdev, 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_dev_err(hdev, "Reading local version info failed (%ld)",
PTR_ERR(skb));
return 0;
}
if (skb->len != sizeof(*ver))
bt_dev_err(hdev, "Event length mismatch for version info");
kfree_skb(skb);
return 0;
}
/** hci_uart_write_wakeup - transmit buffer wakeup
* @serdev: serial device
*
* This function is called by the serdev framework when it accepts
* more data being sent.
*/
static void hci_uart_write_wakeup(struct serdev_device *serdev)
{
struct hci_uart *hu = serdev_device_get_drvdata(serdev);
BT_DBG("");
if (!hu || serdev != hu->serdev) {
WARN_ON(1);
return;
}
if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
hci_uart_tx_wakeup(hu);
}
/** hci_uart_receive_buf - receive buffer wakeup
* @serdev: serial device
* @data: pointer to received data
* @count: count of received data in bytes
*
* This function is called by the serdev framework when it received data
* in the RX buffer.
*
* Return: number of processed bytes
*/
static int hci_uart_receive_buf(struct serdev_device *serdev, const u8 *data,
size_t count)
{
struct hci_uart *hu = serdev_device_get_drvdata(serdev);
if (!hu || serdev != hu->serdev) {
WARN_ON(1);
return 0;
}
if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
return 0;
/* 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;
return count;
}
static struct serdev_device_ops hci_serdev_client_ops = {
.receive_buf = hci_uart_receive_buf,
.write_wakeup = hci_uart_write_wakeup,
};
int hci_uart_register_device(struct hci_uart *hu,
const struct hci_uart_proto *p)
{
int err;
struct hci_dev *hdev;
BT_DBG("");
serdev_device_set_client_ops(hu->serdev, &hci_serdev_client_ops);
err = serdev_device_open(hu->serdev);
if (err)
return err;
err = p->open(hu);
if (err)
goto err_open;
hu->proto = p;
set_bit(HCI_UART_PROTO_READY, &hu->flags);
/* Initialize and register HCI device */
hdev = hci_alloc_dev();
if (!hdev) {
BT_ERR("Can't allocate HCI device");
err = -ENOMEM;
goto err_alloc;
}
hu->hdev = hdev;
hdev->bus = HCI_UART;
hci_set_drvdata(hdev, hu);
INIT_WORK(&hu->init_ready, hci_uart_init_work);
INIT_WORK(&hu->write_work, hci_uart_write_work);
percpu_init_rwsem(&hu->proto_lock);
/* 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->serdev->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");
err = -ENODEV;
goto err_register;
}
set_bit(HCI_UART_REGISTERED, &hu->flags);
return 0;
err_register:
hci_free_dev(hdev);
err_alloc:
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
p->close(hu);
err_open:
serdev_device_close(hu->serdev);
return err;
}
EXPORT_SYMBOL_GPL(hci_uart_register_device);
void hci_uart_unregister_device(struct hci_uart *hu)
{
struct hci_dev *hdev = hu->hdev;
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
cancel_work_sync(&hu->init_ready);
if (test_bit(HCI_UART_REGISTERED, &hu->flags))
hci_unregister_dev(hdev);
hci_free_dev(hdev);
cancel_work_sync(&hu->write_work);
hu->proto->close(hu);
serdev_device_close(hu->serdev);
}
EXPORT_SYMBOL_GPL(hci_uart_unregister_device);
+208
View File
@@ -0,0 +1,208 @@
/*
*
* 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 serdev_device;
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 serdev_device *serdev;
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;
struct percpu_rw_semaphore proto_lock; /* Stop work for proto close */
void *priv;
struct sk_buff *tx_skb;
unsigned long tx_state;
unsigned int init_speed;
unsigned int oper_speed;
u8 alignment;
u8 padding;
};
/* 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_register_device(struct hci_uart *hu, const struct hci_uart_proto *p);
void hci_uart_unregister_device(struct hci_uart *hu);
int hci_uart_tx_wakeup(struct hci_uart *hu);
int hci_uart_init_ready(struct hci_uart *hu);
void hci_uart_init_work(struct work_struct *work);
void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed);
bool hci_uart_has_flow_control(struct hci_uart *hu);
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
+390
View File
@@ -0,0 +1,390 @@
/*
*
* 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_u8(skb, 0xff);
skb_put_u8(skb, 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,
struct iov_iter *from)
{
size_t len = iov_iter_count(from);
struct sk_buff *skb;
__u8 pkt_type, opcode;
int ret;
if (len < 2 || len > HCI_MAX_FRAME_SIZE)
return -EINVAL;
skb = bt_skb_alloc(len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
if (!copy_from_iter_full(skb_put(skb, len), len, from)) {
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, struct iov_iter *from)
{
struct file *file = iocb->ki_filp;
struct vhci_data *data = file->private_data;
return vhci_get_user(data, from);
}
static __poll_t 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 EPOLLIN | EPOLLRDNORM;
return EPOLLOUT | EPOLLWRNORM;
}
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,
.write_iter = 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,
};
module_misc_device(vhci_miscdev);
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);