Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/padovan/bluetooth-next-2.6
Conflicts: net/bluetooth/l2cap_core.c
This commit is contained in:
commit
4b42c542af
|
@ -375,6 +375,11 @@ static int ath3k_probe(struct usb_interface *intf,
|
|||
|
||||
/* load patch and sysconfig files for AR3012 */
|
||||
if (id->driver_info & BTUSB_ATH3012) {
|
||||
|
||||
/* New firmware with patch and sysconfig files already loaded */
|
||||
if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x0001)
|
||||
return -ENODEV;
|
||||
|
||||
ret = ath3k_load_patch(udev);
|
||||
if (ret < 0) {
|
||||
BT_ERR("Loading patch file failed");
|
||||
|
|
|
@ -54,6 +54,7 @@ static struct usb_driver btusb_driver;
|
|||
#define BTUSB_BCM92035 0x10
|
||||
#define BTUSB_BROKEN_ISOC 0x20
|
||||
#define BTUSB_WRONG_SCO_MTU 0x40
|
||||
#define BTUSB_ATH3012 0x80
|
||||
|
||||
static struct usb_device_id btusb_table[] = {
|
||||
/* Generic Bluetooth USB device */
|
||||
|
@ -110,7 +111,7 @@ static struct usb_device_id blacklist_table[] = {
|
|||
{ USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
|
||||
|
||||
/* Atheros 3012 with sflash firmware */
|
||||
{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_IGNORE },
|
||||
{ USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 },
|
||||
|
||||
/* Atheros AR5BBU12 with sflash firmware */
|
||||
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
|
||||
|
@ -914,6 +915,15 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
if (ignore_sniffer && id->driver_info & BTUSB_SNIFFER)
|
||||
return -ENODEV;
|
||||
|
||||
if (id->driver_info & BTUSB_ATH3012) {
|
||||
struct usb_device *udev = interface_to_usbdev(intf);
|
||||
|
||||
/* Old firmware would otherwise let ath3k driver load
|
||||
* patch and sysconfig files */
|
||||
if (le16_to_cpu(udev->descriptor.bcdDevice) <= 0x0001)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
#define BT_SECURITY 4
|
||||
struct bt_security {
|
||||
__u8 level;
|
||||
__u8 key_size;
|
||||
};
|
||||
#define BT_SECURITY_SDP 0
|
||||
#define BT_SECURITY_LOW 1
|
||||
|
@ -76,9 +77,12 @@ struct bt_power {
|
|||
#define BT_POWER_FORCE_ACTIVE_OFF 0
|
||||
#define BT_POWER_FORCE_ACTIVE_ON 1
|
||||
|
||||
#define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg)
|
||||
#define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg)
|
||||
#define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg)
|
||||
__attribute__((format (printf, 2, 3)))
|
||||
int bt_printk(const char *level, const char *fmt, ...);
|
||||
|
||||
#define BT_INFO(fmt, arg...) bt_printk(KERN_INFO, pr_fmt(fmt), ##arg)
|
||||
#define BT_ERR(fmt, arg...) bt_printk(KERN_ERR, pr_fmt(fmt), ##arg)
|
||||
#define BT_DBG(fmt, arg...) pr_debug(fmt "\n", ##arg)
|
||||
|
||||
/* Connection and socket states */
|
||||
enum {
|
||||
|
@ -204,7 +208,7 @@ out:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int bt_err(__u16 code);
|
||||
int bt_to_errno(__u16 code);
|
||||
|
||||
extern int hci_sock_init(void);
|
||||
extern void hci_sock_cleanup(void);
|
||||
|
|
|
@ -211,11 +211,16 @@ enum {
|
|||
#define LMP_EDR_3S_ESCO 0x80
|
||||
|
||||
#define LMP_EXT_INQ 0x01
|
||||
#define LMP_SIMUL_LE_BR 0x02
|
||||
#define LMP_SIMPLE_PAIR 0x08
|
||||
#define LMP_NO_FLUSH 0x40
|
||||
|
||||
#define LMP_LSTO 0x01
|
||||
#define LMP_INQ_TX_PWR 0x02
|
||||
#define LMP_EXTFEATURES 0x80
|
||||
|
||||
/* Extended LMP features */
|
||||
#define LMP_HOST_LE 0x02
|
||||
|
||||
/* Connection modes */
|
||||
#define HCI_CM_ACTIVE 0x0000
|
||||
|
@ -254,6 +259,10 @@ enum {
|
|||
#define HCI_LK_UNAUTH_COMBINATION 0x04
|
||||
#define HCI_LK_AUTH_COMBINATION 0x05
|
||||
#define HCI_LK_CHANGED_COMBINATION 0x06
|
||||
/* The spec doesn't define types for SMP keys */
|
||||
#define HCI_LK_SMP_LTK 0x81
|
||||
#define HCI_LK_SMP_IRK 0x82
|
||||
#define HCI_LK_SMP_CSRK 0x83
|
||||
|
||||
/* ----- HCI Commands ---- */
|
||||
#define HCI_OP_NOP 0x0000
|
||||
|
@ -653,6 +662,12 @@ struct hci_rp_read_local_oob_data {
|
|||
|
||||
#define HCI_OP_READ_INQ_RSP_TX_POWER 0x0c58
|
||||
|
||||
#define HCI_OP_WRITE_LE_HOST_SUPPORTED 0x0c6d
|
||||
struct hci_cp_write_le_host_supported {
|
||||
__u8 le;
|
||||
__u8 simul;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_READ_LOCAL_VERSION 0x1001
|
||||
struct hci_rp_read_local_version {
|
||||
__u8 status;
|
||||
|
@ -676,6 +691,9 @@ struct hci_rp_read_local_features {
|
|||
} __packed;
|
||||
|
||||
#define HCI_OP_READ_LOCAL_EXT_FEATURES 0x1004
|
||||
struct hci_cp_read_local_ext_features {
|
||||
__u8 page;
|
||||
} __packed;
|
||||
struct hci_rp_read_local_ext_features {
|
||||
__u8 status;
|
||||
__u8 page;
|
||||
|
|
|
@ -74,12 +74,28 @@ struct bt_uuid {
|
|||
u8 svc_hint;
|
||||
};
|
||||
|
||||
struct key_master_id {
|
||||
__le16 ediv;
|
||||
u8 rand[8];
|
||||
} __packed;
|
||||
|
||||
struct link_key_data {
|
||||
bdaddr_t bdaddr;
|
||||
u8 type;
|
||||
u8 val[16];
|
||||
u8 pin_len;
|
||||
u8 dlen;
|
||||
u8 data[0];
|
||||
} __packed;
|
||||
|
||||
struct link_key {
|
||||
struct list_head list;
|
||||
bdaddr_t bdaddr;
|
||||
u8 type;
|
||||
u8 val[16];
|
||||
u8 pin_len;
|
||||
u8 dlen;
|
||||
u8 data[0];
|
||||
};
|
||||
|
||||
struct oob_data {
|
||||
|
@ -113,6 +129,7 @@ struct hci_dev {
|
|||
__u8 major_class;
|
||||
__u8 minor_class;
|
||||
__u8 features[8];
|
||||
__u8 extfeatures[8];
|
||||
__u8 commands[64];
|
||||
__u8 ssp_mode;
|
||||
__u8 hci_ver;
|
||||
|
@ -223,7 +240,6 @@ struct hci_conn {
|
|||
struct list_head list;
|
||||
|
||||
atomic_t refcnt;
|
||||
spinlock_t lock;
|
||||
|
||||
bdaddr_t dst;
|
||||
__u8 dst_type;
|
||||
|
@ -245,11 +261,11 @@ struct hci_conn {
|
|||
__u8 sec_level;
|
||||
__u8 pending_sec_level;
|
||||
__u8 pin_length;
|
||||
__u8 enc_key_size;
|
||||
__u8 io_capability;
|
||||
__u8 power_save;
|
||||
__u16 disc_timeout;
|
||||
unsigned long pend;
|
||||
__u8 ltk[16];
|
||||
|
||||
__u8 remote_cap;
|
||||
__u8 remote_oob;
|
||||
|
@ -272,7 +288,6 @@ struct hci_conn {
|
|||
struct hci_dev *hdev;
|
||||
void *l2cap_data;
|
||||
void *sco_data;
|
||||
void *priv;
|
||||
|
||||
struct hci_conn *link;
|
||||
|
||||
|
@ -538,6 +553,11 @@ int hci_link_keys_clear(struct hci_dev *hdev);
|
|||
struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
|
||||
bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len);
|
||||
struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8]);
|
||||
struct link_key *hci_find_link_key_type(struct hci_dev *hdev,
|
||||
bdaddr_t *bdaddr, u8 type);
|
||||
int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
|
||||
u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16]);
|
||||
int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
|
||||
int hci_remote_oob_data_clear(struct hci_dev *hdev);
|
||||
|
@ -579,6 +599,9 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
|||
#define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH)
|
||||
#define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE)
|
||||
|
||||
/* ----- Extended LMP capabilities ----- */
|
||||
#define lmp_host_le_capable(dev) ((dev)->extfeatures[0] & LMP_HOST_LE)
|
||||
|
||||
/* ----- HCI protocols ----- */
|
||||
struct hci_proto {
|
||||
char *name;
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
#define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */
|
||||
#define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */
|
||||
#define L2CAP_DEFAULT_ACK_TO 200
|
||||
#define L2CAP_LOCAL_BUSY_TRIES 12
|
||||
#define L2CAP_LE_DEFAULT_MTU 23
|
||||
|
||||
#define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */
|
||||
|
@ -130,6 +129,12 @@ struct l2cap_conninfo {
|
|||
#define L2CAP_SDU_END 0x8000
|
||||
#define L2CAP_SDU_CONTINUE 0xC000
|
||||
|
||||
/* L2CAP Command rej. reasons */
|
||||
#define L2CAP_REJ_NOT_UNDERSTOOD 0x0000
|
||||
#define L2CAP_REJ_MTU_EXCEEDED 0x0001
|
||||
#define L2CAP_REJ_INVALID_CID 0x0002
|
||||
|
||||
|
||||
/* L2CAP structures */
|
||||
struct l2cap_hdr {
|
||||
__le16 len;
|
||||
|
@ -144,10 +149,21 @@ struct l2cap_cmd_hdr {
|
|||
} __packed;
|
||||
#define L2CAP_CMD_HDR_SIZE 4
|
||||
|
||||
struct l2cap_cmd_rej {
|
||||
struct l2cap_cmd_rej_unk {
|
||||
__le16 reason;
|
||||
} __packed;
|
||||
|
||||
struct l2cap_cmd_rej_mtu {
|
||||
__le16 reason;
|
||||
__le16 max_mtu;
|
||||
} __packed;
|
||||
|
||||
struct l2cap_cmd_rej_cid {
|
||||
__le16 reason;
|
||||
__le16 scid;
|
||||
__le16 dcid;
|
||||
} __packed;
|
||||
|
||||
struct l2cap_conn_req {
|
||||
__le16 psm;
|
||||
__le16 scid;
|
||||
|
@ -352,8 +368,6 @@ struct l2cap_chan {
|
|||
struct sk_buff *tx_send_head;
|
||||
struct sk_buff_head tx_q;
|
||||
struct sk_buff_head srej_q;
|
||||
struct sk_buff_head busy_q;
|
||||
struct work_struct busy_work;
|
||||
struct list_head srej_l;
|
||||
|
||||
struct list_head list;
|
||||
|
@ -422,6 +436,7 @@ struct l2cap_conn {
|
|||
struct l2cap_pinfo {
|
||||
struct bt_sock bt;
|
||||
struct l2cap_chan *chan;
|
||||
struct sk_buff *rx_busy_skb;
|
||||
};
|
||||
|
||||
enum {
|
||||
|
@ -449,7 +464,6 @@ enum {
|
|||
CONN_REJ_ACT,
|
||||
CONN_SEND_FBIT,
|
||||
CONN_RNR_SENT,
|
||||
CONN_SAR_RETRY,
|
||||
};
|
||||
|
||||
#define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t))
|
||||
|
@ -498,5 +512,6 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason);
|
|||
void l2cap_chan_destroy(struct l2cap_chan *chan);
|
||||
int l2cap_chan_connect(struct l2cap_chan *chan);
|
||||
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len);
|
||||
void l2cap_chan_busy(struct l2cap_chan *chan, int busy);
|
||||
|
||||
#endif /* __L2CAP_H */
|
||||
|
|
|
@ -101,6 +101,8 @@ struct mgmt_key_info {
|
|||
u8 type;
|
||||
u8 val[16];
|
||||
u8 pin_len;
|
||||
u8 dlen;
|
||||
u8 data[0];
|
||||
} __packed;
|
||||
|
||||
#define MGMT_OP_LOAD_KEYS 0x000D
|
||||
|
|
|
@ -118,5 +118,6 @@ struct smp_cmd_security_req {
|
|||
/* SMP Commands */
|
||||
int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level);
|
||||
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
|
||||
int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
|
||||
|
||||
#endif /* __SMP_H */
|
||||
|
|
|
@ -60,8 +60,6 @@ static void hci_tx_task(unsigned long arg);
|
|||
|
||||
static DEFINE_RWLOCK(hci_task_lock);
|
||||
|
||||
static int enable_smp;
|
||||
|
||||
/* HCI device list */
|
||||
LIST_HEAD(hci_dev_list);
|
||||
DEFINE_RWLOCK(hci_dev_list_lock);
|
||||
|
@ -148,7 +146,7 @@ static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev,
|
|||
|
||||
switch (hdev->req_status) {
|
||||
case HCI_REQ_DONE:
|
||||
err = -bt_err(hdev->req_result);
|
||||
err = -bt_to_errno(hdev->req_result);
|
||||
break;
|
||||
|
||||
case HCI_REQ_CANCELED:
|
||||
|
@ -542,7 +540,7 @@ int hci_dev_open(__u16 dev)
|
|||
ret = __hci_request(hdev, hci_init_req, 0,
|
||||
msecs_to_jiffies(HCI_INIT_TIMEOUT));
|
||||
|
||||
if (lmp_le_capable(hdev))
|
||||
if (lmp_host_le_capable(hdev))
|
||||
ret = __hci_request(hdev, hci_le_init_req, 0,
|
||||
msecs_to_jiffies(HCI_INIT_TIMEOUT));
|
||||
|
||||
|
@ -1059,6 +1057,42 @@ static int hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn,
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct link_key *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, u8 rand[8])
|
||||
{
|
||||
struct link_key *k;
|
||||
|
||||
list_for_each_entry(k, &hdev->link_keys, list) {
|
||||
struct key_master_id *id;
|
||||
|
||||
if (k->type != HCI_LK_SMP_LTK)
|
||||
continue;
|
||||
|
||||
if (k->dlen != sizeof(*id))
|
||||
continue;
|
||||
|
||||
id = (void *) &k->data;
|
||||
if (id->ediv == ediv &&
|
||||
(memcmp(rand, id->rand, sizeof(id->rand)) == 0))
|
||||
return k;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_find_ltk);
|
||||
|
||||
struct link_key *hci_find_link_key_type(struct hci_dev *hdev,
|
||||
bdaddr_t *bdaddr, u8 type)
|
||||
{
|
||||
struct link_key *k;
|
||||
|
||||
list_for_each_entry(k, &hdev->link_keys, list)
|
||||
if (k->type == type && bacmp(bdaddr, &k->bdaddr) == 0)
|
||||
return k;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_find_link_key_type);
|
||||
|
||||
int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
|
||||
bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len)
|
||||
{
|
||||
|
@ -1114,6 +1148,44 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
|
||||
u8 key_size, __le16 ediv, u8 rand[8], u8 ltk[16])
|
||||
{
|
||||
struct link_key *key, *old_key;
|
||||
struct key_master_id *id;
|
||||
u8 old_key_type;
|
||||
|
||||
BT_DBG("%s addr %s", hdev->name, batostr(bdaddr));
|
||||
|
||||
old_key = hci_find_link_key_type(hdev, bdaddr, HCI_LK_SMP_LTK);
|
||||
if (old_key) {
|
||||
key = old_key;
|
||||
old_key_type = old_key->type;
|
||||
} else {
|
||||
key = kzalloc(sizeof(*key) + sizeof(*id), GFP_ATOMIC);
|
||||
if (!key)
|
||||
return -ENOMEM;
|
||||
list_add(&key->list, &hdev->link_keys);
|
||||
old_key_type = 0xff;
|
||||
}
|
||||
|
||||
key->dlen = sizeof(*id);
|
||||
|
||||
bacpy(&key->bdaddr, bdaddr);
|
||||
memcpy(key->val, ltk, sizeof(key->val));
|
||||
key->type = HCI_LK_SMP_LTK;
|
||||
key->pin_len = key_size;
|
||||
|
||||
id = (void *) &key->data;
|
||||
id->ediv = ediv;
|
||||
memcpy(id->rand, rand, sizeof(id->rand));
|
||||
|
||||
if (new_key)
|
||||
mgmt_new_key(hdev->id, key, old_key_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
struct link_key *key;
|
||||
|
@ -1246,7 +1318,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||
if (bacmp(bdaddr, BDADDR_ANY) == 0)
|
||||
return -EBADF;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
if (hci_blacklist_lookup(hdev, bdaddr)) {
|
||||
err = -EEXIST;
|
||||
|
@ -1266,7 +1338,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||
err = 0;
|
||||
|
||||
err:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1275,7 +1347,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||
struct bdaddr_list *entry;
|
||||
int err = 0;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
if (bacmp(bdaddr, BDADDR_ANY) == 0) {
|
||||
hci_blacklist_clear(hdev);
|
||||
|
@ -1292,7 +1364,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
|||
kfree(entry);
|
||||
|
||||
done:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1368,14 +1440,6 @@ int hci_add_adv_entry(struct hci_dev *hdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct crypto_blkcipher *alloc_cypher(void)
|
||||
{
|
||||
if (enable_smp)
|
||||
return crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
/* Register HCI device */
|
||||
int hci_register_dev(struct hci_dev *hdev)
|
||||
{
|
||||
|
@ -1460,7 +1524,7 @@ int hci_register_dev(struct hci_dev *hdev)
|
|||
if (!hdev->workqueue)
|
||||
goto nomem;
|
||||
|
||||
hdev->tfm = alloc_cypher();
|
||||
hdev->tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(hdev->tfm))
|
||||
BT_INFO("Failed to load transform for ecb(aes): %ld",
|
||||
PTR_ERR(hdev->tfm));
|
||||
|
@ -2352,6 +2416,3 @@ static void hci_cmd_task(unsigned long arg)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
module_param(enable_smp, bool, 0644);
|
||||
MODULE_PARM_DESC(enable_smp, "Enable SMP support (LE only)");
|
||||
|
|
|
@ -45,6 +45,8 @@
|
|||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
static int enable_le;
|
||||
|
||||
/* Handle HCI Event packets */
|
||||
|
||||
static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
@ -525,6 +527,20 @@ static void hci_setup_event_mask(struct hci_dev *hdev)
|
|||
hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
|
||||
}
|
||||
|
||||
static void hci_set_le_support(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_cp_write_le_host_supported cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
if (enable_le) {
|
||||
cp.le = 1;
|
||||
cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
|
||||
}
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static void hci_setup(struct hci_dev *hdev)
|
||||
{
|
||||
hci_setup_event_mask(hdev);
|
||||
|
@ -542,6 +558,17 @@ static void hci_setup(struct hci_dev *hdev)
|
|||
|
||||
if (hdev->features[7] & LMP_INQ_TX_PWR)
|
||||
hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL);
|
||||
|
||||
if (hdev->features[7] & LMP_EXTFEATURES) {
|
||||
struct hci_cp_read_local_ext_features cp;
|
||||
|
||||
cp.page = 0x01;
|
||||
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES,
|
||||
sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
if (hdev->features[4] & LMP_LE)
|
||||
hci_set_le_support(hdev);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
@ -658,6 +685,21 @@ static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb
|
|||
hdev->features[6], hdev->features[7]);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_read_local_ext_features *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
memcpy(hdev->extfeatures, rp->features, 8);
|
||||
|
||||
hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status);
|
||||
}
|
||||
|
||||
static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_read_buffer_size *rp = (void *) skb->data;
|
||||
|
@ -892,6 +934,21 @@ static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status);
|
||||
}
|
||||
|
||||
static inline void hci_cc_write_le_host_supported(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_cp_read_local_ext_features cp;
|
||||
__u8 status = *((__u8 *) skb->data);
|
||||
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
||||
if (status)
|
||||
return;
|
||||
|
||||
cp.page = 0x01;
|
||||
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
|
||||
{
|
||||
BT_DBG("%s status 0x%x", hdev->name, status);
|
||||
|
@ -1826,6 +1883,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
|
|||
hci_cc_read_local_features(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_READ_LOCAL_EXT_FEATURES:
|
||||
hci_cc_read_local_ext_features(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_READ_BUFFER_SIZE:
|
||||
hci_cc_read_buffer_size(hdev, skb);
|
||||
break;
|
||||
|
@ -1894,6 +1955,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk
|
|||
hci_cc_le_ltk_neg_reply(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_WRITE_LE_HOST_SUPPORTED:
|
||||
hci_cc_write_le_host_supported(hdev, skb);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
||||
break;
|
||||
|
@ -2793,21 +2858,36 @@ static inline void hci_le_ltk_request_evt(struct hci_dev *hdev,
|
|||
{
|
||||
struct hci_ev_le_ltk_req *ev = (void *) skb->data;
|
||||
struct hci_cp_le_ltk_reply cp;
|
||||
struct hci_cp_le_ltk_neg_reply neg;
|
||||
struct hci_conn *conn;
|
||||
struct link_key *ltk;
|
||||
|
||||
BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle));
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||
if (conn == NULL)
|
||||
goto not_found;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
ltk = hci_find_ltk(hdev, ev->ediv, ev->random);
|
||||
if (ltk == NULL)
|
||||
goto not_found;
|
||||
|
||||
memcpy(cp.ltk, ltk->val, sizeof(ltk->val));
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
memcpy(cp.ltk, conn->ltk, sizeof(conn->ltk));
|
||||
conn->pin_length = ltk->pin_len;
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
return;
|
||||
|
||||
not_found:
|
||||
neg.handle = ev->handle;
|
||||
hci_send_cmd(hdev, HCI_OP_LE_LTK_NEG_REPLY, sizeof(neg), &neg);
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
|
@ -3022,3 +3102,6 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
|
|||
hci_send_to_sock(hdev, skb, NULL);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
module_param(enable_le, bool, 0444);
|
||||
MODULE_PARM_DESC(enable_le, "Enable LE support");
|
||||
|
|
|
@ -61,13 +61,9 @@ int disable_ertm;
|
|||
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
|
||||
static u8 l2cap_fixed_chan[8] = { 0x02, };
|
||||
|
||||
static struct workqueue_struct *_busy_wq;
|
||||
|
||||
static LIST_HEAD(chan_list);
|
||||
static DEFINE_RWLOCK(chan_list_lock);
|
||||
|
||||
static void l2cap_busy_work(struct work_struct *work);
|
||||
|
||||
static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
|
||||
u8 code, u8 ident, u16 dlen, void *data);
|
||||
static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
|
||||
|
@ -223,18 +219,18 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
|
|||
|
||||
static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, long timeout)
|
||||
{
|
||||
BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout);
|
||||
BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout);
|
||||
|
||||
if (!mod_timer(timer, jiffies + timeout))
|
||||
chan_hold(chan);
|
||||
if (!mod_timer(timer, jiffies + msecs_to_jiffies(timeout)))
|
||||
chan_hold(chan);
|
||||
}
|
||||
|
||||
static void l2cap_clear_timer(struct l2cap_chan *chan, struct timer_list *timer)
|
||||
{
|
||||
BT_DBG("chan %p state %d", chan, chan->state);
|
||||
BT_DBG("chan %p state %d", chan, chan->state);
|
||||
|
||||
if (timer_pending(timer) && del_timer(timer))
|
||||
chan_put(chan);
|
||||
if (timer_pending(timer) && del_timer(timer))
|
||||
chan_put(chan);
|
||||
}
|
||||
|
||||
static void l2cap_state_change(struct l2cap_chan *chan, int state)
|
||||
|
@ -395,7 +391,6 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
|
|||
__clear_ack_timer(chan);
|
||||
|
||||
skb_queue_purge(&chan->srej_q);
|
||||
skb_queue_purge(&chan->busy_q);
|
||||
|
||||
list_for_each_entry_safe(l, tmp, &chan->srej_l, list) {
|
||||
list_del(&l->list);
|
||||
|
@ -741,9 +736,9 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
|
|||
&chan->conf_state)) {
|
||||
/* l2cap_chan_close() calls list_del(chan)
|
||||
* so release the lock */
|
||||
read_unlock_bh(&conn->chan_lock);
|
||||
read_unlock(&conn->chan_lock);
|
||||
l2cap_chan_close(chan, ECONNRESET);
|
||||
read_lock_bh(&conn->chan_lock);
|
||||
read_lock(&conn->chan_lock);
|
||||
bh_unlock_sock(sk);
|
||||
continue;
|
||||
}
|
||||
|
@ -1873,11 +1868,9 @@ static inline void l2cap_ertm_init(struct l2cap_chan *chan)
|
|||
setup_timer(&chan->ack_timer, l2cap_ack_timeout, (unsigned long) chan);
|
||||
|
||||
skb_queue_head_init(&chan->srej_q);
|
||||
skb_queue_head_init(&chan->busy_q);
|
||||
|
||||
INIT_LIST_HEAD(&chan->srej_l);
|
||||
|
||||
INIT_WORK(&chan->busy_work, l2cap_busy_work);
|
||||
|
||||
sk->sk_backlog_rcv = l2cap_ertm_data_rcv;
|
||||
}
|
||||
|
@ -2284,9 +2277,9 @@ done:
|
|||
|
||||
static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
|
||||
{
|
||||
struct l2cap_cmd_rej *rej = (struct l2cap_cmd_rej *) data;
|
||||
struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data;
|
||||
|
||||
if (rej->reason != 0x0000)
|
||||
if (rej->reason != L2CAP_REJ_NOT_UNDERSTOOD)
|
||||
return 0;
|
||||
|
||||
if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&
|
||||
|
@ -2532,9 +2525,12 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
|
|||
|
||||
if ((bt_sk(sk)->defer_setup && chan->state != BT_CONNECT2) ||
|
||||
(!bt_sk(sk)->defer_setup && chan->state != BT_CONFIG)) {
|
||||
struct l2cap_cmd_rej rej;
|
||||
struct l2cap_cmd_rej_cid rej;
|
||||
|
||||
rej.reason = cpu_to_le16(L2CAP_REJ_INVALID_CID);
|
||||
rej.scid = cpu_to_le16(chan->scid);
|
||||
rej.dcid = cpu_to_le16(chan->dcid);
|
||||
|
||||
rej.reason = cpu_to_le16(0x0002);
|
||||
l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
|
||||
sizeof(rej), &rej);
|
||||
goto unlock;
|
||||
|
@ -3025,12 +3021,12 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
|
|||
err = l2cap_bredr_sig_cmd(conn, &cmd, cmd_len, data);
|
||||
|
||||
if (err) {
|
||||
struct l2cap_cmd_rej rej;
|
||||
struct l2cap_cmd_rej_unk rej;
|
||||
|
||||
BT_ERR("Wrong link type (%d)", err);
|
||||
|
||||
/* FIXME: Map err to a valid reason */
|
||||
rej.reason = cpu_to_le16(0);
|
||||
rej.reason = cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
|
||||
l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
|
||||
}
|
||||
|
||||
|
@ -3183,32 +3179,27 @@ static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *sk
|
|||
if (!chan->sdu)
|
||||
goto disconnect;
|
||||
|
||||
if (!test_bit(CONN_SAR_RETRY, &chan->conn_state)) {
|
||||
chan->partial_sdu_len += skb->len;
|
||||
chan->partial_sdu_len += skb->len;
|
||||
|
||||
if (chan->partial_sdu_len > chan->imtu)
|
||||
goto drop;
|
||||
if (chan->partial_sdu_len > chan->imtu)
|
||||
goto drop;
|
||||
|
||||
if (chan->partial_sdu_len != chan->sdu_len)
|
||||
goto drop;
|
||||
if (chan->partial_sdu_len != chan->sdu_len)
|
||||
goto drop;
|
||||
|
||||
memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
|
||||
}
|
||||
memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
|
||||
|
||||
_skb = skb_clone(chan->sdu, GFP_ATOMIC);
|
||||
if (!_skb) {
|
||||
set_bit(CONN_SAR_RETRY, &chan->conn_state);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = chan->ops->recv(chan->data, _skb);
|
||||
if (err < 0) {
|
||||
kfree_skb(_skb);
|
||||
set_bit(CONN_SAR_RETRY, &chan->conn_state);
|
||||
return err;
|
||||
}
|
||||
|
||||
clear_bit(CONN_SAR_RETRY, &chan->conn_state);
|
||||
clear_bit(CONN_SAR_SDU, &chan->conn_state);
|
||||
|
||||
kfree_skb(chan->sdu);
|
||||
|
@ -3228,22 +3219,26 @@ disconnect:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_try_push_rx_skb(struct l2cap_chan *chan)
|
||||
static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u16 control;
|
||||
int err;
|
||||
|
||||
while ((skb = skb_dequeue(&chan->busy_q))) {
|
||||
control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
|
||||
err = l2cap_ertm_reassembly_sdu(chan, skb, control);
|
||||
if (err < 0) {
|
||||
skb_queue_head(&chan->busy_q, skb);
|
||||
return -EBUSY;
|
||||
}
|
||||
BT_DBG("chan %p, Enter local busy", chan);
|
||||
|
||||
chan->buffer_seq = (chan->buffer_seq + 1) % 64;
|
||||
}
|
||||
set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
|
||||
|
||||
control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
|
||||
control |= L2CAP_SUPER_RCV_NOT_READY;
|
||||
l2cap_send_sframe(chan, control);
|
||||
|
||||
set_bit(CONN_RNR_SENT, &chan->conn_state);
|
||||
|
||||
__clear_ack_timer(chan);
|
||||
}
|
||||
|
||||
static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan)
|
||||
{
|
||||
u16 control;
|
||||
|
||||
if (!test_bit(CONN_RNR_SENT, &chan->conn_state))
|
||||
goto done;
|
||||
|
@ -3263,93 +3258,16 @@ done:
|
|||
clear_bit(CONN_RNR_SENT, &chan->conn_state);
|
||||
|
||||
BT_DBG("chan %p, Exit local busy", chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void l2cap_busy_work(struct work_struct *work)
|
||||
void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
struct l2cap_chan *chan =
|
||||
container_of(work, struct l2cap_chan, busy_work);
|
||||
struct sock *sk = chan->sk;
|
||||
int n_tries = 0, timeo = HZ/5, err;
|
||||
struct sk_buff *skb;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
add_wait_queue(sk_sleep(sk), &wait);
|
||||
while ((skb = skb_peek(&chan->busy_q))) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
|
||||
if (n_tries++ > L2CAP_LOCAL_BUSY_TRIES) {
|
||||
err = -EBUSY;
|
||||
l2cap_send_disconn_req(chan->conn, chan, EBUSY);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!timeo)
|
||||
timeo = HZ/5;
|
||||
|
||||
if (signal_pending(current)) {
|
||||
err = sock_intr_errno(timeo);
|
||||
break;
|
||||
}
|
||||
|
||||
release_sock(sk);
|
||||
timeo = schedule_timeout(timeo);
|
||||
lock_sock(sk);
|
||||
|
||||
err = sock_error(sk);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
if (l2cap_try_push_rx_skb(chan) == 0)
|
||||
break;
|
||||
if (chan->mode == L2CAP_MODE_ERTM) {
|
||||
if (busy)
|
||||
l2cap_ertm_enter_local_busy(chan);
|
||||
else
|
||||
l2cap_ertm_exit_local_busy(chan);
|
||||
}
|
||||
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(sk_sleep(sk), &wait);
|
||||
|
||||
release_sock(sk);
|
||||
}
|
||||
|
||||
static int l2cap_push_rx_skb(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
|
||||
{
|
||||
int sctrl, err;
|
||||
|
||||
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
|
||||
bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT;
|
||||
__skb_queue_tail(&chan->busy_q, skb);
|
||||
return l2cap_try_push_rx_skb(chan);
|
||||
|
||||
|
||||
}
|
||||
|
||||
err = l2cap_ertm_reassembly_sdu(chan, skb, control);
|
||||
if (err >= 0) {
|
||||
chan->buffer_seq = (chan->buffer_seq + 1) % 64;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Busy Condition */
|
||||
BT_DBG("chan %p, Enter local busy", chan);
|
||||
|
||||
set_bit(CONN_LOCAL_BUSY, &chan->conn_state);
|
||||
bt_cb(skb)->sar = control >> L2CAP_CTRL_SAR_SHIFT;
|
||||
__skb_queue_tail(&chan->busy_q, skb);
|
||||
|
||||
sctrl = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
|
||||
sctrl |= L2CAP_SUPER_RCV_NOT_READY;
|
||||
l2cap_send_sframe(chan, sctrl);
|
||||
|
||||
set_bit(CONN_RNR_SENT, &chan->conn_state);
|
||||
|
||||
__clear_ack_timer(chan);
|
||||
|
||||
queue_work(_busy_wq, &chan->busy_work);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control)
|
||||
|
@ -3450,13 +3368,22 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
|
|||
struct sk_buff *skb;
|
||||
u16 control;
|
||||
|
||||
while ((skb = skb_peek(&chan->srej_q))) {
|
||||
while ((skb = skb_peek(&chan->srej_q)) &&
|
||||
!test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
|
||||
int err;
|
||||
|
||||
if (bt_cb(skb)->tx_seq != tx_seq)
|
||||
break;
|
||||
|
||||
skb = skb_dequeue(&chan->srej_q);
|
||||
control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
|
||||
l2cap_ertm_reassembly_sdu(chan, skb, control);
|
||||
err = l2cap_ertm_reassembly_sdu(chan, skb, control);
|
||||
|
||||
if (err < 0) {
|
||||
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
|
||||
break;
|
||||
}
|
||||
|
||||
chan->buffer_seq_srej =
|
||||
(chan->buffer_seq_srej + 1) % 64;
|
||||
tx_seq = (tx_seq + 1) % 64;
|
||||
|
@ -3523,9 +3450,6 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
|
|||
chan->expected_ack_seq = req_seq;
|
||||
l2cap_drop_acked_frames(chan);
|
||||
|
||||
if (tx_seq == chan->expected_tx_seq)
|
||||
goto expected;
|
||||
|
||||
tx_seq_offset = (tx_seq - chan->buffer_seq) % 64;
|
||||
if (tx_seq_offset < 0)
|
||||
tx_seq_offset += 64;
|
||||
|
@ -3539,6 +3463,9 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
|
|||
if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state))
|
||||
goto drop;
|
||||
|
||||
if (tx_seq == chan->expected_tx_seq)
|
||||
goto expected;
|
||||
|
||||
if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) {
|
||||
struct srej_list *first;
|
||||
|
||||
|
@ -3590,7 +3517,6 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
|
|||
chan->buffer_seq_srej = chan->buffer_seq;
|
||||
|
||||
__skb_queue_head_init(&chan->srej_q);
|
||||
__skb_queue_head_init(&chan->busy_q);
|
||||
l2cap_add_to_srej_queue(chan, skb, tx_seq, sar);
|
||||
|
||||
set_bit(CONN_SEND_PBIT, &chan->conn_state);
|
||||
|
@ -3611,9 +3537,12 @@ expected:
|
|||
return 0;
|
||||
}
|
||||
|
||||
err = l2cap_push_rx_skb(chan, skb, rx_control);
|
||||
if (err < 0)
|
||||
return 0;
|
||||
err = l2cap_ertm_reassembly_sdu(chan, skb, rx_control);
|
||||
chan->buffer_seq = (chan->buffer_seq + 1) % 64;
|
||||
if (err < 0) {
|
||||
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (rx_control & L2CAP_CTRL_FINAL) {
|
||||
if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state))
|
||||
|
@ -4108,7 +4037,7 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
|||
if (conn)
|
||||
l2cap_conn_ready(conn);
|
||||
} else
|
||||
l2cap_conn_del(hcon, bt_err(status));
|
||||
l2cap_conn_del(hcon, bt_to_errno(status));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -4132,7 +4061,7 @@ static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
|
|||
if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK))
|
||||
return -EINVAL;
|
||||
|
||||
l2cap_conn_del(hcon, bt_err(reason));
|
||||
l2cap_conn_del(hcon, bt_to_errno(reason));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -4178,6 +4107,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
|||
chan->sec_level = hcon->sec_level;
|
||||
del_timer(&conn->security_timer);
|
||||
l2cap_chan_ready(sk);
|
||||
smp_distribute_keys(conn, 0);
|
||||
}
|
||||
|
||||
bh_unlock_sock(sk);
|
||||
|
@ -4415,12 +4345,6 @@ int __init l2cap_init(void)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
_busy_wq = create_singlethread_workqueue("l2cap");
|
||||
if (!_busy_wq) {
|
||||
err = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = hci_register_proto(&l2cap_hci_proto);
|
||||
if (err < 0) {
|
||||
BT_ERR("L2CAP protocol registration failed");
|
||||
|
@ -4438,7 +4362,6 @@ int __init l2cap_init(void)
|
|||
return 0;
|
||||
|
||||
error:
|
||||
destroy_workqueue(_busy_wq);
|
||||
l2cap_cleanup_sockets();
|
||||
return err;
|
||||
}
|
||||
|
@ -4447,9 +4370,6 @@ void l2cap_exit(void)
|
|||
{
|
||||
debugfs_remove(l2cap_debugfs);
|
||||
|
||||
flush_workqueue(_busy_wq);
|
||||
destroy_workqueue(_busy_wq);
|
||||
|
||||
if (hci_unregister_proto(&l2cap_hci_proto) < 0)
|
||||
BT_ERR("L2CAP protocol unregistration failed");
|
||||
|
||||
|
|
|
@ -422,8 +422,12 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
|||
break;
|
||||
}
|
||||
|
||||
memset(&sec, 0, sizeof(sec));
|
||||
sec.level = chan->sec_level;
|
||||
|
||||
if (sk->sk_state == BT_CONNECTED)
|
||||
sec.key_size = chan->conn->hcon->enc_key_size;
|
||||
|
||||
len = min_t(unsigned int, len, sizeof(sec));
|
||||
if (copy_to_user(optval, (char *) &sec, len))
|
||||
err = -EFAULT;
|
||||
|
@ -711,13 +715,15 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
|
|||
static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags)
|
||||
{
|
||||
struct sock *sk = sock->sk;
|
||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
||||
int err;
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) {
|
||||
sk->sk_state = BT_CONFIG;
|
||||
|
||||
__l2cap_connect_rsp_defer(l2cap_pi(sk)->chan);
|
||||
__l2cap_connect_rsp_defer(pi->chan);
|
||||
release_sock(sk);
|
||||
return 0;
|
||||
}
|
||||
|
@ -725,9 +731,37 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct ms
|
|||
release_sock(sk);
|
||||
|
||||
if (sock->type == SOCK_STREAM)
|
||||
return bt_sock_stream_recvmsg(iocb, sock, msg, len, flags);
|
||||
err = bt_sock_stream_recvmsg(iocb, sock, msg, len, flags);
|
||||
else
|
||||
err = bt_sock_recvmsg(iocb, sock, msg, len, flags);
|
||||
|
||||
return bt_sock_recvmsg(iocb, sock, msg, len, flags);
|
||||
if (pi->chan->mode != L2CAP_MODE_ERTM)
|
||||
return err;
|
||||
|
||||
/* Attempt to put pending rx data in the socket buffer */
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (!test_bit(CONN_LOCAL_BUSY, &pi->chan->conn_state))
|
||||
goto done;
|
||||
|
||||
if (pi->rx_busy_skb) {
|
||||
if (!sock_queue_rcv_skb(sk, pi->rx_busy_skb))
|
||||
pi->rx_busy_skb = NULL;
|
||||
else
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Restore data flow when half of the receive buffer is
|
||||
* available. This avoids resending large numbers of
|
||||
* frames.
|
||||
*/
|
||||
if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf >> 1)
|
||||
l2cap_chan_busy(pi->chan, 0);
|
||||
|
||||
done:
|
||||
release_sock(sk);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Kill socket (only if zapped and orphan)
|
||||
|
@ -811,9 +845,31 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(void *data)
|
|||
|
||||
static int l2cap_sock_recv_cb(void *data, struct sk_buff *skb)
|
||||
{
|
||||
int err;
|
||||
struct sock *sk = data;
|
||||
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
||||
|
||||
return sock_queue_rcv_skb(sk, skb);
|
||||
if (pi->rx_busy_skb)
|
||||
return -ENOMEM;
|
||||
|
||||
err = sock_queue_rcv_skb(sk, skb);
|
||||
|
||||
/* For ERTM, handle one skb that doesn't fit into the recv
|
||||
* buffer. This is important to do because the data frames
|
||||
* have already been acked, so the skb cannot be discarded.
|
||||
*
|
||||
* Notify the l2cap core that the buffer is full, so the
|
||||
* LOCAL_BUSY state is entered and no more frames are
|
||||
* acked and reassembled until there is buffer space
|
||||
* available.
|
||||
*/
|
||||
if (err < 0 && pi->chan->mode == L2CAP_MODE_ERTM) {
|
||||
pi->rx_busy_skb = skb;
|
||||
l2cap_chan_busy(pi->chan, 1);
|
||||
err = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void l2cap_sock_close_cb(void *data)
|
||||
|
@ -842,6 +898,11 @@ static void l2cap_sock_destruct(struct sock *sk)
|
|||
{
|
||||
BT_DBG("sk %p", sk);
|
||||
|
||||
if (l2cap_pi(sk)->rx_busy_skb) {
|
||||
kfree_skb(l2cap_pi(sk)->rx_busy_skb);
|
||||
l2cap_pi(sk)->rx_busy_skb = NULL;
|
||||
}
|
||||
|
||||
skb_queue_purge(&sk->sk_receive_queue);
|
||||
skb_queue_purge(&sk->sk_write_queue);
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ char *batostr(bdaddr_t *ba)
|
|||
EXPORT_SYMBOL(batostr);
|
||||
|
||||
/* Bluetooth error codes to Unix errno mapping */
|
||||
int bt_err(__u16 code)
|
||||
int bt_to_errno(__u16 code)
|
||||
{
|
||||
switch (code) {
|
||||
case 0:
|
||||
|
@ -149,4 +149,23 @@ int bt_err(__u16 code)
|
|||
return ENOSYS;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(bt_err);
|
||||
EXPORT_SYMBOL(bt_to_errno);
|
||||
|
||||
int bt_printk(const char *level, const char *format, ...)
|
||||
{
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
int r;
|
||||
|
||||
va_start(args, format);
|
||||
|
||||
vaf.fmt = format;
|
||||
vaf.va = &args;
|
||||
|
||||
r = printk("%sBluetooth: %pV\n", level, &vaf);
|
||||
|
||||
va_end(args);
|
||||
|
||||
return r;
|
||||
}
|
||||
EXPORT_SYMBOL(bt_printk);
|
||||
|
|
|
@ -179,7 +179,7 @@ static int read_controller_info(struct sock *sk, u16 index)
|
|||
|
||||
hci_del_off_timer(hdev);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
set_bit(HCI_MGMT, &hdev->flags);
|
||||
|
||||
|
@ -208,7 +208,7 @@ static int read_controller_info(struct sock *sk, u16 index)
|
|||
|
||||
memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp));
|
||||
|
@ -316,7 +316,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_POWERED, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
up = test_bit(HCI_UP, &hdev->flags);
|
||||
if ((cp->val && up) || (!cp->val && !up)) {
|
||||
|
@ -343,7 +343,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
err = 0;
|
||||
|
||||
failed:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
return err;
|
||||
}
|
||||
|
@ -368,7 +368,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENETDOWN);
|
||||
|
@ -403,7 +403,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
|
|||
mgmt_pending_remove(cmd);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -429,7 +429,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENETDOWN);
|
||||
|
@ -463,7 +463,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data,
|
|||
mgmt_pending_remove(cmd);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -522,7 +522,7 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
if (cp->val)
|
||||
set_bit(HCI_PAIRABLE, &hdev->flags);
|
||||
|
@ -538,7 +538,7 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data,
|
|||
err = mgmt_event(MGMT_EV_PAIRABLE, index, &ev, sizeof(ev), sk);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -739,7 +739,7 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_ADD_UUID, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);
|
||||
if (!uuid) {
|
||||
|
@ -763,7 +763,7 @@ static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -788,7 +788,7 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
|
||||
err = hci_uuids_clear(hdev);
|
||||
|
@ -823,7 +823,7 @@ static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -847,7 +847,7 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
hdev->major_class = cp->major;
|
||||
hdev->minor_class = cp->minor;
|
||||
|
@ -857,7 +857,7 @@ static int set_dev_class(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (err == 0)
|
||||
err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -879,7 +879,7 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
BT_DBG("hci%u enable %d", index, cp->enable);
|
||||
|
||||
|
@ -897,7 +897,7 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data,
|
|||
err = cmd_complete(sk, index, MGMT_OP_SET_SERVICE_CACHE, NULL,
|
||||
0);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -908,7 +908,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
struct hci_dev *hdev;
|
||||
struct mgmt_cp_load_keys *cp;
|
||||
u16 key_count, expected_len;
|
||||
int i;
|
||||
int i, err;
|
||||
|
||||
cp = (void *) data;
|
||||
|
||||
|
@ -918,9 +918,9 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
key_count = get_unaligned_le16(&cp->key_count);
|
||||
|
||||
expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info);
|
||||
if (expected_len != len) {
|
||||
BT_ERR("load_keys: expected %u bytes, got %u bytes",
|
||||
len, expected_len);
|
||||
if (expected_len > len) {
|
||||
BT_ERR("load_keys: expected at least %u bytes, got %u bytes",
|
||||
expected_len, len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -931,7 +931,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
|
||||
key_count);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
hci_link_keys_clear(hdev);
|
||||
|
||||
|
@ -942,17 +942,36 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
else
|
||||
clear_bit(HCI_DEBUG_KEYS, &hdev->flags);
|
||||
|
||||
for (i = 0; i < key_count; i++) {
|
||||
struct mgmt_key_info *key = &cp->keys[i];
|
||||
len -= sizeof(*cp);
|
||||
i = 0;
|
||||
|
||||
while (i < len) {
|
||||
struct mgmt_key_info *key = (void *) cp->keys + i;
|
||||
|
||||
i += sizeof(*key) + key->dlen;
|
||||
|
||||
if (key->type == HCI_LK_SMP_LTK) {
|
||||
struct key_master_id *id = (void *) key->data;
|
||||
|
||||
if (key->dlen != sizeof(struct key_master_id))
|
||||
continue;
|
||||
|
||||
hci_add_ltk(hdev, 0, &key->bdaddr, key->pin_len,
|
||||
id->ediv, id->rand, key->val);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type,
|
||||
key->pin_len);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
err = cmd_complete(sk, index, MGMT_OP_LOAD_KEYS, NULL, 0);
|
||||
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
||||
|
@ -971,7 +990,7 @@ static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
err = hci_remove_link_key(hdev, &cp->bdaddr);
|
||||
if (err < 0) {
|
||||
|
@ -994,7 +1013,7 @@ static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1020,7 +1039,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_DISCONNECT, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENETDOWN);
|
||||
|
@ -1055,7 +1074,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
mgmt_pending_remove(cmd);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1076,7 +1095,7 @@ static int get_connections(struct sock *sk, u16 index)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
count = 0;
|
||||
list_for_each(p, &hdev->conn_hash.list) {
|
||||
|
@ -1103,7 +1122,7 @@ static int get_connections(struct sock *sk, u16 index)
|
|||
|
||||
unlock:
|
||||
kfree(rp);
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
return err;
|
||||
}
|
||||
|
@ -1149,7 +1168,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENETDOWN);
|
||||
|
@ -1190,7 +1209,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
|
|||
mgmt_pending_remove(cmd);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1216,7 +1235,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
|
|||
return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
|
||||
ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
|
||||
|
@ -1227,7 +1246,7 @@ static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data,
|
|||
err = send_pin_code_neg_reply(sk, index, hdev, cp);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1250,14 +1269,14 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
hdev->io_capability = cp->io_capability;
|
||||
|
||||
BT_DBG("%s IO capability set to 0x%02x", hdev->name,
|
||||
hdev->io_capability);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0);
|
||||
|
@ -1343,7 +1362,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
if (cp->io_cap == 0x03) {
|
||||
sec_level = BT_SECURITY_MEDIUM;
|
||||
|
@ -1385,7 +1404,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len)
|
|||
err = 0;
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1417,7 +1436,7 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, mgmt_op, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, mgmt_op, ENETDOWN);
|
||||
|
@ -1435,7 +1454,7 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data,
|
|||
mgmt_pending_remove(cmd);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1459,7 +1478,7 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
|
|||
if (!hdev)
|
||||
return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, index, data, len);
|
||||
if (!cmd) {
|
||||
|
@ -1474,7 +1493,7 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
|
|||
mgmt_pending_remove(cmd);
|
||||
|
||||
failed:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1493,7 +1512,7 @@ static int read_local_oob_data(struct sock *sk, u16 index)
|
|||
return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
|
||||
ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags)) {
|
||||
err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
|
||||
|
@ -1523,7 +1542,7 @@ static int read_local_oob_data(struct sock *sk, u16 index)
|
|||
mgmt_pending_remove(cmd);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1547,7 +1566,7 @@ static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data,
|
|||
return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
|
||||
ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash,
|
||||
cp->randomizer);
|
||||
|
@ -1557,7 +1576,7 @@ static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data,
|
|||
err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
|
||||
0);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1581,7 +1600,7 @@ static int remove_remote_oob_data(struct sock *sk, u16 index,
|
|||
return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
|
||||
ENODEV);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
hci_dev_lock_bh(hdev);
|
||||
|
||||
err = hci_remove_remote_oob_data(hdev, &cp->bdaddr);
|
||||
if (err < 0)
|
||||
|
@ -1591,7 +1610,7 @@ static int remove_remote_oob_data(struct sock *sk, u16 index,
|
|||
err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
|
||||
NULL, 0);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
hci_dev_unlock_bh(hdev);
|
||||
hci_dev_put(hdev);
|
||||
|
||||
return err;
|
||||
|
@ -1958,17 +1977,28 @@ int mgmt_connectable(u16 index, u8 connectable)
|
|||
|
||||
int mgmt_new_key(u16 index, struct link_key *key, u8 persistent)
|
||||
{
|
||||
struct mgmt_ev_new_key ev;
|
||||
struct mgmt_ev_new_key *ev;
|
||||
int err, total;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
total = sizeof(struct mgmt_ev_new_key) + key->dlen;
|
||||
ev = kzalloc(total, GFP_ATOMIC);
|
||||
if (!ev)
|
||||
return -ENOMEM;
|
||||
|
||||
ev.store_hint = persistent;
|
||||
bacpy(&ev.key.bdaddr, &key->bdaddr);
|
||||
ev.key.type = key->type;
|
||||
memcpy(ev.key.val, key->val, 16);
|
||||
ev.key.pin_len = key->pin_len;
|
||||
bacpy(&ev->key.bdaddr, &key->bdaddr);
|
||||
ev->key.type = key->type;
|
||||
memcpy(ev->key.val, key->val, 16);
|
||||
ev->key.pin_len = key->pin_len;
|
||||
ev->key.dlen = key->dlen;
|
||||
ev->store_hint = persistent;
|
||||
|
||||
return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL);
|
||||
memcpy(ev->key.data, key->data, key->dlen);
|
||||
|
||||
err = mgmt_event(MGMT_EV_NEW_KEY, index, ev, total, NULL);
|
||||
|
||||
kfree(ev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int mgmt_connected(u16 index, bdaddr_t *bdaddr)
|
||||
|
|
|
@ -932,7 +932,7 @@ static int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
|
|||
if (conn)
|
||||
sco_conn_ready(conn);
|
||||
} else
|
||||
sco_conn_del(hcon, bt_err(status));
|
||||
sco_conn_del(hcon, bt_to_errno(status));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -944,7 +944,7 @@ static int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
|
|||
if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
|
||||
return -EINVAL;
|
||||
|
||||
sco_conn_del(hcon, bt_err(reason));
|
||||
sco_conn_del(hcon, bt_to_errno(reason));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -197,14 +197,34 @@ static __u8 seclevel_to_authreq(__u8 level)
|
|||
}
|
||||
|
||||
static void build_pairing_cmd(struct l2cap_conn *conn,
|
||||
struct smp_cmd_pairing *cmd, __u8 authreq)
|
||||
struct smp_cmd_pairing *req,
|
||||
struct smp_cmd_pairing *rsp,
|
||||
__u8 authreq)
|
||||
{
|
||||
cmd->io_capability = conn->hcon->io_capability;
|
||||
cmd->oob_flag = SMP_OOB_NOT_PRESENT;
|
||||
cmd->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
cmd->init_key_dist = 0x00;
|
||||
cmd->resp_key_dist = 0x00;
|
||||
cmd->auth_req = authreq;
|
||||
u8 dist_keys;
|
||||
|
||||
dist_keys = 0;
|
||||
if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) {
|
||||
dist_keys = SMP_DIST_ENC_KEY | SMP_DIST_ID_KEY | SMP_DIST_SIGN;
|
||||
authreq |= SMP_AUTH_BONDING;
|
||||
}
|
||||
|
||||
if (rsp == NULL) {
|
||||
req->io_capability = conn->hcon->io_capability;
|
||||
req->oob_flag = SMP_OOB_NOT_PRESENT;
|
||||
req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
req->init_key_dist = dist_keys;
|
||||
req->resp_key_dist = dist_keys;
|
||||
req->auth_req = authreq;
|
||||
return;
|
||||
}
|
||||
|
||||
rsp->io_capability = conn->hcon->io_capability;
|
||||
rsp->oob_flag = SMP_OOB_NOT_PRESENT;
|
||||
rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
rsp->init_key_dist = req->init_key_dist & dist_keys;
|
||||
rsp->resp_key_dist = req->resp_key_dist & dist_keys;
|
||||
rsp->auth_req = authreq;
|
||||
}
|
||||
|
||||
static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
|
||||
|
@ -233,7 +253,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
return SMP_OOB_NOT_AVAIL;
|
||||
|
||||
/* We didn't start the pairing, so no requirements */
|
||||
build_pairing_cmd(conn, &rsp, SMP_AUTH_NONE);
|
||||
build_pairing_cmd(conn, req, &rsp, SMP_AUTH_NONE);
|
||||
|
||||
key_size = min(req->max_key_size, rsp.max_key_size);
|
||||
if (check_enc_key_size(conn, key_size))
|
||||
|
@ -347,8 +367,6 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
swap128(skb->data, random);
|
||||
skb_pull(skb, sizeof(random));
|
||||
|
||||
memset(hcon->ltk, 0, sizeof(hcon->ltk));
|
||||
|
||||
if (conn->hcon->out)
|
||||
ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0,
|
||||
conn->src, conn->hcon->dst_type, conn->dst,
|
||||
|
@ -370,29 +388,38 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
}
|
||||
|
||||
if (conn->hcon->out) {
|
||||
u8 stk[16], rand[8];
|
||||
__le16 ediv;
|
||||
u8 rand[8];
|
||||
|
||||
smp_s1(tfm, conn->tk, random, conn->prnd, key);
|
||||
swap128(key, hcon->ltk);
|
||||
|
||||
memset(hcon->ltk + conn->smp_key_size, 0,
|
||||
SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
|
||||
|
||||
memset(rand, 0, sizeof(rand));
|
||||
ediv = 0;
|
||||
hci_le_start_enc(hcon, ediv, rand, hcon->ltk);
|
||||
|
||||
smp_s1(tfm, conn->tk, random, conn->prnd, key);
|
||||
swap128(key, stk);
|
||||
|
||||
memset(stk + conn->smp_key_size, 0,
|
||||
SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
|
||||
|
||||
hci_le_start_enc(hcon, ediv, rand, stk);
|
||||
hcon->enc_key_size = conn->smp_key_size;
|
||||
} else {
|
||||
u8 r[16];
|
||||
u8 stk[16], r[16], rand[8];
|
||||
__le16 ediv;
|
||||
|
||||
memset(rand, 0, sizeof(rand));
|
||||
ediv = 0;
|
||||
|
||||
swap128(conn->prnd, r);
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r);
|
||||
|
||||
smp_s1(tfm, conn->tk, conn->prnd, random, key);
|
||||
swap128(key, hcon->ltk);
|
||||
swap128(key, stk);
|
||||
|
||||
memset(hcon->ltk + conn->smp_key_size, 0,
|
||||
memset(stk + conn->smp_key_size, 0,
|
||||
SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size);
|
||||
|
||||
hci_add_ltk(conn->hcon->hdev, 0, conn->dst, conn->smp_key_size,
|
||||
ediv, rand, stk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -412,7 +439,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
skb_pull(skb, sizeof(*rp));
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
build_pairing_cmd(conn, &cp, rp->auth_req);
|
||||
build_pairing_cmd(conn, &cp, NULL, rp->auth_req);
|
||||
|
||||
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
||||
|
@ -434,6 +461,9 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
|
|||
|
||||
BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
|
||||
|
||||
if (!lmp_host_le_capable(hcon->hdev))
|
||||
return 1;
|
||||
|
||||
if (IS_ERR(hcon->hdev->tfm))
|
||||
return 1;
|
||||
|
||||
|
@ -450,8 +480,21 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
|
|||
|
||||
if (hcon->link_mode & HCI_LM_MASTER) {
|
||||
struct smp_cmd_pairing cp;
|
||||
struct link_key *key;
|
||||
|
||||
build_pairing_cmd(conn, &cp, authreq);
|
||||
key = hci_find_link_key_type(hcon->hdev, conn->dst,
|
||||
HCI_LK_SMP_LTK);
|
||||
if (key) {
|
||||
struct key_master_id *master = (void *) key->data;
|
||||
|
||||
hci_le_start_enc(hcon, master->ediv, master->rand,
|
||||
key->val);
|
||||
hcon->enc_key_size = key->pin_len;
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
build_pairing_cmd(conn, &cp, NULL, authreq);
|
||||
conn->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||
memcpy(&conn->preq[1], &cp, sizeof(cp));
|
||||
|
||||
|
@ -465,18 +508,50 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
|
|||
smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
done:
|
||||
hcon->pending_sec_level = sec_level;
|
||||
set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct smp_cmd_encrypt_info *rp = (void *) skb->data;
|
||||
|
||||
skb_pull(skb, sizeof(*rp));
|
||||
|
||||
memcpy(conn->tk, rp->ltk, sizeof(conn->tk));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct smp_cmd_master_ident *rp = (void *) skb->data;
|
||||
|
||||
skb_pull(skb, sizeof(*rp));
|
||||
|
||||
hci_add_ltk(conn->hcon->hdev, 1, conn->src, conn->smp_key_size,
|
||||
rp->ediv, rp->rand, conn->tk);
|
||||
|
||||
smp_distribute_keys(conn, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
__u8 code = skb->data[0];
|
||||
__u8 reason;
|
||||
int err = 0;
|
||||
|
||||
if (!lmp_host_le_capable(conn->hcon->hdev)) {
|
||||
err = -ENOTSUPP;
|
||||
reason = SMP_PAIRING_NOTSUPP;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (IS_ERR(conn->hcon->hdev->tfm)) {
|
||||
err = PTR_ERR(conn->hcon->hdev->tfm);
|
||||
reason = SMP_PAIRING_NOTSUPP;
|
||||
|
@ -512,10 +587,20 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
|
|||
break;
|
||||
|
||||
case SMP_CMD_ENCRYPT_INFO:
|
||||
reason = smp_cmd_encrypt_info(conn, skb);
|
||||
break;
|
||||
|
||||
case SMP_CMD_MASTER_IDENT:
|
||||
reason = smp_cmd_master_ident(conn, skb);
|
||||
break;
|
||||
|
||||
case SMP_CMD_IDENT_INFO:
|
||||
case SMP_CMD_IDENT_ADDR_INFO:
|
||||
case SMP_CMD_SIGN_INFO:
|
||||
/* Just ignored */
|
||||
reason = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_DBG("Unknown command code 0x%2.2x", code);
|
||||
|
||||
|
@ -532,3 +617,86 @@ done:
|
|||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
|
||||
{
|
||||
struct smp_cmd_pairing *req, *rsp;
|
||||
__u8 *keydist;
|
||||
|
||||
BT_DBG("conn %p force %d", conn, force);
|
||||
|
||||
if (IS_ERR(conn->hcon->hdev->tfm))
|
||||
return PTR_ERR(conn->hcon->hdev->tfm);
|
||||
|
||||
rsp = (void *) &conn->prsp[1];
|
||||
|
||||
/* The responder sends its keys first */
|
||||
if (!force && conn->hcon->out && (rsp->resp_key_dist & 0x07))
|
||||
return 0;
|
||||
|
||||
req = (void *) &conn->preq[1];
|
||||
|
||||
if (conn->hcon->out) {
|
||||
keydist = &rsp->init_key_dist;
|
||||
*keydist &= req->init_key_dist;
|
||||
} else {
|
||||
keydist = &rsp->resp_key_dist;
|
||||
*keydist &= req->resp_key_dist;
|
||||
}
|
||||
|
||||
|
||||
BT_DBG("keydist 0x%x", *keydist);
|
||||
|
||||
if (*keydist & SMP_DIST_ENC_KEY) {
|
||||
struct smp_cmd_encrypt_info enc;
|
||||
struct smp_cmd_master_ident ident;
|
||||
__le16 ediv;
|
||||
|
||||
get_random_bytes(enc.ltk, sizeof(enc.ltk));
|
||||
get_random_bytes(&ediv, sizeof(ediv));
|
||||
get_random_bytes(ident.rand, sizeof(ident.rand));
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc);
|
||||
|
||||
hci_add_ltk(conn->hcon->hdev, 1, conn->dst, conn->smp_key_size,
|
||||
ediv, ident.rand, enc.ltk);
|
||||
|
||||
ident.ediv = cpu_to_le16(ediv);
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_MASTER_IDENT, sizeof(ident), &ident);
|
||||
|
||||
*keydist &= ~SMP_DIST_ENC_KEY;
|
||||
}
|
||||
|
||||
if (*keydist & SMP_DIST_ID_KEY) {
|
||||
struct smp_cmd_ident_addr_info addrinfo;
|
||||
struct smp_cmd_ident_info idinfo;
|
||||
|
||||
/* Send a dummy key */
|
||||
get_random_bytes(idinfo.irk, sizeof(idinfo.irk));
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_IDENT_INFO, sizeof(idinfo), &idinfo);
|
||||
|
||||
/* Just public address */
|
||||
memset(&addrinfo, 0, sizeof(addrinfo));
|
||||
bacpy(&addrinfo.bdaddr, conn->src);
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo),
|
||||
&addrinfo);
|
||||
|
||||
*keydist &= ~SMP_DIST_ID_KEY;
|
||||
}
|
||||
|
||||
if (*keydist & SMP_DIST_SIGN) {
|
||||
struct smp_cmd_sign_info sign;
|
||||
|
||||
/* Send a dummy key */
|
||||
get_random_bytes(sign.csrk, sizeof(sign.csrk));
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_SIGN_INFO, sizeof(sign), &sign);
|
||||
|
||||
*keydist &= ~SMP_DIST_SIGN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue