summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/bluetooth/bnep/bnep.h4
-rw-r--r--net/bluetooth/bnep/core.c70
-rw-r--r--net/bluetooth/bnep/sock.c7
-rw-r--r--net/bluetooth/cmtp/capi.c2
-rw-r--r--net/bluetooth/cmtp/core.c15
-rw-r--r--net/bluetooth/hci_core.c120
-rw-r--r--net/bluetooth/hci_debugfs.c26
-rw-r--r--net/bluetooth/hci_event.c175
-rw-r--r--net/bluetooth/hci_request.c20
-rw-r--r--net/bluetooth/hci_request.h5
-rw-r--r--net/bluetooth/hci_sock.c2
-rw-r--r--net/bluetooth/hidp/core.c15
-rw-r--r--net/bluetooth/l2cap_core.c48
-rw-r--r--net/bluetooth/l2cap_sock.c6
-rw-r--r--net/bluetooth/mgmt.c267
-rw-r--r--net/bluetooth/selftest.c35
-rw-r--r--net/bluetooth/smp.c45
17 files changed, 546 insertions, 316 deletions
diff --git a/net/bluetooth/bnep/bnep.h b/net/bluetooth/bnep/bnep.h
index 5a5b16f365e9..40854c99bc1e 100644
--- a/net/bluetooth/bnep/bnep.h
+++ b/net/bluetooth/bnep/bnep.h
@@ -111,6 +111,10 @@ struct bnep_ext_hdr {
#define BNEPCONNDEL _IOW('B', 201, int)
#define BNEPGETCONNLIST _IOR('B', 210, int)
#define BNEPGETCONNINFO _IOR('B', 211, int)
+#define BNEPGETSUPPFEAT _IOR('B', 212, int)
+
+#define BNEP_SETUP_RESPONSE 0
+#define BNEP_SETUP_RSP_SENT 10
struct bnep_connadd_req {
int sock; /* Connected socket */
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 05f57e491ccb..1641367e54ca 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -231,7 +231,14 @@ static int bnep_rx_control(struct bnep_session *s, void *data, int len)
break;
case BNEP_SETUP_CONN_REQ:
- err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, BNEP_CONN_NOT_ALLOWED);
+ /* Successful response should be sent only once */
+ if (test_bit(BNEP_SETUP_RESPONSE, &s->flags) &&
+ !test_and_set_bit(BNEP_SETUP_RSP_SENT, &s->flags))
+ err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP,
+ BNEP_SUCCESS);
+ else
+ err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP,
+ BNEP_CONN_NOT_ALLOWED);
break;
default: {
@@ -239,7 +246,7 @@ static int bnep_rx_control(struct bnep_session *s, void *data, int len)
pkt[0] = BNEP_CONTROL;
pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
pkt[2] = cmd;
- bnep_send(s, pkt, sizeof(pkt));
+ err = bnep_send(s, pkt, sizeof(pkt));
}
break;
}
@@ -292,29 +299,55 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
{
struct net_device *dev = s->dev;
struct sk_buff *nskb;
- u8 type;
+ u8 type, ctrl_type;
dev->stats.rx_bytes += skb->len;
type = *(u8 *) skb->data;
skb_pull(skb, 1);
+ ctrl_type = *(u8 *)skb->data;
if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
goto badframe;
if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
- bnep_rx_control(s, skb->data, skb->len);
- kfree_skb(skb);
- return 0;
- }
+ if (bnep_rx_control(s, skb->data, skb->len) < 0) {
+ dev->stats.tx_errors++;
+ kfree_skb(skb);
+ return 0;
+ }
- skb_reset_mac_header(skb);
+ if (!(type & BNEP_EXT_HEADER)) {
+ kfree_skb(skb);
+ return 0;
+ }
- /* Verify and pull out header */
- if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
- goto badframe;
+ /* Verify and pull ctrl message since it's already processed */
+ switch (ctrl_type) {
+ case BNEP_SETUP_CONN_REQ:
+ /* Pull: ctrl type (1 b), len (1 b), data (len bytes) */
+ if (!skb_pull(skb, 2 + *(u8 *)(skb->data + 1) * 2))
+ goto badframe;
+ break;
+ case BNEP_FILTER_MULTI_ADDR_SET:
+ case BNEP_FILTER_NET_TYPE_SET:
+ /* Pull: ctrl type (1 b), len (2 b), data (len bytes) */
+ if (!skb_pull(skb, 3 + *(u16 *)(skb->data + 1) * 2))
+ goto badframe;
+ break;
+ default:
+ kfree_skb(skb);
+ return 0;
+ }
+ } else {
+ skb_reset_mac_header(skb);
- s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
+ /* Verify and pull out header */
+ if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
+ goto badframe;
+
+ s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
+ }
if (type & BNEP_EXT_HEADER) {
if (bnep_rx_extension(s, skb) < 0)
@@ -525,6 +558,7 @@ static struct device_type bnep_type = {
int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
{
+ u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
struct net_device *dev;
struct bnep_session *s, *ss;
u8 dst[ETH_ALEN], src[ETH_ALEN];
@@ -535,6 +569,9 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
if (!l2cap_is_socket(sock))
return -EBADFD;
+ if (req->flags & ~valid_flags)
+ return -EINVAL;
+
baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
@@ -566,6 +603,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
s->sock = sock;
s->role = req->role;
s->state = BT_CONNECTED;
+ s->flags = req->flags;
s->msg.msg_flags = MSG_NOSIGNAL;
@@ -611,11 +649,15 @@ failed:
int bnep_del_connection(struct bnep_conndel_req *req)
{
+ u32 valid_flags = 0;
struct bnep_session *s;
int err = 0;
BT_DBG("");
+ if (req->flags & ~valid_flags)
+ return -EINVAL;
+
down_read(&bnep_session_sem);
s = __bnep_get_session(req->dst);
@@ -631,10 +673,12 @@ int bnep_del_connection(struct bnep_conndel_req *req)
static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
{
+ u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
+
memset(ci, 0, sizeof(*ci));
memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
strcpy(ci->device, s->dev->name);
- ci->flags = s->flags;
+ ci->flags = s->flags & valid_flags;
ci->state = s->state;
ci->role = s->role;
}
diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c
index 5f051290daba..bde2bdd9e929 100644
--- a/net/bluetooth/bnep/sock.c
+++ b/net/bluetooth/bnep/sock.c
@@ -57,6 +57,7 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
struct bnep_conninfo ci;
struct socket *nsock;
void __user *argp = (void __user *)arg;
+ __u32 supp_feat = BIT(BNEP_SETUP_RESPONSE);
int err;
BT_DBG("cmd %x arg %lx", cmd, arg);
@@ -120,6 +121,12 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
return err;
+ case BNEPGETSUPPFEAT:
+ if (copy_to_user(argp, &supp_feat, sizeof(supp_feat)))
+ return -EFAULT;
+
+ return 0;
+
default:
return -EINVAL;
}
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c
index 75bd2c42e3e7..b0c6c6af76ef 100644
--- a/net/bluetooth/cmtp/capi.c
+++ b/net/bluetooth/cmtp/capi.c
@@ -333,7 +333,7 @@ void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb)
return;
}
- if (session->flags & (1 << CMTP_LOOPBACK)) {
+ if (session->flags & BIT(CMTP_LOOPBACK)) {
kfree_skb(skb);
return;
}
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index 278a194e6af4..298ed37010e6 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -75,10 +75,11 @@ static void __cmtp_unlink_session(struct cmtp_session *session)
static void __cmtp_copy_session(struct cmtp_session *session, struct cmtp_conninfo *ci)
{
+ u32 valid_flags = BIT(CMTP_LOOPBACK);
memset(ci, 0, sizeof(*ci));
bacpy(&ci->bdaddr, &session->bdaddr);
- ci->flags = session->flags;
+ ci->flags = session->flags & valid_flags;
ci->state = session->state;
ci->num = session->num;
@@ -313,7 +314,7 @@ static int cmtp_session(void *arg)
down_write(&cmtp_session_sem);
- if (!(session->flags & (1 << CMTP_LOOPBACK)))
+ if (!(session->flags & BIT(CMTP_LOOPBACK)))
cmtp_detach_device(session);
fput(session->sock->file);
@@ -329,6 +330,7 @@ static int cmtp_session(void *arg)
int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
{
+ u32 valid_flags = BIT(CMTP_LOOPBACK);
struct cmtp_session *session, *s;
int i, err;
@@ -337,6 +339,9 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
if (!l2cap_is_socket(sock))
return -EBADFD;
+ if (req->flags & ~valid_flags)
+ return -EINVAL;
+
session = kzalloc(sizeof(struct cmtp_session), GFP_KERNEL);
if (!session)
return -ENOMEM;
@@ -385,7 +390,7 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
goto unlink;
}
- if (!(session->flags & (1 << CMTP_LOOPBACK))) {
+ if (!(session->flags & BIT(CMTP_LOOPBACK))) {
err = cmtp_attach_device(session);
if (err < 0) {
atomic_inc(&session->terminate);
@@ -409,11 +414,15 @@ failed:
int cmtp_del_connection(struct cmtp_conndel_req *req)
{
+ u32 valid_flags = 0;
struct cmtp_session *session;
int err = 0;
BT_DBG("");
+ if (req->flags & ~valid_flags)
+ return -EINVAL;
+
down_read(&cmtp_session_sem);
session = __cmtp_get_session(&req->bdaddr);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index e6bfeb7b4415..46b114c0140b 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -141,13 +141,16 @@ static const struct file_operations dut_mode_fops = {
/* ---- HCI requests ---- */
-static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode)
+static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
+ struct sk_buff *skb)
{
BT_DBG("%s result 0x%2.2x", hdev->name, result);
if (hdev->req_status == HCI_REQ_PEND) {
hdev->req_result = result;
hdev->req_status = HCI_REQ_DONE;
+ if (skb)
+ hdev->req_skb = skb_get(skb);
wake_up_interruptible(&hdev->req_wait_q);
}
}
@@ -163,66 +166,12 @@ static void hci_req_cancel(struct hci_dev *hdev, int err)
}
}
-static struct sk_buff *hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
- u8 event)
-{
- struct hci_ev_cmd_complete *ev;
- struct hci_event_hdr *hdr;
- struct sk_buff *skb;
-
- hci_dev_lock(hdev);
-
- skb = hdev->recv_evt;
- hdev->recv_evt = NULL;
-
- hci_dev_unlock(hdev);
-
- if (!skb)
- return ERR_PTR(-ENODATA);
-
- if (skb->len < sizeof(*hdr)) {
- BT_ERR("Too short HCI event");
- goto failed;
- }
-
- hdr = (void *) skb->data;
- skb_pull(skb, HCI_EVENT_HDR_SIZE);
-
- if (event) {
- if (hdr->evt != event)
- goto failed;
- return skb;
- }
-
- if (hdr->evt != HCI_EV_CMD_COMPLETE) {
- BT_DBG("Last event is not cmd complete (0x%2.2x)", hdr->evt);
- goto failed;
- }
-
- if (skb->len < sizeof(*ev)) {
- BT_ERR("Too short cmd_complete event");
- goto failed;
- }
-
- ev = (void *) skb->data;
- skb_pull(skb, sizeof(*ev));
-
- if (opcode == __le16_to_cpu(ev->opcode))
- return skb;
-
- BT_DBG("opcode doesn't match (0x%2.2x != 0x%2.2x)", opcode,
- __le16_to_cpu(ev->opcode));
-
-failed:
- kfree_skb(skb);
- return ERR_PTR(-ENODATA);
-}
-
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param, u8 event, u32 timeout)
{
DECLARE_WAITQUEUE(wait, current);
struct hci_request req;
+ struct sk_buff *skb;
int err = 0;
BT_DBG("%s", hdev->name);
@@ -236,7 +185,7 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
add_wait_queue(&hdev->req_wait_q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- err = hci_req_run(&req, hci_req_sync_complete);
+ err = hci_req_run_skb(&req, hci_req_sync_complete);
if (err < 0) {
remove_wait_queue(&hdev->req_wait_q, &wait);
set_current_state(TASK_RUNNING);
@@ -265,13 +214,20 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
}
hdev->req_status = hdev->req_result = 0;
+ skb = hdev->req_skb;
+ hdev->req_skb = NULL;
BT_DBG("%s end: err %d", hdev->name, err);
- if (err < 0)
+ if (err < 0) {
+ kfree_skb(skb);
return ERR_PTR(err);
+ }
+
+ if (!skb)
+ return ERR_PTR(-ENODATA);
- return hci_get_cmd_complete(hdev, opcode, event);
+ return skb;
}
EXPORT_SYMBOL(__hci_cmd_sync_ev);
@@ -303,7 +259,7 @@ static int __hci_req_sync(struct hci_dev *hdev,
add_wait_queue(&hdev->req_wait_q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
- err = hci_req_run(&req, hci_req_sync_complete);
+ err = hci_req_run_skb(&req, hci_req_sync_complete);
if (err < 0) {
hdev->req_status = 0;
@@ -1690,9 +1646,6 @@ static int hci_dev_do_close(struct hci_dev *hdev)
hdev->sent_cmd = NULL;
}
- kfree_skb(hdev->recv_evt);
- hdev->recv_evt = NULL;
-
/* After this point our queues are empty
* and no tasks are scheduled. */
hdev->close(hdev);
@@ -3563,11 +3516,6 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
}
}
-bool hci_req_pending(struct hci_dev *hdev)
-{
- return (hdev->req_status == HCI_REQ_PEND);
-}
-
/* Send HCI command */
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
const void *param)
@@ -3585,7 +3533,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
/* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
- bt_cb(skb)->req_start = 1;
+ bt_cb(skb)->req.start = true;
skb_queue_tail(&hdev->cmd_q, skb);
queue_work(hdev->workqueue, &hdev->cmd_work);
@@ -4263,7 +4211,7 @@ static bool hci_req_is_complete(struct hci_dev *hdev)
if (!skb)
return true;
- return bt_cb(skb)->req_start;
+ return bt_cb(skb)->req.start;
}
static void hci_resend_last(struct hci_dev *hdev)
@@ -4288,9 +4236,10 @@ static void hci_resend_last(struct hci_dev *hdev)
queue_work(hdev->workqueue, &hdev->cmd_work);
}
-void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status)
+void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
+ hci_req_complete_t *req_complete,
+ hci_req_complete_skb_t *req_complete_skb)
{
- hci_req_complete_t req_complete = NULL;
struct sk_buff *skb;
unsigned long flags;
@@ -4322,36 +4271,29 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status)
* callback would be found in hdev->sent_cmd instead of the
* command queue (hdev->cmd_q).
*/
- if (hdev->sent_cmd) {
- req_complete = bt_cb(hdev->sent_cmd)->req_complete;
-
- if (req_complete) {
- /* We must set the complete callback to NULL to
- * avoid calling the callback more than once if
- * this function gets called again.
- */
- bt_cb(hdev->sent_cmd)->req_complete = NULL;
+ if (bt_cb(hdev->sent_cmd)->req.complete) {
+ *req_complete = bt_cb(hdev->sent_cmd)->req.complete;
+ return;
+ }
- goto call_complete;
- }
+ if (bt_cb(hdev->sent_cmd)->req.complete_skb) {
+ *req_complete_skb = bt_cb(hdev->sent_cmd)->req.complete_skb;
+ return;
}
/* Remove all pending commands belonging to this request */
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
while ((skb = __skb_dequeue(&hdev->cmd_q))) {
- if (bt_cb(skb)->req_start) {
+ if (bt_cb(skb)->req.start) {
__skb_queue_head(&hdev->cmd_q, skb);
break;
}
- req_complete = bt_cb(skb)->req_complete;
+ *req_complete = bt_cb(skb)->req.complete;
+ *req_complete_skb = bt_cb(skb)->req.complete_skb;
kfree_skb(skb);
}
spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
-
-call_complete:
- if (req_complete)
- req_complete(hdev, status, status ? opcode : HCI_OP_NOP);
}
static void hci_rx_work(struct work_struct *work)
diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c
index e6255833a258..7db4220941cc 100644
--- a/net/bluetooth/hci_debugfs.c
+++ b/net/bluetooth/hci_debugfs.c
@@ -114,6 +114,30 @@ static const struct file_operations features_fops = {
.release = single_release,
};
+static int device_id_show(struct seq_file *f, void *ptr)
+{
+ struct hci_dev *hdev = f->private;
+
+ hci_dev_lock(hdev);
+ seq_printf(f, "%4.4x:%4.4x:%4.4x:%4.4x\n", hdev->devid_source,
+ hdev->devid_vendor, hdev->devid_product, hdev->devid_version);
+ hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static int device_id_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, device_id_show, inode->i_private);
+}
+
+static const struct file_operations device_id_fops = {
+ .open = device_id_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static int device_list_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
@@ -335,6 +359,8 @@ void hci_debugfs_create_common(struct hci_dev *hdev)
debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
debugfs_create_u8("hardware_error", 0444, hdev->debugfs,
&hdev->hw_error_code);
+ debugfs_create_file("device_id", 0444, hdev->debugfs, hdev,
+ &device_id_fops);
debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
&device_list_fops);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 62f92a508961..01031038eb0e 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1045,11 +1045,6 @@ static void hci_cc_read_local_oob_data(struct hci_dev *hdev,
struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
-
- hci_dev_lock(hdev);
- mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->rand, NULL, NULL,
- rp->status);
- hci_dev_unlock(hdev);
}
static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev,
@@ -1058,15 +1053,8 @@ static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev,
struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data;
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
-
- hci_dev_lock(hdev);
- mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->rand192,
- rp->hash256, rp->rand256,
- rp->status);
- hci_dev_unlock(hdev);
}
-
static void hci_cc_le_set_random_addr(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -2732,17 +2720,19 @@ unlock:
hci_dev_unlock(hdev);
}
-static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
+ u16 *opcode, u8 *status,
+ hci_req_complete_t *req_complete,
+ hci_req_complete_skb_t *req_complete_skb)
{
struct hci_ev_cmd_complete *ev = (void *) skb->data;
- u8 status = skb->data[sizeof(*ev)];
- __u16 opcode;
- skb_pull(skb, sizeof(*ev));
+ *opcode = __le16_to_cpu(ev->opcode);
+ *status = skb->data[sizeof(*ev)];
- opcode = __le16_to_cpu(ev->opcode);
+ skb_pull(skb, sizeof(*ev));
- switch (opcode) {
+ switch (*opcode) {
case HCI_OP_INQUIRY_CANCEL:
hci_cc_inquiry_cancel(hdev, skb);
break;
@@ -3020,32 +3010,36 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
break;
default:
- BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
+ BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode);
break;
}
- if (opcode != HCI_OP_NOP)
+ if (*opcode != HCI_OP_NOP)
cancel_delayed_work(&hdev->cmd_timer);
- hci_req_cmd_complete(hdev, opcode, status);
-
- if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
+ if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags))
atomic_set(&hdev->cmd_cnt, 1);
- if (!skb_queue_empty(&hdev->cmd_q))
- queue_work(hdev->workqueue, &hdev->cmd_work);
- }
+
+ hci_req_cmd_complete(hdev, *opcode, *status, req_complete,
+ req_complete_skb);
+
+ if (atomic_read(&hdev->cmd_cnt) && !skb_queue_empty(&hdev->cmd_q))
+ queue_work(hdev->workqueue, &hdev->cmd_work);
}
-static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
+static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb,
+ u16 *opcode, u8 *status,
+ hci_req_complete_t *req_complete,
+ hci_req_complete_skb_t *req_complete_skb)
{
struct hci_ev_cmd_status *ev = (void *) skb->data;
- __u16 opcode;
skb_pull(skb, sizeof(*ev));
- opcode = __le16_to_cpu(ev->opcode);
+ *opcode = __le16_to_cpu(ev->opcode);
+ *status = ev->status;
- switch (opcode) {
+ switch (*opcode) {
case HCI_OP_INQUIRY:
hci_cs_inquiry(hdev, ev->status);
break;
@@ -3115,22 +3109,29 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
break;
default:
- BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
+ BT_DBG("%s opcode 0x%4.4x", hdev->name, *opcode);
break;
}
- if (opcode != HCI_OP_NOP)
+ if (*opcode != HCI_OP_NOP)
cancel_delayed_work(&hdev->cmd_timer);
+ if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags))
+ atomic_set(&hdev->cmd_cnt, 1);
+
+ /* Indicate request completion if the command failed. Also, if
+ * we're not waiting for a special event and we get a success
+ * command status we should try to flag the request as completed
+ * (since for this kind of commands there will not be a command
+ * complete event).
+ */
if (ev->status ||
- (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req_event))
- hci_req_cmd_complete(hdev, opcode, ev->status);
+ (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req.event))
+ hci_req_cmd_complete(hdev, *opcode, ev->status, req_complete,
+ req_complete_skb);
- if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
- atomic_set(&hdev->cmd_cnt, 1);
- if (!skb_queue_empty(&hdev->cmd_q))
- queue_work(hdev->workqueue, &hdev->cmd_work);
- }
+ if (atomic_read(&hdev->cmd_cnt) && !skb_queue_empty(&hdev->cmd_q))
+ queue_work(hdev->workqueue, &hdev->cmd_work);
}
static void hci_hardware_error_evt(struct hci_dev *hdev, struct sk_buff *skb)
@@ -5031,32 +5032,79 @@ static void hci_chan_selected_evt(struct hci_dev *hdev, struct sk_buff *skb)
amp_read_loc_assoc_final_data(hdev, hcon);
}
-void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
+static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
+ u8 event, struct sk_buff *skb)
{
- struct hci_event_hdr *hdr = (void *) skb->data;
- __u8 event = hdr->evt;
+ struct hci_ev_cmd_complete *ev;
+ struct hci_event_hdr *hdr;
- hci_dev_lock(hdev);
+ if (!skb)
+ return false;
- /* Received events are (currently) only needed when a request is
- * ongoing so avoid unnecessary memory allocation.
- */
- if (hci_req_pending(hdev)) {
- kfree_skb(hdev->recv_evt);
- hdev->recv_evt = skb_clone(skb, GFP_KERNEL);
+ if (skb->len < sizeof(*hdr)) {
+ BT_ERR("Too short HCI event");
+ return false;
}
- hci_dev_unlock(hdev);
-
+ hdr = (void *) skb->data;
skb_pull(skb, HCI_EVENT_HDR_SIZE);
- if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req_event == event) {
- struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data;
- u16 opcode = __le16_to_cpu(cmd_hdr->opcode);
+ if (event) {
+ if (hdr->evt != event)
+ return false;
+ return true;
+ }
+
+ if (hdr->evt != HCI_EV_CMD_COMPLETE) {
+ BT_DBG("Last event is not cmd complete (0x%2.2x)", hdr->evt);
+ return false;
+ }
+
+ if (skb->len < sizeof(*ev)) {
+ BT_ERR("Too short cmd_complete event");
+ return false;
+ }
+
+ ev = (void *) skb->data;
+ skb_pull(skb, sizeof(*ev));
- hci_req_cmd_complete(hdev, opcode, 0);
+ if (opcode != __le16_to_cpu(ev->opcode)) {
+ BT_DBG("opcode doesn't match (0x%2.2x != 0x%2.2x)", opcode,
+ __le16_to_cpu(ev->opcode));
+ return false;
}
+ return true;
+}
+
+void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_event_hdr *hdr = (void *) skb->data;
+ hci_req_complete_t req_complete = NULL;
+ hci_req_complete_skb_t req_complete_skb = NULL;
+ struct sk_buff *orig_skb = NULL;
+ u8 status = 0, event = hdr->evt, req_evt = 0;
+ u16 opcode = HCI_OP_NOP;
+
+ if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) {
+ struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data;
+ opcode = __le16_to_cpu(cmd_hdr->opcode);
+ hci_req_cmd_complete(hdev, opcode, status, &req_complete,
+ &req_complete_skb);
+ req_evt = event;
+ }
+
+ /* If it looks like we might end up having to call
+ * req_complete_skb, store a pristine copy of the skb since the
+ * various handlers may modify the original one through
+ * skb_pull() calls, etc.
+ */
+ if (req_complete_skb || event == HCI_EV_CMD_STATUS ||
+ event == HCI_EV_CMD_COMPLETE)
+ orig_skb = skb_clone(skb, GFP_KERNEL);
+
+ skb_pull(skb, HCI_EVENT_HDR_SIZE);
+
switch (event) {
case HCI_EV_INQUIRY_COMPLETE:
hci_inquiry_complete_evt(hdev, skb);
@@ -5099,11 +5147,13 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
break;
case HCI_EV_CMD_COMPLETE:
- hci_cmd_complete_evt(hdev, skb);
+ hci_cmd_complete_evt(hdev, skb, &opcode, &status,
+ &req_complete, &req_complete_skb);
break;
case HCI_EV_CMD_STATUS:
- hci_cmd_status_evt(hdev, skb);
+ hci_cmd_status_evt(hdev, skb, &opcode, &status, &req_complete,
+ &req_complete_skb);
break;
case HCI_EV_HARDWARE_ERROR:
@@ -5235,6 +5285,17 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
break;
}
+ if (req_complete) {
+ req_complete(hdev, status, opcode);
+ } else if (req_complete_skb) {
+ if (!hci_get_cmd_complete(hdev, opcode, req_evt, orig_skb)) {
+ kfree_skb(orig_skb);
+ orig_skb = NULL;
+ }
+ req_complete_skb(hdev, status, opcode, orig_skb);
+ }
+
+ kfree_skb(orig_skb);
kfree_skb(skb);
hdev->stat.evt_rx++;
}
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index 55e096d20a0f..d6025d6e6d59 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -34,7 +34,8 @@ void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
req->err = 0;
}
-int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
+static int req_run(struct hci_request *req, hci_req_complete_t complete,
+ hci_req_complete_skb_t complete_skb)
{
struct hci_dev *hdev = req->hdev;
struct sk_buff *skb;
@@ -55,7 +56,8 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
return -ENODATA;
skb = skb_peek_tail(&req->cmd_q);
- bt_cb(skb)->req_complete = complete;
+ bt_cb(skb)->req.complete = complete;
+ bt_cb(skb)->req.complete_skb = complete_skb;
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);
@@ -66,6 +68,16 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
return 0;
}
+int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
+{
+ return req_run(req, complete, NULL);
+}
+
+int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete)
+{
+ return req_run(req, NULL, complete);
+}
+
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param)
{
@@ -116,9 +128,9 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
}
if (skb_queue_empty(&req->cmd_q))
- bt_cb(skb)->req_start = 1;
+ bt_cb(skb)->req.start = true;
- bt_cb(skb)->req_event = event;
+ bt_cb(skb)->req.event = event;
skb_queue_tail(&req->cmd_q, skb);
}
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
index adf074d33544..bf6df92f42db 100644
--- a/net/bluetooth/hci_request.h
+++ b/net/bluetooth/hci_request.h
@@ -32,11 +32,14 @@ struct hci_request {
void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
+int hci_req_run_skb(struct hci_request *req, hci_req_complete_skb_t complete);
void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
const void *param);
void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
const void *param, u8 event);
-void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status);
+void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status,
+ hci_req_complete_t *req_complete,
+ hci_req_complete_skb_t *req_complete_skb);
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param);
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 85a44a7dc150..56f9edbf3d05 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -1164,7 +1164,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
/* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
- bt_cb(skb)->req_start = 1;
+ bt_cb(skb)->req.start = true;
skb_queue_tail(&hdev->cmd_q, skb);
queue_work(hdev->workqueue, &hdev->cmd_work);
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 07348e142f16..a05b9dbf14c9 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -70,10 +70,11 @@ static void hidp_session_terminate(struct hidp_session *s);
static void hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci)
{
+ u32 valid_flags = 0;
memset(ci, 0, sizeof(*ci));
bacpy(&ci->bdaddr, &session->bdaddr);
- ci->flags = session->flags;
+ ci->flags = session->flags & valid_flags;
ci->state = BT_CONNECTED;
if (session->input) {
@@ -907,7 +908,7 @@ static int hidp_session_new(struct hidp_session **out, const bdaddr_t *bdaddr,
kref_init(&session->ref);
atomic_set(&session->state, HIDP_SESSION_IDLING);
init_waitqueue_head(&session->state_queue);
- session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID);
+ session->flags = req->flags & BIT(HIDP_BLUETOOTH_VENDOR_ID);
/* connection management */
bacpy(&session->bdaddr, bdaddr);
@@ -1312,6 +1313,7 @@ int hidp_connection_add(struct hidp_connadd_req *req,
struct socket *ctrl_sock,
struct socket *intr_sock)
{
+ u32 valid_flags = 0;
struct hidp_session *session;
struct l2cap_conn *conn;
struct l2cap_chan *chan;
@@ -1321,6 +1323,9 @@ int hidp_connection_add(struct hidp_connadd_req *req,
if (ret)
return ret;
+ if (req->flags & ~valid_flags)
+ return -EINVAL;
+
chan = l2cap_pi(ctrl_sock->sk)->chan;
conn = NULL;
l2cap_chan_lock(chan);
@@ -1351,13 +1356,17 @@ out_conn:
int hidp_connection_del(struct hidp_conndel_req *req)
{
+ u32 valid_flags = BIT(HIDP_VIRTUAL_CABLE_UNPLUG);
struct hidp_session *session;
+ if (req->flags & ~valid_flags)
+ return -EINVAL;
+
session = hidp_session_find(&req->bdaddr);
if (!session)
return -ENOENT;
- if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG))
+ if (req->flags & BIT(HIDP_VIRTUAL_CABLE_UNPLUG))
hidp_send_ctrl_message(session,
HIDP_TRANS_HID_CONTROL |
HIDP_CTRL_VIRTUAL_CABLE_UNPLUG,
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index d69861c89bb5..dad419782a12 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -292,7 +292,7 @@ static struct sk_buff *l2cap_ertm_seq_in_queue(struct sk_buff_head *head,
struct sk_buff *skb;
skb_queue_walk(head, skb) {
- if (bt_cb(skb)->control.txseq == seq)
+ if (bt_cb(skb)->l2cap.txseq == seq)
return skb;
}
@@ -954,11 +954,11 @@ static inline void __unpack_control(struct l2cap_chan *chan,
{
if (test_bit(FLAG_EXT_CTRL, &chan->flags)) {
__unpack_extended_control(get_unaligned_le32(skb->data),
- &bt_cb(skb)->control);
+ &bt_cb(skb)->l2cap);
skb_pull(skb, L2CAP_EXT_CTRL_SIZE);
} else {
__unpack_enhanced_control(get_unaligned_le16(skb->data),
- &bt_cb(skb)->control);
+ &bt_cb(skb)->l2cap);
skb_pull(skb, L2CAP_ENH_CTRL_SIZE);
}
}
@@ -1200,8 +1200,8 @@ static void l2cap_move_setup(struct l2cap_chan *chan)
chan->retry_count = 0;
skb_queue_walk(&chan->tx_q, skb) {
- if (bt_cb(skb)->control.retries)
- bt_cb(skb)->control.retries = 1;
+ if (bt_cb(skb)->l2cap.retries)
+ bt_cb(skb)->l2cap.retries = 1;
else
break;
}
@@ -1846,8 +1846,8 @@ static void l2cap_streaming_send(struct l2cap_chan *chan,
skb = skb_dequeue(&chan->tx_q);
- bt_cb(skb)->control.retries = 1;
- control = &bt_cb(skb)->control;
+ bt_cb(skb)->l2cap.retries = 1;
+ control = &bt_cb(skb)->l2cap;
control->reqseq = 0;
control->txseq = chan->next_tx_seq;
@@ -1891,8 +1891,8 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
skb = chan->tx_send_head;
- bt_cb(skb)->control.retries = 1;
- control = &bt_cb(skb)->control;
+ bt_cb(skb)->l2cap.retries = 1;
+ control = &bt_cb(skb)->l2cap;
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state))
control->final = 1;
@@ -1963,11 +1963,11 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan)
continue;
}
- bt_cb(skb)->control.retries++;
- control = bt_cb(skb)->control;
+ bt_cb(skb)->l2cap.retries++;
+ control = bt_cb(skb)->l2cap;
if (chan->max_tx != 0 &&
- bt_cb(skb)->control.retries > chan->max_tx) {
+ bt_cb(skb)->l2cap.retries > chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
l2cap_send_disconn_req(chan, ECONNRESET);
l2cap_seq_list_clear(&chan->retrans_list);
@@ -2045,7 +2045,7 @@ static void l2cap_retransmit_all(struct l2cap_chan *chan,
if (chan->unacked_frames) {
skb_queue_walk(&chan->tx_q, skb) {
- if (bt_cb(skb)->control.txseq == control->reqseq ||
+ if (bt_cb(skb)->l2cap.txseq == control->reqseq ||
skb == chan->tx_send_head)
break;
}
@@ -2055,7 +2055,7 @@ static void l2cap_retransmit_all(struct l2cap_chan *chan,
break;
l2cap_seq_list_append(&chan->retrans_list,
- bt_cb(skb)->control.txseq);
+ bt_cb(skb)->l2cap.txseq);
}
l2cap_ertm_resend(chan);
@@ -2267,8 +2267,8 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
return ERR_PTR(err);
}
- bt_cb(skb)->control.fcs = chan->fcs;
- bt_cb(skb)->control.retries = 0;
+ bt_cb(skb)->l2cap.fcs = chan->fcs;
+ bt_cb(skb)->l2cap.retries = 0;
return skb;
}
@@ -2321,7 +2321,7 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
return PTR_ERR(skb);
}
- bt_cb(skb)->control.sar = sar;
+ bt_cb(skb)->l2cap.sar = sar;
__skb_queue_tail(seg_queue, skb);
len -= pdu_len;
@@ -2856,7 +2856,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
continue;
/* Don't send frame to the channel it came from */
- if (bt_cb(skb)->chan == chan)
+ if (bt_cb(skb)->l2cap.chan == chan)
continue;
nskb = skb_clone(skb, GFP_KERNEL);
@@ -5918,7 +5918,7 @@ static int l2cap_rx_queued_iframes(struct l2cap_chan *chan)
skb_unlink(skb, &chan->srej_q);
chan->buffer_seq = __next_seq(chan, chan->buffer_seq);
- err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->control);
+ err = l2cap_reassemble_sdu(chan, skb, &bt_cb(skb)->l2cap);
if (err)
break;
}
@@ -5952,7 +5952,7 @@ static void l2cap_handle_srej(struct l2cap_chan *chan,
return;
}
- if (chan->max_tx != 0 && bt_cb(skb)->control.retries >= chan->max_tx) {
+ if (chan->max_tx != 0 && bt_cb(skb)->l2cap.retries >= chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
l2cap_send_disconn_req(chan, ECONNRESET);
return;
@@ -6005,7 +6005,7 @@ static void l2cap_handle_rej(struct l2cap_chan *chan,
skb = l2cap_ertm_seq_in_queue(&chan->tx_q, control->reqseq);
if (chan->max_tx && skb &&
- bt_cb(skb)->control.retries >= chan->max_tx) {
+ bt_cb(skb)->l2cap.retries >= chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
l2cap_send_disconn_req(chan, ECONNRESET);
return;
@@ -6565,7 +6565,7 @@ static int l2cap_stream_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
{
- struct l2cap_ctrl *control = &bt_cb(skb)->control;
+ struct l2cap_ctrl *control = &bt_cb(skb)->l2cap;
u16 len;
u8 event;
@@ -6864,8 +6864,8 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
goto drop;
/* Store remote BD_ADDR and PSM for msg_name */
- bacpy(&bt_cb(skb)->bdaddr, &hcon->dst);
- bt_cb(skb)->psm = psm;
+ bacpy(&bt_cb(skb)->l2cap.bdaddr, &hcon->dst);
+ bt_cb(skb)->l2cap.psm = psm;
if (!chan->ops->recv(chan, skb)) {
l2cap_chan_put(chan);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 9070720eedc8..a7278f05eafb 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -1330,7 +1330,7 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
skb->priority = sk->sk_priority;
- bt_cb(skb)->chan = chan;
+ bt_cb(skb)->l2cap.chan = chan;
return skb;
}
@@ -1444,8 +1444,8 @@ static void l2cap_skb_msg_name(struct sk_buff *skb, void *msg_name,
memset(la, 0, sizeof(struct sockaddr_l2));
la->l2_family = AF_BLUETOOTH;
- la->l2_psm = bt_cb(skb)->psm;
- bacpy(&la->l2_bdaddr, &bt_cb(skb)->bdaddr);
+ la->l2_psm = bt_cb(skb)->l2cap.psm;
+ bacpy(&la->l2_bdaddr, &bt_cb(skb)->l2cap.bdaddr);
*msg_namelen = sizeof(struct sockaddr_l2);
}
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index fb2e764c6211..845dfcc43a20 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -985,14 +985,27 @@ static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance)
/* Instance 0 always manages the "Tx Power" and "Flags" fields */
flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
- /* For instance 0, assemble the flags from global settings */
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE) ||
- get_connectable(hdev))
+ /* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting corresponds
+ * to the "connectable" instance flag.
+ */
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
flags |= MGMT_ADV_FLAG_CONNECTABLE;
return flags;
}
+static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance)
+{
+ /* Ignore instance 0 and other unsupported instances */
+ if (instance != 0x01)
+ return 0;
+
+ /* TODO: Take into account the "appearance" and "local-name" flags here.
+ * These are currently being ignored as they are not supported.
+ */
+ return hdev->adv_instance.scan_rsp_len;
+}
+
static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
{
u8 ad_len = 0, flags = 0;
@@ -1030,6 +1043,14 @@ static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
}
}
+ if (instance) {
+ memcpy(ptr, hdev->adv_instance.adv_data,
+ hdev->adv_instance.adv_data_len);
+
+ ad_len += hdev->adv_instance.adv_data_len;
+ ptr += hdev->adv_instance.adv_data_len;
+ }
+
/* Provide Tx Power only if we can provide a valid value for it */
if (hdev->adv_tx_power != HCI_TX_POWER_INVALID &&
(instance_flags & MGMT_ADV_FLAG_TX_POWER)) {
@@ -1041,12 +1062,6 @@ static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
ptr += 3;
}
- if (instance) {
- memcpy(ptr, hdev->adv_instance.adv_data,
- hdev->adv_instance.adv_data_len);
- ad_len += hdev->adv_instance.adv_data_len;
- }
-
return ad_len;
}
@@ -1242,7 +1257,12 @@ static void enable_advertising(struct hci_request *req)
instance = get_current_adv_instance(hdev);
flags = get_adv_instance_flags(hdev, instance);
- connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE);
+
+ /* If the "connectable" instance flag was not set, then choose between
+ * ADV_IND and ADV_NONCONN_IND based on the global connectable setting.
+ */
+ connectable = (flags & MGMT_ADV_FLAG_CONNECTABLE) ||
+ get_connectable(hdev);
/* Set require_privacy to true only when non-connectable
* advertising is used. In that case it is fine to use a
@@ -1254,7 +1274,14 @@ static void enable_advertising(struct hci_request *req)
memset(&cp, 0, sizeof(cp));
cp.min_interval = cpu_to_le16(hdev->le_adv_min_interval);
cp.max_interval = cpu_to_le16(hdev->le_adv_max_interval);
- cp.type = connectable ? LE_ADV_IND : LE_ADV_NONCONN_IND;
+
+ if (connectable)
+ cp.type = LE_ADV_IND;
+ else if (get_adv_instance_scan_rsp_len(hdev, instance))
+ cp.type = LE_ADV_SCAN_IND;
+ else
+ cp.type = LE_ADV_NONCONN_IND;
+
cp.own_address_type = own_addr_type;
cp.channel_map = hdev->le_adv_channel_map;
@@ -2088,7 +2115,8 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
no_scan_update:
/* Update the advertising parameters if necessary */
- if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+ hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
enable_advertising(&req);
err = hci_req_run(&req, set_connectable_complete);
@@ -3757,10 +3785,70 @@ failed:
return err;
}
+static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
+ u16 opcode, struct sk_buff *skb)
+{
+ struct mgmt_rp_read_local_oob_data mgmt_rp;
+ size_t rp_size = sizeof(mgmt_rp);
+ struct mgmt_pending_cmd *cmd;
+
+ BT_DBG("%s status %u", hdev->name, status);
+
+ cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
+ if (!cmd)
+ return;
+
+ if (status || !skb) {
+ mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+ status ? mgmt_status(status) : MGMT_STATUS_FAILED);
+ goto remove;
+ }
+
+ memset(&mgmt_rp, 0, sizeof(mgmt_rp));
+
+ if (opcode == HCI_OP_READ_LOCAL_OOB_DATA) {
+ struct hci_rp_read_local_oob_data *rp = (void *) skb->data;
+
+ if (skb->len < sizeof(*rp)) {
+ mgmt_cmd_status(cmd->sk, hdev->id,
+ MGMT_OP_READ_LOCAL_OOB_DATA,
+ MGMT_STATUS_FAILED);
+ goto remove;
+ }
+
+ memcpy(mgmt_rp.hash192, rp->hash, sizeof(rp->hash));
+ memcpy(mgmt_rp.rand192, rp->rand, sizeof(rp->rand));
+
+ rp_size -= sizeof(mgmt_rp.hash256) + sizeof(mgmt_rp.rand256);
+ } else {
+ struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data;
+
+ if (skb->len < sizeof(*rp)) {
+ mgmt_cmd_status(cmd->sk, hdev->id,
+ MGMT_OP_READ_LOCAL_OOB_DATA,
+ MGMT_STATUS_FAILED);
+ goto remove;
+ }
+
+ memcpy(mgmt_rp.hash192, rp->hash192, sizeof(rp->hash192));
+ memcpy(mgmt_rp.rand192, rp->rand192, sizeof(rp->rand192));
+
+ memcpy(mgmt_rp.hash256, rp->hash256, sizeof(rp->hash256));
+ memcpy(mgmt_rp.rand256, rp->rand256, sizeof(rp->rand256));
+ }
+
+ mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
+ MGMT_STATUS_SUCCESS, &mgmt_rp, rp_size);
+
+remove:
+ mgmt_pending_remove(cmd);
+}
+
static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
struct mgmt_pending_cmd *cmd;
+ struct hci_request req;
int err;
BT_DBG("%s", hdev->name);
@@ -3791,12 +3879,14 @@ static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
goto unlock;
}
+ hci_req_init(&req, hdev);
+
if (bredr_sc_enabled(hdev))
- err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_EXT_DATA,
- 0, NULL);
+ hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_EXT_DATA, 0, NULL);
else
- err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+ hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
+ err = hci_req_run_skb(&req, read_local_oob_data_complete);
if (err < 0)
mgmt_pending_remove(cmd);
@@ -6388,46 +6478,41 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
BT_DBG("%s", hdev->name);
- if (!hdev_is_powered(hdev))
- return mgmt_cmd_complete(sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
- MGMT_STATUS_NOT_POWERED,
- &cp->type, sizeof(cp->type));
-
- switch (cp->type) {
- case BIT(BDADDR_BREDR):
- status = mgmt_bredr_support(hdev);
- if (status)
- return mgmt_cmd_complete(sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
- status, &cp->type,
- sizeof(cp->type));
- eir_len = 5;
- break;
- case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)):
- status = mgmt_le_support(hdev);
- if (status)
- return mgmt_cmd_complete(sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
- status, &cp->type,
- sizeof(cp->type));
- eir_len = 9 + 3 + 18 + 18 + 3;
- break;
- default:
- return mgmt_cmd_complete(sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
- MGMT_STATUS_INVALID_PARAMS,
- &cp->type, sizeof(cp->type));
+ if (hdev_is_powered(hdev)) {
+ switch (cp->type) {
+ case BIT(BDADDR_BREDR):
+ status = mgmt_bredr_support(hdev);
+ if (status)
+ eir_len = 0;
+ else
+ eir_len = 5;
+ break;
+ case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)):
+ status = mgmt_le_support(hdev);
+ if (status)
+ eir_len = 0;
+ else
+ eir_len = 9 + 3 + 18 + 18 + 3;
+ break;
+ default:
+ status = MGMT_STATUS_INVALID_PARAMS;
+ eir_len = 0;
+ break;
+ }
+ } else {
+ status = MGMT_STATUS_NOT_POWERED;
+ eir_len = 0;
}
- hci_dev_lock(hdev);
-
rp_len = sizeof(*rp) + eir_len;
rp = kmalloc(rp_len, GFP_ATOMIC);
- if (!rp) {
- hci_dev_unlock(hdev);
+ if (!rp)
return -ENOMEM;
- }
+
+ if (status)
+ goto complete;
+
+ hci_dev_lock(hdev);
eir_len = 0;
switch (cp->type) {
@@ -6439,20 +6524,30 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
if (hci_dev_test_flag(hdev, HCI_SC_ENABLED) &&
smp_generate_oob(hdev, hash, rand) < 0) {
hci_dev_unlock(hdev);
- err = mgmt_cmd_complete(sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
- MGMT_STATUS_FAILED,
- &cp->type, sizeof(cp->type));
- goto done;
+ status = MGMT_STATUS_FAILED;
+ goto complete;
}
+ /* This should return the active RPA, but since the RPA
+ * is only programmed on demand, it is really hard to fill
+ * this in at the moment. For now disallow retrieving
+ * local out-of-band data when privacy is in use.
+ *
+ * Returning the identity address will not help here since
+ * pairing happens before the identity resolving key is
+ * known and thus the connection establishment happens
+ * based on the RPA and not the identity address.
+ */
if (hci_dev_test_flag(hdev, HCI_PRIVACY)) {
- memcpy(addr, &hdev->rpa, 6);
- addr[6] = 0x01;
- } else if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ||
- !bacmp(&hdev->bdaddr, BDADDR_ANY) ||
- (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) &&
- bacmp(&hdev->static_addr, BDADDR_ANY))) {
+ hci_dev_unlock(hdev);
+ status = MGMT_STATUS_REJECTED;
+ goto complete;
+ }
+
+ if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) ||
+ !bacmp(&hdev->bdaddr, BDADDR_ANY) ||
+ (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) &&
+ bacmp(&hdev->static_addr, BDADDR_ANY))) {
memcpy(addr, &hdev->static_addr, 6);
addr[6] = 0x01;
} else {
@@ -6491,16 +6586,19 @@ static int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev,
break;
}
- rp->type = cp->type;
- rp->eir_len = cpu_to_le16(eir_len);
-
hci_dev_unlock(hdev);
hci_sock_set_flag(sk, HCI_MGMT_OOB_DATA_EVENTS);
+ status = MGMT_STATUS_SUCCESS;
+
+complete:
+ rp->type = cp->type;
+ rp->eir_len = cpu_to_le16(eir_len);
+
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
- MGMT_STATUS_SUCCESS, rp, sizeof(*rp) + eir_len);
- if (err < 0)
+ status, rp, sizeof(*rp) + eir_len);
+ if (err < 0 || status)
goto done;
err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev,
@@ -7899,43 +7997,6 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
cmd ? cmd->sk : NULL);
}
-void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
- u8 *rand192, u8 *hash256, u8 *rand256,
- u8 status)
-{
- struct mgmt_pending_cmd *cmd;
-
- BT_DBG("%s status %u", hdev->name, status);
-
- cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
- if (!cmd)
- return;
-
- if (status) {
- mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
- mgmt_status(status));
- } else {
- struct mgmt_rp_read_local_oob_data rp;
- size_t rp_size = sizeof(rp);
-
- memcpy(rp.hash192, hash192, sizeof(rp.hash192));
- memcpy(rp.rand192, rand192, sizeof(rp.rand192));
-
- if (bredr_sc_enabled(hdev) && hash256 && rand256) {
- memcpy(rp.hash256, hash256, sizeof(rp.hash256));
- memcpy(rp.rand256, rand256, sizeof(rp.rand256));
- } else {
- rp_size -= sizeof(rp.hash256) + sizeof(rp.rand256);
- }
-
- mgmt_cmd_complete(cmd->sk, hdev->id,
- MGMT_OP_READ_LOCAL_OOB_DATA, 0,
- &rp, rp_size);
- }
-
- mgmt_pending_remove(cmd);
-}
-
static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16])
{
int i;
diff --git a/net/bluetooth/selftest.c b/net/bluetooth/selftest.c
index 378f4064952c..dc688f13e496 100644
--- a/net/bluetooth/selftest.c
+++ b/net/bluetooth/selftest.c
@@ -21,6 +21,8 @@
SOFTWARE IS DISCLAIMED.
*/
+#include <linux/debugfs.h>
+
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -154,6 +156,21 @@ static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32],
return 0;
}
+static char test_ecdh_buffer[32];
+
+static ssize_t test_ecdh_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return simple_read_from_buffer(user_buf, count, ppos, test_ecdh_buffer,
+ strlen(test_ecdh_buffer));
+}
+
+static const struct file_operations test_ecdh_fops = {
+ .open = simple_open,
+ .read = test_ecdh_read,
+ .llseek = default_llseek,
+};
+
static int __init test_ecdh(void)
{
ktime_t calltime, delta, rettime;
@@ -165,19 +182,19 @@ static int __init test_ecdh(void)
err = test_ecdh_sample(priv_a_1, priv_b_1, pub_a_1, pub_b_1, dhkey_1);
if (err) {
BT_ERR("ECDH sample 1 failed");
- return err;
+ goto done;
}
err = test_ecdh_sample(priv_a_2, priv_b_2, pub_a_2, pub_b_2, dhkey_2);
if (err) {
BT_ERR("ECDH sample 2 failed");
- return err;
+ goto done;
}
err = test_ecdh_sample(priv_a_3, priv_a_3, pub_a_3, pub_a_3, dhkey_3);
if (err) {
BT_ERR("ECDH sample 3 failed");
- return err;
+ goto done;
}
rettime = ktime_get();
@@ -186,7 +203,17 @@ static int __init test_ecdh(void)
BT_INFO("ECDH test passed in %llu usecs", duration);
- return 0;
+done:
+ if (!err)
+ snprintf(test_ecdh_buffer, sizeof(test_ecdh_buffer),
+ "PASS (%llu usecs)\n", duration);
+ else
+ snprintf(test_ecdh_buffer, sizeof(test_ecdh_buffer), "FAIL\n");
+
+ debugfs_create_file("selftest_ecdh", 0444, bt_debugfs, NULL,
+ &test_ecdh_fops);
+
+ return err;
}
#else
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 1ec3f66b5a74..1ab3dc9c8f99 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -3017,7 +3017,7 @@ static struct sk_buff *smp_alloc_skb_cb(struct l2cap_chan *chan,
return ERR_PTR(-ENOMEM);
skb->priority = HCI_PRIO_MAX;
- bt_cb(skb)->chan = chan;
+ bt_cb(skb)->l2cap.chan = chan;
return skb;
}
@@ -3549,6 +3549,21 @@ static int __init test_h6(struct crypto_hash *tfm_cmac)
return 0;
}
+static char test_smp_buffer[32];
+
+static ssize_t test_smp_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ return simple_read_from_buffer(user_buf, count, ppos, test_smp_buffer,
+ strlen(test_smp_buffer));
+}
+
+static const struct file_operations test_smp_fops = {
+ .open = simple_open,
+ .read = test_smp_read,
+ .llseek = default_llseek,
+};
+
static int __init run_selftests(struct crypto_blkcipher *tfm_aes,
struct crypto_hash *tfm_cmac)
{
@@ -3561,49 +3576,49 @@ static int __init run_selftests(struct crypto_blkcipher *tfm_aes,
err = test_ah(tfm_aes);
if (err) {
BT_ERR("smp_ah test failed");
- return err;
+ goto done;
}
err = test_c1(tfm_aes);
if (err) {
BT_ERR("smp_c1 test failed");
- return err;
+ goto done;
}
err = test_s1(tfm_aes);
if (err) {
BT_ERR("smp_s1 test failed");
- return err;
+ goto done;
}
err = test_f4(tfm_cmac);
if (err) {
BT_ERR("smp_f4 test failed");
- return err;
+ goto done;
}
err = test_f5(tfm_cmac);
if (err) {
BT_ERR("smp_f5 test failed");
- return err;
+ goto done;
}
err = test_f6(tfm_cmac);
if (err) {
BT_ERR("smp_f6 test failed");
- return err;
+ goto done;
}
err = test_g2(tfm_cmac);
if (err) {
BT_ERR("smp_g2 test failed");
- return err;
+ goto done;
}
err = test_h6(tfm_cmac);
if (err) {
BT_ERR("smp_h6 test failed");
- return err;
+ goto done;
}
rettime = ktime_get();
@@ -3612,7 +3627,17 @@ static int __init run_selftests(struct crypto_blkcipher *tfm_aes,
BT_INFO("SMP test passed in %llu usecs", duration);
- return 0;
+done:
+ if (!err)
+ snprintf(test_smp_buffer, sizeof(test_smp_buffer),
+ "PASS (%llu usecs)\n", duration);
+ else
+ snprintf(test_smp_buffer, sizeof(test_smp_buffer), "FAIL\n");
+
+ debugfs_create_file("selftest_smp", 0444, bt_debugfs, NULL,
+ &test_smp_fops);
+
+ return err;
}
int __init bt_selftest_smp(void)