mirror of
https://github.com/rd-stuffs/msm-4.14.git
synced 2025-02-20 11:45:48 +08:00
Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
This commit is contained in:
commit
9fbdc75116
@ -1619,6 +1619,7 @@ static struct usb_driver btusb_driver = {
|
|||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
.suspend = btusb_suspend,
|
.suspend = btusb_suspend,
|
||||||
.resume = btusb_resume,
|
.resume = btusb_resume,
|
||||||
|
.reset_resume = btusb_resume,
|
||||||
#endif
|
#endif
|
||||||
.id_table = btusb_table,
|
.id_table = btusb_table,
|
||||||
.supports_autosuspend = 1,
|
.supports_autosuspend = 1,
|
||||||
|
@ -107,7 +107,6 @@ enum {
|
|||||||
HCI_MGMT,
|
HCI_MGMT,
|
||||||
HCI_PAIRABLE,
|
HCI_PAIRABLE,
|
||||||
HCI_SERVICE_CACHE,
|
HCI_SERVICE_CACHE,
|
||||||
HCI_LINK_KEYS,
|
|
||||||
HCI_DEBUG_KEYS,
|
HCI_DEBUG_KEYS,
|
||||||
HCI_UNREGISTER,
|
HCI_UNREGISTER,
|
||||||
|
|
||||||
|
@ -117,13 +117,6 @@ struct oob_data {
|
|||||||
u8 randomizer[16];
|
u8 randomizer[16];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct le_scan_params {
|
|
||||||
u8 type;
|
|
||||||
u16 interval;
|
|
||||||
u16 window;
|
|
||||||
int timeout;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define HCI_MAX_SHORT_NAME_LENGTH 10
|
#define HCI_MAX_SHORT_NAME_LENGTH 10
|
||||||
|
|
||||||
struct amp_assoc {
|
struct amp_assoc {
|
||||||
@ -283,9 +276,6 @@ struct hci_dev {
|
|||||||
|
|
||||||
struct delayed_work le_scan_disable;
|
struct delayed_work le_scan_disable;
|
||||||
|
|
||||||
struct work_struct le_scan;
|
|
||||||
struct le_scan_params le_scan_params;
|
|
||||||
|
|
||||||
__s8 adv_tx_power;
|
__s8 adv_tx_power;
|
||||||
__u8 adv_data[HCI_MAX_AD_LENGTH];
|
__u8 adv_data[HCI_MAX_AD_LENGTH];
|
||||||
__u8 adv_data_len;
|
__u8 adv_data_len;
|
||||||
@ -432,6 +422,7 @@ void hci_inquiry_cache_update_resolve(struct hci_dev *hdev,
|
|||||||
struct inquiry_entry *ie);
|
struct inquiry_entry *ie);
|
||||||
bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
|
bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
|
||||||
bool name_known, bool *ssp);
|
bool name_known, bool *ssp);
|
||||||
|
void hci_inquiry_cache_flush(struct hci_dev *hdev);
|
||||||
|
|
||||||
/* ----- HCI Connections ----- */
|
/* ----- HCI Connections ----- */
|
||||||
enum {
|
enum {
|
||||||
@ -1114,6 +1105,16 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event);
|
|||||||
BIT(BDADDR_LE_PUBLIC) | \
|
BIT(BDADDR_LE_PUBLIC) | \
|
||||||
BIT(BDADDR_LE_RANDOM))
|
BIT(BDADDR_LE_RANDOM))
|
||||||
|
|
||||||
|
/* These LE scan and inquiry parameters were chosen according to LE General
|
||||||
|
* Discovery Procedure specification.
|
||||||
|
*/
|
||||||
|
#define DISCOV_LE_SCAN_WIN 0x12
|
||||||
|
#define DISCOV_LE_SCAN_INT 0x12
|
||||||
|
#define DISCOV_LE_TIMEOUT msecs_to_jiffies(10240)
|
||||||
|
#define DISCOV_INTERLEAVED_TIMEOUT msecs_to_jiffies(5120)
|
||||||
|
#define DISCOV_INTERLEAVED_INQUIRY_LEN 0x04
|
||||||
|
#define DISCOV_BREDR_INQUIRY_LEN 0x08
|
||||||
|
|
||||||
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len);
|
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len);
|
||||||
int mgmt_index_added(struct hci_dev *hdev);
|
int mgmt_index_added(struct hci_dev *hdev);
|
||||||
int mgmt_index_removed(struct hci_dev *hdev);
|
int mgmt_index_removed(struct hci_dev *hdev);
|
||||||
@ -1169,10 +1170,7 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
|||||||
u8 ssp, u8 *eir, u16 eir_len);
|
u8 ssp, u8 *eir, u16 eir_len);
|
||||||
int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
||||||
u8 addr_type, s8 rssi, u8 *name, u8 name_len);
|
u8 addr_type, s8 rssi, u8 *name, u8 name_len);
|
||||||
int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status);
|
|
||||||
int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status);
|
|
||||||
int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
|
int mgmt_discovering(struct hci_dev *hdev, u8 discovering);
|
||||||
int mgmt_interleaved_discovery(struct hci_dev *hdev);
|
|
||||||
int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
|
int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
|
||||||
int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
|
int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type);
|
||||||
bool mgmt_valid_hdev(struct hci_dev *hdev);
|
bool mgmt_valid_hdev(struct hci_dev *hdev);
|
||||||
@ -1212,11 +1210,6 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
|
|||||||
u16 latency, u16 to_multiplier);
|
u16 latency, u16 to_multiplier);
|
||||||
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
|
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
|
||||||
__u8 ltk[16]);
|
__u8 ltk[16]);
|
||||||
int hci_do_inquiry(struct hci_dev *hdev, u8 length);
|
|
||||||
int hci_cancel_inquiry(struct hci_dev *hdev);
|
|
||||||
int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window,
|
|
||||||
int timeout);
|
|
||||||
int hci_cancel_le_scan(struct hci_dev *hdev);
|
|
||||||
|
|
||||||
u8 bdaddr_to_le(u8 bdaddr_type);
|
u8 bdaddr_to_le(u8 bdaddr_type);
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ struct l2cap_conn_rsp {
|
|||||||
#define L2CAP_CID_SIGNALING 0x0001
|
#define L2CAP_CID_SIGNALING 0x0001
|
||||||
#define L2CAP_CID_CONN_LESS 0x0002
|
#define L2CAP_CID_CONN_LESS 0x0002
|
||||||
#define L2CAP_CID_A2MP 0x0003
|
#define L2CAP_CID_A2MP 0x0003
|
||||||
#define L2CAP_CID_LE_DATA 0x0004
|
#define L2CAP_CID_ATT 0x0004
|
||||||
#define L2CAP_CID_LE_SIGNALING 0x0005
|
#define L2CAP_CID_LE_SIGNALING 0x0005
|
||||||
#define L2CAP_CID_SMP 0x0006
|
#define L2CAP_CID_SMP 0x0006
|
||||||
#define L2CAP_CID_DYN_START 0x0040
|
#define L2CAP_CID_DYN_START 0x0040
|
||||||
|
@ -597,7 +597,15 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
|
|||||||
struct hci_dev *hdev = req->hdev;
|
struct hci_dev *hdev = req->hdev;
|
||||||
u8 p;
|
u8 p;
|
||||||
|
|
||||||
/* Only send HCI_Delete_Stored_Link_Key if it is supported */
|
/* Some Broadcom based Bluetooth controllers do not support the
|
||||||
|
* Delete Stored Link Key command. They are clearly indicating its
|
||||||
|
* absence in the bit mask of supported commands.
|
||||||
|
*
|
||||||
|
* Check the supported commands and only if the the command is marked
|
||||||
|
* as supported send it. If not supported assume that the controller
|
||||||
|
* does not have actual support for stored link keys which makes this
|
||||||
|
* command redundant anyway.
|
||||||
|
*/
|
||||||
if (hdev->commands[6] & 0x80) {
|
if (hdev->commands[6] & 0x80) {
|
||||||
struct hci_cp_delete_stored_link_key cp;
|
struct hci_cp_delete_stored_link_key cp;
|
||||||
|
|
||||||
@ -751,7 +759,7 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state)
|
|||||||
hdev->discovery.state = state;
|
hdev->discovery.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inquiry_cache_flush(struct hci_dev *hdev)
|
void hci_inquiry_cache_flush(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
struct discovery_state *cache = &hdev->discovery;
|
struct discovery_state *cache = &hdev->discovery;
|
||||||
struct inquiry_entry *p, *n;
|
struct inquiry_entry *p, *n;
|
||||||
@ -964,7 +972,7 @@ int hci_inquiry(void __user *arg)
|
|||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
|
if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX ||
|
||||||
inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) {
|
inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) {
|
||||||
inquiry_cache_flush(hdev);
|
hci_inquiry_cache_flush(hdev);
|
||||||
do_inquiry = 1;
|
do_inquiry = 1;
|
||||||
}
|
}
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
@ -1201,8 +1209,6 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
|||||||
{
|
{
|
||||||
BT_DBG("%s %p", hdev->name, hdev);
|
BT_DBG("%s %p", hdev->name, hdev);
|
||||||
|
|
||||||
cancel_work_sync(&hdev->le_scan);
|
|
||||||
|
|
||||||
cancel_delayed_work(&hdev->power_off);
|
cancel_delayed_work(&hdev->power_off);
|
||||||
|
|
||||||
hci_req_cancel(hdev, ENODEV);
|
hci_req_cancel(hdev, ENODEV);
|
||||||
@ -1230,7 +1236,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
|||||||
cancel_delayed_work_sync(&hdev->le_scan_disable);
|
cancel_delayed_work_sync(&hdev->le_scan_disable);
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
inquiry_cache_flush(hdev);
|
hci_inquiry_cache_flush(hdev);
|
||||||
hci_conn_hash_flush(hdev);
|
hci_conn_hash_flush(hdev);
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
|
|
||||||
@ -1331,7 +1337,7 @@ int hci_dev_reset(__u16 dev)
|
|||||||
skb_queue_purge(&hdev->cmd_q);
|
skb_queue_purge(&hdev->cmd_q);
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
inquiry_cache_flush(hdev);
|
hci_inquiry_cache_flush(hdev);
|
||||||
hci_conn_hash_flush(hdev);
|
hci_conn_hash_flush(hdev);
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
|
|
||||||
@ -1991,80 +1997,59 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
|
|||||||
return mgmt_device_unblocked(hdev, bdaddr, type);
|
return mgmt_device_unblocked(hdev, bdaddr, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void le_scan_param_req(struct hci_request *req, unsigned long opt)
|
static void inquiry_complete(struct hci_dev *hdev, u8 status)
|
||||||
{
|
{
|
||||||
struct le_scan_params *param = (struct le_scan_params *) opt;
|
if (status) {
|
||||||
struct hci_cp_le_set_scan_param cp;
|
BT_ERR("Failed to start inquiry: status %d", status);
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
hci_dev_lock(hdev);
|
||||||
cp.type = param->type;
|
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||||
cp.interval = cpu_to_le16(param->interval);
|
hci_dev_unlock(hdev);
|
||||||
cp.window = cpu_to_le16(param->window);
|
return;
|
||||||
|
}
|
||||||
hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(cp), &cp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void le_scan_enable_req(struct hci_request *req, unsigned long opt)
|
static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status)
|
||||||
{
|
{
|
||||||
struct hci_cp_le_set_scan_enable cp;
|
/* General inquiry access code (GIAC) */
|
||||||
|
u8 lap[3] = { 0x33, 0x8b, 0x9e };
|
||||||
memset(&cp, 0, sizeof(cp));
|
struct hci_request req;
|
||||||
cp.enable = LE_SCAN_ENABLE;
|
struct hci_cp_inquiry cp;
|
||||||
cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
|
|
||||||
|
|
||||||
hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval,
|
|
||||||
u16 window, int timeout)
|
|
||||||
{
|
|
||||||
long timeo = msecs_to_jiffies(3000);
|
|
||||||
struct le_scan_params param;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
if (status) {
|
||||||
|
BT_ERR("Failed to disable LE scanning: status %d", status);
|
||||||
if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
|
return;
|
||||||
return -EINPROGRESS;
|
|
||||||
|
|
||||||
param.type = type;
|
|
||||||
param.interval = interval;
|
|
||||||
param.window = window;
|
|
||||||
|
|
||||||
hci_req_lock(hdev);
|
|
||||||
|
|
||||||
err = __hci_req_sync(hdev, le_scan_param_req, (unsigned long) ¶m,
|
|
||||||
timeo);
|
|
||||||
if (!err)
|
|
||||||
err = __hci_req_sync(hdev, le_scan_enable_req, 0, timeo);
|
|
||||||
|
|
||||||
hci_req_unlock(hdev);
|
|
||||||
|
|
||||||
if (err < 0)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
|
|
||||||
timeout);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hci_cancel_le_scan(struct hci_dev *hdev)
|
|
||||||
{
|
|
||||||
BT_DBG("%s", hdev->name);
|
|
||||||
|
|
||||||
if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags))
|
|
||||||
return -EALREADY;
|
|
||||||
|
|
||||||
if (cancel_delayed_work(&hdev->le_scan_disable)) {
|
|
||||||
struct hci_cp_le_set_scan_enable cp;
|
|
||||||
|
|
||||||
/* Send HCI command to disable LE Scan */
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
|
||||||
hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
switch (hdev->discovery.type) {
|
||||||
|
case DISCOV_TYPE_LE:
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DISCOV_TYPE_INTERLEAVED:
|
||||||
|
hci_req_init(&req, hdev);
|
||||||
|
|
||||||
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
memcpy(&cp.lap, lap, sizeof(cp.lap));
|
||||||
|
cp.length = DISCOV_INTERLEAVED_INQUIRY_LEN;
|
||||||
|
hci_req_add(&req, HCI_OP_INQUIRY, sizeof(cp), &cp);
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
hci_inquiry_cache_flush(hdev);
|
||||||
|
|
||||||
|
err = hci_req_run(&req, inquiry_complete);
|
||||||
|
if (err) {
|
||||||
|
BT_ERR("Inquiry request failed: err %d", err);
|
||||||
|
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void le_scan_disable_work(struct work_struct *work)
|
static void le_scan_disable_work(struct work_struct *work)
|
||||||
@ -2072,46 +2057,20 @@ static void le_scan_disable_work(struct work_struct *work)
|
|||||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||||
le_scan_disable.work);
|
le_scan_disable.work);
|
||||||
struct hci_cp_le_set_scan_enable cp;
|
struct hci_cp_le_set_scan_enable cp;
|
||||||
|
struct hci_request req;
|
||||||
|
int err;
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
|
hci_req_init(&req, hdev);
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
cp.enable = LE_SCAN_DISABLE;
|
||||||
|
hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
|
||||||
|
|
||||||
hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
|
err = hci_req_run(&req, le_scan_disable_work_complete);
|
||||||
}
|
if (err)
|
||||||
|
BT_ERR("Disable LE scanning request failed: err %d", err);
|
||||||
static void le_scan_work(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct hci_dev *hdev = container_of(work, struct hci_dev, le_scan);
|
|
||||||
struct le_scan_params *param = &hdev->le_scan_params;
|
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
|
||||||
|
|
||||||
hci_do_le_scan(hdev, param->type, param->interval, param->window,
|
|
||||||
param->timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window,
|
|
||||||
int timeout)
|
|
||||||
{
|
|
||||||
struct le_scan_params *param = &hdev->le_scan_params;
|
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
|
||||||
|
|
||||||
if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
|
|
||||||
return -ENOTSUPP;
|
|
||||||
|
|
||||||
if (work_busy(&hdev->le_scan))
|
|
||||||
return -EINPROGRESS;
|
|
||||||
|
|
||||||
param->type = type;
|
|
||||||
param->interval = interval;
|
|
||||||
param->window = window;
|
|
||||||
param->timeout = timeout;
|
|
||||||
|
|
||||||
queue_work(system_long_wq, &hdev->le_scan);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Alloc HCI device */
|
/* Alloc HCI device */
|
||||||
@ -2148,7 +2107,6 @@ struct hci_dev *hci_alloc_dev(void)
|
|||||||
INIT_WORK(&hdev->cmd_work, hci_cmd_work);
|
INIT_WORK(&hdev->cmd_work, hci_cmd_work);
|
||||||
INIT_WORK(&hdev->tx_work, hci_tx_work);
|
INIT_WORK(&hdev->tx_work, hci_tx_work);
|
||||||
INIT_WORK(&hdev->power_on, hci_power_on);
|
INIT_WORK(&hdev->power_on, hci_power_on);
|
||||||
INIT_WORK(&hdev->le_scan, le_scan_work);
|
|
||||||
|
|
||||||
INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
|
INIT_DELAYED_WORK(&hdev->power_off, hci_power_off);
|
||||||
INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
|
INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
|
||||||
@ -3551,36 +3509,6 @@ static void hci_cmd_work(struct work_struct *work)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int hci_do_inquiry(struct hci_dev *hdev, u8 length)
|
|
||||||
{
|
|
||||||
/* General inquiry access code (GIAC) */
|
|
||||||
u8 lap[3] = { 0x33, 0x8b, 0x9e };
|
|
||||||
struct hci_cp_inquiry cp;
|
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
|
||||||
|
|
||||||
if (test_bit(HCI_INQUIRY, &hdev->flags))
|
|
||||||
return -EINPROGRESS;
|
|
||||||
|
|
||||||
inquiry_cache_flush(hdev);
|
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
|
||||||
memcpy(&cp.lap, lap, sizeof(cp.lap));
|
|
||||||
cp.length = length;
|
|
||||||
|
|
||||||
return hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp);
|
|
||||||
}
|
|
||||||
|
|
||||||
int hci_cancel_inquiry(struct hci_dev *hdev)
|
|
||||||
{
|
|
||||||
BT_DBG("%s", hdev->name);
|
|
||||||
|
|
||||||
if (!test_bit(HCI_INQUIRY, &hdev->flags))
|
|
||||||
return -EALREADY;
|
|
||||||
|
|
||||||
return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 bdaddr_to_le(u8 bdaddr_type)
|
u8 bdaddr_to_le(u8 bdaddr_type)
|
||||||
{
|
{
|
||||||
switch (bdaddr_type) {
|
switch (bdaddr_type) {
|
||||||
|
@ -40,21 +40,13 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||||
|
|
||||||
if (status) {
|
if (status)
|
||||||
hci_dev_lock(hdev);
|
|
||||||
mgmt_stop_discovery_failed(hdev, status);
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
clear_bit(HCI_INQUIRY, &hdev->flags);
|
clear_bit(HCI_INQUIRY, &hdev->flags);
|
||||||
smp_mb__after_clear_bit(); /* wake_up_bit advises about this barrier */
|
smp_mb__after_clear_bit(); /* wake_up_bit advises about this barrier */
|
||||||
wake_up_bit(&hdev->flags, HCI_INQUIRY);
|
wake_up_bit(&hdev->flags, HCI_INQUIRY);
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
|
|
||||||
hci_conn_check_pending(hdev);
|
hci_conn_check_pending(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -937,20 +929,6 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
|
|
||||||
{
|
|
||||||
__u8 status = *((__u8 *) skb->data);
|
|
||||||
|
|
||||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
|
||||||
|
|
||||||
if (status) {
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
mgmt_start_discovery_failed(hdev, status);
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
|
static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
@ -963,41 +941,16 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
|
|||||||
if (!cp)
|
if (!cp)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (status)
|
||||||
|
return;
|
||||||
|
|
||||||
switch (cp->enable) {
|
switch (cp->enable) {
|
||||||
case LE_SCAN_ENABLE:
|
case LE_SCAN_ENABLE:
|
||||||
if (status) {
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
mgmt_start_discovery_failed(hdev, status);
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
set_bit(HCI_LE_SCAN, &hdev->dev_flags);
|
set_bit(HCI_LE_SCAN, &hdev->dev_flags);
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
hci_discovery_set_state(hdev, DISCOVERY_FINDING);
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LE_SCAN_DISABLE:
|
case LE_SCAN_DISABLE:
|
||||||
if (status) {
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
mgmt_stop_discovery_failed(hdev, status);
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
|
clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
|
||||||
|
|
||||||
if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
|
|
||||||
hdev->discovery.state == DISCOVERY_FINDING) {
|
|
||||||
mgmt_interleaved_discovery(hdev);
|
|
||||||
} else {
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1077,18 +1030,10 @@ static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
|||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
hci_conn_check_pending(hdev);
|
hci_conn_check_pending(hdev);
|
||||||
hci_dev_lock(hdev);
|
|
||||||
if (test_bit(HCI_MGMT, &hdev->dev_flags))
|
|
||||||
mgmt_start_discovery_failed(hdev, status);
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_bit(HCI_INQUIRY, &hdev->flags);
|
set_bit(HCI_INQUIRY, &hdev->flags);
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
|
||||||
hci_discovery_set_state(hdev, DISCOVERY_FINDING);
|
|
||||||
hci_dev_unlock(hdev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
|
static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
|
||||||
@ -2298,10 +2243,6 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
hci_cc_user_passkey_neg_reply(hdev, skb);
|
hci_cc_user_passkey_neg_reply(hdev, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HCI_OP_LE_SET_SCAN_PARAM:
|
|
||||||
hci_cc_le_set_scan_param(hdev, skb);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HCI_OP_LE_SET_ADV_ENABLE:
|
case HCI_OP_LE_SET_ADV_ENABLE:
|
||||||
hci_cc_le_set_adv_enable(hdev, skb);
|
hci_cc_le_set_adv_enable(hdev, skb);
|
||||||
break;
|
break;
|
||||||
@ -2670,7 +2611,7 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
|
|
||||||
if (!test_bit(HCI_LINK_KEYS, &hdev->dev_flags))
|
if (!test_bit(HCI_MGMT, &hdev->dev_flags))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
@ -2746,7 +2687,7 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||||||
hci_conn_drop(conn);
|
hci_conn_drop(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (test_bit(HCI_LINK_KEYS, &hdev->dev_flags))
|
if (test_bit(HCI_MGMT, &hdev->dev_flags))
|
||||||
hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key,
|
hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key,
|
||||||
ev->key_type, pin_len);
|
ev->key_type, pin_len);
|
||||||
|
|
||||||
|
@ -76,25 +76,19 @@ static void hidp_copy_session(struct hidp_session *session, struct hidp_conninfo
|
|||||||
ci->flags = session->flags;
|
ci->flags = session->flags;
|
||||||
ci->state = BT_CONNECTED;
|
ci->state = BT_CONNECTED;
|
||||||
|
|
||||||
ci->vendor = 0x0000;
|
|
||||||
ci->product = 0x0000;
|
|
||||||
ci->version = 0x0000;
|
|
||||||
|
|
||||||
if (session->input) {
|
if (session->input) {
|
||||||
ci->vendor = session->input->id.vendor;
|
ci->vendor = session->input->id.vendor;
|
||||||
ci->product = session->input->id.product;
|
ci->product = session->input->id.product;
|
||||||
ci->version = session->input->id.version;
|
ci->version = session->input->id.version;
|
||||||
if (session->input->name)
|
if (session->input->name)
|
||||||
strncpy(ci->name, session->input->name, 128);
|
strlcpy(ci->name, session->input->name, 128);
|
||||||
else
|
else
|
||||||
strncpy(ci->name, "HID Boot Device", 128);
|
strlcpy(ci->name, "HID Boot Device", 128);
|
||||||
}
|
} else if (session->hid) {
|
||||||
|
|
||||||
if (session->hid) {
|
|
||||||
ci->vendor = session->hid->vendor;
|
ci->vendor = session->hid->vendor;
|
||||||
ci->product = session->hid->product;
|
ci->product = session->hid->product;
|
||||||
ci->version = session->hid->version;
|
ci->version = session->hid->version;
|
||||||
strncpy(ci->name, session->hid->name, 128);
|
strlcpy(ci->name, session->hid->name, 128);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,8 +504,10 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
|
|||||||
if (conn->hcon->type == LE_LINK) {
|
if (conn->hcon->type == LE_LINK) {
|
||||||
/* LE connection */
|
/* LE connection */
|
||||||
chan->omtu = L2CAP_DEFAULT_MTU;
|
chan->omtu = L2CAP_DEFAULT_MTU;
|
||||||
chan->scid = L2CAP_CID_LE_DATA;
|
if (chan->dcid == L2CAP_CID_ATT)
|
||||||
chan->dcid = L2CAP_CID_LE_DATA;
|
chan->scid = L2CAP_CID_ATT;
|
||||||
|
else
|
||||||
|
chan->scid = l2cap_alloc_cid(conn);
|
||||||
} else {
|
} else {
|
||||||
/* Alloc CID for connection-oriented socket */
|
/* Alloc CID for connection-oriented socket */
|
||||||
chan->scid = l2cap_alloc_cid(conn);
|
chan->scid = l2cap_alloc_cid(conn);
|
||||||
@ -543,6 +545,8 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
|
|||||||
|
|
||||||
l2cap_chan_hold(chan);
|
l2cap_chan_hold(chan);
|
||||||
|
|
||||||
|
hci_conn_hold(conn->hcon);
|
||||||
|
|
||||||
list_add(&chan->list, &conn->chan_l);
|
list_add(&chan->list, &conn->chan_l);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1338,17 +1342,21 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid,
|
|||||||
|
|
||||||
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
|
static void l2cap_le_conn_ready(struct l2cap_conn *conn)
|
||||||
{
|
{
|
||||||
struct sock *parent, *sk;
|
struct sock *parent;
|
||||||
struct l2cap_chan *chan, *pchan;
|
struct l2cap_chan *chan, *pchan;
|
||||||
|
|
||||||
BT_DBG("");
|
BT_DBG("");
|
||||||
|
|
||||||
/* Check if we have socket listening on cid */
|
/* Check if we have socket listening on cid */
|
||||||
pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_LE_DATA,
|
pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
|
||||||
conn->src, conn->dst);
|
conn->src, conn->dst);
|
||||||
if (!pchan)
|
if (!pchan)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* Client ATT sockets should override the server one */
|
||||||
|
if (__l2cap_get_chan_by_dcid(conn, L2CAP_CID_ATT))
|
||||||
|
return;
|
||||||
|
|
||||||
parent = pchan->sk;
|
parent = pchan->sk;
|
||||||
|
|
||||||
lock_sock(parent);
|
lock_sock(parent);
|
||||||
@ -1357,17 +1365,12 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
|
|||||||
if (!chan)
|
if (!chan)
|
||||||
goto clean;
|
goto clean;
|
||||||
|
|
||||||
sk = chan->sk;
|
chan->dcid = L2CAP_CID_ATT;
|
||||||
|
|
||||||
hci_conn_hold(conn->hcon);
|
bacpy(&bt_sk(chan->sk)->src, conn->src);
|
||||||
conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
|
bacpy(&bt_sk(chan->sk)->dst, conn->dst);
|
||||||
|
|
||||||
bacpy(&bt_sk(sk)->src, conn->src);
|
__l2cap_chan_add(conn, chan);
|
||||||
bacpy(&bt_sk(sk)->dst, conn->dst);
|
|
||||||
|
|
||||||
l2cap_chan_add(conn, chan);
|
|
||||||
|
|
||||||
l2cap_chan_ready(chan);
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
release_sock(parent);
|
release_sock(parent);
|
||||||
@ -1380,14 +1383,17 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
|
|||||||
|
|
||||||
BT_DBG("conn %p", conn);
|
BT_DBG("conn %p", conn);
|
||||||
|
|
||||||
if (!hcon->out && hcon->type == LE_LINK)
|
/* For outgoing pairing which doesn't necessarily have an
|
||||||
l2cap_le_conn_ready(conn);
|
* associated socket (e.g. mgmt_pair_device).
|
||||||
|
*/
|
||||||
if (hcon->out && hcon->type == LE_LINK)
|
if (hcon->out && hcon->type == LE_LINK)
|
||||||
smp_conn_security(hcon, hcon->pending_sec_level);
|
smp_conn_security(hcon, hcon->pending_sec_level);
|
||||||
|
|
||||||
mutex_lock(&conn->chan_lock);
|
mutex_lock(&conn->chan_lock);
|
||||||
|
|
||||||
|
if (hcon->type == LE_LINK)
|
||||||
|
l2cap_le_conn_ready(conn);
|
||||||
|
|
||||||
list_for_each_entry(chan, &conn->chan_l, list) {
|
list_for_each_entry(chan, &conn->chan_l, list) {
|
||||||
|
|
||||||
l2cap_chan_lock(chan);
|
l2cap_chan_lock(chan);
|
||||||
@ -1792,7 +1798,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
|||||||
|
|
||||||
auth_type = l2cap_get_auth_type(chan);
|
auth_type = l2cap_get_auth_type(chan);
|
||||||
|
|
||||||
if (chan->dcid == L2CAP_CID_LE_DATA)
|
if (bdaddr_type_is_le(dst_type))
|
||||||
hcon = hci_connect(hdev, LE_LINK, dst, dst_type,
|
hcon = hci_connect(hdev, LE_LINK, dst, dst_type,
|
||||||
chan->sec_level, auth_type);
|
chan->sec_level, auth_type);
|
||||||
else
|
else
|
||||||
@ -1811,16 +1817,10 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hcon->type == LE_LINK) {
|
if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
|
||||||
err = 0;
|
hci_conn_drop(hcon);
|
||||||
|
err = -EBUSY;
|
||||||
if (!list_empty(&conn->chan_l)) {
|
goto done;
|
||||||
err = -EBUSY;
|
|
||||||
hci_conn_drop(hcon);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err)
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update source addr of the socket */
|
/* Update source addr of the socket */
|
||||||
@ -1830,6 +1830,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
|||||||
l2cap_chan_add(conn, chan);
|
l2cap_chan_add(conn, chan);
|
||||||
l2cap_chan_lock(chan);
|
l2cap_chan_lock(chan);
|
||||||
|
|
||||||
|
/* l2cap_chan_add takes its own ref so we can drop this one */
|
||||||
|
hci_conn_drop(hcon);
|
||||||
|
|
||||||
l2cap_state_change(chan, BT_CONNECT);
|
l2cap_state_change(chan, BT_CONNECT);
|
||||||
__set_chan_timer(chan, sk->sk_sndtimeo);
|
__set_chan_timer(chan, sk->sk_sndtimeo);
|
||||||
|
|
||||||
@ -3751,8 +3754,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
|
|||||||
|
|
||||||
sk = chan->sk;
|
sk = chan->sk;
|
||||||
|
|
||||||
hci_conn_hold(conn->hcon);
|
|
||||||
|
|
||||||
bacpy(&bt_sk(sk)->src, conn->src);
|
bacpy(&bt_sk(sk)->src, conn->src);
|
||||||
bacpy(&bt_sk(sk)->dst, conn->dst);
|
bacpy(&bt_sk(sk)->dst, conn->dst);
|
||||||
chan->psm = psm;
|
chan->psm = psm;
|
||||||
@ -5292,6 +5293,51 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
|
||||||
|
struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
u8 *data = skb->data;
|
||||||
|
int len = skb->len;
|
||||||
|
struct l2cap_cmd_hdr cmd;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
l2cap_raw_recv(conn, skb);
|
||||||
|
|
||||||
|
while (len >= L2CAP_CMD_HDR_SIZE) {
|
||||||
|
u16 cmd_len;
|
||||||
|
memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
|
||||||
|
data += L2CAP_CMD_HDR_SIZE;
|
||||||
|
len -= L2CAP_CMD_HDR_SIZE;
|
||||||
|
|
||||||
|
cmd_len = le16_to_cpu(cmd.len);
|
||||||
|
|
||||||
|
BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len,
|
||||||
|
cmd.ident);
|
||||||
|
|
||||||
|
if (cmd_len > len || !cmd.ident) {
|
||||||
|
BT_DBG("corrupted command");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = l2cap_le_sig_cmd(conn, &cmd, data);
|
||||||
|
if (err) {
|
||||||
|
struct l2cap_cmd_rej_unk rej;
|
||||||
|
|
||||||
|
BT_ERR("Wrong link type (%d)", err);
|
||||||
|
|
||||||
|
/* FIXME: Map err to a valid reason */
|
||||||
|
rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
|
||||||
|
l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
|
||||||
|
sizeof(rej), &rej);
|
||||||
|
}
|
||||||
|
|
||||||
|
data += cmd_len;
|
||||||
|
len -= cmd_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree_skb(skb);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
@ -5318,11 +5364,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->hcon->type == LE_LINK)
|
err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data);
|
||||||
err = l2cap_le_sig_cmd(conn, &cmd, data);
|
|
||||||
else
|
|
||||||
err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data);
|
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
struct l2cap_cmd_rej_unk rej;
|
struct l2cap_cmd_rej_unk rej;
|
||||||
|
|
||||||
@ -6356,16 +6398,13 @@ static void l2cap_att_channel(struct l2cap_conn *conn,
|
|||||||
{
|
{
|
||||||
struct l2cap_chan *chan;
|
struct l2cap_chan *chan;
|
||||||
|
|
||||||
chan = l2cap_global_chan_by_scid(0, L2CAP_CID_LE_DATA,
|
chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT,
|
||||||
conn->src, conn->dst);
|
conn->src, conn->dst);
|
||||||
if (!chan)
|
if (!chan)
|
||||||
goto drop;
|
goto drop;
|
||||||
|
|
||||||
BT_DBG("chan %p, len %d", chan, skb->len);
|
BT_DBG("chan %p, len %d", chan, skb->len);
|
||||||
|
|
||||||
if (chan->state != BT_BOUND && chan->state != BT_CONNECTED)
|
|
||||||
goto drop;
|
|
||||||
|
|
||||||
if (chan->imtu < skb->len)
|
if (chan->imtu < skb->len)
|
||||||
goto drop;
|
goto drop;
|
||||||
|
|
||||||
@ -6395,6 +6434,8 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
|
|
||||||
switch (cid) {
|
switch (cid) {
|
||||||
case L2CAP_CID_LE_SIGNALING:
|
case L2CAP_CID_LE_SIGNALING:
|
||||||
|
l2cap_le_sig_channel(conn, skb);
|
||||||
|
break;
|
||||||
case L2CAP_CID_SIGNALING:
|
case L2CAP_CID_SIGNALING:
|
||||||
l2cap_sig_channel(conn, skb);
|
l2cap_sig_channel(conn, skb);
|
||||||
break;
|
break;
|
||||||
@ -6405,7 +6446,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||||||
l2cap_conless_channel(conn, psm, skb);
|
l2cap_conless_channel(conn, psm, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_CID_LE_DATA:
|
case L2CAP_CID_ATT:
|
||||||
l2cap_att_channel(conn, skb);
|
l2cap_att_channel(conn, skb);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -6531,7 +6572,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chan->scid == L2CAP_CID_LE_DATA) {
|
if (chan->scid == L2CAP_CID_ATT) {
|
||||||
if (!status && encrypt) {
|
if (!status && encrypt) {
|
||||||
chan->sec_level = hcon->sec_level;
|
chan->sec_level = hcon->sec_level;
|
||||||
l2cap_chan_ready(chan);
|
l2cap_chan_ready(chan);
|
||||||
|
@ -466,7 +466,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
|
|||||||
static bool l2cap_valid_mtu(struct l2cap_chan *chan, u16 mtu)
|
static bool l2cap_valid_mtu(struct l2cap_chan *chan, u16 mtu)
|
||||||
{
|
{
|
||||||
switch (chan->scid) {
|
switch (chan->scid) {
|
||||||
case L2CAP_CID_LE_DATA:
|
case L2CAP_CID_ATT:
|
||||||
if (mtu < L2CAP_LE_MIN_MTU)
|
if (mtu < L2CAP_LE_MIN_MTU)
|
||||||
return false;
|
return false;
|
||||||
break;
|
break;
|
||||||
@ -630,7 +630,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
|||||||
conn = chan->conn;
|
conn = chan->conn;
|
||||||
|
|
||||||
/*change security for LE channels */
|
/*change security for LE channels */
|
||||||
if (chan->scid == L2CAP_CID_LE_DATA) {
|
if (chan->scid == L2CAP_CID_ATT) {
|
||||||
if (!conn->hcon->out) {
|
if (!conn->hcon->out) {
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
break;
|
break;
|
||||||
|
@ -102,18 +102,6 @@ static const u16 mgmt_events[] = {
|
|||||||
MGMT_EV_PASSKEY_NOTIFY,
|
MGMT_EV_PASSKEY_NOTIFY,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
* These LE scan and inquiry parameters were chosen according to LE General
|
|
||||||
* Discovery Procedure specification.
|
|
||||||
*/
|
|
||||||
#define LE_SCAN_WIN 0x12
|
|
||||||
#define LE_SCAN_INT 0x12
|
|
||||||
#define LE_SCAN_TIMEOUT_LE_ONLY msecs_to_jiffies(10240)
|
|
||||||
#define LE_SCAN_TIMEOUT_BREDR_LE msecs_to_jiffies(5120)
|
|
||||||
|
|
||||||
#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */
|
|
||||||
#define INQUIRY_LEN_BREDR_LE 0x04 /* TGAP(100)/2 */
|
|
||||||
|
|
||||||
#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000)
|
#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000)
|
||||||
|
|
||||||
#define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \
|
#define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \
|
||||||
@ -1748,8 +1736,6 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||||||
|
|
||||||
hci_link_keys_clear(hdev);
|
hci_link_keys_clear(hdev);
|
||||||
|
|
||||||
set_bit(HCI_LINK_KEYS, &hdev->dev_flags);
|
|
||||||
|
|
||||||
if (cp->debug_keys)
|
if (cp->debug_keys)
|
||||||
set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
|
set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
|
||||||
else
|
else
|
||||||
@ -2633,28 +2619,72 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mgmt_interleaved_discovery(struct hci_dev *hdev)
|
static int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
|
||||||
{
|
{
|
||||||
|
struct pending_cmd *cmd;
|
||||||
|
u8 type;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
|
||||||
|
if (!cmd)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE);
|
type = hdev->discovery.type;
|
||||||
if (err < 0)
|
|
||||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
|
||||||
|
|
||||||
hci_dev_unlock(hdev);
|
err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
|
||||||
|
&type, sizeof(type));
|
||||||
|
mgmt_pending_remove(cmd);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void start_discovery_complete(struct hci_dev *hdev, u8 status)
|
||||||
|
{
|
||||||
|
BT_DBG("status %d", status);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
mgmt_start_discovery_failed(hdev, status);
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
hci_discovery_set_state(hdev, DISCOVERY_FINDING);
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
|
||||||
|
switch (hdev->discovery.type) {
|
||||||
|
case DISCOV_TYPE_LE:
|
||||||
|
queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
|
||||||
|
DISCOV_LE_TIMEOUT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DISCOV_TYPE_INTERLEAVED:
|
||||||
|
queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable,
|
||||||
|
DISCOV_INTERLEAVED_TIMEOUT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DISCOV_TYPE_BREDR:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
BT_ERR("Invalid discovery type %d", hdev->discovery.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int start_discovery(struct sock *sk, struct hci_dev *hdev,
|
static int start_discovery(struct sock *sk, struct hci_dev *hdev,
|
||||||
void *data, u16 len)
|
void *data, u16 len)
|
||||||
{
|
{
|
||||||
struct mgmt_cp_start_discovery *cp = data;
|
struct mgmt_cp_start_discovery *cp = data;
|
||||||
struct pending_cmd *cmd;
|
struct pending_cmd *cmd;
|
||||||
|
struct hci_cp_le_set_scan_param param_cp;
|
||||||
|
struct hci_cp_le_set_scan_enable enable_cp;
|
||||||
|
struct hci_cp_inquiry inq_cp;
|
||||||
|
struct hci_request req;
|
||||||
|
/* General inquiry access code (GIAC) */
|
||||||
|
u8 lap[3] = { 0x33, 0x8b, 0x9e };
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
@ -2687,6 +2717,8 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
|
|||||||
|
|
||||||
hdev->discovery.type = cp->type;
|
hdev->discovery.type = cp->type;
|
||||||
|
|
||||||
|
hci_req_init(&req, hdev);
|
||||||
|
|
||||||
switch (hdev->discovery.type) {
|
switch (hdev->discovery.type) {
|
||||||
case DISCOV_TYPE_BREDR:
|
case DISCOV_TYPE_BREDR:
|
||||||
if (!lmp_bredr_capable(hdev)) {
|
if (!lmp_bredr_capable(hdev)) {
|
||||||
@ -2696,10 +2728,23 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
|
|||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
|
if (test_bit(HCI_INQUIRY, &hdev->flags)) {
|
||||||
|
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
||||||
|
MGMT_STATUS_BUSY);
|
||||||
|
mgmt_pending_remove(cmd);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_inquiry_cache_flush(hdev);
|
||||||
|
|
||||||
|
memset(&inq_cp, 0, sizeof(inq_cp));
|
||||||
|
memcpy(&inq_cp.lap, lap, sizeof(inq_cp.lap));
|
||||||
|
inq_cp.length = DISCOV_BREDR_INQUIRY_LEN;
|
||||||
|
hci_req_add(&req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DISCOV_TYPE_LE:
|
case DISCOV_TYPE_LE:
|
||||||
|
case DISCOV_TYPE_INTERLEAVED:
|
||||||
if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
|
if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
|
||||||
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
||||||
MGMT_STATUS_NOT_SUPPORTED);
|
MGMT_STATUS_NOT_SUPPORTED);
|
||||||
@ -2707,20 +2752,40 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
|
|||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = hci_le_scan(hdev, LE_SCAN_ACTIVE, LE_SCAN_INT,
|
if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
|
||||||
LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
|
!lmp_bredr_capable(hdev)) {
|
||||||
break;
|
|
||||||
|
|
||||||
case DISCOV_TYPE_INTERLEAVED:
|
|
||||||
if (!lmp_host_le_capable(hdev) || !lmp_bredr_capable(hdev)) {
|
|
||||||
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
||||||
MGMT_STATUS_NOT_SUPPORTED);
|
MGMT_STATUS_NOT_SUPPORTED);
|
||||||
mgmt_pending_remove(cmd);
|
mgmt_pending_remove(cmd);
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = hci_le_scan(hdev, LE_SCAN_ACTIVE, LE_SCAN_INT,
|
if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) {
|
||||||
LE_SCAN_WIN, LE_SCAN_TIMEOUT_BREDR_LE);
|
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
||||||
|
MGMT_STATUS_REJECTED);
|
||||||
|
mgmt_pending_remove(cmd);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) {
|
||||||
|
err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
|
||||||
|
MGMT_STATUS_BUSY);
|
||||||
|
mgmt_pending_remove(cmd);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(¶m_cp, 0, sizeof(param_cp));
|
||||||
|
param_cp.type = LE_SCAN_ACTIVE;
|
||||||
|
param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
|
||||||
|
param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
|
||||||
|
hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
|
||||||
|
¶m_cp);
|
||||||
|
|
||||||
|
memset(&enable_cp, 0, sizeof(enable_cp));
|
||||||
|
enable_cp.enable = LE_SCAN_ENABLE;
|
||||||
|
enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
|
||||||
|
hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
|
||||||
|
&enable_cp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -2730,6 +2795,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
|
|||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = hci_req_run(&req, start_discovery_complete);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
mgmt_pending_remove(cmd);
|
mgmt_pending_remove(cmd);
|
||||||
else
|
else
|
||||||
@ -2740,6 +2806,39 @@ failed:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
|
||||||
|
{
|
||||||
|
struct pending_cmd *cmd;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
|
||||||
|
if (!cmd)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
|
||||||
|
&hdev->discovery.type, sizeof(hdev->discovery.type));
|
||||||
|
mgmt_pending_remove(cmd);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop_discovery_complete(struct hci_dev *hdev, u8 status)
|
||||||
|
{
|
||||||
|
BT_DBG("status %d", status);
|
||||||
|
|
||||||
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
mgmt_stop_discovery_failed(hdev, status);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
||||||
|
|
||||||
|
unlock:
|
||||||
|
hci_dev_unlock(hdev);
|
||||||
|
}
|
||||||
|
|
||||||
static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
|
static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||||
u16 len)
|
u16 len)
|
||||||
{
|
{
|
||||||
@ -2747,6 +2846,8 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||||||
struct pending_cmd *cmd;
|
struct pending_cmd *cmd;
|
||||||
struct hci_cp_remote_name_req_cancel cp;
|
struct hci_cp_remote_name_req_cancel cp;
|
||||||
struct inquiry_entry *e;
|
struct inquiry_entry *e;
|
||||||
|
struct hci_request req;
|
||||||
|
struct hci_cp_le_set_scan_enable enable_cp;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BT_DBG("%s", hdev->name);
|
BT_DBG("%s", hdev->name);
|
||||||
@ -2773,12 +2874,20 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hci_req_init(&req, hdev);
|
||||||
|
|
||||||
switch (hdev->discovery.state) {
|
switch (hdev->discovery.state) {
|
||||||
case DISCOVERY_FINDING:
|
case DISCOVERY_FINDING:
|
||||||
if (test_bit(HCI_INQUIRY, &hdev->flags))
|
if (test_bit(HCI_INQUIRY, &hdev->flags)) {
|
||||||
err = hci_cancel_inquiry(hdev);
|
hci_req_add(&req, HCI_OP_INQUIRY_CANCEL, 0, NULL);
|
||||||
else
|
} else {
|
||||||
err = hci_cancel_le_scan(hdev);
|
cancel_delayed_work(&hdev->le_scan_disable);
|
||||||
|
|
||||||
|
memset(&enable_cp, 0, sizeof(enable_cp));
|
||||||
|
enable_cp.enable = LE_SCAN_DISABLE;
|
||||||
|
hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE,
|
||||||
|
sizeof(enable_cp), &enable_cp);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -2796,16 +2905,22 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bacpy(&cp.bdaddr, &e->data.bdaddr);
|
bacpy(&cp.bdaddr, &e->data.bdaddr);
|
||||||
err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL,
|
hci_req_add(&req, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp),
|
||||||
sizeof(cp), &cp);
|
&cp);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BT_DBG("unknown discovery state %u", hdev->discovery.state);
|
BT_DBG("unknown discovery state %u", hdev->discovery.state);
|
||||||
err = -EFAULT;
|
|
||||||
|
mgmt_pending_remove(cmd);
|
||||||
|
err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY,
|
||||||
|
MGMT_STATUS_FAILED, &mgmt_cp->type,
|
||||||
|
sizeof(mgmt_cp->type));
|
||||||
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = hci_req_run(&req, stop_discovery_complete);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
mgmt_pending_remove(cmd);
|
mgmt_pending_remove(cmd);
|
||||||
else
|
else
|
||||||
@ -4063,6 +4178,9 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
|||||||
struct mgmt_ev_device_found *ev = (void *) buf;
|
struct mgmt_ev_device_found *ev = (void *) buf;
|
||||||
size_t ev_size;
|
size_t ev_size;
|
||||||
|
|
||||||
|
if (!hci_discovery_active(hdev))
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
/* Leave 5 bytes for a potential CoD field */
|
/* Leave 5 bytes for a potential CoD field */
|
||||||
if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
|
if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -4114,43 +4232,6 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
|
|||||||
sizeof(*ev) + eir_len, NULL);
|
sizeof(*ev) + eir_len, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
|
|
||||||
{
|
|
||||||
struct pending_cmd *cmd;
|
|
||||||
u8 type;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
|
|
||||||
|
|
||||||
cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
|
|
||||||
if (!cmd)
|
|
||||||
return -ENOENT;
|
|
||||||
|
|
||||||
type = hdev->discovery.type;
|
|
||||||
|
|
||||||
err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
|
|
||||||
&type, sizeof(type));
|
|
||||||
mgmt_pending_remove(cmd);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
|
|
||||||
{
|
|
||||||
struct pending_cmd *cmd;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
|
|
||||||
if (!cmd)
|
|
||||||
return -ENOENT;
|
|
||||||
|
|
||||||
err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
|
|
||||||
&hdev->discovery.type, sizeof(hdev->discovery.type));
|
|
||||||
mgmt_pending_remove(cmd);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
|
int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
|
||||||
{
|
{
|
||||||
struct mgmt_ev_discovering ev;
|
struct mgmt_ev_discovering ev;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user