aboutsummaryrefslogtreecommitdiff
path: root/net/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'net/bluetooth')
-rw-r--r--net/bluetooth/bnep/bnep.h148
-rw-r--r--net/bluetooth/bnep/core.c71
-rw-r--r--net/bluetooth/bnep/sock.c2
-rw-r--r--net/bluetooth/cmtp/capi.c6
-rw-r--r--net/bluetooth/cmtp/cmtp.h11
-rw-r--r--net/bluetooth/cmtp/core.c28
-rw-r--r--net/bluetooth/cmtp/sock.c2
-rw-r--r--net/bluetooth/hci_conn.c95
-rw-r--r--net/bluetooth/hci_core.c154
-rw-r--r--net/bluetooth/hci_event.c267
-rw-r--r--net/bluetooth/hci_sysfs.c71
-rw-r--r--net/bluetooth/hidp/core.c96
-rw-r--r--net/bluetooth/hidp/hidp.h6
-rw-r--r--net/bluetooth/hidp/sock.c7
-rw-r--r--net/bluetooth/l2cap_core.c1966
-rw-r--r--net/bluetooth/l2cap_sock.c330
-rw-r--r--net/bluetooth/mgmt.c612
-rw-r--r--net/bluetooth/rfcomm/core.c23
-rw-r--r--net/bluetooth/rfcomm/sock.c5
19 files changed, 2492 insertions, 1408 deletions
diff --git a/net/bluetooth/bnep/bnep.h b/net/bluetooth/bnep/bnep.h
index 70672544db8..8e6c06158f8 100644
--- a/net/bluetooth/bnep/bnep.h
+++ b/net/bluetooth/bnep/bnep.h
@@ -23,88 +23,88 @@
#include <linux/crc32.h>
#include <net/bluetooth/bluetooth.h>
-// Limits
-#define BNEP_MAX_PROTO_FILTERS 5
-#define BNEP_MAX_MULTICAST_FILTERS 20
-
-// UUIDs
-#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
-#define BNEP_UUID16 0x02
-#define BNEP_UUID32 0x04
-#define BNEP_UUID128 0x16
-
-#define BNEP_SVC_PANU 0x1115
-#define BNEP_SVC_NAP 0x1116
-#define BNEP_SVC_GN 0x1117
-
-// Packet types
-#define BNEP_GENERAL 0x00
-#define BNEP_CONTROL 0x01
-#define BNEP_COMPRESSED 0x02
-#define BNEP_COMPRESSED_SRC_ONLY 0x03
-#define BNEP_COMPRESSED_DST_ONLY 0x04
-
-// Control types
-#define BNEP_CMD_NOT_UNDERSTOOD 0x00
-#define BNEP_SETUP_CONN_REQ 0x01
-#define BNEP_SETUP_CONN_RSP 0x02
-#define BNEP_FILTER_NET_TYPE_SET 0x03
-#define BNEP_FILTER_NET_TYPE_RSP 0x04
-#define BNEP_FILTER_MULTI_ADDR_SET 0x05
-#define BNEP_FILTER_MULTI_ADDR_RSP 0x06
-
-// Extension types
-#define BNEP_EXT_CONTROL 0x00
-
-// Response messages
-#define BNEP_SUCCESS 0x00
-
-#define BNEP_CONN_INVALID_DST 0x01
-#define BNEP_CONN_INVALID_SRC 0x02
-#define BNEP_CONN_INVALID_SVC 0x03
-#define BNEP_CONN_NOT_ALLOWED 0x04
-
-#define BNEP_FILTER_UNSUPPORTED_REQ 0x01
-#define BNEP_FILTER_INVALID_RANGE 0x02
-#define BNEP_FILTER_INVALID_MCADDR 0x02
-#define BNEP_FILTER_LIMIT_REACHED 0x03
-#define BNEP_FILTER_DENIED_SECURITY 0x04
-
-// L2CAP settings
-#define BNEP_MTU 1691
-#define BNEP_PSM 0x0f
-#define BNEP_FLUSH_TO 0xffff
-#define BNEP_CONNECT_TO 15
-#define BNEP_FILTER_TO 15
-
-// Headers
-#define BNEP_TYPE_MASK 0x7f
-#define BNEP_EXT_HEADER 0x80
+/* Limits */
+#define BNEP_MAX_PROTO_FILTERS 5
+#define BNEP_MAX_MULTICAST_FILTERS 20
+
+/* UUIDs */
+#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
+#define BNEP_UUID16 0x02
+#define BNEP_UUID32 0x04
+#define BNEP_UUID128 0x16
+
+#define BNEP_SVC_PANU 0x1115
+#define BNEP_SVC_NAP 0x1116
+#define BNEP_SVC_GN 0x1117
+
+/* Packet types */
+#define BNEP_GENERAL 0x00
+#define BNEP_CONTROL 0x01
+#define BNEP_COMPRESSED 0x02
+#define BNEP_COMPRESSED_SRC_ONLY 0x03
+#define BNEP_COMPRESSED_DST_ONLY 0x04
+
+/* Control types */
+#define BNEP_CMD_NOT_UNDERSTOOD 0x00
+#define BNEP_SETUP_CONN_REQ 0x01
+#define BNEP_SETUP_CONN_RSP 0x02
+#define BNEP_FILTER_NET_TYPE_SET 0x03
+#define BNEP_FILTER_NET_TYPE_RSP 0x04
+#define BNEP_FILTER_MULTI_ADDR_SET 0x05
+#define BNEP_FILTER_MULTI_ADDR_RSP 0x06
+
+/* Extension types */
+#define BNEP_EXT_CONTROL 0x00
+
+/* Response messages */
+#define BNEP_SUCCESS 0x00
+
+#define BNEP_CONN_INVALID_DST 0x01
+#define BNEP_CONN_INVALID_SRC 0x02
+#define BNEP_CONN_INVALID_SVC 0x03
+#define BNEP_CONN_NOT_ALLOWED 0x04
+
+#define BNEP_FILTER_UNSUPPORTED_REQ 0x01
+#define BNEP_FILTER_INVALID_RANGE 0x02
+#define BNEP_FILTER_INVALID_MCADDR 0x02
+#define BNEP_FILTER_LIMIT_REACHED 0x03
+#define BNEP_FILTER_DENIED_SECURITY 0x04
+
+/* L2CAP settings */
+#define BNEP_MTU 1691
+#define BNEP_PSM 0x0f
+#define BNEP_FLUSH_TO 0xffff
+#define BNEP_CONNECT_TO 15
+#define BNEP_FILTER_TO 15
+
+/* Headers */
+#define BNEP_TYPE_MASK 0x7f
+#define BNEP_EXT_HEADER 0x80
struct bnep_setup_conn_req {
- __u8 type;
- __u8 ctrl;
- __u8 uuid_size;
- __u8 service[0];
+ __u8 type;
+ __u8 ctrl;
+ __u8 uuid_size;
+ __u8 service[0];
} __packed;
struct bnep_set_filter_req {
- __u8 type;
- __u8 ctrl;
+ __u8 type;
+ __u8 ctrl;
__be16 len;
- __u8 list[0];
+ __u8 list[0];
} __packed;
struct bnep_control_rsp {
- __u8 type;
- __u8 ctrl;
+ __u8 type;
+ __u8 ctrl;
__be16 resp;
} __packed;
struct bnep_ext_hdr {
- __u8 type;
- __u8 len;
- __u8 data[0];
+ __u8 type;
+ __u8 len;
+ __u8 data[0];
} __packed;
/* BNEP ioctl defines */
@@ -114,10 +114,10 @@ struct bnep_ext_hdr {
#define BNEPGETCONNINFO _IOR('B', 211, int)
struct bnep_connadd_req {
- int sock; // Connected socket
+ int sock; /* Connected socket */
__u32 flags;
__u16 role;
- char device[16]; // Name of the Ethernet device
+ char device[16]; /* Name of the Ethernet device */
};
struct bnep_conndel_req {
@@ -148,14 +148,14 @@ int bnep_del_connection(struct bnep_conndel_req *req);
int bnep_get_connlist(struct bnep_connlist_req *req);
int bnep_get_conninfo(struct bnep_conninfo *ci);
-// BNEP sessions
+/* BNEP sessions */
struct bnep_session {
struct list_head list;
unsigned int role;
unsigned long state;
unsigned long flags;
- atomic_t killed;
+ struct task_struct *task;
struct ethhdr eh;
struct msghdr msg;
@@ -173,7 +173,7 @@ void bnep_sock_cleanup(void);
static inline int bnep_mc_hash(__u8 *addr)
{
- return (crc32_be(~0, addr, ETH_ALEN) >> 26);
+ return crc32_be(~0, addr, ETH_ALEN) >> 26;
}
#endif
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 03d4d1245d5..ca39fcf010c 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -36,6 +36,7 @@
#include <linux/errno.h>
#include <linux/net.h>
#include <linux/slab.h>
+#include <linux/kthread.h>
#include <net/sock.h>
#include <linux/socket.h>
@@ -131,7 +132,8 @@ static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len
return -EILSEQ;
n = get_unaligned_be16(data);
- data++; len -= 2;
+ data++;
+ len -= 2;
if (len < n)
return -EILSEQ;
@@ -176,7 +178,8 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
return -EILSEQ;
n = get_unaligned_be16(data);
- data += 2; len -= 2;
+ data += 2;
+ len -= 2;
if (len < n)
return -EILSEQ;
@@ -187,6 +190,8 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
n /= (ETH_ALEN * 2);
if (n > 0) {
+ int i;
+
s->mc_filter = 0;
/* Always send broadcast */
@@ -196,18 +201,22 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
for (; n > 0; n--) {
u8 a1[6], *a2;
- memcpy(a1, data, ETH_ALEN); data += ETH_ALEN;
- a2 = data; data += ETH_ALEN;
+ memcpy(a1, data, ETH_ALEN);
+ data += ETH_ALEN;
+ a2 = data;
+ data += ETH_ALEN;
BT_DBG("mc filter %s -> %s",
batostr((void *) a1), batostr((void *) a2));
- #define INCA(a) { int i = 5; while (i >=0 && ++a[i--] == 0); }
-
/* Iterate from a1 to a2 */
set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
- INCA(a1);
+ /* Increment a1 */
+ i = 5;
+ while (i >= 0 && ++a1[i--] == 0)
+ ;
+
set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
}
}
@@ -227,7 +236,8 @@ static int bnep_rx_control(struct bnep_session *s, void *data, int len)
u8 cmd = *(u8 *)data;
int err = 0;
- data++; len--;
+ data++;
+ len--;
switch (cmd) {
case BNEP_CMD_NOT_UNDERSTOOD:
@@ -302,7 +312,6 @@ static u8 __bnep_rx_hlen[] = {
ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */
};
-#define BNEP_RX_TYPES (sizeof(__bnep_rx_hlen) - 1)
static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
{
@@ -312,9 +321,10 @@ static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
dev->stats.rx_bytes += skb->len;
- type = *(u8 *) skb->data; skb_pull(skb, 1);
+ type = *(u8 *) skb->data;
+ skb_pull(skb, 1);
- if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES)
+ if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
goto badframe;
if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
@@ -367,14 +377,14 @@ static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
case BNEP_COMPRESSED_DST_ONLY:
memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb),
- ETH_ALEN);
+ ETH_ALEN);
memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source,
- ETH_ALEN + 2);
+ ETH_ALEN + 2);
break;
case BNEP_GENERAL:
memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb),
- ETH_ALEN * 2);
+ ETH_ALEN * 2);
put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
break;
}
@@ -470,15 +480,14 @@ static int bnep_session(void *arg)
BT_DBG("");
- daemonize("kbnepd %s", dev->name);
set_user_nice(current, -15);
init_waitqueue_entry(&wait, current);
add_wait_queue(sk_sleep(sk), &wait);
- while (!atomic_read(&s->killed)) {
+ while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
- // RX
+ /* RX */
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
skb_orphan(skb);
bnep_rx_frame(s, skb);
@@ -487,7 +496,7 @@ static int bnep_session(void *arg)
if (sk->sk_state != BT_CONNECTED)
break;
- // TX
+ /* TX */
while ((skb = skb_dequeue(&sk->sk_write_queue)))
if (bnep_tx_frame(s, skb))
break;
@@ -555,8 +564,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
/* session struct allocated as private part of net_device */
dev = alloc_netdev(sizeof(struct bnep_session),
- (*req->device) ? req->device : "bnep%d",
- bnep_net_setup);
+ (*req->device) ? req->device : "bnep%d",
+ bnep_net_setup);
if (!dev)
return -ENOMEM;
@@ -571,7 +580,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
s = netdev_priv(dev);
/* This is rx header therefore addresses are swapped.
- * ie eh.h_dest is our local address. */
+ * ie. eh.h_dest is our local address. */
memcpy(s->eh.h_dest, &src, ETH_ALEN);
memcpy(s->eh.h_source, &dst, ETH_ALEN);
memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
@@ -597,17 +606,17 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
SET_NETDEV_DEVTYPE(dev, &bnep_type);
err = register_netdev(dev);
- if (err) {
+ if (err)
goto failed;
- }
__bnep_link_session(s);
- err = kernel_thread(bnep_session, s, CLONE_KERNEL);
- if (err < 0) {
+ s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
+ if (IS_ERR(s->task)) {
/* Session thread start failed, gotta cleanup. */
unregister_netdev(dev);
__bnep_unlink_session(s);
+ err = PTR_ERR(s->task);
goto failed;
}
@@ -631,15 +640,9 @@ int bnep_del_connection(struct bnep_conndel_req *req)
down_read(&bnep_session_sem);
s = __bnep_get_session(req->dst);
- if (s) {
- /* Wakeup user-space which is polling for socket errors.
- * This is temporary hack until we have shutdown in L2CAP */
- s->sock->sk->sk_err = EUNATCH;
-
- /* Kill session thread */
- atomic_inc(&s->killed);
- wake_up_interruptible(sk_sleep(s->sock->sk));
- } else
+ if (s)
+ kthread_stop(s->task);
+ else
err = -ENOENT;
up_read(&bnep_session_sem);
diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c
index d935da71ab3..17800b1d28e 100644
--- a/net/bluetooth/bnep/sock.c
+++ b/net/bluetooth/bnep/sock.c
@@ -39,10 +39,10 @@
#include <linux/init.h>
#include <linux/compat.h>
#include <linux/gfp.h>
+#include <linux/uaccess.h>
#include <net/sock.h>
#include <asm/system.h>
-#include <asm/uaccess.h>
#include "bnep.h"
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c
index 67cff810c77..744233cba24 100644
--- a/net/bluetooth/cmtp/capi.c
+++ b/net/bluetooth/cmtp/capi.c
@@ -35,6 +35,7 @@
#include <linux/ioctl.h>
#include <linux/file.h>
#include <linux/wait.h>
+#include <linux/kthread.h>
#include <net/sock.h>
#include <linux/isdn/capilli.h>
@@ -143,7 +144,7 @@ static void cmtp_send_capimsg(struct cmtp_session *session, struct sk_buff *skb)
skb_queue_tail(&session->transmit, skb);
- cmtp_schedule(session);
+ wake_up_interruptible(sk_sleep(session->sock->sk));
}
static void cmtp_send_interopmsg(struct cmtp_session *session,
@@ -386,8 +387,7 @@ static void cmtp_reset_ctr(struct capi_ctr *ctrl)
capi_ctr_down(ctrl);
- atomic_inc(&session->terminate);
- cmtp_schedule(session);
+ kthread_stop(session->task);
}
static void cmtp_register_appl(struct capi_ctr *ctrl, __u16 appl, capi_register_params *rp)
diff --git a/net/bluetooth/cmtp/cmtp.h b/net/bluetooth/cmtp/cmtp.h
index 785e79e953c..db43b54ac9a 100644
--- a/net/bluetooth/cmtp/cmtp.h
+++ b/net/bluetooth/cmtp/cmtp.h
@@ -37,7 +37,7 @@
#define CMTP_LOOPBACK 0
struct cmtp_connadd_req {
- int sock; // Connected socket
+ int sock; /* Connected socket */
__u32 flags;
};
@@ -81,7 +81,7 @@ struct cmtp_session {
char name[BTNAMSIZ];
- atomic_t terminate;
+ struct task_struct *task;
wait_queue_head_t wait;
@@ -121,13 +121,6 @@ void cmtp_detach_device(struct cmtp_session *session);
void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb);
-static inline void cmtp_schedule(struct cmtp_session *session)
-{
- struct sock *sk = session->sock->sk;
-
- wake_up_interruptible(sk_sleep(sk));
-}
-
/* CMTP init defines */
int cmtp_init_sockets(void);
void cmtp_cleanup_sockets(void);
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index 964ea9126f9..c5b11af908b 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -35,6 +35,7 @@
#include <linux/ioctl.h>
#include <linux/file.h>
#include <linux/init.h>
+#include <linux/kthread.h>
#include <net/sock.h>
#include <linux/isdn/capilli.h>
@@ -235,9 +236,12 @@ static void cmtp_process_transmit(struct cmtp_session *session)
size = min_t(uint, ((tail < 258) ? (tail - 2) : (tail - 3)), skb->len);
- if ((scb->id < 0) && ((scb->id = cmtp_alloc_block_id(session)) < 0)) {
- skb_queue_head(&session->transmit, skb);
- break;
+ if (scb->id < 0) {
+ scb->id = cmtp_alloc_block_id(session);
+ if (scb->id < 0) {
+ skb_queue_head(&session->transmit, skb);
+ break;
+ }
}
if (size < 256) {
@@ -284,12 +288,11 @@ static int cmtp_session(void *arg)
BT_DBG("session %p", session);
- daemonize("kcmtpd_ctr_%d", session->num);
set_user_nice(current, -15);
init_waitqueue_entry(&wait, current);
add_wait_queue(sk_sleep(sk), &wait);
- while (!atomic_read(&session->terminate)) {
+ while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
if (sk->sk_state != BT_CONNECTED)
@@ -343,7 +346,8 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst);
- session->mtu = min_t(uint, l2cap_pi(sock->sk)->omtu, l2cap_pi(sock->sk)->imtu);
+ session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu,
+ l2cap_pi(sock->sk)->chan->imtu);
BT_DBG("mtu %d", session->mtu);
@@ -367,9 +371,12 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
__cmtp_link_session(session);
- err = kernel_thread(cmtp_session, session, CLONE_KERNEL);
- if (err < 0)
+ session->task = kthread_run(cmtp_session, session, "kcmtpd_ctr_%d",
+ session->num);
+ if (IS_ERR(session->task)) {
+ err = PTR_ERR(session->task);
goto unlink;
+ }
if (!(session->flags & (1 << CMTP_LOOPBACK))) {
err = cmtp_attach_device(session);
@@ -406,9 +413,8 @@ int cmtp_del_connection(struct cmtp_conndel_req *req)
/* Flush the transmit queue */
skb_queue_purge(&session->transmit);
- /* Kill session thread */
- atomic_inc(&session->terminate);
- cmtp_schedule(session);
+ /* Stop session thread */
+ kthread_stop(session->task);
} else
err = -ENOENT;
diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c
index 7ea1979a8e4..3f2dd5c25ae 100644
--- a/net/bluetooth/cmtp/sock.c
+++ b/net/bluetooth/cmtp/sock.c
@@ -34,12 +34,12 @@
#include <linux/file.h>
#include <linux/compat.h>
#include <linux/gfp.h>
+#include <linux/uaccess.h>
#include <net/sock.h>
#include <linux/isdn/capilli.h>
#include <asm/system.h>
-#include <asm/uaccess.h>
#include "cmtp.h"
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 7a6f56b2f49..3163330cd4f 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -269,6 +269,19 @@ static void hci_conn_idle(unsigned long arg)
hci_conn_enter_sniff_mode(conn);
}
+static void hci_conn_auto_accept(unsigned long arg)
+{
+ struct hci_conn *conn = (void *) arg;
+ struct hci_dev *hdev = conn->hdev;
+
+ hci_dev_lock(hdev);
+
+ hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst),
+ &conn->dst);
+
+ hci_dev_unlock(hdev);
+}
+
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
{
struct hci_conn *conn;
@@ -287,6 +300,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
conn->auth_type = HCI_AT_GENERAL_BONDING;
conn->io_capability = hdev->io_capability;
conn->remote_auth = 0xff;
+ conn->key_type = 0xff;
conn->power_save = 1;
conn->disc_timeout = HCI_DISCONN_TIMEOUT;
@@ -311,6 +325,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn);
setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
+ setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept,
+ (unsigned long) conn);
atomic_set(&conn->refcnt, 0);
@@ -341,6 +357,8 @@ int hci_conn_del(struct hci_conn *conn)
del_timer(&conn->disc_timer);
+ del_timer(&conn->auto_accept_timer);
+
if (conn->type == ACL_LINK) {
struct hci_conn *sco = conn->link;
if (sco)
@@ -535,36 +553,93 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
return 0;
}
+/* Encrypt the the link */
+static void hci_conn_encrypt(struct hci_conn *conn)
+{
+ BT_DBG("conn %p", conn);
+
+ if (!test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
+ struct hci_cp_set_conn_encrypt cp;
+ cp.handle = cpu_to_le16(conn->handle);
+ cp.encrypt = 0x01;
+ hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT, sizeof(cp),
+ &cp);
+ }
+}
+
/* Enable security */
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
{
BT_DBG("conn %p", conn);
+ /* For sdp we don't need the link key. */
if (sec_level == BT_SECURITY_SDP)
return 1;
+ /* For non 2.1 devices and low security level we don't need the link
+ key. */
if (sec_level == BT_SECURITY_LOW &&
(!conn->ssp_mode || !conn->hdev->ssp_mode))
return 1;
- if (conn->link_mode & HCI_LM_ENCRYPT)
- return hci_conn_auth(conn, sec_level, auth_type);
-
+ /* For other security levels we need the link key. */
+ if (!(conn->link_mode & HCI_LM_AUTH))
+ goto auth;
+
+ /* An authenticated combination key has sufficient security for any
+ security level. */
+ if (conn->key_type == HCI_LK_AUTH_COMBINATION)
+ goto encrypt;
+
+ /* An unauthenticated combination key has sufficient security for
+ security level 1 and 2. */
+ if (conn->key_type == HCI_LK_UNAUTH_COMBINATION &&
+ (sec_level == BT_SECURITY_MEDIUM ||
+ sec_level == BT_SECURITY_LOW))
+ goto encrypt;
+
+ /* A combination key has always sufficient security for the security
+ levels 1 or 2. High security level requires the combination key
+ is generated using maximum PIN code length (16).
+ For pre 2.1 units. */
+ if (conn->key_type == HCI_LK_COMBINATION &&
+ (sec_level != BT_SECURITY_HIGH ||
+ conn->pin_length == 16))
+ goto encrypt;
+
+auth:
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
return 0;
- if (hci_conn_auth(conn, sec_level, auth_type)) {
- struct hci_cp_set_conn_encrypt cp;
- cp.handle = cpu_to_le16(conn->handle);
- cp.encrypt = 1;
- hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT,
- sizeof(cp), &cp);
- }
+ hci_conn_auth(conn, sec_level, auth_type);
+ return 0;
+
+encrypt:
+ if (conn->link_mode & HCI_LM_ENCRYPT)
+ return 1;
+ hci_conn_encrypt(conn);
return 0;
}
EXPORT_SYMBOL(hci_conn_security);
+/* Check secure link requirement */
+int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level)
+{
+ BT_DBG("conn %p", conn);
+
+ if (sec_level != BT_SECURITY_HIGH)
+ return 1; /* Accept if non-secure is required */
+
+ if (conn->key_type == HCI_LK_AUTH_COMBINATION ||
+ (conn->key_type == HCI_LK_COMBINATION &&
+ conn->pin_length == 16))
+ return 1;
+
+ return 0; /* Reject not secure link */
+}
+EXPORT_SYMBOL(hci_conn_check_secure);
+
/* Change link key */
int hci_conn_change_link_key(struct hci_conn *conn)
{
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index b5a8afc2be3..815269b07f2 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -56,7 +56,6 @@
static void hci_cmd_task(unsigned long arg);
static void hci_rx_task(unsigned long arg);
static void hci_tx_task(unsigned long arg);
-static void hci_notify(struct hci_dev *hdev, int event);
static DEFINE_RWLOCK(hci_task_lock);
@@ -1021,18 +1020,54 @@ struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
return NULL;
}
-int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
- u8 *val, u8 type, u8 pin_len)
+static int hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn,
+ u8 key_type, u8 old_key_type)
+{
+ /* Legacy key */
+ if (key_type < 0x03)
+ return 1;
+
+ /* Debug keys are insecure so don't store them persistently */
+ if (key_type == HCI_LK_DEBUG_COMBINATION)
+ return 0;
+
+ /* Changed combination key and there's no previous one */
+ if (key_type == HCI_LK_CHANGED_COMBINATION && old_key_type == 0xff)
+ return 0;
+
+ /* Security mode 3 case */
+ if (!conn)
+ return 1;
+
+ /* Neither local nor remote side had no-bonding as requirement */
+ if (conn->auth_type > 0x01 && conn->remote_auth > 0x01)
+ return 1;
+
+ /* Local side had dedicated bonding as requirement */
+ if (conn->auth_type == 0x02 || conn->auth_type == 0x03)
+ return 1;
+
+ /* Remote side had dedicated bonding as requirement */
+ if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03)
+ return 1;
+
+ /* If none of the above criteria match, then don't store the key
+ * persistently */
+ return 0;
+}
+
+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 *key, *old_key;
- u8 old_key_type;
+ u8 old_key_type, persistent;
old_key = hci_find_link_key(hdev, bdaddr);
if (old_key) {
old_key_type = old_key->type;
key = old_key;
} else {
- old_key_type = 0xff;
+ old_key_type = conn ? conn->key_type : 0xff;
key = kzalloc(sizeof(*key), GFP_ATOMIC);
if (!key)
return -ENOMEM;
@@ -1041,16 +1076,37 @@ int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type);
+ /* Some buggy controller combinations generate a changed
+ * combination key for legacy pairing even when there's no
+ * previous key */
+ if (type == HCI_LK_CHANGED_COMBINATION &&
+ (!conn || conn->remote_auth == 0xff) &&
+ old_key_type == 0xff) {
+ type = HCI_LK_COMBINATION;
+ if (conn)
+ conn->key_type = type;
+ }
+
bacpy(&key->bdaddr, bdaddr);
memcpy(key->val, val, 16);
- key->type = type;
key->pin_len = pin_len;
- if (new_key)
- mgmt_new_key(hdev->id, key, old_key_type);
-
- if (type == 0x06)
+ if (type == HCI_LK_CHANGED_COMBINATION)
key->type = old_key_type;
+ else
+ key->type = type;
+
+ if (!new_key)
+ return 0;
+
+ persistent = hci_persistent_key(hdev, conn, type, old_key_type);
+
+ mgmt_new_key(hdev->id, key, persistent);
+
+ if (!persistent) {
+ list_del(&key->list);
+ kfree(key);
+ }
return 0;
}
@@ -1082,6 +1138,70 @@ static void hci_cmd_timer(unsigned long arg)
tasklet_schedule(&hdev->cmd_task);
}
+struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
+ bdaddr_t *bdaddr)
+{
+ struct oob_data *data;
+
+ list_for_each_entry(data, &hdev->remote_oob_data, list)
+ if (bacmp(bdaddr, &data->bdaddr) == 0)
+ return data;
+
+ return NULL;
+}
+
+int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+ struct oob_data *data;
+
+ data = hci_find_remote_oob_data(hdev, bdaddr);
+ if (!data)
+ return -ENOENT;
+
+ BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
+
+ list_del(&data->list);
+ kfree(data);
+
+ return 0;
+}
+
+int hci_remote_oob_data_clear(struct hci_dev *hdev)
+{
+ struct oob_data *data, *n;
+
+ list_for_each_entry_safe(data, n, &hdev->remote_oob_data, list) {
+ list_del(&data->list);
+ kfree(data);
+ }
+
+ return 0;
+}
+
+int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
+ u8 *randomizer)
+{
+ struct oob_data *data;
+
+ data = hci_find_remote_oob_data(hdev, bdaddr);
+
+ if (!data) {
+ data = kmalloc(sizeof(*data), GFP_ATOMIC);
+ if (!data)
+ return -ENOMEM;
+
+ bacpy(&data->bdaddr, bdaddr);
+ list_add(&data->list, &hdev->remote_oob_data);
+ }
+
+ memcpy(data->hash, hash, sizeof(data->hash));
+ memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
+
+ BT_DBG("%s for %s", hdev->name, batostr(bdaddr));
+
+ return 0;
+}
+
/* Register HCI device */
int hci_register_dev(struct hci_dev *hdev)
{
@@ -1146,6 +1266,8 @@ int hci_register_dev(struct hci_dev *hdev)
INIT_LIST_HEAD(&hdev->link_keys);
+ INIT_LIST_HEAD(&hdev->remote_oob_data);
+
INIT_WORK(&hdev->power_on, hci_power_on);
INIT_WORK(&hdev->power_off, hci_power_off);
setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev);
@@ -1225,6 +1347,7 @@ int hci_unregister_dev(struct hci_dev *hdev)
hci_blacklist_clear(hdev);
hci_uuids_clear(hdev);
hci_link_keys_clear(hdev);
+ hci_remote_oob_data_clear(hdev);
hci_dev_unlock_bh(hdev);
__hci_dev_put(hdev);
@@ -1274,7 +1397,7 @@ int hci_recv_frame(struct sk_buff *skb)
EXPORT_SYMBOL(hci_recv_frame);
static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
- int count, __u8 index, gfp_t gfp_mask)
+ int count, __u8 index)
{
int len = 0;
int hlen = 0;
@@ -1304,7 +1427,7 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data,
break;
}
- skb = bt_skb_alloc(len, gfp_mask);
+ skb = bt_skb_alloc(len, GFP_ATOMIC);
if (!skb)
return -ENOMEM;
@@ -1390,8 +1513,7 @@ int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count)
return -EILSEQ;
while (count) {
- rem = hci_reassembly(hdev, type, data, count,
- type - 1, GFP_ATOMIC);
+ rem = hci_reassembly(hdev, type, data, count, type - 1);
if (rem < 0)
return rem;
@@ -1425,8 +1547,8 @@ int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count)
} else
type = bt_cb(skb)->pkt_type;
- rem = hci_reassembly(hdev, type, data,
- count, STREAM_REASSEMBLY, GFP_ATOMIC);
+ rem = hci_reassembly(hdev, type, data, count,
+ STREAM_REASSEMBLY);
if (rem < 0)
return rem;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index b2570159a04..f13ddbf858b 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -56,7 +56,9 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
if (status)
return;
- clear_bit(HCI_INQUIRY, &hdev->flags);
+ if (test_bit(HCI_MGMT, &hdev->flags) &&
+ test_and_clear_bit(HCI_INQUIRY, &hdev->flags))
+ mgmt_discovering(hdev->id, 0);
hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status);
@@ -72,7 +74,9 @@ static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb)
if (status)
return;
- clear_bit(HCI_INQUIRY, &hdev->flags);
+ if (test_bit(HCI_MGMT, &hdev->flags) &&
+ test_and_clear_bit(HCI_INQUIRY, &hdev->flags))
+ mgmt_discovering(hdev->id, 0);
hci_conn_check_pending(hdev);
}
@@ -195,14 +199,17 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
- return;
-
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LOCAL_NAME);