forked from Minki/linux
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/padovan/bluetooth-next
This commit is contained in:
commit
dc0d633e35
@ -101,6 +101,7 @@ static struct usb_device_id btusb_table[] = {
|
||||
{ USB_DEVICE(0x0c10, 0x0000) },
|
||||
|
||||
/* Broadcom BCM20702A0 */
|
||||
{ USB_DEVICE(0x0a5c, 0x21e3) },
|
||||
{ USB_DEVICE(0x413c, 0x8197) },
|
||||
|
||||
{ } /* Terminating entry */
|
||||
@ -508,15 +509,10 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
|
||||
|
||||
pipe = usb_rcvisocpipe(data->udev, data->isoc_rx_ep->bEndpointAddress);
|
||||
|
||||
urb->dev = data->udev;
|
||||
urb->pipe = pipe;
|
||||
urb->context = hdev;
|
||||
urb->complete = btusb_isoc_complete;
|
||||
urb->interval = data->isoc_rx_ep->bInterval;
|
||||
usb_fill_int_urb(urb, data->udev, pipe, buf, size, btusb_isoc_complete,
|
||||
hdev, data->isoc_rx_ep->bInterval);
|
||||
|
||||
urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
|
||||
urb->transfer_buffer = buf;
|
||||
urb->transfer_buffer_length = size;
|
||||
|
||||
__fill_isoc_descriptor(urb, size,
|
||||
le16_to_cpu(data->isoc_rx_ep->wMaxPacketSize));
|
||||
|
@ -250,32 +250,10 @@ extern void bt_sysfs_cleanup(void);
|
||||
|
||||
extern struct dentry *bt_debugfs;
|
||||
|
||||
#ifdef CONFIG_BT_L2CAP
|
||||
int l2cap_init(void);
|
||||
void l2cap_exit(void);
|
||||
#else
|
||||
static inline int l2cap_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void l2cap_exit(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_SCO
|
||||
int sco_init(void);
|
||||
void sco_exit(void);
|
||||
#else
|
||||
static inline int sco_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sco_exit(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __BLUETOOTH_H */
|
||||
|
@ -280,6 +280,10 @@ enum {
|
||||
#define HCI_ERROR_LOCAL_HOST_TERM 0x16
|
||||
#define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18
|
||||
|
||||
/* Flow control modes */
|
||||
#define HCI_FLOW_CTL_MODE_PACKET_BASED 0x00
|
||||
#define HCI_FLOW_CTL_MODE_BLOCK_BASED 0x01
|
||||
|
||||
/* ----- HCI Commands ---- */
|
||||
#define HCI_OP_NOP 0x0000
|
||||
|
||||
@ -800,6 +804,9 @@ struct hci_cp_le_set_scan_param {
|
||||
__u8 filter_policy;
|
||||
} __packed;
|
||||
|
||||
#define LE_SCANNING_DISABLED 0x00
|
||||
#define LE_SCANNING_ENABLED 0x01
|
||||
|
||||
#define HCI_OP_LE_SET_SCAN_ENABLE 0x200c
|
||||
struct hci_cp_le_set_scan_enable {
|
||||
__u8 enable;
|
||||
@ -975,9 +982,14 @@ struct hci_ev_role_change {
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_NUM_COMP_PKTS 0x13
|
||||
struct hci_comp_pkts_info {
|
||||
__le16 handle;
|
||||
__le16 count;
|
||||
} __packed;
|
||||
|
||||
struct hci_ev_num_comp_pkts {
|
||||
__u8 num_hndl;
|
||||
/* variable length part */
|
||||
struct hci_comp_pkts_info handles[0];
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_MODE_CHANGE 0x14
|
||||
|
@ -28,10 +28,6 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <net/bluetooth/hci.h>
|
||||
|
||||
/* HCI upper protocols */
|
||||
#define HCI_PROTO_L2CAP 0
|
||||
#define HCI_PROTO_SCO 1
|
||||
|
||||
/* HCI priority */
|
||||
#define HCI_PRIO_MAX 7
|
||||
|
||||
@ -54,7 +50,6 @@ struct inquiry_entry {
|
||||
};
|
||||
|
||||
struct inquiry_cache {
|
||||
spinlock_t lock;
|
||||
__u32 timestamp;
|
||||
struct inquiry_entry *list;
|
||||
};
|
||||
@ -314,6 +309,7 @@ struct hci_conn {
|
||||
struct hci_dev *hdev;
|
||||
void *l2cap_data;
|
||||
void *sco_data;
|
||||
void *smp_conn;
|
||||
|
||||
struct hci_conn *link;
|
||||
|
||||
@ -330,25 +326,31 @@ struct hci_chan {
|
||||
unsigned int sent;
|
||||
};
|
||||
|
||||
extern struct hci_proto *hci_proto[];
|
||||
extern struct list_head hci_dev_list;
|
||||
extern struct list_head hci_cb_list;
|
||||
extern rwlock_t hci_dev_list_lock;
|
||||
extern rwlock_t hci_cb_list_lock;
|
||||
|
||||
/* ----- HCI interface to upper protocols ----- */
|
||||
extern int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
extern int l2cap_connect_cfm(struct hci_conn *hcon, u8 status);
|
||||
extern int l2cap_disconn_ind(struct hci_conn *hcon);
|
||||
extern int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason);
|
||||
extern int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt);
|
||||
extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags);
|
||||
|
||||
extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
extern int sco_connect_cfm(struct hci_conn *hcon, __u8 status);
|
||||
extern int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason);
|
||||
extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
|
||||
|
||||
/* ----- Inquiry cache ----- */
|
||||
#define INQUIRY_CACHE_AGE_MAX (HZ*30) /* 30 seconds */
|
||||
#define INQUIRY_ENTRY_AGE_MAX (HZ*60) /* 60 seconds */
|
||||
|
||||
#define inquiry_cache_lock(c) spin_lock(&c->lock)
|
||||
#define inquiry_cache_unlock(c) spin_unlock(&c->lock)
|
||||
#define inquiry_cache_lock_bh(c) spin_lock_bh(&c->lock)
|
||||
#define inquiry_cache_unlock_bh(c) spin_unlock_bh(&c->lock)
|
||||
|
||||
static inline void inquiry_cache_init(struct hci_dev *hdev)
|
||||
{
|
||||
struct inquiry_cache *c = &hdev->inq_cache;
|
||||
spin_lock_init(&c->lock);
|
||||
c->list = NULL;
|
||||
}
|
||||
|
||||
@ -677,53 +679,40 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
||||
#define lmp_host_le_capable(dev) ((dev)->extfeatures[0] & LMP_HOST_LE)
|
||||
|
||||
/* ----- HCI protocols ----- */
|
||||
struct hci_proto {
|
||||
char *name;
|
||||
unsigned int id;
|
||||
unsigned long flags;
|
||||
|
||||
void *priv;
|
||||
|
||||
int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
__u8 type);
|
||||
int (*connect_cfm) (struct hci_conn *conn, __u8 status);
|
||||
int (*disconn_ind) (struct hci_conn *conn);
|
||||
int (*disconn_cfm) (struct hci_conn *conn, __u8 reason);
|
||||
int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb,
|
||||
__u16 flags);
|
||||
int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb);
|
||||
int (*security_cfm) (struct hci_conn *conn, __u8 status,
|
||||
__u8 encrypt);
|
||||
};
|
||||
|
||||
static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
__u8 type)
|
||||
{
|
||||
register struct hci_proto *hp;
|
||||
int mask = 0;
|
||||
switch (type) {
|
||||
case ACL_LINK:
|
||||
return l2cap_connect_ind(hdev, bdaddr);
|
||||
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
if (hp && hp->connect_ind)
|
||||
mask |= hp->connect_ind(hdev, bdaddr, type);
|
||||
case SCO_LINK:
|
||||
case ESCO_LINK:
|
||||
return sco_connect_ind(hdev, bdaddr);
|
||||
|
||||
hp = hci_proto[HCI_PROTO_SCO];
|
||||
if (hp && hp->connect_ind)
|
||||
mask |= hp->connect_ind(hdev, bdaddr, type);
|
||||
|
||||
return mask;
|
||||
default:
|
||||
BT_ERR("unknown link type %d", type);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
|
||||
{
|
||||
register struct hci_proto *hp;
|
||||
switch (conn->type) {
|
||||
case ACL_LINK:
|
||||
case LE_LINK:
|
||||
l2cap_connect_cfm(conn, status);
|
||||
break;
|
||||
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
if (hp && hp->connect_cfm)
|
||||
hp->connect_cfm(conn, status);
|
||||
case SCO_LINK:
|
||||
case ESCO_LINK:
|
||||
sco_connect_cfm(conn, status);
|
||||
break;
|
||||
|
||||
hp = hci_proto[HCI_PROTO_SCO];
|
||||
if (hp && hp->connect_cfm)
|
||||
hp->connect_cfm(conn, status);
|
||||
default:
|
||||
BT_ERR("unknown link type %d", conn->type);
|
||||
break;
|
||||
}
|
||||
|
||||
if (conn->connect_cfm_cb)
|
||||
conn->connect_cfm_cb(conn, status);
|
||||
@ -731,31 +720,29 @@ static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
|
||||
|
||||
static inline int hci_proto_disconn_ind(struct hci_conn *conn)
|
||||
{
|
||||
register struct hci_proto *hp;
|
||||
int reason = HCI_ERROR_REMOTE_USER_TERM;
|
||||
if (conn->type != ACL_LINK && conn->type != LE_LINK)
|
||||
return HCI_ERROR_REMOTE_USER_TERM;
|
||||
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
if (hp && hp->disconn_ind)
|
||||
reason = hp->disconn_ind(conn);
|
||||
|
||||
hp = hci_proto[HCI_PROTO_SCO];
|
||||
if (hp && hp->disconn_ind)
|
||||
reason = hp->disconn_ind(conn);
|
||||
|
||||
return reason;
|
||||
return l2cap_disconn_ind(conn);
|
||||
}
|
||||
|
||||
static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason)
|
||||
{
|
||||
register struct hci_proto *hp;
|
||||
switch (conn->type) {
|
||||
case ACL_LINK:
|
||||
case LE_LINK:
|
||||
l2cap_disconn_cfm(conn, reason);
|
||||
break;
|
||||
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
if (hp && hp->disconn_cfm)
|
||||
hp->disconn_cfm(conn, reason);
|
||||
case SCO_LINK:
|
||||
case ESCO_LINK:
|
||||
sco_disconn_cfm(conn, reason);
|
||||
break;
|
||||
|
||||
hp = hci_proto[HCI_PROTO_SCO];
|
||||
if (hp && hp->disconn_cfm)
|
||||
hp->disconn_cfm(conn, reason);
|
||||
default:
|
||||
BT_ERR("unknown link type %d", conn->type);
|
||||
break;
|
||||
}
|
||||
|
||||
if (conn->disconn_cfm_cb)
|
||||
conn->disconn_cfm_cb(conn, reason);
|
||||
@ -763,21 +750,16 @@ static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason)
|
||||
|
||||
static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
|
||||
{
|
||||
register struct hci_proto *hp;
|
||||
__u8 encrypt;
|
||||
|
||||
if (conn->type != ACL_LINK && conn->type != LE_LINK)
|
||||
return;
|
||||
|
||||
if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
|
||||
return;
|
||||
|
||||
encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00;
|
||||
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
if (hp && hp->security_cfm)
|
||||
hp->security_cfm(conn, status, encrypt);
|
||||
|
||||
hp = hci_proto[HCI_PROTO_SCO];
|
||||
if (hp && hp->security_cfm)
|
||||
hp->security_cfm(conn, status, encrypt);
|
||||
l2cap_security_cfm(conn, status, encrypt);
|
||||
|
||||
if (conn->security_cfm_cb)
|
||||
conn->security_cfm_cb(conn, status);
|
||||
@ -786,23 +768,15 @@ static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
|
||||
static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status,
|
||||
__u8 encrypt)
|
||||
{
|
||||
register struct hci_proto *hp;
|
||||
if (conn->type != ACL_LINK && conn->type != LE_LINK)
|
||||
return;
|
||||
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
if (hp && hp->security_cfm)
|
||||
hp->security_cfm(conn, status, encrypt);
|
||||
|
||||
hp = hci_proto[HCI_PROTO_SCO];
|
||||
if (hp && hp->security_cfm)
|
||||
hp->security_cfm(conn, status, encrypt);
|
||||
l2cap_security_cfm(conn, status, encrypt);
|
||||
|
||||
if (conn->security_cfm_cb)
|
||||
conn->security_cfm_cb(conn, status);
|
||||
}
|
||||
|
||||
int hci_register_proto(struct hci_proto *hproto);
|
||||
int hci_unregister_proto(struct hci_proto *hproto);
|
||||
|
||||
/* ----- HCI callbacks ----- */
|
||||
struct hci_cb {
|
||||
struct list_head list;
|
||||
@ -827,13 +801,13 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
|
||||
|
||||
encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00;
|
||||
|
||||
read_lock_bh(&hci_cb_list_lock);
|
||||
read_lock(&hci_cb_list_lock);
|
||||
list_for_each(p, &hci_cb_list) {
|
||||
struct hci_cb *cb = list_entry(p, struct hci_cb, list);
|
||||
if (cb->security_cfm)
|
||||
cb->security_cfm(conn, status, encrypt);
|
||||
}
|
||||
read_unlock_bh(&hci_cb_list_lock);
|
||||
read_unlock(&hci_cb_list_lock);
|
||||
}
|
||||
|
||||
static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
|
||||
@ -849,26 +823,26 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status,
|
||||
|
||||
hci_proto_encrypt_cfm(conn, status, encrypt);
|
||||
|
||||
read_lock_bh(&hci_cb_list_lock);
|
||||
read_lock(&hci_cb_list_lock);
|
||||
list_for_each(p, &hci_cb_list) {
|
||||
struct hci_cb *cb = list_entry(p, struct hci_cb, list);
|
||||
if (cb->security_cfm)
|
||||
cb->security_cfm(conn, status, encrypt);
|
||||
}
|
||||
read_unlock_bh(&hci_cb_list_lock);
|
||||
read_unlock(&hci_cb_list_lock);
|
||||
}
|
||||
|
||||
static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status)
|
||||
{
|
||||
struct list_head *p;
|
||||
|
||||
read_lock_bh(&hci_cb_list_lock);
|
||||
read_lock(&hci_cb_list_lock);
|
||||
list_for_each(p, &hci_cb_list) {
|
||||
struct hci_cb *cb = list_entry(p, struct hci_cb, list);
|
||||
if (cb->key_change_cfm)
|
||||
cb->key_change_cfm(conn, status);
|
||||
}
|
||||
read_unlock_bh(&hci_cb_list_lock);
|
||||
read_unlock(&hci_cb_list_lock);
|
||||
}
|
||||
|
||||
static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
|
||||
@ -876,13 +850,13 @@ static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status,
|
||||
{
|
||||
struct list_head *p;
|
||||
|
||||
read_lock_bh(&hci_cb_list_lock);
|
||||
read_lock(&hci_cb_list_lock);
|
||||
list_for_each(p, &hci_cb_list) {
|
||||
struct hci_cb *cb = list_entry(p, struct hci_cb, list);
|
||||
if (cb->role_switch_cfm)
|
||||
cb->role_switch_cfm(conn, status, role);
|
||||
}
|
||||
read_unlock_bh(&hci_cb_list_lock);
|
||||
read_unlock(&hci_cb_list_lock);
|
||||
}
|
||||
|
||||
int hci_register_cb(struct hci_cb *hcb);
|
||||
|
@ -522,7 +522,7 @@ struct l2cap_conn {
|
||||
__u8 info_state;
|
||||
__u8 info_ident;
|
||||
|
||||
struct delayed_work info_work;
|
||||
struct delayed_work info_timer;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
@ -532,7 +532,7 @@ struct l2cap_conn {
|
||||
|
||||
__u8 disc_reason;
|
||||
|
||||
struct timer_list security_timer;
|
||||
struct delayed_work security_timer;
|
||||
struct smp_chan *smp_chan;
|
||||
|
||||
struct list_head chan_l;
|
||||
@ -595,17 +595,45 @@ enum {
|
||||
FLAG_EFS_ENABLE,
|
||||
};
|
||||
|
||||
static inline void l2cap_chan_hold(struct l2cap_chan *c)
|
||||
{
|
||||
atomic_inc(&c->refcnt);
|
||||
}
|
||||
|
||||
static inline void l2cap_chan_put(struct l2cap_chan *c)
|
||||
{
|
||||
if (atomic_dec_and_test(&c->refcnt))
|
||||
kfree(c);
|
||||
}
|
||||
|
||||
static inline void l2cap_set_timer(struct l2cap_chan *chan,
|
||||
struct delayed_work *work, long timeout)
|
||||
{
|
||||
BT_DBG("chan %p state %d timeout %ld", chan, chan->state, timeout);
|
||||
|
||||
if (!__cancel_delayed_work(work))
|
||||
l2cap_chan_hold(chan);
|
||||
schedule_delayed_work(work, timeout);
|
||||
}
|
||||
|
||||
static inline void l2cap_clear_timer(struct l2cap_chan *chan,
|
||||
struct delayed_work *work)
|
||||
{
|
||||
if (__cancel_delayed_work(work))
|
||||
l2cap_chan_put(chan);
|
||||
}
|
||||
|
||||
#define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t))
|
||||
#define __clear_chan_timer(c) l2cap_clear_timer(&c->chan_timer)
|
||||
#define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer)
|
||||
#define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \
|
||||
L2CAP_DEFAULT_RETRANS_TO);
|
||||
#define __clear_retrans_timer(c) l2cap_clear_timer(&c->retrans_timer)
|
||||
#define __clear_retrans_timer(c) l2cap_clear_timer(c, &c->retrans_timer)
|
||||
#define __set_monitor_timer(c) l2cap_set_timer(c, &c->monitor_timer, \
|
||||
L2CAP_DEFAULT_MONITOR_TO);
|
||||
#define __clear_monitor_timer(c) l2cap_clear_timer(&c->monitor_timer)
|
||||
#define __clear_monitor_timer(c) l2cap_clear_timer(c, &c->monitor_timer)
|
||||
#define __set_ack_timer(c) l2cap_set_timer(c, &chan->ack_timer, \
|
||||
L2CAP_DEFAULT_ACK_TO);
|
||||
#define __clear_ack_timer(c) l2cap_clear_timer(&c->ack_timer)
|
||||
#define __clear_ack_timer(c) l2cap_clear_timer(c, &c->ack_timer)
|
||||
|
||||
static inline int __seq_offset(struct l2cap_chan *chan, __u16 seq1, __u16 seq2)
|
||||
{
|
||||
|
@ -115,6 +115,10 @@ struct smp_cmd_security_req {
|
||||
#define SMP_MIN_ENC_KEY_SIZE 7
|
||||
#define SMP_MAX_ENC_KEY_SIZE 16
|
||||
|
||||
#define SMP_FLAG_TK_VALID 1
|
||||
#define SMP_FLAG_CFM_PENDING 2
|
||||
#define SMP_FLAG_MITM_AUTH 3
|
||||
|
||||
struct smp_chan {
|
||||
struct l2cap_conn *conn;
|
||||
u8 preq[7]; /* SMP Pairing Request */
|
||||
@ -124,6 +128,7 @@ struct smp_chan {
|
||||
u8 pcnf[16]; /* SMP Pairing Confirm */
|
||||
u8 tk[16]; /* SMP Temporary Key */
|
||||
u8 smp_key_size;
|
||||
unsigned long smp_flags;
|
||||
struct crypto_blkcipher *tfm;
|
||||
struct work_struct confirm;
|
||||
struct work_struct random;
|
||||
@ -134,6 +139,7 @@ struct smp_chan {
|
||||
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);
|
||||
int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
|
||||
|
||||
void smp_chan_destroy(struct l2cap_conn *conn);
|
||||
|
||||
|
@ -6,7 +6,11 @@ menuconfig BT
|
||||
tristate "Bluetooth subsystem support"
|
||||
depends on NET && !S390
|
||||
depends on RFKILL || !RFKILL
|
||||
select CRC16
|
||||
select CRYPTO
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_ECB
|
||||
help
|
||||
Bluetooth is low-cost, low-power, short-range wireless technology.
|
||||
It was designed as a replacement for cables and other short-range
|
||||
@ -15,10 +19,12 @@ menuconfig BT
|
||||
Bluetooth can be found at <http://www.bluetooth.com/>.
|
||||
|
||||
Linux Bluetooth subsystem consist of several layers:
|
||||
Bluetooth Core (HCI device and connection manager, scheduler)
|
||||
Bluetooth Core
|
||||
HCI device and connection manager, scheduler
|
||||
SCO audio links
|
||||
L2CAP (Logical Link Control and Adaptation Protocol)
|
||||
SMP (Security Manager Protocol) on LE (Low Energy) links
|
||||
HCI Device drivers (Interface to the hardware)
|
||||
SCO Module (SCO audio links)
|
||||
L2CAP Module (Logical Link Control and Adaptation Protocol)
|
||||
RFCOMM Module (RFCOMM Protocol)
|
||||
BNEP Module (Bluetooth Network Encapsulation Protocol)
|
||||
CMTP Module (CAPI Message Transport Protocol)
|
||||
@ -33,31 +39,6 @@ menuconfig BT
|
||||
to Bluetooth kernel modules are provided in the BlueZ packages. For
|
||||
more information, see <http://www.bluez.org/>.
|
||||
|
||||
if BT != n
|
||||
|
||||
config BT_L2CAP
|
||||
bool "L2CAP protocol support"
|
||||
select CRC16
|
||||
select CRYPTO
|
||||
select CRYPTO_BLKCIPHER
|
||||
select CRYPTO_AES
|
||||
select CRYPTO_ECB
|
||||
help
|
||||
L2CAP (Logical Link Control and Adaptation Protocol) provides
|
||||
connection oriented and connection-less data transport. L2CAP
|
||||
support is required for most Bluetooth applications.
|
||||
|
||||
Also included is support for SMP (Security Manager Protocol) which
|
||||
is the security layer on top of LE (Low Energy) links.
|
||||
|
||||
config BT_SCO
|
||||
bool "SCO links support"
|
||||
help
|
||||
SCO link provides voice transport over Bluetooth. SCO support is
|
||||
required for voice applications like Headset and Audio.
|
||||
|
||||
endif
|
||||
|
||||
source "net/bluetooth/rfcomm/Kconfig"
|
||||
|
||||
source "net/bluetooth/bnep/Kconfig"
|
||||
|
@ -8,6 +8,5 @@ obj-$(CONFIG_BT_BNEP) += bnep/
|
||||
obj-$(CONFIG_BT_CMTP) += cmtp/
|
||||
obj-$(CONFIG_BT_HIDP) += hidp/
|
||||
|
||||
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o hci_sock.o hci_sysfs.o lib.o
|
||||
bluetooth-$(CONFIG_BT_L2CAP) += l2cap_core.o l2cap_sock.o smp.o
|
||||
bluetooth-$(CONFIG_BT_SCO) += sco.o
|
||||
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
|
||||
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o
|
||||
|
@ -199,15 +199,14 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
||||
|
||||
BT_DBG("parent %p", parent);
|
||||
|
||||
local_bh_disable();
|
||||
list_for_each_safe(p, n, &bt_sk(parent)->accept_q) {
|
||||
sk = (struct sock *) list_entry(p, struct bt_sock, accept_q);
|
||||
|
||||
bh_lock_sock(sk);
|
||||
lock_sock(sk);
|
||||
|
||||
/* FIXME: Is this check still needed */
|
||||
if (sk->sk_state == BT_CLOSED) {
|
||||
bh_unlock_sock(sk);
|
||||
release_sock(sk);
|
||||
bt_accept_unlink(sk);
|
||||
continue;
|
||||
}
|
||||
@ -218,14 +217,12 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
|
||||
if (newsock)
|
||||
sock_graft(sk, newsock);
|
||||
|
||||
bh_unlock_sock(sk);
|
||||
local_bh_enable();
|
||||
release_sock(sk);
|
||||
return sk;
|
||||
}
|
||||
|
||||
bh_unlock_sock(sk);
|
||||
release_sock(sk);
|
||||
}
|
||||
local_bh_enable();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
config BT_BNEP
|
||||
tristate "BNEP protocol support"
|
||||
depends on BT && BT_L2CAP
|
||||
depends on BT
|
||||
select CRC32
|
||||
help
|
||||
BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
|
||||
|
@ -1,6 +1,6 @@
|
||||
config BT_CMTP
|
||||
tristate "CMTP protocol support"
|
||||
depends on BT && BT_L2CAP && ISDN_CAPI
|
||||
depends on BT && ISDN_CAPI
|
||||
help
|
||||
CMTP (CAPI Message Transport Protocol) is a transport layer
|
||||
for CAPI messages. CMTP is required for the Bluetooth Common
|
||||
|
@ -487,7 +487,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
|
||||
|
||||
BT_DBG("%s -> %s", batostr(src), batostr(dst));
|
||||
|
||||
read_lock_bh(&hci_dev_list_lock);
|
||||
read_lock(&hci_dev_list_lock);
|
||||
|
||||
list_for_each_entry(d, &hci_dev_list, list) {
|
||||
if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags))
|
||||
@ -512,7 +512,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
|
||||
if (hdev)
|
||||
hdev = hci_dev_hold(hdev);
|
||||
|
||||
read_unlock_bh(&hci_dev_list_lock);
|
||||
read_unlock(&hci_dev_list_lock);
|
||||
return hdev;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_get_route);
|
||||
|
@ -61,8 +61,6 @@ static void hci_rx_work(struct work_struct *work);
|
||||
static void hci_cmd_work(struct work_struct *work);
|
||||
static void hci_tx_work(struct work_struct *work);
|
||||
|
||||
static DEFINE_MUTEX(hci_task_lock);
|
||||
|
||||
/* HCI device list */
|
||||
LIST_HEAD(hci_dev_list);
|
||||
DEFINE_RWLOCK(hci_dev_list_lock);
|
||||
@ -71,10 +69,6 @@ DEFINE_RWLOCK(hci_dev_list_lock);
|
||||
LIST_HEAD(hci_cb_list);
|
||||
DEFINE_RWLOCK(hci_cb_list_lock);
|
||||
|
||||
/* HCI protocols */
|
||||
#define HCI_MAX_PROTO 2
|
||||
struct hci_proto *hci_proto[HCI_MAX_PROTO];
|
||||
|
||||
/* HCI notifiers list */
|
||||
static ATOMIC_NOTIFIER_HEAD(hci_notifier);
|
||||
|
||||
@ -193,33 +187,20 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt)
|
||||
hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);
|
||||
}
|
||||
|
||||
static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
|
||||
static void bredr_init(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_cp_delete_stored_link_key cp;
|
||||
struct sk_buff *skb;
|
||||
__le16 param;
|
||||
__u8 flt_type;
|
||||
|
||||
BT_DBG("%s %ld", hdev->name, opt);
|
||||
|
||||
/* Driver initialization */
|
||||
|
||||
/* Special commands */
|
||||
while ((skb = skb_dequeue(&hdev->driver_init))) {
|
||||
bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
|
||||
skb->dev = (void *) hdev;
|
||||
|
||||
skb_queue_tail(&hdev->cmd_q, skb);
|
||||
queue_work(hdev->workqueue, &hdev->cmd_work);
|
||||
}
|
||||
skb_queue_purge(&hdev->driver_init);
|
||||
hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED;
|
||||
|
||||
/* Mandatory initialization */
|
||||
|
||||
/* Reset */
|
||||
if (!test_bit(HCI_QUIRK_NO_RESET, &hdev->quirks)) {
|
||||
set_bit(HCI_RESET, &hdev->flags);
|
||||
hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);
|
||||
set_bit(HCI_RESET, &hdev->flags);
|
||||
hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);
|
||||
}
|
||||
|
||||
/* Read Local Supported Features */
|
||||
@ -258,6 +239,51 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
|
||||
hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static void amp_init(struct hci_dev *hdev)
|
||||
{
|
||||
hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED;
|
||||
|
||||
/* Reset */
|
||||
hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL);
|
||||
|
||||
/* Read Local Version */
|
||||
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
|
||||
}
|
||||
|
||||
static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
BT_DBG("%s %ld", hdev->name, opt);
|
||||
|
||||
/* Driver initialization */
|
||||
|
||||
/* Special commands */
|
||||
while ((skb = skb_dequeue(&hdev->driver_init))) {
|
||||
bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
|
||||
skb->dev = (void *) hdev;
|
||||
|
||||
skb_queue_tail(&hdev->cmd_q, skb);
|
||||
queue_work(hdev->workqueue, &hdev->cmd_work);
|
||||
}
|
||||
skb_queue_purge(&hdev->driver_init);
|
||||
|
||||
switch (hdev->dev_type) {
|
||||
case HCI_BREDR:
|
||||
bredr_init(hdev);
|
||||
break;
|
||||
|
||||
case HCI_AMP:
|
||||
amp_init(hdev);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_ERR("Unknown device type %d", hdev->dev_type);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt)
|
||||
{
|
||||
BT_DBG("%s", hdev->name);
|
||||
@ -818,7 +844,7 @@ int hci_get_dev_list(void __user *arg)
|
||||
|
||||
dr = dl->dev_req;
|
||||
|
||||
read_lock_bh(&hci_dev_list_lock);
|
||||
read_lock(&hci_dev_list_lock);
|
||||
list_for_each_entry(hdev, &hci_dev_list, list) {
|
||||
if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags))
|
||||
cancel_delayed_work(&hdev->power_off);
|
||||
@ -832,7 +858,7 @@ int hci_get_dev_list(void __user *arg)
|
||||
if (++n >= dev_num)
|
||||
break;
|
||||
}
|
||||
read_unlock_bh(&hci_dev_list_lock);
|
||||
read_unlock(&hci_dev_list_lock);
|
||||
|
||||
dl->dev_num = n;
|
||||
size = sizeof(*dl) + n * sizeof(*dr);
|
||||
@ -1432,7 +1458,7 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||
*/
|
||||
id = (hdev->dev_type == HCI_BREDR) ? 0 : 1;
|
||||
|
||||
write_lock_bh(&hci_dev_list_lock);
|
||||
write_lock(&hci_dev_list_lock);
|
||||
|
||||
/* Find first available device id */
|
||||
list_for_each(p, &hci_dev_list) {
|
||||
@ -1502,7 +1528,7 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||
|
||||
atomic_set(&hdev->promisc, 0);
|
||||
|
||||
write_unlock_bh(&hci_dev_list_lock);
|
||||
write_unlock(&hci_dev_list_lock);
|
||||
|
||||
hdev->workqueue = alloc_workqueue(hdev->name, WQ_HIGHPRI | WQ_UNBOUND |
|
||||
WQ_MEM_RECLAIM, 1);
|
||||
@ -1535,9 +1561,9 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||
err_wqueue:
|
||||
destroy_workqueue(hdev->workqueue);
|
||||
err:
|
||||
write_lock_bh(&hci_dev_list_lock);
|
||||
write_lock(&hci_dev_list_lock);
|
||||
list_del(&hdev->list);
|
||||
write_unlock_bh(&hci_dev_list_lock);
|
||||
write_unlock(&hci_dev_list_lock);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -1550,9 +1576,9 @@ void hci_unregister_dev(struct hci_dev *hdev)
|
||||
|
||||
BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus);
|
||||
|
||||
write_lock_bh(&hci_dev_list_lock);
|
||||
write_lock(&hci_dev_list_lock);
|
||||
list_del(&hdev->list);
|
||||
write_unlock_bh(&hci_dev_list_lock);
|
||||
write_unlock(&hci_dev_list_lock);
|
||||
|
||||
hci_dev_do_close(hdev);
|
||||
|
||||
@ -1800,59 +1826,13 @@ EXPORT_SYMBOL(hci_recv_stream_fragment);
|
||||
|
||||
/* ---- Interface to upper protocols ---- */
|
||||
|
||||
/* Register/Unregister protocols.
|
||||
* hci_task_lock is used to ensure that no tasks are running. */
|
||||
int hci_register_proto(struct hci_proto *hp)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("%p name %s id %d", hp, hp->name, hp->id);
|
||||
|
||||
if (hp->id >= HCI_MAX_PROTO)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&hci_task_lock);
|
||||
|
||||
if (!hci_proto[hp->id])
|
||||
hci_proto[hp->id] = hp;
|
||||
else
|
||||
err = -EEXIST;
|
||||
|
||||
mutex_unlock(&hci_task_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_register_proto);
|
||||
|
||||
int hci_unregister_proto(struct hci_proto *hp)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("%p name %s id %d", hp, hp->name, hp->id);
|
||||
|
||||
if (hp->id >= HCI_MAX_PROTO)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&hci_task_lock);
|
||||
|
||||
if (hci_proto[hp->id])
|
||||
hci_proto[hp->id] = NULL;
|
||||
else
|
||||
err = -ENOENT;
|
||||
|
||||
mutex_unlock(&hci_task_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(hci_unregister_proto);
|
||||
|
||||
int hci_register_cb(struct hci_cb *cb)
|
||||
{
|
||||
BT_DBG("%p name %s", cb, cb->name);
|
||||
|
||||
write_lock_bh(&hci_cb_list_lock);
|
||||
write_lock(&hci_cb_list_lock);
|
||||
list_add(&cb->list, &hci_cb_list);
|
||||
write_unlock_bh(&hci_cb_list_lock);
|
||||
write_unlock(&hci_cb_list_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1862,9 +1842,9 @@ int hci_unregister_cb(struct hci_cb *cb)
|
||||
{
|
||||
BT_DBG("%p name %s", cb, cb->name);
|
||||
|
||||
write_lock_bh(&hci_cb_list_lock);
|
||||
write_lock(&hci_cb_list_lock);
|
||||
list_del(&cb->list);
|
||||
write_unlock_bh(&hci_cb_list_lock);
|
||||
write_unlock(&hci_cb_list_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1980,7 +1960,7 @@ static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue,
|
||||
skb_shinfo(skb)->frag_list = NULL;
|
||||
|
||||
/* Queue all fragments atomically */
|
||||
spin_lock_bh(&queue->lock);
|
||||
spin_lock(&queue->lock);
|
||||
|
||||
__skb_queue_tail(queue, skb);
|
||||
|
||||
@ -1998,7 +1978,7 @@ static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue,
|
||||
__skb_queue_tail(queue, skb);
|
||||
} while (list);
|
||||
|
||||
spin_unlock_bh(&queue->lock);
|
||||
spin_unlock(&queue->lock);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2407,8 +2387,6 @@ static void hci_tx_work(struct work_struct *work)
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev, tx_work);
|
||||
struct sk_buff *skb;
|
||||
|
||||
mutex_lock(&hci_task_lock);
|
||||
|
||||
BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt,
|
||||
hdev->sco_cnt, hdev->le_cnt);
|
||||
|
||||
@ -2425,8 +2403,6 @@ static void hci_tx_work(struct work_struct *work)
|
||||
/* Send next queued raw (unknown type) packet */
|
||||
while ((skb = skb_dequeue(&hdev->raw_q)))
|
||||
hci_send_frame(skb);
|
||||
|
||||
mutex_unlock(&hci_task_lock);
|
||||
}
|
||||
|
||||
/* ----- HCI RX task (incoming data processing) ----- */
|
||||
@ -2453,16 +2429,11 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
if (conn) {
|
||||
register struct hci_proto *hp;
|
||||
|
||||
hci_conn_enter_active_mode(conn, BT_POWER_FORCE_ACTIVE_OFF);
|
||||
|
||||
/* Send to upper protocol */
|
||||
hp = hci_proto[HCI_PROTO_L2CAP];
|
||||
if (hp && hp->recv_acldata) {
|
||||
hp->recv_acldata(conn, skb, flags);
|
||||
return;
|
||||
}
|
||||
l2cap_recv_acldata(conn, skb, flags);
|
||||
return;
|
||||
} else {
|
||||
BT_ERR("%s ACL packet for unknown connection handle %d",
|
||||
hdev->name, handle);
|
||||
@ -2491,14 +2462,9 @@ static inline void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_dev_unlock(hdev);
|
||||
|
||||
if (conn) {
|
||||
register struct hci_proto *hp;
|
||||
|
||||
/* Send to upper protocol */
|
||||
hp = hci_proto[HCI_PROTO_SCO];
|
||||
if (hp && hp->recv_scodata) {
|
||||
hp->recv_scodata(conn, skb);
|
||||
return;
|
||||
}
|
||||
sco_recv_scodata(conn, skb);
|
||||
return;
|
||||
} else {
|
||||
BT_ERR("%s SCO packet for unknown connection handle %d",
|
||||
hdev->name, handle);
|
||||
@ -2514,8 +2480,6 @@ static void hci_rx_work(struct work_struct *work)
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
mutex_lock(&hci_task_lock);
|
||||
|
||||
while ((skb = skb_dequeue(&hdev->rx_q))) {
|
||||
if (atomic_read(&hdev->promisc)) {
|
||||
/* Send copy to the sockets */
|
||||
@ -2559,8 +2523,6 @@ static void hci_rx_work(struct work_struct *work)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&hci_task_lock);
|
||||
}
|
||||
|
||||
static void hci_cmd_work(struct work_struct *work)
|
||||
|
@ -556,6 +556,9 @@ static void hci_set_le_support(struct hci_dev *hdev)
|
||||
|
||||
static void hci_setup(struct hci_dev *hdev)
|
||||
{
|
||||
if (hdev->dev_type != HCI_BREDR)
|
||||
return;
|
||||
|
||||
hci_setup_event_mask(hdev);
|
||||
|
||||
if (hdev->hci_ver > BLUETOOTH_VER_1_1)
|
||||
@ -1030,7 +1033,8 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
if (cp->enable == 0x01) {
|
||||
switch (cp->enable) {
|
||||
case LE_SCANNING_ENABLED:
|
||||
set_bit(HCI_LE_SCAN, &hdev->dev_flags);
|
||||
|
||||
cancel_delayed_work_sync(&hdev->adv_work);
|
||||
@ -1038,12 +1042,19 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
|
||||
hci_dev_lock(hdev);
|
||||
hci_adv_entries_clear(hdev);
|
||||
hci_dev_unlock(hdev);
|
||||
} else if (cp->enable == 0x00) {
|
||||
break;
|
||||
|
||||
case LE_SCANNING_DISABLED:
|
||||
clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
|
||||
|
||||
cancel_delayed_work_sync(&hdev->adv_work);
|
||||
queue_delayed_work(hdev->workqueue, &hdev->adv_work,
|
||||
jiffies + ADV_CLEAR_TIMEOUT);
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_ERR("Used reserved LE_Scan_Enable param %d", cp->enable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2253,24 +2264,29 @@ static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb
|
||||
static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_num_comp_pkts *ev = (void *) skb->data;
|
||||
__le16 *ptr;
|
||||
int i;
|
||||
|
||||
skb_pull(skb, sizeof(*ev));
|
||||
|
||||
BT_DBG("%s num_hndl %d", hdev->name, ev->num_hndl);
|
||||
|
||||
if (hdev->flow_ctl_mode != HCI_FLOW_CTL_MODE_PACKET_BASED) {
|
||||
BT_ERR("Wrong event for mode %d", hdev->flow_ctl_mode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (skb->len < ev->num_hndl * 4) {
|
||||
BT_DBG("%s bad parameters", hdev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0, ptr = (__le16 *) skb->data; i < ev->num_hndl; i++) {
|
||||
for (i = 0; i < ev->num_hndl; i++) {
|
||||
struct hci_comp_pkts_info *info = &ev->handles[i];
|
||||
struct hci_conn *conn;
|
||||
__u16 handle, count;
|
||||
|
||||
handle = get_unaligned_le16(ptr++);
|
||||
count = get_unaligned_le16(ptr++);
|
||||
handle = __le16_to_cpu(info->handle);
|
||||
count = __le16_to_cpu(info->count);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!conn)
|
||||
|
@ -1,6 +1,6 @@
|
||||
config BT_HIDP
|
||||
tristate "HIDP protocol support"
|
||||
depends on BT && BT_L2CAP && INPUT && HID_SUPPORT
|
||||
depends on BT && INPUT && HID_SUPPORT
|
||||
select HID
|
||||
help
|
||||
HIDP (Human Interface Device Protocol) is a transport layer
|
||||
|
@ -77,17 +77,6 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb);
|
||||
|
||||
/* ---- L2CAP channels ---- */
|
||||
|
||||
static inline void chan_hold(struct l2cap_chan *c)
|
||||
{
|
||||
atomic_inc(&c->refcnt);
|
||||
}
|
||||
|
||||
static inline void chan_put(struct l2cap_chan *c)
|
||||
{
|
||||
if (atomic_dec_and_test(&c->refcnt))
|
||||
kfree(c);
|
||||
}
|
||||
|
||||
static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
|
||||
{
|
||||
struct l2cap_chan *c, *r = NULL;
|
||||
@ -228,20 +217,6 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void l2cap_set_timer(struct l2cap_chan *chan, struct delayed_work *work, long timeout)
|
||||
{
|
||||
BT_DBG("chan %p state %d timeout %ld", chan, chan->state, timeout);
|
||||
|
||||
cancel_delayed_work_sync(work);
|
||||
|
||||
schedule_delayed_work(work, timeout);
|
||||
}
|
||||
|
||||
static void l2cap_clear_timer(struct delayed_work *work)
|
||||
{
|
||||
cancel_delayed_work_sync(work);
|
||||
}
|
||||
|
||||
static char *state_to_string(int state)
|
||||
{
|
||||
switch(state) {
|
||||
@ -301,7 +276,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
|
||||
release_sock(sk);
|
||||
|
||||
chan->ops->close(chan->data);
|
||||
chan_put(chan);
|
||||
l2cap_chan_put(chan);
|
||||
}
|
||||
|
||||
struct l2cap_chan *l2cap_chan_create(struct sock *sk)
|
||||
@ -335,7 +310,7 @@ void l2cap_chan_destroy(struct l2cap_chan *chan)
|
||||
list_del(&chan->global_l);
|
||||
write_unlock_bh(&chan_list_lock);
|
||||
|
||||
chan_put(chan);
|
||||
l2cap_chan_put(chan);
|
||||
}
|
||||
|
||||
static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
|
||||
@ -377,7 +352,7 @@ static void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
|
||||
chan->local_acc_lat = L2CAP_DEFAULT_ACC_LAT;
|
||||
chan->local_flush_to = L2CAP_DEFAULT_FLUSH_TO;
|
||||
|
||||
chan_hold(chan);
|
||||
l2cap_chan_hold(chan);
|
||||
|
||||
list_add_rcu(&chan->list, &conn->chan_l);
|
||||
}
|
||||
@ -399,7 +374,7 @@ static void l2cap_chan_del(struct l2cap_chan *chan, int err)
|
||||
list_del_rcu(&chan->list);
|
||||
synchronize_rcu();
|
||||
|
||||
chan_put(chan);
|
||||
l2cap_chan_put(chan);
|
||||
|
||||
chan->conn = NULL;
|
||||
hci_conn_put(conn->hcon);
|
||||
@ -713,7 +688,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
|
||||
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
|
||||
conn->info_ident = l2cap_get_ident(conn);
|
||||
|
||||
schedule_delayed_work(&conn->info_work,
|
||||
schedule_delayed_work(&conn->info_timer,
|
||||
msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
|
||||
|
||||
l2cap_send_cmd(conn, conn->info_ident,
|
||||
@ -1010,7 +985,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
|
||||
static void l2cap_info_timeout(struct work_struct *work)
|
||||
{
|
||||
struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
|
||||
info_work.work);
|
||||
info_timer.work);
|
||||
|
||||
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
||||
conn->info_ident = 0;
|
||||
@ -1043,10 +1018,10 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
||||
hci_chan_del(conn->hchan);
|
||||
|
||||
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT)
|
||||
cancel_delayed_work_sync(&conn->info_work);
|
||||
__cancel_delayed_work(&conn->info_timer);
|
||||
|
||||
if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) {
|
||||
del_timer(&conn->security_timer);
|
||||
__cancel_delayed_work(&conn->security_timer);
|
||||
smp_chan_destroy(conn);
|
||||
}
|
||||
|
||||
@ -1054,9 +1029,10 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
|
||||
kfree(conn);
|
||||
}
|
||||
|
||||
static void security_timeout(unsigned long arg)
|
||||
static void security_timeout(struct work_struct *work)
|
||||
{
|
||||
struct l2cap_conn *conn = (void *) arg;
|
||||
struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
|
||||
security_timer.work);
|
||||
|
||||
l2cap_conn_del(conn->hcon, ETIMEDOUT);
|
||||
}
|
||||
@ -1100,10 +1076,9 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
|
||||
INIT_LIST_HEAD(&conn->chan_l);
|
||||
|
||||
if (hcon->type == LE_LINK)
|
||||
setup_timer(&conn->security_timer, security_timeout,
|
||||
(unsigned long) conn);
|
||||
INIT_DELAYED_WORK(&conn->security_timer, security_timeout);
|
||||
else
|
||||
INIT_DELAYED_WORK(&conn->info_work, l2cap_info_timeout);
|
||||
INIT_DELAYED_WORK(&conn->info_timer, l2cap_info_timeout);
|
||||
|
||||
conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
|
||||
|
||||
@ -2010,6 +1985,8 @@ static void l2cap_ack_timeout(struct work_struct *work)
|
||||
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
|
||||
ack_timer.work);
|
||||
|
||||
BT_DBG("chan %p", chan);
|
||||
|
||||
lock_sock(chan->sk);
|
||||
l2cap_send_ack(chan);
|
||||
release_sock(chan->sk);
|
||||
@ -2597,7 +2574,7 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
||||
|
||||
if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&
|
||||
cmd->ident == conn->info_ident) {
|
||||
cancel_delayed_work_sync(&conn->info_work);
|
||||
__cancel_delayed_work(&conn->info_timer);
|
||||
|
||||
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
||||
conn->info_ident = 0;
|
||||
@ -2718,7 +2695,7 @@ sendresp:
|
||||
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_SENT;
|
||||
conn->info_ident = l2cap_get_ident(conn);
|
||||
|
||||
schedule_delayed_work(&conn->info_work,
|
||||
schedule_delayed_work(&conn->info_timer,
|
||||
msecs_to_jiffies(L2CAP_INFO_TIMEOUT));
|
||||
|
||||
l2cap_send_cmd(conn, conn->info_ident,
|
||||
@ -3143,7 +3120,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
|
||||
conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)
|
||||
return 0;
|
||||
|
||||
cancel_delayed_work_sync(&conn->info_work);
|
||||
__cancel_delayed_work(&conn->info_timer);
|
||||
|
||||
if (result != L2CAP_IR_SUCCESS) {
|
||||
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
|
||||
@ -4427,14 +4404,11 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
|
||||
/* ---- L2CAP interface with lower layer (HCI) ---- */
|
||||
|
||||
static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
|
||||
int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
int exact = 0, lm1 = 0, lm2 = 0;
|
||||
struct l2cap_chan *c;
|
||||
|
||||
if (type != ACL_LINK)
|
||||
return -EINVAL;
|
||||
|
||||
BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
|
||||
|
||||
/* Find listening sockets and check their link_mode */
|
||||
@ -4461,15 +4435,12 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
|
||||
return exact ? lm1 : lm2;
|
||||
}
|
||||
|
||||
static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
||||
int l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
||||
{
|
||||
struct l2cap_conn *conn;
|
||||
|
||||
BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
|
||||
|
||||
if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK))
|
||||
return -EINVAL;
|
||||
|
||||
if (!status) {
|
||||
conn = l2cap_conn_add(hcon, status);
|
||||
if (conn)
|
||||
@ -4480,27 +4451,22 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_disconn_ind(struct hci_conn *hcon)
|
||||
int l2cap_disconn_ind(struct hci_conn *hcon)
|
||||
{
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
|
||||
BT_DBG("hcon %p", hcon);
|
||||
|
||||
if ((hcon->type != ACL_LINK && hcon->type != LE_LINK) || !conn)
|
||||
if (!conn)
|
||||
return HCI_ERROR_REMOTE_USER_TERM;
|
||||
|
||||
return conn->disc_reason;
|
||||
}
|
||||
|
||||
static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
|
||||
int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
|
||||
{
|
||||
BT_DBG("hcon %p reason %d", hcon, reason);
|
||||
|
||||
if (!(hcon->type == ACL_LINK || hcon->type == LE_LINK))
|
||||
return -EINVAL;
|
||||
|
||||
l2cap_conn_del(hcon, bt_to_errno(reason));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4521,7 +4487,7 @@ static inline void l2cap_check_encryption(struct l2cap_chan *chan, u8 encrypt)
|
||||
}
|
||||
}
|
||||
|
||||
static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
||||
int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
||||
{
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
struct l2cap_chan *chan;
|
||||
@ -4533,7 +4499,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
||||
|
||||
if (hcon->type == LE_LINK) {
|
||||
smp_distribute_keys(conn, 0);
|
||||
del_timer(&conn->security_timer);
|
||||
__cancel_delayed_work(&conn->security_timer);
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
@ -4621,7 +4587,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
{
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
|
||||
@ -4768,17 +4734,6 @@ static const struct file_operations l2cap_debugfs_fops = {
|
||||
|
||||
static struct dentry *l2cap_debugfs;
|
||||
|
||||
static struct hci_proto l2cap_hci_proto = {
|
||||
.name = "L2CAP",
|
||||
.id = HCI_PROTO_L2CAP,
|
||||
.connect_ind = l2cap_connect_ind,
|
||||
.connect_cfm = l2cap_connect_cfm,
|
||||
.disconn_ind = l2cap_disconn_ind,
|
||||
.disconn_cfm = l2cap_disconn_cfm,
|
||||
.security_cfm = l2cap_security_cfm,
|
||||
.recv_acldata = l2cap_recv_acldata
|
||||
};
|
||||
|
||||
int __init l2cap_init(void)
|
||||
{
|
||||
int err;
|
||||
@ -4787,13 +4742,6 @@ int __init l2cap_init(void)
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = hci_register_proto(&l2cap_hci_proto);
|
||||
if (err < 0) {
|
||||
BT_ERR("L2CAP protocol registration failed");
|
||||
bt_sock_unregister(BTPROTO_L2CAP);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (bt_debugfs) {
|
||||
l2cap_debugfs = debugfs_create_file("l2cap", 0444,
|
||||
bt_debugfs, NULL, &l2cap_debugfs_fops);
|
||||
@ -4802,19 +4750,11 @@ int __init l2cap_init(void)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
l2cap_cleanup_sockets();
|
||||
return err;
|
||||
}
|
||||
|
||||
void l2cap_exit(void)
|
||||
{
|
||||
debugfs_remove(l2cap_debugfs);
|
||||
|
||||
if (hci_unregister_proto(&l2cap_hci_proto) < 0)
|
||||
BT_ERR("L2CAP protocol unregistration failed");
|
||||
|
||||
l2cap_cleanup_sockets();
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/mgmt.h>
|
||||
#include <net/bluetooth/smp.h>
|
||||
|
||||
#define MGMT_VERSION 0
|
||||
#define MGMT_REVISION 1
|
||||
@ -1642,8 +1643,15 @@ static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
|
||||
}
|
||||
|
||||
/* Continue with pairing via SMP */
|
||||
err = smp_user_confirm_reply(conn, mgmt_op, passkey);
|
||||
|
||||
if (!err)
|
||||
err = cmd_status(sk, index, mgmt_op,
|
||||
MGMT_STATUS_SUCCESS);
|
||||
else
|
||||
err = cmd_status(sk, index, mgmt_op,
|
||||
MGMT_STATUS_FAILED);
|
||||
|
||||
err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_SUCCESS);
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
config BT_RFCOMM
|
||||
tristate "RFCOMM protocol support"
|
||||
depends on BT && BT_L2CAP
|
||||
depends on BT
|
||||
help
|
||||
RFCOMM provides connection oriented stream transport. RFCOMM
|
||||
support is required for Dialup Networking, OBEX and other Bluetooth
|
||||
|
@ -65,8 +65,7 @@ static DEFINE_MUTEX(rfcomm_mutex);
|
||||
|
||||
static LIST_HEAD(session_list);
|
||||
|
||||
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len,
|
||||
u32 priority);
|
||||
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len);
|
||||
static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci);
|
||||
static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci);
|
||||
static int rfcomm_queue_disc(struct rfcomm_dlc *d);
|
||||
@ -748,32 +747,23 @@ void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *d
|
||||
}
|
||||
|
||||
/* ---- RFCOMM frame sending ---- */
|
||||
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len,
|
||||
u32 priority)
|
||||
static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len)
|
||||
{
|
||||
struct socket *sock = s->sock;
|
||||
struct sock *sk = sock->sk;
|
||||
struct kvec iv = { data, len };
|
||||
struct msghdr msg;
|
||||
|
||||
BT_DBG("session %p len %d priority %u", s, len, priority);
|
||||
|
||||
if (sk->sk_priority != priority) {
|
||||
lock_sock(sk);
|
||||
sk->sk_priority = priority;
|
||||
release_sock(sk);
|
||||
}
|
||||
BT_DBG("session %p len %d", s, len);
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
return kernel_sendmsg(sock, &msg, &iv, 1, len);
|
||||
return kernel_sendmsg(s->sock, &msg, &iv, 1, len);
|
||||
}
|
||||
|
||||
static int rfcomm_send_cmd(struct rfcomm_session *s, struct rfcomm_cmd *cmd)
|
||||
{
|
||||
BT_DBG("%p cmd %u", s, cmd->ctrl);
|
||||
|
||||
return rfcomm_send_frame(s, (void *) cmd, sizeof(*cmd), HCI_PRIO_MAX);
|
||||
return rfcomm_send_frame(s, (void *) cmd, sizeof(*cmd));
|
||||
}
|
||||
|
||||
static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
|
||||
@ -829,8 +819,6 @@ static int rfcomm_queue_disc(struct rfcomm_dlc *d)
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
skb->priority = HCI_PRIO_MAX;
|
||||
|
||||
cmd = (void *) __skb_put(skb, sizeof(*cmd));
|
||||
cmd->addr = d->addr;
|
||||
cmd->ctrl = __ctrl(RFCOMM_DISC, 1);
|
||||
@ -878,7 +866,7 @@ static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
|
||||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
}
|
||||
|
||||
static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d)
|
||||
@ -920,7 +908,7 @@ static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d
|
||||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
}
|
||||
|
||||
int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
|
||||
@ -958,7 +946,7 @@ int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
|
||||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
}
|
||||
|
||||
static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
|
||||
@ -985,7 +973,7 @@ static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status)
|
||||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
}
|
||||
|
||||
static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig)
|
||||
@ -1012,7 +1000,7 @@ static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig
|
||||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
}
|
||||
|
||||
static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
|
||||
@ -1034,7 +1022,7 @@ static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr)
|
||||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
}
|
||||
|
||||
static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
|
||||
@ -1056,7 +1044,7 @@ static int rfcomm_send_fcon(struct rfcomm_session *s, int cr)
|
||||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
}
|
||||
|
||||
static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len)
|
||||
@ -1107,7 +1095,7 @@ static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits)
|
||||
|
||||
*ptr = __fcs(buf); ptr++;
|
||||
|
||||
return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX);
|
||||
return rfcomm_send_frame(s, buf, ptr - buf);
|
||||
}
|
||||
|
||||
static void rfcomm_make_uih(struct sk_buff *skb, u8 addr)
|
||||
@ -1786,8 +1774,7 @@ static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
|
||||
return skb_queue_len(&d->tx_queue);
|
||||
|
||||
while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) {
|
||||
err = rfcomm_send_frame(d->session, skb->data, skb->len,
|
||||
skb->priority);
|
||||
err = rfcomm_send_frame(d->session, skb->data, skb->len);
|
||||
if (err < 0) {
|
||||
skb_queue_head(&d->tx_queue, skb);
|
||||
break;
|
||||
|
@ -893,15 +893,12 @@ done:
|
||||
}
|
||||
|
||||
/* ----- SCO interface with lower layer (HCI) ----- */
|
||||
static int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
|
||||
int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
|
||||
{
|
||||
register struct sock *sk;
|
||||
struct hlist_node *node;
|
||||
int lm = 0;
|
||||
|
||||
if (type != SCO_LINK && type != ESCO_LINK)
|
||||
return -EINVAL;
|
||||
|
||||
BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
|
||||
|
||||
/* Find listening sockets */
|
||||
@ -921,13 +918,9 @@ static int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
|
||||
return lm;
|
||||
}
|
||||
|
||||
static int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
{
|
||||
BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
|
||||
|
||||
if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
|
||||
return -EINVAL;
|
||||
|
||||
if (!status) {
|
||||
struct sco_conn *conn;
|
||||
|
||||
@ -940,19 +933,15 @@ static int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
|
||||
int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
|
||||
{
|
||||
BT_DBG("hcon %p reason %d", hcon, reason);
|
||||
|
||||
if (hcon->type != SCO_LINK && hcon->type != ESCO_LINK)
|
||||
return -EINVAL;
|
||||
|
||||
sco_conn_del(hcon, bt_to_errno(reason));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
|
||||
int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
|
||||
{
|
||||
struct sco_conn *conn = hcon->sco_data;
|
||||
|
||||
@ -1028,15 +1017,6 @@ static const struct net_proto_family sco_sock_family_ops = {
|
||||
.create = sco_sock_create,
|
||||
};
|
||||
|
||||
static struct hci_proto sco_hci_proto = {
|
||||
.name = "SCO",
|
||||
.id = HCI_PROTO_SCO,
|
||||
.connect_ind = sco_connect_ind,
|
||||
.connect_cfm = sco_connect_cfm,
|
||||
.disconn_cfm = sco_disconn_cfm,
|
||||
.recv_scodata = sco_recv_scodata
|
||||
};
|
||||
|
||||
int __init sco_init(void)
|
||||
{
|
||||
int err;
|
||||
@ -1051,13 +1031,6 @@ int __init sco_init(void)
|
||||
goto error;
|
||||
}
|
||||
|
||||
err = hci_register_proto(&sco_hci_proto);
|
||||
if (err < 0) {
|
||||
BT_ERR("SCO protocol registration failed");
|
||||
bt_sock_unregister(BTPROTO_SCO);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (bt_debugfs) {
|
||||
sco_debugfs = debugfs_create_file("sco", 0444,
|
||||
bt_debugfs, NULL, &sco_debugfs_fops);
|
||||
@ -1081,9 +1054,6 @@ void __exit sco_exit(void)
|
||||
if (bt_sock_unregister(BTPROTO_SCO) < 0)
|
||||
BT_ERR("SCO socket unregistration failed");
|
||||
|
||||
if (hci_unregister_proto(&sco_hci_proto) < 0)
|
||||
BT_ERR("SCO protocol unregistration failed");
|
||||
|
||||
proto_unregister(&sco_proto);
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
#include <net/bluetooth/mgmt.h>
|
||||
#include <net/bluetooth/smp.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/scatterlist.h>
|
||||
@ -184,28 +185,50 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
|
||||
skb->priority = HCI_PRIO_MAX;
|
||||
hci_send_acl(conn->hchan, skb, 0);
|
||||
|
||||
mod_timer(&conn->security_timer, jiffies +
|
||||
cancel_delayed_work_sync(&conn->security_timer);
|
||||
schedule_delayed_work(&conn->security_timer,
|
||||
msecs_to_jiffies(SMP_TIMEOUT));
|
||||
}
|
||||
|
||||
static __u8 authreq_to_seclevel(__u8 authreq)
|
||||
{
|
||||
if (authreq & SMP_AUTH_MITM)
|
||||
return BT_SECURITY_HIGH;
|
||||
else
|
||||
return BT_SECURITY_MEDIUM;
|
||||
}
|
||||
|
||||
static __u8 seclevel_to_authreq(__u8 sec_level)
|
||||
{
|
||||
switch (sec_level) {
|
||||
case BT_SECURITY_HIGH:
|
||||
return SMP_AUTH_MITM | SMP_AUTH_BONDING;
|
||||
case BT_SECURITY_MEDIUM:
|
||||
return SMP_AUTH_BONDING;
|
||||
default:
|
||||
return SMP_AUTH_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static void build_pairing_cmd(struct l2cap_conn *conn,
|
||||
struct smp_cmd_pairing *req,
|
||||
struct smp_cmd_pairing *rsp,
|
||||
__u8 authreq)
|
||||
{
|
||||
u8 dist_keys;
|
||||
u8 dist_keys = 0;
|
||||
|
||||
dist_keys = 0;
|
||||
if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) {
|
||||
dist_keys = SMP_DIST_ENC_KEY;
|
||||
authreq |= SMP_AUTH_BONDING;
|
||||
} else {
|
||||
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->init_key_dist = 0;
|
||||
req->resp_key_dist = dist_keys;
|
||||
req->auth_req = authreq;
|
||||
return;
|
||||
@ -214,7 +237,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
|
||||
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->init_key_dist = 0;
|
||||
rsp->resp_key_dist = req->resp_key_dist & dist_keys;
|
||||
rsp->auth_req = authreq;
|
||||
}
|
||||
@ -240,10 +263,99 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
|
||||
|
||||
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->pend);
|
||||
mgmt_auth_failed(conn->hcon->hdev, conn->dst, reason);
|
||||
del_timer(&conn->security_timer);
|
||||
cancel_delayed_work_sync(&conn->security_timer);
|
||||
smp_chan_destroy(conn);
|
||||
}
|
||||
|
||||
#define JUST_WORKS 0x00
|
||||
#define JUST_CFM 0x01
|
||||
#define REQ_PASSKEY 0x02
|
||||
#define CFM_PASSKEY 0x03
|
||||
#define REQ_OOB 0x04
|
||||
#define OVERLAP 0xFF
|
||||
|
||||
static const u8 gen_method[5][5] = {
|
||||
{ JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY },
|
||||
{ JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY },
|
||||
{ CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY },
|
||||
{ JUST_WORKS, JUST_CFM, JUST_WORKS, JUST_WORKS, JUST_CFM },
|
||||
{ CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP },
|
||||
};
|
||||
|
||||
static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
|
||||
u8 local_io, u8 remote_io)
|
||||
{
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
struct smp_chan *smp = conn->smp_chan;
|
||||
u8 method;
|
||||
u32 passkey = 0;
|
||||
int ret = 0;
|
||||
|
||||
/* Initialize key for JUST WORKS */
|
||||
memset(smp->tk, 0, sizeof(smp->tk));
|
||||
clear_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
|
||||
|
||||
BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io);
|
||||
|
||||
/* If neither side wants MITM, use JUST WORKS */
|
||||
/* If either side has unknown io_caps, use JUST WORKS */
|
||||
/* Otherwise, look up method from the table */
|
||||
if (!(auth & SMP_AUTH_MITM) ||
|
||||
local_io > SMP_IO_KEYBOARD_DISPLAY ||
|
||||
remote_io > SMP_IO_KEYBOARD_DISPLAY)
|
||||
method = JUST_WORKS;
|
||||
else
|
||||
method = gen_method[local_io][remote_io];
|
||||
|
||||
/* If not bonding, don't ask user to confirm a Zero TK */
|
||||
if (!(auth & SMP_AUTH_BONDING) && method == JUST_CFM)
|
||||
method = JUST_WORKS;
|
||||
|
||||
/* If Just Works, Continue with Zero TK */
|
||||
if (method == JUST_WORKS) {
|
||||
set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Not Just Works/Confirm results in MITM Authentication */
|
||||
if (method != JUST_CFM)
|
||||
set_bit(SMP_FLAG_MITM_AUTH, &smp->smp_flags);
|
||||
|
||||
/* If both devices have Keyoard-Display I/O, the master
|
||||
* Confirms and the slave Enters the passkey.
|
||||
*/
|
||||
if (method == OVERLAP) {
|
||||
if (hcon->link_mode & HCI_LM_MASTER)
|
||||
method = CFM_PASSKEY;
|
||||
else
|
||||
method = REQ_PASSKEY;
|
||||
}
|
||||
|
||||
/* Generate random passkey. Not valid until confirmed. */
|
||||
if (method == CFM_PASSKEY) {
|
||||
u8 key[16];
|
||||
|
||||
memset(key, 0, sizeof(key));
|
||||
get_random_bytes(&passkey, sizeof(passkey));
|
||||
passkey %= 1000000;
|
||||
put_unaligned_le32(passkey, key);
|
||||
swap128(key, smp->tk);
|
||||
BT_DBG("PassKey: %d", passkey);
|
||||
}
|
||||
|
||||
hci_dev_lock(hcon->hdev);
|
||||
|
||||
if (method == REQ_PASSKEY)
|
||||
ret = mgmt_user_passkey_request(hcon->hdev, conn->dst);
|
||||
else
|
||||
ret = mgmt_user_confirm_request(hcon->hdev, conn->dst,
|
||||
cpu_to_le32(passkey), 0);
|
||||
|
||||
hci_dev_unlock(hcon->hdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void confirm_work(struct work_struct *work)
|
||||
{
|
||||
struct smp_chan *smp = container_of(work, struct smp_chan, confirm);
|
||||
@ -276,6 +388,8 @@ static void confirm_work(struct work_struct *work)
|
||||
goto error;
|
||||
}
|
||||
|
||||
clear_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags);
|
||||
|
||||
swap128(res, cp.confirm_val);
|
||||
smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp);
|
||||
|
||||
@ -381,6 +495,7 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
|
||||
|
||||
smp->conn = conn;
|
||||
conn->smp_chan = smp;
|
||||
conn->hcon->smp_conn = conn;
|
||||
|
||||
hci_conn_hold(conn->hcon);
|
||||
|
||||
@ -398,18 +513,64 @@ void smp_chan_destroy(struct l2cap_conn *conn)
|
||||
|
||||
kfree(smp);
|
||||
conn->smp_chan = NULL;
|
||||
conn->hcon->smp_conn = NULL;
|
||||
hci_conn_put(conn->hcon);
|
||||
}
|
||||
|
||||
int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
|
||||
{
|
||||
struct l2cap_conn *conn = hcon->smp_conn;
|
||||
struct smp_chan *smp;
|
||||
u32 value;
|
||||
u8 key[16];
|
||||
|
||||
BT_DBG("");
|
||||
|
||||
if (!conn)
|
||||
return -ENOTCONN;
|
||||
|
||||
smp = conn->smp_chan;
|
||||
|
||||
switch (mgmt_op) {
|
||||
case MGMT_OP_USER_PASSKEY_REPLY:
|
||||
value = le32_to_cpu(passkey);
|
||||
memset(key, 0, sizeof(key));
|
||||
BT_DBG("PassKey: %d", value);
|
||||
put_unaligned_le32(value, key);
|
||||
swap128(key, smp->tk);
|
||||
/* Fall Through */
|
||||
case MGMT_OP_USER_CONFIRM_REPLY:
|
||||
set_bit(SMP_FLAG_TK_VALID, &smp->smp_flags);
|
||||
break;
|
||||
case MGMT_OP_USER_PASSKEY_NEG_REPLY:
|
||||
case MGMT_OP_USER_CONFIRM_NEG_REPLY:
|
||||
smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED, 1);
|
||||
return 0;
|
||||
default:
|
||||
smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED, 1);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* If it is our turn to send Pairing Confirm, do so now */
|
||||
if (test_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags))
|
||||
queue_work(hcon->hdev->workqueue, &smp->confirm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
{
|
||||
struct smp_cmd_pairing rsp, *req = (void *) skb->data;
|
||||
struct smp_chan *smp;
|
||||
u8 key_size;
|
||||
u8 auth = SMP_AUTH_NONE;
|
||||
int ret;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
if (conn->hcon->link_mode & HCI_LM_MASTER)
|
||||
return SMP_CMD_NOTSUPP;
|
||||
|
||||
if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend))
|
||||
smp = smp_chan_create(conn);
|
||||
|
||||
@ -419,19 +580,16 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
memcpy(&smp->preq[1], req, sizeof(*req));
|
||||
skb_pull(skb, sizeof(*req));
|
||||
|
||||
if (req->oob_flag)
|
||||
return SMP_OOB_NOT_AVAIL;
|
||||
/* We didn't start the pairing, so match remote */
|
||||
if (req->auth_req & SMP_AUTH_BONDING)
|
||||
auth = req->auth_req;
|
||||
|
||||
/* We didn't start the pairing, so no requirements */
|
||||
build_pairing_cmd(conn, req, &rsp, SMP_AUTH_NONE);
|
||||
build_pairing_cmd(conn, req, &rsp, auth);
|
||||
|
||||
key_size = min(req->max_key_size, rsp.max_key_size);
|
||||
if (check_enc_key_size(conn, key_size))
|
||||
return SMP_ENC_KEY_SIZE;
|
||||
|
||||
/* Just works */
|
||||
memset(smp->tk, 0, sizeof(smp->tk));
|
||||
|
||||
ret = smp_rand(smp->prnd);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
@ -441,6 +599,11 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
|
||||
|
||||
/* Request setup of TK */
|
||||
ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -449,11 +612,14 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
|
||||
struct smp_chan *smp = conn->smp_chan;
|
||||
struct hci_dev *hdev = conn->hcon->hdev;
|
||||
u8 key_size;
|
||||
u8 key_size, auth = SMP_AUTH_NONE;
|
||||
int ret;
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
if (!(conn->hcon->link_mode & HCI_LM_MASTER))
|
||||
return SMP_CMD_NOTSUPP;
|
||||
|
||||
skb_pull(skb, sizeof(*rsp));
|
||||
|
||||
req = (void *) &smp->preq[1];
|
||||
@ -462,12 +628,6 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
if (check_enc_key_size(conn, key_size))
|
||||
return SMP_ENC_KEY_SIZE;
|
||||
|
||||
if (rsp->oob_flag)
|
||||
return SMP_OOB_NOT_AVAIL;
|
||||
|
||||
/* Just works */
|
||||
memset(smp->tk, 0, sizeof(smp->tk));
|
||||
|
||||
ret = smp_rand(smp->prnd);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
@ -475,6 +635,22 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
smp->prsp[0] = SMP_CMD_PAIRING_RSP;
|
||||
memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
|
||||
|
||||
if ((req->auth_req & SMP_AUTH_BONDING) &&
|
||||
(rsp->auth_req & SMP_AUTH_BONDING))
|
||||
auth = SMP_AUTH_BONDING;
|
||||
|
||||
auth |= (req->auth_req | rsp->auth_req) & SMP_AUTH_MITM;
|
||||
|
||||
ret = tk_request(conn, 0, auth, rsp->io_capability, req->io_capability);
|
||||
if (ret)
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags);
|
||||
|
||||
/* Can't compose response until we have been confirmed */
|
||||
if (!test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags))
|
||||
return 0;
|
||||
|
||||
queue_work(hdev->workqueue, &smp->confirm);
|
||||
|
||||
return 0;
|
||||
@ -496,8 +672,10 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
swap128(smp->prnd, random);
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random),
|
||||
random);
|
||||
} else {
|
||||
} else if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) {
|
||||
queue_work(hdev->workqueue, &smp->confirm);
|
||||
} else {
|
||||
set_bit(SMP_FLAG_CFM_PENDING, &smp->smp_flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -550,7 +728,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
|
||||
BT_DBG("conn %p", conn);
|
||||
|
||||
hcon->pending_sec_level = BT_SECURITY_MEDIUM;
|
||||
hcon->pending_sec_level = authreq_to_seclevel(rp->auth_req);
|
||||
|
||||
if (smp_ltk_encrypt(conn))
|
||||
return 0;
|
||||
@ -577,6 +755,7 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
|
||||
{
|
||||
struct hci_conn *hcon = conn->hcon;
|
||||
struct smp_chan *smp = conn->smp_chan;
|
||||
__u8 authreq;
|
||||
|
||||
BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level);
|
||||
|
||||
@ -597,18 +776,22 @@ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level)
|
||||
return 0;
|
||||
|
||||
smp = smp_chan_create(conn);
|
||||
if (!smp)
|
||||
return 1;
|
||||
|
||||
authreq = seclevel_to_authreq(sec_level);
|
||||
|
||||
if (hcon->link_mode & HCI_LM_MASTER) {
|
||||
struct smp_cmd_pairing cp;
|
||||
|
||||
build_pairing_cmd(conn, &cp, NULL, SMP_AUTH_NONE);
|
||||
build_pairing_cmd(conn, &cp, NULL, authreq);
|
||||
smp->preq[0] = SMP_CMD_PAIRING_REQ;
|
||||
memcpy(&smp->preq[1], &cp, sizeof(cp));
|
||||
|
||||
smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp);
|
||||
} else {
|
||||
struct smp_cmd_security_req cp;
|
||||
cp.auth_req = SMP_AUTH_NONE;
|
||||
cp.auth_req = authreq;
|
||||
smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
@ -637,7 +820,7 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
|
||||
skb_pull(skb, sizeof(*rp));
|
||||
|
||||
hci_add_ltk(conn->hcon->hdev, 1, conn->src, smp->smp_key_size,
|
||||
hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->smp_key_size,
|
||||
rp->ediv, rp->rand, smp->tk);
|
||||
|
||||
smp_distribute_keys(conn, 1);
|
||||
@ -800,7 +983,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force)
|
||||
|
||||
if (conn->hcon->out || force) {
|
||||
clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend);
|
||||
del_timer(&conn->security_timer);
|
||||
cancel_delayed_work_sync(&conn->security_timer);
|
||||
smp_chan_destroy(conn);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user