mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 16:30:52 +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 2020-01-26 Here's (probably) the last bluetooth-next pull request for the 5.6 kernel. - Initial pieces of Bluetooth 5.2 Isochronous Channels support - mgmt: Various cleanups and a new Set Blocked Keys command - btusb: Added support for 04ca:3021 QCA_ROME device - hci_qca: Multiple fixes & cleanups - hci_bcm: Fixes & improved device tree support - Fixed attempts to create duplicate debugfs entries 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
c4c57b974d
@ -11,6 +11,7 @@ Required properties:
|
||||
|
||||
- compatible: should contain one of the following:
|
||||
* "brcm,bcm20702a1"
|
||||
* "brcm,bcm4329-bt"
|
||||
* "brcm,bcm4330-bt"
|
||||
* "brcm,bcm43438-bt"
|
||||
* "brcm,bcm4345c5"
|
||||
@ -22,7 +23,9 @@ Optional properties:
|
||||
- max-speed: see Documentation/devicetree/bindings/serial/slave-device.txt
|
||||
- shutdown-gpios: GPIO specifier, used to enable the BT module
|
||||
- device-wakeup-gpios: GPIO specifier, used to wakeup the controller
|
||||
- host-wakeup-gpios: GPIO specifier, used to wakeup the host processor
|
||||
- host-wakeup-gpios: GPIO specifier, used to wakeup the host processor.
|
||||
deprecated, replaced by interrupts and
|
||||
"host-wakeup" interrupt-names
|
||||
- clocks: 1 or 2 clocks as defined in clock-names below, in that order
|
||||
- clock-names: names for clock inputs, matching the clocks given
|
||||
- "extclk": deprecated, replaced by "txco"
|
||||
@ -36,7 +39,8 @@ Optional properties:
|
||||
- pcm-frame-type: short, long
|
||||
- pcm-sync-mode: slave, master
|
||||
- pcm-clock-mode: slave, master
|
||||
|
||||
- interrupts: must be one, used to wakeup the host processor
|
||||
- interrupt-names: must be "host-wakeup"
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -36,6 +36,7 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
|
||||
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;
|
||||
}
|
||||
@ -223,6 +224,7 @@ static int btbcm_reset(struct hci_dev *hdev)
|
||||
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;
|
||||
}
|
||||
|
@ -78,13 +78,13 @@ static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int btbcm_read_pcm_int_params(struct hci_dev *hdev,
|
||||
static inline int btbcm_read_pcm_int_params(struct hci_dev *hdev,
|
||||
struct bcm_set_pcm_int_params *params)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
int btbcm_write_pcm_int_params(struct hci_dev *hdev,
|
||||
static inline int btbcm_write_pcm_int_params(struct hci_dev *hdev,
|
||||
const struct bcm_set_pcm_int_params *params)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
|
@ -370,11 +370,11 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
|
||||
* the end.
|
||||
*/
|
||||
len = patch_length;
|
||||
buf = kmemdup(btrtl_dev->fw_data + patch_offset, patch_length,
|
||||
GFP_KERNEL);
|
||||
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;
|
||||
@ -460,8 +460,10 @@ static int rtl_load_file(struct hci_dev *hdev, const char *name, u8 **buff)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = fw->size;
|
||||
*buff = kmemdup(fw->data, ret, GFP_KERNEL);
|
||||
if (!*buff)
|
||||
*buff = kvmalloc(fw->size, GFP_KERNEL);
|
||||
if (*buff)
|
||||
memcpy(*buff, fw->data, ret);
|
||||
else
|
||||
ret = -ENOMEM;
|
||||
|
||||
release_firmware(fw);
|
||||
@ -499,14 +501,14 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
|
||||
goto out;
|
||||
|
||||
if (btrtl_dev->cfg_len > 0) {
|
||||
tbuff = kzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL);
|
||||
tbuff = kvzalloc(ret + btrtl_dev->cfg_len, GFP_KERNEL);
|
||||
if (!tbuff) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(tbuff, fw_data, ret);
|
||||
kfree(fw_data);
|
||||
kvfree(fw_data);
|
||||
|
||||
memcpy(tbuff + ret, btrtl_dev->cfg_data, btrtl_dev->cfg_len);
|
||||
ret += btrtl_dev->cfg_len;
|
||||
@ -519,14 +521,14 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev,
|
||||
ret = rtl_download_firmware(hdev, fw_data, ret);
|
||||
|
||||
out:
|
||||
kfree(fw_data);
|
||||
kvfree(fw_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void btrtl_free(struct btrtl_device_info *btrtl_dev)
|
||||
{
|
||||
kfree(btrtl_dev->fw_data);
|
||||
kfree(btrtl_dev->cfg_data);
|
||||
kvfree(btrtl_dev->fw_data);
|
||||
kvfree(btrtl_dev->cfg_data);
|
||||
kfree(btrtl_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btrtl_free);
|
||||
|
@ -145,11 +145,20 @@ static int btsdio_rx_packet(struct btsdio_data *data)
|
||||
|
||||
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;
|
||||
switch (hdr[3]) {
|
||||
case HCI_EVENT_PKT:
|
||||
case HCI_ACLDATA_PKT:
|
||||
case HCI_SCODATA_PKT:
|
||||
case HCI_ISODATA_PKT:
|
||||
hci_skb_pkt_type(skb) = hdr[3];
|
||||
err = hci_recv_frame(data->hdev, skb);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sdio_writeb(data->func, 0x00, REG_PC_RRT, NULL);
|
||||
|
||||
|
@ -266,6 +266,7 @@ 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(0x04ca, 0x3021), .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 },
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/platform_data/x86/apple.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -53,6 +54,7 @@
|
||||
*/
|
||||
struct bcm_device_data {
|
||||
bool no_early_set_baudrate;
|
||||
bool drive_rts_on_open;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -122,6 +124,7 @@ struct bcm_device {
|
||||
bool is_suspended;
|
||||
#endif
|
||||
bool no_early_set_baudrate;
|
||||
bool drive_rts_on_open;
|
||||
u8 pcm_int_params[5];
|
||||
};
|
||||
|
||||
@ -456,7 +459,9 @@ static int bcm_open(struct hci_uart *hu)
|
||||
|
||||
out:
|
||||
if (bcm->dev) {
|
||||
hci_uart_set_flow_control(hu, true);
|
||||
if (bcm->dev->drive_rts_on_open)
|
||||
hci_uart_set_flow_control(hu, true);
|
||||
|
||||
hu->init_speed = bcm->dev->init_speed;
|
||||
|
||||
/* If oper_speed is set, ldisc/serdev will set the baudrate
|
||||
@ -466,7 +471,10 @@ static int bcm_open(struct hci_uart *hu)
|
||||
hu->oper_speed = bcm->dev->oper_speed;
|
||||
|
||||
err = bcm_gpio_set_power(bcm->dev, true);
|
||||
hci_uart_set_flow_control(hu, false);
|
||||
|
||||
if (bcm->dev->drive_rts_on_open)
|
||||
hci_uart_set_flow_control(hu, false);
|
||||
|
||||
if (err)
|
||||
goto err_unset_hu;
|
||||
}
|
||||
@ -1144,6 +1152,8 @@ static int bcm_of_probe(struct bcm_device *bdev)
|
||||
device_property_read_u32(bdev->dev, "max-speed", &bdev->oper_speed);
|
||||
device_property_read_u8_array(bdev->dev, "brcm,bt-pcm-int-params",
|
||||
bdev->pcm_int_params, 5);
|
||||
bdev->irq = of_irq_get_byname(bdev->dev->of_node, "host-wakeup");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1447,8 +1457,10 @@ static int bcm_serdev_probe(struct serdev_device *serdev)
|
||||
dev_err(&serdev->dev, "Failed to power down\n");
|
||||
|
||||
data = device_get_match_data(bcmdev->dev);
|
||||
if (data)
|
||||
if (data) {
|
||||
bcmdev->no_early_set_baudrate = data->no_early_set_baudrate;
|
||||
bcmdev->drive_rts_on_open = data->drive_rts_on_open;
|
||||
}
|
||||
|
||||
return hci_uart_register_device(&bcmdev->serdev_hu, &bcm_proto);
|
||||
}
|
||||
@ -1465,11 +1477,16 @@ static struct bcm_device_data bcm4354_device_data = {
|
||||
.no_early_set_baudrate = true,
|
||||
};
|
||||
|
||||
static struct bcm_device_data bcm43438_device_data = {
|
||||
.drive_rts_on_open = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id bcm_bluetooth_of_match[] = {
|
||||
{ .compatible = "brcm,bcm20702a1" },
|
||||
{ .compatible = "brcm,bcm4329-bt" },
|
||||
{ .compatible = "brcm,bcm4345c5" },
|
||||
{ .compatible = "brcm,bcm4330-bt" },
|
||||
{ .compatible = "brcm,bcm43438-bt" },
|
||||
{ .compatible = "brcm,bcm43438-bt", .data = &bcm43438_device_data },
|
||||
{ .compatible = "brcm,bcm43540-bt", .data = &bcm4354_device_data },
|
||||
{ .compatible = "brcm,bcm4335a0" },
|
||||
{ },
|
||||
|
@ -103,6 +103,7 @@ 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 },
|
||||
{ H4_RECV_ISO, .recv = hci_recv_frame },
|
||||
};
|
||||
|
||||
/* Recv data */
|
||||
|
@ -385,6 +385,7 @@ static void h5_complete_rx_pkt(struct hci_uart *hu)
|
||||
case HCI_EVENT_PKT:
|
||||
case HCI_ACLDATA_PKT:
|
||||
case HCI_SCODATA_PKT:
|
||||
case HCI_ISODATA_PKT:
|
||||
hci_skb_pkt_type(h5->rx_skb) = H5_HDR_PKT_TYPE(hdr);
|
||||
|
||||
/* Remove Three-wire header */
|
||||
@ -594,6 +595,7 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||
break;
|
||||
|
||||
case HCI_SCODATA_PKT:
|
||||
case HCI_ISODATA_PKT:
|
||||
skb_queue_tail(&h5->unrel, skb);
|
||||
break;
|
||||
|
||||
@ -636,6 +638,7 @@ static bool valid_packet_type(u8 type)
|
||||
case HCI_ACLDATA_PKT:
|
||||
case HCI_COMMAND_PKT:
|
||||
case HCI_SCODATA_PKT:
|
||||
case HCI_ISODATA_PKT:
|
||||
case HCI_3WIRE_LINK_PKT:
|
||||
case HCI_3WIRE_ACK_PKT:
|
||||
return true;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/completion.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/devcoredump.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
@ -46,6 +47,7 @@
|
||||
#define IBS_BTSOC_TX_IDLE_TIMEOUT_MS 40
|
||||
#define IBS_HOST_TX_IDLE_TIMEOUT_MS 2000
|
||||
#define CMD_TRANS_TIMEOUT_MS 100
|
||||
#define MEMDUMP_TIMEOUT_MS 8000
|
||||
|
||||
/* susclk rate */
|
||||
#define SUSCLK_RATE_32KHZ 32768
|
||||
@ -53,12 +55,24 @@
|
||||
/* Controller debug log header */
|
||||
#define QCA_DEBUG_HANDLE 0x2EDC
|
||||
|
||||
/* max retry count when init fails */
|
||||
#define MAX_INIT_RETRIES 3
|
||||
|
||||
/* Controller dump header */
|
||||
#define QCA_SSR_DUMP_HANDLE 0x0108
|
||||
#define QCA_DUMP_PACKET_SIZE 255
|
||||
#define QCA_LAST_SEQUENCE_NUM 0xFFFF
|
||||
#define QCA_CRASHBYTE_PACKET_LEN 1096
|
||||
#define QCA_MEMDUMP_BYTE 0xFB
|
||||
|
||||
enum qca_flags {
|
||||
QCA_IBS_ENABLED,
|
||||
QCA_DROP_VENDOR_EVENT,
|
||||
QCA_SUSPENDING,
|
||||
QCA_MEMDUMP_COLLECTION
|
||||
};
|
||||
|
||||
|
||||
/* HCI_IBS transmit side sleep protocol states */
|
||||
enum tx_ibs_states {
|
||||
HCI_IBS_TX_ASLEEP,
|
||||
@ -81,11 +95,40 @@ enum hci_ibs_clock_state_vote {
|
||||
HCI_IBS_RX_VOTE_CLOCK_OFF,
|
||||
};
|
||||
|
||||
/* Controller memory dump states */
|
||||
enum qca_memdump_states {
|
||||
QCA_MEMDUMP_IDLE,
|
||||
QCA_MEMDUMP_COLLECTING,
|
||||
QCA_MEMDUMP_COLLECTED,
|
||||
QCA_MEMDUMP_TIMEOUT,
|
||||
};
|
||||
|
||||
struct qca_memdump_data {
|
||||
char *memdump_buf_head;
|
||||
char *memdump_buf_tail;
|
||||
u32 current_seq_no;
|
||||
u32 received_dump;
|
||||
};
|
||||
|
||||
struct qca_memdump_event_hdr {
|
||||
__u8 evt;
|
||||
__u8 plen;
|
||||
__u16 opcode;
|
||||
__u16 seq_no;
|
||||
__u8 reserved;
|
||||
} __packed;
|
||||
|
||||
|
||||
struct qca_dump_size {
|
||||
u32 dump_size;
|
||||
} __packed;
|
||||
|
||||
struct qca_data {
|
||||
struct hci_uart *hu;
|
||||
struct sk_buff *rx_skb;
|
||||
struct sk_buff_head txq;
|
||||
struct sk_buff_head tx_wait_q; /* HCI_IBS wait queue */
|
||||
struct sk_buff_head rx_memdump_q; /* Memdump wait queue */
|
||||
spinlock_t hci_ibs_lock; /* HCI_IBS state lock */
|
||||
u8 tx_ibs_state; /* HCI_IBS transmit side power state*/
|
||||
u8 rx_ibs_state; /* HCI_IBS receive side power state */
|
||||
@ -95,14 +138,18 @@ struct qca_data {
|
||||
u32 tx_idle_delay;
|
||||
struct timer_list wake_retrans_timer;
|
||||
u32 wake_retrans;
|
||||
struct timer_list memdump_timer;
|
||||
struct workqueue_struct *workqueue;
|
||||
struct work_struct ws_awake_rx;
|
||||
struct work_struct ws_awake_device;
|
||||
struct work_struct ws_rx_vote_off;
|
||||
struct work_struct ws_tx_vote_off;
|
||||
struct work_struct ctrl_memdump_evt;
|
||||
struct qca_memdump_data *qca_memdump;
|
||||
unsigned long flags;
|
||||
struct completion drop_ev_comp;
|
||||
wait_queue_head_t suspend_wait_q;
|
||||
enum qca_memdump_states memdump_state;
|
||||
|
||||
/* For debugging purpose */
|
||||
u64 ibs_sent_wacks;
|
||||
@ -167,6 +214,7 @@ static int qca_regulator_enable(struct qca_serdev *qcadev);
|
||||
static void qca_regulator_disable(struct qca_serdev *qcadev);
|
||||
static void qca_power_shutdown(struct hci_uart *hu);
|
||||
static int qca_power_off(struct hci_dev *hdev);
|
||||
static void qca_controller_memdump(struct work_struct *work);
|
||||
|
||||
static enum qca_btsoc_type qca_soc_type(struct hci_uart *hu)
|
||||
{
|
||||
@ -474,12 +522,28 @@ static void hci_ibs_wake_retrans_timeout(struct timer_list *t)
|
||||
hci_uart_tx_wakeup(hu);
|
||||
}
|
||||
|
||||
static void hci_memdump_timeout(struct timer_list *t)
|
||||
{
|
||||
struct qca_data *qca = from_timer(qca, t, tx_idle_timer);
|
||||
struct hci_uart *hu = qca->hu;
|
||||
struct qca_memdump_data *qca_memdump = qca->qca_memdump;
|
||||
char *memdump_buf = qca_memdump->memdump_buf_tail;
|
||||
|
||||
bt_dev_err(hu->hdev, "clearing allocated memory due to memdump timeout");
|
||||
/* Inject hw error event to reset the device and driver. */
|
||||
hci_reset_dev(hu->hdev);
|
||||
vfree(memdump_buf);
|
||||
kfree(qca_memdump);
|
||||
qca->memdump_state = QCA_MEMDUMP_TIMEOUT;
|
||||
del_timer(&qca->memdump_timer);
|
||||
cancel_work_sync(&qca->ctrl_memdump_evt);
|
||||
}
|
||||
|
||||
/* Initialize protocol */
|
||||
static int qca_open(struct hci_uart *hu)
|
||||
{
|
||||
struct qca_serdev *qcadev;
|
||||
struct qca_data *qca;
|
||||
int ret;
|
||||
|
||||
BT_DBG("hu %p qca_open", hu);
|
||||
|
||||
@ -492,6 +556,7 @@ static int qca_open(struct hci_uart *hu)
|
||||
|
||||
skb_queue_head_init(&qca->txq);
|
||||
skb_queue_head_init(&qca->tx_wait_q);
|
||||
skb_queue_head_init(&qca->rx_memdump_q);
|
||||
spin_lock_init(&qca->hci_ibs_lock);
|
||||
qca->workqueue = alloc_ordered_workqueue("qca_wq", 0);
|
||||
if (!qca->workqueue) {
|
||||
@ -504,7 +569,7 @@ static int qca_open(struct hci_uart *hu)
|
||||
INIT_WORK(&qca->ws_awake_device, qca_wq_awake_device);
|
||||
INIT_WORK(&qca->ws_rx_vote_off, qca_wq_serial_rx_clock_vote_off);
|
||||
INIT_WORK(&qca->ws_tx_vote_off, qca_wq_serial_tx_clock_vote_off);
|
||||
|
||||
INIT_WORK(&qca->ctrl_memdump_evt, qca_controller_memdump);
|
||||
init_waitqueue_head(&qca->suspend_wait_q);
|
||||
|
||||
qca->hu = hu;
|
||||
@ -519,23 +584,10 @@ static int qca_open(struct hci_uart *hu)
|
||||
hu->priv = qca;
|
||||
|
||||
if (hu->serdev) {
|
||||
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
if (!qca_is_wcn399x(qcadev->btsoc_type)) {
|
||||
gpiod_set_value_cansleep(qcadev->bt_en, 1);
|
||||
/* Controller needs time to bootup. */
|
||||
msleep(150);
|
||||
} else {
|
||||
if (qca_is_wcn399x(qcadev->btsoc_type)) {
|
||||
hu->init_speed = qcadev->init_speed;
|
||||
hu->oper_speed = qcadev->oper_speed;
|
||||
ret = qca_regulator_enable(qcadev);
|
||||
if (ret) {
|
||||
destroy_workqueue(qca->workqueue);
|
||||
kfree_skb(qca->rx_skb);
|
||||
hu->priv = NULL;
|
||||
kfree(qca);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -544,6 +596,7 @@ static int qca_open(struct hci_uart *hu)
|
||||
|
||||
timer_setup(&qca->tx_idle_timer, hci_ibs_tx_idle_timeout, 0);
|
||||
qca->tx_idle_delay = IBS_HOST_TX_IDLE_TIMEOUT_MS;
|
||||
timer_setup(&qca->memdump_timer, hci_memdump_timeout, 0);
|
||||
|
||||
BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u",
|
||||
qca->tx_idle_delay, qca->wake_retrans);
|
||||
@ -613,7 +666,6 @@ static int qca_flush(struct hci_uart *hu)
|
||||
/* Close protocol */
|
||||
static int qca_close(struct hci_uart *hu)
|
||||
{
|
||||
struct qca_serdev *qcadev;
|
||||
struct qca_data *qca = hu->priv;
|
||||
|
||||
BT_DBG("hu %p qca close", hu);
|
||||
@ -622,19 +674,14 @@ static int qca_close(struct hci_uart *hu)
|
||||
|
||||
skb_queue_purge(&qca->tx_wait_q);
|
||||
skb_queue_purge(&qca->txq);
|
||||
skb_queue_purge(&qca->rx_memdump_q);
|
||||
del_timer(&qca->tx_idle_timer);
|
||||
del_timer(&qca->wake_retrans_timer);
|
||||
del_timer(&qca->memdump_timer);
|
||||
destroy_workqueue(qca->workqueue);
|
||||
qca->hu = NULL;
|
||||
|
||||
if (hu->serdev) {
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
if (qca_is_wcn399x(qcadev->btsoc_type))
|
||||
qca_power_shutdown(hu);
|
||||
else
|
||||
gpiod_set_value_cansleep(qcadev->bt_en, 0);
|
||||
|
||||
}
|
||||
qca_power_shutdown(hu);
|
||||
|
||||
kfree_skb(qca->rx_skb);
|
||||
|
||||
@ -900,6 +947,125 @@ static int qca_recv_acl_data(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
static void qca_controller_memdump(struct work_struct *work)
|
||||
{
|
||||
struct qca_data *qca = container_of(work, struct qca_data,
|
||||
ctrl_memdump_evt);
|
||||
struct hci_uart *hu = qca->hu;
|
||||
struct sk_buff *skb;
|
||||
struct qca_memdump_event_hdr *cmd_hdr;
|
||||
struct qca_memdump_data *qca_memdump = qca->qca_memdump;
|
||||
struct qca_dump_size *dump;
|
||||
char *memdump_buf;
|
||||
char nullBuff[QCA_DUMP_PACKET_SIZE] = { 0 };
|
||||
u16 seq_no;
|
||||
u32 dump_size;
|
||||
|
||||
while ((skb = skb_dequeue(&qca->rx_memdump_q))) {
|
||||
|
||||
if (!qca_memdump) {
|
||||
qca_memdump = kzalloc(sizeof(struct qca_memdump_data),
|
||||
GFP_ATOMIC);
|
||||
if (!qca_memdump)
|
||||
return;
|
||||
|
||||
qca->qca_memdump = qca_memdump;
|
||||
}
|
||||
|
||||
qca->memdump_state = QCA_MEMDUMP_COLLECTING;
|
||||
cmd_hdr = (void *) skb->data;
|
||||
seq_no = __le16_to_cpu(cmd_hdr->seq_no);
|
||||
skb_pull(skb, sizeof(struct qca_memdump_event_hdr));
|
||||
|
||||
if (!seq_no) {
|
||||
|
||||
/* This is the first frame of memdump packet from
|
||||
* the controller, Disable IBS to recevie dump
|
||||
* with out any interruption, ideally time required for
|
||||
* the controller to send the dump is 8 seconds. let us
|
||||
* start timer to handle this asynchronous activity.
|
||||
*/
|
||||
clear_bit(QCA_IBS_ENABLED, &qca->flags);
|
||||
set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
|
||||
dump = (void *) skb->data;
|
||||
dump_size = __le32_to_cpu(dump->dump_size);
|
||||
if (!(dump_size)) {
|
||||
bt_dev_err(hu->hdev, "Rx invalid memdump size");
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
bt_dev_info(hu->hdev, "QCA collecting dump of size:%u",
|
||||
dump_size);
|
||||
mod_timer(&qca->memdump_timer, (jiffies +
|
||||
msecs_to_jiffies(MEMDUMP_TIMEOUT_MS)));
|
||||
|
||||
skb_pull(skb, sizeof(dump_size));
|
||||
memdump_buf = vmalloc(dump_size);
|
||||
qca_memdump->memdump_buf_head = memdump_buf;
|
||||
qca_memdump->memdump_buf_tail = memdump_buf;
|
||||
}
|
||||
|
||||
memdump_buf = qca_memdump->memdump_buf_tail;
|
||||
|
||||
/* If sequence no 0 is missed then there is no point in
|
||||
* accepting the other sequences.
|
||||
*/
|
||||
if (!memdump_buf) {
|
||||
bt_dev_err(hu->hdev, "QCA: Discarding other packets");
|
||||
kfree(qca_memdump);
|
||||
kfree_skb(skb);
|
||||
qca->qca_memdump = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* There could be chance of missing some packets from
|
||||
* the controller. In such cases let us store the dummy
|
||||
* packets in the buffer.
|
||||
*/
|
||||
while ((seq_no > qca_memdump->current_seq_no + 1) &&
|
||||
seq_no != QCA_LAST_SEQUENCE_NUM) {
|
||||
bt_dev_err(hu->hdev, "QCA controller missed packet:%d",
|
||||
qca_memdump->current_seq_no);
|
||||
memcpy(memdump_buf, nullBuff, QCA_DUMP_PACKET_SIZE);
|
||||
memdump_buf = memdump_buf + QCA_DUMP_PACKET_SIZE;
|
||||
qca_memdump->received_dump += QCA_DUMP_PACKET_SIZE;
|
||||
qca_memdump->current_seq_no++;
|
||||
}
|
||||
|
||||
memcpy(memdump_buf, (unsigned char *) skb->data, skb->len);
|
||||
memdump_buf = memdump_buf + skb->len;
|
||||
qca_memdump->memdump_buf_tail = memdump_buf;
|
||||
qca_memdump->current_seq_no = seq_no + 1;
|
||||
qca_memdump->received_dump += skb->len;
|
||||
qca->qca_memdump = qca_memdump;
|
||||
kfree_skb(skb);
|
||||
if (seq_no == QCA_LAST_SEQUENCE_NUM) {
|
||||
bt_dev_info(hu->hdev, "QCA writing crash dump of size %d bytes",
|
||||
qca_memdump->received_dump);
|
||||
memdump_buf = qca_memdump->memdump_buf_head;
|
||||
dev_coredumpv(&hu->serdev->dev, memdump_buf,
|
||||
qca_memdump->received_dump, GFP_KERNEL);
|
||||
del_timer(&qca->memdump_timer);
|
||||
kfree(qca->qca_memdump);
|
||||
qca->qca_memdump = NULL;
|
||||
qca->memdump_state = QCA_MEMDUMP_COLLECTED;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int qca_controller_memdump_event(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct qca_data *qca = hu->priv;
|
||||
|
||||
skb_queue_tail(&qca->rx_memdump_q, skb);
|
||||
queue_work(qca->workqueue, &qca->ctrl_memdump_evt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qca_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
@ -925,6 +1091,14 @@ static int qca_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* We receive chip memory dump as an event packet, With a dedicated
|
||||
* handler followed by a hardware error event. When this event is
|
||||
* received we store dump into a file before closing hci. This
|
||||
* dump will help in triaging the issues.
|
||||
*/
|
||||
if ((skb->data[0] == HCI_VENDOR_PKT) &&
|
||||
(get_unaligned_be16(skb->data + 2) == QCA_SSR_DUMP_HANDLE))
|
||||
return qca_controller_memdump_event(hdev, skb);
|
||||
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
@ -1203,6 +1377,91 @@ static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qca_send_crashbuffer(struct hci_uart *hu)
|
||||
{
|
||||
struct qca_data *qca = hu->priv;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = bt_skb_alloc(QCA_CRASHBYTE_PACKET_LEN, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
bt_dev_err(hu->hdev, "Failed to allocate memory for skb packet");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* We forcefully crash the controller, by sending 0xfb byte for
|
||||
* 1024 times. We also might have chance of losing data, To be
|
||||
* on safer side we send 1096 bytes to the SoC.
|
||||
*/
|
||||
memset(skb_put(skb, QCA_CRASHBYTE_PACKET_LEN), QCA_MEMDUMP_BYTE,
|
||||
QCA_CRASHBYTE_PACKET_LEN);
|
||||
hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
|
||||
bt_dev_info(hu->hdev, "crash the soc to collect controller dump");
|
||||
skb_queue_tail(&qca->txq, skb);
|
||||
hci_uart_tx_wakeup(hu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qca_wait_for_dump_collection(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct qca_data *qca = hu->priv;
|
||||
struct qca_memdump_data *qca_memdump = qca->qca_memdump;
|
||||
char *memdump_buf = NULL;
|
||||
|
||||
wait_on_bit_timeout(&qca->flags, QCA_MEMDUMP_COLLECTION,
|
||||
TASK_UNINTERRUPTIBLE, MEMDUMP_TIMEOUT_MS);
|
||||
|
||||
clear_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
|
||||
if (qca->memdump_state == QCA_MEMDUMP_IDLE) {
|
||||
bt_dev_err(hu->hdev, "Clearing the buffers due to timeout");
|
||||
if (qca_memdump)
|
||||
memdump_buf = qca_memdump->memdump_buf_tail;
|
||||
vfree(memdump_buf);
|
||||
kfree(qca_memdump);
|
||||
qca->memdump_state = QCA_MEMDUMP_TIMEOUT;
|
||||
del_timer(&qca->memdump_timer);
|
||||
cancel_work_sync(&qca->ctrl_memdump_evt);
|
||||
}
|
||||
}
|
||||
|
||||
static void qca_hw_error(struct hci_dev *hdev, u8 code)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct qca_data *qca = hu->priv;
|
||||
|
||||
bt_dev_info(hdev, "mem_dump_status: %d", qca->memdump_state);
|
||||
|
||||
if (qca->memdump_state == QCA_MEMDUMP_IDLE) {
|
||||
/* If hardware error event received for other than QCA
|
||||
* soc memory dump event, then we need to crash the SOC
|
||||
* and wait here for 8 seconds to get the dump packets.
|
||||
* This will block main thread to be on hold until we
|
||||
* collect dump.
|
||||
*/
|
||||
set_bit(QCA_MEMDUMP_COLLECTION, &qca->flags);
|
||||
qca_send_crashbuffer(hu);
|
||||
qca_wait_for_dump_collection(hdev);
|
||||
} else if (qca->memdump_state == QCA_MEMDUMP_COLLECTING) {
|
||||
/* Let us wait here until memory dump collected or
|
||||
* memory dump timer expired.
|
||||
*/
|
||||
bt_dev_info(hdev, "waiting for dump to complete");
|
||||
qca_wait_for_dump_collection(hdev);
|
||||
}
|
||||
}
|
||||
|
||||
static void qca_cmd_timeout(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct qca_data *qca = hu->priv;
|
||||
|
||||
if (qca->memdump_state == QCA_MEMDUMP_IDLE)
|
||||
qca_send_crashbuffer(hu);
|
||||
else
|
||||
bt_dev_info(hdev, "Dump collection is in process");
|
||||
}
|
||||
|
||||
static int qca_wcn3990_init(struct hci_uart *hu)
|
||||
{
|
||||
struct qca_serdev *qcadev;
|
||||
@ -1253,11 +1512,37 @@ static int qca_wcn3990_init(struct hci_uart *hu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qca_power_on(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
enum qca_btsoc_type soc_type = qca_soc_type(hu);
|
||||
struct qca_serdev *qcadev;
|
||||
int ret = 0;
|
||||
|
||||
/* Non-serdev device usually is powered by external power
|
||||
* and don't need additional action in driver for power on
|
||||
*/
|
||||
if (!hu->serdev)
|
||||
return 0;
|
||||
|
||||
if (qca_is_wcn399x(soc_type)) {
|
||||
ret = qca_wcn3990_init(hu);
|
||||
} else {
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
gpiod_set_value_cansleep(qcadev->bt_en, 1);
|
||||
/* Controller needs time to bootup. */
|
||||
msleep(150);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qca_setup(struct hci_uart *hu)
|
||||
{
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
struct qca_data *qca = hu->priv;
|
||||
unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200;
|
||||
unsigned int retries = 0;
|
||||
enum qca_btsoc_type soc_type = qca_soc_type(hu);
|
||||
const char *firmware_name = qca_get_firmware_name(hu);
|
||||
int ret;
|
||||
@ -1275,24 +1560,21 @@ static int qca_setup(struct hci_uart *hu)
|
||||
*/
|
||||
set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
|
||||
|
||||
if (qca_is_wcn399x(soc_type)) {
|
||||
bt_dev_info(hdev, "setting up wcn3990");
|
||||
bt_dev_info(hdev, "setting up %s",
|
||||
qca_is_wcn399x(soc_type) ? "wcn399x" : "ROME");
|
||||
|
||||
/* Enable NON_PERSISTENT_SETUP QUIRK to ensure to execute
|
||||
* setup for every hci up.
|
||||
*/
|
||||
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
|
||||
retry:
|
||||
ret = qca_power_on(hdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (qca_is_wcn399x(soc_type)) {
|
||||
set_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks);
|
||||
hu->hdev->shutdown = qca_power_off;
|
||||
ret = qca_wcn3990_init(hu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = qca_read_soc_version(hdev, &soc_ver, soc_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
bt_dev_info(hdev, "ROME setup");
|
||||
qca_set_speed(hu, QCA_INIT_SPEED);
|
||||
}
|
||||
|
||||
@ -1320,6 +1602,8 @@ static int qca_setup(struct hci_uart *hu)
|
||||
if (!ret) {
|
||||
set_bit(QCA_IBS_ENABLED, &qca->flags);
|
||||
qca_debugfs_init(hdev);
|
||||
hu->hdev->hw_error = qca_hw_error;
|
||||
hu->hdev->cmd_timeout = qca_cmd_timeout;
|
||||
} else if (ret == -ENOENT) {
|
||||
/* No patch/nvm-config found, run with original fw/config */
|
||||
ret = 0;
|
||||
@ -1329,6 +1613,20 @@ static int qca_setup(struct hci_uart *hu)
|
||||
* patch/nvm-config is found, so run with original fw/config.
|
||||
*/
|
||||
ret = 0;
|
||||
} else {
|
||||
if (retries < MAX_INIT_RETRIES) {
|
||||
qca_power_shutdown(hu);
|
||||
if (hu->serdev) {
|
||||
serdev_device_close(hu->serdev);
|
||||
ret = serdev_device_open(hu->serdev);
|
||||
if (ret) {
|
||||
bt_dev_err(hdev, "failed to open port");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
retries++;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup bdaddr */
|
||||
@ -1393,6 +1691,7 @@ static void qca_power_shutdown(struct hci_uart *hu)
|
||||
struct qca_serdev *qcadev;
|
||||
struct qca_data *qca = hu->priv;
|
||||
unsigned long flags;
|
||||
enum qca_btsoc_type soc_type = qca_soc_type(hu);
|
||||
|
||||
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||
|
||||
@ -1405,20 +1704,36 @@ static void qca_power_shutdown(struct hci_uart *hu)
|
||||
qca_flush(hu);
|
||||
spin_unlock_irqrestore(&qca->hci_ibs_lock, flags);
|
||||
|
||||
host_set_baudrate(hu, 2400);
|
||||
qca_send_power_pulse(hu, false);
|
||||
qca_regulator_disable(qcadev);
|
||||
hu->hdev->hw_error = NULL;
|
||||
hu->hdev->cmd_timeout = NULL;
|
||||
|
||||
/* Non-serdev device usually is powered by external power
|
||||
* and don't need additional action in driver for power down
|
||||
*/
|
||||
if (!hu->serdev)
|
||||
return;
|
||||
|
||||
if (qca_is_wcn399x(soc_type)) {
|
||||
host_set_baudrate(hu, 2400);
|
||||
qca_send_power_pulse(hu, false);
|
||||
qca_regulator_disable(qcadev);
|
||||
} else {
|
||||
gpiod_set_value_cansleep(qcadev->bt_en, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int qca_power_off(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct qca_data *qca = hu->priv;
|
||||
|
||||
/* Perform pre shutdown command */
|
||||
qca_send_pre_shutdown_cmd(hdev);
|
||||
|
||||
usleep_range(8000, 10000);
|
||||
/* Stop sending shutdown command if soc crashes. */
|
||||
if (qca->memdump_state == QCA_MEMDUMP_IDLE) {
|
||||
qca_send_pre_shutdown_cmd(hdev);
|
||||
usleep_range(8000, 10000);
|
||||
}
|
||||
|
||||
qca->memdump_state = QCA_MEMDUMP_IDLE;
|
||||
qca_power_shutdown(hu);
|
||||
return 0;
|
||||
}
|
||||
@ -1493,6 +1808,7 @@ static int qca_init_regulators(struct qca_power *qca,
|
||||
static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
{
|
||||
struct qca_serdev *qcadev;
|
||||
struct hci_dev *hdev;
|
||||
const struct qca_vreg_data *data;
|
||||
int err;
|
||||
|
||||
@ -1501,7 +1817,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
return -ENOMEM;
|
||||
|
||||
qcadev->serdev_hu.serdev = serdev;
|
||||
data = of_device_get_match_data(&serdev->dev);
|
||||
data = device_get_match_data(&serdev->dev);
|
||||
serdev_device_set_drvdata(serdev, qcadev);
|
||||
device_property_read_string(&serdev->dev, "firmware-name",
|
||||
&qcadev->firmware_name);
|
||||
@ -1518,7 +1834,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
data->num_vregs);
|
||||
if (err) {
|
||||
BT_ERR("Failed to init regulators:%d", err);
|
||||
goto out;
|
||||
return err;
|
||||
}
|
||||
|
||||
qcadev->bt_power->vregs_on = false;
|
||||
@ -1531,7 +1847,7 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
|
||||
if (err) {
|
||||
BT_ERR("wcn3990 serdev registration failed");
|
||||
goto out;
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
qcadev->btsoc_type = QCA_ROME;
|
||||
@ -1557,12 +1873,18 @@ static int qca_serdev_probe(struct serdev_device *serdev)
|
||||
return err;
|
||||
|
||||
err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
|
||||
if (err)
|
||||
if (err) {
|
||||
BT_ERR("Rome serdev registration failed");
|
||||
clk_disable_unprepare(qcadev->susclk);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
out: return err;
|
||||
hdev = qcadev->serdev_hu.hdev;
|
||||
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
|
||||
hdev->shutdown = qca_power_off;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qca_serdev_remove(struct serdev_device *serdev)
|
||||
|
@ -143,6 +143,13 @@ struct h4_recv_pkt {
|
||||
.lsize = 1, \
|
||||
.maxlen = HCI_MAX_EVENT_SIZE
|
||||
|
||||
#define H4_RECV_ISO \
|
||||
.type = HCI_ISODATA_PKT, \
|
||||
.hlen = HCI_ISO_HDR_SIZE, \
|
||||
.loff = 2, \
|
||||
.lsize = 2, \
|
||||
.maxlen = HCI_MAX_FRAME_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);
|
||||
|
@ -178,6 +178,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *data,
|
||||
case HCI_EVENT_PKT:
|
||||
case HCI_ACLDATA_PKT:
|
||||
case HCI_SCODATA_PKT:
|
||||
case HCI_ISODATA_PKT:
|
||||
if (!data->hdev) {
|
||||
kfree_skb(skb);
|
||||
return -ENODEV;
|
||||
|
@ -129,6 +129,8 @@ void bt_warn(const char *fmt, ...);
|
||||
__printf(1, 2)
|
||||
void bt_err(const char *fmt, ...);
|
||||
__printf(1, 2)
|
||||
void bt_warn_ratelimited(const char *fmt, ...);
|
||||
__printf(1, 2)
|
||||
void bt_err_ratelimited(const char *fmt, ...);
|
||||
|
||||
#define BT_INFO(fmt, ...) bt_info(fmt "\n", ##__VA_ARGS__)
|
||||
@ -136,8 +138,6 @@ void bt_err_ratelimited(const char *fmt, ...);
|
||||
#define BT_ERR(fmt, ...) bt_err(fmt "\n", ##__VA_ARGS__)
|
||||
#define BT_DBG(fmt, ...) pr_debug(fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#define BT_ERR_RATELIMITED(fmt, ...) bt_err_ratelimited(fmt "\n", ##__VA_ARGS__)
|
||||
|
||||
#define bt_dev_info(hdev, fmt, ...) \
|
||||
BT_INFO("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
|
||||
#define bt_dev_warn(hdev, fmt, ...) \
|
||||
@ -147,8 +147,10 @@ void bt_err_ratelimited(const char *fmt, ...);
|
||||
#define bt_dev_dbg(hdev, fmt, ...) \
|
||||
BT_DBG("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
|
||||
|
||||
#define bt_dev_warn_ratelimited(hdev, fmt, ...) \
|
||||
bt_warn_ratelimited("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
|
||||
#define bt_dev_err_ratelimited(hdev, fmt, ...) \
|
||||
BT_ERR_RATELIMITED("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
|
||||
bt_err_ratelimited("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
|
||||
|
||||
/* Connection and socket states */
|
||||
enum {
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#define HCI_MAX_ACL_SIZE 1024
|
||||
#define HCI_MAX_SCO_SIZE 255
|
||||
#define HCI_MAX_ISO_SIZE 251
|
||||
#define HCI_MAX_EVENT_SIZE 260
|
||||
#define HCI_MAX_FRAME_SIZE (HCI_MAX_ACL_SIZE + 4)
|
||||
|
||||
@ -303,6 +304,7 @@ enum {
|
||||
#define HCI_ACLDATA_PKT 0x02
|
||||
#define HCI_SCODATA_PKT 0x03
|
||||
#define HCI_EVENT_PKT 0x04
|
||||
#define HCI_ISODATA_PKT 0x05
|
||||
#define HCI_DIAG_PKT 0xf0
|
||||
#define HCI_VENDOR_PKT 0xff
|
||||
|
||||
@ -352,6 +354,15 @@ enum {
|
||||
#define ACL_ACTIVE_BCAST 0x04
|
||||
#define ACL_PICO_BCAST 0x08
|
||||
|
||||
/* ISO PB flags */
|
||||
#define ISO_START 0x00
|
||||
#define ISO_CONT 0x01
|
||||
#define ISO_SINGLE 0x02
|
||||
#define ISO_END 0x03
|
||||
|
||||
/* ISO TS flags */
|
||||
#define ISO_TS 0x01
|
||||
|
||||
/* Baseband links */
|
||||
#define SCO_LINK 0x00
|
||||
#define ACL_LINK 0x01
|
||||
@ -359,6 +370,7 @@ enum {
|
||||
/* Low Energy links do not have defined link type. Use invented one */
|
||||
#define LE_LINK 0x80
|
||||
#define AMP_LINK 0x81
|
||||
#define ISO_LINK 0x82
|
||||
#define INVALID_LINK 0xff
|
||||
|
||||
/* LMP features */
|
||||
@ -440,6 +452,8 @@ enum {
|
||||
#define HCI_LE_PHY_2M 0x01
|
||||
#define HCI_LE_PHY_CODED 0x08
|
||||
#define HCI_LE_CHAN_SEL_ALG2 0x40
|
||||
#define HCI_LE_CIS_MASTER 0x10
|
||||
#define HCI_LE_CIS_SLAVE 0x20
|
||||
|
||||
/* Connection modes */
|
||||
#define HCI_CM_ACTIVE 0x0000
|
||||
@ -1718,6 +1732,86 @@ struct hci_cp_le_set_adv_set_rand_addr {
|
||||
bdaddr_t bdaddr;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_READ_BUFFER_SIZE_V2 0x2060
|
||||
struct hci_rp_le_read_buffer_size_v2 {
|
||||
__u8 status;
|
||||
__le16 acl_mtu;
|
||||
__u8 acl_max_pkt;
|
||||
__le16 iso_mtu;
|
||||
__u8 iso_max_pkt;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_READ_ISO_TX_SYNC 0x2061
|
||||
struct hci_cp_le_read_iso_tx_sync {
|
||||
__le16 handle;
|
||||
} __packed;
|
||||
|
||||
struct hci_rp_le_read_iso_tx_sync {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
__le16 seq;
|
||||
__le32 imestamp;
|
||||
__u8 offset[3];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_SET_CIG_PARAMS 0x2062
|
||||
struct hci_cis_params {
|
||||
__u8 cis_id;
|
||||
__le16 m_sdu;
|
||||
__le16 s_sdu;
|
||||
__u8 m_phy;
|
||||
__u8 s_phy;
|
||||
__u8 m_rtn;
|
||||
__u8 s_rtn;
|
||||
} __packed;
|
||||
|
||||
struct hci_cp_le_set_cig_params {
|
||||
__u8 cig_id;
|
||||
__u8 m_interval[3];
|
||||
__u8 s_interval[3];
|
||||
__u8 sca;
|
||||
__u8 packing;
|
||||
__u8 framing;
|
||||
__le16 m_latency;
|
||||
__le16 s_latency;
|
||||
__u8 num_cis;
|
||||
struct hci_cis_params cis[0];
|
||||
} __packed;
|
||||
|
||||
struct hci_rp_le_set_cig_params {
|
||||
__u8 status;
|
||||
__u8 cig_id;
|
||||
__u8 num_handles;
|
||||
__le16 handle[0];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_CREATE_CIS 0x2064
|
||||
struct hci_cis {
|
||||
__le16 cis_handle;
|
||||
__le16 acl_handle;
|
||||
} __packed;
|
||||
|
||||
struct hci_cp_le_create_cis {
|
||||
__u8 num_cis;
|
||||
struct hci_cis cis[0];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_REMOVE_CIG 0x2065
|
||||
struct hci_cp_le_remove_cig {
|
||||
__u8 cig_id;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_ACCEPT_CIS 0x2066
|
||||
struct hci_cp_le_accept_cis {
|
||||
__le16 handle;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_REJECT_CIS 0x2067
|
||||
struct hci_cp_le_reject_cis {
|
||||
__le16 handle;
|
||||
__u8 reason;
|
||||
} __packed;
|
||||
|
||||
/* ---- HCI Events ---- */
|
||||
#define HCI_EV_INQUIRY_COMPLETE 0x01
|
||||
|
||||
@ -2186,6 +2280,14 @@ struct hci_ev_le_direct_adv_info {
|
||||
__s8 rssi;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_LE_PHY_UPDATE_COMPLETE 0x0c
|
||||
struct hci_ev_le_phy_update_complete {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
__u8 tx_phy;
|
||||
__u8 rx_phy;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_LE_EXT_ADV_REPORT 0x0d
|
||||
struct hci_ev_le_ext_adv_report {
|
||||
__le16 evt_type;
|
||||
@ -2226,6 +2328,34 @@ struct hci_evt_le_ext_adv_set_term {
|
||||
__u8 num_evts;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EVT_LE_CIS_ESTABLISHED 0x19
|
||||
struct hci_evt_le_cis_established {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
__u8 cig_sync_delay[3];
|
||||
__u8 cis_sync_delay[3];
|
||||
__u8 m_latency[3];
|
||||
__u8 s_latency[3];
|
||||
__u8 m_phy;
|
||||
__u8 s_phy;
|
||||
__u8 nse;
|
||||
__u8 m_bn;
|
||||
__u8 s_bn;
|
||||
__u8 m_ft;
|
||||
__u8 s_ft;
|
||||
__le16 m_mtu;
|
||||
__le16 s_mtu;
|
||||
__le16 interval;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EVT_LE_CIS_REQ 0x1a
|
||||
struct hci_evt_le_cis_req {
|
||||
__le16 acl_handle;
|
||||
__le16 cis_handle;
|
||||
__u8 cig_id;
|
||||
__u8 cis_id;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_VENDOR 0xff
|
||||
|
||||
/* Internal events generated by Bluetooth stack */
|
||||
@ -2254,6 +2384,7 @@ struct hci_ev_si_security {
|
||||
#define HCI_EVENT_HDR_SIZE 2
|
||||
#define HCI_ACL_HDR_SIZE 4
|
||||
#define HCI_SCO_HDR_SIZE 3
|
||||
#define HCI_ISO_HDR_SIZE 4
|
||||
|
||||
struct hci_command_hdr {
|
||||
__le16 opcode; /* OCF & OGF */
|
||||
@ -2275,6 +2406,30 @@ struct hci_sco_hdr {
|
||||
__u8 dlen;
|
||||
} __packed;
|
||||
|
||||
struct hci_iso_hdr {
|
||||
__le16 handle;
|
||||
__le16 dlen;
|
||||
__u8 data[0];
|
||||
} __packed;
|
||||
|
||||
/* ISO data packet status flags */
|
||||
#define HCI_ISO_STATUS_VALID 0x00
|
||||
#define HCI_ISO_STATUS_INVALID 0x01
|
||||
#define HCI_ISO_STATUS_NOP 0x02
|
||||
|
||||
#define HCI_ISO_DATA_HDR_SIZE 4
|
||||
struct hci_iso_data_hdr {
|
||||
__le16 sn;
|
||||
__le16 slen;
|
||||
};
|
||||
|
||||
#define HCI_ISO_TS_DATA_HDR_SIZE 8
|
||||
struct hci_iso_ts_data_hdr {
|
||||
__le32 ts;
|
||||
__le16 sn;
|
||||
__le16 slen;
|
||||
};
|
||||
|
||||
static inline struct hci_event_hdr *hci_event_hdr(const struct sk_buff *skb)
|
||||
{
|
||||
return (struct hci_event_hdr *) skb->data;
|
||||
@ -2300,4 +2455,14 @@ static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb)
|
||||
#define hci_handle(h) (h & 0x0fff)
|
||||
#define hci_flags(h) (h >> 12)
|
||||
|
||||
/* ISO handle and flags pack/unpack */
|
||||
#define hci_iso_flags_pb(f) (f & 0x0003)
|
||||
#define hci_iso_flags_ts(f) ((f >> 2) & 0x0001)
|
||||
#define hci_iso_flags_pack(pb, ts) ((pb & 0x03) | ((ts & 0x01) << 2))
|
||||
|
||||
/* ISO data length and flags pack/unpack */
|
||||
#define hci_iso_data_len_pack(h, f) ((__u16) ((h) | ((f) << 14)))
|
||||
#define hci_iso_data_len(h) ((h) & 0x3fff)
|
||||
#define hci_iso_data_flags(h) ((h) >> 14)
|
||||
|
||||
#endif /* __HCI_H */
|
||||
|
@ -118,6 +118,13 @@ struct bt_uuid {
|
||||
u8 svc_hint;
|
||||
};
|
||||
|
||||
struct blocked_key {
|
||||
struct list_head list;
|
||||
struct rcu_head rcu;
|
||||
u8 type;
|
||||
u8 val[16];
|
||||
};
|
||||
|
||||
struct smp_csrk {
|
||||
bdaddr_t bdaddr;
|
||||
u8 bdaddr_type;
|
||||
@ -397,6 +404,7 @@ struct hci_dev {
|
||||
struct list_head le_conn_params;
|
||||
struct list_head pend_le_conns;
|
||||
struct list_head pend_le_reports;
|
||||
struct list_head blocked_keys;
|
||||
|
||||
struct hci_dev_stats stat;
|
||||
|
||||
@ -493,6 +501,8 @@ struct hci_conn {
|
||||
__u16 le_supv_timeout;
|
||||
__u8 le_adv_data[HCI_MAX_AD_LENGTH];
|
||||
__u8 le_adv_data_len;
|
||||
__u8 le_tx_phy;
|
||||
__u8 le_rx_phy;
|
||||
__s8 rssi;
|
||||
__s8 tx_power;
|
||||
__s8 max_tx_power;
|
||||
@ -1121,6 +1131,8 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 addr_type, u8 val[16], bdaddr_t *rpa);
|
||||
void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type);
|
||||
bool hci_is_blocked_key(struct hci_dev *hdev, u8 type, u8 val[16]);
|
||||
void hci_blocked_keys_clear(struct hci_dev *hdev);
|
||||
void hci_smp_irks_clear(struct hci_dev *hdev);
|
||||
|
||||
bool hci_bdaddr_is_paired(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
|
||||
|
@ -49,6 +49,8 @@ struct hci_mon_hdr {
|
||||
#define HCI_MON_CTRL_CLOSE 15
|
||||
#define HCI_MON_CTRL_COMMAND 16
|
||||
#define HCI_MON_CTRL_EVENT 17
|
||||
#define HCI_MON_ISO_TX_PKT 18
|
||||
#define HCI_MON_ISO_RX_PKT 19
|
||||
|
||||
struct hci_mon_new_index {
|
||||
__u8 type;
|
||||
|
@ -654,6 +654,23 @@ struct mgmt_cp_set_phy_confguration {
|
||||
} __packed;
|
||||
#define MGMT_SET_PHY_CONFIGURATION_SIZE 4
|
||||
|
||||
#define MGMT_OP_SET_BLOCKED_KEYS 0x0046
|
||||
|
||||
#define HCI_BLOCKED_KEY_TYPE_LINKKEY 0x00
|
||||
#define HCI_BLOCKED_KEY_TYPE_LTK 0x01
|
||||
#define HCI_BLOCKED_KEY_TYPE_IRK 0x02
|
||||
|
||||
struct mgmt_blocked_key_info {
|
||||
__u8 type;
|
||||
__u8 val[16];
|
||||
} __packed;
|
||||
|
||||
struct mgmt_cp_set_blocked_keys {
|
||||
__le16 key_count;
|
||||
struct mgmt_blocked_key_info keys[0];
|
||||
} __packed;
|
||||
#define MGMT_OP_SET_BLOCKED_KEYS_SIZE 2
|
||||
|
||||
#define MGMT_EV_CMD_COMPLETE 0x0001
|
||||
struct mgmt_ev_cmd_complete {
|
||||
__le16 opcode;
|
||||
|
@ -2311,6 +2311,33 @@ void hci_smp_irks_clear(struct hci_dev *hdev)
|
||||
}
|
||||
}
|
||||
|
||||
void hci_blocked_keys_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct blocked_key *b;
|
||||
|
||||
list_for_each_entry_rcu(b, &hdev->blocked_keys, list) {
|
||||
list_del_rcu(&b->list);
|
||||
kfree_rcu(b, rcu);
|
||||
}
|
||||
}
|
||||
|
||||
bool hci_is_blocked_key(struct hci_dev *hdev, u8 type, u8 val[16])
|
||||
{
|
||||
bool blocked = false;
|
||||
struct blocked_key *b;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry(b, &hdev->blocked_keys, list) {
|
||||
if (b->type == type && !memcmp(b->val, val, sizeof(b->val))) {
|
||||
blocked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
return blocked;
|
||||
}
|
||||
|
||||
struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
struct link_key *k;
|
||||
@ -2319,6 +2346,16 @@ struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
list_for_each_entry_rcu(k, &hdev->link_keys, list) {
|
||||
if (bacmp(bdaddr, &k->bdaddr) == 0) {
|
||||
rcu_read_unlock();
|
||||
|
||||
if (hci_is_blocked_key(hdev,
|
||||
HCI_BLOCKED_KEY_TYPE_LINKKEY,
|
||||
k->val)) {
|
||||
bt_dev_warn_ratelimited(hdev,
|
||||
"Link key blocked for %pMR",
|
||||
&k->bdaddr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
}
|
||||
@ -2387,6 +2424,15 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
|
||||
if (smp_ltk_is_sc(k) || ltk_role(k->type) == role) {
|
||||
rcu_read_unlock();
|
||||
|
||||
if (hci_is_blocked_key(hdev, HCI_BLOCKED_KEY_TYPE_LTK,
|
||||
k->val)) {
|
||||
bt_dev_warn_ratelimited(hdev,
|
||||
"LTK blocked for %pMR",
|
||||
&k->bdaddr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
}
|
||||
@ -2397,31 +2443,42 @@ struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
|
||||
struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa)
|
||||
{
|
||||
struct smp_irk *irk_to_return = NULL;
|
||||
struct smp_irk *irk;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
|
||||
if (!bacmp(&irk->rpa, rpa)) {
|
||||
rcu_read_unlock();
|
||||
return irk;
|
||||
irk_to_return = irk;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
|
||||
if (smp_irk_matches(hdev, irk->val, rpa)) {
|
||||
bacpy(&irk->rpa, rpa);
|
||||
rcu_read_unlock();
|
||||
return irk;
|
||||
irk_to_return = irk;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (irk_to_return && hci_is_blocked_key(hdev, HCI_BLOCKED_KEY_TYPE_IRK,
|
||||
irk_to_return->val)) {
|
||||
bt_dev_warn_ratelimited(hdev, "Identity key blocked for %pMR",
|
||||
&irk_to_return->bdaddr);
|
||||
irk_to_return = NULL;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
return irk_to_return;
|
||||
}
|
||||
|
||||
struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 addr_type)
|
||||
{
|
||||
struct smp_irk *irk_to_return = NULL;
|
||||
struct smp_irk *irk;
|
||||
|
||||
/* Identity Address must be public or static random */
|
||||
@ -2432,13 +2489,23 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
|
||||
if (addr_type == irk->addr_type &&
|
||||
bacmp(bdaddr, &irk->bdaddr) == 0) {
|
||||
rcu_read_unlock();
|
||||
return irk;
|
||||
irk_to_return = irk;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
if (irk_to_return && hci_is_blocked_key(hdev, HCI_BLOCKED_KEY_TYPE_IRK,
|
||||
irk_to_return->val)) {
|
||||
bt_dev_warn_ratelimited(hdev, "Identity key blocked for %pMR",
|
||||
&irk_to_return->bdaddr);
|
||||
irk_to_return = NULL;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
return irk_to_return;
|
||||
}
|
||||
|
||||
struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
@ -3244,6 +3311,7 @@ struct hci_dev *hci_alloc_dev(void)
|
||||
INIT_LIST_HEAD(&hdev->pend_le_reports);
|
||||
INIT_LIST_HEAD(&hdev->conn_hash.list);
|
||||
INIT_LIST_HEAD(&hdev->adv_instances);
|
||||
INIT_LIST_HEAD(&hdev->blocked_keys);
|
||||
|
||||
INIT_WORK(&hdev->rx_work, hci_rx_work);
|
||||
INIT_WORK(&hdev->cmd_work, hci_cmd_work);
|
||||
@ -3443,6 +3511,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
|
||||
hci_bdaddr_list_clear(&hdev->le_resolv_list);
|
||||
hci_conn_params_clear_all(hdev);
|
||||
hci_discovery_filter_clear(hdev);
|
||||
hci_blocked_keys_clear(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
hci_dev_put(hdev);
|
||||
@ -3496,7 +3565,8 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
||||
if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_ISODATA_PKT) {
|
||||
kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -4218,15 +4288,10 @@ static void hci_sched_le(struct hci_dev *hdev)
|
||||
if (!hci_conn_num(hdev, LE_LINK))
|
||||
return;
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
|
||||
/* LE tx timeout must be longer than maximum
|
||||
* link supervision timeout (40.9 seconds) */
|
||||
if (!hdev->le_cnt && hdev->le_pkts &&
|
||||
time_after(jiffies, hdev->le_last_tx + HZ * 45))
|
||||
hci_link_tx_to(hdev, LE_LINK);
|
||||
}
|
||||
|
||||
cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt;
|
||||
|
||||
__check_timeout(hdev, cnt);
|
||||
|
||||
tmp = cnt;
|
||||
while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) {
|
||||
u32 priority = (skb_peek(&chan->data_q))->priority;
|
||||
@ -4479,6 +4544,7 @@ static void hci_rx_work(struct work_struct *work)
|
||||
switch (hci_skb_pkt_type(skb)) {
|
||||
case HCI_ACLDATA_PKT:
|
||||
case HCI_SCODATA_PKT:
|
||||
case HCI_ISODATA_PKT:
|
||||
kfree_skb(skb);
|
||||
continue;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "smp.h"
|
||||
#include "hci_debugfs.h"
|
||||
|
||||
#define DEFINE_QUIRK_ATTRIBUTE(__name, __quirk) \
|
||||
@ -152,6 +153,21 @@ static int blacklist_show(struct seq_file *f, void *p)
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(blacklist);
|
||||
|
||||
static int blocked_keys_show(struct seq_file *f, void *p)
|
||||
{
|
||||
struct hci_dev *hdev = f->private;
|
||||
struct blocked_key *key;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(key, &hdev->blocked_keys, list)
|
||||
seq_printf(f, "%u %*phN\n", key->type, 16, key->val);
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(blocked_keys);
|
||||
|
||||
static int uuids_show(struct seq_file *f, void *p)
|
||||
{
|
||||
struct hci_dev *hdev = f->private;
|
||||
@ -308,6 +324,8 @@ void hci_debugfs_create_common(struct hci_dev *hdev)
|
||||
&device_list_fops);
|
||||
debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
|
||||
&blacklist_fops);
|
||||
debugfs_create_file("blocked_keys", 0444, hdev->debugfs, hdev,
|
||||
&blocked_keys_fops);
|
||||
debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
|
||||
debugfs_create_file("remote_oob", 0400, hdev->debugfs, hdev,
|
||||
&remote_oob_fops);
|
||||
@ -972,6 +990,62 @@ 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 min_key_size_set(void *data, u64 val)
|
||||
{
|
||||
struct hci_dev *hdev = data;
|
||||
|
||||
if (val > hdev->le_max_key_size || val < SMP_MIN_ENC_KEY_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hdev->le_min_key_size = val;
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int min_key_size_get(void *data, u64 *val)
|
||||
{
|
||||
struct hci_dev *hdev = data;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
*val = hdev->le_min_key_size;
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(min_key_size_fops, min_key_size_get,
|
||||
min_key_size_set, "%llu\n");
|
||||
|
||||
static int max_key_size_set(void *data, u64 val)
|
||||
{
|
||||
struct hci_dev *hdev = data;
|
||||
|
||||
if (val > SMP_MAX_ENC_KEY_SIZE || val < hdev->le_min_key_size)
|
||||
return -EINVAL;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hdev->le_max_key_size = val;
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max_key_size_get(void *data, u64 *val)
|
||||
{
|
||||
struct hci_dev *hdev = data;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
*val = hdev->le_max_key_size;
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(max_key_size_fops, max_key_size_get,
|
||||
max_key_size_set, "%llu\n");
|
||||
|
||||
static int auth_payload_timeout_set(void *data, u64 val)
|
||||
{
|
||||
struct hci_dev *hdev = data;
|
||||
@ -1054,6 +1128,10 @@ 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("min_key_size", 0644, hdev->debugfs, hdev,
|
||||
&min_key_size_fops);
|
||||
debugfs_create_file("max_key_size", 0644, hdev->debugfs, hdev,
|
||||
&max_key_size_fops);
|
||||
debugfs_create_file("auth_payload_timeout", 0644, hdev->debugfs, hdev,
|
||||
&auth_payload_timeout_fops);
|
||||
|
||||
|
@ -5451,7 +5451,7 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static u8 ext_evt_type_to_legacy(u16 evt_type)
|
||||
static u8 ext_evt_type_to_legacy(struct hci_dev *hdev, u16 evt_type)
|
||||
{
|
||||
if (evt_type & LE_EXT_ADV_LEGACY_PDU) {
|
||||
switch (evt_type) {
|
||||
@ -5468,10 +5468,7 @@ static u8 ext_evt_type_to_legacy(u16 evt_type)
|
||||
return LE_ADV_SCAN_RSP;
|
||||
}
|
||||
|
||||
BT_ERR_RATELIMITED("Unknown advertising packet type: 0x%02x",
|
||||
evt_type);
|
||||
|
||||
return LE_ADV_INVALID;
|
||||
goto invalid;
|
||||
}
|
||||
|
||||
if (evt_type & LE_EXT_ADV_CONN_IND) {
|
||||
@ -5491,8 +5488,9 @@ static u8 ext_evt_type_to_legacy(u16 evt_type)
|
||||
evt_type & LE_EXT_ADV_DIRECT_IND)
|
||||
return LE_ADV_NONCONN_IND;
|
||||
|
||||
BT_ERR_RATELIMITED("Unknown advertising packet type: 0x%02x",
|
||||
evt_type);
|
||||
invalid:
|
||||
bt_dev_err_ratelimited(hdev, "Unknown advertising packet type: 0x%02x",
|
||||
evt_type);
|
||||
|
||||
return LE_ADV_INVALID;
|
||||
}
|
||||
@ -5510,7 +5508,7 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
u16 evt_type;
|
||||
|
||||
evt_type = __le16_to_cpu(ev->evt_type);
|
||||
legacy_evt_type = ext_evt_type_to_legacy(evt_type);
|
||||
legacy_evt_type = ext_evt_type_to_legacy(hdev, evt_type);
|
||||
if (legacy_evt_type != LE_ADV_INVALID) {
|
||||
process_adv_report(hdev, legacy_evt_type, &ev->bdaddr,
|
||||
ev->bdaddr_type, NULL, 0, ev->rssi,
|
||||
@ -5720,6 +5718,29 @@ static void hci_le_direct_adv_report_evt(struct hci_dev *hdev,
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_le_phy_update_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_phy_update_complete *ev = (void *) skb->data;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
|
||||
|
||||
if (!ev->status)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
conn->le_tx_phy = ev->tx_phy;
|
||||
conn->le_rx_phy = ev->rx_phy;
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_meta *le_ev = (void *) skb->data;
|
||||
@ -5755,6 +5776,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_le_direct_adv_report_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_LE_PHY_UPDATE_COMPLETE:
|
||||
hci_le_phy_update_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_LE_EXT_ADV_REPORT:
|
||||
hci_le_ext_adv_report_evt(hdev, skb);
|
||||
break;
|
||||
|
@ -211,7 +211,8 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT)
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_ISODATA_PKT)
|
||||
continue;
|
||||
if (is_filtered_packet(sk, skb))
|
||||
continue;
|
||||
@ -220,7 +221,8 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
continue;
|
||||
if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT)
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_ISODATA_PKT)
|
||||
continue;
|
||||
} else {
|
||||
/* Don't send frame to other channel types */
|
||||
@ -324,6 +326,12 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
else
|
||||
opcode = cpu_to_le16(HCI_MON_SCO_TX_PKT);
|
||||
break;
|
||||
case HCI_ISODATA_PKT:
|
||||
if (bt_cb(skb)->incoming)
|
||||
opcode = cpu_to_le16(HCI_MON_ISO_RX_PKT);
|
||||
else
|
||||
opcode = cpu_to_le16(HCI_MON_ISO_TX_PKT);
|
||||
break;
|
||||
case HCI_DIAG_PKT:
|
||||
opcode = cpu_to_le16(HCI_MON_VENDOR_DIAG);
|
||||
break;
|
||||
@ -831,6 +839,8 @@ static int hci_sock_release(struct socket *sock)
|
||||
if (!sk)
|
||||
return 0;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
switch (hci_pi(sk)->channel) {
|
||||
case HCI_CHANNEL_MONITOR:
|
||||
atomic_dec(&monitor_promisc);
|
||||
@ -878,6 +888,7 @@ static int hci_sock_release(struct socket *sock)
|
||||
skb_queue_purge(&sk->sk_receive_queue);
|
||||
skb_queue_purge(&sk->sk_write_queue);
|
||||
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
return 0;
|
||||
}
|
||||
@ -1762,7 +1773,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
*/
|
||||
if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_ISODATA_PKT) {
|
||||
err = -EINVAL;
|
||||
goto drop;
|
||||
}
|
||||
@ -1806,7 +1818,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
}
|
||||
|
||||
if (hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT) {
|
||||
hci_skb_pkt_type(skb) != HCI_SCODATA_PKT &&
|
||||
hci_skb_pkt_type(skb) != HCI_ISODATA_PKT) {
|
||||
err = -EINVAL;
|
||||
goto drop;
|
||||
}
|
||||
|
@ -1289,6 +1289,9 @@ static void l2cap_le_connect(struct l2cap_chan *chan)
|
||||
if (test_and_set_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags))
|
||||
return;
|
||||
|
||||
if (!chan->imtu)
|
||||
chan->imtu = chan->conn->mtu;
|
||||
|
||||
l2cap_le_flowctl_init(chan, 0);
|
||||
|
||||
req.psm = chan->psm;
|
||||
@ -3226,6 +3229,49 @@ static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
|
||||
chan->ack_win = chan->tx_win;
|
||||
}
|
||||
|
||||
static void l2cap_mtu_auto(struct l2cap_chan *chan)
|
||||
{
|
||||
struct hci_conn *conn = chan->conn->hcon;
|
||||
|
||||
chan->imtu = L2CAP_DEFAULT_MIN_MTU;
|
||||
|
||||
/* The 2-DH1 packet has between 2 and 56 information bytes
|
||||
* (including the 2-byte payload header)
|
||||
*/
|
||||
if (!(conn->pkt_type & HCI_2DH1))
|
||||
chan->imtu = 54;
|
||||
|
||||
/* The 3-DH1 packet has between 2 and 85 information bytes
|
||||
* (including the 2-byte payload header)
|
||||
*/
|
||||
if (!(conn->pkt_type & HCI_3DH1))
|
||||
chan->imtu = 83;
|
||||
|
||||
/* The 2-DH3 packet has between 2 and 369 information bytes
|
||||
* (including the 2-byte payload header)
|
||||
*/
|
||||
if (!(conn->pkt_type & HCI_2DH3))
|
||||
chan->imtu = 367;
|
||||
|
||||
/* The 3-DH3 packet has between 2 and 554 information bytes
|
||||
* (including the 2-byte payload header)
|
||||
*/
|
||||
if (!(conn->pkt_type & HCI_3DH3))
|
||||
chan->imtu = 552;
|
||||
|
||||
/* The 2-DH5 packet has between 2 and 681 information bytes
|
||||
* (including the 2-byte payload header)
|
||||
*/
|
||||
if (!(conn->pkt_type & HCI_2DH5))
|
||||
chan->imtu = 679;
|
||||
|
||||
/* The 3-DH5 packet has between 2 and 1023 information bytes
|
||||
* (including the 2-byte payload header)
|
||||
*/
|
||||
if (!(conn->pkt_type & HCI_3DH5))
|
||||
chan->imtu = 1021;
|
||||
}
|
||||
|
||||
static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data, size_t data_size)
|
||||
{
|
||||
struct l2cap_conf_req *req = data;
|
||||
@ -3255,8 +3301,12 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data, size_t data
|
||||
}
|
||||
|
||||
done:
|
||||
if (chan->imtu != L2CAP_DEFAULT_MTU)
|
||||
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu, endptr - ptr);
|
||||
if (chan->imtu != L2CAP_DEFAULT_MTU) {
|
||||
if (!chan->imtu)
|
||||
l2cap_mtu_auto(chan);
|
||||
l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu,
|
||||
endptr - ptr);
|
||||
}
|
||||
|
||||
switch (chan->mode) {
|
||||
case L2CAP_MODE_BASIC:
|
||||
@ -5031,7 +5081,6 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
|
||||
chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
|
||||
l2cap_move_setup(chan);
|
||||
chan->move_id = req->dest_amp_id;
|
||||
icid = chan->dcid;
|
||||
|
||||
if (req->dest_amp_id == AMP_ID_BREDR) {
|
||||
/* Moving to BR/EDR */
|
||||
|
@ -183,6 +183,22 @@ void bt_err(const char *format, ...)
|
||||
}
|
||||
EXPORT_SYMBOL(bt_err);
|
||||
|
||||
void bt_warn_ratelimited(const char *format, ...)
|
||||
{
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
|
||||
vaf.fmt = format;
|
||||
vaf.va = &args;
|
||||
|
||||
pr_warn_ratelimited("%pV", &vaf);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
EXPORT_SYMBOL(bt_warn_ratelimited);
|
||||
|
||||
void bt_err_ratelimited(const char *format, ...)
|
||||
{
|
||||
struct va_format vaf;
|
||||
|
@ -38,7 +38,7 @@
|
||||
#include "mgmt_util.h"
|
||||
|
||||
#define MGMT_VERSION 1
|
||||
#define MGMT_REVISION 14
|
||||
#define MGMT_REVISION 15
|
||||
|
||||
static const u16 mgmt_commands[] = {
|
||||
MGMT_OP_READ_INDEX_LIST,
|
||||
@ -106,6 +106,7 @@ static const u16 mgmt_commands[] = {
|
||||
MGMT_OP_START_LIMITED_DISCOVERY,
|
||||
MGMT_OP_READ_EXT_INFO,
|
||||
MGMT_OP_SET_APPEARANCE,
|
||||
MGMT_OP_SET_BLOCKED_KEYS,
|
||||
};
|
||||
|
||||
static const u16 mgmt_events[] = {
|
||||
@ -175,7 +176,7 @@ static const u16 mgmt_untrusted_events[] = {
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
/* HCI to MGMT error code conversion table */
|
||||
static u8 mgmt_status_table[] = {
|
||||
static const u8 mgmt_status_table[] = {
|
||||
MGMT_STATUS_SUCCESS,
|
||||
MGMT_STATUS_UNKNOWN_COMMAND, /* Unknown Command */
|
||||
MGMT_STATUS_NOT_CONNECTED, /* No Connection */
|
||||
@ -2341,6 +2342,14 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
for (i = 0; i < key_count; i++) {
|
||||
struct mgmt_link_key_info *key = &cp->keys[i];
|
||||
|
||||
if (hci_is_blocked_key(hdev,
|
||||
HCI_BLOCKED_KEY_TYPE_LINKKEY,
|
||||
key->val)) {
|
||||
bt_dev_warn(hdev, "Skipping blocked link key for %pMR",
|
||||
&key->addr.bdaddr);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Always ignore debug keys and require a new pairing if
|
||||
* the user wants to use them.
|
||||
*/
|
||||
@ -3282,7 +3291,7 @@ static int set_appearance(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
u16 len)
|
||||
{
|
||||
struct mgmt_cp_set_appearance *cp = data;
|
||||
u16 apperance;
|
||||
u16 appearance;
|
||||
int err;
|
||||
|
||||
BT_DBG("");
|
||||
@ -3291,12 +3300,12 @@ static int set_appearance(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_APPEARANCE,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
apperance = le16_to_cpu(cp->appearance);
|
||||
appearance = le16_to_cpu(cp->appearance);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (hdev->appearance != apperance) {
|
||||
hdev->appearance = apperance;
|
||||
if (hdev->appearance != appearance) {
|
||||
hdev->appearance = appearance;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
|
||||
adv_expire(hdev, MGMT_ADV_FLAG_APPEARANCE);
|
||||
@ -3531,6 +3540,55 @@ static int set_phy_configuration(struct sock *sk, struct hci_dev *hdev,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int set_blocked_keys(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
u16 len)
|
||||
{
|
||||
int err = MGMT_STATUS_SUCCESS;
|
||||
struct mgmt_cp_set_blocked_keys *keys = data;
|
||||
const u16 max_key_count = ((U16_MAX - sizeof(*keys)) /
|
||||
sizeof(struct mgmt_blocked_key_info));
|
||||
u16 key_count, expected_len;
|
||||
int i;
|
||||
|
||||
BT_DBG("request for %s", hdev->name);
|
||||
|
||||
key_count = __le16_to_cpu(keys->key_count);
|
||||
if (key_count > max_key_count) {
|
||||
bt_dev_err(hdev, "too big key_count value %u", key_count);
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BLOCKED_KEYS,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
expected_len = struct_size(keys, keys, key_count);
|
||||
if (expected_len != len) {
|
||||
bt_dev_err(hdev, "expected %u bytes, got %u bytes",
|
||||
expected_len, len);
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BLOCKED_KEYS,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
}
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hci_blocked_keys_clear(hdev);
|
||||
|
||||
for (i = 0; i < keys->key_count; ++i) {
|
||||
struct blocked_key *b = kzalloc(sizeof(*b), GFP_KERNEL);
|
||||
|
||||
if (!b) {
|
||||
err = MGMT_STATUS_NO_RESOURCES;
|
||||
break;
|
||||
}
|
||||
|
||||
b->type = keys->keys[i].type;
|
||||
memcpy(b->val, keys->keys[i].val, sizeof(b->val));
|
||||
list_add_rcu(&b->list, &hdev->blocked_keys);
|
||||
}
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_BLOCKED_KEYS,
|
||||
err, NULL, 0);
|
||||
}
|
||||
|
||||
static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode, struct sk_buff *skb)
|
||||
{
|
||||
@ -5051,6 +5109,14 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data,
|
||||
for (i = 0; i < irk_count; i++) {
|
||||
struct mgmt_irk_info *irk = &cp->irks[i];
|
||||
|
||||
if (hci_is_blocked_key(hdev,
|
||||
HCI_BLOCKED_KEY_TYPE_IRK,
|
||||
irk->val)) {
|
||||
bt_dev_warn(hdev, "Skipping blocked IRK for %pMR",
|
||||
&irk->addr.bdaddr);
|
||||
continue;
|
||||
}
|
||||
|
||||
hci_add_irk(hdev, &irk->addr.bdaddr,
|
||||
le_addr_type(irk->addr.type), irk->val,
|
||||
BDADDR_ANY);
|
||||
@ -5134,6 +5200,14 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
|
||||
struct mgmt_ltk_info *key = &cp->keys[i];
|
||||
u8 type, authenticated;
|
||||
|
||||
if (hci_is_blocked_key(hdev,
|
||||
HCI_BLOCKED_KEY_TYPE_LTK,
|
||||
key->val)) {
|
||||
bt_dev_warn(hdev, "Skipping blocked LTK for %pMR",
|
||||
&key->addr.bdaddr);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (key->type) {
|
||||
case MGMT_LTK_UNAUTHENTICATED:
|
||||
authenticated = 0x00;
|
||||
@ -6914,6 +6988,8 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
|
||||
{ set_appearance, MGMT_SET_APPEARANCE_SIZE },
|
||||
{ get_phy_configuration, MGMT_GET_PHY_CONFIGURATION_SIZE },
|
||||
{ set_phy_configuration, MGMT_SET_PHY_CONFIGURATION_SIZE },
|
||||
{ set_blocked_keys, MGMT_OP_SET_BLOCKED_KEYS_SIZE,
|
||||
HCI_MGMT_VAR_LEN },
|
||||
};
|
||||
|
||||
void mgmt_index_added(struct hci_dev *hdev)
|
||||
|
@ -2453,6 +2453,15 @@ static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
if (skb->len < sizeof(*rp))
|
||||
return SMP_INVALID_PARAMS;
|
||||
|
||||
/* Pairing is aborted if any blocked keys are distributed */
|
||||
if (hci_is_blocked_key(conn->hcon->hdev, HCI_BLOCKED_KEY_TYPE_LTK,
|
||||
rp->ltk)) {
|
||||
bt_dev_warn_ratelimited(conn->hcon->hdev,
|
||||
"LTK blocked for %pMR",
|
||||
&conn->hcon->dst);
|
||||
return SMP_INVALID_PARAMS;
|
||||
}
|
||||
|
||||
SMP_ALLOW_CMD(smp, SMP_CMD_MASTER_IDENT);
|
||||
|
||||
skb_pull(skb, sizeof(*rp));
|
||||
@ -2509,6 +2518,15 @@ static int smp_cmd_ident_info(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
if (skb->len < sizeof(*info))
|
||||
return SMP_INVALID_PARAMS;
|
||||
|
||||
/* Pairing is aborted if any blocked keys are distributed */
|
||||
if (hci_is_blocked_key(conn->hcon->hdev, HCI_BLOCKED_KEY_TYPE_IRK,
|
||||
info->irk)) {
|
||||
bt_dev_warn_ratelimited(conn->hcon->hdev,
|
||||
"Identity key blocked for %pMR",
|
||||
&conn->hcon->dst);
|
||||
return SMP_INVALID_PARAMS;
|
||||
}
|
||||
|
||||
SMP_ALLOW_CMD(smp, SMP_CMD_IDENT_ADDR_INFO);
|
||||
|
||||
skb_pull(skb, sizeof(*info));
|
||||
@ -3355,94 +3373,6 @@ static const struct file_operations force_bredr_smp_fops = {
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t le_min_key_size_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[4];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%2u\n", hdev->le_min_key_size);
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static ssize_t le_min_key_size_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[32];
|
||||
size_t buf_size = min(count, (sizeof(buf) - 1));
|
||||
u8 key_size;
|
||||
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
|
||||
buf[buf_size] = '\0';
|
||||
|
||||
sscanf(buf, "%hhu", &key_size);
|
||||
|
||||
if (key_size > hdev->le_max_key_size ||
|
||||
key_size < SMP_MIN_ENC_KEY_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
hdev->le_min_key_size = key_size;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations le_min_key_size_fops = {
|
||||
.open = simple_open,
|
||||
.read = le_min_key_size_read,
|
||||
.write = le_min_key_size_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t le_max_key_size_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[4];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%2u\n", hdev->le_max_key_size);
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static ssize_t le_max_key_size_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[32];
|
||||
size_t buf_size = min(count, (sizeof(buf) - 1));
|
||||
u8 key_size;
|
||||
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
|
||||
buf[buf_size] = '\0';
|
||||
|
||||
sscanf(buf, "%hhu", &key_size);
|
||||
|
||||
if (key_size > SMP_MAX_ENC_KEY_SIZE ||
|
||||
key_size < hdev->le_min_key_size)
|
||||
return -EINVAL;
|
||||
|
||||
hdev->le_max_key_size = key_size;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations le_max_key_size_fops = {
|
||||
.open = simple_open,
|
||||
.read = le_max_key_size_read,
|
||||
.write = le_max_key_size_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
int smp_register(struct hci_dev *hdev)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
@ -3467,11 +3397,6 @@ int smp_register(struct hci_dev *hdev)
|
||||
|
||||
hdev->smp_data = chan;
|
||||
|
||||
debugfs_create_file("le_min_key_size", 0644, hdev->debugfs, hdev,
|
||||
&le_min_key_size_fops);
|
||||
debugfs_create_file("le_max_key_size", 0644, hdev->debugfs, hdev,
|
||||
&le_max_key_size_fops);
|
||||
|
||||
/* If the controller does not support BR/EDR Secure Connections
|
||||
* feature, then the BR/EDR SMP channel shall not be present.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user