mirror of
https://github.com/torvalds/linux.git
synced 2026-05-21 05:18:45 +02:00
bluetooth-next pull request for net-next:
- Add support for IM Networks PID 0x3568 - Add support for BCM4349B1 - Add support for CYW55572 - Add support for MT7922 VID/PID 0489/e0e2 - Add support for Realtek RTL8852C - Initial support for Isochronous Channels/ISO sockets - Remove HCI_QUIRK_BROKEN_ERR_DATA_REPORTING quirk -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmLbPmsZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKSo7EACc8njgHO2pN8ncWvgu/gH8 0v1lRBoi+Tyzk5gZtdM0rIbE3t7tFqml3Kr0WsCkzV6CGgnqCw5i/MKZXCV8G4tG 0ZsY8y6NMiCFR6wQq3rdNS8NqPmlCHWm6yY2EISEM6qbtF8HwxQvXdkzznZPHgVG DR7i5fAVuGA6rIL+9NSG/TxHjJvq6Bmu3v9Uu/V062I7NrBMw9Jr0Ic1EaUqgYck QL8+653ZZMxYPxt978UekbQIYEp3YwZ5MTACtX86j2s5tlZKuivKTIZch1vSaOi1 1zC6up208+p2/4+Yq7FJ2kA6d2be3FD26oT1xymRhqiMakCRrHfdmTFpC7/J4ZgX /4mcIREkUoO2duim+91Hgt1Ww1vaD4joPwXD6AILbK1bdp0pi0gw47bQF8XO8uIh yPQqnoGWSJGD1VknPh5x7lGcAYQ3bgSg0L3TlQ4gN9Qc/emuC6UOQ9QPvwmyOilG ZrDn2p1Rpsoj8vVRTv6+CgKqLokXNUTPixCAaS4AIygRzhIzwReYYNMYUZgYMrTk Qf6bKqczEut1vq8NZiN3TTxLLOWHB5+3cdl/QRv4ZeoNXMXPmbtJ0y9Vud66GhZu vS6F+BNQapIkEbM6xrC/OepCgzrVoVn2BQuDO6SQA3pg4JxFeAl+V434Va3tqVeK 9h6GHrl8KePjh528NXbH3Q== =Ne87 -----END PGP SIGNATURE----- Merge tag 'for-net-next-2022-07-22' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next Luiz Augusto von Dentz says: ==================== bluetooth-next pull request for net-next: - Add support for IM Networks PID 0x3568 - Add support for BCM4349B1 - Add support for CYW55572 - Add support for MT7922 VID/PID 0489/e0e2 - Add support for Realtek RTL8852C - Initial support for Isochronous Channels/ISO sockets - Remove HCI_QUIRK_BROKEN_ERR_DATA_REPORTING quirk * tag 'for-net-next-2022-07-22' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next: (58 commits) Bluetooth: btusb: Detect if an ACL packet is in fact an ISO packet Bluetooth: btusb: Add support for ISO packets Bluetooth: ISO: Add broadcast support Bluetooth: Add initial implementation of BIS connections Bluetooth: Add BTPROTO_ISO socket type Bluetooth: Add initial implementation of CIS connections Bluetooth: hci_core: Introduce hci_recv_event_data Bluetooth: Convert delayed discov_off to hci_sync Bluetooth: Remove update_scan hci_request dependancy Bluetooth: Remove dead code from hci_request.c Bluetooth: btrtl: Fix typo in comment Bluetooth: MGMT: Fix holding hci_conn reference while command is queued Bluetooth: mgmt: Fix using hci_conn_abort Bluetooth: Use bt_status to convert from errno Bluetooth: Add bt_status Bluetooth: hci_sync: Split hci_dev_open_sync Bluetooth: hci_sync: Refactor remove Adv Monitor Bluetooth: hci_sync: Refactor add Adv Monitor Bluetooth: hci_sync: Remove HCI_QUIRK_BROKEN_ERR_DATA_REPORTING Bluetooth: btusb: Remove HCI_QUIRK_BROKEN_ERR_DATA_REPORTING for fake CSR ... ==================== Link: https://lore.kernel.org/r/20220723002232.964796-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
4a934eca7b
|
|
@ -23,6 +23,8 @@ properties:
|
|||
- brcm,bcm4345c5
|
||||
- brcm,bcm43540-bt
|
||||
- brcm,bcm4335a0
|
||||
- brcm,bcm4349-bt
|
||||
- infineon,cyw55572-bt
|
||||
|
||||
shutdown-gpios:
|
||||
maxItems: 1
|
||||
|
|
@ -92,6 +94,13 @@ properties:
|
|||
pcm-sync-mode: slave, master
|
||||
pcm-clock-mode: slave, master
|
||||
|
||||
brcm,requires-autobaud-mode:
|
||||
type: boolean
|
||||
description:
|
||||
Set this property if autobaud mode is required. Autobaud mode is required
|
||||
if the device's initial baud rate in normal mode is not supported by the
|
||||
host or if the device requires autobaud mode startup before loading FW.
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: Handle to the line HOST_WAKE used to wake
|
||||
|
|
@ -108,6 +117,22 @@ properties:
|
|||
required:
|
||||
- compatible
|
||||
|
||||
dependencies:
|
||||
brcm,requires-autobaud-mode: [ 'shutdown-gpios' ]
|
||||
|
||||
if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- brcm,bcm20702a1
|
||||
- brcm,bcm4329-bt
|
||||
- brcm,bcm4330-bt
|
||||
then:
|
||||
properties:
|
||||
reset-gpios: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
|
|
|
|||
|
|
@ -403,6 +403,13 @@ static int btbcm_read_info(struct hci_dev *hdev)
|
|||
bt_dev_info(hdev, "BCM: chip id %u", skb->data[1]);
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btbcm_print_controller_features(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Read Controller Features */
|
||||
skb = btbcm_read_controller_features(hdev);
|
||||
if (IS_ERR(skb))
|
||||
|
|
@ -454,6 +461,8 @@ static const struct bcm_subver_table bcm_uart_subver_table[] = {
|
|||
{ 0x6606, "BCM4345C5" }, /* 003.006.006 */
|
||||
{ 0x230f, "BCM4356A2" }, /* 001.003.015 */
|
||||
{ 0x220e, "BCM20702A1" }, /* 001.002.014 */
|
||||
{ 0x420d, "BCM4349B1" }, /* 002.002.013 */
|
||||
{ 0x420e, "BCM4349B1" }, /* 002.002.014 */
|
||||
{ 0x4217, "BCM4329B1" }, /* 002.002.023 */
|
||||
{ 0x6106, "BCM4359C0" }, /* 003.001.006 */
|
||||
{ 0x4106, "BCM4335A0" }, /* 002.001.006 */
|
||||
|
|
@ -514,7 +523,7 @@ static const char *btbcm_get_board_name(struct device *dev)
|
|||
#endif
|
||||
}
|
||||
|
||||
int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done)
|
||||
int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud_mode)
|
||||
{
|
||||
u16 subver, rev, pid, vid;
|
||||
struct sk_buff *skb;
|
||||
|
|
@ -551,9 +560,16 @@ int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done)
|
|||
if (err)
|
||||
return err;
|
||||
}
|
||||
err = btbcm_print_local_name(hdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!use_autobaud_mode) {
|
||||
err = btbcm_print_controller_features(hdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = btbcm_print_local_name(hdev);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
bcm_subver_table = (hdev->bus == HCI_USB) ? bcm_usb_subver_table :
|
||||
bcm_uart_subver_table;
|
||||
|
|
@ -636,13 +652,13 @@ int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(btbcm_initialize);
|
||||
|
||||
int btbcm_finalize(struct hci_dev *hdev, bool *fw_load_done)
|
||||
int btbcm_finalize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud_mode)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Re-initialize if necessary */
|
||||
if (*fw_load_done) {
|
||||
err = btbcm_initialize(hdev, fw_load_done);
|
||||
err = btbcm_initialize(hdev, fw_load_done, use_autobaud_mode);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
|
@ -658,15 +674,16 @@ EXPORT_SYMBOL_GPL(btbcm_finalize);
|
|||
int btbcm_setup_patchram(struct hci_dev *hdev)
|
||||
{
|
||||
bool fw_load_done = false;
|
||||
bool use_autobaud_mode = false;
|
||||
int err;
|
||||
|
||||
/* Initialize */
|
||||
err = btbcm_initialize(hdev, &fw_load_done);
|
||||
err = btbcm_initialize(hdev, &fw_load_done, use_autobaud_mode);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Re-initialize after loading Patch */
|
||||
return btbcm_finalize(hdev, &fw_load_done);
|
||||
return btbcm_finalize(hdev, &fw_load_done, use_autobaud_mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btbcm_setup_patchram);
|
||||
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ int btbcm_write_pcm_int_params(struct hci_dev *hdev,
|
|||
int btbcm_setup_patchram(struct hci_dev *hdev);
|
||||
int btbcm_setup_apple(struct hci_dev *hdev);
|
||||
|
||||
int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done);
|
||||
int btbcm_finalize(struct hci_dev *hdev, bool *fw_load_done);
|
||||
int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud_mode);
|
||||
int btbcm_finalize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud_mode);
|
||||
|
||||
#else
|
||||
|
||||
|
|
@ -104,12 +104,12 @@ static inline int btbcm_setup_apple(struct hci_dev *hdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done)
|
||||
static inline int btbcm_initialize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud_mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int btbcm_finalize(struct hci_dev *hdev, bool *fw_load_done)
|
||||
static inline int btbcm_finalize(struct hci_dev *hdev, bool *fw_load_done, bool use_autobaud_mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1282,6 +1282,13 @@ static void btmtksdio_cmd_timeout(struct hci_dev *hdev)
|
|||
hci_reset_dev(hdev);
|
||||
}
|
||||
|
||||
static bool btmtksdio_sdio_inband_wakeup(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
|
||||
|
||||
return device_may_wakeup(bdev->dev);
|
||||
}
|
||||
|
||||
static bool btmtksdio_sdio_wakeup(struct hci_dev *hdev)
|
||||
{
|
||||
struct btmtksdio_dev *bdev = hci_get_drvdata(hdev);
|
||||
|
|
@ -1349,6 +1356,14 @@ static int btmtksdio_probe(struct sdio_func *func,
|
|||
hdev->shutdown = btmtksdio_shutdown;
|
||||
hdev->send = btmtksdio_send_frame;
|
||||
hdev->wakeup = btmtksdio_sdio_wakeup;
|
||||
/*
|
||||
* If SDIO controller supports wake on Bluetooth, sending a wakeon
|
||||
* command is not necessary.
|
||||
*/
|
||||
if (device_can_wakeup(func->card->host->parent))
|
||||
hdev->wakeup = btmtksdio_sdio_inband_wakeup;
|
||||
else
|
||||
hdev->wakeup = btmtksdio_sdio_wakeup;
|
||||
hdev->set_bdaddr = btmtk_set_bdaddr;
|
||||
|
||||
SET_HCIDEV_DEV(hdev, &func->dev);
|
||||
|
|
|
|||
|
|
@ -330,7 +330,7 @@ static int rtlbt_parse_firmware(struct hci_dev *hdev,
|
|||
/* Loop from the end of the firmware parsing instructions, until
|
||||
* we find an instruction that identifies the "project ID" for the
|
||||
* hardware supported by this firwmare file.
|
||||
* Once we have that, we double-check that that project_id is suitable
|
||||
* Once we have that, we double-check that project_id is suitable
|
||||
* for the hardware we are working with.
|
||||
*/
|
||||
while (fwptr >= btrtl_dev->fw_data + (sizeof(*epatch_info) + 3)) {
|
||||
|
|
|
|||
|
|
@ -427,6 +427,18 @@ static const struct usb_device_id blacklist_table[] = {
|
|||
{ USB_DEVICE(0x04ca, 0x4006), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Realtek 8852CE Bluetooth devices */
|
||||
{ USB_DEVICE(0x04ca, 0x4007), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x04c5, 0x1675), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0cb8, 0xc558), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3587), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3586), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Realtek Bluetooth devices */
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
|
||||
.driver_info = BTUSB_REALTEK },
|
||||
|
|
@ -477,6 +489,12 @@ static const struct usb_device_id blacklist_table[] = {
|
|||
{ USB_DEVICE(0x0489, 0xe0d9), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x13d3, 0x3568), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0489, 0xe0e2), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
|
||||
/* Additional Realtek 8723AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
|
||||
|
|
@ -893,11 +911,21 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
|
|||
hci_skb_expect(skb) -= len;
|
||||
|
||||
if (skb->len == HCI_ACL_HDR_SIZE) {
|
||||
__u16 handle = __le16_to_cpu(hci_acl_hdr(skb)->handle);
|
||||
__le16 dlen = hci_acl_hdr(skb)->dlen;
|
||||
__u8 type;
|
||||
|
||||
/* Complete ACL header */
|
||||
hci_skb_expect(skb) = __le16_to_cpu(dlen);
|
||||
|
||||
/* Detect if ISO packet has been sent over bulk */
|
||||
if (hci_conn_num(data->hdev, ISO_LINK)) {
|
||||
type = hci_conn_lookup_type(data->hdev,
|
||||
hci_handle(handle));
|
||||
if (type == ISO_LINK)
|
||||
hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
|
||||
}
|
||||
|
||||
if (skb_tailroom(skb) < hci_skb_expect(skb)) {
|
||||
kfree_skb(skb);
|
||||
skb = NULL;
|
||||
|
|
@ -1762,6 +1790,13 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
hdev->stat.sco_tx++;
|
||||
return submit_tx_urb(hdev, urb);
|
||||
|
||||
case HCI_ISODATA_PKT:
|
||||
urb = alloc_bulk_urb(hdev, skb);
|
||||
if (IS_ERR(urb))
|
||||
return PTR_ERR(urb);
|
||||
|
||||
return submit_or_queue_tx_urb(hdev, urb);
|
||||
}
|
||||
|
||||
return -EILSEQ;
|
||||
|
|
@ -2069,7 +2104,6 @@ static int btusb_setup_csr(struct hci_dev *hdev)
|
|||
* without these the controller will lock up.
|
||||
*/
|
||||
set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_BROKEN_ERR_DATA_REPORTING, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_BROKEN_FILTER_CLEAR_ALL, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_NO_SUSPEND_NOTIFIER, &hdev->quirks);
|
||||
|
||||
|
|
@ -2255,6 +2289,13 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
|
||||
hdev->stat.sco_tx++;
|
||||
return submit_tx_urb(hdev, urb);
|
||||
|
||||
case HCI_ISODATA_PKT:
|
||||
urb = alloc_bulk_urb(hdev, skb);
|
||||
if (IS_ERR(urb))
|
||||
return PTR_ERR(urb);
|
||||
|
||||
return submit_or_queue_tx_urb(hdev, urb);
|
||||
}
|
||||
|
||||
return -EILSEQ;
|
||||
|
|
@ -3352,7 +3393,6 @@ static int btusb_setup_qca(struct hci_dev *hdev)
|
|||
* work with the likes of HSP/HFP mSBC.
|
||||
*/
|
||||
set_bit(HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_BROKEN_ERR_DATA_REPORTING, &hdev->quirks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -3795,6 +3835,7 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
hdev->manufacturer = 70;
|
||||
hdev->cmd_timeout = btusb_mtk_cmd_timeout;
|
||||
hdev->set_bdaddr = btmtk_set_bdaddr;
|
||||
set_bit(HCI_QUIRK_BROKEN_ENHANCED_SETUP_SYNC_CONN, &hdev->quirks);
|
||||
set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
|
||||
data->recv_acl = btusb_recv_acl_mtk;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,10 +53,12 @@
|
|||
* struct bcm_device_data - device specific data
|
||||
* @no_early_set_baudrate: Disallow set baudrate before driver setup()
|
||||
* @drive_rts_on_open: drive RTS signal on ->open() when platform requires it
|
||||
* @max_autobaud_speed: max baudrate supported by device in autobaud mode
|
||||
*/
|
||||
struct bcm_device_data {
|
||||
bool no_early_set_baudrate;
|
||||
bool drive_rts_on_open;
|
||||
u32 max_autobaud_speed;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -99,6 +101,8 @@ struct bcm_device_data {
|
|||
* @no_early_set_baudrate: don't set_baudrate before setup()
|
||||
* @drive_rts_on_open: drive RTS signal on ->open() when platform requires it
|
||||
* @pcm_int_params: keep the initial PCM configuration
|
||||
* @use_autobaud_mode: start Bluetooth device in autobaud mode
|
||||
* @max_autobaud_speed: max baudrate supported by device in autobaud mode
|
||||
*/
|
||||
struct bcm_device {
|
||||
/* Must be the first member, hci_serdev.c expects this. */
|
||||
|
|
@ -136,7 +140,9 @@ struct bcm_device {
|
|||
#endif
|
||||
bool no_early_set_baudrate;
|
||||
bool drive_rts_on_open;
|
||||
bool use_autobaud_mode;
|
||||
u8 pcm_int_params[5];
|
||||
u32 max_autobaud_speed;
|
||||
};
|
||||
|
||||
/* generic bcm uart resources */
|
||||
|
|
@ -472,15 +478,20 @@ static int bcm_open(struct hci_uart *hu)
|
|||
|
||||
out:
|
||||
if (bcm->dev) {
|
||||
if (bcm->dev->drive_rts_on_open)
|
||||
if (bcm->dev->use_autobaud_mode)
|
||||
hci_uart_set_flow_control(hu, false); /* Assert BT_UART_CTS_N */
|
||||
else if (bcm->dev->drive_rts_on_open)
|
||||
hci_uart_set_flow_control(hu, true);
|
||||
|
||||
hu->init_speed = bcm->dev->init_speed;
|
||||
if (bcm->dev->use_autobaud_mode && bcm->dev->max_autobaud_speed)
|
||||
hu->init_speed = min(bcm->dev->oper_speed, bcm->dev->max_autobaud_speed);
|
||||
else
|
||||
hu->init_speed = bcm->dev->init_speed;
|
||||
|
||||
/* If oper_speed is set, ldisc/serdev will set the baudrate
|
||||
* before calling setup()
|
||||
*/
|
||||
if (!bcm->dev->no_early_set_baudrate)
|
||||
if (!bcm->dev->no_early_set_baudrate && !bcm->dev->use_autobaud_mode)
|
||||
hu->oper_speed = bcm->dev->oper_speed;
|
||||
|
||||
err = bcm_gpio_set_power(bcm->dev, true);
|
||||
|
|
@ -564,6 +575,7 @@ static int bcm_setup(struct hci_uart *hu)
|
|||
{
|
||||
struct bcm_data *bcm = hu->priv;
|
||||
bool fw_load_done = false;
|
||||
bool use_autobaud_mode = (bcm->dev ? bcm->dev->use_autobaud_mode : 0);
|
||||
unsigned int speed;
|
||||
int err;
|
||||
|
||||
|
|
@ -572,7 +584,7 @@ static int bcm_setup(struct hci_uart *hu)
|
|||
hu->hdev->set_diag = bcm_set_diag;
|
||||
hu->hdev->set_bdaddr = btbcm_set_bdaddr;
|
||||
|
||||
err = btbcm_initialize(hu->hdev, &fw_load_done);
|
||||
err = btbcm_initialize(hu->hdev, &fw_load_done, use_autobaud_mode);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
@ -580,8 +592,8 @@ static int bcm_setup(struct hci_uart *hu)
|
|||
return 0;
|
||||
|
||||
/* Init speed if any */
|
||||
if (hu->init_speed)
|
||||
speed = hu->init_speed;
|
||||
if (bcm->dev && bcm->dev->init_speed)
|
||||
speed = bcm->dev->init_speed;
|
||||
else if (hu->proto->init_speed)
|
||||
speed = hu->proto->init_speed;
|
||||
else
|
||||
|
|
@ -616,7 +628,7 @@ static int bcm_setup(struct hci_uart *hu)
|
|||
btbcm_write_pcm_int_params(hu->hdev, ¶ms);
|
||||
}
|
||||
|
||||
err = btbcm_finalize(hu->hdev, &fw_load_done);
|
||||
err = btbcm_finalize(hu->hdev, &fw_load_done, use_autobaud_mode);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
@ -1197,6 +1209,8 @@ static int bcm_acpi_probe(struct bcm_device *dev)
|
|||
|
||||
static int bcm_of_probe(struct bcm_device *bdev)
|
||||
{
|
||||
bdev->use_autobaud_mode = device_property_read_bool(bdev->dev,
|
||||
"brcm,requires-autobaud-mode");
|
||||
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);
|
||||
|
|
@ -1512,6 +1526,7 @@ static int bcm_serdev_probe(struct serdev_device *serdev)
|
|||
|
||||
data = device_get_match_data(bcmdev->dev);
|
||||
if (data) {
|
||||
bcmdev->max_autobaud_speed = data->max_autobaud_speed;
|
||||
bcmdev->no_early_set_baudrate = data->no_early_set_baudrate;
|
||||
bcmdev->drive_rts_on_open = data->drive_rts_on_open;
|
||||
}
|
||||
|
|
@ -1535,6 +1550,10 @@ static struct bcm_device_data bcm43438_device_data = {
|
|||
.drive_rts_on_open = true,
|
||||
};
|
||||
|
||||
static struct bcm_device_data cyw55572_device_data = {
|
||||
.max_autobaud_speed = 921600,
|
||||
};
|
||||
|
||||
static const struct of_device_id bcm_bluetooth_of_match[] = {
|
||||
{ .compatible = "brcm,bcm20702a1" },
|
||||
{ .compatible = "brcm,bcm4329-bt" },
|
||||
|
|
@ -1544,8 +1563,10 @@ static const struct of_device_id bcm_bluetooth_of_match[] = {
|
|||
{ .compatible = "brcm,bcm43430a0-bt" },
|
||||
{ .compatible = "brcm,bcm43430a1-bt" },
|
||||
{ .compatible = "brcm,bcm43438-bt", .data = &bcm43438_device_data },
|
||||
{ .compatible = "brcm,bcm4349-bt", .data = &bcm43438_device_data },
|
||||
{ .compatible = "brcm,bcm43540-bt", .data = &bcm4354_device_data },
|
||||
{ .compatible = "brcm,bcm4335a0" },
|
||||
{ .compatible = "infineon,cyw55572-bt", .data = &cyw55572_device_data },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcm_bluetooth_of_match);
|
||||
|
|
|
|||
|
|
@ -1217,7 +1217,11 @@ static struct platform_driver intel_driver = {
|
|||
|
||||
int __init intel_init(void)
|
||||
{
|
||||
platform_driver_register(&intel_driver);
|
||||
int err;
|
||||
|
||||
err = platform_driver_register(&intel_driver);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return hci_uart_register_proto(&intel_proto);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1588,7 +1588,7 @@ static bool qca_wakeup(struct hci_dev *hdev)
|
|||
wakeup = device_may_wakeup(hu->serdev->ctrl->dev.parent);
|
||||
bt_dev_dbg(hu->hdev, "wakeup status : %d", wakeup);
|
||||
|
||||
return !wakeup;
|
||||
return wakeup;
|
||||
}
|
||||
|
||||
static int qca_regulator_init(struct hci_uart *hu)
|
||||
|
|
|
|||
|
|
@ -231,6 +231,15 @@ static int hci_uart_setup(struct hci_dev *hdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Check if the device is wakeable */
|
||||
static bool hci_uart_wakeup(struct hci_dev *hdev)
|
||||
{
|
||||
/* HCI UART devices are assumed to be wakeable by default.
|
||||
* Implement wakeup callback to override this behavior.
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/** hci_uart_write_wakeup - transmit buffer wakeup
|
||||
* @serdev: serial device
|
||||
*
|
||||
|
|
@ -342,6 +351,8 @@ int hci_uart_register_device(struct hci_uart *hu,
|
|||
hdev->flush = hci_uart_flush;
|
||||
hdev->send = hci_uart_send_frame;
|
||||
hdev->setup = hci_uart_setup;
|
||||
if (!hdev->wakeup)
|
||||
hdev->wakeup = hci_uart_wakeup;
|
||||
SET_HCIDEV_DEV(hdev, &hu->serdev->dev);
|
||||
|
||||
if (test_bit(HCI_UART_NO_SUSPEND_NOTIFIER, &hu->flags))
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@
|
|||
#define BTPROTO_CMTP 5
|
||||
#define BTPROTO_HIDP 6
|
||||
#define BTPROTO_AVDTP 7
|
||||
#define BTPROTO_ISO 8
|
||||
#define BTPROTO_LAST BTPROTO_ISO
|
||||
|
||||
#define SOL_HCI 0
|
||||
#define SOL_L2CAP 6
|
||||
|
|
@ -149,10 +151,51 @@ struct bt_voice {
|
|||
#define BT_MODE_LE_FLOWCTL 0x03
|
||||
#define BT_MODE_EXT_FLOWCTL 0x04
|
||||
|
||||
#define BT_PKT_STATUS 16
|
||||
#define BT_PKT_STATUS 16
|
||||
|
||||
#define BT_SCM_PKT_STATUS 0x03
|
||||
|
||||
#define BT_ISO_QOS 17
|
||||
|
||||
#define BT_ISO_QOS_CIG_UNSET 0xff
|
||||
#define BT_ISO_QOS_CIS_UNSET 0xff
|
||||
|
||||
#define BT_ISO_QOS_BIG_UNSET 0xff
|
||||
#define BT_ISO_QOS_BIS_UNSET 0xff
|
||||
|
||||
struct bt_iso_io_qos {
|
||||
__u32 interval;
|
||||
__u16 latency;
|
||||
__u16 sdu;
|
||||
__u8 phy;
|
||||
__u8 rtn;
|
||||
};
|
||||
|
||||
struct bt_iso_qos {
|
||||
union {
|
||||
__u8 cig;
|
||||
__u8 big;
|
||||
};
|
||||
union {
|
||||
__u8 cis;
|
||||
__u8 bis;
|
||||
};
|
||||
union {
|
||||
__u8 sca;
|
||||
__u8 sync_interval;
|
||||
};
|
||||
__u8 packing;
|
||||
__u8 framing;
|
||||
struct bt_iso_io_qos in;
|
||||
struct bt_iso_io_qos out;
|
||||
};
|
||||
|
||||
#define BT_ISO_PHY_1M 0x01
|
||||
#define BT_ISO_PHY_2M 0x02
|
||||
#define BT_ISO_PHY_CODED 0x04
|
||||
#define BT_ISO_PHY_ANY (BT_ISO_PHY_1M | BT_ISO_PHY_2M | \
|
||||
BT_ISO_PHY_CODED)
|
||||
|
||||
#define BT_CODEC 19
|
||||
|
||||
struct bt_codec_caps {
|
||||
|
|
@ -177,6 +220,8 @@ struct bt_codecs {
|
|||
#define BT_CODEC_TRANSPARENT 0x03
|
||||
#define BT_CODEC_MSBC 0x05
|
||||
|
||||
#define BT_ISO_BASE 20
|
||||
|
||||
__printf(1, 2)
|
||||
void bt_info(const char *fmt, ...);
|
||||
__printf(1, 2)
|
||||
|
|
@ -494,7 +539,7 @@ static inline struct sk_buff *bt_skb_sendmmsg(struct sock *sk,
|
|||
struct sk_buff *skb, **frag;
|
||||
|
||||
skb = bt_skb_sendmsg(sk, msg, len, mtu, headroom, tailroom);
|
||||
if (IS_ERR_OR_NULL(skb))
|
||||
if (IS_ERR(skb))
|
||||
return skb;
|
||||
|
||||
len -= skb->len;
|
||||
|
|
@ -521,6 +566,7 @@ static inline struct sk_buff *bt_skb_sendmmsg(struct sock *sk,
|
|||
}
|
||||
|
||||
int bt_to_errno(u16 code);
|
||||
__u8 bt_status(int err);
|
||||
|
||||
void hci_sock_set_flag(struct sock *sk, int nr);
|
||||
void hci_sock_clear_flag(struct sock *sk, int nr);
|
||||
|
|
@ -558,6 +604,27 @@ static inline void sco_exit(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_LE)
|
||||
int iso_init(void);
|
||||
int iso_exit(void);
|
||||
bool iso_enabled(void);
|
||||
#else
|
||||
static inline int iso_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int iso_exit(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool iso_enabled(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
int mgmt_init(void);
|
||||
void mgmt_exit(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -228,17 +228,6 @@ enum {
|
|||
*/
|
||||
HCI_QUIRK_VALID_LE_STATES,
|
||||
|
||||
/* When this quirk is set, then erroneous data reporting
|
||||
* is ignored. This is mainly due to the fact that the HCI
|
||||
* Read Default Erroneous Data Reporting command is advertised,
|
||||
* but not supported; these controllers often reply with unknown
|
||||
* command and tend to lock up randomly. Needing a hard reset.
|
||||
*
|
||||
* This quirk can be set before hci_register_dev is called or
|
||||
* during the hdev->setup vendor callback.
|
||||
*/
|
||||
HCI_QUIRK_BROKEN_ERR_DATA_REPORTING,
|
||||
|
||||
/*
|
||||
* When this quirk is set, then the hci_suspend_notifier is not
|
||||
* registered. This is intended for devices which drop completely
|
||||
|
|
@ -327,6 +316,7 @@ enum {
|
|||
HCI_USER_CHANNEL,
|
||||
HCI_EXT_CONFIGURED,
|
||||
HCI_LE_ADV,
|
||||
HCI_LE_PER_ADV,
|
||||
HCI_LE_SCAN,
|
||||
HCI_SSP_ENABLED,
|
||||
HCI_SC_ENABLED,
|
||||
|
|
@ -349,6 +339,7 @@ enum {
|
|||
HCI_LE_SCAN_INTERRUPTED,
|
||||
HCI_WIDEBAND_SPEECH_ENABLED,
|
||||
HCI_EVENT_FILTER_CONFIGURED,
|
||||
HCI_PA_SYNC,
|
||||
|
||||
HCI_DUT_MODE,
|
||||
HCI_VENDOR_DIAG,
|
||||
|
|
@ -361,6 +352,7 @@ enum {
|
|||
HCI_QUALITY_REPORT,
|
||||
HCI_OFFLOAD_CODECS_ENABLED,
|
||||
HCI_LE_SIMULTANEOUS_ROLES,
|
||||
HCI_CMD_DRAIN_WORKQUEUE,
|
||||
|
||||
__HCI_NUM_FLAGS,
|
||||
};
|
||||
|
|
@ -496,6 +488,7 @@ enum {
|
|||
#define LMP_EXT_INQ 0x01
|
||||
#define LMP_SIMUL_LE_BR 0x02
|
||||
#define LMP_SIMPLE_PAIR 0x08
|
||||
#define LMP_ERR_DATA_REPORTING 0x20
|
||||
#define LMP_NO_FLUSH 0x40
|
||||
|
||||
#define LMP_LSTO 0x01
|
||||
|
|
@ -528,9 +521,11 @@ enum {
|
|||
#define HCI_LE_PHY_2M 0x01
|
||||
#define HCI_LE_PHY_CODED 0x08
|
||||
#define HCI_LE_EXT_ADV 0x10
|
||||
#define HCI_LE_PERIODIC_ADV 0x20
|
||||
#define HCI_LE_CHAN_SEL_ALG2 0x40
|
||||
#define HCI_LE_CIS_CENTRAL 0x10
|
||||
#define HCI_LE_CIS_PERIPHERAL 0x20
|
||||
#define HCI_LE_ISO_BROADCASTER 0x40
|
||||
|
||||
/* Connection modes */
|
||||
#define HCI_CM_ACTIVE 0x0000
|
||||
|
|
@ -1874,6 +1869,22 @@ struct hci_cp_le_ext_conn_param {
|
|||
__le16 max_ce_len;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_PA_CREATE_SYNC 0x2044
|
||||
struct hci_cp_le_pa_create_sync {
|
||||
__u8 options;
|
||||
__u8 sid;
|
||||
__u8 addr_type;
|
||||
bdaddr_t addr;
|
||||
__le16 skip;
|
||||
__le16 sync_timeout;
|
||||
__u8 sync_cte_type;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_PA_TERM_SYNC 0x2046
|
||||
struct hci_cp_le_pa_term_sync {
|
||||
__le16 handle;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_READ_NUM_SUPPORTED_ADV_SETS 0x203b
|
||||
struct hci_rp_le_read_num_supported_adv_sets {
|
||||
__u8 status;
|
||||
|
|
@ -1908,13 +1919,6 @@ struct hci_rp_le_set_ext_adv_params {
|
|||
__u8 tx_power;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_SET_EXT_ADV_ENABLE 0x2039
|
||||
struct hci_cp_le_set_ext_adv_enable {
|
||||
__u8 enable;
|
||||
__u8 num_of_sets;
|
||||
__u8 data[];
|
||||
} __packed;
|
||||
|
||||
struct hci_cp_ext_adv_set {
|
||||
__u8 handle;
|
||||
__le16 duration;
|
||||
|
|
@ -1941,6 +1945,37 @@ struct hci_cp_le_set_ext_scan_rsp_data {
|
|||
__u8 data[];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_SET_EXT_ADV_ENABLE 0x2039
|
||||
struct hci_cp_le_set_ext_adv_enable {
|
||||
__u8 enable;
|
||||
__u8 num_of_sets;
|
||||
__u8 data[];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_SET_PER_ADV_PARAMS 0x203e
|
||||
struct hci_cp_le_set_per_adv_params {
|
||||
__u8 handle;
|
||||
__le16 min_interval;
|
||||
__le16 max_interval;
|
||||
__le16 periodic_properties;
|
||||
} __packed;
|
||||
|
||||
#define HCI_MAX_PER_AD_LENGTH 252
|
||||
|
||||
#define HCI_OP_LE_SET_PER_ADV_DATA 0x203f
|
||||
struct hci_cp_le_set_per_adv_data {
|
||||
__u8 handle;
|
||||
__u8 operation;
|
||||
__u8 length;
|
||||
__u8 data[];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_SET_PER_ADV_ENABLE 0x2040
|
||||
struct hci_cp_le_set_per_adv_enable {
|
||||
__u8 enable;
|
||||
__u8 handle;
|
||||
} __packed;
|
||||
|
||||
#define LE_SET_ADV_DATA_OP_COMPLETE 0x03
|
||||
|
||||
#define LE_SET_ADV_DATA_NO_FRAG 0x01
|
||||
|
|
@ -1998,7 +2033,7 @@ struct hci_rp_le_read_iso_tx_sync {
|
|||
struct hci_cis_params {
|
||||
__u8 cis_id;
|
||||
__le16 c_sdu;
|
||||
__le16 p_pdu;
|
||||
__le16 p_sdu;
|
||||
__u8 c_phy;
|
||||
__u8 p_phy;
|
||||
__u8 c_rtn;
|
||||
|
|
@ -2009,7 +2044,7 @@ struct hci_cp_le_set_cig_params {
|
|||
__u8 cig_id;
|
||||
__u8 c_interval[3];
|
||||
__u8 p_interval[3];
|
||||
__u8 wc_sca;
|
||||
__u8 sca;
|
||||
__u8 packing;
|
||||
__u8 framing;
|
||||
__le16 c_latency;
|
||||
|
|
@ -2052,6 +2087,73 @@ struct hci_cp_le_reject_cis {
|
|||
__u8 reason;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_CREATE_BIG 0x2068
|
||||
struct hci_bis {
|
||||
__u8 sdu_interval[3];
|
||||
__le16 sdu;
|
||||
__le16 latency;
|
||||
__u8 rtn;
|
||||
__u8 phy;
|
||||
__u8 packing;
|
||||
__u8 framing;
|
||||
__u8 encryption;
|
||||
__u8 bcode[16];
|
||||
} __packed;
|
||||
|
||||
struct hci_cp_le_create_big {
|
||||
__u8 handle;
|
||||
__u8 adv_handle;
|
||||
__u8 num_bis;
|
||||
struct hci_bis bis;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_TERM_BIG 0x206a
|
||||
struct hci_cp_le_term_big {
|
||||
__u8 handle;
|
||||
__u8 reason;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_BIG_CREATE_SYNC 0x206b
|
||||
struct hci_cp_le_big_create_sync {
|
||||
__u8 handle;
|
||||
__le16 sync_handle;
|
||||
__u8 encryption;
|
||||
__u8 bcode[16];
|
||||
__u8 mse;
|
||||
__le16 timeout;
|
||||
__u8 num_bis;
|
||||
__u8 bis[0];
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_BIG_TERM_SYNC 0x206c
|
||||
struct hci_cp_le_big_term_sync {
|
||||
__u8 handle;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_SETUP_ISO_PATH 0x206e
|
||||
struct hci_cp_le_setup_iso_path {
|
||||
__le16 handle;
|
||||
__u8 direction;
|
||||
__u8 path;
|
||||
__u8 codec;
|
||||
__le16 codec_cid;
|
||||
__le16 codec_vid;
|
||||
__u8 delay[3];
|
||||
__u8 codec_cfg_len;
|
||||
__u8 codec_cfg[0];
|
||||
} __packed;
|
||||
|
||||
struct hci_rp_le_setup_iso_path {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_SET_HOST_FEATURE 0x2074
|
||||
struct hci_cp_le_set_host_feature {
|
||||
__u8 bit_number;
|
||||
__u8 bit_value;
|
||||
} __packed;
|
||||
|
||||
/* ---- HCI Events ---- */
|
||||
struct hci_ev_status {
|
||||
__u8 status;
|
||||
|
|
@ -2580,6 +2682,18 @@ struct hci_ev_le_ext_adv_report {
|
|||
struct hci_ev_le_ext_adv_info info[];
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_LE_PA_SYNC_ESTABLISHED 0x0e
|
||||
struct hci_ev_le_pa_sync_established {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
__u8 sid;
|
||||
__u8 bdaddr_type;
|
||||
bdaddr_t bdaddr;
|
||||
__u8 phy;
|
||||
__le16 interval;
|
||||
__u8 clock_accuracy;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_LE_ENHANCED_CONN_COMPLETE 0x0a
|
||||
struct hci_ev_le_enh_conn_complete {
|
||||
__u8 status;
|
||||
|
|
@ -2631,6 +2745,55 @@ struct hci_evt_le_cis_req {
|
|||
__u8 cis_id;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EVT_LE_CREATE_BIG_COMPLETE 0x1b
|
||||
struct hci_evt_le_create_big_complete {
|
||||
__u8 status;
|
||||
__u8 handle;
|
||||
__u8 sync_delay[3];
|
||||
__u8 transport_delay[3];
|
||||
__u8 phy;
|
||||
__u8 nse;
|
||||
__u8 bn;
|
||||
__u8 pto;
|
||||
__u8 irc;
|
||||
__le16 max_pdu;
|
||||
__le16 interval;
|
||||
__u8 num_bis;
|
||||
__le16 bis_handle[];
|
||||
} __packed;
|
||||
|
||||
#define HCI_EVT_LE_BIG_SYNC_ESTABILISHED 0x1d
|
||||
struct hci_evt_le_big_sync_estabilished {
|
||||
__u8 status;
|
||||
__u8 handle;
|
||||
__u8 latency[3];
|
||||
__u8 nse;
|
||||
__u8 bn;
|
||||
__u8 pto;
|
||||
__u8 irc;
|
||||
__le16 max_pdu;
|
||||
__le16 interval;
|
||||
__u8 num_bis;
|
||||
__le16 bis[];
|
||||
} __packed;
|
||||
|
||||
#define HCI_EVT_LE_BIG_INFO_ADV_REPORT 0x22
|
||||
struct hci_evt_le_big_info_adv_report {
|
||||
__le16 sync_handle;
|
||||
__u8 num_bis;
|
||||
__u8 nse;
|
||||
__le16 iso_interval;
|
||||
__u8 bn;
|
||||
__u8 pto;
|
||||
__u8 irc;
|
||||
__le16 max_pdu;
|
||||
__u8 sdu_interval[3];
|
||||
__le16 max_sdu;
|
||||
__u8 phy;
|
||||
__u8 framing;
|
||||
__u8 encryption;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_VENDOR 0xff
|
||||
|
||||
/* Internal events generated by Bluetooth stack */
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ struct hci_conn_hash {
|
|||
unsigned int acl_num;
|
||||
unsigned int amp_num;
|
||||
unsigned int sco_num;
|
||||
unsigned int iso_num;
|
||||
unsigned int le_num;
|
||||
unsigned int le_num_peripheral;
|
||||
};
|
||||
|
|
@ -234,8 +235,9 @@ struct oob_data {
|
|||
|
||||
struct adv_info {
|
||||
struct list_head list;
|
||||
bool enabled;
|
||||
bool pending;
|
||||
bool enabled;
|
||||
bool pending;
|
||||
bool periodic;
|
||||
__u8 instance;
|
||||
__u32 flags;
|
||||
__u16 timeout;
|
||||
|
|
@ -243,8 +245,12 @@ struct adv_info {
|
|||
__u16 duration;
|
||||
__u16 adv_data_len;
|
||||
__u8 adv_data[HCI_MAX_EXT_AD_LENGTH];
|
||||
bool adv_data_changed;
|
||||
__u16 scan_rsp_len;
|
||||
__u8 scan_rsp_data[HCI_MAX_EXT_AD_LENGTH];
|
||||
bool scan_rsp_changed;
|
||||
__u16 per_adv_data_len;
|
||||
__u8 per_adv_data[HCI_MAX_PER_AD_LENGTH];
|
||||
__s8 tx_power;
|
||||
__u32 min_interval;
|
||||
__u32 max_interval;
|
||||
|
|
@ -258,6 +264,15 @@ struct adv_info {
|
|||
|
||||
#define HCI_ADV_TX_POWER_NO_PREFERENCE 0x7F
|
||||
|
||||
#define DATA_CMP(_d1, _l1, _d2, _l2) \
|
||||
(_l1 == _l2 ? memcmp(_d1, _d2, _l1) : _l1 - _l2)
|
||||
|
||||
#define ADV_DATA_CMP(_adv, _data, _len) \
|
||||
DATA_CMP((_adv)->adv_data, (_adv)->adv_data_len, _data, _len)
|
||||
|
||||
#define SCAN_RSP_CMP(_adv, _data, _len) \
|
||||
DATA_CMP((_adv)->scan_rsp_data, (_adv)->scan_rsp_len, _data, _len)
|
||||
|
||||
struct monitored_device {
|
||||
struct list_head list;
|
||||
|
||||
|
|
@ -463,13 +478,16 @@ struct hci_dev {
|
|||
unsigned int acl_cnt;
|
||||
unsigned int sco_cnt;
|
||||
unsigned int le_cnt;
|
||||
unsigned int iso_cnt;
|
||||
|
||||
unsigned int acl_mtu;
|
||||
unsigned int sco_mtu;
|
||||
unsigned int le_mtu;
|
||||
unsigned int iso_mtu;
|
||||
unsigned int acl_pkts;
|
||||
unsigned int sco_pkts;
|
||||
unsigned int le_pkts;
|
||||
unsigned int iso_pkts;
|
||||
|
||||
__u16 block_len;
|
||||
__u16 block_mtu;
|
||||
|
|
@ -506,8 +524,6 @@ struct hci_dev {
|
|||
struct work_struct cmd_work;
|
||||
struct work_struct tx_work;
|
||||
|
||||
struct work_struct discov_update;
|
||||
struct work_struct scan_update;
|
||||
struct delayed_work le_scan_disable;
|
||||
struct delayed_work le_scan_restart;
|
||||
|
||||
|
|
@ -516,6 +532,7 @@ struct hci_dev {
|
|||
struct sk_buff_head cmd_q;
|
||||
|
||||
struct sk_buff *sent_cmd;
|
||||
struct sk_buff *recv_event;
|
||||
|
||||
struct mutex req_lock;
|
||||
wait_queue_head_t req_wait_q;
|
||||
|
|
@ -580,6 +597,8 @@ struct hci_dev {
|
|||
__u8 adv_data_len;
|
||||
__u8 scan_rsp_data[HCI_MAX_EXT_AD_LENGTH];
|
||||
__u8 scan_rsp_data_len;
|
||||
__u8 per_adv_data[HCI_MAX_PER_AD_LENGTH];
|
||||
__u8 per_adv_data_len;
|
||||
|
||||
struct list_head adv_instances;
|
||||
unsigned int adv_instance_cnt;
|
||||
|
|
@ -647,6 +666,7 @@ enum conn_reasons {
|
|||
CONN_REASON_PAIR_DEVICE,
|
||||
CONN_REASON_L2CAP_CHAN,
|
||||
CONN_REASON_SCO_CONNECT,
|
||||
CONN_REASON_ISO_CONNECT,
|
||||
};
|
||||
|
||||
struct hci_conn {
|
||||
|
|
@ -664,6 +684,7 @@ struct hci_conn {
|
|||
__u8 resp_addr_type;
|
||||
__u8 adv_instance;
|
||||
__u16 handle;
|
||||
__u16 sync_handle;
|
||||
__u16 state;
|
||||
__u8 mode;
|
||||
__u8 type;
|
||||
|
|
@ -694,11 +715,14 @@ struct hci_conn {
|
|||
__u16 le_supv_timeout;
|
||||
__u8 le_adv_data[HCI_MAX_AD_LENGTH];
|
||||
__u8 le_adv_data_len;
|
||||
__u8 le_per_adv_data[HCI_MAX_PER_AD_LENGTH];
|
||||
__u8 le_per_adv_data_len;
|
||||
__u8 le_tx_phy;
|
||||
__u8 le_rx_phy;
|
||||
__s8 rssi;
|
||||
__s8 tx_power;
|
||||
__s8 max_tx_power;
|
||||
struct bt_iso_qos iso_qos;
|
||||
unsigned long flags;
|
||||
|
||||
enum conn_reasons conn_reason;
|
||||
|
|
@ -729,6 +753,7 @@ struct hci_conn {
|
|||
struct hci_dev *hdev;
|
||||
void *l2cap_data;
|
||||
void *sco_data;
|
||||
void *iso_data;
|
||||
struct amp_mgr *amp_mgr;
|
||||
|
||||
struct hci_conn *link;
|
||||
|
|
@ -737,6 +762,8 @@ struct hci_conn {
|
|||
void (*connect_cfm_cb) (struct hci_conn *conn, u8 status);
|
||||
void (*security_cfm_cb) (struct hci_conn *conn, u8 status);
|
||||
void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason);
|
||||
|
||||
void (*cleanup)(struct hci_conn *conn);
|
||||
};
|
||||
|
||||
struct hci_chan {
|
||||
|
|
@ -824,6 +851,21 @@ static inline void sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_LE)
|
||||
int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
|
||||
void iso_recv(struct hci_conn *hcon, struct sk_buff *skb, u16 flags);
|
||||
#else
|
||||
static inline int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
__u8 *flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void iso_recv(struct hci_conn *hcon, struct sk_buff *skb,
|
||||
u16 flags)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ----- Inquiry cache ----- */
|
||||
#define INQUIRY_CACHE_AGE_MAX (HZ*30) /* 30 seconds */
|
||||
#define INQUIRY_ENTRY_AGE_MAX (HZ*60) /* 60 seconds */
|
||||
|
|
@ -908,6 +950,7 @@ enum {
|
|||
HCI_CONN_NEW_LINK_KEY,
|
||||
HCI_CONN_SCANNING,
|
||||
HCI_CONN_AUTH_FAILURE,
|
||||
HCI_CONN_PER_ADV,
|
||||
};
|
||||
|
||||
static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
|
||||
|
|
@ -944,6 +987,9 @@ static inline void hci_conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
|
|||
case ESCO_LINK:
|
||||
h->sco_num++;
|
||||
break;
|
||||
case ISO_LINK:
|
||||
h->iso_num++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -970,6 +1016,9 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
|
|||
case ESCO_LINK:
|
||||
h->sco_num--;
|
||||
break;
|
||||
case ISO_LINK:
|
||||
h->iso_num--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -986,6 +1035,8 @@ static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type)
|
|||
case SCO_LINK:
|
||||
case ESCO_LINK:
|
||||
return h->sco_num;
|
||||
case ISO_LINK:
|
||||
return h->iso_num;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -995,7 +1046,7 @@ static inline unsigned int hci_conn_count(struct hci_dev *hdev)
|
|||
{
|
||||
struct hci_conn_hash *c = &hdev->conn_hash;
|
||||
|
||||
return c->acl_num + c->amp_num + c->sco_num + c->le_num;
|
||||
return c->acl_num + c->amp_num + c->sco_num + c->le_num + c->iso_num;
|
||||
}
|
||||
|
||||
static inline __u8 hci_conn_lookup_type(struct hci_dev *hdev, __u16 handle)
|
||||
|
|
@ -1018,6 +1069,29 @@ static inline __u8 hci_conn_lookup_type(struct hci_dev *hdev, __u16 handle)
|
|||
return type;
|
||||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_bis(struct hci_dev *hdev,
|
||||
bdaddr_t *ba,
|
||||
__u8 big, __u8 bis)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *c;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (bacmp(&c->dst, ba) || c->type != ISO_LINK)
|
||||
continue;
|
||||
|
||||
if (c->iso_qos.big == big && c->iso_qos.bis == bis) {
|
||||
rcu_read_unlock();
|
||||
return c;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev,
|
||||
__u16 handle)
|
||||
{
|
||||
|
|
@ -1081,6 +1155,76 @@ static inline struct hci_conn *hci_conn_hash_lookup_le(struct hci_dev *hdev,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_cis(struct hci_dev *hdev,
|
||||
bdaddr_t *ba,
|
||||
__u8 ba_type)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *c;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type != ISO_LINK)
|
||||
continue;
|
||||
|
||||
if (ba_type == c->dst_type && !bacmp(&c->dst, ba)) {
|
||||
rcu_read_unlock();
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_cig(struct hci_dev *hdev,
|
||||
__u8 handle)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *c;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type != ISO_LINK)
|
||||
continue;
|
||||
|
||||
if (handle == c->iso_qos.cig) {
|
||||
rcu_read_unlock();
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_big(struct hci_dev *hdev,
|
||||
__u8 handle)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *c;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (bacmp(&c->dst, BDADDR_ANY) || c->type != ISO_LINK)
|
||||
continue;
|
||||
|
||||
if (handle == c->iso_qos.big) {
|
||||
rcu_read_unlock();
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
|
||||
__u8 type, __u16 state)
|
||||
{
|
||||
|
|
@ -1101,6 +1245,27 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
typedef void (*hci_conn_func_t)(struct hci_conn *conn, void *data);
|
||||
static inline void hci_conn_hash_list_state(struct hci_dev *hdev,
|
||||
hci_conn_func_t func, __u8 type,
|
||||
__u16 state, void *data)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
struct hci_conn *c;
|
||||
|
||||
if (!func)
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(c, &h->list, list) {
|
||||
if (c->type == type && c->state == state)
|
||||
func(c, data);
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static inline struct hci_conn *hci_lookup_le_connect(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_conn_hash *h = &hdev->conn_hash;
|
||||
|
|
@ -1124,6 +1289,8 @@ static inline struct hci_conn *hci_lookup_le_connect(struct hci_dev *hdev)
|
|||
int hci_disconnect(struct hci_conn *conn, __u8 reason);
|
||||
bool hci_setup_sync(struct hci_conn *conn, __u16 handle);
|
||||
void hci_sco_setup(struct hci_conn *conn, __u8 status);
|
||||
bool hci_iso_setup_path(struct hci_conn *conn);
|
||||
int hci_le_create_cis(struct hci_conn *conn);
|
||||
|
||||
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||
u8 role);
|
||||
|
|
@ -1148,6 +1315,17 @@ struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
|
|||
enum conn_reasons conn_reason);
|
||||
struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||
__u16 setting, struct bt_codec *codec);
|
||||
struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos);
|
||||
struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos);
|
||||
struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos,
|
||||
__u8 data_len, __u8 *data);
|
||||
int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type,
|
||||
__u8 sid);
|
||||
int hci_le_big_create_sync(struct hci_dev *hdev, struct bt_iso_qos *qos,
|
||||
__u16 sync_handle, __u8 num_bis, __u8 bis[]);
|
||||
int hci_conn_check_link_mode(struct hci_conn *conn);
|
||||
int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
|
||||
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type,
|
||||
|
|
@ -1286,6 +1464,8 @@ void hci_free_dev(struct hci_dev *hdev);
|
|||
int hci_register_dev(struct hci_dev *hdev);
|
||||
void hci_unregister_dev(struct hci_dev *hdev);
|
||||
void hci_release_dev(struct hci_dev *hdev);
|
||||
int hci_register_suspend_notifier(struct hci_dev *hdev);
|
||||
int hci_unregister_suspend_notifier(struct hci_dev *hdev);
|
||||
int hci_suspend_dev(struct hci_dev *hdev);
|
||||
int hci_resume_dev(struct hci_dev *hdev);
|
||||
int hci_reset_dev(struct hci_dev *hdev);
|
||||
|
|
@ -1392,11 +1572,14 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
void hci_adv_instances_clear(struct hci_dev *hdev);
|
||||
struct adv_info *hci_find_adv_instance(struct hci_dev *hdev, u8 instance);
|
||||
struct adv_info *hci_get_next_instance(struct hci_dev *hdev, u8 instance);
|
||||
int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
|
||||
u16 adv_data_len, u8 *adv_data,
|
||||
u16 scan_rsp_len, u8 *scan_rsp_data,
|
||||
u16 timeout, u16 duration, s8 tx_power,
|
||||
u32 min_interval, u32 max_interval);
|
||||
struct adv_info *hci_add_adv_instance(struct hci_dev *hdev, u8 instance,
|
||||
u32 flags, u16 adv_data_len, u8 *adv_data,
|
||||
u16 scan_rsp_len, u8 *scan_rsp_data,
|
||||
u16 timeout, u16 duration, s8 tx_power,
|
||||
u32 min_interval, u32 max_interval);
|
||||
struct adv_info *hci_add_per_instance(struct hci_dev *hdev, u8 instance,
|
||||
u32 flags, u8 data_len, u8 *data,
|
||||
u32 min_interval, u32 max_interval);
|
||||
int hci_set_adv_instance_data(struct hci_dev *hdev, u8 instance,
|
||||
u16 adv_data_len, u8 *adv_data,
|
||||
u16 scan_rsp_len, u8 *scan_rsp_data);
|
||||
|
|
@ -1407,12 +1590,9 @@ bool hci_adv_instance_is_scannable(struct hci_dev *hdev, u8 instance);
|
|||
|
||||
void hci_adv_monitors_clear(struct hci_dev *hdev);
|
||||
void hci_free_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor);
|
||||
int hci_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status);
|
||||
int hci_remove_adv_monitor_complete(struct hci_dev *hdev, u8 status);
|
||||
bool hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
|
||||
int *err);
|
||||
bool hci_remove_single_adv_monitor(struct hci_dev *hdev, u16 handle, int *err);
|
||||
bool hci_remove_all_adv_monitor(struct hci_dev *hdev, int *err);
|
||||
int hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor);
|
||||
int hci_remove_single_adv_monitor(struct hci_dev *hdev, u16 handle);
|
||||
int hci_remove_all_adv_monitor(struct hci_dev *hdev);
|
||||
bool hci_is_adv_monitoring(struct hci_dev *hdev);
|
||||
int hci_get_adv_monitor_offload_ext(struct hci_dev *hdev);
|
||||
|
||||
|
|
@ -1516,6 +1696,19 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
|||
#define use_enhanced_conn_complete(dev) (ll_privacy_capable(dev) || \
|
||||
ext_adv_capable(dev))
|
||||
|
||||
/* Periodic advertising support */
|
||||
#define per_adv_capable(dev) (((dev)->le_features[1] & HCI_LE_PERIODIC_ADV))
|
||||
|
||||
/* CIS Master/Slave and BIS support */
|
||||
#define iso_capable(dev) (cis_capable(dev) || bis_capable(dev))
|
||||
#define cis_capable(dev) \
|
||||
(cis_central_capable(dev) || cis_peripheral_capable(dev))
|
||||
#define cis_central_capable(dev) \
|
||||
((dev)->le_features[3] & HCI_LE_CIS_CENTRAL)
|
||||
#define cis_peripheral_capable(dev) \
|
||||
((dev)->le_features[3] & HCI_LE_CIS_PERIPHERAL)
|
||||
#define bis_capable(dev) ((dev)->le_features[3] & HCI_LE_ISO_BROADCASTER)
|
||||
|
||||
/* ----- HCI protocols ----- */
|
||||
#define HCI_PROTO_DEFER 0x01
|
||||
|
||||
|
|
@ -1530,6 +1723,9 @@ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
case ESCO_LINK:
|
||||
return sco_connect_ind(hdev, bdaddr, flags);
|
||||
|
||||
case ISO_LINK:
|
||||
return iso_connect_ind(hdev, bdaddr, flags);
|
||||
|
||||
default:
|
||||
BT_ERR("unknown link type %d", type);
|
||||
return -EINVAL;
|
||||
|
|
@ -1737,8 +1933,10 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
|
|||
const void *param);
|
||||
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
|
||||
void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
|
||||
void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb);
|
||||
|
||||
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
|
||||
void *hci_recv_event_data(struct hci_dev *hdev, __u8 event);
|
||||
|
||||
u32 hci_conn_get_phy(struct hci_conn *conn);
|
||||
|
||||
|
|
@ -1797,6 +1995,8 @@ void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c);
|
|||
#define DISCOV_LE_RESTART_DELAY msecs_to_jiffies(200) /* msec */
|
||||
#define DISCOV_LE_FAST_ADV_INT_MIN 0x00A0 /* 100 msec */
|
||||
#define DISCOV_LE_FAST_ADV_INT_MAX 0x00F0 /* 150 msec */
|
||||
#define DISCOV_LE_PER_ADV_INT_MIN 0x00A0 /* 200 msec */
|
||||
#define DISCOV_LE_PER_ADV_INT_MAX 0x00A0 /* 200 msec */
|
||||
|
||||
#define NAME_RESOLVE_DURATION msecs_to_jiffies(10240) /* 10.24 sec */
|
||||
|
||||
|
|
@ -1872,8 +2072,6 @@ void mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev,
|
|||
u8 instance);
|
||||
void mgmt_adv_monitor_removed(struct hci_dev *hdev, u16 handle);
|
||||
int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip);
|
||||
int mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status);
|
||||
int mgmt_remove_adv_monitor_complete(struct hci_dev *hdev, u8 status);
|
||||
void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle,
|
||||
bdaddr_t *bdaddr, u8 addr_type);
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,8 @@ struct hci_dev_info {
|
|||
__u16 acl_pkts;
|
||||
__u16 sco_mtu;
|
||||
__u16 sco_pkts;
|
||||
__u16 iso_mtu;
|
||||
__u16 iso_pkts;
|
||||
|
||||
struct hci_dev_stats stat;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -65,6 +65,10 @@ int hci_enable_ext_advertising_sync(struct hci_dev *hdev, u8 instance);
|
|||
int hci_enable_advertising_sync(struct hci_dev *hdev);
|
||||
int hci_enable_advertising(struct hci_dev *hdev);
|
||||
|
||||
int hci_start_per_adv_sync(struct hci_dev *hdev, u8 instance, u8 data_len,
|
||||
u8 *data, u32 flags, u16 min_interval,
|
||||
u16 max_interval, u16 sync_interval);
|
||||
|
||||
int hci_remove_advertising_sync(struct hci_dev *hdev, struct sock *sk,
|
||||
u8 instance, bool force);
|
||||
int hci_disable_advertising_sync(struct hci_dev *hdev);
|
||||
|
|
@ -78,10 +82,12 @@ int hci_read_clock_sync(struct hci_dev *hdev, struct hci_cp_read_clock *cp);
|
|||
|
||||
int hci_write_fast_connectable_sync(struct hci_dev *hdev, bool enable);
|
||||
int hci_update_scan_sync(struct hci_dev *hdev);
|
||||
int hci_update_scan(struct hci_dev *hdev);
|
||||
|
||||
int hci_write_le_host_supported_sync(struct hci_dev *hdev, u8 le, u8 simul);
|
||||
int hci_remove_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance,
|
||||
struct sock *sk);
|
||||
int hci_remove_ext_adv_instance(struct hci_dev *hdev, u8 instance);
|
||||
struct sk_buff *hci_read_local_oob_data_sync(struct hci_dev *hdev, bool ext,
|
||||
struct sock *sk);
|
||||
|
||||
|
|
@ -105,4 +111,14 @@ int hci_resume_sync(struct hci_dev *hdev);
|
|||
|
||||
struct hci_conn;
|
||||
|
||||
int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason);
|
||||
|
||||
int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
|
||||
int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle);
|
||||
|
||||
int hci_le_terminate_big_sync(struct hci_dev *hdev, u8 handle, u8 reason);
|
||||
|
||||
int hci_le_big_terminate_sync(struct hci_dev *hdev, u8 handle);
|
||||
|
||||
int hci_le_pa_terminate_sync(struct hci_dev *hdev, u16 handle);
|
||||
|
|
|
|||
32
include/net/bluetooth/iso.h
Normal file
32
include/net/bluetooth/iso.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* BlueZ - Bluetooth protocol stack for Linux
|
||||
*
|
||||
* Copyright (C) 2022 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef __ISO_H
|
||||
#define __ISO_H
|
||||
|
||||
/* ISO defaults */
|
||||
#define ISO_DEFAULT_MTU 251
|
||||
#define ISO_MAX_NUM_BIS 0x1f
|
||||
|
||||
/* ISO socket broadcast address */
|
||||
struct sockaddr_iso_bc {
|
||||
bdaddr_t bc_bdaddr;
|
||||
__u8 bc_bdaddr_type;
|
||||
__u8 bc_sid;
|
||||
__u8 bc_num_bis;
|
||||
__u8 bc_bis[ISO_MAX_NUM_BIS];
|
||||
};
|
||||
|
||||
/* ISO socket address */
|
||||
struct sockaddr_iso {
|
||||
sa_family_t iso_family;
|
||||
bdaddr_t iso_bdaddr;
|
||||
__u8 iso_bdaddr_type;
|
||||
struct sockaddr_iso_bc iso_bc[];
|
||||
};
|
||||
|
||||
#endif /* __ISO_H */
|
||||
|
|
@ -29,6 +29,7 @@ menuconfig BT
|
|||
SCO audio links
|
||||
L2CAP (Logical Link Control and Adaptation Protocol)
|
||||
SMP (Security Manager Protocol) on LE (Low Energy) links
|
||||
ISO isochronous links
|
||||
HCI Device drivers (Interface to the hardware)
|
||||
RFCOMM Module (RFCOMM Protocol)
|
||||
BNEP Module (Bluetooth Network Encapsulation Protocol)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
|
|||
eir.o hci_sync.o
|
||||
|
||||
bluetooth-$(CONFIG_BT_BREDR) += sco.o
|
||||
bluetooth-$(CONFIG_BT_LE) += iso.o
|
||||
bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
|
||||
bluetooth-$(CONFIG_BT_LEDS) += leds.o
|
||||
bluetooth-$(CONFIG_BT_MSFTEXT) += msft.o
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
#include "selftest.h"
|
||||
|
||||
/* Bluetooth sockets */
|
||||
#define BT_MAX_PROTO 8
|
||||
#define BT_MAX_PROTO (BTPROTO_LAST + 1)
|
||||
static const struct net_proto_family *bt_proto[BT_MAX_PROTO];
|
||||
static DEFINE_RWLOCK(bt_proto_lock);
|
||||
|
||||
|
|
@ -52,6 +52,7 @@ static const char *const bt_key_strings[BT_MAX_PROTO] = {
|
|||
"sk_lock-AF_BLUETOOTH-BTPROTO_CMTP",
|
||||
"sk_lock-AF_BLUETOOTH-BTPROTO_HIDP",
|
||||
"sk_lock-AF_BLUETOOTH-BTPROTO_AVDTP",
|
||||
"sk_lock-AF_BLUETOOTH-BTPROTO_ISO",
|
||||
};
|
||||
|
||||
static struct lock_class_key bt_slock_key[BT_MAX_PROTO];
|
||||
|
|
@ -64,6 +65,7 @@ static const char *const bt_slock_key_strings[BT_MAX_PROTO] = {
|
|||
"slock-AF_BLUETOOTH-BTPROTO_CMTP",
|
||||
"slock-AF_BLUETOOTH-BTPROTO_HIDP",
|
||||
"slock-AF_BLUETOOTH-BTPROTO_AVDTP",
|
||||
"slock-AF_BLUETOOTH-BTPROTO_ISO",
|
||||
};
|
||||
|
||||
void bt_sock_reclassify_lock(struct sock *sk, int proto)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,20 @@
|
|||
|
||||
#define PNP_INFO_SVCLASS_ID 0x1200
|
||||
|
||||
static u8 eir_append_name(u8 *eir, u16 eir_len, u8 type, u8 *data, u8 data_len)
|
||||
{
|
||||
u8 name[HCI_MAX_SHORT_NAME_LENGTH + 1];
|
||||
|
||||
/* If data is already NULL terminated just pass it directly */
|
||||
if (data[data_len - 1] == '\0')
|
||||
return eir_append_data(eir, eir_len, type, data, data_len);
|
||||
|
||||
memcpy(name, data, HCI_MAX_SHORT_NAME_LENGTH);
|
||||
name[HCI_MAX_SHORT_NAME_LENGTH] = '\0';
|
||||
|
||||
return eir_append_data(eir, eir_len, type, name, sizeof(name));
|
||||
}
|
||||
|
||||
u8 eir_append_local_name(struct hci_dev *hdev, u8 *ptr, u8 ad_len)
|
||||
{
|
||||
size_t short_len;
|
||||
|
|
@ -23,29 +37,26 @@ u8 eir_append_local_name(struct hci_dev *hdev, u8 *ptr, u8 ad_len)
|
|||
return ad_len;
|
||||
|
||||
/* use complete name if present and fits */
|
||||
complete_len = strlen(hdev->dev_name);
|
||||
complete_len = strnlen(hdev->dev_name, sizeof(hdev->dev_name));
|
||||
if (complete_len && complete_len <= HCI_MAX_SHORT_NAME_LENGTH)
|
||||
return eir_append_data(ptr, ad_len, EIR_NAME_COMPLETE,
|
||||
return eir_append_name(ptr, ad_len, EIR_NAME_COMPLETE,
|
||||
hdev->dev_name, complete_len + 1);
|
||||
|
||||
/* use short name if present */
|
||||
short_len = strlen(hdev->short_name);
|
||||
short_len = strnlen(hdev->short_name, sizeof(hdev->short_name));
|
||||
if (short_len)
|
||||
return eir_append_data(ptr, ad_len, EIR_NAME_SHORT,
|
||||
hdev->short_name, short_len + 1);
|
||||
return eir_append_name(ptr, ad_len, EIR_NAME_SHORT,
|
||||
hdev->short_name,
|
||||
short_len == HCI_MAX_SHORT_NAME_LENGTH ?
|
||||
short_len : short_len + 1);
|
||||
|
||||
/* use shortened full name if present, we already know that name
|
||||
* is longer then HCI_MAX_SHORT_NAME_LENGTH
|
||||
*/
|
||||
if (complete_len) {
|
||||
u8 name[HCI_MAX_SHORT_NAME_LENGTH + 1];
|
||||
|
||||
memcpy(name, hdev->dev_name, HCI_MAX_SHORT_NAME_LENGTH);
|
||||
name[HCI_MAX_SHORT_NAME_LENGTH] = '\0';
|
||||
|
||||
return eir_append_data(ptr, ad_len, EIR_NAME_SHORT, name,
|
||||
sizeof(name));
|
||||
}
|
||||
if (complete_len)
|
||||
return eir_append_name(ptr, ad_len, EIR_NAME_SHORT,
|
||||
hdev->dev_name,
|
||||
HCI_MAX_SHORT_NAME_LENGTH);
|
||||
|
||||
return ad_len;
|
||||
}
|
||||
|
|
@ -181,7 +192,7 @@ void eir_create(struct hci_dev *hdev, u8 *data)
|
|||
u8 *ptr = data;
|
||||
size_t name_len;
|
||||
|
||||
name_len = strlen(hdev->dev_name);
|
||||
name_len = strnlen(hdev->dev_name, sizeof(hdev->dev_name));
|
||||
|
||||
if (name_len > 0) {
|
||||
/* EIR Data type */
|
||||
|
|
@ -225,6 +236,27 @@ void eir_create(struct hci_dev *hdev, u8 *data)
|
|||
ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data));
|
||||
}
|
||||
|
||||
u8 eir_create_per_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
|
||||
{
|
||||
struct adv_info *adv = NULL;
|
||||
u8 ad_len = 0;
|
||||
|
||||
/* Return 0 when the current instance identifier is invalid. */
|
||||
if (instance) {
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (adv) {
|
||||
memcpy(ptr, adv->per_adv_data, adv->per_adv_data_len);
|
||||
ad_len += adv->per_adv_data_len;
|
||||
ptr += adv->per_adv_data_len;
|
||||
}
|
||||
|
||||
return ad_len;
|
||||
}
|
||||
|
||||
u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
|
||||
{
|
||||
struct adv_info *adv = NULL;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ void eir_create(struct hci_dev *hdev, u8 *data);
|
|||
|
||||
u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr);
|
||||
u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr);
|
||||
u8 eir_create_per_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr);
|
||||
|
||||
u8 eir_append_local_name(struct hci_dev *hdev, u8 *eir, u8 ad_len);
|
||||
u8 eir_append_appearance(struct hci_dev *hdev, u8 *ptr, u8 ad_len);
|
||||
|
|
|
|||
|
|
@ -30,10 +30,13 @@
|
|||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
#include <net/bluetooth/iso.h>
|
||||
#include <net/bluetooth/mgmt.h>
|
||||
|
||||
#include "hci_request.h"
|
||||
#include "smp.h"
|
||||
#include "a2mp.h"
|
||||
#include "eir.h"
|
||||
|
||||
struct sco_param {
|
||||
u16 pkt_type;
|
||||
|
|
@ -118,10 +121,16 @@ static void hci_conn_cleanup(struct hci_conn *conn)
|
|||
if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
|
||||
hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
|
||||
|
||||
if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
|
||||
hci_remove_link_key(hdev, &conn->dst);
|
||||
|
||||
hci_chan_list_flush(conn);
|
||||
|
||||
hci_conn_hash_del(hdev, conn);
|
||||
|
||||
if (conn->cleanup)
|
||||
conn->cleanup(conn);
|
||||
|
||||
if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
|
||||
switch (conn->setting & SCO_AIRMODE_MASK) {
|
||||
case SCO_AIRMODE_CVSD:
|
||||
|
|
@ -678,6 +687,199 @@ static void le_conn_timeout(struct work_struct *work)
|
|||
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
|
||||
}
|
||||
|
||||
struct iso_list_data {
|
||||
union {
|
||||
u8 cig;
|
||||
u8 big;
|
||||
};
|
||||
union {
|
||||
u8 cis;
|
||||
u8 bis;
|
||||
u16 sync_handle;
|
||||
};
|
||||
int count;
|
||||
struct {
|
||||
struct hci_cp_le_set_cig_params cp;
|
||||
struct hci_cis_params cis[0x11];
|
||||
} pdu;
|
||||
};
|
||||
|
||||
static void bis_list(struct hci_conn *conn, void *data)
|
||||
{
|
||||
struct iso_list_data *d = data;
|
||||
|
||||
/* Skip if not broadcast/ANY address */
|
||||
if (bacmp(&conn->dst, BDADDR_ANY))
|
||||
return;
|
||||
|
||||
if (d->big != conn->iso_qos.big || d->bis == BT_ISO_QOS_BIS_UNSET ||
|
||||
d->bis != conn->iso_qos.bis)
|
||||
return;
|
||||
|
||||
d->count++;
|
||||
}
|
||||
|
||||
static void find_bis(struct hci_conn *conn, void *data)
|
||||
{
|
||||
struct iso_list_data *d = data;
|
||||
|
||||
/* Ignore unicast */
|
||||
if (bacmp(&conn->dst, BDADDR_ANY))
|
||||
return;
|
||||
|
||||
d->count++;
|
||||
}
|
||||
|
||||
static int terminate_big_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct iso_list_data *d = data;
|
||||
|
||||
bt_dev_dbg(hdev, "big 0x%2.2x bis 0x%2.2x", d->big, d->bis);
|
||||
|
||||
hci_remove_ext_adv_instance_sync(hdev, d->bis, NULL);
|
||||
|
||||
/* Check if ISO connection is a BIS and terminate BIG if there are
|
||||
* no other connections using it.
|
||||
*/
|
||||
hci_conn_hash_list_state(hdev, find_bis, ISO_LINK, BT_CONNECTED, d);
|
||||
if (d->count)
|
||||
return 0;
|
||||
|
||||
return hci_le_terminate_big_sync(hdev, d->big,
|
||||
HCI_ERROR_LOCAL_HOST_TERM);
|
||||
}
|
||||
|
||||
static void terminate_big_destroy(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
kfree(data);
|
||||
}
|
||||
|
||||
static int hci_le_terminate_big(struct hci_dev *hdev, u8 big, u8 bis)
|
||||
{
|
||||
struct iso_list_data *d;
|
||||
|
||||
bt_dev_dbg(hdev, "big 0x%2.2x bis 0x%2.2x", big, bis);
|
||||
|
||||
d = kmalloc(sizeof(*d), GFP_KERNEL);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(d, 0, sizeof(*d));
|
||||
d->big = big;
|
||||
d->bis = bis;
|
||||
|
||||
return hci_cmd_sync_queue(hdev, terminate_big_sync, d,
|
||||
terminate_big_destroy);
|
||||
}
|
||||
|
||||
static int big_terminate_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct iso_list_data *d = data;
|
||||
|
||||
bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", d->big,
|
||||
d->sync_handle);
|
||||
|
||||
/* Check if ISO connection is a BIS and terminate BIG if there are
|
||||
* no other connections using it.
|
||||
*/
|
||||
hci_conn_hash_list_state(hdev, find_bis, ISO_LINK, BT_CONNECTED, d);
|
||||
if (d->count)
|
||||
return 0;
|
||||
|
||||
hci_le_big_terminate_sync(hdev, d->big);
|
||||
|
||||
return hci_le_pa_terminate_sync(hdev, d->sync_handle);
|
||||
}
|
||||
|
||||
static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, u16 sync_handle)
|
||||
{
|
||||
struct iso_list_data *d;
|
||||
|
||||
bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big, sync_handle);
|
||||
|
||||
d = kmalloc(sizeof(*d), GFP_KERNEL);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(d, 0, sizeof(*d));
|
||||
d->big = big;
|
||||
d->sync_handle = sync_handle;
|
||||
|
||||
return hci_cmd_sync_queue(hdev, big_terminate_sync, d,
|
||||
terminate_big_destroy);
|
||||
}
|
||||
|
||||
/* Cleanup BIS connection
|
||||
*
|
||||
* Detects if there any BIS left connected in a BIG
|
||||
* broadcaster: Remove advertising instance and terminate BIG.
|
||||
* broadcaster receiver: Teminate BIG sync and terminate PA sync.
|
||||
*/
|
||||
static void bis_cleanup(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
||||
bt_dev_dbg(hdev, "conn %p", conn);
|
||||
|
||||
if (conn->role == HCI_ROLE_MASTER) {
|
||||
if (!test_and_clear_bit(HCI_CONN_PER_ADV, &conn->flags))
|
||||
return;
|
||||
|
||||
hci_le_terminate_big(hdev, conn->iso_qos.big,
|
||||
conn->iso_qos.bis);
|
||||
} else {
|
||||
hci_le_big_terminate(hdev, conn->iso_qos.big,
|
||||
conn->sync_handle);
|
||||
}
|
||||
}
|
||||
|
||||
static int remove_cig_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
u8 handle = PTR_ERR(data);
|
||||
|
||||
return hci_le_remove_cig_sync(hdev, handle);
|
||||
}
|
||||
|
||||
static int hci_le_remove_cig(struct hci_dev *hdev, u8 handle)
|
||||
{
|
||||
bt_dev_dbg(hdev, "handle 0x%2.2x", handle);
|
||||
|
||||
return hci_cmd_sync_queue(hdev, remove_cig_sync, ERR_PTR(handle), NULL);
|
||||
}
|
||||
|
||||
static void find_cis(struct hci_conn *conn, void *data)
|
||||
{
|
||||
struct iso_list_data *d = data;
|
||||
|
||||
/* Ignore broadcast */
|
||||
if (!bacmp(&conn->dst, BDADDR_ANY))
|
||||
return;
|
||||
|
||||
d->count++;
|
||||
}
|
||||
|
||||
/* Cleanup CIS connection:
|
||||
*
|
||||
* Detects if there any CIS left connected in a CIG and remove it.
|
||||
*/
|
||||
static void cis_cleanup(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct iso_list_data d;
|
||||
|
||||
memset(&d, 0, sizeof(d));
|
||||
d.cig = conn->iso_qos.cig;
|
||||
|
||||
/* Check if ISO connection is a CIS and remove CIG if there are
|
||||
* no other connections using it.
|
||||
*/
|
||||
hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECTED, &d);
|
||||
if (d.count)
|
||||
return;
|
||||
|
||||
hci_le_remove_cig(hdev, conn->iso_qos.cig);
|
||||
}
|
||||
|
||||
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
||||
u8 role)
|
||||
{
|
||||
|
|
@ -721,6 +923,17 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
|||
case LE_LINK:
|
||||
/* conn->src should reflect the local identity address */
|
||||
hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
|
||||
break;
|
||||
case ISO_LINK:
|
||||
/* conn->src should reflect the local identity address */
|
||||
hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
|
||||
|
||||
/* set proper cleanup function */
|
||||
if (!bacmp(dst, BDADDR_ANY))
|
||||
conn->cleanup = bis_cleanup;
|
||||
else if (conn->role == HCI_ROLE_MASTER)
|
||||
conn->cleanup = cis_cleanup;
|
||||
|
||||
break;
|
||||
case SCO_LINK:
|
||||
if (lmp_esco_capable(hdev))
|
||||
|
|
@ -947,7 +1160,7 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
|
|||
if (conn != hci_lookup_le_connect(hdev))
|
||||
goto done;
|
||||
|
||||
hci_conn_failed(conn, err);
|
||||
hci_conn_failed(conn, bt_status(err));
|
||||
|
||||
done:
|
||||
hci_dev_unlock(hdev);
|
||||
|
|
@ -1093,6 +1306,108 @@ static int hci_explicit_conn_params_set(struct hci_dev *hdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int qos_set_big(struct hci_dev *hdev, struct bt_iso_qos *qos)
|
||||
{
|
||||
struct iso_list_data data;
|
||||
|
||||
/* Allocate a BIG if not set */
|
||||
if (qos->big == BT_ISO_QOS_BIG_UNSET) {
|
||||
for (data.big = 0x00; data.big < 0xef; data.big++) {
|
||||
data.count = 0;
|
||||
data.bis = 0xff;
|
||||
|
||||
hci_conn_hash_list_state(hdev, bis_list, ISO_LINK,
|
||||
BT_BOUND, &data);
|
||||
if (!data.count)
|
||||
break;
|
||||
}
|
||||
|
||||
if (data.big == 0xef)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
/* Update BIG */
|
||||
qos->big = data.big;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qos_set_bis(struct hci_dev *hdev, struct bt_iso_qos *qos)
|
||||
{
|
||||
struct iso_list_data data;
|
||||
|
||||
/* Allocate BIS if not set */
|
||||
if (qos->bis == BT_ISO_QOS_BIS_UNSET) {
|
||||
/* Find an unused adv set to advertise BIS, skip instance 0x00
|
||||
* since it is reserved as general purpose set.
|
||||
*/
|
||||
for (data.bis = 0x01; data.bis < hdev->le_num_of_adv_sets;
|
||||
data.bis++) {
|
||||
data.count = 0;
|
||||
|
||||
hci_conn_hash_list_state(hdev, bis_list, ISO_LINK,
|
||||
BT_BOUND, &data);
|
||||
if (!data.count)
|
||||
break;
|
||||
}
|
||||
|
||||
if (data.bis == hdev->le_num_of_adv_sets)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
/* Update BIS */
|
||||
qos->bis = data.bis;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static struct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
struct bt_iso_qos *qos)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
struct iso_list_data data;
|
||||
int err;
|
||||
|
||||
/* Let's make sure that le is enabled.*/
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
|
||||
if (lmp_le_capable(hdev))
|
||||
return ERR_PTR(-ECONNREFUSED);
|
||||
return ERR_PTR(-EOPNOTSUPP);
|
||||
}
|
||||
|
||||
err = qos_set_big(hdev, qos);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
err = qos_set_bis(hdev, qos);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
data.big = qos->big;
|
||||
data.bis = qos->bis;
|
||||
data.count = 0;
|
||||
|
||||
/* Check if there is already a matching BIG/BIS */
|
||||
hci_conn_hash_list_state(hdev, bis_list, ISO_LINK, BT_BOUND, &data);
|
||||
if (data.count)
|
||||
return ERR_PTR(-EADDRINUSE);
|
||||
|
||||
conn = hci_conn_hash_lookup_bis(hdev, dst, qos->big, qos->bis);
|
||||
if (conn)
|
||||
return ERR_PTR(-EADDRINUSE);
|
||||
|
||||
conn = hci_conn_add(hdev, ISO_LINK, dst, HCI_ROLE_MASTER);
|
||||
if (!conn)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
set_bit(HCI_CONN_PER_ADV, &conn->flags);
|
||||
conn->state = BT_CONNECT;
|
||||
|
||||
hci_conn_hold(conn);
|
||||
return conn;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
u8 dst_type, u8 sec_level,
|
||||
|
|
@ -1229,6 +1544,589 @@ struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
|
|||
return sco;
|
||||
}
|
||||
|
||||
static void cis_add(struct iso_list_data *d, struct bt_iso_qos *qos)
|
||||
{
|
||||
struct hci_cis_params *cis = &d->pdu.cis[d->pdu.cp.num_cis];
|
||||
|
||||
cis->cis_id = qos->cis;
|
||||
cis->c_sdu = cpu_to_le16(qos->out.sdu);
|
||||
cis->p_sdu = cpu_to_le16(qos->in.sdu);
|
||||
cis->c_phy = qos->out.phy;
|
||||
cis->p_phy = qos->in.phy;
|
||||
cis->c_rtn = qos->out.rtn;
|
||||
cis->p_rtn = qos->in.rtn;
|
||||
|
||||
d->pdu.cp.num_cis++;
|
||||
}
|
||||
|
||||
static void cis_list(struct hci_conn *conn, void *data)
|
||||
{
|
||||
struct iso_list_data *d = data;
|
||||
|
||||
/* Skip if broadcast/ANY address */
|
||||
if (!bacmp(&conn->dst, BDADDR_ANY))
|
||||
return;
|
||||
|
||||
if (d->cig != conn->iso_qos.cig || d->cis == BT_ISO_QOS_CIS_UNSET ||
|
||||
d->cis != conn->iso_qos.cis)
|
||||
return;
|
||||
|
||||
d->count++;
|
||||
|
||||
if (d->pdu.cp.cig_id == BT_ISO_QOS_CIG_UNSET ||
|
||||
d->count >= ARRAY_SIZE(d->pdu.cis))
|
||||
return;
|
||||
|
||||
cis_add(d, &conn->iso_qos);
|
||||
}
|
||||
|
||||
static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_create_big cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.handle = qos->big;
|
||||
cp.adv_handle = qos->bis;
|
||||
cp.num_bis = 0x01;
|
||||
hci_cpu_to_le24(qos->out.interval, cp.bis.sdu_interval);
|
||||
cp.bis.sdu = cpu_to_le16(qos->out.sdu);
|
||||
cp.bis.latency = cpu_to_le16(qos->out.latency);
|
||||
cp.bis.rtn = qos->out.rtn;
|
||||
cp.bis.phy = qos->out.phy;
|
||||
cp.bis.packing = qos->packing;
|
||||
cp.bis.framing = qos->framing;
|
||||
cp.bis.encryption = 0x00;
|
||||
memset(&cp.bis.bcode, 0, sizeof(cp.bis.bcode));
|
||||
|
||||
return hci_send_cmd(hdev, HCI_OP_LE_CREATE_BIG, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct iso_list_data data;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
|
||||
/* Allocate a CIG if not set */
|
||||
if (qos->cig == BT_ISO_QOS_CIG_UNSET) {
|
||||
for (data.cig = 0x00; data.cig < 0xff; data.cig++) {
|
||||
data.count = 0;
|
||||
data.cis = 0xff;
|
||||
|
||||
hci_conn_hash_list_state(hdev, cis_list, ISO_LINK,
|
||||
BT_BOUND, &data);
|
||||
if (data.count)
|
||||
continue;
|
||||
|
||||
hci_conn_hash_list_state(hdev, cis_list, ISO_LINK,
|
||||
BT_CONNECTED, &data);
|
||||
if (!data.count)
|
||||
break;
|
||||
}
|
||||
|
||||
if (data.cig == 0xff)
|
||||
return false;
|
||||
|
||||
/* Update CIG */
|
||||
qos->cig = data.cig;
|
||||
}
|
||||
|
||||
data.pdu.cp.cig_id = qos->cig;
|
||||
hci_cpu_to_le24(qos->out.interval, data.pdu.cp.c_interval);
|
||||
hci_cpu_to_le24(qos->in.interval, data.pdu.cp.p_interval);
|
||||
data.pdu.cp.sca = qos->sca;
|
||||
data.pdu.cp.packing = qos->packing;
|
||||
data.pdu.cp.framing = qos->framing;
|
||||
data.pdu.cp.c_latency = cpu_to_le16(qos->out.latency);
|
||||
data.pdu.cp.p_latency = cpu_to_le16(qos->in.latency);
|
||||
|
||||
if (qos->cis != BT_ISO_QOS_CIS_UNSET) {
|
||||
data.count = 0;
|
||||
data.cig = qos->cig;
|
||||
data.cis = qos->cis;
|
||||
|
||||
hci_conn_hash_list_state(hdev, cis_list, ISO_LINK, BT_BOUND,
|
||||
&data);
|
||||
if (data.count)
|
||||
return false;
|
||||
|
||||
cis_add(&data, qos);
|
||||
}
|
||||
|
||||
/* Reprogram all CIS(s) with the same CIG */
|
||||
for (data.cig = qos->cig, data.cis = 0x00; data.cis < 0x11;
|
||||
data.cis++) {
|
||||
data.count = 0;
|
||||
|
||||
hci_conn_hash_list_state(hdev, cis_list, ISO_LINK, BT_BOUND,
|
||||
&data);
|
||||
if (data.count)
|
||||
continue;
|
||||
|
||||
/* Allocate a CIS if not set */
|
||||
if (qos->cis == BT_ISO_QOS_CIS_UNSET) {
|
||||
/* Update CIS */
|
||||
qos->cis = data.cis;
|
||||
cis_add(&data, qos);
|
||||
}
|
||||
}
|
||||
|
||||
if (qos->cis == BT_ISO_QOS_CIS_UNSET || !data.pdu.cp.num_cis)
|
||||
return false;
|
||||
|
||||
if (hci_send_cmd(hdev, HCI_OP_LE_SET_CIG_PARAMS,
|
||||
sizeof(data.pdu.cp) +
|
||||
(data.pdu.cp.num_cis * sizeof(*data.pdu.cis)),
|
||||
&data.pdu) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos)
|
||||
{
|
||||
struct hci_conn *cis;
|
||||
|
||||
cis = hci_conn_hash_lookup_cis(hdev, dst, dst_type);
|
||||
if (!cis) {
|
||||
cis = hci_conn_add(hdev, ISO_LINK, dst, HCI_ROLE_MASTER);
|
||||
if (!cis)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
cis->cleanup = cis_cleanup;
|
||||
}
|
||||
|
||||
if (cis->state == BT_CONNECTED)
|
||||
return cis;
|
||||
|
||||
/* Check if CIS has been set and the settings matches */
|
||||
if (cis->state == BT_BOUND &&
|
||||
!memcmp(&cis->iso_qos, qos, sizeof(*qos)))
|
||||
return cis;
|
||||
|
||||
/* Update LINK PHYs according to QoS preference */
|
||||
cis->le_tx_phy = qos->out.phy;
|
||||
cis->le_rx_phy = qos->in.phy;
|
||||
|
||||
/* If output interval is not set use the input interval as it cannot be
|
||||
* 0x000000.
|
||||
*/
|
||||
if (!qos->out.interval)
|
||||
qos->out.interval = qos->in.interval;
|
||||
|
||||
/* If input interval is not set use the output interval as it cannot be
|
||||
* 0x000000.
|
||||
*/
|
||||
if (!qos->in.interval)
|
||||
qos->in.interval = qos->out.interval;
|
||||
|
||||
/* If output latency is not set use the input latency as it cannot be
|
||||
* 0x0000.
|
||||
*/
|
||||
if (!qos->out.latency)
|
||||
qos->out.latency = qos->in.latency;
|
||||
|
||||
/* If input latency is not set use the output latency as it cannot be
|
||||
* 0x0000.
|
||||
*/
|
||||
if (!qos->in.latency)
|
||||
qos->in.latency = qos->out.latency;
|
||||
|
||||
/* Mirror PHYs that are disabled as SDU will be set to 0 */
|
||||
if (!qos->in.phy)
|
||||
qos->in.phy = qos->out.phy;
|
||||
|
||||
if (!qos->out.phy)
|
||||
qos->out.phy = qos->in.phy;
|
||||
|
||||
if (!hci_le_set_cig_params(cis, qos)) {
|
||||
hci_conn_drop(cis);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
cis->iso_qos = *qos;
|
||||
cis->state = BT_BOUND;
|
||||
|
||||
return cis;
|
||||
}
|
||||
|
||||
bool hci_iso_setup_path(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_setup_iso_path cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
|
||||
if (conn->iso_qos.out.sdu) {
|
||||
cmd.handle = cpu_to_le16(conn->handle);
|
||||
cmd.direction = 0x00; /* Input (Host to Controller) */
|
||||
cmd.path = 0x00; /* HCI path if enabled */
|
||||
cmd.codec = 0x03; /* Transparent Data */
|
||||
|
||||
if (hci_send_cmd(hdev, HCI_OP_LE_SETUP_ISO_PATH, sizeof(cmd),
|
||||
&cmd) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (conn->iso_qos.in.sdu) {
|
||||
cmd.handle = cpu_to_le16(conn->handle);
|
||||
cmd.direction = 0x01; /* Output (Controller to Host) */
|
||||
cmd.path = 0x00; /* HCI path if enabled */
|
||||
cmd.codec = 0x03; /* Transparent Data */
|
||||
|
||||
if (hci_send_cmd(hdev, HCI_OP_LE_SETUP_ISO_PATH, sizeof(cmd),
|
||||
&cmd) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int hci_create_cis_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct {
|
||||
struct hci_cp_le_create_cis cp;
|
||||
struct hci_cis cis[0x1f];
|
||||
} cmd;
|
||||
struct hci_conn *conn = data;
|
||||
u8 cig;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cis[0].acl_handle = cpu_to_le16(conn->link->handle);
|
||||
cmd.cis[0].cis_handle = cpu_to_le16(conn->handle);
|
||||
cmd.cp.num_cis++;
|
||||
cig = conn->iso_qos.cig;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
|
||||
struct hci_cis *cis = &cmd.cis[cmd.cp.num_cis];
|
||||
|
||||
if (conn == data || conn->type != ISO_LINK ||
|
||||
conn->state == BT_CONNECTED || conn->iso_qos.cig != cig)
|
||||
continue;
|
||||
|
||||
/* Check if all CIS(s) belonging to a CIG are ready */
|
||||
if (conn->link->state != BT_CONNECTED ||
|
||||
conn->state != BT_CONNECT) {
|
||||
cmd.cp.num_cis = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Group all CIS with state BT_CONNECT since the spec don't
|
||||
* allow to send them individually:
|
||||
*
|
||||
* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 4, Part E
|
||||
* page 2566:
|
||||
*
|
||||
* If the Host issues this command before all the
|
||||
* HCI_LE_CIS_Established events from the previous use of the
|
||||
* command have been generated, the Controller shall return the
|
||||
* error code Command Disallowed (0x0C).
|
||||
*/
|
||||
cis->acl_handle = cpu_to_le16(conn->link->handle);
|
||||
cis->cis_handle = cpu_to_le16(conn->handle);
|
||||
cmd.cp.num_cis++;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
if (!cmd.cp.num_cis)
|
||||
return 0;
|
||||
|
||||
return hci_send_cmd(hdev, HCI_OP_LE_CREATE_CIS, sizeof(cmd.cp) +
|
||||
sizeof(cmd.cis[0]) * cmd.cp.num_cis, &cmd);
|
||||
}
|
||||
|
||||
int hci_le_create_cis(struct hci_conn *conn)
|
||||
{
|
||||
struct hci_conn *cis;
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
int err;
|
||||
|
||||
switch (conn->type) {
|
||||
case LE_LINK:
|
||||
if (!conn->link || conn->state != BT_CONNECTED)
|
||||
return -EINVAL;
|
||||
cis = conn->link;
|
||||
break;
|
||||
case ISO_LINK:
|
||||
cis = conn;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cis->state == BT_CONNECT)
|
||||
return 0;
|
||||
|
||||
/* Queue Create CIS */
|
||||
err = hci_cmd_sync_queue(hdev, hci_create_cis_sync, cis, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cis->state = BT_CONNECT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hci_iso_qos_setup(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
struct bt_iso_io_qos *qos, __u8 phy)
|
||||
{
|
||||
/* Only set MTU if PHY is enabled */
|
||||
if (!qos->sdu && qos->phy) {
|
||||
if (hdev->iso_mtu > 0)
|
||||
qos->sdu = hdev->iso_mtu;
|
||||
else if (hdev->le_mtu > 0)
|
||||
qos->sdu = hdev->le_mtu;
|
||||
else
|
||||
qos->sdu = hdev->acl_mtu;
|
||||
}
|
||||
|
||||
/* Use the same PHY as ACL if set to any */
|
||||
if (qos->phy == BT_ISO_PHY_ANY)
|
||||
qos->phy = phy;
|
||||
|
||||
/* Use LE ACL connection interval if not set */
|
||||
if (!qos->interval)
|
||||
/* ACL interval unit in 1.25 ms to us */
|
||||
qos->interval = conn->le_conn_interval * 1250;
|
||||
|
||||
/* Use LE ACL connection latency if not set */
|
||||
if (!qos->latency)
|
||||
qos->latency = conn->le_conn_latency;
|
||||
}
|
||||
|
||||
static struct hci_conn *hci_bind_bis(struct hci_conn *conn,
|
||||
struct bt_iso_qos *qos)
|
||||
{
|
||||
/* Update LINK PHYs according to QoS preference */
|
||||
conn->le_tx_phy = qos->out.phy;
|
||||
conn->le_tx_phy = qos->out.phy;
|
||||
conn->iso_qos = *qos;
|
||||
conn->state = BT_BOUND;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
static int create_big_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
struct bt_iso_qos *qos = &conn->iso_qos;
|
||||
u16 interval, sync_interval = 0;
|
||||
u32 flags = 0;
|
||||
int err;
|
||||
|
||||
if (qos->out.phy == 0x02)
|
||||
flags |= MGMT_ADV_FLAG_SEC_2M;
|
||||
|
||||
/* Align intervals */
|
||||
interval = qos->out.interval / 1250;
|
||||
|
||||
if (qos->bis)
|
||||
sync_interval = qos->sync_interval * 1600;
|
||||
|
||||
err = hci_start_per_adv_sync(hdev, qos->bis, conn->le_per_adv_data_len,
|
||||
conn->le_per_adv_data, flags, interval,
|
||||
interval, sync_interval);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return hci_le_create_big(conn, &conn->iso_qos);
|
||||
}
|
||||
|
||||
static void create_pa_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct hci_cp_le_pa_create_sync *cp = data;
|
||||
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
if (err)
|
||||
bt_dev_err(hdev, "Unable to create PA: %d", err);
|
||||
|
||||
kfree(cp);
|
||||
}
|
||||
|
||||
static int create_pa_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct hci_cp_le_pa_create_sync *cp = data;
|
||||
int err;
|
||||
|
||||
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_PA_CREATE_SYNC,
|
||||
sizeof(*cp), cp, HCI_CMD_TIMEOUT);
|
||||
if (err) {
|
||||
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
|
||||
return err;
|
||||
}
|
||||
|
||||
return hci_update_passive_scan_sync(hdev);
|
||||
}
|
||||
|
||||
int hci_pa_create_sync(struct hci_dev *hdev, bdaddr_t *dst, __u8 dst_type,
|
||||
__u8 sid)
|
||||
{
|
||||
struct hci_cp_le_pa_create_sync *cp;
|
||||
|
||||
if (hci_dev_test_and_set_flag(hdev, HCI_PA_SYNC))
|
||||
return -EBUSY;
|
||||
|
||||
cp = kmalloc(sizeof(*cp), GFP_KERNEL);
|
||||
if (!cp) {
|
||||
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Convert from ISO socket address type to HCI address type */
|
||||
if (dst_type == BDADDR_LE_PUBLIC)
|
||||
dst_type = ADDR_LE_DEV_PUBLIC;
|
||||
else
|
||||
dst_type = ADDR_LE_DEV_RANDOM;
|
||||
|
||||
memset(cp, 0, sizeof(*cp));
|
||||
cp->sid = sid;
|
||||
cp->addr_type = dst_type;
|
||||
bacpy(&cp->addr, dst);
|
||||
|
||||
/* Queue start pa_create_sync and scan */
|
||||
return hci_cmd_sync_queue(hdev, create_pa_sync, cp, create_pa_complete);
|
||||
}
|
||||
|
||||
int hci_le_big_create_sync(struct hci_dev *hdev, struct bt_iso_qos *qos,
|
||||
__u16 sync_handle, __u8 num_bis, __u8 bis[])
|
||||
{
|
||||
struct _packed {
|
||||
struct hci_cp_le_big_create_sync cp;
|
||||
__u8 bis[0x11];
|
||||
} pdu;
|
||||
int err;
|
||||
|
||||
if (num_bis > sizeof(pdu.bis))
|
||||
return -EINVAL;
|
||||
|
||||
err = qos_set_big(hdev, qos);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memset(&pdu, 0, sizeof(pdu));
|
||||
pdu.cp.handle = qos->big;
|
||||
pdu.cp.sync_handle = cpu_to_le16(sync_handle);
|
||||
pdu.cp.num_bis = num_bis;
|
||||
memcpy(pdu.bis, bis, num_bis);
|
||||
|
||||
return hci_send_cmd(hdev, HCI_OP_LE_BIG_CREATE_SYNC,
|
||||
sizeof(pdu.cp) + num_bis, &pdu);
|
||||
}
|
||||
|
||||
static void create_big_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
|
||||
bt_dev_dbg(hdev, "conn %p", conn);
|
||||
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "Unable to create BIG: %d", err);
|
||||
hci_connect_cfm(conn, err);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
}
|
||||
|
||||
struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos,
|
||||
__u8 base_len, __u8 *base)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
int err;
|
||||
|
||||
/* We need hci_conn object using the BDADDR_ANY as dst */
|
||||
conn = hci_add_bis(hdev, dst, qos);
|
||||
if (IS_ERR(conn))
|
||||
return conn;
|
||||
|
||||
conn = hci_bind_bis(conn, qos);
|
||||
if (!conn) {
|
||||
hci_conn_drop(conn);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/* Add Basic Announcement into Peridic Adv Data if BASE is set */
|
||||
if (base_len && base) {
|
||||
base_len = eir_append_service_data(conn->le_per_adv_data, 0,
|
||||
0x1851, base, base_len);
|
||||
conn->le_per_adv_data_len = base_len;
|
||||
}
|
||||
|
||||
/* Queue start periodic advertising and create BIG */
|
||||
err = hci_cmd_sync_queue(hdev, create_big_sync, conn,
|
||||
create_big_complete);
|
||||
if (err < 0) {
|
||||
hci_conn_drop(conn);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
hci_iso_qos_setup(hdev, conn, &qos->out,
|
||||
conn->le_tx_phy ? conn->le_tx_phy :
|
||||
hdev->le_tx_def_phys);
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos)
|
||||
{
|
||||
struct hci_conn *le;
|
||||
struct hci_conn *cis;
|
||||
|
||||
/* Convert from ISO socket address type to HCI address type */
|
||||
if (dst_type == BDADDR_LE_PUBLIC)
|
||||
dst_type = ADDR_LE_DEV_PUBLIC;
|
||||
else
|
||||
dst_type = ADDR_LE_DEV_RANDOM;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
le = hci_connect_le(hdev, dst, dst_type, false,
|
||||
BT_SECURITY_LOW,
|
||||
HCI_LE_CONN_TIMEOUT,
|
||||
HCI_ROLE_SLAVE);
|
||||
else
|
||||
le = hci_connect_le_scan(hdev, dst, dst_type,
|
||||
BT_SECURITY_LOW,
|
||||
HCI_LE_CONN_TIMEOUT,
|
||||
CONN_REASON_ISO_CONNECT);
|
||||
if (IS_ERR(le))
|
||||
return le;
|
||||
|
||||
hci_iso_qos_setup(hdev, le, &qos->out,
|
||||
le->le_tx_phy ? le->le_tx_phy : hdev->le_tx_def_phys);
|
||||
hci_iso_qos_setup(hdev, le, &qos->in,
|
||||
le->le_rx_phy ? le->le_rx_phy : hdev->le_rx_def_phys);
|
||||
|
||||
cis = hci_bind_cis(hdev, dst, dst_type, qos);
|
||||
if (IS_ERR(cis)) {
|
||||
hci_conn_drop(le);
|
||||
return cis;
|
||||
}
|
||||
|
||||
le->link = cis;
|
||||
cis->link = le;
|
||||
|
||||
hci_conn_hold(cis);
|
||||
|
||||
/* If LE is already connected and CIS handle is already set proceed to
|
||||
* Create CIS immediately.
|
||||
*/
|
||||
if (le->state == BT_CONNECTED && cis->handle != HCI_CONN_HANDLE_UNSET)
|
||||
hci_le_create_cis(le);
|
||||
|
||||
return cis;
|
||||
}
|
||||
|
||||
/* Check link security requirement */
|
||||
int hci_conn_check_link_mode(struct hci_conn *conn)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/rfkill.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/kcov.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/wait.h>
|
||||
|
|
@ -594,6 +595,11 @@ static int hci_dev_do_reset(struct hci_dev *hdev)
|
|||
skb_queue_purge(&hdev->rx_q);
|
||||
skb_queue_purge(&hdev->cmd_q);
|
||||
|
||||
/* Cancel these to avoid queueing non-chained pending work */
|
||||
hci_dev_set_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE);
|
||||
cancel_delayed_work(&hdev->cmd_timer);
|
||||
cancel_delayed_work(&hdev->ncmd_timer);
|
||||
|
||||
/* Avoid potential lockdep warnings from the *_flush() calls by
|
||||
* ensuring the workqueue is empty up front.
|
||||
*/
|
||||
|
|
@ -607,8 +613,13 @@ static int hci_dev_do_reset(struct hci_dev *hdev)
|
|||
if (hdev->flush)
|
||||
hdev->flush(hdev);
|
||||
|
||||
hci_dev_clear_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE);
|
||||
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0;
|
||||
hdev->acl_cnt = 0;
|
||||
hdev->sco_cnt = 0;
|
||||
hdev->le_cnt = 0;
|
||||
hdev->iso_cnt = 0;
|
||||
|
||||
ret = hci_reset_sync(hdev);
|
||||
|
||||
|
|
@ -1691,63 +1702,77 @@ static void adv_instance_rpa_expired(struct work_struct *work)
|
|||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
|
||||
u16 adv_data_len, u8 *adv_data,
|
||||
u16 scan_rsp_len, u8 *scan_rsp_data,
|
||||
u16 timeout, u16 duration, s8 tx_power,
|
||||
u32 min_interval, u32 max_interval)
|
||||
struct adv_info *hci_add_adv_instance(struct hci_dev *hdev, u8 instance,
|
||||
u32 flags, u16 adv_data_len, u8 *adv_data,
|
||||
u16 scan_rsp_len, u8 *scan_rsp_data,
|
||||
u16 timeout, u16 duration, s8 tx_power,
|
||||
u32 min_interval, u32 max_interval)
|
||||
{
|
||||
struct adv_info *adv_instance;
|
||||
struct adv_info *adv;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (adv_instance) {
|
||||
memset(adv_instance->adv_data, 0,
|
||||
sizeof(adv_instance->adv_data));
|
||||
memset(adv_instance->scan_rsp_data, 0,
|
||||
sizeof(adv_instance->scan_rsp_data));
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (adv) {
|
||||
memset(adv->adv_data, 0, sizeof(adv->adv_data));
|
||||
memset(adv->scan_rsp_data, 0, sizeof(adv->scan_rsp_data));
|
||||
memset(adv->per_adv_data, 0, sizeof(adv->per_adv_data));
|
||||
} else {
|
||||
if (hdev->adv_instance_cnt >= hdev->le_num_of_adv_sets ||
|
||||
instance < 1 || instance > hdev->le_num_of_adv_sets)
|
||||
return -EOVERFLOW;
|
||||
return ERR_PTR(-EOVERFLOW);
|
||||
|
||||
adv_instance = kzalloc(sizeof(*adv_instance), GFP_KERNEL);
|
||||
if (!adv_instance)
|
||||
return -ENOMEM;
|
||||
adv = kzalloc(sizeof(*adv), GFP_KERNEL);
|
||||
if (!adv)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
adv_instance->pending = true;
|
||||
adv_instance->instance = instance;
|
||||
list_add(&adv_instance->list, &hdev->adv_instances);
|
||||
adv->pending = true;
|
||||
adv->instance = instance;
|
||||
list_add(&adv->list, &hdev->adv_instances);
|
||||
hdev->adv_instance_cnt++;
|
||||
}
|
||||
|
||||
adv_instance->flags = flags;
|
||||
adv_instance->adv_data_len = adv_data_len;
|
||||
adv_instance->scan_rsp_len = scan_rsp_len;
|
||||
adv_instance->min_interval = min_interval;
|
||||
adv_instance->max_interval = max_interval;
|
||||
adv_instance->tx_power = tx_power;
|
||||
adv->flags = flags;
|
||||
adv->min_interval = min_interval;
|
||||
adv->max_interval = max_interval;
|
||||
adv->tx_power = tx_power;
|
||||
|
||||
if (adv_data_len)
|
||||
memcpy(adv_instance->adv_data, adv_data, adv_data_len);
|
||||
hci_set_adv_instance_data(hdev, instance, adv_data_len, adv_data,
|
||||
scan_rsp_len, scan_rsp_data);
|
||||
|
||||
if (scan_rsp_len)
|
||||
memcpy(adv_instance->scan_rsp_data,
|
||||
scan_rsp_data, scan_rsp_len);
|
||||
|
||||
adv_instance->timeout = timeout;
|
||||
adv_instance->remaining_time = timeout;
|
||||
adv->timeout = timeout;
|
||||
adv->remaining_time = timeout;
|
||||
|
||||
if (duration == 0)
|
||||
adv_instance->duration = hdev->def_multi_adv_rotation_duration;
|
||||
adv->duration = hdev->def_multi_adv_rotation_duration;
|
||||
else
|
||||
adv_instance->duration = duration;
|
||||
adv->duration = duration;
|
||||
|
||||
INIT_DELAYED_WORK(&adv_instance->rpa_expired_cb,
|
||||
adv_instance_rpa_expired);
|
||||
INIT_DELAYED_WORK(&adv->rpa_expired_cb, adv_instance_rpa_expired);
|
||||
|
||||
BT_DBG("%s for %dMR", hdev->name, instance);
|
||||
|
||||
return 0;
|
||||
return adv;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
struct adv_info *hci_add_per_instance(struct hci_dev *hdev, u8 instance,
|
||||
u32 flags, u8 data_len, u8 *data,
|
||||
u32 min_interval, u32 max_interval)
|
||||
{
|
||||
struct adv_info *adv;
|
||||
|
||||
adv = hci_add_adv_instance(hdev, instance, flags, 0, NULL, 0, NULL,
|
||||
0, 0, HCI_ADV_TX_POWER_NO_PREFERENCE,
|
||||
min_interval, max_interval);
|
||||
if (IS_ERR(adv))
|
||||
return adv;
|
||||
|
||||
adv->periodic = true;
|
||||
adv->per_adv_data_len = data_len;
|
||||
|
||||
if (data)
|
||||
memcpy(adv->per_adv_data, data, data_len);
|
||||
|
||||
return adv;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
|
|
@ -1755,29 +1780,33 @@ int hci_set_adv_instance_data(struct hci_dev *hdev, u8 instance,
|
|||
u16 adv_data_len, u8 *adv_data,
|
||||
u16 scan_rsp_len, u8 *scan_rsp_data)
|
||||
{
|
||||
struct adv_info *adv_instance;
|
||||
struct adv_info *adv;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
|
||||
/* If advertisement doesn't exist, we can't modify its data */
|
||||
if (!adv_instance)
|
||||
if (!adv)
|
||||
return -ENOENT;
|
||||
|
||||
if (adv_data_len) {
|
||||
memset(adv_instance->adv_data, 0,
|
||||
sizeof(adv_instance->adv_data));
|
||||
memcpy(adv_instance->adv_data, adv_data, adv_data_len);
|
||||
adv_instance->adv_data_len = adv_data_len;
|
||||
if (adv_data_len && ADV_DATA_CMP(adv, adv_data, adv_data_len)) {
|
||||
memset(adv->adv_data, 0, sizeof(adv->adv_data));
|
||||
memcpy(adv->adv_data, adv_data, adv_data_len);
|
||||
adv->adv_data_len = adv_data_len;
|
||||
adv->adv_data_changed = true;
|
||||
}
|
||||
|
||||
if (scan_rsp_len) {
|
||||
memset(adv_instance->scan_rsp_data, 0,
|
||||
sizeof(adv_instance->scan_rsp_data));
|
||||
memcpy(adv_instance->scan_rsp_data,
|
||||
scan_rsp_data, scan_rsp_len);
|
||||
adv_instance->scan_rsp_len = scan_rsp_len;
|
||||
if (scan_rsp_len && SCAN_RSP_CMP(adv, scan_rsp_data, scan_rsp_len)) {
|
||||
memset(adv->scan_rsp_data, 0, sizeof(adv->scan_rsp_data));
|
||||
memcpy(adv->scan_rsp_data, scan_rsp_data, scan_rsp_len);
|
||||
adv->scan_rsp_len = scan_rsp_len;
|
||||
adv->scan_rsp_changed = true;
|
||||
}
|
||||
|
||||
/* Mark as changed if there are flags which would affect it */
|
||||
if (((adv->flags & MGMT_ADV_FLAG_APPEARANCE) && hdev->appearance) ||
|
||||
adv->flags & MGMT_ADV_FLAG_LOCAL_NAME)
|
||||
adv->scan_rsp_changed = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1874,151 +1903,120 @@ void hci_free_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor)
|
|||
kfree(monitor);
|
||||
}
|
||||
|
||||
int hci_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
return mgmt_add_adv_patterns_monitor_complete(hdev, status);
|
||||
}
|
||||
|
||||
int hci_remove_adv_monitor_complete(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
return mgmt_remove_adv_monitor_complete(hdev, status);
|
||||
}
|
||||
|
||||
/* Assigns handle to a monitor, and if offloading is supported and power is on,
|
||||
* also attempts to forward the request to the controller.
|
||||
* Returns true if request is forwarded (result is pending), false otherwise.
|
||||
* This function requires the caller holds hdev->lock.
|
||||
* This function requires the caller holds hci_req_sync_lock.
|
||||
*/
|
||||
bool hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
|
||||
int *err)
|
||||
int hci_add_adv_monitor(struct hci_dev *hdev, struct adv_monitor *monitor)
|
||||
{
|
||||
int min, max, handle;
|
||||
int status = 0;
|
||||
|
||||
*err = 0;
|
||||
if (!monitor)
|
||||
return -EINVAL;
|
||||
|
||||
if (!monitor) {
|
||||
*err = -EINVAL;
|
||||
return false;
|
||||
}
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
min = HCI_MIN_ADV_MONITOR_HANDLE;
|
||||
max = HCI_MIN_ADV_MONITOR_HANDLE + HCI_MAX_ADV_MONITOR_NUM_HANDLES;
|
||||
handle = idr_alloc(&hdev->adv_monitors_idr, monitor, min, max,
|
||||
GFP_KERNEL);
|
||||
if (handle < 0) {
|
||||
*err = handle;
|
||||
return false;
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
if (handle < 0)
|
||||
return handle;
|
||||
|
||||
monitor->handle = handle;
|
||||
|
||||
if (!hdev_is_powered(hdev))
|
||||
return false;
|
||||
return status;
|
||||
|
||||
switch (hci_get_adv_monitor_offload_ext(hdev)) {
|
||||
case HCI_ADV_MONITOR_EXT_NONE:
|
||||
hci_update_passive_scan(hdev);
|
||||
bt_dev_dbg(hdev, "%s add monitor status %d", hdev->name, *err);
|
||||
bt_dev_dbg(hdev, "%s add monitor %d status %d", hdev->name,
|
||||
monitor->handle, status);
|
||||
/* Message was not forwarded to controller - not an error */
|
||||
return false;
|
||||
break;
|
||||
|
||||
case HCI_ADV_MONITOR_EXT_MSFT:
|
||||
*err = msft_add_monitor_pattern(hdev, monitor);
|
||||
bt_dev_dbg(hdev, "%s add monitor msft status %d", hdev->name,
|
||||
*err);
|
||||
status = msft_add_monitor_pattern(hdev, monitor);
|
||||
bt_dev_dbg(hdev, "%s add monitor %d msft status %d", hdev->name,
|
||||
monitor->handle, status);
|
||||
break;
|
||||
}
|
||||
|
||||
return (*err == 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Attempts to tell the controller and free the monitor. If somehow the
|
||||
* controller doesn't have a corresponding handle, remove anyway.
|
||||
* Returns true if request is forwarded (result is pending), false otherwise.
|
||||
* This function requires the caller holds hdev->lock.
|
||||
* This function requires the caller holds hci_req_sync_lock.
|
||||
*/
|
||||
static bool hci_remove_adv_monitor(struct hci_dev *hdev,
|
||||
struct adv_monitor *monitor,
|
||||
u16 handle, int *err)
|
||||
static int hci_remove_adv_monitor(struct hci_dev *hdev,
|
||||
struct adv_monitor *monitor)
|
||||
{
|
||||
*err = 0;
|
||||
int status = 0;
|
||||
|
||||
switch (hci_get_adv_monitor_offload_ext(hdev)) {
|
||||
case HCI_ADV_MONITOR_EXT_NONE: /* also goes here when powered off */
|
||||
bt_dev_dbg(hdev, "%s remove monitor %d status %d", hdev->name,
|
||||
monitor->handle, status);
|
||||
goto free_monitor;
|
||||
|
||||
case HCI_ADV_MONITOR_EXT_MSFT:
|
||||
*err = msft_remove_monitor(hdev, monitor, handle);
|
||||
status = msft_remove_monitor(hdev, monitor);
|
||||
bt_dev_dbg(hdev, "%s remove monitor %d msft status %d",
|
||||
hdev->name, monitor->handle, status);
|
||||
break;
|
||||
}
|
||||
|
||||
/* In case no matching handle registered, just free the monitor */
|
||||
if (*err == -ENOENT)
|
||||
if (status == -ENOENT)
|
||||
goto free_monitor;
|
||||
|
||||
return (*err == 0);
|
||||
return status;
|
||||
|
||||
free_monitor:
|
||||
if (*err == -ENOENT)
|
||||
if (status == -ENOENT)
|
||||
bt_dev_warn(hdev, "Removing monitor with no matching handle %d",
|
||||
monitor->handle);
|
||||
hci_free_adv_monitor(hdev, monitor);
|
||||
|
||||
*err = 0;
|
||||
return false;
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Returns true if request is forwarded (result is pending), false otherwise.
|
||||
* This function requires the caller holds hdev->lock.
|
||||
*/
|
||||
bool hci_remove_single_adv_monitor(struct hci_dev *hdev, u16 handle, int *err)
|
||||
/* This function requires the caller holds hci_req_sync_lock */
|
||||
int hci_remove_single_adv_monitor(struct hci_dev *hdev, u16 handle)
|
||||
{
|
||||
struct adv_monitor *monitor = idr_find(&hdev->adv_monitors_idr, handle);
|
||||
bool pending;
|
||||
|
||||
if (!monitor) {
|
||||
*err = -EINVAL;
|
||||
return false;
|
||||
}
|
||||
if (!monitor)
|
||||
return -EINVAL;
|
||||
|
||||
pending = hci_remove_adv_monitor(hdev, monitor, handle, err);
|
||||
if (!*err && !pending)
|
||||
hci_update_passive_scan(hdev);
|
||||
|
||||
bt_dev_dbg(hdev, "%s remove monitor handle %d, status %d, %spending",
|
||||
hdev->name, handle, *err, pending ? "" : "not ");
|
||||
|
||||
return pending;
|
||||
return hci_remove_adv_monitor(hdev, monitor);
|
||||
}
|
||||
|
||||
/* Returns true if request is forwarded (result is pending), false otherwise.
|
||||
* This function requires the caller holds hdev->lock.
|
||||
*/
|
||||
bool hci_remove_all_adv_monitor(struct hci_dev *hdev, int *err)
|
||||
/* This function requires the caller holds hci_req_sync_lock */
|
||||
int hci_remove_all_adv_monitor(struct hci_dev *hdev)
|
||||
{
|
||||
struct adv_monitor *monitor;
|
||||
int idr_next_id = 0;
|
||||
bool pending = false;
|
||||
bool update = false;
|
||||
int status = 0;
|
||||
|
||||
*err = 0;
|
||||
|
||||
while (!*err && !pending) {
|
||||
while (1) {
|
||||
monitor = idr_get_next(&hdev->adv_monitors_idr, &idr_next_id);
|
||||
if (!monitor)
|
||||
break;
|
||||
|
||||
pending = hci_remove_adv_monitor(hdev, monitor, 0, err);
|
||||
status = hci_remove_adv_monitor(hdev, monitor);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (!*err && !pending)
|
||||
update = true;
|
||||
idr_next_id++;
|
||||
}
|
||||
|
||||
if (update)
|
||||
hci_update_passive_scan(hdev);
|
||||
|
||||
bt_dev_dbg(hdev, "%s remove all monitors status %d, %spending",
|
||||
hdev->name, *err, pending ? "" : "not ");
|
||||
|
||||
return pending;
|
||||
return status;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
|
|
@ -2640,12 +2638,9 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||
hci_sock_dev_event(hdev, HCI_DEV_REG);
|
||||
hci_dev_hold(hdev);
|
||||
|
||||
if (!test_bit(HCI_QUIRK_NO_SUSPEND_NOTIFIER, &hdev->quirks)) {
|
||||
hdev->suspend_notifier.notifier_call = hci_suspend_notifier;
|
||||
error = register_pm_notifier(&hdev->suspend_notifier);
|
||||
if (error)
|
||||
goto err_wqueue;
|
||||
}
|
||||
error = hci_register_suspend_notifier(hdev);
|
||||
if (error)
|
||||
goto err_wqueue;
|
||||
|
||||
queue_work(hdev->req_workqueue, &hdev->power_on);
|
||||
|
||||
|
|
@ -2680,8 +2675,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
|
|||
|
||||
hci_cmd_sync_clear(hdev);
|
||||
|
||||
if (!test_bit(HCI_QUIRK_NO_SUSPEND_NOTIFIER, &hdev->quirks))
|
||||
unregister_pm_notifier(&hdev->suspend_notifier);
|
||||
hci_unregister_suspend_notifier(hdev);
|
||||
|
||||
msft_unregister(hdev);
|
||||
|
||||
|
|
@ -2741,10 +2735,33 @@ void hci_release_dev(struct hci_dev *hdev)
|
|||
|
||||
ida_simple_remove(&hci_index_ida, hdev->id);
|
||||
kfree_skb(hdev->sent_cmd);
|
||||
kfree_skb(hdev->recv_event);
|
||||
kfree(hdev);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_release_dev);
|
||||
|
||||
int hci_register_suspend_notifier(struct hci_dev *hdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!test_bit(HCI_QUIRK_NO_SUSPEND_NOTIFIER, &hdev->quirks)) {
|
||||
hdev->suspend_notifier.notifier_call = hci_suspend_notifier;
|
||||
ret = register_pm_notifier(&hdev->suspend_notifier);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hci_unregister_suspend_notifier(struct hci_dev *hdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!test_bit(HCI_QUIRK_NO_SUSPEND_NOTIFIER, &hdev->quirks))
|
||||
ret = unregister_pm_notifier(&hdev->suspend_notifier);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Suspend HCI device */
|
||||
int hci_suspend_dev(struct hci_dev *hdev)
|
||||
{
|
||||
|
|
@ -3025,6 +3042,37 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
|
|||
return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
|
||||
}
|
||||
|
||||
/* Get data from last received event */
|
||||
void *hci_recv_event_data(struct hci_dev *hdev, __u8 event)
|
||||
{
|
||||
struct hci_event_hdr *hdr;
|
||||
int offset;
|
||||
|
||||
if (!hdev->recv_event)
|
||||
return NULL;
|
||||
|
||||
hdr = (void *)hdev->recv_event->data;
|
||||
offset = sizeof(*hdr);
|
||||
|
||||
if (hdr->evt != event) {
|
||||
/* In case of LE metaevent check the subevent match */
|
||||
if (hdr->evt == HCI_EV_LE_META) {
|
||||
struct hci_ev_le_meta *ev;
|
||||
|
||||
ev = (void *)hdev->recv_event->data + offset;
|
||||
offset += sizeof(*ev);
|
||||
if (ev->subevent == event)
|
||||
goto found;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
found:
|
||||
bt_dev_dbg(hdev, "event 0x%2.2x", event);
|
||||
|
||||
return hdev->recv_event->data + offset;
|
||||
}
|
||||
|
||||
/* Send ACL data */
|
||||
static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
|
||||
{
|
||||
|
|
@ -3132,9 +3180,117 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb)
|
|||
queue_work(hdev->workqueue, &hdev->tx_work);
|
||||
}
|
||||
|
||||
/* Send ISO data */
|
||||
static void hci_add_iso_hdr(struct sk_buff *skb, __u16 handle, __u8 flags)
|
||||
{
|
||||
struct hci_iso_hdr *hdr;
|
||||
int len = skb->len;
|
||||
|
||||
skb_push(skb, HCI_ISO_HDR_SIZE);
|
||||
skb_reset_transport_header(skb);
|
||||
hdr = (struct hci_iso_hdr *)skb_transport_header(skb);
|
||||
hdr->handle = cpu_to_le16(hci_handle_pack(handle, flags));
|
||||
hdr->dlen = cpu_to_le16(len);
|
||||
}
|
||||
|
||||
static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct sk_buff *list;
|
||||
__u16 flags;
|
||||
|
||||
skb->len = skb_headlen(skb);
|
||||
skb->data_len = 0;
|
||||
|
||||
hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
|
||||
|
||||
list = skb_shinfo(skb)->frag_list;
|
||||
|
||||
flags = hci_iso_flags_pack(list ? ISO_START : ISO_SINGLE, 0x00);
|
||||
hci_add_iso_hdr(skb, conn->handle, flags);
|
||||
|
||||
if (!list) {
|
||||
/* Non fragmented */
|
||||
BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
|
||||
|
||||
skb_queue_tail(queue, skb);
|
||||
} else {
|
||||
/* Fragmented */
|
||||
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
|
||||
|
||||
skb_shinfo(skb)->frag_list = NULL;
|
||||
|
||||
__skb_queue_tail(queue, skb);
|
||||
|
||||
do {
|
||||
skb = list; list = list->next;
|
||||
|
||||
hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
|
||||
flags = hci_iso_flags_pack(list ? ISO_CONT : ISO_END,
|
||||
0x00);
|
||||
hci_add_iso_hdr(skb, conn->handle, flags);
|
||||
|
||||
BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
|
||||
|
||||
__skb_queue_tail(queue, skb);
|
||||
} while (list);
|
||||
}
|
||||
}
|
||||
|
||||
void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
|
||||
BT_DBG("%s len %d", hdev->name, skb->len);
|
||||
|
||||
hci_queue_iso(conn, &conn->data_q, skb);
|
||||
|
||||
queue_work(hdev->workqueue, &hdev->tx_work);
|
||||
}
|
||||
|
||||
/* ---- HCI TX task (outgoing data) ---- */
|
||||
|
||||
/* HCI Connection scheduler */
|
||||
static inline void hci_quote_sent(struct hci_conn *conn, int num, int *quote)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
int cnt, q;
|
||||
|
||||
if (!conn) {
|
||||
*quote = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
hdev = conn->hdev;
|
||||
|
||||
switch (conn->type) {
|
||||
case ACL_LINK:
|
||||
cnt = hdev->acl_cnt;
|
||||
break;
|
||||
case AMP_LINK:
|
||||
cnt = hdev->block_cnt;
|
||||
break;
|
||||
case SCO_LINK:
|
||||
case ESCO_LINK:
|
||||
cnt = hdev->sco_cnt;
|
||||
break;
|
||||
case LE_LINK:
|
||||
cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
|
||||
break;
|
||||
case ISO_LINK:
|
||||
cnt = hdev->iso_mtu ? hdev->iso_cnt :
|
||||
hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
|
||||
break;
|
||||
default:
|
||||
cnt = 0;
|
||||
bt_dev_err(hdev, "unknown link type %d", conn->type);
|
||||
}
|
||||
|
||||
q = cnt / num;
|
||||
*quote = q ? q : 1;
|
||||
}
|
||||
|
||||
static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,
|
||||
int *quote)
|
||||
{
|
||||
|
|
@ -3167,29 +3323,7 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type,
|
|||
|
||||
rcu_read_unlock();
|
||||
|
||||
if (conn) {
|
||||
int cnt, q;
|
||||
|
||||
switch (conn->type) {
|
||||
case ACL_LINK:
|
||||
cnt = hdev->acl_cnt;
|
||||
break;
|
||||
case SCO_LINK:
|
||||
case ESCO_LINK:
|
||||
cnt = hdev->sco_cnt;
|
||||
break;
|
||||
case LE_LINK:
|
||||
cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
|
||||
break;
|
||||
default:
|
||||
cnt = 0;
|
||||
bt_dev_err(hdev, "unknown link type %d", conn->type);
|
||||
}
|
||||
|
||||
q = cnt / num;
|
||||
*quote = q ? q : 1;
|
||||
} else
|
||||
*quote = 0;
|
||||
hci_quote_sent(conn, num, quote);
|
||||
|
||||
BT_DBG("conn %p quote %d", conn, *quote);
|
||||
return conn;
|
||||
|
|
@ -3223,7 +3357,7 @@ static struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
|
|||
struct hci_chan *chan = NULL;
|
||||
unsigned int num = 0, min = ~0, cur_prio = 0;
|
||||
struct hci_conn *conn;
|
||||
int cnt, q, conn_num = 0;
|
||||
int conn_num = 0;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
|
|
@ -3273,27 +3407,8 @@ static struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
|
|||
if (!chan)
|
||||
return NULL;
|
||||
|
||||
switch (chan->conn->type) {
|
||||
case ACL_LINK:
|
||||
cnt = hdev->acl_cnt;
|
||||
break;
|
||||
case AMP_LINK:
|
||||
cnt = hdev->block_cnt;
|
||||
break;
|
||||
case SCO_LINK:
|
||||
case ESCO_LINK:
|
||||
cnt = hdev->sco_cnt;
|
||||
break;
|
||||
case LE_LINK:
|
||||
cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt;
|
||||
break;
|
||||
default:
|
||||
cnt = 0;
|
||||
bt_dev_err(hdev, "unknown link type %d", chan->conn->type);
|
||||
}
|
||||
hci_quote_sent(chan->conn, num, quote);
|
||||
|
||||
q = cnt / num;
|
||||
*quote = q ? q : 1;
|
||||
BT_DBG("chan %p quote %d", chan, *quote);
|
||||
return chan;
|
||||
}
|
||||
|
|
@ -3582,18 +3697,46 @@ static void hci_sched_le(struct hci_dev *hdev)
|
|||
hci_prio_recalculate(hdev, LE_LINK);
|
||||
}
|
||||
|
||||
/* Schedule CIS */
|
||||
static void hci_sched_iso(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
struct sk_buff *skb;
|
||||
int quote, *cnt;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (!hci_conn_num(hdev, ISO_LINK))
|
||||
return;
|
||||
|
||||
cnt = hdev->iso_pkts ? &hdev->iso_cnt :
|
||||
hdev->le_pkts ? &hdev->le_cnt : &hdev->acl_cnt;
|
||||
while (*cnt && (conn = hci_low_sent(hdev, ISO_LINK, "e))) {
|
||||
while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
|
||||
BT_DBG("skb %p len %d", skb, skb->len);
|
||||
hci_send_frame(hdev, skb);
|
||||
|
||||
conn->sent++;
|
||||
if (conn->sent == ~0)
|
||||
conn->sent = 0;
|
||||
(*cnt)--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hci_tx_work(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev, tx_work);
|
||||
struct sk_buff *skb;
|
||||
|
||||
BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
|
||||
hdev->sco_cnt, hdev->le_cnt);
|
||||
BT_DBG("%s acl %d sco %d le %d iso %d", hdev->name, hdev->acl_cnt,
|
||||
hdev->sco_cnt, hdev->le_cnt, hdev->iso_cnt);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
|
||||
/* Schedule queues and send stuff to HCI driver */
|
||||
hci_sched_sco(hdev);
|
||||
hci_sched_esco(hdev);
|
||||
hci_sched_iso(hdev);
|
||||
hci_sched_acl(hdev);
|
||||
hci_sched_le(hdev);
|
||||
}
|
||||
|
|
@ -3676,6 +3819,43 @@ static void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static void hci_isodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_iso_hdr *hdr;
|
||||
struct hci_conn *conn;
|
||||
__u16 handle, flags;
|
||||
|
||||
hdr = skb_pull_data(skb, sizeof(*hdr));
|
||||
if (!hdr) {
|
||||
bt_dev_err(hdev, "ISO packet too small");
|
||||
goto drop;
|
||||
}
|
||||
|
||||
handle = __le16_to_cpu(hdr->handle);
|
||||
flags = hci_flags(handle);
|
||||
handle = hci_handle(handle);
|
||||
|
||||
bt_dev_dbg(hdev, "len %d handle 0x%4.4x flags 0x%4.4x", skb->len,
|
||||
handle, flags);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
if (!conn) {
|
||||
bt_dev_err(hdev, "ISO packet for unknown connection handle %d",
|
||||
handle);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
/* Send to upper protocol */
|
||||
iso_recv(conn, skb, flags);
|
||||
return;
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static bool hci_req_is_complete(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
|
@ -3781,7 +3961,14 @@ static void hci_rx_work(struct work_struct *work)
|
|||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
while ((skb = skb_dequeue(&hdev->rx_q))) {
|
||||
/* The kcov_remote functions used for collecting packet parsing
|
||||
* coverage information from this background thread and associate
|
||||
* the coverage with the syscall's thread which originally injected
|
||||
* the packet. This helps fuzzing the kernel.
|
||||
*/
|
||||
for (; (skb = skb_dequeue(&hdev->rx_q)); kcov_remote_stop()) {
|
||||
kcov_remote_start_common(skb_get_kcov_handle(skb));
|
||||
|
||||
/* Send copy to monitor */
|
||||
hci_send_to_monitor(hdev, skb);
|
||||
|
||||
|
|
@ -3830,6 +4017,11 @@ static void hci_rx_work(struct work_struct *work)
|
|||
hci_scodata_packet(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_ISODATA_PKT:
|
||||
BT_DBG("%s ISO data packet", hdev->name);
|
||||
hci_isodata_packet(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
kfree_skb(skb);
|
||||
break;
|
||||
|
|
@ -3864,7 +4056,8 @@ static void hci_cmd_work(struct work_struct *work)
|
|||
if (res < 0)
|
||||
__hci_cmd_sync_cancel(hdev, -res);
|
||||
|
||||
if (test_bit(HCI_RESET, &hdev->flags))
|
||||
if (test_bit(HCI_RESET, &hdev->flags) ||
|
||||
hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE))
|
||||
cancel_delayed_work(&hdev->cmd_timer);
|
||||
else
|
||||
schedule_delayed_work(&hdev->cmd_timer,
|
||||
|
|
|
|||
|
|
@ -2741,7 +2741,7 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
|
|||
mgmt_conn = test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags);
|
||||
|
||||
if (conn->type == ACL_LINK) {
|
||||
if (test_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
|
||||
if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
|
||||
hci_remove_link_key(hdev, &conn->dst);
|
||||
}
|
||||
|
||||
|
|
@ -3173,7 +3173,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
|
|||
hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
|
||||
sizeof(cp), &cp);
|
||||
|
||||
hci_req_update_scan(hdev);
|
||||
hci_update_scan(hdev);
|
||||
}
|
||||
|
||||
/* Set packet type for incoming connection */
|
||||
|
|
@ -3368,10 +3368,10 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data,
|
|||
reason, mgmt_connected);
|
||||
|
||||
if (conn->type == ACL_LINK) {
|
||||
if (test_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
|
||||
if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
|
||||
hci_remove_link_key(hdev, &conn->dst);
|
||||
|
||||
hci_req_update_scan(hdev);
|
||||
hci_update_scan(hdev);
|
||||
}
|
||||
|
||||
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
|
||||
|
|
@ -3768,12 +3768,182 @@ static inline void handle_cmd_cnt_and_timer(struct hci_dev *hdev, u8 ncmd)
|
|||
cancel_delayed_work(&hdev->ncmd_timer);
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
} else {
|
||||
schedule_delayed_work(&hdev->ncmd_timer,
|
||||
HCI_NCMD_TIMEOUT);
|
||||
if (!hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE))
|
||||
schedule_delayed_work(&hdev->ncmd_timer,
|
||||
HCI_NCMD_TIMEOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static u8 hci_cc_le_read_buffer_size_v2(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_read_buffer_size_v2 *rp = data;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return rp->status;
|
||||
|
||||
hdev->le_mtu = __le16_to_cpu(rp->acl_mtu);
|
||||
hdev->le_pkts = rp->acl_max_pkt;
|
||||
hdev->iso_mtu = __le16_to_cpu(rp->iso_mtu);
|
||||
hdev->iso_pkts = rp->iso_max_pkt;
|
||||
|
||||
hdev->le_cnt = hdev->le_pkts;
|
||||
hdev->iso_cnt = hdev->iso_pkts;
|
||||
|
||||
BT_DBG("%s acl mtu %d:%d iso mtu %d:%d", hdev->name, hdev->acl_mtu,
|
||||
hdev->acl_pkts, hdev->iso_mtu, hdev->iso_pkts);
|
||||
|
||||
return rp->status;
|
||||
}
|
||||
|
||||
static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_set_cig_params *rp = data;
|
||||
struct hci_conn *conn;
|
||||
int i = 0;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (rp->status) {
|
||||
while ((conn = hci_conn_hash_lookup_cig(hdev, rp->cig_id))) {
|
||||
conn->state = BT_CLOSED;
|
||||
hci_connect_cfm(conn, rp->status);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
|
||||
if (conn->type != ISO_LINK || conn->iso_qos.cig != rp->cig_id ||
|
||||
conn->state == BT_CONNECTED)
|
||||
continue;
|
||||
|
||||
conn->handle = __le16_to_cpu(rp->handle[i++]);
|
||||
|
||||
bt_dev_dbg(hdev, "%p handle 0x%4.4x link %p", conn,
|
||||
conn->handle, conn->link);
|
||||
|
||||
/* Create CIS if LE is already connected */
|
||||
if (conn->link && conn->link->state == BT_CONNECTED)
|
||||
hci_le_create_cis(conn->link);
|
||||
|
||||
if (i == rp->num_handles)
|
||||
break;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return rp->status;
|
||||
}
|
||||
|
||||
static u8 hci_cc_le_setup_iso_path(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_setup_iso_path *rp = data;
|
||||
struct hci_cp_le_setup_iso_path *cp;
|
||||
struct hci_conn *conn;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SETUP_ISO_PATH);
|
||||
if (!cp)
|
||||
return rp->status;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
if (rp->status) {
|
||||
hci_connect_cfm(conn, rp->status);
|
||||
hci_conn_del(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
switch (cp->direction) {
|
||||
/* Input (Host to Controller) */
|
||||
case 0x00:
|
||||
/* Only confirm connection if output only */
|
||||
if (conn->iso_qos.out.sdu && !conn->iso_qos.in.sdu)
|
||||
hci_connect_cfm(conn, rp->status);
|
||||
break;
|
||||
/* Output (Controller to Host) */
|
||||
case 0x01:
|
||||
/* Confirm connection since conn->iso_qos is always configured
|
||||
* last.
|
||||
*/
|
||||
hci_connect_cfm(conn, rp->status);
|
||||
break;
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
return rp->status;
|
||||
}
|
||||
|
||||
static void hci_cs_le_create_big(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
}
|
||||
|
||||
static u8 hci_cc_set_per_adv_param(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_status *rp = data;
|
||||
struct hci_cp_le_set_per_adv_params *cp;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return rp->status;
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_PER_ADV_PARAMS);
|
||||
if (!cp)
|
||||
return rp->status;
|
||||
|
||||
/* TODO: set the conn state */
|
||||
return rp->status;
|
||||
}
|
||||
|
||||
static u8 hci_cc_le_set_per_adv_enable(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_status *rp = data;
|
||||
__u8 *sent;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return rp->status;
|
||||
|
||||
sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_PER_ADV_ENABLE);
|
||||
if (!sent)
|
||||
return rp->status;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (*sent)
|
||||
hci_dev_set_flag(hdev, HCI_LE_PER_ADV);
|
||||
else
|
||||
hci_dev_clear_flag(hdev, HCI_LE_PER_ADV);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return rp->status;
|
||||
}
|
||||
|
||||
#define HCI_CC_VL(_op, _func, _min, _max) \
|
||||
{ \
|
||||
.op = _op, \
|
||||
|
|
@ -3947,9 +4117,18 @@ static const struct hci_cc {
|
|||
hci_cc_le_set_adv_set_random_addr),
|
||||
HCI_CC_STATUS(HCI_OP_LE_REMOVE_ADV_SET, hci_cc_le_remove_adv_set),
|
||||
HCI_CC_STATUS(HCI_OP_LE_CLEAR_ADV_SETS, hci_cc_le_clear_adv_sets),
|
||||
HCI_CC_STATUS(HCI_OP_LE_SET_PER_ADV_PARAMS, hci_cc_set_per_adv_param),
|
||||
HCI_CC_STATUS(HCI_OP_LE_SET_PER_ADV_ENABLE,
|
||||
hci_cc_le_set_per_adv_enable),
|
||||
HCI_CC(HCI_OP_LE_READ_TRANSMIT_POWER, hci_cc_le_read_transmit_power,
|
||||
sizeof(struct hci_rp_le_read_transmit_power)),
|
||||
HCI_CC_STATUS(HCI_OP_LE_SET_PRIVACY_MODE, hci_cc_le_set_privacy_mode)
|
||||
HCI_CC_STATUS(HCI_OP_LE_SET_PRIVACY_MODE, hci_cc_le_set_privacy_mode),
|
||||
HCI_CC(HCI_OP_LE_READ_BUFFER_SIZE_V2, hci_cc_le_read_buffer_size_v2,
|
||||
sizeof(struct hci_rp_le_read_buffer_size_v2)),
|
||||
HCI_CC_VL(HCI_OP_LE_SET_CIG_PARAMS, hci_cc_le_set_cig_params,
|
||||
sizeof(struct hci_rp_le_set_cig_params), HCI_MAX_EVENT_SIZE),
|
||||
HCI_CC(HCI_OP_LE_SETUP_ISO_PATH, hci_cc_le_setup_iso_path,
|
||||
sizeof(struct hci_rp_le_setup_iso_path)),
|
||||
};
|
||||
|
||||
static u8 hci_cc_func(struct hci_dev *hdev, const struct hci_cc *cc,
|
||||
|
|
@ -4012,6 +4191,40 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, void *data,
|
|||
queue_work(hdev->workqueue, &hdev->cmd_work);
|
||||
}
|
||||
|
||||
static void hci_cs_le_create_cis(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
struct hci_cp_le_create_cis *cp;
|
||||
int i;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
|
||||
if (!status)
|
||||
return;
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CIS);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
/* Remove connection if command failed */
|
||||
for (i = 0; cp->num_cis; cp->num_cis--, i++) {
|
||||
struct hci_conn *conn;
|
||||
u16 handle;
|
||||
|
||||
handle = __le16_to_cpu(cp->cis[i].cis_handle);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (conn) {
|
||||
conn->state = BT_CLOSED;
|
||||
hci_connect_cfm(conn, status);
|
||||
hci_conn_del(conn);
|
||||
}
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
#define HCI_CS(_op, _func) \
|
||||
{ \
|
||||
.op = _op, \
|
||||
|
|
@ -4041,7 +4254,9 @@ static const struct hci_cs {
|
|||
HCI_CS(HCI_OP_LE_CREATE_CONN, hci_cs_le_create_conn),
|
||||
HCI_CS(HCI_OP_LE_READ_REMOTE_FEATURES, hci_cs_le_read_remote_features),
|
||||
HCI_CS(HCI_OP_LE_START_ENC, hci_cs_le_start_enc),
|
||||
HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn)
|
||||
HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn),
|
||||
HCI_CS(HCI_OP_LE_CREATE_CIS, hci_cs_le_create_cis),
|
||||
HCI_CS(HCI_OP_LE_CREATE_BIG, hci_cs_le_create_big),
|
||||
};
|
||||
|
||||
static void hci_cmd_status_evt(struct hci_dev *hdev, void *data,
|
||||
|
|
@ -4177,6 +4392,22 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, void *data,
|
|||
hdev->sco_cnt = hdev->sco_pkts;
|
||||
break;
|
||||
|
||||
case ISO_LINK:
|
||||
if (hdev->iso_pkts) {
|
||||
hdev->iso_cnt += count;
|
||||
if (hdev->iso_cnt > hdev->iso_pkts)
|
||||
hdev->iso_cnt = hdev->iso_pkts;
|
||||
} else if (hdev->le_pkts) {
|
||||
hdev->le_cnt += count;
|
||||
if (hdev->le_cnt > hdev->le_pkts)
|
||||
hdev->le_cnt = hdev->le_pkts;
|
||||
} else {
|
||||
hdev->acl_cnt += count;
|
||||
if (hdev->acl_cnt > hdev->acl_pkts)
|
||||
hdev->acl_cnt = hdev->acl_pkts;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
bt_dev_err(hdev, "unknown type %d conn %p",
|
||||
conn->type, conn);
|
||||
|
|
@ -6249,6 +6480,39 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data,
|
|||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle)
|
||||
{
|
||||
struct hci_cp_le_pa_term_sync cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = handle;
|
||||
|
||||
return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_pa_sync_established *ev = data;
|
||||
int mask = hdev->link_mode;
|
||||
__u8 flags = 0;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
|
||||
|
||||
if (ev->status)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hci_dev_clear_flag(hdev, HCI_PA_SYNC);
|
||||
|
||||
mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ISO_LINK, &flags);
|
||||
if (!(mask & HCI_LM_ACCEPT))
|
||||
hci_le_pa_term_sync(hdev, ev->handle);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
|
@ -6479,6 +6743,226 @@ static void hci_le_phy_update_evt(struct hci_dev *hdev, void *data,
|
|||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_evt_le_cis_established *ev = data;
|
||||
struct hci_conn *conn;
|
||||
u16 handle = __le16_to_cpu(ev->handle);
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!conn) {
|
||||
bt_dev_err(hdev,
|
||||
"Unable to find connection with handle 0x%4.4x",
|
||||
handle);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (conn->role == HCI_ROLE_SLAVE) {
|
||||
__le32 interval;
|
||||
|
||||
memset(&interval, 0, sizeof(interval));
|
||||
|
||||
memcpy(&interval, ev->c_latency, sizeof(ev->c_latency));
|
||||
conn->iso_qos.in.interval = le32_to_cpu(interval);
|
||||
memcpy(&interval, ev->p_latency, sizeof(ev->p_latency));
|
||||
conn->iso_qos.out.interval = le32_to_cpu(interval);
|
||||
conn->iso_qos.in.latency = le16_to_cpu(ev->interval);
|
||||
conn->iso_qos.out.latency = le16_to_cpu(ev->interval);
|
||||
conn->iso_qos.in.sdu = le16_to_cpu(ev->c_mtu);
|
||||
conn->iso_qos.out.sdu = le16_to_cpu(ev->p_mtu);
|
||||
conn->iso_qos.in.phy = ev->c_phy;
|
||||
conn->iso_qos.out.phy = ev->p_phy;
|
||||
}
|
||||
|
||||
if (!ev->status) {
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_debugfs_create_conn(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
hci_iso_setup_path(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_del(conn);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_le_reject_cis(struct hci_dev *hdev, __le16 handle)
|
||||
{
|
||||
struct hci_cp_le_reject_cis cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = handle;
|
||||
cp.reason = HCI_ERROR_REJ_BAD_ADDR;
|
||||
hci_send_cmd(hdev, HCI_OP_LE_REJECT_CIS, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static void hci_le_accept_cis(struct hci_dev *hdev, __le16 handle)
|
||||
{
|
||||
struct hci_cp_le_accept_cis cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = handle;
|
||||
hci_send_cmd(hdev, HCI_OP_LE_ACCEPT_CIS, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static void hci_le_cis_req_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_evt_le_cis_req *ev = data;
|
||||
u16 acl_handle, cis_handle;
|
||||
struct hci_conn *acl, *cis;
|
||||
int mask;
|
||||
__u8 flags = 0;
|
||||
|
||||
acl_handle = __le16_to_cpu(ev->acl_handle);
|
||||
cis_handle = __le16_to_cpu(ev->cis_handle);
|
||||
|
||||
bt_dev_dbg(hdev, "acl 0x%4.4x handle 0x%4.4x cig 0x%2.2x cis 0x%2.2x",
|
||||
acl_handle, cis_handle, ev->cig_id, ev->cis_id);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
acl = hci_conn_hash_lookup_handle(hdev, acl_handle);
|
||||
if (!acl)
|
||||
goto unlock;
|
||||
|
||||
mask = hci_proto_connect_ind(hdev, &acl->dst, ISO_LINK, &flags);
|
||||
if (!(mask & HCI_LM_ACCEPT)) {
|
||||
hci_le_reject_cis(hdev, ev->cis_handle);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
cis = hci_conn_hash_lookup_handle(hdev, cis_handle);
|
||||
if (!cis) {
|
||||
cis = hci_conn_add(hdev, ISO_LINK, &acl->dst, HCI_ROLE_SLAVE);
|
||||
if (!cis) {
|
||||
hci_le_reject_cis(hdev, ev->cis_handle);
|
||||
goto unlock;
|
||||
}
|
||||
cis->handle = cis_handle;
|
||||
}
|
||||
|
||||
cis->iso_qos.cig = ev->cig_id;
|
||||
cis->iso_qos.cis = ev->cis_id;
|
||||
|
||||
if (!(flags & HCI_PROTO_DEFER)) {
|
||||
hci_le_accept_cis(hdev, ev->cis_handle);
|
||||
} else {
|
||||
cis->state = BT_CONNECT2;
|
||||
hci_connect_cfm(cis, 0);
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_evt_le_create_big_complete *ev = data;
|
||||
struct hci_conn *conn;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
|
||||
|
||||
if (!hci_le_ev_skb_pull(hdev, skb, HCI_EVT_LE_CREATE_BIG_COMPLETE,
|
||||
flex_array_size(ev, bis_handle, ev->num_bis)))
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_big(hdev, ev->handle);
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
if (ev->num_bis)
|
||||
conn->handle = __le16_to_cpu(ev->bis_handle[0]);
|
||||
|
||||
if (!ev->status) {
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_debugfs_create_conn(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
hci_iso_setup_path(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_del(conn);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_evt_le_big_sync_estabilished *ev = data;
|
||||
struct hci_conn *bis;
|
||||
int i;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
|
||||
|
||||
if (!hci_le_ev_skb_pull(hdev, skb, HCI_EVT_LE_BIG_SYNC_ESTABILISHED,
|
||||
flex_array_size(ev, bis, ev->num_bis)))
|
||||
return;
|
||||
|
||||
if (ev->status)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
for (i = 0; i < ev->num_bis; i++) {
|
||||
u16 handle = le16_to_cpu(ev->bis[i]);
|
||||
__le32 interval;
|
||||
|
||||
bis = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!bis) {
|
||||
bis = hci_conn_add(hdev, ISO_LINK, BDADDR_ANY,
|
||||
HCI_ROLE_SLAVE);
|
||||
if (!bis)
|
||||
continue;
|
||||
bis->handle = handle;
|
||||
}
|
||||
|
||||
bis->iso_qos.big = ev->handle;
|
||||
memset(&interval, 0, sizeof(interval));
|
||||
memcpy(&interval, ev->latency, sizeof(ev->latency));
|
||||
bis->iso_qos.in.interval = le32_to_cpu(interval);
|
||||
/* Convert ISO Interval (1.25 ms slots) to latency (ms) */
|
||||
bis->iso_qos.in.latency = le16_to_cpu(ev->interval) * 125 / 100;
|
||||
bis->iso_qos.in.sdu = le16_to_cpu(ev->max_pdu);
|
||||
|
||||
hci_connect_cfm(bis, ev->status);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_evt_le_big_info_adv_report *ev = data;
|
||||
int mask = hdev->link_mode;
|
||||
__u8 flags = 0;
|
||||
|
||||
bt_dev_dbg(hdev, "sync_handle 0x%4.4x", le16_to_cpu(ev->sync_handle));
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, ISO_LINK, &flags);
|
||||
if (!(mask & HCI_LM_ACCEPT))
|
||||
hci_le_pa_term_sync(hdev, ev->sync_handle);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
#define HCI_LE_EV_VL(_op, _func, _min_len, _max_len) \
|
||||
[_op] = { \
|
||||
.func = _func, \
|
||||
|
|
@ -6539,9 +7023,34 @@ static const struct hci_le_ev {
|
|||
HCI_LE_EV_VL(HCI_EV_LE_EXT_ADV_REPORT, hci_le_ext_adv_report_evt,
|
||||
sizeof(struct hci_ev_le_ext_adv_report),
|
||||
HCI_MAX_EVENT_SIZE),
|
||||
/* [0x0e = HCI_EV_LE_PA_SYNC_ESTABLISHED] */
|
||||
HCI_LE_EV(HCI_EV_LE_PA_SYNC_ESTABLISHED,
|
||||
hci_le_pa_sync_estabilished_evt,
|
||||
sizeof(struct hci_ev_le_pa_sync_established)),
|
||||
/* [0x12 = HCI_EV_LE_EXT_ADV_SET_TERM] */
|
||||
HCI_LE_EV(HCI_EV_LE_EXT_ADV_SET_TERM, hci_le_ext_adv_term_evt,
|
||||
sizeof(struct hci_evt_le_ext_adv_set_term)),
|
||||
/* [0x19 = HCI_EVT_LE_CIS_ESTABLISHED] */
|
||||
HCI_LE_EV(HCI_EVT_LE_CIS_ESTABLISHED, hci_le_cis_estabilished_evt,
|
||||
sizeof(struct hci_evt_le_cis_established)),
|
||||
/* [0x1a = HCI_EVT_LE_CIS_REQ] */
|
||||
HCI_LE_EV(HCI_EVT_LE_CIS_REQ, hci_le_cis_req_evt,
|
||||
sizeof(struct hci_evt_le_cis_req)),
|
||||
/* [0x1b = HCI_EVT_LE_CREATE_BIG_COMPLETE] */
|
||||
HCI_LE_EV_VL(HCI_EVT_LE_CREATE_BIG_COMPLETE,
|
||||
hci_le_create_big_complete_evt,
|
||||
sizeof(struct hci_evt_le_create_big_complete),
|
||||
HCI_MAX_EVENT_SIZE),
|
||||
/* [0x1d = HCI_EV_LE_BIG_SYNC_ESTABILISHED] */
|
||||
HCI_LE_EV_VL(HCI_EVT_LE_BIG_SYNC_ESTABILISHED,
|
||||
hci_le_big_sync_established_evt,
|
||||
sizeof(struct hci_evt_le_big_sync_estabilished),
|
||||
HCI_MAX_EVENT_SIZE),
|
||||
/* [0x22 = HCI_EVT_LE_BIG_INFO_ADV_REPORT] */
|
||||
HCI_LE_EV_VL(HCI_EVT_LE_BIG_INFO_ADV_REPORT,
|
||||
hci_le_big_info_adv_report_evt,
|
||||
sizeof(struct hci_evt_le_big_info_adv_report),
|
||||
HCI_MAX_EVENT_SIZE),
|
||||
};
|
||||
|
||||
static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
|
||||
|
|
@ -6580,7 +7089,6 @@ static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
|
|||
if (skb->len > subev->max_len)
|
||||
bt_dev_warn(hdev, "unexpected subevent 0x%2.2x length: %u > %u",
|
||||
ev->subevent, skb->len, subev->max_len);
|
||||
|
||||
data = hci_le_ev_skb_pull(hdev, skb, ev->subevent, subev->min_len);
|
||||
if (!data)
|
||||
return;
|
||||
|
|
@ -6935,6 +7443,9 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
goto done;
|
||||
}
|
||||
|
||||
kfree_skb(hdev->recv_event);
|
||||
hdev->recv_event = skb_clone(skb, GFP_KERNEL);
|
||||
|
||||
event = hdr->evt;
|
||||
if (!event) {
|
||||
bt_dev_warn(hdev, "Received unexpected HCI Event 0x%2.2x",
|
||||
|
|
|
|||
|
|
@ -827,7 +827,6 @@ void __hci_req_disable_advertising(struct hci_request *req)
|
|||
{
|
||||
if (ext_adv_capable(req->hdev)) {
|
||||
__hci_req_disable_ext_adv_instance(req, 0x00);
|
||||
|
||||
} else {
|
||||
u8 enable = 0x00;
|
||||
|
||||
|
|
@ -1338,15 +1337,15 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
|
|||
bdaddr_t random_addr;
|
||||
u8 own_addr_type;
|
||||
int err;
|
||||
struct adv_info *adv_instance;
|
||||
bool secondary_adv;
|
||||
struct adv_info *adv;
|
||||
bool secondary_adv, require_privacy;
|
||||
|
||||
if (instance > 0) {
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
adv_instance = NULL;
|
||||
adv = NULL;
|
||||
}
|
||||
|
||||
flags = hci_adv_instance_flags(hdev, instance);
|
||||
|
|
@ -1364,18 +1363,24 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
|
|||
* advertising is used. In that case it is fine to use a
|
||||
* non-resolvable private address.
|
||||
*/
|
||||
err = hci_get_random_address(hdev, !connectable,
|
||||
adv_use_rpa(hdev, flags), adv_instance,
|
||||
require_privacy = !connectable;
|
||||
|
||||
/* Don't require privacy for periodic adv? */
|
||||
if (adv && adv->periodic)
|
||||
require_privacy = false;
|
||||
|
||||
err = hci_get_random_address(hdev, require_privacy,
|
||||
adv_use_rpa(hdev, flags), adv,
|
||||
&own_addr_type, &random_addr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
if (adv_instance) {
|
||||
hci_cpu_to_le24(adv_instance->min_interval, cp.min_interval);
|
||||
hci_cpu_to_le24(adv_instance->max_interval, cp.max_interval);
|
||||
cp.tx_power = adv_instance->tx_power;
|
||||
if (adv) {
|
||||
hci_cpu_to_le24(adv->min_interval, cp.min_interval);
|
||||
hci_cpu_to_le24(adv->max_interval, cp.max_interval);
|
||||
cp.tx_power = adv->tx_power;
|
||||
} else {
|
||||
hci_cpu_to_le24(hdev->le_adv_min_interval, cp.min_interval);
|
||||
hci_cpu_to_le24(hdev->le_adv_max_interval, cp.max_interval);
|
||||
|
|
@ -1396,7 +1401,8 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
|
|||
else
|
||||
cp.evt_properties = cpu_to_le16(LE_LEGACY_ADV_SCAN_IND);
|
||||
} else {
|
||||
if (secondary_adv)
|
||||
/* Secondary and periodic cannot use legacy PDUs */
|
||||
if (secondary_adv || (adv && adv->periodic))
|
||||
cp.evt_properties = cpu_to_le16(LE_EXT_ADV_NON_CONN_IND);
|
||||
else
|
||||
cp.evt_properties = cpu_to_le16(LE_LEGACY_NONCONN_IND);
|
||||
|
|
@ -1426,8 +1432,8 @@ int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance)
|
|||
struct hci_cp_le_set_adv_set_rand_addr cp;
|
||||
|
||||
/* Check if random address need to be updated */
|
||||
if (adv_instance) {
|
||||
if (!bacmp(&random_addr, &adv_instance->random_addr))
|
||||
if (adv) {
|
||||
if (!bacmp(&random_addr, &adv->random_addr))
|
||||
return 0;
|
||||
} else {
|
||||
if (!bacmp(&random_addr, &hdev->random_addr))
|
||||
|
|
@ -1835,21 +1841,6 @@ void __hci_req_update_scan(struct hci_request *req)
|
|||
hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
|
||||
}
|
||||
|
||||
static int update_scan(struct hci_request *req, unsigned long opt)
|
||||
{
|
||||
hci_dev_lock(req->hdev);
|
||||
__hci_req_update_scan(req);
|
||||
hci_dev_unlock(req->hdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scan_update_work(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev, scan_update);
|
||||
|
||||
hci_req_sync(hdev, update_scan, 0, HCI_CMD_TIMEOUT, NULL);
|
||||
}
|
||||
|
||||
static u8 get_service_classes(struct hci_dev *hdev)
|
||||
{
|
||||
struct bt_uuid *uuid;
|
||||
|
|
@ -1890,69 +1881,6 @@ void __hci_req_update_class(struct hci_request *req)
|
|||
hci_req_add(req, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
|
||||
}
|
||||
|
||||
static void write_iac(struct hci_request *req)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct hci_cp_write_current_iac_lap cp;
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_DISCOVERABLE))
|
||||
return;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) {
|
||||
/* Limited discoverable mode */
|
||||
cp.num_iac = min_t(u8, hdev->num_iac, 2);
|
||||
cp.iac_lap[0] = 0x00; /* LIAC */
|
||||
cp.iac_lap[1] = 0x8b;
|
||||
cp.iac_lap[2] = 0x9e;
|
||||
cp.iac_lap[3] = 0x33; /* GIAC */
|
||||
cp.iac_lap[4] = 0x8b;
|
||||
cp.iac_lap[5] = 0x9e;
|
||||
} else {
|
||||
/* General discoverable mode */
|
||||
cp.num_iac = 1;
|
||||
cp.iac_lap[0] = 0x33; /* GIAC */
|
||||
cp.iac_lap[1] = 0x8b;
|
||||
cp.iac_lap[2] = 0x9e;
|
||||
}
|
||||
|
||||
hci_req_add(req, HCI_OP_WRITE_CURRENT_IAC_LAP,
|
||||
(cp.num_iac * 3) + 1, &cp);
|
||||
}
|
||||
|
||||
static int discoverable_update(struct hci_request *req, unsigned long opt)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) {
|
||||
write_iac(req);
|
||||
__hci_req_update_scan(req);
|
||||
__hci_req_update_class(req);
|
||||
}
|
||||
|
||||
/* Advertising instances don't use the global discoverable setting, so
|
||||
* only update AD if advertising was enabled using Set Advertising.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
|
||||
__hci_req_update_adv_data(req, 0x00);
|
||||
|
||||
/* Discoverable mode affects the local advertising
|
||||
* address in limited privacy mode.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_LIMITED_PRIVACY)) {
|
||||
if (ext_adv_capable(hdev))
|
||||
__hci_req_start_ext_adv(req, 0x00);
|
||||
else
|
||||
__hci_req_enable_advertising(req);
|
||||
}
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
|
||||
u8 reason)
|
||||
{
|
||||
|
|
@ -2227,146 +2155,6 @@ static void le_scan_restart_work(struct work_struct *work)
|
|||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static int active_scan(struct hci_request *req, unsigned long opt)
|
||||
{
|
||||
uint16_t interval = opt;
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
u8 own_addr_type;
|
||||
/* Accept list is not used for discovery */
|
||||
u8 filter_policy = 0x00;
|
||||
/* Default is to enable duplicates filter */
|
||||
u8 filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
|
||||
/* Discovery doesn't require controller address resolution */
|
||||
bool addr_resolv = false;
|
||||
int err;
|
||||
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
/* If controller is scanning, it means the background scanning is
|
||||
* running. Thus, we should temporarily stop it in order to set the
|
||||
* discovery scanning parameters.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
|
||||
hci_req_add_le_scan_disable(req, false);
|
||||
cancel_interleave_scan(hdev);
|
||||
}
|
||||
|
||||
/* All active scans will be done with either a resolvable private
|
||||
* address (when privacy feature has been enabled) or non-resolvable
|
||||
* private address.
|
||||
*/
|
||||
err = hci_update_random_address(req, true, scan_use_rpa(hdev),
|
||||
&own_addr_type);
|
||||
if (err < 0)
|
||||
own_addr_type = ADDR_LE_DEV_PUBLIC;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
if (hci_is_adv_monitoring(hdev)) {
|
||||
/* Duplicate filter should be disabled when some advertisement
|
||||
* monitor is activated, otherwise AdvMon can only receive one
|
||||
* advertisement for one peer(*) during active scanning, and
|
||||
* might report loss to these peers.
|
||||
*
|
||||
* Note that different controllers have different meanings of
|
||||
* |duplicate|. Some of them consider packets with the same
|
||||
* address as duplicate, and others consider packets with the
|
||||
* same address and the same RSSI as duplicate. Although in the
|
||||
* latter case we don't need to disable duplicate filter, but
|
||||
* it is common to have active scanning for a short period of
|
||||
* time, the power impact should be neglectable.
|
||||
*/
|
||||
filter_dup = LE_SCAN_FILTER_DUP_DISABLE;
|
||||
}
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
hci_req_start_scan(req, LE_SCAN_ACTIVE, interval,
|
||||
hdev->le_scan_window_discovery, own_addr_type,
|
||||
filter_policy, filter_dup, addr_resolv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int interleaved_discov(struct hci_request *req, unsigned long opt)
|
||||
{
|
||||
int err;
|
||||
|
||||
bt_dev_dbg(req->hdev, "");
|
||||
|
||||
err = active_scan(req, opt);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return bredr_inquiry(req, DISCOV_BREDR_INQUIRY_LEN);
|
||||
}
|
||||
|
||||
static void start_discovery(struct hci_dev *hdev, u8 *status)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
bt_dev_dbg(hdev, "type %u", hdev->discovery.type);
|
||||
|
||||
switch (hdev->discovery.type) {
|
||||
case DISCOV_TYPE_BREDR:
|
||||
if (!hci_dev_test_flag(hdev, HCI_INQUIRY))
|
||||
hci_req_sync(hdev, bredr_inquiry,
|
||||
DISCOV_BREDR_INQUIRY_LEN, HCI_CMD_TIMEOUT,
|
||||
status);
|
||||
return;
|
||||
case DISCOV_TYPE_INTERLEAVED:
|
||||
/* When running simultaneous discovery, the LE scanning time
|
||||
* should occupy the whole discovery time sine BR/EDR inquiry
|
||||
* and LE scanning are scheduled by the controller.
|
||||
*
|
||||
* For interleaving discovery in comparison, BR/EDR inquiry
|
||||
* and LE scanning are done sequentially with separate
|
||||
* timeouts.
|
||||
*/
|
||||
if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY,
|
||||
&hdev->quirks)) {
|
||||
timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
|
||||
/* During simultaneous discovery, we double LE scan
|
||||
* interval. We must leave some time for the controller
|
||||
* to do BR/EDR inquiry.
|
||||
*/
|
||||
hci_req_sync(hdev, interleaved_discov,
|
||||
hdev->le_scan_int_discovery * 2, HCI_CMD_TIMEOUT,
|
||||
status);
|
||||
break;
|
||||
}
|
||||
|
||||
timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
|
||||
hci_req_sync(hdev, active_scan, hdev->le_scan_int_discovery,
|
||||
HCI_CMD_TIMEOUT, status);
|
||||
break;
|
||||
case DISCOV_TYPE_LE:
|
||||
timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
|
||||
hci_req_sync(hdev, active_scan, hdev->le_scan_int_discovery,
|
||||
HCI_CMD_TIMEOUT, status);
|
||||
break;
|
||||
default:
|
||||
*status = HCI_ERROR_UNSPECIFIED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (*status)
|
||||
return;
|
||||
|
||||
bt_dev_dbg(hdev, "timeout %u ms", jiffies_to_msecs(timeout));
|
||||
|
||||
/* When service discovery is used and the controller has a
|
||||
* strict duplicate filter, it is important to remember the
|
||||
* start and duration of the scan. This is required for
|
||||
* restarting scanning during the discovery phase.
|
||||
*/
|
||||
if (test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) &&
|
||||
hdev->discovery.result_filtering) {
|
||||
hdev->discovery.scan_start = jiffies;
|
||||
hdev->discovery.scan_duration = timeout;
|
||||
}
|
||||
|
||||
queue_delayed_work(hdev->req_workqueue, &hdev->le_scan_disable,
|
||||
timeout);
|
||||
}
|
||||
|
||||
bool hci_req_stop_discovery(struct hci_request *req)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
|
|
@ -2462,180 +2250,8 @@ int hci_req_configure_datapath(struct hci_dev *hdev, struct bt_codec *codec)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int stop_discovery(struct hci_request *req, unsigned long opt)
|
||||
{
|
||||
hci_dev_lock(req->hdev);
|
||||
hci_req_stop_discovery(req);
|
||||
hci_dev_unlock(req->hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void discov_update(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
discov_update);
|
||||
u8 status = 0;
|
||||
|
||||
switch (hdev->discovery.state) {
|
||||
case DISCOVERY_STARTING:
|
||||
start_discovery(hdev, &status);
|
||||
mgmt_start_discovery_complete(hdev, status);
|
||||
if (status)
|
||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||
else
|
||||
hci_discovery_set_state(hdev, DISCOVERY_FINDING);
|
||||
break;
|
||||
case DISCOVERY_STOPPING:
|
||||
hci_req_sync(hdev, stop_discovery, 0, HCI_CMD_TIMEOUT, &status);
|
||||
mgmt_stop_discovery_complete(hdev, status);
|
||||
if (!status)
|
||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||
break;
|
||||
case DISCOVERY_STOPPED:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void discov_off(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
discov_off.work);
|
||||
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
/* When discoverable timeout triggers, then just make sure
|
||||
* the limited discoverable flag is cleared. Even in the case
|
||||
* of a timeout triggered from general discoverable, it is
|
||||
* safe to unconditionally clear the flag.
|
||||
*/
|
||||
hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
|
||||
hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
|
||||
hdev->discov_timeout = 0;
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
hci_req_sync(hdev, discoverable_update, 0, HCI_CMD_TIMEOUT, NULL);
|
||||
mgmt_new_settings(hdev);
|
||||
}
|
||||
|
||||
static int powered_update_hci(struct hci_request *req, unsigned long opt)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
u8 link_sec;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED) &&
|
||||
!lmp_host_ssp_capable(hdev)) {
|
||||
u8 mode = 0x01;
|
||||
|
||||
hci_req_add(req, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
|
||||
|
||||
if (bredr_sc_enabled(hdev) && !lmp_host_sc_capable(hdev)) {
|
||||
u8 support = 0x01;
|
||||
|
||||
hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
|
||||
sizeof(support), &support);
|
||||
}
|
||||
}
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
|
||||
lmp_bredr_capable(hdev)) {
|
||||
struct hci_cp_write_le_host_supported cp;
|
||||
|
||||
cp.le = 0x01;
|
||||
cp.simul = 0x00;
|
||||
|
||||
/* Check first if we already have the right
|
||||
* host state (host features set)
|
||||
*/
|
||||
if (cp.le != lmp_host_le_capable(hdev) ||
|
||||
cp.simul != lmp_host_le_br_capable(hdev))
|
||||
hci_req_add(req, HCI_OP_WRITE_LE_HOST_SUPPORTED,
|
||||
sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
|
||||
/* Make sure the controller has a good default for
|
||||
* advertising data. This also applies to the case
|
||||
* where BR/EDR was toggled during the AUTO_OFF phase.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||
list_empty(&hdev->adv_instances)) {
|
||||
int err;
|
||||
|
||||
if (ext_adv_capable(hdev)) {
|
||||
err = __hci_req_setup_ext_adv_instance(req,
|
||||
0x00);
|
||||
if (!err)
|
||||
__hci_req_update_scan_rsp_data(req,
|
||||
0x00);
|
||||
} else {
|
||||
err = 0;
|
||||
__hci_req_update_adv_data(req, 0x00);
|
||||
__hci_req_update_scan_rsp_data(req, 0x00);
|
||||
}
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
|
||||
if (!ext_adv_capable(hdev))
|
||||
__hci_req_enable_advertising(req);
|
||||
else if (!err)
|
||||
__hci_req_enable_ext_advertising(req,
|
||||
0x00);
|
||||
}
|
||||
} else if (!list_empty(&hdev->adv_instances)) {
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
adv_instance = list_first_entry(&hdev->adv_instances,
|
||||
struct adv_info, list);
|
||||
__hci_req_schedule_adv_instance(req,
|
||||
adv_instance->instance,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
link_sec = hci_dev_test_flag(hdev, HCI_LINK_SECURITY);
|
||||
if (link_sec != test_bit(HCI_AUTH, &hdev->flags))
|
||||
hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE,
|
||||
sizeof(link_sec), &link_sec);
|
||||
|
||||
if (lmp_bredr_capable(hdev)) {
|
||||
if (hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE))
|
||||
__hci_req_write_fast_connectable(req, true);
|
||||
else
|
||||
__hci_req_write_fast_connectable(req, false);
|
||||
__hci_req_update_scan(req);
|
||||
__hci_req_update_class(req);
|
||||
__hci_req_update_name(req);
|
||||
__hci_req_update_eir(req);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __hci_req_hci_power_on(struct hci_dev *hdev)
|
||||
{
|
||||
/* Register the available SMP channels (BR/EDR and LE) only when
|
||||
* successfully powering on the controller. This late
|
||||
* registration is required so that LE SMP can clearly decide if
|
||||
* the public address or static address is used.
|
||||
*/
|
||||
smp_register(hdev);
|
||||
|
||||
return __hci_req_sync(hdev, powered_update_hci, 0, HCI_CMD_TIMEOUT,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void hci_request_setup(struct hci_dev *hdev)
|
||||
{
|
||||
INIT_WORK(&hdev->discov_update, discov_update);
|
||||
INIT_WORK(&hdev->scan_update, scan_update_work);
|
||||
INIT_DELAYED_WORK(&hdev->discov_off, discov_off);
|
||||
INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
|
||||
INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
|
||||
INIT_DELAYED_WORK(&hdev->adv_instance_expire, adv_timeout_expire);
|
||||
|
|
@ -2646,9 +2262,6 @@ void hci_request_cancel_all(struct hci_dev *hdev)
|
|||
{
|
||||
__hci_cmd_sync_cancel(hdev, ENODEV);
|
||||
|
||||
cancel_work_sync(&hdev->discov_update);
|
||||
cancel_work_sync(&hdev->scan_update);
|
||||
cancel_delayed_work_sync(&hdev->discov_off);
|
||||
cancel_delayed_work_sync(&hdev->le_scan_disable);
|
||||
cancel_delayed_work_sync(&hdev->le_scan_restart);
|
||||
|
||||
|
|
|
|||
|
|
@ -68,8 +68,6 @@ int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
|
|||
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
const void *param);
|
||||
|
||||
int __hci_req_hci_power_on(struct hci_dev *hdev);
|
||||
|
||||
void __hci_req_write_fast_connectable(struct hci_request *req, bool enable);
|
||||
void __hci_req_update_name(struct hci_request *req);
|
||||
void __hci_req_update_eir(struct hci_request *req);
|
||||
|
|
@ -85,6 +83,9 @@ void __hci_req_enable_advertising(struct hci_request *req);
|
|||
void __hci_req_disable_advertising(struct hci_request *req);
|
||||
void __hci_req_update_adv_data(struct hci_request *req, u8 instance);
|
||||
int hci_req_update_adv_data(struct hci_dev *hdev, u8 instance);
|
||||
int hci_req_start_per_adv(struct hci_dev *hdev, u8 instance, u32 flags,
|
||||
u16 min_interval, u16 max_interval,
|
||||
u16 sync_interval);
|
||||
void __hci_req_update_scan_rsp_data(struct hci_request *req, u8 instance);
|
||||
|
||||
int __hci_req_schedule_adv_instance(struct hci_request *req, u8 instance,
|
||||
|
|
@ -94,8 +95,14 @@ void hci_req_clear_adv_instance(struct hci_dev *hdev, struct sock *sk,
|
|||
bool force);
|
||||
|
||||
int __hci_req_setup_ext_adv_instance(struct hci_request *req, u8 instance);
|
||||
int __hci_req_setup_per_adv_instance(struct hci_request *req, u8 instance,
|
||||
u16 min_interval, u16 max_interval);
|
||||
int __hci_req_start_ext_adv(struct hci_request *req, u8 instance);
|
||||
int __hci_req_start_per_adv(struct hci_request *req, u8 instance, u32 flags,
|
||||
u16 min_interval, u16 max_interval,
|
||||
u16 sync_interval);
|
||||
int __hci_req_enable_ext_advertising(struct hci_request *req, u8 instance);
|
||||
int __hci_req_enable_per_advertising(struct hci_request *req, u8 instance);
|
||||
int __hci_req_disable_ext_adv_instance(struct hci_request *req, u8 instance);
|
||||
int __hci_req_remove_ext_adv_instance(struct hci_request *req, u8 instance);
|
||||
void __hci_req_clear_ext_adv_sets(struct hci_request *req);
|
||||
|
|
@ -110,11 +117,6 @@ bool hci_req_stop_discovery(struct hci_request *req);
|
|||
|
||||
int hci_req_configure_datapath(struct hci_dev *hdev, struct bt_codec *codec);
|
||||
|
||||
static inline void hci_req_update_scan(struct hci_dev *hdev)
|
||||
{
|
||||
queue_work(hdev->req_workqueue, &hdev->scan_update);
|
||||
}
|
||||
|
||||
void __hci_req_update_scan(struct hci_request *req);
|
||||
|
||||
int hci_update_random_address(struct hci_request *req, bool require_privacy,
|
||||
|
|
|
|||
|
|
@ -869,7 +869,8 @@ static int hci_sock_release(struct socket *sock)
|
|||
|
||||
hdev = hci_pi(sk)->hdev;
|
||||
if (hdev) {
|
||||
if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
|
||||
if (hci_pi(sk)->channel == HCI_CHANNEL_USER &&
|
||||
!hci_dev_test_flag(hdev, HCI_UNREGISTER)) {
|
||||
/* When releasing a user channel exclusive access,
|
||||
* call hci_dev_do_close directly instead of calling
|
||||
* hci_dev_close to ensure the exclusive access will
|
||||
|
|
@ -878,9 +879,15 @@ static int hci_sock_release(struct socket *sock)
|
|||
* The checking of HCI_AUTO_OFF is not needed in this
|
||||
* case since it will have been cleared already when
|
||||
* opening the user channel.
|
||||
*
|
||||
* Make sure to also check that we haven't already
|
||||
* unregistered since all the cleanup will have already
|
||||
* been complete and hdev will get released when we put
|
||||
* below.
|
||||
*/
|
||||
hci_dev_do_close(hdev);
|
||||
hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
|
||||
hci_register_suspend_notifier(hdev);
|
||||
mgmt_index_added(hdev);
|
||||
}
|
||||
|
||||
|
|
@ -1209,6 +1216,7 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
|
|||
}
|
||||
|
||||
mgmt_index_removed(hdev);
|
||||
hci_unregister_suspend_notifier(hdev);
|
||||
|
||||
err = hci_dev_open(hdev->id);
|
||||
if (err) {
|
||||
|
|
@ -1223,6 +1231,7 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
|
|||
err = 0;
|
||||
} else {
|
||||
hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
|
||||
hci_register_suspend_notifier(hdev);
|
||||
mgmt_index_added(hdev);
|
||||
hci_dev_put(hdev);
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -849,26 +849,38 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
|
|||
u8 data[HCI_MAX_EXT_AD_LENGTH];
|
||||
} pdu;
|
||||
u8 len;
|
||||
struct adv_info *adv = NULL;
|
||||
int err;
|
||||
|
||||
memset(&pdu, 0, sizeof(pdu));
|
||||
|
||||
if (instance) {
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv || !adv->scan_rsp_changed)
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = eir_create_scan_rsp(hdev, instance, pdu.data);
|
||||
|
||||
if (hdev->scan_rsp_data_len == len &&
|
||||
!memcmp(pdu.data, hdev->scan_rsp_data, len))
|
||||
return 0;
|
||||
|
||||
memcpy(hdev->scan_rsp_data, pdu.data, len);
|
||||
hdev->scan_rsp_data_len = len;
|
||||
|
||||
pdu.cp.handle = instance;
|
||||
pdu.cp.length = len;
|
||||
pdu.cp.operation = LE_SET_ADV_DATA_OP_COMPLETE;
|
||||
pdu.cp.frag_pref = LE_SET_ADV_DATA_NO_FRAG;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_RSP_DATA,
|
||||
sizeof(pdu.cp) + len, &pdu.cp,
|
||||
HCI_CMD_TIMEOUT);
|
||||
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_RSP_DATA,
|
||||
sizeof(pdu.cp) + len, &pdu.cp,
|
||||
HCI_CMD_TIMEOUT);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (adv) {
|
||||
adv->scan_rsp_changed = false;
|
||||
} else {
|
||||
memcpy(hdev->scan_rsp_data, pdu.data, len);
|
||||
hdev->scan_rsp_data_len = len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __hci_set_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
|
||||
|
|
@ -965,6 +977,187 @@ int hci_start_ext_adv_sync(struct hci_dev *hdev, u8 instance)
|
|||
return hci_enable_ext_advertising_sync(hdev, instance);
|
||||
}
|
||||
|
||||
static int hci_disable_per_advertising_sync(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct hci_cp_le_set_per_adv_enable cp;
|
||||
|
||||
/* If periodic advertising already disabled there is nothing to do. */
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_PER_ADV))
|
||||
return 0;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.enable = 0x00;
|
||||
cp.handle = instance;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_ENABLE,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static int hci_set_per_adv_params_sync(struct hci_dev *hdev, u8 instance,
|
||||
u16 min_interval, u16 max_interval)
|
||||
{
|
||||
struct hci_cp_le_set_per_adv_params cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
if (!min_interval)
|
||||
min_interval = DISCOV_LE_PER_ADV_INT_MIN;
|
||||
|
||||
if (!max_interval)
|
||||
max_interval = DISCOV_LE_PER_ADV_INT_MAX;
|
||||
|
||||
cp.handle = instance;
|
||||
cp.min_interval = cpu_to_le16(min_interval);
|
||||
cp.max_interval = cpu_to_le16(max_interval);
|
||||
cp.periodic_properties = 0x0000;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_PARAMS,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static int hci_set_per_adv_data_sync(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct {
|
||||
struct hci_cp_le_set_per_adv_data cp;
|
||||
u8 data[HCI_MAX_PER_AD_LENGTH];
|
||||
} pdu;
|
||||
u8 len;
|
||||
|
||||
memset(&pdu, 0, sizeof(pdu));
|
||||
|
||||
if (instance) {
|
||||
struct adv_info *adv = hci_find_adv_instance(hdev, instance);
|
||||
|
||||
if (!adv || !adv->periodic)
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = eir_create_per_adv_data(hdev, instance, pdu.data);
|
||||
|
||||
pdu.cp.length = len;
|
||||
pdu.cp.handle = instance;
|
||||
pdu.cp.operation = LE_SET_ADV_DATA_OP_COMPLETE;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_DATA,
|
||||
sizeof(pdu.cp) + len, &pdu,
|
||||
HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static int hci_enable_per_advertising_sync(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct hci_cp_le_set_per_adv_enable cp;
|
||||
|
||||
/* If periodic advertising already enabled there is nothing to do. */
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_PER_ADV))
|
||||
return 0;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
cp.enable = 0x01;
|
||||
cp.handle = instance;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_PER_ADV_ENABLE,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
/* Checks if periodic advertising data contains a Basic Announcement and if it
|
||||
* does generates a Broadcast ID and add Broadcast Announcement.
|
||||
*/
|
||||
static int hci_adv_bcast_annoucement(struct hci_dev *hdev, struct adv_info *adv)
|
||||
{
|
||||
u8 bid[3];
|
||||
u8 ad[4 + 3];
|
||||
|
||||
/* Skip if NULL adv as instance 0x00 is used for general purpose
|
||||
* advertising so it cannot used for the likes of Broadcast Announcement
|
||||
* as it can be overwritten at any point.
|
||||
*/
|
||||
if (!adv)
|
||||
return 0;
|
||||
|
||||
/* Check if PA data doesn't contains a Basic Audio Announcement then
|
||||
* there is nothing to do.
|
||||
*/
|
||||
if (!eir_get_service_data(adv->per_adv_data, adv->per_adv_data_len,
|
||||
0x1851, NULL))
|
||||
return 0;
|
||||
|
||||
/* Check if advertising data already has a Broadcast Announcement since
|
||||
* the process may want to control the Broadcast ID directly and in that
|
||||
* case the kernel shall no interfere.
|
||||
*/
|
||||
if (eir_get_service_data(adv->adv_data, adv->adv_data_len, 0x1852,
|
||||
NULL))
|
||||
return 0;
|
||||
|
||||
/* Generate Broadcast ID */
|
||||
get_random_bytes(bid, sizeof(bid));
|
||||
eir_append_service_data(ad, 0, 0x1852, bid, sizeof(bid));
|
||||
hci_set_adv_instance_data(hdev, adv->instance, sizeof(ad), ad, 0, NULL);
|
||||
|
||||
return hci_update_adv_data_sync(hdev, adv->instance);
|
||||
}
|
||||
|
||||
int hci_start_per_adv_sync(struct hci_dev *hdev, u8 instance, u8 data_len,
|
||||
u8 *data, u32 flags, u16 min_interval,
|
||||
u16 max_interval, u16 sync_interval)
|
||||
{
|
||||
struct adv_info *adv = NULL;
|
||||
int err;
|
||||
bool added = false;
|
||||
|
||||
hci_disable_per_advertising_sync(hdev, instance);
|
||||
|
||||
if (instance) {
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
/* Create an instance if that could not be found */
|
||||
if (!adv) {
|
||||
adv = hci_add_per_instance(hdev, instance, flags,
|
||||
data_len, data,
|
||||
sync_interval,
|
||||
sync_interval);
|
||||
if (IS_ERR(adv))
|
||||
return PTR_ERR(adv);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only start advertising if instance 0 or if a dedicated instance has
|
||||
* been added.
|
||||
*/
|
||||
if (!adv || added) {
|
||||
err = hci_start_ext_adv_sync(hdev, instance);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
err = hci_adv_bcast_annoucement(hdev, adv);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = hci_set_per_adv_params_sync(hdev, instance, min_interval,
|
||||
max_interval);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
err = hci_set_per_adv_data_sync(hdev, instance);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
err = hci_enable_per_advertising_sync(hdev, instance);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (added)
|
||||
hci_remove_adv_instance(hdev, instance);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hci_start_adv_sync(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
int err;
|
||||
|
|
@ -1104,6 +1297,42 @@ int hci_remove_ext_adv_instance_sync(struct hci_dev *hdev, u8 instance,
|
|||
HCI_CMD_TIMEOUT, sk);
|
||||
}
|
||||
|
||||
static int remove_ext_adv_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct adv_info *adv = data;
|
||||
u8 instance = 0;
|
||||
|
||||
if (adv)
|
||||
instance = adv->instance;
|
||||
|
||||
return hci_remove_ext_adv_instance_sync(hdev, instance, NULL);
|
||||
}
|
||||
|
||||
int hci_remove_ext_adv_instance(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct adv_info *adv = NULL;
|
||||
|
||||
if (instance) {
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return hci_cmd_sync_queue(hdev, remove_ext_adv_sync, adv, NULL);
|
||||
}
|
||||
|
||||
int hci_le_terminate_big_sync(struct hci_dev *hdev, u8 handle, u8 reason)
|
||||
{
|
||||
struct hci_cp_le_term_big cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = handle;
|
||||
cp.reason = reason;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_TERM_BIG,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static void cancel_adv_timeout(struct hci_dev *hdev)
|
||||
{
|
||||
if (hdev->adv_instance_timeout) {
|
||||
|
|
@ -1119,27 +1348,39 @@ static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)
|
|||
u8 data[HCI_MAX_EXT_AD_LENGTH];
|
||||
} pdu;
|
||||
u8 len;
|
||||
struct adv_info *adv = NULL;
|
||||
int err;
|
||||
|
||||
memset(&pdu, 0, sizeof(pdu));
|
||||
|
||||
if (instance) {
|
||||
adv = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv || !adv->adv_data_changed)
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = eir_create_adv_data(hdev, instance, pdu.data);
|
||||
|
||||
/* There's nothing to do if the data hasn't changed */
|
||||
if (hdev->adv_data_len == len &&
|
||||
memcmp(pdu.data, hdev->adv_data, len) == 0)
|
||||
return 0;
|
||||
|
||||
memcpy(hdev->adv_data, pdu.data, len);
|
||||
hdev->adv_data_len = len;
|
||||
|
||||
pdu.cp.length = len;
|
||||
pdu.cp.handle = instance;
|
||||
pdu.cp.operation = LE_SET_ADV_DATA_OP_COMPLETE;
|
||||
pdu.cp.frag_pref = LE_SET_ADV_DATA_NO_FRAG;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA,
|
||||
sizeof(pdu.cp) + len, &pdu.cp,
|
||||
HCI_CMD_TIMEOUT);
|
||||
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_DATA,
|
||||
sizeof(pdu.cp) + len, &pdu.cp,
|
||||
HCI_CMD_TIMEOUT);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Update data if the command succeed */
|
||||
if (adv) {
|
||||
adv->adv_data_changed = false;
|
||||
} else {
|
||||
memcpy(hdev->adv_data, pdu.data, len);
|
||||
hdev->adv_data_len = len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hci_set_adv_data_sync(struct hci_dev *hdev, u8 instance)
|
||||
|
|
@ -1612,6 +1853,9 @@ static int hci_le_add_resolve_list_sync(struct hci_dev *hdev,
|
|||
bacpy(&cp.bdaddr, ¶ms->addr);
|
||||
memcpy(cp.peer_irk, irk->val, 16);
|
||||
|
||||
/* Default privacy mode is always Network */
|
||||
params->privacy_mode = HCI_NETWORK_PRIVACY;
|
||||
|
||||
done:
|
||||
if (hci_dev_test_flag(hdev, HCI_PRIVACY))
|
||||
memcpy(cp.local_irk, hdev->irk, 16);
|
||||
|
|
@ -1865,12 +2109,15 @@ static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
|
|||
}
|
||||
|
||||
/* Go through the current accept list programmed into the
|
||||
* controller one by one and check if that address is still
|
||||
* in the list of pending connections or list of devices to
|
||||
* controller one by one and check if that address is connected or is
|
||||
* still in the list of pending connections or list of devices to
|
||||
* report. If not present in either list, then remove it from
|
||||
* the controller.
|
||||
*/
|
||||
list_for_each_entry_safe(b, t, &hdev->le_accept_list, list) {
|
||||
if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type))
|
||||
continue;
|
||||
|
||||
pend_conn = hci_pend_le_action_lookup(&hdev->pend_le_conns,
|
||||
&b->bdaddr,
|
||||
b->bdaddr_type);
|
||||
|
|
@ -2171,7 +2418,8 @@ int hci_update_passive_scan_sync(struct hci_dev *hdev)
|
|||
|
||||
if (list_empty(&hdev->pend_le_conns) &&
|
||||
list_empty(&hdev->pend_le_reports) &&
|
||||
!hci_is_adv_monitoring(hdev)) {
|
||||
!hci_is_adv_monitoring(hdev) &&
|
||||
!hci_dev_test_flag(hdev, HCI_PA_SYNC)) {
|
||||
/* If there is no pending LE connections or devices
|
||||
* to be scanned for or no ADV monitors, we should stop the
|
||||
* background scanning.
|
||||
|
|
@ -2206,6 +2454,16 @@ int hci_update_passive_scan_sync(struct hci_dev *hdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int update_scan_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
return hci_update_scan_sync(hdev);
|
||||
}
|
||||
|
||||
int hci_update_scan(struct hci_dev *hdev)
|
||||
{
|
||||
return hci_cmd_sync_queue(hdev, update_scan_sync, NULL, NULL);
|
||||
}
|
||||
|
||||
static int update_passive_scan_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
return hci_update_passive_scan_sync(hdev);
|
||||
|
|
@ -2760,6 +3018,12 @@ static const struct hci_init_stage amp_init2[] = {
|
|||
/* Read Buffer Size (ACL mtu, max pkt, etc.) */
|
||||
static int hci_read_buffer_size_sync(struct hci_dev *hdev)
|
||||
{
|
||||
/* Use Read LE Buffer Size V2 if supported */
|
||||
if (hdev->commands[41] & 0x20)
|
||||
return __hci_cmd_sync_status(hdev,
|
||||
HCI_OP_LE_READ_BUFFER_SIZE_V2,
|
||||
0, NULL, HCI_CMD_TIMEOUT);
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_READ_BUFFER_SIZE,
|
||||
0, NULL, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
|
@ -3011,6 +3275,10 @@ static int hci_init2_sync(struct hci_dev *hdev)
|
|||
if (hdev->dev_type == HCI_AMP)
|
||||
return hci_init_stage_sync(hdev, amp_init2);
|
||||
|
||||
err = hci_init_stage_sync(hdev, hci_init2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (lmp_bredr_capable(hdev)) {
|
||||
err = hci_init_stage_sync(hdev, br_init2);
|
||||
if (err)
|
||||
|
|
@ -3028,7 +3296,7 @@ static int hci_init2_sync(struct hci_dev *hdev)
|
|||
hci_dev_set_flag(hdev, HCI_LE_ENABLED);
|
||||
}
|
||||
|
||||
return hci_init_stage_sync(hdev, hci_init2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hci_set_event_mask_sync(struct hci_dev *hdev)
|
||||
|
|
@ -3191,7 +3459,7 @@ static int hci_read_page_scan_activity_sync(struct hci_dev *hdev)
|
|||
static int hci_read_def_err_data_reporting_sync(struct hci_dev *hdev)
|
||||
{
|
||||
if (!(hdev->commands[18] & 0x04) ||
|
||||
test_bit(HCI_QUIRK_BROKEN_ERR_DATA_REPORTING, &hdev->quirks))
|
||||
!(hdev->features[0][6] & LMP_ERR_DATA_REPORTING))
|
||||
return 0;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_READ_DEF_ERR_DATA_REPORTING,
|
||||
|
|
@ -3349,6 +3617,19 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev)
|
|||
if (ext_adv_capable(hdev))
|
||||
events[2] |= 0x02; /* LE Advertising Set Terminated */
|
||||
|
||||
if (cis_capable(hdev)) {
|
||||
events[3] |= 0x01; /* LE CIS Established */
|
||||
if (cis_peripheral_capable(hdev))
|
||||
events[3] |= 0x02; /* LE CIS Request */
|
||||
}
|
||||
|
||||
if (bis_capable(hdev)) {
|
||||
events[3] |= 0x04; /* LE Create BIG Complete */
|
||||
events[3] |= 0x08; /* LE Terminate BIG Complete */
|
||||
events[3] |= 0x10; /* LE BIG Sync Established */
|
||||
events[3] |= 0x20; /* LE BIG Sync Loss */
|
||||
}
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EVENT_MASK,
|
||||
sizeof(events), events, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
|
@ -3489,6 +3770,24 @@ static int hci_set_le_support_sync(struct hci_dev *hdev)
|
|||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
/* LE Set Host Feature */
|
||||
static int hci_le_set_host_feature_sync(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_cp_le_set_host_feature cp;
|
||||
|
||||
if (!iso_capable(hdev))
|
||||
return 0;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
/* Isochronous Channels (Host Support) */
|
||||
cp.bit_number = 32;
|
||||
cp.bit_value = 1;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_HOST_FEATURE,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
/* LE Controller init stage 3 command sequence */
|
||||
static const struct hci_init_stage le_init3[] = {
|
||||
/* HCI_OP_LE_SET_EVENT_MASK */
|
||||
|
|
@ -3515,6 +3814,8 @@ static const struct hci_init_stage le_init3[] = {
|
|||
HCI_INIT(hci_le_read_num_support_adv_sets_sync),
|
||||
/* HCI_OP_WRITE_LE_HOST_SUPPORTED */
|
||||
HCI_INIT(hci_set_le_support_sync),
|
||||
/* HCI_OP_LE_SET_HOST_FEATURE */
|
||||
HCI_INIT(hci_le_set_host_feature_sync),
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
@ -3578,7 +3879,7 @@ static int hci_set_event_mask_page_2_sync(struct hci_dev *hdev)
|
|||
if (lmp_cpb_central_capable(hdev)) {
|
||||
events[1] |= 0x40; /* Triggered Clock Capture */
|
||||
events[1] |= 0x80; /* Synchronization Train Complete */
|
||||
events[2] |= 0x10; /* Peripheral Page Response Timeout */
|
||||
events[2] |= 0x08; /* Truncated Page Complete */
|
||||
events[2] |= 0x20; /* CPB Channel Map Change */
|
||||
changed = true;
|
||||
}
|
||||
|
|
@ -3590,7 +3891,7 @@ static int hci_set_event_mask_page_2_sync(struct hci_dev *hdev)
|
|||
events[2] |= 0x01; /* Synchronization Train Received */
|
||||
events[2] |= 0x02; /* CPB Receive */
|
||||
events[2] |= 0x04; /* CPB Timeout */
|
||||
events[2] |= 0x08; /* Truncated Page Complete */
|
||||
events[2] |= 0x10; /* Peripheral Page Response Timeout */
|
||||
changed = true;
|
||||
}
|
||||
|
||||
|
|
@ -3676,7 +3977,7 @@ static int hci_set_err_data_report_sync(struct hci_dev *hdev)
|
|||
bool enabled = hci_dev_test_flag(hdev, HCI_WIDEBAND_SPEECH_ENABLED);
|
||||
|
||||
if (!(hdev->commands[18] & 0x08) ||
|
||||
test_bit(HCI_QUIRK_BROKEN_ERR_DATA_REPORTING, &hdev->quirks))
|
||||
!(hdev->features[0][6] & LMP_ERR_DATA_REPORTING))
|
||||
return 0;
|
||||
|
||||
if (enabled == hdev->err_data_reporting)
|
||||
|
|
@ -3835,9 +4136,6 @@ static const struct {
|
|||
HCI_QUIRK_BROKEN(STORED_LINK_KEY,
|
||||
"HCI Delete Stored Link Key command is advertised, "
|
||||
"but not supported."),
|
||||
HCI_QUIRK_BROKEN(ERR_DATA_REPORTING,
|
||||
"HCI Read Default Erroneous Data Reporting command is "
|
||||
"advertised, but not supported."),
|
||||
HCI_QUIRK_BROKEN(READ_TRANSMIT_POWER,
|
||||
"HCI Read Transmit Power Level command is advertised, "
|
||||
"but not supported."),
|
||||
|
|
@ -3848,9 +4146,148 @@ static const struct {
|
|||
"advertised, but not supported.")
|
||||
};
|
||||
|
||||
int hci_dev_open_sync(struct hci_dev *hdev)
|
||||
/* This function handles hdev setup stage:
|
||||
*
|
||||
* Calls hdev->setup
|
||||
* Setup address if HCI_QUIRK_USE_BDADDR_PROPERTY is set.
|
||||
*/
|
||||
static int hci_dev_setup_sync(struct hci_dev *hdev)
|
||||
{
|
||||
int ret = 0;
|
||||
bool invalid_bdaddr;
|
||||
size_t i;
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_SETUP) &&
|
||||
!test_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks))
|
||||
return 0;
|
||||
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
hci_sock_dev_event(hdev, HCI_DEV_SETUP);
|
||||
|
||||
if (hdev->setup)
|
||||
ret = hdev->setup(hdev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hci_broken_table); i++) {
|
||||
if (test_bit(hci_broken_table[i].quirk, &hdev->quirks))
|
||||
bt_dev_warn(hdev, "%s", hci_broken_table[i].desc);
|
||||
}
|
||||
|
||||
/* The transport driver can set the quirk to mark the
|
||||
* BD_ADDR invalid before creating the HCI device or in
|
||||
* its setup callback.
|
||||
*/
|
||||
invalid_bdaddr = test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
|
||||
|
||||
if (!ret) {
|
||||
if (test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks)) {
|
||||
if (!bacmp(&hdev->public_addr, BDADDR_ANY))
|
||||
hci_dev_get_bd_addr_from_property(hdev);
|
||||
|
||||
if (bacmp(&hdev->public_addr, BDADDR_ANY) &&
|
||||
hdev->set_bdaddr) {
|
||||
ret = hdev->set_bdaddr(hdev,
|
||||
&hdev->public_addr);
|
||||
|
||||
/* If setting of the BD_ADDR from the device
|
||||
* property succeeds, then treat the address
|
||||
* as valid even if the invalid BD_ADDR
|
||||
* quirk indicates otherwise.
|
||||
*/
|
||||
if (!ret)
|
||||
invalid_bdaddr = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The transport driver can set these quirks before
|
||||
* creating the HCI device or in its setup callback.
|
||||
*
|
||||
* For the invalid BD_ADDR quirk it is possible that
|
||||
* it becomes a valid address if the bootloader does
|
||||
* provide it (see above).
|
||||
*
|
||||
* In case any of them is set, the controller has to
|
||||
* start up as unconfigured.
|
||||
*/
|
||||
if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) ||
|
||||
invalid_bdaddr)
|
||||
hci_dev_set_flag(hdev, HCI_UNCONFIGURED);
|
||||
|
||||
/* For an unconfigured controller it is required to
|
||||
* read at least the version information provided by
|
||||
* the Read Local Version Information command.
|
||||
*
|
||||
* If the set_bdaddr driver callback is provided, then
|
||||
* also the original Bluetooth public device address
|
||||
* will be read using the Read BD Address command.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED))
|
||||
return hci_unconf_init_sync(hdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function handles hdev init stage:
|
||||
*
|
||||
* Calls hci_dev_setup_sync to perform setup stage
|
||||
* Calls hci_init_sync to perform HCI command init sequence
|
||||
*/
|
||||
static int hci_dev_init_sync(struct hci_dev *hdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
set_bit(HCI_INIT, &hdev->flags);
|
||||
|
||||
ret = hci_dev_setup_sync(hdev);
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_CONFIG)) {
|
||||
/* If public address change is configured, ensure that
|
||||
* the address gets programmed. If the driver does not
|
||||
* support changing the public address, fail the power
|
||||
* on procedure.
|
||||
*/
|
||||
if (bacmp(&hdev->public_addr, BDADDR_ANY) &&
|
||||
hdev->set_bdaddr)
|
||||
ret = hdev->set_bdaddr(hdev, &hdev->public_addr);
|
||||
else
|
||||
ret = -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
if (!hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
|
||||
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
|
||||
ret = hci_init_sync(hdev);
|
||||
if (!ret && hdev->post_init)
|
||||
ret = hdev->post_init(hdev);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the HCI Reset command is clearing all diagnostic settings,
|
||||
* then they need to be reprogrammed after the init procedure
|
||||
* completed.
|
||||
*/
|
||||
if (test_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks) &&
|
||||
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
|
||||
hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) && hdev->set_diag)
|
||||
ret = hdev->set_diag(hdev, true);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
|
||||
msft_do_open(hdev);
|
||||
aosp_do_open(hdev);
|
||||
}
|
||||
|
||||
clear_bit(HCI_INIT, &hdev->flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hci_dev_open_sync(struct hci_dev *hdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
|
|
@ -3903,119 +4340,7 @@ int hci_dev_open_sync(struct hci_dev *hdev)
|
|||
set_bit(HCI_RUNNING, &hdev->flags);
|
||||
hci_sock_dev_event(hdev, HCI_DEV_OPEN);
|
||||
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
set_bit(HCI_INIT, &hdev->flags);
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_SETUP) ||
|
||||
test_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks)) {
|
||||
bool invalid_bdaddr;
|
||||
size_t i;
|
||||
|
||||
hci_sock_dev_event(hdev, HCI_DEV_SETUP);
|
||||
|
||||
if (hdev->setup)
|
||||
ret = hdev->setup(hdev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hci_broken_table); i++) {
|
||||
if (test_bit(hci_broken_table[i].quirk, &hdev->quirks))
|
||||
bt_dev_warn(hdev, "%s",
|
||||
hci_broken_table[i].desc);
|
||||
}
|
||||
|
||||
/* The transport driver can set the quirk to mark the
|
||||
* BD_ADDR invalid before creating the HCI device or in
|
||||
* its setup callback.
|
||||
*/
|
||||
invalid_bdaddr = test_bit(HCI_QUIRK_INVALID_BDADDR,
|
||||
&hdev->quirks);
|
||||
|
||||
if (ret)
|
||||
goto setup_failed;
|
||||
|
||||
if (test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks)) {
|
||||
if (!bacmp(&hdev->public_addr, BDADDR_ANY))
|
||||
hci_dev_get_bd_addr_from_property(hdev);
|
||||
|
||||
if (bacmp(&hdev->public_addr, BDADDR_ANY) &&
|
||||
hdev->set_bdaddr) {
|
||||
ret = hdev->set_bdaddr(hdev,
|
||||
&hdev->public_addr);
|
||||
|
||||
/* If setting of the BD_ADDR from the device
|
||||
* property succeeds, then treat the address
|
||||
* as valid even if the invalid BD_ADDR
|
||||
* quirk indicates otherwise.
|
||||
*/
|
||||
if (!ret)
|
||||
invalid_bdaddr = false;
|
||||
}
|
||||
}
|
||||
|
||||
setup_failed:
|
||||
/* The transport driver can set these quirks before
|
||||
* creating the HCI device or in its setup callback.
|
||||
*
|
||||
* For the invalid BD_ADDR quirk it is possible that
|
||||
* it becomes a valid address if the bootloader does
|
||||
* provide it (see above).
|
||||
*
|
||||
* In case any of them is set, the controller has to
|
||||
* start up as unconfigured.
|
||||
*/
|
||||
if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) ||
|
||||
invalid_bdaddr)
|
||||
hci_dev_set_flag(hdev, HCI_UNCONFIGURED);
|
||||
|
||||
/* For an unconfigured controller it is required to
|
||||
* read at least the version information provided by
|
||||
* the Read Local Version Information command.
|
||||
*
|
||||
* If the set_bdaddr driver callback is provided, then
|
||||
* also the original Bluetooth public device address
|
||||
* will be read using the Read BD Address command.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED))
|
||||
ret = hci_unconf_init_sync(hdev);
|
||||
}
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_CONFIG)) {
|
||||
/* If public address change is configured, ensure that
|
||||
* the address gets programmed. If the driver does not
|
||||
* support changing the public address, fail the power
|
||||
* on procedure.
|
||||
*/
|
||||
if (bacmp(&hdev->public_addr, BDADDR_ANY) &&
|
||||
hdev->set_bdaddr)
|
||||
ret = hdev->set_bdaddr(hdev, &hdev->public_addr);
|
||||
else
|
||||
ret = -EADDRNOTAVAIL;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
if (!hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
|
||||
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
|
||||
ret = hci_init_sync(hdev);
|
||||
if (!ret && hdev->post_init)
|
||||
ret = hdev->post_init(hdev);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the HCI Reset command is clearing all diagnostic settings,
|
||||
* then they need to be reprogrammed after the init procedure
|
||||
* completed.
|
||||
*/
|
||||
if (test_bit(HCI_QUIRK_NON_PERSISTENT_DIAG, &hdev->quirks) &&
|
||||
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
|
||||
hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) && hdev->set_diag)
|
||||
ret = hdev->set_diag(hdev, true);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
|
||||
msft_do_open(hdev);
|
||||
aosp_do_open(hdev);
|
||||
}
|
||||
|
||||
clear_bit(HCI_INIT, &hdev->flags);
|
||||
|
||||
ret = hci_dev_init_sync(hdev);
|
||||
if (!ret) {
|
||||
hci_dev_hold(hdev);
|
||||
hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
|
||||
|
|
@ -4435,8 +4760,7 @@ static int hci_reject_conn_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
|||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
u8 reason)
|
||||
int hci_abort_conn_sync(struct hci_dev *hdev, struct hci_conn *conn, u8 reason)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
|
@ -5039,13 +5363,13 @@ static int hci_resume_scan_sync(struct hci_dev *hdev)
|
|||
if (!hdev->scanning_paused)
|
||||
return 0;
|
||||
|
||||
hdev->scanning_paused = false;
|
||||
|
||||
hci_update_scan_sync(hdev);
|
||||
|
||||
/* Reset passive scanning to normal */
|
||||
hci_update_passive_scan_sync(hdev);
|
||||
|
||||
hdev->scanning_paused = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -5064,7 +5388,6 @@ int hci_resume_sync(struct hci_dev *hdev)
|
|||
return 0;
|
||||
|
||||
hdev->suspended = false;
|
||||
hdev->scanning_paused = false;
|
||||
|
||||
/* Restore event mask */
|
||||
hci_set_event_mask_sync(hdev);
|
||||
|
|
@ -5375,3 +5698,36 @@ int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
|||
hci_resume_advertising_sync(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
int hci_le_remove_cig_sync(struct hci_dev *hdev, u8 handle)
|
||||
{
|
||||
struct hci_cp_le_remove_cig cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.cig_id = handle;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_REMOVE_CIG, sizeof(cp),
|
||||
&cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
int hci_le_big_terminate_sync(struct hci_dev *hdev, u8 handle)
|
||||
{
|
||||
struct hci_cp_le_big_term_sync cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = handle;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_BIG_TERM_SYNC,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
int hci_le_pa_terminate_sync(struct hci_dev *hdev, u16 handle)
|
||||
{
|
||||
struct hci_cp_le_pa_term_sync cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.handle = cpu_to_le16(handle);
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_PA_TERM_SYNC,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
|
|
|||
1824
net/bluetooth/iso.c
Normal file
1824
net/bluetooth/iso.c
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -1369,6 +1369,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan)
|
|||
|
||||
l2cap_le_flowctl_init(chan, 0);
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.psm = chan->psm;
|
||||
req.scid = cpu_to_le16(chan->scid);
|
||||
req.mtu = cpu_to_le16(chan->imtu);
|
||||
|
|
|
|||
|
|
@ -135,6 +135,77 @@ int bt_to_errno(__u16 code)
|
|||
}
|
||||
EXPORT_SYMBOL(bt_to_errno);
|
||||
|
||||
/* Unix errno to Bluetooth error codes mapping */
|
||||
__u8 bt_status(int err)
|
||||
{
|
||||
/* Don't convert if already positive value */
|
||||
if (err >= 0)
|
||||
return err;
|
||||
|
||||
switch (err) {
|
||||
case -EBADRQC:
|
||||
return 0x01;
|
||||
|
||||
case -ENOTCONN:
|
||||
return 0x02;
|
||||
|
||||
case -EIO:
|
||||
return 0x03;
|
||||
|
||||
case -EHOSTDOWN:
|
||||
return 0x04;
|
||||
|
||||
case -EACCES:
|
||||
return 0x05;
|
||||
|
||||
case -EBADE:
|
||||
return 0x06;
|
||||
|
||||
case -ENOMEM:
|
||||
return 0x07;
|
||||
|
||||
case -ETIMEDOUT:
|
||||
return 0x08;
|
||||
|
||||
case -EMLINK:
|
||||
return 0x09;
|
||||
|
||||
case EALREADY:
|
||||
return 0x0b;
|
||||
|
||||
case -EBUSY:
|
||||
return 0x0c;
|
||||
|
||||
case -ECONNREFUSED:
|
||||
return 0x0d;
|
||||
|
||||
case -EOPNOTSUPP:
|
||||
return 0x11;
|
||||
|
||||
case -EINVAL:
|
||||
return 0x12;
|
||||
|
||||
case -ECONNRESET:
|
||||
return 0x13;
|
||||
|
||||
case -ECONNABORTED:
|
||||
return 0x16;
|
||||
|
||||
case ELOOP:
|
||||
return 0x17;
|
||||
|
||||
case -EPROTONOSUPPORT:
|
||||
return 0x1a;
|
||||
|
||||
case -EPROTO:
|
||||
return 0x19;
|
||||
|
||||
default:
|
||||
return 0x1f;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(bt_status);
|
||||
|
||||
void bt_info(const char *format, ...)
|
||||
{
|
||||
struct va_format vaf;
|
||||
|
|
|
|||
|
|
@ -1023,11 +1023,39 @@ static void rpa_expired(struct work_struct *work)
|
|||
hci_cmd_sync_queue(hdev, rpa_expired_sync, NULL, NULL);
|
||||
}
|
||||
|
||||
static void discov_off(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
discov_off.work);
|
||||
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
/* When discoverable timeout triggers, then just make sure
|
||||
* the limited discoverable flag is cleared. Even in the case
|
||||
* of a timeout triggered from general discoverable, it is
|
||||
* safe to unconditionally clear the flag.
|
||||
*/
|
||||
hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE);
|
||||
hci_dev_clear_flag(hdev, HCI_DISCOVERABLE);
|
||||
hdev->discov_timeout = 0;
|
||||
|
||||
hci_update_discoverable(hdev);
|
||||
|
||||
mgmt_new_settings(hdev);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev)
|
||||
{
|
||||
if (hci_dev_test_and_set_flag(hdev, HCI_MGMT))
|
||||
return;
|
||||
|
||||
BT_INFO("MGMT ver %d.%d", MGMT_VERSION, MGMT_REVISION);
|
||||
|
||||
INIT_DELAYED_WORK(&hdev->discov_off, discov_off);
|
||||
INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off);
|
||||
INIT_DELAYED_WORK(&hdev->rpa_expired, rpa_expired);
|
||||
|
||||
|
|
@ -1082,11 +1110,11 @@ static u16 append_eir_data_to_buf(struct hci_dev *hdev, u8 *eir)
|
|||
eir_len = eir_append_le16(eir, eir_len, EIR_APPEARANCE,
|
||||
hdev->appearance);
|
||||
|
||||
name_len = strlen(hdev->dev_name);
|
||||
name_len = strnlen(hdev->dev_name, sizeof(hdev->dev_name));
|
||||
eir_len = eir_append_data(eir, eir_len, EIR_NAME_COMPLETE,
|
||||
hdev->dev_name, name_len);
|
||||
|
||||
name_len = strlen(hdev->short_name);
|
||||
name_len = strnlen(hdev->short_name, sizeof(hdev->short_name));
|
||||
eir_len = eir_append_data(eir, eir_len, EIR_NAME_SHORT,
|
||||
hdev->short_name, name_len);
|
||||
|
||||
|
|
@ -1611,7 +1639,7 @@ static int set_connectable_update_settings(struct hci_dev *hdev,
|
|||
return err;
|
||||
|
||||
if (changed) {
|
||||
hci_req_update_scan(hdev);
|
||||
hci_update_scan(hdev);
|
||||
hci_update_passive_scan(hdev);
|
||||
return new_settings(hdev, sk);
|
||||
}
|
||||
|
|
@ -2528,6 +2556,37 @@ static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
|||
skip_sk);
|
||||
}
|
||||
|
||||
static void unpair_device_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_cp_unpair_device *cp = cmd->param;
|
||||
|
||||
if (!err)
|
||||
device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk);
|
||||
|
||||
cmd->cmd_complete(cmd, err);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
static int unpair_device_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_cp_unpair_device *cp = cmd->param;
|
||||
struct hci_conn *conn;
|
||||
|
||||
if (cp->addr.type == BDADDR_BREDR)
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
|
||||
&cp->addr.bdaddr);
|
||||
else
|
||||
conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr,
|
||||
le_addr_type(cp->addr.type));
|
||||
|
||||
if (!conn)
|
||||
return 0;
|
||||
|
||||
return hci_abort_conn_sync(hdev, conn, HCI_ERROR_REMOTE_USER_TERM);
|
||||
}
|
||||
|
||||
static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
u16 len)
|
||||
{
|
||||
|
|
@ -2638,7 +2697,7 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp,
|
||||
cmd = mgmt_pending_new(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp,
|
||||
sizeof(*cp));
|
||||
if (!cmd) {
|
||||
err = -ENOMEM;
|
||||
|
|
@ -2647,9 +2706,10 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
cmd->cmd_complete = addr_cmd_complete;
|
||||
|
||||
err = hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
|
||||
err = hci_cmd_sync_queue(hdev, unpair_device_sync, cmd,
|
||||
unpair_device_complete);
|
||||
if (err < 0)
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_pending_free(cmd);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
|
|
@ -3925,10 +3985,16 @@ static const u8 rpa_resolution_uuid[16] = {
|
|||
0xea, 0x11, 0x73, 0xc2, 0x48, 0xa1, 0xc0, 0x15,
|
||||
};
|
||||
|
||||
/* 6fbaf188-05e0-496a-9885-d6ddfdb4e03e */
|
||||
static const u8 iso_socket_uuid[16] = {
|
||||
0x3e, 0xe0, 0xb4, 0xfd, 0xdd, 0xd6, 0x85, 0x98,
|
||||
0x6a, 0x49, 0xe0, 0x05, 0x88, 0xf1, 0xba, 0x6f,
|
||||
};
|
||||
|
||||
static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 data_len)
|
||||
{
|
||||
char buf[102]; /* Enough space for 5 features: 2 + 20 * 5 */
|
||||
char buf[122]; /* Enough space for 6 features: 2 + 20 * 6 */
|
||||
struct mgmt_rp_read_exp_features_info *rp = (void *)buf;
|
||||
u16 idx = 0;
|
||||
u32 flags;
|
||||
|
|
@ -3992,6 +4058,13 @@ static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
|
|||
idx++;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_LE)) {
|
||||
flags = iso_enabled() ? BIT(0) : 0;
|
||||
memcpy(rp->features[idx].uuid, iso_socket_uuid, 16);
|
||||
rp->features[idx].flags = cpu_to_le32(flags);
|
||||
idx++;
|
||||
}
|
||||
|
||||
rp->feature_count = cpu_to_le16(idx);
|
||||
|
||||
/* After reading the experimental features information, enable
|
||||
|
|
@ -4384,6 +4457,57 @@ static int set_le_simultaneous_roles_func(struct sock *sk, struct hci_dev *hdev,
|
|||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BT_LE
|
||||
static int set_iso_socket_func(struct sock *sk, struct hci_dev *hdev,
|
||||
struct mgmt_cp_set_exp_feature *cp, u16 data_len)
|
||||
{
|
||||
struct mgmt_rp_set_exp_feature rp;
|
||||
bool val, changed = false;
|
||||
int err;
|
||||
|
||||
/* Command requires to use the non-controller index */
|
||||
if (hdev)
|
||||
return mgmt_cmd_status(sk, hdev->id,
|
||||
MGMT_OP_SET_EXP_FEATURE,
|
||||
MGMT_STATUS_INVALID_INDEX);
|
||||
|
||||
/* Parameters are limited to a single octet */
|
||||
if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
|
||||
return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
|
||||
MGMT_OP_SET_EXP_FEATURE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
/* Only boolean on/off is supported */
|
||||
if (cp->param[0] != 0x00 && cp->param[0] != 0x01)
|
||||
return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
|
||||
MGMT_OP_SET_EXP_FEATURE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
val = cp->param[0] ? true : false;
|
||||
if (val)
|
||||
err = iso_init();
|
||||
else
|
||||
err = iso_exit();
|
||||
|
||||
if (!err)
|
||||
changed = true;
|
||||
|
||||
memcpy(rp.uuid, iso_socket_uuid, 16);
|
||||
rp.flags = cpu_to_le32(val ? BIT(0) : 0);
|
||||
|
||||
hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
|
||||
|
||||
err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE,
|
||||
MGMT_OP_SET_EXP_FEATURE, 0,
|
||||
&rp, sizeof(rp));
|
||||
|
||||
if (changed)
|
||||
exp_feature_changed(hdev, iso_socket_uuid, val, sk);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct mgmt_exp_feature {
|
||||
const u8 *uuid;
|
||||
int (*set_func)(struct sock *sk, struct hci_dev *hdev,
|
||||
|
|
@ -4397,6 +4521,9 @@ static const struct mgmt_exp_feature {
|
|||
EXP_FEAT(quality_report_uuid, set_quality_report_func),
|
||||
EXP_FEAT(offload_codecs_uuid, set_offload_codec_func),
|
||||
EXP_FEAT(le_simultaneous_roles_uuid, set_le_simultaneous_roles_func),
|
||||
#ifdef CONFIG_BT_LE
|
||||
EXP_FEAT(iso_socket_uuid, set_iso_socket_func),
|
||||
#endif
|
||||
|
||||
/* end with a null feature */
|
||||
EXP_FEAT(NULL, NULL)
|
||||
|
|
@ -4646,23 +4773,15 @@ static int read_adv_mon_features(struct sock *sk, struct hci_dev *hdev,
|
|||
return err;
|
||||
}
|
||||
|
||||
int mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status)
|
||||
static void mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev,
|
||||
void *data, int status)
|
||||
{
|
||||
struct mgmt_rp_add_adv_patterns_monitor rp;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct adv_monitor *monitor;
|
||||
int err = 0;
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct adv_monitor *monitor = cmd->user_data;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
cmd = pending_find(MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI, hdev);
|
||||
if (!cmd) {
|
||||
cmd = pending_find(MGMT_OP_ADD_ADV_PATTERNS_MONITOR, hdev);
|
||||
if (!cmd)
|
||||
goto done;
|
||||
}
|
||||
|
||||
monitor = cmd->user_data;
|
||||
rp.monitor_handle = cpu_to_le16(monitor->handle);
|
||||
|
||||
if (!status) {
|
||||
|
|
@ -4673,26 +4792,29 @@ int mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status)
|
|||
hci_update_passive_scan(hdev);
|
||||
}
|
||||
|
||||
err = mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode,
|
||||
mgmt_status(status), &rp, sizeof(rp));
|
||||
mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode,
|
||||
mgmt_status(status), &rp, sizeof(rp));
|
||||
mgmt_pending_remove(cmd);
|
||||
|
||||
done:
|
||||
hci_dev_unlock(hdev);
|
||||
bt_dev_dbg(hdev, "add monitor %d complete, status %u",
|
||||
bt_dev_dbg(hdev, "add monitor %d complete, status %d",
|
||||
rp.monitor_handle, status);
|
||||
}
|
||||
|
||||
return err;
|
||||
static int mgmt_add_adv_patterns_monitor_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct adv_monitor *monitor = cmd->user_data;
|
||||
|
||||
return hci_add_adv_monitor(hdev, monitor);
|
||||
}
|
||||
|
||||
static int __add_adv_patterns_monitor(struct sock *sk, struct hci_dev *hdev,
|
||||
struct adv_monitor *m, u8 status,
|
||||
void *data, u16 len, u16 op)
|
||||
{
|
||||
struct mgmt_rp_add_adv_patterns_monitor rp;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
int err;
|
||||
bool pending;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
|
|
@ -4714,12 +4836,11 @@ static int __add_adv_patterns_monitor(struct sock *sk, struct hci_dev *hdev,
|
|||
}
|
||||
|
||||
cmd->user_data = m;
|
||||
pending = hci_add_adv_monitor(hdev, m, &err);
|
||||
err = hci_cmd_sync_queue(hdev, mgmt_add_adv_patterns_monitor_sync, cmd,
|
||||
mgmt_add_adv_patterns_monitor_complete);
|
||||
if (err) {
|
||||
if (err == -ENOSPC || err == -ENOMEM)
|
||||
if (err == -ENOMEM)
|
||||
status = MGMT_STATUS_NO_RESOURCES;
|
||||
else if (err == -EINVAL)
|
||||
status = MGMT_STATUS_INVALID_PARAMS;
|
||||
else
|
||||
status = MGMT_STATUS_FAILED;
|
||||
|
||||
|
|
@ -4727,18 +4848,6 @@ static int __add_adv_patterns_monitor(struct sock *sk, struct hci_dev *hdev,
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
if (!pending) {
|
||||
mgmt_pending_remove(cmd);
|
||||
rp.monitor_handle = cpu_to_le16(m->handle);
|
||||
mgmt_adv_monitor_added(sk, hdev, m->handle);
|
||||
m->state = ADV_MONITOR_STATE_REGISTERED;
|
||||
hdev->adv_monitors_cnt++;
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
return mgmt_cmd_complete(sk, hdev->id, op, MGMT_STATUS_SUCCESS,
|
||||
&rp, sizeof(rp));
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return 0;
|
||||
|
|
@ -4879,49 +4988,46 @@ static int add_adv_patterns_monitor_rssi(struct sock *sk, struct hci_dev *hdev,
|
|||
MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI);
|
||||
}
|
||||
|
||||
int mgmt_remove_adv_monitor_complete(struct hci_dev *hdev, u8 status)
|
||||
static void mgmt_remove_adv_monitor_complete(struct hci_dev *hdev,
|
||||
void *data, int status)
|
||||
{
|
||||
struct mgmt_rp_remove_adv_monitor rp;
|
||||
struct mgmt_cp_remove_adv_monitor *cp;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
int err = 0;
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_cp_remove_adv_monitor *cp = cmd->param;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
cmd = pending_find(MGMT_OP_REMOVE_ADV_MONITOR, hdev);
|
||||
if (!cmd)
|
||||
goto done;
|
||||
|
||||
cp = cmd->param;
|
||||
rp.monitor_handle = cp->monitor_handle;
|
||||
|
||||
if (!status)
|
||||
hci_update_passive_scan(hdev);
|
||||
|
||||
err = mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode,
|
||||
mgmt_status(status), &rp, sizeof(rp));
|
||||
mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode,
|
||||
mgmt_status(status), &rp, sizeof(rp));
|
||||
mgmt_pending_remove(cmd);
|
||||
|
||||
done:
|
||||
hci_dev_unlock(hdev);
|
||||
bt_dev_dbg(hdev, "remove monitor %d complete, status %u",
|
||||
bt_dev_dbg(hdev, "remove monitor %d complete, status %d",
|
||||
rp.monitor_handle, status);
|
||||
}
|
||||
|
||||
return err;
|
||||
static int mgmt_remove_adv_monitor_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_cp_remove_adv_monitor *cp = cmd->param;
|
||||
u16 handle = __le16_to_cpu(cp->monitor_handle);
|
||||
|
||||
if (!handle)
|
||||
return hci_remove_all_adv_monitor(hdev);
|
||||
|
||||
return hci_remove_single_adv_monitor(hdev, handle);
|
||||
}
|
||||
|
||||
static int remove_adv_monitor(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 len)
|
||||
{
|
||||
struct mgmt_cp_remove_adv_monitor *cp = data;
|
||||
struct mgmt_rp_remove_adv_monitor rp;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
u16 handle = __le16_to_cpu(cp->monitor_handle);
|
||||
int err, status;
|
||||
bool pending;
|
||||
|
||||
BT_DBG("request for %s", hdev->name);
|
||||
rp.monitor_handle = cp->monitor_handle;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
|
|
@ -4939,34 +5045,23 @@ static int remove_adv_monitor(struct sock *sk, struct hci_dev *hdev,
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
if (handle)
|
||||
pending = hci_remove_single_adv_monitor(hdev, handle, &err);
|
||||
else
|
||||
pending = hci_remove_all_adv_monitor(hdev, &err);
|
||||
err = hci_cmd_sync_queue(hdev, mgmt_remove_adv_monitor_sync, cmd,
|
||||
mgmt_remove_adv_monitor_complete);
|
||||
|
||||
if (err) {
|
||||
mgmt_pending_remove(cmd);
|
||||
|
||||
if (err == -ENOENT)
|
||||
status = MGMT_STATUS_INVALID_INDEX;
|
||||
if (err == -ENOMEM)
|
||||
status = MGMT_STATUS_NO_RESOURCES;
|
||||
else
|
||||
status = MGMT_STATUS_FAILED;
|
||||
|
||||
mgmt_pending_remove(cmd);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* monitor can be removed without forwarding request to controller */
|
||||
if (!pending) {
|
||||
mgmt_pending_remove(cmd);
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return mgmt_cmd_complete(sk, hdev->id,
|
||||
MGMT_OP_REMOVE_ADV_MONITOR,
|
||||
MGMT_STATUS_SUCCESS,
|
||||
&rp, sizeof(rp));
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return 0;
|
||||
|
||||
unlock:
|
||||
|
|
@ -6711,11 +6806,6 @@ static void get_conn_info_complete(struct hci_dev *hdev, void *data, int err)
|
|||
mgmt_cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
|
||||
&rp, sizeof(rp));
|
||||
|
||||
if (conn) {
|
||||
hci_conn_drop(conn);
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
|
|
@ -6734,15 +6824,10 @@ static int get_conn_info_sync(struct hci_dev *hdev, void *data)
|
|||
else
|
||||
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
|
||||
|
||||
if (!conn || conn != cmd->user_data || conn->state != BT_CONNECTED) {
|
||||
if (cmd->user_data) {
|
||||
hci_conn_drop(cmd->user_data);
|
||||
hci_conn_put(cmd->user_data);
|
||||
cmd->user_data = NULL;
|
||||
}
|
||||
if (!conn || conn->state != BT_CONNECTED)
|
||||
return MGMT_STATUS_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
cmd->user_data = conn;
|
||||
handle = cpu_to_le16(conn->handle);
|
||||
|
||||
/* Refresh RSSI each time */
|
||||
|
|
@ -6821,11 +6906,12 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
cmd = mgmt_pending_new(sk, MGMT_OP_GET_CONN_INFO, hdev, data,
|
||||
len);
|
||||
if (!cmd)
|
||||
if (!cmd) {
|
||||
err = -ENOMEM;
|
||||
else
|
||||
} else {
|
||||
err = hci_cmd_sync_queue(hdev, get_conn_info_sync,
|
||||
cmd, get_conn_info_complete);
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
|
||||
|
|
@ -6837,9 +6923,6 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
hci_conn_hold(conn);
|
||||
cmd->user_data = hci_conn_get(conn);
|
||||
|
||||
conn->conn_info_timestamp = jiffies;
|
||||
} else {
|
||||
/* Cache is valid, just reply with values cached in hci_conn */
|
||||
|
|
@ -6878,8 +6961,6 @@ static void get_clock_info_complete(struct hci_dev *hdev, void *data, int err)
|
|||
if (conn) {
|
||||
rp.piconet_clock = cpu_to_le32(conn->clock);
|
||||
rp.accuracy = cpu_to_le16(conn->clock_accuracy);
|
||||
hci_conn_drop(conn);
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
complete:
|
||||
|
|
@ -6894,30 +6975,21 @@ static int get_clock_info_sync(struct hci_dev *hdev, void *data)
|
|||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_cp_get_clock_info *cp = cmd->param;
|
||||
struct hci_cp_read_clock hci_cp;
|
||||
struct hci_conn *conn = cmd->user_data;
|
||||
int err;
|
||||
struct hci_conn *conn;
|
||||
|
||||
memset(&hci_cp, 0, sizeof(hci_cp));
|
||||
err = hci_read_clock_sync(hdev, &hci_cp);
|
||||
hci_read_clock_sync(hdev, &hci_cp);
|
||||
|
||||
if (conn) {
|
||||
/* Make sure connection still exists */
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
|
||||
&cp->addr.bdaddr);
|
||||
/* Make sure connection still exists */
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
|
||||
if (!conn || conn->state != BT_CONNECTED)
|
||||
return MGMT_STATUS_NOT_CONNECTED;
|
||||
|
||||
if (conn && conn == cmd->user_data &&
|
||||
conn->state == BT_CONNECTED) {
|
||||
hci_cp.handle = cpu_to_le16(conn->handle);
|
||||
hci_cp.which = 0x01; /* Piconet clock */
|
||||
err = hci_read_clock_sync(hdev, &hci_cp);
|
||||
} else if (cmd->user_data) {
|
||||
hci_conn_drop(cmd->user_data);
|
||||
hci_conn_put(cmd->user_data);
|
||||
cmd->user_data = NULL;
|
||||
}
|
||||
}
|
||||
cmd->user_data = conn;
|
||||
hci_cp.handle = cpu_to_le16(conn->handle);
|
||||
hci_cp.which = 0x01; /* Piconet clock */
|
||||
|
||||
return err;
|
||||
return hci_read_clock_sync(hdev, &hci_cp);
|
||||
}
|
||||
|
||||
static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
|
|
@ -6976,10 +7048,6 @@ static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||
|
||||
if (cmd)
|
||||
mgmt_pending_free(cmd);
|
||||
|
||||
} else if (conn) {
|
||||
hci_conn_hold(conn);
|
||||
cmd->user_data = hci_conn_get(conn);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -7108,7 +7176,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
|
|||
if (err)
|
||||
goto unlock;
|
||||
|
||||
hci_req_update_scan(hdev);
|
||||
hci_update_scan(hdev);
|
||||
|
||||
goto added;
|
||||
}
|
||||
|
|
@ -7220,7 +7288,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
hci_req_update_scan(hdev);
|
||||
hci_update_scan(hdev);
|
||||
|
||||
device_removed(sk, hdev, &cp->addr.bdaddr,
|
||||
cp->addr.type);
|
||||
|
|
@ -7284,7 +7352,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
|
|||
kfree(b);
|
||||
}
|
||||
|
||||
hci_req_update_scan(hdev);
|
||||
hci_update_scan(hdev);
|
||||
|
||||
list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) {
|
||||
if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
|
||||
|
|
@ -8090,7 +8158,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
|||
u16 timeout, duration;
|
||||
unsigned int prev_instance_cnt;
|
||||
u8 schedule_instance = 0;
|
||||
struct adv_info *next_instance;
|
||||
struct adv_info *adv, *next_instance;
|
||||
int err;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
|
||||
|
|
@ -8141,7 +8209,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
|||
|
||||
prev_instance_cnt = hdev->adv_instance_cnt;
|
||||
|
||||
err = hci_add_adv_instance(hdev, cp->instance, flags,
|
||||
adv = hci_add_adv_instance(hdev, cp->instance, flags,
|
||||
cp->adv_data_len, cp->data,
|
||||
cp->scan_rsp_len,
|
||||
cp->data + cp->adv_data_len,
|
||||
|
|
@ -8149,7 +8217,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
|||
HCI_ADV_TX_POWER_NO_PREFERENCE,
|
||||
hdev->le_adv_min_interval,
|
||||
hdev->le_adv_max_interval);
|
||||
if (err < 0) {
|
||||
if (IS_ERR(adv)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
MGMT_STATUS_FAILED);
|
||||
goto unlock;
|
||||
|
|
@ -8280,6 +8348,7 @@ static int add_ext_adv_params(struct sock *sk, struct hci_dev *hdev,
|
|||
struct mgmt_cp_add_ext_adv_params *cp = data;
|
||||
struct mgmt_rp_add_ext_adv_params rp;
|
||||
struct mgmt_pending_cmd *cmd = NULL;
|
||||
struct adv_info *adv;
|
||||
u32 flags, min_interval, max_interval;
|
||||
u16 timeout, duration;
|
||||
u8 status;
|
||||
|
|
@ -8349,11 +8418,11 @@ static int add_ext_adv_params(struct sock *sk, struct hci_dev *hdev,
|
|||
HCI_ADV_TX_POWER_NO_PREFERENCE;
|
||||
|
||||
/* Create advertising instance with no advertising or response data */
|
||||
err = hci_add_adv_instance(hdev, cp->instance, flags,
|
||||
0, NULL, 0, NULL, timeout, duration,
|
||||
tx_power, min_interval, max_interval);
|
||||
adv = hci_add_adv_instance(hdev, cp->instance, flags, 0, NULL, 0, NULL,
|
||||
timeout, duration, tx_power, min_interval,
|
||||
max_interval);
|
||||
|
||||
if (err < 0) {
|
||||
if (IS_ERR(adv)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_PARAMS,
|
||||
MGMT_STATUS_FAILED);
|
||||
goto unlock;
|
||||
|
|
@ -8866,6 +8935,11 @@ void mgmt_index_removed(struct hci_dev *hdev)
|
|||
|
||||
mgmt_index_event(MGMT_EV_EXT_INDEX_REMOVED, hdev, &ev, sizeof(ev),
|
||||
HCI_MGMT_EXT_INDEX_EVENTS);
|
||||
|
||||
/* Cancel any remaining timed work */
|
||||
cancel_delayed_work_sync(&hdev->discov_off);
|
||||
cancel_delayed_work_sync(&hdev->service_cache);
|
||||
cancel_delayed_work_sync(&hdev->rpa_expired);
|
||||
}
|
||||
|
||||
void mgmt_power_on(struct hci_dev *hdev, int err)
|
||||
|
|
|
|||
|
|
@ -99,18 +99,11 @@ struct msft_data {
|
|||
__u8 evt_prefix_len;
|
||||
__u8 *evt_prefix;
|
||||
struct list_head handle_map;
|
||||
__u16 pending_add_handle;
|
||||
__u16 pending_remove_handle;
|
||||
__u8 resuming;
|
||||
__u8 suspending;
|
||||
__u8 filter_enabled;
|
||||
};
|
||||
|
||||
static int __msft_add_monitor_pattern(struct hci_dev *hdev,
|
||||
struct adv_monitor *monitor);
|
||||
static int __msft_remove_monitor(struct hci_dev *hdev,
|
||||
struct adv_monitor *monitor, u16 handle);
|
||||
|
||||
bool msft_monitor_supported(struct hci_dev *hdev)
|
||||
{
|
||||
return !!(msft_get_features(hdev) & MSFT_FEATURE_MASK_LE_ADV_MONITOR);
|
||||
|
|
@ -164,34 +157,6 @@ static bool read_supported_features(struct hci_dev *hdev,
|
|||
return false;
|
||||
}
|
||||
|
||||
static void reregister_monitor(struct hci_dev *hdev, int handle)
|
||||
{
|
||||
struct adv_monitor *monitor;
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
int err;
|
||||
|
||||
while (1) {
|
||||
monitor = idr_get_next(&hdev->adv_monitors_idr, &handle);
|
||||
if (!monitor) {
|
||||
/* All monitors have been resumed */
|
||||
msft->resuming = false;
|
||||
hci_update_passive_scan(hdev);
|
||||
return;
|
||||
}
|
||||
|
||||
msft->pending_add_handle = (u16)handle;
|
||||
err = __msft_add_monitor_pattern(hdev, monitor);
|
||||
|
||||
/* If success, we return and wait for monitor added callback */
|
||||
if (!err)
|
||||
return;
|
||||
|
||||
/* Otherwise remove the monitor and keep registering */
|
||||
hci_free_adv_monitor(hdev, monitor);
|
||||
handle++;
|
||||
}
|
||||
}
|
||||
|
||||
/* is_mgmt = true matches the handle exposed to userspace via mgmt.
|
||||
* is_mgmt = false matches the handle used by the msft controller.
|
||||
* This function requires the caller holds hdev->lock
|
||||
|
|
@ -243,34 +208,27 @@ static int msft_monitor_device_del(struct hci_dev *hdev, __u16 mgmt_handle,
|
|||
return count;
|
||||
}
|
||||
|
||||
static void msft_le_monitor_advertisement_cb(struct hci_dev *hdev,
|
||||
u8 status, u16 opcode,
|
||||
struct sk_buff *skb)
|
||||
static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode,
|
||||
struct adv_monitor *monitor,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct msft_rp_le_monitor_advertisement *rp;
|
||||
struct adv_monitor *monitor;
|
||||
struct msft_monitor_advertisement_handle_data *handle_data;
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
int status = 0;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
monitor = idr_find(&hdev->adv_monitors_idr, msft->pending_add_handle);
|
||||
if (!monitor) {
|
||||
bt_dev_err(hdev, "msft add advmon: monitor %u is not found!",
|
||||
msft->pending_add_handle);
|
||||
status = HCI_ERROR_UNSPECIFIED;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (status)
|
||||
goto unlock;
|
||||
|
||||
rp = (struct msft_rp_le_monitor_advertisement *)skb->data;
|
||||
if (skb->len < sizeof(*rp)) {
|
||||
status = HCI_ERROR_UNSPECIFIED;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
status = rp->status;
|
||||
if (status)
|
||||
goto unlock;
|
||||
|
||||
handle_data = kmalloc(sizeof(*handle_data), GFP_KERNEL);
|
||||
if (!handle_data) {
|
||||
status = HCI_ERROR_UNSPECIFIED;
|
||||
|
|
@ -285,29 +243,23 @@ static void msft_le_monitor_advertisement_cb(struct hci_dev *hdev,
|
|||
monitor->state = ADV_MONITOR_STATE_OFFLOADED;
|
||||
|
||||
unlock:
|
||||
if (status && monitor)
|
||||
if (status)
|
||||
hci_free_adv_monitor(hdev, monitor);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
if (!msft->resuming)
|
||||
hci_add_adv_patterns_monitor_complete(hdev, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
|
||||
u8 status, u16 opcode,
|
||||
struct sk_buff *skb)
|
||||
static int msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
|
||||
u16 opcode,
|
||||
struct adv_monitor *monitor,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct msft_cp_le_cancel_monitor_advertisement *cp;
|
||||
struct msft_rp_le_cancel_monitor_advertisement *rp;
|
||||
struct adv_monitor *monitor;
|
||||
struct msft_monitor_advertisement_handle_data *handle_data;
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
int err;
|
||||
bool pending;
|
||||
|
||||
if (status)
|
||||
goto done;
|
||||
int status = 0;
|
||||
|
||||
rp = (struct msft_rp_le_cancel_monitor_advertisement *)skb->data;
|
||||
if (skb->len < sizeof(*rp)) {
|
||||
|
|
@ -315,22 +267,22 @@ static void msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
|
|||
goto done;
|
||||
}
|
||||
|
||||
status = rp->status;
|
||||
if (status)
|
||||
goto done;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, hdev->msft_opcode);
|
||||
handle_data = msft_find_handle_data(hdev, cp->handle, false);
|
||||
handle_data = msft_find_handle_data(hdev, monitor->handle, true);
|
||||
|
||||
if (handle_data) {
|
||||
monitor = idr_find(&hdev->adv_monitors_idr,
|
||||
handle_data->mgmt_handle);
|
||||
|
||||
if (monitor && monitor->state == ADV_MONITOR_STATE_OFFLOADED)
|
||||
if (monitor->state == ADV_MONITOR_STATE_OFFLOADED)
|
||||
monitor->state = ADV_MONITOR_STATE_REGISTERED;
|
||||
|
||||
/* Do not free the monitor if it is being removed due to
|
||||
* suspend. It will be re-monitored on resume.
|
||||
*/
|
||||
if (monitor && !msft->suspending) {
|
||||
if (!msft->suspending) {
|
||||
hci_free_adv_monitor(hdev, monitor);
|
||||
|
||||
/* Clear any monitored devices by this Adv Monitor */
|
||||
|
|
@ -342,35 +294,19 @@ static void msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
|
|||
kfree(handle_data);
|
||||
}
|
||||
|
||||
/* If remove all monitors is required, we need to continue the process
|
||||
* here because the earlier it was paused when waiting for the
|
||||
* response from controller.
|
||||
*/
|
||||
if (msft->pending_remove_handle == 0) {
|
||||
pending = hci_remove_all_adv_monitor(hdev, &err);
|
||||
if (pending) {
|
||||
hci_dev_unlock(hdev);
|
||||
return;
|
||||
}
|
||||
|
||||
if (err)
|
||||
status = HCI_ERROR_UNSPECIFIED;
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
done:
|
||||
if (!msft->suspending)
|
||||
hci_remove_adv_monitor_complete(hdev, status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hci_req_sync_lock */
|
||||
static int msft_remove_monitor_sync(struct hci_dev *hdev,
|
||||
struct adv_monitor *monitor)
|
||||
{
|
||||
struct msft_cp_le_cancel_monitor_advertisement cp;
|
||||
struct msft_monitor_advertisement_handle_data *handle_data;
|
||||
struct sk_buff *skb;
|
||||
u8 status;
|
||||
|
||||
handle_data = msft_find_handle_data(hdev, monitor->handle, true);
|
||||
|
||||
|
|
@ -386,13 +322,8 @@ static int msft_remove_monitor_sync(struct hci_dev *hdev,
|
|||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
status = skb->data[0];
|
||||
skb_pull(skb, 1);
|
||||
|
||||
msft_le_cancel_monitor_advertisement_cb(hdev, status, hdev->msft_opcode,
|
||||
skb);
|
||||
|
||||
return status;
|
||||
return msft_le_cancel_monitor_advertisement_cb(hdev, hdev->msft_opcode,
|
||||
monitor, skb);
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hci_req_sync_lock */
|
||||
|
|
@ -463,7 +394,6 @@ static int msft_add_monitor_sync(struct hci_dev *hdev,
|
|||
ptrdiff_t offset = 0;
|
||||
u8 pattern_count = 0;
|
||||
struct sk_buff *skb;
|
||||
u8 status;
|
||||
|
||||
if (!msft_monitor_pattern_valid(monitor))
|
||||
return -EINVAL;
|
||||
|
|
@ -505,20 +435,40 @@ static int msft_add_monitor_sync(struct hci_dev *hdev,
|
|||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
status = skb->data[0];
|
||||
skb_pull(skb, 1);
|
||||
return msft_le_monitor_advertisement_cb(hdev, hdev->msft_opcode,
|
||||
monitor, skb);
|
||||
}
|
||||
|
||||
msft_le_monitor_advertisement_cb(hdev, status, hdev->msft_opcode, skb);
|
||||
/* This function requires the caller holds hci_req_sync_lock */
|
||||
static void reregister_monitor(struct hci_dev *hdev)
|
||||
{
|
||||
struct adv_monitor *monitor;
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
int handle = 0;
|
||||
|
||||
return status;
|
||||
if (!msft)
|
||||
return;
|
||||
|
||||
msft->resuming = true;
|
||||
|
||||
while (1) {
|
||||
monitor = idr_get_next(&hdev->adv_monitors_idr, &handle);
|
||||
if (!monitor)
|
||||
break;
|
||||
|
||||
msft_add_monitor_sync(hdev, monitor);
|
||||
|
||||
handle++;
|
||||
}
|
||||
|
||||
/* All monitors have been reregistered */
|
||||
msft->resuming = false;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hci_req_sync_lock */
|
||||
int msft_resume_sync(struct hci_dev *hdev)
|
||||
{
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
struct adv_monitor *monitor;
|
||||
int handle = 0;
|
||||
|
||||
if (!msft || !msft_monitor_supported(hdev))
|
||||
return 0;
|
||||
|
|
@ -533,24 +483,12 @@ int msft_resume_sync(struct hci_dev *hdev)
|
|||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
msft->resuming = true;
|
||||
|
||||
while (1) {
|
||||
monitor = idr_get_next(&hdev->adv_monitors_idr, &handle);
|
||||
if (!monitor)
|
||||
break;
|
||||
|
||||
msft_add_monitor_sync(hdev, monitor);
|
||||
|
||||
handle++;
|
||||
}
|
||||
|
||||
/* All monitors have been resumed */
|
||||
msft->resuming = false;
|
||||
reregister_monitor(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hci_req_sync_lock */
|
||||
void msft_do_open(struct hci_dev *hdev)
|
||||
{
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
|
|
@ -583,7 +521,7 @@ void msft_do_open(struct hci_dev *hdev)
|
|||
/* Monitors get removed on power off, so we need to explicitly
|
||||
* tell the controller to re-monitor.
|
||||
*/
|
||||
reregister_monitor(hdev, 0);
|
||||
reregister_monitor(hdev);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -829,66 +767,7 @@ static void msft_le_set_advertisement_filter_enable_cb(struct hci_dev *hdev,
|
|||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static int __msft_add_monitor_pattern(struct hci_dev *hdev,
|
||||
struct adv_monitor *monitor)
|
||||
{
|
||||
struct msft_cp_le_monitor_advertisement *cp;
|
||||
struct msft_le_monitor_advertisement_pattern_data *pattern_data;
|
||||
struct msft_le_monitor_advertisement_pattern *pattern;
|
||||
struct adv_pattern *entry;
|
||||
struct hci_request req;
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
size_t total_size = sizeof(*cp) + sizeof(*pattern_data);
|
||||
ptrdiff_t offset = 0;
|
||||
u8 pattern_count = 0;
|
||||
int err = 0;
|
||||
|
||||
if (!msft_monitor_pattern_valid(monitor))
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry(entry, &monitor->patterns, list) {
|
||||
pattern_count++;
|
||||
total_size += sizeof(*pattern) + entry->length;
|
||||
}
|
||||
|
||||
cp = kmalloc(total_size, GFP_KERNEL);
|
||||
if (!cp)
|
||||
return -ENOMEM;
|
||||
|
||||
cp->sub_opcode = MSFT_OP_LE_MONITOR_ADVERTISEMENT;
|
||||
cp->rssi_high = monitor->rssi.high_threshold;
|
||||
cp->rssi_low = monitor->rssi.low_threshold;
|
||||
cp->rssi_low_interval = (u8)monitor->rssi.low_threshold_timeout;
|
||||
cp->rssi_sampling_period = monitor->rssi.sampling_period;
|
||||
|
||||
cp->cond_type = MSFT_MONITOR_ADVERTISEMENT_TYPE_PATTERN;
|
||||
|
||||
pattern_data = (void *)cp->data;
|
||||
pattern_data->count = pattern_count;
|
||||
|
||||
list_for_each_entry(entry, &monitor->patterns, list) {
|
||||
pattern = (void *)(pattern_data->data + offset);
|
||||
/* the length also includes data_type and offset */
|
||||
pattern->length = entry->length + 2;
|
||||
pattern->data_type = entry->ad_type;
|
||||
pattern->start_byte = entry->offset;
|
||||
memcpy(pattern->pattern, entry->value, entry->length);
|
||||
offset += sizeof(*pattern) + entry->length;
|
||||
}
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, hdev->msft_opcode, total_size, cp);
|
||||
err = hci_req_run_skb(&req, msft_le_monitor_advertisement_cb);
|
||||
kfree(cp);
|
||||
|
||||
if (!err)
|
||||
msft->pending_add_handle = monitor->handle;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
/* This function requires the caller holds hci_req_sync_lock */
|
||||
int msft_add_monitor_pattern(struct hci_dev *hdev, struct adv_monitor *monitor)
|
||||
{
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
|
|
@ -899,41 +778,11 @@ int msft_add_monitor_pattern(struct hci_dev *hdev, struct adv_monitor *monitor)
|
|||
if (msft->resuming || msft->suspending)
|
||||
return -EBUSY;
|
||||
|
||||
return __msft_add_monitor_pattern(hdev, monitor);
|
||||
return msft_add_monitor_sync(hdev, monitor);
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static int __msft_remove_monitor(struct hci_dev *hdev,
|
||||
struct adv_monitor *monitor, u16 handle)
|
||||
{
|
||||
struct msft_cp_le_cancel_monitor_advertisement cp;
|
||||
struct msft_monitor_advertisement_handle_data *handle_data;
|
||||
struct hci_request req;
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
int err = 0;
|
||||
|
||||
handle_data = msft_find_handle_data(hdev, monitor->handle, true);
|
||||
|
||||
/* If no matched handle, just remove without telling controller */
|
||||
if (!handle_data)
|
||||
return -ENOENT;
|
||||
|
||||
cp.sub_opcode = MSFT_OP_LE_CANCEL_MONITOR_ADVERTISEMENT;
|
||||
cp.handle = handle_data->msft_handle;
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, hdev->msft_opcode, sizeof(cp), &cp);
|
||||
err = hci_req_run_skb(&req, msft_le_cancel_monitor_advertisement_cb);
|
||||
|
||||
if (!err)
|
||||
msft->pending_remove_handle = handle;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
|
||||
u16 handle)
|
||||
/* This function requires the caller holds hci_req_sync_lock */
|
||||
int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor)
|
||||
{
|
||||
struct msft_data *msft = hdev->msft_data;
|
||||
|
||||
|
|
@ -943,7 +792,7 @@ int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
|
|||
if (msft->resuming || msft->suspending)
|
||||
return -EBUSY;
|
||||
|
||||
return __msft_remove_monitor(hdev, monitor, handle);
|
||||
return msft_remove_monitor_sync(hdev, monitor);
|
||||
}
|
||||
|
||||
void msft_req_add_set_filter_enable(struct hci_request *req, bool enable)
|
||||
|
|
|
|||
|
|
@ -20,8 +20,7 @@ void msft_do_close(struct hci_dev *hdev);
|
|||
void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb);
|
||||
__u64 msft_get_features(struct hci_dev *hdev);
|
||||
int msft_add_monitor_pattern(struct hci_dev *hdev, struct adv_monitor *monitor);
|
||||
int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
|
||||
u16 handle);
|
||||
int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor);
|
||||
void msft_req_add_set_filter_enable(struct hci_request *req, bool enable);
|
||||
int msft_set_filter_enable(struct hci_dev *hdev, bool enable);
|
||||
int msft_suspend_sync(struct hci_dev *hdev);
|
||||
|
|
@ -49,8 +48,7 @@ static inline int msft_add_monitor_pattern(struct hci_dev *hdev,
|
|||
}
|
||||
|
||||
static inline int msft_remove_monitor(struct hci_dev *hdev,
|
||||
struct adv_monitor *monitor,
|
||||
u16 handle)
|
||||
struct adv_monitor *monitor)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user