mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-14 01:46:21 +07:00
Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg says: ==================== pull request: bluetooth-next 2019-07-07 Here's the main bluetooth-next pull request for 5.3: - Added support for new devices from Qualcomm, Realtek and Broadcom and MediaTek - Various fixes to 6LoWPAN - Fix L2CAP PSM namespace separation for LE & BR/EDR - Fix behavior with Microsoft Surface Precision Mouse - Added support for LE Ping feature - Fix L2CAP Disconnect response handling if received in wrong state Please let me know if there are any issues pulling. Thanks. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
f7623d3407
25
Documentation/devicetree/bindings/net/marvell-bluetooth.txt
Normal file
25
Documentation/devicetree/bindings/net/marvell-bluetooth.txt
Normal file
@ -0,0 +1,25 @@
|
||||
Marvell Bluetooth Chips
|
||||
-----------------------
|
||||
|
||||
This documents the binding structure and common properties for serial
|
||||
attached Marvell Bluetooth devices. The following chips are included in
|
||||
this binding:
|
||||
|
||||
* Marvell 88W8897 Bluetooth devices
|
||||
|
||||
Required properties:
|
||||
- compatible: should be:
|
||||
"mrvl,88w8897"
|
||||
|
||||
Optional properties:
|
||||
None so far
|
||||
|
||||
Example:
|
||||
|
||||
&serial0 {
|
||||
compatible = "ns16550a";
|
||||
...
|
||||
bluetooth {
|
||||
compatible = "mrvl,88w8897";
|
||||
};
|
||||
};
|
@ -50,16 +50,33 @@ Required properties:
|
||||
"mediatek,mt7663u-bluetooth": for MT7663U device
|
||||
"mediatek,mt7668u-bluetooth": for MT7668U device
|
||||
- vcc-supply: Main voltage regulator
|
||||
|
||||
If the pin controller on the platform can support both pinmux and GPIO
|
||||
control such as the most of MediaTek platform. Please use below properties.
|
||||
|
||||
- pinctrl-names: Should be "default", "runtime"
|
||||
- pinctrl-0: Should contain UART RXD low when the device is powered up to
|
||||
enter proper bootstrap mode.
|
||||
- pinctrl-1: Should contain UART mode pin ctrl
|
||||
|
||||
Else, the pin controller on the platform only can support pinmux control and
|
||||
the GPIO control still has to rely on the dedicated GPIO controller such as
|
||||
a legacy MediaTek SoC, MT7621. Please use the below properties.
|
||||
|
||||
- boot-gpios: GPIO same to the pin as UART RXD and used to keep LOW when
|
||||
the device is powered up to enter proper bootstrap mode when
|
||||
- pinctrl-names: Should be "default"
|
||||
- pinctrl-0: Should contain UART mode pin ctrl
|
||||
|
||||
Optional properties:
|
||||
|
||||
- reset-gpios: GPIO used to reset the device whose initial state keeps low,
|
||||
if the GPIO is missing, then board-level design should be
|
||||
guaranteed.
|
||||
- clocks: Should be the clock specifiers corresponding to the entry in
|
||||
clock-names property. If the clock is missing, then board-level
|
||||
design should be guaranteed.
|
||||
- clock-names: Should contain "osc" entry for the external oscillator.
|
||||
- current-speed: Current baud rate of the device whose defaults to 921600
|
||||
|
||||
Example:
|
||||
|
@ -17,6 +17,7 @@ Optional properties for compatible string qcom,qca6174-bt:
|
||||
|
||||
- enable-gpios: gpio specifier used to enable chip
|
||||
- clocks: clock provided to the controller (SUSCLK_32KHZ)
|
||||
- firmware-name: specify the name of nvm firmware to load
|
||||
|
||||
Required properties for compatible string qcom,wcn399x-bt:
|
||||
|
||||
@ -28,6 +29,7 @@ Required properties for compatible string qcom,wcn399x-bt:
|
||||
Optional properties for compatible string qcom,wcn399x-bt:
|
||||
|
||||
- max-speed: see Documentation/devicetree/bindings/serial/slave-device.txt
|
||||
- firmware-name: specify the name of nvm firmware to load
|
||||
|
||||
Examples:
|
||||
|
||||
@ -40,6 +42,7 @@ serial@7570000 {
|
||||
|
||||
enable-gpios = <&pm8994_gpios 19 GPIO_ACTIVE_HIGH>;
|
||||
clocks = <&divclk4>;
|
||||
firmware-name = "nvm_00440302.bin";
|
||||
};
|
||||
};
|
||||
|
||||
@ -52,5 +55,6 @@ serial@898000 {
|
||||
vddrf-supply = <&vreg_l17a_1p3>;
|
||||
vddch0-supply = <&vreg_l25a_3p3>;
|
||||
max-speed = <3200000>;
|
||||
firmware-name = "crnv21.bin";
|
||||
};
|
||||
};
|
||||
|
@ -52,6 +52,17 @@ config BT_HCIBTUSB_BCM
|
||||
|
||||
Say Y here to compile support for Broadcom protocol.
|
||||
|
||||
config BT_HCIBTUSB_MTK
|
||||
bool "MediaTek protocol support"
|
||||
depends on BT_HCIBTUSB
|
||||
default n
|
||||
help
|
||||
The MediaTek protocol support enables firmware download
|
||||
support and chip initialization for MediaTek Bluetooth
|
||||
USB controllers.
|
||||
|
||||
Say Y here to compile support for MediaTek protocol.
|
||||
|
||||
config BT_HCIBTUSB_RTL
|
||||
bool "Realtek protocol support"
|
||||
depends on BT_HCIBTUSB
|
||||
@ -237,6 +248,7 @@ config BT_HCIUART_AG6XX
|
||||
config BT_HCIUART_MRVL
|
||||
bool "Marvell protocol support"
|
||||
depends on BT_HCIUART
|
||||
depends on BT_HCIUART_SERDEV
|
||||
select BT_HCIUART_H4
|
||||
help
|
||||
Marvell is serial protocol for communication between Bluetooth
|
||||
|
@ -359,7 +359,8 @@ static int bpa10x_set_diag(struct hci_dev *hdev, bool enable)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
static int bpa10x_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
struct bpa10x_data *data;
|
||||
struct hci_dev *hdev;
|
||||
|
@ -335,6 +335,7 @@ static const struct bcm_subver_table bcm_uart_subver_table[] = {
|
||||
{ 0x230f, "BCM4356A2" }, /* 001.003.015 */
|
||||
{ 0x220e, "BCM20702A1" }, /* 001.002.014 */
|
||||
{ 0x4217, "BCM4329B1" }, /* 002.002.023 */
|
||||
{ 0x6106, "BCM4359C0" }, /* 003.001.006 */
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -115,10 +115,12 @@ struct btmtk_hci_wmt_params {
|
||||
struct btmtkuart_dev {
|
||||
struct hci_dev *hdev;
|
||||
struct serdev_device *serdev;
|
||||
struct clk *clk;
|
||||
|
||||
struct clk *clk;
|
||||
struct clk *osc;
|
||||
struct regulator *vcc;
|
||||
struct gpio_desc *reset;
|
||||
struct gpio_desc *boot;
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pins_runtime;
|
||||
struct pinctrl_state *pins_boot;
|
||||
@ -911,6 +913,19 @@ static int btmtkuart_parse_dt(struct serdev_device *serdev)
|
||||
return err;
|
||||
}
|
||||
|
||||
bdev->osc = devm_clk_get_optional(&serdev->dev, "osc");
|
||||
if (IS_ERR(bdev->osc)) {
|
||||
err = PTR_ERR(bdev->osc);
|
||||
return err;
|
||||
}
|
||||
|
||||
bdev->boot = devm_gpiod_get_optional(&serdev->dev, "boot",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(bdev->boot)) {
|
||||
err = PTR_ERR(bdev->boot);
|
||||
return err;
|
||||
}
|
||||
|
||||
bdev->pinctrl = devm_pinctrl_get(&serdev->dev);
|
||||
if (IS_ERR(bdev->pinctrl)) {
|
||||
err = PTR_ERR(bdev->pinctrl);
|
||||
@ -919,8 +934,10 @@ static int btmtkuart_parse_dt(struct serdev_device *serdev)
|
||||
|
||||
bdev->pins_boot = pinctrl_lookup_state(bdev->pinctrl,
|
||||
"default");
|
||||
if (IS_ERR(bdev->pins_boot)) {
|
||||
if (IS_ERR(bdev->pins_boot) && !bdev->boot) {
|
||||
err = PTR_ERR(bdev->pins_boot);
|
||||
dev_err(&serdev->dev,
|
||||
"Should assign RXD to LOW at boot stage\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -996,13 +1013,25 @@ static int btmtkuart_probe(struct serdev_device *serdev)
|
||||
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
|
||||
|
||||
if (btmtkuart_is_standalone(bdev)) {
|
||||
/* Switch to the specific pin state for the booting requires */
|
||||
pinctrl_select_state(bdev->pinctrl, bdev->pins_boot);
|
||||
err = clk_prepare_enable(bdev->osc);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (bdev->boot) {
|
||||
gpiod_set_value_cansleep(bdev->boot, 1);
|
||||
} else {
|
||||
/* Switch to the specific pin state for the booting
|
||||
* requires.
|
||||
*/
|
||||
pinctrl_select_state(bdev->pinctrl, bdev->pins_boot);
|
||||
}
|
||||
|
||||
/* Power on */
|
||||
err = regulator_enable(bdev->vcc);
|
||||
if (err < 0)
|
||||
if (err < 0) {
|
||||
clk_disable_unprepare(bdev->osc);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Reset if the reset-gpios is available otherwise the board
|
||||
* -level design should be guaranteed.
|
||||
@ -1017,6 +1046,10 @@ static int btmtkuart_probe(struct serdev_device *serdev)
|
||||
* mode the device requires for UART transfers.
|
||||
*/
|
||||
msleep(50);
|
||||
|
||||
if (bdev->boot)
|
||||
devm_gpiod_put(&serdev->dev, bdev->boot);
|
||||
|
||||
pinctrl_select_state(bdev->pinctrl, bdev->pins_runtime);
|
||||
|
||||
/* A standalone device doesn't depends on power domain on SoC,
|
||||
@ -1037,10 +1070,8 @@ static int btmtkuart_probe(struct serdev_device *serdev)
|
||||
return 0;
|
||||
|
||||
err_regulator_disable:
|
||||
if (btmtkuart_is_standalone(bdev)) {
|
||||
pinctrl_select_state(bdev->pinctrl, bdev->pins_boot);
|
||||
if (btmtkuart_is_standalone(bdev))
|
||||
regulator_disable(bdev->vcc);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -1050,9 +1081,9 @@ static void btmtkuart_remove(struct serdev_device *serdev)
|
||||
struct btmtkuart_dev *bdev = serdev_device_get_drvdata(serdev);
|
||||
struct hci_dev *hdev = bdev->hdev;
|
||||
|
||||
if (btmtkuart_is_standalone(bdev)) {
|
||||
pinctrl_select_state(bdev->pinctrl, bdev->pins_boot);
|
||||
if (btmtkuart_is_standalone(bdev)) {
|
||||
regulator_disable(bdev->vcc);
|
||||
clk_disable_unprepare(bdev->osc);
|
||||
}
|
||||
|
||||
hci_unregister_dev(hdev);
|
||||
|
@ -131,6 +131,7 @@ static void qca_tlv_check_data(struct rome_config *config,
|
||||
* In case VSE is skipped, only the last segment is acked.
|
||||
*/
|
||||
config->dnld_mode = tlv_patch->download_mode;
|
||||
config->dnld_type = config->dnld_mode;
|
||||
|
||||
BT_DBG("Total Length : %d bytes",
|
||||
le32_to_cpu(tlv_patch->total_size));
|
||||
@ -251,6 +252,31 @@ static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int qca_inject_cmd_complete_event(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_event_hdr *hdr;
|
||||
struct hci_ev_cmd_complete *evt;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = skb_put(skb, sizeof(*hdr));
|
||||
hdr->evt = HCI_EV_CMD_COMPLETE;
|
||||
hdr->plen = sizeof(*evt) + 1;
|
||||
|
||||
evt = skb_put(skb, sizeof(*evt));
|
||||
evt->ncmd = 1;
|
||||
evt->opcode = QCA_HCI_CC_OPCODE;
|
||||
|
||||
skb_put_u8(skb, QCA_HCI_CC_SUCCESS);
|
||||
|
||||
hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
|
||||
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
static int qca_download_firmware(struct hci_dev *hdev,
|
||||
struct rome_config *config)
|
||||
{
|
||||
@ -284,11 +310,22 @@ static int qca_download_firmware(struct hci_dev *hdev,
|
||||
ret = qca_tlv_send_segment(hdev, segsize, segment,
|
||||
config->dnld_mode);
|
||||
if (ret)
|
||||
break;
|
||||
goto out;
|
||||
|
||||
segment += segsize;
|
||||
}
|
||||
|
||||
/* Latest qualcomm chipsets are not sending a command complete event
|
||||
* for every fw packet sent. They only respond with a vendor specific
|
||||
* event for the last packet. This optimization in the chip will
|
||||
* decrease the BT in initialization time. Here we will inject a command
|
||||
* complete event to avoid a command timeout error message.
|
||||
*/
|
||||
if (config->dnld_type == ROME_SKIP_EVT_VSE_CC ||
|
||||
config->dnld_type == ROME_SKIP_EVT_VSE)
|
||||
return qca_inject_cmd_complete_event(hdev);
|
||||
|
||||
out:
|
||||
release_firmware(fw);
|
||||
|
||||
return ret;
|
||||
@ -319,7 +356,8 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
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)
|
||||
enum qca_btsoc_type soc_type, u32 soc_ver,
|
||||
const char *firmware_name)
|
||||
{
|
||||
struct rome_config config;
|
||||
int err;
|
||||
@ -352,7 +390,10 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
|
||||
/* Download NVM configuration */
|
||||
config.type = TLV_TYPE_NVM;
|
||||
if (qca_is_wcn399x(soc_type))
|
||||
if (firmware_name)
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/%s", firmware_name);
|
||||
else if (qca_is_wcn399x(soc_type))
|
||||
snprintf(config.fwname, sizeof(config.fwname),
|
||||
"qca/crnv%02x.bin", rom_ver);
|
||||
else
|
||||
|
@ -28,6 +28,9 @@
|
||||
#define QCA_WCN3990_POWERON_PULSE 0xFC
|
||||
#define QCA_WCN3990_POWEROFF_PULSE 0xC0
|
||||
|
||||
#define QCA_HCI_CC_OPCODE 0xFC00
|
||||
#define QCA_HCI_CC_SUCCESS 0x00
|
||||
|
||||
enum qca_baudrate {
|
||||
QCA_BAUDRATE_115200 = 0,
|
||||
QCA_BAUDRATE_57600,
|
||||
@ -69,6 +72,7 @@ struct rome_config {
|
||||
char fwname[64];
|
||||
uint8_t user_baud_rate;
|
||||
enum rome_tlv_dnld_mode dnld_mode;
|
||||
enum rome_tlv_dnld_mode dnld_type;
|
||||
};
|
||||
|
||||
struct edl_event_hdr {
|
||||
@ -127,7 +131,8 @@ enum qca_btsoc_type {
|
||||
|
||||
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);
|
||||
enum qca_btsoc_type soc_type, u32 soc_ver,
|
||||
const char *firmware_name);
|
||||
int qca_read_soc_version(struct hci_dev *hdev, u32 *soc_version);
|
||||
int qca_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
||||
static inline bool qca_is_wcn399x(enum qca_btsoc_type soc_type)
|
||||
@ -142,7 +147,8 @@ static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdad
|
||||
}
|
||||
|
||||
static inline int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
|
||||
enum qca_btsoc_type soc_type, u32 soc_ver)
|
||||
enum qca_btsoc_type soc_type, u32 soc_ver,
|
||||
const char *firmware_name)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define RTL_ROM_LMP_3499 0x3499
|
||||
#define RTL_ROM_LMP_8723A 0x1200
|
||||
#define RTL_ROM_LMP_8723B 0x8723
|
||||
#define RTL_ROM_LMP_8723D 0x8873
|
||||
#define RTL_ROM_LMP_8821A 0x8821
|
||||
#define RTL_ROM_LMP_8761A 0x8761
|
||||
#define RTL_ROM_LMP_8822B 0x8822
|
||||
@ -107,6 +108,13 @@ static const struct id_table ic_id_table[] = {
|
||||
.fw_name = "rtl_bt/rtl8723ds_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723ds_config" },
|
||||
|
||||
/* 8723DU */
|
||||
{ IC_INFO(RTL_ROM_LMP_8723D, 0x826C),
|
||||
.config_needed = true,
|
||||
.has_rom_version = true,
|
||||
.fw_name = "rtl_bt/rtl8723d_fw.bin",
|
||||
.cfg_name = "rtl_bt/rtl8723d_config" },
|
||||
|
||||
/* 8821A */
|
||||
{ IC_INFO(RTL_ROM_LMP_8821A, 0xa),
|
||||
.config_needed = false,
|
||||
@ -637,6 +645,26 @@ int btrtl_setup_realtek(struct hci_dev *hdev)
|
||||
}
|
||||
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) {
|
||||
|
@ -55,6 +55,7 @@ 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,
|
||||
@ -83,6 +84,11 @@ 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,
|
||||
|
@ -286,6 +286,7 @@ static int btsdio_probe(struct sdio_func *func,
|
||||
switch (func->device) {
|
||||
case SDIO_DEVICE_ID_BROADCOM_43341:
|
||||
case SDIO_DEVICE_ID_BROADCOM_43430:
|
||||
case SDIO_DEVICE_ID_BROADCOM_4356:
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/quirks.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/suspend.h>
|
||||
@ -55,6 +56,7 @@ static struct usb_driver btusb_driver;
|
||||
#define BTUSB_BCM2045 0x40000
|
||||
#define BTUSB_IFNUM_2 0x80000
|
||||
#define BTUSB_CW6622 0x100000
|
||||
#define BTUSB_MEDIATEK 0x200000
|
||||
|
||||
static const struct usb_device_id btusb_table[] = {
|
||||
/* Generic Bluetooth USB device */
|
||||
@ -264,7 +266,9 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x04ca, 0x3015), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x04ca, 0x3016), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x04ca, 0x301a), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x13d3, 0x3491), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x13d3, 0x3496), .driver_info = BTUSB_QCA_ROME },
|
||||
{ USB_DEVICE(0x13d3, 0x3501), .driver_info = BTUSB_QCA_ROME },
|
||||
|
||||
/* Broadcom BCM2035 */
|
||||
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
|
||||
@ -346,6 +350,10 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
|
||||
.driver_info = BTUSB_REALTEK },
|
||||
|
||||
/* MediaTek Bluetooth devices */
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0e8d, 0xe0, 0x01, 0x01),
|
||||
.driver_info = BTUSB_MEDIATEK },
|
||||
|
||||
/* Additional Realtek 8723AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x13d3, 0x3394), .driver_info = BTUSB_REALTEK },
|
||||
@ -426,6 +434,7 @@ static const struct dmi_system_id btusb_needs_reset_resume_table[] = {
|
||||
#define BTUSB_DIAG_RUNNING 10
|
||||
#define BTUSB_OOB_WAKE_ENABLED 11
|
||||
#define BTUSB_HW_RESET_ACTIVE 12
|
||||
#define BTUSB_TX_WAIT_VND_EVT 13
|
||||
|
||||
struct btusb_data {
|
||||
struct hci_dev *hdev;
|
||||
@ -449,6 +458,7 @@ struct btusb_data {
|
||||
struct usb_anchor bulk_anchor;
|
||||
struct usb_anchor isoc_anchor;
|
||||
struct usb_anchor diag_anchor;
|
||||
struct usb_anchor ctrl_anchor;
|
||||
spinlock_t rxlock;
|
||||
|
||||
struct sk_buff *evt_skb;
|
||||
@ -1202,6 +1212,7 @@ static void btusb_stop_traffic(struct btusb_data *data)
|
||||
usb_kill_anchored_urbs(&data->bulk_anchor);
|
||||
usb_kill_anchored_urbs(&data->isoc_anchor);
|
||||
usb_kill_anchored_urbs(&data->diag_anchor);
|
||||
usb_kill_anchored_urbs(&data->ctrl_anchor);
|
||||
}
|
||||
|
||||
static int btusb_close(struct hci_dev *hdev)
|
||||
@ -2437,6 +2448,568 @@ static int btusb_shutdown_intel_new(struct hci_dev *hdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BT_HCIBTUSB_MTK
|
||||
|
||||
#define FIRMWARE_MT7663 "mediatek/mt7663pr2h.bin"
|
||||
#define FIRMWARE_MT7668 "mediatek/mt7668pr2h.bin"
|
||||
|
||||
#define HCI_WMT_MAX_EVENT_SIZE 64
|
||||
|
||||
enum {
|
||||
BTMTK_WMT_PATCH_DWNLD = 0x1,
|
||||
BTMTK_WMT_FUNC_CTRL = 0x6,
|
||||
BTMTK_WMT_RST = 0x7,
|
||||
BTMTK_WMT_SEMAPHORE = 0x17,
|
||||
};
|
||||
|
||||
enum {
|
||||
BTMTK_WMT_INVALID,
|
||||
BTMTK_WMT_PATCH_UNDONE,
|
||||
BTMTK_WMT_PATCH_DONE,
|
||||
BTMTK_WMT_ON_UNDONE,
|
||||
BTMTK_WMT_ON_DONE,
|
||||
BTMTK_WMT_ON_PROGRESS,
|
||||
};
|
||||
|
||||
struct btmtk_wmt_hdr {
|
||||
u8 dir;
|
||||
u8 op;
|
||||
__le16 dlen;
|
||||
u8 flag;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_cmd {
|
||||
struct btmtk_wmt_hdr hdr;
|
||||
u8 data[256];
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_evt {
|
||||
struct hci_event_hdr hhdr;
|
||||
struct btmtk_wmt_hdr whdr;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_evt_funcc {
|
||||
struct btmtk_hci_wmt_evt hwhdr;
|
||||
__be16 status;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_tci_sleep {
|
||||
u8 mode;
|
||||
__le16 duration;
|
||||
__le16 host_duration;
|
||||
u8 host_wakeup_pin;
|
||||
u8 time_compensation;
|
||||
} __packed;
|
||||
|
||||
struct btmtk_hci_wmt_params {
|
||||
u8 op;
|
||||
u8 flag;
|
||||
u16 dlen;
|
||||
const void *data;
|
||||
u32 *status;
|
||||
};
|
||||
|
||||
static void btusb_mtk_wmt_recv(struct urb *urb)
|
||||
{
|
||||
struct hci_dev *hdev = urb->context;
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
struct hci_event_hdr *hdr;
|
||||
struct sk_buff *skb;
|
||||
int err;
|
||||
|
||||
if (urb->status == 0 && urb->actual_length > 0) {
|
||||
hdev->stat.byte_rx += urb->actual_length;
|
||||
|
||||
/* WMT event shouldn't be fragmented and the size should be
|
||||
* less than HCI_WMT_MAX_EVENT_SIZE.
|
||||
*/
|
||||
skb = bt_skb_alloc(HCI_WMT_MAX_EVENT_SIZE, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
hdev->stat.err_rx++;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
|
||||
skb_put_data(skb, urb->transfer_buffer, urb->actual_length);
|
||||
|
||||
hdr = (void *)skb->data;
|
||||
/* 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.
|
||||
*/
|
||||
hdr->evt = 0xff;
|
||||
|
||||
/* When someone waits for the WMT event, the skb is being cloned
|
||||
* and being processed the events from there then.
|
||||
*/
|
||||
if (test_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags)) {
|
||||
data->evt_skb = skb_clone(skb, GFP_KERNEL);
|
||||
if (!data->evt_skb)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
err = hci_recv_frame(hdev, skb);
|
||||
if (err < 0)
|
||||
goto err_free_skb;
|
||||
|
||||
if (test_and_clear_bit(BTUSB_TX_WAIT_VND_EVT,
|
||||
&data->flags)) {
|
||||
/* Barrier to sync with other CPUs */
|
||||
smp_mb__after_atomic();
|
||||
wake_up_bit(&data->flags,
|
||||
BTUSB_TX_WAIT_VND_EVT);
|
||||
}
|
||||
err_out:
|
||||
return;
|
||||
err_free_skb:
|
||||
kfree_skb(data->evt_skb);
|
||||
data->evt_skb = NULL;
|
||||
return;
|
||||
} else if (urb->status == -ENOENT) {
|
||||
/* Avoid suspend failed when usb_kill_urb */
|
||||
return;
|
||||
}
|
||||
|
||||
usb_mark_last_busy(data->udev);
|
||||
|
||||
/* The URB complete handler is still called with urb->actual_length = 0
|
||||
* when the event is not available, so we should keep re-submitting
|
||||
* URB until WMT event returns, Also, It's necessary to wait some time
|
||||
* between the two consecutive control URBs to relax the target device
|
||||
* to generate the event. Otherwise, the WMT event cannot return from
|
||||
* the device successfully.
|
||||
*/
|
||||
udelay(100);
|
||||
|
||||
usb_anchor_urb(urb, &data->ctrl_anchor);
|
||||
err = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
/* -EPERM: urb is being killed;
|
||||
* -ENODEV: device got disconnected
|
||||
*/
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
bt_dev_err(hdev, "urb %p failed to resubmit (%d)",
|
||||
urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
}
|
||||
|
||||
static int btusb_mtk_submit_wmt_recv_urb(struct hci_dev *hdev)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
struct usb_ctrlrequest *dr;
|
||||
unsigned char *buf;
|
||||
int err, size = 64;
|
||||
unsigned int pipe;
|
||||
struct urb *urb;
|
||||
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!urb)
|
||||
return -ENOMEM;
|
||||
|
||||
dr = kmalloc(sizeof(*dr), GFP_KERNEL);
|
||||
if (!dr) {
|
||||
usb_free_urb(urb);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dr->bRequestType = USB_TYPE_VENDOR | USB_DIR_IN;
|
||||
dr->bRequest = 1;
|
||||
dr->wIndex = cpu_to_le16(0);
|
||||
dr->wValue = cpu_to_le16(48);
|
||||
dr->wLength = cpu_to_le16(size);
|
||||
|
||||
buf = kmalloc(size, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
kfree(dr);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
pipe = usb_rcvctrlpipe(data->udev, 0);
|
||||
|
||||
usb_fill_control_urb(urb, data->udev, pipe, (void *)dr,
|
||||
buf, size, btusb_mtk_wmt_recv, hdev);
|
||||
|
||||
urb->transfer_flags |= URB_FREE_BUFFER;
|
||||
|
||||
usb_anchor_urb(urb, &data->ctrl_anchor);
|
||||
err = usb_submit_urb(urb, GFP_KERNEL);
|
||||
if (err < 0) {
|
||||
if (err != -EPERM && err != -ENODEV)
|
||||
bt_dev_err(hdev, "urb %p submission failed (%d)",
|
||||
urb, -err);
|
||||
usb_unanchor_urb(urb);
|
||||
}
|
||||
|
||||
usb_free_urb(urb);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
|
||||
struct btmtk_hci_wmt_params *wmt_params)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
struct btmtk_hci_wmt_evt_funcc *wmt_evt_funcc;
|
||||
u32 hlen, status = BTMTK_WMT_INVALID;
|
||||
struct btmtk_hci_wmt_evt *wmt_evt;
|
||||
struct btmtk_hci_wmt_cmd wc;
|
||||
struct btmtk_wmt_hdr *hdr;
|
||||
int err;
|
||||
|
||||
/* Submit control IN URB on demand to process the WMT event */
|
||||
err = btusb_mtk_submit_wmt_recv_urb(hdev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Send the WMT command and wait until the WMT event returns */
|
||||
hlen = sizeof(*hdr) + wmt_params->dlen;
|
||||
if (hlen > 255)
|
||||
return -EINVAL;
|
||||
|
||||
hdr = (struct btmtk_wmt_hdr *)&wc;
|
||||
hdr->dir = 1;
|
||||
hdr->op = wmt_params->op;
|
||||
hdr->dlen = cpu_to_le16(wmt_params->dlen + 1);
|
||||
hdr->flag = wmt_params->flag;
|
||||
memcpy(wc.data, wmt_params->data, wmt_params->dlen);
|
||||
|
||||
set_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
|
||||
|
||||
err = __hci_cmd_send(hdev, 0xfc6f, hlen, &wc);
|
||||
|
||||
if (err < 0) {
|
||||
clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* The vendor specific WMT commands are all answered by a vendor
|
||||
* specific event and will have the Command Status or Command
|
||||
* Complete as with usual HCI command flow control.
|
||||
*
|
||||
* After sending the command, wait for BTUSB_TX_WAIT_VND_EVT
|
||||
* state to be cleared. The driver specific event receive routine
|
||||
* will clear that state and with that indicate completion of the
|
||||
* WMT command.
|
||||
*/
|
||||
err = wait_on_bit_timeout(&data->flags, BTUSB_TX_WAIT_VND_EVT,
|
||||
TASK_INTERRUPTIBLE, HCI_INIT_TIMEOUT);
|
||||
if (err == -EINTR) {
|
||||
bt_dev_err(hdev, "Execution of wmt command interrupted");
|
||||
clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Execution of wmt command timed out");
|
||||
clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Parse and handle the return WMT event */
|
||||
wmt_evt = (struct btmtk_hci_wmt_evt *)data->evt_skb->data;
|
||||
if (wmt_evt->whdr.op != hdr->op) {
|
||||
bt_dev_err(hdev, "Wrong op received %d expected %d",
|
||||
wmt_evt->whdr.op, hdr->op);
|
||||
err = -EIO;
|
||||
goto err_free_skb;
|
||||
}
|
||||
|
||||
switch (wmt_evt->whdr.op) {
|
||||
case BTMTK_WMT_SEMAPHORE:
|
||||
if (wmt_evt->whdr.flag == 2)
|
||||
status = BTMTK_WMT_PATCH_UNDONE;
|
||||
else
|
||||
status = BTMTK_WMT_PATCH_DONE;
|
||||
break;
|
||||
case BTMTK_WMT_FUNC_CTRL:
|
||||
wmt_evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)wmt_evt;
|
||||
if (be16_to_cpu(wmt_evt_funcc->status) == 0x404)
|
||||
status = BTMTK_WMT_ON_DONE;
|
||||
else if (be16_to_cpu(wmt_evt_funcc->status) == 0x420)
|
||||
status = BTMTK_WMT_ON_PROGRESS;
|
||||
else
|
||||
status = BTMTK_WMT_ON_UNDONE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (wmt_params->status)
|
||||
*wmt_params->status = status;
|
||||
|
||||
err_free_skb:
|
||||
kfree_skb(data->evt_skb);
|
||||
data->evt_skb = NULL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btusb_mtk_setup_firmware(struct hci_dev *hdev, const char *fwname)
|
||||
{
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
const struct firmware *fw;
|
||||
const u8 *fw_ptr;
|
||||
size_t fw_size;
|
||||
int err, dlen;
|
||||
u8 flag;
|
||||
|
||||
err = request_firmware(&fw, fwname, &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)
|
||||
goto err_release_fw;
|
||||
|
||||
fw_size -= 30;
|
||||
fw_ptr += 30;
|
||||
flag = 1;
|
||||
|
||||
wmt_params.op = BTMTK_WMT_PATCH_DWNLD;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
while (fw_size > 0) {
|
||||
dlen = min_t(int, 250, fw_size);
|
||||
|
||||
/* Tell deivice the position in sequence */
|
||||
if (fw_size - dlen <= 0)
|
||||
flag = 3;
|
||||
else if (fw_size < fw->size - 30)
|
||||
flag = 2;
|
||||
|
||||
wmt_params.flag = flag;
|
||||
wmt_params.dlen = dlen;
|
||||
wmt_params.data = fw_ptr;
|
||||
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
|
||||
err);
|
||||
goto err_release_fw;
|
||||
}
|
||||
|
||||
fw_size -= dlen;
|
||||
fw_ptr += dlen;
|
||||
}
|
||||
|
||||
wmt_params.op = BTMTK_WMT_RST;
|
||||
wmt_params.flag = 4;
|
||||
wmt_params.dlen = 0;
|
||||
wmt_params.data = NULL;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
/* Activate funciton the firmware providing to */
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt rst (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Wait a few moments for firmware activation done */
|
||||
usleep_range(10000, 12000);
|
||||
|
||||
err_release_fw:
|
||||
release_firmware(fw);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btusb_mtk_func_query(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
int status, err;
|
||||
u8 param = 0;
|
||||
|
||||
/* Query whether the function is enabled */
|
||||
wmt_params.op = BTMTK_WMT_FUNC_CTRL;
|
||||
wmt_params.flag = 4;
|
||||
wmt_params.dlen = sizeof(param);
|
||||
wmt_params.data = ¶m;
|
||||
wmt_params.status = &status;
|
||||
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to query function status (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int btusb_mtk_reg_read(struct btusb_data *data, u32 reg, u32 *val)
|
||||
{
|
||||
int pipe, err, size = sizeof(u32);
|
||||
void *buf;
|
||||
|
||||
buf = kzalloc(size, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
pipe = usb_rcvctrlpipe(data->udev, 0);
|
||||
err = usb_control_msg(data->udev, pipe, 0x63,
|
||||
USB_TYPE_VENDOR | USB_DIR_IN,
|
||||
reg >> 16, reg & 0xffff,
|
||||
buf, size, USB_CTRL_SET_TIMEOUT);
|
||||
if (err < 0)
|
||||
goto err_free_buf;
|
||||
|
||||
*val = get_unaligned_le32(buf);
|
||||
|
||||
err_free_buf:
|
||||
kfree(buf);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int btusb_mtk_id_get(struct btusb_data *data, u32 *id)
|
||||
{
|
||||
return btusb_mtk_reg_read(data, 0x80000008, id);
|
||||
}
|
||||
|
||||
static int btusb_mtk_setup(struct hci_dev *hdev)
|
||||
{
|
||||
struct btusb_data *data = hci_get_drvdata(hdev);
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
ktime_t calltime, delta, rettime;
|
||||
struct btmtk_tci_sleep tci_sleep;
|
||||
unsigned long long duration;
|
||||
struct sk_buff *skb;
|
||||
const char *fwname;
|
||||
int err, status;
|
||||
u32 dev_id;
|
||||
u8 param;
|
||||
|
||||
calltime = ktime_get();
|
||||
|
||||
err = btusb_mtk_id_get(data, &dev_id);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to get device id (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (dev_id) {
|
||||
case 0x7663:
|
||||
fwname = FIRMWARE_MT7663;
|
||||
break;
|
||||
case 0x7668:
|
||||
fwname = FIRMWARE_MT7668;
|
||||
break;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unsupported support hardware variant (%08x)",
|
||||
dev_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Query whether the firmware is already download */
|
||||
wmt_params.op = BTMTK_WMT_SEMAPHORE;
|
||||
wmt_params.flag = 1;
|
||||
wmt_params.dlen = 0;
|
||||
wmt_params.data = NULL;
|
||||
wmt_params.status = &status;
|
||||
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to query firmware status (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (status == BTMTK_WMT_PATCH_DONE) {
|
||||
bt_dev_info(hdev, "firmware already downloaded");
|
||||
goto ignore_setup_fw;
|
||||
}
|
||||
|
||||
/* Setup a firmware which the device definitely requires */
|
||||
err = btusb_mtk_setup_firmware(hdev, fwname);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
ignore_setup_fw:
|
||||
err = readx_poll_timeout(btusb_mtk_func_query, hdev, status,
|
||||
status < 0 || status != BTMTK_WMT_ON_PROGRESS,
|
||||
2000, 5000000);
|
||||
/* -ETIMEDOUT happens */
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* The other errors happen in btusb_mtk_func_query */
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
if (status == BTMTK_WMT_ON_DONE) {
|
||||
bt_dev_info(hdev, "function already on");
|
||||
goto ignore_func_on;
|
||||
}
|
||||
|
||||
/* Enable Bluetooth protocol */
|
||||
param = 1;
|
||||
wmt_params.op = BTMTK_WMT_FUNC_CTRL;
|
||||
wmt_params.flag = 0;
|
||||
wmt_params.dlen = sizeof(param);
|
||||
wmt_params.data = ¶m;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
ignore_func_on:
|
||||
/* Apply the low power environment setup */
|
||||
tci_sleep.mode = 0x5;
|
||||
tci_sleep.duration = cpu_to_le16(0x640);
|
||||
tci_sleep.host_duration = cpu_to_le16(0x640);
|
||||
tci_sleep.host_wakeup_pin = 0;
|
||||
tci_sleep.time_compensation = 0;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc7a, sizeof(tci_sleep), &tci_sleep,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
err = PTR_ERR(skb);
|
||||
bt_dev_err(hdev, "Failed to apply low power setting (%d)", err);
|
||||
return err;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
rettime = ktime_get();
|
||||
delta = ktime_sub(rettime, calltime);
|
||||
duration = (unsigned long long)ktime_to_ns(delta) >> 10;
|
||||
|
||||
bt_dev_info(hdev, "Device setup in %llu usecs", duration);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btusb_mtk_shutdown(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmtk_hci_wmt_params wmt_params;
|
||||
u8 param = 0;
|
||||
int err;
|
||||
|
||||
/* Disable the device */
|
||||
wmt_params.op = BTMTK_WMT_FUNC_CTRL;
|
||||
wmt_params.flag = 0;
|
||||
wmt_params.dlen = sizeof(param);
|
||||
wmt_params.data = ¶m;
|
||||
wmt_params.status = NULL;
|
||||
|
||||
err = btusb_mtk_hci_wmt_sync(hdev, &wmt_params);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7663);
|
||||
MODULE_FIRMWARE(FIRMWARE_MT7668);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/* Configure an out-of-band gpio as wake-up pin, if specified in device tree */
|
||||
static int marvell_config_oob_wake(struct hci_dev *hdev)
|
||||
@ -3044,6 +3617,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
init_usb_anchor(&data->bulk_anchor);
|
||||
init_usb_anchor(&data->isoc_anchor);
|
||||
init_usb_anchor(&data->diag_anchor);
|
||||
init_usb_anchor(&data->ctrl_anchor);
|
||||
spin_lock_init(&data->rxlock);
|
||||
|
||||
if (id->driver_info & BTUSB_INTEL_NEW) {
|
||||
@ -3157,6 +3731,15 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
if (id->driver_info & BTUSB_MARVELL)
|
||||
hdev->set_bdaddr = btusb_set_bdaddr_marvell;
|
||||
|
||||
#ifdef CONFIG_BT_HCIBTUSB_MTK
|
||||
if (id->driver_info & BTUSB_MEDIATEK) {
|
||||
hdev->setup = btusb_mtk_setup;
|
||||
hdev->shutdown = btusb_mtk_shutdown;
|
||||
hdev->manufacturer = 70;
|
||||
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (id->driver_info & BTUSB_SWAVE) {
|
||||
set_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
|
||||
@ -3184,6 +3767,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
#ifdef CONFIG_BT_HCIBTUSB_RTL
|
||||
if (id->driver_info & BTUSB_REALTEK) {
|
||||
hdev->setup = btrtl_setup_realtek;
|
||||
hdev->shutdown = btrtl_shutdown_realtek;
|
||||
|
||||
/* Realtek devices lose their updated firmware over suspend,
|
||||
* but the USB hub doesn't notice any status change.
|
||||
|
@ -744,6 +744,11 @@ static int bcsp_close(struct hci_uart *hu)
|
||||
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;
|
||||
}
|
||||
|
@ -178,6 +178,7 @@ static void hci_uart_write_work(struct work_struct *work)
|
||||
goto restart;
|
||||
|
||||
clear_bit(HCI_UART_SENDING, &hu->tx_state);
|
||||
wake_up_bit(&hu->tx_state, HCI_UART_SENDING);
|
||||
}
|
||||
|
||||
void hci_uart_init_work(struct work_struct *work)
|
||||
@ -213,6 +214,13 @@ int hci_uart_init_ready(struct hci_uart *hu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hci_uart_wait_until_sent(struct hci_uart *hu)
|
||||
{
|
||||
return wait_on_bit_timeout(&hu->tx_state, HCI_UART_SENDING,
|
||||
TASK_INTERRUPTIBLE,
|
||||
msecs_to_jiffies(2000));
|
||||
}
|
||||
|
||||
/* ------- Interface to HCI layer ------ */
|
||||
/* Reset device */
|
||||
static int hci_uart_flush(struct hci_dev *hdev)
|
||||
|
@ -128,6 +128,7 @@ static int ll_open(struct hci_uart *hu)
|
||||
|
||||
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);
|
||||
}
|
||||
@ -162,6 +163,7 @@ static int ll_close(struct hci_uart *hu)
|
||||
|
||||
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);
|
||||
@ -227,7 +229,8 @@ static void ll_device_want_to_wakeup(struct hci_uart *hu)
|
||||
break;
|
||||
default:
|
||||
/* any other state is illegal */
|
||||
BT_ERR("received HCILL_WAKE_UP_IND in state %ld", ll->hcill_state);
|
||||
BT_ERR("received HCILL_WAKE_UP_IND in state %ld",
|
||||
ll->hcill_state);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -256,7 +259,8 @@ static void ll_device_want_to_sleep(struct hci_uart *hu)
|
||||
|
||||
/* sanity check */
|
||||
if (ll->hcill_state != HCILL_AWAKE)
|
||||
BT_ERR("ERR: HCILL_GO_TO_SLEEP_IND in state %ld", ll->hcill_state);
|
||||
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) {
|
||||
@ -289,7 +293,8 @@ static void ll_device_woke_up(struct hci_uart *hu)
|
||||
|
||||
/* sanity check */
|
||||
if (ll->hcill_state != HCILL_ASLEEP_TO_AWAKE)
|
||||
BT_ERR("received HCILL_WAKE_UP_ACK in state %ld", ll->hcill_state);
|
||||
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);
|
||||
@ -338,7 +343,8 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||
skb_queue_tail(&ll->tx_wait_q, skb);
|
||||
break;
|
||||
default:
|
||||
BT_ERR("illegal hcill state: %ld (losing packet)", ll->hcill_state);
|
||||
BT_ERR("illegal hcill state: %ld (losing packet)",
|
||||
ll->hcill_state);
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
}
|
||||
@ -438,6 +444,7 @@ static int ll_recv(struct hci_uart *hu, const void *data, int count)
|
||||
static struct sk_buff *ll_dequeue(struct hci_uart *hu)
|
||||
{
|
||||
struct ll_struct *ll = hu->priv;
|
||||
|
||||
return skb_dequeue(&ll->txq);
|
||||
}
|
||||
|
||||
@ -449,7 +456,8 @@ static int read_local_version(struct hci_dev *hdev)
|
||||
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);
|
||||
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));
|
||||
@ -469,11 +477,38 @@ static int read_local_version(struct hci_dev *hdev)
|
||||
version = le16_to_cpu(ver->lmp_subver);
|
||||
|
||||
out:
|
||||
if (err) bt_dev_err(hdev, "Failed to read TI version info: %d", err);
|
||||
if (err)
|
||||
bt_dev_err(hdev, "Failed to read TI version info: %d", err);
|
||||
kfree_skb(skb);
|
||||
return err ? err : version;
|
||||
}
|
||||
|
||||
static int send_command_from_firmware(struct ll_device *lldev,
|
||||
struct hci_command *cmd)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
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");
|
||||
return 0;
|
||||
}
|
||||
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");
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* download_firmware -
|
||||
* internal function which parses through the .bts firmware
|
||||
@ -486,7 +521,6 @@ static int download_firmware(struct ll_device *lldev)
|
||||
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);
|
||||
@ -528,23 +562,9 @@ static int download_firmware(struct ll_device *lldev)
|
||||
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);
|
||||
err = send_command_from_firmware(lldev, cmd);
|
||||
if (err)
|
||||
goto out_rel_fw;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
case ACTION_WAIT_EVENT: /* wait */
|
||||
/* no need to wait as command was synchronous */
|
||||
@ -601,6 +621,13 @@ static int ll_setup(struct hci_uart *hu)
|
||||
|
||||
serdev_device_set_flow_control(serdev, true);
|
||||
|
||||
if (hu->oper_speed)
|
||||
speed = hu->oper_speed;
|
||||
else if (hu->proto->oper_speed)
|
||||
speed = hu->proto->oper_speed;
|
||||
else
|
||||
speed = 0;
|
||||
|
||||
do {
|
||||
/* Reset the Bluetooth device */
|
||||
gpiod_set_value_cansleep(lldev->enable_gpio, 0);
|
||||
@ -612,6 +639,20 @@ static int ll_setup(struct hci_uart *hu)
|
||||
return err;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
err = download_firmware(lldev);
|
||||
if (!err)
|
||||
break;
|
||||
@ -636,25 +677,7 @@ static int ll_setup(struct hci_uart *hu)
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
@ -676,7 +699,9 @@ static int hci_ti_probe(struct serdev_device *serdev)
|
||||
serdev_device_set_drvdata(serdev, lldev);
|
||||
lldev->serdev = hu->serdev = serdev;
|
||||
|
||||
lldev->enable_gpio = devm_gpiod_get_optional(&serdev->dev, "enable", GPIOD_OUT_LOW);
|
||||
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);
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/serdev.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
@ -40,6 +42,10 @@ struct mrvl_data {
|
||||
u8 id, rev;
|
||||
};
|
||||
|
||||
struct mrvl_serdev {
|
||||
struct hci_uart hu;
|
||||
};
|
||||
|
||||
struct hci_mrvl_pkt {
|
||||
__le16 lhs;
|
||||
__le16 rhs;
|
||||
@ -49,6 +55,7 @@ struct hci_mrvl_pkt {
|
||||
static int mrvl_open(struct hci_uart *hu)
|
||||
{
|
||||
struct mrvl_data *mrvl;
|
||||
int ret;
|
||||
|
||||
BT_DBG("hu %p", hu);
|
||||
|
||||
@ -62,7 +69,18 @@ static int mrvl_open(struct hci_uart *hu)
|
||||
set_bit(STATE_CHIP_VER_PENDING, &mrvl->flags);
|
||||
|
||||
hu->priv = mrvl;
|
||||
|
||||
if (hu->serdev) {
|
||||
ret = serdev_device_open(hu->serdev);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
kfree(mrvl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mrvl_close(struct hci_uart *hu)
|
||||
@ -71,6 +89,9 @@ static int mrvl_close(struct hci_uart *hu)
|
||||
|
||||
BT_DBG("hu %p", hu);
|
||||
|
||||
if (hu->serdev)
|
||||
serdev_device_close(hu->serdev);
|
||||
|
||||
skb_queue_purge(&mrvl->txq);
|
||||
skb_queue_purge(&mrvl->rawq);
|
||||
kfree_skb(mrvl->rx_skb);
|
||||
@ -339,7 +360,14 @@ static int mrvl_setup(struct hci_uart *hu)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hci_uart_set_baudrate(hu, 3000000);
|
||||
/* Let the final ack go out before switching the baudrate */
|
||||
hci_uart_wait_until_sent(hu);
|
||||
|
||||
if (hu->serdev)
|
||||
serdev_device_set_baudrate(hu->serdev, 3000000);
|
||||
else
|
||||
hci_uart_set_baudrate(hu, 3000000);
|
||||
|
||||
hci_uart_set_flow_control(hu, false);
|
||||
|
||||
err = mrvl_load_firmware(hu->hdev, "mrvl/uart8897_bt.bin");
|
||||
@ -362,12 +390,54 @@ static const struct hci_uart_proto mrvl_proto = {
|
||||
.dequeue = mrvl_dequeue,
|
||||
};
|
||||
|
||||
static int mrvl_serdev_probe(struct serdev_device *serdev)
|
||||
{
|
||||
struct mrvl_serdev *mrvldev;
|
||||
|
||||
mrvldev = devm_kzalloc(&serdev->dev, sizeof(*mrvldev), GFP_KERNEL);
|
||||
if (!mrvldev)
|
||||
return -ENOMEM;
|
||||
|
||||
mrvldev->hu.serdev = serdev;
|
||||
serdev_device_set_drvdata(serdev, mrvldev);
|
||||
|
||||
return hci_uart_register_device(&mrvldev->hu, &mrvl_proto);
|
||||
}
|
||||
|
||||
static void mrvl_serdev_remove(struct serdev_device *serdev)
|
||||
{
|
||||
struct mrvl_serdev *mrvldev = serdev_device_get_drvdata(serdev);
|
||||
|
||||
hci_uart_unregister_device(&mrvldev->hu);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id mrvl_bluetooth_of_match[] = {
|
||||
{ .compatible = "mrvl,88w8897" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mrvl_bluetooth_of_match);
|
||||
#endif
|
||||
|
||||
static struct serdev_device_driver mrvl_serdev_driver = {
|
||||
.probe = mrvl_serdev_probe,
|
||||
.remove = mrvl_serdev_remove,
|
||||
.driver = {
|
||||
.name = "hci_uart_mrvl",
|
||||
.of_match_table = of_match_ptr(mrvl_bluetooth_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
int __init mrvl_init(void)
|
||||
{
|
||||
serdev_device_driver_register(&mrvl_serdev_driver);
|
||||
|
||||
return hci_uart_register_proto(&mrvl_proto);
|
||||
}
|
||||
|
||||
int __exit mrvl_deinit(void)
|
||||
{
|
||||
serdev_device_driver_unregister(&mrvl_serdev_driver);
|
||||
|
||||
return hci_uart_unregister_proto(&mrvl_proto);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
@ -53,6 +54,7 @@
|
||||
|
||||
enum qca_flags {
|
||||
QCA_IBS_ENABLED,
|
||||
QCA_DROP_VENDOR_EVENT,
|
||||
};
|
||||
|
||||
/* HCI_IBS transmit side sleep protocol states */
|
||||
@ -97,6 +99,7 @@ struct qca_data {
|
||||
struct work_struct ws_rx_vote_off;
|
||||
struct work_struct ws_tx_vote_off;
|
||||
unsigned long flags;
|
||||
struct completion drop_ev_comp;
|
||||
|
||||
/* For debugging purpose */
|
||||
u64 ibs_sent_wacks;
|
||||
@ -156,6 +159,7 @@ struct qca_serdev {
|
||||
struct qca_power *bt_power;
|
||||
u32 init_speed;
|
||||
u32 oper_speed;
|
||||
const char *firmware_name;
|
||||
};
|
||||
|
||||
static int qca_power_setup(struct hci_uart *hu, bool on);
|
||||
@ -177,6 +181,17 @@ static enum qca_btsoc_type qca_soc_type(struct hci_uart *hu)
|
||||
return soc_type;
|
||||
}
|
||||
|
||||
static const char *qca_get_firmware_name(struct hci_uart *hu)
|
||||
{
|
||||
if (hu->serdev) {
|
||||
struct qca_serdev *qsd = serdev_device_get_drvdata(hu->serdev);
|
||||
|
||||
return qsd->firmware_name;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void __serial_clock_on(struct tty_struct *tty)
|
||||
{
|
||||
/* TODO: Some chipset requires to enable UART clock on client
|
||||
@ -478,6 +493,7 @@ static int qca_open(struct hci_uart *hu)
|
||||
INIT_WORK(&qca->ws_tx_vote_off, qca_wq_serial_tx_clock_vote_off);
|
||||
|
||||
qca->hu = hu;
|
||||
init_completion(&qca->drop_ev_comp);
|
||||
|
||||
/* Assume we start with both sides asleep -- extra wakes OK */
|
||||
qca->tx_ibs_state = HCI_IBS_TX_ASLEEP;
|
||||
@ -872,6 +888,35 @@ static int qca_recv_acl_data(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
static int qca_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct qca_data *qca = hu->priv;
|
||||
|
||||
if (test_bit(QCA_DROP_VENDOR_EVENT, &qca->flags)) {
|
||||
struct hci_event_hdr *hdr = (void *)skb->data;
|
||||
|
||||
/* For the WCN3990 the vendor command for a baudrate change
|
||||
* isn't sent as synchronous HCI command, because the
|
||||
* controller sends the corresponding vendor event with the
|
||||
* new baudrate. The event is received and properly decoded
|
||||
* after changing the baudrate of the host port. It needs to
|
||||
* be dropped, otherwise it can be misinterpreted as
|
||||
* response to a later firmware download command (also a
|
||||
* vendor command).
|
||||
*/
|
||||
|
||||
if (hdr->evt == HCI_EV_VENDOR)
|
||||
complete(&qca->drop_ev_comp);
|
||||
|
||||
kfree(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
#define QCA_IBS_SLEEP_IND_EVENT \
|
||||
.type = HCI_IBS_SLEEP_IND, \
|
||||
.hlen = 0, \
|
||||
@ -896,7 +941,7 @@ static int qca_recv_acl_data(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
static const struct h4_recv_pkt qca_recv_pkts[] = {
|
||||
{ H4_RECV_ACL, .recv = qca_recv_acl_data },
|
||||
{ H4_RECV_SCO, .recv = hci_recv_frame },
|
||||
{ H4_RECV_EVENT, .recv = hci_recv_frame },
|
||||
{ H4_RECV_EVENT, .recv = qca_recv_event },
|
||||
{ QCA_IBS_WAKE_IND_EVENT, .recv = qca_ibs_wake_ind },
|
||||
{ QCA_IBS_WAKE_ACK_EVENT, .recv = qca_ibs_wake_ack },
|
||||
{ QCA_IBS_SLEEP_IND_EVENT, .recv = qca_ibs_sleep_ind },
|
||||
@ -1091,6 +1136,7 @@ static int qca_check_speeds(struct hci_uart *hu)
|
||||
static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
|
||||
{
|
||||
unsigned int speed, qca_baudrate;
|
||||
struct qca_data *qca = hu->priv;
|
||||
int ret = 0;
|
||||
|
||||
if (speed_type == QCA_INIT_SPEED) {
|
||||
@ -1110,6 +1156,11 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
|
||||
if (qca_is_wcn399x(soc_type))
|
||||
hci_uart_set_flow_control(hu, true);
|
||||
|
||||
if (soc_type == QCA_WCN3990) {
|
||||
reinit_completion(&qca->drop_ev_comp);
|
||||
set_bit(QCA_DROP_VENDOR_EVENT, &qca->flags);
|
||||
}
|
||||
|
||||
qca_baudrate = qca_get_baudrate_value(speed);
|
||||
bt_dev_dbg(hu->hdev, "Set UART speed to %d", speed);
|
||||
ret = qca_set_baudrate(hu->hdev, qca_baudrate);
|
||||
@ -1121,6 +1172,20 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
|
||||
error:
|
||||
if (qca_is_wcn399x(soc_type))
|
||||
hci_uart_set_flow_control(hu, false);
|
||||
|
||||
if (soc_type == QCA_WCN3990) {
|
||||
/* Wait for the controller to send the vendor event
|
||||
* for the baudrate change command.
|
||||
*/
|
||||
if (!wait_for_completion_timeout(&qca->drop_ev_comp,
|
||||
msecs_to_jiffies(100))) {
|
||||
bt_dev_err(hu->hdev,
|
||||
"Failed to change controller baudrate\n");
|
||||
ret = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
clear_bit(QCA_DROP_VENDOR_EVENT, &qca->flags);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -1182,6 +1247,7 @@ static int qca_setup(struct hci_uart *hu)
|
||||
struct qca_data *qca = hu->priv;
|
||||
unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200;
|
||||
enum qca_btsoc_type soc_type = qca_soc_type(hu);
|
||||
const char *firmware_name = qca_get_firmware_name(hu);
|
||||
int ret;
|
||||
int soc_ver = 0;
|
||||
|
||||
@ -1232,7 +1298,8 @@ static int qca_setup(struct hci_uart *hu)
|
||||
|
||||
bt_dev_info(hdev, "QCA controller version 0x%08x", soc_ver);
|
||||
/* Setup patch / NVM configurations */
|
||||
ret = qca_uart_setup(hdev, qca_baudrate, soc_type, soc_ver);
|
||||
ret = qca_uart_setup(hdev, qca_baudrate, soc_type, soc_ver,
|
||||
firmware_name);
|
||||
if (!ret) {
|
||||
set_bit(QCA_IBS_ENABLED, &qca->flags);
|
||||
qca_debugfs_init(hdev);
|
||||
@ -1426,6 +1493,8 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
qcadev->serdev_hu.serdev = serdev;
|
||||
data = of_device_get_match_data(&serdev->dev);
|
||||
serdev_device_set_drvdata(serdev, qcadev);
|
||||
device_property_read_string(&serdev->dev, "firmware-name",
|
||||
&qcadev->firmware_name);
|
||||
if (data && qca_is_wcn399x(data->soc_type)) {
|
||||
qcadev->btsoc_type = data->soc_type;
|
||||
qcadev->bt_power = devm_kzalloc(&serdev->dev,
|
||||
|
@ -100,6 +100,7 @@ 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_wait_until_sent(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);
|
||||
|
@ -1143,6 +1143,26 @@ struct hci_cp_write_sc_support {
|
||||
__u8 support;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_READ_AUTH_PAYLOAD_TO 0x0c7b
|
||||
struct hci_cp_read_auth_payload_to {
|
||||
__le16 handle;
|
||||
} __packed;
|
||||
struct hci_rp_read_auth_payload_to {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
__le16 timeout;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_WRITE_AUTH_PAYLOAD_TO 0x0c7c
|
||||
struct hci_cp_write_auth_payload_to {
|
||||
__le16 handle;
|
||||
__le16 timeout;
|
||||
} __packed;
|
||||
struct hci_rp_write_auth_payload_to {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_READ_LOCAL_OOB_EXT_DATA 0x0c7d
|
||||
struct hci_rp_read_local_oob_ext_data {
|
||||
__u8 status;
|
||||
|
@ -199,6 +199,8 @@ struct adv_info {
|
||||
/* Default min/max age of connection information (1s/3s) */
|
||||
#define DEFAULT_CONN_INFO_MIN_AGE 1000
|
||||
#define DEFAULT_CONN_INFO_MAX_AGE 3000
|
||||
/* Default authenticated payload timeout 30s */
|
||||
#define DEFAULT_AUTH_PAYLOAD_TIMEOUT 0x0bb8
|
||||
|
||||
struct amp_assoc {
|
||||
__u16 len;
|
||||
@ -275,6 +277,7 @@ struct hci_dev {
|
||||
__u16 discov_interleaved_timeout;
|
||||
__u16 conn_info_min_age;
|
||||
__u16 conn_info_max_age;
|
||||
__u16 auth_payload_timeout;
|
||||
__u8 ssp_debug_mode;
|
||||
__u8 hw_error_code;
|
||||
__u32 clock;
|
||||
@ -481,6 +484,7 @@ struct hci_conn {
|
||||
__u16 disc_timeout;
|
||||
__u16 conn_timeout;
|
||||
__u16 setting;
|
||||
__u16 auth_payload_timeout;
|
||||
__u16 le_conn_min_interval;
|
||||
__u16 le_conn_max_interval;
|
||||
__u16 le_conn_interval;
|
||||
|
@ -18,24 +18,16 @@ extern const struct ndisc_ops lowpan_ndisc_ops;
|
||||
int addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev);
|
||||
|
||||
#ifdef CONFIG_6LOWPAN_DEBUGFS
|
||||
int lowpan_dev_debugfs_init(struct net_device *dev);
|
||||
void lowpan_dev_debugfs_init(struct net_device *dev);
|
||||
void lowpan_dev_debugfs_exit(struct net_device *dev);
|
||||
|
||||
int __init lowpan_debugfs_init(void);
|
||||
void __init lowpan_debugfs_init(void);
|
||||
void lowpan_debugfs_exit(void);
|
||||
#else
|
||||
static inline int lowpan_dev_debugfs_init(struct net_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void lowpan_dev_debugfs_init(struct net_device *dev) { }
|
||||
static inline void lowpan_dev_debugfs_exit(struct net_device *dev) { }
|
||||
|
||||
static inline int __init lowpan_debugfs_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void __init lowpan_debugfs_init(void) { }
|
||||
static inline void lowpan_debugfs_exit(void) { }
|
||||
#endif /* CONFIG_6LOWPAN_DEBUGFS */
|
||||
|
||||
|
@ -42,9 +42,7 @@ int lowpan_register_netdevice(struct net_device *dev,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = lowpan_dev_debugfs_init(dev);
|
||||
if (ret < 0)
|
||||
unregister_netdevice(dev);
|
||||
lowpan_dev_debugfs_init(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -152,9 +150,7 @@ static int __init lowpan_module_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = lowpan_debugfs_init();
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
lowpan_debugfs_init();
|
||||
|
||||
ret = register_netdevice_notifier(&lowpan_notifier);
|
||||
if (ret < 0) {
|
||||
|
@ -163,11 +163,11 @@ static const struct file_operations lowpan_ctx_pfx_fops = {
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int lowpan_dev_debugfs_ctx_init(struct net_device *dev,
|
||||
struct dentry *ctx, u8 id)
|
||||
static void lowpan_dev_debugfs_ctx_init(struct net_device *dev,
|
||||
struct dentry *ctx, u8 id)
|
||||
{
|
||||
struct lowpan_dev *ldev = lowpan_dev(dev);
|
||||
struct dentry *dentry, *root;
|
||||
struct dentry *root;
|
||||
char buf[32];
|
||||
|
||||
WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE);
|
||||
@ -175,34 +175,18 @@ static int lowpan_dev_debugfs_ctx_init(struct net_device *dev,
|
||||
sprintf(buf, "%d", id);
|
||||
|
||||
root = debugfs_create_dir(buf, ctx);
|
||||
if (!root)
|
||||
return -EINVAL;
|
||||
|
||||
dentry = debugfs_create_file_unsafe("active", 0644, root,
|
||||
&ldev->ctx.table[id],
|
||||
&lowpan_ctx_flag_active_fops);
|
||||
if (!dentry)
|
||||
return -EINVAL;
|
||||
debugfs_create_file("active", 0644, root, &ldev->ctx.table[id],
|
||||
&lowpan_ctx_flag_active_fops);
|
||||
|
||||
dentry = debugfs_create_file_unsafe("compression", 0644, root,
|
||||
&ldev->ctx.table[id],
|
||||
&lowpan_ctx_flag_c_fops);
|
||||
if (!dentry)
|
||||
return -EINVAL;
|
||||
debugfs_create_file("compression", 0644, root, &ldev->ctx.table[id],
|
||||
&lowpan_ctx_flag_c_fops);
|
||||
|
||||
dentry = debugfs_create_file("prefix", 0644, root,
|
||||
&ldev->ctx.table[id],
|
||||
&lowpan_ctx_pfx_fops);
|
||||
if (!dentry)
|
||||
return -EINVAL;
|
||||
debugfs_create_file("prefix", 0644, root, &ldev->ctx.table[id],
|
||||
&lowpan_ctx_pfx_fops);
|
||||
|
||||
dentry = debugfs_create_file_unsafe("prefix_len", 0644, root,
|
||||
&ldev->ctx.table[id],
|
||||
&lowpan_ctx_plen_fops);
|
||||
if (!dentry)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
debugfs_create_file("prefix_len", 0644, root, &ldev->ctx.table[id],
|
||||
&lowpan_ctx_plen_fops);
|
||||
}
|
||||
|
||||
static int lowpan_context_show(struct seq_file *file, void *offset)
|
||||
@ -242,64 +226,39 @@ static int lowpan_short_addr_get(void *data, u64 *val)
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(lowpan_short_addr_fops, lowpan_short_addr_get, NULL,
|
||||
"0x%04llx\n");
|
||||
|
||||
static int lowpan_dev_debugfs_802154_init(const struct net_device *dev,
|
||||
static void lowpan_dev_debugfs_802154_init(const struct net_device *dev,
|
||||
struct lowpan_dev *ldev)
|
||||
{
|
||||
struct dentry *dentry, *root;
|
||||
struct dentry *root;
|
||||
|
||||
if (!lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154))
|
||||
return 0;
|
||||
return;
|
||||
|
||||
root = debugfs_create_dir("ieee802154", ldev->iface_debugfs);
|
||||
if (!root)
|
||||
return -EINVAL;
|
||||
|
||||
dentry = debugfs_create_file_unsafe("short_addr", 0444, root,
|
||||
lowpan_802154_dev(dev)->wdev->ieee802154_ptr,
|
||||
&lowpan_short_addr_fops);
|
||||
if (!dentry)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
debugfs_create_file("short_addr", 0444, root,
|
||||
lowpan_802154_dev(dev)->wdev->ieee802154_ptr,
|
||||
&lowpan_short_addr_fops);
|
||||
}
|
||||
|
||||
int lowpan_dev_debugfs_init(struct net_device *dev)
|
||||
void lowpan_dev_debugfs_init(struct net_device *dev)
|
||||
{
|
||||
struct lowpan_dev *ldev = lowpan_dev(dev);
|
||||
struct dentry *contexts, *dentry;
|
||||
int ret, i;
|
||||
struct dentry *contexts;
|
||||
int i;
|
||||
|
||||
/* creating the root */
|
||||
ldev->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
|
||||
if (!ldev->iface_debugfs)
|
||||
goto fail;
|
||||
|
||||
contexts = debugfs_create_dir("contexts", ldev->iface_debugfs);
|
||||
if (!contexts)
|
||||
goto remove_root;
|
||||
|
||||
dentry = debugfs_create_file("show", 0644, contexts,
|
||||
&lowpan_dev(dev)->ctx,
|
||||
&lowpan_context_fops);
|
||||
if (!dentry)
|
||||
goto remove_root;
|
||||
debugfs_create_file("show", 0644, contexts, &lowpan_dev(dev)->ctx,
|
||||
&lowpan_context_fops);
|
||||
|
||||
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
|
||||
ret = lowpan_dev_debugfs_ctx_init(dev, contexts, i);
|
||||
if (ret < 0)
|
||||
goto remove_root;
|
||||
}
|
||||
for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
|
||||
lowpan_dev_debugfs_ctx_init(dev, contexts, i);
|
||||
|
||||
ret = lowpan_dev_debugfs_802154_init(dev, ldev);
|
||||
if (ret < 0)
|
||||
goto remove_root;
|
||||
|
||||
return 0;
|
||||
|
||||
remove_root:
|
||||
lowpan_dev_debugfs_exit(dev);
|
||||
fail:
|
||||
return -EINVAL;
|
||||
lowpan_dev_debugfs_802154_init(dev, ldev);
|
||||
}
|
||||
|
||||
void lowpan_dev_debugfs_exit(struct net_device *dev)
|
||||
@ -307,13 +266,9 @@ void lowpan_dev_debugfs_exit(struct net_device *dev)
|
||||
debugfs_remove_recursive(lowpan_dev(dev)->iface_debugfs);
|
||||
}
|
||||
|
||||
int __init lowpan_debugfs_init(void)
|
||||
void __init lowpan_debugfs_init(void)
|
||||
{
|
||||
lowpan_debugfs = debugfs_create_dir("6lowpan", NULL);
|
||||
if (!lowpan_debugfs)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lowpan_debugfs_exit(void)
|
||||
|
@ -164,26 +164,21 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
|
||||
int count = atomic_read(&dev->peer_count);
|
||||
const struct in6_addr *nexthop;
|
||||
struct lowpan_peer *peer;
|
||||
struct neighbour *neigh;
|
||||
|
||||
BT_DBG("peers %d addr %pI6c rt %p", count, daddr, rt);
|
||||
|
||||
/* If we have multiple 6lowpan peers, then check where we should
|
||||
* send the packet. If only one peer exists, then we can send the
|
||||
* packet right away.
|
||||
*/
|
||||
if (count == 1) {
|
||||
rcu_read_lock();
|
||||
peer = list_first_or_null_rcu(&dev->peers, struct lowpan_peer,
|
||||
list);
|
||||
rcu_read_unlock();
|
||||
return peer;
|
||||
}
|
||||
|
||||
if (!rt) {
|
||||
nexthop = &lowpan_cb(skb)->gw;
|
||||
|
||||
if (ipv6_addr_any(nexthop))
|
||||
return NULL;
|
||||
if (ipv6_addr_any(&lowpan_cb(skb)->gw)) {
|
||||
/* There is neither route nor gateway,
|
||||
* probably the destination is a direct peer.
|
||||
*/
|
||||
nexthop = daddr;
|
||||
} else {
|
||||
/* There is a known gateway
|
||||
*/
|
||||
nexthop = &lowpan_cb(skb)->gw;
|
||||
}
|
||||
} else {
|
||||
nexthop = rt6_nexthop(rt, daddr);
|
||||
|
||||
@ -209,6 +204,20 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_btle_dev *dev,
|
||||
}
|
||||
}
|
||||
|
||||
/* use the neighbour cache for matching addresses assigned by SLAAC
|
||||
*/
|
||||
neigh = __ipv6_neigh_lookup(dev->netdev, nexthop);
|
||||
if (neigh) {
|
||||
list_for_each_entry_rcu(peer, &dev->peers, list) {
|
||||
if (!memcmp(neigh->ha, peer->lladdr, ETH_ALEN)) {
|
||||
neigh_release(neigh);
|
||||
rcu_read_unlock();
|
||||
return peer;
|
||||
}
|
||||
}
|
||||
neigh_release(neigh);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
|
@ -520,6 +520,9 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||
set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
|
||||
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
|
||||
|
||||
/* Set Default Authenticated payload timeout to 30s */
|
||||
conn->auth_payload_timeout = DEFAULT_AUTH_PAYLOAD_TIMEOUT;
|
||||
|
||||
if (conn->role == HCI_ROLE_MASTER)
|
||||
conn->out = true;
|
||||
|
||||
@ -912,7 +915,7 @@ static void hci_req_directed_advertising(struct hci_request *req,
|
||||
sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
__hci_req_enable_ext_advertising(req);
|
||||
__hci_req_enable_ext_advertising(req, 0x00);
|
||||
} else {
|
||||
struct hci_cp_le_set_adv_param cp;
|
||||
|
||||
|
@ -2827,7 +2827,7 @@ int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
|
||||
memset(adv_instance->scan_rsp_data, 0,
|
||||
sizeof(adv_instance->scan_rsp_data));
|
||||
} else {
|
||||
if (hdev->adv_instance_cnt >= HCI_MAX_ADV_INSTANCES ||
|
||||
if (hdev->adv_instance_cnt >= hdev->le_num_of_adv_sets ||
|
||||
instance < 1 || instance > HCI_MAX_ADV_INSTANCES)
|
||||
return -EOVERFLOW;
|
||||
|
||||
@ -3195,11 +3195,13 @@ struct hci_dev *hci_alloc_dev(void)
|
||||
hdev->le_min_key_size = SMP_MIN_ENC_KEY_SIZE;
|
||||
hdev->le_tx_def_phys = HCI_LE_SET_PHY_1M;
|
||||
hdev->le_rx_def_phys = HCI_LE_SET_PHY_1M;
|
||||
hdev->le_num_of_adv_sets = HCI_MAX_ADV_INSTANCES;
|
||||
|
||||
hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT;
|
||||
hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT;
|
||||
hdev->conn_info_min_age = DEFAULT_CONN_INFO_MIN_AGE;
|
||||
hdev->conn_info_max_age = DEFAULT_CONN_INFO_MAX_AGE;
|
||||
hdev->auth_payload_timeout = DEFAULT_AUTH_PAYLOAD_TIMEOUT;
|
||||
|
||||
mutex_init(&hdev->lock);
|
||||
mutex_init(&hdev->req_lock);
|
||||
|
@ -941,6 +941,35 @@ static int adv_max_interval_get(void *data, u64 *val)
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adv_max_interval_fops, adv_max_interval_get,
|
||||
adv_max_interval_set, "%llu\n");
|
||||
|
||||
static int auth_payload_timeout_set(void *data, u64 val)
|
||||
{
|
||||
struct hci_dev *hdev = data;
|
||||
|
||||
if (val < 0x0001 || val > 0xffff)
|
||||
return -EINVAL;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hdev->auth_payload_timeout = val;
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auth_payload_timeout_get(void *data, u64 *val)
|
||||
{
|
||||
struct hci_dev *hdev = data;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
*val = hdev->auth_payload_timeout;
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(auth_payload_timeout_fops,
|
||||
auth_payload_timeout_get,
|
||||
auth_payload_timeout_set, "%llu\n");
|
||||
|
||||
DEFINE_QUIRK_ATTRIBUTE(quirk_strict_duplicate_filter,
|
||||
HCI_QUIRK_STRICT_DUPLICATE_FILTER);
|
||||
DEFINE_QUIRK_ATTRIBUTE(quirk_simultaneous_discovery,
|
||||
@ -994,6 +1023,8 @@ void hci_debugfs_create_le(struct hci_dev *hdev)
|
||||
&adv_max_interval_fops);
|
||||
debugfs_create_u16("discov_interleaved_timeout", 0644, hdev->debugfs,
|
||||
&hdev->discov_interleaved_timeout);
|
||||
debugfs_create_file("auth_payload_timeout", 0644, hdev->debugfs, hdev,
|
||||
&auth_payload_timeout_fops);
|
||||
|
||||
debugfs_create_file("quirk_strict_duplicate_filter", 0644,
|
||||
hdev->debugfs, hdev,
|
||||
|
@ -579,6 +579,51 @@ static void hci_cc_read_local_commands(struct hci_dev *hdev,
|
||||
memcpy(hdev->commands, rp->commands, sizeof(hdev->commands));
|
||||
}
|
||||
|
||||
static void hci_cc_read_auth_payload_timeout(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_read_auth_payload_to *rp = (void *)skb->data;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
|
||||
if (conn)
|
||||
conn->auth_payload_timeout = __le16_to_cpu(rp->timeout);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cc_write_auth_payload_timeout(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_write_auth_payload_to *rp = (void *)skb->data;
|
||||
struct hci_conn *conn;
|
||||
void *sent;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_AUTH_PAYLOAD_TO);
|
||||
if (!sent)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
|
||||
if (conn)
|
||||
conn->auth_payload_timeout = get_unaligned_le16(sent + 2);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_features(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
@ -2975,6 +3020,25 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Set the default Authenticated Payload Timeout after
|
||||
* an LE Link is established. As per Core Spec v5.0, Vol 2, Part B
|
||||
* Section 3.3, the HCI command WRITE_AUTH_PAYLOAD_TIMEOUT should be
|
||||
* sent when the link is active and Encryption is enabled, the conn
|
||||
* type can be either LE or ACL and controller must support LMP Ping.
|
||||
* Ensure for AES-CCM encryption as well.
|
||||
*/
|
||||
if (test_bit(HCI_CONN_ENCRYPT, &conn->flags) &&
|
||||
test_bit(HCI_CONN_AES_CCM, &conn->flags) &&
|
||||
((conn->type == ACL_LINK && lmp_ping_capable(hdev)) ||
|
||||
(conn->type == LE_LINK && (hdev->le_features[0] & HCI_LE_PING)))) {
|
||||
struct hci_cp_write_auth_payload_to cp;
|
||||
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.timeout = cpu_to_le16(hdev->auth_payload_timeout);
|
||||
hci_send_cmd(conn->hdev, HCI_OP_WRITE_AUTH_PAYLOAD_TO,
|
||||
sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
notify:
|
||||
if (conn->state == BT_CONFIG) {
|
||||
if (!ev->status)
|
||||
@ -3170,6 +3234,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
hci_cc_write_sc_support(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_READ_AUTH_PAYLOAD_TO:
|
||||
hci_cc_read_auth_payload_timeout(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_WRITE_AUTH_PAYLOAD_TO:
|
||||
hci_cc_write_auth_payload_timeout(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_READ_LOCAL_VERSION:
|
||||
hci_cc_read_local_version(hdev, skb);
|
||||
break;
|
||||
@ -5588,6 +5660,11 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev,
|
||||
return send_conn_param_neg_reply(hdev, handle,
|
||||
HCI_ERROR_UNKNOWN_CONN_ID);
|
||||
|
||||
if (min < hcon->le_conn_min_interval ||
|
||||
max > hcon->le_conn_max_interval)
|
||||
return send_conn_param_neg_reply(hdev, handle,
|
||||
HCI_ERROR_INVALID_LL_PARAMS);
|
||||
|
||||
if (hci_check_conn_params(min, max, latency, timeout))
|
||||
return send_conn_param_neg_reply(hdev, handle,
|
||||
HCI_ERROR_INVALID_LL_PARAMS);
|
||||
|
@ -1601,7 +1601,7 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
|
||||
cp.own_addr_type = own_addr_type;
|
||||
cp.channel_map = hdev->le_adv_channel_map;
|
||||
cp.tx_power = 127;
|
||||
cp.handle = 0;
|
||||
cp.handle = instance;
|
||||
|
||||
if (flags & MGMT_ADV_FLAG_SEC_2M) {
|
||||
cp.primary_phy = HCI_ADV_PHY_1M;
|
||||
@ -1643,11 +1643,21 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __hci_req_enable_ext_advertising(struct hci_request *req)
|
||||
int __hci_req_enable_ext_advertising(struct hci_request *req, u8 instance)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct hci_cp_le_set_ext_adv_enable *cp;
|
||||
struct hci_cp_ext_adv_set *adv_set;
|
||||
u8 data[sizeof(*cp) + sizeof(*adv_set) * 1];
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
if (instance > 0) {
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
adv_instance = NULL;
|
||||
}
|
||||
|
||||
cp = (void *) data;
|
||||
adv_set = (void *) cp->data;
|
||||
@ -1659,11 +1669,23 @@ void __hci_req_enable_ext_advertising(struct hci_request *req)
|
||||
|
||||
memset(adv_set, 0, sizeof(*adv_set));
|
||||
|
||||
adv_set->handle = 0;
|
||||
adv_set->handle = instance;
|
||||
|
||||
/* Set duration per instance since controller is responsible for
|
||||
* scheduling it.
|
||||
*/
|
||||
if (adv_instance && adv_instance->duration) {
|
||||
u16 duration = adv_instance->duration * MSEC_PER_SEC;
|
||||
|
||||
/* Time = N * 10 ms */
|
||||
adv_set->duration = cpu_to_le16(duration / 10);
|
||||
}
|
||||
|
||||
hci_req_add(req, HCI_OP_LE_SET_EXT_ADV_ENABLE,
|
||||
sizeof(*cp) + sizeof(*adv_set) * cp->num_of_sets,
|
||||
data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __hci_req_start_ext_adv(struct hci_request *req, u8 instance)
|
||||
@ -1679,7 +1701,7 @@ int __hci_req_start_ext_adv(struct hci_request *req, u8 instance)
|
||||
return err;
|
||||
|
||||
__hci_req_update_scan_rsp_data(req, instance);
|
||||
__hci_req_enable_ext_advertising(req);
|
||||
__hci_req_enable_ext_advertising(req, instance);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1723,10 +1745,13 @@ int __hci_req_schedule_adv_instance(struct hci_request *req, u8 instance,
|
||||
adv_instance->remaining_time =
|
||||
adv_instance->remaining_time - timeout;
|
||||
|
||||
hdev->adv_instance_timeout = timeout;
|
||||
queue_delayed_work(hdev->req_workqueue,
|
||||
/* Only use work for scheduling instances with legacy advertising */
|
||||
if (!ext_adv_capable(hdev)) {
|
||||
hdev->adv_instance_timeout = timeout;
|
||||
queue_delayed_work(hdev->req_workqueue,
|
||||
&hdev->adv_instance_expire,
|
||||
msecs_to_jiffies(timeout * 1000));
|
||||
}
|
||||
|
||||
/* If we're just re-scheduling the same instance again then do not
|
||||
* execute any HCI commands. This happens when a single instance is
|
||||
@ -2744,7 +2769,8 @@ static int powered_update_hci(struct hci_request *req, unsigned long opt)
|
||||
if (!ext_adv_capable(hdev))
|
||||
__hci_req_enable_advertising(req);
|
||||
else if (!err)
|
||||
__hci_req_enable_ext_advertising(req);
|
||||
__hci_req_enable_ext_advertising(req,
|
||||
0x00);
|
||||
}
|
||||
} else if (!list_empty(&hdev->adv_instances)) {
|
||||
struct adv_info *adv_instance;
|
||||
|
@ -83,7 +83,7 @@ void hci_req_clear_adv_instance(struct hci_dev *hdev, struct sock *sk,
|
||||
|
||||
int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance);
|
||||
int __hci_req_start_ext_adv(struct hci_request *req, u8 instance);
|
||||
void __hci_req_enable_ext_advertising(struct hci_request *req);
|
||||
int __hci_req_enable_ext_advertising(struct hci_request *req, u8 instance);
|
||||
void __hci_req_clear_ext_adv_sets(struct hci_request *req);
|
||||
int hci_get_random_address(struct hci_dev *hdev, bool require_privacy,
|
||||
bool use_rpa, struct adv_info *adv_instance,
|
||||
|
@ -775,7 +775,7 @@ static int hidp_setup_hid(struct hidp_session *session,
|
||||
hid->version = req->version;
|
||||
hid->country = req->country;
|
||||
|
||||
strncpy(hid->name, req->name, sizeof(hid->name));
|
||||
strscpy(hid->name, req->name, sizeof(hid->name));
|
||||
|
||||
snprintf(hid->phys, sizeof(hid->phys), "%pMR",
|
||||
&l2cap_pi(session->ctrl_sock->sk)->chan->src);
|
||||
|
@ -192,6 +192,7 @@ static int hidp_sock_compat_ioctl(struct socket *sock, unsigned int cmd, unsigne
|
||||
ca.version = ca32.version;
|
||||
ca.flags = ca32.flags;
|
||||
ca.idle_to = ca32.idle_to;
|
||||
ca32.name[sizeof(ca32.name) - 1] = '\0';
|
||||
memcpy(ca.name, ca32.name, 128);
|
||||
|
||||
csock = sockfd_lookup(ca.ctrl_sock, &err);
|
||||
|
@ -168,11 +168,18 @@ static struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn,
|
||||
return c;
|
||||
}
|
||||
|
||||
static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
|
||||
static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src,
|
||||
u8 src_type)
|
||||
{
|
||||
struct l2cap_chan *c;
|
||||
|
||||
list_for_each_entry(c, &chan_list, global_l) {
|
||||
if (src_type == BDADDR_BREDR && c->src_type != BDADDR_BREDR)
|
||||
continue;
|
||||
|
||||
if (src_type != BDADDR_BREDR && c->src_type == BDADDR_BREDR)
|
||||
continue;
|
||||
|
||||
if (c->sport == psm && !bacmp(&c->src, src))
|
||||
return c;
|
||||
}
|
||||
@ -185,7 +192,7 @@ int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm)
|
||||
|
||||
write_lock(&chan_list_lock);
|
||||
|
||||
if (psm && __l2cap_global_chan_by_addr(psm, src)) {
|
||||
if (psm && __l2cap_global_chan_by_addr(psm, src, chan->src_type)) {
|
||||
err = -EADDRINUSE;
|
||||
goto done;
|
||||
}
|
||||
@ -209,7 +216,8 @@ int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm)
|
||||
|
||||
err = -EINVAL;
|
||||
for (p = start; p <= end; p += incr)
|
||||
if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src)) {
|
||||
if (!__l2cap_global_chan_by_addr(cpu_to_le16(p), src,
|
||||
chan->src_type)) {
|
||||
chan->psm = cpu_to_le16(p);
|
||||
chan->sport = cpu_to_le16(p);
|
||||
err = 0;
|
||||
@ -4394,6 +4402,12 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
|
||||
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
if (chan->state != BT_DISCONN) {
|
||||
l2cap_chan_unlock(chan);
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
l2cap_chan_hold(chan);
|
||||
l2cap_chan_del(chan, 0);
|
||||
|
||||
@ -5291,7 +5305,14 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
|
||||
|
||||
memset(&rsp, 0, sizeof(rsp));
|
||||
|
||||
err = hci_check_conn_params(min, max, latency, to_multiplier);
|
||||
if (min < hcon->le_conn_min_interval ||
|
||||
max > hcon->le_conn_max_interval) {
|
||||
BT_DBG("requested connection interval exceeds current bounds.");
|
||||
err = -EINVAL;
|
||||
} else {
|
||||
err = hci_check_conn_params(min, max, latency, to_multiplier);
|
||||
}
|
||||
|
||||
if (err)
|
||||
rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED);
|
||||
else
|
||||
|
@ -2579,6 +2579,19 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
|
||||
goto distribute;
|
||||
}
|
||||
|
||||
/* Drop IRK if peer is using identity address during pairing but is
|
||||
* providing different address as identity information.
|
||||
*
|
||||
* Microsoft Surface Precision Mouse is known to have this bug.
|
||||
*/
|
||||
if (hci_is_identity_address(&hcon->dst, hcon->dst_type) &&
|
||||
(bacmp(&info->bdaddr, &hcon->dst) ||
|
||||
info->addr_type != hcon->dst_type)) {
|
||||
bt_dev_err(hcon->hdev,
|
||||
"ignoring IRK with invalid identity address");
|
||||
goto distribute;
|
||||
}
|
||||
|
||||
bacpy(&smp->id_addr, &info->bdaddr);
|
||||
smp->id_addr_type = info->addr_type;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user