forked from Minki/linux
bluetooth-next pull request for net-next:
- Add RTL8761BUV device (Edimax BT-8500) - Add a new PID/VID 13d3/3583 for MT7921 - Add Realtek RTL8852C support ID 0x13D3:0x3592 - Add VID/PID 0489/e0e0 for MediaTek MT7921 - Add a new VID/PID 0e8d/0608 for MT7921 - Add a new PID/VID 13d3/3578 for MT7921 - Add BT device 0cb8:c549 from RTW8852AE - Add support for Intel Magnetor -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE7E6oRXp8w05ovYr/9JCA4xAyCykFAmM3jT0ZHGx1aXoudm9u LmRlbnR6QGludGVsLmNvbQAKCRD0kIDjEDILKYMhEACfjFS9KCeXyyAVSNk2RwF4 c+rhJW78VmAGd3yWCa+L/6g+xmxgF+sQYNJP35FMW6SAgL9K9yu3+hH0X4O2GNXT YA9YltHOL60KVKAiHzMqTPwNUoafqbzVz3MLE/yragd9G8dZeMt2W4YObO6/UOoJ 2aWfUNZUNCTyUTHdM2ImXpgkrYe3/ytm/xNKBsJDnYG1MptQmmKAiOr1TUxPN0Sj pq1Qg2mH9Qj1bo0250v8JVD3ya+NPmtMtCZ7a2vJcf/7BhzMqbxM6bGul3N34cpK kDL7ma0rj0MqNKVdhY0hPUpLwE4ImNjpIjbZfUJ3KlLr0OqFcl9p7pXQXjeSlR9/ Jsfoyq+kcB693FX8U/6GjTivRmkVfOI3UQeej9tspSS20sLGGHzYczdQosm9vq5h VZVJ3HowRXCs0tpEvssuwdITXMZTFjtEl8phAVkcap1GwUM+Qe1wz1jAzXGcuwoh N8VHJIBR3DCfWh3c3S5y/J/PMuhxhayMjdaCQD7M10Fg00FvlY7iFvlocLCkHBga sWmaBPDt8Ytp8Ar7lXdotw7ICFBZYX3AZe241PFngZBXFR3R50lqBtbJKV0YglSk HViWhjwrvmOt3UclV/G8VFqxyRcBI5XgqDxyI6+1lg0PO5nJlfZJhkJJ8wyISEp1 238k6Y9EosV9ERlj9Zss/w== =BmY6 -----END PGP SIGNATURE----- Merge tag 'for-net-next-2022-09-30' 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 RTL8761BUV device (Edimax BT-8500) - Add a new PID/VID 13d3/3583 for MT7921 - Add Realtek RTL8852C support ID 0x13D3:0x3592 - Add VID/PID 0489/e0e0 for MediaTek MT7921 - Add a new VID/PID 0e8d/0608 for MT7921 - Add a new PID/VID 13d3/3578 for MT7921 - Add BT device 0cb8:c549 from RTW8852AE - Add support for Intel Magnetor * tag 'for-net-next-2022-09-30' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next: (49 commits) Bluetooth: hci_sync: Fix not indicating power state Bluetooth: L2CAP: Fix user-after-free Bluetooth: Call shutdown for HCI_USER_CHANNEL Bluetooth: Prevent double register of suspend Bluetooth: hci_core: Fix not handling link timeouts propertly Bluetooth: hci_event: Make sure ISO events don't affect non-ISO connections Bluetooth: hci_debugfs: Fix not checking conn->debugfs Bluetooth: hci_sysfs: Fix attempting to call device_add multiple times Bluetooth: MGMT: fix zalloc-simple.cocci warnings Bluetooth: hci_{ldisc,serdev}: check percpu_init_rwsem() failure Bluetooth: use hdev->workqueue when queuing hdev->{cmd,ncmd}_timer works Bluetooth: L2CAP: initialize delayed works at l2cap_chan_create() Bluetooth: RFCOMM: Fix possible deadlock on socket shutdown/release Bluetooth: hci_sync: allow advertise when scan without RPA Bluetooth: btusb: Add a new VID/PID 0e8d/0608 for MT7921 Bluetooth: btusb: Add a new PID/VID 13d3/3583 for MT7921 Bluetooth: avoid hci_dev_test_and_set_flag() in mgmt_init_hdev() Bluetooth: btintel: Mark Intel controller to support LE_STATES quirk Bluetooth: btintel: Add support for Magnetor Bluetooth: btusb: Add a new PID/VID 13d3/3578 for MT7921 ... ==================== Link: https://lore.kernel.org/r/20221001004602.297366-1-luiz.dentz@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
a507ea32b9
@ -449,6 +449,7 @@ static int btintel_version_info_tlv(struct hci_dev *hdev,
|
||||
case 0x17: /* TyP */
|
||||
case 0x18: /* Slr */
|
||||
case 0x19: /* Slr-F */
|
||||
case 0x1b: /* Mgr */
|
||||
break;
|
||||
default:
|
||||
bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%x)",
|
||||
@ -2330,6 +2331,7 @@ static void btintel_set_msft_opcode(struct hci_dev *hdev, u8 hw_variant)
|
||||
case 0x17:
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
case 0x1b:
|
||||
hci_set_msft_opcode(hdev, 0xFC1E);
|
||||
break;
|
||||
default:
|
||||
@ -2439,15 +2441,20 @@ static int btintel_setup_combined(struct hci_dev *hdev)
|
||||
INTEL_ROM_LEGACY_NO_WBS_SUPPORT))
|
||||
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED,
|
||||
&hdev->quirks);
|
||||
if (ver.hw_variant == 0x08 && ver.fw_variant == 0x22)
|
||||
set_bit(HCI_QUIRK_VALID_LE_STATES,
|
||||
&hdev->quirks);
|
||||
|
||||
err = btintel_legacy_rom_setup(hdev, &ver);
|
||||
break;
|
||||
case 0x0b: /* SfP */
|
||||
case 0x0c: /* WsP */
|
||||
case 0x11: /* JfP */
|
||||
case 0x12: /* ThP */
|
||||
case 0x13: /* HrP */
|
||||
case 0x14: /* CcP */
|
||||
set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
|
||||
fallthrough;
|
||||
case 0x0c: /* WsP */
|
||||
/* Apply the device specific HCI quirks
|
||||
*
|
||||
* All Legacy bootloader devices support WBS
|
||||
@ -2455,11 +2462,6 @@ static int btintel_setup_combined(struct hci_dev *hdev)
|
||||
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED,
|
||||
&hdev->quirks);
|
||||
|
||||
/* Valid LE States quirk for JfP/ThP familiy */
|
||||
if (ver.hw_variant == 0x11 || ver.hw_variant == 0x12)
|
||||
set_bit(HCI_QUIRK_VALID_LE_STATES,
|
||||
&hdev->quirks);
|
||||
|
||||
/* Setup MSFT Extension support */
|
||||
btintel_set_msft_opcode(hdev, ver.hw_variant);
|
||||
|
||||
@ -2530,9 +2532,8 @@ static int btintel_setup_combined(struct hci_dev *hdev)
|
||||
*/
|
||||
set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
|
||||
|
||||
/* Valid LE States quirk for JfP/ThP familiy */
|
||||
if (ver.hw_variant == 0x11 || ver.hw_variant == 0x12)
|
||||
set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
|
||||
/* Set Valid LE States quirk */
|
||||
set_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks);
|
||||
|
||||
/* Setup MSFT Extension support */
|
||||
btintel_set_msft_opcode(hdev, ver.hw_variant);
|
||||
@ -2542,6 +2543,7 @@ static int btintel_setup_combined(struct hci_dev *hdev)
|
||||
case 0x17:
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
case 0x1b:
|
||||
/* Display version information of TLV type */
|
||||
btintel_version_info_tlv(hdev, &ver_tlv);
|
||||
|
||||
|
@ -426,6 +426,8 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x04ca, 0x4006), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x0cb8, 0xc549), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Realtek 8852CE Bluetooth devices */
|
||||
{ USB_DEVICE(0x04ca, 0x4007), .driver_info = BTUSB_REALTEK |
|
||||
@ -438,6 +440,8 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3586), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x13d3, 0x3592), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Realtek Bluetooth devices */
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
|
||||
@ -466,6 +470,9 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x0489, 0xe0c8), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0489, 0xe0e0), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x04ca, 0x3802), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
@ -478,9 +485,18 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x13d3, 0x3567), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x13d3, 0x3578), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x13d3, 0x3583), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0489, 0xe0cd), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
{ USB_DEVICE(0x0e8d, 0x0608), .driver_info = BTUSB_MEDIATEK |
|
||||
BTUSB_WIDEBAND_SPEECH |
|
||||
BTUSB_VALID_LE_STATES },
|
||||
|
||||
/* MediaTek MT7922A Bluetooth devices */
|
||||
{ USB_DEVICE(0x0489, 0xe0d8), .driver_info = BTUSB_MEDIATEK |
|
||||
@ -516,19 +532,17 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x0bda, 0xb009), .driver_info = BTUSB_REALTEK },
|
||||
{ USB_DEVICE(0x2ff8, 0xb011), .driver_info = BTUSB_REALTEK },
|
||||
|
||||
/* Additional Realtek 8761B Bluetooth devices */
|
||||
/* Additional Realtek 8761BUV Bluetooth devices */
|
||||
{ USB_DEVICE(0x2357, 0x0604), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Additional Realtek 8761BU Bluetooth devices */
|
||||
{ USB_DEVICE(0x0b05, 0x190e), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x2550, 0x8761), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Additional Realtek 8761BUV Bluetooth devices */
|
||||
{ USB_DEVICE(0x0bda, 0x8771), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
{ USB_DEVICE(0x7392, 0xc611), .driver_info = BTUSB_REALTEK |
|
||||
BTUSB_WIDEBAND_SPEECH },
|
||||
|
||||
/* Additional Realtek 8821AE Bluetooth devices */
|
||||
{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
|
||||
@ -2477,15 +2491,29 @@ static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
|
||||
|
||||
set_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
|
||||
|
||||
/* WMT cmd/event doesn't follow up the generic HCI cmd/event handling,
|
||||
* it needs constantly polling control pipe until the host received the
|
||||
* WMT event, thus, we should require to specifically acquire PM counter
|
||||
* on the USB to prevent the interface from entering auto suspended
|
||||
* while WMT cmd/event in progress.
|
||||
*/
|
||||
err = usb_autopm_get_interface(data->intf);
|
||||
if (err < 0)
|
||||
goto err_free_wc;
|
||||
|
||||
err = __hci_cmd_send(hdev, 0xfc6f, hlen, wc);
|
||||
|
||||
if (err < 0) {
|
||||
clear_bit(BTUSB_TX_WAIT_VND_EVT, &data->flags);
|
||||
usb_autopm_put_interface(data->intf);
|
||||
goto err_free_wc;
|
||||
}
|
||||
|
||||
/* Submit control IN URB on demand to process the WMT event */
|
||||
err = btusb_mtk_submit_wmt_recv_urb(hdev);
|
||||
|
||||
usb_autopm_put_interface(data->intf);
|
||||
|
||||
if (err < 0)
|
||||
goto err_free_wc;
|
||||
|
||||
|
@ -493,6 +493,11 @@ static int hci_uart_tty_open(struct tty_struct *tty)
|
||||
BT_ERR("Can't allocate control structure");
|
||||
return -ENFILE;
|
||||
}
|
||||
if (percpu_init_rwsem(&hu->proto_lock)) {
|
||||
BT_ERR("Can't allocate semaphore structure");
|
||||
kfree(hu);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tty->disc_data = hu;
|
||||
hu->tty = tty;
|
||||
@ -505,8 +510,6 @@ static int hci_uart_tty_open(struct tty_struct *tty)
|
||||
INIT_WORK(&hu->init_ready, hci_uart_init_work);
|
||||
INIT_WORK(&hu->write_work, hci_uart_write_work);
|
||||
|
||||
percpu_init_rwsem(&hu->proto_lock);
|
||||
|
||||
/* Flush any pending characters in the driver */
|
||||
tty_driver_flush_buffer(tty);
|
||||
|
||||
|
@ -310,11 +310,12 @@ int hci_uart_register_device(struct hci_uart *hu,
|
||||
|
||||
serdev_device_set_client_ops(hu->serdev, &hci_serdev_client_ops);
|
||||
|
||||
if (percpu_init_rwsem(&hu->proto_lock))
|
||||
return -ENOMEM;
|
||||
|
||||
err = serdev_device_open(hu->serdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
percpu_init_rwsem(&hu->proto_lock);
|
||||
goto err_rwsem;
|
||||
|
||||
err = p->open(hu);
|
||||
if (err)
|
||||
@ -389,6 +390,8 @@ err_alloc:
|
||||
p->close(hu);
|
||||
err_open:
|
||||
serdev_device_close(hu->serdev);
|
||||
err_rwsem:
|
||||
percpu_free_rwsem(&hu->proto_lock);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hci_uart_register_device);
|
||||
@ -410,5 +413,6 @@ void hci_uart_unregister_device(struct hci_uart *hu)
|
||||
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
|
||||
serdev_device_close(hu->serdev);
|
||||
}
|
||||
percpu_free_rwsem(&hu->proto_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hci_uart_unregister_device);
|
||||
|
@ -627,6 +627,7 @@ static inline bool iso_enabled(void)
|
||||
|
||||
int mgmt_init(void);
|
||||
void mgmt_exit(void);
|
||||
void mgmt_cleanup(struct sock *sk);
|
||||
|
||||
void bt_sock_reclassify_lock(struct sock *sk, int proto);
|
||||
|
||||
|
@ -354,6 +354,10 @@ enum {
|
||||
HCI_LE_SIMULTANEOUS_ROLES,
|
||||
HCI_CMD_DRAIN_WORKQUEUE,
|
||||
|
||||
HCI_MESH_EXPERIMENTAL,
|
||||
HCI_MESH,
|
||||
HCI_MESH_SENDING,
|
||||
|
||||
__HCI_NUM_FLAGS,
|
||||
};
|
||||
|
||||
|
@ -238,6 +238,7 @@ struct adv_info {
|
||||
bool enabled;
|
||||
bool pending;
|
||||
bool periodic;
|
||||
__u8 mesh;
|
||||
__u8 instance;
|
||||
__u32 flags;
|
||||
__u16 timeout;
|
||||
@ -372,6 +373,8 @@ struct hci_dev {
|
||||
__u8 le_resolv_list_size;
|
||||
__u8 le_num_of_adv_sets;
|
||||
__u8 le_states[8];
|
||||
__u8 mesh_ad_types[16];
|
||||
__u8 mesh_send_ref;
|
||||
__u8 commands[64];
|
||||
__u8 hci_ver;
|
||||
__u16 hci_rev;
|
||||
@ -511,6 +514,7 @@ struct hci_dev {
|
||||
struct list_head cmd_sync_work_list;
|
||||
struct mutex cmd_sync_work_lock;
|
||||
struct work_struct cmd_sync_cancel_work;
|
||||
struct work_struct reenable_adv_work;
|
||||
|
||||
__u16 discov_timeout;
|
||||
struct delayed_work discov_off;
|
||||
@ -561,6 +565,7 @@ struct hci_dev {
|
||||
|
||||
struct hci_conn_hash conn_hash;
|
||||
|
||||
struct list_head mesh_pending;
|
||||
struct list_head mgmt_pending;
|
||||
struct list_head reject_list;
|
||||
struct list_head accept_list;
|
||||
@ -614,6 +619,8 @@ struct hci_dev {
|
||||
struct delayed_work rpa_expired;
|
||||
bdaddr_t rpa;
|
||||
|
||||
struct delayed_work mesh_send_done;
|
||||
|
||||
enum {
|
||||
INTERLEAVE_SCAN_NONE,
|
||||
INTERLEAVE_SCAN_NO_FILTER,
|
||||
@ -1576,7 +1583,8 @@ 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);
|
||||
u32 min_interval, u32 max_interval,
|
||||
u8 mesh_handle);
|
||||
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);
|
||||
@ -1997,6 +2005,9 @@ void hci_mgmt_chan_unregister(struct hci_mgmt_chan *c);
|
||||
#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 DISCOV_LE_ADV_MESH_MIN 0x00A0 /* 100 msec */
|
||||
#define DISCOV_LE_ADV_MESH_MAX 0x00A0 /* 100 msec */
|
||||
#define INTERVAL_TO_MS(x) (((x) * 10) / 0x10)
|
||||
|
||||
#define NAME_RESOLVE_DURATION msecs_to_jiffies(10240) /* 10.24 sec */
|
||||
|
||||
@ -2048,7 +2059,8 @@ void mgmt_start_discovery_complete(struct hci_dev *hdev, u8 status);
|
||||
void mgmt_stop_discovery_complete(struct hci_dev *hdev, u8 status);
|
||||
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
||||
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
|
||||
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len);
|
||||
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len,
|
||||
u64 instant);
|
||||
void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
||||
u8 addr_type, s8 rssi, u8 *name, u8 name_len);
|
||||
void mgmt_discovering(struct hci_dev *hdev, u8 discovering);
|
||||
@ -2075,6 +2087,7 @@ int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip);
|
||||
void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle,
|
||||
bdaddr_t *bdaddr, u8 addr_type);
|
||||
|
||||
int hci_abort_conn(struct hci_conn *conn, u8 reason);
|
||||
u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
|
||||
u16 to_multiplier);
|
||||
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
|
||||
|
@ -16,6 +16,7 @@ struct hci_cmd_sync_work_entry {
|
||||
hci_cmd_sync_work_destroy_t destroy;
|
||||
};
|
||||
|
||||
struct adv_info;
|
||||
/* Function with sync suffix shall not be called with hdev->lock held as they
|
||||
* wait the command to complete and in the meantime an event could be received
|
||||
* which could attempt to acquire hdev->lock causing a deadlock.
|
||||
@ -51,11 +52,16 @@ int hci_update_class_sync(struct hci_dev *hdev);
|
||||
int hci_update_name_sync(struct hci_dev *hdev);
|
||||
int hci_write_ssp_mode_sync(struct hci_dev *hdev, u8 mode);
|
||||
|
||||
int hci_get_random_address(struct hci_dev *hdev, bool require_privacy,
|
||||
bool use_rpa, struct adv_info *adv_instance,
|
||||
u8 *own_addr_type, bdaddr_t *rand_addr);
|
||||
|
||||
int hci_update_random_address_sync(struct hci_dev *hdev, bool require_privacy,
|
||||
bool rpa, u8 *own_addr_type);
|
||||
|
||||
int hci_update_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance);
|
||||
int hci_update_adv_data_sync(struct hci_dev *hdev, u8 instance);
|
||||
int hci_update_adv_data(struct hci_dev *hdev, u8 instance);
|
||||
int hci_schedule_adv_instance_sync(struct hci_dev *hdev, u8 instance,
|
||||
bool force);
|
||||
|
||||
@ -72,7 +78,8 @@ int hci_start_per_adv_sync(struct hci_dev *hdev, u8 instance, u8 data_len,
|
||||
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);
|
||||
|
||||
int hci_clear_adv_instance_sync(struct hci_dev *hdev, struct sock *sk,
|
||||
u8 instance, bool force);
|
||||
int hci_update_passive_scan_sync(struct hci_dev *hdev);
|
||||
int hci_update_passive_scan(struct hci_dev *hdev);
|
||||
int hci_read_rssi_sync(struct hci_dev *hdev, __le16 handle);
|
||||
|
@ -837,6 +837,42 @@ struct mgmt_cp_add_adv_patterns_monitor_rssi {
|
||||
struct mgmt_adv_pattern patterns[];
|
||||
} __packed;
|
||||
#define MGMT_ADD_ADV_PATTERNS_MONITOR_RSSI_SIZE 8
|
||||
#define MGMT_OP_SET_MESH_RECEIVER 0x0057
|
||||
struct mgmt_cp_set_mesh {
|
||||
__u8 enable;
|
||||
__le16 window;
|
||||
__le16 period;
|
||||
__u8 num_ad_types;
|
||||
__u8 ad_types[];
|
||||
} __packed;
|
||||
#define MGMT_SET_MESH_RECEIVER_SIZE 6
|
||||
|
||||
#define MGMT_OP_MESH_READ_FEATURES 0x0058
|
||||
#define MGMT_MESH_READ_FEATURES_SIZE 0
|
||||
#define MESH_HANDLES_MAX 3
|
||||
struct mgmt_rp_mesh_read_features {
|
||||
__le16 index;
|
||||
__u8 max_handles;
|
||||
__u8 used_handles;
|
||||
__u8 handles[MESH_HANDLES_MAX];
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_MESH_SEND 0x0059
|
||||
struct mgmt_cp_mesh_send {
|
||||
struct mgmt_addr_info addr;
|
||||
__le64 instant;
|
||||
__le16 delay;
|
||||
__u8 cnt;
|
||||
__u8 adv_data_len;
|
||||
__u8 adv_data[];
|
||||
} __packed;
|
||||
#define MGMT_MESH_SEND_SIZE 19
|
||||
|
||||
#define MGMT_OP_MESH_SEND_CANCEL 0x005A
|
||||
struct mgmt_cp_mesh_send_cancel {
|
||||
__u8 handle;
|
||||
} __packed;
|
||||
#define MGMT_MESH_SEND_CANCEL_SIZE 1
|
||||
|
||||
#define MGMT_EV_CMD_COMPLETE 0x0001
|
||||
struct mgmt_ev_cmd_complete {
|
||||
@ -1120,3 +1156,19 @@ struct mgmt_ev_adv_monitor_device_lost {
|
||||
__le16 monitor_handle;
|
||||
struct mgmt_addr_info addr;
|
||||
} __packed;
|
||||
|
||||
#define MGMT_EV_MESH_DEVICE_FOUND 0x0031
|
||||
struct mgmt_ev_mesh_device_found {
|
||||
struct mgmt_addr_info addr;
|
||||
__s8 rssi;
|
||||
__le64 instant;
|
||||
__le32 flags;
|
||||
__le16 eir_len;
|
||||
__u8 eir[];
|
||||
} __packed;
|
||||
|
||||
|
||||
#define MGMT_EV_MESH_PACKET_CMPLT 0x0032
|
||||
struct mgmt_ev_mesh_pkt_cmplt {
|
||||
__u8 handle;
|
||||
} __packed;
|
||||
|
@ -44,6 +44,11 @@ struct sco_param {
|
||||
u8 retrans_effort;
|
||||
};
|
||||
|
||||
struct conn_handle_t {
|
||||
struct hci_conn *conn;
|
||||
__u16 handle;
|
||||
};
|
||||
|
||||
static const struct sco_param esco_param_cvsd[] = {
|
||||
{ EDR_ESCO_MASK & ~ESCO_2EV3, 0x000a, 0x01 }, /* S3 */
|
||||
{ EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007, 0x01 }, /* S2 */
|
||||
@ -316,17 +321,60 @@ static bool find_next_esco_param(struct hci_conn *conn,
|
||||
return conn->attempt <= size;
|
||||
}
|
||||
|
||||
static bool hci_enhanced_setup_sync_conn(struct hci_conn *conn, __u16 handle)
|
||||
static int configure_datapath_sync(struct hci_dev *hdev, struct bt_codec *codec)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
int err;
|
||||
__u8 vnd_len, *vnd_data = NULL;
|
||||
struct hci_op_configure_data_path *cmd = NULL;
|
||||
|
||||
err = hdev->get_codec_config_data(hdev, ESCO_LINK, codec, &vnd_len,
|
||||
&vnd_data);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
cmd = kzalloc(sizeof(*cmd) + vnd_len, GFP_KERNEL);
|
||||
if (!cmd) {
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = hdev->get_data_path_id(hdev, &cmd->data_path_id);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
cmd->vnd_len = vnd_len;
|
||||
memcpy(cmd->vnd_data, vnd_data, vnd_len);
|
||||
|
||||
cmd->direction = 0x00;
|
||||
__hci_cmd_sync_status(hdev, HCI_CONFIGURE_DATA_PATH,
|
||||
sizeof(*cmd) + vnd_len, cmd, HCI_CMD_TIMEOUT);
|
||||
|
||||
cmd->direction = 0x01;
|
||||
err = __hci_cmd_sync_status(hdev, HCI_CONFIGURE_DATA_PATH,
|
||||
sizeof(*cmd) + vnd_len, cmd,
|
||||
HCI_CMD_TIMEOUT);
|
||||
error:
|
||||
|
||||
kfree(cmd);
|
||||
kfree(vnd_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hci_enhanced_setup_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct conn_handle_t *conn_handle = data;
|
||||
struct hci_conn *conn = conn_handle->conn;
|
||||
__u16 handle = conn_handle->handle;
|
||||
struct hci_cp_enhanced_setup_sync_conn cp;
|
||||
const struct sco_param *param;
|
||||
|
||||
kfree(conn_handle);
|
||||
|
||||
bt_dev_dbg(hdev, "hcon %p", conn);
|
||||
|
||||
/* for offload use case, codec needs to configured before opening SCO */
|
||||
if (conn->codec.data_path)
|
||||
hci_req_configure_datapath(hdev, &conn->codec);
|
||||
configure_datapath_sync(hdev, &conn->codec);
|
||||
|
||||
conn->state = BT_CONNECT;
|
||||
conn->out = true;
|
||||
@ -344,7 +392,7 @@ static bool hci_enhanced_setup_sync_conn(struct hci_conn *conn, __u16 handle)
|
||||
case BT_CODEC_MSBC:
|
||||
if (!find_next_esco_param(conn, esco_param_msbc,
|
||||
ARRAY_SIZE(esco_param_msbc)))
|
||||
return false;
|
||||
return -EINVAL;
|
||||
|
||||
param = &esco_param_msbc[conn->attempt - 1];
|
||||
cp.tx_coding_format.id = 0x05;
|
||||
@ -396,11 +444,11 @@ static bool hci_enhanced_setup_sync_conn(struct hci_conn *conn, __u16 handle)
|
||||
if (lmp_esco_capable(conn->link)) {
|
||||
if (!find_next_esco_param(conn, esco_param_cvsd,
|
||||
ARRAY_SIZE(esco_param_cvsd)))
|
||||
return false;
|
||||
return -EINVAL;
|
||||
param = &esco_param_cvsd[conn->attempt - 1];
|
||||
} else {
|
||||
if (conn->attempt > ARRAY_SIZE(sco_param_cvsd))
|
||||
return false;
|
||||
return -EINVAL;
|
||||
param = &sco_param_cvsd[conn->attempt - 1];
|
||||
}
|
||||
cp.tx_coding_format.id = 2;
|
||||
@ -423,7 +471,7 @@ static bool hci_enhanced_setup_sync_conn(struct hci_conn *conn, __u16 handle)
|
||||
cp.out_transport_unit_size = 16;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cp.retrans_effort = param->retrans_effort;
|
||||
@ -431,9 +479,9 @@ static bool hci_enhanced_setup_sync_conn(struct hci_conn *conn, __u16 handle)
|
||||
cp.max_latency = __cpu_to_le16(param->max_latency);
|
||||
|
||||
if (hci_send_cmd(hdev, HCI_OP_ENHANCED_SETUP_SYNC_CONN, sizeof(cp), &cp) < 0)
|
||||
return false;
|
||||
return -EIO;
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle)
|
||||
@ -490,8 +538,24 @@ static bool hci_setup_sync_conn(struct hci_conn *conn, __u16 handle)
|
||||
|
||||
bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
|
||||
{
|
||||
if (enhanced_sync_conn_capable(conn->hdev))
|
||||
return hci_enhanced_setup_sync_conn(conn, handle);
|
||||
int result;
|
||||
struct conn_handle_t *conn_handle;
|
||||
|
||||
if (enhanced_sync_conn_capable(conn->hdev)) {
|
||||
conn_handle = kzalloc(sizeof(*conn_handle), GFP_KERNEL);
|
||||
|
||||
if (!conn_handle)
|
||||
return false;
|
||||
|
||||
conn_handle->conn = conn;
|
||||
conn_handle->handle = handle;
|
||||
result = hci_cmd_sync_queue(conn->hdev, hci_enhanced_setup_sync,
|
||||
conn_handle, NULL);
|
||||
if (result < 0)
|
||||
kfree(conn_handle);
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
return hci_setup_sync_conn(conn, handle);
|
||||
}
|
||||
@ -2696,3 +2760,79 @@ u32 hci_conn_get_phy(struct hci_conn *conn)
|
||||
|
||||
return phys;
|
||||
}
|
||||
|
||||
int hci_abort_conn(struct hci_conn *conn, u8 reason)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
switch (conn->state) {
|
||||
case BT_CONNECTED:
|
||||
case BT_CONFIG:
|
||||
if (conn->type == AMP_LINK) {
|
||||
struct hci_cp_disconn_phy_link cp;
|
||||
|
||||
cp.phy_handle = HCI_PHY_HANDLE(conn->handle);
|
||||
cp.reason = reason;
|
||||
r = hci_send_cmd(conn->hdev, HCI_OP_DISCONN_PHY_LINK,
|
||||
sizeof(cp), &cp);
|
||||
} else {
|
||||
struct hci_cp_disconnect dc;
|
||||
|
||||
dc.handle = cpu_to_le16(conn->handle);
|
||||
dc.reason = reason;
|
||||
r = hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT,
|
||||
sizeof(dc), &dc);
|
||||
}
|
||||
|
||||
conn->state = BT_DISCONN;
|
||||
|
||||
break;
|
||||
case BT_CONNECT:
|
||||
if (conn->type == LE_LINK) {
|
||||
if (test_bit(HCI_CONN_SCANNING, &conn->flags))
|
||||
break;
|
||||
r = hci_send_cmd(conn->hdev,
|
||||
HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL);
|
||||
} else if (conn->type == ACL_LINK) {
|
||||
if (conn->hdev->hci_ver < BLUETOOTH_VER_1_2)
|
||||
break;
|
||||
r = hci_send_cmd(conn->hdev,
|
||||
HCI_OP_CREATE_CONN_CANCEL,
|
||||
6, &conn->dst);
|
||||
}
|
||||
break;
|
||||
case BT_CONNECT2:
|
||||
if (conn->type == ACL_LINK) {
|
||||
struct hci_cp_reject_conn_req rej;
|
||||
|
||||
bacpy(&rej.bdaddr, &conn->dst);
|
||||
rej.reason = reason;
|
||||
|
||||
r = hci_send_cmd(conn->hdev,
|
||||
HCI_OP_REJECT_CONN_REQ,
|
||||
sizeof(rej), &rej);
|
||||
} else if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
|
||||
struct hci_cp_reject_sync_conn_req rej;
|
||||
|
||||
bacpy(&rej.bdaddr, &conn->dst);
|
||||
|
||||
/* SCO rejection has its own limited set of
|
||||
* allowed error values (0x0D-0x0F) which isn't
|
||||
* compatible with most values passed to this
|
||||
* function. To be safe hard-code one of the
|
||||
* values that's suitable for SCO.
|
||||
*/
|
||||
rej.reason = HCI_ERROR_REJ_LIMITED_RESOURCES;
|
||||
|
||||
r = hci_send_cmd(conn->hdev,
|
||||
HCI_OP_REJECT_SYNC_CONN_REQ,
|
||||
sizeof(rej), &rej);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
conn->state = BT_CLOSED;
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -597,6 +597,15 @@ static int hci_dev_do_reset(struct hci_dev *hdev)
|
||||
|
||||
/* Cancel these to avoid queueing non-chained pending work */
|
||||
hci_dev_set_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE);
|
||||
/* Wait for
|
||||
*
|
||||
* if (!hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE))
|
||||
* queue_delayed_work(&hdev->{cmd,ncmd}_timer)
|
||||
*
|
||||
* inside RCU section to see the flag or complete scheduling.
|
||||
*/
|
||||
synchronize_rcu();
|
||||
/* Explicitly cancel works in case scheduled after setting the flag. */
|
||||
cancel_delayed_work(&hdev->cmd_timer);
|
||||
cancel_delayed_work(&hdev->ncmd_timer);
|
||||
|
||||
@ -714,7 +723,7 @@ static void hci_update_passive_scan_state(struct hci_dev *hdev, u8 scan)
|
||||
hci_dev_set_flag(hdev, HCI_BREDR_ENABLED);
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ENABLED))
|
||||
hci_req_update_adv_data(hdev, hdev->cur_adv_instance);
|
||||
hci_update_adv_data(hdev, hdev->cur_adv_instance);
|
||||
|
||||
mgmt_new_settings(hdev);
|
||||
}
|
||||
@ -1706,7 +1715,8 @@ 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)
|
||||
u32 min_interval, u32 max_interval,
|
||||
u8 mesh_handle)
|
||||
{
|
||||
struct adv_info *adv;
|
||||
|
||||
@ -1717,7 +1727,7 @@ struct adv_info *hci_add_adv_instance(struct hci_dev *hdev, u8 instance,
|
||||
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)
|
||||
instance < 1 || instance > hdev->le_num_of_adv_sets + 1)
|
||||
return ERR_PTR(-EOVERFLOW);
|
||||
|
||||
adv = kzalloc(sizeof(*adv), GFP_KERNEL);
|
||||
@ -1734,6 +1744,11 @@ struct adv_info *hci_add_adv_instance(struct hci_dev *hdev, u8 instance,
|
||||
adv->min_interval = min_interval;
|
||||
adv->max_interval = max_interval;
|
||||
adv->tx_power = tx_power;
|
||||
/* Defining a mesh_handle changes the timing units to ms,
|
||||
* rather than seconds, and ties the instance to the requested
|
||||
* mesh_tx queue.
|
||||
*/
|
||||
adv->mesh = mesh_handle;
|
||||
|
||||
hci_set_adv_instance_data(hdev, instance, adv_data_len, adv_data,
|
||||
scan_rsp_len, scan_rsp_data);
|
||||
@ -1762,7 +1777,7 @@ struct adv_info *hci_add_per_instance(struct hci_dev *hdev, u8 instance,
|
||||
|
||||
adv = hci_add_adv_instance(hdev, instance, flags, 0, NULL, 0, NULL,
|
||||
0, 0, HCI_ADV_TX_POWER_NO_PREFERENCE,
|
||||
min_interval, max_interval);
|
||||
min_interval, max_interval, 0);
|
||||
if (IS_ERR(adv))
|
||||
return adv;
|
||||
|
||||
@ -2391,6 +2406,10 @@ static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
|
||||
container_of(nb, struct hci_dev, suspend_notifier);
|
||||
int ret = 0;
|
||||
|
||||
/* Userspace has full control of this device. Do nothing. */
|
||||
if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
|
||||
return NOTIFY_DONE;
|
||||
|
||||
if (action == PM_SUSPEND_PREPARE)
|
||||
ret = hci_suspend_dev(hdev);
|
||||
else if (action == PM_POST_SUSPEND)
|
||||
@ -2486,6 +2505,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
|
||||
mutex_init(&hdev->lock);
|
||||
mutex_init(&hdev->req_lock);
|
||||
|
||||
INIT_LIST_HEAD(&hdev->mesh_pending);
|
||||
INIT_LIST_HEAD(&hdev->mgmt_pending);
|
||||
INIT_LIST_HEAD(&hdev->reject_list);
|
||||
INIT_LIST_HEAD(&hdev->accept_list);
|
||||
@ -3469,15 +3489,27 @@ static inline int __get_blocks(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
return DIV_ROUND_UP(skb->len - HCI_ACL_HDR_SIZE, hdev->block_len);
|
||||
}
|
||||
|
||||
static void __check_timeout(struct hci_dev *hdev, unsigned int cnt)
|
||||
static void __check_timeout(struct hci_dev *hdev, unsigned int cnt, u8 type)
|
||||
{
|
||||
if (!hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
|
||||
/* ACL tx timeout must be longer than maximum
|
||||
* link supervision timeout (40.9 seconds) */
|
||||
if (!cnt && time_after(jiffies, hdev->acl_last_tx +
|
||||
HCI_ACL_TX_TIMEOUT))
|
||||
hci_link_tx_to(hdev, ACL_LINK);
|
||||
unsigned long last_tx;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED))
|
||||
return;
|
||||
|
||||
switch (type) {
|
||||
case LE_LINK:
|
||||
last_tx = hdev->le_last_tx;
|
||||
break;
|
||||
default:
|
||||
last_tx = hdev->acl_last_tx;
|
||||
break;
|
||||
}
|
||||
|
||||
/* tx timeout must be longer than maximum link supervision timeout
|
||||
* (40.9 seconds)
|
||||
*/
|
||||
if (!cnt && time_after(jiffies, last_tx + HCI_ACL_TX_TIMEOUT))
|
||||
hci_link_tx_to(hdev, type);
|
||||
}
|
||||
|
||||
/* Schedule SCO */
|
||||
@ -3535,7 +3567,7 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev)
|
||||
struct sk_buff *skb;
|
||||
int quote;
|
||||
|
||||
__check_timeout(hdev, cnt);
|
||||
__check_timeout(hdev, cnt, ACL_LINK);
|
||||
|
||||
while (hdev->acl_cnt &&
|
||||
(chan = hci_chan_sent(hdev, ACL_LINK, "e))) {
|
||||
@ -3578,8 +3610,6 @@ static void hci_sched_acl_blk(struct hci_dev *hdev)
|
||||
int quote;
|
||||
u8 type;
|
||||
|
||||
__check_timeout(hdev, cnt);
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
if (hdev->dev_type == HCI_AMP)
|
||||
@ -3587,6 +3617,8 @@ static void hci_sched_acl_blk(struct hci_dev *hdev)
|
||||
else
|
||||
type = ACL_LINK;
|
||||
|
||||
__check_timeout(hdev, cnt, type);
|
||||
|
||||
while (hdev->block_cnt > 0 &&
|
||||
(chan = hci_chan_sent(hdev, type, "e))) {
|
||||
u32 priority = (skb_peek(&chan->data_q))->priority;
|
||||
@ -3660,7 +3692,7 @@ static void hci_sched_le(struct hci_dev *hdev)
|
||||
|
||||
cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt;
|
||||
|
||||
__check_timeout(hdev, cnt);
|
||||
__check_timeout(hdev, cnt, LE_LINK);
|
||||
|
||||
tmp = cnt;
|
||||
while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) {
|
||||
@ -4056,12 +4088,14 @@ static void hci_cmd_work(struct work_struct *work)
|
||||
if (res < 0)
|
||||
__hci_cmd_sync_cancel(hdev, -res);
|
||||
|
||||
rcu_read_lock();
|
||||
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,
|
||||
HCI_CMD_TIMEOUT);
|
||||
queue_delayed_work(hdev->workqueue, &hdev->cmd_timer,
|
||||
HCI_CMD_TIMEOUT);
|
||||
rcu_read_unlock();
|
||||
} else {
|
||||
skb_queue_head(&hdev->cmd_q, skb);
|
||||
queue_work(hdev->workqueue, &hdev->cmd_work);
|
||||
|
@ -1245,7 +1245,7 @@ void hci_debugfs_create_conn(struct hci_conn *conn)
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
char name[6];
|
||||
|
||||
if (IS_ERR_OR_NULL(hdev->debugfs))
|
||||
if (IS_ERR_OR_NULL(hdev->debugfs) || conn->debugfs)
|
||||
return;
|
||||
|
||||
snprintf(name, sizeof(name), "%u", conn->handle);
|
||||
|
@ -712,6 +712,47 @@ static u8 hci_cc_read_local_version(struct hci_dev *hdev, void *data,
|
||||
return rp->status;
|
||||
}
|
||||
|
||||
static u8 hci_cc_read_enc_key_size(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_read_enc_key_size *rp = data;
|
||||
struct hci_conn *conn;
|
||||
u16 handle;
|
||||
u8 status = rp->status;
|
||||
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
|
||||
handle = le16_to_cpu(rp->handle);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!conn) {
|
||||
status = 0xFF;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* While unexpected, the read_enc_key_size command may fail. The most
|
||||
* secure approach is to then assume the key size is 0 to force a
|
||||
* disconnection.
|
||||
*/
|
||||
if (status) {
|
||||
bt_dev_err(hdev, "failed to read key size for handle %u",
|
||||
handle);
|
||||
conn->enc_key_size = 0;
|
||||
} else {
|
||||
conn->enc_key_size = rp->key_size;
|
||||
status = 0;
|
||||
}
|
||||
|
||||
hci_encrypt_cfm(conn, 0);
|
||||
|
||||
done:
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static u8 hci_cc_read_local_commands(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
@ -1715,6 +1756,8 @@ static void le_set_scan_enable_complete(struct hci_dev *hdev, u8 enable)
|
||||
hci_dev_set_flag(hdev, HCI_LE_SCAN);
|
||||
if (hdev->le_scan_type == LE_SCAN_ACTIVE)
|
||||
clear_pending_adv_report(hdev);
|
||||
if (hci_dev_test_flag(hdev, HCI_MESH))
|
||||
hci_discovery_set_state(hdev, DISCOVERY_FINDING);
|
||||
break;
|
||||
|
||||
case LE_SCAN_DISABLE:
|
||||
@ -1729,7 +1772,7 @@ static void le_set_scan_enable_complete(struct hci_dev *hdev, u8 enable)
|
||||
d->last_adv_addr_type, NULL,
|
||||
d->last_adv_rssi, d->last_adv_flags,
|
||||
d->last_adv_data,
|
||||
d->last_adv_data_len, NULL, 0);
|
||||
d->last_adv_data_len, NULL, 0, 0);
|
||||
}
|
||||
|
||||
/* Cancel this timer so that we don't try to disable scanning
|
||||
@ -1745,6 +1788,9 @@ static void le_set_scan_enable_complete(struct hci_dev *hdev, u8 enable)
|
||||
*/
|
||||
if (hci_dev_test_and_clear_flag(hdev, HCI_LE_SCAN_INTERRUPTED))
|
||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||
else if (!hci_dev_test_flag(hdev, HCI_LE_ADV) &&
|
||||
hdev->discovery.state == DISCOVERY_FINDING)
|
||||
queue_work(hdev->workqueue, &hdev->reenable_adv_work);
|
||||
|
||||
break;
|
||||
|
||||
@ -2152,7 +2198,7 @@ static u8 hci_cc_set_ext_adv_param(struct hci_dev *hdev, void *data,
|
||||
adv_instance->tx_power = rp->tx_power;
|
||||
}
|
||||
/* Update adv data as tx power is known now */
|
||||
hci_req_update_adv_data(hdev, cp->handle);
|
||||
hci_update_adv_data(hdev, cp->handle);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
@ -3071,7 +3117,7 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, void *edata,
|
||||
|
||||
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
|
||||
info->dev_class, HCI_RSSI_INVALID,
|
||||
flags, NULL, 0, NULL, 0);
|
||||
flags, NULL, 0, NULL, 0, 0);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
@ -3534,47 +3580,6 @@ unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void read_enc_key_size_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode, struct sk_buff *skb)
|
||||
{
|
||||
const struct hci_rp_read_enc_key_size *rp;
|
||||
struct hci_conn *conn;
|
||||
u16 handle;
|
||||
|
||||
BT_DBG("%s status 0x%02x", hdev->name, status);
|
||||
|
||||
if (!skb || skb->len < sizeof(*rp)) {
|
||||
bt_dev_err(hdev, "invalid read key size response");
|
||||
return;
|
||||
}
|
||||
|
||||
rp = (void *)skb->data;
|
||||
handle = le16_to_cpu(rp->handle);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
/* While unexpected, the read_enc_key_size command may fail. The most
|
||||
* secure approach is to then assume the key size is 0 to force a
|
||||
* disconnection.
|
||||
*/
|
||||
if (rp->status) {
|
||||
bt_dev_err(hdev, "failed to read key size for handle %u",
|
||||
handle);
|
||||
conn->enc_key_size = 0;
|
||||
} else {
|
||||
conn->enc_key_size = rp->key_size;
|
||||
}
|
||||
|
||||
hci_encrypt_cfm(conn, 0);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
@ -3639,7 +3644,6 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
|
||||
/* Try reading the encryption key size for encrypted ACL links */
|
||||
if (!ev->status && ev->encrypt && conn->type == ACL_LINK) {
|
||||
struct hci_cp_read_enc_key_size cp;
|
||||
struct hci_request req;
|
||||
|
||||
/* Only send HCI_Read_Encryption_Key_Size if the
|
||||
* controller really supports it. If it doesn't, assume
|
||||
@ -3650,12 +3654,9 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, void *data,
|
||||
goto notify;
|
||||
}
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
hci_req_add(&req, HCI_OP_READ_ENC_KEY_SIZE, sizeof(cp), &cp);
|
||||
|
||||
if (hci_req_run_skb(&req, read_enc_key_size_complete)) {
|
||||
if (hci_send_cmd(hdev, HCI_OP_READ_ENC_KEY_SIZE,
|
||||
sizeof(cp), &cp)) {
|
||||
bt_dev_err(hdev, "sending read key size failed");
|
||||
conn->enc_key_size = HCI_LINK_KEY_SIZE;
|
||||
goto notify;
|
||||
@ -3766,16 +3767,18 @@ static inline void handle_cmd_cnt_and_timer(struct hci_dev *hdev, u8 ncmd)
|
||||
{
|
||||
cancel_delayed_work(&hdev->cmd_timer);
|
||||
|
||||
rcu_read_lock();
|
||||
if (!test_bit(HCI_RESET, &hdev->flags)) {
|
||||
if (ncmd) {
|
||||
cancel_delayed_work(&hdev->ncmd_timer);
|
||||
atomic_set(&hdev->cmd_cnt, 1);
|
||||
} else {
|
||||
if (!hci_dev_test_flag(hdev, HCI_CMD_DRAIN_WORKQUEUE))
|
||||
schedule_delayed_work(&hdev->ncmd_timer,
|
||||
HCI_NCMD_TIMEOUT);
|
||||
queue_delayed_work(hdev->workqueue, &hdev->ncmd_timer,
|
||||
HCI_NCMD_TIMEOUT);
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static u8 hci_cc_le_read_buffer_size_v2(struct hci_dev *hdev, void *data,
|
||||
@ -4037,6 +4040,8 @@ static const struct hci_cc {
|
||||
sizeof(struct hci_rp_read_local_amp_info)),
|
||||
HCI_CC(HCI_OP_READ_CLOCK, hci_cc_read_clock,
|
||||
sizeof(struct hci_rp_read_clock)),
|
||||
HCI_CC(HCI_OP_READ_ENC_KEY_SIZE, hci_cc_read_enc_key_size,
|
||||
sizeof(struct hci_rp_read_enc_key_size)),
|
||||
HCI_CC(HCI_OP_READ_INQ_RSP_TX_POWER, hci_cc_read_inq_rsp_tx_power,
|
||||
sizeof(struct hci_rp_read_inq_rsp_tx_power)),
|
||||
HCI_CC(HCI_OP_READ_DEF_ERR_DATA_REPORTING,
|
||||
@ -4829,7 +4834,7 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, void *edata,
|
||||
|
||||
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
|
||||
info->dev_class, info->rssi,
|
||||
flags, NULL, 0, NULL, 0);
|
||||
flags, NULL, 0, NULL, 0, 0);
|
||||
}
|
||||
} else if (skb->len == array_size(ev->num,
|
||||
sizeof(struct inquiry_info_rssi))) {
|
||||
@ -4860,7 +4865,7 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, void *edata,
|
||||
|
||||
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
|
||||
info->dev_class, info->rssi,
|
||||
flags, NULL, 0, NULL, 0);
|
||||
flags, NULL, 0, NULL, 0, 0);
|
||||
}
|
||||
} else {
|
||||
bt_dev_err(hdev, "Malformed HCI Event: 0x%2.2x",
|
||||
@ -5116,7 +5121,7 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, void *edata,
|
||||
|
||||
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
|
||||
info->dev_class, info->rssi,
|
||||
flags, info->data, eir_len, NULL, 0);
|
||||
flags, info->data, eir_len, NULL, 0, 0);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
@ -6172,7 +6177,7 @@ static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
|
||||
static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
||||
u8 bdaddr_type, bdaddr_t *direct_addr,
|
||||
u8 direct_addr_type, s8 rssi, u8 *data, u8 len,
|
||||
bool ext_adv)
|
||||
bool ext_adv, bool ctl_time, u64 instant)
|
||||
{
|
||||
struct discovery_state *d = &hdev->discovery;
|
||||
struct smp_irk *irk;
|
||||
@ -6220,7 +6225,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
||||
* important to see if the address is matching the local
|
||||
* controller address.
|
||||
*/
|
||||
if (direct_addr) {
|
||||
if (!hci_dev_test_flag(hdev, HCI_MESH) && direct_addr) {
|
||||
direct_addr_type = ev_bdaddr_type(hdev, direct_addr_type,
|
||||
&bdaddr_resolved);
|
||||
|
||||
@ -6268,6 +6273,18 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
||||
conn->le_adv_data_len = len;
|
||||
}
|
||||
|
||||
if (type == LE_ADV_NONCONN_IND || type == LE_ADV_SCAN_IND)
|
||||
flags = MGMT_DEV_FOUND_NOT_CONNECTABLE;
|
||||
else
|
||||
flags = 0;
|
||||
|
||||
/* All scan results should be sent up for Mesh systems */
|
||||
if (hci_dev_test_flag(hdev, HCI_MESH)) {
|
||||
mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
|
||||
rssi, flags, data, len, NULL, 0, instant);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Passive scanning shouldn't trigger any device found events,
|
||||
* except for devices marked as CONN_REPORT for which we do send
|
||||
* device found events, or advertisement monitoring requested.
|
||||
@ -6281,12 +6298,8 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
||||
idr_is_empty(&hdev->adv_monitors_idr))
|
||||
return;
|
||||
|
||||
if (type == LE_ADV_NONCONN_IND || type == LE_ADV_SCAN_IND)
|
||||
flags = MGMT_DEV_FOUND_NOT_CONNECTABLE;
|
||||
else
|
||||
flags = 0;
|
||||
mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
|
||||
rssi, flags, data, len, NULL, 0);
|
||||
rssi, flags, data, len, NULL, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -6305,11 +6318,8 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
||||
* and just sends a scan response event, then it is marked as
|
||||
* not connectable as well.
|
||||
*/
|
||||
if (type == LE_ADV_NONCONN_IND || type == LE_ADV_SCAN_IND ||
|
||||
type == LE_ADV_SCAN_RSP)
|
||||
if (type == LE_ADV_SCAN_RSP)
|
||||
flags = MGMT_DEV_FOUND_NOT_CONNECTABLE;
|
||||
else
|
||||
flags = 0;
|
||||
|
||||
/* If there's nothing pending either store the data from this
|
||||
* event or send an immediate device found event if the data
|
||||
@ -6326,7 +6336,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
||||
}
|
||||
|
||||
mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
|
||||
rssi, flags, data, len, NULL, 0);
|
||||
rssi, flags, data, len, NULL, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -6345,7 +6355,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
||||
d->last_adv_addr_type, NULL,
|
||||
d->last_adv_rssi, d->last_adv_flags,
|
||||
d->last_adv_data,
|
||||
d->last_adv_data_len, NULL, 0);
|
||||
d->last_adv_data_len, NULL, 0, 0);
|
||||
|
||||
/* If the new report will trigger a SCAN_REQ store it for
|
||||
* later merging.
|
||||
@ -6362,7 +6372,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
||||
*/
|
||||
clear_pending_adv_report(hdev);
|
||||
mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL,
|
||||
rssi, flags, data, len, NULL, 0);
|
||||
rssi, flags, data, len, NULL, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -6372,7 +6382,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
||||
*/
|
||||
mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK,
|
||||
d->last_adv_addr_type, NULL, rssi, d->last_adv_flags,
|
||||
d->last_adv_data, d->last_adv_data_len, data, len);
|
||||
d->last_adv_data, d->last_adv_data_len, data, len, 0);
|
||||
clear_pending_adv_report(hdev);
|
||||
}
|
||||
|
||||
@ -6380,6 +6390,7 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_advertising_report *ev = data;
|
||||
u64 instant = jiffies;
|
||||
|
||||
if (!ev->num)
|
||||
return;
|
||||
@ -6404,7 +6415,8 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
rssi = info->data[info->length];
|
||||
process_adv_report(hdev, info->type, &info->bdaddr,
|
||||
info->bdaddr_type, NULL, 0, rssi,
|
||||
info->data, info->length, false);
|
||||
info->data, info->length, false,
|
||||
false, instant);
|
||||
} else {
|
||||
bt_dev_err(hdev, "Dropping invalid advertising data");
|
||||
}
|
||||
@ -6461,6 +6473,7 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_ext_adv_report *ev = data;
|
||||
u64 instant = jiffies;
|
||||
|
||||
if (!ev->num)
|
||||
return;
|
||||
@ -6487,7 +6500,8 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
process_adv_report(hdev, legacy_evt_type, &info->bdaddr,
|
||||
info->bdaddr_type, NULL, 0,
|
||||
info->rssi, info->data, info->length,
|
||||
!(evt_type & LE_EXT_ADV_LEGACY_PDU));
|
||||
!(evt_type & LE_EXT_ADV_LEGACY_PDU),
|
||||
false, instant);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6710,6 +6724,7 @@ static void hci_le_direct_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_le_direct_adv_report *ev = data;
|
||||
u64 instant = jiffies;
|
||||
int i;
|
||||
|
||||
if (!hci_le_ev_skb_pull(hdev, skb, HCI_EV_LE_DIRECT_ADV_REPORT,
|
||||
@ -6727,7 +6742,7 @@ static void hci_le_direct_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
process_adv_report(hdev, info->type, &info->bdaddr,
|
||||
info->bdaddr_type, &info->direct_addr,
|
||||
info->direct_addr_type, info->rssi, NULL, 0,
|
||||
false);
|
||||
false, false, instant);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
@ -6776,6 +6791,13 @@ static void hci_le_cis_estabilished_evt(struct hci_dev *hdev, void *data,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (conn->type != ISO_LINK) {
|
||||
bt_dev_err(hdev,
|
||||
"Invalid connection link type handle 0x%4.4x",
|
||||
handle);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (conn->role == HCI_ROLE_SLAVE) {
|
||||
__le32 interval;
|
||||
|
||||
@ -6896,6 +6918,13 @@ static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data,
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
if (conn->type != ISO_LINK) {
|
||||
bt_dev_err(hdev,
|
||||
"Invalid connection link type handle 0x%2.2x",
|
||||
ev->handle);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (ev->num_bis)
|
||||
conn->handle = __le16_to_cpu(ev->bis_handle[0]);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -68,63 +68,10 @@ 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);
|
||||
|
||||
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);
|
||||
|
||||
void hci_req_add_le_scan_disable(struct hci_request *req, bool rpa_le_conn);
|
||||
void hci_req_add_le_passive_scan(struct hci_request *req);
|
||||
|
||||
void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next);
|
||||
|
||||
void hci_req_disable_address_resolution(struct hci_dev *hdev);
|
||||
void hci_req_reenable_advertising(struct hci_dev *hdev);
|
||||
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,
|
||||
bool force);
|
||||
void hci_req_clear_adv_instance(struct hci_dev *hdev, struct sock *sk,
|
||||
struct hci_request *req, u8 instance,
|
||||
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);
|
||||
int hci_get_random_address(struct hci_dev *hdev, bool require_privacy,
|
||||
bool use_rpa, struct adv_info *adv_instance,
|
||||
u8 *own_addr_type, bdaddr_t *rand_addr);
|
||||
|
||||
void __hci_req_update_class(struct hci_request *req);
|
||||
|
||||
/* Returns true if HCI commands were queued */
|
||||
bool hci_req_stop_discovery(struct hci_request *req);
|
||||
|
||||
int hci_req_configure_datapath(struct hci_dev *hdev, struct bt_codec *codec);
|
||||
|
||||
void __hci_req_update_scan(struct hci_request *req);
|
||||
|
||||
int hci_update_random_address(struct hci_request *req, bool require_privacy,
|
||||
bool use_rpa, u8 *own_addr_type);
|
||||
|
||||
int hci_abort_conn(struct hci_conn *conn, u8 reason);
|
||||
void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
|
||||
u8 reason);
|
||||
|
||||
void hci_request_setup(struct hci_dev *hdev);
|
||||
void hci_request_cancel_all(struct hci_dev *hdev);
|
||||
|
@ -887,7 +887,6 @@ static int hci_sock_release(struct socket *sock)
|
||||
*/
|
||||
hci_dev_do_close(hdev);
|
||||
hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
|
||||
hci_register_suspend_notifier(hdev);
|
||||
mgmt_index_added(hdev);
|
||||
}
|
||||
|
||||
@ -1216,7 +1215,6 @@ 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) {
|
||||
@ -1231,7 +1229,6 @@ 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;
|
||||
@ -2065,6 +2062,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname,
|
||||
|
||||
static void hci_sock_destruct(struct sock *sk)
|
||||
{
|
||||
mgmt_cleanup(sk);
|
||||
skb_queue_purge(&sk->sk_receive_queue);
|
||||
skb_queue_purge(&sk->sk_write_queue);
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ int __hci_cmd_sync_status_sk(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
skb = __hci_cmd_sync_sk(hdev, opcode, plen, param, event, timeout, sk);
|
||||
if (IS_ERR(skb)) {
|
||||
bt_dev_err(hdev, "Opcode 0x%4x failed: %ld", opcode,
|
||||
PTR_ERR(skb));
|
||||
PTR_ERR(skb));
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
|
||||
@ -321,6 +321,307 @@ static void hci_cmd_sync_cancel_work(struct work_struct *work)
|
||||
wake_up_interruptible(&hdev->req_wait_q);
|
||||
}
|
||||
|
||||
static int hci_scan_disable_sync(struct hci_dev *hdev);
|
||||
static int scan_disable_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
return hci_scan_disable_sync(hdev);
|
||||
}
|
||||
|
||||
static int hci_inquiry_sync(struct hci_dev *hdev, u8 length);
|
||||
static int interleaved_inquiry_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
return hci_inquiry_sync(hdev, DISCOV_INTERLEAVED_INQUIRY_LEN);
|
||||
}
|
||||
|
||||
static void le_scan_disable(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
le_scan_disable.work);
|
||||
int status;
|
||||
|
||||
bt_dev_dbg(hdev, "");
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
|
||||
goto _return;
|
||||
|
||||
cancel_delayed_work(&hdev->le_scan_restart);
|
||||
|
||||
status = hci_cmd_sync_queue(hdev, scan_disable_sync, NULL, NULL);
|
||||
if (status) {
|
||||
bt_dev_err(hdev, "failed to disable LE scan: %d", status);
|
||||
goto _return;
|
||||
}
|
||||
|
||||
hdev->discovery.scan_start = 0;
|
||||
|
||||
/* If we were running LE only scan, change discovery state. If
|
||||
* we were running both LE and BR/EDR inquiry simultaneously,
|
||||
* and BR/EDR inquiry is already finished, stop discovery,
|
||||
* otherwise BR/EDR inquiry will stop discovery when finished.
|
||||
* If we will resolve remote device name, do not change
|
||||
* discovery state.
|
||||
*/
|
||||
|
||||
if (hdev->discovery.type == DISCOV_TYPE_LE)
|
||||
goto discov_stopped;
|
||||
|
||||
if (hdev->discovery.type != DISCOV_TYPE_INTERLEAVED)
|
||||
goto _return;
|
||||
|
||||
if (test_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks)) {
|
||||
if (!test_bit(HCI_INQUIRY, &hdev->flags) &&
|
||||
hdev->discovery.state != DISCOVERY_RESOLVING)
|
||||
goto discov_stopped;
|
||||
|
||||
goto _return;
|
||||
}
|
||||
|
||||
status = hci_cmd_sync_queue(hdev, interleaved_inquiry_sync, NULL, NULL);
|
||||
if (status) {
|
||||
bt_dev_err(hdev, "inquiry failed: status %d", status);
|
||||
goto discov_stopped;
|
||||
}
|
||||
|
||||
goto _return;
|
||||
|
||||
discov_stopped:
|
||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||
|
||||
_return:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static int hci_le_set_scan_enable_sync(struct hci_dev *hdev, u8 val,
|
||||
u8 filter_dup);
|
||||
static int hci_le_scan_restart_sync(struct hci_dev *hdev)
|
||||
{
|
||||
/* If controller is not scanning we are done. */
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_SCAN))
|
||||
return 0;
|
||||
|
||||
if (hdev->scanning_paused) {
|
||||
bt_dev_dbg(hdev, "Scanning is paused for suspend");
|
||||
return 0;
|
||||
}
|
||||
|
||||
hci_le_set_scan_enable_sync(hdev, LE_SCAN_DISABLE, 0x00);
|
||||
return hci_le_set_scan_enable_sync(hdev, LE_SCAN_ENABLE,
|
||||
LE_SCAN_FILTER_DUP_ENABLE);
|
||||
}
|
||||
|
||||
static int le_scan_restart_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
return hci_le_scan_restart_sync(hdev);
|
||||
}
|
||||
|
||||
static void le_scan_restart(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
le_scan_restart.work);
|
||||
unsigned long timeout, duration, scan_start, now;
|
||||
int status;
|
||||
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
status = hci_cmd_sync_queue(hdev, le_scan_restart_sync, NULL, NULL);
|
||||
if (status) {
|
||||
bt_dev_err(hdev, "failed to restart LE scan: status %d",
|
||||
status);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) ||
|
||||
!hdev->discovery.scan_start)
|
||||
goto unlock;
|
||||
|
||||
/* When the scan was started, hdev->le_scan_disable has been queued
|
||||
* after duration from scan_start. During scan restart this job
|
||||
* has been canceled, and we need to queue it again after proper
|
||||
* timeout, to make sure that scan does not run indefinitely.
|
||||
*/
|
||||
duration = hdev->discovery.scan_duration;
|
||||
scan_start = hdev->discovery.scan_start;
|
||||
now = jiffies;
|
||||
if (now - scan_start <= duration) {
|
||||
int elapsed;
|
||||
|
||||
if (now >= scan_start)
|
||||
elapsed = now - scan_start;
|
||||
else
|
||||
elapsed = ULONG_MAX - scan_start + now;
|
||||
|
||||
timeout = duration - elapsed;
|
||||
} else {
|
||||
timeout = 0;
|
||||
}
|
||||
|
||||
queue_delayed_work(hdev->req_workqueue,
|
||||
&hdev->le_scan_disable, timeout);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static int reenable_adv_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
|
||||
list_empty(&hdev->adv_instances))
|
||||
return 0;
|
||||
|
||||
if (hdev->cur_adv_instance) {
|
||||
return hci_schedule_adv_instance_sync(hdev,
|
||||
hdev->cur_adv_instance,
|
||||
true);
|
||||
} else {
|
||||
if (ext_adv_capable(hdev)) {
|
||||
hci_start_ext_adv_sync(hdev, 0x00);
|
||||
} else {
|
||||
hci_update_adv_data_sync(hdev, 0x00);
|
||||
hci_update_scan_rsp_data_sync(hdev, 0x00);
|
||||
hci_enable_advertising_sync(hdev);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reenable_adv(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
reenable_adv_work);
|
||||
int status;
|
||||
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
status = hci_cmd_sync_queue(hdev, reenable_adv_sync, NULL, NULL);
|
||||
if (status)
|
||||
bt_dev_err(hdev, "failed to reenable ADV: %d", status);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void cancel_adv_timeout(struct hci_dev *hdev)
|
||||
{
|
||||
if (hdev->adv_instance_timeout) {
|
||||
hdev->adv_instance_timeout = 0;
|
||||
cancel_delayed_work(&hdev->adv_instance_expire);
|
||||
}
|
||||
}
|
||||
|
||||
/* For a single instance:
|
||||
* - force == true: The instance will be removed even when its remaining
|
||||
* lifetime is not zero.
|
||||
* - force == false: the instance will be deactivated but kept stored unless
|
||||
* the remaining lifetime is zero.
|
||||
*
|
||||
* For instance == 0x00:
|
||||
* - force == true: All instances will be removed regardless of their timeout
|
||||
* setting.
|
||||
* - force == false: Only instances that have a timeout will be removed.
|
||||
*/
|
||||
int hci_clear_adv_instance_sync(struct hci_dev *hdev, struct sock *sk,
|
||||
u8 instance, bool force)
|
||||
{
|
||||
struct adv_info *adv_instance, *n, *next_instance = NULL;
|
||||
int err;
|
||||
u8 rem_inst;
|
||||
|
||||
/* Cancel any timeout concerning the removed instance(s). */
|
||||
if (!instance || hdev->cur_adv_instance == instance)
|
||||
cancel_adv_timeout(hdev);
|
||||
|
||||
/* Get the next instance to advertise BEFORE we remove
|
||||
* the current one. This can be the same instance again
|
||||
* if there is only one instance.
|
||||
*/
|
||||
if (instance && hdev->cur_adv_instance == instance)
|
||||
next_instance = hci_get_next_instance(hdev, instance);
|
||||
|
||||
if (instance == 0x00) {
|
||||
list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances,
|
||||
list) {
|
||||
if (!(force || adv_instance->timeout))
|
||||
continue;
|
||||
|
||||
rem_inst = adv_instance->instance;
|
||||
err = hci_remove_adv_instance(hdev, rem_inst);
|
||||
if (!err)
|
||||
mgmt_advertising_removed(sk, hdev, rem_inst);
|
||||
}
|
||||
} else {
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
|
||||
if (force || (adv_instance && adv_instance->timeout &&
|
||||
!adv_instance->remaining_time)) {
|
||||
/* Don't advertise a removed instance. */
|
||||
if (next_instance &&
|
||||
next_instance->instance == instance)
|
||||
next_instance = NULL;
|
||||
|
||||
err = hci_remove_adv_instance(hdev, instance);
|
||||
if (!err)
|
||||
mgmt_advertising_removed(sk, hdev, instance);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hdev_is_powered(hdev) || hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
return 0;
|
||||
|
||||
if (next_instance && !ext_adv_capable(hdev))
|
||||
return hci_schedule_adv_instance_sync(hdev,
|
||||
next_instance->instance,
|
||||
false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adv_timeout_expire_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
u8 instance = *(u8 *)data;
|
||||
|
||||
kfree(data);
|
||||
|
||||
hci_clear_adv_instance_sync(hdev, NULL, instance, false);
|
||||
|
||||
if (list_empty(&hdev->adv_instances))
|
||||
return hci_disable_advertising_sync(hdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void adv_timeout_expire(struct work_struct *work)
|
||||
{
|
||||
u8 *inst_ptr;
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
adv_instance_expire.work);
|
||||
|
||||
bt_dev_dbg(hdev, "");
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
hdev->adv_instance_timeout = 0;
|
||||
|
||||
if (hdev->cur_adv_instance == 0x00)
|
||||
goto unlock;
|
||||
|
||||
inst_ptr = kmalloc(1, GFP_KERNEL);
|
||||
if (!inst_ptr)
|
||||
goto unlock;
|
||||
|
||||
*inst_ptr = hdev->cur_adv_instance;
|
||||
hci_cmd_sync_queue(hdev, adv_timeout_expire_sync, inst_ptr, NULL);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
void hci_cmd_sync_init(struct hci_dev *hdev)
|
||||
{
|
||||
INIT_WORK(&hdev->cmd_sync_work, hci_cmd_sync_work);
|
||||
@ -328,6 +629,10 @@ void hci_cmd_sync_init(struct hci_dev *hdev)
|
||||
mutex_init(&hdev->cmd_sync_work_lock);
|
||||
|
||||
INIT_WORK(&hdev->cmd_sync_cancel_work, hci_cmd_sync_cancel_work);
|
||||
INIT_WORK(&hdev->reenable_adv_work, reenable_adv);
|
||||
INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable);
|
||||
INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart);
|
||||
INIT_DELAYED_WORK(&hdev->adv_instance_expire, adv_timeout_expire);
|
||||
}
|
||||
|
||||
void hci_cmd_sync_clear(struct hci_dev *hdev)
|
||||
@ -335,6 +640,7 @@ void hci_cmd_sync_clear(struct hci_dev *hdev)
|
||||
struct hci_cmd_sync_work_entry *entry, *tmp;
|
||||
|
||||
cancel_work_sync(&hdev->cmd_sync_work);
|
||||
cancel_work_sync(&hdev->reenable_adv_work);
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, &hdev->cmd_sync_work_list, list) {
|
||||
if (entry->destroy)
|
||||
@ -1333,14 +1639,6 @@ int hci_le_terminate_big_sync(struct hci_dev *hdev, u8 handle, u8 reason)
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
static void cancel_adv_timeout(struct hci_dev *hdev)
|
||||
{
|
||||
if (hdev->adv_instance_timeout) {
|
||||
hdev->adv_instance_timeout = 0;
|
||||
cancel_delayed_work(&hdev->adv_instance_expire);
|
||||
}
|
||||
}
|
||||
|
||||
static int hci_set_ext_adv_data_sync(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct {
|
||||
@ -1492,10 +1790,13 @@ static int hci_clear_adv_sets_sync(struct hci_dev *hdev, struct sock *sk)
|
||||
static int hci_clear_adv_sync(struct hci_dev *hdev, struct sock *sk, bool force)
|
||||
{
|
||||
struct adv_info *adv, *n;
|
||||
int err = 0;
|
||||
|
||||
if (ext_adv_capable(hdev))
|
||||
/* Remove all existing sets */
|
||||
return hci_clear_adv_sets_sync(hdev, sk);
|
||||
err = hci_clear_adv_sets_sync(hdev, sk);
|
||||
if (ext_adv_capable(hdev))
|
||||
return err;
|
||||
|
||||
/* This is safe as long as there is no command send while the lock is
|
||||
* held.
|
||||
@ -1523,11 +1824,13 @@ static int hci_clear_adv_sync(struct hci_dev *hdev, struct sock *sk, bool force)
|
||||
static int hci_remove_adv_sync(struct hci_dev *hdev, u8 instance,
|
||||
struct sock *sk)
|
||||
{
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
/* If we use extended advertising, instance has to be removed first. */
|
||||
if (ext_adv_capable(hdev))
|
||||
return hci_remove_ext_adv_instance_sync(hdev, instance, sk);
|
||||
err = hci_remove_ext_adv_instance_sync(hdev, instance, sk);
|
||||
if (ext_adv_capable(hdev))
|
||||
return err;
|
||||
|
||||
/* This is safe as long as there is no command send while the lock is
|
||||
* held.
|
||||
@ -1626,13 +1929,16 @@ int hci_read_tx_power_sync(struct hci_dev *hdev, __le16 handle, u8 type)
|
||||
int hci_disable_advertising_sync(struct hci_dev *hdev)
|
||||
{
|
||||
u8 enable = 0x00;
|
||||
int err = 0;
|
||||
|
||||
/* If controller is not advertising we are done. */
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_ADV))
|
||||
return 0;
|
||||
|
||||
if (ext_adv_capable(hdev))
|
||||
return hci_disable_ext_adv_instance_sync(hdev, 0x00);
|
||||
err = hci_disable_ext_adv_instance_sync(hdev, 0x00);
|
||||
if (ext_adv_capable(hdev))
|
||||
return err;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_ENABLE,
|
||||
sizeof(enable), &enable, HCI_CMD_TIMEOUT);
|
||||
@ -1645,7 +1951,11 @@ static int hci_le_set_ext_scan_enable_sync(struct hci_dev *hdev, u8 val,
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.enable = val;
|
||||
cp.filter_dup = filter_dup;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_MESH))
|
||||
cp.filter_dup = LE_SCAN_FILTER_DUP_DISABLE;
|
||||
else
|
||||
cp.filter_dup = filter_dup;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_ENABLE,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
@ -1661,7 +1971,11 @@ static int hci_le_set_scan_enable_sync(struct hci_dev *hdev, u8 val,
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.enable = val;
|
||||
cp.filter_dup = filter_dup;
|
||||
|
||||
if (val && hci_dev_test_flag(hdev, HCI_MESH))
|
||||
cp.filter_dup = LE_SCAN_FILTER_DUP_DISABLE;
|
||||
else
|
||||
cp.filter_dup = filter_dup;
|
||||
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_SCAN_ENABLE,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
@ -2300,6 +2614,7 @@ static int hci_passive_scan_sync(struct hci_dev *hdev)
|
||||
u8 own_addr_type;
|
||||
u8 filter_policy;
|
||||
u16 window, interval;
|
||||
u8 filter_dups = LE_SCAN_FILTER_DUP_ENABLE;
|
||||
int err;
|
||||
|
||||
if (hdev->scanning_paused) {
|
||||
@ -2362,11 +2677,16 @@ static int hci_passive_scan_sync(struct hci_dev *hdev)
|
||||
interval = hdev->le_scan_interval;
|
||||
}
|
||||
|
||||
/* Disable all filtering for Mesh */
|
||||
if (hci_dev_test_flag(hdev, HCI_MESH)) {
|
||||
filter_policy = 0;
|
||||
filter_dups = LE_SCAN_FILTER_DUP_DISABLE;
|
||||
}
|
||||
|
||||
bt_dev_dbg(hdev, "LE passive scan with acceptlist = %d", filter_policy);
|
||||
|
||||
return hci_start_scan_sync(hdev, LE_SCAN_PASSIVE, interval, window,
|
||||
own_addr_type, filter_policy,
|
||||
LE_SCAN_FILTER_DUP_ENABLE);
|
||||
own_addr_type, filter_policy, filter_dups);
|
||||
}
|
||||
|
||||
/* This function controls the passive scanning based on hdev->pend_le_conns
|
||||
@ -2416,7 +2736,8 @@ int hci_update_passive_scan_sync(struct hci_dev *hdev)
|
||||
bt_dev_dbg(hdev, "ADV monitoring is %s",
|
||||
hci_is_adv_monitoring(hdev) ? "on" : "off");
|
||||
|
||||
if (list_empty(&hdev->pend_le_conns) &&
|
||||
if (!hci_dev_test_flag(hdev, HCI_MESH) &&
|
||||
list_empty(&hdev->pend_le_conns) &&
|
||||
list_empty(&hdev->pend_le_reports) &&
|
||||
!hci_is_adv_monitoring(hdev) &&
|
||||
!hci_dev_test_flag(hdev, HCI_PA_SYNC)) {
|
||||
@ -4355,6 +4676,7 @@ int hci_dev_open_sync(struct hci_dev *hdev)
|
||||
hci_dev_test_flag(hdev, HCI_MGMT) &&
|
||||
hdev->dev_type == HCI_PRIMARY) {
|
||||
ret = hci_powered_update_sync(hdev);
|
||||
mgmt_power_on(hdev, ret);
|
||||
}
|
||||
} else {
|
||||
/* Init failed, cleanup */
|
||||
@ -4406,6 +4728,31 @@ static void hci_pend_le_actions_clear(struct hci_dev *hdev)
|
||||
BT_DBG("All LE pending actions cleared");
|
||||
}
|
||||
|
||||
static int hci_dev_shutdown(struct hci_dev *hdev)
|
||||
{
|
||||
int err = 0;
|
||||
/* Similar to how we first do setup and then set the exclusive access
|
||||
* bit for userspace, we must first unset userchannel and then clean up.
|
||||
* Otherwise, the kernel can't properly use the hci channel to clean up
|
||||
* the controller (some shutdown routines require sending additional
|
||||
* commands to the controller for example).
|
||||
*/
|
||||
bool was_userchannel =
|
||||
hci_dev_test_and_clear_flag(hdev, HCI_USER_CHANNEL);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) &&
|
||||
test_bit(HCI_UP, &hdev->flags)) {
|
||||
/* Execute vendor specific shutdown routine */
|
||||
if (hdev->shutdown)
|
||||
err = hdev->shutdown(hdev);
|
||||
}
|
||||
|
||||
if (was_userchannel)
|
||||
hci_dev_set_flag(hdev, HCI_USER_CHANNEL);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int hci_dev_close_sync(struct hci_dev *hdev)
|
||||
{
|
||||
bool auto_off;
|
||||
@ -4415,17 +4762,18 @@ int hci_dev_close_sync(struct hci_dev *hdev)
|
||||
|
||||
cancel_delayed_work(&hdev->power_off);
|
||||
cancel_delayed_work(&hdev->ncmd_timer);
|
||||
cancel_delayed_work(&hdev->le_scan_disable);
|
||||
cancel_delayed_work(&hdev->le_scan_restart);
|
||||
|
||||
hci_request_cancel_all(hdev);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) &&
|
||||
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
|
||||
test_bit(HCI_UP, &hdev->flags)) {
|
||||
/* Execute vendor specific shutdown routine */
|
||||
if (hdev->shutdown)
|
||||
err = hdev->shutdown(hdev);
|
||||
if (hdev->adv_instance_timeout) {
|
||||
cancel_delayed_work_sync(&hdev->adv_instance_expire);
|
||||
hdev->adv_instance_timeout = 0;
|
||||
}
|
||||
|
||||
err = hci_dev_shutdown(hdev);
|
||||
|
||||
if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
|
||||
cancel_delayed_work_sync(&hdev->cmd_timer);
|
||||
return err;
|
||||
@ -5023,7 +5371,7 @@ static int hci_active_scan_sync(struct hci_dev *hdev, uint16_t interval)
|
||||
/* Pause advertising since active scanning disables address resolution
|
||||
* which advertising depend on in order to generate its RPAs.
|
||||
*/
|
||||
if (use_ll_privacy(hdev)) {
|
||||
if (use_ll_privacy(hdev) && hci_dev_test_flag(hdev, HCI_PRIVACY)) {
|
||||
err = hci_pause_advertising_sync(hdev);
|
||||
if (err) {
|
||||
bt_dev_err(hdev, "pause advertising failed: %d", err);
|
||||
@ -5737,3 +6085,96 @@ int hci_le_pa_terminate_sync(struct hci_dev *hdev, u16 handle)
|
||||
return __hci_cmd_sync_status(hdev, HCI_OP_LE_PA_TERM_SYNC,
|
||||
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
}
|
||||
|
||||
int hci_get_random_address(struct hci_dev *hdev, bool require_privacy,
|
||||
bool use_rpa, struct adv_info *adv_instance,
|
||||
u8 *own_addr_type, bdaddr_t *rand_addr)
|
||||
{
|
||||
int err;
|
||||
|
||||
bacpy(rand_addr, BDADDR_ANY);
|
||||
|
||||
/* If privacy is enabled use a resolvable private address. If
|
||||
* current RPA has expired then generate a new one.
|
||||
*/
|
||||
if (use_rpa) {
|
||||
/* If Controller supports LL Privacy use own address type is
|
||||
* 0x03
|
||||
*/
|
||||
if (use_ll_privacy(hdev))
|
||||
*own_addr_type = ADDR_LE_DEV_RANDOM_RESOLVED;
|
||||
else
|
||||
*own_addr_type = ADDR_LE_DEV_RANDOM;
|
||||
|
||||
if (adv_instance) {
|
||||
if (adv_rpa_valid(adv_instance))
|
||||
return 0;
|
||||
} else {
|
||||
if (rpa_valid(hdev))
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "failed to generate new RPA");
|
||||
return err;
|
||||
}
|
||||
|
||||
bacpy(rand_addr, &hdev->rpa);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* In case of required privacy without resolvable private address,
|
||||
* use an non-resolvable private address. This is useful for
|
||||
* non-connectable advertising.
|
||||
*/
|
||||
if (require_privacy) {
|
||||
bdaddr_t nrpa;
|
||||
|
||||
while (true) {
|
||||
/* The non-resolvable private address is generated
|
||||
* from random six bytes with the two most significant
|
||||
* bits cleared.
|
||||
*/
|
||||
get_random_bytes(&nrpa, 6);
|
||||
nrpa.b[5] &= 0x3f;
|
||||
|
||||
/* The non-resolvable private address shall not be
|
||||
* equal to the public address.
|
||||
*/
|
||||
if (bacmp(&hdev->bdaddr, &nrpa))
|
||||
break;
|
||||
}
|
||||
|
||||
*own_addr_type = ADDR_LE_DEV_RANDOM;
|
||||
bacpy(rand_addr, &nrpa);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No privacy so use a public address. */
|
||||
*own_addr_type = ADDR_LE_DEV_PUBLIC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _update_adv_data_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
u8 instance = *(u8 *)data;
|
||||
|
||||
kfree(data);
|
||||
|
||||
return hci_update_adv_data_sync(hdev, instance);
|
||||
}
|
||||
|
||||
int hci_update_adv_data(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
u8 *inst_ptr = kmalloc(1, GFP_KERNEL);
|
||||
|
||||
if (!inst_ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
*inst_ptr = instance;
|
||||
return hci_cmd_sync_queue(hdev, _update_adv_data_sync, inst_ptr, NULL);
|
||||
}
|
||||
|
@ -48,6 +48,9 @@ void hci_conn_add_sysfs(struct hci_conn *conn)
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
if (device_is_registered(&conn->dev))
|
||||
return;
|
||||
|
||||
dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle);
|
||||
|
||||
if (device_add(&conn->dev) < 0) {
|
||||
|
@ -61,6 +61,9 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err);
|
||||
|
||||
static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
|
||||
struct sk_buff_head *skbs, u8 event);
|
||||
static void l2cap_retrans_timeout(struct work_struct *work);
|
||||
static void l2cap_monitor_timeout(struct work_struct *work);
|
||||
static void l2cap_ack_timeout(struct work_struct *work);
|
||||
|
||||
static inline u8 bdaddr_type(u8 link_type, u8 bdaddr_type)
|
||||
{
|
||||
@ -476,6 +479,9 @@ struct l2cap_chan *l2cap_chan_create(void)
|
||||
write_unlock(&chan_list_lock);
|
||||
|
||||
INIT_DELAYED_WORK(&chan->chan_timer, l2cap_chan_timeout);
|
||||
INIT_DELAYED_WORK(&chan->retrans_timer, l2cap_retrans_timeout);
|
||||
INIT_DELAYED_WORK(&chan->monitor_timer, l2cap_monitor_timeout);
|
||||
INIT_DELAYED_WORK(&chan->ack_timer, l2cap_ack_timeout);
|
||||
|
||||
chan->state = BT_OPEN;
|
||||
|
||||
@ -3320,10 +3326,6 @@ int l2cap_ertm_init(struct l2cap_chan *chan)
|
||||
chan->rx_state = L2CAP_RX_STATE_RECV;
|
||||
chan->tx_state = L2CAP_TX_STATE_XMIT;
|
||||
|
||||
INIT_DELAYED_WORK(&chan->retrans_timer, l2cap_retrans_timeout);
|
||||
INIT_DELAYED_WORK(&chan->monitor_timer, l2cap_monitor_timeout);
|
||||
INIT_DELAYED_WORK(&chan->ack_timer, l2cap_ack_timeout);
|
||||
|
||||
skb_queue_head_init(&chan->srej_q);
|
||||
|
||||
err = l2cap_seq_list_init(&chan->srej_list, chan->tx_win);
|
||||
@ -4307,6 +4309,12 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
|
||||
}
|
||||
}
|
||||
|
||||
chan = l2cap_chan_hold_unless_zero(chan);
|
||||
if (!chan) {
|
||||
err = -EBADSLT;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
l2cap_chan_lock(chan);
|
||||
@ -4336,6 +4344,7 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
|
||||
}
|
||||
|
||||
l2cap_chan_unlock(chan);
|
||||
l2cap_chan_put(chan);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
@ -129,6 +129,10 @@ static const u16 mgmt_commands[] = {
|
||||
MGMT_OP_ADD_EXT_ADV_PARAMS,
|
||||
MGMT_OP_ADD_EXT_ADV_DATA,
|
||||
MGMT_OP_ADD_ADV_PATTERNS_MONITOR_RSSI,
|
||||
MGMT_OP_SET_MESH_RECEIVER,
|
||||
MGMT_OP_MESH_READ_FEATURES,
|
||||
MGMT_OP_MESH_SEND,
|
||||
MGMT_OP_MESH_SEND_CANCEL,
|
||||
};
|
||||
|
||||
static const u16 mgmt_events[] = {
|
||||
@ -1048,9 +1052,66 @@ static void discov_off(struct work_struct *work)
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev);
|
||||
|
||||
static void mesh_send_complete(struct hci_dev *hdev,
|
||||
struct mgmt_mesh_tx *mesh_tx, bool silent)
|
||||
{
|
||||
u8 handle = mesh_tx->handle;
|
||||
|
||||
if (!silent)
|
||||
mgmt_event(MGMT_EV_MESH_PACKET_CMPLT, hdev, &handle,
|
||||
sizeof(handle), NULL);
|
||||
|
||||
mgmt_mesh_remove(mesh_tx);
|
||||
}
|
||||
|
||||
static int mesh_send_done_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_mesh_tx *mesh_tx;
|
||||
|
||||
hci_dev_clear_flag(hdev, HCI_MESH_SENDING);
|
||||
hci_disable_advertising_sync(hdev);
|
||||
mesh_tx = mgmt_mesh_next(hdev, NULL);
|
||||
|
||||
if (mesh_tx)
|
||||
mesh_send_complete(hdev, mesh_tx, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mesh_send_sync(struct hci_dev *hdev, void *data);
|
||||
static void mesh_send_start_complete(struct hci_dev *hdev, void *data, int err);
|
||||
static void mesh_next(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct mgmt_mesh_tx *mesh_tx = mgmt_mesh_next(hdev, NULL);
|
||||
|
||||
if (!mesh_tx)
|
||||
return;
|
||||
|
||||
err = hci_cmd_sync_queue(hdev, mesh_send_sync, mesh_tx,
|
||||
mesh_send_start_complete);
|
||||
|
||||
if (err < 0)
|
||||
mesh_send_complete(hdev, mesh_tx, false);
|
||||
else
|
||||
hci_dev_set_flag(hdev, HCI_MESH_SENDING);
|
||||
}
|
||||
|
||||
static void mesh_send_done(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
mesh_send_done.work);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_MESH_SENDING))
|
||||
return;
|
||||
|
||||
hci_cmd_sync_queue(hdev, mesh_send_done_sync, NULL, mesh_next);
|
||||
}
|
||||
|
||||
static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev)
|
||||
{
|
||||
if (hci_dev_test_and_set_flag(hdev, HCI_MGMT))
|
||||
if (hci_dev_test_flag(hdev, HCI_MGMT))
|
||||
return;
|
||||
|
||||
BT_INFO("MGMT ver %d.%d", MGMT_VERSION, MGMT_REVISION);
|
||||
@ -1058,6 +1119,7 @@ static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev)
|
||||
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);
|
||||
INIT_DELAYED_WORK(&hdev->mesh_send_done, mesh_send_done);
|
||||
|
||||
/* Non-mgmt controlled devices get this bit set
|
||||
* implicitly so that pairing works for them, however
|
||||
@ -1065,6 +1127,8 @@ static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev)
|
||||
* it
|
||||
*/
|
||||
hci_dev_clear_flag(hdev, HCI_BONDABLE);
|
||||
|
||||
hci_dev_set_flag(hdev, HCI_MGMT);
|
||||
}
|
||||
|
||||
static int read_controller_info(struct sock *sk, struct hci_dev *hdev,
|
||||
@ -2058,6 +2122,8 @@ static int set_le_sync(struct hci_dev *hdev, void *data)
|
||||
int err;
|
||||
|
||||
if (!val) {
|
||||
hci_clear_adv_instance_sync(hdev, NULL, 0x00, true);
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
|
||||
hci_disable_advertising_sync(hdev);
|
||||
|
||||
@ -2092,6 +2158,317 @@ static int set_le_sync(struct hci_dev *hdev, void *data)
|
||||
return err;
|
||||
}
|
||||
|
||||
static void set_mesh_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
u8 status = mgmt_status(err);
|
||||
struct sock *sk = cmd->sk;
|
||||
|
||||
if (status) {
|
||||
mgmt_pending_foreach(MGMT_OP_SET_MESH_RECEIVER, hdev,
|
||||
cmd_status_rsp, &status);
|
||||
return;
|
||||
}
|
||||
|
||||
mgmt_pending_remove(cmd);
|
||||
mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_MESH_RECEIVER, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static int set_mesh_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_cp_set_mesh *cp = cmd->param;
|
||||
size_t len = cmd->param_len;
|
||||
|
||||
memset(hdev->mesh_ad_types, 0, sizeof(hdev->mesh_ad_types));
|
||||
|
||||
if (cp->enable)
|
||||
hci_dev_set_flag(hdev, HCI_MESH);
|
||||
else
|
||||
hci_dev_clear_flag(hdev, HCI_MESH);
|
||||
|
||||
len -= sizeof(*cp);
|
||||
|
||||
/* If filters don't fit, forward all adv pkts */
|
||||
if (len <= sizeof(hdev->mesh_ad_types))
|
||||
memcpy(hdev->mesh_ad_types, cp->ad_types, len);
|
||||
|
||||
hci_update_passive_scan_sync(hdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_mesh(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
{
|
||||
struct mgmt_cp_set_mesh *cp = data;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
int err = 0;
|
||||
|
||||
bt_dev_dbg(hdev, "sock %p", sk);
|
||||
|
||||
if (!lmp_le_capable(hdev) ||
|
||||
!hci_dev_test_flag(hdev, HCI_MESH_EXPERIMENTAL))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_MESH_RECEIVER,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
if (cp->enable != 0x00 && cp->enable != 0x01)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_MESH_RECEIVER,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_SET_MESH_RECEIVER, hdev, data, len);
|
||||
if (!cmd)
|
||||
err = -ENOMEM;
|
||||
else
|
||||
err = hci_cmd_sync_queue(hdev, set_mesh_sync, cmd,
|
||||
set_mesh_complete);
|
||||
|
||||
if (err < 0) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_MESH_RECEIVER,
|
||||
MGMT_STATUS_FAILED);
|
||||
|
||||
if (cmd)
|
||||
mgmt_pending_remove(cmd);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mesh_send_start_complete(struct hci_dev *hdev, void *data, int err)
|
||||
{
|
||||
struct mgmt_mesh_tx *mesh_tx = data;
|
||||
struct mgmt_cp_mesh_send *send = (void *)mesh_tx->param;
|
||||
unsigned long mesh_send_interval;
|
||||
u8 mgmt_err = mgmt_status(err);
|
||||
|
||||
/* Report any errors here, but don't report completion */
|
||||
|
||||
if (mgmt_err) {
|
||||
hci_dev_clear_flag(hdev, HCI_MESH_SENDING);
|
||||
/* Send Complete Error Code for handle */
|
||||
mesh_send_complete(hdev, mesh_tx, false);
|
||||
return;
|
||||
}
|
||||
|
||||
mesh_send_interval = msecs_to_jiffies((send->cnt) * 25);
|
||||
queue_delayed_work(hdev->req_workqueue, &hdev->mesh_send_done,
|
||||
mesh_send_interval);
|
||||
}
|
||||
|
||||
static int mesh_send_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_mesh_tx *mesh_tx = data;
|
||||
struct mgmt_cp_mesh_send *send = (void *)mesh_tx->param;
|
||||
struct adv_info *adv, *next_instance;
|
||||
u8 instance = hdev->le_num_of_adv_sets + 1;
|
||||
u16 timeout, duration;
|
||||
int err = 0;
|
||||
|
||||
if (hdev->le_num_of_adv_sets <= hdev->adv_instance_cnt)
|
||||
return MGMT_STATUS_BUSY;
|
||||
|
||||
timeout = 1000;
|
||||
duration = send->cnt * INTERVAL_TO_MS(hdev->le_adv_max_interval);
|
||||
adv = hci_add_adv_instance(hdev, instance, 0,
|
||||
send->adv_data_len, send->adv_data,
|
||||
0, NULL,
|
||||
timeout, duration,
|
||||
HCI_ADV_TX_POWER_NO_PREFERENCE,
|
||||
hdev->le_adv_min_interval,
|
||||
hdev->le_adv_max_interval,
|
||||
mesh_tx->handle);
|
||||
|
||||
if (!IS_ERR(adv))
|
||||
mesh_tx->instance = instance;
|
||||
else
|
||||
err = PTR_ERR(adv);
|
||||
|
||||
if (hdev->cur_adv_instance == instance) {
|
||||
/* If the currently advertised instance is being changed then
|
||||
* cancel the current advertising and schedule the next
|
||||
* instance. If there is only one instance then the overridden
|
||||
* advertising data will be visible right away.
|
||||
*/
|
||||
cancel_adv_timeout(hdev);
|
||||
|
||||
next_instance = hci_get_next_instance(hdev, instance);
|
||||
if (next_instance)
|
||||
instance = next_instance->instance;
|
||||
else
|
||||
instance = 0;
|
||||
} else if (hdev->adv_instance_timeout) {
|
||||
/* Immediately advertise the new instance if no other, or
|
||||
* let it go naturally from queue if ADV is already happening
|
||||
*/
|
||||
instance = 0;
|
||||
}
|
||||
|
||||
if (instance)
|
||||
return hci_schedule_adv_instance_sync(hdev, instance, true);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void send_count(struct mgmt_mesh_tx *mesh_tx, void *data)
|
||||
{
|
||||
struct mgmt_rp_mesh_read_features *rp = data;
|
||||
|
||||
if (rp->used_handles >= rp->max_handles)
|
||||
return;
|
||||
|
||||
rp->handles[rp->used_handles++] = mesh_tx->handle;
|
||||
}
|
||||
|
||||
static int mesh_features(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 len)
|
||||
{
|
||||
struct mgmt_rp_mesh_read_features rp;
|
||||
|
||||
if (!lmp_le_capable(hdev) ||
|
||||
!hci_dev_test_flag(hdev, HCI_MESH_EXPERIMENTAL))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_READ_FEATURES,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
memset(&rp, 0, sizeof(rp));
|
||||
rp.index = cpu_to_le16(hdev->id);
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ENABLED))
|
||||
rp.max_handles = MESH_HANDLES_MAX;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (rp.max_handles)
|
||||
mgmt_mesh_foreach(hdev, send_count, &rp, sk);
|
||||
|
||||
mgmt_cmd_complete(sk, hdev->id, MGMT_OP_MESH_READ_FEATURES, 0, &rp,
|
||||
rp.used_handles + sizeof(rp) - MESH_HANDLES_MAX);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_cancel(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd = data;
|
||||
struct mgmt_cp_mesh_send_cancel *cancel = (void *)cmd->param;
|
||||
struct mgmt_mesh_tx *mesh_tx;
|
||||
|
||||
if (!cancel->handle) {
|
||||
do {
|
||||
mesh_tx = mgmt_mesh_next(hdev, cmd->sk);
|
||||
|
||||
if (mesh_tx)
|
||||
mesh_send_complete(hdev, mesh_tx, false);
|
||||
} while (mesh_tx);
|
||||
} else {
|
||||
mesh_tx = mgmt_mesh_find(hdev, cancel->handle);
|
||||
|
||||
if (mesh_tx && mesh_tx->sk == cmd->sk)
|
||||
mesh_send_complete(hdev, mesh_tx, false);
|
||||
}
|
||||
|
||||
mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_MESH_SEND_CANCEL,
|
||||
0, NULL, 0);
|
||||
mgmt_pending_free(cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mesh_send_cancel(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 len)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
int err;
|
||||
|
||||
if (!lmp_le_capable(hdev) ||
|
||||
!hci_dev_test_flag(hdev, HCI_MESH_EXPERIMENTAL))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND_CANCEL,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND_CANCEL,
|
||||
MGMT_STATUS_REJECTED);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
cmd = mgmt_pending_new(sk, MGMT_OP_MESH_SEND_CANCEL, hdev, data, len);
|
||||
if (!cmd)
|
||||
err = -ENOMEM;
|
||||
else
|
||||
err = hci_cmd_sync_queue(hdev, send_cancel, cmd, NULL);
|
||||
|
||||
if (err < 0) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND_CANCEL,
|
||||
MGMT_STATUS_FAILED);
|
||||
|
||||
if (cmd)
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mesh_send(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
{
|
||||
struct mgmt_mesh_tx *mesh_tx;
|
||||
struct mgmt_cp_mesh_send *send = data;
|
||||
struct mgmt_rp_mesh_read_features rp;
|
||||
bool sending;
|
||||
int err = 0;
|
||||
|
||||
if (!lmp_le_capable(hdev) ||
|
||||
!hci_dev_test_flag(hdev, HCI_MESH_EXPERIMENTAL))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND,
|
||||
MGMT_STATUS_NOT_SUPPORTED);
|
||||
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED) ||
|
||||
len <= MGMT_MESH_SEND_SIZE ||
|
||||
len > (MGMT_MESH_SEND_SIZE + 31))
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND,
|
||||
MGMT_STATUS_REJECTED);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
memset(&rp, 0, sizeof(rp));
|
||||
rp.max_handles = MESH_HANDLES_MAX;
|
||||
|
||||
mgmt_mesh_foreach(hdev, send_count, &rp, sk);
|
||||
|
||||
if (rp.max_handles <= rp.used_handles) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND,
|
||||
MGMT_STATUS_BUSY);
|
||||
goto done;
|
||||
}
|
||||
|
||||
sending = hci_dev_test_flag(hdev, HCI_MESH_SENDING);
|
||||
mesh_tx = mgmt_mesh_add(sk, hdev, send, len);
|
||||
|
||||
if (!mesh_tx)
|
||||
err = -ENOMEM;
|
||||
else if (!sending)
|
||||
err = hci_cmd_sync_queue(hdev, mesh_send_sync, mesh_tx,
|
||||
mesh_send_start_complete);
|
||||
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "Send Mesh Failed %d", err);
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND,
|
||||
MGMT_STATUS_FAILED);
|
||||
|
||||
if (mesh_tx) {
|
||||
if (sending)
|
||||
mgmt_mesh_remove(mesh_tx);
|
||||
}
|
||||
} else {
|
||||
hci_dev_set_flag(hdev, HCI_MESH_SENDING);
|
||||
|
||||
mgmt_cmd_complete(sk, hdev->id, MGMT_OP_MESH_SEND, 0,
|
||||
&mesh_tx->handle, 1);
|
||||
}
|
||||
|
||||
done:
|
||||
hci_dev_unlock(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
{
|
||||
struct mgmt_mode *cp = data;
|
||||
@ -2131,9 +2508,6 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
val = !!cp->val;
|
||||
enabled = lmp_host_le_capable(hdev);
|
||||
|
||||
if (!val)
|
||||
hci_req_clear_adv_instance(hdev, NULL, NULL, 0x00, true);
|
||||
|
||||
if (!hdev_is_powered(hdev) || val == enabled) {
|
||||
bool changed = false;
|
||||
|
||||
@ -3186,6 +3560,18 @@ unlock:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int abort_conn_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
u16 handle = PTR_ERR(data);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!conn)
|
||||
return 0;
|
||||
|
||||
return hci_abort_conn_sync(hdev, conn, HCI_ERROR_REMOTE_USER_TERM);
|
||||
}
|
||||
|
||||
static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
u16 len)
|
||||
{
|
||||
@ -3236,7 +3622,8 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
le_addr_type(addr->type));
|
||||
|
||||
if (conn->conn_reason == CONN_REASON_PAIR_DEVICE)
|
||||
hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
|
||||
hci_cmd_sync_queue(hdev, abort_conn_sync, ERR_PTR(conn->handle),
|
||||
NULL);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
@ -3991,17 +4378,28 @@ static const u8 iso_socket_uuid[16] = {
|
||||
0x6a, 0x49, 0xe0, 0x05, 0x88, 0xf1, 0xba, 0x6f,
|
||||
};
|
||||
|
||||
/* 2ce463d7-7a03-4d8d-bf05-5f24e8f36e76 */
|
||||
static const u8 mgmt_mesh_uuid[16] = {
|
||||
0x76, 0x6e, 0xf3, 0xe8, 0x24, 0x5f, 0x05, 0xbf,
|
||||
0x8d, 0x4d, 0x03, 0x7a, 0xd7, 0x63, 0xe4, 0x2c,
|
||||
};
|
||||
|
||||
static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 data_len)
|
||||
{
|
||||
char buf[122]; /* Enough space for 6 features: 2 + 20 * 6 */
|
||||
struct mgmt_rp_read_exp_features_info *rp = (void *)buf;
|
||||
struct mgmt_rp_read_exp_features_info *rp;
|
||||
size_t len;
|
||||
u16 idx = 0;
|
||||
u32 flags;
|
||||
int status;
|
||||
|
||||
bt_dev_dbg(hdev, "sock %p", sk);
|
||||
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
/* Enough space for 7 features */
|
||||
len = sizeof(*rp) + (sizeof(rp->features[0]) * 7);
|
||||
rp = kzalloc(len, GFP_KERNEL);
|
||||
if (!rp)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef CONFIG_BT_FEATURE_DEBUG
|
||||
if (!hdev) {
|
||||
@ -4065,6 +4463,17 @@ static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (hdev && lmp_le_capable(hdev)) {
|
||||
if (hci_dev_test_flag(hdev, HCI_MESH_EXPERIMENTAL))
|
||||
flags = BIT(0);
|
||||
else
|
||||
flags = 0;
|
||||
|
||||
memcpy(rp->features[idx].uuid, mgmt_mesh_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
|
||||
@ -4072,9 +4481,12 @@ static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
|
||||
*/
|
||||
hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
|
||||
|
||||
return mgmt_cmd_complete(sk, hdev ? hdev->id : MGMT_INDEX_NONE,
|
||||
MGMT_OP_READ_EXP_FEATURES_INFO,
|
||||
0, rp, sizeof(*rp) + (20 * idx));
|
||||
status = mgmt_cmd_complete(sk, hdev ? hdev->id : MGMT_INDEX_NONE,
|
||||
MGMT_OP_READ_EXP_FEATURES_INFO,
|
||||
0, rp, sizeof(*rp) + (20 * idx));
|
||||
|
||||
kfree(rp);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int exp_ll_privacy_feature_changed(bool enabled, struct hci_dev *hdev,
|
||||
@ -4202,6 +4614,63 @@ static int set_debug_func(struct sock *sk, struct hci_dev *hdev,
|
||||
}
|
||||
#endif
|
||||
|
||||
static int set_mgmt_mesh_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;
|
||||
int err;
|
||||
|
||||
/* Command requires to use the controller index */
|
||||
if (!hdev)
|
||||
return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
|
||||
MGMT_OP_SET_EXP_FEATURE,
|
||||
MGMT_STATUS_INVALID_INDEX);
|
||||
|
||||
/* Changes can only be made when controller is powered down */
|
||||
if (hdev_is_powered(hdev))
|
||||
return mgmt_cmd_status(sk, hdev->id,
|
||||
MGMT_OP_SET_EXP_FEATURE,
|
||||
MGMT_STATUS_REJECTED);
|
||||
|
||||
/* Parameters are limited to a single octet */
|
||||
if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
|
||||
return mgmt_cmd_status(sk, hdev->id,
|
||||
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, hdev->id,
|
||||
MGMT_OP_SET_EXP_FEATURE,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
val = !!cp->param[0];
|
||||
|
||||
if (val) {
|
||||
changed = !hci_dev_test_and_set_flag(hdev,
|
||||
HCI_MESH_EXPERIMENTAL);
|
||||
} else {
|
||||
hci_dev_clear_flag(hdev, HCI_MESH);
|
||||
changed = hci_dev_test_and_clear_flag(hdev,
|
||||
HCI_MESH_EXPERIMENTAL);
|
||||
}
|
||||
|
||||
memcpy(rp.uuid, mgmt_mesh_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, hdev->id,
|
||||
MGMT_OP_SET_EXP_FEATURE, 0,
|
||||
&rp, sizeof(rp));
|
||||
|
||||
if (changed)
|
||||
exp_feature_changed(hdev, mgmt_mesh_uuid, val, sk);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int set_rpa_resolution_func(struct sock *sk, struct hci_dev *hdev,
|
||||
struct mgmt_cp_set_exp_feature *cp,
|
||||
u16 data_len)
|
||||
@ -4517,6 +4986,7 @@ static const struct mgmt_exp_feature {
|
||||
#ifdef CONFIG_BT_FEATURE_DEBUG
|
||||
EXP_FEAT(debug_uuid, set_debug_func),
|
||||
#endif
|
||||
EXP_FEAT(mgmt_mesh_uuid, set_mgmt_mesh_func),
|
||||
EXP_FEAT(rpa_resolution_uuid, set_rpa_resolution_func),
|
||||
EXP_FEAT(quality_report_uuid, set_quality_report_func),
|
||||
EXP_FEAT(offload_codecs_uuid, set_offload_codec_func),
|
||||
@ -5981,6 +6451,7 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
if (!hdev_is_powered(hdev) ||
|
||||
(val == hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
|
||||
(cp->val == 0x02) == hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE)) ||
|
||||
hci_dev_test_flag(hdev, HCI_MESH) ||
|
||||
hci_conn_num(hdev, LE_LINK) > 0 ||
|
||||
(hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
|
||||
hdev->le_scan_type == LE_SCAN_ACTIVE)) {
|
||||
@ -7909,8 +8380,7 @@ static u32 get_supported_adv_flags(struct hci_dev *hdev)
|
||||
/* In extended adv TX_POWER returned from Set Adv Param
|
||||
* will be always valid.
|
||||
*/
|
||||
if ((hdev->adv_tx_power != HCI_TX_POWER_INVALID) ||
|
||||
ext_adv_capable(hdev))
|
||||
if (hdev->adv_tx_power != HCI_TX_POWER_INVALID || ext_adv_capable(hdev))
|
||||
flags |= MGMT_ADV_FLAG_TX_POWER;
|
||||
|
||||
if (ext_adv_capable(hdev)) {
|
||||
@ -7963,8 +8433,14 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
|
||||
|
||||
instance = rp->instance;
|
||||
list_for_each_entry(adv_instance, &hdev->adv_instances, list) {
|
||||
*instance = adv_instance->instance;
|
||||
instance++;
|
||||
/* Only instances 1-le_num_of_adv_sets are externally visible */
|
||||
if (adv_instance->instance <= hdev->adv_instance_cnt) {
|
||||
*instance = adv_instance->instance;
|
||||
instance++;
|
||||
} else {
|
||||
rp->num_instances--;
|
||||
rp_len--;
|
||||
}
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
@ -8226,7 +8702,7 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
timeout, duration,
|
||||
HCI_ADV_TX_POWER_NO_PREFERENCE,
|
||||
hdev->le_adv_min_interval,
|
||||
hdev->le_adv_max_interval);
|
||||
hdev->le_adv_max_interval, 0);
|
||||
if (IS_ERR(adv)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
MGMT_STATUS_FAILED);
|
||||
@ -8430,7 +8906,7 @@ static int add_ext_adv_params(struct sock *sk, struct hci_dev *hdev,
|
||||
/* Create advertising instance with no advertising or response data */
|
||||
adv = hci_add_adv_instance(hdev, cp->instance, flags, 0, NULL, 0, NULL,
|
||||
timeout, duration, tx_power, min_interval,
|
||||
max_interval);
|
||||
max_interval, 0);
|
||||
|
||||
if (IS_ERR(adv)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_EXT_ADV_PARAMS,
|
||||
@ -8876,8 +9352,13 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
|
||||
{ add_ext_adv_data, MGMT_ADD_EXT_ADV_DATA_SIZE,
|
||||
HCI_MGMT_VAR_LEN },
|
||||
{ add_adv_patterns_monitor_rssi,
|
||||
MGMT_ADD_ADV_PATTERNS_MONITOR_RSSI_SIZE,
|
||||
MGMT_ADD_ADV_PATTERNS_MONITOR_RSSI_SIZE },
|
||||
{ set_mesh, MGMT_SET_MESH_RECEIVER_SIZE,
|
||||
HCI_MGMT_VAR_LEN },
|
||||
{ mesh_features, MGMT_MESH_READ_FEATURES_SIZE },
|
||||
{ mesh_send, MGMT_MESH_SEND_SIZE,
|
||||
HCI_MGMT_VAR_LEN },
|
||||
{ mesh_send_cancel, MGMT_MESH_SEND_CANCEL_SIZE },
|
||||
};
|
||||
|
||||
void mgmt_index_added(struct hci_dev *hdev)
|
||||
@ -9817,14 +10298,86 @@ static void mgmt_adv_monitor_device_found(struct hci_dev *hdev,
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static void mesh_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 addr_type, s8 rssi, u32 flags, u8 *eir,
|
||||
u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len,
|
||||
u64 instant)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct mgmt_ev_mesh_device_found *ev;
|
||||
int i, j;
|
||||
|
||||
if (!hdev->mesh_ad_types[0])
|
||||
goto accepted;
|
||||
|
||||
/* Scan for requested AD types */
|
||||
if (eir_len > 0) {
|
||||
for (i = 0; i + 1 < eir_len; i += eir[i] + 1) {
|
||||
for (j = 0; j < sizeof(hdev->mesh_ad_types); j++) {
|
||||
if (!hdev->mesh_ad_types[j])
|
||||
break;
|
||||
|
||||
if (hdev->mesh_ad_types[j] == eir[i + 1])
|
||||
goto accepted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scan_rsp_len > 0) {
|
||||
for (i = 0; i + 1 < scan_rsp_len; i += scan_rsp[i] + 1) {
|
||||
for (j = 0; j < sizeof(hdev->mesh_ad_types); j++) {
|
||||
if (!hdev->mesh_ad_types[j])
|
||||
break;
|
||||
|
||||
if (hdev->mesh_ad_types[j] == scan_rsp[i + 1])
|
||||
goto accepted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
accepted:
|
||||
skb = mgmt_alloc_skb(hdev, MGMT_EV_MESH_DEVICE_FOUND,
|
||||
sizeof(*ev) + eir_len + scan_rsp_len);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
ev = skb_put(skb, sizeof(*ev));
|
||||
|
||||
bacpy(&ev->addr.bdaddr, bdaddr);
|
||||
ev->addr.type = link_to_bdaddr(LE_LINK, addr_type);
|
||||
ev->rssi = rssi;
|
||||
ev->flags = cpu_to_le32(flags);
|
||||
ev->instant = cpu_to_le64(instant);
|
||||
|
||||
if (eir_len > 0)
|
||||
/* Copy EIR or advertising data into event */
|
||||
skb_put_data(skb, eir, eir_len);
|
||||
|
||||
if (scan_rsp_len > 0)
|
||||
/* Append scan response data to event */
|
||||
skb_put_data(skb, scan_rsp, scan_rsp_len);
|
||||
|
||||
ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len);
|
||||
|
||||
mgmt_event_skb(skb, NULL);
|
||||
}
|
||||
|
||||
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
||||
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
|
||||
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len)
|
||||
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len,
|
||||
u64 instant)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct mgmt_ev_device_found *ev;
|
||||
bool report_device = hci_discovery_active(hdev);
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_MESH) && link_type == LE_LINK)
|
||||
mesh_device_found(hdev, bdaddr, addr_type, rssi, flags,
|
||||
eir, eir_len, scan_rsp, scan_rsp_len,
|
||||
instant);
|
||||
|
||||
/* Don't send events for a non-kernel initiated discovery. With
|
||||
* LE one exception is if we have pend_le_reports > 0 in which
|
||||
* case we're doing passive scanning and want these events.
|
||||
@ -9983,3 +10536,22 @@ void mgmt_exit(void)
|
||||
{
|
||||
hci_mgmt_chan_unregister(&chan);
|
||||
}
|
||||
|
||||
void mgmt_cleanup(struct sock *sk)
|
||||
{
|
||||
struct mgmt_mesh_tx *mesh_tx;
|
||||
struct hci_dev *hdev;
|
||||
|
||||
read_lock(&hci_dev_list_lock);
|
||||
|
||||
list_for_each_entry(hdev, &hci_dev_list, list) {
|
||||
do {
|
||||
mesh_tx = mgmt_mesh_next(hdev, sk);
|
||||
|
||||
if (mesh_tx)
|
||||
mesh_send_complete(hdev, mesh_tx, true);
|
||||
} while (mesh_tx);
|
||||
}
|
||||
|
||||
read_unlock(&hci_dev_list_lock);
|
||||
}
|
||||
|
@ -314,3 +314,77 @@ void mgmt_pending_remove(struct mgmt_pending_cmd *cmd)
|
||||
list_del(&cmd->list);
|
||||
mgmt_pending_free(cmd);
|
||||
}
|
||||
|
||||
void mgmt_mesh_foreach(struct hci_dev *hdev,
|
||||
void (*cb)(struct mgmt_mesh_tx *mesh_tx, void *data),
|
||||
void *data, struct sock *sk)
|
||||
{
|
||||
struct mgmt_mesh_tx *mesh_tx, *tmp;
|
||||
|
||||
list_for_each_entry_safe(mesh_tx, tmp, &hdev->mgmt_pending, list) {
|
||||
if (!sk || mesh_tx->sk == sk)
|
||||
cb(mesh_tx, data);
|
||||
}
|
||||
}
|
||||
|
||||
struct mgmt_mesh_tx *mgmt_mesh_next(struct hci_dev *hdev, struct sock *sk)
|
||||
{
|
||||
struct mgmt_mesh_tx *mesh_tx;
|
||||
|
||||
if (list_empty(&hdev->mesh_pending))
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(mesh_tx, &hdev->mesh_pending, list) {
|
||||
if (!sk || mesh_tx->sk == sk)
|
||||
return mesh_tx;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mgmt_mesh_tx *mgmt_mesh_find(struct hci_dev *hdev, u8 handle)
|
||||
{
|
||||
struct mgmt_mesh_tx *mesh_tx;
|
||||
|
||||
if (list_empty(&hdev->mesh_pending))
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(mesh_tx, &hdev->mesh_pending, list) {
|
||||
if (mesh_tx->handle == handle)
|
||||
return mesh_tx;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mgmt_mesh_tx *mgmt_mesh_add(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 len)
|
||||
{
|
||||
struct mgmt_mesh_tx *mesh_tx;
|
||||
|
||||
mesh_tx = kzalloc(sizeof(*mesh_tx), GFP_KERNEL);
|
||||
if (!mesh_tx)
|
||||
return NULL;
|
||||
|
||||
hdev->mesh_send_ref++;
|
||||
if (!hdev->mesh_send_ref)
|
||||
hdev->mesh_send_ref++;
|
||||
|
||||
mesh_tx->handle = hdev->mesh_send_ref;
|
||||
mesh_tx->index = hdev->id;
|
||||
memcpy(mesh_tx->param, data, len);
|
||||
mesh_tx->param_len = len;
|
||||
mesh_tx->sk = sk;
|
||||
sock_hold(sk);
|
||||
|
||||
list_add_tail(&mesh_tx->list, &hdev->mesh_pending);
|
||||
|
||||
return mesh_tx;
|
||||
}
|
||||
|
||||
void mgmt_mesh_remove(struct mgmt_mesh_tx *mesh_tx)
|
||||
{
|
||||
list_del(&mesh_tx->list);
|
||||
sock_put(mesh_tx->sk);
|
||||
kfree(mesh_tx);
|
||||
}
|
||||
|
@ -20,6 +20,16 @@
|
||||
SOFTWARE IS DISCLAIMED.
|
||||
*/
|
||||
|
||||
struct mgmt_mesh_tx {
|
||||
struct list_head list;
|
||||
int index;
|
||||
size_t param_len;
|
||||
struct sock *sk;
|
||||
u8 handle;
|
||||
u8 instance;
|
||||
u8 param[sizeof(struct mgmt_cp_mesh_send) + 29];
|
||||
};
|
||||
|
||||
struct mgmt_pending_cmd {
|
||||
struct list_head list;
|
||||
u16 opcode;
|
||||
@ -59,3 +69,11 @@ struct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
|
||||
void *data, u16 len);
|
||||
void mgmt_pending_free(struct mgmt_pending_cmd *cmd);
|
||||
void mgmt_pending_remove(struct mgmt_pending_cmd *cmd);
|
||||
void mgmt_mesh_foreach(struct hci_dev *hdev,
|
||||
void (*cb)(struct mgmt_mesh_tx *mesh_tx, void *data),
|
||||
void *data, struct sock *sk);
|
||||
struct mgmt_mesh_tx *mgmt_mesh_find(struct hci_dev *hdev, u8 handle);
|
||||
struct mgmt_mesh_tx *mgmt_mesh_next(struct hci_dev *hdev, struct sock *sk);
|
||||
struct mgmt_mesh_tx *mgmt_mesh_add(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 len);
|
||||
void mgmt_mesh_remove(struct mgmt_mesh_tx *mesh_tx);
|
||||
|
@ -902,7 +902,10 @@ static int rfcomm_sock_shutdown(struct socket *sock, int how)
|
||||
lock_sock(sk);
|
||||
if (!sk->sk_shutdown) {
|
||||
sk->sk_shutdown = SHUTDOWN_MASK;
|
||||
|
||||
release_sock(sk);
|
||||
__rfcomm_sock_close(sk);
|
||||
lock_sock(sk);
|
||||
|
||||
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime &&
|
||||
!(current->flags & PF_EXITING))
|
||||
|
Loading…
Reference in New Issue
Block a user